acer-wmi.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  1. /*
  2. * Acer WMI Laptop Extras
  3. *
  4. * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
  5. *
  6. * Based on acer_acpi:
  7. * Copyright (C) 2005-2007 E.M. Smith
  8. * Copyright (C) 2007-2008 Carlos Corbacho <cathectic@gmail.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  23. */
  24. #define ACER_WMI_VERSION "0.1"
  25. #include <linux/kernel.h>
  26. #include <linux/module.h>
  27. #include <linux/init.h>
  28. #include <linux/types.h>
  29. #include <linux/dmi.h>
  30. #include <linux/fb.h>
  31. #include <linux/backlight.h>
  32. #include <linux/leds.h>
  33. #include <linux/platform_device.h>
  34. #include <linux/acpi.h>
  35. #include <linux/i8042.h>
  36. #include <acpi/acpi_drivers.h>
  37. MODULE_AUTHOR("Carlos Corbacho");
  38. MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
  39. MODULE_LICENSE("GPL");
  40. #define ACER_LOGPREFIX "acer-wmi: "
  41. #define ACER_ERR KERN_ERR ACER_LOGPREFIX
  42. #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
  43. #define ACER_INFO KERN_INFO ACER_LOGPREFIX
  44. /*
  45. * The following defines quirks to get some specific functions to work
  46. * which are known to not be supported over ACPI-WMI (such as the mail LED
  47. * on WMID based Acer's)
  48. */
  49. struct acer_quirks {
  50. const char *vendor;
  51. const char *model;
  52. u16 quirks;
  53. };
  54. /*
  55. * Magic Number
  56. * Meaning is unknown - this number is required for writing to ACPI for AMW0
  57. * (it's also used in acerhk when directly accessing the BIOS)
  58. */
  59. #define ACER_AMW0_WRITE 0x9610
  60. /*
  61. * Bit masks for the AMW0 interface
  62. */
  63. #define ACER_AMW0_WIRELESS_MASK 0x35
  64. #define ACER_AMW0_BLUETOOTH_MASK 0x34
  65. #define ACER_AMW0_MAILLED_MASK 0x31
  66. /*
  67. * Method IDs for WMID interface
  68. */
  69. #define ACER_WMID_GET_WIRELESS_METHODID 1
  70. #define ACER_WMID_GET_BLUETOOTH_METHODID 2
  71. #define ACER_WMID_GET_BRIGHTNESS_METHODID 3
  72. #define ACER_WMID_SET_WIRELESS_METHODID 4
  73. #define ACER_WMID_SET_BLUETOOTH_METHODID 5
  74. #define ACER_WMID_SET_BRIGHTNESS_METHODID 6
  75. #define ACER_WMID_GET_THREEG_METHODID 10
  76. #define ACER_WMID_SET_THREEG_METHODID 11
  77. /*
  78. * Acer ACPI method GUIDs
  79. */
  80. #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
  81. #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
  82. #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A"
  83. MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
  84. MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
  85. /* Temporary workaround until the WMI sysfs interface goes in */
  86. MODULE_ALIAS("dmi:*:*Acer*:*:");
  87. /*
  88. * Interface capability flags
  89. */
  90. #define ACER_CAP_MAILLED (1<<0)
  91. #define ACER_CAP_WIRELESS (1<<1)
  92. #define ACER_CAP_BLUETOOTH (1<<2)
  93. #define ACER_CAP_BRIGHTNESS (1<<3)
  94. #define ACER_CAP_THREEG (1<<4)
  95. #define ACER_CAP_ANY (0xFFFFFFFF)
  96. /*
  97. * Interface type flags
  98. */
  99. enum interface_flags {
  100. ACER_AMW0,
  101. ACER_AMW0_V2,
  102. ACER_WMID,
  103. };
  104. #define ACER_DEFAULT_WIRELESS 0
  105. #define ACER_DEFAULT_BLUETOOTH 0
  106. #define ACER_DEFAULT_MAILLED 0
  107. #define ACER_DEFAULT_THREEG 0
  108. static int max_brightness = 0xF;
  109. static int wireless = -1;
  110. static int bluetooth = -1;
  111. static int mailled = -1;
  112. static int brightness = -1;
  113. static int threeg = -1;
  114. static int force_series;
  115. module_param(mailled, int, 0444);
  116. module_param(wireless, int, 0444);
  117. module_param(bluetooth, int, 0444);
  118. module_param(brightness, int, 0444);
  119. module_param(threeg, int, 0444);
  120. module_param(force_series, int, 0444);
  121. MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware");
  122. MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware");
  123. MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
  124. MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
  125. MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
  126. MODULE_PARM_DESC(force_series, "Force a different laptop series");
  127. struct acer_data {
  128. int mailled;
  129. int wireless;
  130. int bluetooth;
  131. int threeg;
  132. int brightness;
  133. };
  134. /* Each low-level interface must define at least some of the following */
  135. struct wmi_interface {
  136. /* The WMI device type */
  137. u32 type;
  138. /* The capabilities this interface provides */
  139. u32 capability;
  140. /* Private data for the current interface */
  141. struct acer_data data;
  142. };
  143. /* The static interface pointer, points to the currently detected interface */
  144. static struct wmi_interface *interface;
  145. /*
  146. * Embedded Controller quirks
  147. * Some laptops require us to directly access the EC to either enable or query
  148. * features that are not available through WMI.
  149. */
  150. struct quirk_entry {
  151. u8 wireless;
  152. u8 mailled;
  153. s8 brightness;
  154. u8 bluetooth;
  155. };
  156. static struct quirk_entry *quirks;
  157. static void set_quirks(void)
  158. {
  159. if (quirks->mailled)
  160. interface->capability |= ACER_CAP_MAILLED;
  161. if (quirks->brightness)
  162. interface->capability |= ACER_CAP_BRIGHTNESS;
  163. }
  164. static int dmi_matched(const struct dmi_system_id *dmi)
  165. {
  166. quirks = dmi->driver_data;
  167. return 0;
  168. }
  169. static struct quirk_entry quirk_unknown = {
  170. };
  171. static struct quirk_entry quirk_acer_aspire_1520 = {
  172. .brightness = -1,
  173. };
  174. static struct quirk_entry quirk_acer_travelmate_2490 = {
  175. .mailled = 1,
  176. };
  177. /* This AMW0 laptop has no bluetooth */
  178. static struct quirk_entry quirk_medion_md_98300 = {
  179. .wireless = 1,
  180. };
  181. static struct dmi_system_id acer_quirks[] = {
  182. {
  183. .callback = dmi_matched,
  184. .ident = "Acer Aspire 1360",
  185. .matches = {
  186. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  187. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
  188. },
  189. .driver_data = &quirk_acer_aspire_1520,
  190. },
  191. {
  192. .callback = dmi_matched,
  193. .ident = "Acer Aspire 1520",
  194. .matches = {
  195. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  196. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
  197. },
  198. .driver_data = &quirk_acer_aspire_1520,
  199. },
  200. {
  201. .callback = dmi_matched,
  202. .ident = "Acer Aspire 3100",
  203. .matches = {
  204. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  205. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
  206. },
  207. .driver_data = &quirk_acer_travelmate_2490,
  208. },
  209. {
  210. .callback = dmi_matched,
  211. .ident = "Acer Aspire 3610",
  212. .matches = {
  213. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  214. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
  215. },
  216. .driver_data = &quirk_acer_travelmate_2490,
  217. },
  218. {
  219. .callback = dmi_matched,
  220. .ident = "Acer Aspire 5100",
  221. .matches = {
  222. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  223. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
  224. },
  225. .driver_data = &quirk_acer_travelmate_2490,
  226. },
  227. {
  228. .callback = dmi_matched,
  229. .ident = "Acer Aspire 5610",
  230. .matches = {
  231. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  232. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
  233. },
  234. .driver_data = &quirk_acer_travelmate_2490,
  235. },
  236. {
  237. .callback = dmi_matched,
  238. .ident = "Acer Aspire 5630",
  239. .matches = {
  240. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  241. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
  242. },
  243. .driver_data = &quirk_acer_travelmate_2490,
  244. },
  245. {
  246. .callback = dmi_matched,
  247. .ident = "Acer Aspire 5650",
  248. .matches = {
  249. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  250. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
  251. },
  252. .driver_data = &quirk_acer_travelmate_2490,
  253. },
  254. {
  255. .callback = dmi_matched,
  256. .ident = "Acer Aspire 5680",
  257. .matches = {
  258. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  259. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
  260. },
  261. .driver_data = &quirk_acer_travelmate_2490,
  262. },
  263. {
  264. .callback = dmi_matched,
  265. .ident = "Acer Aspire 9110",
  266. .matches = {
  267. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  268. DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
  269. },
  270. .driver_data = &quirk_acer_travelmate_2490,
  271. },
  272. {
  273. .callback = dmi_matched,
  274. .ident = "Acer TravelMate 2490",
  275. .matches = {
  276. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  277. DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
  278. },
  279. .driver_data = &quirk_acer_travelmate_2490,
  280. },
  281. {
  282. .callback = dmi_matched,
  283. .ident = "Acer TravelMate 4200",
  284. .matches = {
  285. DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
  286. DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
  287. },
  288. .driver_data = &quirk_acer_travelmate_2490,
  289. },
  290. {
  291. .callback = dmi_matched,
  292. .ident = "Medion MD 98300",
  293. .matches = {
  294. DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
  295. DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
  296. },
  297. .driver_data = &quirk_medion_md_98300,
  298. },
  299. {}
  300. };
  301. /* Find which quirks are needed for a particular vendor/ model pair */
  302. static void find_quirks(void)
  303. {
  304. if (!force_series) {
  305. dmi_check_system(acer_quirks);
  306. } else if (force_series == 2490) {
  307. quirks = &quirk_acer_travelmate_2490;
  308. }
  309. if (quirks == NULL)
  310. quirks = &quirk_unknown;
  311. set_quirks();
  312. }
  313. /*
  314. * General interface convenience methods
  315. */
  316. static bool has_cap(u32 cap)
  317. {
  318. if ((interface->capability & cap) != 0)
  319. return 1;
  320. return 0;
  321. }
  322. /*
  323. * AMW0 (V1) interface
  324. */
  325. struct wmab_args {
  326. u32 eax;
  327. u32 ebx;
  328. u32 ecx;
  329. u32 edx;
  330. };
  331. struct wmab_ret {
  332. u32 eax;
  333. u32 ebx;
  334. u32 ecx;
  335. u32 edx;
  336. u32 eex;
  337. };
  338. static acpi_status wmab_execute(struct wmab_args *regbuf,
  339. struct acpi_buffer *result)
  340. {
  341. struct acpi_buffer input;
  342. acpi_status status;
  343. input.length = sizeof(struct wmab_args);
  344. input.pointer = (u8 *)regbuf;
  345. status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
  346. return status;
  347. }
  348. static acpi_status AMW0_get_u32(u32 *value, u32 cap,
  349. struct wmi_interface *iface)
  350. {
  351. int err;
  352. u8 result;
  353. switch (cap) {
  354. case ACER_CAP_MAILLED:
  355. switch (quirks->mailled) {
  356. default:
  357. err = ec_read(0xA, &result);
  358. if (err)
  359. return AE_ERROR;
  360. *value = (result >> 7) & 0x1;
  361. return AE_OK;
  362. }
  363. break;
  364. case ACER_CAP_WIRELESS:
  365. switch (quirks->wireless) {
  366. case 1:
  367. err = ec_read(0x7B, &result);
  368. if (err)
  369. return AE_ERROR;
  370. *value = result & 0x1;
  371. return AE_OK;
  372. default:
  373. err = ec_read(0xA, &result);
  374. if (err)
  375. return AE_ERROR;
  376. *value = (result >> 2) & 0x1;
  377. return AE_OK;
  378. }
  379. break;
  380. case ACER_CAP_BLUETOOTH:
  381. switch (quirks->bluetooth) {
  382. default:
  383. err = ec_read(0xA, &result);
  384. if (err)
  385. return AE_ERROR;
  386. *value = (result >> 4) & 0x1;
  387. return AE_OK;
  388. }
  389. break;
  390. case ACER_CAP_BRIGHTNESS:
  391. switch (quirks->brightness) {
  392. default:
  393. err = ec_read(0x83, &result);
  394. if (err)
  395. return AE_ERROR;
  396. *value = result;
  397. return AE_OK;
  398. }
  399. break;
  400. default:
  401. return AE_BAD_ADDRESS;
  402. }
  403. return AE_OK;
  404. }
  405. static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
  406. {
  407. struct wmab_args args;
  408. args.eax = ACER_AMW0_WRITE;
  409. args.ebx = value ? (1<<8) : 0;
  410. args.ecx = args.edx = 0;
  411. switch (cap) {
  412. case ACER_CAP_MAILLED:
  413. if (value > 1)
  414. return AE_BAD_PARAMETER;
  415. args.ebx |= ACER_AMW0_MAILLED_MASK;
  416. break;
  417. case ACER_CAP_WIRELESS:
  418. if (value > 1)
  419. return AE_BAD_PARAMETER;
  420. args.ebx |= ACER_AMW0_WIRELESS_MASK;
  421. break;
  422. case ACER_CAP_BLUETOOTH:
  423. if (value > 1)
  424. return AE_BAD_PARAMETER;
  425. args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
  426. break;
  427. case ACER_CAP_BRIGHTNESS:
  428. if (value > max_brightness)
  429. return AE_BAD_PARAMETER;
  430. switch (quirks->brightness) {
  431. default:
  432. return ec_write(0x83, value);
  433. break;
  434. }
  435. default:
  436. return AE_BAD_ADDRESS;
  437. }
  438. /* Actually do the set */
  439. return wmab_execute(&args, NULL);
  440. }
  441. static acpi_status AMW0_find_mailled(void)
  442. {
  443. struct wmab_args args;
  444. struct wmab_ret ret;
  445. acpi_status status = AE_OK;
  446. struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
  447. union acpi_object *obj;
  448. args.eax = 0x86;
  449. args.ebx = args.ecx = args.edx = 0;
  450. status = wmab_execute(&args, &out);
  451. if (ACPI_FAILURE(status))
  452. return status;
  453. obj = (union acpi_object *) out.pointer;
  454. if (obj && obj->type == ACPI_TYPE_BUFFER &&
  455. obj->buffer.length == sizeof(struct wmab_ret)) {
  456. ret = *((struct wmab_ret *) obj->buffer.pointer);
  457. } else {
  458. return AE_ERROR;
  459. }
  460. if (ret.eex & 0x1)
  461. interface->capability |= ACER_CAP_MAILLED;
  462. kfree(out.pointer);
  463. return AE_OK;
  464. }
  465. static acpi_status AMW0_set_capabilities(void)
  466. {
  467. struct wmab_args args;
  468. struct wmab_ret ret;
  469. acpi_status status = AE_OK;
  470. struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
  471. union acpi_object *obj;
  472. args.eax = ACER_AMW0_WRITE;
  473. args.ecx = args.edx = 0;
  474. args.ebx = 0xa2 << 8;
  475. args.ebx |= ACER_AMW0_WIRELESS_MASK;
  476. status = wmab_execute(&args, &out);
  477. if (ACPI_FAILURE(status))
  478. return status;
  479. obj = (union acpi_object *) out.pointer;
  480. if (obj && obj->type == ACPI_TYPE_BUFFER &&
  481. obj->buffer.length == sizeof(struct wmab_ret)) {
  482. ret = *((struct wmab_ret *) obj->buffer.pointer);
  483. } else {
  484. return AE_ERROR;
  485. }
  486. if (ret.eax & 0x1)
  487. interface->capability |= ACER_CAP_WIRELESS;
  488. args.ebx = 2 << 8;
  489. args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
  490. status = wmab_execute(&args, &out);
  491. if (ACPI_FAILURE(status))
  492. return status;
  493. obj = (union acpi_object *) out.pointer;
  494. if (obj && obj->type == ACPI_TYPE_BUFFER
  495. && obj->buffer.length == sizeof(struct wmab_ret)) {
  496. ret = *((struct wmab_ret *) obj->buffer.pointer);
  497. } else {
  498. return AE_ERROR;
  499. }
  500. if (ret.eax & 0x1)
  501. interface->capability |= ACER_CAP_BLUETOOTH;
  502. kfree(out.pointer);
  503. /*
  504. * This appears to be safe to enable, since all Wistron based laptops
  505. * appear to use the same EC register for brightness, even if they
  506. * differ for wireless, etc
  507. */
  508. if (quirks->brightness >= 0)
  509. interface->capability |= ACER_CAP_BRIGHTNESS;
  510. return AE_OK;
  511. }
  512. static struct wmi_interface AMW0_interface = {
  513. .type = ACER_AMW0,
  514. };
  515. static struct wmi_interface AMW0_V2_interface = {
  516. .type = ACER_AMW0_V2,
  517. };
  518. /*
  519. * New interface (The WMID interface)
  520. */
  521. static acpi_status
  522. WMI_execute_u32(u32 method_id, u32 in, u32 *out)
  523. {
  524. struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
  525. struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
  526. union acpi_object *obj;
  527. u32 tmp;
  528. acpi_status status;
  529. status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
  530. if (ACPI_FAILURE(status))
  531. return status;
  532. obj = (union acpi_object *) result.pointer;
  533. if (obj && obj->type == ACPI_TYPE_BUFFER &&
  534. obj->buffer.length == sizeof(u32)) {
  535. tmp = *((u32 *) obj->buffer.pointer);
  536. } else {
  537. tmp = 0;
  538. }
  539. if (out)
  540. *out = tmp;
  541. kfree(result.pointer);
  542. return status;
  543. }
  544. static acpi_status WMID_get_u32(u32 *value, u32 cap,
  545. struct wmi_interface *iface)
  546. {
  547. acpi_status status;
  548. u8 tmp;
  549. u32 result, method_id = 0;
  550. switch (cap) {
  551. case ACER_CAP_WIRELESS:
  552. method_id = ACER_WMID_GET_WIRELESS_METHODID;
  553. break;
  554. case ACER_CAP_BLUETOOTH:
  555. method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
  556. break;
  557. case ACER_CAP_BRIGHTNESS:
  558. method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
  559. break;
  560. case ACER_CAP_THREEG:
  561. method_id = ACER_WMID_GET_THREEG_METHODID;
  562. break;
  563. case ACER_CAP_MAILLED:
  564. if (quirks->mailled == 1) {
  565. ec_read(0x9f, &tmp);
  566. *value = tmp & 0x1;
  567. return 0;
  568. }
  569. default:
  570. return AE_BAD_ADDRESS;
  571. }
  572. status = WMI_execute_u32(method_id, 0, &result);
  573. if (ACPI_SUCCESS(status))
  574. *value = (u8)result;
  575. return status;
  576. }
  577. static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
  578. {
  579. u32 method_id = 0;
  580. char param;
  581. switch (cap) {
  582. case ACER_CAP_BRIGHTNESS:
  583. if (value > max_brightness)
  584. return AE_BAD_PARAMETER;
  585. method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
  586. break;
  587. case ACER_CAP_WIRELESS:
  588. if (value > 1)
  589. return AE_BAD_PARAMETER;
  590. method_id = ACER_WMID_SET_WIRELESS_METHODID;
  591. break;
  592. case ACER_CAP_BLUETOOTH:
  593. if (value > 1)
  594. return AE_BAD_PARAMETER;
  595. method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
  596. break;
  597. case ACER_CAP_THREEG:
  598. if (value > 1)
  599. return AE_BAD_PARAMETER;
  600. method_id = ACER_WMID_SET_THREEG_METHODID;
  601. break;
  602. case ACER_CAP_MAILLED:
  603. if (value > 1)
  604. return AE_BAD_PARAMETER;
  605. if (quirks->mailled == 1) {
  606. param = value ? 0x92 : 0x93;
  607. i8042_command(&param, 0x1059);
  608. return 0;
  609. }
  610. break;
  611. default:
  612. return AE_BAD_ADDRESS;
  613. }
  614. return WMI_execute_u32(method_id, (u32)value, NULL);
  615. }
  616. static acpi_status WMID_set_capabilities(void)
  617. {
  618. struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
  619. union acpi_object *obj;
  620. acpi_status status;
  621. u32 devices;
  622. status = wmi_query_block(WMID_GUID2, 1, &out);
  623. if (ACPI_FAILURE(status))
  624. return status;
  625. obj = (union acpi_object *) out.pointer;
  626. if (obj && obj->type == ACPI_TYPE_BUFFER &&
  627. obj->buffer.length == sizeof(u32)) {
  628. devices = *((u32 *) obj->buffer.pointer);
  629. } else {
  630. return AE_ERROR;
  631. }
  632. /* Not sure on the meaning of the relevant bits yet to detect these */
  633. interface->capability |= ACER_CAP_WIRELESS;
  634. interface->capability |= ACER_CAP_THREEG;
  635. /* WMID always provides brightness methods */
  636. interface->capability |= ACER_CAP_BRIGHTNESS;
  637. if (devices & 0x10)
  638. interface->capability |= ACER_CAP_BLUETOOTH;
  639. if (!(devices & 0x20))
  640. max_brightness = 0x9;
  641. return status;
  642. }
  643. static struct wmi_interface wmid_interface = {
  644. .type = ACER_WMID,
  645. };
  646. /*
  647. * Generic Device (interface-independent)
  648. */
  649. static acpi_status get_u32(u32 *value, u32 cap)
  650. {
  651. acpi_status status = AE_BAD_ADDRESS;
  652. switch (interface->type) {
  653. case ACER_AMW0:
  654. status = AMW0_get_u32(value, cap, interface);
  655. break;
  656. case ACER_AMW0_V2:
  657. if (cap == ACER_CAP_MAILLED) {
  658. status = AMW0_get_u32(value, cap, interface);
  659. break;
  660. }
  661. case ACER_WMID:
  662. status = WMID_get_u32(value, cap, interface);
  663. break;
  664. }
  665. return status;
  666. }
  667. static acpi_status set_u32(u32 value, u32 cap)
  668. {
  669. if (interface->capability & cap) {
  670. switch (interface->type) {
  671. case ACER_AMW0:
  672. return AMW0_set_u32(value, cap, interface);
  673. case ACER_AMW0_V2:
  674. case ACER_WMID:
  675. return WMID_set_u32(value, cap, interface);
  676. default:
  677. return AE_BAD_PARAMETER;
  678. }
  679. }
  680. return AE_BAD_PARAMETER;
  681. }
  682. static void __init acer_commandline_init(void)
  683. {
  684. /*
  685. * These will all fail silently if the value given is invalid, or the
  686. * capability isn't available on the given interface
  687. */
  688. set_u32(mailled, ACER_CAP_MAILLED);
  689. set_u32(wireless, ACER_CAP_WIRELESS);
  690. set_u32(bluetooth, ACER_CAP_BLUETOOTH);
  691. set_u32(threeg, ACER_CAP_THREEG);
  692. set_u32(brightness, ACER_CAP_BRIGHTNESS);
  693. }
  694. /*
  695. * LED device (Mail LED only, no other LEDs known yet)
  696. */
  697. static void mail_led_set(struct led_classdev *led_cdev,
  698. enum led_brightness value)
  699. {
  700. set_u32(value, ACER_CAP_MAILLED);
  701. }
  702. static struct led_classdev mail_led = {
  703. .name = "acer-wmi::mail",
  704. .brightness_set = mail_led_set,
  705. };
  706. static int __devinit acer_led_init(struct device *dev)
  707. {
  708. return led_classdev_register(dev, &mail_led);
  709. }
  710. static void acer_led_exit(void)
  711. {
  712. led_classdev_unregister(&mail_led);
  713. }
  714. /*
  715. * Backlight device
  716. */
  717. static struct backlight_device *acer_backlight_device;
  718. static int read_brightness(struct backlight_device *bd)
  719. {
  720. u32 value;
  721. get_u32(&value, ACER_CAP_BRIGHTNESS);
  722. return value;
  723. }
  724. static int update_bl_status(struct backlight_device *bd)
  725. {
  726. int intensity = bd->props.brightness;
  727. if (bd->props.power != FB_BLANK_UNBLANK)
  728. intensity = 0;
  729. if (bd->props.fb_blank != FB_BLANK_UNBLANK)
  730. intensity = 0;
  731. set_u32(intensity, ACER_CAP_BRIGHTNESS);
  732. return 0;
  733. }
  734. static struct backlight_ops acer_bl_ops = {
  735. .get_brightness = read_brightness,
  736. .update_status = update_bl_status,
  737. };
  738. static int __devinit acer_backlight_init(struct device *dev)
  739. {
  740. struct backlight_device *bd;
  741. bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
  742. if (IS_ERR(bd)) {
  743. printk(ACER_ERR "Could not register Acer backlight device\n");
  744. acer_backlight_device = NULL;
  745. return PTR_ERR(bd);
  746. }
  747. acer_backlight_device = bd;
  748. bd->props.power = FB_BLANK_UNBLANK;
  749. bd->props.brightness = max_brightness;
  750. bd->props.max_brightness = max_brightness;
  751. backlight_update_status(bd);
  752. return 0;
  753. }
  754. static void acer_backlight_exit(void)
  755. {
  756. backlight_device_unregister(acer_backlight_device);
  757. }
  758. /*
  759. * Read/ write bool sysfs macro
  760. */
  761. #define show_set_bool(value, cap) \
  762. static ssize_t \
  763. show_bool_##value(struct device *dev, struct device_attribute *attr, \
  764. char *buf) \
  765. { \
  766. u32 result; \
  767. acpi_status status = get_u32(&result, cap); \
  768. if (ACPI_SUCCESS(status)) \
  769. return sprintf(buf, "%u\n", result); \
  770. return sprintf(buf, "Read error\n"); \
  771. } \
  772. \
  773. static ssize_t \
  774. set_bool_##value(struct device *dev, struct device_attribute *attr, \
  775. const char *buf, size_t count) \
  776. { \
  777. u32 tmp = simple_strtoul(buf, NULL, 10); \
  778. acpi_status status = set_u32(tmp, cap); \
  779. if (ACPI_FAILURE(status)) \
  780. return -EINVAL; \
  781. return count; \
  782. } \
  783. static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
  784. show_bool_##value, set_bool_##value);
  785. show_set_bool(wireless, ACER_CAP_WIRELESS);
  786. show_set_bool(bluetooth, ACER_CAP_BLUETOOTH);
  787. show_set_bool(threeg, ACER_CAP_THREEG);
  788. /*
  789. * Read interface sysfs macro
  790. */
  791. static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
  792. char *buf)
  793. {
  794. switch (interface->type) {
  795. case ACER_AMW0:
  796. return sprintf(buf, "AMW0\n");
  797. case ACER_AMW0_V2:
  798. return sprintf(buf, "AMW0 v2\n");
  799. case ACER_WMID:
  800. return sprintf(buf, "WMID\n");
  801. default:
  802. return sprintf(buf, "Error!\n");
  803. }
  804. }
  805. static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
  806. show_interface, NULL);
  807. /*
  808. * Platform device
  809. */
  810. static int __devinit acer_platform_probe(struct platform_device *device)
  811. {
  812. int err;
  813. if (has_cap(ACER_CAP_MAILLED)) {
  814. err = acer_led_init(&device->dev);
  815. if (err)
  816. goto error_mailled;
  817. }
  818. if (has_cap(ACER_CAP_BRIGHTNESS)) {
  819. err = acer_backlight_init(&device->dev);
  820. if (err)
  821. goto error_brightness;
  822. }
  823. return 0;
  824. error_brightness:
  825. acer_led_exit();
  826. error_mailled:
  827. return err;
  828. }
  829. static int acer_platform_remove(struct platform_device *device)
  830. {
  831. if (has_cap(ACER_CAP_MAILLED))
  832. acer_led_exit();
  833. if (has_cap(ACER_CAP_BRIGHTNESS))
  834. acer_backlight_exit();
  835. return 0;
  836. }
  837. static int acer_platform_suspend(struct platform_device *dev,
  838. pm_message_t state)
  839. {
  840. u32 value;
  841. struct acer_data *data = &interface->data;
  842. if (!data)
  843. return -ENOMEM;
  844. if (has_cap(ACER_CAP_WIRELESS)) {
  845. get_u32(&value, ACER_CAP_WIRELESS);
  846. data->wireless = value;
  847. }
  848. if (has_cap(ACER_CAP_BLUETOOTH)) {
  849. get_u32(&value, ACER_CAP_BLUETOOTH);
  850. data->bluetooth = value;
  851. }
  852. if (has_cap(ACER_CAP_MAILLED)) {
  853. get_u32(&value, ACER_CAP_MAILLED);
  854. data->mailled = value;
  855. }
  856. if (has_cap(ACER_CAP_BRIGHTNESS)) {
  857. get_u32(&value, ACER_CAP_BRIGHTNESS);
  858. data->brightness = value;
  859. }
  860. return 0;
  861. }
  862. static int acer_platform_resume(struct platform_device *device)
  863. {
  864. struct acer_data *data = &interface->data;
  865. if (!data)
  866. return -ENOMEM;
  867. if (has_cap(ACER_CAP_WIRELESS))
  868. set_u32(data->wireless, ACER_CAP_WIRELESS);
  869. if (has_cap(ACER_CAP_BLUETOOTH))
  870. set_u32(data->bluetooth, ACER_CAP_BLUETOOTH);
  871. if (has_cap(ACER_CAP_THREEG))
  872. set_u32(data->threeg, ACER_CAP_THREEG);
  873. if (has_cap(ACER_CAP_MAILLED))
  874. set_u32(data->mailled, ACER_CAP_MAILLED);
  875. if (has_cap(ACER_CAP_BRIGHTNESS))
  876. set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
  877. return 0;
  878. }
  879. static struct platform_driver acer_platform_driver = {
  880. .driver = {
  881. .name = "acer-wmi",
  882. .owner = THIS_MODULE,
  883. },
  884. .probe = acer_platform_probe,
  885. .remove = acer_platform_remove,
  886. .suspend = acer_platform_suspend,
  887. .resume = acer_platform_resume,
  888. };
  889. static struct platform_device *acer_platform_device;
  890. static int remove_sysfs(struct platform_device *device)
  891. {
  892. if (has_cap(ACER_CAP_WIRELESS))
  893. device_remove_file(&device->dev, &dev_attr_wireless);
  894. if (has_cap(ACER_CAP_BLUETOOTH))
  895. device_remove_file(&device->dev, &dev_attr_bluetooth);
  896. if (has_cap(ACER_CAP_THREEG))
  897. device_remove_file(&device->dev, &dev_attr_threeg);
  898. device_remove_file(&device->dev, &dev_attr_interface);
  899. return 0;
  900. }
  901. static int create_sysfs(void)
  902. {
  903. int retval = -ENOMEM;
  904. if (has_cap(ACER_CAP_WIRELESS)) {
  905. retval = device_create_file(&acer_platform_device->dev,
  906. &dev_attr_wireless);
  907. if (retval)
  908. goto error_sysfs;
  909. }
  910. if (has_cap(ACER_CAP_BLUETOOTH)) {
  911. retval = device_create_file(&acer_platform_device->dev,
  912. &dev_attr_bluetooth);
  913. if (retval)
  914. goto error_sysfs;
  915. }
  916. if (has_cap(ACER_CAP_THREEG)) {
  917. retval = device_create_file(&acer_platform_device->dev,
  918. &dev_attr_threeg);
  919. if (retval)
  920. goto error_sysfs;
  921. }
  922. retval = device_create_file(&acer_platform_device->dev,
  923. &dev_attr_interface);
  924. if (retval)
  925. goto error_sysfs;
  926. return 0;
  927. error_sysfs:
  928. remove_sysfs(acer_platform_device);
  929. return retval;
  930. }
  931. static int __init acer_wmi_init(void)
  932. {
  933. int err;
  934. printk(ACER_INFO "Acer Laptop ACPI-WMI Extras version %s\n",
  935. ACER_WMI_VERSION);
  936. find_quirks();
  937. /*
  938. * Detect which ACPI-WMI interface we're using.
  939. */
  940. if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
  941. interface = &AMW0_V2_interface;
  942. if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
  943. interface = &wmid_interface;
  944. if (wmi_has_guid(WMID_GUID2) && interface) {
  945. if (ACPI_FAILURE(WMID_set_capabilities())) {
  946. printk(ACER_ERR "Unable to detect available WMID "
  947. "devices\n");
  948. return -ENODEV;
  949. }
  950. } else if (!wmi_has_guid(WMID_GUID2) && interface) {
  951. printk(ACER_ERR "No WMID device detection method found\n");
  952. return -ENODEV;
  953. }
  954. if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
  955. interface = &AMW0_interface;
  956. if (ACPI_FAILURE(AMW0_set_capabilities())) {
  957. printk(ACER_ERR "Unable to detect available AMW0 "
  958. "devices\n");
  959. return -ENODEV;
  960. }
  961. }
  962. if (wmi_has_guid(AMW0_GUID1))
  963. AMW0_find_mailled();
  964. if (!interface) {
  965. printk(ACER_ERR "No or unsupported WMI interface, unable to "
  966. "load\n");
  967. return -ENODEV;
  968. }
  969. if (platform_driver_register(&acer_platform_driver)) {
  970. printk(ACER_ERR "Unable to register platform driver.\n");
  971. goto error_platform_register;
  972. }
  973. acer_platform_device = platform_device_alloc("acer-wmi", -1);
  974. platform_device_add(acer_platform_device);
  975. err = create_sysfs();
  976. if (err)
  977. return err;
  978. /* Override any initial settings with values from the commandline */
  979. acer_commandline_init();
  980. return 0;
  981. error_platform_register:
  982. return -ENODEV;
  983. }
  984. static void __exit acer_wmi_exit(void)
  985. {
  986. remove_sysfs(acer_platform_device);
  987. platform_device_del(acer_platform_device);
  988. platform_driver_unregister(&acer_platform_driver);
  989. printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
  990. return;
  991. }
  992. module_init(acer_wmi_init);
  993. module_exit(acer_wmi_exit);