ac.c revision 48fe112744d1ff2e899a6491633ac58a3229aabf
1/*
2 *  acpi_ac.c - ACPI AC Adapter Driver ($Revision: 27 $)
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#ifdef CONFIG_ACPI_PROCFS_POWER
31#include <linux/proc_fs.h>
32#include <linux/seq_file.h>
33#endif
34#ifdef CONFIG_ACPI_SYSFS_POWER
35#include <linux/power_supply.h>
36#endif
37#include <acpi/acpi_bus.h>
38#include <acpi/acpi_drivers.h>
39
40#define ACPI_AC_CLASS			"ac_adapter"
41#define ACPI_AC_DEVICE_NAME		"AC Adapter"
42#define ACPI_AC_FILE_STATE		"state"
43#define ACPI_AC_NOTIFY_STATUS		0x80
44#define ACPI_AC_STATUS_OFFLINE		0x00
45#define ACPI_AC_STATUS_ONLINE		0x01
46#define ACPI_AC_STATUS_UNKNOWN		0xFF
47
48#define _COMPONENT		ACPI_AC_COMPONENT
49ACPI_MODULE_NAME("ac");
50
51MODULE_AUTHOR("Paul Diefenbaugh");
52MODULE_DESCRIPTION("ACPI AC Adapter Driver");
53MODULE_LICENSE("GPL");
54
55#ifdef CONFIG_ACPI_PROCFS_POWER
56extern struct proc_dir_entry *acpi_lock_ac_dir(void);
57extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
58static int acpi_ac_open_fs(struct inode *inode, struct file *file);
59#endif
60
61static int acpi_ac_add(struct acpi_device *device);
62static int acpi_ac_remove(struct acpi_device *device, int type);
63static int acpi_ac_resume(struct acpi_device *device);
64static void acpi_ac_notify(struct acpi_device *device, u32 event);
65
66static const struct acpi_device_id ac_device_ids[] = {
67	{"ACPI0003", 0},
68	{"", 0},
69};
70MODULE_DEVICE_TABLE(acpi, ac_device_ids);
71
72static struct acpi_driver acpi_ac_driver = {
73	.name = "ac",
74	.class = ACPI_AC_CLASS,
75	.ids = ac_device_ids,
76	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
77	.ops = {
78		.add = acpi_ac_add,
79		.remove = acpi_ac_remove,
80		.resume = acpi_ac_resume,
81		.notify = acpi_ac_notify,
82		},
83};
84
85struct acpi_ac {
86#ifdef CONFIG_ACPI_SYSFS_POWER
87	struct power_supply charger;
88#endif
89	struct acpi_device * device;
90	unsigned long long state;
91};
92
93#define to_acpi_ac(x) container_of(x, struct acpi_ac, charger);
94
95#ifdef CONFIG_ACPI_PROCFS_POWER
96static const struct file_operations acpi_ac_fops = {
97	.owner = THIS_MODULE,
98	.open = acpi_ac_open_fs,
99	.read = seq_read,
100	.llseek = seq_lseek,
101	.release = single_release,
102};
103#endif
104#ifdef CONFIG_ACPI_SYSFS_POWER
105static int get_ac_property(struct power_supply *psy,
106			   enum power_supply_property psp,
107			   union power_supply_propval *val)
108{
109	struct acpi_ac *ac = to_acpi_ac(psy);
110	switch (psp) {
111	case POWER_SUPPLY_PROP_ONLINE:
112		val->intval = ac->state;
113		break;
114	default:
115		return -EINVAL;
116	}
117	return 0;
118}
119
120static enum power_supply_property ac_props[] = {
121	POWER_SUPPLY_PROP_ONLINE,
122};
123#endif
124/* --------------------------------------------------------------------------
125                               AC Adapter Management
126   -------------------------------------------------------------------------- */
127
128static int acpi_ac_get_state(struct acpi_ac *ac)
129{
130	acpi_status status = AE_OK;
131
132
133	if (!ac)
134		return -EINVAL;
135
136	status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL, &ac->state);
137	if (ACPI_FAILURE(status)) {
138		ACPI_EXCEPTION((AE_INFO, status, "Error reading AC Adapter state"));
139		ac->state = ACPI_AC_STATUS_UNKNOWN;
140		return -ENODEV;
141	}
142
143	return 0;
144}
145
146#ifdef CONFIG_ACPI_PROCFS_POWER
147/* --------------------------------------------------------------------------
148                              FS Interface (/proc)
149   -------------------------------------------------------------------------- */
150
151static struct proc_dir_entry *acpi_ac_dir;
152
153static int acpi_ac_seq_show(struct seq_file *seq, void *offset)
154{
155	struct acpi_ac *ac = seq->private;
156
157
158	if (!ac)
159		return 0;
160
161	if (acpi_ac_get_state(ac)) {
162		seq_puts(seq, "ERROR: Unable to read AC Adapter state\n");
163		return 0;
164	}
165
166	seq_puts(seq, "state:                   ");
167	switch (ac->state) {
168	case ACPI_AC_STATUS_OFFLINE:
169		seq_puts(seq, "off-line\n");
170		break;
171	case ACPI_AC_STATUS_ONLINE:
172		seq_puts(seq, "on-line\n");
173		break;
174	default:
175		seq_puts(seq, "unknown\n");
176		break;
177	}
178
179	return 0;
180}
181
182static int acpi_ac_open_fs(struct inode *inode, struct file *file)
183{
184	return single_open(file, acpi_ac_seq_show, PDE(inode)->data);
185}
186
187static int acpi_ac_add_fs(struct acpi_device *device)
188{
189	struct proc_dir_entry *entry = NULL;
190
191
192	if (!acpi_device_dir(device)) {
193		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
194						     acpi_ac_dir);
195		if (!acpi_device_dir(device))
196			return -ENODEV;
197	}
198
199	/* 'state' [R] */
200	entry = proc_create_data(ACPI_AC_FILE_STATE,
201				 S_IRUGO, acpi_device_dir(device),
202				 &acpi_ac_fops, acpi_driver_data(device));
203	if (!entry)
204		return -ENODEV;
205	return 0;
206}
207
208static int acpi_ac_remove_fs(struct acpi_device *device)
209{
210
211	if (acpi_device_dir(device)) {
212		remove_proc_entry(ACPI_AC_FILE_STATE, acpi_device_dir(device));
213
214		remove_proc_entry(acpi_device_bid(device), acpi_ac_dir);
215		acpi_device_dir(device) = NULL;
216	}
217
218	return 0;
219}
220#endif
221
222/* --------------------------------------------------------------------------
223                                   Driver Model
224   -------------------------------------------------------------------------- */
225
226static void acpi_ac_notify(struct acpi_device *device, u32 event)
227{
228	struct acpi_ac *ac = acpi_driver_data(device);
229
230
231	if (!ac)
232		return;
233
234	switch (event) {
235	default:
236		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
237				  "Unsupported event [0x%x]\n", event));
238	case ACPI_AC_NOTIFY_STATUS:
239	case ACPI_NOTIFY_BUS_CHECK:
240	case ACPI_NOTIFY_DEVICE_CHECK:
241		acpi_ac_get_state(ac);
242		acpi_bus_generate_proc_event(device, event, (u32) ac->state);
243		acpi_bus_generate_netlink_event(device->pnp.device_class,
244						  dev_name(&device->dev), event,
245						  (u32) ac->state);
246#ifdef CONFIG_ACPI_SYSFS_POWER
247		kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
248#endif
249	}
250
251	return;
252}
253
254static int acpi_ac_add(struct acpi_device *device)
255{
256	int result = 0;
257	struct acpi_ac *ac = NULL;
258
259
260	if (!device)
261		return -EINVAL;
262
263	ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
264	if (!ac)
265		return -ENOMEM;
266
267	ac->device = device;
268	strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME);
269	strcpy(acpi_device_class(device), ACPI_AC_CLASS);
270	device->driver_data = ac;
271
272	result = acpi_ac_get_state(ac);
273	if (result)
274		goto end;
275
276#ifdef CONFIG_ACPI_PROCFS_POWER
277	result = acpi_ac_add_fs(device);
278#endif
279	if (result)
280		goto end;
281#ifdef CONFIG_ACPI_SYSFS_POWER
282	ac->charger.name = acpi_device_bid(device);
283	ac->charger.type = POWER_SUPPLY_TYPE_MAINS;
284	ac->charger.properties = ac_props;
285	ac->charger.num_properties = ARRAY_SIZE(ac_props);
286	ac->charger.get_property = get_ac_property;
287	power_supply_register(&ac->device->dev, &ac->charger);
288#endif
289
290	printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
291	       acpi_device_name(device), acpi_device_bid(device),
292	       ac->state ? "on-line" : "off-line");
293
294      end:
295	if (result) {
296#ifdef CONFIG_ACPI_PROCFS_POWER
297		acpi_ac_remove_fs(device);
298#endif
299		kfree(ac);
300	}
301
302	return result;
303}
304
305static int acpi_ac_resume(struct acpi_device *device)
306{
307	struct acpi_ac *ac;
308	unsigned old_state;
309	if (!device || !acpi_driver_data(device))
310		return -EINVAL;
311	ac = acpi_driver_data(device);
312	old_state = ac->state;
313	if (acpi_ac_get_state(ac))
314		return 0;
315#ifdef CONFIG_ACPI_SYSFS_POWER
316	if (old_state != ac->state)
317		kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
318#endif
319	return 0;
320}
321
322static int acpi_ac_remove(struct acpi_device *device, int type)
323{
324	struct acpi_ac *ac = NULL;
325
326
327	if (!device || !acpi_driver_data(device))
328		return -EINVAL;
329
330	ac = acpi_driver_data(device);
331
332#ifdef CONFIG_ACPI_SYSFS_POWER
333	if (ac->charger.dev)
334		power_supply_unregister(&ac->charger);
335#endif
336#ifdef CONFIG_ACPI_PROCFS_POWER
337	acpi_ac_remove_fs(device);
338#endif
339
340	kfree(ac);
341
342	return 0;
343}
344
345static int __init acpi_ac_init(void)
346{
347	int result;
348
349	if (acpi_disabled)
350		return -ENODEV;
351
352#ifdef CONFIG_ACPI_PROCFS_POWER
353	acpi_ac_dir = acpi_lock_ac_dir();
354	if (!acpi_ac_dir)
355		return -ENODEV;
356#endif
357
358	result = acpi_bus_register_driver(&acpi_ac_driver);
359	if (result < 0) {
360#ifdef CONFIG_ACPI_PROCFS_POWER
361		acpi_unlock_ac_dir(acpi_ac_dir);
362#endif
363		return -ENODEV;
364	}
365
366	return 0;
367}
368
369static void __exit acpi_ac_exit(void)
370{
371
372	acpi_bus_unregister_driver(&acpi_ac_driver);
373
374#ifdef CONFIG_ACPI_PROCFS_POWER
375	acpi_unlock_ac_dir(acpi_ac_dir);
376#endif
377
378	return;
379}
380
381module_init(acpi_ac_init);
382module_exit(acpi_ac_exit);
383