blob: 98ec717b14cf7e1deb84f6645bf82a2561cc80d2 [file] [log] [blame]
Len Brownc8f7a622006-07-09 17:22:28 -04001/*
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>
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -080030#include <linux/platform_device.h>
Al Viro914e2632006-10-18 13:55:46 -040031#include <linux/jiffies.h>
Randy Dunlap62a6d7f2007-03-26 21:38:49 -080032#include <linux/stddef.h>
Len Brownc8f7a622006-07-09 17:22:28 -040033#include <acpi/acpi_bus.h>
34#include <acpi/acpi_drivers.h>
35
Len Brown7cda93e2007-02-12 23:50:02 -050036#define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver"
Len Brownc8f7a622006-07-09 17:22:28 -040037
Len Brownf52fd662007-02-12 22:42:12 -050038ACPI_MODULE_NAME("dock");
Len Brownc8f7a622006-07-09 17:22:28 -040039MODULE_AUTHOR("Kristen Carlson Accardi");
Len Brown7cda93e2007-02-12 23:50:02 -050040MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION);
Len Brownc8f7a622006-07-09 17:22:28 -040041MODULE_LICENSE("GPL");
42
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -070043static int immediate_undock = 1;
44module_param(immediate_undock, bool, 0644);
45MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
46 "undock immediately when the undock button is pressed, 0 will cause"
47 " the driver to wait for userspace to write the undock sysfs file "
48 " before undocking");
49
Len Brownc8f7a622006-07-09 17:22:28 -040050static struct atomic_notifier_head dock_notifier_list;
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -070051static struct platform_device *dock_device;
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -080052static char dock_device_name[] = "dock";
Len Brownc8f7a622006-07-09 17:22:28 -040053
54struct dock_station {
55 acpi_handle handle;
56 unsigned long last_dock_time;
57 u32 flags;
58 spinlock_t dd_lock;
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -080059 struct mutex hp_lock;
Len Brownc8f7a622006-07-09 17:22:28 -040060 struct list_head dependent_devices;
61 struct list_head hotplug_devices;
62};
63
64struct dock_dependent_device {
65 struct list_head list;
66 struct list_head hotplug_list;
67 acpi_handle handle;
68 acpi_notify_handler handler;
69 void *context;
70};
71
72#define DOCK_DOCKING 0x00000001
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -070073#define DOCK_UNDOCKING 0x00000002
Kristen Carlson Accardi56690212006-08-01 14:59:19 -070074#define DOCK_EVENT 3
75#define UNDOCK_EVENT 2
Len Brownc8f7a622006-07-09 17:22:28 -040076
77static struct dock_station *dock_station;
78
79/*****************************************************************************
80 * Dock Dependent device functions *
81 *****************************************************************************/
82/**
83 * alloc_dock_dependent_device - allocate and init a dependent device
84 * @handle: the acpi_handle of the dependent device
85 *
86 * Allocate memory for a dependent device structure for a device referenced
87 * by the acpi handle
88 */
89static struct dock_dependent_device *
90alloc_dock_dependent_device(acpi_handle handle)
91{
92 struct dock_dependent_device *dd;
93
94 dd = kzalloc(sizeof(*dd), GFP_KERNEL);
95 if (dd) {
96 dd->handle = handle;
97 INIT_LIST_HEAD(&dd->list);
98 INIT_LIST_HEAD(&dd->hotplug_list);
99 }
100 return dd;
101}
102
103/**
104 * add_dock_dependent_device - associate a device with the dock station
105 * @ds: The dock station
106 * @dd: The dependent device
107 *
108 * Add the dependent device to the dock's dependent device list.
109 */
110static void
111add_dock_dependent_device(struct dock_station *ds,
112 struct dock_dependent_device *dd)
113{
114 spin_lock(&ds->dd_lock);
115 list_add_tail(&dd->list, &ds->dependent_devices);
116 spin_unlock(&ds->dd_lock);
117}
118
119/**
120 * dock_add_hotplug_device - associate a hotplug handler with the dock station
121 * @ds: The dock station
122 * @dd: The dependent device struct
123 *
124 * Add the dependent device to the dock's hotplug device list
125 */
126static void
127dock_add_hotplug_device(struct dock_station *ds,
128 struct dock_dependent_device *dd)
129{
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800130 mutex_lock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400131 list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800132 mutex_unlock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400133}
134
135/**
136 * dock_del_hotplug_device - remove a hotplug handler from the dock station
137 * @ds: The dock station
138 * @dd: the dependent device struct
139 *
140 * Delete the dependent device from the dock's hotplug device list
141 */
142static void
143dock_del_hotplug_device(struct dock_station *ds,
144 struct dock_dependent_device *dd)
145{
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800146 mutex_lock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400147 list_del(&dd->hotplug_list);
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800148 mutex_unlock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400149}
150
151/**
152 * find_dock_dependent_device - get a device dependent on this dock
153 * @ds: the dock station
154 * @handle: the acpi_handle of the device we want
155 *
156 * iterate over the dependent device list for this dock. If the
157 * dependent device matches the handle, return.
158 */
159static struct dock_dependent_device *
160find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
161{
162 struct dock_dependent_device *dd;
163
164 spin_lock(&ds->dd_lock);
165 list_for_each_entry(dd, &ds->dependent_devices, list) {
166 if (handle == dd->handle) {
167 spin_unlock(&ds->dd_lock);
168 return dd;
169 }
170 }
171 spin_unlock(&ds->dd_lock);
172 return NULL;
173}
174
175/*****************************************************************************
176 * Dock functions *
177 *****************************************************************************/
178/**
179 * is_dock - see if a device is a dock station
180 * @handle: acpi handle of the device
181 *
182 * If an acpi object has a _DCK method, then it is by definition a dock
183 * station, so return true.
184 */
185static int is_dock(acpi_handle handle)
186{
187 acpi_status status;
188 acpi_handle tmp;
189
190 status = acpi_get_handle(handle, "_DCK", &tmp);
191 if (ACPI_FAILURE(status))
192 return 0;
193 return 1;
194}
195
196/**
197 * is_dock_device - see if a device is on a dock station
198 * @handle: acpi handle of the device
199 *
200 * If this device is either the dock station itself,
201 * or is a device dependent on the dock station, then it
202 * is a dock device
203 */
204int is_dock_device(acpi_handle handle)
205{
206 if (!dock_station)
207 return 0;
208
209 if (is_dock(handle) || find_dock_dependent_device(dock_station, handle))
210 return 1;
211
212 return 0;
213}
214
215EXPORT_SYMBOL_GPL(is_dock_device);
216
217/**
218 * dock_present - see if the dock station is present.
219 * @ds: the dock station
220 *
221 * execute the _STA method. note that present does not
222 * imply that we are docked.
223 */
224static int dock_present(struct dock_station *ds)
225{
226 unsigned long sta;
227 acpi_status status;
228
229 if (ds) {
230 status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
231 if (ACPI_SUCCESS(status) && sta)
232 return 1;
233 }
234 return 0;
235}
236
237
238
239/**
240 * dock_create_acpi_device - add new devices to acpi
241 * @handle - handle of the device to add
242 *
243 * This function will create a new acpi_device for the given
244 * handle if one does not exist already. This should cause
245 * acpi to scan for drivers for the given devices, and call
246 * matching driver's add routine.
247 *
248 * Returns a pointer to the acpi_device corresponding to the handle.
249 */
250static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
251{
252 struct acpi_device *device = NULL;
253 struct acpi_device *parent_device;
254 acpi_handle parent;
255 int ret;
256
257 if (acpi_bus_get_device(handle, &device)) {
258 /*
259 * no device created for this object,
260 * so we should create one.
261 */
262 acpi_get_parent(handle, &parent);
263 if (acpi_bus_get_device(parent, &parent_device))
264 parent_device = NULL;
265
266 ret = acpi_bus_add(&device, parent_device, handle,
267 ACPI_BUS_TYPE_DEVICE);
268 if (ret) {
269 pr_debug("error adding bus, %x\n",
270 -ret);
271 return NULL;
272 }
273 }
274 return device;
275}
276
277/**
278 * dock_remove_acpi_device - remove the acpi_device struct from acpi
279 * @handle - the handle of the device to remove
280 *
281 * Tell acpi to remove the acpi_device. This should cause any loaded
282 * driver to have it's remove routine called.
283 */
284static void dock_remove_acpi_device(acpi_handle handle)
285{
286 struct acpi_device *device;
287 int ret;
288
289 if (!acpi_bus_get_device(handle, &device)) {
290 ret = acpi_bus_trim(device, 1);
291 if (ret)
292 pr_debug("error removing bus, %x\n", -ret);
293 }
294}
295
296
297/**
298 * hotplug_dock_devices - insert or remove devices on the dock station
299 * @ds: the dock station
300 * @event: either bus check or eject request
301 *
302 * Some devices on the dock station need to have drivers called
303 * to perform hotplug operations after a dock event has occurred.
304 * Traverse the list of dock devices that have registered a
305 * hotplug handler, and call the handler.
306 */
307static void hotplug_dock_devices(struct dock_station *ds, u32 event)
308{
309 struct dock_dependent_device *dd;
310
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800311 mutex_lock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400312
313 /*
314 * First call driver specific hotplug functions
315 */
316 list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
317 if (dd->handler)
318 dd->handler(dd->handle, event, dd->context);
319 }
320
321 /*
322 * Now make sure that an acpi_device is created for each
323 * dependent device, or removed if this is an eject request.
324 * This will cause acpi_drivers to be stopped/started if they
325 * exist
326 */
327 list_for_each_entry(dd, &ds->dependent_devices, list) {
328 if (event == ACPI_NOTIFY_EJECT_REQUEST)
329 dock_remove_acpi_device(dd->handle);
330 else
331 dock_create_acpi_device(dd->handle);
332 }
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800333 mutex_unlock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400334}
335
336static void dock_event(struct dock_station *ds, u32 event, int num)
337{
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700338 struct device *dev = &dock_device->dev;
Kristen Carlson Accardi56690212006-08-01 14:59:19 -0700339 /*
Kristen Carlson Accardi8ea86e02006-12-11 12:05:08 -0800340 * Indicate that the status of the dock station has
341 * changed.
Kristen Carlson Accardi56690212006-08-01 14:59:19 -0700342 */
Kristen Carlson Accardi8ea86e02006-12-11 12:05:08 -0800343 kobject_uevent(&dev->kobj, KOBJ_CHANGE);
Len Brownc8f7a622006-07-09 17:22:28 -0400344}
345
346/**
347 * eject_dock - respond to a dock eject request
348 * @ds: the dock station
349 *
350 * This is called after _DCK is called, to execute the dock station's
351 * _EJ0 method.
352 */
353static void eject_dock(struct dock_station *ds)
354{
355 struct acpi_object_list arg_list;
356 union acpi_object arg;
357 acpi_status status;
358 acpi_handle tmp;
359
360 /* all dock devices should have _EJ0, but check anyway */
361 status = acpi_get_handle(ds->handle, "_EJ0", &tmp);
362 if (ACPI_FAILURE(status)) {
363 pr_debug("No _EJ0 support for dock device\n");
364 return;
365 }
366
367 arg_list.count = 1;
368 arg_list.pointer = &arg;
369 arg.type = ACPI_TYPE_INTEGER;
370 arg.integer.value = 1;
371
372 if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
373 &arg_list, NULL)))
374 pr_debug("Failed to evaluate _EJ0!\n");
375}
376
377/**
378 * handle_dock - handle a dock event
379 * @ds: the dock station
380 * @dock: to dock, or undock - that is the question
381 *
382 * Execute the _DCK method in response to an acpi event
383 */
384static void handle_dock(struct dock_station *ds, int dock)
385{
386 acpi_status status;
387 struct acpi_object_list arg_list;
388 union acpi_object arg;
389 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
390 struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
391 union acpi_object *obj;
392
393 acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer);
394 obj = name_buffer.pointer;
395
396 printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking");
397
398 /* _DCK method has one argument */
399 arg_list.count = 1;
400 arg_list.pointer = &arg;
401 arg.type = ACPI_TYPE_INTEGER;
402 arg.integer.value = dock;
403 status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
404 if (ACPI_FAILURE(status))
405 pr_debug("%s: failed to execute _DCK\n", obj->string.pointer);
406 kfree(buffer.pointer);
407 kfree(name_buffer.pointer);
408}
409
410static inline void dock(struct dock_station *ds)
411{
412 handle_dock(ds, 1);
413}
414
415static inline void undock(struct dock_station *ds)
416{
417 handle_dock(ds, 0);
418}
419
420static inline void begin_dock(struct dock_station *ds)
421{
422 ds->flags |= DOCK_DOCKING;
423}
424
425static inline void complete_dock(struct dock_station *ds)
426{
427 ds->flags &= ~(DOCK_DOCKING);
428 ds->last_dock_time = jiffies;
429}
430
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700431static inline void begin_undock(struct dock_station *ds)
432{
433 ds->flags |= DOCK_UNDOCKING;
434}
435
436static inline void complete_undock(struct dock_station *ds)
437{
438 ds->flags &= ~(DOCK_UNDOCKING);
439}
440
Len Brownc8f7a622006-07-09 17:22:28 -0400441/**
442 * dock_in_progress - see if we are in the middle of handling a dock event
443 * @ds: the dock station
444 *
445 * Sometimes while docking, false dock events can be sent to the driver
446 * because good connections aren't made or some other reason. Ignore these
447 * if we are in the middle of doing something.
448 */
449static int dock_in_progress(struct dock_station *ds)
450{
451 if ((ds->flags & DOCK_DOCKING) ||
452 time_before(jiffies, (ds->last_dock_time + HZ)))
453 return 1;
454 return 0;
455}
456
457/**
458 * register_dock_notifier - add yourself to the dock notifier list
459 * @nb: the callers notifier block
460 *
461 * If a driver wishes to be notified about dock events, they can
462 * use this function to put a notifier block on the dock notifier list.
463 * this notifier call chain will be called after a dock event, but
464 * before hotplugging any new devices.
465 */
466int register_dock_notifier(struct notifier_block *nb)
467{
Prarit Bhargava2548c062006-12-04 14:50:17 -0800468 if (!dock_station)
469 return -ENODEV;
470
Len Brownc8f7a622006-07-09 17:22:28 -0400471 return atomic_notifier_chain_register(&dock_notifier_list, nb);
472}
473
474EXPORT_SYMBOL_GPL(register_dock_notifier);
475
476/**
477 * unregister_dock_notifier - remove yourself from the dock notifier list
478 * @nb: the callers notifier block
479 */
480void unregister_dock_notifier(struct notifier_block *nb)
481{
Prarit Bhargava2548c062006-12-04 14:50:17 -0800482 if (!dock_station)
483 return;
484
Len Brownc8f7a622006-07-09 17:22:28 -0400485 atomic_notifier_chain_unregister(&dock_notifier_list, nb);
486}
487
488EXPORT_SYMBOL_GPL(unregister_dock_notifier);
489
490/**
491 * register_hotplug_dock_device - register a hotplug function
492 * @handle: the handle of the device
493 * @handler: the acpi_notifier_handler to call after docking
494 * @context: device specific data
495 *
496 * If a driver would like to perform a hotplug operation after a dock
497 * event, they can register an acpi_notifiy_handler to be called by
498 * the dock driver after _DCK is executed.
499 */
500int
501register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
502 void *context)
503{
504 struct dock_dependent_device *dd;
505
506 if (!dock_station)
507 return -ENODEV;
508
509 /*
510 * make sure this handle is for a device dependent on the dock,
511 * this would include the dock station itself
512 */
513 dd = find_dock_dependent_device(dock_station, handle);
514 if (dd) {
515 dd->handler = handler;
516 dd->context = context;
517 dock_add_hotplug_device(dock_station, dd);
518 return 0;
519 }
520
521 return -EINVAL;
522}
523
524EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
525
526/**
527 * unregister_hotplug_dock_device - remove yourself from the hotplug list
528 * @handle: the acpi handle of the device
529 */
530void unregister_hotplug_dock_device(acpi_handle handle)
531{
532 struct dock_dependent_device *dd;
533
534 if (!dock_station)
535 return;
536
537 dd = find_dock_dependent_device(dock_station, handle);
538 if (dd)
539 dock_del_hotplug_device(dock_station, dd);
540}
541
542EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
543
544/**
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800545 * handle_eject_request - handle an undock request checking for error conditions
546 *
547 * Check to make sure the dock device is still present, then undock and
548 * hotremove all the devices that may need removing.
549 */
550static int handle_eject_request(struct dock_station *ds, u32 event)
551{
552 if (!dock_present(ds))
553 return -ENODEV;
554
555 if (dock_in_progress(ds))
556 return -EBUSY;
557
558 /*
559 * here we need to generate the undock
560 * event prior to actually doing the undock
561 * so that the device struct still exists.
562 */
563 dock_event(ds, event, UNDOCK_EVENT);
564 hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
565 undock(ds);
566 eject_dock(ds);
567 if (dock_present(ds)) {
568 printk(KERN_ERR PREFIX "Unable to undock!\n");
569 return -EBUSY;
570 }
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700571 complete_undock(ds);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800572 return 0;
573}
574
575/**
Len Brownc8f7a622006-07-09 17:22:28 -0400576 * dock_notify - act upon an acpi dock notification
577 * @handle: the dock station handle
578 * @event: the acpi event
579 * @data: our driver data struct
580 *
581 * If we are notified to dock, then check to see if the dock is
582 * present and then dock. Notify all drivers of the dock event,
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800583 * and then hotplug and devices that may need hotplugging.
Len Brownc8f7a622006-07-09 17:22:28 -0400584 */
585static void dock_notify(acpi_handle handle, u32 event, void *data)
586{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200587 struct dock_station *ds = data;
Len Brownc8f7a622006-07-09 17:22:28 -0400588
589 switch (event) {
590 case ACPI_NOTIFY_BUS_CHECK:
591 if (!dock_in_progress(ds) && dock_present(ds)) {
592 begin_dock(ds);
593 dock(ds);
594 if (!dock_present(ds)) {
595 printk(KERN_ERR PREFIX "Unable to dock!\n");
596 break;
597 }
598 atomic_notifier_call_chain(&dock_notifier_list,
599 event, NULL);
600 hotplug_dock_devices(ds, event);
601 complete_dock(ds);
602 dock_event(ds, event, DOCK_EVENT);
603 }
604 break;
605 case ACPI_NOTIFY_DEVICE_CHECK:
606 /*
607 * According to acpi spec 3.0a, if a DEVICE_CHECK notification
608 * is sent and _DCK is present, it is assumed to mean an
609 * undock request. This notify routine will only be called
610 * for objects defining _DCK, so we will fall through to eject
611 * request here. However, we will pass an eject request through
612 * to the driver who wish to hotplug.
613 */
614 case ACPI_NOTIFY_EJECT_REQUEST:
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700615 begin_undock(ds);
616 if (immediate_undock)
617 handle_eject_request(ds, event);
618 else
619 dock_event(ds, event, UNDOCK_EVENT);
Len Brownc8f7a622006-07-09 17:22:28 -0400620 break;
621 default:
622 printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
623 }
624}
625
626/**
627 * find_dock_devices - find devices on the dock station
628 * @handle: the handle of the device we are examining
629 * @lvl: unused
630 * @context: the dock station private data
631 * @rv: unused
632 *
633 * This function is called by acpi_walk_namespace. It will
634 * check to see if an object has an _EJD method. If it does, then it
635 * will see if it is dependent on the dock station.
636 */
637static acpi_status
638find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
639{
640 acpi_status status;
Kristen Carlson Accardife9a2f72007-02-02 22:33:00 -0500641 acpi_handle tmp, parent;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200642 struct dock_station *ds = context;
Len Brownc8f7a622006-07-09 17:22:28 -0400643 struct dock_dependent_device *dd;
644
645 status = acpi_bus_get_ejd(handle, &tmp);
Kristen Carlson Accardife9a2f72007-02-02 22:33:00 -0500646 if (ACPI_FAILURE(status)) {
647 /* try the parent device as well */
648 status = acpi_get_parent(handle, &parent);
649 if (ACPI_FAILURE(status))
650 goto fdd_out;
651 /* see if parent is dependent on dock */
652 status = acpi_bus_get_ejd(parent, &tmp);
653 if (ACPI_FAILURE(status))
654 goto fdd_out;
655 }
Len Brownc8f7a622006-07-09 17:22:28 -0400656
657 if (tmp == ds->handle) {
658 dd = alloc_dock_dependent_device(handle);
659 if (dd)
660 add_dock_dependent_device(ds, dd);
661 }
Kristen Carlson Accardife9a2f72007-02-02 22:33:00 -0500662fdd_out:
Len Brownc8f7a622006-07-09 17:22:28 -0400663 return AE_OK;
664}
665
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800666/*
667 * show_docked - read method for "docked" file in sysfs
668 */
669static ssize_t show_docked(struct device *dev,
670 struct device_attribute *attr, char *buf)
671{
672 return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station));
673
674}
675DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
676
677/*
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700678 * show_flags - read method for flags file in sysfs
679 */
680static ssize_t show_flags(struct device *dev,
681 struct device_attribute *attr, char *buf)
682{
683 return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
684
685}
686DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
687
688/*
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800689 * write_undock - write method for "undock" file in sysfs
690 */
691static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
692 const char *buf, size_t count)
693{
694 int ret;
695
696 if (!count)
697 return -EINVAL;
698
699 ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
700 return ret ? ret: count;
701}
702DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
703
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800704/*
705 * show_dock_uid - read method for "uid" file in sysfs
706 */
707static ssize_t show_dock_uid(struct device *dev,
708 struct device_attribute *attr, char *buf)
709{
710 unsigned long lbuf;
Kristen Carlson Accardi38ff4ff2007-05-09 15:04:24 -0700711 acpi_status status = acpi_evaluate_integer(dock_station->handle,
712 "_UID", NULL, &lbuf);
713 if (ACPI_FAILURE(status))
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800714 return 0;
Kristen Carlson Accardi38ff4ff2007-05-09 15:04:24 -0700715
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800716 return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf);
717}
718DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
719
Len Brownc8f7a622006-07-09 17:22:28 -0400720/**
721 * dock_add - add a new dock station
722 * @handle: the dock station handle
723 *
724 * allocated and initialize a new dock station device. Find all devices
725 * that are on the dock station, and register for dock event notifications.
726 */
727static int dock_add(acpi_handle handle)
728{
729 int ret;
730 acpi_status status;
731 struct dock_dependent_device *dd;
732
733 /* allocate & initialize the dock_station private data */
734 dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
735 if (!dock_station)
736 return -ENOMEM;
737 dock_station->handle = handle;
738 dock_station->last_dock_time = jiffies - HZ;
739 INIT_LIST_HEAD(&dock_station->dependent_devices);
740 INIT_LIST_HEAD(&dock_station->hotplug_devices);
741 spin_lock_init(&dock_station->dd_lock);
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800742 mutex_init(&dock_station->hp_lock);
Kristen Accardi07a18682006-07-10 14:19:15 -0400743 ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
Len Brownc8f7a622006-07-09 17:22:28 -0400744
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800745 /* initialize platform device stuff */
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700746 dock_device =
747 platform_device_register_simple(dock_device_name, 0, NULL, 0);
748 if (IS_ERR(dock_device)) {
749 kfree(dock_station);
750 dock_station = NULL;
751 return PTR_ERR(dock_device);
752 }
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700753
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700754 ret = device_create_file(&dock_device->dev, &dev_attr_docked);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800755 if (ret) {
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700756 printk("Error %d adding sysfs file\n", ret);
757 platform_device_unregister(dock_device);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800758 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700759 dock_station = NULL;
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800760 return ret;
761 }
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700762 ret = device_create_file(&dock_device->dev, &dev_attr_undock);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800763 if (ret) {
764 printk("Error %d adding sysfs file\n", ret);
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700765 device_remove_file(&dock_device->dev, &dev_attr_docked);
766 platform_device_unregister(dock_device);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800767 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700768 dock_station = NULL;
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800769 return ret;
770 }
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700771 ret = device_create_file(&dock_device->dev, &dev_attr_uid);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800772 if (ret) {
773 printk("Error %d adding sysfs file\n", ret);
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700774 device_remove_file(&dock_device->dev, &dev_attr_docked);
775 device_remove_file(&dock_device->dev, &dev_attr_undock);
776 platform_device_unregister(dock_device);
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800777 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700778 dock_station = NULL;
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800779 return ret;
780 }
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700781 ret = device_create_file(&dock_device->dev, &dev_attr_flags);
782 if (ret) {
783 printk("Error %d adding sysfs file\n", ret);
784 device_remove_file(&dock_device->dev, &dev_attr_docked);
785 device_remove_file(&dock_device->dev, &dev_attr_undock);
786 device_remove_file(&dock_device->dev, &dev_attr_uid);
787 platform_device_unregister(dock_device);
788 kfree(dock_station);
789 dock_station = NULL;
790 return ret;
791 }
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800792
Len Brownc8f7a622006-07-09 17:22:28 -0400793 /* Find dependent devices */
794 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
795 ACPI_UINT32_MAX, find_dock_devices, dock_station,
796 NULL);
797
798 /* add the dock station as a device dependent on itself */
799 dd = alloc_dock_dependent_device(handle);
800 if (!dd) {
801 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700802 dock_station = NULL;
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800803 ret = -ENOMEM;
804 goto dock_add_err_unregister;
Len Brownc8f7a622006-07-09 17:22:28 -0400805 }
806 add_dock_dependent_device(dock_station, dd);
807
808 /* register for dock events */
809 status = acpi_install_notify_handler(dock_station->handle,
810 ACPI_SYSTEM_NOTIFY,
811 dock_notify, dock_station);
812
813 if (ACPI_FAILURE(status)) {
814 printk(KERN_ERR PREFIX "Error installing notify handler\n");
815 ret = -ENODEV;
816 goto dock_add_err;
817 }
818
Len Brown7cda93e2007-02-12 23:50:02 -0500819 printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_DESCRIPTION);
Len Brownc8f7a622006-07-09 17:22:28 -0400820
821 return 0;
822
823dock_add_err:
Len Brownc8f7a622006-07-09 17:22:28 -0400824 kfree(dd);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800825dock_add_err_unregister:
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700826 device_remove_file(&dock_device->dev, &dev_attr_docked);
827 device_remove_file(&dock_device->dev, &dev_attr_undock);
828 device_remove_file(&dock_device->dev, &dev_attr_uid);
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700829 device_remove_file(&dock_device->dev, &dev_attr_flags);
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700830 platform_device_unregister(dock_device);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800831 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700832 dock_station = NULL;
Len Brownc8f7a622006-07-09 17:22:28 -0400833 return ret;
834}
835
836/**
837 * dock_remove - free up resources related to the dock station
838 */
839static int dock_remove(void)
840{
841 struct dock_dependent_device *dd, *tmp;
842 acpi_status status;
843
844 if (!dock_station)
845 return 0;
846
847 /* remove dependent devices */
848 list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
849 list)
850 kfree(dd);
851
852 /* remove dock notify handler */
853 status = acpi_remove_notify_handler(dock_station->handle,
854 ACPI_SYSTEM_NOTIFY,
855 dock_notify);
856 if (ACPI_FAILURE(status))
857 printk(KERN_ERR "Error removing notify handler\n");
858
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800859 /* cleanup sysfs */
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700860 device_remove_file(&dock_device->dev, &dev_attr_docked);
861 device_remove_file(&dock_device->dev, &dev_attr_undock);
862 device_remove_file(&dock_device->dev, &dev_attr_uid);
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700863 device_remove_file(&dock_device->dev, &dev_attr_flags);
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700864 platform_device_unregister(dock_device);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800865
Len Brownc8f7a622006-07-09 17:22:28 -0400866 /* free dock station memory */
867 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700868 dock_station = NULL;
Len Brownc8f7a622006-07-09 17:22:28 -0400869 return 0;
870}
871
872/**
873 * find_dock - look for a dock station
874 * @handle: acpi handle of a device
875 * @lvl: unused
876 * @context: counter of dock stations found
877 * @rv: unused
878 *
879 * This is called by acpi_walk_namespace to look for dock stations.
880 */
881static acpi_status
882find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
883{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200884 int *count = context;
Len Brownc8f7a622006-07-09 17:22:28 -0400885 acpi_status status = AE_OK;
886
887 if (is_dock(handle)) {
888 if (dock_add(handle) >= 0) {
889 (*count)++;
890 status = AE_CTRL_TERMINATE;
891 }
892 }
893 return status;
894}
895
896static int __init dock_init(void)
897{
898 int num = 0;
899
900 dock_station = NULL;
901
902 /* look for a dock station */
903 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
904 ACPI_UINT32_MAX, find_dock, &num, NULL);
905
906 if (!num)
Prarit Bhargava2548c062006-12-04 14:50:17 -0800907 printk(KERN_INFO "No dock devices found.\n");
Len Brownc8f7a622006-07-09 17:22:28 -0400908
909 return 0;
910}
911
912static void __exit dock_exit(void)
913{
914 dock_remove();
915}
916
917postcore_initcall(dock_init);
918module_exit(dock_exit);