blob: 3e20b13fa272f5b3ecef195c6518ef5debfe43c0 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090027#include <linux/slab.h>
Len Brownc8f7a622006-07-09 17:22:28 -040028#include <linux/init.h>
29#include <linux/types.h>
30#include <linux/notifier.h>
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -080031#include <linux/platform_device.h>
Al Viro914e2632006-10-18 13:55:46 -040032#include <linux/jiffies.h>
Randy Dunlap62a6d7f2007-03-26 21:38:49 -080033#include <linux/stddef.h>
Toshi Kanicd730182012-11-20 23:42:30 +000034#include <linux/acpi.h>
Len Brownc8f7a622006-07-09 17:22:28 -040035#include <acpi/acpi_bus.h>
36#include <acpi/acpi_drivers.h>
37
Len Browna192a952009-07-28 16:45:54 -040038#define PREFIX "ACPI: "
39
Len Brown7cda93e2007-02-12 23:50:02 -050040#define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver"
Len Brownc8f7a622006-07-09 17:22:28 -040041
Len Brownf52fd662007-02-12 22:42:12 -050042ACPI_MODULE_NAME("dock");
Len Brownc8f7a622006-07-09 17:22:28 -040043MODULE_AUTHOR("Kristen Carlson Accardi");
Len Brown7cda93e2007-02-12 23:50:02 -050044MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION);
Len Brownc8f7a622006-07-09 17:22:28 -040045MODULE_LICENSE("GPL");
46
Rusty Russell90ab5ee2012-01-13 09:32:20 +103047static bool immediate_undock = 1;
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -070048module_param(immediate_undock, bool, 0644);
49MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
50 "undock immediately when the undock button is pressed, 0 will cause"
51 " the driver to wait for userspace to write the undock sysfs file "
52 " before undocking");
53
Len Brownc8f7a622006-07-09 17:22:28 -040054static struct atomic_notifier_head dock_notifier_list;
55
Frank Seidela340af12007-12-07 13:20:42 +010056static const struct acpi_device_id dock_device_ids[] = {
57 {"LNXDOCK", 0},
58 {"", 0},
59};
60MODULE_DEVICE_TABLE(acpi, dock_device_ids);
61
Len Brownc8f7a622006-07-09 17:22:28 -040062struct dock_station {
63 acpi_handle handle;
64 unsigned long last_dock_time;
65 u32 flags;
Len Brownc8f7a622006-07-09 17:22:28 -040066 struct list_head dependent_devices;
Shaohua Lidb350b02008-08-28 10:03:58 +080067
Alex Chiang50d716e2009-10-01 11:59:23 -060068 struct list_head sibling;
Shaohua Lidb350b02008-08-28 10:03:58 +080069 struct platform_device *dock_device;
Len Brownc8f7a622006-07-09 17:22:28 -040070};
Shaohua Lidb350b02008-08-28 10:03:58 +080071static LIST_HEAD(dock_stations);
72static int dock_station_count;
Rafael J. Wysocki21a31012013-06-24 11:22:53 +020073static DEFINE_MUTEX(hotplug_lock);
Len Brownc8f7a622006-07-09 17:22:28 -040074
75struct dock_dependent_device {
76 struct list_head list;
Len Brownc8f7a622006-07-09 17:22:28 -040077 acpi_handle handle;
Rafael J. Wysocki21a31012013-06-24 11:22:53 +020078 const struct acpi_dock_ops *hp_ops;
79 void *hp_context;
80 unsigned int hp_refcount;
81 void (*hp_release)(void *);
Len Brownc8f7a622006-07-09 17:22:28 -040082};
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 */
Jiang Liud423c082013-06-29 00:24:36 +0800102static int __init
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600103add_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);
Len Brownc8f7a622006-07-09 17:22:28 -0400113 list_add_tail(&dd->list, &ds->dependent_devices);
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600114
115 return 0;
Len Brownc8f7a622006-07-09 17:22:28 -0400116}
117
Rafael J. Wysockia30c4c52013-06-30 23:50:24 +0200118static void remove_dock_dependent_devices(struct dock_station *ds)
119{
120 struct dock_dependent_device *dd, *aux;
121
122 list_for_each_entry_safe(dd, aux, &ds->dependent_devices, list) {
123 list_del(&dd->list);
124 kfree(dd);
125 }
126}
127
Len Brownc8f7a622006-07-09 17:22:28 -0400128/**
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200129 * dock_init_hotplug - Initialize a hotplug device on a docking station.
130 * @dd: Dock-dependent device.
131 * @ops: Dock operations to attach to the dependent device.
132 * @context: Data to pass to the @ops callbacks and @release.
133 * @init: Optional initialization routine to run after setting up context.
134 * @release: Optional release routine to run on removal.
Len Brownc8f7a622006-07-09 17:22:28 -0400135 */
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200136static int dock_init_hotplug(struct dock_dependent_device *dd,
137 const struct acpi_dock_ops *ops, void *context,
138 void (*init)(void *), void (*release)(void *))
Len Brownc8f7a622006-07-09 17:22:28 -0400139{
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200140 int ret = 0;
141
142 mutex_lock(&hotplug_lock);
Rafael J. Wysocki4ec24062013-06-30 23:47:14 +0200143 if (WARN_ON(dd->hp_context)) {
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200144 ret = -EEXIST;
145 } else {
146 dd->hp_refcount = 1;
147 dd->hp_ops = ops;
148 dd->hp_context = context;
149 dd->hp_release = release;
Rafael J. Wysocki4ec24062013-06-30 23:47:14 +0200150 if (init)
151 init(context);
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200152 }
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200153 mutex_unlock(&hotplug_lock);
154 return ret;
Len Brownc8f7a622006-07-09 17:22:28 -0400155}
156
157/**
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200158 * dock_release_hotplug - Decrement hotplug reference counter of dock device.
159 * @dd: Dock-dependent device.
Len Brownc8f7a622006-07-09 17:22:28 -0400160 *
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200161 * Decrement the reference counter of @dd and if 0, detach its hotplug
162 * operations from it, reset its context pointer and run the optional release
163 * routine if present.
Len Brownc8f7a622006-07-09 17:22:28 -0400164 */
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200165static void dock_release_hotplug(struct dock_dependent_device *dd)
Len Brownc8f7a622006-07-09 17:22:28 -0400166{
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200167 mutex_lock(&hotplug_lock);
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200168 if (dd->hp_context && !--dd->hp_refcount) {
Rafael J. Wysocki4ec24062013-06-30 23:47:14 +0200169 void (*release)(void *) = dd->hp_release;
170 void *context = dd->hp_context;
171
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200172 dd->hp_ops = NULL;
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200173 dd->hp_context = NULL;
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200174 dd->hp_release = NULL;
Rafael J. Wysocki4ec24062013-06-30 23:47:14 +0200175 if (release)
176 release(context);
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200177 }
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200178 mutex_unlock(&hotplug_lock);
179}
180
181static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
182 bool uevent)
183{
184 acpi_notify_handler cb = NULL;
185 bool run = false;
186
187 mutex_lock(&hotplug_lock);
188
189 if (dd->hp_context) {
190 run = true;
191 dd->hp_refcount++;
192 if (dd->hp_ops)
193 cb = uevent ? dd->hp_ops->uevent : dd->hp_ops->handler;
194 }
195
196 mutex_unlock(&hotplug_lock);
197
198 if (!run)
199 return;
200
201 if (cb)
202 cb(dd->handle, event, dd->hp_context);
203
204 dock_release_hotplug(dd);
Len Brownc8f7a622006-07-09 17:22:28 -0400205}
206
207/**
208 * find_dock_dependent_device - get a device dependent on this dock
209 * @ds: the dock station
210 * @handle: the acpi_handle of the device we want
211 *
212 * iterate over the dependent device list for this dock. If the
213 * dependent device matches the handle, return.
214 */
215static struct dock_dependent_device *
216find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
217{
218 struct dock_dependent_device *dd;
219
Jiang Liued633e72013-06-29 00:24:35 +0800220 list_for_each_entry(dd, &ds->dependent_devices, list)
221 if (handle == dd->handle)
Len Brownc8f7a622006-07-09 17:22:28 -0400222 return dd;
Jiang Liued633e72013-06-29 00:24:35 +0800223
Len Brownc8f7a622006-07-09 17:22:28 -0400224 return NULL;
225}
226
227/*****************************************************************************
228 * Dock functions *
229 *****************************************************************************/
Jiang Liud423c082013-06-29 00:24:36 +0800230static int __init is_battery(acpi_handle handle)
Shaohua Lidb350b02008-08-28 10:03:58 +0800231{
232 struct acpi_device_info *info;
Shaohua Lidb350b02008-08-28 10:03:58 +0800233 int ret = 1;
234
Bob Moore15b8dd52009-06-29 13:39:29 +0800235 if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info)))
Shaohua Lidb350b02008-08-28 10:03:58 +0800236 return 0;
Shaohua Lidb350b02008-08-28 10:03:58 +0800237 if (!(info->valid & ACPI_VALID_HID))
238 ret = 0;
239 else
Bob Moore15b8dd52009-06-29 13:39:29 +0800240 ret = !strcmp("PNP0C0A", info->hardware_id.string);
Shaohua Lidb350b02008-08-28 10:03:58 +0800241
Bob Moore15b8dd52009-06-29 13:39:29 +0800242 kfree(info);
Shaohua Lidb350b02008-08-28 10:03:58 +0800243 return ret;
244}
245
Jiang Liuc9b54712013-06-29 00:24:42 +0800246/* Check whether ACPI object is an ejectable battery or disk bay */
247static bool __init is_ejectable_bay(acpi_handle handle)
Shaohua Lidb350b02008-08-28 10:03:58 +0800248{
Jiang Liuc9b54712013-06-29 00:24:42 +0800249 if (acpi_has_method(handle, "_EJ0") && is_battery(handle))
250 return true;
Alex Chiang747479a2009-10-19 15:14:50 -0600251
Jiang Liuc9b54712013-06-29 00:24:42 +0800252 return acpi_bay_match(handle);
Shaohua Lidb350b02008-08-28 10:03:58 +0800253}
254
Len Brownc8f7a622006-07-09 17:22:28 -0400255/**
256 * is_dock_device - see if a device is on a dock station
257 * @handle: acpi handle of the device
258 *
259 * If this device is either the dock station itself,
260 * or is a device dependent on the dock station, then it
261 * is a dock device
262 */
263int is_dock_device(acpi_handle handle)
264{
Shaohua Lidb350b02008-08-28 10:03:58 +0800265 struct dock_station *dock_station;
266
267 if (!dock_station_count)
Len Brownc8f7a622006-07-09 17:22:28 -0400268 return 0;
269
Jiang Liuc9b54712013-06-29 00:24:42 +0800270 if (acpi_dock_match(handle))
Len Brownc8f7a622006-07-09 17:22:28 -0400271 return 1;
Alex Chiang747479a2009-10-19 15:14:50 -0600272
273 list_for_each_entry(dock_station, &dock_stations, sibling)
Shaohua Lidb350b02008-08-28 10:03:58 +0800274 if (find_dock_dependent_device(dock_station, handle))
275 return 1;
Len Brownc8f7a622006-07-09 17:22:28 -0400276
277 return 0;
278}
Len Brownc8f7a622006-07-09 17:22:28 -0400279EXPORT_SYMBOL_GPL(is_dock_device);
280
281/**
282 * dock_present - see if the dock station is present.
283 * @ds: the dock station
284 *
285 * execute the _STA method. note that present does not
286 * imply that we are docked.
287 */
288static int dock_present(struct dock_station *ds)
289{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400290 unsigned long long sta;
Len Brownc8f7a622006-07-09 17:22:28 -0400291 acpi_status status;
292
293 if (ds) {
294 status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
295 if (ACPI_SUCCESS(status) && sta)
296 return 1;
297 }
298 return 0;
299}
300
Len Brownc8f7a622006-07-09 17:22:28 -0400301/**
302 * dock_create_acpi_device - add new devices to acpi
303 * @handle - handle of the device to add
304 *
305 * This function will create a new acpi_device for the given
306 * handle if one does not exist already. This should cause
307 * acpi to scan for drivers for the given devices, and call
308 * matching driver's add routine.
Len Brownc8f7a622006-07-09 17:22:28 -0400309 */
Jiang Liu472d9632013-06-29 00:24:37 +0800310static void dock_create_acpi_device(acpi_handle handle)
Len Brownc8f7a622006-07-09 17:22:28 -0400311{
Alex Chiang747479a2009-10-19 15:14:50 -0600312 struct acpi_device *device;
Len Brownc8f7a622006-07-09 17:22:28 -0400313 int ret;
314
315 if (acpi_bus_get_device(handle, &device)) {
316 /*
317 * no device created for this object,
318 * so we should create one.
319 */
Rafael J. Wysockib8bd7592013-01-19 01:27:35 +0100320 ret = acpi_bus_scan(handle);
Rafael J. Wysocki636458d2012-12-21 00:36:47 +0100321 if (ret)
Alex Chiang747479a2009-10-19 15:14:50 -0600322 pr_debug("error adding bus, %x\n", -ret);
Len Brownc8f7a622006-07-09 17:22:28 -0400323 }
Len Brownc8f7a622006-07-09 17:22:28 -0400324}
325
326/**
327 * dock_remove_acpi_device - remove the acpi_device struct from acpi
328 * @handle - the handle of the device to remove
329 *
330 * Tell acpi to remove the acpi_device. This should cause any loaded
331 * driver to have it's remove routine called.
332 */
333static void dock_remove_acpi_device(acpi_handle handle)
334{
335 struct acpi_device *device;
Len Brownc8f7a622006-07-09 17:22:28 -0400336
Rafael J. Wysockibfee26d2013-01-26 00:27:44 +0100337 if (!acpi_bus_get_device(handle, &device))
338 acpi_bus_trim(device);
Len Brownc8f7a622006-07-09 17:22:28 -0400339}
340
Len Brownc8f7a622006-07-09 17:22:28 -0400341/**
Rafael J. Wysocki37f90872013-06-30 23:46:42 +0200342 * hot_remove_dock_devices - Remove dock station devices.
343 * @ds: Dock station.
344 */
345static void hot_remove_dock_devices(struct dock_station *ds)
346{
347 struct dock_dependent_device *dd;
348
349 /*
350 * Walk the list in reverse order so that devices that have been added
351 * last are removed first (in case there are some indirect dependencies
352 * between them).
353 */
354 list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
355 dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false);
356
357 list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
358 dock_remove_acpi_device(dd->handle);
359}
360
361/**
362 * hotplug_dock_devices - Insert devices on a dock station.
Len Brownc8f7a622006-07-09 17:22:28 -0400363 * @ds: the dock station
Rafael J. Wysocki37f90872013-06-30 23:46:42 +0200364 * @event: either bus check or device check request
Len Brownc8f7a622006-07-09 17:22:28 -0400365 *
366 * Some devices on the dock station need to have drivers called
367 * to perform hotplug operations after a dock event has occurred.
368 * Traverse the list of dock devices that have registered a
369 * hotplug handler, and call the handler.
370 */
371static void hotplug_dock_devices(struct dock_station *ds, u32 event)
372{
373 struct dock_dependent_device *dd;
374
Rafael J. Wysocki37f90872013-06-30 23:46:42 +0200375 /* Call driver specific hotplug functions. */
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200376 list_for_each_entry(dd, &ds->dependent_devices, list)
377 dock_hotplug_event(dd, event, false);
Len Brownc8f7a622006-07-09 17:22:28 -0400378
379 /*
Rafael J. Wysocki37f90872013-06-30 23:46:42 +0200380 * Now make sure that an acpi_device is created for each dependent
381 * device. That will cause scan handlers to be attached to device
382 * objects or acpi_drivers to be stopped/started if they are present.
Len Brownc8f7a622006-07-09 17:22:28 -0400383 */
Rafael J. Wysocki37f90872013-06-30 23:46:42 +0200384 list_for_each_entry(dd, &ds->dependent_devices, list)
385 dock_create_acpi_device(dd->handle);
Len Brownc8f7a622006-07-09 17:22:28 -0400386}
387
388static void dock_event(struct dock_station *ds, u32 event, int num)
389{
Shaohua Lidb350b02008-08-28 10:03:58 +0800390 struct device *dev = &ds->dock_device->dev;
Holger Macht66b56822007-08-10 13:10:32 -0700391 char event_string[13];
Kristen Carlson Accardi79a8f702007-05-09 15:10:22 -0700392 char *envp[] = { event_string, NULL };
Shaohua Li1253f7a2008-08-28 10:06:16 +0800393 struct dock_dependent_device *dd;
Kristen Carlson Accardi79a8f702007-05-09 15:10:22 -0700394
395 if (num == UNDOCK_EVENT)
Holger Macht66b56822007-08-10 13:10:32 -0700396 sprintf(event_string, "EVENT=undock");
Kristen Carlson Accardi79a8f702007-05-09 15:10:22 -0700397 else
Holger Macht66b56822007-08-10 13:10:32 -0700398 sprintf(event_string, "EVENT=dock");
Kristen Carlson Accardi79a8f702007-05-09 15:10:22 -0700399
Kristen Carlson Accardi56690212006-08-01 14:59:19 -0700400 /*
Kristen Carlson Accardi8ea86e02006-12-11 12:05:08 -0800401 * Indicate that the status of the dock station has
402 * changed.
Kristen Carlson Accardi56690212006-08-01 14:59:19 -0700403 */
Shaohua Li1253f7a2008-08-28 10:06:16 +0800404 if (num == DOCK_EVENT)
405 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
406
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200407 list_for_each_entry(dd, &ds->dependent_devices, list)
408 dock_hotplug_event(dd, event, true);
Alex Chiang747479a2009-10-19 15:14:50 -0600409
Shaohua Li1253f7a2008-08-28 10:06:16 +0800410 if (num != DOCK_EVENT)
411 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
Len Brownc8f7a622006-07-09 17:22:28 -0400412}
413
414/**
Len Brownc8f7a622006-07-09 17:22:28 -0400415 * handle_dock - handle a dock event
416 * @ds: the dock station
417 * @dock: to dock, or undock - that is the question
418 *
419 * Execute the _DCK method in response to an acpi event
420 */
421static void handle_dock(struct dock_station *ds, int dock)
422{
423 acpi_status status;
424 struct acpi_object_list arg_list;
425 union acpi_object arg;
426 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Len Brownc8f7a622006-07-09 17:22:28 -0400427
Toshi Kanicd730182012-11-20 23:42:30 +0000428 acpi_handle_info(ds->handle, "%s\n", dock ? "docking" : "undocking");
Len Brownc8f7a622006-07-09 17:22:28 -0400429
430 /* _DCK method has one argument */
431 arg_list.count = 1;
432 arg_list.pointer = &arg;
433 arg.type = ACPI_TYPE_INTEGER;
434 arg.integer.value = dock;
435 status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
Shaohua Lidb350b02008-08-28 10:03:58 +0800436 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
Toshi Kanicd730182012-11-20 23:42:30 +0000437 acpi_handle_err(ds->handle, "Failed to execute _DCK (0x%x)\n",
438 status);
Thomas Renninger0a918a92008-10-11 00:15:04 -0400439
Len Brownc8f7a622006-07-09 17:22:28 -0400440 kfree(buffer.pointer);
Len Brownc8f7a622006-07-09 17:22:28 -0400441}
442
443static inline void dock(struct dock_station *ds)
444{
445 handle_dock(ds, 1);
446}
447
448static inline void undock(struct dock_station *ds)
449{
450 handle_dock(ds, 0);
451}
452
453static inline void begin_dock(struct dock_station *ds)
454{
455 ds->flags |= DOCK_DOCKING;
456}
457
458static inline void complete_dock(struct dock_station *ds)
459{
460 ds->flags &= ~(DOCK_DOCKING);
461 ds->last_dock_time = jiffies;
462}
463
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700464static inline void begin_undock(struct dock_station *ds)
465{
466 ds->flags |= DOCK_UNDOCKING;
467}
468
469static inline void complete_undock(struct dock_station *ds)
470{
471 ds->flags &= ~(DOCK_UNDOCKING);
472}
473
Len Brownc8f7a622006-07-09 17:22:28 -0400474/**
475 * dock_in_progress - see if we are in the middle of handling a dock event
476 * @ds: the dock station
477 *
478 * Sometimes while docking, false dock events can be sent to the driver
479 * because good connections aren't made or some other reason. Ignore these
480 * if we are in the middle of doing something.
481 */
482static int dock_in_progress(struct dock_station *ds)
483{
484 if ((ds->flags & DOCK_DOCKING) ||
485 time_before(jiffies, (ds->last_dock_time + HZ)))
486 return 1;
487 return 0;
488}
489
490/**
491 * register_dock_notifier - add yourself to the dock notifier list
492 * @nb: the callers notifier block
493 *
494 * If a driver wishes to be notified about dock events, they can
495 * use this function to put a notifier block on the dock notifier list.
496 * this notifier call chain will be called after a dock event, but
497 * before hotplugging any new devices.
498 */
499int register_dock_notifier(struct notifier_block *nb)
500{
Shaohua Lidb350b02008-08-28 10:03:58 +0800501 if (!dock_station_count)
Prarit Bhargava2548c062006-12-04 14:50:17 -0800502 return -ENODEV;
503
Len Brownc8f7a622006-07-09 17:22:28 -0400504 return atomic_notifier_chain_register(&dock_notifier_list, nb);
505}
Len Brownc8f7a622006-07-09 17:22:28 -0400506EXPORT_SYMBOL_GPL(register_dock_notifier);
507
508/**
509 * unregister_dock_notifier - remove yourself from the dock notifier list
510 * @nb: the callers notifier block
511 */
512void unregister_dock_notifier(struct notifier_block *nb)
513{
Shaohua Lidb350b02008-08-28 10:03:58 +0800514 if (!dock_station_count)
Prarit Bhargava2548c062006-12-04 14:50:17 -0800515 return;
516
Len Brownc8f7a622006-07-09 17:22:28 -0400517 atomic_notifier_chain_unregister(&dock_notifier_list, nb);
518}
Len Brownc8f7a622006-07-09 17:22:28 -0400519EXPORT_SYMBOL_GPL(unregister_dock_notifier);
520
521/**
522 * register_hotplug_dock_device - register a hotplug function
523 * @handle: the handle of the device
Shaohua Li1253f7a2008-08-28 10:06:16 +0800524 * @ops: handlers to call after docking
Len Brownc8f7a622006-07-09 17:22:28 -0400525 * @context: device specific data
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200526 * @init: Optional initialization routine to run after registration
527 * @release: Optional release routine to run on unregistration
Len Brownc8f7a622006-07-09 17:22:28 -0400528 *
529 * If a driver would like to perform a hotplug operation after a dock
530 * event, they can register an acpi_notifiy_handler to be called by
531 * the dock driver after _DCK is executed.
532 */
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200533int register_hotplug_dock_device(acpi_handle handle,
534 const struct acpi_dock_ops *ops, void *context,
535 void (*init)(void *), void (*release)(void *))
Len Brownc8f7a622006-07-09 17:22:28 -0400536{
537 struct dock_dependent_device *dd;
Shaohua Lidb350b02008-08-28 10:03:58 +0800538 struct dock_station *dock_station;
Shaohua Li61b83692008-08-28 10:07:14 +0800539 int ret = -EINVAL;
Len Brownc8f7a622006-07-09 17:22:28 -0400540
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200541 if (WARN_ON(!context))
542 return -EINVAL;
543
Shaohua Lidb350b02008-08-28 10:03:58 +0800544 if (!dock_station_count)
Len Brownc8f7a622006-07-09 17:22:28 -0400545 return -ENODEV;
546
547 /*
548 * make sure this handle is for a device dependent on the dock,
549 * this would include the dock station itself
550 */
Alex Chiang50d716e2009-10-01 11:59:23 -0600551 list_for_each_entry(dock_station, &dock_stations, sibling) {
Shaohua Li61b83692008-08-28 10:07:14 +0800552 /*
553 * An ATA bay can be in a dock and itself can be ejected
Daniel Mack3ad2f3f2010-02-03 08:01:28 +0800554 * separately, so there are two 'dock stations' which need the
Shaohua Li61b83692008-08-28 10:07:14 +0800555 * ops
556 */
Shaohua Lidb350b02008-08-28 10:03:58 +0800557 dd = find_dock_dependent_device(dock_station, handle);
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200558 if (dd && !dock_init_hotplug(dd, ops, context, init, release))
Shaohua Li61b83692008-08-28 10:07:14 +0800559 ret = 0;
Len Brownc8f7a622006-07-09 17:22:28 -0400560 }
561
Shaohua Li61b83692008-08-28 10:07:14 +0800562 return ret;
Len Brownc8f7a622006-07-09 17:22:28 -0400563}
Len Brownc8f7a622006-07-09 17:22:28 -0400564EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
565
566/**
567 * unregister_hotplug_dock_device - remove yourself from the hotplug list
568 * @handle: the acpi handle of the device
569 */
570void unregister_hotplug_dock_device(acpi_handle handle)
571{
572 struct dock_dependent_device *dd;
Shaohua Lidb350b02008-08-28 10:03:58 +0800573 struct dock_station *dock_station;
Len Brownc8f7a622006-07-09 17:22:28 -0400574
Shaohua Lidb350b02008-08-28 10:03:58 +0800575 if (!dock_station_count)
Len Brownc8f7a622006-07-09 17:22:28 -0400576 return;
577
Alex Chiang50d716e2009-10-01 11:59:23 -0600578 list_for_each_entry(dock_station, &dock_stations, sibling) {
Shaohua Lidb350b02008-08-28 10:03:58 +0800579 dd = find_dock_dependent_device(dock_station, handle);
580 if (dd)
Rafael J. Wysocki21a31012013-06-24 11:22:53 +0200581 dock_release_hotplug(dd);
Shaohua Lidb350b02008-08-28 10:03:58 +0800582 }
Len Brownc8f7a622006-07-09 17:22:28 -0400583}
Len Brownc8f7a622006-07-09 17:22:28 -0400584EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
585
586/**
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800587 * handle_eject_request - handle an undock request checking for error conditions
588 *
589 * Check to make sure the dock device is still present, then undock and
590 * hotremove all the devices that may need removing.
591 */
592static int handle_eject_request(struct dock_station *ds, u32 event)
593{
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800594 if (dock_in_progress(ds))
595 return -EBUSY;
596
597 /*
598 * here we need to generate the undock
599 * event prior to actually doing the undock
600 * so that the device struct still exists.
Holger Machtafd73012008-08-06 17:56:01 +0200601 * Also, even send the dock event if the
602 * device is not present anymore
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800603 */
604 dock_event(ds, event, UNDOCK_EVENT);
Holger Machtafd73012008-08-06 17:56:01 +0200605
Rafael J. Wysocki37f90872013-06-30 23:46:42 +0200606 hot_remove_dock_devices(ds);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800607 undock(ds);
Jiang Liuc9b54712013-06-29 00:24:42 +0800608 acpi_evaluate_lck(ds->handle, 0);
609 acpi_evaluate_ej0(ds->handle);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800610 if (dock_present(ds)) {
Toshi Kanicd730182012-11-20 23:42:30 +0000611 acpi_handle_err(ds->handle, "Unable to undock!\n");
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800612 return -EBUSY;
613 }
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700614 complete_undock(ds);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800615 return 0;
616}
617
618/**
Len Brownc8f7a622006-07-09 17:22:28 -0400619 * dock_notify - act upon an acpi dock notification
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200620 * @ds: dock station
Len Brownc8f7a622006-07-09 17:22:28 -0400621 * @event: the acpi event
Len Brownc8f7a622006-07-09 17:22:28 -0400622 *
623 * If we are notified to dock, then check to see if the dock is
624 * present and then dock. Notify all drivers of the dock event,
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800625 * and then hotplug and devices that may need hotplugging.
Len Brownc8f7a622006-07-09 17:22:28 -0400626 */
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200627static void dock_notify(struct dock_station *ds, u32 event)
Len Brownc8f7a622006-07-09 17:22:28 -0400628{
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200629 acpi_handle handle = ds->handle;
630 struct acpi_device *ad;
Shaohua Lidb350b02008-08-28 10:03:58 +0800631 int surprise_removal = 0;
Len Brownc8f7a622006-07-09 17:22:28 -0400632
Shaohua Lidb350b02008-08-28 10:03:58 +0800633 /*
634 * According to acpi spec 3.0a, if a DEVICE_CHECK notification
635 * is sent and _DCK is present, it is assumed to mean an undock
636 * request.
637 */
638 if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)
639 event = ACPI_NOTIFY_EJECT_REQUEST;
640
641 /*
642 * dock station: BUS_CHECK - docked or surprise removal
643 * DEVICE_CHECK - undocked
644 * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal
645 *
646 * To simplify event handling, dock dependent device handler always
647 * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and
648 * ACPI_NOTIFY_EJECT_REQUEST for removal
649 */
Len Brownc8f7a622006-07-09 17:22:28 -0400650 switch (event) {
651 case ACPI_NOTIFY_BUS_CHECK:
Shaohua Lidb350b02008-08-28 10:03:58 +0800652 case ACPI_NOTIFY_DEVICE_CHECK:
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200653 if (!dock_in_progress(ds) && acpi_bus_get_device(handle, &ad)) {
Len Brownc8f7a622006-07-09 17:22:28 -0400654 begin_dock(ds);
655 dock(ds);
656 if (!dock_present(ds)) {
Toshi Kanicd730182012-11-20 23:42:30 +0000657 acpi_handle_err(handle, "Unable to dock!\n");
Shaohua Li8b595602008-08-28 10:02:03 +0800658 complete_dock(ds);
Len Brownc8f7a622006-07-09 17:22:28 -0400659 break;
660 }
661 atomic_notifier_call_chain(&dock_notifier_list,
662 event, NULL);
663 hotplug_dock_devices(ds, event);
664 complete_dock(ds);
665 dock_event(ds, event, DOCK_EVENT);
Jiang Liuc9b54712013-06-29 00:24:42 +0800666 acpi_evaluate_lck(ds->handle, 1);
Lin Ming3a378982010-12-13 13:36:15 +0800667 acpi_update_all_gpes();
Shaohua Lidb350b02008-08-28 10:03:58 +0800668 break;
Len Brownc8f7a622006-07-09 17:22:28 -0400669 }
Shaohua Lidb350b02008-08-28 10:03:58 +0800670 if (dock_present(ds) || dock_in_progress(ds))
671 break;
672 /* This is a surprise removal */
673 surprise_removal = 1;
674 event = ACPI_NOTIFY_EJECT_REQUEST;
675 /* Fall back */
Len Brownc8f7a622006-07-09 17:22:28 -0400676 case ACPI_NOTIFY_EJECT_REQUEST:
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700677 begin_undock(ds);
Shaohua Lif730ae12008-08-28 10:05:45 +0800678 if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))
679 || surprise_removal)
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700680 handle_eject_request(ds, event);
681 else
682 dock_event(ds, event, UNDOCK_EVENT);
Len Brownc8f7a622006-07-09 17:22:28 -0400683 break;
684 default:
Toshi Kanicd730182012-11-20 23:42:30 +0000685 acpi_handle_err(handle, "Unknown dock event %d\n", event);
Len Brownc8f7a622006-07-09 17:22:28 -0400686 }
687}
688
Zhang Rui19cd8472008-08-28 10:05:06 +0800689struct dock_data {
Zhang Rui19cd8472008-08-28 10:05:06 +0800690 struct dock_station *ds;
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200691 u32 event;
Zhang Rui19cd8472008-08-28 10:05:06 +0800692};
693
694static void acpi_dock_deferred_cb(void *context)
695{
Alex Chiang747479a2009-10-19 15:14:50 -0600696 struct dock_data *data = context;
Zhang Rui19cd8472008-08-28 10:05:06 +0800697
Rafael J. Wysocki3757b942013-02-13 14:36:47 +0100698 acpi_scan_lock_acquire();
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200699 dock_notify(data->ds, data->event);
Rafael J. Wysocki3757b942013-02-13 14:36:47 +0100700 acpi_scan_lock_release();
Zhang Rui19cd8472008-08-28 10:05:06 +0800701 kfree(data);
702}
703
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200704static void dock_notify_handler(acpi_handle handle, u32 event, void *data)
Shaohua Li6bd00a62008-08-28 10:04:29 +0800705{
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200706 struct dock_data *dd;
Shaohua Li6bd00a62008-08-28 10:04:29 +0800707
708 if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
709 && event != ACPI_NOTIFY_EJECT_REQUEST)
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200710 return;
Rafael J. Wysocki3757b942013-02-13 14:36:47 +0100711
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200712 dd = kmalloc(sizeof(*dd), GFP_KERNEL);
713 if (dd) {
714 acpi_status status;
Rafael J. Wysocki3757b942013-02-13 14:36:47 +0100715
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200716 dd->ds = data;
717 dd->event = event;
718 status = acpi_os_hotplug_execute(acpi_dock_deferred_cb, dd);
719 if (ACPI_FAILURE(status))
720 kfree(dd);
Shaohua Li6bd00a62008-08-28 10:04:29 +0800721 }
Shaohua Li6bd00a62008-08-28 10:04:29 +0800722}
723
Len Brownc8f7a622006-07-09 17:22:28 -0400724/**
725 * find_dock_devices - find devices on the dock station
726 * @handle: the handle of the device we are examining
727 * @lvl: unused
728 * @context: the dock station private data
729 * @rv: unused
730 *
731 * This function is called by acpi_walk_namespace. It will
732 * check to see if an object has an _EJD method. If it does, then it
733 * will see if it is dependent on the dock station.
734 */
Rafael J. Wysocki96c0a4d2013-06-30 23:46:02 +0200735static acpi_status __init find_dock_devices(acpi_handle handle, u32 lvl,
736 void *context, void **rv)
Len Brownc8f7a622006-07-09 17:22:28 -0400737{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200738 struct dock_station *ds = context;
Rafael J. Wysocki96c0a4d2013-06-30 23:46:02 +0200739 acpi_handle ejd = NULL;
Len Brownc8f7a622006-07-09 17:22:28 -0400740
Rafael J. Wysocki96c0a4d2013-06-30 23:46:02 +0200741 acpi_bus_get_ejd(handle, &ejd);
742 if (ejd == ds->handle)
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600743 add_dock_dependent_device(ds, handle);
744
Len Brownc8f7a622006-07-09 17:22:28 -0400745 return AE_OK;
746}
747
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800748/*
749 * show_docked - read method for "docked" file in sysfs
750 */
751static ssize_t show_docked(struct device *dev,
752 struct device_attribute *attr, char *buf)
753{
Holger Machtfc5a9f82009-01-20 12:18:24 +0100754 struct acpi_device *tmp;
755
Alex Chiangfe06fba2009-10-19 15:14:45 -0600756 struct dock_station *dock_station = dev->platform_data;
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800757
Yasuaki Ishimatsu02df7342013-01-31 03:23:53 +0000758 if (!acpi_bus_get_device(dock_station->handle, &tmp))
Holger Machtfc5a9f82009-01-20 12:18:24 +0100759 return snprintf(buf, PAGE_SIZE, "1\n");
760 return snprintf(buf, PAGE_SIZE, "0\n");
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800761}
Adrian Bunke5685b92007-10-24 18:24:42 +0200762static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800763
764/*
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700765 * show_flags - read method for flags file in sysfs
766 */
767static ssize_t show_flags(struct device *dev,
768 struct device_attribute *attr, char *buf)
769{
Alex Chiangfe06fba2009-10-19 15:14:45 -0600770 struct dock_station *dock_station = dev->platform_data;
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700771 return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
772
773}
Adrian Bunke5685b92007-10-24 18:24:42 +0200774static DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700775
776/*
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800777 * write_undock - write method for "undock" file in sysfs
778 */
779static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
780 const char *buf, size_t count)
781{
782 int ret;
Alex Chiangfe06fba2009-10-19 15:14:45 -0600783 struct dock_station *dock_station = dev->platform_data;
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800784
785 if (!count)
786 return -EINVAL;
787
Rafael J. Wysocki81120062013-06-16 00:38:30 +0200788 acpi_scan_lock_acquire();
Holger Macht9171f832008-03-12 01:07:27 +0100789 begin_undock(dock_station);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800790 ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
Rafael J. Wysocki81120062013-06-16 00:38:30 +0200791 acpi_scan_lock_release();
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800792 return ret ? ret: count;
793}
Adrian Bunke5685b92007-10-24 18:24:42 +0200794static DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
brandon@ifup.orgc80fdbe2006-12-04 14:49:58 -0800795
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800796/*
797 * show_dock_uid - read method for "uid" file in sysfs
798 */
799static ssize_t show_dock_uid(struct device *dev,
800 struct device_attribute *attr, char *buf)
801{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400802 unsigned long long lbuf;
Alex Chiangfe06fba2009-10-19 15:14:45 -0600803 struct dock_station *dock_station = dev->platform_data;
Kristen Carlson Accardi38ff4ff2007-05-09 15:04:24 -0700804 acpi_status status = acpi_evaluate_integer(dock_station->handle,
805 "_UID", NULL, &lbuf);
806 if (ACPI_FAILURE(status))
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800807 return 0;
Kristen Carlson Accardi38ff4ff2007-05-09 15:04:24 -0700808
Matthew Wilcox27663c52008-10-10 02:22:59 -0400809 return snprintf(buf, PAGE_SIZE, "%llx\n", lbuf);
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800810}
Adrian Bunke5685b92007-10-24 18:24:42 +0200811static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
Ilya A. Volynets-Evenbakhac122bb2007-02-19 15:19:31 -0800812
Shaohua Li8652b002008-08-28 10:07:45 +0800813static ssize_t show_dock_type(struct device *dev,
814 struct device_attribute *attr, char *buf)
815{
Alex Chiangfe06fba2009-10-19 15:14:45 -0600816 struct dock_station *dock_station = dev->platform_data;
Shaohua Li8652b002008-08-28 10:07:45 +0800817 char *type;
818
819 if (dock_station->flags & DOCK_IS_DOCK)
820 type = "dock_station";
821 else if (dock_station->flags & DOCK_IS_ATA)
822 type = "ata_bay";
823 else if (dock_station->flags & DOCK_IS_BAT)
824 type = "battery_bay";
825 else
826 type = "unknown";
827
828 return snprintf(buf, PAGE_SIZE, "%s\n", type);
829}
830static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
831
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600832static struct attribute *dock_attributes[] = {
833 &dev_attr_docked.attr,
834 &dev_attr_flags.attr,
835 &dev_attr_undock.attr,
836 &dev_attr_uid.attr,
837 &dev_attr_type.attr,
838 NULL
839};
840
841static struct attribute_group dock_attribute_group = {
842 .attrs = dock_attributes
843};
844
Len Brownc8f7a622006-07-09 17:22:28 -0400845/**
846 * dock_add - add a new dock station
847 * @handle: the dock station handle
848 *
849 * allocated and initialize a new dock station device. Find all devices
850 * that are on the dock station, and register for dock event notifications.
851 */
Uwe Kleine-Königd38a5ed2010-10-19 09:13:39 +0200852static int __init dock_add(acpi_handle handle)
Len Brownc8f7a622006-07-09 17:22:28 -0400853{
Alex Chiangfe06fba2009-10-19 15:14:45 -0600854 int ret, id;
855 struct dock_station ds, *dock_station;
Alex Chiang747479a2009-10-19 15:14:50 -0600856 struct platform_device *dd;
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200857 acpi_status status;
Len Brownc8f7a622006-07-09 17:22:28 -0400858
Alex Chiangfe06fba2009-10-19 15:14:45 -0600859 id = dock_station_count;
Alex Chiang49c6fb22010-02-01 10:35:18 -0700860 memset(&ds, 0, sizeof(ds));
Alex Chiang747479a2009-10-19 15:14:50 -0600861 dd = platform_device_register_data(NULL, "dock", id, &ds, sizeof(ds));
862 if (IS_ERR(dd))
863 return PTR_ERR(dd);
Alex Chiang9751cb72009-10-19 15:14:40 -0600864
Alex Chiang747479a2009-10-19 15:14:50 -0600865 dock_station = dd->dev.platform_data;
866
Len Brownc8f7a622006-07-09 17:22:28 -0400867 dock_station->handle = handle;
Alex Chiang747479a2009-10-19 15:14:50 -0600868 dock_station->dock_device = dd;
Len Brownc8f7a622006-07-09 17:22:28 -0400869 dock_station->last_dock_time = jiffies - HZ;
Len Brownc8f7a622006-07-09 17:22:28 -0400870
Alex Chiang747479a2009-10-19 15:14:50 -0600871 INIT_LIST_HEAD(&dock_station->sibling);
Alex Chiang747479a2009-10-19 15:14:50 -0600872 INIT_LIST_HEAD(&dock_station->dependent_devices);
Kristen Carlson Accardia0cd35f2007-05-09 15:08:15 -0700873
Kristen Carlson Accardi9ef2a9a2007-05-09 15:09:12 -0700874 /* we want the dock device to send uevents */
Alex Chiang747479a2009-10-19 15:14:50 -0600875 dev_set_uevent_suppress(&dd->dev, 0);
Kristen Carlson Accardi9ef2a9a2007-05-09 15:09:12 -0700876
Jiang Liuc9b54712013-06-29 00:24:42 +0800877 if (acpi_dock_match(handle))
Shaohua Lidb350b02008-08-28 10:03:58 +0800878 dock_station->flags |= DOCK_IS_DOCK;
Jiang Liuc9b54712013-06-29 00:24:42 +0800879 if (acpi_ata_match(handle))
Shaohua Lidb350b02008-08-28 10:03:58 +0800880 dock_station->flags |= DOCK_IS_ATA;
881 if (is_battery(handle))
882 dock_station->flags |= DOCK_IS_BAT;
883
Alex Chiang747479a2009-10-19 15:14:50 -0600884 ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
Shaohua Li8652b002008-08-28 10:07:45 +0800885 if (ret)
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600886 goto err_unregister;
Kristen Carlson Accardi671adbe2006-12-04 14:49:43 -0800887
Len Brownc8f7a622006-07-09 17:22:28 -0400888 /* Find dependent devices */
889 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
Lin Ming22635762009-11-13 10:06:08 +0800890 ACPI_UINT32_MAX, find_dock_devices, NULL,
891 dock_station, NULL);
Len Brownc8f7a622006-07-09 17:22:28 -0400892
893 /* add the dock station as a device dependent on itself */
Alex Chiangf69cfdd2009-10-19 15:14:29 -0600894 ret = add_dock_dependent_device(dock_station, handle);
895 if (ret)
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600896 goto err_rmgroup;
Len Brownc8f7a622006-07-09 17:22:28 -0400897
Rafael J. Wysocki59401cc2013-06-30 23:48:49 +0200898 status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
899 dock_notify_handler, dock_station);
900 if (ACPI_FAILURE(status))
901 goto err_rmgroup;
902
Shaohua Lidb350b02008-08-28 10:03:58 +0800903 dock_station_count++;
Alex Chiang50d716e2009-10-01 11:59:23 -0600904 list_add(&dock_station->sibling, &dock_stations);
Len Brownc8f7a622006-07-09 17:22:28 -0400905 return 0;
906
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600907err_rmgroup:
Rafael J. Wysockia30c4c52013-06-30 23:50:24 +0200908 remove_dock_dependent_devices(dock_station);
Alex Chiang747479a2009-10-19 15:14:50 -0600909 sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);
Alex Chiang5f46c2f2009-10-19 15:14:24 -0600910err_unregister:
Alex Chiang747479a2009-10-19 15:14:50 -0600911 platform_device_unregister(dd);
Toshi Kanicd730182012-11-20 23:42:30 +0000912 acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);
Len Brownc8f7a622006-07-09 17:22:28 -0400913 return ret;
914}
915
916/**
Toshi Kani8ab0ab22012-10-23 01:30:26 +0200917 * find_dock_and_bay - look for dock stations and bays
Len Brownc8f7a622006-07-09 17:22:28 -0400918 * @handle: acpi handle of a device
919 * @lvl: unused
Toshi Kani8ab0ab22012-10-23 01:30:26 +0200920 * @context: unused
Len Brownc8f7a622006-07-09 17:22:28 -0400921 * @rv: unused
922 *
Toshi Kani8ab0ab22012-10-23 01:30:26 +0200923 * This is called by acpi_walk_namespace to look for dock stations and bays.
Len Brownc8f7a622006-07-09 17:22:28 -0400924 */
Uwe Kleine-Königd38a5ed2010-10-19 09:13:39 +0200925static __init acpi_status
Toshi Kani8ab0ab22012-10-23 01:30:26 +0200926find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
Len Brownc8f7a622006-07-09 17:22:28 -0400927{
Jiang Liuc9b54712013-06-29 00:24:42 +0800928 if (acpi_dock_match(handle) || is_ejectable_bay(handle))
Zhang Rui1ee4d612010-03-22 15:46:49 +0800929 dock_add(handle);
Alex Chiang747479a2009-10-19 15:14:50 -0600930
Zhang Rui1ee4d612010-03-22 15:46:49 +0800931 return AE_OK;
Len Brownc8f7a622006-07-09 17:22:28 -0400932}
933
Rafael J. Wysocki2ce65fe2013-07-04 13:25:04 +0200934void __init acpi_dock_init(void)
Len Brownc8f7a622006-07-09 17:22:28 -0400935{
Len Brown816c2ed2008-06-24 22:57:12 -0400936 if (acpi_disabled)
Rafael J. Wysocki2ce65fe2013-07-04 13:25:04 +0200937 return;
Len Brown816c2ed2008-06-24 22:57:12 -0400938
Toshi Kani8ab0ab22012-10-23 01:30:26 +0200939 /* look for dock stations and bays */
Len Brownc8f7a622006-07-09 17:22:28 -0400940 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
Toshi Kani8ab0ab22012-10-23 01:30:26 +0200941 ACPI_UINT32_MAX, find_dock_and_bay, NULL, NULL, NULL);
Len Brownc8f7a622006-07-09 17:22:28 -0400942
Shaohua Lidb350b02008-08-28 10:03:58 +0800943 if (!dock_station_count) {
Toshi Kanicd730182012-11-20 23:42:30 +0000944 pr_info(PREFIX "No dock devices found.\n");
Rafael J. Wysocki2ce65fe2013-07-04 13:25:04 +0200945 return;
Shaohua Lidb350b02008-08-28 10:03:58 +0800946 }
Len Brownc8f7a622006-07-09 17:22:28 -0400947
Jiang Liuf22ff552013-06-29 00:24:34 +0800948 ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
Toshi Kanicd730182012-11-20 23:42:30 +0000949 pr_info(PREFIX "%s: %d docks/bays found\n",
Shaohua Lidb350b02008-08-28 10:03:58 +0800950 ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
Len Brownc8f7a622006-07-09 17:22:28 -0400951}