Merge branch 'acpi-hotplug' into linux-next

* acpi-hotplug:
  ACPI / memhotplug: Remove info->failed bit
  ACPI / memhotplug: set info->enabled for memory present at boot time
  ACPI: Verify device status after eject
  acpi: remove reference to ACPI_HOTPLUG_IO
  ACPI: Update _OST handling for notify
  ACPI: Update PNPID match handling for notify
  ACPI: Update PNPID set/free interfaces
  ACPI: Remove acpi_device dependency in acpi_device_set_id()
  ACPI / hotplug: Make acpi_hotplug_profile_ktype static
  ACPI / scan: Make memory hotplug driver use struct acpi_scan_handler
  ACPI / container: Use hotplug profile user space interface
  ACPI / hotplug: Introduce user space interface for hotplug profiles
  ACPI / scan: Introduce acpi_scan_handler_matching()
  ACPI / container: Use common hotplug code
  ACPI / scan: Introduce common code for ACPI-based device hotplug
  ACPI / scan: Introduce acpi_scan_match_handler()
This commit is contained in:
Rafael J. Wysocki 2013-04-08 12:31:23 +02:00
commit 408fb7f37b
12 changed files with 601 additions and 593 deletions

View File

@ -18,6 +18,32 @@ Description:
yoffset: The number of pixels between the top of the screen yoffset: The number of pixels between the top of the screen
and the top edge of the image. and the top edge of the image.
What: /sys/firmware/acpi/hotplug/
Date: February 2013
Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Description:
There are separate hotplug profiles for different classes of
devices supported by ACPI, such as containers, memory modules,
processors, PCI root bridges etc. A hotplug profile for a given
class of devices is a collection of settings defining the way
that class of devices will be handled by the ACPI core hotplug
code. Those profiles are represented in sysfs as subdirectories
of /sys/firmware/acpi/hotplug/.
The following setting is available to user space for each
hotplug profile:
enabled: If set, the ACPI core will handle notifications of
hotplug events associated with the given class of
devices and will allow those devices to be ejected with
the help of the _EJ0 control method. Unsetting it
effectively disables hotplug for the correspoinding
class of devices.
The value of the above attribute is an integer number: 1 (set)
or 0 (unset). Attempts to write any other values to it will
cause -EINVAL to be returned.
What: /sys/firmware/acpi/interrupts/ What: /sys/firmware/acpi/interrupts/
Date: February 2008 Date: February 2008
Contact: Len Brown <lenb@kernel.org> Contact: Len Brown <lenb@kernel.org>

View File

@ -334,7 +334,7 @@ config X86_PM_TIMER
config ACPI_CONTAINER config ACPI_CONTAINER
bool "Container and Module Devices" bool "Container and Module Devices"
default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU || ACPI_HOTPLUG_IO) default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU)
help help
This driver supports ACPI Container and Module devices (IDs This driver supports ACPI Container and Module devices (IDs
ACPI0004, PNP0A05, and PNP0A06). ACPI0004, PNP0A05, and PNP0A06).
@ -345,9 +345,8 @@ config ACPI_CONTAINER
the module will be called container. the module will be called container.
config ACPI_HOTPLUG_MEMORY config ACPI_HOTPLUG_MEMORY
tristate "Memory Hotplug" bool "Memory Hotplug"
depends on MEMORY_HOTPLUG depends on MEMORY_HOTPLUG
default n
help help
This driver supports ACPI memory hotplug. The driver This driver supports ACPI memory hotplug. The driver
fields notifications on ACPI memory devices (PNP0C80), fields notifications on ACPI memory devices (PNP0C80),

View File

@ -1,5 +1,7 @@
/* /*
* Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com> * Copyright (C) 2004, 2013 Intel Corporation
* Author: Naveen B S <naveen.b.s@intel.com>
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
* *
* All rights reserved. * All rights reserved.
* *
@ -25,14 +27,10 @@
* ranges. * ranges.
*/ */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/memory_hotplug.h>
#include <linux/slab.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <acpi/acpi_drivers.h> #include <linux/memory_hotplug.h>
#include "internal.h"
#define ACPI_MEMORY_DEVICE_CLASS "memory" #define ACPI_MEMORY_DEVICE_CLASS "memory"
#define ACPI_MEMORY_DEVICE_HID "PNP0C80" #define ACPI_MEMORY_DEVICE_HID "PNP0C80"
@ -44,32 +42,28 @@
#define PREFIX "ACPI:memory_hp:" #define PREFIX "ACPI:memory_hp:"
ACPI_MODULE_NAME("acpi_memhotplug"); ACPI_MODULE_NAME("acpi_memhotplug");
MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
MODULE_DESCRIPTION("Hotplug Mem Driver");
MODULE_LICENSE("GPL");
/* Memory Device States */ /* Memory Device States */
#define MEMORY_INVALID_STATE 0 #define MEMORY_INVALID_STATE 0
#define MEMORY_POWER_ON_STATE 1 #define MEMORY_POWER_ON_STATE 1
#define MEMORY_POWER_OFF_STATE 2 #define MEMORY_POWER_OFF_STATE 2
static int acpi_memory_device_add(struct acpi_device *device); static int acpi_memory_device_add(struct acpi_device *device,
static int acpi_memory_device_remove(struct acpi_device *device); const struct acpi_device_id *not_used);
static void acpi_memory_device_remove(struct acpi_device *device);
static const struct acpi_device_id memory_device_ids[] = { static const struct acpi_device_id memory_device_ids[] = {
{ACPI_MEMORY_DEVICE_HID, 0}, {ACPI_MEMORY_DEVICE_HID, 0},
{"", 0}, {"", 0},
}; };
MODULE_DEVICE_TABLE(acpi, memory_device_ids);
static struct acpi_driver acpi_memory_device_driver = { static struct acpi_scan_handler memory_device_handler = {
.name = "acpi_memhotplug",
.class = ACPI_MEMORY_DEVICE_CLASS,
.ids = memory_device_ids, .ids = memory_device_ids,
.ops = { .attach = acpi_memory_device_add,
.add = acpi_memory_device_add, .detach = acpi_memory_device_remove,
.remove = acpi_memory_device_remove, .hotplug = {
}, .enabled = true,
},
}; };
struct acpi_memory_info { struct acpi_memory_info {
@ -79,7 +73,6 @@ struct acpi_memory_info {
unsigned short caching; /* memory cache attribute */ unsigned short caching; /* memory cache attribute */
unsigned short write_protect; /* memory read/write attribute */ unsigned short write_protect; /* memory read/write attribute */
unsigned int enabled:1; unsigned int enabled:1;
unsigned int failed:1;
}; };
struct acpi_memory_device { struct acpi_memory_device {
@ -153,48 +146,6 @@ acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
return 0; return 0;
} }
static int acpi_memory_get_device(acpi_handle handle,
struct acpi_memory_device **mem_device)
{
struct acpi_device *device = NULL;
int result = 0;
acpi_scan_lock_acquire();
acpi_bus_get_device(handle, &device);
if (device)
goto end;
/*
* Now add the notified device. This creates the acpi_device
* and invokes .add function
*/
result = acpi_bus_scan(handle);
if (result) {
acpi_handle_warn(handle, "ACPI namespace scan failed\n");
result = -EINVAL;
goto out;
}
result = acpi_bus_get_device(handle, &device);
if (result) {
acpi_handle_warn(handle, "Missing device object\n");
result = -EINVAL;
goto out;
}
end:
*mem_device = acpi_driver_data(device);
if (!(*mem_device)) {
dev_err(&device->dev, "driver data not found\n");
result = -ENODEV;
goto out;
}
out:
acpi_scan_lock_release();
return result;
}
static int acpi_memory_check_device(struct acpi_memory_device *mem_device) static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
{ {
unsigned long long current_status; unsigned long long current_status;
@ -249,13 +200,11 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
* returns -EEXIST. If add_memory() returns the other error, it * returns -EEXIST. If add_memory() returns the other error, it
* means that this memory block is not used by the kernel. * means that this memory block is not used by the kernel.
*/ */
if (result && result != -EEXIST) { if (result && result != -EEXIST)
info->failed = 1;
continue; continue;
}
if (!result) info->enabled = 1;
info->enabled = 1;
/* /*
* Add num_enable even if add_memory() returns -EEXIST, so the * Add num_enable even if add_memory() returns -EEXIST, so the
* device is bound to this driver. * device is bound to this driver.
@ -286,16 +235,8 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
nid = acpi_get_node(mem_device->device->handle); nid = acpi_get_node(mem_device->device->handle);
list_for_each_entry_safe(info, n, &mem_device->res_list, list) { list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
if (info->failed)
/* The kernel does not use this memory block */
continue;
if (!info->enabled) if (!info->enabled)
/* continue;
* The kernel uses this memory block, but it may be not
* managed by us.
*/
return -EBUSY;
if (nid < 0) if (nid < 0)
nid = memory_add_physaddr_to_nid(info->start_addr); nid = memory_add_physaddr_to_nid(info->start_addr);
@ -310,95 +251,21 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
return result; return result;
} }
static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
{
struct acpi_memory_device *mem_device;
struct acpi_device *device;
struct acpi_eject_event *ej_event = NULL;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
acpi_status status;
switch (event) {
case ACPI_NOTIFY_BUS_CHECK:
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"\nReceived BUS CHECK notification for device\n"));
/* Fall Through */
case ACPI_NOTIFY_DEVICE_CHECK:
if (event == ACPI_NOTIFY_DEVICE_CHECK)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"\nReceived DEVICE CHECK notification for device\n"));
if (acpi_memory_get_device(handle, &mem_device)) {
acpi_handle_err(handle, "Cannot find driver data\n");
break;
}
ost_code = ACPI_OST_SC_SUCCESS;
break;
case ACPI_NOTIFY_EJECT_REQUEST:
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"\nReceived EJECT REQUEST notification for device\n"));
status = AE_ERROR;
acpi_scan_lock_acquire();
if (acpi_bus_get_device(handle, &device)) {
acpi_handle_err(handle, "Device doesn't exist\n");
goto unlock;
}
mem_device = acpi_driver_data(device);
if (!mem_device) {
acpi_handle_err(handle, "Driver Data is NULL\n");
goto unlock;
}
ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
if (!ej_event) {
pr_err(PREFIX "No memory, dropping EJECT\n");
goto unlock;
}
get_device(&device->dev);
ej_event->device = device;
ej_event->event = ACPI_NOTIFY_EJECT_REQUEST;
/* The eject is carried out asynchronously. */
status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device,
ej_event);
if (ACPI_FAILURE(status)) {
put_device(&device->dev);
kfree(ej_event);
}
unlock:
acpi_scan_lock_release();
if (ACPI_SUCCESS(status))
return;
default:
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Unsupported event [0x%x]\n", event));
/* non-hotplug event; possibly handled by other handler */
return;
}
/* Inform firmware that the hotplug operation has completed */
(void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL);
}
static void acpi_memory_device_free(struct acpi_memory_device *mem_device) static void acpi_memory_device_free(struct acpi_memory_device *mem_device)
{ {
if (!mem_device) if (!mem_device)
return; return;
acpi_memory_free_device_resources(mem_device); acpi_memory_free_device_resources(mem_device);
mem_device->device->driver_data = NULL;
kfree(mem_device); kfree(mem_device);
} }
static int acpi_memory_device_add(struct acpi_device *device) static int acpi_memory_device_add(struct acpi_device *device,
const struct acpi_device_id *not_used)
{ {
struct acpi_memory_device *mem_device;
int result; int result;
struct acpi_memory_device *mem_device = NULL;
if (!device) if (!device)
return -EINVAL; return -EINVAL;
@ -423,147 +290,36 @@ static int acpi_memory_device_add(struct acpi_device *device)
/* Set the device state */ /* Set the device state */
mem_device->state = MEMORY_POWER_ON_STATE; mem_device->state = MEMORY_POWER_ON_STATE;
pr_debug("%s\n", acpi_device_name(device)); result = acpi_memory_check_device(mem_device);
if (result) {
if (!acpi_memory_check_device(mem_device)) { acpi_memory_device_free(mem_device);
/* call add_memory func */ return 0;
result = acpi_memory_enable_device(mem_device);
if (result) {
dev_err(&device->dev,
"Error in acpi_memory_enable_device\n");
acpi_memory_device_free(mem_device);
}
} }
return result;
result = acpi_memory_enable_device(mem_device);
if (result) {
dev_err(&device->dev, "acpi_memory_enable_device() error\n");
acpi_memory_device_free(mem_device);
return -ENODEV;
}
dev_dbg(&device->dev, "Memory device configured by ACPI\n");
return 1;
} }
static int acpi_memory_device_remove(struct acpi_device *device) static void acpi_memory_device_remove(struct acpi_device *device)
{ {
struct acpi_memory_device *mem_device = NULL; struct acpi_memory_device *mem_device;
int result;
if (!device || !acpi_driver_data(device)) if (!device || !acpi_driver_data(device))
return -EINVAL; return;
mem_device = acpi_driver_data(device); mem_device = acpi_driver_data(device);
acpi_memory_remove_memory(mem_device);
result = acpi_memory_remove_memory(mem_device);
if (result)
return result;
acpi_memory_device_free(mem_device); acpi_memory_device_free(mem_device);
return 0;
} }
/* void __init acpi_memory_hotplug_init(void)
* Helper function to check for memory device
*/
static acpi_status is_memory_device(acpi_handle handle)
{ {
char *hardware_id; acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory");
acpi_status status;
struct acpi_device_info *info;
status = acpi_get_object_info(handle, &info);
if (ACPI_FAILURE(status))
return status;
if (!(info->valid & ACPI_VALID_HID)) {
kfree(info);
return AE_ERROR;
}
hardware_id = info->hardware_id.string;
if ((hardware_id == NULL) ||
(strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
status = AE_ERROR;
kfree(info);
return status;
} }
static acpi_status
acpi_memory_register_notify_handler(acpi_handle handle,
u32 level, void *ctxt, void **retv)
{
acpi_status status;
status = is_memory_device(handle);
if (ACPI_FAILURE(status))
return AE_OK; /* continue */
status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
acpi_memory_device_notify, NULL);
/* continue */
return AE_OK;
}
static acpi_status
acpi_memory_deregister_notify_handler(acpi_handle handle,
u32 level, void *ctxt, void **retv)
{
acpi_status status;
status = is_memory_device(handle);
if (ACPI_FAILURE(status))
return AE_OK; /* continue */
status = acpi_remove_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
acpi_memory_device_notify);
return AE_OK; /* continue */
}
static int __init acpi_memory_device_init(void)
{
int result;
acpi_status status;
result = acpi_bus_register_driver(&acpi_memory_device_driver);
if (result < 0)
return -ENODEV;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
acpi_memory_register_notify_handler, NULL,
NULL, NULL);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
acpi_bus_unregister_driver(&acpi_memory_device_driver);
return -ENODEV;
}
return 0;
}
static void __exit acpi_memory_device_exit(void)
{
acpi_status status;
/*
* Adding this to un-install notification handlers for all the device
* handles.
*/
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
acpi_memory_deregister_notify_handler, NULL,
NULL, NULL);
if (ACPI_FAILURE(status))
ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
acpi_bus_unregister_driver(&acpi_memory_device_driver);
return;
}
module_init(acpi_memory_device_init);
module_exit(acpi_memory_device_exit);

View File

@ -1,12 +1,12 @@
/* /*
* acpi_container.c - ACPI Generic Container Driver * container.c - ACPI Generic Container Driver
* ($Revision: )
* *
* Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com) * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com)
* Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com) * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com)
* Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com) * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com)
* Copyright (C) 2004 Intel Corp.
* Copyright (C) 2004 FUJITSU LIMITED * Copyright (C) 2004 FUJITSU LIMITED
* Copyright (C) 2004, 2013 Intel Corp.
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
* *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* *
@ -26,14 +26,9 @@
* *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h> #include "internal.h"
#define PREFIX "ACPI: " #define PREFIX "ACPI: "
@ -50,141 +45,20 @@ static const struct acpi_device_id container_device_ids[] = {
static int container_device_attach(struct acpi_device *device, static int container_device_attach(struct acpi_device *device,
const struct acpi_device_id *not_used) const struct acpi_device_id *not_used)
{ {
/* /* This is necessary for container hotplug to work. */
* FIXME: This is necessary, so that acpi_eject_store() doesn't return
* -ENODEV for containers.
*/
return 1; return 1;
} }
static struct acpi_scan_handler container_device_handler = { static struct acpi_scan_handler container_handler = {
.ids = container_device_ids, .ids = container_device_ids,
.attach = container_device_attach, .attach = container_device_attach,
.hotplug = {
.enabled = true,
.mode = AHM_CONTAINER,
},
}; };
static int is_device_present(acpi_handle handle)
{
acpi_handle temp;
acpi_status status;
unsigned long long sta;
status = acpi_get_handle(handle, "_STA", &temp);
if (ACPI_FAILURE(status))
return 1; /* _STA not found, assume device present */
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status))
return 0; /* Firmware error */
return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT);
}
static void container_notify_cb(acpi_handle handle, u32 type, void *context)
{
struct acpi_device *device = NULL;
int result;
int present;
acpi_status status;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
acpi_scan_lock_acquire();
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
/* Fall through */
case ACPI_NOTIFY_DEVICE_CHECK:
pr_debug("Container driver received %s event\n",
(type == ACPI_NOTIFY_BUS_CHECK) ?
"ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK");
present = is_device_present(handle);
status = acpi_bus_get_device(handle, &device);
if (!present) {
if (ACPI_SUCCESS(status)) {
/* device exist and this is a remove request */
device->flags.eject_pending = 1;
kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
goto out;
}
break;
}
if (!ACPI_FAILURE(status) || device)
break;
result = acpi_bus_scan(handle);
if (result) {
acpi_handle_warn(handle, "Failed to add container\n");
break;
}
result = acpi_bus_get_device(handle, &device);
if (result) {
acpi_handle_warn(handle, "Missing device object\n");
break;
}
kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
ost_code = ACPI_OST_SC_SUCCESS;
break;
case ACPI_NOTIFY_EJECT_REQUEST:
if (!acpi_bus_get_device(handle, &device) && device) {
device->flags.eject_pending = 1;
kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
goto out;
}
break;
default:
/* non-hotplug event; possibly handled by other handler */
goto out;
}
/* Inform firmware that the hotplug operation has completed */
(void) acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
out:
acpi_scan_lock_release();
}
static bool is_container(acpi_handle handle)
{
struct acpi_device_info *info;
bool ret = false;
if (ACPI_FAILURE(acpi_get_object_info(handle, &info)))
return false;
if (info->valid & ACPI_VALID_HID) {
const struct acpi_device_id *id;
for (id = container_device_ids; id->id[0]; id++) {
ret = !strcmp((char *)id->id, info->hardware_id.string);
if (ret)
break;
}
}
kfree(info);
return ret;
}
static acpi_status acpi_container_register_notify_handler(acpi_handle handle,
u32 lvl, void *ctxt,
void **retv)
{
if (is_container(handle))
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
container_notify_cb, NULL);
return AE_OK;
}
void __init acpi_container_init(void) void __init acpi_container_init(void)
{ {
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, acpi_scan_add_handler_with_hotplug(&container_handler, "container");
acpi_container_register_notify_handler, NULL,
NULL, NULL);
acpi_scan_add_handler(&container_device_handler);
} }

View File

@ -41,6 +41,17 @@ void acpi_container_init(void);
#else #else
static inline void acpi_container_init(void) {} static inline void acpi_container_init(void) {}
#endif #endif
#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
void acpi_memory_hotplug_init(void);
#else
static inline void acpi_memory_hotplug_init(void) {}
#endif
void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
const char *name);
int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
const char *hotplug_profile_name);
void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
extern struct dentry *acpi_debugfs_dir; extern struct dentry *acpi_debugfs_dir;
@ -60,7 +71,7 @@ int acpi_device_add(struct acpi_device *device,
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
int type, unsigned long long sta); int type, unsigned long long sta);
void acpi_device_add_finalize(struct acpi_device *device); void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_ids(struct acpi_device *device); void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
Power Resource Power Resource

View File

@ -824,7 +824,7 @@ static void acpi_release_power_resource(struct device *dev)
list_del(&resource->list_node); list_del(&resource->list_node);
mutex_unlock(&power_resource_list_lock); mutex_unlock(&power_resource_list_lock);
acpi_free_ids(device); acpi_free_pnp_ids(&device->pnp);
kfree(resource); kfree(resource);
} }

View File

@ -63,6 +63,19 @@ int acpi_scan_add_handler(struct acpi_scan_handler *handler)
return 0; return 0;
} }
int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
const char *hotplug_profile_name)
{
int error;
error = acpi_scan_add_handler(handler);
if (error)
return error;
acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name);
return 0;
}
/* /*
* Creates hid/cid(s) string needed for modalias and uevent * Creates hid/cid(s) string needed for modalias and uevent
* e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
@ -107,32 +120,20 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
} }
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
/** static int acpi_scan_hot_remove(struct acpi_device *device)
* acpi_bus_hot_remove_device: hot-remove a device and its children
* @context: struct acpi_eject_event pointer (freed in this func)
*
* Hot-remove a device and its children. This function frees up the
* memory space passed by arg context, so that the caller may call
* this function asynchronously through acpi_os_hotplug_execute().
*/
void acpi_bus_hot_remove_device(void *context)
{ {
struct acpi_eject_event *ej_event = context;
struct acpi_device *device = ej_event->device;
acpi_handle handle = device->handle; acpi_handle handle = device->handle;
acpi_handle temp; acpi_handle not_used;
struct acpi_object_list arg_list; struct acpi_object_list arg_list;
union acpi_object arg; union acpi_object arg;
acpi_status status = AE_OK; acpi_status status;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ unsigned long long sta;
mutex_lock(&acpi_scan_lock);
/* If there is no handle, the device node has been unregistered. */ /* If there is no handle, the device node has been unregistered. */
if (!device->handle) { if (!handle) {
dev_dbg(&device->dev, "ACPI handle missing\n"); dev_dbg(&device->dev, "ACPI handle missing\n");
put_device(&device->dev); put_device(&device->dev);
goto out; return -EINVAL;
} }
ACPI_DEBUG_PRINT((ACPI_DB_INFO, ACPI_DEBUG_PRINT((ACPI_DB_INFO,
@ -143,7 +144,7 @@ void acpi_bus_hot_remove_device(void *context)
put_device(&device->dev); put_device(&device->dev);
device = NULL; device = NULL;
if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) { if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &not_used))) {
arg_list.count = 1; arg_list.count = 1;
arg_list.pointer = &arg; arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER; arg.type = ACPI_TYPE_INTEGER;
@ -161,18 +162,205 @@ void acpi_bus_hot_remove_device(void *context)
*/ */
status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND) if (status == AE_NOT_FOUND) {
acpi_handle_warn(handle, "Eject failed\n"); return -ENODEV;
} else {
acpi_handle_warn(handle, "Eject failed (0x%x)\n",
status);
return -EIO;
}
}
/* Tell the firmware the hot-remove operation has failed. */ /*
acpi_evaluate_hotplug_ost(handle, ej_event->event, * Verify if eject was indeed successful. If not, log an error
ost_code, NULL); * message. No need to call _OST since _EJ0 call was made OK.
*/
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status)) {
acpi_handle_warn(handle,
"Status check after eject failed (0x%x)\n", status);
} else if (sta & ACPI_STA_DEVICE_ENABLED) {
acpi_handle_warn(handle,
"Eject incomplete - status 0x%llx\n", sta);
}
return 0;
}
static void acpi_bus_device_eject(void *context)
{
acpi_handle handle = context;
struct acpi_device *device = NULL;
struct acpi_scan_handler *handler;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
mutex_lock(&acpi_scan_lock);
acpi_bus_get_device(handle, &device);
if (!device)
goto err_out;
handler = device->handler;
if (!handler || !handler->hotplug.enabled) {
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
goto err_out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
if (handler->hotplug.mode == AHM_CONTAINER) {
device->flags.eject_pending = true;
kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
} else {
int error;
get_device(&device->dev);
error = acpi_scan_hot_remove(device);
if (error)
goto err_out;
} }
out: out:
mutex_unlock(&acpi_scan_lock); mutex_unlock(&acpi_scan_lock);
kfree(context);
return; return;
err_out:
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ost_code,
NULL);
goto out;
}
static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
{
struct acpi_device *device = NULL;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
int error;
mutex_lock(&acpi_scan_lock);
acpi_bus_get_device(handle, &device);
if (device) {
dev_warn(&device->dev, "Attempt to re-insert\n");
goto out;
}
acpi_evaluate_hotplug_ost(handle, ost_source,
ACPI_OST_SC_INSERT_IN_PROGRESS, NULL);
error = acpi_bus_scan(handle);
if (error) {
acpi_handle_warn(handle, "Namespace scan failure\n");
goto out;
}
error = acpi_bus_get_device(handle, &device);
if (error) {
acpi_handle_warn(handle, "Missing device node object\n");
goto out;
}
ost_code = ACPI_OST_SC_SUCCESS;
if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER)
kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
out:
acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL);
mutex_unlock(&acpi_scan_lock);
}
static void acpi_scan_bus_check(void *context)
{
acpi_scan_bus_device_check((acpi_handle)context,
ACPI_NOTIFY_BUS_CHECK);
}
static void acpi_scan_device_check(void *context)
{
acpi_scan_bus_device_check((acpi_handle)context,
ACPI_NOTIFY_DEVICE_CHECK);
}
static void acpi_hotplug_unsupported(acpi_handle handle, u32 type)
{
u32 ost_status;
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle,
"ACPI_NOTIFY_BUS_CHECK event: unsupported\n");
ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED;
break;
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle,
"ACPI_NOTIFY_DEVICE_CHECK event: unsupported\n");
ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED;
break;
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle,
"ACPI_NOTIFY_EJECT_REQUEST event: unsupported\n");
ost_status = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
break;
default:
/* non-hotplug event; possibly handled by other handler */
return;
}
acpi_evaluate_hotplug_ost(handle, type, ost_status, NULL);
}
static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
acpi_osd_exec_callback callback;
struct acpi_scan_handler *handler = data;
acpi_status status;
if (!handler->hotplug.enabled)
return acpi_hotplug_unsupported(handle, type);
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
callback = acpi_scan_bus_check;
break;
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
callback = acpi_scan_device_check;
break;
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
callback = acpi_bus_device_eject;
break;
default:
/* non-hotplug event; possibly handled by other handler */
return;
}
status = acpi_os_hotplug_execute(callback, handle);
if (ACPI_FAILURE(status))
acpi_evaluate_hotplug_ost(handle, type,
ACPI_OST_SC_NON_SPECIFIC_FAILURE,
NULL);
}
/**
* acpi_bus_hot_remove_device: hot-remove a device and its children
* @context: struct acpi_eject_event pointer (freed in this func)
*
* Hot-remove a device and its children. This function frees up the
* memory space passed by arg context, so that the caller may call
* this function asynchronously through acpi_os_hotplug_execute().
*/
void acpi_bus_hot_remove_device(void *context)
{
struct acpi_eject_event *ej_event = context;
struct acpi_device *device = ej_event->device;
acpi_handle handle = device->handle;
int error;
mutex_lock(&acpi_scan_lock);
error = acpi_scan_hot_remove(device);
if (error && handle)
acpi_evaluate_hotplug_ost(handle, ej_event->event,
ACPI_OST_SC_NON_SPECIFIC_FAILURE,
NULL);
mutex_unlock(&acpi_scan_lock);
kfree(context);
} }
EXPORT_SYMBOL(acpi_bus_hot_remove_device); EXPORT_SYMBOL(acpi_bus_hot_remove_device);
@ -206,51 +394,61 @@ static ssize_t
acpi_eject_store(struct device *d, struct device_attribute *attr, acpi_eject_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
int ret = count;
acpi_status status;
acpi_object_type type = 0;
struct acpi_device *acpi_device = to_acpi_device(d); struct acpi_device *acpi_device = to_acpi_device(d);
struct acpi_eject_event *ej_event; struct acpi_eject_event *ej_event;
acpi_object_type not_used;
acpi_status status;
u32 ost_source;
int ret;
if ((!count) || (buf[0] != '1')) { if (!count || buf[0] != '1')
return -EINVAL; return -EINVAL;
}
if (!acpi_device->driver && !acpi_device->handler) {
ret = -ENODEV;
goto err;
}
status = acpi_get_type(acpi_device->handle, &type);
if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) {
ret = -ENODEV;
goto err;
}
if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled)
&& !acpi_device->driver)
return -ENODEV;
status = acpi_get_type(acpi_device->handle, &not_used);
if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
return -ENODEV;
mutex_lock(&acpi_scan_lock);
if (acpi_device->flags.eject_pending) {
/* ACPI eject notification event. */
ost_source = ACPI_NOTIFY_EJECT_REQUEST;
acpi_device->flags.eject_pending = 0;
} else {
/* Eject initiated by user space. */
ost_source = ACPI_OST_EC_OSPM_EJECT;
}
ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
if (!ej_event) { if (!ej_event) {
ret = -ENOMEM; ret = -ENOMEM;
goto err; goto err_out;
} }
acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source,
get_device(&acpi_device->dev); ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
ej_event->device = acpi_device; ej_event->device = acpi_device;
if (acpi_device->flags.eject_pending) { ej_event->event = ost_source;
/* event originated from ACPI eject notification */ get_device(&acpi_device->dev);
ej_event->event = ACPI_NOTIFY_EJECT_REQUEST;
acpi_device->flags.eject_pending = 0;
} else {
/* event originated from user */
ej_event->event = ACPI_OST_EC_OSPM_EJECT;
(void) acpi_evaluate_hotplug_ost(acpi_device->handle,
ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
}
status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
put_device(&acpi_device->dev); put_device(&acpi_device->dev);
kfree(ej_event); kfree(ej_event);
ret = status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN;
goto err_out;
} }
err: ret = count;
out:
mutex_unlock(&acpi_scan_lock);
return ret; return ret;
err_out:
acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source,
ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
goto out;
} }
static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store);
@ -376,7 +574,7 @@ static int acpi_device_setup_files(struct acpi_device *dev)
goto end; goto end;
} }
if (dev->flags.bus_address) if (dev->pnp.type.bus_address)
result = device_create_file(&dev->dev, &dev_attr_adr); result = device_create_file(&dev->dev, &dev_attr_adr);
if (dev->pnp.unique_id) if (dev->pnp.unique_id)
result = device_create_file(&dev->dev, &dev_attr_uid); result = device_create_file(&dev->dev, &dev_attr_uid);
@ -449,7 +647,7 @@ static void acpi_device_remove_files(struct acpi_device *dev)
if (dev->pnp.unique_id) if (dev->pnp.unique_id)
device_remove_file(&dev->dev, &dev_attr_uid); device_remove_file(&dev->dev, &dev_attr_uid);
if (dev->flags.bus_address) if (dev->pnp.type.bus_address)
device_remove_file(&dev->dev, &dev_attr_adr); device_remove_file(&dev->dev, &dev_attr_adr);
device_remove_file(&dev->dev, &dev_attr_modalias); device_remove_file(&dev->dev, &dev_attr_modalias);
device_remove_file(&dev->dev, &dev_attr_hid); device_remove_file(&dev->dev, &dev_attr_hid);
@ -512,17 +710,6 @@ int acpi_match_device_ids(struct acpi_device *device,
} }
EXPORT_SYMBOL(acpi_match_device_ids); EXPORT_SYMBOL(acpi_match_device_ids);
void acpi_free_ids(struct acpi_device *device)
{
struct acpi_hardware_id *id, *tmp;
list_for_each_entry_safe(id, tmp, &device->pnp.ids, list) {
kfree(id->id);
kfree(id);
}
kfree(device->pnp.unique_id);
}
static void acpi_free_power_resources_lists(struct acpi_device *device) static void acpi_free_power_resources_lists(struct acpi_device *device)
{ {
int i; int i;
@ -543,7 +730,7 @@ static void acpi_device_release(struct device *dev)
{ {
struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_device *acpi_dev = to_acpi_device(dev);
acpi_free_ids(acpi_dev); acpi_free_pnp_ids(&acpi_dev->pnp);
acpi_free_power_resources_lists(acpi_dev); acpi_free_power_resources_lists(acpi_dev);
kfree(acpi_dev); kfree(acpi_dev);
} }
@ -1256,19 +1443,17 @@ static void acpi_device_get_busid(struct acpi_device *device)
} }
/* /*
* acpi_bay_match - see if a device is an ejectable driver bay * acpi_bay_match - see if an acpi object is an ejectable driver bay
* *
* If an acpi object is ejectable and has one of the ACPI ATA methods defined, * If an acpi object is ejectable and has one of the ACPI ATA methods defined,
* then we can safely call it an ejectable drive bay * then we can safely call it an ejectable drive bay
*/ */
static int acpi_bay_match(struct acpi_device *device){ static int acpi_bay_match(acpi_handle handle)
{
acpi_status status; acpi_status status;
acpi_handle handle;
acpi_handle tmp; acpi_handle tmp;
acpi_handle phandle; acpi_handle phandle;
handle = device->handle;
status = acpi_get_handle(handle, "_EJ0", &tmp); status = acpi_get_handle(handle, "_EJ0", &tmp);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
@ -1292,12 +1477,12 @@ static int acpi_bay_match(struct acpi_device *device){
} }
/* /*
* acpi_dock_match - see if a device has a _DCK method * acpi_dock_match - see if an acpi object has a _DCK method
*/ */
static int acpi_dock_match(struct acpi_device *device) static int acpi_dock_match(acpi_handle handle)
{ {
acpi_handle tmp; acpi_handle tmp;
return acpi_get_handle(device->handle, "_DCK", &tmp); return acpi_get_handle(handle, "_DCK", &tmp);
} }
const char *acpi_device_hid(struct acpi_device *device) const char *acpi_device_hid(struct acpi_device *device)
@ -1312,7 +1497,7 @@ const char *acpi_device_hid(struct acpi_device *device)
} }
EXPORT_SYMBOL(acpi_device_hid); EXPORT_SYMBOL(acpi_device_hid);
static void acpi_add_id(struct acpi_device *device, const char *dev_id) static void acpi_add_id(struct acpi_device_pnp *pnp, const char *dev_id)
{ {
struct acpi_hardware_id *id; struct acpi_hardware_id *id;
@ -1326,7 +1511,8 @@ static void acpi_add_id(struct acpi_device *device, const char *dev_id)
return; return;
} }
list_add_tail(&id->list, &device->pnp.ids); list_add_tail(&id->list, &pnp->ids);
pnp->type.hardware_id = 1;
} }
/* /*
@ -1334,7 +1520,7 @@ static void acpi_add_id(struct acpi_device *device, const char *dev_id)
* lacks the SMBUS01 HID and the methods do not have the necessary "_" * lacks the SMBUS01 HID and the methods do not have the necessary "_"
* prefix. Work around this. * prefix. Work around this.
*/ */
static int acpi_ibm_smbus_match(struct acpi_device *device) static int acpi_ibm_smbus_match(acpi_handle handle)
{ {
acpi_handle h_dummy; acpi_handle h_dummy;
struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};
@ -1344,7 +1530,7 @@ static int acpi_ibm_smbus_match(struct acpi_device *device)
return -ENODEV; return -ENODEV;
/* Look for SMBS object */ /* Look for SMBS object */
result = acpi_get_name(device->handle, ACPI_SINGLE_NAME, &path); result = acpi_get_name(handle, ACPI_SINGLE_NAME, &path);
if (result) if (result)
return result; return result;
@ -1355,48 +1541,50 @@ static int acpi_ibm_smbus_match(struct acpi_device *device)
/* Does it have the necessary (but misnamed) methods? */ /* Does it have the necessary (but misnamed) methods? */
result = -ENODEV; result = -ENODEV;
if (ACPI_SUCCESS(acpi_get_handle(device->handle, "SBI", &h_dummy)) && if (ACPI_SUCCESS(acpi_get_handle(handle, "SBI", &h_dummy)) &&
ACPI_SUCCESS(acpi_get_handle(device->handle, "SBR", &h_dummy)) && ACPI_SUCCESS(acpi_get_handle(handle, "SBR", &h_dummy)) &&
ACPI_SUCCESS(acpi_get_handle(device->handle, "SBW", &h_dummy))) ACPI_SUCCESS(acpi_get_handle(handle, "SBW", &h_dummy)))
result = 0; result = 0;
out: out:
kfree(path.pointer); kfree(path.pointer);
return result; return result;
} }
static void acpi_device_set_id(struct acpi_device *device) static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
int device_type)
{ {
acpi_status status; acpi_status status;
struct acpi_device_info *info; struct acpi_device_info *info;
struct acpi_pnp_device_id_list *cid_list; struct acpi_pnp_device_id_list *cid_list;
int i; int i;
switch (device->device_type) { switch (device_type) {
case ACPI_BUS_TYPE_DEVICE: case ACPI_BUS_TYPE_DEVICE:
if (ACPI_IS_ROOT_DEVICE(device)) { if (handle == ACPI_ROOT_OBJECT) {
acpi_add_id(device, ACPI_SYSTEM_HID); acpi_add_id(pnp, ACPI_SYSTEM_HID);
break; break;
} }
status = acpi_get_object_info(device->handle, &info); status = acpi_get_object_info(handle, &info);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX "%s: Error reading device info\n", __func__); pr_err(PREFIX "%s: Error reading device info\n",
__func__);
return; return;
} }
if (info->valid & ACPI_VALID_HID) if (info->valid & ACPI_VALID_HID)
acpi_add_id(device, info->hardware_id.string); acpi_add_id(pnp, info->hardware_id.string);
if (info->valid & ACPI_VALID_CID) { if (info->valid & ACPI_VALID_CID) {
cid_list = &info->compatible_id_list; cid_list = &info->compatible_id_list;
for (i = 0; i < cid_list->count; i++) for (i = 0; i < cid_list->count; i++)
acpi_add_id(device, cid_list->ids[i].string); acpi_add_id(pnp, cid_list->ids[i].string);
} }
if (info->valid & ACPI_VALID_ADR) { if (info->valid & ACPI_VALID_ADR) {
device->pnp.bus_address = info->address; pnp->bus_address = info->address;
device->flags.bus_address = 1; pnp->type.bus_address = 1;
} }
if (info->valid & ACPI_VALID_UID) if (info->valid & ACPI_VALID_UID)
device->pnp.unique_id = kstrdup(info->unique_id.string, pnp->unique_id = kstrdup(info->unique_id.string,
GFP_KERNEL); GFP_KERNEL);
kfree(info); kfree(info);
@ -1405,40 +1593,50 @@ static void acpi_device_set_id(struct acpi_device *device)
* Some devices don't reliably have _HIDs & _CIDs, so add * Some devices don't reliably have _HIDs & _CIDs, so add
* synthetic HIDs to make sure drivers can find them. * synthetic HIDs to make sure drivers can find them.
*/ */
if (acpi_is_video_device(device)) if (acpi_is_video_device(handle))
acpi_add_id(device, ACPI_VIDEO_HID); acpi_add_id(pnp, ACPI_VIDEO_HID);
else if (ACPI_SUCCESS(acpi_bay_match(device))) else if (ACPI_SUCCESS(acpi_bay_match(handle)))
acpi_add_id(device, ACPI_BAY_HID); acpi_add_id(pnp, ACPI_BAY_HID);
else if (ACPI_SUCCESS(acpi_dock_match(device))) else if (ACPI_SUCCESS(acpi_dock_match(handle)))
acpi_add_id(device, ACPI_DOCK_HID); acpi_add_id(pnp, ACPI_DOCK_HID);
else if (!acpi_ibm_smbus_match(device)) else if (!acpi_ibm_smbus_match(handle))
acpi_add_id(device, ACPI_SMBUS_IBM_HID); acpi_add_id(pnp, ACPI_SMBUS_IBM_HID);
else if (list_empty(&device->pnp.ids) && else if (list_empty(&pnp->ids) && handle == ACPI_ROOT_OBJECT) {
ACPI_IS_ROOT_DEVICE(device->parent)) { acpi_add_id(pnp, ACPI_BUS_HID); /* \_SB, LNXSYBUS */
acpi_add_id(device, ACPI_BUS_HID); /* \_SB, LNXSYBUS */ strcpy(pnp->device_name, ACPI_BUS_DEVICE_NAME);
strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); strcpy(pnp->device_class, ACPI_BUS_CLASS);
strcpy(device->pnp.device_class, ACPI_BUS_CLASS);
} }
break; break;
case ACPI_BUS_TYPE_POWER: case ACPI_BUS_TYPE_POWER:
acpi_add_id(device, ACPI_POWER_HID); acpi_add_id(pnp, ACPI_POWER_HID);
break; break;
case ACPI_BUS_TYPE_PROCESSOR: case ACPI_BUS_TYPE_PROCESSOR:
acpi_add_id(device, ACPI_PROCESSOR_OBJECT_HID); acpi_add_id(pnp, ACPI_PROCESSOR_OBJECT_HID);
break; break;
case ACPI_BUS_TYPE_THERMAL: case ACPI_BUS_TYPE_THERMAL:
acpi_add_id(device, ACPI_THERMAL_HID); acpi_add_id(pnp, ACPI_THERMAL_HID);
break; break;
case ACPI_BUS_TYPE_POWER_BUTTON: case ACPI_BUS_TYPE_POWER_BUTTON:
acpi_add_id(device, ACPI_BUTTON_HID_POWERF); acpi_add_id(pnp, ACPI_BUTTON_HID_POWERF);
break; break;
case ACPI_BUS_TYPE_SLEEP_BUTTON: case ACPI_BUS_TYPE_SLEEP_BUTTON:
acpi_add_id(device, ACPI_BUTTON_HID_SLEEPF); acpi_add_id(pnp, ACPI_BUTTON_HID_SLEEPF);
break; break;
} }
} }
void acpi_free_pnp_ids(struct acpi_device_pnp *pnp)
{
struct acpi_hardware_id *id, *tmp;
list_for_each_entry_safe(id, tmp, &pnp->ids, list) {
kfree(id->id);
kfree(id);
}
kfree(pnp->unique_id);
}
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
int type, unsigned long long sta) int type, unsigned long long sta)
{ {
@ -1448,7 +1646,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
device->parent = acpi_bus_get_parent(handle); device->parent = acpi_bus_get_parent(handle);
STRUCT_TO_INT(device->status) = sta; STRUCT_TO_INT(device->status) = sta;
acpi_device_get_busid(device); acpi_device_get_busid(device);
acpi_device_set_id(device); acpi_set_pnp_ids(handle, &device->pnp, type);
acpi_bus_get_flags(device); acpi_bus_get_flags(device);
device->flags.match_driver = false; device->flags.match_driver = false;
device_initialize(&device->dev); device_initialize(&device->dev);
@ -1536,6 +1734,75 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type,
return 0; return 0;
} }
static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
char *idstr,
const struct acpi_device_id **matchid)
{
const struct acpi_device_id *devid;
for (devid = handler->ids; devid->id[0]; devid++)
if (!strcmp((char *)devid->id, idstr)) {
if (matchid)
*matchid = devid;
return true;
}
return false;
}
static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr,
const struct acpi_device_id **matchid)
{
struct acpi_scan_handler *handler;
list_for_each_entry(handler, &acpi_scan_handlers_list, list_node)
if (acpi_scan_handler_matching(handler, idstr, matchid))
return handler;
return NULL;
}
void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val)
{
if (!!hotplug->enabled == !!val)
return;
mutex_lock(&acpi_scan_lock);
hotplug->enabled = val;
mutex_unlock(&acpi_scan_lock);
}
static void acpi_scan_init_hotplug(acpi_handle handle, int type)
{
struct acpi_device_pnp pnp = {};
struct acpi_hardware_id *hwid;
struct acpi_scan_handler *handler;
INIT_LIST_HEAD(&pnp.ids);
acpi_set_pnp_ids(handle, &pnp, type);
if (!pnp.type.hardware_id)
return;
/*
* This relies on the fact that acpi_install_notify_handler() will not
* install the same notify handler routine twice for the same handle.
*/
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
acpi_hotplug_notify_cb, handler);
break;
}
}
acpi_free_pnp_ids(&pnp);
}
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **return_value) void *not_used, void **return_value)
{ {
@ -1558,6 +1825,8 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
return AE_OK; return AE_OK;
} }
acpi_scan_init_hotplug(handle, type);
if (!(sta & ACPI_STA_DEVICE_PRESENT) && if (!(sta & ACPI_STA_DEVICE_PRESENT) &&
!(sta & ACPI_STA_DEVICE_FUNCTIONING)) { !(sta & ACPI_STA_DEVICE_FUNCTIONING)) {
struct acpi_device_wakeup wakeup; struct acpi_device_wakeup wakeup;
@ -1583,41 +1852,25 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
return AE_OK; return AE_OK;
} }
static int acpi_scan_do_attach_handler(struct acpi_device *device, char *id)
{
struct acpi_scan_handler *handler;
list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) {
const struct acpi_device_id *devid;
for (devid = handler->ids; devid->id[0]; devid++) {
int ret;
if (strcmp((char *)devid->id, id))
continue;
ret = handler->attach(device, devid);
if (ret > 0) {
device->handler = handler;
return ret;
} else if (ret < 0) {
return ret;
}
}
}
return 0;
}
static int acpi_scan_attach_handler(struct acpi_device *device) static int acpi_scan_attach_handler(struct acpi_device *device)
{ {
struct acpi_hardware_id *hwid; struct acpi_hardware_id *hwid;
int ret = 0; int ret = 0;
list_for_each_entry(hwid, &device->pnp.ids, list) { list_for_each_entry(hwid, &device->pnp.ids, list) {
ret = acpi_scan_do_attach_handler(device, hwid->id); const struct acpi_device_id *devid;
if (ret) struct acpi_scan_handler *handler;
break;
handler = acpi_scan_match_handler(hwid->id, &devid);
if (handler) {
ret = handler->attach(device, devid);
if (ret > 0) {
device->handler = handler;
break;
} else if (ret < 0) {
break;
}
}
} }
return ret; return ret;
} }
@ -1791,6 +2044,7 @@ int __init acpi_scan_init(void)
acpi_csrt_init(); acpi_csrt_init();
acpi_container_init(); acpi_container_init();
acpi_pci_slot_init(); acpi_pci_slot_init();
acpi_memory_hotplug_init();
mutex_lock(&acpi_scan_lock); mutex_lock(&acpi_scan_lock);
/* /*

View File

@ -7,6 +7,8 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
#include "internal.h"
#define _COMPONENT ACPI_SYSTEM_COMPONENT #define _COMPONENT ACPI_SYSTEM_COMPONENT
ACPI_MODULE_NAME("sysfs"); ACPI_MODULE_NAME("sysfs");
@ -249,6 +251,7 @@ module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444);
static LIST_HEAD(acpi_table_attr_list); static LIST_HEAD(acpi_table_attr_list);
static struct kobject *tables_kobj; static struct kobject *tables_kobj;
static struct kobject *dynamic_tables_kobj; static struct kobject *dynamic_tables_kobj;
static struct kobject *hotplug_kobj;
struct acpi_table_attr { struct acpi_table_attr {
struct bin_attribute attr; struct bin_attribute attr;
@ -716,6 +719,67 @@ acpi_show_profile(struct device *dev, struct device_attribute *attr,
static const struct device_attribute pm_profile_attr = static const struct device_attribute pm_profile_attr =
__ATTR(pm_profile, S_IRUGO, acpi_show_profile, NULL); __ATTR(pm_profile, S_IRUGO, acpi_show_profile, NULL);
static ssize_t hotplug_enabled_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);
return sprintf(buf, "%d\n", hotplug->enabled);
}
static ssize_t hotplug_enabled_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t size)
{
struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);
unsigned int val;
if (kstrtouint(buf, 10, &val) || val > 1)
return -EINVAL;
acpi_scan_hotplug_enabled(hotplug, val);
return size;
}
static struct kobj_attribute hotplug_enabled_attr =
__ATTR(enabled, S_IRUGO | S_IWUSR, hotplug_enabled_show,
hotplug_enabled_store);
static struct attribute *hotplug_profile_attrs[] = {
&hotplug_enabled_attr.attr,
NULL
};
static struct kobj_type acpi_hotplug_profile_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.default_attrs = hotplug_profile_attrs,
};
void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
const char *name)
{
int error;
if (!hotplug_kobj)
goto err_out;
kobject_init(&hotplug->kobj, &acpi_hotplug_profile_ktype);
error = kobject_set_name(&hotplug->kobj, "%s", name);
if (error)
goto err_out;
hotplug->kobj.parent = hotplug_kobj;
error = kobject_add(&hotplug->kobj, hotplug_kobj, NULL);
if (error)
goto err_out;
kobject_uevent(&hotplug->kobj, KOBJ_ADD);
return;
err_out:
pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name);
}
int __init acpi_sysfs_init(void) int __init acpi_sysfs_init(void)
{ {
int result; int result;
@ -723,6 +787,8 @@ int __init acpi_sysfs_init(void)
result = acpi_tables_sysfs_init(); result = acpi_tables_sysfs_init();
if (result) if (result)
return result; return result;
hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj);
result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr);
return result; return result;
} }

View File

@ -67,40 +67,37 @@ acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context,
return 0; return 0;
} }
/* Returns true if the device is a video device which can be handled by /* Returns true if the ACPI object is a video device which can be
* video.ko. * handled by video.ko.
* The device will get a Linux specific CID added in scan.c to * The device will get a Linux specific CID added in scan.c to
* identify the device as an ACPI graphics device * identify the device as an ACPI graphics device
* Be aware that the graphics device may not be physically present * Be aware that the graphics device may not be physically present
* Use acpi_video_get_capabilities() to detect general ACPI video * Use acpi_video_get_capabilities() to detect general ACPI video
* capabilities of present cards * capabilities of present cards
*/ */
long acpi_is_video_device(struct acpi_device *device) long acpi_is_video_device(acpi_handle handle)
{ {
acpi_handle h_dummy; acpi_handle h_dummy;
long video_caps = 0; long video_caps = 0;
if (!device)
return 0;
/* Is this device able to support video switching ? */ /* Is this device able to support video switching ? */
if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) || if (ACPI_SUCCESS(acpi_get_handle(handle, "_DOD", &h_dummy)) ||
ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy))) ACPI_SUCCESS(acpi_get_handle(handle, "_DOS", &h_dummy)))
video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
/* Is this device able to retrieve a video ROM ? */ /* Is this device able to retrieve a video ROM ? */
if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy))) if (ACPI_SUCCESS(acpi_get_handle(handle, "_ROM", &h_dummy)))
video_caps |= ACPI_VIDEO_ROM_AVAILABLE; video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
/* Is this device able to configure which video head to be POSTed ? */ /* Is this device able to configure which video head to be POSTed ? */
if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) && if (ACPI_SUCCESS(acpi_get_handle(handle, "_VPO", &h_dummy)) &&
ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) && ACPI_SUCCESS(acpi_get_handle(handle, "_GPD", &h_dummy)) &&
ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy))) ACPI_SUCCESS(acpi_get_handle(handle, "_SPD", &h_dummy)))
video_caps |= ACPI_VIDEO_DEVICE_POSTING; video_caps |= ACPI_VIDEO_DEVICE_POSTING;
/* Only check for backlight functionality if one of the above hit. */ /* Only check for backlight functionality if one of the above hit. */
if (video_caps) if (video_caps)
acpi_walk_namespace(ACPI_TYPE_DEVICE, device->handle, acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL, ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL,
&video_caps, NULL); &video_caps, NULL);
@ -127,7 +124,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
if (!dev) if (!dev)
return AE_OK; return AE_OK;
pci_dev_put(dev); pci_dev_put(dev);
*cap |= acpi_is_video_device(acpi_dev); *cap |= acpi_is_video_device(handle);
} }
return AE_OK; return AE_OK;
} }

View File

@ -350,11 +350,11 @@ static void intel_didl_outputs(struct drm_device *dev)
if (!handle || acpi_bus_get_device(handle, &acpi_dev)) if (!handle || acpi_bus_get_device(handle, &acpi_dev))
return; return;
if (acpi_is_video_device(acpi_dev)) if (acpi_is_video_device(handle))
acpi_video_bus = acpi_dev; acpi_video_bus = acpi_dev;
else { else {
list_for_each_entry(acpi_cdev, &acpi_dev->children, node) { list_for_each_entry(acpi_cdev, &acpi_dev->children, node) {
if (acpi_is_video_device(acpi_cdev)) { if (acpi_is_video_device(acpi_cdev->handle)) {
acpi_video_bus = acpi_cdev; acpi_video_bus = acpi_cdev;
break; break;
} }

View File

@ -88,11 +88,30 @@ struct acpi_device;
* ----------------- * -----------------
*/ */
enum acpi_hotplug_mode {
AHM_GENERIC = 0,
AHM_CONTAINER,
AHM_COUNT
};
struct acpi_hotplug_profile {
struct kobject kobj;
bool enabled:1;
enum acpi_hotplug_mode mode;
};
static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile(
struct kobject *kobj)
{
return container_of(kobj, struct acpi_hotplug_profile, kobj);
}
struct acpi_scan_handler { struct acpi_scan_handler {
const struct acpi_device_id *ids; const struct acpi_device_id *ids;
struct list_head list_node; struct list_head list_node;
int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id);
void (*detach)(struct acpi_device *dev); void (*detach)(struct acpi_device *dev);
struct acpi_hotplug_profile hotplug;
}; };
/* /*
@ -142,7 +161,6 @@ struct acpi_device_status {
struct acpi_device_flags { struct acpi_device_flags {
u32 dynamic_status:1; u32 dynamic_status:1;
u32 bus_address:1;
u32 removable:1; u32 removable:1;
u32 ejectable:1; u32 ejectable:1;
u32 suprise_removal_ok:1; u32 suprise_removal_ok:1;
@ -150,7 +168,7 @@ struct acpi_device_flags {
u32 performance_manageable:1; u32 performance_manageable:1;
u32 eject_pending:1; u32 eject_pending:1;
u32 match_driver:1; u32 match_driver:1;
u32 reserved:23; u32 reserved:24;
}; };
/* File System */ /* File System */
@ -173,10 +191,17 @@ struct acpi_hardware_id {
char *id; char *id;
}; };
struct acpi_pnp_type {
u32 hardware_id:1;
u32 bus_address:1;
u32 reserved:30;
};
struct acpi_device_pnp { struct acpi_device_pnp {
acpi_bus_id bus_id; /* Object name */ acpi_bus_id bus_id; /* Object name */
struct acpi_pnp_type type; /* ID type */
acpi_bus_address bus_address; /* _ADR */ acpi_bus_address bus_address; /* _ADR */
char *unique_id; /* _UID */ char *unique_id; /* _UID */
struct list_head ids; /* _HID and _CIDs */ struct list_head ids; /* _HID and _CIDs */
acpi_device_name device_name; /* Driver-determined */ acpi_device_name device_name; /* Driver-determined */
acpi_device_class device_class; /* " */ acpi_device_class device_class; /* " */

View File

@ -204,7 +204,7 @@ extern bool wmi_has_guid(const char *guid);
#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) #if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE)
extern long acpi_video_get_capabilities(acpi_handle graphics_dev_handle); extern long acpi_video_get_capabilities(acpi_handle graphics_dev_handle);
extern long acpi_is_video_device(struct acpi_device *device); extern long acpi_is_video_device(acpi_handle handle);
extern void acpi_video_dmi_promote_vendor(void); extern void acpi_video_dmi_promote_vendor(void);
extern void acpi_video_dmi_demote_vendor(void); extern void acpi_video_dmi_demote_vendor(void);
extern int acpi_video_backlight_support(void); extern int acpi_video_backlight_support(void);
@ -217,7 +217,7 @@ static inline long acpi_video_get_capabilities(acpi_handle graphics_dev_handle)
return 0; return 0;
} }
static inline long acpi_is_video_device(struct acpi_device *device) static inline long acpi_is_video_device(acpi_handle handle)
{ {
return 0; return 0;
} }