diff options
| author | Len Brown <len.brown@intel.com> | 2006-07-10 02:39:33 -0400 |
|---|---|---|
| committer | Len Brown <len.brown@intel.com> | 2006-07-10 02:39:33 -0400 |
| commit | dece75b3a288fa49b3aab685543ec2f5c94b8cfc (patch) | |
| tree | 42b4871522182106d06061923fe9e567ac2e10db | |
| parent | 1a39ed5888a8336ed2762d5b367195b14b878850 (diff) | |
| parent | 8d7bff6c0896feba2fbd5ce37062c212aee13870 (diff) | |
Pull dock into test branch
| -rw-r--r-- | drivers/acpi/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/acpi/dock.c | 739 | ||||
| -rw-r--r-- | drivers/acpi/scan.c | 23 | ||||
| -rw-r--r-- | drivers/pci/hotplug/Kconfig | 2 | ||||
| -rw-r--r-- | include/acpi/acpi_bus.h | 2 | ||||
| -rw-r--r-- | include/acpi/acpi_drivers.h | 17 |
7 files changed, 789 insertions, 2 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 89eacd1bfeeb..56c5ba874623 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
| @@ -132,6 +132,12 @@ config ACPI_FAN | |||
| 132 | This driver adds support for ACPI fan devices, allowing user-mode | 132 | This driver adds support for ACPI fan devices, allowing user-mode |
| 133 | applications to perform basic fan control (on, off, status). | 133 | applications to perform basic fan control (on, off, status). |
| 134 | 134 | ||
| 135 | config ACPI_DOCK | ||
| 136 | tristate "Dock" | ||
| 137 | depends on EXPERIMENTAL | ||
| 138 | help | ||
| 139 | This driver adds support for ACPI controlled docking stations | ||
| 140 | |||
| 135 | config ACPI_PROCESSOR | 141 | config ACPI_PROCESSOR |
| 136 | tristate "Processor" | 142 | tristate "Processor" |
| 137 | default y | 143 | default y |
| @@ -206,6 +212,7 @@ config ACPI_IBM | |||
| 206 | config ACPI_IBM_DOCK | 212 | config ACPI_IBM_DOCK |
| 207 | bool "Legacy Docking Station Support" | 213 | bool "Legacy Docking Station Support" |
| 208 | depends on ACPI_IBM | 214 | depends on ACPI_IBM |
| 215 | depends on ACPI_DOCK=n | ||
| 209 | default n | 216 | default n |
| 210 | ---help--- | 217 | ---help--- |
| 211 | Allows the ibm_acpi driver to handle docking station events. | 218 | Allows the ibm_acpi driver to handle docking station events. |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index bb5b80a80b18..bce7ca27b429 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
| @@ -42,6 +42,7 @@ obj-$(CONFIG_ACPI_BATTERY) += battery.o | |||
| 42 | obj-$(CONFIG_ACPI_BUTTON) += button.o | 42 | obj-$(CONFIG_ACPI_BUTTON) += button.o |
| 43 | obj-$(CONFIG_ACPI_EC) += ec.o | 43 | obj-$(CONFIG_ACPI_EC) += ec.o |
| 44 | obj-$(CONFIG_ACPI_FAN) += fan.o | 44 | obj-$(CONFIG_ACPI_FAN) += fan.o |
| 45 | obj-$(CONFIG_ACPI_DOCK) += dock.o | ||
| 45 | obj-$(CONFIG_ACPI_VIDEO) += video.o | 46 | obj-$(CONFIG_ACPI_VIDEO) += video.o |
| 46 | obj-$(CONFIG_ACPI_HOTKEY) += hotkey.o | 47 | obj-$(CONFIG_ACPI_HOTKEY) += hotkey.o |
| 47 | obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o | 48 | obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o |
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c new file mode 100644 index 000000000000..510a94524298 --- /dev/null +++ b/drivers/acpi/dock.c | |||
| @@ -0,0 +1,739 @@ | |||
| 1 | /* | ||
| 2 | * dock.c - ACPI dock station driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com> | ||
| 5 | * | ||
| 6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
| 11 | * your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but | ||
| 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 16 | * General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along | ||
| 19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 21 | * | ||
| 22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 23 | */ | ||
| 24 | |||
| 25 | #include <linux/kernel.h> | ||
| 26 | #include <linux/module.h> | ||
| 27 | #include <linux/init.h> | ||
| 28 | #include <linux/types.h> | ||
| 29 | #include <linux/notifier.h> | ||
| 30 | #include <acpi/acpi_bus.h> | ||
| 31 | #include <acpi/acpi_drivers.h> | ||
| 32 | |||
| 33 | #define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver" | ||
| 34 | |||
| 35 | ACPI_MODULE_NAME("dock") | ||
| 36 | MODULE_AUTHOR("Kristen Carlson Accardi"); | ||
| 37 | MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME); | ||
| 38 | MODULE_LICENSE("GPL"); | ||
| 39 | |||
| 40 | static struct atomic_notifier_head dock_notifier_list; | ||
| 41 | |||
| 42 | struct dock_station { | ||
| 43 | acpi_handle handle; | ||
| 44 | unsigned long last_dock_time; | ||
| 45 | u32 flags; | ||
| 46 | spinlock_t dd_lock; | ||
| 47 | spinlock_t hp_lock; | ||
| 48 | struct list_head dependent_devices; | ||
| 49 | struct list_head hotplug_devices; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct dock_dependent_device { | ||
| 53 | struct list_head list; | ||
| 54 | struct list_head hotplug_list; | ||
| 55 | acpi_handle handle; | ||
| 56 | acpi_notify_handler handler; | ||
| 57 | void *context; | ||
| 58 | }; | ||
| 59 | |||
| 60 | #define DOCK_DOCKING 0x00000001 | ||
| 61 | #define DOCK_EVENT KOBJ_DOCK | ||
| 62 | #define UNDOCK_EVENT KOBJ_UNDOCK | ||
| 63 | |||
| 64 | static struct dock_station *dock_station; | ||
| 65 | |||
| 66 | /***************************************************************************** | ||
| 67 | * Dock Dependent device functions * | ||
| 68 | *****************************************************************************/ | ||
| 69 | /** | ||
| 70 | * alloc_dock_dependent_device - allocate and init a dependent device | ||
| 71 | * @handle: the acpi_handle of the dependent device | ||
| 72 | * | ||
| 73 | * Allocate memory for a dependent device structure for a device referenced | ||
| 74 | * by the acpi handle | ||
| 75 | */ | ||
| 76 | static struct dock_dependent_device * | ||
| 77 | alloc_dock_dependent_device(acpi_handle handle) | ||
| 78 | { | ||
| 79 | struct dock_dependent_device *dd; | ||
| 80 | |||
| 81 | dd = kzalloc(sizeof(*dd), GFP_KERNEL); | ||
| 82 | if (dd) { | ||
| 83 | dd->handle = handle; | ||
| 84 | INIT_LIST_HEAD(&dd->list); | ||
| 85 | INIT_LIST_HEAD(&dd->hotplug_list); | ||
| 86 | } | ||
| 87 | return dd; | ||
| 88 | } | ||
| 89 | |||
| 90 | /** | ||
| 91 | * add_dock_dependent_device - associate a device with the dock station | ||
| 92 | * @ds: The dock station | ||
| 93 | * @dd: The dependent device | ||
| 94 | * | ||
| 95 | * Add the dependent device to the dock's dependent device list. | ||
| 96 | */ | ||
| 97 | static void | ||
| 98 | add_dock_dependent_device(struct dock_station *ds, | ||
| 99 | struct dock_dependent_device *dd) | ||
| 100 | { | ||
| 101 | spin_lock(&ds->dd_lock); | ||
| 102 | list_add_tail(&dd->list, &ds->dependent_devices); | ||
| 103 | spin_unlock(&ds->dd_lock); | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 107 | * dock_add_hotplug_device - associate a hotplug handler with the dock station | ||
| 108 | * @ds: The dock station | ||
| 109 | * @dd: The dependent device struct | ||
| 110 | * | ||
| 111 | * Add the dependent device to the dock's hotplug device list | ||
| 112 | */ | ||
| 113 | static void | ||
| 114 | dock_add_hotplug_device(struct dock_station *ds, | ||
| 115 | struct dock_dependent_device *dd) | ||
| 116 | { | ||
| 117 | spin_lock(&ds->hp_lock); | ||
| 118 | list_add_tail(&dd->hotplug_list, &ds->hotplug_devices); | ||
| 119 | spin_unlock(&ds->hp_lock); | ||
| 120 | } | ||
| 121 | |||
| 122 | /** | ||
| 123 | * dock_del_hotplug_device - remove a hotplug handler from the dock station | ||
| 124 | * @ds: The dock station | ||
| 125 | * @dd: the dependent device struct | ||
| 126 | * | ||
| 127 | * Delete the dependent device from the dock's hotplug device list | ||
| 128 | */ | ||
| 129 | static void | ||
| 130 | dock_del_hotplug_device(struct dock_station *ds, | ||
| 131 | struct dock_dependent_device *dd) | ||
| 132 | { | ||
| 133 | spin_lock(&ds->hp_lock); | ||
| 134 | list_del(&dd->hotplug_list); | ||
| 135 | spin_unlock(&ds->hp_lock); | ||
| 136 | } | ||
| 137 | |||
| 138 | /** | ||
| 139 | * find_dock_dependent_device - get a device dependent on this dock | ||
| 140 | * @ds: the dock station | ||
| 141 | * @handle: the acpi_handle of the device we want | ||
| 142 | * | ||
| 143 | * iterate over the dependent device list for this dock. If the | ||
| 144 | * dependent device matches the handle, return. | ||
| 145 | */ | ||
| 146 | static struct dock_dependent_device * | ||
| 147 | find_dock_dependent_device(struct dock_station *ds, acpi_handle handle) | ||
| 148 | { | ||
| 149 | struct dock_dependent_device *dd; | ||
| 150 | |||
| 151 | spin_lock(&ds->dd_lock); | ||
| 152 | list_for_each_entry(dd, &ds->dependent_devices, list) { | ||
| 153 | if (handle == dd->handle) { | ||
| 154 | spin_unlock(&ds->dd_lock); | ||
| 155 | return dd; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | spin_unlock(&ds->dd_lock); | ||
| 159 | return NULL; | ||
| 160 | } | ||
| 161 | |||
| 162 | /***************************************************************************** | ||
| 163 | * Dock functions * | ||
| 164 | *****************************************************************************/ | ||
| 165 | /** | ||
| 166 | * is_dock - see if a device is a dock station | ||
| 167 | * @handle: acpi handle of the device | ||
| 168 | * | ||
| 169 | * If an acpi object has a _DCK method, then it is by definition a dock | ||
| 170 | * station, so return true. | ||
| 171 | */ | ||
| 172 | static int is_dock(acpi_handle handle) | ||
| 173 | { | ||
| 174 | acpi_status status; | ||
| 175 | acpi_handle tmp; | ||
| 176 | |||
| 177 | status = acpi_get_handle(handle, "_DCK", &tmp); | ||
| 178 | if (ACPI_FAILURE(status)) | ||
| 179 | return 0; | ||
| 180 | return 1; | ||
| 181 | } | ||
| 182 | |||
| 183 | /** | ||
| 184 | * is_dock_device - see if a device is on a dock station | ||
| 185 | * @handle: acpi handle of the device | ||
| 186 | * | ||
| 187 | * If this device is either the dock station itself, | ||
| 188 | * or is a device dependent on the dock station, then it | ||
| 189 | * is a dock device | ||
| 190 | */ | ||
| 191 | int is_dock_device(acpi_handle handle) | ||
| 192 | { | ||
| 193 | if (!dock_station) | ||
| 194 | return 0; | ||
| 195 | |||
| 196 | if (is_dock(handle) || find_dock_dependent_device(dock_station, handle)) | ||
| 197 | return 1; | ||
| 198 | |||
| 199 | return 0; | ||
| 200 | } | ||
| 201 | |||
| 202 | EXPORT_SYMBOL_GPL(is_dock_device); | ||
| 203 | |||
| 204 | /** | ||
| 205 | * dock_present - see if the dock station is present. | ||
| 206 | * @ds: the dock station | ||
| 207 | * | ||
| 208 | * execute the _STA method. note that present does not | ||
| 209 | * imply that we are docked. | ||
| 210 | */ | ||
| 211 | static int dock_present(struct dock_station *ds) | ||
| 212 | { | ||
| 213 | unsigned long sta; | ||
| 214 | acpi_status status; | ||
| 215 | |||
| 216 | if (ds) { | ||
| 217 | status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta); | ||
| 218 | if (ACPI_SUCCESS(status) && sta) | ||
| 219 | return 1; | ||
| 220 | } | ||
| 221 | return 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | |||
| 225 | |||
| 226 | /** | ||
| 227 | * dock_create_acpi_device - add new devices to acpi | ||
| 228 | * @handle - handle of the device to add | ||
| 229 | * | ||
| 230 | * This function will create a new acpi_device for the given | ||
| 231 | * handle if one does not exist already. This should cause | ||
| 232 | * acpi to scan for drivers for the given devices, and call | ||
| 233 | * matching driver's add routine. | ||
| 234 | * | ||
| 235 | * Returns a pointer to the acpi_device corresponding to the handle. | ||
| 236 | */ | ||
| 237 | static struct acpi_device * dock_create_acpi_device(acpi_handle handle) | ||
| 238 | { | ||
| 239 | struct acpi_device *device = NULL; | ||
| 240 | struct acpi_device *parent_device; | ||
| 241 | acpi_handle parent; | ||
| 242 | int ret; | ||
| 243 | |||
| 244 | if (acpi_bus_get_device(handle, &device)) { | ||
| 245 | /* | ||
| 246 | * no device created for this object, | ||
| 247 | * so we should create one. | ||
| 248 | */ | ||
| 249 | acpi_get_parent(handle, &parent); | ||
| 250 | if (acpi_bus_get_device(parent, &parent_device)) | ||
| 251 | parent_device = NULL; | ||
| 252 | |||
| 253 | ret = acpi_bus_add(&device, parent_device, handle, | ||
| 254 | ACPI_BUS_TYPE_DEVICE); | ||
| 255 | if (ret) { | ||
| 256 | pr_debug("error adding bus, %x\n", | ||
| 257 | -ret); | ||
| 258 | return NULL; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | return device; | ||
| 262 | } | ||
| 263 | |||
| 264 | /** | ||
| 265 | * dock_remove_acpi_device - remove the acpi_device struct from acpi | ||
| 266 | * @handle - the handle of the device to remove | ||
| 267 | * | ||
| 268 | * Tell acpi to remove the acpi_device. This should cause any loaded | ||
| 269 | * driver to have it's remove routine called. | ||
| 270 | */ | ||
| 271 | static void dock_remove_acpi_device(acpi_handle handle) | ||
| 272 | { | ||
| 273 | struct acpi_device *device; | ||
| 274 | int ret; | ||
| 275 | |||
| 276 | if (!acpi_bus_get_device(handle, &device)) { | ||
| 277 | ret = acpi_bus_trim(device, 1); | ||
| 278 | if (ret) | ||
| 279 | pr_debug("error removing bus, %x\n", -ret); | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | |||
| 284 | /** | ||
| 285 | * hotplug_dock_devices - insert or remove devices on the dock station | ||
| 286 | * @ds: the dock station | ||
| 287 | * @event: either bus check or eject request | ||
| 288 | * | ||
| 289 | * Some devices on the dock station need to have drivers called | ||
| 290 | * to perform hotplug operations after a dock event has occurred. | ||
| 291 | * Traverse the list of dock devices that have registered a | ||
| 292 | * hotplug handler, and call the handler. | ||
| 293 | */ | ||
| 294 | static void hotplug_dock_devices(struct dock_station *ds, u32 event) | ||
| 295 | { | ||
| 296 | struct dock_dependent_device *dd; | ||
| 297 | |||
| 298 | spin_lock(&ds->hp_lock); | ||
| 299 | |||
| 300 | /* | ||
| 301 | * First call driver specific hotplug functions | ||
| 302 | */ | ||
| 303 | list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) { | ||
| 304 | if (dd->handler) | ||
| 305 | dd->handler(dd->handle, event, dd->context); | ||
| 306 | } | ||
| 307 | |||
| 308 | /* | ||
| 309 | * Now make sure that an acpi_device is created for each | ||
| 310 | * dependent device, or removed if this is an eject request. | ||
| 311 | * This will cause acpi_drivers to be stopped/started if they | ||
| 312 | * exist | ||
| 313 | */ | ||
| 314 | list_for_each_entry(dd, &ds->dependent_devices, list) { | ||
| 315 | if (event == ACPI_NOTIFY_EJECT_REQUEST) | ||
| 316 | dock_remove_acpi_device(dd->handle); | ||
| 317 | else | ||
| 318 | dock_create_acpi_device(dd->handle); | ||
| 319 | } | ||
| 320 | spin_unlock(&ds->hp_lock); | ||
| 321 | } | ||
| 322 | |||
| 323 | static void dock_event(struct dock_station *ds, u32 event, int num) | ||
| 324 | { | ||
| 325 | struct acpi_device *device; | ||
| 326 | |||
| 327 | device = dock_create_acpi_device(ds->handle); | ||
| 328 | if (device) | ||
| 329 | kobject_uevent(&device->kobj, num); | ||
| 330 | } | ||
| 331 | |||
| 332 | /** | ||
| 333 | * eject_dock - respond to a dock eject request | ||
| 334 | * @ds: the dock station | ||
| 335 | * | ||
| 336 | * This is called after _DCK is called, to execute the dock station's | ||
| 337 | * _EJ0 method. | ||
| 338 | */ | ||
| 339 | static void eject_dock(struct dock_station *ds) | ||
| 340 | { | ||
| 341 | struct acpi_object_list arg_list; | ||
| 342 | union acpi_object arg; | ||
| 343 | acpi_status status; | ||
| 344 | acpi_handle tmp; | ||
| 345 | |||
| 346 | /* all dock devices should have _EJ0, but check anyway */ | ||
| 347 | status = acpi_get_handle(ds->handle, "_EJ0", &tmp); | ||
| 348 | if (ACPI_FAILURE(status)) { | ||
| 349 | pr_debug("No _EJ0 support for dock device\n"); | ||
| 350 | return; | ||
| 351 | } | ||
| 352 | |||
| 353 | arg_list.count = 1; | ||
| 354 | arg_list.pointer = &arg; | ||
| 355 | arg.type = ACPI_TYPE_INTEGER; | ||
| 356 | arg.integer.value = 1; | ||
| 357 | |||
| 358 | if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0", | ||
| 359 | &arg_list, NULL))) | ||
| 360 | pr_debug("Failed to evaluate _EJ0!\n"); | ||
| 361 | } | ||
| 362 | |||
| 363 | /** | ||
| 364 | * handle_dock - handle a dock event | ||
| 365 | * @ds: the dock station | ||
| 366 | * @dock: to dock, or undock - that is the question | ||
| 367 | * | ||
| 368 | * Execute the _DCK method in response to an acpi event | ||
| 369 | */ | ||
| 370 | static void handle_dock(struct dock_station *ds, int dock) | ||
| 371 | { | ||
| 372 | acpi_status status; | ||
| 373 | struct acpi_object_list arg_list; | ||
| 374 | union acpi_object arg; | ||
| 375 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 376 | struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 377 | union acpi_object *obj; | ||
| 378 | |||
| 379 | acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer); | ||
| 380 | obj = name_buffer.pointer; | ||
| 381 | |||
| 382 | printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking"); | ||
| 383 | |||
| 384 | /* _DCK method has one argument */ | ||
| 385 | arg_list.count = 1; | ||
| 386 | arg_list.pointer = &arg; | ||
| 387 | arg.type = ACPI_TYPE_INTEGER; | ||
| 388 | arg.integer.value = dock; | ||
| 389 | status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer); | ||
| 390 | if (ACPI_FAILURE(status)) | ||
| 391 | pr_debug("%s: failed to execute _DCK\n", obj->string.pointer); | ||
| 392 | kfree(buffer.pointer); | ||
| 393 | kfree(name_buffer.pointer); | ||
| 394 | } | ||
| 395 | |||
| 396 | static inline void dock(struct dock_station *ds) | ||
| 397 | { | ||
| 398 | handle_dock(ds, 1); | ||
| 399 | } | ||
| 400 | |||
| 401 | static inline void undock(struct dock_station *ds) | ||
| 402 | { | ||
| 403 | handle_dock(ds, 0); | ||
| 404 | } | ||
| 405 | |||
| 406 | static inline void begin_dock(struct dock_station *ds) | ||
| 407 | { | ||
| 408 | ds->flags |= DOCK_DOCKING; | ||
| 409 | } | ||
| 410 | |||
| 411 | static inline void complete_dock(struct dock_station *ds) | ||
| 412 | { | ||
| 413 | ds->flags &= ~(DOCK_DOCKING); | ||
| 414 | ds->last_dock_time = jiffies; | ||
| 415 | } | ||
| 416 | |||
| 417 | /** | ||
| 418 | * dock_in_progress - see if we are in the middle of handling a dock event | ||
| 419 | * @ds: the dock station | ||
| 420 | * | ||
| 421 | * Sometimes while docking, false dock events can be sent to the driver | ||
| 422 | * because good connections aren't made or some other reason. Ignore these | ||
| 423 | * if we are in the middle of doing something. | ||
| 424 | */ | ||
| 425 | static int dock_in_progress(struct dock_station *ds) | ||
| 426 | { | ||
| 427 | if ((ds->flags & DOCK_DOCKING) || | ||
| 428 | time_before(jiffies, (ds->last_dock_time + HZ))) | ||
| 429 | return 1; | ||
| 430 | return 0; | ||
| 431 | } | ||
| 432 | |||
| 433 | /** | ||
| 434 | * register_dock_notifier - add yourself to the dock notifier list | ||
| 435 | * @nb: the callers notifier block | ||
| 436 | * | ||
| 437 | * If a driver wishes to be notified about dock events, they can | ||
| 438 | * use this function to put a notifier block on the dock notifier list. | ||
| 439 | * this notifier call chain will be called after a dock event, but | ||
| 440 | * before hotplugging any new devices. | ||
| 441 | */ | ||
| 442 | int register_dock_notifier(struct notifier_block *nb) | ||
| 443 | { | ||
| 444 | return atomic_notifier_chain_register(&dock_notifier_list, nb); | ||
| 445 | } | ||
| 446 | |||
| 447 | EXPORT_SYMBOL_GPL(register_dock_notifier); | ||
| 448 | |||
| 449 | /** | ||
| 450 | * unregister_dock_notifier - remove yourself from the dock notifier list | ||
| 451 | * @nb: the callers notifier block | ||
| 452 | */ | ||
| 453 | void unregister_dock_notifier(struct notifier_block *nb) | ||
| 454 | { | ||
| 455 | atomic_notifier_chain_unregister(&dock_notifier_list, nb); | ||
| 456 | } | ||
| 457 | |||
| 458 | EXPORT_SYMBOL_GPL(unregister_dock_notifier); | ||
| 459 | |||
| 460 | /** | ||
| 461 | * register_hotplug_dock_device - register a hotplug function | ||
| 462 | * @handle: the handle of the device | ||
| 463 | * @handler: the acpi_notifier_handler to call after docking | ||
| 464 | * @context: device specific data | ||
| 465 | * | ||
| 466 | * If a driver would like to perform a hotplug operation after a dock | ||
| 467 | * event, they can register an acpi_notifiy_handler to be called by | ||
| 468 | * the dock driver after _DCK is executed. | ||
| 469 | */ | ||
| 470 | int | ||
| 471 | register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler, | ||
| 472 | void *context) | ||
| 473 | { | ||
| 474 | struct dock_dependent_device *dd; | ||
| 475 | |||
| 476 | if (!dock_station) | ||
| 477 | return -ENODEV; | ||
| 478 | |||
| 479 | /* | ||
| 480 | * make sure this handle is for a device dependent on the dock, | ||
| 481 | * this would include the dock station itself | ||
| 482 | */ | ||
| 483 | dd = find_dock_dependent_device(dock_station, handle); | ||
| 484 | if (dd) { | ||
| 485 | dd->handler = handler; | ||
| 486 | dd->context = context; | ||
| 487 | dock_add_hotplug_device(dock_station, dd); | ||
| 488 | return 0; | ||
| 489 | } | ||
| 490 | |||
| 491 | return -EINVAL; | ||
| 492 | } | ||
| 493 | |||
| 494 | EXPORT_SYMBOL_GPL(register_hotplug_dock_device); | ||
| 495 | |||
| 496 | /** | ||
| 497 | * unregister_hotplug_dock_device - remove yourself from the hotplug list | ||
| 498 | * @handle: the acpi handle of the device | ||
| 499 | */ | ||
| 500 | void unregister_hotplug_dock_device(acpi_handle handle) | ||
| 501 | { | ||
| 502 | struct dock_dependent_device *dd; | ||
| 503 | |||
| 504 | if (!dock_station) | ||
| 505 | return; | ||
| 506 | |||
| 507 | dd = find_dock_dependent_device(dock_station, handle); | ||
| 508 | if (dd) | ||
| 509 | dock_del_hotplug_device(dock_station, dd); | ||
| 510 | } | ||
| 511 | |||
| 512 | EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); | ||
| 513 | |||
| 514 | /** | ||
| 515 | * dock_notify - act upon an acpi dock notification | ||
| 516 | * @handle: the dock station handle | ||
| 517 | * @event: the acpi event | ||
| 518 | * @data: our driver data struct | ||
| 519 | * | ||
| 520 | * If we are notified to dock, then check to see if the dock is | ||
| 521 | * present and then dock. Notify all drivers of the dock event, | ||
| 522 | * and then hotplug and devices that may need hotplugging. For undock | ||
| 523 | * check to make sure the dock device is still present, then undock | ||
| 524 | * and hotremove all the devices that may need removing. | ||
| 525 | */ | ||
| 526 | static void dock_notify(acpi_handle handle, u32 event, void *data) | ||
| 527 | { | ||
| 528 | struct dock_station *ds = (struct dock_station *)data; | ||
| 529 | |||
| 530 | switch (event) { | ||
| 531 | case ACPI_NOTIFY_BUS_CHECK: | ||
| 532 | if (!dock_in_progress(ds) && dock_present(ds)) { | ||
| 533 | begin_dock(ds); | ||
| 534 | dock(ds); | ||
| 535 | if (!dock_present(ds)) { | ||
| 536 | printk(KERN_ERR PREFIX "Unable to dock!\n"); | ||
| 537 | break; | ||
| 538 | } | ||
| 539 | atomic_notifier_call_chain(&dock_notifier_list, | ||
| 540 | event, NULL); | ||
| 541 | hotplug_dock_devices(ds, event); | ||
| 542 | complete_dock(ds); | ||
| 543 | dock_event(ds, event, DOCK_EVENT); | ||
| 544 | } | ||
| 545 | break; | ||
| 546 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
| 547 | /* | ||
| 548 | * According to acpi spec 3.0a, if a DEVICE_CHECK notification | ||
| 549 | * is sent and _DCK is present, it is assumed to mean an | ||
| 550 | * undock request. This notify routine will only be called | ||
| 551 | * for objects defining _DCK, so we will fall through to eject | ||
| 552 | * request here. However, we will pass an eject request through | ||
| 553 | * to the driver who wish to hotplug. | ||
| 554 | */ | ||
| 555 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
| 556 | if (!dock_in_progress(ds) && dock_present(ds)) { | ||
| 557 | /* | ||
| 558 | * here we need to generate the undock | ||
| 559 | * event prior to actually doing the undock | ||
| 560 | * so that the device struct still exists. | ||
| 561 | */ | ||
| 562 | dock_event(ds, event, UNDOCK_EVENT); | ||
| 563 | hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); | ||
| 564 | undock(ds); | ||
| 565 | eject_dock(ds); | ||
| 566 | if (dock_present(ds)) | ||
| 567 | printk(KERN_ERR PREFIX "Unable to undock!\n"); | ||
| 568 | } | ||
| 569 | break; | ||
| 570 | default: | ||
| 571 | printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | /** | ||
| 576 | * find_dock_devices - find devices on the dock station | ||
| 577 | * @handle: the handle of the device we are examining | ||
| 578 | * @lvl: unused | ||
| 579 | * @context: the dock station private data | ||
| 580 | * @rv: unused | ||
| 581 | * | ||
| 582 | * This function is called by acpi_walk_namespace. It will | ||
| 583 | * check to see if an object has an _EJD method. If it does, then it | ||
| 584 | * will see if it is dependent on the dock station. | ||
| 585 | */ | ||
| 586 | static acpi_status | ||
| 587 | find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
| 588 | { | ||
| 589 | acpi_status status; | ||
| 590 | acpi_handle tmp; | ||
| 591 | struct dock_station *ds = (struct dock_station *)context; | ||
| 592 | struct dock_dependent_device *dd; | ||
| 593 | |||
| 594 | status = acpi_bus_get_ejd(handle, &tmp); | ||
| 595 | if (ACPI_FAILURE(status)) | ||
| 596 | return AE_OK; | ||
| 597 | |||
| 598 | if (tmp == ds->handle) { | ||
| 599 | dd = alloc_dock_dependent_device(handle); | ||
| 600 | if (dd) | ||
| 601 | add_dock_dependent_device(ds, dd); | ||
| 602 | } | ||
| 603 | |||
| 604 | return AE_OK; | ||
| 605 | } | ||
| 606 | |||
| 607 | /** | ||
| 608 | * dock_add - add a new dock station | ||
| 609 | * @handle: the dock station handle | ||
| 610 | * | ||
| 611 | * allocated and initialize a new dock station device. Find all devices | ||
| 612 | * that are on the dock station, and register for dock event notifications. | ||
| 613 | */ | ||
| 614 | static int dock_add(acpi_handle handle) | ||
| 615 | { | ||
| 616 | int ret; | ||
| 617 | acpi_status status; | ||
| 618 | struct dock_dependent_device *dd; | ||
| 619 | |||
| 620 | /* allocate & initialize the dock_station private data */ | ||
| 621 | dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); | ||
| 622 | if (!dock_station) | ||
| 623 | return -ENOMEM; | ||
| 624 | dock_station->handle = handle; | ||
| 625 | dock_station->last_dock_time = jiffies - HZ; | ||
| 626 | INIT_LIST_HEAD(&dock_station->dependent_devices); | ||
| 627 | INIT_LIST_HEAD(&dock_station->hotplug_devices); | ||
| 628 | spin_lock_init(&dock_station->dd_lock); | ||
| 629 | spin_lock_init(&dock_station->hp_lock); | ||
| 630 | |||
| 631 | /* Find dependent devices */ | ||
| 632 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
| 633 | ACPI_UINT32_MAX, find_dock_devices, dock_station, | ||
| 634 | NULL); | ||
| 635 | |||
| 636 | /* add the dock station as a device dependent on itself */ | ||
| 637 | dd = alloc_dock_dependent_device(handle); | ||
| 638 | if (!dd) { | ||
| 639 | kfree(dock_station); | ||
| 640 | return -ENOMEM; | ||
| 641 | } | ||
| 642 | add_dock_dependent_device(dock_station, dd); | ||
| 643 | |||
| 644 | /* register for dock events */ | ||
| 645 | status = acpi_install_notify_handler(dock_station->handle, | ||
| 646 | ACPI_SYSTEM_NOTIFY, | ||
| 647 | dock_notify, dock_station); | ||
| 648 | |||
| 649 | if (ACPI_FAILURE(status)) { | ||
| 650 | printk(KERN_ERR PREFIX "Error installing notify handler\n"); | ||
| 651 | ret = -ENODEV; | ||
| 652 | goto dock_add_err; | ||
| 653 | } | ||
| 654 | |||
| 655 | printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME); | ||
| 656 | |||
| 657 | return 0; | ||
| 658 | |||
| 659 | dock_add_err: | ||
| 660 | kfree(dock_station); | ||
| 661 | kfree(dd); | ||
| 662 | return ret; | ||
| 663 | } | ||
| 664 | |||
| 665 | /** | ||
| 666 | * dock_remove - free up resources related to the dock station | ||
| 667 | */ | ||
| 668 | static int dock_remove(void) | ||
| 669 | { | ||
| 670 | struct dock_dependent_device *dd, *tmp; | ||
| 671 | acpi_status status; | ||
| 672 | |||
| 673 | if (!dock_station) | ||
| 674 | return 0; | ||
| 675 | |||
| 676 | /* remove dependent devices */ | ||
| 677 | list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices, | ||
| 678 | list) | ||
| 679 | kfree(dd); | ||
| 680 | |||
| 681 | /* remove dock notify handler */ | ||
| 682 | status = acpi_remove_notify_handler(dock_station->handle, | ||
| 683 | ACPI_SYSTEM_NOTIFY, | ||
| 684 | dock_notify); | ||
| 685 | if (ACPI_FAILURE(status)) | ||
| 686 | printk(KERN_ERR "Error removing notify handler\n"); | ||
| 687 | |||
| 688 | /* free dock station memory */ | ||
| 689 | kfree(dock_station); | ||
| 690 | return 0; | ||
| 691 | } | ||
| 692 | |||
| 693 | /** | ||
| 694 | * find_dock - look for a dock station | ||
| 695 | * @handle: acpi handle of a device | ||
| 696 | * @lvl: unused | ||
| 697 | * @context: counter of dock stations found | ||
| 698 | * @rv: unused | ||
| 699 | * | ||
| 700 | * This is called by acpi_walk_namespace to look for dock stations. | ||
| 701 | */ | ||
| 702 | static acpi_status | ||
| 703 | find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
| 704 | { | ||
| 705 | int *count = (int *)context; | ||
| 706 | acpi_status status = AE_OK; | ||
| 707 | |||
| 708 | if (is_dock(handle)) { | ||
| 709 | if (dock_add(handle) >= 0) { | ||
| 710 | (*count)++; | ||
| 711 | status = AE_CTRL_TERMINATE; | ||
| 712 | } | ||
| 713 | } | ||
| 714 | return status; | ||
| 715 | } | ||
| 716 | |||
| 717 | static int __init dock_init(void) | ||
| 718 | { | ||
| 719 | int num = 0; | ||
| 720 | |||
| 721 | dock_station = NULL; | ||
| 722 | |||
| 723 | /* look for a dock station */ | ||
| 724 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
| 725 | ACPI_UINT32_MAX, find_dock, &num, NULL); | ||
| 726 | |||
| 727 | if (!num) | ||
| 728 | return -ENODEV; | ||
| 729 | |||
| 730 | return 0; | ||
| 731 | } | ||
| 732 | |||
| 733 | static void __exit dock_exit(void) | ||
| 734 | { | ||
| 735 | dock_remove(); | ||
| 736 | } | ||
| 737 | |||
| 738 | postcore_initcall(dock_init); | ||
| 739 | module_exit(dock_exit); | ||
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index cac4fcdcfc8d..5fcb50c7b778 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
| @@ -663,6 +663,29 @@ static int acpi_bus_find_driver(struct acpi_device *device) | |||
| 663 | Device Enumeration | 663 | Device Enumeration |
| 664 | -------------------------------------------------------------------------- */ | 664 | -------------------------------------------------------------------------- */ |
| 665 | 665 | ||
| 666 | acpi_status | ||
| 667 | acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) | ||
| 668 | { | ||
| 669 | acpi_status status; | ||
| 670 | acpi_handle tmp; | ||
| 671 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
| 672 | union acpi_object *obj; | ||
| 673 | |||
| 674 | status = acpi_get_handle(handle, "_EJD", &tmp); | ||
| 675 | if (ACPI_FAILURE(status)) | ||
| 676 | return status; | ||
| 677 | |||
| 678 | status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer); | ||
| 679 | if (ACPI_SUCCESS(status)) { | ||
| 680 | obj = buffer.pointer; | ||
| 681 | status = acpi_get_handle(NULL, obj->string.pointer, ejd); | ||
| 682 | kfree(buffer.pointer); | ||
| 683 | } | ||
| 684 | return status; | ||
| 685 | } | ||
| 686 | EXPORT_SYMBOL_GPL(acpi_bus_get_ejd); | ||
| 687 | |||
| 688 | |||
| 666 | static int acpi_bus_get_flags(struct acpi_device *device) | 689 | static int acpi_bus_get_flags(struct acpi_device *device) |
| 667 | { | 690 | { |
| 668 | acpi_status status = AE_OK; | 691 | acpi_status status = AE_OK; |
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 222a1cc4aa28..d305d212ad6c 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig | |||
| @@ -77,7 +77,7 @@ config HOTPLUG_PCI_IBM | |||
| 77 | 77 | ||
| 78 | config HOTPLUG_PCI_ACPI | 78 | config HOTPLUG_PCI_ACPI |
| 79 | tristate "ACPI PCI Hotplug driver" | 79 | tristate "ACPI PCI Hotplug driver" |
| 80 | depends on ACPI && HOTPLUG_PCI | 80 | depends on ACPI_DOCK && HOTPLUG_PCI |
| 81 | help | 81 | help |
| 82 | Say Y here if you have a system that supports PCI Hotplug using | 82 | Say Y here if you have a system that supports PCI Hotplug using |
| 83 | ACPI. | 83 | ACPI. |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a2b3e390a503..f338e40bd544 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
| @@ -334,7 +334,7 @@ int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, | |||
| 334 | acpi_handle handle, int type); | 334 | acpi_handle handle, int type); |
| 335 | int acpi_bus_trim(struct acpi_device *start, int rmdevice); | 335 | int acpi_bus_trim(struct acpi_device *start, int rmdevice); |
| 336 | int acpi_bus_start(struct acpi_device *device); | 336 | int acpi_bus_start(struct acpi_device *device); |
| 337 | 337 | acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd); | |
| 338 | int acpi_match_ids(struct acpi_device *device, char *ids); | 338 | int acpi_match_ids(struct acpi_device *device, char *ids); |
| 339 | int acpi_create_dir(struct acpi_device *); | 339 | int acpi_create_dir(struct acpi_device *); |
| 340 | void acpi_remove_dir(struct acpi_device *); | 340 | void acpi_remove_dir(struct acpi_device *); |
diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index b425f9bb6d43..6a5bdcefec64 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h | |||
| @@ -110,4 +110,21 @@ int acpi_processor_set_thermal_limit(acpi_handle handle, int type); | |||
| 110 | 110 | ||
| 111 | extern int acpi_specific_hotkey_enabled; | 111 | extern int acpi_specific_hotkey_enabled; |
| 112 | 112 | ||
| 113 | /*-------------------------------------------------------------------------- | ||
| 114 | Dock Station | ||
| 115 | -------------------------------------------------------------------------- */ | ||
| 116 | #if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE) | ||
| 117 | extern int is_dock_device(acpi_handle handle); | ||
| 118 | extern int register_dock_notifier(struct notifier_block *nb); | ||
| 119 | extern void unregister_dock_notifier(struct notifier_block *nb); | ||
| 120 | extern int register_hotplug_dock_device(acpi_handle handle, | ||
| 121 | acpi_notify_handler handler, void *context); | ||
| 122 | extern void unregister_hotplug_dock_device(acpi_handle handle); | ||
| 123 | #else | ||
| 124 | #define is_dock_device(h) (0) | ||
| 125 | #define register_dock_notifier(nb) (-ENODEV) | ||
| 126 | #define unregister_dock_notifier(nb) do { } while(0) | ||
| 127 | #define register_hotplug_dock_device(h1, h2, c) (-ENODEV) | ||
| 128 | #define unregister_hotplug_dock_device(h) do { } while(0) | ||
| 129 | #endif | ||
| 113 | #endif /*__ACPI_DRIVERS_H__*/ | 130 | #endif /*__ACPI_DRIVERS_H__*/ |
