blob: dc3df93d2310a6ab7c4d22dcbba8f2285efa4825 [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 Accardi79a8f702007-05-09 15:10:22 -0700339 char event_string[7];
340 char *envp[] = { event_string, NULL };
341
342 if (num == UNDOCK_EVENT)
343 sprintf(event_string, "UNDOCK");
344 else
345 sprintf(event_string, "DOCK");
346
Kristen Carlson Accardi56690212006-08-01 14:59:19 -0700347 /*
Kristen Carlson Accardi8ea86e02006-12-11 12:05:08 -0800348 * Indicate that the status of the dock station has
349 * changed.
Kristen Carlson Accardi56690212006-08-01 14:59:19 -0700350 */
Kristen Carlson Accardi79a8f702007-05-09 15:10:22 -0700351 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
Len Brownc8f7a622006-07-09 17:22:28 -0400352}
353
354/**
355 * eject_dock - respond to a dock eject request
356 * @ds: the dock station
357 *
358 * This is called after _DCK is called, to execute the dock station's
359 * _EJ0 method.
360 */
361static void eject_dock(struct dock_station *ds)
362{
363 struct acpi_object_list arg_list;
364 union acpi_object arg;
365 acpi_status status;
366 acpi_handle tmp;
367
368 /* all dock devices should have _EJ0, but check anyway */
369 status = acpi_get_handle(ds->handle, "_EJ0", &tmp);
370 if (ACPI_FAILURE(status)) {
371 pr_debug("No _EJ0 support for dock device\n");
372 return;
373 }
374
375 arg_list.count = 1;
376 arg_list.pointer = &arg;
377 arg.type = ACPI_TYPE_INTEGER;
378 arg.integer.value = 1;
379
380 if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
381 &arg_list, NULL)))
382 pr_debug("Failed to evaluate _EJ0!\n");
383}
384
385/**
386 * handle_dock - handle a dock event
387 * @ds: the dock station
388 * @dock: to dock, or undock - that is the question
389 *
390 * Execute the _DCK method in response to an acpi event
391 */
392static void handle_dock(struct dock_station *ds, int dock)
393{
394 acpi_status status;
395 struct acpi_object_list arg_list;
396 union acpi_object arg;
397 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
398 struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
399 union acpi_object *obj;
400
401 acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer);
402 obj = name_buffer.pointer;
403
404 printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking");
405
406 /* _DCK method has one argument */
407 arg_list.count = 1;
408 arg_list.pointer = &arg;
409 arg.type = ACPI_TYPE_INTEGER;
410 arg.integer.value = dock;
411 status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
412 if (ACPI_FAILURE(status))
413 pr_debug("%s: failed to execute _DCK\n", obj->string.pointer);
414 kfree(buffer.pointer);
415 kfree(name_buffer.pointer);
416}
417
418static inline void dock(struct dock_station *ds)
419{
420 handle_dock(ds, 1);
421}
422
423static inline void undock(struct dock_station *ds)
424{
425 handle_dock(ds, 0);
426}
427
428static inline void begin_dock(struct dock_station *ds)
429{
430 ds->flags |= DOCK_DOCKING;
431}
432
433static inline void complete_dock(struct dock_station *ds)
434{
435 ds->flags &= ~(DOCK_DOCKING);
436 ds->last_dock_time = jiffies;
437}
438
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700439static inline void begin_undock(struct dock_station *ds)
440{
441 ds->flags |= DOCK_UNDOCKING;
442}
443
444static inline void complete_undock(struct dock_station *ds)
445{
446 ds->flags &= ~(DOCK_UNDOCKING);
447}
448
Len Brownc8f7a622006-07-09 17:22:28 -0400449/**
450 * dock_in_progress - see if we are in the middle of handling a dock event
451 * @ds: the dock station
452 *
453 * Sometimes while docking, false dock events can be sent to the driver
454 * because good connections aren't made or some other reason. Ignore these
455 * if we are in the middle of doing something.
456 */
457static int dock_in_progress(struct dock_station *ds)
458{
459 if ((ds->flags & DOCK_DOCKING) ||
460 time_before(jiffies, (ds->last_dock_time + HZ)))
461 return 1;
462 return 0;
463}
464
465/**
466 * register_dock_notifier - add yourself to the dock notifier list
467 * @nb: the callers notifier block
468 *
469 * If a driver wishes to be notified about dock events, they can
470 * use this function to put a notifier block on the dock notifier list.
471 * this notifier call chain will be called after a dock event, but
472 * before hotplugging any new devices.
473 */
474int register_dock_notifier(struct notifier_block *nb)
475{
Prarit Bhargava2548c062006-12-04 14:50:17 -0800476 if (!dock_station)
477 return -ENODEV;
478
Len Brownc8f7a622006-07-09 17:22:28 -0400479 return atomic_notifier_chain_register(&dock_notifier_list, nb);
480}
481
482EXPORT_SYMBOL_GPL(register_dock_notifier);
483
484/**
485 * unregister_dock_notifier - remove yourself from the dock notifier list
486 * @nb: the callers notifier block
487 */
488void unregister_dock_notifier(struct notifier_block *nb)
489{
Prarit Bhargava2548c062006-12-04 14:50:17 -0800490 if (!dock_station)
491 return;
492
Len Brownc8f7a622006-07-09 17:22:28 -0400493 atomic_notifier_chain_unregister(&dock_notifier_list, nb);
494}
495
496EXPORT_SYMBOL_GPL(unregister_dock_notifier);
497
498/**
499 * register_hotplug_dock_device - register a hotplug function
500 * @handle: the handle of the device
501 * @handler: the acpi_notifier_handler to call after docking
502 * @context: device specific data
503 *
504 * If a driver would like to perform a hotplug operation after a dock
505 * event, they can register an acpi_notifiy_handler to be called by
506 * the dock driver after _DCK is executed.
507 */
508int
509register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
510 void *context)
511{
512 struct dock_dependent_device *dd;
513
514 if (!dock_station)
515 return -ENODEV;
516
517 /*
518 * make sure this handle is for a device dependent on the dock,
519 * this would include the dock station itself
520 */
521 dd = find_dock_dependent_device(dock_station, handle);
522 if (dd) {
523 dd->handler = handler;
524 dd->context = context;
525 dock_add_hotplug_device(dock_station, dd);
526 return 0;
527 }
528
529 return -EINVAL;
530}
531
532EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
533
534/**
535 * unregister_hotplug_dock_device - remove yourself from the hotplug list
536 * @handle: the acpi handle of the device
537 */
538void unregister_hotplug_dock_device(acpi_handle handle)
539{
540 struct dock_dependent_device *dd;
541
542 if (!dock_station)
543 return;
544
545 dd = find_dock_dependent_device(dock_station, handle);
546 if (dd)
547 dock_del_hotplug_device(dock_station, dd);
548}
549
550EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
551
552/**
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800553 * handle_eject_request - handle an undock request checking for error conditions
554 *
555 * Check to make sure the dock device is still present, then undock and
556 * hotremove all the devices that may need removing.
557 */
558static int handle_eject_request(struct dock_station *ds, u32 event)
559{
560 if (!dock_present(ds))
561 return -ENODEV;
562
563 if (dock_in_progress(ds))
564 return -EBUSY;
565
566 /*
567 * here we need to generate the undock
568 * event prior to actually doing the undock
569 * so that the device struct still exists.
570 */
571 dock_event(ds, event, UNDOCK_EVENT);
572 hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
573 undock(ds);
574 eject_dock(ds);
575 if (dock_present(ds)) {
576 printk(KERN_ERR PREFIX "Unable to undock!\n");
577 return -EBUSY;
578 }
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700579 complete_undock(ds);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800580 return 0;
581}
582
583/**
Len Brownc8f7a622006-07-09 17:22:28 -0400584 * dock_notify - act upon an acpi dock notification
585 * @handle: the dock station handle
586 * @event: the acpi event
587 * @data: our driver data struct
588 *
589 * If we are notified to dock, then check to see if the dock is
590 * present and then dock. Notify all drivers of the dock event,
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800591 * and then hotplug and devices that may need hotplugging.
Len Brownc8f7a622006-07-09 17:22:28 -0400592 */
593static void dock_notify(acpi_handle handle, u32 event, void *data)
594{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200595 struct dock_station *ds = data;
Len Brownc8f7a622006-07-09 17:22:28 -0400596
597 switch (event) {
598 case ACPI_NOTIFY_BUS_CHECK:
599 if (!dock_in_progress(ds) && dock_present(ds)) {
600 begin_dock(ds);
601 dock(ds);
602 if (!dock_present(ds)) {
603 printk(KERN_ERR PREFIX "Unable to dock!\n");
604 break;
605 }
606 atomic_notifier_call_chain(&dock_notifier_list,
607 event, NULL);
608 hotplug_dock_devices(ds, event);
609 complete_dock(ds);
610 dock_event(ds, event, DOCK_EVENT);
611 }
612 break;
613 case ACPI_NOTIFY_DEVICE_CHECK:
614 /*
615 * According to acpi spec 3.0a, if a DEVICE_CHECK notification
616 * is sent and _DCK is present, it is assumed to mean an
617 * undock request. This notify routine will only be called
618 * for objects defining _DCK, so we will fall through to eject
619 * request here. However, we will pass an eject request through
620 * to the driver who wish to hotplug.
621 */
622 case ACPI_NOTIFY_EJECT_REQUEST:
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700623 begin_undock(ds);
624 if (immediate_undock)
625 handle_eject_request(ds, event);
626 else
627 dock_event(ds, event, UNDOCK_EVENT);
Len Brownc8f7a622006-07-09 17:22:28 -0400628 break;
629 default:
630 printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
631 }
632}
633
634/**
635 * find_dock_devices - find devices on the dock station
636 * @handle: the handle of the device we are examining
637 * @lvl: unused
638 * @context: the dock station private data
639 * @rv: unused
640 *
641 * This function is called by acpi_walk_namespace. It will
642 * check to see if an object has an _EJD method. If it does, then it
643 * will see if it is dependent on the dock station.
644 */
645static acpi_status
646find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
647{
648 acpi_status status;
Kristen Carlson Accardife9a2f72007-02-02 22:33:00 -0500649 acpi_handle tmp, parent;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200650 struct dock_station *ds = context;
Len Brownc8f7a622006-07-09 17:22:28 -0400651 struct dock_dependent_device *dd;
652
653 status = acpi_bus_get_ejd(handle, &tmp);
Kristen Carlson Accardife9a2f72007-02-02 22:33:00 -0500654 if (ACPI_FAILURE(status)) {
655 /* try the parent device as well */
656 status = acpi_get_parent(handle, &parent);
657 if (ACPI_FAILURE(status))
658 goto fdd_out;
659 /* see if parent is dependent on dock */
660 status = acpi_bus_get_ejd(parent, &tmp);
661 if (ACPI_FAILURE(status))
662 goto fdd_out;
663 }
Len Brownc8f7a622006-07-09 17:22:28 -0400664
665 if (tmp == ds->handle) {
666 dd = alloc_dock_dependent_device(handle);
667 if (dd)
668 add_dock_dependent_device(ds, dd);
669 }
Kristen Carlson Accardife9a2f72007-02-02 22:33:00 -0500670fdd_out:
Len Brownc8f7a622006-07-09 17:22:28 -0400671 return AE_OK;
672}
673
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800674/*
675 * show_docked - read method for "docked" file in sysfs
676 */
677static ssize_t show_docked(struct device *dev,
678 struct device_attribute *attr, char *buf)
679{
680 return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station));
681
682}
683DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
684
685/*
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700686 * show_flags - read method for flags file in sysfs
687 */
688static ssize_t show_flags(struct device *dev,
689 struct device_attribute *attr, char *buf)
690{
691 return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
692
693}
694DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
695
696/*
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800697 * write_undock - write method for "undock" file in sysfs
698 */
699static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
700 const char *buf, size_t count)
701{
702 int ret;
703
704 if (!count)
705 return -EINVAL;
706
707 ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
708 return ret ? ret: count;
709}
710DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
711
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800712/*
713 * show_dock_uid - read method for "uid" file in sysfs
714 */
715static ssize_t show_dock_uid(struct device *dev,
716 struct device_attribute *attr, char *buf)
717{
718 unsigned long lbuf;
Kristen Carlson Accardi38ff4ff2007-05-09 15:04:24 -0700719 acpi_status status = acpi_evaluate_integer(dock_station->handle,
720 "_UID", NULL, &lbuf);
721 if (ACPI_FAILURE(status))
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800722 return 0;
Kristen Carlson Accardi38ff4ff2007-05-09 15:04:24 -0700723
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800724 return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf);
725}
726DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
727
Len Brownc8f7a622006-07-09 17:22:28 -0400728/**
729 * dock_add - add a new dock station
730 * @handle: the dock station handle
731 *
732 * allocated and initialize a new dock station device. Find all devices
733 * that are on the dock station, and register for dock event notifications.
734 */
735static int dock_add(acpi_handle handle)
736{
737 int ret;
738 acpi_status status;
739 struct dock_dependent_device *dd;
740
741 /* allocate & initialize the dock_station private data */
742 dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
743 if (!dock_station)
744 return -ENOMEM;
745 dock_station->handle = handle;
746 dock_station->last_dock_time = jiffies - HZ;
747 INIT_LIST_HEAD(&dock_station->dependent_devices);
748 INIT_LIST_HEAD(&dock_station->hotplug_devices);
749 spin_lock_init(&dock_station->dd_lock);
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800750 mutex_init(&dock_station->hp_lock);
Kristen Accardi07a18682006-07-10 14:19:15 -0400751 ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
Len Brownc8f7a622006-07-09 17:22:28 -0400752
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800753 /* initialize platform device stuff */
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700754 dock_device =
755 platform_device_register_simple(dock_device_name, 0, NULL, 0);
756 if (IS_ERR(dock_device)) {
757 kfree(dock_station);
758 dock_station = NULL;
759 return PTR_ERR(dock_device);
760 }
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700761
Kristen Carlson Accardi9ef2a9a2007-05-09 15:09:12 -0700762 /* we want the dock device to send uevents */
763 dock_device->dev.uevent_suppress = 0;
764
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700765 ret = device_create_file(&dock_device->dev, &dev_attr_docked);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800766 if (ret) {
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700767 printk("Error %d adding sysfs file\n", ret);
768 platform_device_unregister(dock_device);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800769 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700770 dock_station = NULL;
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800771 return ret;
772 }
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700773 ret = device_create_file(&dock_device->dev, &dev_attr_undock);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800774 if (ret) {
775 printk("Error %d adding sysfs file\n", ret);
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700776 device_remove_file(&dock_device->dev, &dev_attr_docked);
777 platform_device_unregister(dock_device);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800778 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700779 dock_station = NULL;
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800780 return ret;
781 }
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700782 ret = device_create_file(&dock_device->dev, &dev_attr_uid);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800783 if (ret) {
784 printk("Error %d adding sysfs file\n", ret);
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700785 device_remove_file(&dock_device->dev, &dev_attr_docked);
786 device_remove_file(&dock_device->dev, &dev_attr_undock);
787 platform_device_unregister(dock_device);
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800788 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700789 dock_station = NULL;
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800790 return ret;
791 }
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700792 ret = device_create_file(&dock_device->dev, &dev_attr_flags);
793 if (ret) {
794 printk("Error %d adding sysfs file\n", ret);
795 device_remove_file(&dock_device->dev, &dev_attr_docked);
796 device_remove_file(&dock_device->dev, &dev_attr_undock);
797 device_remove_file(&dock_device->dev, &dev_attr_uid);
798 platform_device_unregister(dock_device);
799 kfree(dock_station);
800 dock_station = NULL;
801 return ret;
802 }
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800803
Len Brownc8f7a622006-07-09 17:22:28 -0400804 /* Find dependent devices */
805 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
806 ACPI_UINT32_MAX, find_dock_devices, dock_station,
807 NULL);
808
809 /* add the dock station as a device dependent on itself */
810 dd = alloc_dock_dependent_device(handle);
811 if (!dd) {
812 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700813 dock_station = NULL;
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800814 ret = -ENOMEM;
815 goto dock_add_err_unregister;
Len Brownc8f7a622006-07-09 17:22:28 -0400816 }
817 add_dock_dependent_device(dock_station, dd);
818
819 /* register for dock events */
820 status = acpi_install_notify_handler(dock_station->handle,
821 ACPI_SYSTEM_NOTIFY,
822 dock_notify, dock_station);
823
824 if (ACPI_FAILURE(status)) {
825 printk(KERN_ERR PREFIX "Error installing notify handler\n");
826 ret = -ENODEV;
827 goto dock_add_err;
828 }
829
Len Brown7cda93e2007-02-12 23:50:02 -0500830 printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_DESCRIPTION);
Len Brownc8f7a622006-07-09 17:22:28 -0400831
832 return 0;
833
834dock_add_err:
Len Brownc8f7a622006-07-09 17:22:28 -0400835 kfree(dd);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800836dock_add_err_unregister:
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700837 device_remove_file(&dock_device->dev, &dev_attr_docked);
838 device_remove_file(&dock_device->dev, &dev_attr_undock);
839 device_remove_file(&dock_device->dev, &dev_attr_uid);
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700840 device_remove_file(&dock_device->dev, &dev_attr_flags);
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700841 platform_device_unregister(dock_device);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800842 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700843 dock_station = NULL;
Len Brownc8f7a622006-07-09 17:22:28 -0400844 return ret;
845}
846
847/**
848 * dock_remove - free up resources related to the dock station
849 */
850static int dock_remove(void)
851{
852 struct dock_dependent_device *dd, *tmp;
853 acpi_status status;
854
855 if (!dock_station)
856 return 0;
857
858 /* remove dependent devices */
859 list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
860 list)
861 kfree(dd);
862
863 /* remove dock notify handler */
864 status = acpi_remove_notify_handler(dock_station->handle,
865 ACPI_SYSTEM_NOTIFY,
866 dock_notify);
867 if (ACPI_FAILURE(status))
868 printk(KERN_ERR "Error removing notify handler\n");
869
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800870 /* cleanup sysfs */
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700871 device_remove_file(&dock_device->dev, &dev_attr_docked);
872 device_remove_file(&dock_device->dev, &dev_attr_undock);
873 device_remove_file(&dock_device->dev, &dev_attr_uid);
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700874 device_remove_file(&dock_device->dev, &dev_attr_flags);
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700875 platform_device_unregister(dock_device);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800876
Len Brownc8f7a622006-07-09 17:22:28 -0400877 /* free dock station memory */
878 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -0700879 dock_station = NULL;
Len Brownc8f7a622006-07-09 17:22:28 -0400880 return 0;
881}
882
883/**
884 * find_dock - look for a dock station
885 * @handle: acpi handle of a device
886 * @lvl: unused
887 * @context: counter of dock stations found
888 * @rv: unused
889 *
890 * This is called by acpi_walk_namespace to look for dock stations.
891 */
892static acpi_status
893find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
894{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200895 int *count = context;
Len Brownc8f7a622006-07-09 17:22:28 -0400896 acpi_status status = AE_OK;
897
898 if (is_dock(handle)) {
899 if (dock_add(handle) >= 0) {
900 (*count)++;
901 status = AE_CTRL_TERMINATE;
902 }
903 }
904 return status;
905}
906
907static int __init dock_init(void)
908{
909 int num = 0;
910
911 dock_station = NULL;
912
913 /* look for a dock station */
914 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
915 ACPI_UINT32_MAX, find_dock, &num, NULL);
916
917 if (!num)
Prarit Bhargava2548c062006-12-04 14:50:17 -0800918 printk(KERN_INFO "No dock devices found.\n");
Len Brownc8f7a622006-07-09 17:22:28 -0400919
920 return 0;
921}
922
923static void __exit dock_exit(void)
924{
925 dock_remove();
926}
927
928postcore_initcall(dock_init);
929module_exit(dock_exit);