button.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * button.c - ACPI Button Driver
  4. *
  5. * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
  6. * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
  7. */
  8. #define pr_fmt(fmt) "ACPI: button: " fmt
  9. #include <linux/compiler.h>
  10. #include <linux/kernel.h>
  11. #include <linux/module.h>
  12. #include <linux/init.h>
  13. #include <linux/types.h>
  14. #include <linux/proc_fs.h>
  15. #include <linux/seq_file.h>
  16. #include <linux/input.h>
  17. #include <linux/slab.h>
  18. #include <linux/acpi.h>
  19. #include <linux/dmi.h>
  20. #include <acpi/button.h>
  21. #define PREFIX "ACPI: "
  22. #define ACPI_BUTTON_CLASS "button"
  23. #define ACPI_BUTTON_FILE_STATE "state"
  24. #define ACPI_BUTTON_TYPE_UNKNOWN 0x00
  25. #define ACPI_BUTTON_NOTIFY_STATUS 0x80
  26. #define ACPI_BUTTON_SUBCLASS_POWER "power"
  27. #define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button"
  28. #define ACPI_BUTTON_TYPE_POWER 0x01
  29. #define ACPI_BUTTON_SUBCLASS_SLEEP "sleep"
  30. #define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button"
  31. #define ACPI_BUTTON_TYPE_SLEEP 0x03
  32. #define ACPI_BUTTON_SUBCLASS_LID "lid"
  33. #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
  34. #define ACPI_BUTTON_TYPE_LID 0x05
  35. enum {
  36. ACPI_BUTTON_LID_INIT_IGNORE,
  37. ACPI_BUTTON_LID_INIT_OPEN,
  38. ACPI_BUTTON_LID_INIT_METHOD,
  39. ACPI_BUTTON_LID_INIT_DISABLED,
  40. };
  41. static const char * const lid_init_state_str[] = {
  42. [ACPI_BUTTON_LID_INIT_IGNORE] = "ignore",
  43. [ACPI_BUTTON_LID_INIT_OPEN] = "open",
  44. [ACPI_BUTTON_LID_INIT_METHOD] = "method",
  45. [ACPI_BUTTON_LID_INIT_DISABLED] = "disabled",
  46. };
  47. #define _COMPONENT ACPI_BUTTON_COMPONENT
  48. ACPI_MODULE_NAME("button");
  49. MODULE_AUTHOR("Paul Diefenbaugh");
  50. MODULE_DESCRIPTION("ACPI Button Driver");
  51. MODULE_LICENSE("GPL");
  52. static const struct acpi_device_id button_device_ids[] = {
  53. {ACPI_BUTTON_HID_LID, 0},
  54. {ACPI_BUTTON_HID_SLEEP, 0},
  55. {ACPI_BUTTON_HID_SLEEPF, 0},
  56. {ACPI_BUTTON_HID_POWER, 0},
  57. {ACPI_BUTTON_HID_POWERF, 0},
  58. {"", 0},
  59. };
  60. MODULE_DEVICE_TABLE(acpi, button_device_ids);
  61. /* Please keep this list sorted alphabetically by vendor and model */
  62. static const struct dmi_system_id dmi_lid_quirks[] = {
  63. {
  64. /* GP-electronic T701, _LID method points to a floating GPIO */
  65. .matches = {
  66. DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
  67. DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
  68. DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
  69. },
  70. .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
  71. },
  72. {
  73. /*
  74. * Medion Akoya E2215T, notification of the LID device only
  75. * happens on close, not on open and _LID always returns closed.
  76. */
  77. .matches = {
  78. DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
  79. DMI_MATCH(DMI_PRODUCT_NAME, "E2215T"),
  80. },
  81. .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
  82. },
  83. {
  84. /*
  85. * Medion Akoya E2228T, notification of the LID device only
  86. * happens on close, not on open and _LID always returns closed.
  87. */
  88. .matches = {
  89. DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
  90. DMI_MATCH(DMI_PRODUCT_NAME, "E2228T"),
  91. },
  92. .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
  93. },
  94. {
  95. /*
  96. * Razer Blade Stealth 13 late 2019, notification of the LID device
  97. * only happens on close, not on open and _LID always returns closed.
  98. */
  99. .matches = {
  100. DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
  101. DMI_MATCH(DMI_PRODUCT_NAME, "Razer Blade Stealth 13 Late 2019"),
  102. },
  103. .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
  104. },
  105. {}
  106. };
  107. static int acpi_button_add(struct acpi_device *device);
  108. static int acpi_button_remove(struct acpi_device *device);
  109. static void acpi_button_notify(struct acpi_device *device, u32 event);
  110. #ifdef CONFIG_PM_SLEEP
  111. static int acpi_button_suspend(struct device *dev);
  112. static int acpi_button_resume(struct device *dev);
  113. #else
  114. #define acpi_button_suspend NULL
  115. #define acpi_button_resume NULL
  116. #endif
  117. static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
  118. static struct acpi_driver acpi_button_driver = {
  119. .name = "button",
  120. .class = ACPI_BUTTON_CLASS,
  121. .ids = button_device_ids,
  122. .ops = {
  123. .add = acpi_button_add,
  124. .remove = acpi_button_remove,
  125. .notify = acpi_button_notify,
  126. },
  127. .drv.pm = &acpi_button_pm,
  128. };
  129. struct acpi_button {
  130. unsigned int type;
  131. struct input_dev *input;
  132. char phys[32]; /* for input device */
  133. unsigned long pushed;
  134. int last_state;
  135. ktime_t last_time;
  136. bool suspended;
  137. bool lid_state_initialized;
  138. };
  139. static struct acpi_device *lid_device;
  140. static long lid_init_state = -1;
  141. static unsigned long lid_report_interval __read_mostly = 500;
  142. module_param(lid_report_interval, ulong, 0644);
  143. MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
  144. /* --------------------------------------------------------------------------
  145. FS Interface (/proc)
  146. -------------------------------------------------------------------------- */
  147. static struct proc_dir_entry *acpi_button_dir;
  148. static struct proc_dir_entry *acpi_lid_dir;
  149. static int acpi_lid_evaluate_state(struct acpi_device *device)
  150. {
  151. unsigned long long lid_state;
  152. acpi_status status;
  153. status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state);
  154. if (ACPI_FAILURE(status))
  155. return -ENODEV;
  156. return lid_state ? 1 : 0;
  157. }
  158. static int acpi_lid_notify_state(struct acpi_device *device, int state)
  159. {
  160. struct acpi_button *button = acpi_driver_data(device);
  161. ktime_t next_report;
  162. bool do_update;
  163. /*
  164. * In lid_init_state=ignore mode, if user opens/closes lid
  165. * frequently with "open" missing, and "last_time" is also updated
  166. * frequently, "close" cannot be delivered to the userspace.
  167. * So "last_time" is only updated after a timeout or an actual
  168. * switch.
  169. */
  170. if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
  171. button->last_state != !!state)
  172. do_update = true;
  173. else
  174. do_update = false;
  175. next_report = ktime_add(button->last_time,
  176. ms_to_ktime(lid_report_interval));
  177. if (button->last_state == !!state &&
  178. ktime_after(ktime_get(), next_report)) {
  179. /* Complain the buggy firmware */
  180. pr_warn_once("The lid device is not compliant to SW_LID.\n");
  181. /*
  182. * Send the unreliable complement switch event:
  183. *
  184. * On most platforms, the lid device is reliable. However
  185. * there are exceptions:
  186. * 1. Platforms returning initial lid state as "close" by
  187. * default after booting/resuming:
  188. * https://bugzilla.kernel.org/show_bug.cgi?id=89211
  189. * https://bugzilla.kernel.org/show_bug.cgi?id=106151
  190. * 2. Platforms never reporting "open" events:
  191. * https://bugzilla.kernel.org/show_bug.cgi?id=106941
  192. * On these buggy platforms, the usage model of the ACPI
  193. * lid device actually is:
  194. * 1. The initial returning value of _LID may not be
  195. * reliable.
  196. * 2. The open event may not be reliable.
  197. * 3. The close event is reliable.
  198. *
  199. * But SW_LID is typed as input switch event, the input
  200. * layer checks if the event is redundant. Hence if the
  201. * state is not switched, the userspace cannot see this
  202. * platform triggered reliable event. By inserting a
  203. * complement switch event, it then is guaranteed that the
  204. * platform triggered reliable one can always be seen by
  205. * the userspace.
  206. */
  207. if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
  208. do_update = true;
  209. /*
  210. * Do generate complement switch event for "close"
  211. * as "close" is reliable and wrong "open" won't
  212. * trigger unexpected behaviors.
  213. * Do not generate complement switch event for
  214. * "open" as "open" is not reliable and wrong
  215. * "close" will trigger unexpected behaviors.
  216. */
  217. if (!state) {
  218. input_report_switch(button->input,
  219. SW_LID, state);
  220. input_sync(button->input);
  221. }
  222. }
  223. }
  224. /* Send the platform triggered reliable event */
  225. if (do_update) {
  226. acpi_handle_debug(device->handle, "ACPI LID %s\n",
  227. state ? "open" : "closed");
  228. input_report_switch(button->input, SW_LID, !state);
  229. input_sync(button->input);
  230. button->last_state = !!state;
  231. button->last_time = ktime_get();
  232. }
  233. return 0;
  234. }
  235. static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq,
  236. void *offset)
  237. {
  238. struct acpi_device *device = seq->private;
  239. int state;
  240. state = acpi_lid_evaluate_state(device);
  241. seq_printf(seq, "state: %s\n",
  242. state < 0 ? "unsupported" : (state ? "open" : "closed"));
  243. return 0;
  244. }
  245. static int acpi_button_add_fs(struct acpi_device *device)
  246. {
  247. struct acpi_button *button = acpi_driver_data(device);
  248. struct proc_dir_entry *entry = NULL;
  249. int ret = 0;
  250. /* procfs I/F for ACPI lid device only */
  251. if (button->type != ACPI_BUTTON_TYPE_LID)
  252. return 0;
  253. if (acpi_button_dir || acpi_lid_dir) {
  254. printk(KERN_ERR PREFIX "More than one Lid device found!\n");
  255. return -EEXIST;
  256. }
  257. /* create /proc/acpi/button */
  258. acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
  259. if (!acpi_button_dir)
  260. return -ENODEV;
  261. /* create /proc/acpi/button/lid */
  262. acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
  263. if (!acpi_lid_dir) {
  264. ret = -ENODEV;
  265. goto remove_button_dir;
  266. }
  267. /* create /proc/acpi/button/lid/LID/ */
  268. acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
  269. if (!acpi_device_dir(device)) {
  270. ret = -ENODEV;
  271. goto remove_lid_dir;
  272. }
  273. /* create /proc/acpi/button/lid/LID/state */
  274. entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO,
  275. acpi_device_dir(device), acpi_button_state_seq_show,
  276. device);
  277. if (!entry) {
  278. ret = -ENODEV;
  279. goto remove_dev_dir;
  280. }
  281. done:
  282. return ret;
  283. remove_dev_dir:
  284. remove_proc_entry(acpi_device_bid(device),
  285. acpi_lid_dir);
  286. acpi_device_dir(device) = NULL;
  287. remove_lid_dir:
  288. remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
  289. acpi_lid_dir = NULL;
  290. remove_button_dir:
  291. remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
  292. acpi_button_dir = NULL;
  293. goto done;
  294. }
  295. static int acpi_button_remove_fs(struct acpi_device *device)
  296. {
  297. struct acpi_button *button = acpi_driver_data(device);
  298. if (button->type != ACPI_BUTTON_TYPE_LID)
  299. return 0;
  300. remove_proc_entry(ACPI_BUTTON_FILE_STATE,
  301. acpi_device_dir(device));
  302. remove_proc_entry(acpi_device_bid(device),
  303. acpi_lid_dir);
  304. acpi_device_dir(device) = NULL;
  305. remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
  306. acpi_lid_dir = NULL;
  307. remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
  308. acpi_button_dir = NULL;
  309. return 0;
  310. }
  311. /* --------------------------------------------------------------------------
  312. Driver Interface
  313. -------------------------------------------------------------------------- */
  314. int acpi_lid_open(void)
  315. {
  316. if (!lid_device)
  317. return -ENODEV;
  318. return acpi_lid_evaluate_state(lid_device);
  319. }
  320. EXPORT_SYMBOL(acpi_lid_open);
  321. static int acpi_lid_update_state(struct acpi_device *device,
  322. bool signal_wakeup)
  323. {
  324. int state;
  325. state = acpi_lid_evaluate_state(device);
  326. if (state < 0)
  327. return state;
  328. if (state && signal_wakeup)
  329. acpi_pm_wakeup_event(&device->dev);
  330. return acpi_lid_notify_state(device, state);
  331. }
  332. static void acpi_lid_initialize_state(struct acpi_device *device)
  333. {
  334. struct acpi_button *button = acpi_driver_data(device);
  335. switch (lid_init_state) {
  336. case ACPI_BUTTON_LID_INIT_OPEN:
  337. (void)acpi_lid_notify_state(device, 1);
  338. break;
  339. case ACPI_BUTTON_LID_INIT_METHOD:
  340. (void)acpi_lid_update_state(device, false);
  341. break;
  342. case ACPI_BUTTON_LID_INIT_IGNORE:
  343. default:
  344. break;
  345. }
  346. button->lid_state_initialized = true;
  347. }
  348. static void acpi_button_notify(struct acpi_device *device, u32 event)
  349. {
  350. struct acpi_button *button = acpi_driver_data(device);
  351. struct input_dev *input;
  352. switch (event) {
  353. case ACPI_FIXED_HARDWARE_EVENT:
  354. event = ACPI_BUTTON_NOTIFY_STATUS;
  355. fallthrough;
  356. case ACPI_BUTTON_NOTIFY_STATUS:
  357. input = button->input;
  358. if (button->type == ACPI_BUTTON_TYPE_LID) {
  359. if (button->lid_state_initialized)
  360. acpi_lid_update_state(device, true);
  361. } else {
  362. int keycode;
  363. acpi_pm_wakeup_event(&device->dev);
  364. if (button->suspended)
  365. break;
  366. keycode = test_bit(KEY_SLEEP, input->keybit) ?
  367. KEY_SLEEP : KEY_POWER;
  368. input_report_key(input, keycode, 1);
  369. input_sync(input);
  370. input_report_key(input, keycode, 0);
  371. input_sync(input);
  372. acpi_bus_generate_netlink_event(
  373. device->pnp.device_class,
  374. dev_name(&device->dev),
  375. event, ++button->pushed);
  376. }
  377. break;
  378. default:
  379. ACPI_DEBUG_PRINT((ACPI_DB_INFO,
  380. "Unsupported event [0x%x]\n", event));
  381. break;
  382. }
  383. }
  384. #ifdef CONFIG_PM_SLEEP
  385. static int acpi_button_suspend(struct device *dev)
  386. {
  387. struct acpi_device *device = to_acpi_device(dev);
  388. struct acpi_button *button = acpi_driver_data(device);
  389. button->suspended = true;
  390. return 0;
  391. }
  392. static int acpi_button_resume(struct device *dev)
  393. {
  394. struct acpi_device *device = to_acpi_device(dev);
  395. struct acpi_button *button = acpi_driver_data(device);
  396. button->suspended = false;
  397. if (button->type == ACPI_BUTTON_TYPE_LID) {
  398. button->last_state = !!acpi_lid_evaluate_state(device);
  399. button->last_time = ktime_get();
  400. acpi_lid_initialize_state(device);
  401. }
  402. return 0;
  403. }
  404. #endif
  405. static int acpi_lid_input_open(struct input_dev *input)
  406. {
  407. struct acpi_device *device = input_get_drvdata(input);
  408. struct acpi_button *button = acpi_driver_data(device);
  409. button->last_state = !!acpi_lid_evaluate_state(device);
  410. button->last_time = ktime_get();
  411. acpi_lid_initialize_state(device);
  412. return 0;
  413. }
  414. static int acpi_button_add(struct acpi_device *device)
  415. {
  416. struct acpi_button *button;
  417. struct input_dev *input;
  418. const char *hid = acpi_device_hid(device);
  419. char *name, *class;
  420. int error;
  421. if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
  422. lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)
  423. return -ENODEV;
  424. button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
  425. if (!button)
  426. return -ENOMEM;
  427. device->driver_data = button;
  428. button->input = input = input_allocate_device();
  429. if (!input) {
  430. error = -ENOMEM;
  431. goto err_free_button;
  432. }
  433. name = acpi_device_name(device);
  434. class = acpi_device_class(device);
  435. if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
  436. !strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
  437. button->type = ACPI_BUTTON_TYPE_POWER;
  438. strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
  439. sprintf(class, "%s/%s",
  440. ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
  441. } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
  442. !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
  443. button->type = ACPI_BUTTON_TYPE_SLEEP;
  444. strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
  445. sprintf(class, "%s/%s",
  446. ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
  447. } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
  448. button->type = ACPI_BUTTON_TYPE_LID;
  449. strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
  450. sprintf(class, "%s/%s",
  451. ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
  452. input->open = acpi_lid_input_open;
  453. } else {
  454. printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
  455. error = -ENODEV;
  456. goto err_free_input;
  457. }
  458. error = acpi_button_add_fs(device);
  459. if (error)
  460. goto err_free_input;
  461. snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
  462. input->name = name;
  463. input->phys = button->phys;
  464. input->id.bustype = BUS_HOST;
  465. input->id.product = button->type;
  466. input->dev.parent = &device->dev;
  467. switch (button->type) {
  468. case ACPI_BUTTON_TYPE_POWER:
  469. input_set_capability(input, EV_KEY, KEY_POWER);
  470. break;
  471. case ACPI_BUTTON_TYPE_SLEEP:
  472. input_set_capability(input, EV_KEY, KEY_SLEEP);
  473. break;
  474. case ACPI_BUTTON_TYPE_LID:
  475. input_set_capability(input, EV_SW, SW_LID);
  476. break;
  477. }
  478. input_set_drvdata(input, device);
  479. error = input_register_device(input);
  480. if (error)
  481. goto err_remove_fs;
  482. if (button->type == ACPI_BUTTON_TYPE_LID) {
  483. /*
  484. * This assumes there's only one lid device, or if there are
  485. * more we only care about the last one...
  486. */
  487. lid_device = device;
  488. }
  489. device_init_wakeup(&device->dev, true);
  490. printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
  491. return 0;
  492. err_remove_fs:
  493. acpi_button_remove_fs(device);
  494. err_free_input:
  495. input_free_device(input);
  496. err_free_button:
  497. kfree(button);
  498. return error;
  499. }
  500. static int acpi_button_remove(struct acpi_device *device)
  501. {
  502. struct acpi_button *button = acpi_driver_data(device);
  503. acpi_button_remove_fs(device);
  504. input_unregister_device(button->input);
  505. kfree(button);
  506. return 0;
  507. }
  508. static int param_set_lid_init_state(const char *val,
  509. const struct kernel_param *kp)
  510. {
  511. int i;
  512. i = sysfs_match_string(lid_init_state_str, val);
  513. if (i < 0)
  514. return i;
  515. lid_init_state = i;
  516. pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]);
  517. return 0;
  518. }
  519. static int param_get_lid_init_state(char *buf, const struct kernel_param *kp)
  520. {
  521. int i, c = 0;
  522. for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++)
  523. if (i == lid_init_state)
  524. c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]);
  525. else
  526. c += sprintf(buf + c, "%s ", lid_init_state_str[i]);
  527. buf[c - 1] = '\n'; /* Replace the final space with a newline */
  528. return c;
  529. }
  530. module_param_call(lid_init_state,
  531. param_set_lid_init_state, param_get_lid_init_state,
  532. NULL, 0644);
  533. MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
  534. static int acpi_button_register_driver(struct acpi_driver *driver)
  535. {
  536. const struct dmi_system_id *dmi_id;
  537. if (lid_init_state == -1) {
  538. dmi_id = dmi_first_match(dmi_lid_quirks);
  539. if (dmi_id)
  540. lid_init_state = (long)dmi_id->driver_data;
  541. else
  542. lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
  543. }
  544. /*
  545. * Modules such as nouveau.ko and i915.ko have a link time dependency
  546. * on acpi_lid_open(), and would therefore not be loadable on ACPI
  547. * capable kernels booted in non-ACPI mode if the return value of
  548. * acpi_bus_register_driver() is returned from here with ACPI disabled
  549. * when this driver is built as a module.
  550. */
  551. if (acpi_disabled)
  552. return 0;
  553. return acpi_bus_register_driver(driver);
  554. }
  555. static void acpi_button_unregister_driver(struct acpi_driver *driver)
  556. {
  557. if (!acpi_disabled)
  558. acpi_bus_unregister_driver(driver);
  559. }
  560. module_driver(acpi_button_driver, acpi_button_register_driver,
  561. acpi_button_unregister_driver);