Pull asus_acpi-0.30 into release branch

Len Brown 37672d4c b2f71bad

+223 -108
+223 -108
drivers/acpi/asus_acpi.c
··· 2 2 * asus_acpi.c - Asus Laptop ACPI Extras 3 3 * 4 4 * 5 - * Copyright (C) 2002, 2003, 2004 Julien Lerouge, Karol Kozimor 5 + * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor 6 6 * 7 7 * This program is free software; you can redistribute it and/or modify 8 8 * it under the terms of the GNU General Public License as published by ··· 26 26 * Pontus Fuchs - Helper functions, cleanup 27 27 * Johann Wiesner - Small compile fixes 28 28 * John Belmonte - ACPI code for Toshiba laptop was a good starting point. 29 + * �ric Burghard - LED display support for W1N 29 30 * 30 - * TODO: 31 - * add Fn key status 32 - * Add mode selection on module loading (parameter) -> still necessary? 33 - * Complete display switching -- may require dirty hacks or calling _DOS? 34 31 */ 35 32 36 33 #include <linux/kernel.h> ··· 39 42 #include <acpi/acpi_bus.h> 40 43 #include <asm/uaccess.h> 41 44 42 - #define ASUS_ACPI_VERSION "0.29" 45 + #define ASUS_ACPI_VERSION "0.30" 43 46 44 47 #define PROC_ASUS "asus" //the directory 45 48 #define PROC_MLED "mled" 46 49 #define PROC_WLED "wled" 47 50 #define PROC_TLED "tled" 51 + #define PROC_BT "bluetooth" 52 + #define PROC_LEDD "ledd" 48 53 #define PROC_INFO "info" 49 54 #define PROC_LCD "lcd" 50 55 #define PROC_BRN "brn" ··· 66 67 /* 67 68 * Flags for hotk status 68 69 */ 69 - #define MLED_ON 0x01 //is MLED ON ? 70 - #define WLED_ON 0x02 71 - #define TLED_ON 0x04 70 + #define MLED_ON 0x01 //mail LED 71 + #define WLED_ON 0x02 //wireless LED 72 + #define TLED_ON 0x04 //touchpad LED 73 + #define BT_ON 0x08 //internal Bluetooth 72 74 73 75 MODULE_AUTHOR("Julien Lerouge, Karol Kozimor"); 74 76 MODULE_DESCRIPTION(ACPI_HOTK_NAME); ··· 92 92 char *wled_status; //node to handle wled reading_______A 93 93 char *mt_tled; //method to handle tled_____________R 94 94 char *tled_status; //node to handle tled reading_______A 95 - char *mt_lcd_switch; //method to turn LCD ON/OFF_________A 95 + char *mt_ledd; //method to handle LED display______R 96 + char *mt_bt_switch; //method to switch Bluetooth on/off_R 97 + char *bt_status; //no model currently supports this__? 98 + char *mt_lcd_switch; //method to turn LCD on/off_________A 96 99 char *lcd_status; //node to read LCD panel state______A 97 100 char *brightness_up; //method to set brightness up_______A 98 101 char *brightness_down; //guess what ?______________________A ··· 114 111 struct acpi_device *device; //the device we are in 115 112 acpi_handle handle; //the handle of the hotk device 116 113 char status; //status of the hotk, for LEDs, ... 114 + u32 ledd_status; //status of the LED display 117 115 struct model_data *methods; //methods available on the laptop 118 116 u8 brightness; //brightness level 119 117 enum { 120 118 A1x = 0, //A1340D, A1300F 121 119 A2x, //A2500H 120 + A4G, //A4700G 122 121 D1x, //D1 123 122 L2D, //L2000D 124 123 L3C, //L3800C 125 124 L3D, //L3400D 126 - L3H, //L3H, but also L2000E 125 + L3H, //L3H, L2000E, L5D 127 126 L4R, //L4500R 128 127 L5x, //L5800C 129 128 L8L, //L8400L 130 129 M1A, //M1300A 131 130 M2E, //M2400E, L4400L 132 - M6N, //M6800N 133 - M6R, //M6700R 131 + M6N, //M6800N, W3400N 132 + M6R, //M6700R, A3000G 134 133 P30, //Samsung P30 135 134 S1x, //S1300A, but also L1400B and M2400A (L84F) 136 135 S2x, //S200 (J1 reported), Victor MP-XP7210 137 - xxN, //M2400N, M3700N, M5200N, S1300N, S5200N, W1OOON 136 + W1N, //W1000N 137 + W5A, //W5A 138 + xxN, //M2400N, M3700N, M5200N, M6800N, S1300N, S5200N 138 139 //(Centrino) 139 140 END_MODEL 140 141 } model; //Models currently supported ··· 156 149 157 150 static struct model_data model_conf[END_MODEL] = { 158 151 /* 159 - * Those pathnames are relative to the HOTK / ATKD device : 160 - * - mt_mled 161 - * - mt_wled 162 - * - brightness_set 163 - * - brightness_get 164 - * - display_set 165 - * - display_get 166 - * 167 152 * TODO I have seen a SWBX and AIBX method on some models, like L1400B, 168 153 * it seems to be a kind of switch, but what for ? 169 - * 170 154 */ 171 155 172 156 { ··· 180 182 .brightness_get = "GPLV", 181 183 .display_set = "SDSP", 182 184 .display_get = "\\INFB"}, 185 + 186 + { 187 + .name = "A4G", 188 + .mt_mled = "MLED", 189 + /* WLED present, but not controlled by ACPI */ 190 + .mt_lcd_switch = xxN_PREFIX "_Q10", 191 + .brightness_set = "SPLV", 192 + .brightness_get = "GPLV", 193 + .display_set = "SDSP", 194 + .display_get = "\\ADVG"}, 183 195 184 196 { 185 197 .name = "D1x", ··· 310 302 .brightness_set = "SPLV", 311 303 .brightness_get = "GPLV", 312 304 .display_set = "SDSP", 313 - .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"}, 305 + .display_get = "\\SSTE"}, 306 + 314 307 { 315 308 .name = "M6R", 316 309 .mt_mled = "MLED", ··· 321 312 .brightness_set = "SPLV", 322 313 .brightness_get = "GPLV", 323 314 .display_set = "SDSP", 324 - .display_get = "\\SSTE"}, 315 + .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"}, 325 316 326 317 { 327 318 .name = "P30", ··· 352 343 .lcd_status = "\\BKLI", 353 344 .brightness_up = S2x_PREFIX "_Q0B", 354 345 .brightness_down = S2x_PREFIX "_Q0A"}, 346 + 347 + { 348 + .name = "W1N", 349 + .mt_mled = "MLED", 350 + .mt_wled = "WLED", 351 + .mt_ledd = "SLCM", 352 + .mt_lcd_switch = xxN_PREFIX "_Q10", 353 + .lcd_status = "\\BKLT", 354 + .brightness_set = "SPLV", 355 + .brightness_get = "GPLV", 356 + .display_set = "SDSP", 357 + .display_get = "\\ADVG"}, 358 + 359 + { 360 + .name = "W5A", 361 + .mt_bt_switch = "BLED", 362 + .mt_wled = "WLED", 363 + .mt_lcd_switch = xxN_PREFIX "_Q10", 364 + .brightness_set = "SPLV", 365 + .brightness_get = "GPLV", 366 + .display_set = "SDSP", 367 + .display_get = "\\ADVG"}, 355 368 356 369 { 357 370 .name = "xxN", ··· 594 563 } 595 564 596 565 /* 566 + * Proc handlers for LED display 567 + */ 568 + static int 569 + proc_read_ledd(char *page, char **start, off_t off, int count, int *eof, 570 + void *data) 571 + { 572 + return sprintf(page, "0x%08x\n", hotk->ledd_status); 573 + } 574 + 575 + static int 576 + proc_write_ledd(struct file *file, const char __user * buffer, 577 + unsigned long count, void *data) 578 + { 579 + int value; 580 + 581 + count = parse_arg(buffer, count, &value); 582 + if (count > 0) { 583 + if (!write_acpi_int 584 + (hotk->handle, hotk->methods->mt_ledd, value, NULL)) 585 + printk(KERN_WARNING 586 + "Asus ACPI: LED display write failed\n"); 587 + else 588 + hotk->ledd_status = (u32) value; 589 + } else if (count < 0) 590 + printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); 591 + 592 + return count; 593 + } 594 + 595 + /* 597 596 * Proc handlers for WLED 598 597 */ 599 598 static int ··· 639 578 unsigned long count, void *data) 640 579 { 641 580 return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0); 581 + } 582 + 583 + /* 584 + * Proc handlers for Bluetooth 585 + */ 586 + static int 587 + proc_read_bluetooth(char *page, char **start, off_t off, int count, int *eof, 588 + void *data) 589 + { 590 + return sprintf(page, "%d\n", read_led(hotk->methods->bt_status, BT_ON)); 591 + } 592 + 593 + static int 594 + proc_write_bluetooth(struct file *file, const char __user * buffer, 595 + unsigned long count, void *data) 596 + { 597 + /* Note: mt_bt_switch controls both internal Bluetooth adapter's 598 + presence and its LED */ 599 + return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0); 642 600 } 643 601 644 602 /* ··· 956 876 mode, device); 957 877 } 958 878 879 + if (hotk->methods->mt_ledd) { 880 + asus_proc_add(PROC_LEDD, &proc_write_ledd, &proc_read_ledd, 881 + mode, device); 882 + } 883 + 959 884 if (hotk->methods->mt_mled) { 960 885 asus_proc_add(PROC_MLED, &proc_write_mled, &proc_read_mled, 961 886 mode, device); ··· 969 884 if (hotk->methods->mt_tled) { 970 885 asus_proc_add(PROC_TLED, &proc_write_tled, &proc_read_tled, 971 886 mode, device); 887 + } 888 + 889 + if (hotk->methods->mt_bt_switch) { 890 + asus_proc_add(PROC_BT, &proc_write_bluetooth, 891 + &proc_read_bluetooth, mode, device); 972 892 } 973 893 974 894 /* ··· 1009 919 remove_proc_entry(PROC_MLED, acpi_device_dir(device)); 1010 920 if (hotk->methods->mt_tled) 1011 921 remove_proc_entry(PROC_TLED, acpi_device_dir(device)); 922 + if (hotk->methods->mt_ledd) 923 + remove_proc_entry(PROC_LEDD, acpi_device_dir(device)); 924 + if (hotk->methods->mt_bt_switch) 925 + remove_proc_entry(PROC_BT, acpi_device_dir(device)); 1012 926 if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) 1013 927 remove_proc_entry(PROC_LCD, acpi_device_dir(device)); 1014 928 if ((hotk->methods->brightness_up ··· 1045 951 } 1046 952 1047 953 /* 954 + * Match the model string to the list of supported models. Return END_MODEL if 955 + * no match or model is NULL. 956 + */ 957 + static int asus_model_match(char *model) 958 + { 959 + if (model == NULL) 960 + return END_MODEL; 961 + 962 + if (strncmp(model, "L3D", 3) == 0) 963 + return L3D; 964 + else if (strncmp(model, "L2E", 3) == 0 || 965 + strncmp(model, "L3H", 3) == 0 || strncmp(model, "L5D", 3) == 0) 966 + return L3H; 967 + else if (strncmp(model, "L3", 2) == 0 || strncmp(model, "L2B", 3) == 0) 968 + return L3C; 969 + else if (strncmp(model, "L8L", 3) == 0) 970 + return L8L; 971 + else if (strncmp(model, "L4R", 3) == 0) 972 + return L4R; 973 + else if (strncmp(model, "M6N", 3) == 0 || strncmp(model, "W3N", 3) == 0) 974 + return M6N; 975 + else if (strncmp(model, "M6R", 3) == 0 || strncmp(model, "A3G", 3) == 0) 976 + return M6R; 977 + else if (strncmp(model, "M2N", 3) == 0 || 978 + strncmp(model, "M3N", 3) == 0 || 979 + strncmp(model, "M5N", 3) == 0 || 980 + strncmp(model, "M6N", 3) == 0 || 981 + strncmp(model, "S1N", 3) == 0 || 982 + strncmp(model, "S5N", 3) == 0 || strncmp(model, "W1N", 3) == 0) 983 + return xxN; 984 + else if (strncmp(model, "M1", 2) == 0) 985 + return M1A; 986 + else if (strncmp(model, "M2", 2) == 0 || strncmp(model, "L4E", 3) == 0) 987 + return M2E; 988 + else if (strncmp(model, "L2", 2) == 0) 989 + return L2D; 990 + else if (strncmp(model, "L8", 2) == 0) 991 + return S1x; 992 + else if (strncmp(model, "D1", 2) == 0) 993 + return D1x; 994 + else if (strncmp(model, "A1", 2) == 0) 995 + return A1x; 996 + else if (strncmp(model, "A2", 2) == 0) 997 + return A2x; 998 + else if (strncmp(model, "J1", 2) == 0) 999 + return S2x; 1000 + else if (strncmp(model, "L5", 2) == 0) 1001 + return L5x; 1002 + else if (strncmp(model, "A4G", 3) == 0) 1003 + return A4G; 1004 + else if (strncmp(model, "W1N", 3) == 0) 1005 + return W1N; 1006 + else if (strncmp(model, "W5A", 3) == 0) 1007 + return W5A; 1008 + else 1009 + return END_MODEL; 1010 + } 1011 + 1012 + /* 1048 1013 * This function is used to initialize the hotk with right values. In this 1049 1014 * method, we can make all the detection we want, and modify the hotk struct 1050 1015 */ ··· 1113 960 struct acpi_buffer dsdt = { ACPI_ALLOCATE_BUFFER, NULL }; 1114 961 union acpi_object *model = NULL; 1115 962 int bsts_result; 963 + char *string = NULL; 1116 964 acpi_status status; 1117 965 1118 966 /* ··· 1143 989 printk(KERN_NOTICE " BSTS called, 0x%02x returned\n", 1144 990 bsts_result); 1145 991 1146 - /* This is unlikely with implicit return */ 1147 - if (buffer.pointer == NULL) 1148 - return -EINVAL; 1149 - 1150 - model = (union acpi_object *) buffer.pointer; 1151 992 /* 1152 - * Samsung P30 has a device with a valid _HID whose INIT does not 1153 - * return anything. It used to be possible to catch this exception, 1154 - * but the implicit return code will now happily confuse the 1155 - * driver. We assume that every ACPI_TYPE_STRING is a valid model 1156 - * identifier but it's still possible to get completely bogus data. 993 + * Try to match the object returned by INIT to the specific model. 994 + * Handle every possible object (or the lack of thereof) the DSDT 995 + * writers might throw at us. When in trouble, we pass NULL to 996 + * asus_model_match() and try something completely different. 1157 997 */ 1158 - if (model->type == ACPI_TYPE_STRING) { 1159 - printk(KERN_NOTICE " %s model detected, ", model->string.pointer); 1160 - } else { 1161 - if (asus_info && /* Samsung P30 */ 998 + if (buffer.pointer) { 999 + model = (union acpi_object *)buffer.pointer; 1000 + switch (model->type) { 1001 + case ACPI_TYPE_STRING: 1002 + string = model->string.pointer; 1003 + break; 1004 + case ACPI_TYPE_BUFFER: 1005 + string = model->buffer.pointer; 1006 + break; 1007 + default: 1008 + kfree(model); 1009 + break; 1010 + } 1011 + } 1012 + hotk->model = asus_model_match(string); 1013 + if (hotk->model == END_MODEL) { /* match failed */ 1014 + if (asus_info && 1162 1015 strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) { 1163 1016 hotk->model = P30; 1164 1017 printk(KERN_NOTICE 1165 1018 " Samsung P30 detected, supported\n"); 1166 1019 } else { 1167 1020 hotk->model = M2E; 1168 - printk(KERN_WARNING " no string returned by INIT\n"); 1169 - printk(KERN_WARNING " trying default values, supply " 1170 - "the developers with your DSDT\n"); 1021 + printk(KERN_NOTICE " unsupported model %s, trying " 1022 + "default values\n", string); 1023 + printk(KERN_NOTICE 1024 + " send /proc/acpi/dsdt to the developers\n"); 1171 1025 } 1172 1026 hotk->methods = &model_conf[hotk->model]; 1173 - 1174 - kfree(model); 1175 - 1176 1027 return AE_OK; 1177 1028 } 1178 - 1179 - hotk->model = END_MODEL; 1180 - if (strncmp(model->string.pointer, "L3D", 3) == 0) 1181 - hotk->model = L3D; 1182 - else if (strncmp(model->string.pointer, "L3H", 3) == 0 || 1183 - strncmp(model->string.pointer, "L2E", 3) == 0) 1184 - hotk->model = L3H; 1185 - else if (strncmp(model->string.pointer, "L3", 2) == 0 || 1186 - strncmp(model->string.pointer, "L2B", 3) == 0) 1187 - hotk->model = L3C; 1188 - else if (strncmp(model->string.pointer, "L8L", 3) == 0) 1189 - hotk->model = L8L; 1190 - else if (strncmp(model->string.pointer, "L4R", 3) == 0) 1191 - hotk->model = L4R; 1192 - else if (strncmp(model->string.pointer, "M6N", 3) == 0) 1193 - hotk->model = M6N; 1194 - else if (strncmp(model->string.pointer, "M6R", 3) == 0) 1195 - hotk->model = M6R; 1196 - else if (strncmp(model->string.pointer, "M2N", 3) == 0 || 1197 - strncmp(model->string.pointer, "M3N", 3) == 0 || 1198 - strncmp(model->string.pointer, "M5N", 3) == 0 || 1199 - strncmp(model->string.pointer, "M6N", 3) == 0 || 1200 - strncmp(model->string.pointer, "S1N", 3) == 0 || 1201 - strncmp(model->string.pointer, "S5N", 3) == 0 || 1202 - strncmp(model->string.pointer, "W1N", 3) == 0) 1203 - hotk->model = xxN; 1204 - else if (strncmp(model->string.pointer, "M1", 2) == 0) 1205 - hotk->model = M1A; 1206 - else if (strncmp(model->string.pointer, "M2", 2) == 0 || 1207 - strncmp(model->string.pointer, "L4E", 3) == 0) 1208 - hotk->model = M2E; 1209 - else if (strncmp(model->string.pointer, "L2", 2) == 0) 1210 - hotk->model = L2D; 1211 - else if (strncmp(model->string.pointer, "L8", 2) == 0) 1212 - hotk->model = S1x; 1213 - else if (strncmp(model->string.pointer, "D1", 2) == 0) 1214 - hotk->model = D1x; 1215 - else if (strncmp(model->string.pointer, "A1", 2) == 0) 1216 - hotk->model = A1x; 1217 - else if (strncmp(model->string.pointer, "A2", 2) == 0) 1218 - hotk->model = A2x; 1219 - else if (strncmp(model->string.pointer, "J1", 2) == 0) 1220 - hotk->model = S2x; 1221 - else if (strncmp(model->string.pointer, "L5", 2) == 0) 1222 - hotk->model = L5x; 1223 - 1224 - if (hotk->model == END_MODEL) { 1225 - printk("unsupported, trying default values, supply the " 1226 - "developers with your DSDT\n"); 1227 - hotk->model = M2E; 1228 - } else { 1229 - printk("supported\n"); 1230 - } 1231 - 1232 1029 hotk->methods = &model_conf[hotk->model]; 1030 + printk(KERN_NOTICE " %s model detected, supported\n", string); 1233 1031 1234 1032 /* Sort of per-model blacklist */ 1235 - if (strncmp(model->string.pointer, "L2B", 3) == 0) 1033 + if (strncmp(string, "L2B", 3) == 0) 1236 1034 hotk->methods->lcd_status = NULL; 1237 1035 /* L2B is similar enough to L3C to use its settings, with this only 1238 1036 exception */ 1239 - else if (strncmp(model->string.pointer, "S5N", 3) == 0 || 1240 - strncmp(model->string.pointer, "M5N", 3) == 0) 1037 + else if (strncmp(string, "A3G", 3) == 0) 1038 + hotk->methods->lcd_status = "\\BLFG"; 1039 + /* A3G is like M6R */ 1040 + else if (strncmp(string, "S5N", 3) == 0 || 1041 + strncmp(string, "M5N", 3) == 0 || 1042 + strncmp(string, "W3N", 3) == 0) 1241 1043 hotk->methods->mt_mled = NULL; 1242 - /* S5N and M5N have no MLED */ 1243 - else if (strncmp(model->string.pointer, "M2N", 3) == 0 || 1244 - strncmp(model->string.pointer, "W1N", 3) == 0) 1044 + /* S5N, M5N and W3N have no MLED */ 1045 + else if (strncmp(string, "L5D", 3) == 0) 1046 + hotk->methods->mt_wled = NULL; 1047 + /* L5D's WLED is not controlled by ACPI */ 1048 + else if (strncmp(string, "M2N", 3) == 0 || 1049 + strncmp(string, "S1N", 3) == 0) 1245 1050 hotk->methods->mt_wled = "WLED"; 1246 - /* M2N and W1N have a usable WLED */ 1051 + /* M2N and S1N have a usable WLED */ 1247 1052 else if (asus_info) { 1248 1053 if (strncmp(asus_info->oem_table_id, "L1", 2) == 0) 1249 1054 hotk->methods->mled_status = NULL; ··· 1277 1164 /* For laptops without GPLV: init the hotk->brightness value */ 1278 1165 if ((!hotk->methods->brightness_get) 1279 1166 && (!hotk->methods->brightness_status) 1280 - && (hotk->methods->brightness_up 1281 - && hotk->methods->brightness_down)) { 1167 + && (hotk->methods->brightness_up && hotk->methods->brightness_down)) { 1282 1168 status = 1283 1169 acpi_evaluate_object(NULL, hotk->methods->brightness_down, 1284 1170 NULL, NULL); ··· 1295 1183 } 1296 1184 1297 1185 asus_hotk_found = 1; 1186 + 1187 + /* LED display is off by default */ 1188 + hotk->ledd_status = 0xFFF; 1298 1189 1299 1190 end: 1300 1191 if (result) {