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