0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/acpi.h>
0013 #include <linux/leds.h>
0014 #include <linux/slab.h>
0015 #include <linux/module.h>
0016
0017 MODULE_AUTHOR("Louis Davis/Jim Dailey");
0018 MODULE_DESCRIPTION("Dell LED Control Driver");
0019 MODULE_LICENSE("GPL");
0020
0021 #define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396"
0022 MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
0023
0024
0025 #define INVALID_DEVICE_ID 250
0026 #define INVALID_PARAMETER 251
0027 #define INVALID_BUFFER 252
0028 #define INTERFACE_ERROR 253
0029 #define UNSUPPORTED_COMMAND 254
0030 #define UNSPECIFIED_ERROR 255
0031
0032
0033 #define DEVICE_ID_PANEL_BACK 1
0034
0035
0036 #define CMD_LED_ON 16
0037 #define CMD_LED_OFF 17
0038 #define CMD_LED_BLINK 18
0039
0040 struct bios_args {
0041 unsigned char length;
0042 unsigned char result_code;
0043 unsigned char device_id;
0044 unsigned char command;
0045 unsigned char on_time;
0046 unsigned char off_time;
0047 };
0048
0049 static int dell_led_perform_fn(u8 length, u8 result_code, u8 device_id,
0050 u8 command, u8 on_time, u8 off_time)
0051 {
0052 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
0053 struct bios_args *bios_return;
0054 struct acpi_buffer input;
0055 union acpi_object *obj;
0056 acpi_status status;
0057 u8 return_code;
0058
0059 struct bios_args args = {
0060 .length = length,
0061 .result_code = result_code,
0062 .device_id = device_id,
0063 .command = command,
0064 .on_time = on_time,
0065 .off_time = off_time
0066 };
0067
0068 input.length = sizeof(struct bios_args);
0069 input.pointer = &args;
0070
0071 status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 0, 1, &input, &output);
0072 if (ACPI_FAILURE(status))
0073 return status;
0074
0075 obj = output.pointer;
0076
0077 if (!obj)
0078 return -EINVAL;
0079 if (obj->type != ACPI_TYPE_BUFFER) {
0080 kfree(obj);
0081 return -EINVAL;
0082 }
0083
0084 bios_return = ((struct bios_args *)obj->buffer.pointer);
0085 return_code = bios_return->result_code;
0086
0087 kfree(obj);
0088
0089 return return_code;
0090 }
0091
0092 static int led_on(void)
0093 {
0094 return dell_led_perform_fn(3,
0095 INTERFACE_ERROR,
0096 DEVICE_ID_PANEL_BACK,
0097 CMD_LED_ON,
0098 0,
0099 0);
0100 }
0101
0102 static int led_off(void)
0103 {
0104 return dell_led_perform_fn(3,
0105 INTERFACE_ERROR,
0106 DEVICE_ID_PANEL_BACK,
0107 CMD_LED_OFF,
0108 0,
0109 0);
0110 }
0111
0112 static int led_blink(unsigned char on_eighths, unsigned char off_eighths)
0113 {
0114 return dell_led_perform_fn(5,
0115 INTERFACE_ERROR,
0116 DEVICE_ID_PANEL_BACK,
0117 CMD_LED_BLINK,
0118 on_eighths,
0119 off_eighths);
0120 }
0121
0122 static void dell_led_set(struct led_classdev *led_cdev,
0123 enum led_brightness value)
0124 {
0125 if (value == LED_OFF)
0126 led_off();
0127 else
0128 led_on();
0129 }
0130
0131 static int dell_led_blink(struct led_classdev *led_cdev,
0132 unsigned long *delay_on, unsigned long *delay_off)
0133 {
0134 unsigned long on_eighths;
0135 unsigned long off_eighths;
0136
0137
0138
0139
0140
0141
0142 on_eighths = DIV_ROUND_UP(*delay_on, 125);
0143 on_eighths = clamp_t(unsigned long, on_eighths, 1, 255);
0144 *delay_on = on_eighths * 125;
0145
0146 off_eighths = DIV_ROUND_UP(*delay_off, 125);
0147 off_eighths = clamp_t(unsigned long, off_eighths, 1, 255);
0148 *delay_off = off_eighths * 125;
0149
0150 led_blink(on_eighths, off_eighths);
0151
0152 return 0;
0153 }
0154
0155 static struct led_classdev dell_led = {
0156 .name = "dell::lid",
0157 .brightness = LED_OFF,
0158 .max_brightness = 1,
0159 .brightness_set = dell_led_set,
0160 .blink_set = dell_led_blink,
0161 .flags = LED_CORE_SUSPENDRESUME,
0162 };
0163
0164 static int __init dell_led_init(void)
0165 {
0166 int error = 0;
0167
0168 if (!wmi_has_guid(DELL_LED_BIOS_GUID))
0169 return -ENODEV;
0170
0171 error = led_off();
0172 if (error != 0)
0173 return -ENODEV;
0174
0175 return led_classdev_register(NULL, &dell_led);
0176 }
0177
0178 static void __exit dell_led_exit(void)
0179 {
0180 led_classdev_unregister(&dell_led);
0181
0182 led_off();
0183 }
0184
0185 module_init(dell_led_init);
0186 module_exit(dell_led_exit);