/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2010, Intel Corporation.
* All rights reserved.
*/
#include <sys/fastboot_impl.h>
#include <sys/sysevent.h>
#include <sys/acpidev_dr.h>
int acpinex_event_support_remove = 0;
/*
* Generate DR_REQ event to syseventd.
*/
static int
{
int rv = 0;
char *attach_pnt;
/* Add "attachment point" attribute. */
attach_pnt, MAXPATHLEN))) {
"!acpinex: failed to generate AP name for %s.", objname);
return (-1);
}
if (rv != 0) {
"!acpinex: failed to add attr [%s] for %s event.",
return (rv);
}
/* Add "request type" attribute. */
KM_SLEEP);
if (rv != 0) {
"!acpinex: failed to add attr [%s] for %s event.",
DR_REQ_TYPE, EC_DR);
return (rv);
}
/* Add "acpi-event-type" attribute. */
switch (event) {
case ACPI_NOTIFY_BUS_CHECK:
break;
case ACPI_NOTIFY_DEVICE_CHECK:
break;
break;
break;
default:
"!acpinex: unknown ACPI event type %d.", event);
return (-1);
}
if (rv != 0) {
"!acpinex: failed to add attr [%s] for %s event.",
return (rv);
}
if (rv != DDI_SUCCESS) {
"!acpinex: failed to log DR_REQ event for %s.", objname);
rv = -1;
}
return (rv);
}
/*
* Event handler for ACPI EJECT_REQUEST notifications.
* EJECT_REQUEST notifications should be generated on the device to be ejected,
* so no need to scan subtree of it.
* It also invokes ACPI _OST method to update event status if call_ost is true.
*/
static void
{
int code;
char *objname;
if (call_ost) {
ACPI_OST_STA_FAILURE, NULL, 0);
}
"!acpinex: softstate data structure is invalid.");
"!acpinex: failed to handle EJECT_REQUEST event from %s.",
objname);
return;
}
if (acpinex_event_support_remove == 0) {
"!acpinex: hot-removing of device %s is unsupported.",
objname);
"event for device eject request from %s.", objname);
} else {
"device eject request from %s.", objname);
}
if (call_ost) {
}
}
struct acpinex_event_check_arg {
int event_type;
};
static ACPI_STATUS
void **retval)
{
char *objname;
/* Skip subtree if failed to get the data handle. */
"!acpinex: failed to get data associated with %p.", hdl);
return (AE_CTRL_DEPTH);
} else if (!acpidev_data_dr_capable(dhdl)) {
return (AE_OK);
}
status = 0;
/* Query previous device status. */
if (acpidev_check_device_enabled(psta)) {
status |= 0x1;
}
/* Query current device status. */
if (acpidev_check_device_enabled(csta)) {
status |= 0x2;
}
switch (status) {
case 0x0:
/*FALLTHROUGH*/
case 0x3:
/* No status changes, keep on walking. */
return (AE_OK);
case 0x1:
/* Surprising removal. */
"!acpinex: device %s has been surprisingly removed.",
objname);
/*
* According to ACPI spec, BUS_CHECK notification
* should be triggered for hot-adding events only.
*/
"!acpinex: device %s has been surprisingly removed "
"when handling BUS_CHECK event.", objname);
}
argp->device_remove++;
return (AE_CTRL_DEPTH);
case 0x2:
/* Hot-adding. */
"!acpinex: device %s has been inserted.", objname);
argp->device_insert++;
"!acpinex: failed to generate ESC_DR_REQ event for "
"device insert request from %s.", objname);
argp->device_fail++;
} else {
"for device insert request from %s.", objname);
}
return (AE_OK);
default:
ASSERT(0);
break;
}
return (AE_ERROR);
}
/*
* Event handler for BUS_CHECK/DEVICE_CHECK/DEVICE_CHECK_LIGHT notifications.
* so need to scan ACPI namespace to figure out devices in question.
* It also invokes ACPI _OST method to update event status if call_ost is true.
*/
static void
{
int code;
char *objname;
if (call_ost) {
ACPI_OST_STA_FAILURE, NULL, 0);
}
"!acpinex: softstate data structure is invalid.");
"BUS/DEVICE_CHECK event from %s.", objname);
return;
}
if (ACPI_SUCCESS(rv)) {
}
if (ACPI_FAILURE(rv)) {
/* Failed to scan the ACPI namespace. */
} else if (arg.device_remove != 0) {
/* Surprising removal happened. */
"!acpinex: some devices have been surprisingly removed.");
} else if (arg.device_fail != 0) {
/* Failed to handle some devices. */
"!acpinex: failed to check status of some devices.");
} else if (arg.device_insert == 0) {
/* No hot-added devices found. */
"!acpinex: no hot-added devices under %s found.", objname);
} else {
}
if (call_ost) {
}
}
static void
{
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
/*
* Bus Check. This notification is performed on a device object
* to indicate to OSPM that it needs to perform the Plug and
* Play re-enumeration operation on the device tree starting
* from the point where it has been notified. OSPM will only
* perform this operation at boot, and when notified. It is
* the responsibility of the ACPI AML code to notify OSPM at
* any other times that this operation is required. The more
* accurately and closer to the actual device tree change the
* notification can be done, the more efficient the operating
* system response will be; however, it can also be an issue
* when a device change cannot be confirmed. For example, if
* the hardware cannot notice a device change for a particular
* location during a system sleeping state, it issues a Bus
* Check notification on wake to inform OSPM that it needs to
* check the configuration for a device change.
*/
/*FALLTHROUGH*/
case ACPI_NOTIFY_DEVICE_CHECK:
/*
* Device Check. Used to notify OSPM that the device either
* appeared or disappeared. If the device has appeared, OSPM
* will re-enumerate from the parent. If the device has
* disappeared, OSPM will invalidate the state of the device.
* OSPM may optimize out re-enumeration. If _DCK is present,
* then Notify(object,1) is assumed to indicate an undock
* request.
*/
/*FALLTHROUGH*/
/*
* Device Check Light. Used to notify OSPM that the device
* either appeared or disappeared. If the device has appeared,
* OSPM will re-enumerate from the device itself, not the
* parent. If the device has disappeared, OSPM will invalidate
* the state of the device.
*/
break;
/*
* Eject Request. Used to notify OSPM that the device should
* be ejected, and that OSPM needs to perform the Plug and Play
* ejection operation. OSPM will run the _EJx method.
*/
break;
default:
"!acpinex: unhandled event(%d) on hdl %p under %s.",
NULL, 0);
break;
}
if (acpinex_dr_event_cnt != 0) {
/*
* Note: this is a temporary solution and will be revised when
* future.
*
* ACPI BIOS generates some static ACPI tables, such as MADT,
* SRAT and SLIT, to describe the system hardware configuration
* static tables won't be updated and will become stale.
*
* If we reset the system by fast reboot, BIOS will have no
* chance to regenerate those staled static tables. Fast reboot
* can't tolerate such inconsistency between staled ACPI tables
* and real hardware configuration yet.
*
* A temporary solution is introduced to disable fast reboot if
* DR operations.
*/
}
}
/*
* Install event handler for ACPI system events.
* Acpinex driver handles ACPI system events for its children,
* device specific events will be handled by device drivers.
* Return DDI_SUCCESS on success, and DDI_FAILURE on failure.
*/
static int
{
/*
* Check whether the event handler has already been installed on the
* device object. With the introduction of ACPI Alias objects, which are
* similar to symlinks in file systems, there may be multiple name
* objects in the ACPI namespace pointing to the same underlying device
* object. Those Alias objects need to be filtered out, otherwise
* it will attempt to install the event handler multiple times on the
* same device object which will fail.
*/
return (DDI_SUCCESS);
}
} else {
char *objname;
"!acpinex: failed to install system event handler for %s.",
objname);
rc = DDI_FAILURE;
}
return (rc);
}
/*
* Uninstall event handler for ACPI system events.
* Return DDI_SUCCESS on success, and DDI_FAILURE on failure.
*/
static int
{
return (DDI_SUCCESS);
}
} else {
char *objname;
"handler for %s.", objname);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Return DDI_SUCCESS on success, and DDI_FAILURE on failure.
*/
static int
{
int rc;
/* Walk all child objects. */
&child))) {
/* Skip unwanted object types. */
continue;
}
/* Get data associated with the object. Skip it if fails. */
"associated with %p, skip.", child);
continue;
}
/* Query ACPI object info for the object. */
"!acpidnex: failed to get object info for %p.",
child);
continue;
}
if (init) {
dhdl);
if (rc != DDI_SUCCESS) {
"install handler for child %p of %s.",
/*
* Try to handle descendants if both of the
* following two conditions are true:
* 1) Device corresponding to the current object is
* no notification should be generated from
* descendant objects of it.
* 2) No Solaris device node has been created for the
* current object yet. If the device node has been
* created for the current object, notification
* events from child objects should be handled by
* the corresponding driver.
*/
} else if (acpidev_check_device_enabled(
if (rc != DDI_SUCCESS) {
"!acpinex: failed to install "
"handler for descendants of %s.",
}
}
} else {
rc = DDI_SUCCESS;
/* Uninstall handler for descendants if needed. */
}
if (rc == DDI_SUCCESS) {
}
/* Undo will be done by caller in case of failure. */
if (rc != DDI_SUCCESS) {
"uninstall handler for descendants of %s.",
break;
}
}
/* Release cached resources. */
}
return (retval);
}
int
{
int rc;
"!acpinex: invalid parameter to acpinex_event_scan().");
return (DDI_FAILURE);
}
/* Lock current device node and walk all child device nodes of it. */
if (rc != DDI_SUCCESS) {
if (init) {
rc = DDI_FAILURE;
} else {
/* Undo in case of errors */
rc = DDI_FAILURE;
}
}
return (rc);
}
void
acpinex_event_init(void)
{
/*
* According to ACPI specifications, notification is only supported on
* Device, Processor and ThermalZone. Currently we only need to handle
* Device and Processor objects.
*/
}
void
acpinex_event_fini(void)
{
}