@@ -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+
193353static 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