diff options
| -rw-r--r-- | Documentation/acpi/scan_handlers.txt | 77 | ||||
| -rw-r--r-- | drivers/acpi/scan.c | 60 | ||||
| -rw-r--r-- | include/acpi/acpi_bus.h | 14 | 
3 files changed, 144 insertions, 7 deletions
diff --git a/Documentation/acpi/scan_handlers.txt b/Documentation/acpi/scan_handlers.txt new file mode 100644 index 00000000000..3246ccf1599 --- /dev/null +++ b/Documentation/acpi/scan_handlers.txt @@ -0,0 +1,77 @@ +ACPI Scan Handlers + +Copyright (C) 2012, Intel Corporation +Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> + +During system initialization and ACPI-based device hot-add, the ACPI namespace +is scanned in search of device objects that generally represent various pieces +of hardware.  This causes a struct acpi_device object to be created and +registered with the driver core for every device object in the ACPI namespace +and the hierarchy of those struct acpi_device objects reflects the namespace +layout (i.e. parent device objects in the namespace are represented by parent +struct acpi_device objects and analogously for their children).  Those struct +acpi_device objects are referred to as "device nodes" in what follows, but they +should not be confused with struct device_node objects used by the Device Trees +parsing code (although their role is analogous to the role of those objects). + +During ACPI-based device hot-remove device nodes representing pieces of hardware +being removed are unregistered and deleted. + +The core ACPI namespace scanning code in drivers/acpi/scan.c carries out basic +initialization of device nodes, such as retrieving common configuration +information from the device objects represented by them and populating them with +appropriate data, but some of them require additional handling after they have +been registered.  For example, if the given device node represents a PCI host +bridge, its registration should cause the PCI bus under that bridge to be +enumerated and PCI devices on that bus to be registered with the driver core. +Similarly, if the device node represents a PCI interrupt link, it is necessary +to configure that link so that the kernel can use it. + +Those additional configuration tasks usually depend on the type of the hardware +component represented by the given device node which can be determined on the +basis of the device node's hardware ID (HID).  They are performed by objects +called ACPI scan handlers represented by the following structure: + +struct acpi_scan_handler { +	const struct acpi_device_id *ids; +	struct list_head list_node; +	int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); +	void (*detach)(struct acpi_device *dev); +}; + +where ids is the list of IDs of device nodes the given handler is supposed to +take care of, list_node is the hook to the global list of ACPI scan handlers +maintained by the ACPI core and the .attach() and .detach() callbacks are +executed, respectively, after registration of new device nodes and before +unregistration of device nodes the handler attached to previously. + +The namespace scanning function, acpi_bus_scan(), first registers all of the +device nodes in the given namespace scope with the driver core.  Then, it tries +to match a scan handler against each of them using the ids arrays of the +available scan handlers.  If a matching scan handler is found, its .attach() +callback is executed for the given device node.  If that callback returns 1, +that means that the handler has claimed the device node and is now responsible +for carrying out any additional configuration tasks related to it.  It also will +be responsible for preparing the device node for unregistration in that case. +The device node's handler field is then populated with the address of the scan +handler that has claimed it. + +If the .attach() callback returns 0, it means that the device node is not +interesting to the given scan handler and may be matched against the next scan +handler in the list.  If it returns a (negative) error code, that means that +the namespace scan should be terminated due to a serious error.  The error code +returned should then reflect the type of the error. + +The namespace trimming function, acpi_bus_trim(), first executes .detach() +callbacks from the scan handlers of all device nodes in the given namespace +scope (if they have scan handlers).  Next, it unregisters all of the device +nodes in that scope. + +ACPI scan handlers can be added to the list maintained by the ACPI core with the +help of the acpi_scan_add_handler() function taking a pointer to the new scan +handler as an argument.  The order in which scan handlers are added to the list +is the order in which they are matched against device nodes during namespace +scans. + +All scan handles must be added to the list before acpi_bus_scan() is run for the +first time and they cannot be removed from it. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index fe9f2c92666..1453cd0672f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -53,6 +53,7 @@ static const struct acpi_device_id acpi_platform_device_ids[] = {  static LIST_HEAD(acpi_device_list);  static LIST_HEAD(acpi_bus_id_list);  static DEFINE_MUTEX(acpi_scan_lock); +static LIST_HEAD(acpi_scan_handlers_list);  DEFINE_MUTEX(acpi_device_lock);  LIST_HEAD(acpi_wakeup_device_list); @@ -62,6 +63,15 @@ struct acpi_device_bus_id{  	struct list_head node;  }; +int acpi_scan_add_handler(struct acpi_scan_handler *handler) +{ +	if (!handler || !handler->attach) +		return -EINVAL; + +	list_add_tail(&handler->list_node, &acpi_scan_handlers_list); +	return 0; +} +  /*   * Creates hid/cid(s) string needed for modalias and uevent   * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: @@ -1570,20 +1580,42 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,  	return AE_OK;  } +static int acpi_scan_attach_handler(struct acpi_device *device) +{ +	struct acpi_scan_handler *handler; +	int ret = 0; + +	list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { +		const struct acpi_device_id *id; + +		id = __acpi_match_device(device, handler->ids); +		if (!id) +			continue; + +		ret = handler->attach(device, id); +		if (ret > 0) { +			device->handler = handler; +			break; +		} else if (ret < 0) { +			break; +		} +	} +	return ret; +} +  static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,  					  void *not_used, void **ret_not_used)  {  	const struct acpi_device_id *id; -	acpi_status status = AE_OK;  	struct acpi_device *device;  	unsigned long long sta_not_used; -	int type_not_used; +	int ret;  	/*  	 * Ignore errors ignored by acpi_bus_check_add() to avoid terminating  	 * namespace walks prematurely.  	 */ -	if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used)) +	if (acpi_bus_type_and_status(handle, &ret, &sta_not_used))  		return AE_OK;  	if (acpi_bus_get_device(handle, &device)) @@ -1593,10 +1625,15 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,  	if (id) {  		/* This is a known good platform device. */  		acpi_create_platform_device(device, id->driver_data); -	} else if (device_attach(&device->dev) < 0) { -		status = AE_CTRL_DEPTH; +		return AE_OK;  	} -	return status; + +	ret = acpi_scan_attach_handler(device); +	if (ret) +		return ret > 0 ? AE_OK : AE_CTRL_DEPTH; + +	ret = device_attach(&device->dev); +	return ret >= 0 ? AE_OK : AE_CTRL_DEPTH;  }  /** @@ -1639,8 +1676,17 @@ static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used,  	struct acpi_device *device = NULL;  	if (!acpi_bus_get_device(handle, &device)) { +		struct acpi_scan_handler *dev_handler = device->handler; +  		device->removal_type = ACPI_BUS_REMOVAL_EJECT; -		device_release_driver(&device->dev); +		if (dev_handler) { +			if (dev_handler->detach) +				dev_handler->detach(device); + +			device->handler = NULL; +		} else { +			device_release_driver(&device->dev); +		}  	}  	return AE_OK;  } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index ad0a86ac5cc..41850cb2173 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -84,6 +84,18 @@ struct acpi_driver;  struct acpi_device;  /* + * ACPI Scan Handler + * ----------------- + */ + +struct acpi_scan_handler { +	const struct acpi_device_id *ids; +	struct list_head list_node; +	int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); +	void (*detach)(struct acpi_device *dev); +}; + +/*   * ACPI Driver   * -----------   */ @@ -269,6 +281,7 @@ struct acpi_device {  	struct acpi_device_wakeup wakeup;  	struct acpi_device_perf performance;  	struct acpi_device_dir dir; +	struct acpi_scan_handler *handler;  	struct acpi_driver *driver;  	void *driver_data;  	struct device dev; @@ -382,6 +395,7 @@ int acpi_bus_receive_event(struct acpi_bus_event *event);  static inline int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data)  	{ return 0; }  #endif +int acpi_scan_add_handler(struct acpi_scan_handler *handler);  int acpi_bus_register_driver(struct acpi_driver *driver);  void acpi_bus_unregister_driver(struct acpi_driver *driver);  int acpi_bus_scan(acpi_handle handle);  |