diff options
Diffstat (limited to 'drivers/platform/x86/compal-laptop.c')
-rw-r--r-- | drivers/platform/x86/compal-laptop.c | 259 |
1 files changed, 116 insertions, 143 deletions
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 11003bba10d3..71ff1545a93e 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,7 +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> |
54 | #include <linux/autoconf.h> | 45 | #include <linux/rfkill.h> |
55 | 46 | ||
56 | #define COMPAL_DRIVER_VERSION "0.2.6" | 47 | #define COMPAL_DRIVER_VERSION "0.2.6" |
57 | 48 | ||
@@ -64,6 +55,10 @@ | |||
64 | #define WLAN_MASK 0x01 | 55 | #define WLAN_MASK 0x01 |
65 | #define BT_MASK 0x02 | 56 | #define BT_MASK 0x02 |
66 | 57 | ||
58 | static struct rfkill *wifi_rfkill; | ||
59 | static struct rfkill *bt_rfkill; | ||
60 | static struct platform_device *compal_device; | ||
61 | |||
67 | static int force; | 62 | static int force; |
68 | module_param(force, bool, 0); | 63 | module_param(force, bool, 0); |
69 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | 64 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); |
@@ -89,65 +84,75 @@ static int get_lcd_level(void) | |||
89 | return (int) result; | 84 | return (int) result; |
90 | } | 85 | } |
91 | 86 | ||
92 | static int set_wlan_state(int state) | 87 | static int compal_rfkill_set(void *data, bool blocked) |
93 | { | 88 | { |
89 | unsigned long radio = (unsigned long) data; | ||
94 | u8 result, value; | 90 | u8 result, value; |
95 | 91 | ||
96 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 92 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); |
97 | 93 | ||
98 | if ((result & KILLSWITCH_MASK) == 0) | 94 | if (!blocked) |
99 | return -EINVAL; | 95 | value = (u8) (result | radio); |
100 | else { | 96 | else |
101 | if (state) | 97 | value = (u8) (result & ~radio); |
102 | value = (u8) (result | WLAN_MASK); | 98 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); |
103 | else | ||
104 | value = (u8) (result & ~WLAN_MASK); | ||
105 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
106 | } | ||
107 | 99 | ||
108 | return 0; | 100 | return 0; |
109 | } | 101 | } |
110 | 102 | ||
111 | static int set_bluetooth_state(int state) | 103 | static void compal_rfkill_poll(struct rfkill *rfkill, void *data) |
112 | { | 104 | { |
113 | u8 result, value; | 105 | u8 result; |
106 | bool hw_blocked; | ||
114 | 107 | ||
115 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 108 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); |
116 | 109 | ||
117 | if ((result & KILLSWITCH_MASK) == 0) | 110 | hw_blocked = !(result & KILLSWITCH_MASK); |
118 | return -EINVAL; | 111 | rfkill_set_hw_state(rfkill, hw_blocked); |
119 | else { | ||
120 | if (state) | ||
121 | value = (u8) (result | BT_MASK); | ||
122 | else | ||
123 | value = (u8) (result & ~BT_MASK); | ||
124 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
125 | } | ||
126 | |||
127 | return 0; | ||
128 | } | 112 | } |
129 | 113 | ||
130 | 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) | ||
131 | { | 120 | { |
132 | u8 result; | 121 | int ret; |
133 | 122 | ||
134 | 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; | ||
135 | 128 | ||
136 | if (wlan) { | 129 | ret = rfkill_register(wifi_rfkill); |
137 | if ((result & KILLSWITCH_MASK) == 0) | 130 | if (ret) |
138 | *wlan = 0; | 131 | goto err_wifi; |
139 | else | ||
140 | *wlan = result & WLAN_MASK; | ||
141 | } | ||
142 | 132 | ||
143 | if (bluetooth) { | 133 | bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev, |
144 | if ((result & KILLSWITCH_MASK) == 0) | 134 | RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops, |
145 | *bluetooth = 0; | 135 | (void *) BT_MASK); |
146 | else | 136 | if (!bt_rfkill) { |
147 | *bluetooth = (result & BT_MASK) >> 1; | 137 | ret = -ENOMEM; |
138 | goto err_allocate_bt; | ||
148 | } | 139 | } |
140 | ret = rfkill_register(bt_rfkill); | ||
141 | if (ret) | ||
142 | goto err_register_bt; | ||
149 | 143 | ||
150 | 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; | ||
151 | } | 156 | } |
152 | 157 | ||
153 | /* Backlight device stuff */ | 158 | /* Backlight device stuff */ |
@@ -170,86 +175,6 @@ static struct backlight_ops compalbl_ops = { | |||
170 | 175 | ||
171 | static struct backlight_device *compalbl_device; | 176 | static struct backlight_device *compalbl_device; |
172 | 177 | ||
173 | /* Platform device */ | ||
174 | |||
175 | static ssize_t show_wlan(struct device *dev, | ||
176 | struct device_attribute *attr, char *buf) | ||
177 | { | ||
178 | int ret, enabled; | ||
179 | |||
180 | ret = get_wireless_state(&enabled, NULL); | ||
181 | if (ret < 0) | ||
182 | return ret; | ||
183 | |||
184 | return sprintf(buf, "%i\n", enabled); | ||
185 | } | ||
186 | |||
187 | static ssize_t show_raw(struct device *dev, | ||
188 | struct device_attribute *attr, char *buf) | ||
189 | { | ||
190 | u8 result; | ||
191 | |||
192 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
193 | |||
194 | return sprintf(buf, "%i\n", result); | ||
195 | } | ||
196 | |||
197 | static ssize_t show_bluetooth(struct device *dev, | ||
198 | struct device_attribute *attr, char *buf) | ||
199 | { | ||
200 | int ret, enabled; | ||
201 | |||
202 | ret = get_wireless_state(NULL, &enabled); | ||
203 | if (ret < 0) | ||
204 | return ret; | ||
205 | |||
206 | return sprintf(buf, "%i\n", enabled); | ||
207 | } | ||
208 | |||
209 | static ssize_t store_wlan_state(struct device *dev, | ||
210 | struct device_attribute *attr, const char *buf, size_t count) | ||
211 | { | ||
212 | int state, ret; | ||
213 | |||
214 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
215 | return -EINVAL; | ||
216 | |||
217 | ret = set_wlan_state(state); | ||
218 | if (ret < 0) | ||
219 | return ret; | ||
220 | |||
221 | return count; | ||
222 | } | ||
223 | |||
224 | static ssize_t store_bluetooth_state(struct device *dev, | ||
225 | struct device_attribute *attr, const char *buf, size_t count) | ||
226 | { | ||
227 | int state, ret; | ||
228 | |||
229 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
230 | return -EINVAL; | ||
231 | |||
232 | ret = set_bluetooth_state(state); | ||
233 | if (ret < 0) | ||
234 | return ret; | ||
235 | |||
236 | return count; | ||
237 | } | ||
238 | |||
239 | static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state); | ||
240 | static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state); | ||
241 | static DEVICE_ATTR(raw, 0444, show_raw, NULL); | ||
242 | |||
243 | static struct attribute *compal_attributes[] = { | ||
244 | &dev_attr_bluetooth.attr, | ||
245 | &dev_attr_wlan.attr, | ||
246 | &dev_attr_raw.attr, | ||
247 | NULL | ||
248 | }; | ||
249 | |||
250 | static struct attribute_group compal_attribute_group = { | ||
251 | .attrs = compal_attributes | ||
252 | }; | ||
253 | 178 | ||
254 | static struct platform_driver compal_driver = { | 179 | static struct platform_driver compal_driver = { |
255 | .driver = { | 180 | .driver = { |
@@ -258,8 +183,6 @@ static struct platform_driver compal_driver = { | |||
258 | } | 183 | } |
259 | }; | 184 | }; |
260 | 185 | ||
261 | static struct platform_device *compal_device; | ||
262 | |||
263 | /* Initialization */ | 186 | /* Initialization */ |
264 | 187 | ||
265 | static int dmi_check_cb(const struct dmi_system_id *id) | 188 | static int dmi_check_cb(const struct dmi_system_id *id) |
@@ -311,6 +234,47 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { | |||
311 | }, | 234 | }, |
312 | .callback = dmi_check_cb | 235 | .callback = dmi_check_cb |
313 | }, | 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 | |||
314 | { } | 278 | { } |
315 | }; | 279 | }; |
316 | 280 | ||
@@ -327,12 +291,15 @@ static int __init compal_init(void) | |||
327 | /* Register backlight stuff */ | 291 | /* Register backlight stuff */ |
328 | 292 | ||
329 | if (!acpi_video_backlight_support()) { | 293 | if (!acpi_video_backlight_support()) { |
330 | compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, | 294 | struct backlight_properties props; |
331 | &compalbl_ops); | 295 | memset(&props, 0, sizeof(struct backlight_properties)); |
296 | props.max_brightness = COMPAL_LCD_LEVEL_MAX - 1; | ||
297 | compalbl_device = backlight_device_register("compal-laptop", | ||
298 | NULL, NULL, | ||
299 | &compalbl_ops, | ||
300 | &props); | ||
332 | if (IS_ERR(compalbl_device)) | 301 | if (IS_ERR(compalbl_device)) |
333 | return PTR_ERR(compalbl_device); | 302 | return PTR_ERR(compalbl_device); |
334 | |||
335 | compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; | ||
336 | } | 303 | } |
337 | 304 | ||
338 | ret = platform_driver_register(&compal_driver); | 305 | ret = platform_driver_register(&compal_driver); |
@@ -349,23 +316,21 @@ static int __init compal_init(void) | |||
349 | 316 | ||
350 | ret = platform_device_add(compal_device); | 317 | ret = platform_device_add(compal_device); |
351 | if (ret) | 318 | if (ret) |
352 | goto fail_platform_device1; | 319 | goto fail_platform_device; |
353 | 320 | ||
354 | ret = sysfs_create_group(&compal_device->dev.kobj, | 321 | ret = setup_rfkill(); |
355 | &compal_attribute_group); | ||
356 | if (ret) | 322 | if (ret) |
357 | goto fail_platform_device2; | 323 | goto fail_rfkill; |
358 | 324 | ||
359 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION | 325 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION |
360 | " successfully loaded.\n"); | 326 | " successfully loaded.\n"); |
361 | 327 | ||
362 | return 0; | 328 | return 0; |
363 | 329 | ||
364 | fail_platform_device2: | 330 | fail_rfkill: |
365 | |||
366 | platform_device_del(compal_device); | 331 | platform_device_del(compal_device); |
367 | 332 | ||
368 | fail_platform_device1: | 333 | fail_platform_device: |
369 | 334 | ||
370 | platform_device_put(compal_device); | 335 | platform_device_put(compal_device); |
371 | 336 | ||
@@ -383,10 +348,13 @@ fail_backlight: | |||
383 | static void __exit compal_cleanup(void) | 348 | static void __exit compal_cleanup(void) |
384 | { | 349 | { |
385 | 350 | ||
386 | sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group); | ||
387 | platform_device_unregister(compal_device); | 351 | platform_device_unregister(compal_device); |
388 | platform_driver_unregister(&compal_driver); | 352 | platform_driver_unregister(&compal_driver); |
389 | backlight_device_unregister(compalbl_device); | 353 | backlight_device_unregister(compalbl_device); |
354 | rfkill_unregister(wifi_rfkill); | ||
355 | rfkill_destroy(wifi_rfkill); | ||
356 | rfkill_unregister(bt_rfkill); | ||
357 | rfkill_destroy(bt_rfkill); | ||
390 | 358 | ||
391 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); | 359 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); |
392 | } | 360 | } |
@@ -404,3 +372,8 @@ MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); | |||
404 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); | 372 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); |
405 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); | 373 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); |
406 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); | 374 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); |
375 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); | ||
376 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); | ||
377 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); | ||
378 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*"); | ||
379 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*"); | ||