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