dock.c revision 38ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1b
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 43c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic struct atomic_notifier_head dock_notifier_list; 44671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardistatic struct platform_device dock_device; 45671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardistatic char dock_device_name[] = "dock"; 46c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 47c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstruct dock_station { 48c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_handle handle; 49c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown unsigned long last_dock_time; 50c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown u32 flags; 51c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown spinlock_t dd_lock; 528b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi struct mutex hp_lock; 53c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct list_head dependent_devices; 54c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct list_head hotplug_devices; 55c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}; 56c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 57c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstruct dock_dependent_device { 58c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct list_head list; 59c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct list_head hotplug_list; 60c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_handle handle; 61c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_notify_handler handler; 62c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown void *context; 63c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown}; 64c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 65c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown#define DOCK_DOCKING 0x00000001 665669021e40964303994a20633548732c6bb26636Kristen Carlson Accardi#define DOCK_EVENT 3 675669021e40964303994a20633548732c6bb26636Kristen Carlson Accardi#define UNDOCK_EVENT 2 68c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 69c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic struct dock_station *dock_station; 70c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 71c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/***************************************************************************** 72c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Dock Dependent device functions * 73c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *****************************************************************************/ 74c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 75c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * alloc_dock_dependent_device - allocate and init a dependent device 76c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the acpi_handle of the dependent device 77c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 78c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Allocate memory for a dependent device structure for a device referenced 79c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * by the acpi handle 80c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 81c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic struct dock_dependent_device * 82c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownalloc_dock_dependent_device(acpi_handle handle) 83c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 84c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd; 85c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 86c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dd = kzalloc(sizeof(*dd), GFP_KERNEL); 87c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (dd) { 88c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dd->handle = handle; 89c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown INIT_LIST_HEAD(&dd->list); 90c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown INIT_LIST_HEAD(&dd->hotplug_list); 91c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 92c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return dd; 93c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 94c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 95c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 96c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * add_dock_dependent_device - associate a device with the dock station 97c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: The dock station 98c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @dd: The dependent device 99c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 100c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Add the dependent device to the dock's dependent device list. 101c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 102c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void 103c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownadd_dock_dependent_device(struct dock_station *ds, 104c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd) 105c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 106c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown spin_lock(&ds->dd_lock); 107c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown list_add_tail(&dd->list, &ds->dependent_devices); 108c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown spin_unlock(&ds->dd_lock); 109c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 110c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 111c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 112c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_add_hotplug_device - associate a hotplug handler with the dock station 113c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: The dock station 114c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @dd: The dependent device struct 115c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 116c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Add the dependent device to the dock's hotplug device list 117c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 118c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void 119c8f7a62cdde461914c6457d5f4362538ed810bf4Len Browndock_add_hotplug_device(struct dock_station *ds, 120c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd) 121c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 1228b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi mutex_lock(&ds->hp_lock); 123c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown list_add_tail(&dd->hotplug_list, &ds->hotplug_devices); 1248b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi mutex_unlock(&ds->hp_lock); 125c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 126c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 127c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 128c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_del_hotplug_device - remove a hotplug handler from the dock station 129c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: The dock station 130c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @dd: the dependent device struct 131c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 132c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Delete the dependent device from the dock's hotplug device list 133c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 134c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void 135c8f7a62cdde461914c6457d5f4362538ed810bf4Len Browndock_del_hotplug_device(struct dock_station *ds, 136c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd) 137c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 1388b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi mutex_lock(&ds->hp_lock); 139c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown list_del(&dd->hotplug_list); 1408b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi mutex_unlock(&ds->hp_lock); 141c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 142c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 143c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 144c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * find_dock_dependent_device - get a device dependent on this dock 145c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station 146c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the acpi_handle of the device we want 147c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 148c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * iterate over the dependent device list for this dock. If the 149c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dependent device matches the handle, return. 150c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 151c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic struct dock_dependent_device * 152c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownfind_dock_dependent_device(struct dock_station *ds, acpi_handle handle) 153c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 154c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd; 155c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 156c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown spin_lock(&ds->dd_lock); 157c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown list_for_each_entry(dd, &ds->dependent_devices, list) { 158c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (handle == dd->handle) { 159c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown spin_unlock(&ds->dd_lock); 160c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return dd; 161c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 162c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 163c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown spin_unlock(&ds->dd_lock); 164c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return NULL; 165c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 166c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 167c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/***************************************************************************** 168c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Dock functions * 169c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown *****************************************************************************/ 170c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 171c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * is_dock - see if a device is a dock station 172c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: acpi handle of the device 173c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 174c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If an acpi object has a _DCK method, then it is by definition a dock 175c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * station, so return true. 176c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 177c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int is_dock(acpi_handle handle) 178c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 179c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_status status; 180c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_handle tmp; 181c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 182c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown status = acpi_get_handle(handle, "_DCK", &tmp); 183c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ACPI_FAILURE(status)) 184c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 185c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 1; 186c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 187c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 188c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 189c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * is_dock_device - see if a device is on a dock station 190c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: acpi handle of the device 191c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 192c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If this device is either the dock station itself, 193c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * or is a device dependent on the dock station, then it 194c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * is a dock device 195c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 196c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownint is_dock_device(acpi_handle handle) 197c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 198c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!dock_station) 199c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 200c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 201c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (is_dock(handle) || find_dock_dependent_device(dock_station, handle)) 202c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 1; 203c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 204c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 205c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 206c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 207c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(is_dock_device); 208c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 209c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 210c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_present - see if the dock station is present. 211c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station 212c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 213c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * execute the _STA method. note that present does not 214c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * imply that we are docked. 215c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 216c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int dock_present(struct dock_station *ds) 217c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 218c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown unsigned long sta; 219c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_status status; 220c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 221c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ds) { 222c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta); 223c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ACPI_SUCCESS(status) && sta) 224c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 1; 225c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 226c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 227c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 228c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 229c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 230c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 231c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 232c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_create_acpi_device - add new devices to acpi 233c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle - handle of the device to add 234c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 235c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * This function will create a new acpi_device for the given 236c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * handle if one does not exist already. This should cause 237c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * acpi to scan for drivers for the given devices, and call 238c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * matching driver's add routine. 239c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 240c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Returns a pointer to the acpi_device corresponding to the handle. 241c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 242c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic struct acpi_device * dock_create_acpi_device(acpi_handle handle) 243c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 244c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct acpi_device *device = NULL; 245c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct acpi_device *parent_device; 246c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_handle parent; 247c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown int ret; 248c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 249c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (acpi_bus_get_device(handle, &device)) { 250c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* 251c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * no device created for this object, 252c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * so we should create one. 253c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 254c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_get_parent(handle, &parent); 255c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (acpi_bus_get_device(parent, &parent_device)) 256c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown parent_device = NULL; 257c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 258c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ret = acpi_bus_add(&device, parent_device, handle, 259c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ACPI_BUS_TYPE_DEVICE); 260c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ret) { 261c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown pr_debug("error adding bus, %x\n", 262c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown -ret); 263c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return NULL; 264c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 265c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 266c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return device; 267c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 268c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 269c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 270c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_remove_acpi_device - remove the acpi_device struct from acpi 271c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle - the handle of the device to remove 272c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 273c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Tell acpi to remove the acpi_device. This should cause any loaded 274c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * driver to have it's remove routine called. 275c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 276c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void dock_remove_acpi_device(acpi_handle handle) 277c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 278c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct acpi_device *device; 279c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown int ret; 280c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 281c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!acpi_bus_get_device(handle, &device)) { 282c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ret = acpi_bus_trim(device, 1); 283c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ret) 284c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown pr_debug("error removing bus, %x\n", -ret); 285c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 286c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 287c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 288c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 289c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 290c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * hotplug_dock_devices - insert or remove devices on the dock station 291c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station 292c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @event: either bus check or eject request 293c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 294c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Some devices on the dock station need to have drivers called 295c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * to perform hotplug operations after a dock event has occurred. 296c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Traverse the list of dock devices that have registered a 297c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * hotplug handler, and call the handler. 298c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 299c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void hotplug_dock_devices(struct dock_station *ds, u32 event) 300c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 301c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd; 302c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 3038b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi mutex_lock(&ds->hp_lock); 304c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 305c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* 306c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * First call driver specific hotplug functions 307c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 308c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) { 309c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (dd->handler) 310c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dd->handler(dd->handle, event, dd->context); 311c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 312c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 313c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* 314c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Now make sure that an acpi_device is created for each 315c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dependent device, or removed if this is an eject request. 316c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * This will cause acpi_drivers to be stopped/started if they 317c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * exist 318c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 319c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown list_for_each_entry(dd, &ds->dependent_devices, list) { 320c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (event == ACPI_NOTIFY_EJECT_REQUEST) 321c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_remove_acpi_device(dd->handle); 322c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown else 323c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_create_acpi_device(dd->handle); 324c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 3258b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi mutex_unlock(&ds->hp_lock); 326c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 327c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 328c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void dock_event(struct dock_station *ds, u32 event, int num) 329c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 3308ea86e0ba7c9d16ae0f35cb0c4165194fa573f7aKristen Carlson Accardi struct device *dev = &dock_device.dev; 3315669021e40964303994a20633548732c6bb26636Kristen Carlson Accardi /* 3328ea86e0ba7c9d16ae0f35cb0c4165194fa573f7aKristen Carlson Accardi * Indicate that the status of the dock station has 3338ea86e0ba7c9d16ae0f35cb0c4165194fa573f7aKristen Carlson Accardi * changed. 3345669021e40964303994a20633548732c6bb26636Kristen Carlson Accardi */ 3358ea86e0ba7c9d16ae0f35cb0c4165194fa573f7aKristen Carlson Accardi kobject_uevent(&dev->kobj, KOBJ_CHANGE); 336c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 337c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 338c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 339c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * eject_dock - respond to a dock eject request 340c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station 341c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 342c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * This is called after _DCK is called, to execute the dock station's 343c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * _EJ0 method. 344c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 345c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void eject_dock(struct dock_station *ds) 346c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 347c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct acpi_object_list arg_list; 348c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown union acpi_object arg; 349c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_status status; 350c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_handle tmp; 351c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 352c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* all dock devices should have _EJ0, but check anyway */ 353c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown status = acpi_get_handle(ds->handle, "_EJ0", &tmp); 354c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ACPI_FAILURE(status)) { 355c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown pr_debug("No _EJ0 support for dock device\n"); 356c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return; 357c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 358c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 359c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown arg_list.count = 1; 360c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown arg_list.pointer = &arg; 361c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown arg.type = ACPI_TYPE_INTEGER; 362c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown arg.integer.value = 1; 363c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 364c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0", 365c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown &arg_list, NULL))) 366c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown pr_debug("Failed to evaluate _EJ0!\n"); 367c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 368c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 369c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 370c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * handle_dock - handle a dock event 371c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station 372c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @dock: to dock, or undock - that is the question 373c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 374c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Execute the _DCK method in response to an acpi event 375c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 376c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void handle_dock(struct dock_station *ds, int dock) 377c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 378c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_status status; 379c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct acpi_object_list arg_list; 380c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown union acpi_object arg; 381c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 382c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 383c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown union acpi_object *obj; 384c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 385c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer); 386c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown obj = name_buffer.pointer; 387c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 388c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking"); 389c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 390c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* _DCK method has one argument */ 391c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown arg_list.count = 1; 392c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown arg_list.pointer = &arg; 393c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown arg.type = ACPI_TYPE_INTEGER; 394c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown arg.integer.value = dock; 395c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer); 396c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ACPI_FAILURE(status)) 397c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown pr_debug("%s: failed to execute _DCK\n", obj->string.pointer); 398c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown kfree(buffer.pointer); 399c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown kfree(name_buffer.pointer); 400c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 401c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 402c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic inline void dock(struct dock_station *ds) 403c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 404c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown handle_dock(ds, 1); 405c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 406c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 407c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic inline void undock(struct dock_station *ds) 408c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 409c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown handle_dock(ds, 0); 410c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 411c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 412c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic inline void begin_dock(struct dock_station *ds) 413c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 414c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ds->flags |= DOCK_DOCKING; 415c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 416c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 417c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic inline void complete_dock(struct dock_station *ds) 418c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 419c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ds->flags &= ~(DOCK_DOCKING); 420c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ds->last_dock_time = jiffies; 421c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 422c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 423c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 424c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_in_progress - see if we are in the middle of handling a dock event 425c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @ds: the dock station 426c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 427c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * Sometimes while docking, false dock events can be sent to the driver 428c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * because good connections aren't made or some other reason. Ignore these 429c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * if we are in the middle of doing something. 430c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 431c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int dock_in_progress(struct dock_station *ds) 432c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 433c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if ((ds->flags & DOCK_DOCKING) || 434c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown time_before(jiffies, (ds->last_dock_time + HZ))) 435c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 1; 436c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 437c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 438c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 439c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 440c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * register_dock_notifier - add yourself to the dock notifier list 441c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @nb: the callers notifier block 442c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 443c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If a driver wishes to be notified about dock events, they can 444c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * use this function to put a notifier block on the dock notifier list. 445c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * this notifier call chain will be called after a dock event, but 446c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * before hotplugging any new devices. 447c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 448c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownint register_dock_notifier(struct notifier_block *nb) 449c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 4502548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava if (!dock_station) 4512548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava return -ENODEV; 4522548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava 453c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return atomic_notifier_chain_register(&dock_notifier_list, nb); 454c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 455c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 456c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(register_dock_notifier); 457c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 458c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 459c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * unregister_dock_notifier - remove yourself from the dock notifier list 460c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @nb: the callers notifier block 461c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 462c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownvoid unregister_dock_notifier(struct notifier_block *nb) 463c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 4642548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava if (!dock_station) 4652548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava return; 4662548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava 467c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown atomic_notifier_chain_unregister(&dock_notifier_list, nb); 468c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 469c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 470c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(unregister_dock_notifier); 471c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 472c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 473c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * register_hotplug_dock_device - register a hotplug function 474c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the handle of the device 475c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handler: the acpi_notifier_handler to call after docking 476c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @context: device specific data 477c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 478c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If a driver would like to perform a hotplug operation after a dock 479c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * event, they can register an acpi_notifiy_handler to be called by 480c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * the dock driver after _DCK is executed. 481c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 482c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownint 483c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownregister_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler, 484c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown void *context) 485c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 486c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd; 487c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 488c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!dock_station) 489c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return -ENODEV; 490c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 491c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* 492c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * make sure this handle is for a device dependent on the dock, 493c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * this would include the dock station itself 494c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 495c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dd = find_dock_dependent_device(dock_station, handle); 496c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (dd) { 497c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dd->handler = handler; 498c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dd->context = context; 499c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_add_hotplug_device(dock_station, dd); 500c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 501c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 502c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 503c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return -EINVAL; 504c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 505c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 506c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(register_hotplug_dock_device); 507c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 508c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 509c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * unregister_hotplug_dock_device - remove yourself from the hotplug list 510c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the acpi handle of the device 511c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 512c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownvoid unregister_hotplug_dock_device(acpi_handle handle) 513c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 514c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd; 515c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 516c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!dock_station) 517c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return; 518c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 519c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dd = find_dock_dependent_device(dock_station, handle); 520c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (dd) 521c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_del_hotplug_device(dock_station, dd); 522c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 523c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 524c8f7a62cdde461914c6457d5f4362538ed810bf4Len BrownEXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); 525c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 526c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 527c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * handle_eject_request - handle an undock request checking for error conditions 528c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * 529c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * Check to make sure the dock device is still present, then undock and 530c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * hotremove all the devices that may need removing. 531c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org */ 532c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.orgstatic int handle_eject_request(struct dock_station *ds, u32 event) 533c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org{ 534c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org if (!dock_present(ds)) 535c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org return -ENODEV; 536c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org 537c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org if (dock_in_progress(ds)) 538c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org return -EBUSY; 539c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org 540c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org /* 541c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * here we need to generate the undock 542c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * event prior to actually doing the undock 543c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * so that the device struct still exists. 544c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org */ 545c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org dock_event(ds, event, UNDOCK_EVENT); 546c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); 547c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org undock(ds); 548c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org eject_dock(ds); 549c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org if (dock_present(ds)) { 550c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org printk(KERN_ERR PREFIX "Unable to undock!\n"); 551c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org return -EBUSY; 552c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org } 553c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org 554c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org return 0; 555c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org} 556c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org 557c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org/** 558c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_notify - act upon an acpi dock notification 559c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the dock station handle 560c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @event: the acpi event 561c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @data: our driver data struct 562c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 563c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * If we are notified to dock, then check to see if the dock is 564c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * present and then dock. Notify all drivers of the dock event, 565c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * and then hotplug and devices that may need hotplugging. 566c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 567c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void dock_notify(acpi_handle handle, u32 event, void *data) 568c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 56950dd096973f1d95aa03c6a6d9e148d706b62b68eJan Engelhardt struct dock_station *ds = data; 570c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 571c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown switch (event) { 572c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown case ACPI_NOTIFY_BUS_CHECK: 573c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!dock_in_progress(ds) && dock_present(ds)) { 574c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown begin_dock(ds); 575c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock(ds); 576c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!dock_present(ds)) { 577c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown printk(KERN_ERR PREFIX "Unable to dock!\n"); 578c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown break; 579c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 580c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown atomic_notifier_call_chain(&dock_notifier_list, 581c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown event, NULL); 582c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown hotplug_dock_devices(ds, event); 583c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown complete_dock(ds); 584c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_event(ds, event, DOCK_EVENT); 585c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 586c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown break; 587c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown case ACPI_NOTIFY_DEVICE_CHECK: 588c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* 589c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * According to acpi spec 3.0a, if a DEVICE_CHECK notification 590c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * is sent and _DCK is present, it is assumed to mean an 591c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * undock request. This notify routine will only be called 592c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * for objects defining _DCK, so we will fall through to eject 593c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * request here. However, we will pass an eject request through 594c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * to the driver who wish to hotplug. 595c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 596c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown case ACPI_NOTIFY_EJECT_REQUEST: 597c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org handle_eject_request(ds, event); 598c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown break; 599c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown default: 600c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); 601c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 602c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 603c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 604c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 605c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * find_dock_devices - find devices on the dock station 606c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the handle of the device we are examining 607c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @lvl: unused 608c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @context: the dock station private data 609c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @rv: unused 610c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 611c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * This function is called by acpi_walk_namespace. It will 612c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * check to see if an object has an _EJD method. If it does, then it 613c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * will see if it is dependent on the dock station. 614c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 615c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic acpi_status 616c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownfind_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) 617c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 618c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_status status; 619fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi acpi_handle tmp, parent; 62050dd096973f1d95aa03c6a6d9e148d706b62b68eJan Engelhardt struct dock_station *ds = context; 621c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd; 622c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 623c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown status = acpi_bus_get_ejd(handle, &tmp); 624fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi if (ACPI_FAILURE(status)) { 625fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi /* try the parent device as well */ 626fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi status = acpi_get_parent(handle, &parent); 627fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi if (ACPI_FAILURE(status)) 628fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi goto fdd_out; 629fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi /* see if parent is dependent on dock */ 630fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi status = acpi_bus_get_ejd(parent, &tmp); 631fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi if (ACPI_FAILURE(status)) 632fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi goto fdd_out; 633fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardi } 634c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 635c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (tmp == ds->handle) { 636c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dd = alloc_dock_dependent_device(handle); 637c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (dd) 638c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown add_dock_dependent_device(ds, dd); 639c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 640fe9a2f77e5ad508b18671571c0b3f6f79ea709a8Kristen Carlson Accardifdd_out: 641c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return AE_OK; 642c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 643c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 644c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org/* 645c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * show_docked - read method for "docked" file in sysfs 646c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org */ 647c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.orgstatic ssize_t show_docked(struct device *dev, 648c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org struct device_attribute *attr, char *buf) 649c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org{ 650c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station)); 651c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org 652c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org} 653c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.orgDEVICE_ATTR(docked, S_IRUGO, show_docked, NULL); 654c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org 655c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org/* 656c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org * write_undock - write method for "undock" file in sysfs 657c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org */ 658c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.orgstatic ssize_t write_undock(struct device *dev, struct device_attribute *attr, 659c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org const char *buf, size_t count) 660c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org{ 661c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org int ret; 662c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org 663c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org if (!count) 664c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org return -EINVAL; 665c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org 666c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST); 667c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org return ret ? ret: count; 668c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org} 669c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.orgDEVICE_ATTR(undock, S_IWUSR, NULL, write_undock); 670c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org 671ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh/* 672ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh * show_dock_uid - read method for "uid" file in sysfs 673ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh */ 674ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakhstatic ssize_t show_dock_uid(struct device *dev, 675ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh struct device_attribute *attr, char *buf) 676ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh{ 677ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh unsigned long lbuf; 67838ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi acpi_status status = acpi_evaluate_integer(dock_station->handle, 67938ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi "_UID", NULL, &lbuf); 68038ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi if (ACPI_FAILURE(status)) 681ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh return 0; 68238ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi 683ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf); 684ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh} 685ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-EvenbakhDEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL); 686ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh 687c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 688c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_add - add a new dock station 689c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: the dock station handle 690c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 691c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * allocated and initialize a new dock station device. Find all devices 692c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * that are on the dock station, and register for dock event notifications. 693c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 694c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int dock_add(acpi_handle handle) 695c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 696c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown int ret; 697c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_status status; 698c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd; 699c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 700c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* allocate & initialize the dock_station private data */ 701c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); 702c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!dock_station) 703c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return -ENOMEM; 704c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_station->handle = handle; 705c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_station->last_dock_time = jiffies - HZ; 706c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown INIT_LIST_HEAD(&dock_station->dependent_devices); 707c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown INIT_LIST_HEAD(&dock_station->hotplug_devices); 708c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown spin_lock_init(&dock_station->dd_lock); 7098b0dc866dd9b8d10a53cb3537385a51b7ee54b62Kristen Carlson Accardi mutex_init(&dock_station->hp_lock); 71007a18684c92c0156f87ea158b5adc3022485f82aKristen Accardi ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); 711c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 712671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi /* initialize platform device stuff */ 713671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi dock_device.name = dock_device_name; 714671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi ret = platform_device_register(&dock_device); 715671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi if (ret) { 716e67beb37df7a9da9d5d1e59c5358654d007a97c5Len Brown printk(KERN_ERR PREFIX "Error %d registering dock device\n", ret); 717671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi kfree(dock_station); 718671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi return ret; 719671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi } 720c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org ret = device_create_file(&dock_device.dev, &dev_attr_docked); 721c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org if (ret) { 722c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org printk("Error %d adding sysfs file\n", ret); 723c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org platform_device_unregister(&dock_device); 724c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org kfree(dock_station); 725c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org return ret; 726c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org } 727c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org ret = device_create_file(&dock_device.dev, &dev_attr_undock); 728c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org if (ret) { 729c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org printk("Error %d adding sysfs file\n", ret); 730c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org device_remove_file(&dock_device.dev, &dev_attr_docked); 731c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org platform_device_unregister(&dock_device); 732c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org kfree(dock_station); 733c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org return ret; 734c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org } 735ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh ret = device_create_file(&dock_device.dev, &dev_attr_uid); 736ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh if (ret) { 737ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh printk("Error %d adding sysfs file\n", ret); 73838ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi device_remove_file(&dock_device.dev, &dev_attr_docked); 73938ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi device_remove_file(&dock_device.dev, &dev_attr_undock); 740ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh platform_device_unregister(&dock_device); 741ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh kfree(dock_station); 742ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh return ret; 743ac122bb64b0d51f0512185d3522a75f3f3a80bc9Ilya A. Volynets-Evenbakh } 744671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi 745c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* Find dependent devices */ 746c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 747c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ACPI_UINT32_MAX, find_dock_devices, dock_station, 748c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown NULL); 749c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 750c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* add the dock station as a device dependent on itself */ 751c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dd = alloc_dock_dependent_device(handle); 752c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!dd) { 753c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown kfree(dock_station); 754671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi ret = -ENOMEM; 755671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi goto dock_add_err_unregister; 756c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 757c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown add_dock_dependent_device(dock_station, dd); 758c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 759c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* register for dock events */ 760c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown status = acpi_install_notify_handler(dock_station->handle, 761c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ACPI_SYSTEM_NOTIFY, 762c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_notify, dock_station); 763c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 764c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ACPI_FAILURE(status)) { 765c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown printk(KERN_ERR PREFIX "Error installing notify handler\n"); 766c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ret = -ENODEV; 767c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown goto dock_add_err; 768c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 769c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 7707cda93e008e1a477970adbf82dba81a5d4f0ae40Len Brown printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_DESCRIPTION); 771c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 772c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 773c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 774c8f7a62cdde461914c6457d5f4362538ed810bf4Len Browndock_add_err: 775c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown kfree(dd); 776671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardidock_add_err_unregister: 777c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org device_remove_file(&dock_device.dev, &dev_attr_docked); 778c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org device_remove_file(&dock_device.dev, &dev_attr_undock); 77938ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi device_remove_file(&dock_device.dev, &dev_attr_uid); 780671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi platform_device_unregister(&dock_device); 781671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi kfree(dock_station); 782c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return ret; 783c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 784c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 785c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 786c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * dock_remove - free up resources related to the dock station 787c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 788c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int dock_remove(void) 789c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 790c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown struct dock_dependent_device *dd, *tmp; 791c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_status status; 792c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 793c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!dock_station) 794c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 795c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 796c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* remove dependent devices */ 797c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices, 798c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown list) 799c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown kfree(dd); 800c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 801c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* remove dock notify handler */ 802c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown status = acpi_remove_notify_handler(dock_station->handle, 803c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ACPI_SYSTEM_NOTIFY, 804c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_notify); 805c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (ACPI_FAILURE(status)) 806c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown printk(KERN_ERR "Error removing notify handler\n"); 807c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 808671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi /* cleanup sysfs */ 809c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org device_remove_file(&dock_device.dev, &dev_attr_docked); 810c80fdbe81a617c82e2f95233f8ddcf046ffe21b3brandon@ifup.org device_remove_file(&dock_device.dev, &dev_attr_undock); 81138ff4ffc039ba5a5878f2dcbb03d87c3a1f02f1bKristen Carlson Accardi device_remove_file(&dock_device.dev, &dev_attr_uid); 812671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi platform_device_unregister(&dock_device); 813671adbec210efc15cef81b4616adae8bcd667296Kristen Carlson Accardi 814c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* free dock station memory */ 815c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown kfree(dock_station); 816c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 817c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 818c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 819c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown/** 820c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * find_dock - look for a dock station 821c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @handle: acpi handle of a device 822c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @lvl: unused 823c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @context: counter of dock stations found 824c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * @rv: unused 825c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * 826c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown * This is called by acpi_walk_namespace to look for dock stations. 827c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown */ 828c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic acpi_status 829c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownfind_dock(acpi_handle handle, u32 lvl, void *context, void **rv) 830c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 83150dd096973f1d95aa03c6a6d9e148d706b62b68eJan Engelhardt int *count = context; 832c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_status status = AE_OK; 833c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 834c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (is_dock(handle)) { 835c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (dock_add(handle) >= 0) { 836c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown (*count)++; 837c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown status = AE_CTRL_TERMINATE; 838c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 839c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown } 840c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return status; 841c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 842c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 843c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic int __init dock_init(void) 844c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 845c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown int num = 0; 846c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 847c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_station = NULL; 848c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 849c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown /* look for a dock station */ 850c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 851c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown ACPI_UINT32_MAX, find_dock, &num, NULL); 852c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 853c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown if (!num) 8542548c06b72396e28abdb5dd572ab589c3c22f4b9Prarit Bhargava printk(KERN_INFO "No dock devices found.\n"); 855c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 856c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown return 0; 857c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 858c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 859c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownstatic void __exit dock_exit(void) 860c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown{ 861c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown dock_remove(); 862c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown} 863c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brown 864c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownpostcore_initcall(dock_init); 865c8f7a62cdde461914c6457d5f4362538ed810bf4Len Brownmodule_exit(dock_exit); 866