Blame view

drivers/leds/led-core.c 12.7 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
c72a1d608   Richard Purdie   [PATCH] LED: add ...
2
3
4
5
6
7
  /*
   * LED Class Core
   *
   * Copyright 2005-2006 Openedhand Ltd.
   *
   * Author: Richard Purdie <rpurdie@openedhand.com>
c72a1d608   Richard Purdie   [PATCH] LED: add ...
8
9
10
   */
  
  #include <linux/kernel.h>
047133066   Jacek Anaszewski   leds: Reorder inc...
11
  #include <linux/leds.h>
c72a1d608   Richard Purdie   [PATCH] LED: add ...
12
13
  #include <linux/list.h>
  #include <linux/module.h>
047133066   Jacek Anaszewski   leds: Reorder inc...
14
  #include <linux/mutex.h>
8e1f45612   Krzysztof Kozlowski   leds: Add helper ...
15
  #include <linux/of.h>
bb4e9af03   Jacek Anaszewski   leds: core: Add s...
16
  #include <linux/property.h>
72f8da329   Richard Purdie   leds: Fix leds_li...
17
  #include <linux/rwsem.h>
8e1f45612   Krzysztof Kozlowski   leds: Add helper ...
18
  #include <linux/slab.h>
bb4e9af03   Jacek Anaszewski   leds: core: Add s...
19
  #include <uapi/linux/uleds.h>
c72a1d608   Richard Purdie   [PATCH] LED: add ...
20
  #include "leds.h"
72f8da329   Richard Purdie   leds: Fix leds_li...
21
  DECLARE_RWSEM(leds_list_lock);
4d404fd5c   Németh Márton   leds: Cleanup var...
22
  EXPORT_SYMBOL_GPL(leds_list_lock);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
23

4d404fd5c   Németh Márton   leds: Cleanup var...
24
  LIST_HEAD(leds_list);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
25
  EXPORT_SYMBOL_GPL(leds_list);
a403d930c   Bryan Wu   led-class: change...
26

bb4e9af03   Jacek Anaszewski   leds: core: Add s...
27
28
29
30
31
32
33
34
35
  const char * const led_colors[LED_COLOR_ID_MAX] = {
  	[LED_COLOR_ID_WHITE] = "white",
  	[LED_COLOR_ID_RED] = "red",
  	[LED_COLOR_ID_GREEN] = "green",
  	[LED_COLOR_ID_BLUE] = "blue",
  	[LED_COLOR_ID_AMBER] = "amber",
  	[LED_COLOR_ID_VIOLET] = "violet",
  	[LED_COLOR_ID_YELLOW] = "yellow",
  	[LED_COLOR_ID_IR] = "ir",
10d3e0d81   Dan Murphy   leds: Add multico...
36
  	[LED_COLOR_ID_MULTI] = "multicolor",
54212f5a1   Pavel Machek   leds: add RGB col...
37
  	[LED_COLOR_ID_RGB] = "rgb",
bb4e9af03   Jacek Anaszewski   leds: core: Add s...
38
39
  };
  EXPORT_SYMBOL_GPL(led_colors);
d4887af9c   Heiner Kallweit   leds: core: add h...
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  static int __led_set_brightness(struct led_classdev *led_cdev,
  				enum led_brightness value)
  {
  	if (!led_cdev->brightness_set)
  		return -ENOTSUPP;
  
  	led_cdev->brightness_set(led_cdev, value);
  
  	return 0;
  }
  
  static int __led_set_brightness_blocking(struct led_classdev *led_cdev,
  					 enum led_brightness value)
  {
  	if (!led_cdev->brightness_set_blocking)
  		return -ENOTSUPP;
  
  	return led_cdev->brightness_set_blocking(led_cdev, value);
  }
49404665b   Kees Cook   leds: Convert tim...
59
  static void led_timer_function(struct timer_list *t)
757b06ae0   Jacek Anaszewski   leds: core: Move ...
60
  {
49404665b   Kees Cook   leds: Convert tim...
61
  	struct led_classdev *led_cdev = from_timer(led_cdev, t, blink_timer);
757b06ae0   Jacek Anaszewski   leds: core: Move ...
62
63
64
65
  	unsigned long brightness;
  	unsigned long delay;
  
  	if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
81fe8e5b7   Jacek Anaszewski   leds: core: Add l...
66
  		led_set_brightness_nosleep(led_cdev, LED_OFF);
a9c6ce57e   Hans de Goede   led: core: Use at...
67
  		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
757b06ae0   Jacek Anaszewski   leds: core: Move ...
68
69
  		return;
  	}
a9c6ce57e   Hans de Goede   led: core: Use at...
70
71
72
  	if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
  			       &led_cdev->work_flags)) {
  		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
757b06ae0   Jacek Anaszewski   leds: core: Move ...
73
74
75
76
77
78
  		return;
  	}
  
  	brightness = led_get_brightness(led_cdev);
  	if (!brightness) {
  		/* Time to switch the LED on. */
eb1610b4c   Hans de Goede   led: core: Fix bl...
79
80
81
82
83
  		if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
  					&led_cdev->work_flags))
  			brightness = led_cdev->new_blink_brightness;
  		else
  			brightness = led_cdev->blink_brightness;
757b06ae0   Jacek Anaszewski   leds: core: Move ...
84
85
86
87
88
  		delay = led_cdev->blink_delay_on;
  	} else {
  		/* Store the current brightness value to be able
  		 * to restore it when the delay_off period is over.
  		 */
eb1610b4c   Hans de Goede   led: core: Fix bl...
89
  		led_cdev->blink_brightness = brightness;
757b06ae0   Jacek Anaszewski   leds: core: Move ...
90
91
92
  		brightness = LED_OFF;
  		delay = led_cdev->blink_delay_off;
  	}
81fe8e5b7   Jacek Anaszewski   leds: core: Add l...
93
  	led_set_brightness_nosleep(led_cdev, brightness);
757b06ae0   Jacek Anaszewski   leds: core: Move ...
94
95
96
97
98
  
  	/* Return in next iteration if led is in one-shot mode and we are in
  	 * the final blink state so that the led is toggled each delay_on +
  	 * delay_off milliseconds in worst case.
  	 */
a9c6ce57e   Hans de Goede   led: core: Use at...
99
100
  	if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags)) {
  		if (test_bit(LED_BLINK_INVERT, &led_cdev->work_flags)) {
757b06ae0   Jacek Anaszewski   leds: core: Move ...
101
  			if (brightness)
a9c6ce57e   Hans de Goede   led: core: Use at...
102
103
  				set_bit(LED_BLINK_ONESHOT_STOP,
  					&led_cdev->work_flags);
757b06ae0   Jacek Anaszewski   leds: core: Move ...
104
105
  		} else {
  			if (!brightness)
a9c6ce57e   Hans de Goede   led: core: Use at...
106
107
  				set_bit(LED_BLINK_ONESHOT_STOP,
  					&led_cdev->work_flags);
757b06ae0   Jacek Anaszewski   leds: core: Move ...
108
109
110
111
112
113
114
115
116
117
  		}
  	}
  
  	mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
  }
  
  static void set_brightness_delayed(struct work_struct *ws)
  {
  	struct led_classdev *led_cdev =
  		container_of(ws, struct led_classdev, set_brightness_work);
1afcadfcd   Jacek Anaszewski   leds: core: Use s...
118
  	int ret = 0;
757b06ae0   Jacek Anaszewski   leds: core: Move ...
119

a9c6ce57e   Hans de Goede   led: core: Use at...
120
  	if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
f1e80c074   Jacek Anaszewski   leds: core: Add t...
121
122
  		led_cdev->delayed_set_value = LED_OFF;
  		led_stop_software_blink(led_cdev);
f1e80c074   Jacek Anaszewski   leds: core: Add t...
123
  	}
757b06ae0   Jacek Anaszewski   leds: core: Move ...
124

d4887af9c   Heiner Kallweit   leds: core: add h...
125
126
127
128
  	ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
  	if (ret == -ENOTSUPP)
  		ret = __led_set_brightness_blocking(led_cdev,
  					led_cdev->delayed_set_value);
d84d80f38   Heiner Kallweit   leds: core: avoid...
129
130
131
132
  	if (ret < 0 &&
  	    /* LED HW might have been unplugged, therefore don't warn */
  	    !(ret == -ENODEV && (led_cdev->flags & LED_UNREGISTERING) &&
  	    (led_cdev->flags & LED_HW_PLUGGABLE)))
1afcadfcd   Jacek Anaszewski   leds: core: Use s...
133
134
135
  		dev_err(led_cdev->dev,
  			"Setting an LED's brightness failed (%d)
  ", ret);
757b06ae0   Jacek Anaszewski   leds: core: Move ...
136
  }
a403d930c   Bryan Wu   led-class: change...
137
138
139
140
141
142
143
144
145
146
147
  static void led_set_software_blink(struct led_classdev *led_cdev,
  				   unsigned long delay_on,
  				   unsigned long delay_off)
  {
  	int current_brightness;
  
  	current_brightness = led_get_brightness(led_cdev);
  	if (current_brightness)
  		led_cdev->blink_brightness = current_brightness;
  	if (!led_cdev->blink_brightness)
  		led_cdev->blink_brightness = led_cdev->max_brightness;
a403d930c   Bryan Wu   led-class: change...
148
149
  	led_cdev->blink_delay_on = delay_on;
  	led_cdev->blink_delay_off = delay_off;
8d82fef8b   Stefan Sørensen   leds: Turn off le...
150
151
  	/* never on - just set to off */
  	if (!delay_on) {
81fe8e5b7   Jacek Anaszewski   leds: core: Add l...
152
  		led_set_brightness_nosleep(led_cdev, LED_OFF);
a403d930c   Bryan Wu   led-class: change...
153
  		return;
8d82fef8b   Stefan Sørensen   leds: Turn off le...
154
  	}
a403d930c   Bryan Wu   led-class: change...
155
156
157
  
  	/* never off - just set to brightness */
  	if (!delay_off) {
81fe8e5b7   Jacek Anaszewski   leds: core: Add l...
158
159
  		led_set_brightness_nosleep(led_cdev,
  					   led_cdev->blink_brightness);
a403d930c   Bryan Wu   led-class: change...
160
161
  		return;
  	}
a9c6ce57e   Hans de Goede   led: core: Use at...
162
  	set_bit(LED_BLINK_SW, &led_cdev->work_flags);
9067359fa   Jiri Kosina   Revert "leds: con...
163
  	mod_timer(&led_cdev->blink_timer, jiffies + 1);
a403d930c   Bryan Wu   led-class: change...
164
  }
20c0e6b87   Bryan Wu   leds: fix sparse ...
165
  static void led_blink_setup(struct led_classdev *led_cdev,
5bb629c50   Fabio Baltieri   leds: add oneshot...
166
167
  		     unsigned long *delay_on,
  		     unsigned long *delay_off)
a403d930c   Bryan Wu   led-class: change...
168
  {
a9c6ce57e   Hans de Goede   led: core: Use at...
169
  	if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
5bb629c50   Fabio Baltieri   leds: add oneshot...
170
  	    led_cdev->blink_set &&
a403d930c   Bryan Wu   led-class: change...
171
172
173
174
175
176
177
178
179
  	    !led_cdev->blink_set(led_cdev, delay_on, delay_off))
  		return;
  
  	/* blink with 1 Hz as default if nothing specified */
  	if (!*delay_on && !*delay_off)
  		*delay_on = *delay_off = 500;
  
  	led_set_software_blink(led_cdev, *delay_on, *delay_off);
  }
5bb629c50   Fabio Baltieri   leds: add oneshot...
180

757b06ae0   Jacek Anaszewski   leds: core: Move ...
181
182
183
  void led_init_core(struct led_classdev *led_cdev)
  {
  	INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
49404665b   Kees Cook   leds: Convert tim...
184
  	timer_setup(&led_cdev->blink_timer, led_timer_function, 0);
757b06ae0   Jacek Anaszewski   leds: core: Move ...
185
186
  }
  EXPORT_SYMBOL_GPL(led_init_core);
5bb629c50   Fabio Baltieri   leds: add oneshot...
187
188
189
190
  void led_blink_set(struct led_classdev *led_cdev,
  		   unsigned long *delay_on,
  		   unsigned long *delay_off)
  {
7b6af2c53   Jacek Anaszewski   leds: core: Fix r...
191
  	del_timer_sync(&led_cdev->blink_timer);
5bb629c50   Fabio Baltieri   leds: add oneshot...
192

7b6af2c53   Jacek Anaszewski   leds: core: Fix r...
193
  	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
a9c6ce57e   Hans de Goede   led: core: Use at...
194
195
  	clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
  	clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
5bb629c50   Fabio Baltieri   leds: add oneshot...
196
197
198
  
  	led_blink_setup(led_cdev, delay_on, delay_off);
  }
2806e2ff4   Jacek Anaszewski   leds: core: Use E...
199
  EXPORT_SYMBOL_GPL(led_blink_set);
a403d930c   Bryan Wu   led-class: change...
200

5bb629c50   Fabio Baltieri   leds: add oneshot...
201
202
203
204
205
  void led_blink_set_oneshot(struct led_classdev *led_cdev,
  			   unsigned long *delay_on,
  			   unsigned long *delay_off,
  			   int invert)
  {
a9c6ce57e   Hans de Goede   led: core: Use at...
206
  	if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
9067359fa   Jiri Kosina   Revert "leds: con...
207
  	     timer_pending(&led_cdev->blink_timer))
5bb629c50   Fabio Baltieri   leds: add oneshot...
208
  		return;
a9c6ce57e   Hans de Goede   led: core: Use at...
209
210
  	set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
  	clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
5bb629c50   Fabio Baltieri   leds: add oneshot...
211
212
  
  	if (invert)
a9c6ce57e   Hans de Goede   led: core: Use at...
213
  		set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
5bb629c50   Fabio Baltieri   leds: add oneshot...
214
  	else
a9c6ce57e   Hans de Goede   led: core: Use at...
215
  		clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
5bb629c50   Fabio Baltieri   leds: add oneshot...
216
217
218
  
  	led_blink_setup(led_cdev, delay_on, delay_off);
  }
2806e2ff4   Jacek Anaszewski   leds: core: Use E...
219
  EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
5bb629c50   Fabio Baltieri   leds: add oneshot...
220

d23a22a74   Fabio Baltieri   leds: delay led_s...
221
  void led_stop_software_blink(struct led_classdev *led_cdev)
a403d930c   Bryan Wu   led-class: change...
222
  {
9067359fa   Jiri Kosina   Revert "leds: con...
223
  	del_timer_sync(&led_cdev->blink_timer);
437864828   Fabio Baltieri   leds: fix led_bri...
224
225
  	led_cdev->blink_delay_on = 0;
  	led_cdev->blink_delay_off = 0;
a9c6ce57e   Hans de Goede   led: core: Use at...
226
  	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
d23a22a74   Fabio Baltieri   leds: delay led_s...
227
228
229
230
231
232
  }
  EXPORT_SYMBOL_GPL(led_stop_software_blink);
  
  void led_set_brightness(struct led_classdev *led_cdev,
  			enum led_brightness brightness)
  {
f1e80c074   Jacek Anaszewski   leds: core: Add t...
233
  	/*
7cfe749fa   Tony Makkiel   leds: core: Fix b...
234
  	 * If software blink is active, delay brightness setting
f1e80c074   Jacek Anaszewski   leds: core: Add t...
235
236
  	 * until the next timer tick.
  	 */
a9c6ce57e   Hans de Goede   led: core: Use at...
237
  	if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
f1e80c074   Jacek Anaszewski   leds: core: Add t...
238
239
240
241
242
243
  		/*
  		 * If we need to disable soft blinking delegate this to the
  		 * work queue task to avoid problems in case we are called
  		 * from hard irq context.
  		 */
  		if (brightness == LED_OFF) {
a9c6ce57e   Hans de Goede   led: core: Use at...
244
  			set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
76931edd5   Stas Sergeev   leds: fix brightn...
245
  			schedule_work(&led_cdev->set_brightness_work);
f1e80c074   Jacek Anaszewski   leds: core: Add t...
246
  		} else {
a9c6ce57e   Hans de Goede   led: core: Use at...
247
248
  			set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
  				&led_cdev->work_flags);
eb1610b4c   Hans de Goede   led: core: Fix bl...
249
  			led_cdev->new_blink_brightness = brightness;
f1e80c074   Jacek Anaszewski   leds: core: Add t...
250
  		}
d23a22a74   Fabio Baltieri   leds: delay led_s...
251
252
  		return;
  	}
437864828   Fabio Baltieri   leds: fix led_bri...
253

13ae79bbe   Jacek Anaszewski   leds: core: Drive...
254
  	led_set_brightness_nosleep(led_cdev, brightness);
a403d930c   Bryan Wu   led-class: change...
255
  }
2806e2ff4   Jacek Anaszewski   leds: core: Use E...
256
  EXPORT_SYMBOL_GPL(led_set_brightness);
3ef7de530   Jacek Anaszewski   leds: Improve and...
257

81fe8e5b7   Jacek Anaszewski   leds: core: Add l...
258
259
260
261
  void led_set_brightness_nopm(struct led_classdev *led_cdev,
  			      enum led_brightness value)
  {
  	/* Use brightness_set op if available, it is guaranteed not to sleep */
d4887af9c   Heiner Kallweit   leds: core: add h...
262
  	if (!__led_set_brightness(led_cdev, value))
81fe8e5b7   Jacek Anaszewski   leds: core: Add l...
263
  		return;
81fe8e5b7   Jacek Anaszewski   leds: core: Add l...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
  
  	/* If brightness setting can sleep, delegate it to a work queue task */
  	led_cdev->delayed_set_value = value;
  	schedule_work(&led_cdev->set_brightness_work);
  }
  EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
  
  void led_set_brightness_nosleep(struct led_classdev *led_cdev,
  				enum led_brightness value)
  {
  	led_cdev->brightness = min(value, led_cdev->max_brightness);
  
  	if (led_cdev->flags & LED_SUSPENDED)
  		return;
  
  	led_set_brightness_nopm(led_cdev, led_cdev->brightness);
  }
  EXPORT_SYMBOL_GPL(led_set_brightness_nosleep);
13ae79bbe   Jacek Anaszewski   leds: core: Drive...
282
283
284
285
286
287
288
289
290
291
  int led_set_brightness_sync(struct led_classdev *led_cdev,
  			    enum led_brightness value)
  {
  	if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
  		return -EBUSY;
  
  	led_cdev->brightness = min(value, led_cdev->max_brightness);
  
  	if (led_cdev->flags & LED_SUSPENDED)
  		return 0;
d4887af9c   Heiner Kallweit   leds: core: add h...
292
  	return __led_set_brightness_blocking(led_cdev, led_cdev->brightness);
13ae79bbe   Jacek Anaszewski   leds: core: Drive...
293
294
  }
  EXPORT_SYMBOL_GPL(led_set_brightness_sync);
3ef7de530   Jacek Anaszewski   leds: Improve and...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
  int led_update_brightness(struct led_classdev *led_cdev)
  {
  	int ret = 0;
  
  	if (led_cdev->brightness_get) {
  		ret = led_cdev->brightness_get(led_cdev);
  		if (ret >= 0) {
  			led_cdev->brightness = ret;
  			return 0;
  		}
  	}
  
  	return ret;
  }
2806e2ff4   Jacek Anaszewski   leds: core: Use E...
309
  EXPORT_SYMBOL_GPL(led_update_brightness);
acd899e4f   Jacek Anaszewski   leds: implement s...
310

8e1f45612   Krzysztof Kozlowski   leds: Add helper ...
311
312
  u32 *led_get_default_pattern(struct led_classdev *led_cdev, unsigned int *size)
  {
fd81d7e94   Andy Shevchenko   leds: Switch to u...
313
  	struct fwnode_handle *fwnode = led_cdev->dev->fwnode;
8e1f45612   Krzysztof Kozlowski   leds: Add helper ...
314
315
  	u32 *pattern;
  	int count;
fd81d7e94   Andy Shevchenko   leds: Switch to u...
316
  	count = fwnode_property_count_u32(fwnode, "led-pattern");
8e1f45612   Krzysztof Kozlowski   leds: Add helper ...
317
318
319
320
321
322
  	if (count < 0)
  		return NULL;
  
  	pattern = kcalloc(count, sizeof(*pattern), GFP_KERNEL);
  	if (!pattern)
  		return NULL;
fd81d7e94   Andy Shevchenko   leds: Switch to u...
323
  	if (fwnode_property_read_u32_array(fwnode, "led-pattern", pattern, count)) {
8e1f45612   Krzysztof Kozlowski   leds: Add helper ...
324
325
326
327
328
329
330
331
332
  		kfree(pattern);
  		return NULL;
  	}
  
  	*size = count;
  
  	return pattern;
  }
  EXPORT_SYMBOL_GPL(led_get_default_pattern);
acd899e4f   Jacek Anaszewski   leds: implement s...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  /* Caller must ensure led_cdev->led_access held */
  void led_sysfs_disable(struct led_classdev *led_cdev)
  {
  	lockdep_assert_held(&led_cdev->led_access);
  
  	led_cdev->flags |= LED_SYSFS_DISABLE;
  }
  EXPORT_SYMBOL_GPL(led_sysfs_disable);
  
  /* Caller must ensure led_cdev->led_access held */
  void led_sysfs_enable(struct led_classdev *led_cdev)
  {
  	lockdep_assert_held(&led_cdev->led_access);
  
  	led_cdev->flags &= ~LED_SYSFS_DISABLE;
  }
  EXPORT_SYMBOL_GPL(led_sysfs_enable);
bb4e9af03   Jacek Anaszewski   leds: core: Add s...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
  
  static void led_parse_fwnode_props(struct device *dev,
  				   struct fwnode_handle *fwnode,
  				   struct led_properties *props)
  {
  	int ret;
  
  	if (!fwnode)
  		return;
  
  	if (fwnode_property_present(fwnode, "label")) {
  		ret = fwnode_property_read_string(fwnode, "label", &props->label);
  		if (ret)
  			dev_err(dev, "Error parsing 'label' property (%d)
  ", ret);
  		return;
  	}
  
  	if (fwnode_property_present(fwnode, "color")) {
  		ret = fwnode_property_read_u32(fwnode, "color", &props->color);
  		if (ret)
  			dev_err(dev, "Error parsing 'color' property (%d)
  ", ret);
  		else if (props->color >= LED_COLOR_ID_MAX)
  			dev_err(dev, "LED color identifier out of range
  ");
  		else
  			props->color_present = true;
  	}
  
  
  	if (!fwnode_property_present(fwnode, "function"))
  		return;
  
  	ret = fwnode_property_read_string(fwnode, "function", &props->function);
  	if (ret) {
  		dev_err(dev,
  			"Error parsing 'function' property (%d)
  ",
  			ret);
  	}
  
  	if (!fwnode_property_present(fwnode, "function-enumerator"))
  		return;
  
  	ret = fwnode_property_read_u32(fwnode, "function-enumerator",
  				       &props->func_enum);
  	if (ret) {
  		dev_err(dev,
  			"Error parsing 'function-enumerator' property (%d)
  ",
  			ret);
  	} else {
  		props->func_enum_present = true;
  	}
  }
  
  int led_compose_name(struct device *dev, struct led_init_data *init_data,
  		     char *led_classdev_name)
  {
  	struct led_properties props = {};
  	struct fwnode_handle *fwnode = init_data->fwnode;
  	const char *devicename = init_data->devicename;
77dce3a22   Pavel Machek   leds: disallow /s...
413
414
415
  	/* We want to label LEDs that can produce full range of colors
  	 * as RGB, not multicolor */
  	BUG_ON(props.color == LED_COLOR_ID_MULTI);
bb4e9af03   Jacek Anaszewski   leds: core: Add s...
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
  	if (!led_classdev_name)
  		return -EINVAL;
  
  	led_parse_fwnode_props(dev, fwnode, &props);
  
  	if (props.label) {
  		/*
  		 * If init_data.devicename is NULL, then it indicates that
  		 * DT label should be used as-is for LED class device name.
  		 * Otherwise the label is prepended with devicename to compose
  		 * the final LED class device name.
  		 */
  		if (!devicename) {
  			strscpy(led_classdev_name, props.label,
  				LED_MAX_NAME_SIZE);
  		} else {
  			snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
  				 devicename, props.label);
  		}
  	} else if (props.function || props.color_present) {
  		char tmp_buf[LED_MAX_NAME_SIZE];
  
  		if (props.func_enum_present) {
  			snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-%d",
  				 props.color_present ? led_colors[props.color] : "",
  				 props.function ?: "", props.func_enum);
  		} else {
  			snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
  				 props.color_present ? led_colors[props.color] : "",
  				 props.function ?: "");
  		}
  		if (init_data->devname_mandatory) {
  			snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
  				 devicename, tmp_buf);
  		} else {
  			strscpy(led_classdev_name, tmp_buf, LED_MAX_NAME_SIZE);
  
  		}
  	} else if (init_data->default_label) {
  		if (!devicename) {
  			dev_err(dev, "Legacy LED naming requires devicename segment");
  			return -EINVAL;
  		}
  		snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
  			 devicename, init_data->default_label);
  	} else if (is_of_node(fwnode)) {
  		strscpy(led_classdev_name, to_of_node(fwnode)->name,
  			LED_MAX_NAME_SIZE);
  	} else
  		return -EINVAL;
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(led_compose_name);