xref: /openbmc/linux/drivers/acpi/button.c (revision 78c99ba1)
1 /*
2  *  button.c - ACPI Button Driver
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>
29 #include <linux/types.h>
30 #include <linux/proc_fs.h>
31 #include <linux/seq_file.h>
32 #include <linux/input.h>
33 #include <acpi/acpi_bus.h>
34 #include <acpi/acpi_drivers.h>
35 
36 #define ACPI_BUTTON_CLASS		"button"
37 #define ACPI_BUTTON_FILE_INFO		"info"
38 #define ACPI_BUTTON_FILE_STATE		"state"
39 #define ACPI_BUTTON_TYPE_UNKNOWN	0x00
40 #define ACPI_BUTTON_NOTIFY_STATUS	0x80
41 
42 #define ACPI_BUTTON_SUBCLASS_POWER	"power"
43 #define ACPI_BUTTON_HID_POWER		"PNP0C0C"
44 #define ACPI_BUTTON_DEVICE_NAME_POWER	"Power Button"
45 #define ACPI_BUTTON_TYPE_POWER		0x01
46 
47 #define ACPI_BUTTON_SUBCLASS_SLEEP	"sleep"
48 #define ACPI_BUTTON_HID_SLEEP		"PNP0C0E"
49 #define ACPI_BUTTON_DEVICE_NAME_SLEEP	"Sleep Button"
50 #define ACPI_BUTTON_TYPE_SLEEP		0x03
51 
52 #define ACPI_BUTTON_SUBCLASS_LID	"lid"
53 #define ACPI_BUTTON_HID_LID		"PNP0C0D"
54 #define ACPI_BUTTON_DEVICE_NAME_LID	"Lid Switch"
55 #define ACPI_BUTTON_TYPE_LID		0x05
56 
57 #define _COMPONENT		ACPI_BUTTON_COMPONENT
58 ACPI_MODULE_NAME("button");
59 
60 MODULE_AUTHOR("Paul Diefenbaugh");
61 MODULE_DESCRIPTION("ACPI Button Driver");
62 MODULE_LICENSE("GPL");
63 
64 static const struct acpi_device_id button_device_ids[] = {
65 	{ACPI_BUTTON_HID_LID,    0},
66 	{ACPI_BUTTON_HID_SLEEP,  0},
67 	{ACPI_BUTTON_HID_SLEEPF, 0},
68 	{ACPI_BUTTON_HID_POWER,  0},
69 	{ACPI_BUTTON_HID_POWERF, 0},
70 	{"", 0},
71 };
72 MODULE_DEVICE_TABLE(acpi, button_device_ids);
73 
74 static int acpi_button_add(struct acpi_device *device);
75 static int acpi_button_remove(struct acpi_device *device, int type);
76 static int acpi_button_resume(struct acpi_device *device);
77 static void acpi_button_notify(struct acpi_device *device, u32 event);
78 static int acpi_button_info_open_fs(struct inode *inode, struct file *file);
79 static int acpi_button_state_open_fs(struct inode *inode, struct file *file);
80 
81 static struct acpi_driver acpi_button_driver = {
82 	.name = "button",
83 	.class = ACPI_BUTTON_CLASS,
84 	.ids = button_device_ids,
85 	.ops = {
86 		.add = acpi_button_add,
87 		.resume = acpi_button_resume,
88 		.remove = acpi_button_remove,
89 		.notify = acpi_button_notify,
90 	},
91 };
92 
93 struct acpi_button {
94 	unsigned int type;
95 	struct input_dev *input;
96 	char phys[32];			/* for input device */
97 	unsigned long pushed;
98 };
99 
100 static const struct file_operations acpi_button_info_fops = {
101 	.owner = THIS_MODULE,
102 	.open = acpi_button_info_open_fs,
103 	.read = seq_read,
104 	.llseek = seq_lseek,
105 	.release = single_release,
106 };
107 
108 static const struct file_operations acpi_button_state_fops = {
109 	.owner = THIS_MODULE,
110 	.open = acpi_button_state_open_fs,
111 	.read = seq_read,
112 	.llseek = seq_lseek,
113 	.release = single_release,
114 };
115 
116 /* --------------------------------------------------------------------------
117                               FS Interface (/proc)
118    -------------------------------------------------------------------------- */
119 
120 static struct proc_dir_entry *acpi_button_dir;
121 
122 static int acpi_button_info_seq_show(struct seq_file *seq, void *offset)
123 {
124 	struct acpi_device *device = seq->private;
125 
126 	seq_printf(seq, "type:                    %s\n",
127 		   acpi_device_name(device));
128 	return 0;
129 }
130 
131 static int acpi_button_info_open_fs(struct inode *inode, struct file *file)
132 {
133 	return single_open(file, acpi_button_info_seq_show, PDE(inode)->data);
134 }
135 
136 static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
137 {
138 	struct acpi_device *device = seq->private;
139 	acpi_status status;
140 	unsigned long long state;
141 
142 	status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
143 	seq_printf(seq, "state:      %s\n",
144 		   ACPI_FAILURE(status) ? "unsupported" :
145 			(state ? "open" : "closed"));
146 	return 0;
147 }
148 
149 static int acpi_button_state_open_fs(struct inode *inode, struct file *file)
150 {
151 	return single_open(file, acpi_button_state_seq_show, PDE(inode)->data);
152 }
153 
154 static struct proc_dir_entry *acpi_power_dir;
155 static struct proc_dir_entry *acpi_sleep_dir;
156 static struct proc_dir_entry *acpi_lid_dir;
157 
158 static int acpi_button_add_fs(struct acpi_device *device)
159 {
160 	struct acpi_button *button = acpi_driver_data(device);
161 	struct proc_dir_entry *entry = NULL;
162 
163 	switch (button->type) {
164 	case ACPI_BUTTON_TYPE_POWER:
165 		if (!acpi_power_dir)
166 			acpi_power_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER,
167 						    acpi_button_dir);
168 		entry = acpi_power_dir;
169 		break;
170 	case ACPI_BUTTON_TYPE_SLEEP:
171 		if (!acpi_sleep_dir)
172 			acpi_sleep_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP,
173 						    acpi_button_dir);
174 		entry = acpi_sleep_dir;
175 		break;
176 	case ACPI_BUTTON_TYPE_LID:
177 		if (!acpi_lid_dir)
178 			acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID,
179 						  acpi_button_dir);
180 		entry = acpi_lid_dir;
181 		break;
182 	}
183 
184 	if (!entry)
185 		return -ENODEV;
186 
187 	acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry);
188 	if (!acpi_device_dir(device))
189 		return -ENODEV;
190 
191 	/* 'info' [R] */
192 	entry = proc_create_data(ACPI_BUTTON_FILE_INFO,
193 				 S_IRUGO, acpi_device_dir(device),
194 				 &acpi_button_info_fops, device);
195 	if (!entry)
196 		return -ENODEV;
197 
198 	/* show lid state [R] */
199 	if (button->type == ACPI_BUTTON_TYPE_LID) {
200 		entry = proc_create_data(ACPI_BUTTON_FILE_STATE,
201 					 S_IRUGO, acpi_device_dir(device),
202 					 &acpi_button_state_fops, device);
203 		if (!entry)
204 			return -ENODEV;
205 	}
206 
207 	return 0;
208 }
209 
210 static int acpi_button_remove_fs(struct acpi_device *device)
211 {
212 	struct acpi_button *button = acpi_driver_data(device);
213 
214 	if (acpi_device_dir(device)) {
215 		if (button->type == ACPI_BUTTON_TYPE_LID)
216 			remove_proc_entry(ACPI_BUTTON_FILE_STATE,
217 					  acpi_device_dir(device));
218 		remove_proc_entry(ACPI_BUTTON_FILE_INFO,
219 				  acpi_device_dir(device));
220 
221 		remove_proc_entry(acpi_device_bid(device),
222 				  acpi_device_dir(device)->parent);
223 		acpi_device_dir(device) = NULL;
224 	}
225 
226 	return 0;
227 }
228 
229 /* --------------------------------------------------------------------------
230                                 Driver Interface
231    -------------------------------------------------------------------------- */
232 static int acpi_lid_send_state(struct acpi_device *device)
233 {
234 	struct acpi_button *button = acpi_driver_data(device);
235 	unsigned long long state;
236 	acpi_status status;
237 
238 	status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
239 	if (ACPI_FAILURE(status))
240 		return -ENODEV;
241 
242 	/* input layer checks if event is redundant */
243 	input_report_switch(button->input, SW_LID, !state);
244 	input_sync(button->input);
245 	return 0;
246 }
247 
248 static void acpi_button_notify(struct acpi_device *device, u32 event)
249 {
250 	struct acpi_button *button = acpi_driver_data(device);
251 	struct input_dev *input;
252 
253 	switch (event) {
254 	case ACPI_FIXED_HARDWARE_EVENT:
255 		event = ACPI_BUTTON_NOTIFY_STATUS;
256 		/* fall through */
257 	case ACPI_BUTTON_NOTIFY_STATUS:
258 		input = button->input;
259 		if (button->type == ACPI_BUTTON_TYPE_LID) {
260 			acpi_lid_send_state(device);
261 		} else {
262 			int keycode = test_bit(KEY_SLEEP, input->keybit) ?
263 						KEY_SLEEP : KEY_POWER;
264 
265 			input_report_key(input, keycode, 1);
266 			input_sync(input);
267 			input_report_key(input, keycode, 0);
268 			input_sync(input);
269 		}
270 
271 		acpi_bus_generate_proc_event(device, event, ++button->pushed);
272 		break;
273 	default:
274 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
275 				  "Unsupported event [0x%x]\n", event));
276 		break;
277 	}
278 }
279 
280 static int acpi_button_resume(struct acpi_device *device)
281 {
282 	struct acpi_button *button = acpi_driver_data(device);
283 
284 	if (button->type == ACPI_BUTTON_TYPE_LID)
285 		return acpi_lid_send_state(device);
286 	return 0;
287 }
288 
289 static int acpi_button_add(struct acpi_device *device)
290 {
291 	struct acpi_button *button;
292 	struct input_dev *input;
293 	char *hid, *name, *class;
294 	int error;
295 
296 	button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
297 	if (!button)
298 		return -ENOMEM;
299 
300 	device->driver_data = button;
301 
302 	button->input = input = input_allocate_device();
303 	if (!input) {
304 		error = -ENOMEM;
305 		goto err_free_button;
306 	}
307 
308 	hid = acpi_device_hid(device);
309 	name = acpi_device_name(device);
310 	class = acpi_device_class(device);
311 
312 	if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
313 	    !strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
314 		button->type = ACPI_BUTTON_TYPE_POWER;
315 		strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
316 		sprintf(class, "%s/%s",
317 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
318 	} else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
319 		   !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
320 		button->type = ACPI_BUTTON_TYPE_SLEEP;
321 		strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
322 		sprintf(class, "%s/%s",
323 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
324 	} else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
325 		button->type = ACPI_BUTTON_TYPE_LID;
326 		strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
327 		sprintf(class, "%s/%s",
328 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
329 	} else {
330 		printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
331 		error = -ENODEV;
332 		goto err_free_input;
333 	}
334 
335 	error = acpi_button_add_fs(device);
336 	if (error)
337 		goto err_free_input;
338 
339 	snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
340 
341 	input->name = name;
342 	input->phys = button->phys;
343 	input->id.bustype = BUS_HOST;
344 	input->id.product = button->type;
345 	input->dev.parent = &device->dev;
346 
347 	switch (button->type) {
348 	case ACPI_BUTTON_TYPE_POWER:
349 		input->evbit[0] = BIT_MASK(EV_KEY);
350 		set_bit(KEY_POWER, input->keybit);
351 		break;
352 
353 	case ACPI_BUTTON_TYPE_SLEEP:
354 		input->evbit[0] = BIT_MASK(EV_KEY);
355 		set_bit(KEY_SLEEP, input->keybit);
356 		break;
357 
358 	case ACPI_BUTTON_TYPE_LID:
359 		input->evbit[0] = BIT_MASK(EV_SW);
360 		set_bit(SW_LID, input->swbit);
361 		break;
362 	}
363 
364 	error = input_register_device(input);
365 	if (error)
366 		goto err_remove_fs;
367 	if (button->type == ACPI_BUTTON_TYPE_LID)
368 		acpi_lid_send_state(device);
369 
370 	if (device->wakeup.flags.valid) {
371 		/* Button's GPE is run-wake GPE */
372 		acpi_set_gpe_type(device->wakeup.gpe_device,
373 				  device->wakeup.gpe_number,
374 				  ACPI_GPE_TYPE_WAKE_RUN);
375 		acpi_enable_gpe(device->wakeup.gpe_device,
376 				device->wakeup.gpe_number);
377 		device->wakeup.state.enabled = 1;
378 	}
379 
380 	printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
381 	return 0;
382 
383  err_remove_fs:
384 	acpi_button_remove_fs(device);
385  err_free_input:
386 	input_free_device(input);
387  err_free_button:
388 	kfree(button);
389 	return error;
390 }
391 
392 static int acpi_button_remove(struct acpi_device *device, int type)
393 {
394 	struct acpi_button *button = acpi_driver_data(device);
395 
396 	acpi_button_remove_fs(device);
397 	input_unregister_device(button->input);
398 	kfree(button);
399 	return 0;
400 }
401 
402 static int __init acpi_button_init(void)
403 {
404 	int result;
405 
406 	acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
407 	if (!acpi_button_dir)
408 		return -ENODEV;
409 
410 	result = acpi_bus_register_driver(&acpi_button_driver);
411 	if (result < 0) {
412 		remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
413 		return -ENODEV;
414 	}
415 
416 	return 0;
417 }
418 
419 static void __exit acpi_button_exit(void)
420 {
421 	acpi_bus_unregister_driver(&acpi_button_driver);
422 
423 	if (acpi_power_dir)
424 		remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, acpi_button_dir);
425 	if (acpi_sleep_dir)
426 		remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, acpi_button_dir);
427 	if (acpi_lid_dir)
428 		remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
429 	remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
430 }
431 
432 module_init(acpi_button_init);
433 module_exit(acpi_button_exit);
434