msi-laptop.c 23 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. /*-*-linux-c-*-*/
  2. /*
  3. Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301, USA.
  16. */
  17. /*
  18. * msi-laptop.c - MSI S270 laptop support. This laptop is sold under
  19. * various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
  20. *
  21. * Driver also supports S271, S420 models.
  22. *
  23. * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
  24. *
  25. * lcd_level - Screen brightness: contains a single integer in the
  26. * range 0..8. (rw)
  27. *
  28. * auto_brightness - Enable automatic brightness control: contains
  29. * either 0 or 1. If set to 1 the hardware adjusts the screen
  30. * brightness automatically when the power cord is
  31. * plugged/unplugged. (rw)
  32. *
  33. * wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
  34. *
  35. * bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
  36. * Please note that this file is constantly 0 if no Bluetooth
  37. * hardware is available. (ro)
  38. *
  39. * In addition to these platform device attributes the driver
  40. * registers itself in the Linux backlight control subsystem and is
  41. * available to userspace under /sys/class/backlight/msi-laptop-bl/.
  42. *
  43. * This driver might work on other laptops produced by MSI. If you
  44. * want to try it you can pass force=1 as argument to the module which
  45. * will force it to load even when the DMI data doesn't identify the
  46. * laptop as MSI S270. YMMV.
  47. */
  48. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  49. #include <linux/module.h>
  50. #include <linux/kernel.h>
  51. #include <linux/init.h>
  52. #include <linux/acpi.h>
  53. #include <linux/dmi.h>
  54. #include <linux/backlight.h>
  55. #include <linux/platform_device.h>
  56. #include <linux/rfkill.h>
  57. #include <linux/i8042.h>
  58. #include <linux/input.h>
  59. #include <linux/input/sparse-keymap.h>
  60. #define MSI_DRIVER_VERSION "0.5"
  61. #define MSI_LCD_LEVEL_MAX 9
  62. #define MSI_EC_COMMAND_WIRELESS 0x10
  63. #define MSI_EC_COMMAND_LCD_LEVEL 0x11
  64. #define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e
  65. #define MSI_STANDARD_EC_BLUETOOTH_MASK (1 << 0)
  66. #define MSI_STANDARD_EC_WEBCAM_MASK (1 << 1)
  67. #define MSI_STANDARD_EC_WLAN_MASK (1 << 3)
  68. #define MSI_STANDARD_EC_3G_MASK (1 << 4)
  69. /* For set SCM load flag to disable BIOS fn key */
  70. #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
  71. #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
  72. #define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4
  73. #define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
  74. static int msi_laptop_resume(struct platform_device *device);
  75. #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
  76. static bool force;
  77. module_param(force, bool, 0);
  78. MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
  79. static int auto_brightness;
  80. module_param(auto_brightness, int, 0);
  81. MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
  82. static const struct key_entry msi_laptop_keymap[] = {
  83. {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */
  84. {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */
  85. {KE_END, 0}
  86. };
  87. static struct input_dev *msi_laptop_input_dev;
  88. static bool old_ec_model;
  89. static int wlan_s, bluetooth_s, threeg_s;
  90. static int threeg_exists;
  91. /* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
  92. * those netbook will load the SCM (windows app) to disable the original
  93. * Wlan/Bluetooth control by BIOS when user press fn key, then control
  94. * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
  95. * cann't on/off 3G module on those 3G netbook.
  96. * On Linux, msi-laptop driver will do the same thing to disable the
  97. * original BIOS control, then might need use HAL or other userland
  98. * application to do the software control that simulate with SCM.
  99. * e.g. MSI N034 netbook
  100. */
  101. static bool load_scm_model;
  102. static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
  103. /* Hardware access */
  104. static int set_lcd_level(int level)
  105. {
  106. u8 buf[2];
  107. if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
  108. return -EINVAL;
  109. buf[0] = 0x80;
  110. buf[1] = (u8) (level*31);
  111. return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf),
  112. NULL, 0);
  113. }
  114. static int get_lcd_level(void)
  115. {
  116. u8 wdata = 0, rdata;
  117. int result;
  118. result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
  119. &rdata, 1);
  120. if (result < 0)
  121. return result;
  122. return (int) rdata / 31;
  123. }
  124. static int get_auto_brightness(void)
  125. {
  126. u8 wdata = 4, rdata;
  127. int result;
  128. result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
  129. &rdata, 1);
  130. if (result < 0)
  131. return result;
  132. return !!(rdata & 8);
  133. }
  134. static int set_auto_brightness(int enable)
  135. {
  136. u8 wdata[2], rdata;
  137. int result;
  138. wdata[0] = 4;
  139. result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1,
  140. &rdata, 1);
  141. if (result < 0)
  142. return result;
  143. wdata[0] = 0x84;
  144. wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
  145. return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2,
  146. NULL, 0);
  147. }
  148. static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
  149. {
  150. int status;
  151. u8 wdata = 0, rdata;
  152. int result;
  153. if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
  154. return -EINVAL;
  155. /* read current device state */
  156. result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
  157. if (result < 0)
  158. return -EINVAL;
  159. if (!!(rdata & mask) != status) {
  160. /* reverse device bit */
  161. if (rdata & mask)
  162. wdata = rdata & ~mask;
  163. else
  164. wdata = rdata | mask;
  165. result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
  166. if (result < 0)
  167. return -EINVAL;
  168. }
  169. return count;
  170. }
  171. static int get_wireless_state(int *wlan, int *bluetooth)
  172. {
  173. u8 wdata = 0, rdata;
  174. int result;
  175. result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);
  176. if (result < 0)
  177. return -1;
  178. if (wlan)
  179. *wlan = !!(rdata & 8);
  180. if (bluetooth)
  181. *bluetooth = !!(rdata & 128);
  182. return 0;
  183. }
  184. static int get_wireless_state_ec_standard(void)
  185. {
  186. u8 rdata;
  187. int result;
  188. result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
  189. if (result < 0)
  190. return -1;
  191. wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
  192. bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK);
  193. threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK);
  194. return 0;
  195. }
  196. static int get_threeg_exists(void)
  197. {
  198. u8 rdata;
  199. int result;
  200. result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
  201. if (result < 0)
  202. return -1;
  203. threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
  204. return 0;
  205. }
  206. /* Backlight device stuff */
  207. static int bl_get_brightness(struct backlight_device *b)
  208. {
  209. return get_lcd_level();
  210. }
  211. static int bl_update_status(struct backlight_device *b)
  212. {
  213. return set_lcd_level(b->props.brightness);
  214. }
  215. static const struct backlight_ops msibl_ops = {
  216. .get_brightness = bl_get_brightness,
  217. .update_status = bl_update_status,
  218. };
  219. static struct backlight_device *msibl_device;
  220. /* Platform device */
  221. static ssize_t show_wlan(struct device *dev,
  222. struct device_attribute *attr, char *buf)
  223. {
  224. int ret, enabled;
  225. if (old_ec_model) {
  226. ret = get_wireless_state(&enabled, NULL);
  227. } else {
  228. ret = get_wireless_state_ec_standard();
  229. enabled = wlan_s;
  230. }
  231. if (ret < 0)
  232. return ret;
  233. return sprintf(buf, "%i\n", enabled);
  234. }
  235. static ssize_t store_wlan(struct device *dev,
  236. struct device_attribute *attr, const char *buf, size_t count)
  237. {
  238. return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK);
  239. }
  240. static ssize_t show_bluetooth(struct device *dev,
  241. struct device_attribute *attr, char *buf)
  242. {
  243. int ret, enabled;
  244. if (old_ec_model) {
  245. ret = get_wireless_state(NULL, &enabled);
  246. } else {
  247. ret = get_wireless_state_ec_standard();
  248. enabled = bluetooth_s;
  249. }
  250. if (ret < 0)
  251. return ret;
  252. return sprintf(buf, "%i\n", enabled);
  253. }
  254. static ssize_t store_bluetooth(struct device *dev,
  255. struct device_attribute *attr, const char *buf, size_t count)
  256. {
  257. return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK);
  258. }
  259. static ssize_t show_threeg(struct device *dev,
  260. struct device_attribute *attr, char *buf)
  261. {
  262. int ret;
  263. /* old msi ec not support 3G */
  264. if (old_ec_model)
  265. return -1;
  266. ret = get_wireless_state_ec_standard();
  267. if (ret < 0)
  268. return ret;
  269. return sprintf(buf, "%i\n", threeg_s);
  270. }
  271. static ssize_t store_threeg(struct device *dev,
  272. struct device_attribute *attr, const char *buf, size_t count)
  273. {
  274. return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK);
  275. }
  276. static ssize_t show_lcd_level(struct device *dev,
  277. struct device_attribute *attr, char *buf)
  278. {
  279. int ret;
  280. ret = get_lcd_level();
  281. if (ret < 0)
  282. return ret;
  283. return sprintf(buf, "%i\n", ret);
  284. }
  285. static ssize_t store_lcd_level(struct device *dev,
  286. struct device_attribute *attr, const char *buf, size_t count)
  287. {
  288. int level, ret;
  289. if (sscanf(buf, "%i", &level) != 1 ||
  290. (level < 0 || level >= MSI_LCD_LEVEL_MAX))
  291. return -EINVAL;
  292. ret = set_lcd_level(level);
  293. if (ret < 0)
  294. return ret;
  295. return count;
  296. }
  297. static ssize_t show_auto_brightness(struct device *dev,
  298. struct device_attribute *attr, char *buf)
  299. {
  300. int ret;
  301. ret = get_auto_brightness();
  302. if (ret < 0)
  303. return ret;
  304. return sprintf(buf, "%i\n", ret);
  305. }
  306. static ssize_t store_auto_brightness(struct device *dev,
  307. struct device_attribute *attr, const char *buf, size_t count)
  308. {
  309. int enable, ret;
  310. if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
  311. return -EINVAL;
  312. ret = set_auto_brightness(enable);
  313. if (ret < 0)
  314. return ret;
  315. return count;
  316. }
  317. static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
  318. static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
  319. store_auto_brightness);
  320. static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
  321. static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
  322. static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
  323. static struct attribute *msipf_attributes[] = {
  324. &dev_attr_lcd_level.attr,
  325. &dev_attr_auto_brightness.attr,
  326. &dev_attr_bluetooth.attr,
  327. &dev_attr_wlan.attr,
  328. NULL
  329. };
  330. static struct attribute_group msipf_attribute_group = {
  331. .attrs = msipf_attributes
  332. };
  333. static struct platform_driver msipf_driver = {
  334. .driver = {
  335. .name = "msi-laptop-pf",
  336. .owner = THIS_MODULE,
  337. },
  338. .resume = msi_laptop_resume,
  339. };
  340. static struct platform_device *msipf_device;
  341. /* Initialization */
  342. static int dmi_check_cb(const struct dmi_system_id *id)
  343. {
  344. pr_info("Identified laptop model '%s'\n", id->ident);
  345. return 1;
  346. }
  347. static struct dmi_system_id __initdata msi_dmi_table[] = {
  348. {
  349. .ident = "MSI S270",
  350. .matches = {
  351. DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
  352. DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
  353. DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
  354. DMI_MATCH(DMI_CHASSIS_VENDOR,
  355. "MICRO-STAR INT'L CO.,LTD")
  356. },
  357. .callback = dmi_check_cb
  358. },
  359. {
  360. .ident = "MSI S271",
  361. .matches = {
  362. DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
  363. DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
  364. DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
  365. DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
  366. },
  367. .callback = dmi_check_cb
  368. },
  369. {
  370. .ident = "MSI S420",
  371. .matches = {
  372. DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
  373. DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
  374. DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
  375. DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
  376. },
  377. .callback = dmi_check_cb
  378. },
  379. {
  380. .ident = "Medion MD96100",
  381. .matches = {
  382. DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
  383. DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
  384. DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
  385. DMI_MATCH(DMI_CHASSIS_VENDOR,
  386. "MICRO-STAR INT'L CO.,LTD")
  387. },
  388. .callback = dmi_check_cb
  389. },
  390. { }
  391. };
  392. static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
  393. {
  394. .ident = "MSI N034",
  395. .matches = {
  396. DMI_MATCH(DMI_SYS_VENDOR,
  397. "MICRO-STAR INTERNATIONAL CO., LTD"),
  398. DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"),
  399. DMI_MATCH(DMI_CHASSIS_VENDOR,
  400. "MICRO-STAR INTERNATIONAL CO., LTD")
  401. },
  402. .callback = dmi_check_cb
  403. },
  404. {
  405. .ident = "MSI N051",
  406. .matches = {
  407. DMI_MATCH(DMI_SYS_VENDOR,
  408. "MICRO-STAR INTERNATIONAL CO., LTD"),
  409. DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"),
  410. DMI_MATCH(DMI_CHASSIS_VENDOR,
  411. "MICRO-STAR INTERNATIONAL CO., LTD")
  412. },
  413. .callback = dmi_check_cb
  414. },
  415. {
  416. .ident = "MSI N014",
  417. .matches = {
  418. DMI_MATCH(DMI_SYS_VENDOR,
  419. "MICRO-STAR INTERNATIONAL CO., LTD"),
  420. DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
  421. },
  422. .callback = dmi_check_cb
  423. },
  424. {
  425. .ident = "MSI CR620",
  426. .matches = {
  427. DMI_MATCH(DMI_SYS_VENDOR,
  428. "Micro-Star International"),
  429. DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
  430. },
  431. .callback = dmi_check_cb
  432. },
  433. {
  434. .ident = "MSI U270",
  435. .matches = {
  436. DMI_MATCH(DMI_SYS_VENDOR,
  437. "Micro-Star International Co., Ltd."),
  438. DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
  439. },
  440. .callback = dmi_check_cb
  441. },
  442. { }
  443. };
  444. static int rfkill_bluetooth_set(void *data, bool blocked)
  445. {
  446. /* Do something with blocked...*/
  447. /*
  448. * blocked == false is on
  449. * blocked == true is off
  450. */
  451. if (blocked)
  452. set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
  453. else
  454. set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
  455. return 0;
  456. }
  457. static int rfkill_wlan_set(void *data, bool blocked)
  458. {
  459. if (blocked)
  460. set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK);
  461. else
  462. set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK);
  463. return 0;
  464. }
  465. static int rfkill_threeg_set(void *data, bool blocked)
  466. {
  467. if (blocked)
  468. set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK);
  469. else
  470. set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK);
  471. return 0;
  472. }
  473. static const struct rfkill_ops rfkill_bluetooth_ops = {
  474. .set_block = rfkill_bluetooth_set
  475. };
  476. static const struct rfkill_ops rfkill_wlan_ops = {
  477. .set_block = rfkill_wlan_set
  478. };
  479. static const struct rfkill_ops rfkill_threeg_ops = {
  480. .set_block = rfkill_threeg_set
  481. };
  482. static void rfkill_cleanup(void)
  483. {
  484. if (rfk_bluetooth) {
  485. rfkill_unregister(rfk_bluetooth);
  486. rfkill_destroy(rfk_bluetooth);
  487. }
  488. if (rfk_threeg) {
  489. rfkill_unregister(rfk_threeg);
  490. rfkill_destroy(rfk_threeg);
  491. }
  492. if (rfk_wlan) {
  493. rfkill_unregister(rfk_wlan);
  494. rfkill_destroy(rfk_wlan);
  495. }
  496. }
  497. static void msi_update_rfkill(struct work_struct *ignored)
  498. {
  499. get_wireless_state_ec_standard();
  500. if (rfk_wlan)
  501. rfkill_set_sw_state(rfk_wlan, !wlan_s);
  502. if (rfk_bluetooth)
  503. rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
  504. if (rfk_threeg)
  505. rfkill_set_sw_state(rfk_threeg, !threeg_s);
  506. }
  507. static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
  508. static void msi_send_touchpad_key(struct work_struct *ignored)
  509. {
  510. u8 rdata;
  511. int result;
  512. result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata);
  513. if (result < 0)
  514. return;
  515. sparse_keymap_report_event(msi_laptop_input_dev,
  516. (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
  517. KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
  518. }
  519. static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key);
  520. static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
  521. struct serio *port)
  522. {
  523. static bool extended;
  524. if (str & 0x20)
  525. return false;
  526. /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
  527. if (unlikely(data == 0xe0)) {
  528. extended = true;
  529. return false;
  530. } else if (unlikely(extended)) {
  531. extended = false;
  532. switch (data) {
  533. case 0xE4:
  534. schedule_delayed_work(&msi_touchpad_work,
  535. round_jiffies_relative(0.5 * HZ));
  536. break;
  537. case 0x54:
  538. case 0x62:
  539. case 0x76:
  540. schedule_delayed_work(&msi_rfkill_work,
  541. round_jiffies_relative(0.5 * HZ));
  542. break;
  543. }
  544. }
  545. return false;
  546. }
  547. static void msi_init_rfkill(struct work_struct *ignored)
  548. {
  549. if (rfk_wlan) {
  550. rfkill_set_sw_state(rfk_wlan, !wlan_s);
  551. rfkill_wlan_set(NULL, !wlan_s);
  552. }
  553. if (rfk_bluetooth) {
  554. rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
  555. rfkill_bluetooth_set(NULL, !bluetooth_s);
  556. }
  557. if (rfk_threeg) {
  558. rfkill_set_sw_state(rfk_threeg, !threeg_s);
  559. rfkill_threeg_set(NULL, !threeg_s);
  560. }
  561. }
  562. static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill);
  563. static int rfkill_init(struct platform_device *sdev)
  564. {
  565. /* add rfkill */
  566. int retval;
  567. /* keep the hardware wireless state */
  568. get_wireless_state_ec_standard();
  569. rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
  570. RFKILL_TYPE_BLUETOOTH,
  571. &rfkill_bluetooth_ops, NULL);
  572. if (!rfk_bluetooth) {
  573. retval = -ENOMEM;
  574. goto err_bluetooth;
  575. }
  576. retval = rfkill_register(rfk_bluetooth);
  577. if (retval)
  578. goto err_bluetooth;
  579. rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN,
  580. &rfkill_wlan_ops, NULL);
  581. if (!rfk_wlan) {
  582. retval = -ENOMEM;
  583. goto err_wlan;
  584. }
  585. retval = rfkill_register(rfk_wlan);
  586. if (retval)
  587. goto err_wlan;
  588. if (threeg_exists) {
  589. rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev,
  590. RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL);
  591. if (!rfk_threeg) {
  592. retval = -ENOMEM;
  593. goto err_threeg;
  594. }
  595. retval = rfkill_register(rfk_threeg);
  596. if (retval)
  597. goto err_threeg;
  598. }
  599. /* schedule to run rfkill state initial */
  600. schedule_delayed_work(&msi_rfkill_init,
  601. round_jiffies_relative(1 * HZ));
  602. return 0;
  603. err_threeg:
  604. rfkill_destroy(rfk_threeg);
  605. if (rfk_wlan)
  606. rfkill_unregister(rfk_wlan);
  607. err_wlan:
  608. rfkill_destroy(rfk_wlan);
  609. if (rfk_bluetooth)
  610. rfkill_unregister(rfk_bluetooth);
  611. err_bluetooth:
  612. rfkill_destroy(rfk_bluetooth);
  613. return retval;
  614. }
  615. static int msi_laptop_resume(struct platform_device *device)
  616. {
  617. u8 data;
  618. int result;
  619. if (!load_scm_model)
  620. return 0;
  621. /* set load SCM to disable hardware control by fn key */
  622. result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
  623. if (result < 0)
  624. return result;
  625. result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
  626. data | MSI_STANDARD_EC_SCM_LOAD_MASK);
  627. if (result < 0)
  628. return result;
  629. return 0;
  630. }
  631. static int __init msi_laptop_input_setup(void)
  632. {
  633. int err;
  634. msi_laptop_input_dev = input_allocate_device();
  635. if (!msi_laptop_input_dev)
  636. return -ENOMEM;
  637. msi_laptop_input_dev->name = "MSI Laptop hotkeys";
  638. msi_laptop_input_dev->phys = "msi-laptop/input0";
  639. msi_laptop_input_dev->id.bustype = BUS_HOST;
  640. err = sparse_keymap_setup(msi_laptop_input_dev,
  641. msi_laptop_keymap, NULL);
  642. if (err)
  643. goto err_free_dev;
  644. err = input_register_device(msi_laptop_input_dev);
  645. if (err)
  646. goto err_free_keymap;
  647. return 0;
  648. err_free_keymap:
  649. sparse_keymap_free(msi_laptop_input_dev);
  650. err_free_dev:
  651. input_free_device(msi_laptop_input_dev);
  652. return err;
  653. }
  654. static void msi_laptop_input_destroy(void)
  655. {
  656. sparse_keymap_free(msi_laptop_input_dev);
  657. input_unregister_device(msi_laptop_input_dev);
  658. }
  659. static int __init load_scm_model_init(struct platform_device *sdev)
  660. {
  661. u8 data;
  662. int result;
  663. /* allow userland write sysfs file */
  664. dev_attr_bluetooth.store = store_bluetooth;
  665. dev_attr_wlan.store = store_wlan;
  666. dev_attr_threeg.store = store_threeg;
  667. dev_attr_bluetooth.attr.mode |= S_IWUSR;
  668. dev_attr_wlan.attr.mode |= S_IWUSR;
  669. dev_attr_threeg.attr.mode |= S_IWUSR;
  670. /* disable hardware control by fn key */
  671. result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
  672. if (result < 0)
  673. return result;
  674. result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
  675. data | MSI_STANDARD_EC_SCM_LOAD_MASK);
  676. if (result < 0)
  677. return result;
  678. /* initial rfkill */
  679. result = rfkill_init(sdev);
  680. if (result < 0)
  681. goto fail_rfkill;
  682. /* setup input device */
  683. result = msi_laptop_input_setup();
  684. if (result)
  685. goto fail_input;
  686. result = i8042_install_filter(msi_laptop_i8042_filter);
  687. if (result) {
  688. pr_err("Unable to install key filter\n");
  689. goto fail_filter;
  690. }
  691. return 0;
  692. fail_filter:
  693. msi_laptop_input_destroy();
  694. fail_input:
  695. rfkill_cleanup();
  696. fail_rfkill:
  697. return result;
  698. }
  699. static int __init msi_init(void)
  700. {
  701. int ret;
  702. if (acpi_disabled)
  703. return -ENODEV;
  704. if (force || dmi_check_system(msi_dmi_table))
  705. old_ec_model = 1;
  706. if (!old_ec_model)
  707. get_threeg_exists();
  708. if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table))
  709. load_scm_model = 1;
  710. if (auto_brightness < 0 || auto_brightness > 2)
  711. return -EINVAL;
  712. /* Register backlight stuff */
  713. if (acpi_video_backlight_support()) {
  714. pr_info("Brightness ignored, must be controlled by ACPI video driver\n");
  715. } else {
  716. struct backlight_properties props;
  717. memset(&props, 0, sizeof(struct backlight_properties));
  718. props.type = BACKLIGHT_PLATFORM;
  719. props.max_brightness = MSI_LCD_LEVEL_MAX - 1;
  720. msibl_device = backlight_device_register("msi-laptop-bl", NULL,
  721. NULL, &msibl_ops,
  722. &props);
  723. if (IS_ERR(msibl_device))
  724. return PTR_ERR(msibl_device);
  725. }
  726. ret = platform_driver_register(&msipf_driver);
  727. if (ret)
  728. goto fail_backlight;
  729. /* Register platform stuff */
  730. msipf_device = platform_device_alloc("msi-laptop-pf", -1);
  731. if (!msipf_device) {
  732. ret = -ENOMEM;
  733. goto fail_platform_driver;
  734. }
  735. ret = platform_device_add(msipf_device);
  736. if (ret)
  737. goto fail_platform_device1;
  738. if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
  739. ret = -EINVAL;
  740. goto fail_platform_device1;
  741. }
  742. ret = sysfs_create_group(&msipf_device->dev.kobj,
  743. &msipf_attribute_group);
  744. if (ret)
  745. goto fail_platform_device2;
  746. if (!old_ec_model) {
  747. if (threeg_exists)
  748. ret = device_create_file(&msipf_device->dev,
  749. &dev_attr_threeg);
  750. if (ret)
  751. goto fail_platform_device2;
  752. }
  753. /* Disable automatic brightness control by default because
  754. * this module was probably loaded to do brightness control in
  755. * software. */
  756. if (auto_brightness != 2)
  757. set_auto_brightness(auto_brightness);
  758. pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n");
  759. return 0;
  760. fail_platform_device2:
  761. if (load_scm_model) {
  762. i8042_remove_filter(msi_laptop_i8042_filter);
  763. cancel_delayed_work_sync(&msi_rfkill_work);
  764. rfkill_cleanup();
  765. }
  766. platform_device_del(msipf_device);
  767. fail_platform_device1:
  768. platform_device_put(msipf_device);
  769. fail_platform_driver:
  770. platform_driver_unregister(&msipf_driver);
  771. fail_backlight:
  772. backlight_device_unregister(msibl_device);
  773. return ret;
  774. }
  775. static void __exit msi_cleanup(void)
  776. {
  777. if (load_scm_model) {
  778. i8042_remove_filter(msi_laptop_i8042_filter);
  779. msi_laptop_input_destroy();
  780. cancel_delayed_work_sync(&msi_rfkill_work);
  781. rfkill_cleanup();
  782. }
  783. sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
  784. if (!old_ec_model && threeg_exists)
  785. device_remove_file(&msipf_device->dev, &dev_attr_threeg);
  786. platform_device_unregister(msipf_device);
  787. platform_driver_unregister(&msipf_driver);
  788. backlight_device_unregister(msibl_device);
  789. /* Enable automatic brightness control again */
  790. if (auto_brightness != 2)
  791. set_auto_brightness(1);
  792. pr_info("driver unloaded\n");
  793. }
  794. module_init(msi_init);
  795. module_exit(msi_cleanup);
  796. MODULE_AUTHOR("Lennart Poettering");
  797. MODULE_DESCRIPTION("MSI Laptop Support");
  798. MODULE_VERSION(MSI_DRIVER_VERSION);
  799. MODULE_LICENSE("GPL");
  800. MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
  801. MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
  802. MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
  803. MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
  804. MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");
  805. MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
  806. MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
  807. MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
  808. MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");