2 * dell_led.c - Dell LED Driver
4 * Copyright (C) 2010 Dell Inc.
5 * Louis Davis <louis_davis@dell.com>
6 * Jim Dailey <jim_dailey@dell.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation.
14 #include <linux/acpi.h>
15 #include <linux/leds.h>
16 #include <linux/slab.h>
17 #include <linux/module.h>
18 #include <linux/dmi.h>
19 #include <linux/dell-led.h>
21 MODULE_AUTHOR("Louis Davis/Jim Dailey");
22 MODULE_DESCRIPTION("Dell LED Control Driver");
23 MODULE_LICENSE("GPL");
25 #define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396"
26 #define DELL_APP_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
27 MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID
);
29 /* Error Result Codes: */
30 #define INVALID_DEVICE_ID 250
31 #define INVALID_PARAMETER 251
32 #define INVALID_BUFFER 252
33 #define INTERFACE_ERROR 253
34 #define UNSUPPORTED_COMMAND 254
35 #define UNSPECIFIED_ERROR 255
38 #define DEVICE_ID_PANEL_BACK 1
42 #define CMD_LED_OFF 17
43 #define CMD_LED_BLINK 18
59 #define GLOBAL_MIC_MUTE_ENABLE 0x364
60 #define GLOBAL_MIC_MUTE_DISABLE 0x365
62 struct dell_bios_data_token
{
68 struct __attribute__ ((__packed__
)) dell_bios_calling_interface
{
69 struct dmi_header header
;
73 struct dell_bios_data_token damap
[];
76 static struct dell_bios_data_token dell_mic_tokens
[2];
78 static int dell_wmi_perform_query(struct app_wmi_args
*args
)
80 struct app_wmi_args
*bios_return
;
81 union acpi_object
*obj
;
82 struct acpi_buffer input
;
83 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
90 status
= wmi_evaluate_method(DELL_APP_GUID
, 0, 1, &input
, &output
);
91 if (!ACPI_SUCCESS(status
))
98 if (obj
->type
!= ACPI_TYPE_BUFFER
)
101 bios_return
= (struct app_wmi_args
*)obj
->buffer
.pointer
;
102 rc
= bios_return
->res1
;
106 memcpy(args
, bios_return
, sizeof(struct app_wmi_args
));
115 static void __init
find_micmute_tokens(const struct dmi_header
*dm
, void *dummy
)
117 struct dell_bios_calling_interface
*calling_interface
;
118 struct dell_bios_data_token
*token
;
119 int token_size
= sizeof(struct dell_bios_data_token
);
122 if (dm
->type
== 0xda && dm
->length
> 17) {
123 calling_interface
= container_of(dm
,
124 struct dell_bios_calling_interface
, header
);
126 token
= &calling_interface
->damap
[i
];
127 while (token
->tokenid
!= 0xffff) {
128 if (token
->tokenid
== GLOBAL_MIC_MUTE_DISABLE
)
129 memcpy(&dell_mic_tokens
[0], token
, token_size
);
130 else if (token
->tokenid
== GLOBAL_MIC_MUTE_ENABLE
)
131 memcpy(&dell_mic_tokens
[1], token
, token_size
);
134 token
= &calling_interface
->damap
[i
];
139 static int dell_micmute_led_set(int state
)
141 struct app_wmi_args args
;
142 struct dell_bios_data_token
*token
;
144 if (!wmi_has_guid(DELL_APP_GUID
))
147 if (state
== 0 || state
== 1)
148 token
= &dell_mic_tokens
[state
];
152 memset(&args
, 0, sizeof(struct app_wmi_args
));
155 args
.arg1
= token
->location
;
156 args
.arg2
= token
->value
;
158 dell_wmi_perform_query(&args
);
163 int dell_app_wmi_led_set(int whichled
, int on
)
168 case DELL_LED_MICMUTE
:
169 state
= dell_micmute_led_set(on
);
172 pr_warn("led type %x is not supported\n", whichled
);
178 EXPORT_SYMBOL_GPL(dell_app_wmi_led_set
);
180 static int __init
dell_micmute_led_init(void)
182 memset(dell_mic_tokens
, 0, sizeof(struct dell_bios_data_token
) * 2);
183 dmi_walk(find_micmute_tokens
, NULL
);
189 unsigned char length
;
190 unsigned char result_code
;
191 unsigned char device_id
;
192 unsigned char command
;
193 unsigned char on_time
;
194 unsigned char off_time
;
197 static int dell_led_perform_fn(u8 length
,
204 struct bios_args
*bios_return
;
206 union acpi_object
*obj
;
207 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
208 struct acpi_buffer input
;
211 struct bios_args args
;
212 args
.length
= length
;
213 args
.result_code
= result_code
;
214 args
.device_id
= device_id
;
215 args
.command
= command
;
216 args
.on_time
= on_time
;
217 args
.off_time
= off_time
;
219 input
.length
= sizeof(struct bios_args
);
220 input
.pointer
= &args
;
222 status
= wmi_evaluate_method(DELL_LED_BIOS_GUID
,
228 if (ACPI_FAILURE(status
))
231 obj
= output
.pointer
;
235 else if (obj
->type
!= ACPI_TYPE_BUFFER
) {
240 bios_return
= ((struct bios_args
*)obj
->buffer
.pointer
);
241 return_code
= bios_return
->result_code
;
248 static int led_on(void)
250 return dell_led_perform_fn(3, /* Length of command */
251 INTERFACE_ERROR
, /* Init to INTERFACE_ERROR */
252 DEVICE_ID_PANEL_BACK
, /* Device ID */
253 CMD_LED_ON
, /* Command */
258 static int led_off(void)
260 return dell_led_perform_fn(3, /* Length of command */
261 INTERFACE_ERROR
, /* Init to INTERFACE_ERROR */
262 DEVICE_ID_PANEL_BACK
, /* Device ID */
263 CMD_LED_OFF
, /* Command */
268 static int led_blink(unsigned char on_eighths
,
269 unsigned char off_eighths
)
271 return dell_led_perform_fn(5, /* Length of command */
272 INTERFACE_ERROR
, /* Init to INTERFACE_ERROR */
273 DEVICE_ID_PANEL_BACK
, /* Device ID */
274 CMD_LED_BLINK
, /* Command */
275 on_eighths
, /* blink on in eigths of a second */
276 off_eighths
); /* blink off in eights of a second */
279 static void dell_led_set(struct led_classdev
*led_cdev
,
280 enum led_brightness value
)
282 if (value
== LED_OFF
)
288 static int dell_led_blink(struct led_classdev
*led_cdev
,
289 unsigned long *delay_on
,
290 unsigned long *delay_off
)
292 unsigned long on_eighths
;
293 unsigned long off_eighths
;
295 /* The Dell LED delay is based on 125ms intervals.
296 Need to round up to next interval. */
298 on_eighths
= (*delay_on
+ 124) / 125;
301 if (on_eighths
> 255)
303 *delay_on
= on_eighths
* 125;
305 off_eighths
= (*delay_off
+ 124) / 125;
306 if (0 == off_eighths
)
308 if (off_eighths
> 255)
310 *delay_off
= off_eighths
* 125;
312 led_blink(on_eighths
, off_eighths
);
317 static struct led_classdev dell_led
= {
319 .brightness
= LED_OFF
,
321 .brightness_set
= dell_led_set
,
322 .blink_set
= dell_led_blink
,
323 .flags
= LED_CORE_SUSPENDRESUME
,
326 static int __init
dell_led_init(void)
330 if (!wmi_has_guid(DELL_LED_BIOS_GUID
) && !wmi_has_guid(DELL_APP_GUID
))
333 if (wmi_has_guid(DELL_APP_GUID
))
334 error
= dell_micmute_led_init();
336 if (wmi_has_guid(DELL_LED_BIOS_GUID
)) {
341 error
= led_classdev_register(NULL
, &dell_led
);
347 static void __exit
dell_led_exit(void)
351 if (wmi_has_guid(DELL_LED_BIOS_GUID
)) {
354 led_classdev_unregister(&dell_led
);
358 module_init(dell_led_init
);
359 module_exit(dell_led_exit
);