@@ -17,7 +17,23 @@ MODULE_LICENSE("GPL");
17
17
#define MAX_LED (((SNDRV_CTL_ELEM_ACCESS_MIC_LED - SNDRV_CTL_ELEM_ACCESS_SPK_LED) \
18
18
>> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) + 1)
19
19
20
+ enum snd_ctl_led_mode {
21
+ MODE_FOLLOW_MUTE = 0 ,
22
+ MODE_FOLLOW_ROUTE ,
23
+ MODE_OFF ,
24
+ MODE_ON ,
25
+ };
26
+
20
27
struct snd_ctl_led {
28
+ struct device dev ;
29
+ struct list_head controls ;
30
+ const char * name ;
31
+ unsigned int group ;
32
+ enum led_audio trigger_type ;
33
+ enum snd_ctl_led_mode mode ;
34
+ };
35
+
36
+ struct snd_ctl_led_ctl {
21
37
struct list_head list ;
22
38
struct snd_card * card ;
23
39
unsigned int access ;
@@ -26,8 +42,21 @@ struct snd_ctl_led {
26
42
};
27
43
28
44
static DEFINE_MUTEX (snd_ctl_led_mutex );
29
- static struct list_head snd_ctl_led_controls [MAX_LED ];
30
45
static bool snd_ctl_led_card_valid [SNDRV_CARDS ];
46
+ static struct snd_ctl_led snd_ctl_leds [MAX_LED ] = {
47
+ {
48
+ .name = "speaker" ,
49
+ .group = (SNDRV_CTL_ELEM_ACCESS_SPK_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT ) - 1 ,
50
+ .trigger_type = LED_AUDIO_MUTE ,
51
+ .mode = MODE_FOLLOW_MUTE ,
52
+ },
53
+ {
54
+ .name = "mic" ,
55
+ .group = (SNDRV_CTL_ELEM_ACCESS_MIC_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT ) - 1 ,
56
+ .trigger_type = LED_AUDIO_MICMUTE ,
57
+ .mode = MODE_FOLLOW_MUTE ,
58
+ },
59
+ };
31
60
32
61
#define UPDATE_ROUTE (route , cb ) \
33
62
do { \
@@ -47,15 +76,15 @@ static inline unsigned int group_to_access(unsigned int group)
47
76
return (group + 1 ) << SNDRV_CTL_ELEM_ACCESS_LED_SHIFT ;
48
77
}
49
78
50
- static struct list_head * snd_ctl_led_controls_by_access (unsigned int access )
79
+ static struct snd_ctl_led * snd_ctl_led_get_by_access (unsigned int access )
51
80
{
52
81
unsigned int group = access_to_group (access );
53
82
if (group >= MAX_LED )
54
83
return NULL ;
55
- return & snd_ctl_led_controls [group ];
84
+ return & snd_ctl_leds [group ];
56
85
}
57
86
58
- static int snd_ctl_led_get (struct snd_ctl_led * lctl )
87
+ static int snd_ctl_led_get (struct snd_ctl_led_ctl * lctl )
59
88
{
60
89
struct snd_kcontrol * kctl = lctl -> kctl ;
61
90
struct snd_ctl_elem_info info ;
@@ -91,22 +120,14 @@ static int snd_ctl_led_get(struct snd_ctl_led *lctl)
91
120
static void snd_ctl_led_set_state (struct snd_card * card , unsigned int access ,
92
121
struct snd_kcontrol * kctl , unsigned int ioff )
93
122
{
94
- struct list_head * controls ;
95
- struct snd_ctl_led * lctl ;
96
- enum led_audio led_trigger_type ;
123
+ struct snd_ctl_led * led ;
124
+ struct snd_ctl_led_ctl * lctl ;
97
125
int route ;
98
126
bool found ;
99
127
100
- controls = snd_ctl_led_controls_by_access (access );
101
- if (!controls )
128
+ led = snd_ctl_led_get_by_access (access );
129
+ if (!led )
102
130
return ;
103
- if (access == SNDRV_CTL_ELEM_ACCESS_SPK_LED ) {
104
- led_trigger_type = LED_AUDIO_MUTE ;
105
- } else if (access == SNDRV_CTL_ELEM_ACCESS_MIC_LED ) {
106
- led_trigger_type = LED_AUDIO_MICMUTE ;
107
- } else {
108
- return ;
109
- }
110
131
route = -1 ;
111
132
found = false;
112
133
mutex_lock (& snd_ctl_led_mutex );
@@ -115,7 +136,7 @@ static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
115
136
mutex_unlock (& snd_ctl_led_mutex );
116
137
return ;
117
138
}
118
- list_for_each_entry (lctl , controls , list ) {
139
+ list_for_each_entry (lctl , & led -> controls , list ) {
119
140
if (lctl -> kctl == kctl && lctl -> index_offset == ioff )
120
141
found = true;
121
142
UPDATE_ROUTE (route , snd_ctl_led_get (lctl ));
@@ -127,23 +148,29 @@ static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
127
148
lctl -> access = access ;
128
149
lctl -> kctl = kctl ;
129
150
lctl -> index_offset = ioff ;
130
- list_add (& lctl -> list , controls );
151
+ list_add (& lctl -> list , & led -> controls );
131
152
UPDATE_ROUTE (route , snd_ctl_led_get (lctl ));
132
153
}
133
154
}
134
155
mutex_unlock (& snd_ctl_led_mutex );
156
+ switch (led -> mode ) {
157
+ case MODE_OFF : route = 1 ; break ;
158
+ case MODE_ON : route = 0 ; break ;
159
+ case MODE_FOLLOW_ROUTE : if (route >= 0 ) route ^= 1 ; break ;
160
+ case MODE_FOLLOW_MUTE : /* noop */ break ;
161
+ }
135
162
if (route >= 0 )
136
- ledtrig_audio_set (led_trigger_type , route ? LED_OFF : LED_ON );
163
+ ledtrig_audio_set (led -> trigger_type , route ? LED_OFF : LED_ON );
137
164
}
138
165
139
- static struct snd_ctl_led * snd_ctl_led_find (struct snd_kcontrol * kctl , unsigned int ioff )
166
+ static struct snd_ctl_led_ctl * snd_ctl_led_find (struct snd_kcontrol * kctl , unsigned int ioff )
140
167
{
141
168
struct list_head * controls ;
142
- struct snd_ctl_led * lctl ;
169
+ struct snd_ctl_led_ctl * lctl ;
143
170
unsigned int group ;
144
171
145
172
for (group = 0 ; group < MAX_LED ; group ++ ) {
146
- controls = & snd_ctl_led_controls [group ];
173
+ controls = & snd_ctl_leds [group ]. controls ;
147
174
list_for_each_entry (lctl , controls , list )
148
175
if (lctl -> kctl == kctl && lctl -> index_offset == ioff )
149
176
return lctl ;
@@ -154,7 +181,7 @@ static struct snd_ctl_led *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned
154
181
static unsigned int snd_ctl_led_remove (struct snd_kcontrol * kctl , unsigned int ioff ,
155
182
unsigned int access )
156
183
{
157
- struct snd_ctl_led * lctl ;
184
+ struct snd_ctl_led_ctl * lctl ;
158
185
unsigned int ret = 0 ;
159
186
160
187
mutex_lock (& snd_ctl_led_mutex );
@@ -206,13 +233,13 @@ static void snd_ctl_led_refresh(void)
206
233
static void snd_ctl_led_clean (struct snd_card * card )
207
234
{
208
235
unsigned int group ;
209
- struct list_head * controls ;
210
- struct snd_ctl_led * lctl ;
236
+ struct snd_ctl_led * led ;
237
+ struct snd_ctl_led_ctl * lctl ;
211
238
212
239
for (group = 0 ; group < MAX_LED ; group ++ ) {
213
- controls = & snd_ctl_led_controls [group ];
240
+ led = & snd_ctl_leds [group ];
214
241
repeat :
215
- list_for_each_entry (lctl , controls , list )
242
+ list_for_each_entry (lctl , & led -> controls , list )
216
243
if (!card || lctl -> card == card ) {
217
244
list_del (& lctl -> list );
218
245
kfree (lctl );
@@ -248,6 +275,82 @@ static void snd_ctl_led_disconnect(struct snd_card *card)
248
275
snd_ctl_led_refresh ();
249
276
}
250
277
278
+ /*
279
+ * sysfs
280
+ */
281
+
282
+ static ssize_t show_mode (struct device * dev ,
283
+ struct device_attribute * attr , char * buf )
284
+ {
285
+ struct snd_ctl_led * led = container_of (dev , struct snd_ctl_led , dev );
286
+ const char * str ;
287
+
288
+ switch (led -> mode ) {
289
+ case MODE_FOLLOW_MUTE : str = "follow-mute" ; break ;
290
+ case MODE_FOLLOW_ROUTE : str = "follow-route" ; break ;
291
+ case MODE_ON : str = "on" ; break ;
292
+ case MODE_OFF : str = "off" ; break ;
293
+ }
294
+ return sprintf (buf , "%s\n" , str );
295
+ }
296
+
297
+ static ssize_t store_mode (struct device * dev , struct device_attribute * attr ,
298
+ const char * buf , size_t count )
299
+ {
300
+ struct snd_ctl_led * led = container_of (dev , struct snd_ctl_led , dev );
301
+ char _buf [16 ];
302
+ size_t l = min (count , sizeof (_buf ) - 1 ) + 1 ;
303
+ enum snd_ctl_led_mode mode ;
304
+
305
+ memcpy (_buf , buf , l );
306
+ _buf [l ] = '\0' ;
307
+ if (strstr (_buf , "mute" ))
308
+ mode = MODE_FOLLOW_MUTE ;
309
+ else if (strstr (_buf , "route" ))
310
+ mode = MODE_FOLLOW_ROUTE ;
311
+ else if (strncmp (_buf , "off" , 3 ) == 0 || strncmp (_buf , "0" , 1 ) == 0 )
312
+ mode = MODE_OFF ;
313
+ else if (strncmp (_buf , "on" , 2 ) == 0 || strncmp (_buf , "1" , 1 ) == 0 )
314
+ mode = MODE_ON ;
315
+ else
316
+ return count ;
317
+
318
+ mutex_lock (& snd_ctl_led_mutex );
319
+ led -> mode = mode ;
320
+ mutex_unlock (& snd_ctl_led_mutex );
321
+
322
+ snd_ctl_led_set_state (NULL , group_to_access (led -> group ), NULL , 0 );
323
+ return count ;
324
+ }
325
+
326
+ static ssize_t show_brightness (struct device * dev ,
327
+ struct device_attribute * attr , char * buf )
328
+ {
329
+ struct snd_ctl_led * led = container_of (dev , struct snd_ctl_led , dev );
330
+
331
+ return sprintf (buf , "%u\n" , ledtrig_audio_get (led -> trigger_type ));
332
+ }
333
+
334
+ static DEVICE_ATTR (mode , 0644 , show_mode , store_mode ) ;
335
+ static DEVICE_ATTR (brightness , 0444 , show_brightness , NULL) ;
336
+
337
+ static struct attribute * snd_ctl_led_dev_attrs [] = {
338
+ & dev_attr_mode .attr ,
339
+ & dev_attr_brightness .attr ,
340
+ NULL ,
341
+ };
342
+
343
+ static const struct attribute_group snd_ctl_led_dev_attr_group = {
344
+ .attrs = snd_ctl_led_dev_attrs ,
345
+ };
346
+
347
+ static const struct attribute_group * snd_ctl_led_dev_attr_groups [] = {
348
+ & snd_ctl_led_dev_attr_group ,
349
+ NULL ,
350
+ };
351
+
352
+ static struct device snd_ctl_led_dev ;
353
+
251
354
/*
252
355
* Control layer registration
253
356
*/
@@ -260,16 +363,47 @@ static struct snd_ctl_layer_ops snd_ctl_led_lops = {
260
363
261
364
static int __init snd_ctl_led_init (void )
262
365
{
366
+ struct snd_ctl_led * led ;
263
367
unsigned int group ;
264
368
265
- for (group = 0 ; group < MAX_LED ; group ++ )
266
- INIT_LIST_HEAD (& snd_ctl_led_controls [group ]);
369
+ device_initialize (& snd_ctl_led_dev );
370
+ snd_ctl_led_dev .class = sound_class ;
371
+ dev_set_name (& snd_ctl_led_dev , "ctl-led" );
372
+ if (device_add (& snd_ctl_led_dev )) {
373
+ put_device (& snd_ctl_led_dev );
374
+ return - ENOMEM ;
375
+ }
376
+ for (group = 0 ; group < MAX_LED ; group ++ ) {
377
+ led = & snd_ctl_leds [group ];
378
+ INIT_LIST_HEAD (& led -> controls );
379
+ device_initialize (& led -> dev );
380
+ led -> dev .parent = & snd_ctl_led_dev ;
381
+ led -> dev .groups = snd_ctl_led_dev_attr_groups ;
382
+ dev_set_name (& led -> dev , led -> name );
383
+ if (device_add (& led -> dev )) {
384
+ put_device (& led -> dev );
385
+ for (; group > 0 ; group -- ) {
386
+ led = & snd_ctl_leds [group ];
387
+ device_del (& led -> dev );
388
+ }
389
+ device_del (& snd_ctl_led_dev );
390
+ return - ENOMEM ;
391
+ }
392
+ }
267
393
snd_ctl_register_layer (& snd_ctl_led_lops );
268
394
return 0 ;
269
395
}
270
396
271
397
static void __exit snd_ctl_led_exit (void )
272
398
{
399
+ struct snd_ctl_led * led ;
400
+ unsigned int group ;
401
+
402
+ for (group = 0 ; group < MAX_LED ; group ++ ) {
403
+ led = & snd_ctl_leds [group ];
404
+ device_del (& led -> dev );
405
+ }
406
+ device_del (& snd_ctl_led_dev );
273
407
snd_ctl_disconnect_layer (& snd_ctl_led_lops );
274
408
snd_ctl_led_clean (NULL );
275
409
}
0 commit comments