diff options
| author | Corentin Chary <corentincj@iksaif.net> | 2007-01-26 08:04:40 -0500 |
|---|---|---|
| committer | Len Brown <len.brown@intel.com> | 2007-01-30 01:37:02 -0500 |
| commit | 4564de172dcdce641c0d6c689e79e95b5f6bee2c (patch) | |
| tree | a330a4c2ffd6f9a5c2349d99322f8599294b2711 | |
| parent | be18cdabb8ed40ff4b8a240e0d6f4e6c30ff866d (diff) | |
asus-laptop: add bluetooth and wlan support
WLED and BLED are not handled like other leds (MLED, etc ..),
because sometime they also control the wlan/bluetooth device.
If the method for wireless_status is found, it's used to get the
status, otherwise hotk->status is used. We also use the HWRS
method, which tell if the bluetooth/wlan device is present or not.
This patch show why we need a ASUS_SET_DEVICE_ATTR macro : if
there is a bluetooth device, /sys/dev.../asus-laptop/bluetooth
is usable, else it's not but it's clean.
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Len Brown <len.brown@intel.com>
| -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); |
