Blame view

drivers/acpi/dock.c 27.8 KB
c8f7a62cd   Len Brown   Revert "Revert "A...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  /*
   *  dock.c - ACPI dock station driver
   *
   *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
   *
   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2 of the License, or (at
   *  your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful, but
   *  WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   *  General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License along
   *  with this program; if not, write to the Free Software Foundation, Inc.,
   *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
   *
   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
27
  #include <linux/slab.h>
c8f7a62cd   Len Brown   Revert "Revert "A...
28
29
30
  #include <linux/init.h>
  #include <linux/types.h>
  #include <linux/notifier.h>
671adbec2   Kristen Carlson Accardi   ACPI: dock: Make ...
31
  #include <linux/platform_device.h>
914e26379   Al Viro   [PATCH] severing ...
32
  #include <linux/jiffies.h>
62a6d7fd9   Randy Dunlap   ACPI: dock: use N...
33
  #include <linux/stddef.h>
c8f7a62cd   Len Brown   Revert "Revert "A...
34
35
  #include <acpi/acpi_bus.h>
  #include <acpi/acpi_drivers.h>
a192a9580   Len Brown   ACPI: Move defini...
36
  #define PREFIX "ACPI: "
7cda93e00   Len Brown   ACPI: delete extr...
37
  #define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver"
c8f7a62cd   Len Brown   Revert "Revert "A...
38

f52fd66d2   Len Brown   ACPI: clean up AC...
39
  ACPI_MODULE_NAME("dock");
c8f7a62cd   Len Brown   Revert "Revert "A...
40
  MODULE_AUTHOR("Kristen Carlson Accardi");
7cda93e00   Len Brown   ACPI: delete extr...
41
  MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION);
c8f7a62cd   Len Brown   Revert "Revert "A...
42
  MODULE_LICENSE("GPL");
90ab5ee94   Rusty Russell   module_param: mak...
43
  static bool immediate_undock = 1;
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
44
45
46
47
48
  module_param(immediate_undock, bool, 0644);
  MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
  	"undock immediately when the undock button is pressed, 0 will cause"
  	" the driver to wait for userspace to write the undock sysfs file "
  	" before undocking");
c8f7a62cd   Len Brown   Revert "Revert "A...
49
  static struct atomic_notifier_head dock_notifier_list;
a340af14b   Frank Seidel   ACPI: Add autoloa...
50
51
52
53
54
  static const struct acpi_device_id dock_device_ids[] = {
  	{"LNXDOCK", 0},
  	{"", 0},
  };
  MODULE_DEVICE_TABLE(acpi, dock_device_ids);
c8f7a62cd   Len Brown   Revert "Revert "A...
55
56
57
58
59
  struct dock_station {
  	acpi_handle handle;
  	unsigned long last_dock_time;
  	u32 flags;
  	spinlock_t dd_lock;
8b0dc866d   Kristen Carlson Accardi   ACPI: dock: use m...
60
  	struct mutex hp_lock;
c8f7a62cd   Len Brown   Revert "Revert "A...
61
62
  	struct list_head dependent_devices;
  	struct list_head hotplug_devices;
db350b084   Shaohua Li   dock: add bay and...
63

50d716e47   Alex Chiang   ACPI: dock: fix "...
64
  	struct list_head sibling;
db350b084   Shaohua Li   dock: add bay and...
65
  	struct platform_device *dock_device;
c8f7a62cd   Len Brown   Revert "Revert "A...
66
  };
db350b084   Shaohua Li   dock: add bay and...
67
68
  static LIST_HEAD(dock_stations);
  static int dock_station_count;
c8f7a62cd   Len Brown   Revert "Revert "A...
69
70
71
72
73
  
  struct dock_dependent_device {
  	struct list_head list;
  	struct list_head hotplug_list;
  	acpi_handle handle;
9c8b04be4   Vasiliy Kulikov   ACPI: constify op...
74
  	const struct acpi_dock_ops *ops;
c8f7a62cd   Len Brown   Revert "Revert "A...
75
76
77
78
  	void *context;
  };
  
  #define DOCK_DOCKING	0x00000001
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
79
  #define DOCK_UNDOCKING  0x00000002
db350b084   Shaohua Li   dock: add bay and...
80
81
82
  #define DOCK_IS_DOCK	0x00000010
  #define DOCK_IS_ATA	0x00000020
  #define DOCK_IS_BAT	0x00000040
5669021e4   Kristen Carlson Accardi   PCI: docking stat...
83
84
  #define DOCK_EVENT	3
  #define UNDOCK_EVENT	2
c8f7a62cd   Len Brown   Revert "Revert "A...
85

c8f7a62cd   Len Brown   Revert "Revert "A...
86
87
88
89
  /*****************************************************************************
   *                         Dock Dependent device functions                   *
   *****************************************************************************/
  /**
f69cfdd24   Alex Chiang   ACPI: dock: combi...
90
91
92
   * add_dock_dependent_device - associate a device with the dock station
   * @ds: The dock station
   * @handle: handle of the dependent device
c8f7a62cd   Len Brown   Revert "Revert "A...
93
   *
f69cfdd24   Alex Chiang   ACPI: dock: combi...
94
   * Add the dependent device to the dock's dependent device list.
c8f7a62cd   Len Brown   Revert "Revert "A...
95
   */
f69cfdd24   Alex Chiang   ACPI: dock: combi...
96
97
  static int
  add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
c8f7a62cd   Len Brown   Revert "Revert "A...
98
99
100
101
  {
  	struct dock_dependent_device *dd;
  
  	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
f69cfdd24   Alex Chiang   ACPI: dock: combi...
102
103
104
105
106
107
  	if (!dd)
  		return -ENOMEM;
  
  	dd->handle = handle;
  	INIT_LIST_HEAD(&dd->list);
  	INIT_LIST_HEAD(&dd->hotplug_list);
c8f7a62cd   Len Brown   Revert "Revert "A...
108

c8f7a62cd   Len Brown   Revert "Revert "A...
109
110
111
  	spin_lock(&ds->dd_lock);
  	list_add_tail(&dd->list, &ds->dependent_devices);
  	spin_unlock(&ds->dd_lock);
f69cfdd24   Alex Chiang   ACPI: dock: combi...
112
113
  
  	return 0;
c8f7a62cd   Len Brown   Revert "Revert "A...
114
115
116
117
118
119
120
121
122
123
124
125
126
  }
  
  /**
   * dock_add_hotplug_device - associate a hotplug handler with the dock station
   * @ds: The dock station
   * @dd: The dependent device struct
   *
   * Add the dependent device to the dock's hotplug device list
   */
  static void
  dock_add_hotplug_device(struct dock_station *ds,
  			struct dock_dependent_device *dd)
  {
8b0dc866d   Kristen Carlson Accardi   ACPI: dock: use m...
127
  	mutex_lock(&ds->hp_lock);
c8f7a62cd   Len Brown   Revert "Revert "A...
128
  	list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
8b0dc866d   Kristen Carlson Accardi   ACPI: dock: use m...
129
  	mutex_unlock(&ds->hp_lock);
c8f7a62cd   Len Brown   Revert "Revert "A...
130
131
132
133
134
135
136
137
138
139
140
141
142
  }
  
  /**
   * dock_del_hotplug_device - remove a hotplug handler from the dock station
   * @ds: The dock station
   * @dd: the dependent device struct
   *
   * Delete the dependent device from the dock's hotplug device list
   */
  static void
  dock_del_hotplug_device(struct dock_station *ds,
  			struct dock_dependent_device *dd)
  {
8b0dc866d   Kristen Carlson Accardi   ACPI: dock: use m...
143
  	mutex_lock(&ds->hp_lock);
c8f7a62cd   Len Brown   Revert "Revert "A...
144
  	list_del(&dd->hotplug_list);
8b0dc866d   Kristen Carlson Accardi   ACPI: dock: use m...
145
  	mutex_unlock(&ds->hp_lock);
c8f7a62cd   Len Brown   Revert "Revert "A...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  }
  
  /**
   * find_dock_dependent_device - get a device dependent on this dock
   * @ds: the dock station
   * @handle: the acpi_handle of the device we want
   *
   * iterate over the dependent device list for this dock.  If the
   * dependent device matches the handle, return.
   */
  static struct dock_dependent_device *
  find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
  {
  	struct dock_dependent_device *dd;
  
  	spin_lock(&ds->dd_lock);
  	list_for_each_entry(dd, &ds->dependent_devices, list) {
  		if (handle == dd->handle) {
  			spin_unlock(&ds->dd_lock);
  			return dd;
  		}
  	}
  	spin_unlock(&ds->dd_lock);
  	return NULL;
  }
  
  /*****************************************************************************
   *                         Dock functions                                    *
   *****************************************************************************/
  /**
   * is_dock - see if a device is a dock station
   * @handle: acpi handle of the device
   *
   * If an acpi object has a _DCK method, then it is by definition a dock
   * station, so return true.
   */
  static int is_dock(acpi_handle handle)
  {
  	acpi_status status;
  	acpi_handle tmp;
  
  	status = acpi_get_handle(handle, "_DCK", &tmp);
  	if (ACPI_FAILURE(status))
  		return 0;
  	return 1;
  }
db350b084   Shaohua Li   dock: add bay and...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  static int is_ejectable(acpi_handle handle)
  {
  	acpi_status status;
  	acpi_handle tmp;
  
  	status = acpi_get_handle(handle, "_EJ0", &tmp);
  	if (ACPI_FAILURE(status))
  		return 0;
  	return 1;
  }
  
  static int is_ata(acpi_handle handle)
  {
  	acpi_handle tmp;
  
  	if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) ||
  	   (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) ||
  	   (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) ||
  	   (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp))))
  		return 1;
  
  	return 0;
  }
  
  static int is_battery(acpi_handle handle)
  {
  	struct acpi_device_info *info;
db350b084   Shaohua Li   dock: add bay and...
219
  	int ret = 1;
15b8dd53f   Bob Moore   ACPICA: Major upd...
220
  	if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info)))
db350b084   Shaohua Li   dock: add bay and...
221
  		return 0;
db350b084   Shaohua Li   dock: add bay and...
222
223
224
  	if (!(info->valid & ACPI_VALID_HID))
  		ret = 0;
  	else
15b8dd53f   Bob Moore   ACPICA: Major upd...
225
  		ret = !strcmp("PNP0C0A", info->hardware_id.string);
db350b084   Shaohua Li   dock: add bay and...
226

15b8dd53f   Bob Moore   ACPICA: Major upd...
227
  	kfree(info);
db350b084   Shaohua Li   dock: add bay and...
228
229
230
231
232
233
  	return ret;
  }
  
  static int is_ejectable_bay(acpi_handle handle)
  {
  	acpi_handle phandle;
747479a3f   Alex Chiang   ACPI: dock: minor...
234

db350b084   Shaohua Li   dock: add bay and...
235
236
237
238
239
240
241
242
  	if (!is_ejectable(handle))
  		return 0;
  	if (is_battery(handle) || is_ata(handle))
  		return 1;
  	if (!acpi_get_parent(handle, &phandle) && is_ata(phandle))
  		return 1;
  	return 0;
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
243
244
245
246
247
248
249
250
251
252
  /**
   * is_dock_device - see if a device is on a dock station
   * @handle: acpi handle of the device
   *
   * If this device is either the dock station itself,
   * or is a device dependent on the dock station, then it
   * is a dock device
   */
  int is_dock_device(acpi_handle handle)
  {
db350b084   Shaohua Li   dock: add bay and...
253
254
255
  	struct dock_station *dock_station;
  
  	if (!dock_station_count)
c8f7a62cd   Len Brown   Revert "Revert "A...
256
  		return 0;
db350b084   Shaohua Li   dock: add bay and...
257
  	if (is_dock(handle))
c8f7a62cd   Len Brown   Revert "Revert "A...
258
  		return 1;
747479a3f   Alex Chiang   ACPI: dock: minor...
259
260
  
  	list_for_each_entry(dock_station, &dock_stations, sibling)
db350b084   Shaohua Li   dock: add bay and...
261
262
  		if (find_dock_dependent_device(dock_station, handle))
  			return 1;
c8f7a62cd   Len Brown   Revert "Revert "A...
263
264
265
  
  	return 0;
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
266
267
268
269
270
271
272
273
274
275
276
  EXPORT_SYMBOL_GPL(is_dock_device);
  
  /**
   * dock_present - see if the dock station is present.
   * @ds: the dock station
   *
   * execute the _STA method.  note that present does not
   * imply that we are docked.
   */
  static int dock_present(struct dock_station *ds)
  {
27663c585   Matthew Wilcox   ACPI: Change acpi...
277
  	unsigned long long sta;
c8f7a62cd   Len Brown   Revert "Revert "A...
278
279
280
281
282
283
284
285
286
  	acpi_status status;
  
  	if (ds) {
  		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
  		if (ACPI_SUCCESS(status) && sta)
  			return 1;
  	}
  	return 0;
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
287
288
289
290
291
292
293
294
295
296
297
298
299
  /**
   * dock_create_acpi_device - add new devices to acpi
   * @handle - handle of the device to add
   *
   *  This function will create a new acpi_device for the given
   *  handle if one does not exist already.  This should cause
   *  acpi to scan for drivers for the given devices, and call
   *  matching driver's add routine.
   *
   *  Returns a pointer to the acpi_device corresponding to the handle.
   */
  static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
  {
747479a3f   Alex Chiang   ACPI: dock: minor...
300
  	struct acpi_device *device;
c8f7a62cd   Len Brown   Revert "Revert "A...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  	struct acpi_device *parent_device;
  	acpi_handle parent;
  	int ret;
  
  	if (acpi_bus_get_device(handle, &device)) {
  		/*
  		 * no device created for this object,
  		 * so we should create one.
  		 */
  		acpi_get_parent(handle, &parent);
  		if (acpi_bus_get_device(parent, &parent_device))
  			parent_device = NULL;
  
  		ret = acpi_bus_add(&device, parent_device, handle,
  			ACPI_BUS_TYPE_DEVICE);
  		if (ret) {
747479a3f   Alex Chiang   ACPI: dock: minor...
317
318
  			pr_debug("error adding bus, %x
  ", -ret);
c8f7a62cd   Len Brown   Revert "Revert "A...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  			return NULL;
  		}
  	}
  	return device;
  }
  
  /**
   * dock_remove_acpi_device - remove the acpi_device struct from acpi
   * @handle - the handle of the device to remove
   *
   *  Tell acpi to remove the acpi_device.  This should cause any loaded
   *  driver to have it's remove routine called.
   */
  static void dock_remove_acpi_device(acpi_handle handle)
  {
  	struct acpi_device *device;
  	int ret;
  
  	if (!acpi_bus_get_device(handle, &device)) {
  		ret = acpi_bus_trim(device, 1);
  		if (ret)
  			pr_debug("error removing bus, %x
  ", -ret);
  	}
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
344
345
346
347
348
349
350
351
352
353
354
355
356
  /**
   * hotplug_dock_devices - insert or remove devices on the dock station
   * @ds: the dock station
   * @event: either bus check or eject request
   *
   * Some devices on the dock station need to have drivers called
   * to perform hotplug operations after a dock event has occurred.
   * Traverse the list of dock devices that have registered a
   * hotplug handler, and call the handler.
   */
  static void hotplug_dock_devices(struct dock_station *ds, u32 event)
  {
  	struct dock_dependent_device *dd;
8b0dc866d   Kristen Carlson Accardi   ACPI: dock: use m...
357
  	mutex_lock(&ds->hp_lock);
c8f7a62cd   Len Brown   Revert "Revert "A...
358
359
360
361
  
  	/*
  	 * First call driver specific hotplug functions
  	 */
747479a3f   Alex Chiang   ACPI: dock: minor...
362
  	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
1253f7aab   Shaohua Li   dock: introduce ....
363
364
  		if (dd->ops && dd->ops->handler)
  			dd->ops->handler(dd->handle, event, dd->context);
c8f7a62cd   Len Brown   Revert "Revert "A...
365
366
367
368
369
370
371
372
373
374
375
376
377
  
  	/*
  	 * Now make sure that an acpi_device is created for each
  	 * dependent device, or removed if this is an eject request.
  	 * This will cause acpi_drivers to be stopped/started if they
  	 * exist
  	 */
  	list_for_each_entry(dd, &ds->dependent_devices, list) {
  		if (event == ACPI_NOTIFY_EJECT_REQUEST)
  			dock_remove_acpi_device(dd->handle);
  		else
  			dock_create_acpi_device(dd->handle);
  	}
8b0dc866d   Kristen Carlson Accardi   ACPI: dock: use m...
378
  	mutex_unlock(&ds->hp_lock);
c8f7a62cd   Len Brown   Revert "Revert "A...
379
380
381
382
  }
  
  static void dock_event(struct dock_station *ds, u32 event, int num)
  {
db350b084   Shaohua Li   dock: add bay and...
383
  	struct device *dev = &ds->dock_device->dev;
66b568218   Holger Macht   ACPI: dock: Send ...
384
  	char event_string[13];
79a8f70b4   Kristen Carlson Accardi   ACPI: dock: send ...
385
  	char *envp[] = { event_string, NULL };
1253f7aab   Shaohua Li   dock: introduce ....
386
  	struct dock_dependent_device *dd;
79a8f70b4   Kristen Carlson Accardi   ACPI: dock: send ...
387
388
  
  	if (num == UNDOCK_EVENT)
66b568218   Holger Macht   ACPI: dock: Send ...
389
  		sprintf(event_string, "EVENT=undock");
79a8f70b4   Kristen Carlson Accardi   ACPI: dock: send ...
390
  	else
66b568218   Holger Macht   ACPI: dock: Send ...
391
  		sprintf(event_string, "EVENT=dock");
79a8f70b4   Kristen Carlson Accardi   ACPI: dock: send ...
392

5669021e4   Kristen Carlson Accardi   PCI: docking stat...
393
  	/*
8ea86e0ba   Kristen Carlson Accardi   ACPI: dock: add u...
394
395
  	 * Indicate that the status of the dock station has
  	 * changed.
5669021e4   Kristen Carlson Accardi   PCI: docking stat...
396
  	 */
1253f7aab   Shaohua Li   dock: introduce ....
397
398
399
400
401
402
  	if (num == DOCK_EVENT)
  		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
  
  	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
  		if (dd->ops && dd->ops->uevent)
  			dd->ops->uevent(dd->handle, event, dd->context);
747479a3f   Alex Chiang   ACPI: dock: minor...
403

1253f7aab   Shaohua Li   dock: introduce ....
404
405
  	if (num != DOCK_EVENT)
  		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
c8f7a62cd   Len Brown   Revert "Revert "A...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
  }
  
  /**
   * eject_dock - respond to a dock eject request
   * @ds: the dock station
   *
   * This is called after _DCK is called, to execute the dock station's
   * _EJ0 method.
   */
  static void eject_dock(struct dock_station *ds)
  {
  	struct acpi_object_list arg_list;
  	union acpi_object arg;
  	acpi_status status;
  	acpi_handle tmp;
  
  	/* all dock devices should have _EJ0, but check anyway */
  	status = acpi_get_handle(ds->handle, "_EJ0", &tmp);
  	if (ACPI_FAILURE(status)) {
  		pr_debug("No _EJ0 support for dock device
  ");
  		return;
  	}
  
  	arg_list.count = 1;
  	arg_list.pointer = &arg;
  	arg.type = ACPI_TYPE_INTEGER;
  	arg.integer.value = 1;
747479a3f   Alex Chiang   ACPI: dock: minor...
434
435
  	status = acpi_evaluate_object(ds->handle, "_EJ0", &arg_list, NULL);
  	if (ACPI_FAILURE(status))
c8f7a62cd   Len Brown   Revert "Revert "A...
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
  		pr_debug("Failed to evaluate _EJ0!
  ");
  }
  
  /**
   * handle_dock - handle a dock event
   * @ds: the dock station
   * @dock: to dock, or undock - that is the question
   *
   * Execute the _DCK method in response to an acpi event
   */
  static void handle_dock(struct dock_station *ds, int dock)
  {
  	acpi_status status;
  	struct acpi_object_list arg_list;
  	union acpi_object arg;
  	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  	struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
c8f7a62cd   Len Brown   Revert "Revert "A...
454
455
  
  	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer);
c8f7a62cd   Len Brown   Revert "Revert "A...
456

9254bc845   Dmitry Torokhov   ACPI: dock: fix o...
457
458
459
  	printk(KERN_INFO PREFIX "%s - %s
  ",
  		(char *)name_buffer.pointer, dock ? "docking" : "undocking");
c8f7a62cd   Len Brown   Revert "Revert "A...
460
461
462
463
464
465
466
  
  	/* _DCK method has one argument */
  	arg_list.count = 1;
  	arg_list.pointer = &arg;
  	arg.type = ACPI_TYPE_INTEGER;
  	arg.integer.value = dock;
  	status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
db350b084   Shaohua Li   dock: add bay and...
467
  	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
0a918a943   Thomas Renninger   Subject: ACPI doc...
468
469
470
  		ACPI_EXCEPTION((AE_INFO, status, "%s - failed to execute"
  			" _DCK
  ", (char *)name_buffer.pointer));
c8f7a62cd   Len Brown   Revert "Revert "A...
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
  	kfree(buffer.pointer);
  	kfree(name_buffer.pointer);
  }
  
  static inline void dock(struct dock_station *ds)
  {
  	handle_dock(ds, 1);
  }
  
  static inline void undock(struct dock_station *ds)
  {
  	handle_dock(ds, 0);
  }
  
  static inline void begin_dock(struct dock_station *ds)
  {
  	ds->flags |= DOCK_DOCKING;
  }
  
  static inline void complete_dock(struct dock_station *ds)
  {
  	ds->flags &= ~(DOCK_DOCKING);
  	ds->last_dock_time = jiffies;
  }
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
495
496
497
498
499
500
501
502
503
  static inline void begin_undock(struct dock_station *ds)
  {
  	ds->flags |= DOCK_UNDOCKING;
  }
  
  static inline void complete_undock(struct dock_station *ds)
  {
  	ds->flags &= ~(DOCK_UNDOCKING);
  }
406f692d0   Shaohua Li   dock: add _LCK su...
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  static void dock_lock(struct dock_station *ds, int lock)
  {
  	struct acpi_object_list arg_list;
  	union acpi_object arg;
  	acpi_status status;
  
  	arg_list.count = 1;
  	arg_list.pointer = &arg;
  	arg.type = ACPI_TYPE_INTEGER;
  	arg.integer.value = !!lock;
  	status = acpi_evaluate_object(ds->handle, "_LCK", &arg_list, NULL);
  	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
  		if (lock)
  			printk(KERN_WARNING PREFIX "Locking device failed
  ");
  		else
  			printk(KERN_WARNING PREFIX "Unlocking device failed
  ");
  	}
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
  /**
   * dock_in_progress - see if we are in the middle of handling a dock event
   * @ds: the dock station
   *
   * Sometimes while docking, false dock events can be sent to the driver
   * because good connections aren't made or some other reason.  Ignore these
   * if we are in the middle of doing something.
   */
  static int dock_in_progress(struct dock_station *ds)
  {
  	if ((ds->flags & DOCK_DOCKING) ||
  	    time_before(jiffies, (ds->last_dock_time + HZ)))
  		return 1;
  	return 0;
  }
  
  /**
   * register_dock_notifier - add yourself to the dock notifier list
   * @nb: the callers notifier block
   *
   * If a driver wishes to be notified about dock events, they can
   * use this function to put a notifier block on the dock notifier list.
   * this notifier call chain will be called after a dock event, but
   * before hotplugging any new devices.
   */
  int register_dock_notifier(struct notifier_block *nb)
  {
db350b084   Shaohua Li   dock: add bay and...
551
  	if (!dock_station_count)
2548c06b7   Prarit Bhargava   ACPI: dock: Fix s...
552
  		return -ENODEV;
c8f7a62cd   Len Brown   Revert "Revert "A...
553
554
  	return atomic_notifier_chain_register(&dock_notifier_list, nb);
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
555
556
557
558
559
560
561
562
  EXPORT_SYMBOL_GPL(register_dock_notifier);
  
  /**
   * unregister_dock_notifier - remove yourself from the dock notifier list
   * @nb: the callers notifier block
   */
  void unregister_dock_notifier(struct notifier_block *nb)
  {
db350b084   Shaohua Li   dock: add bay and...
563
  	if (!dock_station_count)
2548c06b7   Prarit Bhargava   ACPI: dock: Fix s...
564
  		return;
c8f7a62cd   Len Brown   Revert "Revert "A...
565
566
  	atomic_notifier_chain_unregister(&dock_notifier_list, nb);
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
567
568
569
570
571
  EXPORT_SYMBOL_GPL(unregister_dock_notifier);
  
  /**
   * register_hotplug_dock_device - register a hotplug function
   * @handle: the handle of the device
1253f7aab   Shaohua Li   dock: introduce ....
572
   * @ops: handlers to call after docking
c8f7a62cd   Len Brown   Revert "Revert "A...
573
574
575
576
577
578
579
   * @context: device specific data
   *
   * If a driver would like to perform a hotplug operation after a dock
   * event, they can register an acpi_notifiy_handler to be called by
   * the dock driver after _DCK is executed.
   */
  int
9c8b04be4   Vasiliy Kulikov   ACPI: constify op...
580
  register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops,
c8f7a62cd   Len Brown   Revert "Revert "A...
581
582
583
  			     void *context)
  {
  	struct dock_dependent_device *dd;
db350b084   Shaohua Li   dock: add bay and...
584
  	struct dock_station *dock_station;
61b836958   Shaohua Li   dock: fix for ATA...
585
  	int ret = -EINVAL;
c8f7a62cd   Len Brown   Revert "Revert "A...
586

db350b084   Shaohua Li   dock: add bay and...
587
  	if (!dock_station_count)
c8f7a62cd   Len Brown   Revert "Revert "A...
588
589
590
591
592
593
  		return -ENODEV;
  
  	/*
  	 * make sure this handle is for a device dependent on the dock,
  	 * this would include the dock station itself
  	 */
50d716e47   Alex Chiang   ACPI: dock: fix "...
594
  	list_for_each_entry(dock_station, &dock_stations, sibling) {
61b836958   Shaohua Li   dock: fix for ATA...
595
596
  		/*
  		 * An ATA bay can be in a dock and itself can be ejected
3ad2f3fbb   Daniel Mack   tree-wide: Assort...
597
  		 * separately, so there are two 'dock stations' which need the
61b836958   Shaohua Li   dock: fix for ATA...
598
599
  		 * ops
  		 */
db350b084   Shaohua Li   dock: add bay and...
600
601
  		dd = find_dock_dependent_device(dock_station, handle);
  		if (dd) {
1253f7aab   Shaohua Li   dock: introduce ....
602
  			dd->ops = ops;
db350b084   Shaohua Li   dock: add bay and...
603
604
  			dd->context = context;
  			dock_add_hotplug_device(dock_station, dd);
61b836958   Shaohua Li   dock: fix for ATA...
605
  			ret = 0;
db350b084   Shaohua Li   dock: add bay and...
606
  		}
c8f7a62cd   Len Brown   Revert "Revert "A...
607
  	}
61b836958   Shaohua Li   dock: fix for ATA...
608
  	return ret;
c8f7a62cd   Len Brown   Revert "Revert "A...
609
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
610
611
612
613
614
615
616
617
618
  EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
  
  /**
   * unregister_hotplug_dock_device - remove yourself from the hotplug list
   * @handle: the acpi handle of the device
   */
  void unregister_hotplug_dock_device(acpi_handle handle)
  {
  	struct dock_dependent_device *dd;
db350b084   Shaohua Li   dock: add bay and...
619
  	struct dock_station *dock_station;
c8f7a62cd   Len Brown   Revert "Revert "A...
620

db350b084   Shaohua Li   dock: add bay and...
621
  	if (!dock_station_count)
c8f7a62cd   Len Brown   Revert "Revert "A...
622
  		return;
50d716e47   Alex Chiang   ACPI: dock: fix "...
623
  	list_for_each_entry(dock_station, &dock_stations, sibling) {
db350b084   Shaohua Li   dock: add bay and...
624
625
626
627
  		dd = find_dock_dependent_device(dock_station, handle);
  		if (dd)
  			dock_del_hotplug_device(dock_station, dd);
  	}
c8f7a62cd   Len Brown   Revert "Revert "A...
628
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
629
630
631
  EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
  
  /**
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
632
633
634
635
636
637
638
   * handle_eject_request - handle an undock request checking for error conditions
   *
   * Check to make sure the dock device is still present, then undock and
   * hotremove all the devices that may need removing.
   */
  static int handle_eject_request(struct dock_station *ds, u32 event)
  {
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
639
640
641
642
643
644
645
  	if (dock_in_progress(ds))
  		return -EBUSY;
  
  	/*
  	 * here we need to generate the undock
  	 * event prior to actually doing the undock
  	 * so that the device struct still exists.
afd7301dd   Holger Macht   ACPI: Properly cl...
646
647
  	 * Also, even send the dock event if the
  	 * device is not present anymore
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
648
649
  	 */
  	dock_event(ds, event, UNDOCK_EVENT);
afd7301dd   Holger Macht   ACPI: Properly cl...
650

c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
651
652
  	hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
  	undock(ds);
406f692d0   Shaohua Li   dock: add _LCK su...
653
  	dock_lock(ds, 0);
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
654
655
656
657
658
659
  	eject_dock(ds);
  	if (dock_present(ds)) {
  		printk(KERN_ERR PREFIX "Unable to undock!
  ");
  		return -EBUSY;
  	}
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
660
  	complete_undock(ds);
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
661
662
663
664
  	return 0;
  }
  
  /**
c8f7a62cd   Len Brown   Revert "Revert "A...
665
666
667
668
669
670
671
   * dock_notify - act upon an acpi dock notification
   * @handle: the dock station handle
   * @event: the acpi event
   * @data: our driver data struct
   *
   * If we are notified to dock, then check to see if the dock is
   * present and then dock.  Notify all drivers of the dock event,
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
672
   * and then hotplug and devices that may need hotplugging.
c8f7a62cd   Len Brown   Revert "Revert "A...
673
674
675
   */
  static void dock_notify(acpi_handle handle, u32 event, void *data)
  {
50dd09697   Jan Engelhardt   ACPI: Remove unne...
676
  	struct dock_station *ds = data;
8b59560a3   Shaohua Li   ACPI: dock: avoid...
677
  	struct acpi_device *tmp;
db350b084   Shaohua Li   dock: add bay and...
678
  	int surprise_removal = 0;
c8f7a62cd   Len Brown   Revert "Revert "A...
679

db350b084   Shaohua Li   dock: add bay and...
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  	/*
  	 * According to acpi spec 3.0a, if a DEVICE_CHECK notification
  	 * is sent and _DCK is present, it is assumed to mean an undock
  	 * request.
  	 */
  	if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)
  		event = ACPI_NOTIFY_EJECT_REQUEST;
  
  	/*
  	 * dock station: BUS_CHECK - docked or surprise removal
  	 *		 DEVICE_CHECK - undocked
  	 * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal
  	 *
  	 * To simplify event handling, dock dependent device handler always
  	 * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and
  	 * ACPI_NOTIFY_EJECT_REQUEST for removal
  	 */
c8f7a62cd   Len Brown   Revert "Revert "A...
697
698
  	switch (event) {
  	case ACPI_NOTIFY_BUS_CHECK:
db350b084   Shaohua Li   dock: add bay and...
699
  	case ACPI_NOTIFY_DEVICE_CHECK:
8b59560a3   Shaohua Li   ACPI: dock: avoid...
700
701
  		if (!dock_in_progress(ds) && acpi_bus_get_device(ds->handle,
  		   &tmp)) {
c8f7a62cd   Len Brown   Revert "Revert "A...
702
703
704
705
706
  			begin_dock(ds);
  			dock(ds);
  			if (!dock_present(ds)) {
  				printk(KERN_ERR PREFIX "Unable to dock!
  ");
8b59560a3   Shaohua Li   ACPI: dock: avoid...
707
  				complete_dock(ds);
c8f7a62cd   Len Brown   Revert "Revert "A...
708
709
710
711
712
713
714
  				break;
  			}
  			atomic_notifier_call_chain(&dock_notifier_list,
  						   event, NULL);
  			hotplug_dock_devices(ds, event);
  			complete_dock(ds);
  			dock_event(ds, event, DOCK_EVENT);
406f692d0   Shaohua Li   dock: add _LCK su...
715
  			dock_lock(ds, 1);
3a37898d5   Lin Ming   ACPICA: Rename so...
716
  			acpi_update_all_gpes();
db350b084   Shaohua Li   dock: add bay and...
717
  			break;
c8f7a62cd   Len Brown   Revert "Revert "A...
718
  		}
db350b084   Shaohua Li   dock: add bay and...
719
720
721
722
723
724
  		if (dock_present(ds) || dock_in_progress(ds))
  			break;
  		/* This is a surprise removal */
  		surprise_removal = 1;
  		event = ACPI_NOTIFY_EJECT_REQUEST;
  		/* Fall back */
c8f7a62cd   Len Brown   Revert "Revert "A...
725
  	case ACPI_NOTIFY_EJECT_REQUEST:
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
726
  		begin_undock(ds);
f730ae183   Shaohua Li   libata: remove fu...
727
728
  		if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))
  		   || surprise_removal)
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
729
730
731
  			handle_eject_request(ds, event);
  		else
  			dock_event(ds, event, UNDOCK_EVENT);
c8f7a62cd   Len Brown   Revert "Revert "A...
732
733
734
735
736
737
  		break;
  	default:
  		printk(KERN_ERR PREFIX "Unknown dock event %d
  ", event);
  	}
  }
19cd847ab   Zhang Rui   ACPI: fix hotplug...
738
739
740
741
742
743
744
745
  struct dock_data {
  	acpi_handle handle;
  	unsigned long event;
  	struct dock_station *ds;
  };
  
  static void acpi_dock_deferred_cb(void *context)
  {
747479a3f   Alex Chiang   ACPI: dock: minor...
746
  	struct dock_data *data = context;
19cd847ab   Zhang Rui   ACPI: fix hotplug...
747
748
749
750
  
  	dock_notify(data->handle, data->event, data->ds);
  	kfree(data);
  }
6bd00a61a   Shaohua Li   ACPI: introduce n...
751
752
753
754
  static int acpi_dock_notifier_call(struct notifier_block *this,
  	unsigned long event, void *data)
  {
  	struct dock_station *dock_station;
747479a3f   Alex Chiang   ACPI: dock: minor...
755
  	acpi_handle handle = data;
6bd00a61a   Shaohua Li   ACPI: introduce n...
756
757
758
759
  
  	if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
  	   && event != ACPI_NOTIFY_EJECT_REQUEST)
  		return 0;
50d716e47   Alex Chiang   ACPI: dock: fix "...
760
  	list_for_each_entry(dock_station, &dock_stations, sibling) {
6bd00a61a   Shaohua Li   ACPI: introduce n...
761
  		if (dock_station->handle == handle) {
747479a3f   Alex Chiang   ACPI: dock: minor...
762
  			struct dock_data *dd;
19cd847ab   Zhang Rui   ACPI: fix hotplug...
763

747479a3f   Alex Chiang   ACPI: dock: minor...
764
765
  			dd = kmalloc(sizeof(*dd), GFP_KERNEL);
  			if (!dd)
19cd847ab   Zhang Rui   ACPI: fix hotplug...
766
  				return 0;
747479a3f   Alex Chiang   ACPI: dock: minor...
767
768
769
770
  			dd->handle = handle;
  			dd->event = event;
  			dd->ds = dock_station;
  			acpi_os_hotplug_execute(acpi_dock_deferred_cb, dd);
6bd00a61a   Shaohua Li   ACPI: introduce n...
771
772
773
774
775
776
777
778
779
  			return 0 ;
  		}
  	}
  	return 0;
  }
  
  static struct notifier_block dock_acpi_notifier = {
  	.notifier_call = acpi_dock_notifier_call,
  };
c8f7a62cd   Len Brown   Revert "Revert "A...
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
  /**
   * find_dock_devices - find devices on the dock station
   * @handle: the handle of the device we are examining
   * @lvl: unused
   * @context: the dock station private data
   * @rv: unused
   *
   * This function is called by acpi_walk_namespace.  It will
   * check to see if an object has an _EJD method.  If it does, then it
   * will see if it is dependent on the dock station.
   */
  static acpi_status
  find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
  {
  	acpi_status status;
fe9a2f77e   Kristen Carlson Accardi   ACPI: dock: check...
795
  	acpi_handle tmp, parent;
50dd09697   Jan Engelhardt   ACPI: Remove unne...
796
  	struct dock_station *ds = context;
c8f7a62cd   Len Brown   Revert "Revert "A...
797
798
  
  	status = acpi_bus_get_ejd(handle, &tmp);
fe9a2f77e   Kristen Carlson Accardi   ACPI: dock: check...
799
800
801
802
803
804
805
806
807
808
  	if (ACPI_FAILURE(status)) {
  		/* try the parent device as well */
  		status = acpi_get_parent(handle, &parent);
  		if (ACPI_FAILURE(status))
  			goto fdd_out;
  		/* see if parent is dependent on dock */
  		status = acpi_bus_get_ejd(parent, &tmp);
  		if (ACPI_FAILURE(status))
  			goto fdd_out;
  	}
c8f7a62cd   Len Brown   Revert "Revert "A...
809

f69cfdd24   Alex Chiang   ACPI: dock: combi...
810
811
  	if (tmp == ds->handle)
  		add_dock_dependent_device(ds, handle);
fe9a2f77e   Kristen Carlson Accardi   ACPI: dock: check...
812
  fdd_out:
c8f7a62cd   Len Brown   Revert "Revert "A...
813
814
  	return AE_OK;
  }
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
815
816
817
818
819
820
  /*
   * show_docked - read method for "docked" file in sysfs
   */
  static ssize_t show_docked(struct device *dev,
  			   struct device_attribute *attr, char *buf)
  {
fc5a9f884   Holger Macht   ACPI: dock: Don't...
821
  	struct acpi_device *tmp;
fe06fba29   Alex Chiang   ACPI: dock: add s...
822
  	struct dock_station *dock_station = dev->platform_data;
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
823

fc5a9f884   Holger Macht   ACPI: dock: Don't...
824
825
826
827
828
  	if (ACPI_SUCCESS(acpi_bus_get_device(dock_station->handle, &tmp)))
  		return snprintf(buf, PAGE_SIZE, "1
  ");
  	return snprintf(buf, PAGE_SIZE, "0
  ");
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
829
  }
e5685b9d3   Adrian Bunk   ACPI: misc cleanups
830
  static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
831
832
  
  /*
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
833
834
835
836
837
   * show_flags - read method for flags file in sysfs
   */
  static ssize_t show_flags(struct device *dev,
  			  struct device_attribute *attr, char *buf)
  {
fe06fba29   Alex Chiang   ACPI: dock: add s...
838
  	struct dock_station *dock_station = dev->platform_data;
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
839
840
841
842
  	return snprintf(buf, PAGE_SIZE, "%d
  ", dock_station->flags);
  
  }
e5685b9d3   Adrian Bunk   ACPI: misc cleanups
843
  static DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
844
845
  
  /*
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
846
847
848
849
850
851
   * write_undock - write method for "undock" file in sysfs
   */
  static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
  			   const char *buf, size_t count)
  {
  	int ret;
fe06fba29   Alex Chiang   ACPI: dock: add s...
852
  	struct dock_station *dock_station = dev->platform_data;
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
853
854
855
  
  	if (!count)
  		return -EINVAL;
9171f8348   Holger Macht   ACPI: Set flag DO...
856
  	begin_undock(dock_station);
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
857
858
859
  	ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
  	return ret ? ret: count;
  }
e5685b9d3   Adrian Bunk   ACPI: misc cleanups
860
  static DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
c80fdbe81   brandon@ifup.org   ACPI: dock: Add a...
861

ac122bb64   Ilya A. Volynets-Evenbakh   ACPI: dock: add a...
862
863
864
865
866
867
  /*
   * show_dock_uid - read method for "uid" file in sysfs
   */
  static ssize_t show_dock_uid(struct device *dev,
  			     struct device_attribute *attr, char *buf)
  {
27663c585   Matthew Wilcox   ACPI: Change acpi...
868
  	unsigned long long lbuf;
fe06fba29   Alex Chiang   ACPI: dock: add s...
869
  	struct dock_station *dock_station = dev->platform_data;
38ff4ffc0   Kristen Carlson Accardi   ACPI: dock: clean...
870
871
872
  	acpi_status status = acpi_evaluate_integer(dock_station->handle,
  					"_UID", NULL, &lbuf);
  	if (ACPI_FAILURE(status))
ac122bb64   Ilya A. Volynets-Evenbakh   ACPI: dock: add a...
873
  	    return 0;
38ff4ffc0   Kristen Carlson Accardi   ACPI: dock: clean...
874

27663c585   Matthew Wilcox   ACPI: Change acpi...
875
876
  	return snprintf(buf, PAGE_SIZE, "%llx
  ", lbuf);
ac122bb64   Ilya A. Volynets-Evenbakh   ACPI: dock: add a...
877
  }
e5685b9d3   Adrian Bunk   ACPI: misc cleanups
878
  static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
ac122bb64   Ilya A. Volynets-Evenbakh   ACPI: dock: add a...
879

8652b00fd   Shaohua Li   dock: add 'type' ...
880
881
882
  static ssize_t show_dock_type(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
fe06fba29   Alex Chiang   ACPI: dock: add s...
883
  	struct dock_station *dock_station = dev->platform_data;
8652b00fd   Shaohua Li   dock: add 'type' ...
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
  	char *type;
  
  	if (dock_station->flags & DOCK_IS_DOCK)
  		type = "dock_station";
  	else if (dock_station->flags & DOCK_IS_ATA)
  		type = "ata_bay";
  	else if (dock_station->flags & DOCK_IS_BAT)
  		type = "battery_bay";
  	else
  		type = "unknown";
  
  	return snprintf(buf, PAGE_SIZE, "%s
  ", type);
  }
  static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
5f46c2f25   Alex Chiang   ACPI: dock: conve...
899
900
901
902
903
904
905
906
907
908
909
910
  static struct attribute *dock_attributes[] = {
  	&dev_attr_docked.attr,
  	&dev_attr_flags.attr,
  	&dev_attr_undock.attr,
  	&dev_attr_uid.attr,
  	&dev_attr_type.attr,
  	NULL
  };
  
  static struct attribute_group dock_attribute_group = {
  	.attrs = dock_attributes
  };
c8f7a62cd   Len Brown   Revert "Revert "A...
911
912
913
914
915
916
917
  /**
   * dock_add - add a new dock station
   * @handle: the dock station handle
   *
   * allocated and initialize a new dock station device.  Find all devices
   * that are on the dock station, and register for dock event notifications.
   */
d38a5edf8   Uwe Kleine-König   ACPI dock: move s...
918
  static int __init dock_add(acpi_handle handle)
c8f7a62cd   Len Brown   Revert "Revert "A...
919
  {
fe06fba29   Alex Chiang   ACPI: dock: add s...
920
921
  	int ret, id;
  	struct dock_station ds, *dock_station;
747479a3f   Alex Chiang   ACPI: dock: minor...
922
  	struct platform_device *dd;
c8f7a62cd   Len Brown   Revert "Revert "A...
923

fe06fba29   Alex Chiang   ACPI: dock: add s...
924
  	id = dock_station_count;
49c6fb2e4   Alex Chiang   ACPI: dock: prope...
925
  	memset(&ds, 0, sizeof(ds));
747479a3f   Alex Chiang   ACPI: dock: minor...
926
927
928
929
930
  	dd = platform_device_register_data(NULL, "dock", id, &ds, sizeof(ds));
  	if (IS_ERR(dd))
  		return PTR_ERR(dd);
  
  	dock_station = dd->dev.platform_data;
c8f7a62cd   Len Brown   Revert "Revert "A...
931

c8f7a62cd   Len Brown   Revert "Revert "A...
932
  	dock_station->handle = handle;
747479a3f   Alex Chiang   ACPI: dock: minor...
933
  	dock_station->dock_device = dd;
c8f7a62cd   Len Brown   Revert "Revert "A...
934
  	dock_station->last_dock_time = jiffies - HZ;
747479a3f   Alex Chiang   ACPI: dock: minor...
935

8b0dc866d   Kristen Carlson Accardi   ACPI: dock: use m...
936
  	mutex_init(&dock_station->hp_lock);
747479a3f   Alex Chiang   ACPI: dock: minor...
937
938
939
  	spin_lock_init(&dock_station->dd_lock);
  	INIT_LIST_HEAD(&dock_station->sibling);
  	INIT_LIST_HEAD(&dock_station->hotplug_devices);
07a18684c   Kristen Accardi   ACPI: ACPI_DOCK: ...
940
  	ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
747479a3f   Alex Chiang   ACPI: dock: minor...
941
  	INIT_LIST_HEAD(&dock_station->dependent_devices);
a0cd35fdc   Kristen Carlson Accardi   ACPI: dock: add i...
942

9ef2a9a9f   Kristen Carlson Accardi   ACPI: dock: unsup...
943
  	/* we want the dock device to send uevents */
747479a3f   Alex Chiang   ACPI: dock: minor...
944
  	dev_set_uevent_suppress(&dd->dev, 0);
9ef2a9a9f   Kristen Carlson Accardi   ACPI: dock: unsup...
945

db350b084   Shaohua Li   dock: add bay and...
946
947
948
949
950
951
  	if (is_dock(handle))
  		dock_station->flags |= DOCK_IS_DOCK;
  	if (is_ata(handle))
  		dock_station->flags |= DOCK_IS_ATA;
  	if (is_battery(handle))
  		dock_station->flags |= DOCK_IS_BAT;
747479a3f   Alex Chiang   ACPI: dock: minor...
952
  	ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
8652b00fd   Shaohua Li   dock: add 'type' ...
953
  	if (ret)
5f46c2f25   Alex Chiang   ACPI: dock: conve...
954
  		goto err_unregister;
671adbec2   Kristen Carlson Accardi   ACPI: dock: Make ...
955

c8f7a62cd   Len Brown   Revert "Revert "A...
956
957
  	/* Find dependent devices */
  	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
2263576cf   Lin Ming   ACPICA: Add post-...
958
959
  			    ACPI_UINT32_MAX, find_dock_devices, NULL,
  			    dock_station, NULL);
c8f7a62cd   Len Brown   Revert "Revert "A...
960
961
  
  	/* add the dock station as a device dependent on itself */
f69cfdd24   Alex Chiang   ACPI: dock: combi...
962
963
  	ret = add_dock_dependent_device(dock_station, handle);
  	if (ret)
5f46c2f25   Alex Chiang   ACPI: dock: conve...
964
  		goto err_rmgroup;
c8f7a62cd   Len Brown   Revert "Revert "A...
965

db350b084   Shaohua Li   dock: add bay and...
966
  	dock_station_count++;
50d716e47   Alex Chiang   ACPI: dock: fix "...
967
  	list_add(&dock_station->sibling, &dock_stations);
c8f7a62cd   Len Brown   Revert "Revert "A...
968
  	return 0;
5f46c2f25   Alex Chiang   ACPI: dock: conve...
969
  err_rmgroup:
747479a3f   Alex Chiang   ACPI: dock: minor...
970
  	sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);
5f46c2f25   Alex Chiang   ACPI: dock: conve...
971
  err_unregister:
747479a3f   Alex Chiang   ACPI: dock: minor...
972
  	platform_device_unregister(dd);
5f46c2f25   Alex Chiang   ACPI: dock: conve...
973
974
  	printk(KERN_ERR "%s encountered error %d
  ", __func__, ret);
c8f7a62cd   Len Brown   Revert "Revert "A...
975
976
977
978
979
980
  	return ret;
  }
  
  /**
   * dock_remove - free up resources related to the dock station
   */
747479a3f   Alex Chiang   ACPI: dock: minor...
981
  static int dock_remove(struct dock_station *ds)
c8f7a62cd   Len Brown   Revert "Revert "A...
982
983
  {
  	struct dock_dependent_device *dd, *tmp;
747479a3f   Alex Chiang   ACPI: dock: minor...
984
  	struct platform_device *dock_device = ds->dock_device;
c8f7a62cd   Len Brown   Revert "Revert "A...
985

db350b084   Shaohua Li   dock: add bay and...
986
  	if (!dock_station_count)
c8f7a62cd   Len Brown   Revert "Revert "A...
987
988
989
  		return 0;
  
  	/* remove dependent devices */
747479a3f   Alex Chiang   ACPI: dock: minor...
990
991
  	list_for_each_entry_safe(dd, tmp, &ds->dependent_devices, list)
  		kfree(dd);
c8f7a62cd   Len Brown   Revert "Revert "A...
992

747479a3f   Alex Chiang   ACPI: dock: minor...
993
  	list_del(&ds->sibling);
c8f7a62cd   Len Brown   Revert "Revert "A...
994

671adbec2   Kristen Carlson Accardi   ACPI: dock: Make ...
995
  	/* cleanup sysfs */
5f46c2f25   Alex Chiang   ACPI: dock: conve...
996
  	sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group);
0f6f28045   Kristen Carlson Accardi   ACPI: dock: use d...
997
  	platform_device_unregister(dock_device);
671adbec2   Kristen Carlson Accardi   ACPI: dock: Make ...
998

c8f7a62cd   Len Brown   Revert "Revert "A...
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
  	return 0;
  }
  
  /**
   * find_dock - look for a dock station
   * @handle: acpi handle of a device
   * @lvl: unused
   * @context: counter of dock stations found
   * @rv: unused
   *
   * This is called by acpi_walk_namespace to look for dock stations.
   */
d38a5edf8   Uwe Kleine-König   ACPI dock: move s...
1011
  static __init acpi_status
c8f7a62cd   Len Brown   Revert "Revert "A...
1012
1013
  find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
  {
747479a3f   Alex Chiang   ACPI: dock: minor...
1014
  	if (is_dock(handle))
1ee4d61fd   Zhang Rui   ACPI dock: suppor...
1015
  		dock_add(handle);
747479a3f   Alex Chiang   ACPI: dock: minor...
1016

1ee4d61fd   Zhang Rui   ACPI dock: suppor...
1017
  	return AE_OK;
c8f7a62cd   Len Brown   Revert "Revert "A...
1018
  }
d38a5edf8   Uwe Kleine-König   ACPI dock: move s...
1019
  static __init acpi_status
db350b084   Shaohua Li   dock: add bay and...
1020
  find_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
c8f7a62cd   Len Brown   Revert "Revert "A...
1021
  {
61b836958   Shaohua Li   dock: fix for ATA...
1022
1023
  	/* If bay is a dock, it's already handled */
  	if (is_ejectable_bay(handle) && !is_dock(handle))
db350b084   Shaohua Li   dock: add bay and...
1024
1025
1026
  		dock_add(handle);
  	return AE_OK;
  }
c8f7a62cd   Len Brown   Revert "Revert "A...
1027

db350b084   Shaohua Li   dock: add bay and...
1028
1029
  static int __init dock_init(void)
  {
816c2eda3   Len Brown   dock: bay: Don't ...
1030
1031
  	if (acpi_disabled)
  		return 0;
c8f7a62cd   Len Brown   Revert "Revert "A...
1032
1033
  	/* look for a dock station */
  	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
2263576cf   Lin Ming   ACPICA: Add post-...
1034
  			    ACPI_UINT32_MAX, find_dock, NULL, NULL, NULL);
c8f7a62cd   Len Brown   Revert "Revert "A...
1035

db350b084   Shaohua Li   dock: add bay and...
1036
1037
  	/* look for bay */
  	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
2263576cf   Lin Ming   ACPICA: Add post-...
1038
  			ACPI_UINT32_MAX, find_bay, NULL, NULL, NULL);
db350b084   Shaohua Li   dock: add bay and...
1039
1040
1041
1042
1043
  	if (!dock_station_count) {
  		printk(KERN_INFO PREFIX "No dock devices found.
  ");
  		return 0;
  	}
c8f7a62cd   Len Brown   Revert "Revert "A...
1044

6bd00a61a   Shaohua Li   ACPI: introduce n...
1045
  	register_acpi_bus_notifier(&dock_acpi_notifier);
db350b084   Shaohua Li   dock: add bay and...
1046
1047
1048
  	printk(KERN_INFO PREFIX "%s: %d docks/bays found
  ",
  		ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
c8f7a62cd   Len Brown   Revert "Revert "A...
1049
1050
1051
1052
1053
  	return 0;
  }
  
  static void __exit dock_exit(void)
  {
747479a3f   Alex Chiang   ACPI: dock: minor...
1054
  	struct dock_station *tmp, *dock_station;
db350b084   Shaohua Li   dock: add bay and...
1055

6bd00a61a   Shaohua Li   ACPI: introduce n...
1056
  	unregister_acpi_bus_notifier(&dock_acpi_notifier);
50d716e47   Alex Chiang   ACPI: dock: fix "...
1057
  	list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling)
db350b084   Shaohua Li   dock: add bay and...
1058
  		dock_remove(dock_station);
c8f7a62cd   Len Brown   Revert "Revert "A...
1059
  }
db350b084   Shaohua Li   dock: add bay and...
1060
1061
1062
1063
1064
  /*
   * Must be called before drivers of devices in dock, otherwise we can't know
   * which devices are in a dock
   */
  subsys_initcall(dock_init);
c8f7a62cd   Len Brown   Revert "Revert "A...
1065
  module_exit(dock_exit);