dock.c revision 0a918a9432cc30aede10f904253b66ea6ab485ac
1c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/*
2c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  dock.c - ACPI dock station driver
3c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
4c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
5c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
6c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
8c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  This program is free software; you can redistribute it and/or modify
9c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  it under the terms of the GNU General Public License as published by
10c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  the Free Software Foundation; either version 2 of the License, or (at
11c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  your option) any later version.
12c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
13c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  This program is distributed in the hope that it will be useful, but
14c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  WITHOUT ANY WARRANTY; without even the implied warranty of
15c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  General Public License for more details.
17c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
18c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  You should have received a copy of the GNU General Public License along
19c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  with this program; if not, write to the Free Software Foundation, Inc.,
20c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
22c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
24c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
25c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown#include <linux/kernel.h>
26c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown#include <linux/module.h>
27c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown#include <linux/init.h>
28c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown#include <linux/types.h>
29c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown#include <linux/notifier.h>
30671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi#include <linux/platform_device.h>
31914e26379decf1fd984b22e51fd2e4209b7a7f1bAl Viro#include <linux/jiffies.h>
3262a6d7fd9bc1d85f9aae734c46234e88fa839db0Randy Dunlap#include <linux/stddef.h>
33c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown#include <acpi/acpi_bus.h>
34c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown#include <acpi/acpi_drivers.h>
35c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
367cda93e008e1a477970adbf82dba81a5d4f0ae40Len Brown#define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver"
37c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
38f52fd66d2ea794010c2d7536cf8e6abed0ac4947Len BrownACPI_MODULE_NAME("dock");
39c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownMODULE_AUTHOR("Kristen Carlson Accardi");
407cda93e008e1a477970adbf82dba81a5d4f0ae40Len BrownMODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION);
41c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownMODULE_LICENSE("GPL");
42c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
43a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardistatic int immediate_undock = 1;
44a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardimodule_param(immediate_undock, bool, 0644);
45a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson AccardiMODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
46a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	"undock immediately when the undock button is pressed, 0 will cause"
47a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	" the driver to wait for userspace to write the undock sysfs file "
48a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	" before undocking");
49a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi
50c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic struct atomic_notifier_head dock_notifier_list;
51671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardistatic char dock_device_name[] = "dock";
52c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
53a340af14b4c08a53c5f7d821d8bd910e17403384Frank Seidelstatic const struct acpi_device_id dock_device_ids[] = {
54a340af14b4c08a53c5f7d821d8bd910e17403384Frank Seidel	{"LNXDOCK", 0},
55a340af14b4c08a53c5f7d821d8bd910e17403384Frank Seidel	{"", 0},
56a340af14b4c08a53c5f7d821d8bd910e17403384Frank Seidel};
57a340af14b4c08a53c5f7d821d8bd910e17403384Frank SeidelMODULE_DEVICE_TABLE(acpi, dock_device_ids);
58a340af14b4c08a53c5f7d821d8bd910e17403384Frank Seidel
59c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstruct dock_station {
60c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_handle handle;
61c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	unsigned long last_dock_time;
62c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	u32 flags;
63c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	spinlock_t dd_lock;
648b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi	struct mutex hp_lock;
65c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct list_head dependent_devices;
66c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct list_head hotplug_devices;
67db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
68db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct list_head sibiling;
69db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct platform_device *dock_device;
70c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown};
71db350b084dc2cf816288643861ce07b0562dd723Shaohua Listatic LIST_HEAD(dock_stations);
72db350b084dc2cf816288643861ce07b0562dd723Shaohua Listatic int dock_station_count;
73c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
74c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstruct dock_dependent_device {
75c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct list_head list;
76c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct list_head hotplug_list;
77c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_handle handle;
781253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li	struct acpi_dock_ops *ops;
79c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	void *context;
80c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown};
81c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
82c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown#define DOCK_DOCKING	0x00000001
83a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi#define DOCK_UNDOCKING  0x00000002
84db350b084dc2cf816288643861ce07b0562dd723Shaohua Li#define DOCK_IS_DOCK	0x00000010
85db350b084dc2cf816288643861ce07b0562dd723Shaohua Li#define DOCK_IS_ATA	0x00000020
86db350b084dc2cf816288643861ce07b0562dd723Shaohua Li#define DOCK_IS_BAT	0x00000040
875669021e40964303994a20633548732c6bb26636Kristen Carlson Accardi#define DOCK_EVENT	3
885669021e40964303994a20633548732c6bb26636Kristen Carlson Accardi#define UNDOCK_EVENT	2
89c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
90c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/*****************************************************************************
91c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *                         Dock Dependent device functions                   *
92c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *****************************************************************************/
93c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
94c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  alloc_dock_dependent_device - allocate and init a dependent device
95c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  @handle: the acpi_handle of the dependent device
96c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
97c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  Allocate memory for a dependent device structure for a device referenced
98c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  by the acpi handle
99c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
100c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic struct dock_dependent_device *
101c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownalloc_dock_dependent_device(acpi_handle handle)
102c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
103c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct dock_dependent_device *dd;
104c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
105c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
106c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (dd) {
107c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		dd->handle = handle;
108c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		INIT_LIST_HEAD(&dd->list);
109c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		INIT_LIST_HEAD(&dd->hotplug_list);
110c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
111c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return dd;
112c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
113c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
114c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
115c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * add_dock_dependent_device - associate a device with the dock station
116c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: The dock station
117c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @dd: The dependent device
118c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
119c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Add the dependent device to the dock's dependent device list.
120c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
121c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void
122c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownadd_dock_dependent_device(struct dock_station *ds,
123c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			  struct dock_dependent_device *dd)
124c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
125c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	spin_lock(&ds->dd_lock);
126c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	list_add_tail(&dd->list, &ds->dependent_devices);
127c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	spin_unlock(&ds->dd_lock);
128c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
129c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
130c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
131c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_add_hotplug_device - associate a hotplug handler with the dock station
132c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: The dock station
133c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @dd: The dependent device struct
134c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
135c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Add the dependent device to the dock's hotplug device list
136c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
137c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void
138c8f7a62cdde461914c6457d5f4362538ed810bf4Len Browndock_add_hotplug_device(struct dock_station *ds,
139c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			struct dock_dependent_device *dd)
140c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
1418b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi	mutex_lock(&ds->hp_lock);
142c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
1438b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi	mutex_unlock(&ds->hp_lock);
144c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
145c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
146c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
147c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_del_hotplug_device - remove a hotplug handler from the dock station
148c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: The dock station
149c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @dd: the dependent device struct
150c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
151c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Delete the dependent device from the dock's hotplug device list
152c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
153c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void
154c8f7a62cdde461914c6457d5f4362538ed810bf4Len Browndock_del_hotplug_device(struct dock_station *ds,
155c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			struct dock_dependent_device *dd)
156c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
1578b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi	mutex_lock(&ds->hp_lock);
158c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	list_del(&dd->hotplug_list);
1598b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi	mutex_unlock(&ds->hp_lock);
160c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
161c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
162c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
163c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * find_dock_dependent_device - get a device dependent on this dock
164c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station
165c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the acpi_handle of the device we want
166c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
167c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * iterate over the dependent device list for this dock.  If the
168c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dependent device matches the handle, return.
169c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
170c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic struct dock_dependent_device *
171c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownfind_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
172c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
173c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct dock_dependent_device *dd;
174c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
175c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	spin_lock(&ds->dd_lock);
176c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	list_for_each_entry(dd, &ds->dependent_devices, list) {
177c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		if (handle == dd->handle) {
178c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			spin_unlock(&ds->dd_lock);
179c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			return dd;
180c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		}
181c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
182c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	spin_unlock(&ds->dd_lock);
183c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return NULL;
184c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
185c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
186c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/*****************************************************************************
187c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *                         Dock functions                                    *
188c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *****************************************************************************/
189c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
190c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * is_dock - see if a device is a dock station
191c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: acpi handle of the device
192c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
193c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If an acpi object has a _DCK method, then it is by definition a dock
194c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * station, so return true.
195c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
196c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int is_dock(acpi_handle handle)
197c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
198c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_status status;
199c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_handle tmp;
200c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
201c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	status = acpi_get_handle(handle, "_DCK", &tmp);
202c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (ACPI_FAILURE(status))
203c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		return 0;
204c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return 1;
205c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
206c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
207db350b084dc2cf816288643861ce07b0562dd723Shaohua Listatic int is_ejectable(acpi_handle handle)
208db350b084dc2cf816288643861ce07b0562dd723Shaohua Li{
209db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	acpi_status status;
210db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	acpi_handle tmp;
211db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
212db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	status = acpi_get_handle(handle, "_EJ0", &tmp);
213db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (ACPI_FAILURE(status))
214db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		return 0;
215db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	return 1;
216db350b084dc2cf816288643861ce07b0562dd723Shaohua Li}
217db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
218db350b084dc2cf816288643861ce07b0562dd723Shaohua Listatic int is_ata(acpi_handle handle)
219db350b084dc2cf816288643861ce07b0562dd723Shaohua Li{
220db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	acpi_handle tmp;
221db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
222db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) ||
223db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	   (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) ||
224db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	   (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) ||
225db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	   (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp))))
226db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		return 1;
227db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
228db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	return 0;
229db350b084dc2cf816288643861ce07b0562dd723Shaohua Li}
230db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
231db350b084dc2cf816288643861ce07b0562dd723Shaohua Listatic int is_battery(acpi_handle handle)
232db350b084dc2cf816288643861ce07b0562dd723Shaohua Li{
233db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct acpi_device_info *info;
234db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
235db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	int ret = 1;
236db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
237db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!ACPI_SUCCESS(acpi_get_object_info(handle, &buffer)))
238db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		return 0;
239db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	info = buffer.pointer;
240db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!(info->valid & ACPI_VALID_HID))
241db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		ret = 0;
242db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	else
243db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		ret = !strcmp("PNP0C0A", info->hardware_id.value);
244db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
245db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	kfree(buffer.pointer);
246db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	return ret;
247db350b084dc2cf816288643861ce07b0562dd723Shaohua Li}
248db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
249db350b084dc2cf816288643861ce07b0562dd723Shaohua Listatic int is_ejectable_bay(acpi_handle handle)
250db350b084dc2cf816288643861ce07b0562dd723Shaohua Li{
251db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	acpi_handle phandle;
252db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!is_ejectable(handle))
253db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		return 0;
254db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (is_battery(handle) || is_ata(handle))
255db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		return 1;
256db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!acpi_get_parent(handle, &phandle) && is_ata(phandle))
257db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		return 1;
258db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	return 0;
259db350b084dc2cf816288643861ce07b0562dd723Shaohua Li}
260db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
261c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
262c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * is_dock_device - see if a device is on a dock station
263c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: acpi handle of the device
264c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
265c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If this device is either the dock station itself,
266c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * or is a device dependent on the dock station, then it
267c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * is a dock device
268c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
269c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownint is_dock_device(acpi_handle handle)
270c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
271db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct dock_station *dock_station;
272db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
273db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!dock_station_count)
274c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		return 0;
275c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
276db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (is_dock(handle))
277c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		return 1;
278db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	list_for_each_entry(dock_station, &dock_stations, sibiling) {
279db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		if (find_dock_dependent_device(dock_station, handle))
280db350b084dc2cf816288643861ce07b0562dd723Shaohua Li			return 1;
281db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	}
282c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
283c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return 0;
284c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
285c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
286c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(is_dock_device);
287c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
288c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
289c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_present - see if the dock station is present.
290c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station
291c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
292c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * execute the _STA method.  note that present does not
293c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * imply that we are docked.
294c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
295c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int dock_present(struct dock_station *ds)
296c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
297c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	unsigned long sta;
298c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_status status;
299c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
300c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (ds) {
301c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
302c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		if (ACPI_SUCCESS(status) && sta)
303c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			return 1;
304c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
305c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return 0;
306c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
307c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
308c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
309c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
310c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
311c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_create_acpi_device - add new devices to acpi
312c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle - handle of the device to add
313c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
314c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  This function will create a new acpi_device for the given
315c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  handle if one does not exist already.  This should cause
316c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  acpi to scan for drivers for the given devices, and call
317c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  matching driver's add routine.
318c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
319c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  Returns a pointer to the acpi_device corresponding to the handle.
320c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
321c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic struct acpi_device * dock_create_acpi_device(acpi_handle handle)
322c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
323c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct acpi_device *device = NULL;
324c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct acpi_device *parent_device;
325c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_handle parent;
326c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	int ret;
327c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
328c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (acpi_bus_get_device(handle, &device)) {
329c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		/*
330c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		 * no device created for this object,
331c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		 * so we should create one.
332c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		 */
333c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		acpi_get_parent(handle, &parent);
334c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		if (acpi_bus_get_device(parent, &parent_device))
335c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			parent_device = NULL;
336c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
337c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		ret = acpi_bus_add(&device, parent_device, handle,
338c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			ACPI_BUS_TYPE_DEVICE);
339c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		if (ret) {
340c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			pr_debug("error adding bus, %x\n",
341c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown				-ret);
342c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			return NULL;
343c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		}
344c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
345c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return device;
346c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
347c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
348c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
349c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_remove_acpi_device - remove the acpi_device struct from acpi
350c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle - the handle of the device to remove
351c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
352c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  Tell acpi to remove the acpi_device.  This should cause any loaded
353c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *  driver to have it's remove routine called.
354c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
355c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void dock_remove_acpi_device(acpi_handle handle)
356c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
357c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct acpi_device *device;
358c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	int ret;
359c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
360c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (!acpi_bus_get_device(handle, &device)) {
361c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		ret = acpi_bus_trim(device, 1);
362c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		if (ret)
363c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			pr_debug("error removing bus, %x\n", -ret);
364c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
365c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
366c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
367c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
368c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
369c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * hotplug_dock_devices - insert or remove devices on the dock station
370c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station
371c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @event: either bus check or eject request
372c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
373c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Some devices on the dock station need to have drivers called
374c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * to perform hotplug operations after a dock event has occurred.
375c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Traverse the list of dock devices that have registered a
376c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * hotplug handler, and call the handler.
377c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
378c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void hotplug_dock_devices(struct dock_station *ds, u32 event)
379c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
380c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct dock_dependent_device *dd;
381c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
3828b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi	mutex_lock(&ds->hp_lock);
383c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
384c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/*
385c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 * First call driver specific hotplug functions
386c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 */
387c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
3881253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li		if (dd->ops && dd->ops->handler)
3891253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li			dd->ops->handler(dd->handle, event, dd->context);
390c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
391c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
392c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/*
393c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 * Now make sure that an acpi_device is created for each
394c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 * dependent device, or removed if this is an eject request.
395c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 * This will cause acpi_drivers to be stopped/started if they
396c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 * exist
397c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 */
398c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	list_for_each_entry(dd, &ds->dependent_devices, list) {
399c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		if (event == ACPI_NOTIFY_EJECT_REQUEST)
400c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			dock_remove_acpi_device(dd->handle);
401c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		else
402c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			dock_create_acpi_device(dd->handle);
403c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
4048b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi	mutex_unlock(&ds->hp_lock);
405c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
406c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
407c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void dock_event(struct dock_station *ds, u32 event, int num)
408c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
409db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct device *dev = &ds->dock_device->dev;
41066b568218ab73be161dc109b913e6fa7dda44e16Holger Macht	char event_string[13];
41179a8f70b4b9127eacfc91dd1436c4a7be05e62abKristen Carlson Accardi	char *envp[] = { event_string, NULL };
4121253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li	struct dock_dependent_device *dd;
41379a8f70b4b9127eacfc91dd1436c4a7be05e62abKristen Carlson Accardi
41479a8f70b4b9127eacfc91dd1436c4a7be05e62abKristen Carlson Accardi	if (num == UNDOCK_EVENT)
41566b568218ab73be161dc109b913e6fa7dda44e16Holger Macht		sprintf(event_string, "EVENT=undock");
41679a8f70b4b9127eacfc91dd1436c4a7be05e62abKristen Carlson Accardi	else
41766b568218ab73be161dc109b913e6fa7dda44e16Holger Macht		sprintf(event_string, "EVENT=dock");
41879a8f70b4b9127eacfc91dd1436c4a7be05e62abKristen Carlson Accardi
4195669021e40964303994a20633548732c6bb26636Kristen Carlson Accardi	/*
4208ea86e0ba7c9d16ae0f35cb0c4165194fa573f7aKristen Carlson Accardi	 * Indicate that the status of the dock station has
4218ea86e0ba7c9d16ae0f35cb0c4165194fa573f7aKristen Carlson Accardi	 * changed.
4225669021e40964303994a20633548732c6bb26636Kristen Carlson Accardi	 */
4231253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li	if (num == DOCK_EVENT)
4241253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
4251253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li
4261253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
4271253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li		if (dd->ops && dd->ops->uevent)
4281253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li			dd->ops->uevent(dd->handle, event, dd->context);
4291253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li	if (num != DOCK_EVENT)
4301253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
431c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
432c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
433c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
434c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * eject_dock - respond to a dock eject request
435c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station
436c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
437c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * This is called after _DCK is called, to execute the dock station's
438c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * _EJ0 method.
439c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
440c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void eject_dock(struct dock_station *ds)
441c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
442c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct acpi_object_list arg_list;
443c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	union acpi_object arg;
444c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_status status;
445c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_handle tmp;
446c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
447c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/* all dock devices should have _EJ0, but check anyway */
448c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	status = acpi_get_handle(ds->handle, "_EJ0", &tmp);
449c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (ACPI_FAILURE(status)) {
450c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		pr_debug("No _EJ0 support for dock device\n");
451c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		return;
452c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
453c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
454c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	arg_list.count = 1;
455c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	arg_list.pointer = &arg;
456c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	arg.type = ACPI_TYPE_INTEGER;
457c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	arg.integer.value = 1;
458c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
459c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
460c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown					      &arg_list, NULL)))
461c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		pr_debug("Failed to evaluate _EJ0!\n");
462c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
463c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
464c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
465c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * handle_dock - handle a dock event
466c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station
467c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @dock: to dock, or undock - that is the question
468c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
469c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Execute the _DCK method in response to an acpi event
470c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
471c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void handle_dock(struct dock_station *ds, int dock)
472c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
473c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_status status;
474c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct acpi_object_list arg_list;
475c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	union acpi_object arg;
476c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
477c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
478c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
479c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer);
480c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
4819254bc845db90a123cf992e983539d0ee409f22aDmitry Torokhov	printk(KERN_INFO PREFIX "%s - %s\n",
4829254bc845db90a123cf992e983539d0ee409f22aDmitry Torokhov		(char *)name_buffer.pointer, dock ? "docking" : "undocking");
483c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
484c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/* _DCK method has one argument */
485c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	arg_list.count = 1;
486c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	arg_list.pointer = &arg;
487c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	arg.type = ACPI_TYPE_INTEGER;
488c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	arg.integer.value = dock;
489c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
490db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
4910a918a9432cc30aede10f904253b66ea6ab485acThomas Renninger		ACPI_EXCEPTION((AE_INFO, status, "%s - failed to execute"
4920a918a9432cc30aede10f904253b66ea6ab485acThomas Renninger			" _DCK\n", (char *)name_buffer.pointer));
4930a918a9432cc30aede10f904253b66ea6ab485acThomas Renninger
494c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	kfree(buffer.pointer);
495c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	kfree(name_buffer.pointer);
496c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
497c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
498c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic inline void dock(struct dock_station *ds)
499c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
500c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	handle_dock(ds, 1);
501c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
502c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
503c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic inline void undock(struct dock_station *ds)
504c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
505c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	handle_dock(ds, 0);
506c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
507c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
508c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic inline void begin_dock(struct dock_station *ds)
509c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
510c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	ds->flags |= DOCK_DOCKING;
511c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
512c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
513c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic inline void complete_dock(struct dock_station *ds)
514c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
515c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	ds->flags &= ~(DOCK_DOCKING);
516c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	ds->last_dock_time = jiffies;
517c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
518c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
519a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardistatic inline void begin_undock(struct dock_station *ds)
520a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi{
521a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	ds->flags |= DOCK_UNDOCKING;
522a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi}
523a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi
524a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardistatic inline void complete_undock(struct dock_station *ds)
525a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi{
526a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	ds->flags &= ~(DOCK_UNDOCKING);
527a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi}
528a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi
529406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Listatic void dock_lock(struct dock_station *ds, int lock)
530406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li{
531406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	struct acpi_object_list arg_list;
532406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	union acpi_object arg;
533406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	acpi_status status;
534406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li
535406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	arg_list.count = 1;
536406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	arg_list.pointer = &arg;
537406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	arg.type = ACPI_TYPE_INTEGER;
538406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	arg.integer.value = !!lock;
539406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	status = acpi_evaluate_object(ds->handle, "_LCK", &arg_list, NULL);
540406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
541406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li		if (lock)
542406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li			printk(KERN_WARNING PREFIX "Locking device failed\n");
543406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li		else
544406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li			printk(KERN_WARNING PREFIX "Unlocking device failed\n");
545406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	}
546406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li}
547406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li
548c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
549c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_in_progress - see if we are in the middle of handling a dock event
550c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station
551c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
552c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Sometimes while docking, false dock events can be sent to the driver
553c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * because good connections aren't made or some other reason.  Ignore these
554c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * if we are in the middle of doing something.
555c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
556c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int dock_in_progress(struct dock_station *ds)
557c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
558c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if ((ds->flags & DOCK_DOCKING) ||
559c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	    time_before(jiffies, (ds->last_dock_time + HZ)))
560c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		return 1;
561c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return 0;
562c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
563c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
564c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
565c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * register_dock_notifier - add yourself to the dock notifier list
566c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @nb: the callers notifier block
567c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
568c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If a driver wishes to be notified about dock events, they can
569c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * use this function to put a notifier block on the dock notifier list.
570c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * this notifier call chain will be called after a dock event, but
571c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * before hotplugging any new devices.
572c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
573c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownint register_dock_notifier(struct notifier_block *nb)
574c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
575db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!dock_station_count)
5762548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava		return -ENODEV;
5772548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava
578c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return atomic_notifier_chain_register(&dock_notifier_list, nb);
579c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
580c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
581c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(register_dock_notifier);
582c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
583c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
584c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * unregister_dock_notifier - remove yourself from the dock notifier list
585c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @nb: the callers notifier block
586c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
587c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownvoid unregister_dock_notifier(struct notifier_block *nb)
588c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
589db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!dock_station_count)
5902548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava		return;
5912548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava
592c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	atomic_notifier_chain_unregister(&dock_notifier_list, nb);
593c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
594c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
595c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(unregister_dock_notifier);
596c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
597c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
598c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * register_hotplug_dock_device - register a hotplug function
599c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the handle of the device
6001253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li * @ops: handlers to call after docking
601c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @context: device specific data
602c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
603c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If a driver would like to perform a hotplug operation after a dock
604c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * event, they can register an acpi_notifiy_handler to be called by
605c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * the dock driver after _DCK is executed.
606c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
607c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownint
6081253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Liregister_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops,
609c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			     void *context)
610c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
611c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct dock_dependent_device *dd;
612db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct dock_station *dock_station;
61361b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li	int ret = -EINVAL;
614c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
615db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!dock_station_count)
616c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		return -ENODEV;
617c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
618c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/*
619c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 * make sure this handle is for a device dependent on the dock,
620c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 * this would include the dock station itself
621c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	 */
622db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	list_for_each_entry(dock_station, &dock_stations, sibiling) {
62361b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li		/*
62461b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li		 * An ATA bay can be in a dock and itself can be ejected
62561b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li		 * seperately, so there are two 'dock stations' which need the
62661b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li		 * ops
62761b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li		 */
628db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dd = find_dock_dependent_device(dock_station, handle);
629db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		if (dd) {
6301253f7aabfebc51446dbec5c8895c5c8846dfe06Shaohua Li			dd->ops = ops;
631db350b084dc2cf816288643861ce07b0562dd723Shaohua Li			dd->context = context;
632db350b084dc2cf816288643861ce07b0562dd723Shaohua Li			dock_add_hotplug_device(dock_station, dd);
63361b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li			ret = 0;
634db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		}
635c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
636c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
63761b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li	return ret;
638c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
639c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
640c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(register_hotplug_dock_device);
641c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
642c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
643c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * unregister_hotplug_dock_device - remove yourself from the hotplug list
644c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the acpi handle of the device
645c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
646c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownvoid unregister_hotplug_dock_device(acpi_handle handle)
647c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
648c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct dock_dependent_device *dd;
649db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct dock_station *dock_station;
650c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
651db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!dock_station_count)
652c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		return;
653c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
654db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	list_for_each_entry(dock_station, &dock_stations, sibiling) {
655db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dd = find_dock_dependent_device(dock_station, handle);
656db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		if (dd)
657db350b084dc2cf816288643861ce07b0562dd723Shaohua Li			dock_del_hotplug_device(dock_station, dd);
658db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	}
659c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
660c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
661c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
662c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
663c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
664c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * handle_eject_request - handle an undock request checking for error conditions
665c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org *
666c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * Check to make sure the dock device is still present, then undock and
667c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * hotremove all the devices that may need removing.
668c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org */
669c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.orgstatic int handle_eject_request(struct dock_station *ds, u32 event)
670c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org{
671c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	if (dock_in_progress(ds))
672c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		return -EBUSY;
673c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org
674c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	/*
675c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	 * here we need to generate the undock
676c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	 * event prior to actually doing the undock
677c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	 * so that the device struct still exists.
678afd7301ddb762b66bf1831b0820b402e5d2c439dHolger Macht	 * Also, even send the dock event if the
679afd7301ddb762b66bf1831b0820b402e5d2c439dHolger Macht	 * device is not present anymore
680c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	 */
681c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	dock_event(ds, event, UNDOCK_EVENT);
682afd7301ddb762b66bf1831b0820b402e5d2c439dHolger Macht
683c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
684c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	undock(ds);
685406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li	dock_lock(ds, 0);
686c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	eject_dock(ds);
687c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	if (dock_present(ds)) {
688c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		printk(KERN_ERR PREFIX "Unable to undock!\n");
689c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		return -EBUSY;
690c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	}
691a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	complete_undock(ds);
692c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	return 0;
693c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org}
694c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org
695c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org/**
696c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_notify - act upon an acpi dock notification
697c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the dock station handle
698c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @event: the acpi event
699c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @data: our driver data struct
700c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
701c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If we are notified to dock, then check to see if the dock is
702c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * present and then dock.  Notify all drivers of the dock event,
703c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * and then hotplug and devices that may need hotplugging.
704c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
705c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void dock_notify(acpi_handle handle, u32 event, void *data)
706c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
70750dd096973f1d95aa03c6a6d9e148d706b62b68eJan Engelhardt	struct dock_station *ds = data;
7088b59560a3baf2e7c24e0fb92ea5d09eca92805dbShaohua Li	struct acpi_device *tmp;
709db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	int surprise_removal = 0;
710c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
711db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	/*
712db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 * According to acpi spec 3.0a, if a DEVICE_CHECK notification
713db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 * is sent and _DCK is present, it is assumed to mean an undock
714db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 * request.
715db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 */
716db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)
717db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		event = ACPI_NOTIFY_EJECT_REQUEST;
718db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
719db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	/*
720db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 * dock station: BUS_CHECK - docked or surprise removal
721db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 *		 DEVICE_CHECK - undocked
722db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal
723db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 *
724db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 * To simplify event handling, dock dependent device handler always
725db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and
726db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 * ACPI_NOTIFY_EJECT_REQUEST for removal
727db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	 */
728c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	switch (event) {
729c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	case ACPI_NOTIFY_BUS_CHECK:
730db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	case ACPI_NOTIFY_DEVICE_CHECK:
7318b59560a3baf2e7c24e0fb92ea5d09eca92805dbShaohua Li		if (!dock_in_progress(ds) && acpi_bus_get_device(ds->handle,
7328b59560a3baf2e7c24e0fb92ea5d09eca92805dbShaohua Li		   &tmp)) {
733c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			begin_dock(ds);
734c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			dock(ds);
735c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			if (!dock_present(ds)) {
736c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown				printk(KERN_ERR PREFIX "Unable to dock!\n");
7378b59560a3baf2e7c24e0fb92ea5d09eca92805dbShaohua Li				complete_dock(ds);
738c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown				break;
739c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			}
740c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			atomic_notifier_call_chain(&dock_notifier_list,
741c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown						   event, NULL);
742c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			hotplug_dock_devices(ds, event);
743c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			complete_dock(ds);
744c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			dock_event(ds, event, DOCK_EVENT);
745406f692d0803d73acd3984c1e11719d3a913fd5eShaohua Li			dock_lock(ds, 1);
746db350b084dc2cf816288643861ce07b0562dd723Shaohua Li			break;
747c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		}
748db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		if (dock_present(ds) || dock_in_progress(ds))
749db350b084dc2cf816288643861ce07b0562dd723Shaohua Li			break;
750db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		/* This is a surprise removal */
751db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		surprise_removal = 1;
752db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		event = ACPI_NOTIFY_EJECT_REQUEST;
753db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		/* Fall back */
754c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	case ACPI_NOTIFY_EJECT_REQUEST:
755a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		begin_undock(ds);
756f730ae1838635a02aa60834762c61566911d004cShaohua Li		if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))
757f730ae1838635a02aa60834762c61566911d004cShaohua Li		   || surprise_removal)
758a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi			handle_eject_request(ds, event);
759a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		else
760a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi			dock_event(ds, event, UNDOCK_EVENT);
761c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		break;
762c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	default:
763c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
764c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
765c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
766c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
76719cd847ab24fefe9e50101ec94479e0400a08650Zhang Ruistruct dock_data {
76819cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui	acpi_handle handle;
76919cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui	unsigned long event;
77019cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui	struct dock_station *ds;
77119cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui};
77219cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui
77319cd847ab24fefe9e50101ec94479e0400a08650Zhang Ruistatic void acpi_dock_deferred_cb(void *context)
77419cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui{
77519cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui	struct dock_data *data = (struct dock_data *)context;
77619cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui
77719cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui	dock_notify(data->handle, data->event, data->ds);
77819cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui	kfree(data);
77919cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui}
78019cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui
7816bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Listatic int acpi_dock_notifier_call(struct notifier_block *this,
7826bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	unsigned long event, void *data)
7836bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li{
7846bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	struct dock_station *dock_station;
7856bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	acpi_handle handle = (acpi_handle)data;
7866bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li
7876bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
7886bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	   && event != ACPI_NOTIFY_EJECT_REQUEST)
7896bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li		return 0;
7906bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	list_for_each_entry(dock_station, &dock_stations, sibiling) {
7916bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li		if (dock_station->handle == handle) {
79219cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui			struct dock_data *dock_data;
79319cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui
79419cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui			dock_data = kmalloc(sizeof(*dock_data), GFP_KERNEL);
79519cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui			if (!dock_data)
79619cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui				return 0;
79719cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui			dock_data->handle = handle;
79819cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui			dock_data->event = event;
79919cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui			dock_data->ds = dock_station;
80019cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui			acpi_os_hotplug_execute(acpi_dock_deferred_cb,
80119cd847ab24fefe9e50101ec94479e0400a08650Zhang Rui				dock_data);
8026bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li			return 0 ;
8036bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li		}
8046bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	}
8056bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	return 0;
8066bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li}
8076bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li
8086bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Listatic struct notifier_block dock_acpi_notifier = {
8096bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	.notifier_call = acpi_dock_notifier_call,
8106bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li};
8116bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li
812c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
813c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * find_dock_devices - find devices on the dock station
814c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the handle of the device we are examining
815c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @lvl: unused
816c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @context: the dock station private data
817c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @rv: unused
818c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
819c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * This function is called by acpi_walk_namespace.  It will
820c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * check to see if an object has an _EJD method.  If it does, then it
821c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * will see if it is dependent on the dock station.
822c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
823c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic acpi_status
824c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownfind_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
825c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
826c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_status status;
827fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi	acpi_handle tmp, parent;
82850dd096973f1d95aa03c6a6d9e148d706b62b68eJan Engelhardt	struct dock_station *ds = context;
829c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct dock_dependent_device *dd;
830c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
831c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	status = acpi_bus_get_ejd(handle, &tmp);
832fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi	if (ACPI_FAILURE(status)) {
833fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi		/* try the parent device as well */
834fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi		status = acpi_get_parent(handle, &parent);
835fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi		if (ACPI_FAILURE(status))
836fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi			goto fdd_out;
837fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi		/* see if parent is dependent on dock */
838fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi		status = acpi_bus_get_ejd(parent, &tmp);
839fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi		if (ACPI_FAILURE(status))
840fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi			goto fdd_out;
841fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi	}
842c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
843c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (tmp == ds->handle) {
844c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		dd = alloc_dock_dependent_device(handle);
845c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		if (dd)
846c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			add_dock_dependent_device(ds, dd);
847c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
848fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardifdd_out:
849c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return AE_OK;
850c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
851c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
852c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org/*
853c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * show_docked - read method for "docked" file in sysfs
854c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org */
855c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.orgstatic ssize_t show_docked(struct device *dev,
856c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org			   struct device_attribute *attr, char *buf)
857c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org{
858db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct dock_station *dock_station = *((struct dock_station **)
859db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dev->platform_data);
860c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station));
861c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org
862c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org}
863e5685b9d35c2cc0a98425b05df30cb837dd1e632Adrian Bunkstatic DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
864c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org
865c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org/*
866a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi * show_flags - read method for flags file in sysfs
867a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi */
868a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardistatic ssize_t show_flags(struct device *dev,
869a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi			  struct device_attribute *attr, char *buf)
870a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi{
871db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct dock_station *dock_station = *((struct dock_station **)
872db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dev->platform_data);
873a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
874a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi
875a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi}
876e5685b9d35c2cc0a98425b05df30cb837dd1e632Adrian Bunkstatic DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
877a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi
878a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi/*
879c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * write_undock - write method for "undock" file in sysfs
880c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org */
881c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.orgstatic ssize_t write_undock(struct device *dev, struct device_attribute *attr,
882c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org			   const char *buf, size_t count)
883c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org{
884c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	int ret;
885db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct dock_station *dock_station = *((struct dock_station **)
886db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dev->platform_data);
887c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org
888c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	if (!count)
889c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		return -EINVAL;
890c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org
8919171f83488940e4ff85f1b7137773eb1f306cdefHolger Macht	begin_undock(dock_station);
892c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
893c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	return ret ? ret: count;
894c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org}
895e5685b9d35c2cc0a98425b05df30cb837dd1e632Adrian Bunkstatic DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
896c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org
897ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh/*
898ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh * show_dock_uid - read method for "uid" file in sysfs
899ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh */
900ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakhstatic ssize_t show_dock_uid(struct device *dev,
901ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh			     struct device_attribute *attr, char *buf)
902ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh{
903ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh	unsigned long lbuf;
904db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct dock_station *dock_station = *((struct dock_station **)
905db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dev->platform_data);
90638ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi	acpi_status status = acpi_evaluate_integer(dock_station->handle,
90738ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi					"_UID", NULL, &lbuf);
90838ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi	if (ACPI_FAILURE(status))
909ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh	    return 0;
91038ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi
911ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh	return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf);
912ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh}
913e5685b9d35c2cc0a98425b05df30cb837dd1e632Adrian Bunkstatic DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
914ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh
9158652b00fd6416773f113dab3dfa0d4509def825bShaohua Listatic ssize_t show_dock_type(struct device *dev,
9168652b00fd6416773f113dab3dfa0d4509def825bShaohua Li		struct device_attribute *attr, char *buf)
9178652b00fd6416773f113dab3dfa0d4509def825bShaohua Li{
9188652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	struct dock_station *dock_station = *((struct dock_station **)
9198652b00fd6416773f113dab3dfa0d4509def825bShaohua Li		dev->platform_data);
9208652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	char *type;
9218652b00fd6416773f113dab3dfa0d4509def825bShaohua Li
9228652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	if (dock_station->flags & DOCK_IS_DOCK)
9238652b00fd6416773f113dab3dfa0d4509def825bShaohua Li		type = "dock_station";
9248652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	else if (dock_station->flags & DOCK_IS_ATA)
9258652b00fd6416773f113dab3dfa0d4509def825bShaohua Li		type = "ata_bay";
9268652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	else if (dock_station->flags & DOCK_IS_BAT)
9278652b00fd6416773f113dab3dfa0d4509def825bShaohua Li		type = "battery_bay";
9288652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	else
9298652b00fd6416773f113dab3dfa0d4509def825bShaohua Li		type = "unknown";
9308652b00fd6416773f113dab3dfa0d4509def825bShaohua Li
9318652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	return snprintf(buf, PAGE_SIZE, "%s\n", type);
9328652b00fd6416773f113dab3dfa0d4509def825bShaohua Li}
9338652b00fd6416773f113dab3dfa0d4509def825bShaohua Listatic DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
9348652b00fd6416773f113dab3dfa0d4509def825bShaohua Li
935c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
936c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_add - add a new dock station
937c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the dock station handle
938c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
939c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * allocated and initialize a new dock station device.  Find all devices
940c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * that are on the dock station, and register for dock event notifications.
941c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
942c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int dock_add(acpi_handle handle)
943c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
944c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	int ret;
945c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct dock_dependent_device *dd;
946db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct dock_station *dock_station;
947db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct platform_device *dock_device;
948c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
949c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/* allocate & initialize the dock_station private data */
950c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
951c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (!dock_station)
952c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		return -ENOMEM;
953c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	dock_station->handle = handle;
954c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	dock_station->last_dock_time = jiffies - HZ;
955c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	INIT_LIST_HEAD(&dock_station->dependent_devices);
956c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	INIT_LIST_HEAD(&dock_station->hotplug_devices);
957db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	INIT_LIST_HEAD(&dock_station->sibiling);
958c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	spin_lock_init(&dock_station->dd_lock);
9598b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi	mutex_init(&dock_station->hp_lock);
96007a18684c92c0156f87ea158b5adc3022485f82aKristen Accardi	ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
961c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
962671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi	/* initialize platform device stuff */
963db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	dock_station->dock_device =
964db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		platform_device_register_simple(dock_device_name,
965db350b084dc2cf816288643861ce07b0562dd723Shaohua Li			dock_station_count, NULL, 0);
966db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	dock_device = dock_station->dock_device;
9670f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	if (IS_ERR(dock_device)) {
968671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi		kfree(dock_station);
96922fe4c2114e29477ca6738729c074ee8f60d3b73Chuck Ebbert		dock_station = NULL;
9700f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi		return PTR_ERR(dock_device);
971671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi	}
972db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	platform_device_add_data(dock_device, &dock_station,
973db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		sizeof(struct dock_station *));
974a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi
9759ef2a9a9f08722998540ed2ff38bccd0c54344c8Kristen Carlson Accardi	/* we want the dock device to send uevents */
9769ef2a9a9f08722998540ed2ff38bccd0c54344c8Kristen Carlson Accardi	dock_device->dev.uevent_suppress = 0;
9779ef2a9a9f08722998540ed2ff38bccd0c54344c8Kristen Carlson Accardi
978db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (is_dock(handle))
979db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dock_station->flags |= DOCK_IS_DOCK;
980db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (is_ata(handle))
981db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dock_station->flags |= DOCK_IS_ATA;
982db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (is_battery(handle))
983db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dock_station->flags |= DOCK_IS_BAT;
984db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
9850f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	ret = device_create_file(&dock_device->dev, &dev_attr_docked);
986c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	if (ret) {
987c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		printk("Error %d adding sysfs file\n", ret);
9880f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi		platform_device_unregister(dock_device);
989c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		kfree(dock_station);
99022fe4c2114e29477ca6738729c074ee8f60d3b73Chuck Ebbert		dock_station = NULL;
991c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		return ret;
992c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	}
9930f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	ret = device_create_file(&dock_device->dev, &dev_attr_undock);
994c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	if (ret) {
995c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		printk("Error %d adding sysfs file\n", ret);
9960f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi		device_remove_file(&dock_device->dev, &dev_attr_docked);
9970f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi		platform_device_unregister(dock_device);
998c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		kfree(dock_station);
99922fe4c2114e29477ca6738729c074ee8f60d3b73Chuck Ebbert		dock_station = NULL;
1000c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org		return ret;
1001c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org	}
10020f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	ret = device_create_file(&dock_device->dev, &dev_attr_uid);
1003ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh	if (ret) {
1004ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh		printk("Error %d adding sysfs file\n", ret);
10050f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi		device_remove_file(&dock_device->dev, &dev_attr_docked);
10060f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi		device_remove_file(&dock_device->dev, &dev_attr_undock);
10070f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi		platform_device_unregister(dock_device);
1008ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh		kfree(dock_station);
100922fe4c2114e29477ca6738729c074ee8f60d3b73Chuck Ebbert		dock_station = NULL;
1010ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh		return ret;
1011ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh	}
1012a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	ret = device_create_file(&dock_device->dev, &dev_attr_flags);
1013a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	if (ret) {
1014a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		printk("Error %d adding sysfs file\n", ret);
1015a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		device_remove_file(&dock_device->dev, &dev_attr_docked);
1016a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		device_remove_file(&dock_device->dev, &dev_attr_undock);
1017a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		device_remove_file(&dock_device->dev, &dev_attr_uid);
1018a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		platform_device_unregister(dock_device);
1019a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		kfree(dock_station);
1020a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		dock_station = NULL;
1021a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi		return ret;
1022a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	}
10238652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	ret = device_create_file(&dock_device->dev, &dev_attr_type);
10248652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	if (ret)
10258652b00fd6416773f113dab3dfa0d4509def825bShaohua Li		printk(KERN_ERR"Error %d adding sysfs file\n", ret);
1026671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi
1027c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/* Find dependent devices */
1028c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
1029c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			    ACPI_UINT32_MAX, find_dock_devices, dock_station,
1030c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			    NULL);
1031c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1032c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/* add the dock station as a device dependent on itself */
1033c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	dd = alloc_dock_dependent_device(handle);
1034c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (!dd) {
1035c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		kfree(dock_station);
103622fe4c2114e29477ca6738729c074ee8f60d3b73Chuck Ebbert		dock_station = NULL;
1037671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi		ret = -ENOMEM;
1038671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi		goto dock_add_err_unregister;
1039c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
1040c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	add_dock_dependent_device(dock_station, dd);
1041c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1042db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	dock_station_count++;
1043db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	list_add(&dock_station->sibiling, &dock_stations);
1044c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return 0;
1045c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1046671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardidock_add_err_unregister:
10478652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	device_remove_file(&dock_device->dev, &dev_attr_type);
10480f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	device_remove_file(&dock_device->dev, &dev_attr_docked);
10490f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	device_remove_file(&dock_device->dev, &dev_attr_undock);
10500f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	device_remove_file(&dock_device->dev, &dev_attr_uid);
1051a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	device_remove_file(&dock_device->dev, &dev_attr_flags);
10520f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	platform_device_unregister(dock_device);
1053671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi	kfree(dock_station);
105422fe4c2114e29477ca6738729c074ee8f60d3b73Chuck Ebbert	dock_station = NULL;
1055c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return ret;
1056c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
1057c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1058c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
1059c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_remove - free up resources related to the dock station
1060c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
1061db350b084dc2cf816288643861ce07b0562dd723Shaohua Listatic int dock_remove(struct dock_station *dock_station)
1062c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
1063c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	struct dock_dependent_device *dd, *tmp;
1064db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct platform_device *dock_device = dock_station->dock_device;
1065c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1066db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!dock_station_count)
1067c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		return 0;
1068c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1069c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/* remove dependent devices */
1070c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
1071c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown				 list)
1072c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	    kfree(dd);
1073c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1074671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi	/* cleanup sysfs */
10758652b00fd6416773f113dab3dfa0d4509def825bShaohua Li	device_remove_file(&dock_device->dev, &dev_attr_type);
10760f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	device_remove_file(&dock_device->dev, &dev_attr_docked);
10770f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	device_remove_file(&dock_device->dev, &dev_attr_undock);
10780f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	device_remove_file(&dock_device->dev, &dev_attr_uid);
1079a0cd35fdca0bb711854edeaf016cec6cdf82eecaKristen Carlson Accardi	device_remove_file(&dock_device->dev, &dev_attr_flags);
10800f6f2804563eee64f0fc7cbcb009b98b6f332af6Kristen Carlson Accardi	platform_device_unregister(dock_device);
1081671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi
1082c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/* free dock station memory */
1083c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	kfree(dock_station);
108422fe4c2114e29477ca6738729c074ee8f60d3b73Chuck Ebbert	dock_station = NULL;
1085c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return 0;
1086c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
1087c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1088c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/**
1089c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * find_dock - look for a dock station
1090c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: acpi handle of a device
1091c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @lvl: unused
1092c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @context: counter of dock stations found
1093c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @rv: unused
1094c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *
1095c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * This is called by acpi_walk_namespace to look for dock stations.
1096c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */
1097c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic acpi_status
1098c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownfind_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
1099c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
1100c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_status status = AE_OK;
1101c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1102c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	if (is_dock(handle)) {
1103c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		if (dock_add(handle) >= 0) {
1104c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown			status = AE_CTRL_TERMINATE;
1105c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown		}
1106c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	}
1107c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return status;
1108c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
1109c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1110db350b084dc2cf816288643861ce07b0562dd723Shaohua Listatic acpi_status
1111db350b084dc2cf816288643861ce07b0562dd723Shaohua Lifind_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
1112c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
111361b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li	/* If bay is a dock, it's already handled */
111461b836958371c717d1e6d4fea1d2c512969ad20bShaohua Li	if (is_ejectable_bay(handle) && !is_dock(handle))
1115db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dock_add(handle);
1116db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	return AE_OK;
1117db350b084dc2cf816288643861ce07b0562dd723Shaohua Li}
1118c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1119db350b084dc2cf816288643861ce07b0562dd723Shaohua Listatic int __init dock_init(void)
1120db350b084dc2cf816288643861ce07b0562dd723Shaohua Li{
1121816c2eda3ce8fa7eb62f22e01e2ec7a3f7d677c0Len Brown	if (acpi_disabled)
1122816c2eda3ce8fa7eb62f22e01e2ec7a3f7d677c0Len Brown		return 0;
1123816c2eda3ce8fa7eb62f22e01e2ec7a3f7d677c0Len Brown
1124c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	/* look for a dock station */
1125c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
1126db350b084dc2cf816288643861ce07b0562dd723Shaohua Li			    ACPI_UINT32_MAX, find_dock, NULL, NULL);
1127c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1128db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	/* look for bay */
1129db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
1130db350b084dc2cf816288643861ce07b0562dd723Shaohua Li			ACPI_UINT32_MAX, find_bay, NULL, NULL);
1131db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	if (!dock_station_count) {
1132db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		printk(KERN_INFO PREFIX "No dock devices found.\n");
1133db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		return 0;
1134db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	}
1135c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
11366bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	register_acpi_bus_notifier(&dock_acpi_notifier);
1137db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	printk(KERN_INFO PREFIX "%s: %d docks/bays found\n",
1138db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
1139c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown	return 0;
1140c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
1141c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1142c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void __exit dock_exit(void)
1143c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{
1144db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	struct dock_station *dock_station;
1145db350b084dc2cf816288643861ce07b0562dd723Shaohua Li
11466bd00a61ab63d4ceb635ae0316353c11c900b8d8Shaohua Li	unregister_acpi_bus_notifier(&dock_acpi_notifier);
1147db350b084dc2cf816288643861ce07b0562dd723Shaohua Li	list_for_each_entry(dock_station, &dock_stations, sibiling)
1148db350b084dc2cf816288643861ce07b0562dd723Shaohua Li		dock_remove(dock_station);
1149c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}
1150c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown
1151db350b084dc2cf816288643861ce07b0562dd723Shaohua Li/*
1152db350b084dc2cf816288643861ce07b0562dd723Shaohua Li * Must be called before drivers of devices in dock, otherwise we can't know
1153db350b084dc2cf816288643861ce07b0562dd723Shaohua Li * which devices are in a dock
1154db350b084dc2cf816288643861ce07b0562dd723Shaohua Li */
1155db350b084dc2cf816288643861ce07b0562dd723Shaohua Lisubsys_initcall(dock_init);
1156c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownmodule_exit(dock_exit);
1157