diff options
Diffstat (limited to 'drivers/acpi/dock.c')
-rw-r--r-- | drivers/acpi/dock.c | 740 |
1 files changed, 740 insertions, 0 deletions
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c new file mode 100644 index 000000000000..1c0a39d8b04e --- /dev/null +++ b/drivers/acpi/dock.c | |||
@@ -0,0 +1,740 @@ | |||
1 | /* | ||
2 | * dock.c - ACPI dock station driver | ||
3 | * | ||
4 | * Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com> | ||
5 | * | ||
6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along | ||
19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
21 | * | ||
22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
23 | */ | ||
24 | |||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/types.h> | ||
29 | #include <linux/notifier.h> | ||
30 | #include <acpi/acpi_bus.h> | ||
31 | #include <acpi/acpi_drivers.h> | ||
32 | |||
33 | #define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver" | ||
34 | |||
35 | ACPI_MODULE_NAME("dock") | ||
36 | MODULE_AUTHOR("Kristen Carlson Accardi"); | ||
37 | MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | |||
40 | static struct atomic_notifier_head dock_notifier_list; | ||
41 | |||
42 | struct dock_station { | ||
43 | acpi_handle handle; | ||
44 | unsigned long last_dock_time; | ||
45 | u32 flags; | ||
46 | spinlock_t dd_lock; | ||
47 | spinlock_t hp_lock; | ||
48 | struct list_head dependent_devices; | ||
49 | struct list_head hotplug_devices; | ||
50 | }; | ||
51 | |||
52 | struct dock_dependent_device { | ||
53 | struct list_head list; | ||
54 | struct list_head hotplug_list; | ||
55 | acpi_handle handle; | ||
56 | acpi_notify_handler handler; | ||
57 | void *context; | ||
58 | }; | ||
59 | |||
60 | #define DOCK_DOCKING 0x00000001 | ||
61 | #define DOCK_EVENT KOBJ_DOCK | ||
62 | #define UNDOCK_EVENT KOBJ_UNDOCK | ||
63 | |||
64 | static struct dock_station *dock_station; | ||
65 | |||
66 | /***************************************************************************** | ||
67 | * Dock Dependent device functions * | ||
68 | *****************************************************************************/ | ||
69 | /** | ||
70 | * alloc_dock_dependent_device - allocate and init a dependent device | ||
71 | * @handle: the acpi_handle of the dependent device | ||
72 | * | ||
73 | * Allocate memory for a dependent device structure for a device referenced | ||
74 | * by the acpi handle | ||
75 | */ | ||
76 | static struct dock_dependent_device * | ||
77 | alloc_dock_dependent_device(acpi_handle handle) | ||
78 | { | ||
79 | struct dock_dependent_device *dd; | ||
80 | |||
81 | dd = kzalloc(sizeof(*dd), GFP_KERNEL); | ||
82 | if (dd) { | ||
83 | dd->handle = handle; | ||
84 | INIT_LIST_HEAD(&dd->list); | ||
85 | INIT_LIST_HEAD(&dd->hotplug_list); | ||
86 | } | ||
87 | return dd; | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * add_dock_dependent_device - associate a device with the dock station | ||
92 | * @ds: The dock station | ||
93 | * @dd: The dependent device | ||
94 | * | ||
95 | * Add the dependent device to the dock's dependent device list. | ||
96 | */ | ||
97 | static void | ||
98 | add_dock_dependent_device(struct dock_station *ds, | ||
99 | struct dock_dependent_device *dd) | ||
100 | { | ||
101 | spin_lock(&ds->dd_lock); | ||
102 | list_add_tail(&dd->list, &ds->dependent_devices); | ||
103 | spin_unlock(&ds->dd_lock); | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * dock_add_hotplug_device - associate a hotplug handler with the dock station | ||
108 | * @ds: The dock station | ||
109 | * @dd: The dependent device struct | ||
110 | * | ||
111 | * Add the dependent device to the dock's hotplug device list | ||
112 | */ | ||
113 | static void | ||
114 | dock_add_hotplug_device(struct dock_station *ds, | ||
115 | struct dock_dependent_device *dd) | ||
116 | { | ||
117 | spin_lock(&ds->hp_lock); | ||
118 | list_add_tail(&dd->hotplug_list, &ds->hotplug_devices); | ||
119 | spin_unlock(&ds->hp_lock); | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * dock_del_hotplug_device - remove a hotplug handler from the dock station | ||
124 | * @ds: The dock station | ||
125 | * @dd: the dependent device struct | ||
126 | * | ||
127 | * Delete the dependent device from the dock's hotplug device list | ||
128 | */ | ||
129 | static void | ||
130 | dock_del_hotplug_device(struct dock_station *ds, | ||
131 | struct dock_dependent_device *dd) | ||
132 | { | ||
133 | spin_lock(&ds->hp_lock); | ||
134 | list_del(&dd->hotplug_list); | ||
135 | spin_unlock(&ds->hp_lock); | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * find_dock_dependent_device - get a device dependent on this dock | ||
140 | * @ds: the dock station | ||
141 | * @handle: the acpi_handle of the device we want | ||
142 | * | ||
143 | * iterate over the dependent device list for this dock. If the | ||
144 | * dependent device matches the handle, return. | ||
145 | */ | ||
146 | static struct dock_dependent_device * | ||
147 | find_dock_dependent_device(struct dock_station *ds, acpi_handle handle) | ||
148 | { | ||
149 | struct dock_dependent_device *dd; | ||
150 | |||
151 | spin_lock(&ds->dd_lock); | ||
152 | list_for_each_entry(dd, &ds->dependent_devices, list) { | ||
153 | if (handle == dd->handle) { | ||
154 | spin_unlock(&ds->dd_lock); | ||
155 | return dd; | ||
156 | } | ||
157 | } | ||
158 | spin_unlock(&ds->dd_lock); | ||
159 | return NULL; | ||
160 | } | ||
161 | |||
162 | /***************************************************************************** | ||
163 | * Dock functions * | ||
164 | *****************************************************************************/ | ||
165 | /** | ||
166 | * is_dock - see if a device is a dock station | ||
167 | * @handle: acpi handle of the device | ||
168 | * | ||
169 | * If an acpi object has a _DCK method, then it is by definition a dock | ||
170 | * station, so return true. | ||
171 | */ | ||
172 | static int is_dock(acpi_handle handle) | ||
173 | { | ||
174 | acpi_status status; | ||
175 | acpi_handle tmp; | ||
176 | |||
177 | status = acpi_get_handle(handle, "_DCK", &tmp); | ||
178 | if (ACPI_FAILURE(status)) | ||
179 | return 0; | ||
180 | return 1; | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * is_dock_device - see if a device is on a dock station | ||
185 | * @handle: acpi handle of the device | ||
186 | * | ||
187 | * If this device is either the dock station itself, | ||
188 | * or is a device dependent on the dock station, then it | ||
189 | * is a dock device | ||
190 | */ | ||
191 | int is_dock_device(acpi_handle handle) | ||
192 | { | ||
193 | if (!dock_station) | ||
194 | return 0; | ||
195 | |||
196 | if (is_dock(handle) || find_dock_dependent_device(dock_station, handle)) | ||
197 | return 1; | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | EXPORT_SYMBOL_GPL(is_dock_device); | ||
203 | |||
204 | /** | ||
205 | * dock_present - see if the dock station is present. | ||
206 | * @ds: the dock station | ||
207 | * | ||
208 | * execute the _STA method. note that present does not | ||
209 | * imply that we are docked. | ||
210 | */ | ||
211 | static int dock_present(struct dock_station *ds) | ||
212 | { | ||
213 | unsigned long sta; | ||
214 | acpi_status status; | ||
215 | |||
216 | if (ds) { | ||
217 | status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta); | ||
218 | if (ACPI_SUCCESS(status) && sta) | ||
219 | return 1; | ||
220 | } | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | |||
225 | |||
226 | /** | ||
227 | * dock_create_acpi_device - add new devices to acpi | ||
228 | * @handle - handle of the device to add | ||
229 | * | ||
230 | * This function will create a new acpi_device for the given | ||
231 | * handle if one does not exist already. This should cause | ||
232 | * acpi to scan for drivers for the given devices, and call | ||
233 | * matching driver's add routine. | ||
234 | * | ||
235 | * Returns a pointer to the acpi_device corresponding to the handle. | ||
236 | */ | ||
237 | static struct acpi_device * dock_create_acpi_device(acpi_handle handle) | ||
238 | { | ||
239 | struct acpi_device *device = NULL; | ||
240 | struct acpi_device *parent_device; | ||
241 | acpi_handle parent; | ||
242 | int ret; | ||
243 | |||
244 | if (acpi_bus_get_device(handle, &device)) { | ||
245 | /* | ||
246 | * no device created for this object, | ||
247 | * so we should create one. | ||
248 | */ | ||
249 | acpi_get_parent(handle, &parent); | ||
250 | if (acpi_bus_get_device(parent, &parent_device)) | ||
251 | parent_device = NULL; | ||
252 | |||
253 | ret = acpi_bus_add(&device, parent_device, handle, | ||
254 | ACPI_BUS_TYPE_DEVICE); | ||
255 | if (ret) { | ||
256 | pr_debug("error adding bus, %x\n", | ||
257 | -ret); | ||
258 | return NULL; | ||
259 | } | ||
260 | } | ||
261 | return device; | ||
262 | } | ||
263 | |||
264 | /** | ||
265 | * dock_remove_acpi_device - remove the acpi_device struct from acpi | ||
266 | * @handle - the handle of the device to remove | ||
267 | * | ||
268 | * Tell acpi to remove the acpi_device. This should cause any loaded | ||
269 | * driver to have it's remove routine called. | ||
270 | */ | ||
271 | static void dock_remove_acpi_device(acpi_handle handle) | ||
272 | { | ||
273 | struct acpi_device *device; | ||
274 | int ret; | ||
275 | |||
276 | if (!acpi_bus_get_device(handle, &device)) { | ||
277 | ret = acpi_bus_trim(device, 1); | ||
278 | if (ret) | ||
279 | pr_debug("error removing bus, %x\n", -ret); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | |||
284 | /** | ||
285 | * hotplug_dock_devices - insert or remove devices on the dock station | ||
286 | * @ds: the dock station | ||
287 | * @event: either bus check or eject request | ||
288 | * | ||
289 | * Some devices on the dock station need to have drivers called | ||
290 | * to perform hotplug operations after a dock event has occurred. | ||
291 | * Traverse the list of dock devices that have registered a | ||
292 | * hotplug handler, and call the handler. | ||
293 | */ | ||
294 | static void hotplug_dock_devices(struct dock_station *ds, u32 event) | ||
295 | { | ||
296 | struct dock_dependent_device *dd; | ||
297 | |||
298 | spin_lock(&ds->hp_lock); | ||
299 | |||
300 | /* | ||
301 | * First call driver specific hotplug functions | ||
302 | */ | ||
303 | list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) { | ||
304 | if (dd->handler) | ||
305 | dd->handler(dd->handle, event, dd->context); | ||
306 | } | ||
307 | |||
308 | /* | ||
309 | * Now make sure that an acpi_device is created for each | ||
310 | * dependent device, or removed if this is an eject request. | ||
311 | * This will cause acpi_drivers to be stopped/started if they | ||
312 | * exist | ||
313 | */ | ||
314 | list_for_each_entry(dd, &ds->dependent_devices, list) { | ||
315 | if (event == ACPI_NOTIFY_EJECT_REQUEST) | ||
316 | dock_remove_acpi_device(dd->handle); | ||
317 | else | ||
318 | dock_create_acpi_device(dd->handle); | ||
319 | } | ||
320 | spin_unlock(&ds->hp_lock); | ||
321 | } | ||
322 | |||
323 | static void dock_event(struct dock_station *ds, u32 event, int num) | ||
324 | { | ||
325 | struct acpi_device *device; | ||
326 | |||
327 | device = dock_create_acpi_device(ds->handle); | ||
328 | if (device) | ||
329 | kobject_uevent(&device->kobj, num); | ||
330 | } | ||
331 | |||
332 | /** | ||
333 | * eject_dock - respond to a dock eject request | ||
334 | * @ds: the dock station | ||
335 | * | ||
336 | * This is called after _DCK is called, to execute the dock station's | ||
337 | * _EJ0 method. | ||
338 | */ | ||
339 | static void eject_dock(struct dock_station *ds) | ||
340 | { | ||
341 | struct acpi_object_list arg_list; | ||
342 | union acpi_object arg; | ||
343 | acpi_status status; | ||
344 | acpi_handle tmp; | ||
345 | |||
346 | /* all dock devices should have _EJ0, but check anyway */ | ||
347 | status = acpi_get_handle(ds->handle, "_EJ0", &tmp); | ||
348 | if (ACPI_FAILURE(status)) { | ||
349 | pr_debug("No _EJ0 support for dock device\n"); | ||
350 | return; | ||
351 | } | ||
352 | |||
353 | arg_list.count = 1; | ||
354 | arg_list.pointer = &arg; | ||
355 | arg.type = ACPI_TYPE_INTEGER; | ||
356 | arg.integer.value = 1; | ||
357 | |||
358 | if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0", | ||
359 | &arg_list, NULL))) | ||
360 | pr_debug("Failed to evaluate _EJ0!\n"); | ||
361 | } | ||
362 | |||
363 | /** | ||
364 | * handle_dock - handle a dock event | ||
365 | * @ds: the dock station | ||
366 | * @dock: to dock, or undock - that is the question | ||
367 | * | ||
368 | * Execute the _DCK method in response to an acpi event | ||
369 | */ | ||
370 | static void handle_dock(struct dock_station *ds, int dock) | ||
371 | { | ||
372 | acpi_status status; | ||
373 | struct acpi_object_list arg_list; | ||
374 | union acpi_object arg; | ||
375 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
376 | struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
377 | union acpi_object *obj; | ||
378 | |||
379 | acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer); | ||
380 | obj = name_buffer.pointer; | ||
381 | |||
382 | printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking"); | ||
383 | |||
384 | /* _DCK method has one argument */ | ||
385 | arg_list.count = 1; | ||
386 | arg_list.pointer = &arg; | ||
387 | arg.type = ACPI_TYPE_INTEGER; | ||
388 | arg.integer.value = dock; | ||
389 | status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer); | ||
390 | if (ACPI_FAILURE(status)) | ||
391 | pr_debug("%s: failed to execute _DCK\n", obj->string.pointer); | ||
392 | kfree(buffer.pointer); | ||
393 | kfree(name_buffer.pointer); | ||
394 | } | ||
395 | |||
396 | static inline void dock(struct dock_station *ds) | ||
397 | { | ||
398 | handle_dock(ds, 1); | ||
399 | } | ||
400 | |||
401 | static inline void undock(struct dock_station *ds) | ||
402 | { | ||
403 | handle_dock(ds, 0); | ||
404 | } | ||
405 | |||
406 | static inline void begin_dock(struct dock_station *ds) | ||
407 | { | ||
408 | ds->flags |= DOCK_DOCKING; | ||
409 | } | ||
410 | |||
411 | static inline void complete_dock(struct dock_station *ds) | ||
412 | { | ||
413 | ds->flags &= ~(DOCK_DOCKING); | ||
414 | ds->last_dock_time = jiffies; | ||
415 | } | ||
416 | |||
417 | /** | ||
418 | * dock_in_progress - see if we are in the middle of handling a dock event | ||
419 | * @ds: the dock station | ||
420 | * | ||
421 | * Sometimes while docking, false dock events can be sent to the driver | ||
422 | * because good connections aren't made or some other reason. Ignore these | ||
423 | * if we are in the middle of doing something. | ||
424 | */ | ||
425 | static int dock_in_progress(struct dock_station *ds) | ||
426 | { | ||
427 | if ((ds->flags & DOCK_DOCKING) || | ||
428 | time_before(jiffies, (ds->last_dock_time + HZ))) | ||
429 | return 1; | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | /** | ||
434 | * register_dock_notifier - add yourself to the dock notifier list | ||
435 | * @nb: the callers notifier block | ||
436 | * | ||
437 | * If a driver wishes to be notified about dock events, they can | ||
438 | * use this function to put a notifier block on the dock notifier list. | ||
439 | * this notifier call chain will be called after a dock event, but | ||
440 | * before hotplugging any new devices. | ||
441 | */ | ||
442 | int register_dock_notifier(struct notifier_block *nb) | ||
443 | { | ||
444 | return atomic_notifier_chain_register(&dock_notifier_list, nb); | ||
445 | } | ||
446 | |||
447 | EXPORT_SYMBOL_GPL(register_dock_notifier); | ||
448 | |||
449 | /** | ||
450 | * unregister_dock_notifier - remove yourself from the dock notifier list | ||
451 | * @nb: the callers notifier block | ||
452 | */ | ||
453 | void unregister_dock_notifier(struct notifier_block *nb) | ||
454 | { | ||
455 | atomic_notifier_chain_unregister(&dock_notifier_list, nb); | ||
456 | } | ||
457 | |||
458 | EXPORT_SYMBOL_GPL(unregister_dock_notifier); | ||
459 | |||
460 | /** | ||
461 | * register_hotplug_dock_device - register a hotplug function | ||
462 | * @handle: the handle of the device | ||
463 | * @handler: the acpi_notifier_handler to call after docking | ||
464 | * @context: device specific data | ||
465 | * | ||
466 | * If a driver would like to perform a hotplug operation after a dock | ||
467 | * event, they can register an acpi_notifiy_handler to be called by | ||
468 | * the dock driver after _DCK is executed. | ||
469 | */ | ||
470 | int | ||
471 | register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler, | ||
472 | void *context) | ||
473 | { | ||
474 | struct dock_dependent_device *dd; | ||
475 | |||
476 | if (!dock_station) | ||
477 | return -ENODEV; | ||
478 | |||
479 | /* | ||
480 | * make sure this handle is for a device dependent on the dock, | ||
481 | * this would include the dock station itself | ||
482 | */ | ||
483 | dd = find_dock_dependent_device(dock_station, handle); | ||
484 | if (dd) { | ||
485 | dd->handler = handler; | ||
486 | dd->context = context; | ||
487 | dock_add_hotplug_device(dock_station, dd); | ||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | return -EINVAL; | ||
492 | } | ||
493 | |||
494 | EXPORT_SYMBOL_GPL(register_hotplug_dock_device); | ||
495 | |||
496 | /** | ||
497 | * unregister_hotplug_dock_device - remove yourself from the hotplug list | ||
498 | * @handle: the acpi handle of the device | ||
499 | */ | ||
500 | void unregister_hotplug_dock_device(acpi_handle handle) | ||
501 | { | ||
502 | struct dock_dependent_device *dd; | ||
503 | |||
504 | if (!dock_station) | ||
505 | return; | ||
506 | |||
507 | dd = find_dock_dependent_device(dock_station, handle); | ||
508 | if (dd) | ||
509 | dock_del_hotplug_device(dock_station, dd); | ||
510 | } | ||
511 | |||
512 | EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); | ||
513 | |||
514 | /** | ||
515 | * dock_notify - act upon an acpi dock notification | ||
516 | * @handle: the dock station handle | ||
517 | * @event: the acpi event | ||
518 | * @data: our driver data struct | ||
519 | * | ||
520 | * If we are notified to dock, then check to see if the dock is | ||
521 | * present and then dock. Notify all drivers of the dock event, | ||
522 | * and then hotplug and devices that may need hotplugging. For undock | ||
523 | * check to make sure the dock device is still present, then undock | ||
524 | * and hotremove all the devices that may need removing. | ||
525 | */ | ||
526 | static void dock_notify(acpi_handle handle, u32 event, void *data) | ||
527 | { | ||
528 | struct dock_station *ds = (struct dock_station *)data; | ||
529 | |||
530 | switch (event) { | ||
531 | case ACPI_NOTIFY_BUS_CHECK: | ||
532 | if (!dock_in_progress(ds) && dock_present(ds)) { | ||
533 | begin_dock(ds); | ||
534 | dock(ds); | ||
535 | if (!dock_present(ds)) { | ||
536 | printk(KERN_ERR PREFIX "Unable to dock!\n"); | ||
537 | break; | ||
538 | } | ||
539 | atomic_notifier_call_chain(&dock_notifier_list, | ||
540 | event, NULL); | ||
541 | hotplug_dock_devices(ds, event); | ||
542 | complete_dock(ds); | ||
543 | dock_event(ds, event, DOCK_EVENT); | ||
544 | } | ||
545 | break; | ||
546 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
547 | /* | ||
548 | * According to acpi spec 3.0a, if a DEVICE_CHECK notification | ||
549 | * is sent and _DCK is present, it is assumed to mean an | ||
550 | * undock request. This notify routine will only be called | ||
551 | * for objects defining _DCK, so we will fall through to eject | ||
552 | * request here. However, we will pass an eject request through | ||
553 | * to the driver who wish to hotplug. | ||
554 | */ | ||
555 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
556 | if (!dock_in_progress(ds) && dock_present(ds)) { | ||
557 | /* | ||
558 | * here we need to generate the undock | ||
559 | * event prior to actually doing the undock | ||
560 | * so that the device struct still exists. | ||
561 | */ | ||
562 | dock_event(ds, event, UNDOCK_EVENT); | ||
563 | hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); | ||
564 | undock(ds); | ||
565 | eject_dock(ds); | ||
566 | if (dock_present(ds)) | ||
567 | printk(KERN_ERR PREFIX "Unable to undock!\n"); | ||
568 | } | ||
569 | break; | ||
570 | default: | ||
571 | printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); | ||
572 | } | ||
573 | } | ||
574 | |||
575 | /** | ||
576 | * find_dock_devices - find devices on the dock station | ||
577 | * @handle: the handle of the device we are examining | ||
578 | * @lvl: unused | ||
579 | * @context: the dock station private data | ||
580 | * @rv: unused | ||
581 | * | ||
582 | * This function is called by acpi_walk_namespace. It will | ||
583 | * check to see if an object has an _EJD method. If it does, then it | ||
584 | * will see if it is dependent on the dock station. | ||
585 | */ | ||
586 | static acpi_status | ||
587 | find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
588 | { | ||
589 | acpi_status status; | ||
590 | acpi_handle tmp; | ||
591 | struct dock_station *ds = (struct dock_station *)context; | ||
592 | struct dock_dependent_device *dd; | ||
593 | |||
594 | status = acpi_bus_get_ejd(handle, &tmp); | ||
595 | if (ACPI_FAILURE(status)) | ||
596 | return AE_OK; | ||
597 | |||
598 | if (tmp == ds->handle) { | ||
599 | dd = alloc_dock_dependent_device(handle); | ||
600 | if (dd) | ||
601 | add_dock_dependent_device(ds, dd); | ||
602 | } | ||
603 | |||
604 | return AE_OK; | ||
605 | } | ||
606 | |||
607 | /** | ||
608 | * dock_add - add a new dock station | ||
609 | * @handle: the dock station handle | ||
610 | * | ||
611 | * allocated and initialize a new dock station device. Find all devices | ||
612 | * that are on the dock station, and register for dock event notifications. | ||
613 | */ | ||
614 | static int dock_add(acpi_handle handle) | ||
615 | { | ||
616 | int ret; | ||
617 | acpi_status status; | ||
618 | struct dock_dependent_device *dd; | ||
619 | |||
620 | /* allocate & initialize the dock_station private data */ | ||
621 | dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); | ||
622 | if (!dock_station) | ||
623 | return -ENOMEM; | ||
624 | dock_station->handle = handle; | ||
625 | dock_station->last_dock_time = jiffies - HZ; | ||
626 | INIT_LIST_HEAD(&dock_station->dependent_devices); | ||
627 | INIT_LIST_HEAD(&dock_station->hotplug_devices); | ||
628 | spin_lock_init(&dock_station->dd_lock); | ||
629 | spin_lock_init(&dock_station->hp_lock); | ||
630 | ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); | ||
631 | |||
632 | /* Find dependent devices */ | ||
633 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
634 | ACPI_UINT32_MAX, find_dock_devices, dock_station, | ||
635 | NULL); | ||
636 | |||
637 | /* add the dock station as a device dependent on itself */ | ||
638 | dd = alloc_dock_dependent_device(handle); | ||
639 | if (!dd) { | ||
640 | kfree(dock_station); | ||
641 | return -ENOMEM; | ||
642 | } | ||
643 | add_dock_dependent_device(dock_station, dd); | ||
644 | |||
645 | /* register for dock events */ | ||
646 | status = acpi_install_notify_handler(dock_station->handle, | ||
647 | ACPI_SYSTEM_NOTIFY, | ||
648 | dock_notify, dock_station); | ||
649 | |||
650 | if (ACPI_FAILURE(status)) { | ||
651 | printk(KERN_ERR PREFIX "Error installing notify handler\n"); | ||
652 | ret = -ENODEV; | ||
653 | goto dock_add_err; | ||
654 | } | ||
655 | |||
656 | printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME); | ||
657 | |||
658 | return 0; | ||
659 | |||
660 | dock_add_err: | ||
661 | kfree(dock_station); | ||
662 | kfree(dd); | ||
663 | return ret; | ||
664 | } | ||
665 | |||
666 | /** | ||
667 | * dock_remove - free up resources related to the dock station | ||
668 | */ | ||
669 | static int dock_remove(void) | ||
670 | { | ||
671 | struct dock_dependent_device *dd, *tmp; | ||
672 | acpi_status status; | ||
673 | |||
674 | if (!dock_station) | ||
675 | return 0; | ||
676 | |||
677 | /* remove dependent devices */ | ||
678 | list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices, | ||
679 | list) | ||
680 | kfree(dd); | ||
681 | |||
682 | /* remove dock notify handler */ | ||
683 | status = acpi_remove_notify_handler(dock_station->handle, | ||
684 | ACPI_SYSTEM_NOTIFY, | ||
685 | dock_notify); | ||
686 | if (ACPI_FAILURE(status)) | ||
687 | printk(KERN_ERR "Error removing notify handler\n"); | ||
688 | |||
689 | /* free dock station memory */ | ||
690 | kfree(dock_station); | ||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | /** | ||
695 | * find_dock - look for a dock station | ||
696 | * @handle: acpi handle of a device | ||
697 | * @lvl: unused | ||
698 | * @context: counter of dock stations found | ||
699 | * @rv: unused | ||
700 | * | ||
701 | * This is called by acpi_walk_namespace to look for dock stations. | ||
702 | */ | ||
703 | static acpi_status | ||
704 | find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
705 | { | ||
706 | int *count = (int *)context; | ||
707 | acpi_status status = AE_OK; | ||
708 | |||
709 | if (is_dock(handle)) { | ||
710 | if (dock_add(handle) >= 0) { | ||
711 | (*count)++; | ||
712 | status = AE_CTRL_TERMINATE; | ||
713 | } | ||
714 | } | ||
715 | return status; | ||
716 | } | ||
717 | |||
718 | static int __init dock_init(void) | ||
719 | { | ||
720 | int num = 0; | ||
721 | |||
722 | dock_station = NULL; | ||
723 | |||
724 | /* look for a dock station */ | ||
725 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
726 | ACPI_UINT32_MAX, find_dock, &num, NULL); | ||
727 | |||
728 | if (!num) | ||
729 | return -ENODEV; | ||
730 | |||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static void __exit dock_exit(void) | ||
735 | { | ||
736 | dock_remove(); | ||
737 | } | ||
738 | |||
739 | postcore_initcall(dock_init); | ||
740 | module_exit(dock_exit); | ||