/*
* dock.c - ACPI dock station driver
*
* Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <linux/stddef.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#define PREFIX "ACPI: "
#define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver"
ACPI_MODULE_NAME("dock");
MODULE_AUTHOR("Kristen Carlson Accardi");
MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION);
MODULE_LICENSE("GPL");
static int immediate_undock = 1;
module_param(immediate_undock, bool, 0644);
MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
"undock immediately when the undock button is pressed, 0 will cause"
" the driver to wait for userspace to write the undock sysfs file "
" before undocking");
static struct atomic_notifier_head dock_notifier_list;
static const struct acpi_device_id dock_device_ids[] = {
{"LNXDOCK", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, dock_device_ids);
struct dock_station {
acpi_handle handle;
unsigned long last_dock_time;
u32 flags;
spinlock_t dd_lock;
struct mutex hp_lock;
struct list_head dependent_devices;
struct list_head hotplug_devices;
struct list_head sibling;
struct platform_device *dock_device;
};
static LIST_HEAD(dock_stations);
static int dock_station_count;
struct dock_dependent_device {
struct list_head list;
struct list_head hotplug_list;
acpi_handle handle;
const struct acpi_dock_ops *ops;
void *context;
};
#define DOCK_DOCKING 0x00000001
#define DOCK_UNDOCKING 0x00000002
#define DOCK_IS_DOCK 0x00000010
#define DOCK_IS_ATA 0x00000020
#define DOCK_IS_BAT 0x00000040
#define DOCK_EVENT 3
#define UNDOCK_EVENT 2
/*****************************************************************************
* Dock Dependent device functions *
*****************************************************************************/
/**
* add_dock_dependent_device - associate a device with the dock station
* @ds: The dock station
* @handle: handle of the dependent device
*
* Add the dependent device to the dock's dependent device list.
*/
static int
add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
{
struct dock_dependent_device *dd;
dd = kzalloc(sizeof(*dd), GFP_KERNEL);
if (!dd)
return -ENOMEM;
dd->handle = handle;
INIT_LIST_HEAD(&dd->list);
INIT_LIST_HEAD(&dd->hotplug_list);
spin_lock(&ds->dd_lock);
list_add_tail(&dd->list, &ds->dependent_devices);
spin_unlock(&ds->dd_lock);
return 0;
}
/**
* dock_add_hotplug_device - associate a hotplug handler with the dock station
* @ds: The dock station
* @dd: The dependent device struct
*
* Add the dependent device to the dock's hotplug device list
*/
static void
dock_add_hotplug_device(struct dock_station *ds,
struct dock_dependent_device *dd)
{
mutex_lock(&ds->hp_lock);
list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
mutex_unlock(&ds->hp_lock);
}
/**
* dock_del_hotplug_device - remove a hotplug handler from the dock station
* @ds: The dock station
* @dd: the dependent device struct
*
* Delete the dependent device from the dock's hotplug device list
*/
static void
dock_del_hotplug_device(struct dock_station *ds,
struct dock_dependent_device *dd)
{
mutex_lock(&ds->hp_lock);
list_del(&dd->hotplug_list);
mutex_unlock(&ds->hp_lock);
}
/**
* find_dock_dependent_device - get a device dependent on this dock
* @ds: the dock station
* @handle: the acpi_handle of the device we want
*
* iterate over the dependent device list for this dock. If the
* dependent device matches the handle, return.
*/
static struct dock_dependent_device *
find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
{
struct dock_dependent_device *dd;
spin_lock(&ds->dd_lock);
list_for_each_entry(dd, &ds->dependent_devices, list) {
if (handle == dd->handle) {
spin_unlock(&ds->dd_lock);
return dd;
}
}
spin_unlock(&ds->dd_lock);
return NULL;
}
/*****************************************************************************
* Dock functions *
*****************************************************************************/
/**