diff options
-rw-r--r-- | drivers/misc/asus-laptop.c | 115 |
1 files changed, 113 insertions, 2 deletions
diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index d0d5ee9b358f..222d4fba0ffa 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c | |||
@@ -56,8 +56,17 @@ | |||
56 | #define ASUS_HOTK_PREFIX "\\_SB.ATKD." | 56 | #define ASUS_HOTK_PREFIX "\\_SB.ATKD." |
57 | 57 | ||
58 | /* | 58 | /* |
59 | * Known bits returned by \_SB.ATKD.HWRS | ||
60 | */ | ||
61 | #define WL_HWRS 0x80 | ||
62 | #define BT_HWRS 0x100 | ||
63 | |||
64 | /* | ||
59 | * Flags for hotk status | 65 | * Flags for hotk status |
66 | * WL_ON and BT_ON are also used for wireless_status() | ||
60 | */ | 67 | */ |
68 | #define WL_ON 0x01 //internal Wifi | ||
69 | #define BT_ON 0x02 //internal Bluetooth | ||
61 | #define MLED_ON 0x04 //mail LED | 70 | #define MLED_ON 0x04 //mail LED |
62 | #define TLED_ON 0x08 //touchpad LED | 71 | #define TLED_ON 0x08 //touchpad LED |
63 | #define RLED_ON 0x10 //Record LED | 72 | #define RLED_ON 0x10 //Record LED |
@@ -84,6 +93,14 @@ ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED"); | |||
84 | ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */ | 93 | ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */ |
85 | ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */ | 94 | ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */ |
86 | 95 | ||
96 | /* Bluetooth and WLAN | ||
97 | * WLED and BLED are not handled like other XLED, because in some dsdt | ||
98 | * they also control the WLAN/Bluetooth device. | ||
99 | */ | ||
100 | ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED"); | ||
101 | ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED"); | ||
102 | ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */ | ||
103 | |||
87 | /* | 104 | /* |
88 | * This is the main structure, we can use it to store anything interesting | 105 | * This is the main structure, we can use it to store anything interesting |
89 | * about the hotk device | 106 | * about the hotk device |
@@ -181,14 +198,32 @@ static int read_acpi_int(acpi_handle handle, const char *method, int *val, | |||
181 | return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); | 198 | return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); |
182 | } | 199 | } |
183 | 200 | ||
201 | static int read_wireless_status(int mask) { | ||
202 | int status; | ||
203 | |||
204 | if (!wireless_status_handle) | ||
205 | return (hotk->status & mask) ? 1 : 0; | ||
206 | |||
207 | if (read_acpi_int(wireless_status_handle, NULL, &status, NULL)) { | ||
208 | return (status & mask) ? 1 : 0; | ||
209 | } else | ||
210 | printk(ASUS_WARNING "Error reading Wireless status\n"); | ||
211 | |||
212 | return (hotk->status & mask) ? 1 : 0; | ||
213 | } | ||
214 | |||
184 | /* Generic LED functions */ | 215 | /* Generic LED functions */ |
185 | static int read_status(int mask) | 216 | static int read_status(int mask) |
186 | { | 217 | { |
218 | /* There is a special method for both wireless devices */ | ||
219 | if (mask == BT_ON || mask == WL_ON) | ||
220 | return read_wireless_status(mask); | ||
221 | |||
187 | return (hotk->status & mask) ? 1 : 0; | 222 | return (hotk->status & mask) ? 1 : 0; |
188 | } | 223 | } |
189 | 224 | ||
190 | static void write_status(acpi_handle handle, int out, int mask, | 225 | static void write_status(acpi_handle handle, int out, int mask, |
191 | int invert) | 226 | int invert) |
192 | { | 227 | { |
193 | hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask); | 228 | hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask); |
194 | 229 | ||
@@ -292,6 +327,51 @@ static int parse_arg(const char *buf, unsigned long count, int *val) | |||
292 | return count; | 327 | return count; |
293 | } | 328 | } |
294 | 329 | ||
330 | static ssize_t store_status(const char *buf, size_t count, | ||
331 | acpi_handle handle, int mask, int invert) | ||
332 | { | ||
333 | int rv, value; | ||
334 | int out = 0; | ||
335 | |||
336 | rv = parse_arg(buf, count, &value); | ||
337 | if (rv > 0) | ||
338 | out = value ? 1 : 0; | ||
339 | |||
340 | write_status(handle, out, mask, invert); | ||
341 | |||
342 | return rv; | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * WLAN | ||
347 | */ | ||
348 | static ssize_t show_wlan(struct device *dev, | ||
349 | struct device_attribute *attr, char *buf) | ||
350 | { | ||
351 | return sprintf(buf, "%d\n", read_status(WL_ON)); | ||
352 | } | ||
353 | |||
354 | static ssize_t store_wlan(struct device *dev, struct device_attribute *attr, | ||
355 | const char *buf, size_t count) | ||
356 | { | ||
357 | return store_status(buf, count, wl_switch_handle, WL_ON, 0); | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | * Bluetooth | ||
362 | */ | ||
363 | static ssize_t show_bluetooth(struct device *dev, | ||
364 | struct device_attribute *attr, char *buf) | ||
365 | { | ||
366 | return sprintf(buf, "%d\n", read_status(BT_ON)); | ||
367 | } | ||
368 | |||
369 | static ssize_t store_bluetooth(struct device *dev, struct device_attribute *attr, | ||
370 | const char *buf, size_t count) | ||
371 | { | ||
372 | return store_status(buf, count, bt_switch_handle, BT_ON, 0); | ||
373 | } | ||
374 | |||
295 | static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) | 375 | static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) |
296 | { | 376 | { |
297 | /* TODO Find a better way to handle events count. */ | 377 | /* TODO Find a better way to handle events count. */ |
@@ -322,9 +402,13 @@ static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) | |||
322 | } while(0) | 402 | } while(0) |
323 | 403 | ||
324 | static ASUS_CREATE_DEVICE_ATTR(infos); | 404 | static ASUS_CREATE_DEVICE_ATTR(infos); |
405 | static ASUS_CREATE_DEVICE_ATTR(wlan); | ||
406 | static ASUS_CREATE_DEVICE_ATTR(bluetooth); | ||
325 | 407 | ||
326 | static struct attribute *asuspf_attributes[] = { | 408 | static struct attribute *asuspf_attributes[] = { |
327 | &dev_attr_infos.attr, | 409 | &dev_attr_infos.attr, |
410 | &dev_attr_wlan.attr, | ||
411 | &dev_attr_bluetooth.attr, | ||
328 | NULL | 412 | NULL |
329 | }; | 413 | }; |
330 | 414 | ||
@@ -345,6 +429,13 @@ static struct platform_device *asuspf_device; | |||
345 | static void asus_hotk_add_fs(void) | 429 | static void asus_hotk_add_fs(void) |
346 | { | 430 | { |
347 | ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL); | 431 | ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL); |
432 | |||
433 | if (wl_switch_handle) | ||
434 | ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan); | ||
435 | |||
436 | if (bt_switch_handle) | ||
437 | ASUS_SET_DEVICE_ATTR(bluetooth, 0644, | ||
438 | show_bluetooth, store_bluetooth); | ||
348 | } | 439 | } |
349 | 440 | ||
350 | static int asus_handle_init(char *name, acpi_handle *handle, | 441 | static int asus_handle_init(char *name, acpi_handle *handle, |
@@ -377,7 +468,7 @@ static int asus_hotk_get_info(void) | |||
377 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 468 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
378 | struct acpi_buffer dsdt = { ACPI_ALLOCATE_BUFFER, NULL }; | 469 | struct acpi_buffer dsdt = { ACPI_ALLOCATE_BUFFER, NULL }; |
379 | union acpi_object *model = NULL; | 470 | union acpi_object *model = NULL; |
380 | int bsts_result; | 471 | int bsts_result, hwrs_result; |
381 | char *string = NULL; | 472 | char *string = NULL; |
382 | acpi_status status; | 473 | acpi_status status; |
383 | 474 | ||
@@ -440,6 +531,22 @@ static int asus_hotk_get_info(void) | |||
440 | ASUS_HANDLE_INIT(rled_set); | 531 | ASUS_HANDLE_INIT(rled_set); |
441 | ASUS_HANDLE_INIT(pled_set); | 532 | ASUS_HANDLE_INIT(pled_set); |
442 | 533 | ||
534 | /* | ||
535 | * The HWRS method return informations about the hardware. | ||
536 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. | ||
537 | * The significance of others is yet to be found. | ||
538 | * If we don't find the method, we assume the device are present. | ||
539 | */ | ||
540 | if (!read_acpi_int(hotk->handle, "HRWS", &hwrs_result, NULL)) | ||
541 | hwrs_result = WL_HWRS | BT_HWRS; | ||
542 | |||
543 | if(hwrs_result & WL_HWRS) | ||
544 | ASUS_HANDLE_INIT(wl_switch); | ||
545 | if(hwrs_result & BT_HWRS) | ||
546 | ASUS_HANDLE_INIT(bt_switch); | ||
547 | |||
548 | ASUS_HANDLE_INIT(wireless_status); | ||
549 | |||
443 | kfree(model); | 550 | kfree(model); |
444 | 551 | ||
445 | return AE_OK; | 552 | return AE_OK; |
@@ -504,6 +611,10 @@ static int asus_hotk_add(struct acpi_device *device) | |||
504 | 611 | ||
505 | asus_hotk_found = 1; | 612 | asus_hotk_found = 1; |
506 | 613 | ||
614 | /* WLED and BLED are on by default */ | ||
615 | write_status(bt_switch_handle, 1, BT_ON, 0); | ||
616 | write_status(wl_switch_handle, 1, WL_ON, 0); | ||
617 | |||
507 | end: | 618 | end: |
508 | if (result) { | 619 | if (result) { |
509 | kfree(hotk->name); | 620 | kfree(hotk->name); |