diff options
Diffstat (limited to 'drivers/platform/x86/compal-laptop.c')
-rw-r--r-- | drivers/platform/x86/compal-laptop.c | 247 |
1 files changed, 109 insertions, 138 deletions
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 1a387e79f719..2740b40aad9b 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c | |||
@@ -26,17 +26,8 @@ | |||
26 | /* | 26 | /* |
27 | * comapl-laptop.c - Compal laptop support. | 27 | * comapl-laptop.c - Compal laptop support. |
28 | * | 28 | * |
29 | * This driver exports a few files in /sys/devices/platform/compal-laptop/: | 29 | * The driver registers itself with the rfkill subsystem and |
30 | * | 30 | * the Linux backlight control subsystem. |
31 | * wlan - wlan subsystem state: contains 0 or 1 (rw) | ||
32 | * | ||
33 | * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw) | ||
34 | * | ||
35 | * raw - raw value taken from embedded controller register (ro) | ||
36 | * | ||
37 | * In addition to these platform device attributes the driver | ||
38 | * registers itself in the Linux backlight control subsystem and is | ||
39 | * available to userspace under /sys/class/backlight/compal-laptop/. | ||
40 | * | 31 | * |
41 | * This driver might work on other laptops produced by Compal. If you | 32 | * This driver might work on other laptops produced by Compal. If you |
42 | * want to try it you can pass force=1 as argument to the module which | 33 | * want to try it you can pass force=1 as argument to the module which |
@@ -51,6 +42,7 @@ | |||
51 | #include <linux/dmi.h> | 42 | #include <linux/dmi.h> |
52 | #include <linux/backlight.h> | 43 | #include <linux/backlight.h> |
53 | #include <linux/platform_device.h> | 44 | #include <linux/platform_device.h> |
45 | #include <linux/rfkill.h> | ||
54 | 46 | ||
55 | #define COMPAL_DRIVER_VERSION "0.2.6" | 47 | #define COMPAL_DRIVER_VERSION "0.2.6" |
56 | 48 | ||
@@ -63,6 +55,10 @@ | |||
63 | #define WLAN_MASK 0x01 | 55 | #define WLAN_MASK 0x01 |
64 | #define BT_MASK 0x02 | 56 | #define BT_MASK 0x02 |
65 | 57 | ||
58 | static struct rfkill *wifi_rfkill; | ||
59 | static struct rfkill *bt_rfkill; | ||
60 | static struct platform_device *compal_device; | ||
61 | |||
66 | static int force; | 62 | static int force; |
67 | module_param(force, bool, 0); | 63 | module_param(force, bool, 0); |
68 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | 64 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); |
@@ -88,65 +84,75 @@ static int get_lcd_level(void) | |||
88 | return (int) result; | 84 | return (int) result; |
89 | } | 85 | } |
90 | 86 | ||
91 | static int set_wlan_state(int state) | 87 | static int compal_rfkill_set(void *data, bool blocked) |
92 | { | 88 | { |
89 | unsigned long radio = (unsigned long) data; | ||
93 | u8 result, value; | 90 | u8 result, value; |
94 | 91 | ||
95 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 92 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); |
96 | 93 | ||
97 | if ((result & KILLSWITCH_MASK) == 0) | 94 | if (!blocked) |
98 | return -EINVAL; | 95 | value = (u8) (result | radio); |
99 | else { | 96 | else |
100 | if (state) | 97 | value = (u8) (result & ~radio); |
101 | value = (u8) (result | WLAN_MASK); | 98 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); |
102 | else | ||
103 | value = (u8) (result & ~WLAN_MASK); | ||
104 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
105 | } | ||
106 | 99 | ||
107 | return 0; | 100 | return 0; |
108 | } | 101 | } |
109 | 102 | ||
110 | static int set_bluetooth_state(int state) | 103 | static void compal_rfkill_poll(struct rfkill *rfkill, void *data) |
111 | { | 104 | { |
112 | u8 result, value; | 105 | u8 result; |
106 | bool hw_blocked; | ||
113 | 107 | ||
114 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 108 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); |
115 | 109 | ||
116 | if ((result & KILLSWITCH_MASK) == 0) | 110 | hw_blocked = !(result & KILLSWITCH_MASK); |
117 | return -EINVAL; | 111 | rfkill_set_hw_state(rfkill, hw_blocked); |
118 | else { | ||
119 | if (state) | ||
120 | value = (u8) (result | BT_MASK); | ||
121 | else | ||
122 | value = (u8) (result & ~BT_MASK); | ||
123 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
124 | } | ||
125 | |||
126 | return 0; | ||
127 | } | 112 | } |
128 | 113 | ||
129 | static int get_wireless_state(int *wlan, int *bluetooth) | 114 | static const struct rfkill_ops compal_rfkill_ops = { |
115 | .poll = compal_rfkill_poll, | ||
116 | .set_block = compal_rfkill_set, | ||
117 | }; | ||
118 | |||
119 | static int setup_rfkill(void) | ||
130 | { | 120 | { |
131 | u8 result; | 121 | int ret; |
132 | 122 | ||
133 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 123 | wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev, |
124 | RFKILL_TYPE_WLAN, &compal_rfkill_ops, | ||
125 | (void *) WLAN_MASK); | ||
126 | if (!wifi_rfkill) | ||
127 | return -ENOMEM; | ||
134 | 128 | ||
135 | if (wlan) { | 129 | ret = rfkill_register(wifi_rfkill); |
136 | if ((result & KILLSWITCH_MASK) == 0) | 130 | if (ret) |
137 | *wlan = 0; | 131 | goto err_wifi; |
138 | else | ||
139 | *wlan = result & WLAN_MASK; | ||
140 | } | ||
141 | 132 | ||
142 | if (bluetooth) { | 133 | bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev, |
143 | if ((result & KILLSWITCH_MASK) == 0) | 134 | RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops, |
144 | *bluetooth = 0; | 135 | (void *) BT_MASK); |
145 | else | 136 | if (!bt_rfkill) { |
146 | *bluetooth = (result & BT_MASK) >> 1; | 137 | ret = -ENOMEM; |
138 | goto err_allocate_bt; | ||
147 | } | 139 | } |
140 | ret = rfkill_register(bt_rfkill); | ||
141 | if (ret) | ||
142 | goto err_register_bt; | ||
148 | 143 | ||
149 | return 0; | 144 | return 0; |
145 | |||
146 | err_register_bt: | ||
147 | rfkill_destroy(bt_rfkill); | ||
148 | |||
149 | err_allocate_bt: | ||
150 | rfkill_unregister(wifi_rfkill); | ||
151 | |||
152 | err_wifi: | ||
153 | rfkill_destroy(wifi_rfkill); | ||
154 | |||
155 | return ret; | ||
150 | } | 156 | } |
151 | 157 | ||
152 | /* Backlight device stuff */ | 158 | /* Backlight device stuff */ |
@@ -169,86 +175,6 @@ static struct backlight_ops compalbl_ops = { | |||
169 | 175 | ||
170 | static struct backlight_device *compalbl_device; | 176 | static struct backlight_device *compalbl_device; |
171 | 177 | ||
172 | /* Platform device */ | ||
173 | |||
174 | static ssize_t show_wlan(struct device *dev, | ||
175 | struct device_attribute *attr, char *buf) | ||
176 | { | ||
177 | int ret, enabled; | ||
178 | |||
179 | ret = get_wireless_state(&enabled, NULL); | ||
180 | if (ret < 0) | ||
181 | return ret; | ||
182 | |||
183 | return sprintf(buf, "%i\n", enabled); | ||
184 | } | ||
185 | |||
186 | static ssize_t show_raw(struct device *dev, | ||
187 | struct device_attribute *attr, char *buf) | ||
188 | { | ||
189 | u8 result; | ||
190 | |||
191 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
192 | |||
193 | return sprintf(buf, "%i\n", result); | ||
194 | } | ||
195 | |||
196 | static ssize_t show_bluetooth(struct device *dev, | ||
197 | struct device_attribute *attr, char *buf) | ||
198 | { | ||
199 | int ret, enabled; | ||
200 | |||
201 | ret = get_wireless_state(NULL, &enabled); | ||
202 | if (ret < 0) | ||
203 | return ret; | ||
204 | |||
205 | return sprintf(buf, "%i\n", enabled); | ||
206 | } | ||
207 | |||
208 | static ssize_t store_wlan_state(struct device *dev, | ||
209 | struct device_attribute *attr, const char *buf, size_t count) | ||
210 | { | ||
211 | int state, ret; | ||
212 | |||
213 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
214 | return -EINVAL; | ||
215 | |||
216 | ret = set_wlan_state(state); | ||
217 | if (ret < 0) | ||
218 | return ret; | ||
219 | |||
220 | return count; | ||
221 | } | ||
222 | |||
223 | static ssize_t store_bluetooth_state(struct device *dev, | ||
224 | struct device_attribute *attr, const char *buf, size_t count) | ||
225 | { | ||
226 | int state, ret; | ||
227 | |||
228 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
229 | return -EINVAL; | ||
230 | |||
231 | ret = set_bluetooth_state(state); | ||
232 | if (ret < 0) | ||
233 | return ret; | ||
234 | |||
235 | return count; | ||
236 | } | ||
237 | |||
238 | static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state); | ||
239 | static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state); | ||
240 | static DEVICE_ATTR(raw, 0444, show_raw, NULL); | ||
241 | |||
242 | static struct attribute *compal_attributes[] = { | ||
243 | &dev_attr_bluetooth.attr, | ||
244 | &dev_attr_wlan.attr, | ||
245 | &dev_attr_raw.attr, | ||
246 | NULL | ||
247 | }; | ||
248 | |||
249 | static struct attribute_group compal_attribute_group = { | ||
250 | .attrs = compal_attributes | ||
251 | }; | ||
252 | 178 | ||
253 | static struct platform_driver compal_driver = { | 179 | static struct platform_driver compal_driver = { |
254 | .driver = { | 180 | .driver = { |
@@ -257,8 +183,6 @@ static struct platform_driver compal_driver = { | |||
257 | } | 183 | } |
258 | }; | 184 | }; |
259 | 185 | ||
260 | static struct platform_device *compal_device; | ||
261 | |||
262 | /* Initialization */ | 186 | /* Initialization */ |
263 | 187 | ||
264 | static int dmi_check_cb(const struct dmi_system_id *id) | 188 | static int dmi_check_cb(const struct dmi_system_id *id) |
@@ -310,6 +234,47 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { | |||
310 | }, | 234 | }, |
311 | .callback = dmi_check_cb | 235 | .callback = dmi_check_cb |
312 | }, | 236 | }, |
237 | { | ||
238 | .ident = "Dell Mini 9", | ||
239 | .matches = { | ||
240 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
241 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), | ||
242 | }, | ||
243 | .callback = dmi_check_cb | ||
244 | }, | ||
245 | { | ||
246 | .ident = "Dell Mini 10", | ||
247 | .matches = { | ||
248 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
249 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), | ||
250 | }, | ||
251 | .callback = dmi_check_cb | ||
252 | }, | ||
253 | { | ||
254 | .ident = "Dell Mini 10v", | ||
255 | .matches = { | ||
256 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
257 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), | ||
258 | }, | ||
259 | .callback = dmi_check_cb | ||
260 | }, | ||
261 | { | ||
262 | .ident = "Dell Inspiron 11z", | ||
263 | .matches = { | ||
264 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
265 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), | ||
266 | }, | ||
267 | .callback = dmi_check_cb | ||
268 | }, | ||
269 | { | ||
270 | .ident = "Dell Mini 12", | ||
271 | .matches = { | ||
272 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
273 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), | ||
274 | }, | ||
275 | .callback = dmi_check_cb | ||
276 | }, | ||
277 | |||
313 | { } | 278 | { } |
314 | }; | 279 | }; |
315 | 280 | ||
@@ -348,23 +313,21 @@ static int __init compal_init(void) | |||
348 | 313 | ||
349 | ret = platform_device_add(compal_device); | 314 | ret = platform_device_add(compal_device); |
350 | if (ret) | 315 | if (ret) |
351 | goto fail_platform_device1; | 316 | goto fail_platform_device; |
352 | 317 | ||
353 | ret = sysfs_create_group(&compal_device->dev.kobj, | 318 | ret = setup_rfkill(); |
354 | &compal_attribute_group); | ||
355 | if (ret) | 319 | if (ret) |
356 | goto fail_platform_device2; | 320 | goto fail_rfkill; |
357 | 321 | ||
358 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION | 322 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION |
359 | " successfully loaded.\n"); | 323 | " successfully loaded.\n"); |
360 | 324 | ||
361 | return 0; | 325 | return 0; |
362 | 326 | ||
363 | fail_platform_device2: | 327 | fail_rfkill: |
364 | |||
365 | platform_device_del(compal_device); | 328 | platform_device_del(compal_device); |
366 | 329 | ||
367 | fail_platform_device1: | 330 | fail_platform_device: |
368 | 331 | ||
369 | platform_device_put(compal_device); | 332 | platform_device_put(compal_device); |
370 | 333 | ||
@@ -382,10 +345,13 @@ fail_backlight: | |||
382 | static void __exit compal_cleanup(void) | 345 | static void __exit compal_cleanup(void) |
383 | { | 346 | { |
384 | 347 | ||
385 | sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group); | ||
386 | platform_device_unregister(compal_device); | 348 | platform_device_unregister(compal_device); |
387 | platform_driver_unregister(&compal_driver); | 349 | platform_driver_unregister(&compal_driver); |
388 | backlight_device_unregister(compalbl_device); | 350 | backlight_device_unregister(compalbl_device); |
351 | rfkill_unregister(wifi_rfkill); | ||
352 | rfkill_destroy(wifi_rfkill); | ||
353 | rfkill_unregister(bt_rfkill); | ||
354 | rfkill_destroy(bt_rfkill); | ||
389 | 355 | ||
390 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); | 356 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); |
391 | } | 357 | } |
@@ -403,3 +369,8 @@ MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); | |||
403 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); | 369 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); |
404 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); | 370 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); |
405 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); | 371 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); |
372 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); | ||
373 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); | ||
374 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); | ||
375 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*"); | ||
376 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*"); | ||