blob: ee9829b47e20226de103c1ec0c2a78d91f7fc588 [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 Browna192a952009-07-28 16:45:54 -040036#define PREFIX "ACPI: "
37
Len Brown7cda93e2007-02-12 23:50:02 -050038#define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver"
Len Brownc8f7a622006-07-09 17:22:28 -040039
Len Brownf52fd662007-02-12 22:42:12 -050040ACPI_MODULE_NAME("dock");
Len Brownc8f7a622006-07-09 17:22:28 -040041MODULE_AUTHOR("Kristen Carlson Accardi");
Len Brown7cda93e2007-02-12 23:50:02 -050042MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION);
Len Brownc8f7a622006-07-09 17:22:28 -040043MODULE_LICENSE("GPL");
44
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -070045static int immediate_undock = 1;
46module_param(immediate_undock, bool, 0644);
47MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
48 "undock immediately when the undock button is pressed, 0 will cause"
49 " the driver to wait for userspace to write the undock sysfs file "
50 " before undocking");
51
Len Brownc8f7a622006-07-09 17:22:28 -040052static struct atomic_notifier_head dock_notifier_list;
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -080053static char dock_device_name[] = "dock";
Len Brownc8f7a622006-07-09 17:22:28 -040054
Frank Seidela340af12007-12-07 13:20:42 +010055static const struct acpi_device_id dock_device_ids[] = {
56 {"LNXDOCK", 0},
57 {"", 0},
58};
59MODULE_DEVICE_TABLE(acpi, dock_device_ids);
60
Len Brownc8f7a622006-07-09 17:22:28 -040061struct dock_station {
62 acpi_handle handle;
63 unsigned long last_dock_time;
64 u32 flags;
65 spinlock_t dd_lock;
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -080066 struct mutex hp_lock;
Len Brownc8f7a622006-07-09 17:22:28 -040067 struct list_head dependent_devices;
68 struct list_head hotplug_devices;
Shaohua Lidb350b02008-08-28 10:03:58 +080069
Alex Chiang50d716e2009-10-01 11:59:23 -060070 struct list_head sibling;
Shaohua Lidb350b02008-08-28 10:03:58 +080071 struct platform_device *dock_device;
Len Brownc8f7a622006-07-09 17:22:28 -040072};
Shaohua Lidb350b02008-08-28 10:03:58 +080073static LIST_HEAD(dock_stations);
74static int dock_station_count;
Len Brownc8f7a622006-07-09 17:22:28 -040075
76struct dock_dependent_device {
77 struct list_head list;
78 struct list_head hotplug_list;
79 acpi_handle handle;
Shaohua Li1253f7a2008-08-28 10:06:16 +080080 struct acpi_dock_ops *ops;
Len Brownc8f7a622006-07-09 17:22:28 -040081 void *context;
82};
83
84#define DOCK_DOCKING 0x00000001
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -070085#define DOCK_UNDOCKING 0x00000002
Shaohua Lidb350b02008-08-28 10:03:58 +080086#define DOCK_IS_DOCK 0x00000010
87#define DOCK_IS_ATA 0x00000020
88#define DOCK_IS_BAT 0x00000040
Kristen Carlson Accardi56690212006-08-01 14:59:19 -070089#define DOCK_EVENT 3
90#define UNDOCK_EVENT 2
Len Brownc8f7a622006-07-09 17:22:28 -040091
Len Brownc8f7a622006-07-09 17:22:28 -040092/*****************************************************************************
93 * Dock Dependent device functions *
94 *****************************************************************************/
95/**
Alex Chiangf69cfdd2009-10-19 15:14:29 -060096 * add_dock_dependent_device - associate a device with the dock station
97 * @ds: The dock station
98 * @handle: handle of the dependent device
Len Brownc8f7a622006-07-09 17:22:28 -040099 *
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600100 * Add the dependent device to the dock's dependent device list.
Len Brownc8f7a622006-07-09 17:22:28 -0400101 */
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600102static int
103add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
Len Brownc8f7a622006-07-09 17:22:28 -0400104{
105 struct dock_dependent_device *dd;
106
107 dd = kzalloc(sizeof(*dd), GFP_KERNEL);
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600108 if (!dd)
109 return -ENOMEM;
Len Brownc8f7a622006-07-09 17:22:28 -0400110
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600111 dd->handle = handle;
112 INIT_LIST_HEAD(&dd->list);
113 INIT_LIST_HEAD(&dd->hotplug_list);
114
Len Brownc8f7a622006-07-09 17:22:28 -0400115 spin_lock(&ds->dd_lock);
116 list_add_tail(&dd->list, &ds->dependent_devices);
117 spin_unlock(&ds->dd_lock);
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600118
119 return 0;
Len Brownc8f7a622006-07-09 17:22:28 -0400120}
121
122/**
123 * dock_add_hotplug_device - associate a hotplug handler with the dock station
124 * @ds: The dock station
125 * @dd: The dependent device struct
126 *
127 * Add the dependent device to the dock's hotplug device list
128 */
129static void
130dock_add_hotplug_device(struct dock_station *ds,
131 struct dock_dependent_device *dd)
132{
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800133 mutex_lock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400134 list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800135 mutex_unlock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400136}
137
138/**
139 * dock_del_hotplug_device - remove a hotplug handler from the dock station
140 * @ds: The dock station
141 * @dd: the dependent device struct
142 *
143 * Delete the dependent device from the dock's hotplug device list
144 */
145static void
146dock_del_hotplug_device(struct dock_station *ds,
147 struct dock_dependent_device *dd)
148{
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800149 mutex_lock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400150 list_del(&dd->hotplug_list);
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800151 mutex_unlock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400152}
153
154/**
155 * find_dock_dependent_device - get a device dependent on this dock
156 * @ds: the dock station
157 * @handle: the acpi_handle of the device we want
158 *
159 * iterate over the dependent device list for this dock. If the
160 * dependent device matches the handle, return.
161 */
162static struct dock_dependent_device *
163find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
164{
165 struct dock_dependent_device *dd;
166
167 spin_lock(&ds->dd_lock);
168 list_for_each_entry(dd, &ds->dependent_devices, list) {
169 if (handle == dd->handle) {
170 spin_unlock(&ds->dd_lock);
171 return dd;
172 }
173 }
174 spin_unlock(&ds->dd_lock);
175 return NULL;
176}
177
178/*****************************************************************************
179 * Dock functions *
180 *****************************************************************************/
181/**
182 * is_dock - see if a device is a dock station
183 * @handle: acpi handle of the device
184 *
185 * If an acpi object has a _DCK method, then it is by definition a dock
186 * station, so return true.
187 */
188static int is_dock(acpi_handle handle)
189{
190 acpi_status status;
191 acpi_handle tmp;
192
193 status = acpi_get_handle(handle, "_DCK", &tmp);
194 if (ACPI_FAILURE(status))
195 return 0;
196 return 1;
197}
198
Shaohua Lidb350b02008-08-28 10:03:58 +0800199static int is_ejectable(acpi_handle handle)
200{
201 acpi_status status;
202 acpi_handle tmp;
203
204 status = acpi_get_handle(handle, "_EJ0", &tmp);
205 if (ACPI_FAILURE(status))
206 return 0;
207 return 1;
208}
209
210static int is_ata(acpi_handle handle)
211{
212 acpi_handle tmp;
213
214 if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) ||
215 (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) ||
216 (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) ||
217 (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp))))
218 return 1;
219
220 return 0;
221}
222
223static int is_battery(acpi_handle handle)
224{
225 struct acpi_device_info *info;
Shaohua Lidb350b02008-08-28 10:03:58 +0800226 int ret = 1;
227
Bob Moore15b8dd52009-06-29 13:39:29 +0800228 if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info)))
Shaohua Lidb350b02008-08-28 10:03:58 +0800229 return 0;
Shaohua Lidb350b02008-08-28 10:03:58 +0800230 if (!(info->valid & ACPI_VALID_HID))
231 ret = 0;
232 else
Bob Moore15b8dd52009-06-29 13:39:29 +0800233 ret = !strcmp("PNP0C0A", info->hardware_id.string);
Shaohua Lidb350b02008-08-28 10:03:58 +0800234
Bob Moore15b8dd52009-06-29 13:39:29 +0800235 kfree(info);
Shaohua Lidb350b02008-08-28 10:03:58 +0800236 return ret;
237}
238
239static int is_ejectable_bay(acpi_handle handle)
240{
241 acpi_handle phandle;
242 if (!is_ejectable(handle))
243 return 0;
244 if (is_battery(handle) || is_ata(handle))
245 return 1;
246 if (!acpi_get_parent(handle, &phandle) && is_ata(phandle))
247 return 1;
248 return 0;
249}
250
Len Brownc8f7a622006-07-09 17:22:28 -0400251/**
252 * is_dock_device - see if a device is on a dock station
253 * @handle: acpi handle of the device
254 *
255 * If this device is either the dock station itself,
256 * or is a device dependent on the dock station, then it
257 * is a dock device
258 */
259int is_dock_device(acpi_handle handle)
260{
Shaohua Lidb350b02008-08-28 10:03:58 +0800261 struct dock_station *dock_station;
262
263 if (!dock_station_count)
Len Brownc8f7a622006-07-09 17:22:28 -0400264 return 0;
265
Shaohua Lidb350b02008-08-28 10:03:58 +0800266 if (is_dock(handle))
Len Brownc8f7a622006-07-09 17:22:28 -0400267 return 1;
Alex Chiang50d716e2009-10-01 11:59:23 -0600268 list_for_each_entry(dock_station, &dock_stations, sibling) {
Shaohua Lidb350b02008-08-28 10:03:58 +0800269 if (find_dock_dependent_device(dock_station, handle))
270 return 1;
271 }
Len Brownc8f7a622006-07-09 17:22:28 -0400272
273 return 0;
274}
275
276EXPORT_SYMBOL_GPL(is_dock_device);
277
278/**
279 * dock_present - see if the dock station is present.
280 * @ds: the dock station
281 *
282 * execute the _STA method. note that present does not
283 * imply that we are docked.
284 */
285static int dock_present(struct dock_station *ds)
286{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400287 unsigned long long sta;
Len Brownc8f7a622006-07-09 17:22:28 -0400288 acpi_status status;
289
290 if (ds) {
291 status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
292 if (ACPI_SUCCESS(status) && sta)
293 return 1;
294 }
295 return 0;
296}
297
298
299
300/**
301 * dock_create_acpi_device - add new devices to acpi
302 * @handle - handle of the device to add
303 *
304 * This function will create a new acpi_device for the given
305 * handle if one does not exist already. This should cause
306 * acpi to scan for drivers for the given devices, and call
307 * matching driver's add routine.
308 *
309 * Returns a pointer to the acpi_device corresponding to the handle.
310 */
311static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
312{
313 struct acpi_device *device = NULL;
314 struct acpi_device *parent_device;
315 acpi_handle parent;
316 int ret;
317
318 if (acpi_bus_get_device(handle, &device)) {
319 /*
320 * no device created for this object,
321 * so we should create one.
322 */
323 acpi_get_parent(handle, &parent);
324 if (acpi_bus_get_device(parent, &parent_device))
325 parent_device = NULL;
326
327 ret = acpi_bus_add(&device, parent_device, handle,
328 ACPI_BUS_TYPE_DEVICE);
329 if (ret) {
330 pr_debug("error adding bus, %x\n",
331 -ret);
332 return NULL;
333 }
334 }
335 return device;
336}
337
338/**
339 * dock_remove_acpi_device - remove the acpi_device struct from acpi
340 * @handle - the handle of the device to remove
341 *
342 * Tell acpi to remove the acpi_device. This should cause any loaded
343 * driver to have it's remove routine called.
344 */
345static void dock_remove_acpi_device(acpi_handle handle)
346{
347 struct acpi_device *device;
348 int ret;
349
350 if (!acpi_bus_get_device(handle, &device)) {
351 ret = acpi_bus_trim(device, 1);
352 if (ret)
353 pr_debug("error removing bus, %x\n", -ret);
354 }
355}
356
357
358/**
359 * hotplug_dock_devices - insert or remove devices on the dock station
360 * @ds: the dock station
361 * @event: either bus check or eject request
362 *
363 * Some devices on the dock station need to have drivers called
364 * to perform hotplug operations after a dock event has occurred.
365 * Traverse the list of dock devices that have registered a
366 * hotplug handler, and call the handler.
367 */
368static void hotplug_dock_devices(struct dock_station *ds, u32 event)
369{
370 struct dock_dependent_device *dd;
371
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800372 mutex_lock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400373
374 /*
375 * First call driver specific hotplug functions
376 */
377 list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
Shaohua Li1253f7a2008-08-28 10:06:16 +0800378 if (dd->ops && dd->ops->handler)
379 dd->ops->handler(dd->handle, event, dd->context);
Len Brownc8f7a622006-07-09 17:22:28 -0400380 }
381
382 /*
383 * Now make sure that an acpi_device is created for each
384 * dependent device, or removed if this is an eject request.
385 * This will cause acpi_drivers to be stopped/started if they
386 * exist
387 */
388 list_for_each_entry(dd, &ds->dependent_devices, list) {
389 if (event == ACPI_NOTIFY_EJECT_REQUEST)
390 dock_remove_acpi_device(dd->handle);
391 else
392 dock_create_acpi_device(dd->handle);
393 }
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800394 mutex_unlock(&ds->hp_lock);
Len Brownc8f7a622006-07-09 17:22:28 -0400395}
396
397static void dock_event(struct dock_station *ds, u32 event, int num)
398{
Shaohua Lidb350b02008-08-28 10:03:58 +0800399 struct device *dev = &ds->dock_device->dev;
Holger Macht66b56822007-08-10 13:10:32 -0700400 char event_string[13];
Kristen Carlson Accardi79a8f702007-05-09 15:10:22 -0700401 char *envp[] = { event_string, NULL };
Shaohua Li1253f7a2008-08-28 10:06:16 +0800402 struct dock_dependent_device *dd;
Kristen Carlson Accardi79a8f702007-05-09 15:10:22 -0700403
404 if (num == UNDOCK_EVENT)
Holger Macht66b56822007-08-10 13:10:32 -0700405 sprintf(event_string, "EVENT=undock");
Kristen Carlson Accardi79a8f702007-05-09 15:10:22 -0700406 else
Holger Macht66b56822007-08-10 13:10:32 -0700407 sprintf(event_string, "EVENT=dock");
Kristen Carlson Accardi79a8f702007-05-09 15:10:22 -0700408
Kristen Carlson Accardi56690212006-08-01 14:59:19 -0700409 /*
Kristen Carlson Accardi8ea86e02006-12-11 12:05:08 -0800410 * Indicate that the status of the dock station has
411 * changed.
Kristen Carlson Accardi56690212006-08-01 14:59:19 -0700412 */
Shaohua Li1253f7a2008-08-28 10:06:16 +0800413 if (num == DOCK_EVENT)
414 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
415
416 list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
417 if (dd->ops && dd->ops->uevent)
418 dd->ops->uevent(dd->handle, event, dd->context);
419 if (num != DOCK_EVENT)
420 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
Len Brownc8f7a622006-07-09 17:22:28 -0400421}
422
423/**
424 * eject_dock - respond to a dock eject request
425 * @ds: the dock station
426 *
427 * This is called after _DCK is called, to execute the dock station's
428 * _EJ0 method.
429 */
430static void eject_dock(struct dock_station *ds)
431{
432 struct acpi_object_list arg_list;
433 union acpi_object arg;
434 acpi_status status;
435 acpi_handle tmp;
436
437 /* all dock devices should have _EJ0, but check anyway */
438 status = acpi_get_handle(ds->handle, "_EJ0", &tmp);
439 if (ACPI_FAILURE(status)) {
440 pr_debug("No _EJ0 support for dock device\n");
441 return;
442 }
443
444 arg_list.count = 1;
445 arg_list.pointer = &arg;
446 arg.type = ACPI_TYPE_INTEGER;
447 arg.integer.value = 1;
448
449 if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
450 &arg_list, NULL)))
451 pr_debug("Failed to evaluate _EJ0!\n");
452}
453
454/**
455 * handle_dock - handle a dock event
456 * @ds: the dock station
457 * @dock: to dock, or undock - that is the question
458 *
459 * Execute the _DCK method in response to an acpi event
460 */
461static void handle_dock(struct dock_station *ds, int dock)
462{
463 acpi_status status;
464 struct acpi_object_list arg_list;
465 union acpi_object arg;
466 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
467 struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Len Brownc8f7a622006-07-09 17:22:28 -0400468
469 acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer);
Len Brownc8f7a622006-07-09 17:22:28 -0400470
Dmitry Torokhov9254bc82007-07-18 01:10:24 -0400471 printk(KERN_INFO PREFIX "%s - %s\n",
472 (char *)name_buffer.pointer, dock ? "docking" : "undocking");
Len Brownc8f7a622006-07-09 17:22:28 -0400473
474 /* _DCK method has one argument */
475 arg_list.count = 1;
476 arg_list.pointer = &arg;
477 arg.type = ACPI_TYPE_INTEGER;
478 arg.integer.value = dock;
479 status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
Shaohua Lidb350b02008-08-28 10:03:58 +0800480 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
Thomas Renninger0a918a92008-10-11 00:15:04 -0400481 ACPI_EXCEPTION((AE_INFO, status, "%s - failed to execute"
482 " _DCK\n", (char *)name_buffer.pointer));
483
Len Brownc8f7a622006-07-09 17:22:28 -0400484 kfree(buffer.pointer);
485 kfree(name_buffer.pointer);
486}
487
488static inline void dock(struct dock_station *ds)
489{
490 handle_dock(ds, 1);
491}
492
493static inline void undock(struct dock_station *ds)
494{
495 handle_dock(ds, 0);
496}
497
498static inline void begin_dock(struct dock_station *ds)
499{
500 ds->flags |= DOCK_DOCKING;
501}
502
503static inline void complete_dock(struct dock_station *ds)
504{
505 ds->flags &= ~(DOCK_DOCKING);
506 ds->last_dock_time = jiffies;
507}
508
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700509static inline void begin_undock(struct dock_station *ds)
510{
511 ds->flags |= DOCK_UNDOCKING;
512}
513
514static inline void complete_undock(struct dock_station *ds)
515{
516 ds->flags &= ~(DOCK_UNDOCKING);
517}
518
Shaohua Li406f6922008-08-28 10:03:26 +0800519static void dock_lock(struct dock_station *ds, int lock)
520{
521 struct acpi_object_list arg_list;
522 union acpi_object arg;
523 acpi_status status;
524
525 arg_list.count = 1;
526 arg_list.pointer = &arg;
527 arg.type = ACPI_TYPE_INTEGER;
528 arg.integer.value = !!lock;
529 status = acpi_evaluate_object(ds->handle, "_LCK", &arg_list, NULL);
530 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
531 if (lock)
532 printk(KERN_WARNING PREFIX "Locking device failed\n");
533 else
534 printk(KERN_WARNING PREFIX "Unlocking device failed\n");
535 }
536}
537
Len Brownc8f7a622006-07-09 17:22:28 -0400538/**
539 * dock_in_progress - see if we are in the middle of handling a dock event
540 * @ds: the dock station
541 *
542 * Sometimes while docking, false dock events can be sent to the driver
543 * because good connections aren't made or some other reason. Ignore these
544 * if we are in the middle of doing something.
545 */
546static int dock_in_progress(struct dock_station *ds)
547{
548 if ((ds->flags & DOCK_DOCKING) ||
549 time_before(jiffies, (ds->last_dock_time + HZ)))
550 return 1;
551 return 0;
552}
553
554/**
555 * register_dock_notifier - add yourself to the dock notifier list
556 * @nb: the callers notifier block
557 *
558 * If a driver wishes to be notified about dock events, they can
559 * use this function to put a notifier block on the dock notifier list.
560 * this notifier call chain will be called after a dock event, but
561 * before hotplugging any new devices.
562 */
563int register_dock_notifier(struct notifier_block *nb)
564{
Shaohua Lidb350b02008-08-28 10:03:58 +0800565 if (!dock_station_count)
Prarit Bhargava2548c062006-12-04 14:50:17 -0800566 return -ENODEV;
567
Len Brownc8f7a622006-07-09 17:22:28 -0400568 return atomic_notifier_chain_register(&dock_notifier_list, nb);
569}
570
571EXPORT_SYMBOL_GPL(register_dock_notifier);
572
573/**
574 * unregister_dock_notifier - remove yourself from the dock notifier list
575 * @nb: the callers notifier block
576 */
577void unregister_dock_notifier(struct notifier_block *nb)
578{
Shaohua Lidb350b02008-08-28 10:03:58 +0800579 if (!dock_station_count)
Prarit Bhargava2548c062006-12-04 14:50:17 -0800580 return;
581
Len Brownc8f7a622006-07-09 17:22:28 -0400582 atomic_notifier_chain_unregister(&dock_notifier_list, nb);
583}
584
585EXPORT_SYMBOL_GPL(unregister_dock_notifier);
586
587/**
588 * register_hotplug_dock_device - register a hotplug function
589 * @handle: the handle of the device
Shaohua Li1253f7a2008-08-28 10:06:16 +0800590 * @ops: handlers to call after docking
Len Brownc8f7a622006-07-09 17:22:28 -0400591 * @context: device specific data
592 *
593 * If a driver would like to perform a hotplug operation after a dock
594 * event, they can register an acpi_notifiy_handler to be called by
595 * the dock driver after _DCK is executed.
596 */
597int
Shaohua Li1253f7a2008-08-28 10:06:16 +0800598register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops,
Len Brownc8f7a622006-07-09 17:22:28 -0400599 void *context)
600{
601 struct dock_dependent_device *dd;
Shaohua Lidb350b02008-08-28 10:03:58 +0800602 struct dock_station *dock_station;
Shaohua Li61b83692008-08-28 10:07:14 +0800603 int ret = -EINVAL;
Len Brownc8f7a622006-07-09 17:22:28 -0400604
Shaohua Lidb350b02008-08-28 10:03:58 +0800605 if (!dock_station_count)
Len Brownc8f7a622006-07-09 17:22:28 -0400606 return -ENODEV;
607
608 /*
609 * make sure this handle is for a device dependent on the dock,
610 * this would include the dock station itself
611 */
Alex Chiang50d716e2009-10-01 11:59:23 -0600612 list_for_each_entry(dock_station, &dock_stations, sibling) {
Shaohua Li61b83692008-08-28 10:07:14 +0800613 /*
614 * An ATA bay can be in a dock and itself can be ejected
615 * seperately, so there are two 'dock stations' which need the
616 * ops
617 */
Shaohua Lidb350b02008-08-28 10:03:58 +0800618 dd = find_dock_dependent_device(dock_station, handle);
619 if (dd) {
Shaohua Li1253f7a2008-08-28 10:06:16 +0800620 dd->ops = ops;
Shaohua Lidb350b02008-08-28 10:03:58 +0800621 dd->context = context;
622 dock_add_hotplug_device(dock_station, dd);
Shaohua Li61b83692008-08-28 10:07:14 +0800623 ret = 0;
Shaohua Lidb350b02008-08-28 10:03:58 +0800624 }
Len Brownc8f7a622006-07-09 17:22:28 -0400625 }
626
Shaohua Li61b83692008-08-28 10:07:14 +0800627 return ret;
Len Brownc8f7a622006-07-09 17:22:28 -0400628}
629
630EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
631
632/**
633 * unregister_hotplug_dock_device - remove yourself from the hotplug list
634 * @handle: the acpi handle of the device
635 */
636void unregister_hotplug_dock_device(acpi_handle handle)
637{
638 struct dock_dependent_device *dd;
Shaohua Lidb350b02008-08-28 10:03:58 +0800639 struct dock_station *dock_station;
Len Brownc8f7a622006-07-09 17:22:28 -0400640
Shaohua Lidb350b02008-08-28 10:03:58 +0800641 if (!dock_station_count)
Len Brownc8f7a622006-07-09 17:22:28 -0400642 return;
643
Alex Chiang50d716e2009-10-01 11:59:23 -0600644 list_for_each_entry(dock_station, &dock_stations, sibling) {
Shaohua Lidb350b02008-08-28 10:03:58 +0800645 dd = find_dock_dependent_device(dock_station, handle);
646 if (dd)
647 dock_del_hotplug_device(dock_station, dd);
648 }
Len Brownc8f7a622006-07-09 17:22:28 -0400649}
650
651EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
652
653/**
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800654 * handle_eject_request - handle an undock request checking for error conditions
655 *
656 * Check to make sure the dock device is still present, then undock and
657 * hotremove all the devices that may need removing.
658 */
659static int handle_eject_request(struct dock_station *ds, u32 event)
660{
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800661 if (dock_in_progress(ds))
662 return -EBUSY;
663
664 /*
665 * here we need to generate the undock
666 * event prior to actually doing the undock
667 * so that the device struct still exists.
Holger Machtafd73012008-08-06 17:56:01 +0200668 * Also, even send the dock event if the
669 * device is not present anymore
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800670 */
671 dock_event(ds, event, UNDOCK_EVENT);
Holger Machtafd73012008-08-06 17:56:01 +0200672
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800673 hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
674 undock(ds);
Shaohua Li406f6922008-08-28 10:03:26 +0800675 dock_lock(ds, 0);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800676 eject_dock(ds);
677 if (dock_present(ds)) {
678 printk(KERN_ERR PREFIX "Unable to undock!\n");
679 return -EBUSY;
680 }
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700681 complete_undock(ds);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800682 return 0;
683}
684
685/**
Len Brownc8f7a622006-07-09 17:22:28 -0400686 * dock_notify - act upon an acpi dock notification
687 * @handle: the dock station handle
688 * @event: the acpi event
689 * @data: our driver data struct
690 *
691 * If we are notified to dock, then check to see if the dock is
692 * present and then dock. Notify all drivers of the dock event,
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800693 * and then hotplug and devices that may need hotplugging.
Len Brownc8f7a622006-07-09 17:22:28 -0400694 */
695static void dock_notify(acpi_handle handle, u32 event, void *data)
696{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200697 struct dock_station *ds = data;
Shaohua Li8b595602008-08-28 10:02:03 +0800698 struct acpi_device *tmp;
Shaohua Lidb350b02008-08-28 10:03:58 +0800699 int surprise_removal = 0;
Len Brownc8f7a622006-07-09 17:22:28 -0400700
Shaohua Lidb350b02008-08-28 10:03:58 +0800701 /*
702 * According to acpi spec 3.0a, if a DEVICE_CHECK notification
703 * is sent and _DCK is present, it is assumed to mean an undock
704 * request.
705 */
706 if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)
707 event = ACPI_NOTIFY_EJECT_REQUEST;
708
709 /*
710 * dock station: BUS_CHECK - docked or surprise removal
711 * DEVICE_CHECK - undocked
712 * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal
713 *
714 * To simplify event handling, dock dependent device handler always
715 * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and
716 * ACPI_NOTIFY_EJECT_REQUEST for removal
717 */
Len Brownc8f7a622006-07-09 17:22:28 -0400718 switch (event) {
719 case ACPI_NOTIFY_BUS_CHECK:
Shaohua Lidb350b02008-08-28 10:03:58 +0800720 case ACPI_NOTIFY_DEVICE_CHECK:
Shaohua Li8b595602008-08-28 10:02:03 +0800721 if (!dock_in_progress(ds) && acpi_bus_get_device(ds->handle,
722 &tmp)) {
Len Brownc8f7a622006-07-09 17:22:28 -0400723 begin_dock(ds);
724 dock(ds);
725 if (!dock_present(ds)) {
726 printk(KERN_ERR PREFIX "Unable to dock!\n");
Shaohua Li8b595602008-08-28 10:02:03 +0800727 complete_dock(ds);
Len Brownc8f7a622006-07-09 17:22:28 -0400728 break;
729 }
730 atomic_notifier_call_chain(&dock_notifier_list,
731 event, NULL);
732 hotplug_dock_devices(ds, event);
733 complete_dock(ds);
734 dock_event(ds, event, DOCK_EVENT);
Shaohua Li406f6922008-08-28 10:03:26 +0800735 dock_lock(ds, 1);
Shaohua Lidb350b02008-08-28 10:03:58 +0800736 break;
Len Brownc8f7a622006-07-09 17:22:28 -0400737 }
Shaohua Lidb350b02008-08-28 10:03:58 +0800738 if (dock_present(ds) || dock_in_progress(ds))
739 break;
740 /* This is a surprise removal */
741 surprise_removal = 1;
742 event = ACPI_NOTIFY_EJECT_REQUEST;
743 /* Fall back */
Len Brownc8f7a622006-07-09 17:22:28 -0400744 case ACPI_NOTIFY_EJECT_REQUEST:
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700745 begin_undock(ds);
Shaohua Lif730ae12008-08-28 10:05:45 +0800746 if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))
747 || surprise_removal)
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700748 handle_eject_request(ds, event);
749 else
750 dock_event(ds, event, UNDOCK_EVENT);
Len Brownc8f7a622006-07-09 17:22:28 -0400751 break;
752 default:
753 printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
754 }
755}
756
Zhang Rui19cd8472008-08-28 10:05:06 +0800757struct dock_data {
758 acpi_handle handle;
759 unsigned long event;
760 struct dock_station *ds;
761};
762
763static void acpi_dock_deferred_cb(void *context)
764{
765 struct dock_data *data = (struct dock_data *)context;
766
767 dock_notify(data->handle, data->event, data->ds);
768 kfree(data);
769}
770
Shaohua Li6bd00a62008-08-28 10:04:29 +0800771static int acpi_dock_notifier_call(struct notifier_block *this,
772 unsigned long event, void *data)
773{
774 struct dock_station *dock_station;
775 acpi_handle handle = (acpi_handle)data;
776
777 if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
778 && event != ACPI_NOTIFY_EJECT_REQUEST)
779 return 0;
Alex Chiang50d716e2009-10-01 11:59:23 -0600780 list_for_each_entry(dock_station, &dock_stations, sibling) {
Shaohua Li6bd00a62008-08-28 10:04:29 +0800781 if (dock_station->handle == handle) {
Zhang Rui19cd8472008-08-28 10:05:06 +0800782 struct dock_data *dock_data;
783
784 dock_data = kmalloc(sizeof(*dock_data), GFP_KERNEL);
785 if (!dock_data)
786 return 0;
787 dock_data->handle = handle;
788 dock_data->event = event;
789 dock_data->ds = dock_station;
790 acpi_os_hotplug_execute(acpi_dock_deferred_cb,
791 dock_data);
Shaohua Li6bd00a62008-08-28 10:04:29 +0800792 return 0 ;
793 }
794 }
795 return 0;
796}
797
798static struct notifier_block dock_acpi_notifier = {
799 .notifier_call = acpi_dock_notifier_call,
800};
801
Len Brownc8f7a622006-07-09 17:22:28 -0400802/**
803 * find_dock_devices - find devices on the dock station
804 * @handle: the handle of the device we are examining
805 * @lvl: unused
806 * @context: the dock station private data
807 * @rv: unused
808 *
809 * This function is called by acpi_walk_namespace. It will
810 * check to see if an object has an _EJD method. If it does, then it
811 * will see if it is dependent on the dock station.
812 */
813static acpi_status
814find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
815{
816 acpi_status status;
Kristen Carlson Accardife9a2f72007-02-02 22:33:00 -0500817 acpi_handle tmp, parent;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200818 struct dock_station *ds = context;
Len Brownc8f7a622006-07-09 17:22:28 -0400819
820 status = acpi_bus_get_ejd(handle, &tmp);
Kristen Carlson Accardife9a2f72007-02-02 22:33:00 -0500821 if (ACPI_FAILURE(status)) {
822 /* try the parent device as well */
823 status = acpi_get_parent(handle, &parent);
824 if (ACPI_FAILURE(status))
825 goto fdd_out;
826 /* see if parent is dependent on dock */
827 status = acpi_bus_get_ejd(parent, &tmp);
828 if (ACPI_FAILURE(status))
829 goto fdd_out;
830 }
Len Brownc8f7a622006-07-09 17:22:28 -0400831
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600832 if (tmp == ds->handle)
833 add_dock_dependent_device(ds, handle);
834
Kristen Carlson Accardife9a2f72007-02-02 22:33:00 -0500835fdd_out:
Len Brownc8f7a622006-07-09 17:22:28 -0400836 return AE_OK;
837}
838
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800839/*
840 * show_docked - read method for "docked" file in sysfs
841 */
842static ssize_t show_docked(struct device *dev,
843 struct device_attribute *attr, char *buf)
844{
Holger Machtfc5a9f82009-01-20 12:18:24 +0100845 struct acpi_device *tmp;
846
Shaohua Lidb350b02008-08-28 10:03:58 +0800847 struct dock_station *dock_station = *((struct dock_station **)
848 dev->platform_data);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800849
Holger Machtfc5a9f82009-01-20 12:18:24 +0100850 if (ACPI_SUCCESS(acpi_bus_get_device(dock_station->handle, &tmp)))
851 return snprintf(buf, PAGE_SIZE, "1\n");
852 return snprintf(buf, PAGE_SIZE, "0\n");
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800853}
Adrian Bunke5685b92007-10-24 18:24:42 +0200854static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800855
856/*
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700857 * show_flags - read method for flags file in sysfs
858 */
859static ssize_t show_flags(struct device *dev,
860 struct device_attribute *attr, char *buf)
861{
Shaohua Lidb350b02008-08-28 10:03:58 +0800862 struct dock_station *dock_station = *((struct dock_station **)
863 dev->platform_data);
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700864 return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
865
866}
Adrian Bunke5685b92007-10-24 18:24:42 +0200867static DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700868
869/*
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800870 * write_undock - write method for "undock" file in sysfs
871 */
872static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
873 const char *buf, size_t count)
874{
875 int ret;
Shaohua Lidb350b02008-08-28 10:03:58 +0800876 struct dock_station *dock_station = *((struct dock_station **)
877 dev->platform_data);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800878
879 if (!count)
880 return -EINVAL;
881
Holger Macht9171f832008-03-12 01:07:27 +0100882 begin_undock(dock_station);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800883 ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
884 return ret ? ret: count;
885}
Adrian Bunke5685b92007-10-24 18:24:42 +0200886static DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800887
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800888/*
889 * show_dock_uid - read method for "uid" file in sysfs
890 */
891static ssize_t show_dock_uid(struct device *dev,
892 struct device_attribute *attr, char *buf)
893{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400894 unsigned long long lbuf;
Shaohua Lidb350b02008-08-28 10:03:58 +0800895 struct dock_station *dock_station = *((struct dock_station **)
896 dev->platform_data);
Kristen Carlson Accardi38ff4ff2007-05-09 15:04:24 -0700897 acpi_status status = acpi_evaluate_integer(dock_station->handle,
898 "_UID", NULL, &lbuf);
899 if (ACPI_FAILURE(status))
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800900 return 0;
Kristen Carlson Accardi38ff4ff2007-05-09 15:04:24 -0700901
Matthew Wilcox27663c52008-10-10 02:22:59 -0400902 return snprintf(buf, PAGE_SIZE, "%llx\n", lbuf);
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800903}
Adrian Bunke5685b92007-10-24 18:24:42 +0200904static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800905
Shaohua Li8652b002008-08-28 10:07:45 +0800906static ssize_t show_dock_type(struct device *dev,
907 struct device_attribute *attr, char *buf)
908{
909 struct dock_station *dock_station = *((struct dock_station **)
910 dev->platform_data);
911 char *type;
912
913 if (dock_station->flags & DOCK_IS_DOCK)
914 type = "dock_station";
915 else if (dock_station->flags & DOCK_IS_ATA)
916 type = "ata_bay";
917 else if (dock_station->flags & DOCK_IS_BAT)
918 type = "battery_bay";
919 else
920 type = "unknown";
921
922 return snprintf(buf, PAGE_SIZE, "%s\n", type);
923}
924static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
925
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600926static struct attribute *dock_attributes[] = {
927 &dev_attr_docked.attr,
928 &dev_attr_flags.attr,
929 &dev_attr_undock.attr,
930 &dev_attr_uid.attr,
931 &dev_attr_type.attr,
932 NULL
933};
934
935static struct attribute_group dock_attribute_group = {
936 .attrs = dock_attributes
937};
938
Len Brownc8f7a622006-07-09 17:22:28 -0400939/**
940 * dock_add - add a new dock station
941 * @handle: the dock station handle
942 *
943 * allocated and initialize a new dock station device. Find all devices
944 * that are on the dock station, and register for dock event notifications.
945 */
946static int dock_add(acpi_handle handle)
947{
948 int ret;
Shaohua Lidb350b02008-08-28 10:03:58 +0800949 struct dock_station *dock_station;
950 struct platform_device *dock_device;
Len Brownc8f7a622006-07-09 17:22:28 -0400951
952 /* allocate & initialize the dock_station private data */
953 dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
954 if (!dock_station)
955 return -ENOMEM;
956 dock_station->handle = handle;
957 dock_station->last_dock_time = jiffies - HZ;
958 INIT_LIST_HEAD(&dock_station->dependent_devices);
959 INIT_LIST_HEAD(&dock_station->hotplug_devices);
Alex Chiang50d716e2009-10-01 11:59:23 -0600960 INIT_LIST_HEAD(&dock_station->sibling);
Len Brownc8f7a622006-07-09 17:22:28 -0400961 spin_lock_init(&dock_station->dd_lock);
Kristen Carlson Accardi8b0dc862006-10-30 11:18:45 -0800962 mutex_init(&dock_station->hp_lock);
Kristen Accardi07a186842006-07-10 14:19:15 -0400963 ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
Len Brownc8f7a622006-07-09 17:22:28 -0400964
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800965 /* initialize platform device stuff */
Shaohua Lidb350b02008-08-28 10:03:58 +0800966 dock_station->dock_device =
967 platform_device_register_simple(dock_device_name,
968 dock_station_count, NULL, 0);
969 dock_device = dock_station->dock_device;
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700970 if (IS_ERR(dock_device)) {
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600971 ret = PTR_ERR(dock_device);
972 goto out;
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -0700973 }
Shaohua Lidb350b02008-08-28 10:03:58 +0800974 platform_device_add_data(dock_device, &dock_station,
975 sizeof(struct dock_station *));
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700976
Kristen Carlson Accardi9ef2a9a2007-05-09 15:09:12 -0700977 /* we want the dock device to send uevents */
Ming Leif67f1292009-03-01 21:10:49 +0800978 dev_set_uevent_suppress(&dock_device->dev, 0);
Kristen Carlson Accardi9ef2a9a2007-05-09 15:09:12 -0700979
Shaohua Lidb350b02008-08-28 10:03:58 +0800980 if (is_dock(handle))
981 dock_station->flags |= DOCK_IS_DOCK;
982 if (is_ata(handle))
983 dock_station->flags |= DOCK_IS_ATA;
984 if (is_battery(handle))
985 dock_station->flags |= DOCK_IS_BAT;
986
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600987 ret = sysfs_create_group(&dock_device->dev.kobj, &dock_attribute_group);
Shaohua Li8652b002008-08-28 10:07:45 +0800988 if (ret)
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600989 goto err_unregister;
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800990
Len Brownc8f7a622006-07-09 17:22:28 -0400991 /* Find dependent devices */
992 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
993 ACPI_UINT32_MAX, find_dock_devices, dock_station,
994 NULL);
995
996 /* add the dock station as a device dependent on itself */
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600997 ret = add_dock_dependent_device(dock_station, handle);
998 if (ret)
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600999 goto err_rmgroup;
Len Brownc8f7a622006-07-09 17:22:28 -04001000
Shaohua Lidb350b02008-08-28 10:03:58 +08001001 dock_station_count++;
Alex Chiang50d716e2009-10-01 11:59:23 -06001002 list_add(&dock_station->sibling, &dock_stations);
Len Brownc8f7a622006-07-09 17:22:28 -04001003 return 0;
1004
Alex Chiang5f46c2f2009-10-19 15:14:24 -06001005err_rmgroup:
1006 sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group);
1007err_unregister:
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -07001008 platform_device_unregister(dock_device);
Alex Chiang5f46c2f2009-10-19 15:14:24 -06001009out:
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -08001010 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -07001011 dock_station = NULL;
Alex Chiang5f46c2f2009-10-19 15:14:24 -06001012 printk(KERN_ERR "%s encountered error %d\n", __func__, ret);
Len Brownc8f7a622006-07-09 17:22:28 -04001013 return ret;
1014}
1015
1016/**
1017 * dock_remove - free up resources related to the dock station
1018 */
Shaohua Lidb350b02008-08-28 10:03:58 +08001019static int dock_remove(struct dock_station *dock_station)
Len Brownc8f7a622006-07-09 17:22:28 -04001020{
1021 struct dock_dependent_device *dd, *tmp;
Shaohua Lidb350b02008-08-28 10:03:58 +08001022 struct platform_device *dock_device = dock_station->dock_device;
Len Brownc8f7a622006-07-09 17:22:28 -04001023
Shaohua Lidb350b02008-08-28 10:03:58 +08001024 if (!dock_station_count)
Len Brownc8f7a622006-07-09 17:22:28 -04001025 return 0;
1026
1027 /* remove dependent devices */
1028 list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
1029 list)
1030 kfree(dd);
1031
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -08001032 /* cleanup sysfs */
Alex Chiang5f46c2f2009-10-19 15:14:24 -06001033 sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group);
Kristen Carlson Accardi0f6f2802007-05-09 15:07:04 -07001034 platform_device_unregister(dock_device);
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -08001035
Len Brownc8f7a622006-07-09 17:22:28 -04001036 /* free dock station memory */
1037 kfree(dock_station);
Chuck Ebbert22fe4c22007-05-09 15:05:48 -07001038 dock_station = NULL;
Len Brownc8f7a622006-07-09 17:22:28 -04001039 return 0;
1040}
1041
1042/**
1043 * find_dock - look for a dock station
1044 * @handle: acpi handle of a device
1045 * @lvl: unused
1046 * @context: counter of dock stations found
1047 * @rv: unused
1048 *
1049 * This is called by acpi_walk_namespace to look for dock stations.
1050 */
1051static acpi_status
1052find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
1053{
Len Brownc8f7a622006-07-09 17:22:28 -04001054 acpi_status status = AE_OK;
1055
1056 if (is_dock(handle)) {
1057 if (dock_add(handle) >= 0) {
Len Brownc8f7a622006-07-09 17:22:28 -04001058 status = AE_CTRL_TERMINATE;
1059 }
1060 }
1061 return status;
1062}
1063
Shaohua Lidb350b02008-08-28 10:03:58 +08001064static acpi_status
1065find_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
1066{
Shaohua Li61b83692008-08-28 10:07:14 +08001067 /* If bay is a dock, it's already handled */
1068 if (is_ejectable_bay(handle) && !is_dock(handle))
Shaohua Lidb350b02008-08-28 10:03:58 +08001069 dock_add(handle);
1070 return AE_OK;
1071}
1072
Len Brownc8f7a622006-07-09 17:22:28 -04001073static int __init dock_init(void)
1074{
Len Brown816c2ed2008-06-24 22:57:12 -04001075 if (acpi_disabled)
1076 return 0;
1077
Len Brownc8f7a622006-07-09 17:22:28 -04001078 /* look for a dock station */
1079 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
Shaohua Lidb350b02008-08-28 10:03:58 +08001080 ACPI_UINT32_MAX, find_dock, NULL, NULL);
Len Brownc8f7a622006-07-09 17:22:28 -04001081
Shaohua Lidb350b02008-08-28 10:03:58 +08001082 /* look for bay */
1083 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
1084 ACPI_UINT32_MAX, find_bay, NULL, NULL);
1085 if (!dock_station_count) {
1086 printk(KERN_INFO PREFIX "No dock devices found.\n");
1087 return 0;
1088 }
Len Brownc8f7a622006-07-09 17:22:28 -04001089
Shaohua Li6bd00a62008-08-28 10:04:29 +08001090 register_acpi_bus_notifier(&dock_acpi_notifier);
Shaohua Lidb350b02008-08-28 10:03:58 +08001091 printk(KERN_INFO PREFIX "%s: %d docks/bays found\n",
1092 ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
Len Brownc8f7a622006-07-09 17:22:28 -04001093 return 0;
1094}
1095
1096static void __exit dock_exit(void)
1097{
Shaohua Lidb350b02008-08-28 10:03:58 +08001098 struct dock_station *dock_station;
Dan Carpenterf2407292009-04-02 08:29:56 +03001099 struct dock_station *tmp;
Shaohua Lidb350b02008-08-28 10:03:58 +08001100
Shaohua Li6bd00a62008-08-28 10:04:29 +08001101 unregister_acpi_bus_notifier(&dock_acpi_notifier);
Alex Chiang50d716e2009-10-01 11:59:23 -06001102 list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling)
Shaohua Lidb350b02008-08-28 10:03:58 +08001103 dock_remove(dock_station);
Len Brownc8f7a622006-07-09 17:22:28 -04001104}
1105
Shaohua Lidb350b02008-08-28 10:03:58 +08001106/*
1107 * Must be called before drivers of devices in dock, otherwise we can't know
1108 * which devices are in a dock
1109 */
1110subsys_initcall(dock_init);
Len Brownc8f7a622006-07-09 17:22:28 -04001111module_exit(dock_exit);