From 98c5360b2b80dc380ac8fe376e82cc218d7e08a7 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@protonmail.com>
Date: Sun, 9 Mar 2025 16:45:04 +0100
Subject: [PATCH 01/11] Add drvdata

---
 .../lightning_node_pro_led.c                  | 39 ++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index 2f9ba8f..5b30171 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -5,17 +5,54 @@
 #define USB_VENDOR_ID_CORSAIR 0x1b1c
 #define USB_DEVICE_ID_LIGHTNING_NODE_PRO 0x0c0b
 
+#define NUMBER_OF_ZONES_PER_LL120_FAN 16
+#define LIGHTNODE_PRO_MAX_FAN 6
+
+#define MSG_SIZE 64
+
+#define RED_COLOR 0x00
+#define BLUE_COLOR 0x01
+#define GREEN_COLOR 0x02
+
+static short int number_of_fan = 1;
+module_param(number_of_fan, short, 0000);
+MODULE_PARM_DESC(number_of_fan,
+		 "Number of LL120 FAN connected to the lightning node pro");
+
+struct lightning_node_pro_led_data {
+	struct hid_device *hdev;
+	struct mutex lock;
+};
+
 static int lightning_node_pro_led_probe(struct hid_device *hdev,
 					const struct hid_device_id *id)
 {
 	pr_info("Détection USB pour Lightning Node Pro\n");
 
+	int ret;
+	struct lightning_node_pro_led_data *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->hdev = hdev;
+	mutex_init(&data->lock);
+	hid_set_drvdata(hdev, data);
+
 	return 0;
+
+free_ldata:
+	kfree(data);
+
+	return ret;
 }
 
-static void lightning_node_pro_led_remove(struct hid_device *dev)
+static void lightning_node_pro_led_remove(struct hid_device *hdev)
 {
+	struct lightning_node_pro_led_data *data = hid_get_drvdata(hdev);
 	pr_info("Retrait USB pour Lightning Node Pro\n");
+	kfree(data);
 }
 
 static struct hid_device_id lightning_node_pro_led_table[] = {

From b2e9dfa0c340a2ad4ffef7e95ce470b10794c4d8 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@protonmail.com>
Date: Sun, 9 Mar 2025 18:35:20 +0100
Subject: [PATCH 02/11] Add fans initialization

---
 .../lightning_node_pro_led.c                  | 200 ++++++++++++++++--
 1 file changed, 182 insertions(+), 18 deletions(-)

diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index 5b30171..aab0f90 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -1,11 +1,12 @@
 #include <linux/init.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/led-class-multicolor.h>
 
 #define USB_VENDOR_ID_CORSAIR 0x1b1c
 #define USB_DEVICE_ID_LIGHTNING_NODE_PRO 0x0c0b
 
-#define NUMBER_OF_ZONES_PER_LL120_FAN 16
+#define NUMBER_OF_LEDS_PER_LL120_FAN 16
 #define LIGHTNODE_PRO_MAX_FAN 6
 
 #define MSG_SIZE 64
@@ -19,18 +20,155 @@ module_param(number_of_fan, short, 0000);
 MODULE_PARM_DESC(number_of_fan,
 		 "Number of LL120 FAN connected to the lightning node pro");
 
-struct lightning_node_pro_led_data {
-	struct hid_device *hdev;
-	struct mutex lock;
+enum LNP_LED_TYPE {
+	LNP_LED_LL120,
 };
 
-static int lightning_node_pro_led_probe(struct hid_device *hdev,
-					const struct hid_device_id *id)
+struct lnp_rgb_led {
+	struct led_classdev_mc cdev;
+	uint8_t red;
+	uint8_t green;
+	uint8_t blue;
+};
+
+struct lnp_fan {
+	int fan_index;
+	int rgb_leds_count;
+	struct lnp_rgb_led *rgb_leds_data;
+};
+
+struct lnp_device {
+	struct hid_device *hdev;
+	struct mutex lock;
+	enum LNP_LED_TYPE type;
+	int fans_count;
+	void *fans_data;
+};
+
+static inline int lnp_register_rgb_led(struct rgb_led *rgb_led_data,
+				       int fan_number)
+{
+}
+
+static inline int lnp_unregister_rgb_led(struct rgb_led *rgb_led_data,
+					 int fan_number)
+{
+}
+
+static inline int lnp_register_fan(struct lnp_fan *fan_data, int rgb_leds_count)
+{
+	int ret, led_index;
+
+	fan_data->rgb_leds_data = kzalloc(
+		sizeof(struct lnp_rgb_led) * rgb_leds_count, GFP_KERNEL);
+	if (!fan_data->rgb_leds_data) {
+		ret = -ENOMEM;
+		goto fail_alloc_data;
+	}
+
+	for (led_index = 0; led_index < fan_data->rgb_leds_count; led_index++) {
+		struct lnp_rgb_led *rgb_led_data =
+			fan_data->rgb_leds_data + led_index;
+
+		ret = lnp_register_rgb_led(rgb_led_data, fan_data->fan_index);
+		if (ret)
+			goto fail_register_leds;
+	}
+
+	return 0;
+
+fail_register_leds:
+	for (led_index--; led_index >= 0; led_index--) {
+		struct rgb_led *rgb_led_data =
+			fan_data->rgb_leds_data + led_index;
+		lnp_unregister_rgb_led(rgb_led_data, fan_data->fan_index);
+	}
+
+fail_alloc_data:
+	kfree(fan_data->rgb_leds_data);
+
+	return ret;
+}
+
+static inline void lnp_unregister_fan(struct lnp_fan *fan_data)
+{
+	int ret, led_index;
+	for (led_index = 0; led_index < fan_data->rgb_leds_count; led_index++) {
+		struct lnp_rgb_led *rgb_led_data =
+			fan_data->rgb_leds_data + led_index;
+		lnp_unregister_rgb_led(rgb_led_data, fan_data->fan_index);
+	}
+}
+
+static inline int lnp_register_fans(struct lnp_device *data, int rgb_leds_count)
+{
+	int ret, fan_index, led_index;
+	struct lnp_fan *fans_data = data->fans_data;
+
+	for (fan_index = 0; fan_index < data->fans_count; fan_index++) {
+		struct lnp_fan *fan_data = fans_data + fan_index;
+		fan_data->fan_index = fan_index;
+
+		ret = lnp_register_fan(fan_data, rgb_leds_count);
+		if (ret)
+			goto fail;
+	}
+
+	return 0;
+
+fail:
+	for (fan_index--; fan_index >= 0; fan_index--) {
+		struct ll120_fan *fan_data = fans_data + fan_index;
+		lnp_unregister_fan(fan_data);
+	}
+	return ret;
+}
+
+static inline void lnp_unregister_fans(struct lnp_device *data)
+{
+	int fan_index;
+	struct lnp_fan *fans_data = data->fans_data;
+
+	for (fan_index = 0; fan_index < data->fans_count; fan_index++) {
+		struct lnp_fan *fan_data = fans_data + fan_index;
+		lnp_unregister_fan(fan_data);
+	}
+}
+
+static inline int lnp_ll120_register(struct lnp_device *data)
+{
+	data->fans_count = number_of_fan;
+	data->fans_data =
+		kzalloc(sizeof(struct lnp_fan) * data->fans_count, GFP_KERNEL);
+
+	if (!data->fans_data)
+		return -ENOMEM;
+
+	int ret;
+
+	ret = lnp_register_fans(data, NUMBER_OF_LEDS_PER_LL120_FAN);
+	if (ret)
+		goto fans_data_free;
+
+	return 0;
+
+fans_data_free:
+	kfree(data->fans_data);
+
+	return ret;
+}
+
+static inline void lnp_ll120_unregister(struct lnp_device *data)
+{
+	lnp_unregister_fans(data);
+}
+
+static int lnp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 	pr_info("Détection USB pour Lightning Node Pro\n");
 
 	int ret;
-	struct lightning_node_pro_led_data *data;
+	struct lnp_device *data;
 
 	data = kzalloc(sizeof(*data), GFP_KERNEL);
 	if (!data)
@@ -38,40 +176,66 @@ static int lightning_node_pro_led_probe(struct hid_device *hdev,
 
 	data->hdev = hdev;
 	mutex_init(&data->lock);
+
+	data->type = LNP_LED_LL120; // Only support LL120 for now
+
+	switch (data->type) {
+	case LNP_LED_LL120:
+		ret = lnp_ll120_register(data);
+		break;
+	default:
+		ret = -ENOSYS;
+		pr_err("Fan type invalid\n");
+		break;
+	}
+
+	if (ret)
+		goto free_data;
+
 	hid_set_drvdata(hdev, data);
 
 	return 0;
 
-free_ldata:
+free_data:
 	kfree(data);
 
 	return ret;
 }
 
-static void lightning_node_pro_led_remove(struct hid_device *hdev)
+static void lnp_remove(struct hid_device *hdev)
 {
-	struct lightning_node_pro_led_data *data = hid_get_drvdata(hdev);
+	struct lnp_device *data = hid_get_drvdata(hdev);
+
+	switch (data->type) {
+	case LNP_LED_LL120:
+		lnp_ll120_unregister(data);
+		break;
+	default:
+		pr_err("Fan type invalid\n");
+		break;
+	}
+
 	pr_info("Retrait USB pour Lightning Node Pro\n");
 	kfree(data);
 }
 
-static struct hid_device_id lightning_node_pro_led_table[] = {
+static struct hid_device_id lnp_table[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,
 			 USB_DEVICE_ID_LIGHTNING_NODE_PRO) },
 	{} /* Entrée de terminaison */
 };
-MODULE_DEVICE_TABLE(hid, lightning_node_pro_led_table);
+MODULE_DEVICE_TABLE(hid, lnp_table);
 
-static struct hid_driver lightning_node_pro_led_driver = {
+static struct hid_driver lnp_driver = {
 	.name = "lightning-node-pro",
-	.id_table = lightning_node_pro_led_table,
-	.probe = lightning_node_pro_led_probe,
-	.remove = lightning_node_pro_led_remove,
+	.id_table = lnp_table,
+	.probe = lnp_probe,
+	.remove = lnp_remove,
 };
 
-module_hid_driver(lightning_node_pro_led_driver);
+module_hid_driver(lnp_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Florian RICHER <florian.richer@protonmail.com>");
-MODULE_DESCRIPTION("Un module noyau pour utiliser le Lightning Node Pro LED");
+MODULE_DESCRIPTION("Un module noyau pour utiliser le Lightning Node Pro");
 MODULE_VERSION("1.0");
\ No newline at end of file

From c780a8e8578f82c33e72ebc165dcd008ba2b7816 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@protonmail.com>
Date: Sun, 9 Mar 2025 19:34:00 +0100
Subject: [PATCH 03/11] Use device linked allocator instead and led registering

---
 .../lightning_node_pro_led.c                  | 211 ++++++++----------
 1 file changed, 88 insertions(+), 123 deletions(-)

diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index aab0f90..cf6e999 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -26,6 +26,7 @@ enum LNP_LED_TYPE {
 
 struct lnp_rgb_led {
 	struct led_classdev_mc cdev;
+	int led_index;
 	uint8_t red;
 	uint8_t green;
 	uint8_t blue;
@@ -45,122 +46,101 @@ struct lnp_device {
 	void *fans_data;
 };
 
-static inline int lnp_register_rgb_led(struct rgb_led *rgb_led_data,
-				       int fan_number)
+static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
+				       struct lnp_fan *fan_data,
+				       struct lnp_rgb_led *rgb_led_data)
 {
-}
+	struct hid_device *hdev = lnp_dev->hdev;
+	struct mc_subled *mc_leds_info;
+	struct led_classdev *led_cdev;
+	int ret;
 
-static inline int lnp_unregister_rgb_led(struct rgb_led *rgb_led_data,
-					 int fan_number)
-{
-}
+	mc_leds_info = devm_kmalloc_array(&hdev->dev, 3, sizeof(*mc_leds_info),
+					  GFP_KERNEL);
 
-static inline int lnp_register_fan(struct lnp_fan *fan_data, int rgb_leds_count)
-{
-	int ret, led_index;
+	if (!mc_leds_info)
+		return -ENOMEM;
 
-	fan_data->rgb_leds_data = kzalloc(
-		sizeof(struct lnp_rgb_led) * rgb_leds_count, GFP_KERNEL);
-	if (!fan_data->rgb_leds_data) {
-		ret = -ENOMEM;
-		goto fail_alloc_data;
-	}
+	mc_leds_info[0].color_index = LED_COLOR_ID_RED;
+	mc_leds_info[1].color_index = LED_COLOR_ID_GREEN;
+	mc_leds_info[2].color_index = LED_COLOR_ID_BLUE;
 
-	for (led_index = 0; led_index < fan_data->rgb_leds_count; led_index++) {
-		struct lnp_rgb_led *rgb_led_data =
-			fan_data->rgb_leds_data + led_index;
+	rgb_led_data->cdev.subled_info = mc_leds_info;
+	rgb_led_data->cdev.num_colors = 3;
 
-		ret = lnp_register_rgb_led(rgb_led_data, fan_data->fan_index);
-		if (ret)
-			goto fail_register_leds;
+	led_cdev = &rgb_led_data->cdev.led_cdev;
+	led_cdev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+					"ll120:rgb:fan-%d-led-%d",
+					fan_data->fan_index,
+					rgb_led_data->led_index);
+	if (!led_cdev->name)
+		return -ENOMEM;
+
+	led_cdev->brightness = 0;
+	led_cdev->max_brightness = 255;
+
+	ret = devm_led_classdev_multicolor_register(&hdev->dev,
+						    &rgb_led_data->cdev);
+	if (ret < 0) {
+		hid_err(hdev, "Cannot register multicolor LED device\n");
+		return ret;
 	}
 
 	return 0;
-
-fail_register_leds:
-	for (led_index--; led_index >= 0; led_index--) {
-		struct rgb_led *rgb_led_data =
-			fan_data->rgb_leds_data + led_index;
-		lnp_unregister_rgb_led(rgb_led_data, fan_data->fan_index);
-	}
-
-fail_alloc_data:
-	kfree(fan_data->rgb_leds_data);
-
-	return ret;
 }
 
-static inline void lnp_unregister_fan(struct lnp_fan *fan_data)
+static inline int led_number_for_device(struct lnp_device *lnp_dev)
+{
+	switch (lnp_dev->type) {
+	case LNP_LED_LL120:
+		return NUMBER_OF_LEDS_PER_LL120_FAN;
+	default:
+		return 0;
+	}
+}
+
+static inline int lnp_register_fan(struct lnp_device *lnp_dev,
+				   struct lnp_fan *fan_data)
 {
 	int ret, led_index;
+
+	fan_data->rgb_leds_count = led_number_for_device(lnp_dev);
+	fan_data->rgb_leds_data = devm_kmalloc_array(&lnp_dev->hdev->dev,
+						     fan_data->rgb_leds_count,
+						     sizeof(struct lnp_rgb_led),
+						     GFP_KERNEL | __GFP_ZERO);
+	if (!fan_data->rgb_leds_data) {
+		return -ENOMEM;
+	}
+
 	for (led_index = 0; led_index < fan_data->rgb_leds_count; led_index++) {
 		struct lnp_rgb_led *rgb_led_data =
 			fan_data->rgb_leds_data + led_index;
-		lnp_unregister_rgb_led(rgb_led_data, fan_data->fan_index);
+		rgb_led_data->led_index = led_index;
+
+		ret = lnp_register_rgb_led(lnp_dev, fan_data, rgb_led_data);
+		if (ret)
+			return ret;
 	}
+
+	return 0;
 }
 
-static inline int lnp_register_fans(struct lnp_device *data, int rgb_leds_count)
+static inline int lnp_register_fans(struct lnp_device *lnp_dev)
 {
-	int ret, fan_index, led_index;
-	struct lnp_fan *fans_data = data->fans_data;
+	int ret, fan_index;
+	struct lnp_fan *fans_data = lnp_dev->fans_data;
 
-	for (fan_index = 0; fan_index < data->fans_count; fan_index++) {
+	for (fan_index = 0; fan_index < lnp_dev->fans_count; fan_index++) {
 		struct lnp_fan *fan_data = fans_data + fan_index;
 		fan_data->fan_index = fan_index;
 
-		ret = lnp_register_fan(fan_data, rgb_leds_count);
+		ret = lnp_register_fan(lnp_dev, fan_data);
 		if (ret)
-			goto fail;
+			return ret;
 	}
 
 	return 0;
-
-fail:
-	for (fan_index--; fan_index >= 0; fan_index--) {
-		struct ll120_fan *fan_data = fans_data + fan_index;
-		lnp_unregister_fan(fan_data);
-	}
-	return ret;
-}
-
-static inline void lnp_unregister_fans(struct lnp_device *data)
-{
-	int fan_index;
-	struct lnp_fan *fans_data = data->fans_data;
-
-	for (fan_index = 0; fan_index < data->fans_count; fan_index++) {
-		struct lnp_fan *fan_data = fans_data + fan_index;
-		lnp_unregister_fan(fan_data);
-	}
-}
-
-static inline int lnp_ll120_register(struct lnp_device *data)
-{
-	data->fans_count = number_of_fan;
-	data->fans_data =
-		kzalloc(sizeof(struct lnp_fan) * data->fans_count, GFP_KERNEL);
-
-	if (!data->fans_data)
-		return -ENOMEM;
-
-	int ret;
-
-	ret = lnp_register_fans(data, NUMBER_OF_LEDS_PER_LL120_FAN);
-	if (ret)
-		goto fans_data_free;
-
-	return 0;
-
-fans_data_free:
-	kfree(data->fans_data);
-
-	return ret;
-}
-
-static inline void lnp_ll120_unregister(struct lnp_device *data)
-{
-	lnp_unregister_fans(data);
 }
 
 static int lnp_probe(struct hid_device *hdev, const struct hid_device_id *id)
@@ -168,55 +148,40 @@ static int lnp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	pr_info("Détection USB pour Lightning Node Pro\n");
 
 	int ret;
-	struct lnp_device *data;
+	struct lnp_device *lnp_dev;
 
-	data = kzalloc(sizeof(*data), GFP_KERNEL);
-	if (!data)
+	lnp_dev = devm_kzalloc(&hdev->dev, sizeof(*lnp_dev), GFP_KERNEL);
+	if (!lnp_dev)
 		return -ENOMEM;
 
-	data->hdev = hdev;
-	mutex_init(&data->lock);
+	lnp_dev->hdev = hdev;
+	mutex_init(&lnp_dev->lock);
 
-	data->type = LNP_LED_LL120; // Only support LL120 for now
+	lnp_dev->type = LNP_LED_LL120; // Only support LL120 for now
+	lnp_dev->fans_count = number_of_fan;
 
-	switch (data->type) {
-	case LNP_LED_LL120:
-		ret = lnp_ll120_register(data);
-		break;
-	default:
-		ret = -ENOSYS;
-		pr_err("Fan type invalid\n");
-		break;
+	lnp_dev->fans_data = devm_kmalloc_array(&lnp_dev->hdev->dev,
+						lnp_dev->fans_count,
+						sizeof(struct lnp_fan),
+						GFP_KERNEL | __GFP_ZERO);
+
+	if (!lnp_dev->fans_data)
+		return -ENOMEM;
+
+	ret = lnp_register_fans(lnp_dev);
+	if (ret) {
+		hid_err(lnp_dev->hdev, "Failed to register device\n");
+		return ret;
 	}
 
-	if (ret)
-		goto free_data;
-
-	hid_set_drvdata(hdev, data);
+	hid_set_drvdata(hdev, lnp_dev);
 
 	return 0;
-
-free_data:
-	kfree(data);
-
-	return ret;
 }
 
 static void lnp_remove(struct hid_device *hdev)
 {
-	struct lnp_device *data = hid_get_drvdata(hdev);
-
-	switch (data->type) {
-	case LNP_LED_LL120:
-		lnp_ll120_unregister(data);
-		break;
-	default:
-		pr_err("Fan type invalid\n");
-		break;
-	}
-
 	pr_info("Retrait USB pour Lightning Node Pro\n");
-	kfree(data);
 }
 
 static struct hid_device_id lnp_table[] = {

From a124cb33a8f9b18f18383e8e52392771736d6814 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@protonmail.com>
Date: Sun, 9 Mar 2025 20:13:23 +0100
Subject: [PATCH 04/11] add dev_name, move rgb_leds_count into device, open hw

---
 .../lightning_node_pro_led.c                  | 81 ++++++++++++++-----
 1 file changed, 59 insertions(+), 22 deletions(-)

diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index cf6e999..1c74265 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -34,15 +34,18 @@ struct lnp_rgb_led {
 
 struct lnp_fan {
 	int fan_index;
-	int rgb_leds_count;
 	struct lnp_rgb_led *rgb_leds_data;
 };
 
 struct lnp_device {
 	struct hid_device *hdev;
 	struct mutex lock;
+
+	const char *dev_name;
+
 	enum LNP_LED_TYPE type;
 	int fans_count;
+	int rgb_leds_per_fan_count;
 	void *fans_data;
 };
 
@@ -70,8 +73,8 @@ static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
 
 	led_cdev = &rgb_led_data->cdev.led_cdev;
 	led_cdev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
-					"ll120:rgb:fan-%d-led-%d",
-					fan_data->fan_index,
+					"%s:rgb:fan-%d-led-%d",
+					lnp_dev->dev_name, fan_data->fan_index,
 					rgb_led_data->led_index);
 	if (!led_cdev->name)
 		return -ENOMEM;
@@ -89,31 +92,20 @@ static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
 	return 0;
 }
 
-static inline int led_number_for_device(struct lnp_device *lnp_dev)
-{
-	switch (lnp_dev->type) {
-	case LNP_LED_LL120:
-		return NUMBER_OF_LEDS_PER_LL120_FAN;
-	default:
-		return 0;
-	}
-}
-
 static inline int lnp_register_fan(struct lnp_device *lnp_dev,
 				   struct lnp_fan *fan_data)
 {
 	int ret, led_index;
 
-	fan_data->rgb_leds_count = led_number_for_device(lnp_dev);
-	fan_data->rgb_leds_data = devm_kmalloc_array(&lnp_dev->hdev->dev,
-						     fan_data->rgb_leds_count,
-						     sizeof(struct lnp_rgb_led),
-						     GFP_KERNEL | __GFP_ZERO);
+	fan_data->rgb_leds_data = devm_kmalloc_array(
+		&lnp_dev->hdev->dev, lnp_dev->rgb_leds_per_fan_count,
+		sizeof(struct lnp_rgb_led), GFP_KERNEL | __GFP_ZERO);
 	if (!fan_data->rgb_leds_data) {
 		return -ENOMEM;
 	}
 
-	for (led_index = 0; led_index < fan_data->rgb_leds_count; led_index++) {
+	for (led_index = 0; led_index < lnp_dev->rgb_leds_per_fan_count;
+	     led_index++) {
 		struct lnp_rgb_led *rgb_led_data =
 			fan_data->rgb_leds_data + led_index;
 		rgb_led_data->led_index = led_index;
@@ -143,10 +135,8 @@ static inline int lnp_register_fans(struct lnp_device *lnp_dev)
 	return 0;
 }
 
-static int lnp_probe(struct hid_device *hdev, const struct hid_device_id *id)
+static inline int lnp_init(struct hid_device *hdev)
 {
-	pr_info("Détection USB pour Lightning Node Pro\n");
-
 	int ret;
 	struct lnp_device *lnp_dev;
 
@@ -155,11 +145,19 @@ static int lnp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		return -ENOMEM;
 
 	lnp_dev->hdev = hdev;
+	lnp_dev->dev_name = dev_name(&hdev->dev);
 	mutex_init(&lnp_dev->lock);
 
 	lnp_dev->type = LNP_LED_LL120; // Only support LL120 for now
 	lnp_dev->fans_count = number_of_fan;
 
+	if (lnp_dev->type == LNP_LED_LL120) {
+		lnp_dev->rgb_leds_per_fan_count = NUMBER_OF_LEDS_PER_LL120_FAN;
+	} else {
+		hid_err(lnp_dev->hdev, "Invalid fan type\n");
+		return -ENOSYS;
+	}
+
 	lnp_dev->fans_data = devm_kmalloc_array(&lnp_dev->hdev->dev,
 						lnp_dev->fans_count,
 						sizeof(struct lnp_fan),
@@ -179,9 +177,48 @@ static int lnp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	return 0;
 }
 
+static int lnp_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	pr_info("Détection USB pour Lightning Node Pro\n");
+
+	int ret;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "Parse failed\n");
+		return ret;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+	if (ret) {
+		hid_err(hdev, "Failed to start HID device\n");
+		return ret;
+	}
+
+	ret = hid_hw_open(hdev);
+	if (ret) {
+		hid_err(hdev, "Failed to open HID device\n");
+		goto err_stop;
+	}
+
+	ret = lnp_init(hdev);
+	if (ret)
+		goto err_close;
+
+	return 0;
+
+err_close:
+	hid_hw_close(hdev);
+err_stop:
+	hid_hw_stop(hdev);
+	return ret;
+}
+
 static void lnp_remove(struct hid_device *hdev)
 {
 	pr_info("Retrait USB pour Lightning Node Pro\n");
+	hid_hw_close(hdev);
+	hid_hw_stop(hdev);
 }
 
 static struct hid_device_id lnp_table[] = {

From 5095cabcadb2e63c9db016fff35dd9861019c145 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@protonmail.com>
Date: Sun, 9 Mar 2025 20:51:43 +0100
Subject: [PATCH 05/11] Add more hid_err

---
 .../lightning_node_pro_led.c                  | 34 ++++++++++++++-----
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index 1c74265..5b42122 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -61,8 +61,12 @@ static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
 	mc_leds_info = devm_kmalloc_array(&hdev->dev, 3, sizeof(*mc_leds_info),
 					  GFP_KERNEL);
 
-	if (!mc_leds_info)
+	if (!mc_leds_info) {
+		hid_err(lnp_dev->hdev,
+			"Failed to allocate multicolor leds info for LED %d on FAN %d\n",
+			rgb_led_data->led_index, fan_data->fan_index);
 		return -ENOMEM;
+	}
 
 	mc_leds_info[0].color_index = LED_COLOR_ID_RED;
 	mc_leds_info[1].color_index = LED_COLOR_ID_GREEN;
@@ -76,8 +80,13 @@ static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
 					"%s:rgb:fan-%d-led-%d",
 					lnp_dev->dev_name, fan_data->fan_index,
 					rgb_led_data->led_index);
-	if (!led_cdev->name)
+
+	if (!led_cdev->name) {
+		hid_err(lnp_dev->hdev,
+			"Failed to allocate name for LED %d on FAN %d\n",
+			rgb_led_data->led_index, fan_data->fan_index);
 		return -ENOMEM;
+	}
 
 	led_cdev->brightness = 0;
 	led_cdev->max_brightness = 255;
@@ -85,7 +94,9 @@ static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
 	ret = devm_led_classdev_multicolor_register(&hdev->dev,
 						    &rgb_led_data->cdev);
 	if (ret < 0) {
-		hid_err(hdev, "Cannot register multicolor LED device\n");
+		hid_err(hdev,
+			"Cannot register multicolor LED device for LED %d on FAN %d\n",
+			rgb_led_data->led_index, fan_data->fan_index);
 		return ret;
 	}
 
@@ -100,7 +111,11 @@ static inline int lnp_register_fan(struct lnp_device *lnp_dev,
 	fan_data->rgb_leds_data = devm_kmalloc_array(
 		&lnp_dev->hdev->dev, lnp_dev->rgb_leds_per_fan_count,
 		sizeof(struct lnp_rgb_led), GFP_KERNEL | __GFP_ZERO);
+
 	if (!fan_data->rgb_leds_data) {
+		hid_err(lnp_dev->hdev,
+			"Failed to allocate rgb leds data for FAN %d\n",
+			fan_data->fan_index);
 		return -ENOMEM;
 	}
 
@@ -141,8 +156,10 @@ static inline int lnp_init(struct hid_device *hdev)
 	struct lnp_device *lnp_dev;
 
 	lnp_dev = devm_kzalloc(&hdev->dev, sizeof(*lnp_dev), GFP_KERNEL);
-	if (!lnp_dev)
+	if (!lnp_dev) {
+		hid_err(hdev, "Failed to allocate lnp device data\n");
 		return -ENOMEM;
+	}
 
 	lnp_dev->hdev = hdev;
 	lnp_dev->dev_name = dev_name(&hdev->dev);
@@ -154,7 +171,7 @@ static inline int lnp_init(struct hid_device *hdev)
 	if (lnp_dev->type == LNP_LED_LL120) {
 		lnp_dev->rgb_leds_per_fan_count = NUMBER_OF_LEDS_PER_LL120_FAN;
 	} else {
-		hid_err(lnp_dev->hdev, "Invalid fan type\n");
+		hid_err(lnp_dev->hdev, "Invalid fan type %d\n", lnp_dev->type);
 		return -ENOSYS;
 	}
 
@@ -163,8 +180,10 @@ static inline int lnp_init(struct hid_device *hdev)
 						sizeof(struct lnp_fan),
 						GFP_KERNEL | __GFP_ZERO);
 
-	if (!lnp_dev->fans_data)
+	if (!lnp_dev->fans_data) {
+		hid_err(hdev, "Failed to allocate fans data\n");
 		return -ENOMEM;
+	}
 
 	ret = lnp_register_fans(lnp_dev);
 	if (ret) {
@@ -179,8 +198,6 @@ static inline int lnp_init(struct hid_device *hdev)
 
 static int lnp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
-	pr_info("Détection USB pour Lightning Node Pro\n");
-
 	int ret;
 
 	ret = hid_parse(hdev);
@@ -216,7 +233,6 @@ err_stop:
 
 static void lnp_remove(struct hid_device *hdev)
 {
-	pr_info("Retrait USB pour Lightning Node Pro\n");
 	hid_hw_close(hdev);
 	hid_hw_stop(hdev);
 }

From 0d285558c3c34e97eec2a10765cad220ecf8ea82 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@axyus.com>
Date: Mon, 10 Mar 2025 13:58:10 +0100
Subject: [PATCH 06/11] Begin add get lnp_rgb_led from cdev

---
 .../lightning_node_pro_led.c                  | 31 +++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index 5b42122..384e4d4 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -49,6 +49,35 @@ struct lnp_device {
 	void *fans_data;
 };
 
+static int lnp_rgb_let_brightness_set(struct led_classdev *cdev,
+				      enum led_brightness)
+{
+	struct hid_device *hdev = to_hid_device(cdev->dev->parent);
+	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
+	struct lnp_device *lnp_dev = hid_get_drvdata(hdev);
+
+	int ret, fan_index, led_index;
+	ret = sscanf(cdev->name, "%*[^:]:%*[^:]:fan-%d-led-%d", &fan_index,
+		     &led_index);
+	if (ret != 2) {
+		hid_warn(hdev, "Failed to get fan index and led_index for %s",
+			 lnp_dev->dev_name);
+		return -ENOMSG;
+	}
+
+	hid_info(hdev, "Fan Index: %d, Led Index: %d", fan_index, led_index);
+
+	struct lnp_fan *lnp_fan = lnp_dev->fans_data + fan_index;
+	struct lnp_rgb_led *lnp_rgb_led = lnp_fan->rgb_leds_data + led_index;
+
+	return 0;
+}
+
+static enum led_brightness lnp_rgb_let_brightness_get(struct led_classdev *cdev)
+{
+	return 0;
+}
+
 static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
 				       struct lnp_fan *fan_data,
 				       struct lnp_rgb_led *rgb_led_data)
@@ -90,6 +119,8 @@ static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
 
 	led_cdev->brightness = 0;
 	led_cdev->max_brightness = 255;
+	led_cdev->brightness_get = lnp_rgb_let_brightness_get;
+	led_cdev->brightness_set_blocking = lnp_rgb_let_brightness_set;
 
 	ret = devm_led_classdev_multicolor_register(&hdev->dev,
 						    &rgb_led_data->cdev);

From e29b9e272326e487e4f7ae168ca6ac19a610e8ef Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@protonmail.com>
Date: Tue, 11 Mar 2025 21:29:25 +0100
Subject: [PATCH 07/11] Test and fix extract fan index and led index

---
 .../lightning_node_pro_led.c                  | 26 ++++++++++++++-----
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index 384e4d4..8c1c0be 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -49,6 +49,22 @@ struct lnp_device {
 	void *fans_data;
 };
 
+static inline int lnp_extract_fan_and_led_index(struct led_classdev *cdev,
+						int *fan_index, int *led_index)
+{
+	int ret;
+
+	char *substr = strstr(cdev->name, "fan-");
+	if (!substr)
+		return -EINVAL;
+
+	ret = ret = sscanf(substr, "fan-%d-led-%d", fan_index, led_index);
+	if (ret != 2)
+		return -EINVAL;
+
+	return 0;
+}
+
 static int lnp_rgb_let_brightness_set(struct led_classdev *cdev,
 				      enum led_brightness)
 {
@@ -57,16 +73,14 @@ static int lnp_rgb_let_brightness_set(struct led_classdev *cdev,
 	struct lnp_device *lnp_dev = hid_get_drvdata(hdev);
 
 	int ret, fan_index, led_index;
-	ret = sscanf(cdev->name, "%*[^:]:%*[^:]:fan-%d-led-%d", &fan_index,
-		     &led_index);
-	if (ret != 2) {
+
+	ret = lnp_extract_fan_and_led_index(cdev, &fan_index, &led_index);
+	if (ret) {
 		hid_warn(hdev, "Failed to get fan index and led_index for %s",
 			 lnp_dev->dev_name);
-		return -ENOMSG;
+		return ret;
 	}
 
-	hid_info(hdev, "Fan Index: %d, Led Index: %d", fan_index, led_index);
-
 	struct lnp_fan *lnp_fan = lnp_dev->fans_data + fan_index;
 	struct lnp_rgb_led *lnp_rgb_led = lnp_fan->rgb_leds_data + led_index;
 

From cc1deb72bf8c25f7b93d3fdabcedc1e93c072516 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@axyus.com>
Date: Wed, 12 Mar 2025 17:13:37 +0100
Subject: [PATCH 08/11] Add set_led_colors

---
 .../lightning_node_pro_led.c                  | 42 ++++++++++++++++---
 1 file changed, 37 insertions(+), 5 deletions(-)

diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index 8c1c0be..92b5e26 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -39,7 +39,7 @@ struct lnp_fan {
 
 struct lnp_device {
 	struct hid_device *hdev;
-	struct mutex lock;
+	spinlock_t lock;
 
 	const char *dev_name;
 
@@ -47,8 +47,33 @@ struct lnp_device {
 	int fans_count;
 	int rgb_leds_per_fan_count;
 	void *fans_data;
+	bool update_fans_leds;
 };
 
+static inline void lnp_schedule_work(struct lnp_device *lnp_dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&lnp_dev->lock, flags);
+	// if (ds->output_worker_initialized)
+	// 	schedule_work(&ds->output_worker);
+	spin_unlock_irqrestore(&lnp_dev->lock, flags);
+}
+
+static void lnp_set_led_colors(struct lnp_device *lnp_dev,
+			       struct lnp_rgb_led *lnp_rgb_led, uint8_t red,
+			       uint8_t green, uint8_t blue)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&lnp_dev->lock, flags);
+	lnp_dev->update_fans_leds = true;
+	lnp_rgb_led->red = red;
+	lnp_rgb_led->green = green;
+	lnp_rgb_led->blue = blue;
+	spin_unlock_irqrestore(&lnp_dev->lock, flags);
+}
+
 static inline int lnp_extract_fan_and_led_index(struct led_classdev *cdev,
 						int *fan_index, int *led_index)
 {
@@ -58,7 +83,7 @@ static inline int lnp_extract_fan_and_led_index(struct led_classdev *cdev,
 	if (!substr)
 		return -EINVAL;
 
-	ret = ret = sscanf(substr, "fan-%d-led-%d", fan_index, led_index);
+	ret = sscanf(substr, "fan-%d-led-%d", fan_index, led_index);
 	if (ret != 2)
 		return -EINVAL;
 
@@ -66,7 +91,7 @@ static inline int lnp_extract_fan_and_led_index(struct led_classdev *cdev,
 }
 
 static int lnp_rgb_let_brightness_set(struct led_classdev *cdev,
-				      enum led_brightness)
+				      enum led_brightness brightness)
 {
 	struct hid_device *hdev = to_hid_device(cdev->dev->parent);
 	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
@@ -83,6 +108,15 @@ static int lnp_rgb_let_brightness_set(struct led_classdev *cdev,
 
 	struct lnp_fan *lnp_fan = lnp_dev->fans_data + fan_index;
 	struct lnp_rgb_led *lnp_rgb_led = lnp_fan->rgb_leds_data + led_index;
+	uint8_t red, green, blue;
+
+	led_mc_calc_color_components(mc_cdev, brightness);
+
+	red = mc_cdev->subled_info[0].brightness;
+	green = mc_cdev->subled_info[1].brightness;
+	blue = mc_cdev->subled_info[2].brightness;
+
+	lnp_set_led_colors(lnp_dev, lnp_rgb_led, red, green, blue);
 
 	return 0;
 }
@@ -208,8 +242,6 @@ static inline int lnp_init(struct hid_device *hdev)
 
 	lnp_dev->hdev = hdev;
 	lnp_dev->dev_name = dev_name(&hdev->dev);
-	mutex_init(&lnp_dev->lock);
-
 	lnp_dev->type = LNP_LED_LL120; // Only support LL120 for now
 	lnp_dev->fans_count = number_of_fan;
 

From ca62408dc511df47b6890c6ce953d1c5be5d84ff Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@protonmail.com>
Date: Wed, 12 Mar 2025 21:16:00 +0100
Subject: [PATCH 09/11] Begin implement packets

---
 .../lightning_node_pro_led.c                  | 122 ++++++++++++++++--
 1 file changed, 112 insertions(+), 10 deletions(-)

diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index 92b5e26..8be8b5b 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -12,8 +12,8 @@
 #define MSG_SIZE 64
 
 #define RED_COLOR 0x00
-#define BLUE_COLOR 0x01
-#define GREEN_COLOR 0x02
+#define GREEN_COLOR 0x01
+#define BLUE_COLOR 0x02
 
 static short int number_of_fan = 1;
 module_param(number_of_fan, short, 0000);
@@ -48,15 +48,107 @@ struct lnp_device {
 	int rgb_leds_per_fan_count;
 	void *fans_data;
 	bool update_fans_leds;
+
+	struct work_struct output_worker;
+	bool output_worker_initialized;
 };
 
+static int lnp_send_reports(struct hid_device *hdev, u8 **pkts, u8 pkts_count)
+{
+	int i, ret;
+
+	for (i = 0; i < pkts_count; i++) {
+		u8 *pkt = *(pkts + i);
+
+		ret = hid_hw_output_report(hdev, pkt, MSG_SIZE);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int lnp_send_fans_leds_report(struct lnp_device *led_dev)
+{
+	int ret;
+
+	u8 *packets[7];
+	u8 pktcolors[6][MSG_SIZE];
+
+	// INDEX 1 = r, 2 = g, 3 = b, 4 = s, 5 = t, 6 = u
+	// s, t, u are R, G, B extended for fans 3-6
+	for (u8 i = 1; i <= 6; i++) {
+		// Prepare the packet in pktcolors[i]
+		u8 *pktcolor = pktcolors[i];
+
+		// Initialize the first 4 bytes
+		pktcolor[0] = 0x32;
+		pktcolor[1] = 0x00;
+		// For Fan [1-3]: 0x32, 0x00, 0x00, 0x32
+		// For Fan [4-6]: 0x32, 0x00, 0x32, 0x2e
+		pktcolor[2] = (i > 2) ? 0x32 : 0x00;
+		pktcolor[3] = (i > 2) ? 0x2e : 0x32;
+
+		// For red color the fifth Bytes must be equals to 0x00
+		// For green color the fifth Bytes must be equals to 0x01
+		// For blue color the fifth Bytes must be equals to 0x02
+		if (i == 1 || i == 4) {
+			pktcolor[4] = RED_COLOR;
+		}
+		if (i == 2 || i == 5) {
+			pktcolor[4] = GREEN_COLOR;
+		}
+		if (i == 3 || i == 6) {
+			pktcolor[4] = BLUE_COLOR;
+		}
+
+		packets[i - 1] = pktcolor;
+	}
+
+	u8 pktend[MSG_SIZE] = { 0x33, 0xff };
+	packets[6] = &pktend[0];
+
+	ret = lnp_send_reports(led_dev->hdev, &packets[0],
+			       7); // TODO: Check if 2d array usage is correct
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int lnp_send_init_report(struct hid_device *hdev)
+{
+	int ret;
+
+	u8 pkt1[MSG_SIZE] = { 0x37 };
+	// 0x35 - Init
+	u8 pkt2[MSG_SIZE] = { 0x35, 0x00, 0x00, number_of_fan << 4,
+			      0x00, 0x01, 0x01 };
+	u8 pkt3[MSG_SIZE] = { 0x3b, 0x00, 0x01 };
+	u8 pkt4[MSG_SIZE] = { 0x38, 0x00, 0x02 };
+	u8 pkt5[MSG_SIZE] = { 0x34 };
+	u8 pkt6[MSG_SIZE] = { 0x37, 0x01 };
+	u8 pkt7[MSG_SIZE] = { 0x34, 0x01 };
+	u8 pkt8[MSG_SIZE] = { 0x38, 0x01, 0x01 };
+	u8 pkt9[MSG_SIZE] = { 0x33, 0xff };
+
+	u8 *pkts[9] = { &pkt1[0], &pkt2[0], &pkt3[0], &pkt4[0], &pkt5[0],
+			&pkt6[0], &pkt7[0], &pkt8[0], &pkt9[0] };
+
+	ret = lnp_send_reports(hdev, &pkts[0], 9);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
 static inline void lnp_schedule_work(struct lnp_device *lnp_dev)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&lnp_dev->lock, flags);
-	// if (ds->output_worker_initialized)
-	// 	schedule_work(&ds->output_worker);
+	if (lnp_dev->output_worker_initialized)
+		schedule_work(&lnp_dev->output_worker);
 	spin_unlock_irqrestore(&lnp_dev->lock, flags);
 }
 
@@ -121,11 +213,6 @@ static int lnp_rgb_let_brightness_set(struct led_classdev *cdev,
 	return 0;
 }
 
-static enum led_brightness lnp_rgb_let_brightness_get(struct led_classdev *cdev)
-{
-	return 0;
-}
-
 static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
 				       struct lnp_fan *fan_data,
 				       struct lnp_rgb_led *rgb_led_data)
@@ -167,7 +254,6 @@ static inline int lnp_register_rgb_led(struct lnp_device *lnp_dev,
 
 	led_cdev->brightness = 0;
 	led_cdev->max_brightness = 255;
-	led_cdev->brightness_get = lnp_rgb_let_brightness_get;
 	led_cdev->brightness_set_blocking = lnp_rgb_let_brightness_set;
 
 	ret = devm_led_classdev_multicolor_register(&hdev->dev,
@@ -229,6 +315,19 @@ static inline int lnp_register_fans(struct lnp_device *lnp_dev)
 	return 0;
 }
 
+static void lnp_output_worker(struct work_struct *work)
+{
+	struct lnp_device *lnp_dev =
+		container_of(work, struct lnp_device, output_worker);
+	unsigned long flags;
+
+	spin_lock_irqsave(&lnp_dev->lock, flags);
+
+	spin_unlock_irqrestore(&lnp_dev->lock, flags);
+
+	// hid_hw_output_report(lnp_dev->hdev, data, len);
+}
+
 static inline int lnp_init(struct hid_device *hdev)
 {
 	int ret;
@@ -244,6 +343,9 @@ static inline int lnp_init(struct hid_device *hdev)
 	lnp_dev->dev_name = dev_name(&hdev->dev);
 	lnp_dev->type = LNP_LED_LL120; // Only support LL120 for now
 	lnp_dev->fans_count = number_of_fan;
+	spin_lock_init(&lnp_dev->lock);
+	INIT_WORK(&lnp_dev->output_worker, lnp_output_worker);
+	lnp_dev->output_worker_initialized = true;
 
 	if (lnp_dev->type == LNP_LED_LL120) {
 		lnp_dev->rgb_leds_per_fan_count = NUMBER_OF_LEDS_PER_LL120_FAN;

From f589e28d3e673c415b8187618661e309940545b4 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@protonmail.com>
Date: Thu, 13 Mar 2025 01:01:16 +0100
Subject: [PATCH 10/11] Add docs

---
 10_lightning_node_pro_led/README.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/10_lightning_node_pro_led/README.md b/10_lightning_node_pro_led/README.md
index dca7723..10e6fd1 100644
--- a/10_lightning_node_pro_led/README.md
+++ b/10_lightning_node_pro_led/README.md
@@ -16,4 +16,6 @@ If is attached to driver, it appear here.
 
 ## Usefull links
 
-- https://github.com/torvalds/linux/blob/master/drivers/hid/hid-led.c
\ No newline at end of file
+- https://github.com/torvalds/linux/blob/master/drivers/hid/hid-led.c
+- https://github.com/torvalds/linux/blob/master/drivers/hid/hid-playstation.c
+- https://github.com/Legion2/CorsairLightingProtocol
\ No newline at end of file

From afa51c37b34fe2a70287d3bd9f8f8b56f02f2ec8 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@axyus.com>
Date: Thu, 13 Mar 2025 13:31:22 +0100
Subject: [PATCH 11/11] Get consts from OpenRGB

---
 10_lightning_node_pro_led/README.md           |  2 +
 .../lightning_node_pro_led.c                  | 83 +++++++++++++++++--
 2 files changed, 77 insertions(+), 8 deletions(-)

diff --git a/10_lightning_node_pro_led/README.md b/10_lightning_node_pro_led/README.md
index 10e6fd1..1ba3d39 100644
--- a/10_lightning_node_pro_led/README.md
+++ b/10_lightning_node_pro_led/README.md
@@ -18,4 +18,6 @@ If is attached to driver, it appear here.
 
 - https://github.com/torvalds/linux/blob/master/drivers/hid/hid-led.c
 - https://github.com/torvalds/linux/blob/master/drivers/hid/hid-playstation.c
+- https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/master/Controllers/CorsairLightingNodeController/CorsairLightingNodeController.cpp
+- https://github.com/benburkhart1/lighting-node-pro
 - https://github.com/Legion2/CorsairLightingProtocol
\ No newline at end of file
diff --git a/10_lightning_node_pro_led/lightning_node_pro_led.c b/10_lightning_node_pro_led/lightning_node_pro_led.c
index 8be8b5b..cc38eee 100644
--- a/10_lightning_node_pro_led/lightning_node_pro_led.c
+++ b/10_lightning_node_pro_led/lightning_node_pro_led.c
@@ -11,10 +11,6 @@
 
 #define MSG_SIZE 64
 
-#define RED_COLOR 0x00
-#define GREEN_COLOR 0x01
-#define BLUE_COLOR 0x02
-
 static short int number_of_fan = 1;
 module_param(number_of_fan, short, 0000);
 MODULE_PARM_DESC(number_of_fan,
@@ -24,6 +20,77 @@ enum LNP_LED_TYPE {
 	LNP_LED_LL120,
 };
 
+enum {
+	LNP_PACKET_ID_FIRMWARE =
+		0x02, /* Get firmware version                 */
+	LNP_PACKET_ID_DIRECT = 0x32, /* Direct mode LED update packet        */
+	LNP_PACKET_ID_COMMIT = 0x33, /* Commit changes packet                */
+	LNP_PACKET_ID_BEGIN = 0x34, /* Begin effect packet                  */
+	LNP_PACKET_ID_EFFECT_CONFIG =
+		0x35, /* Effect mode configuration packet     */
+	LNP_PACKET_ID_TEMPERATURE =
+		0x36, /* Update temperature value packet      */
+	LNP_PACKET_ID_RESET = 0x37, /* Reset channel packet                 */
+	LNP_PACKET_ID_PORT_STATE =
+		0x38, /* Set port state packet                */
+	LNP_PACKET_ID_BRIGHTNESS =
+		0x39, /* Set brightness packet                */
+	LNP_PACKET_ID_LED_COUNT =
+		0x3A, /* Set LED count packet                 */
+	LNP_PACKET_ID_PROTOCOL =
+		0x3B, /* Set protocol packet                  */
+};
+
+enum {
+	LNP_DIRECT_CHANNEL_RED =
+		0x00, /* Red channel for direct update        */
+	LNP_DIRECT_CHANNEL_GREEN =
+		0x01, /* Green channel for direct update      */
+	LNP_DIRECT_CHANNEL_BLUE =
+		0x02, /* Blue channel for direct update       */
+};
+
+enum {
+	LNP_PORT_STATE_HARDWARE =
+		0x01, /* Effect hardware control of channel   */
+	LNP_PORT_STATE_SOFTWARE =
+		0x02, /* Direct software control of channel   */
+};
+
+enum {
+	LNP_LED_TYPE_LED_STRIP =
+		0x0A, /* Corsair LED Strip Type               */
+	LNP_LED_TYPE_HD_FAN = 0x0C, /* Corsair HD-series Fan Type           */
+	LNP_LED_TYPE_SP_FAN = 0x01, /* Corsair SP-series Fan Type           */
+	LNP_LED_TYPE_ML_FAN = 0x02, /* Corsair ML-series Fan Type           */
+};
+
+enum {
+	LNP_CHANNEL_1 = 0x00, /* Channel 1                            */
+	LNP_CHANNEL_2 = 0x01, /* Channel 2                            */
+	LNP_NUM_CHANNELS = 0x02, /* Number of channels                   */
+};
+
+enum {
+	LNP_SPEED_FAST = 0x00, /* Fast speed                           */
+	LNP_SPEED_MEDIUM = 0x01, /* Medium speed                         */
+	LNP_SPEED_SLOW = 0x02, /* Slow speed                           */
+};
+
+enum {
+	LNP_MODE_RAINBOW_WAVE = 0x00, /* Rainbow Wave mode                    */
+	LNP_MODE_COLOR_SHIFT = 0x01, /* Color Shift mode                     */
+	LNP_MODE_COLOR_PULSE = 0x02, /* Color Pulse mode                     */
+	LNP_MODE_COLOR_WAVE = 0x03, /* Color Wave mode                      */
+	LNP_MODE_STATIC = 0x04, /* Static mode                          */
+	LNP_MODE_TEMPERATURE = 0x05, /* Temperature mode                     */
+	LNP_MODE_VISOR = 0x06, /* Visor mode                           */
+	LNP_MODE_MARQUEE = 0x07, /* Marquee mode                         */
+	LNP_MODE_BLINK = 0x08, /* Blink mode                           */
+	LNP_MODE_SEQUENTIAL = 0x09, /* Sequential mode                      */
+	LNP_MODE_RAINBOW = 0x0A, /* Rainbow mode                         */
+};
+
 struct lnp_rgb_led {
 	struct led_classdev_mc cdev;
 	int led_index;
@@ -93,13 +160,13 @@ static int lnp_send_fans_leds_report(struct lnp_device *led_dev)
 		// For green color the fifth Bytes must be equals to 0x01
 		// For blue color the fifth Bytes must be equals to 0x02
 		if (i == 1 || i == 4) {
-			pktcolor[4] = RED_COLOR;
+			pktcolor[4] = LNP_DIRECT_CHANNEL_RED;
 		}
 		if (i == 2 || i == 5) {
-			pktcolor[4] = GREEN_COLOR;
+			pktcolor[4] = LNP_DIRECT_CHANNEL_GREEN;
 		}
 		if (i == 3 || i == 6) {
-			pktcolor[4] = BLUE_COLOR;
+			pktcolor[4] = LNP_DIRECT_CHANNEL_BLUE;
 		}
 
 		packets[i - 1] = pktcolor;
@@ -120,7 +187,7 @@ static int lnp_send_init_report(struct hid_device *hdev)
 {
 	int ret;
 
-	u8 pkt1[MSG_SIZE] = { 0x37 };
+	u8 pkt1[MSG_SIZE] = { LNP_PACKET_ID_RESET };
 	// 0x35 - Init
 	u8 pkt2[MSG_SIZE] = { 0x35, 0x00, 0x00, number_of_fan << 4,
 			      0x00, 0x01, 0x01 };