]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/acpi/button.c
Merge branch 'akpm' (patches from Andrew)
[mirror_ubuntu-artful-kernel.git] / drivers / acpi / button.c
CommitLineData
1da177e4 1/*
50a4da89 2 * button.c - ACPI Button Driver
1da177e4
LT
3 *
4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
6 *
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 *
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 */
25
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/init.h>
b34a8030
AS
29#include <linux/types.h>
30#include <linux/proc_fs.h>
31#include <linux/seq_file.h>
c0968f0e 32#include <linux/input.h>
5a0e3ad6 33#include <linux/slab.h>
8b48463f 34#include <linux/acpi.h>
6270da6f 35#include <acpi/button.h>
1da177e4 36
a192a958
LB
37#define PREFIX "ACPI: "
38
1da177e4 39#define ACPI_BUTTON_CLASS "button"
b34a8030
AS
40#define ACPI_BUTTON_FILE_INFO "info"
41#define ACPI_BUTTON_FILE_STATE "state"
42#define ACPI_BUTTON_TYPE_UNKNOWN 0x00
1da177e4
LT
43#define ACPI_BUTTON_NOTIFY_STATUS 0x80
44
45#define ACPI_BUTTON_SUBCLASS_POWER "power"
4be44fcd 46#define ACPI_BUTTON_HID_POWER "PNP0C0C"
d68b597c 47#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button"
1da177e4 48#define ACPI_BUTTON_TYPE_POWER 0x01
1da177e4
LT
49
50#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep"
51#define ACPI_BUTTON_HID_SLEEP "PNP0C0E"
d68b597c 52#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button"
1da177e4 53#define ACPI_BUTTON_TYPE_SLEEP 0x03
1da177e4
LT
54
55#define ACPI_BUTTON_SUBCLASS_LID "lid"
56#define ACPI_BUTTON_HID_LID "PNP0C0D"
57#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
58#define ACPI_BUTTON_TYPE_LID 0x05
59
60#define _COMPONENT ACPI_BUTTON_COMPONENT
f52fd66d 61ACPI_MODULE_NAME("button");
1da177e4 62
c0968f0e 63MODULE_AUTHOR("Paul Diefenbaugh");
7cda93e0 64MODULE_DESCRIPTION("ACPI Button Driver");
1da177e4
LT
65MODULE_LICENSE("GPL");
66
1ba90e3a
TR
67static const struct acpi_device_id button_device_ids[] = {
68 {ACPI_BUTTON_HID_LID, 0},
69 {ACPI_BUTTON_HID_SLEEP, 0},
70 {ACPI_BUTTON_HID_SLEEPF, 0},
71 {ACPI_BUTTON_HID_POWER, 0},
72 {ACPI_BUTTON_HID_POWERF, 0},
73 {"", 0},
74};
75MODULE_DEVICE_TABLE(acpi, button_device_ids);
76
4be44fcd 77static int acpi_button_add(struct acpi_device *device);
51fac838 78static int acpi_button_remove(struct acpi_device *device);
373cfc36 79static void acpi_button_notify(struct acpi_device *device, u32 event);
1da177e4 80
90692404 81#ifdef CONFIG_PM_SLEEP
e71eeb2a 82static int acpi_button_suspend(struct device *dev);
1be532de 83static int acpi_button_resume(struct device *dev);
2de9fd17 84#else
e71eeb2a 85#define acpi_button_suspend NULL
2de9fd17 86#define acpi_button_resume NULL
90692404 87#endif
e71eeb2a 88static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
1be532de 89
1da177e4 90static struct acpi_driver acpi_button_driver = {
c2b6705b 91 .name = "button",
4be44fcd 92 .class = ACPI_BUTTON_CLASS,
1ba90e3a 93 .ids = button_device_ids,
4be44fcd
LB
94 .ops = {
95 .add = acpi_button_add,
96 .remove = acpi_button_remove,
373cfc36 97 .notify = acpi_button_notify,
c0968f0e 98 },
1be532de 99 .drv.pm = &acpi_button_pm,
1da177e4
LT
100};
101
102struct acpi_button {
c0968f0e
DT
103 unsigned int type;
104 struct input_dev *input;
105 char phys[32]; /* for input device */
4be44fcd 106 unsigned long pushed;
e71eeb2a 107 bool suspended;
1da177e4
LT
108};
109
7e12715e
JB
110static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
111static struct acpi_device *lid_device;
112
b34a8030
AS
113/* --------------------------------------------------------------------------
114 FS Interface (/proc)
115 -------------------------------------------------------------------------- */
116
4be44fcd 117static struct proc_dir_entry *acpi_button_dir;
912b7427 118static struct proc_dir_entry *acpi_lid_dir;
4be44fcd 119
b34a8030
AS
120static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
121{
106c19e7 122 struct acpi_device *device = seq->private;
4be44fcd 123 acpi_status status;
27663c58 124 unsigned long long state;
b34a8030 125
106c19e7 126 status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
c0968f0e
DT
127 seq_printf(seq, "state: %s\n",
128 ACPI_FAILURE(status) ? "unsupported" :
129 (state ? "open" : "closed"));
d550d98d 130 return 0;
b34a8030
AS
131}
132
133static int acpi_button_state_open_fs(struct inode *inode, struct file *file)
134{
d9dda78b 135 return single_open(file, acpi_button_state_seq_show, PDE_DATA(inode));
b34a8030
AS
136}
137
912b7427
ZR
138static const struct file_operations acpi_button_state_fops = {
139 .owner = THIS_MODULE,
140 .open = acpi_button_state_open_fs,
141 .read = seq_read,
142 .llseek = seq_lseek,
143 .release = single_release,
144};
b34a8030 145
4be44fcd 146static int acpi_button_add_fs(struct acpi_device *device)
b34a8030 147{
1bce8113 148 struct acpi_button *button = acpi_driver_data(device);
4be44fcd 149 struct proc_dir_entry *entry = NULL;
912b7427 150 int ret = 0;
b34a8030 151
912b7427
ZR
152 /* procfs I/F for ACPI lid device only */
153 if (button->type != ACPI_BUTTON_TYPE_LID)
154 return 0;
155
156 if (acpi_button_dir || acpi_lid_dir) {
157 printk(KERN_ERR PREFIX "More than one Lid device found!\n");
158 return -EEXIST;
b34a8030
AS
159 }
160
912b7427
ZR
161 /* create /proc/acpi/button */
162 acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
163 if (!acpi_button_dir)
d550d98d 164 return -ENODEV;
b34a8030 165
912b7427
ZR
166 /* create /proc/acpi/button/lid */
167 acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
168 if (!acpi_lid_dir) {
169 ret = -ENODEV;
170 goto remove_button_dir;
171 }
b34a8030 172
912b7427
ZR
173 /* create /proc/acpi/button/lid/LID/ */
174 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
175 if (!acpi_device_dir(device)) {
176 ret = -ENODEV;
177 goto remove_lid_dir;
178 }
b34a8030 179
912b7427
ZR
180 /* create /proc/acpi/button/lid/LID/state */
181 entry = proc_create_data(ACPI_BUTTON_FILE_STATE,
182 S_IRUGO, acpi_device_dir(device),
183 &acpi_button_state_fops, device);
184 if (!entry) {
185 ret = -ENODEV;
186 goto remove_dev_dir;
b34a8030
AS
187 }
188
912b7427
ZR
189done:
190 return ret;
191
192remove_dev_dir:
193 remove_proc_entry(acpi_device_bid(device),
194 acpi_lid_dir);
195 acpi_device_dir(device) = NULL;
196remove_lid_dir:
197 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
198remove_button_dir:
199 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
200 goto done;
b34a8030
AS
201}
202
4be44fcd 203static int acpi_button_remove_fs(struct acpi_device *device)
b34a8030 204{
c0968f0e 205 struct acpi_button *button = acpi_driver_data(device);
b34a8030 206
912b7427
ZR
207 if (button->type != ACPI_BUTTON_TYPE_LID)
208 return 0;
b34a8030 209
912b7427
ZR
210 remove_proc_entry(ACPI_BUTTON_FILE_STATE,
211 acpi_device_dir(device));
212 remove_proc_entry(acpi_device_bid(device),
213 acpi_lid_dir);
214 acpi_device_dir(device) = NULL;
215 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
216 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
b34a8030 217
d550d98d 218 return 0;
b34a8030
AS
219}
220
1da177e4
LT
221/* --------------------------------------------------------------------------
222 Driver Interface
223 -------------------------------------------------------------------------- */
7e12715e
JB
224int acpi_lid_notifier_register(struct notifier_block *nb)
225{
226 return blocking_notifier_chain_register(&acpi_lid_notifier, nb);
227}
228EXPORT_SYMBOL(acpi_lid_notifier_register);
229
230int acpi_lid_notifier_unregister(struct notifier_block *nb)
231{
232 return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb);
233}
234EXPORT_SYMBOL(acpi_lid_notifier_unregister);
235
236int acpi_lid_open(void)
237{
238 acpi_status status;
239 unsigned long long state;
240
2c907b72
JB
241 if (!lid_device)
242 return -ENODEV;
243
7e12715e
JB
244 status = acpi_evaluate_integer(lid_device->handle, "_LID", NULL,
245 &state);
246 if (ACPI_FAILURE(status))
247 return -ENODEV;
248
249 return !!state;
250}
251EXPORT_SYMBOL(acpi_lid_open);
252
106c19e7 253static int acpi_lid_send_state(struct acpi_device *device)
23de5d9e 254{
106c19e7 255 struct acpi_button *button = acpi_driver_data(device);
27663c58 256 unsigned long long state;
23de5d9e 257 acpi_status status;
7e12715e 258 int ret;
23de5d9e 259
106c19e7 260 status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
23de5d9e
AS
261 if (ACPI_FAILURE(status))
262 return -ENODEV;
50a4da89 263
23de5d9e
AS
264 /* input layer checks if event is redundant */
265 input_report_switch(button->input, SW_LID, !state);
df316e93 266 input_sync(button->input);
7e12715e 267
1f83511b
RW
268 if (state)
269 pm_wakeup_event(&device->dev, 0);
270
7e12715e
JB
271 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
272 if (ret == NOTIFY_DONE)
273 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
274 device);
13c199c0
ZY
275 if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
276 /*
277 * It is also regarded as success if the notifier_chain
278 * returns NOTIFY_OK or NOTIFY_DONE.
279 */
280 ret = 0;
281 }
7e12715e 282 return ret;
23de5d9e 283}
1da177e4 284
373cfc36 285static void acpi_button_notify(struct acpi_device *device, u32 event)
1da177e4 286{
373cfc36 287 struct acpi_button *button = acpi_driver_data(device);
c0968f0e 288 struct input_dev *input;
1da177e4 289
1da177e4 290 switch (event) {
373cfc36
BH
291 case ACPI_FIXED_HARDWARE_EVENT:
292 event = ACPI_BUTTON_NOTIFY_STATUS;
293 /* fall through */
1da177e4 294 case ACPI_BUTTON_NOTIFY_STATUS:
c0968f0e 295 input = button->input;
c0968f0e 296 if (button->type == ACPI_BUTTON_TYPE_LID) {
106c19e7 297 acpi_lid_send_state(device);
c0968f0e 298 } else {
e71eeb2a
RW
299 int keycode;
300
301 pm_wakeup_event(&device->dev, 0);
302 if (button->suspended)
303 break;
c0968f0e 304
e71eeb2a
RW
305 keycode = test_bit(KEY_SLEEP, input->keybit) ?
306 KEY_SLEEP : KEY_POWER;
c0968f0e
DT
307 input_report_key(input, keycode, 1);
308 input_sync(input);
309 input_report_key(input, keycode, 0);
df316e93 310 input_sync(input);
1f83511b 311
0bf6368e
LT
312 acpi_bus_generate_netlink_event(
313 device->pnp.device_class,
314 dev_name(&device->dev),
315 event, ++button->pushed);
c0968f0e 316 }
1da177e4
LT
317 break;
318 default:
319 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
4be44fcd 320 "Unsupported event [0x%x]\n", event));
1da177e4
LT
321 break;
322 }
1da177e4
LT
323}
324
90692404 325#ifdef CONFIG_PM_SLEEP
e71eeb2a
RW
326static int acpi_button_suspend(struct device *dev)
327{
328 struct acpi_device *device = to_acpi_device(dev);
329 struct acpi_button *button = acpi_driver_data(device);
330
331 button->suspended = true;
332 return 0;
333}
334
1be532de 335static int acpi_button_resume(struct device *dev)
23de5d9e 336{
1be532de 337 struct acpi_device *device = to_acpi_device(dev);
1bce8113 338 struct acpi_button *button = acpi_driver_data(device);
50a4da89 339
e71eeb2a 340 button->suspended = false;
e2fb9754 341 if (button->type == ACPI_BUTTON_TYPE_LID)
106c19e7 342 return acpi_lid_send_state(device);
23de5d9e
AS
343 return 0;
344}
90692404 345#endif
23de5d9e 346
c0968f0e
DT
347static int acpi_button_add(struct acpi_device *device)
348{
c0968f0e
DT
349 struct acpi_button *button;
350 struct input_dev *input;
620e112c
TR
351 const char *hid = acpi_device_hid(device);
352 char *name, *class;
1bce8113 353 int error;
1da177e4 354
c0968f0e 355 button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
1da177e4 356 if (!button)
d550d98d 357 return -ENOMEM;
1da177e4 358
db89b4f0 359 device->driver_data = button;
1da177e4 360
c0968f0e
DT
361 button->input = input = input_allocate_device();
362 if (!input) {
363 error = -ENOMEM;
364 goto err_free_button;
365 }
366
bf04a772
BH
367 name = acpi_device_name(device);
368 class = acpi_device_class(device);
369
d68b597c
BH
370 if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
371 !strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
1da177e4 372 button->type = ACPI_BUTTON_TYPE_POWER;
bf04a772
BH
373 strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
374 sprintf(class, "%s/%s",
1da177e4 375 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
d68b597c
BH
376 } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
377 !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
1da177e4 378 button->type = ACPI_BUTTON_TYPE_SLEEP;
bf04a772
BH
379 strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
380 sprintf(class, "%s/%s",
1da177e4 381 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
bf04a772 382 } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
1da177e4 383 button->type = ACPI_BUTTON_TYPE_LID;
bf04a772
BH
384 strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
385 sprintf(class, "%s/%s",
1da177e4 386 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
4be44fcd 387 } else {
bf04a772 388 printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
c0968f0e
DT
389 error = -ENODEV;
390 goto err_free_input;
1da177e4
LT
391 }
392
c0968f0e
DT
393 error = acpi_button_add_fs(device);
394 if (error)
395 goto err_free_input;
396
bf04a772 397 snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
c0968f0e 398
bf04a772 399 input->name = name;
c0968f0e
DT
400 input->phys = button->phys;
401 input->id.bustype = BUS_HOST;
402 input->id.product = button->type;
3b34e523 403 input->dev.parent = &device->dev;
b34a8030 404
1da177e4 405 switch (button->type) {
c0968f0e 406 case ACPI_BUTTON_TYPE_POWER:
763f527b 407 input_set_capability(input, EV_KEY, KEY_POWER);
1da177e4 408 break;
c0968f0e
DT
409
410 case ACPI_BUTTON_TYPE_SLEEP:
763f527b 411 input_set_capability(input, EV_KEY, KEY_SLEEP);
1da177e4 412 break;
c0968f0e
DT
413
414 case ACPI_BUTTON_TYPE_LID:
763f527b 415 input_set_capability(input, EV_SW, SW_LID);
1da177e4
LT
416 break;
417 }
418
c0968f0e
DT
419 error = input_register_device(input);
420 if (error)
373cfc36 421 goto err_remove_fs;
7e12715e 422 if (button->type == ACPI_BUTTON_TYPE_LID) {
106c19e7 423 acpi_lid_send_state(device);
7e12715e
JB
424 /*
425 * This assumes there's only one lid device, or if there are
426 * more we only care about the last one...
427 */
428 lid_device = device;
429 }
1da177e4 430
bf04a772 431 printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
c0968f0e 432 return 0;
1da177e4 433
c0968f0e
DT
434 err_remove_fs:
435 acpi_button_remove_fs(device);
436 err_free_input:
437 input_free_device(input);
438 err_free_button:
439 kfree(button);
440 return error;
1da177e4
LT
441}
442
51fac838 443static int acpi_button_remove(struct acpi_device *device)
1da177e4 444{
1bce8113 445 struct acpi_button *button = acpi_driver_data(device);
1da177e4 446
4be44fcd 447 acpi_button_remove_fs(device);
c0968f0e 448 input_unregister_device(button->input);
1da177e4 449 kfree(button);
d550d98d 450 return 0;
1da177e4
LT
451}
452
466e78f7 453module_acpi_driver(acpi_button_driver);