2006-04-15 12:31:34 +00:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 2005-2006 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* $FreeBSD$
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "opt_acpi.h"
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/bus.h>
|
2006-06-22 06:34:05 +00:00
|
|
|
#include <sys/kernel.h>
|
2006-04-15 12:31:34 +00:00
|
|
|
#include <sys/module.h>
|
|
|
|
|
2009-06-05 18:44:36 +00:00
|
|
|
#include <contrib/dev/acpica/include/acpi.h>
|
|
|
|
#include <contrib/dev/acpica/include/accommon.h>
|
|
|
|
|
2006-04-15 12:31:34 +00:00
|
|
|
#include <dev/acpica/acpivar.h>
|
|
|
|
#include <dev/acpica/acpiio.h>
|
|
|
|
|
|
|
|
/* Hooks for the ACPI CA debugging infrastructure */
|
|
|
|
#define _COMPONENT ACPI_DOCK
|
|
|
|
ACPI_MODULE_NAME("DOCK")
|
|
|
|
|
|
|
|
/* For Docking status */
|
|
|
|
#define ACPI_DOCK_STATUS_UNKNOWN -1
|
|
|
|
#define ACPI_DOCK_STATUS_UNDOCKED 0
|
|
|
|
#define ACPI_DOCK_STATUS_DOCKED 1
|
|
|
|
|
2006-08-08 01:33:03 +00:00
|
|
|
#define ACPI_DOCK_UNLOCK 0 /* Allow device to be ejected */
|
|
|
|
#define ACPI_DOCK_LOCK 1 /* Prevent dev from being removed */
|
|
|
|
|
|
|
|
#define ACPI_DOCK_ISOLATE 0 /* Isolate from dock connector */
|
|
|
|
#define ACPI_DOCK_CONNECT 1 /* Connect to dock */
|
2006-06-22 06:34:05 +00:00
|
|
|
|
2006-04-15 12:31:34 +00:00
|
|
|
struct acpi_dock_softc {
|
|
|
|
int _sta;
|
|
|
|
int _bdn;
|
|
|
|
int _uid;
|
|
|
|
int status;
|
|
|
|
struct sysctl_ctx_list *sysctl_ctx;
|
|
|
|
struct sysctl_oid *sysctl_tree;
|
|
|
|
};
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
ACPI_SERIAL_DECL(dock, "ACPI Docking Station");
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
/*
|
2006-06-22 06:34:05 +00:00
|
|
|
* Utility functions
|
2006-04-15 12:31:34 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
acpi_dock_get_info(device_t dev)
|
|
|
|
{
|
|
|
|
struct acpi_dock_softc *sc;
|
|
|
|
ACPI_HANDLE h;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
h = acpi_get_handle(dev);
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
if (ACPI_FAILURE(acpi_GetInteger(h, "_STA", &sc->_sta)))
|
2006-04-15 12:31:34 +00:00
|
|
|
sc->_sta = ACPI_DOCK_STATUS_UNKNOWN;
|
2006-06-22 06:34:05 +00:00
|
|
|
if (ACPI_FAILURE(acpi_GetInteger(h, "_BDN", &sc->_bdn)))
|
2006-04-15 12:31:34 +00:00
|
|
|
sc->_bdn = ACPI_DOCK_STATUS_UNKNOWN;
|
2006-06-22 06:34:05 +00:00
|
|
|
if (ACPI_FAILURE(acpi_GetInteger(h, "_UID", &sc->_uid)))
|
2006-04-15 12:31:34 +00:00
|
|
|
sc->_uid = ACPI_DOCK_STATUS_UNKNOWN;
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
2006-06-22 06:34:05 +00:00
|
|
|
"_STA: %04x, _BDN: %04x, _UID: %04x\n", sc->_sta,
|
|
|
|
sc->_bdn, sc->_uid);
|
2006-04-15 12:31:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
acpi_dock_execute_dck(device_t dev, int dock)
|
|
|
|
{
|
|
|
|
ACPI_HANDLE h;
|
|
|
|
ACPI_OBJECT argobj;
|
|
|
|
ACPI_OBJECT_LIST args;
|
|
|
|
ACPI_BUFFER buf;
|
|
|
|
ACPI_OBJECT retobj;
|
|
|
|
ACPI_STATUS status;
|
|
|
|
|
|
|
|
h = acpi_get_handle(dev);
|
|
|
|
|
|
|
|
argobj.Type = ACPI_TYPE_INTEGER;
|
|
|
|
argobj.Integer.Value = dock;
|
|
|
|
args.Count = 1;
|
|
|
|
args.Pointer = &argobj;
|
|
|
|
buf.Pointer = &retobj;
|
|
|
|
buf.Length = sizeof(retobj);
|
|
|
|
status = AcpiEvaluateObject(h, "_DCK", &args, &buf);
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
/*
|
|
|
|
* When _DCK is called with 0, OSPM will ignore the return value.
|
|
|
|
*/
|
2006-08-08 01:33:03 +00:00
|
|
|
if (dock == ACPI_DOCK_ISOLATE)
|
2006-04-15 12:31:34 +00:00
|
|
|
return (0);
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
/* If _DCK returned 1, the request succeeded. */
|
|
|
|
if (ACPI_SUCCESS(status) && retobj.Type == ACPI_TYPE_INTEGER &&
|
|
|
|
retobj.Integer.Value == 1)
|
|
|
|
return (0);
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2006-08-08 01:33:03 +00:00
|
|
|
/* Lock devices while docked to prevent surprise removal. */
|
2006-04-15 12:31:34 +00:00
|
|
|
static void
|
|
|
|
acpi_dock_execute_lck(device_t dev, int lock)
|
|
|
|
{
|
|
|
|
ACPI_HANDLE h;
|
|
|
|
|
|
|
|
h = acpi_get_handle(dev);
|
|
|
|
acpi_SetInteger(h, "_LCK", lock);
|
|
|
|
}
|
|
|
|
|
2006-08-08 01:33:03 +00:00
|
|
|
/* Eject a device (i.e., motorized). */
|
2006-04-15 12:31:34 +00:00
|
|
|
static int
|
|
|
|
acpi_dock_execute_ejx(device_t dev, int eject, int state)
|
|
|
|
{
|
|
|
|
ACPI_HANDLE h;
|
|
|
|
ACPI_STATUS status;
|
|
|
|
char ejx[5];
|
|
|
|
|
|
|
|
h = acpi_get_handle(dev);
|
|
|
|
snprintf(ejx, sizeof(ejx), "_EJ%d", state);
|
|
|
|
status = acpi_SetInteger(h, ejx, eject);
|
2006-06-22 06:34:05 +00:00
|
|
|
if (ACPI_SUCCESS(status))
|
2006-04-15 12:31:34 +00:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2006-08-08 01:33:03 +00:00
|
|
|
/* Find dependent devices. When their parent is removed, so are they. */
|
2006-04-15 12:31:34 +00:00
|
|
|
static int
|
|
|
|
acpi_dock_is_ejd_device(ACPI_HANDLE dock_handle, ACPI_HANDLE handle)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
ACPI_STATUS ret_status;
|
|
|
|
ACPI_BUFFER ejd_buffer;
|
|
|
|
ACPI_OBJECT *obj;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
ejd_buffer.Pointer = NULL;
|
|
|
|
ejd_buffer.Length = ACPI_ALLOCATE_BUFFER;
|
|
|
|
ret_status = AcpiEvaluateObject(handle, "_EJD", NULL, &ejd_buffer);
|
2006-06-22 06:34:05 +00:00
|
|
|
if (ACPI_FAILURE(ret_status))
|
2006-04-15 12:31:34 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
obj = (ACPI_OBJECT *)ejd_buffer.Pointer;
|
2006-06-22 06:34:05 +00:00
|
|
|
if (dock_handle == acpi_GetReference(NULL, obj))
|
|
|
|
ret = 1;
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (ejd_buffer.Pointer != NULL)
|
|
|
|
AcpiOsFree(ejd_buffer.Pointer);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2006-06-22 06:34:05 +00:00
|
|
|
* Docking functions
|
2006-04-15 12:31:34 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
acpi_dock_attach_later(void *context)
|
|
|
|
{
|
|
|
|
device_t dev;
|
|
|
|
|
|
|
|
dev = (device_t)context;
|
|
|
|
|
Make the newbus subsystem Giant free by adding the new newbus sxlock.
The newbus lock is responsible for protecting newbus internIal structures,
device states and devclass flags. It is necessary to hold it when all
such datas are accessed. For the other operations, softc locking should
ensure enough protection to avoid races.
Newbus lock is automatically held when virtual operations on the device
and bus are invoked when loading the driver or when the suspend/resume
take place. For other 'spourious' operations trying to access/modify
the newbus topology, newbus lock needs to be automatically acquired and
dropped.
For the moment Giant is also acquired in some key point (modules subsystem)
in order to avoid problems before the 8.0 release as module handlers could
make assumptions about it. This Giant locking should go just after
the release happens.
Please keep in mind that the public interface can be expanded in order
to provide more support, if there are really necessities at some point
and also some bugs could arise as long as the patch needs a bit of
further testing.
Bump __FreeBSD_version in order to reflect the newbus lock introduction.
Reviewed by: ed, hps, jhb, imp, mav, scottl
No answer by: ariff, thompsa, yongari
Tested by: pho,
G. Trematerra <giovanni dot trematerra at gmail dot com>,
Brandon Gooch <jamesbrandongooch at gmail dot com>
Sponsored by: Yahoo! Incorporated
Approved by: re (ksmith)
2009-08-02 14:28:40 +00:00
|
|
|
newbus_xlock();
|
2006-06-22 06:34:05 +00:00
|
|
|
if (!device_is_enabled(dev))
|
2006-04-15 12:31:34 +00:00
|
|
|
device_enable(dev);
|
|
|
|
|
|
|
|
device_probe_and_attach(dev);
|
Make the newbus subsystem Giant free by adding the new newbus sxlock.
The newbus lock is responsible for protecting newbus internIal structures,
device states and devclass flags. It is necessary to hold it when all
such datas are accessed. For the other operations, softc locking should
ensure enough protection to avoid races.
Newbus lock is automatically held when virtual operations on the device
and bus are invoked when loading the driver or when the suspend/resume
take place. For other 'spourious' operations trying to access/modify
the newbus topology, newbus lock needs to be automatically acquired and
dropped.
For the moment Giant is also acquired in some key point (modules subsystem)
in order to avoid problems before the 8.0 release as module handlers could
make assumptions about it. This Giant locking should go just after
the release happens.
Please keep in mind that the public interface can be expanded in order
to provide more support, if there are really necessities at some point
and also some bugs could arise as long as the patch needs a bit of
further testing.
Bump __FreeBSD_version in order to reflect the newbus lock introduction.
Reviewed by: ed, hps, jhb, imp, mav, scottl
No answer by: ariff, thompsa, yongari
Tested by: pho,
G. Trematerra <giovanni dot trematerra at gmail dot com>,
Brandon Gooch <jamesbrandongooch at gmail dot com>
Sponsored by: Yahoo! Incorporated
Approved by: re (ksmith)
2009-08-02 14:28:40 +00:00
|
|
|
newbus_xunlock();
|
2006-04-15 12:31:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ACPI_STATUS
|
2006-06-22 06:34:05 +00:00
|
|
|
acpi_dock_insert_child(ACPI_HANDLE handle, UINT32 level, void *context,
|
|
|
|
void **status)
|
2006-04-15 12:31:34 +00:00
|
|
|
{
|
|
|
|
device_t dock_dev, dev;
|
|
|
|
ACPI_HANDLE dock_handle;
|
|
|
|
|
|
|
|
dock_dev = (device_t)context;
|
|
|
|
dock_handle = acpi_get_handle(dock_dev);
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
if (!acpi_dock_is_ejd_device(dock_handle, handle))
|
2006-04-15 12:31:34 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
ACPI_VPRINT(dock_dev, acpi_device_get_parent_softc(dock_dev),
|
|
|
|
"inserting device for %s\n", acpi_name(handle));
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* If the system boot up w/o Docking, the devices under the dock
|
|
|
|
* still un-initialized, also control methods such as _INI, _STA
|
|
|
|
* are not executed.
|
|
|
|
* Normal devices are initialized at booting by calling
|
|
|
|
* AcpiInitializeObjects(), however the devices under the dock
|
|
|
|
* need to be initialized here on the scheme of ACPICA.
|
|
|
|
*/
|
|
|
|
ACPI_INIT_WALK_INFO Info;
|
|
|
|
|
|
|
|
AcpiNsWalkNamespace(ACPI_TYPE_ANY, handle,
|
|
|
|
100, TRUE, AcpiNsInitOneDevice, &Info, NULL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dev = acpi_get_device(handle);
|
|
|
|
if (dev == NULL) {
|
2006-06-22 06:34:05 +00:00
|
|
|
device_printf(dock_dev, "error: %s has no associated device\n",
|
2006-04-15 12:31:34 +00:00
|
|
|
acpi_name(handle));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2007-03-22 18:16:43 +00:00
|
|
|
AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_dock_attach_later, dev);
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
return (AE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
acpi_dock_insert_children(device_t dev)
|
|
|
|
{
|
2006-06-22 06:34:05 +00:00
|
|
|
ACPI_STATUS status;
|
2006-04-15 12:31:34 +00:00
|
|
|
ACPI_HANDLE sb_handle;
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
status = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle);
|
|
|
|
if (ACPI_SUCCESS(status)) {
|
2006-04-15 12:31:34 +00:00
|
|
|
AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle,
|
|
|
|
100, acpi_dock_insert_child, dev, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
acpi_dock_insert(device_t dev)
|
|
|
|
{
|
|
|
|
struct acpi_dock_softc *sc;
|
|
|
|
ACPI_HANDLE h;
|
|
|
|
|
|
|
|
ACPI_SERIAL_ASSERT(dock);
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
h = acpi_get_handle(dev);
|
|
|
|
|
2006-08-08 01:33:03 +00:00
|
|
|
if (sc->status == ACPI_DOCK_STATUS_UNDOCKED ||
|
|
|
|
sc->status == ACPI_DOCK_STATUS_UNKNOWN) {
|
2006-06-22 06:34:05 +00:00
|
|
|
acpi_dock_execute_lck(dev, ACPI_DOCK_LOCK);
|
2006-08-08 01:33:03 +00:00
|
|
|
if (acpi_dock_execute_dck(dev, ACPI_DOCK_CONNECT) != 0) {
|
2006-04-15 12:31:34 +00:00
|
|
|
device_printf(dev, "_DCK failed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
if (!cold)
|
2006-04-15 12:31:34 +00:00
|
|
|
acpi_dock_insert_children(dev);
|
2006-08-08 01:33:03 +00:00
|
|
|
sc->status = ACPI_DOCK_STATUS_DOCKED;
|
2006-04-15 12:31:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Undock
|
|
|
|
*/
|
|
|
|
|
|
|
|
static ACPI_STATUS
|
2006-06-22 06:34:05 +00:00
|
|
|
acpi_dock_eject_child(ACPI_HANDLE handle, UINT32 level, void *context,
|
|
|
|
void **status)
|
2006-04-15 12:31:34 +00:00
|
|
|
{
|
|
|
|
device_t dock_dev, dev;
|
|
|
|
ACPI_HANDLE dock_handle;
|
|
|
|
|
|
|
|
dock_dev = *(device_t *)context;
|
|
|
|
dock_handle = acpi_get_handle(dock_dev);
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
if (!acpi_dock_is_ejd_device(dock_handle, handle))
|
2006-04-15 12:31:34 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
ACPI_VPRINT(dock_dev, acpi_device_get_parent_softc(dock_dev),
|
2006-06-22 06:34:05 +00:00
|
|
|
"ejecting device for %s\n", acpi_name(handle));
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
dev = acpi_get_device(handle);
|
Make the newbus subsystem Giant free by adding the new newbus sxlock.
The newbus lock is responsible for protecting newbus internIal structures,
device states and devclass flags. It is necessary to hold it when all
such datas are accessed. For the other operations, softc locking should
ensure enough protection to avoid races.
Newbus lock is automatically held when virtual operations on the device
and bus are invoked when loading the driver or when the suspend/resume
take place. For other 'spourious' operations trying to access/modify
the newbus topology, newbus lock needs to be automatically acquired and
dropped.
For the moment Giant is also acquired in some key point (modules subsystem)
in order to avoid problems before the 8.0 release as module handlers could
make assumptions about it. This Giant locking should go just after
the release happens.
Please keep in mind that the public interface can be expanded in order
to provide more support, if there are really necessities at some point
and also some bugs could arise as long as the patch needs a bit of
further testing.
Bump __FreeBSD_version in order to reflect the newbus lock introduction.
Reviewed by: ed, hps, jhb, imp, mav, scottl
No answer by: ariff, thompsa, yongari
Tested by: pho,
G. Trematerra <giovanni dot trematerra at gmail dot com>,
Brandon Gooch <jamesbrandongooch at gmail dot com>
Sponsored by: Yahoo! Incorporated
Approved by: re (ksmith)
2009-08-02 14:28:40 +00:00
|
|
|
newbus_xlock();
|
2006-04-15 12:31:34 +00:00
|
|
|
if (dev != NULL && device_is_attached(dev)) {
|
|
|
|
device_detach(dev);
|
|
|
|
}
|
Make the newbus subsystem Giant free by adding the new newbus sxlock.
The newbus lock is responsible for protecting newbus internIal structures,
device states and devclass flags. It is necessary to hold it when all
such datas are accessed. For the other operations, softc locking should
ensure enough protection to avoid races.
Newbus lock is automatically held when virtual operations on the device
and bus are invoked when loading the driver or when the suspend/resume
take place. For other 'spourious' operations trying to access/modify
the newbus topology, newbus lock needs to be automatically acquired and
dropped.
For the moment Giant is also acquired in some key point (modules subsystem)
in order to avoid problems before the 8.0 release as module handlers could
make assumptions about it. This Giant locking should go just after
the release happens.
Please keep in mind that the public interface can be expanded in order
to provide more support, if there are really necessities at some point
and also some bugs could arise as long as the patch needs a bit of
further testing.
Bump __FreeBSD_version in order to reflect the newbus lock introduction.
Reviewed by: ed, hps, jhb, imp, mav, scottl
No answer by: ariff, thompsa, yongari
Tested by: pho,
G. Trematerra <giovanni dot trematerra at gmail dot com>,
Brandon Gooch <jamesbrandongooch at gmail dot com>
Sponsored by: Yahoo! Incorporated
Approved by: re (ksmith)
2009-08-02 14:28:40 +00:00
|
|
|
newbus_xunlock();
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
acpi_SetInteger(handle, "_EJ0", 0);
|
|
|
|
out:
|
|
|
|
return (AE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
acpi_dock_eject_children(device_t dev)
|
|
|
|
{
|
|
|
|
ACPI_HANDLE sb_handle;
|
2006-06-22 06:34:05 +00:00
|
|
|
ACPI_STATUS status;
|
2006-04-15 12:31:34 +00:00
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
status = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle);
|
|
|
|
if (ACPI_SUCCESS(status)) {
|
2006-04-15 12:31:34 +00:00
|
|
|
AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle,
|
|
|
|
100, acpi_dock_eject_child, &dev, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
acpi_dock_removal(device_t dev)
|
|
|
|
{
|
|
|
|
struct acpi_dock_softc *sc;
|
|
|
|
|
|
|
|
ACPI_SERIAL_ASSERT(dock);
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
2006-08-08 01:33:03 +00:00
|
|
|
if (sc->status == ACPI_DOCK_STATUS_DOCKED ||
|
|
|
|
sc->status == ACPI_DOCK_STATUS_UNKNOWN) {
|
2006-04-15 12:31:34 +00:00
|
|
|
acpi_dock_eject_children(dev);
|
2006-08-08 01:33:03 +00:00
|
|
|
if (acpi_dock_execute_dck(dev, ACPI_DOCK_ISOLATE) != 0)
|
2006-04-15 12:31:34 +00:00
|
|
|
return;
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
acpi_dock_execute_lck(dev, ACPI_DOCK_UNLOCK);
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
if (acpi_dock_execute_ejx(dev, 1, 0) != 0) {
|
|
|
|
device_printf(dev, "_EJ0 failed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-08-08 01:33:03 +00:00
|
|
|
sc->status = ACPI_DOCK_STATUS_UNDOCKED;
|
2006-04-15 12:31:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
acpi_dock_get_info(dev);
|
2006-06-22 06:34:05 +00:00
|
|
|
if (sc->_sta != 0)
|
|
|
|
device_printf(dev, "mechanical failure (%#x).\n", sc->_sta);
|
2006-04-15 12:31:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Device/Bus check
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
acpi_dock_device_check(device_t dev)
|
|
|
|
{
|
|
|
|
struct acpi_dock_softc *sc;
|
|
|
|
|
|
|
|
ACPI_SERIAL_ASSERT(dock);
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
acpi_dock_get_info(dev);
|
|
|
|
|
|
|
|
/*
|
2006-08-08 01:33:03 +00:00
|
|
|
* If the _STA method indicates 'present' and 'functioning', the
|
|
|
|
* system is docked. If _STA does not exist for this device, it
|
|
|
|
* is always present.
|
2006-04-15 12:31:34 +00:00
|
|
|
*/
|
2006-08-08 01:33:03 +00:00
|
|
|
if (sc->_sta == ACPI_DOCK_STATUS_UNKNOWN ||
|
|
|
|
ACPI_DEVICE_PRESENT(sc->_sta))
|
2006-04-15 12:31:34 +00:00
|
|
|
acpi_dock_insert(dev);
|
2006-08-08 01:33:03 +00:00
|
|
|
else if (sc->_sta == 0)
|
2006-04-15 12:31:34 +00:00
|
|
|
acpi_dock_removal(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Notify Handler
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
acpi_dock_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
|
|
|
|
{
|
|
|
|
device_t dev;
|
|
|
|
|
|
|
|
dev = (device_t) context;
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
|
|
"got notification %#x\n", notify);
|
|
|
|
|
|
|
|
ACPI_SERIAL_BEGIN(dock);
|
|
|
|
switch (notify) {
|
2007-05-31 08:49:51 +00:00
|
|
|
case ACPI_NOTIFY_BUS_CHECK:
|
|
|
|
case ACPI_NOTIFY_DEVICE_CHECK:
|
2006-04-15 12:31:34 +00:00
|
|
|
acpi_dock_device_check(dev);
|
|
|
|
break;
|
2007-05-31 08:49:51 +00:00
|
|
|
case ACPI_NOTIFY_EJECT_REQUEST:
|
2006-04-15 12:31:34 +00:00
|
|
|
acpi_dock_removal(dev);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
device_printf(dev, "unknown notify %#x\n", notify);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ACPI_SERIAL_END(dock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
acpi_dock_status_sysctl(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct acpi_dock_softc *sc;
|
|
|
|
device_t dev;
|
2006-08-08 01:33:03 +00:00
|
|
|
int status, err;
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
err = 0;
|
2006-08-08 01:33:03 +00:00
|
|
|
dev = (device_t)arg1;
|
|
|
|
|
2006-04-15 12:31:34 +00:00
|
|
|
sc = device_get_softc(dev);
|
2006-08-08 01:33:03 +00:00
|
|
|
status = sc->status;
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
ACPI_SERIAL_BEGIN(dock);
|
2006-08-08 01:33:03 +00:00
|
|
|
err = sysctl_handle_int(oidp, &status, 0, req);
|
|
|
|
if (err != 0 || req->newptr == NULL)
|
|
|
|
goto out;
|
2006-04-15 12:31:34 +00:00
|
|
|
|
|
|
|
if (status != ACPI_DOCK_STATUS_UNDOCKED &&
|
|
|
|
status != ACPI_DOCK_STATUS_DOCKED) {
|
|
|
|
err = EINVAL;
|
2006-08-08 01:33:03 +00:00
|
|
|
goto out;
|
2006-04-15 12:31:34 +00:00
|
|
|
}
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
if (status == sc->status)
|
2006-04-15 12:31:34 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
case ACPI_DOCK_STATUS_UNDOCKED:
|
|
|
|
acpi_dock_removal(dev);
|
|
|
|
break;
|
|
|
|
case ACPI_DOCK_STATUS_DOCKED:
|
|
|
|
acpi_dock_device_check(dev);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
err = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
ACPI_SERIAL_END(dock);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
acpi_dock_probe(device_t dev)
|
|
|
|
{
|
|
|
|
ACPI_HANDLE h, tmp;
|
|
|
|
|
|
|
|
h = acpi_get_handle(dev);
|
|
|
|
if (acpi_disabled("dock") ||
|
|
|
|
ACPI_FAILURE(AcpiGetHandle(h, "_DCK", &tmp)))
|
|
|
|
return (ENXIO);
|
|
|
|
|
2006-06-22 06:34:05 +00:00
|
|
|
device_set_desc(dev, "ACPI Docking Station");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX Somewhere else in the kernel panics on "sysctl kern" if we
|
|
|
|
* return a negative value here (reprobe ok).
|
|
|
|
*/
|
2006-04-15 12:31:34 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
acpi_dock_attach(device_t dev)
|
|
|
|
{
|
|
|
|
struct acpi_dock_softc *sc;
|
|
|
|
ACPI_HANDLE h;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
h = acpi_get_handle(dev);
|
2006-08-08 01:33:03 +00:00
|
|
|
if (sc == NULL || h == NULL)
|
2006-04-15 12:31:34 +00:00
|
|
|
return (ENXIO);
|
|
|
|
|
|
|
|
sc->status = ACPI_DOCK_STATUS_UNKNOWN;
|
|
|
|
|
|
|
|
AcpiEvaluateObject(h, "_INI", NULL, NULL);
|
|
|
|
|
|
|
|
ACPI_SERIAL_BEGIN(dock);
|
|
|
|
|
|
|
|
acpi_dock_device_check(dev);
|
|
|
|
|
2006-08-08 01:33:03 +00:00
|
|
|
/* Get the sysctl tree */
|
2006-04-15 12:31:34 +00:00
|
|
|
sc->sysctl_ctx = device_get_sysctl_ctx(dev);
|
|
|
|
sc->sysctl_tree = device_get_sysctl_tree(dev);
|
|
|
|
|
|
|
|
SYSCTL_ADD_INT(sc->sysctl_ctx,
|
|
|
|
SYSCTL_CHILDREN(sc->sysctl_tree),
|
|
|
|
OID_AUTO, "_sta", CTLFLAG_RD,
|
|
|
|
&sc->_sta, 0, "Dock _STA");
|
|
|
|
SYSCTL_ADD_INT(sc->sysctl_ctx,
|
|
|
|
SYSCTL_CHILDREN(sc->sysctl_tree),
|
|
|
|
OID_AUTO, "_bdn", CTLFLAG_RD,
|
|
|
|
&sc->_bdn, 0, "Dock _BDN");
|
|
|
|
SYSCTL_ADD_INT(sc->sysctl_ctx,
|
|
|
|
SYSCTL_CHILDREN(sc->sysctl_tree),
|
|
|
|
OID_AUTO, "_uid", CTLFLAG_RD,
|
|
|
|
&sc->_uid, 0, "Dock _UID");
|
|
|
|
SYSCTL_ADD_PROC(sc->sysctl_ctx,
|
|
|
|
SYSCTL_CHILDREN(sc->sysctl_tree),
|
|
|
|
OID_AUTO, "status",
|
|
|
|
CTLTYPE_INT|CTLFLAG_RW, dev, 0,
|
|
|
|
acpi_dock_status_sysctl, "I",
|
|
|
|
"Dock/Undock operation");
|
|
|
|
|
|
|
|
ACPI_SERIAL_END(dock);
|
|
|
|
|
|
|
|
AcpiInstallNotifyHandler(h, ACPI_ALL_NOTIFY,
|
|
|
|
acpi_dock_notify_handler, dev);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static device_method_t acpi_dock_methods[] = {
|
|
|
|
/* Device interface */
|
|
|
|
DEVMETHOD(device_probe, acpi_dock_probe),
|
|
|
|
DEVMETHOD(device_attach, acpi_dock_attach),
|
|
|
|
|
|
|
|
{0, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
static driver_t acpi_dock_driver = {
|
|
|
|
"acpi_dock",
|
|
|
|
acpi_dock_methods,
|
|
|
|
sizeof(struct acpi_dock_softc),
|
|
|
|
};
|
|
|
|
|
|
|
|
static devclass_t acpi_dock_devclass;
|
|
|
|
|
|
|
|
DRIVER_MODULE(acpi_dock, acpi, acpi_dock_driver, acpi_dock_devclass, 0, 0);
|
|
|
|
MODULE_DEPEND(acpi_dock, acpi, 1, 1, 1);
|
|
|
|
|