~ubuntu-branches/debian/wheezy/linux-2.6/wheezy

« back to all changes in this revision

Viewing changes to drivers/leds/dell-led.c

  • Committer: Bazaar Package Importer
  • Author(s): Ben Hutchings, Ben Hutchings, Aurelien Jarno, Martin Michlmayr
  • Date: 2011-04-06 13:53:30 UTC
  • mfrom: (43.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20110406135330-wjufxhd0tvn3zx4z
Tags: 2.6.38-3
[ Ben Hutchings ]
* [ppc64] Add to linux-tools package architectures (Closes: #620124)
* [amd64] Save cr4 to mmu_cr4_features at boot time (Closes: #620284)
* appletalk: Fix bugs introduced when removing use of BKL
* ALSA: Fix yet another race in disconnection
* cciss: Fix lost command issue
* ath9k: Fix kernel panic in AR2427
* ses: Avoid kernel panic when lun 0 is not mapped
* PCI/ACPI: Report ASPM support to BIOS if not disabled from command line

[ Aurelien Jarno ]
* rtlwifi: fix build when PCI is not enabled.

[ Martin Michlmayr ]
* rtlwifi: Eliminate udelay calls with too large values (Closes: #620204)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * dell_led.c - Dell LED Driver
 
3
 *
 
4
 * Copyright (C) 2010 Dell Inc.
 
5
 * Louis Davis <louis_davis@dell.com>
 
6
 * Jim Dailey <jim_dailey@dell.com>
 
7
 *
 
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.
 
11
 *
 
12
 */
 
13
 
 
14
#include <linux/acpi.h>
 
15
#include <linux/leds.h>
 
16
#include <linux/slab.h>
 
17
 
 
18
MODULE_AUTHOR("Louis Davis/Jim Dailey");
 
19
MODULE_DESCRIPTION("Dell LED Control Driver");
 
20
MODULE_LICENSE("GPL");
 
21
 
 
22
#define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396"
 
23
MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
 
24
 
 
25
/* Error Result Codes: */
 
26
#define INVALID_DEVICE_ID       250
 
27
#define INVALID_PARAMETER       251
 
28
#define INVALID_BUFFER          252
 
29
#define INTERFACE_ERROR         253
 
30
#define UNSUPPORTED_COMMAND     254
 
31
#define UNSPECIFIED_ERROR       255
 
32
 
 
33
/* Device ID */
 
34
#define DEVICE_ID_PANEL_BACK    1
 
35
 
 
36
/* LED Commands */
 
37
#define CMD_LED_ON      16
 
38
#define CMD_LED_OFF     17
 
39
#define CMD_LED_BLINK   18
 
40
 
 
41
struct bios_args {
 
42
        unsigned char length;
 
43
        unsigned char result_code;
 
44
        unsigned char device_id;
 
45
        unsigned char command;
 
46
        unsigned char on_time;
 
47
        unsigned char off_time;
 
48
};
 
49
 
 
50
static int dell_led_perform_fn(u8 length,
 
51
                u8 result_code,
 
52
                u8 device_id,
 
53
                u8 command,
 
54
                u8 on_time,
 
55
                u8 off_time)
 
56
{
 
57
        struct bios_args *bios_return;
 
58
        u8 return_code;
 
59
        union acpi_object *obj;
 
60
        struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
 
61
        struct acpi_buffer input;
 
62
        acpi_status status;
 
63
 
 
64
        struct bios_args args;
 
65
        args.length = length;
 
66
        args.result_code = result_code;
 
67
        args.device_id = device_id;
 
68
        args.command = command;
 
69
        args.on_time = on_time;
 
70
        args.off_time = off_time;
 
71
 
 
72
        input.length = sizeof(struct bios_args);
 
73
        input.pointer = &args;
 
74
 
 
75
        status = wmi_evaluate_method(DELL_LED_BIOS_GUID,
 
76
                1,
 
77
                1,
 
78
                &input,
 
79
                &output);
 
80
 
 
81
        if (ACPI_FAILURE(status))
 
82
                return status;
 
83
 
 
84
        obj = output.pointer;
 
85
 
 
86
        if (!obj)
 
87
                return -EINVAL;
 
88
        else if (obj->type != ACPI_TYPE_BUFFER) {
 
89
                kfree(obj);
 
90
                return -EINVAL;
 
91
        }
 
92
 
 
93
        bios_return = ((struct bios_args *)obj->buffer.pointer);
 
94
        return_code = bios_return->result_code;
 
95
 
 
96
        kfree(obj);
 
97
 
 
98
        return return_code;
 
99
}
 
100
 
 
101
static int led_on(void)
 
102
{
 
103
        return dell_led_perform_fn(3,   /* Length of command */
 
104
                INTERFACE_ERROR,        /* Init to  INTERFACE_ERROR */
 
105
                DEVICE_ID_PANEL_BACK,   /* Device ID */
 
106
                CMD_LED_ON,             /* Command */
 
107
                0,                      /* not used */
 
108
                0);                     /* not used */
 
109
}
 
110
 
 
111
static int led_off(void)
 
112
{
 
113
        return dell_led_perform_fn(3,   /* Length of command */
 
114
                INTERFACE_ERROR,        /* Init to  INTERFACE_ERROR */
 
115
                DEVICE_ID_PANEL_BACK,   /* Device ID */
 
116
                CMD_LED_OFF,            /* Command */
 
117
                0,                      /* not used */
 
118
                0);                     /* not used */
 
119
}
 
120
 
 
121
static int led_blink(unsigned char on_eighths,
 
122
                unsigned char off_eighths)
 
123
{
 
124
        return dell_led_perform_fn(5,   /* Length of command */
 
125
                INTERFACE_ERROR,        /* Init to  INTERFACE_ERROR */
 
126
                DEVICE_ID_PANEL_BACK,   /* Device ID */
 
127
                CMD_LED_BLINK,          /* Command */
 
128
                on_eighths,             /* blink on in eigths of a second */
 
129
                off_eighths);           /* blink off in eights of a second */
 
130
}
 
131
 
 
132
static void dell_led_set(struct led_classdev *led_cdev,
 
133
                enum led_brightness value)
 
134
{
 
135
        if (value == LED_OFF)
 
136
                led_off();
 
137
        else
 
138
                led_on();
 
139
}
 
140
 
 
141
static int dell_led_blink(struct led_classdev *led_cdev,
 
142
                unsigned long *delay_on,
 
143
                unsigned long *delay_off)
 
144
{
 
145
        unsigned long on_eighths;
 
146
        unsigned long off_eighths;
 
147
 
 
148
        /* The Dell LED delay is based on 125ms intervals.
 
149
           Need to round up to next interval. */
 
150
 
 
151
        on_eighths = (*delay_on + 124) / 125;
 
152
        if (0 == on_eighths)
 
153
                on_eighths = 1;
 
154
        if (on_eighths > 255)
 
155
                on_eighths = 255;
 
156
        *delay_on = on_eighths * 125;
 
157
 
 
158
        off_eighths = (*delay_off + 124) / 125;
 
159
        if (0 == off_eighths)
 
160
                off_eighths = 1;
 
161
        if (off_eighths > 255)
 
162
                off_eighths = 255;
 
163
        *delay_off = off_eighths * 125;
 
164
 
 
165
        led_blink(on_eighths, off_eighths);
 
166
 
 
167
        return 0;
 
168
}
 
169
 
 
170
static struct led_classdev dell_led = {
 
171
        .name           = "dell::lid",
 
172
        .brightness     = LED_OFF,
 
173
        .max_brightness = 1,
 
174
        .brightness_set = dell_led_set,
 
175
        .blink_set      = dell_led_blink,
 
176
        .flags          = LED_CORE_SUSPENDRESUME,
 
177
};
 
178
 
 
179
static int __init dell_led_init(void)
 
180
{
 
181
        int error = 0;
 
182
 
 
183
        if (!wmi_has_guid(DELL_LED_BIOS_GUID))
 
184
                return -ENODEV;
 
185
 
 
186
        error = led_off();
 
187
        if (error != 0)
 
188
                return -ENODEV;
 
189
 
 
190
        return led_classdev_register(NULL, &dell_led);
 
191
}
 
192
 
 
193
static void __exit dell_led_exit(void)
 
194
{
 
195
        led_classdev_unregister(&dell_led);
 
196
 
 
197
        led_off();
 
198
}
 
199
 
 
200
module_init(dell_led_init);
 
201
module_exit(dell_led_exit);