Skip to content

Commit c2d5d8f

Browse files
mmindlag-linaro
authored andcommitted
leds: qnap-mcu: Add support for the red and green status LEDs
There is one more set of two LEDs on the qnap devices to indicate status. One LED is green, the other is red and while they occupy the same space on the front panel, they cannot be enabled at the same time. But they can interact via blink functions, the MCU can flash them alternately, going red -> green -> red -> ... either in 500ms or 1s intervals. They can of course also blink individually. Add specific LED functions for them and register them on probe. Signed-off-by: Heiko Stuebner <heiko@sntech.de> Link: https://lore.kernel.org/r/20250804114949.3127417-3-heiko@sntech.de Signed-off-by: Lee Jones <lee@kernel.org>
1 parent fe4ffdb commit c2d5d8f

1 file changed

Lines changed: 165 additions & 0 deletions

File tree

drivers/leds/leds-qnap-mcu.c

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,166 @@ static int qnap_mcu_register_usb_led(struct device *dev, struct qnap_mcu *mcu)
190190
return qnap_mcu_usb_led_set(&usb_led->cdev, 0);
191191
}
192192

193+
enum qnap_mcu_status_led_mode {
194+
QNAP_MCU_STATUS_LED_OFF = 0,
195+
QNAP_MCU_STATUS_LED_ON = 1,
196+
QNAP_MCU_STATUS_LED_BLINK_FAST = 2, /* 500ms / 500ms */
197+
QNAP_MCU_STATUS_LED_BLINK_SLOW = 3, /* 1s / 1s */
198+
};
199+
200+
struct qnap_mcu_status_led {
201+
struct led_classdev cdev;
202+
struct qnap_mcu_status_led *red;
203+
u8 mode;
204+
};
205+
206+
struct qnap_mcu_status {
207+
struct qnap_mcu *mcu;
208+
struct qnap_mcu_status_led red;
209+
struct qnap_mcu_status_led green;
210+
};
211+
212+
static inline struct qnap_mcu_status_led *cdev_to_qnap_mcu_status_led(struct led_classdev *led_cdev)
213+
{
214+
return container_of(led_cdev, struct qnap_mcu_status_led, cdev);
215+
}
216+
217+
static inline struct qnap_mcu_status *statusled_to_qnap_mcu_status(struct qnap_mcu_status_led *led)
218+
{
219+
return container_of(led->red, struct qnap_mcu_status, red);
220+
}
221+
222+
static u8 qnap_mcu_status_led_encode(struct qnap_mcu_status *status)
223+
{
224+
if (status->red.mode == QNAP_MCU_STATUS_LED_OFF) {
225+
switch (status->green.mode) {
226+
case QNAP_MCU_STATUS_LED_OFF:
227+
return '9';
228+
case QNAP_MCU_STATUS_LED_ON:
229+
return '6';
230+
case QNAP_MCU_STATUS_LED_BLINK_FAST:
231+
return '5';
232+
case QNAP_MCU_STATUS_LED_BLINK_SLOW:
233+
return 'A';
234+
}
235+
} else if (status->green.mode == QNAP_MCU_STATUS_LED_OFF) {
236+
switch (status->red.mode) {
237+
case QNAP_MCU_STATUS_LED_OFF:
238+
return '9';
239+
case QNAP_MCU_STATUS_LED_ON:
240+
return '7';
241+
case QNAP_MCU_STATUS_LED_BLINK_FAST:
242+
return '4';
243+
case QNAP_MCU_STATUS_LED_BLINK_SLOW:
244+
return 'B';
245+
}
246+
} else if (status->green.mode == QNAP_MCU_STATUS_LED_ON &&
247+
status->red.mode == QNAP_MCU_STATUS_LED_ON) {
248+
return 'D';
249+
} else if (status->green.mode == QNAP_MCU_STATUS_LED_BLINK_SLOW &&
250+
status->red.mode == QNAP_MCU_STATUS_LED_BLINK_SLOW) {
251+
return 'C';
252+
}
253+
254+
/*
255+
* Here both LEDs are on in some fashion, either both blinking fast,
256+
* or in different speeds, so default to fast blinking for both.
257+
*/
258+
return '8';
259+
}
260+
261+
static int qnap_mcu_status_led_update(struct qnap_mcu *mcu,
262+
struct qnap_mcu_status *status)
263+
{
264+
u8 cmd[] = { '@', 'C', 0 };
265+
266+
cmd[2] = qnap_mcu_status_led_encode(status);
267+
268+
return qnap_mcu_exec_with_ack(mcu, cmd, sizeof(cmd));
269+
}
270+
271+
static int qnap_mcu_status_led_set(struct led_classdev *led_cdev,
272+
enum led_brightness brightness)
273+
{
274+
struct qnap_mcu_status_led *status_led = cdev_to_qnap_mcu_status_led(led_cdev);
275+
struct qnap_mcu_status *base = statusled_to_qnap_mcu_status(status_led);
276+
277+
/* Don't disturb a possible set blink-mode if LED stays on */
278+
if (brightness != 0 && status_led->mode >= QNAP_MCU_STATUS_LED_BLINK_FAST)
279+
return 0;
280+
281+
status_led->mode = brightness ? QNAP_MCU_STATUS_LED_ON :
282+
QNAP_MCU_STATUS_LED_OFF;
283+
284+
return qnap_mcu_status_led_update(base->mcu, base);
285+
}
286+
287+
static int qnap_mcu_status_led_blink_set(struct led_classdev *led_cdev,
288+
unsigned long *delay_on,
289+
unsigned long *delay_off)
290+
{
291+
struct qnap_mcu_status_led *status_led = cdev_to_qnap_mcu_status_led(led_cdev);
292+
struct qnap_mcu_status *base = statusled_to_qnap_mcu_status(status_led);
293+
294+
if (status_led->mode == QNAP_MCU_STATUS_LED_OFF)
295+
return 0;
296+
297+
if (*delay_on <= 500) {
298+
*delay_on = 500;
299+
*delay_off = 500;
300+
status_led->mode = QNAP_MCU_STATUS_LED_BLINK_FAST;
301+
} else {
302+
*delay_on = 1000;
303+
*delay_off = 1000;
304+
status_led->mode = QNAP_MCU_STATUS_LED_BLINK_SLOW;
305+
}
306+
307+
return qnap_mcu_status_led_update(base->mcu, base);
308+
}
309+
310+
static int qnap_mcu_register_status_leds(struct device *dev, struct qnap_mcu *mcu)
311+
{
312+
struct qnap_mcu_status *status;
313+
int ret;
314+
315+
status = devm_kzalloc(dev, sizeof(*status), GFP_KERNEL);
316+
if (!status)
317+
return -ENOMEM;
318+
319+
status->mcu = mcu;
320+
321+
/*
322+
* point to the red led, so that statusled_to_qnap_mcu_status
323+
* can resolve the main status struct containing both leds
324+
*/
325+
status->red.red = &status->red;
326+
status->green.red = &status->red;
327+
328+
status->red.mode = QNAP_MCU_STATUS_LED_OFF;
329+
status->red.cdev.name = "red:status";
330+
status->red.cdev.brightness_set_blocking = qnap_mcu_status_led_set;
331+
status->red.cdev.blink_set = qnap_mcu_status_led_blink_set;
332+
status->red.cdev.brightness = 0;
333+
status->red.cdev.max_brightness = 1;
334+
335+
status->green.mode = QNAP_MCU_STATUS_LED_OFF;
336+
status->green.cdev.name = "green:status";
337+
status->green.cdev.brightness_set_blocking = qnap_mcu_status_led_set;
338+
status->green.cdev.blink_set = qnap_mcu_status_led_blink_set;
339+
status->green.cdev.brightness = 0;
340+
status->green.cdev.max_brightness = 1;
341+
342+
ret = devm_led_classdev_register(dev, &status->red.cdev);
343+
if (ret)
344+
return ret;
345+
346+
ret = devm_led_classdev_register(dev, &status->green.cdev);
347+
if (ret)
348+
return ret;
349+
350+
return qnap_mcu_status_led_update(status->mcu, status);
351+
}
352+
193353
static int qnap_mcu_leds_probe(struct platform_device *pdev)
194354
{
195355
struct qnap_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
@@ -210,6 +370,11 @@ static int qnap_mcu_leds_probe(struct platform_device *pdev)
210370
"failed to register USB LED\n");
211371
}
212372

373+
ret = qnap_mcu_register_status_leds(&pdev->dev, mcu);
374+
if (ret)
375+
return dev_err_probe(&pdev->dev, ret,
376+
"failed to register status LEDs\n");
377+
213378
return 0;
214379
}
215380

0 commit comments

Comments
 (0)