diff options
Diffstat (limited to 'drivers/acpi/container.c')
-rw-r--r-- | drivers/acpi/container.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c new file mode 100644 index 000000000000..5a0adbf8bc04 --- /dev/null +++ b/drivers/acpi/container.c | |||
@@ -0,0 +1,303 @@ | |||
1 | /* | ||
2 | * acpi_container.c - ACPI Generic Container Driver | ||
3 | * ($Revision: ) | ||
4 | * | ||
5 | * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com) | ||
6 | * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com) | ||
7 | * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com) | ||
8 | * Copyright (C) 2004 Intel Corp. | ||
9 | * Copyright (C) 2004 FUJITSU LIMITED | ||
10 | * | ||
11 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or (at | ||
16 | * your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, but | ||
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
21 | * General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License along | ||
24 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
25 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
26 | * | ||
27 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
28 | */ | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/acpi.h> | ||
34 | #include <acpi/acpi_bus.h> | ||
35 | #include <acpi/acpi_drivers.h> | ||
36 | #include <acpi/container.h> | ||
37 | |||
38 | #define ACPI_CONTAINER_DRIVER_NAME "ACPI container driver" | ||
39 | #define ACPI_CONTAINER_DEVICE_NAME "ACPI container device" | ||
40 | #define ACPI_CONTAINER_CLASS "container" | ||
41 | |||
42 | #define INSTALL_NOTIFY_HANDLER 1 | ||
43 | #define UNINSTALL_NOTIFY_HANDLER 2 | ||
44 | |||
45 | #define ACPI_CONTAINER_COMPONENT 0x01000000 | ||
46 | #define _COMPONENT ACPI_CONTAINER_COMPONENT | ||
47 | ACPI_MODULE_NAME ("acpi_container") | ||
48 | |||
49 | MODULE_AUTHOR("Anil S Keshavamurthy"); | ||
50 | MODULE_DESCRIPTION(ACPI_CONTAINER_DRIVER_NAME); | ||
51 | MODULE_LICENSE("GPL"); | ||
52 | |||
53 | #define ACPI_STA_PRESENT (0x00000001) | ||
54 | |||
55 | static int acpi_container_add(struct acpi_device *device); | ||
56 | static int acpi_container_remove(struct acpi_device *device, int type); | ||
57 | |||
58 | static struct acpi_driver acpi_container_driver = { | ||
59 | .name = ACPI_CONTAINER_DRIVER_NAME, | ||
60 | .class = ACPI_CONTAINER_CLASS, | ||
61 | .ids = "ACPI0004,PNP0A05,PNP0A06", | ||
62 | .ops = { | ||
63 | .add = acpi_container_add, | ||
64 | .remove = acpi_container_remove, | ||
65 | }, | ||
66 | }; | ||
67 | |||
68 | |||
69 | /*******************************************************************/ | ||
70 | |||
71 | static int | ||
72 | is_device_present(acpi_handle handle) | ||
73 | { | ||
74 | acpi_handle temp; | ||
75 | acpi_status status; | ||
76 | unsigned long sta; | ||
77 | |||
78 | ACPI_FUNCTION_TRACE("is_device_present"); | ||
79 | |||
80 | status = acpi_get_handle(handle, "_STA", &temp); | ||
81 | if (ACPI_FAILURE(status)) | ||
82 | return_VALUE(1); /* _STA not found, assmue device present */ | ||
83 | |||
84 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); | ||
85 | if (ACPI_FAILURE(status)) | ||
86 | return_VALUE(0); /* Firmware error */ | ||
87 | |||
88 | return_VALUE((sta & ACPI_STA_PRESENT) == ACPI_STA_PRESENT); | ||
89 | } | ||
90 | |||
91 | /*******************************************************************/ | ||
92 | static int | ||
93 | acpi_container_add(struct acpi_device *device) | ||
94 | { | ||
95 | struct acpi_container *container; | ||
96 | |||
97 | ACPI_FUNCTION_TRACE("acpi_container_add"); | ||
98 | |||
99 | if (!device) { | ||
100 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "device is NULL\n")); | ||
101 | return_VALUE(-EINVAL); | ||
102 | } | ||
103 | |||
104 | container = kmalloc(sizeof(struct acpi_container), GFP_KERNEL); | ||
105 | if(!container) | ||
106 | return_VALUE(-ENOMEM); | ||
107 | |||
108 | memset(container, 0, sizeof(struct acpi_container)); | ||
109 | container->handle = device->handle; | ||
110 | strcpy(acpi_device_name(device), ACPI_CONTAINER_DEVICE_NAME); | ||
111 | strcpy(acpi_device_class(device), ACPI_CONTAINER_CLASS); | ||
112 | acpi_driver_data(device) = container; | ||
113 | |||
114 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device <%s> bid <%s>\n", \ | ||
115 | acpi_device_name(device), acpi_device_bid(device))); | ||
116 | |||
117 | |||
118 | return_VALUE(0); | ||
119 | } | ||
120 | |||
121 | static int | ||
122 | acpi_container_remove(struct acpi_device *device, int type) | ||
123 | { | ||
124 | acpi_status status = AE_OK; | ||
125 | struct acpi_container *pc = NULL; | ||
126 | pc = (struct acpi_container*) acpi_driver_data(device); | ||
127 | |||
128 | if (pc) | ||
129 | kfree(pc); | ||
130 | |||
131 | return status; | ||
132 | } | ||
133 | |||
134 | |||
135 | static int | ||
136 | container_device_add(struct acpi_device **device, acpi_handle handle) | ||
137 | { | ||
138 | acpi_handle phandle; | ||
139 | struct acpi_device *pdev; | ||
140 | int result; | ||
141 | |||
142 | ACPI_FUNCTION_TRACE("container_device_add"); | ||
143 | |||
144 | if (acpi_get_parent(handle, &phandle)) { | ||
145 | return_VALUE(-ENODEV); | ||
146 | } | ||
147 | |||
148 | if (acpi_bus_get_device(phandle, &pdev)) { | ||
149 | return_VALUE(-ENODEV); | ||
150 | } | ||
151 | |||
152 | if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_DEVICE)) { | ||
153 | return_VALUE(-ENODEV); | ||
154 | } | ||
155 | |||
156 | result = acpi_bus_scan(*device); | ||
157 | |||
158 | return_VALUE(result); | ||
159 | } | ||
160 | |||
161 | static void | ||
162 | container_notify_cb(acpi_handle handle, u32 type, void *context) | ||
163 | { | ||
164 | struct acpi_device *device = NULL; | ||
165 | int result; | ||
166 | int present; | ||
167 | acpi_status status; | ||
168 | |||
169 | ACPI_FUNCTION_TRACE("container_notify_cb"); | ||
170 | |||
171 | present = is_device_present(handle); | ||
172 | |||
173 | switch (type) { | ||
174 | case ACPI_NOTIFY_BUS_CHECK: | ||
175 | /* Fall through */ | ||
176 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
177 | printk("Container driver received %s event\n", | ||
178 | (type == ACPI_NOTIFY_BUS_CHECK)? | ||
179 | "ACPI_NOTIFY_BUS_CHECK":"ACPI_NOTIFY_DEVICE_CHECK"); | ||
180 | status = acpi_bus_get_device(handle, &device); | ||
181 | if (present) { | ||
182 | if (ACPI_FAILURE(status) || !device) { | ||
183 | result = container_device_add(&device, handle); | ||
184 | if (!result) | ||
185 | kobject_hotplug(&device->kobj, | ||
186 | KOBJ_ONLINE); | ||
187 | else | ||
188 | printk("Failed to add container\n"); | ||
189 | } | ||
190 | } else { | ||
191 | if (ACPI_SUCCESS(status)) { | ||
192 | /* device exist and this is a remove request */ | ||
193 | kobject_hotplug(&device->kobj, KOBJ_OFFLINE); | ||
194 | } | ||
195 | } | ||
196 | break; | ||
197 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
198 | if (!acpi_bus_get_device(handle, &device) && device) { | ||
199 | kobject_hotplug(&device->kobj, KOBJ_OFFLINE); | ||
200 | } | ||
201 | break; | ||
202 | default: | ||
203 | break; | ||
204 | } | ||
205 | return_VOID; | ||
206 | } | ||
207 | |||
208 | static acpi_status | ||
209 | container_walk_namespace_cb(acpi_handle handle, | ||
210 | u32 lvl, | ||
211 | void *context, | ||
212 | void **rv) | ||
213 | { | ||
214 | char *hid = NULL; | ||
215 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
216 | struct acpi_device_info *info; | ||
217 | acpi_status status; | ||
218 | int *action = context; | ||
219 | |||
220 | ACPI_FUNCTION_TRACE("container_walk_namespace_cb"); | ||
221 | |||
222 | status = acpi_get_object_info(handle, &buffer); | ||
223 | if (ACPI_FAILURE(status) || !buffer.pointer) { | ||
224 | return_ACPI_STATUS(AE_OK); | ||
225 | } | ||
226 | |||
227 | info = buffer.pointer; | ||
228 | if (info->valid & ACPI_VALID_HID) | ||
229 | hid = info->hardware_id.value; | ||
230 | |||
231 | if (hid == NULL) { | ||
232 | goto end; | ||
233 | } | ||
234 | |||
235 | if (strcmp(hid, "ACPI0004") && strcmp(hid, "PNP0A05") && | ||
236 | strcmp(hid, "PNP0A06")) { | ||
237 | goto end; | ||
238 | } | ||
239 | |||
240 | switch(*action) { | ||
241 | case INSTALL_NOTIFY_HANDLER: | ||
242 | acpi_install_notify_handler(handle, | ||
243 | ACPI_SYSTEM_NOTIFY, | ||
244 | container_notify_cb, | ||
245 | NULL); | ||
246 | break; | ||
247 | case UNINSTALL_NOTIFY_HANDLER: | ||
248 | acpi_remove_notify_handler(handle, | ||
249 | ACPI_SYSTEM_NOTIFY, | ||
250 | container_notify_cb); | ||
251 | break; | ||
252 | default: | ||
253 | break; | ||
254 | } | ||
255 | |||
256 | end: | ||
257 | acpi_os_free(buffer.pointer); | ||
258 | |||
259 | return_ACPI_STATUS(AE_OK); | ||
260 | } | ||
261 | |||
262 | |||
263 | static int __init | ||
264 | acpi_container_init(void) | ||
265 | { | ||
266 | int result = 0; | ||
267 | int action = INSTALL_NOTIFY_HANDLER; | ||
268 | |||
269 | result = acpi_bus_register_driver(&acpi_container_driver); | ||
270 | if (result < 0) { | ||
271 | return(result); | ||
272 | } | ||
273 | |||
274 | /* register notify handler to every container device */ | ||
275 | acpi_walk_namespace(ACPI_TYPE_DEVICE, | ||
276 | ACPI_ROOT_OBJECT, | ||
277 | ACPI_UINT32_MAX, | ||
278 | container_walk_namespace_cb, | ||
279 | &action, NULL); | ||
280 | |||
281 | return(0); | ||
282 | } | ||
283 | |||
284 | static void __exit | ||
285 | acpi_container_exit(void) | ||
286 | { | ||
287 | int action = UNINSTALL_NOTIFY_HANDLER; | ||
288 | |||
289 | ACPI_FUNCTION_TRACE("acpi_container_exit"); | ||
290 | |||
291 | acpi_walk_namespace(ACPI_TYPE_DEVICE, | ||
292 | ACPI_ROOT_OBJECT, | ||
293 | ACPI_UINT32_MAX, | ||
294 | container_walk_namespace_cb, | ||
295 | &action, NULL); | ||
296 | |||
297 | acpi_bus_unregister_driver(&acpi_container_driver); | ||
298 | |||
299 | return_VOID; | ||
300 | } | ||
301 | |||
302 | module_init(acpi_container_init); | ||
303 | module_exit(acpi_container_exit); | ||