* acpi-scan:
  ACPI / scan: Rework modalias creation when "compatible" is present
  ACPI / scan: Take the PRP0001 position in the list of IDs into account
  ACPI / scan: Simplify acpi_match_device()
  ACPI / scan: Generalize of_compatible matching
  ACPI / scan: fix fixed event handler return value
This commit is contained in:
Rafael J. Wysocki 2015-04-13 00:34:57 +02:00
Родитель f303906d4d 8765c5ba19
Коммит 1a7fd41ac2
1 изменённых файлов: 232 добавлений и 156 удалений

Просмотреть файл

@ -114,7 +114,12 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
return 0; return 0;
} }
/* /**
* create_pnp_modalias - Create hid/cid(s) string for modalias and uevent
* @acpi_dev: ACPI device object.
* @modalias: Buffer to print into.
* @size: Size of the buffer.
*
* 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:
* char *modalias: "acpi:IBM0001:ACPI0001" * char *modalias: "acpi:IBM0001:ACPI0001"
@ -122,68 +127,98 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
* -EINVAL: output error * -EINVAL: output error
* -ENOMEM: output is truncated * -ENOMEM: output is truncated
*/ */
static int create_modalias(struct acpi_device *acpi_dev, char *modalias, static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
int size) int size)
{ {
int len; int len;
int count; int count;
struct acpi_hardware_id *id; struct acpi_hardware_id *id;
if (list_empty(&acpi_dev->pnp.ids)) /*
* Since we skip PRP0001 from the modalias below, 0 should be returned
* if PRP0001 is the only ACPI/PNP ID in the device's list.
*/
count = 0;
list_for_each_entry(id, &acpi_dev->pnp.ids, list)
if (strcmp(id->id, "PRP0001"))
count++;
if (!count)
return 0; return 0;
/* len = snprintf(modalias, size, "acpi:");
* If the device has PRP0001 we expose DT compatible modalias if (len <= 0)
* instead in form of of:NnameTCcompatible. return len;
*/
if (acpi_dev->data.of_compatible) {
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
const union acpi_object *of_compatible, *obj;
int i, nval;
char *c;
acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); size -= len;
/* DT strings are all in lower case */
for (c = buf.pointer; *c != '\0'; c++)
*c = tolower(*c);
len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
ACPI_FREE(buf.pointer); if (!strcmp(id->id, "PRP0001"))
continue;
of_compatible = acpi_dev->data.of_compatible; count = snprintf(&modalias[len], size, "%s:", id->id);
if (of_compatible->type == ACPI_TYPE_PACKAGE) { if (count < 0)
nval = of_compatible->package.count; return -EINVAL;
obj = of_compatible->package.elements;
} else { /* Must be ACPI_TYPE_STRING. */
nval = 1;
obj = of_compatible;
}
for (i = 0; i < nval; i++, obj++) {
count = snprintf(&modalias[len], size, "C%s",
obj->string.pointer);
if (count < 0)
return -EINVAL;
if (count >= size)
return -ENOMEM;
len += count; if (count >= size)
size -= count; return -ENOMEM;
}
} else {
len = snprintf(modalias, size, "acpi:");
size -= len;
list_for_each_entry(id, &acpi_dev->pnp.ids, list) { len += count;
count = snprintf(&modalias[len], size, "%s:", id->id); size -= count;
if (count < 0)
return -EINVAL;
if (count >= size)
return -ENOMEM;
len += count;
size -= count;
}
} }
modalias[len] = '\0';
return len;
}
/**
* create_of_modalias - Creates DT compatible string for modalias and uevent
* @acpi_dev: ACPI device object.
* @modalias: Buffer to print into.
* @size: Size of the buffer.
*
* Expose DT compatible modalias as of:NnameTCcompatible. This function should
* only be called for devices having PRP0001 in their list of ACPI/PNP IDs.
*/
static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
int size)
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
const union acpi_object *of_compatible, *obj;
int len, count;
int i, nval;
char *c;
acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);
/* DT strings are all in lower case */
for (c = buf.pointer; *c != '\0'; c++)
*c = tolower(*c);
len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer);
ACPI_FREE(buf.pointer);
if (len <= 0)
return len;
of_compatible = acpi_dev->data.of_compatible;
if (of_compatible->type == ACPI_TYPE_PACKAGE) {
nval = of_compatible->package.count;
obj = of_compatible->package.elements;
} else { /* Must be ACPI_TYPE_STRING. */
nval = 1;
obj = of_compatible;
}
for (i = 0; i < nval; i++, obj++) {
count = snprintf(&modalias[len], size, "C%s",
obj->string.pointer);
if (count < 0)
return -EINVAL;
if (count >= size)
return -ENOMEM;
len += count;
size -= count;
}
modalias[len] = '\0'; modalias[len] = '\0';
return len; return len;
} }
@ -194,7 +229,8 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
* *
* Check if the given device has an ACPI companion and if that companion has * Check if the given device has an ACPI companion and if that companion has
* a valid list of PNP IDs, and if the device is the first (primary) physical * a valid list of PNP IDs, and if the device is the first (primary) physical
* device associated with it. * device associated with it. Return the companion pointer if that's the case
* or NULL otherwise.
* *
* If multiple physical devices are attached to a single ACPI companion, we need * If multiple physical devices are attached to a single ACPI companion, we need
* to be careful. The usage scenario for this kind of relationship is that all * to be careful. The usage scenario for this kind of relationship is that all
@ -208,31 +244,67 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
* resources available from it but they will be matched normally using functions * resources available from it but they will be matched normally using functions
* provided by their bus types (and analogously for their modalias). * provided by their bus types (and analogously for their modalias).
*/ */
static bool acpi_companion_match(const struct device *dev) static struct acpi_device *acpi_companion_match(const struct device *dev)
{ {
struct acpi_device *adev; struct acpi_device *adev;
bool ret;
adev = ACPI_COMPANION(dev); adev = ACPI_COMPANION(dev);
if (!adev) if (!adev)
return false; return NULL;
if (list_empty(&adev->pnp.ids)) if (list_empty(&adev->pnp.ids))
return false; return NULL;
mutex_lock(&adev->physical_node_lock); mutex_lock(&adev->physical_node_lock);
if (list_empty(&adev->physical_node_list)) { if (list_empty(&adev->physical_node_list)) {
ret = false; adev = NULL;
} else { } else {
const struct acpi_device_physical_node *node; const struct acpi_device_physical_node *node;
node = list_first_entry(&adev->physical_node_list, node = list_first_entry(&adev->physical_node_list,
struct acpi_device_physical_node, node); struct acpi_device_physical_node, node);
ret = node->dev == dev; if (node->dev != dev)
adev = NULL;
} }
mutex_unlock(&adev->physical_node_lock); mutex_unlock(&adev->physical_node_lock);
return ret; return adev;
}
static int __acpi_device_uevent_modalias(struct acpi_device *adev,
struct kobj_uevent_env *env)
{
int len;
if (!adev)
return -ENODEV;
if (list_empty(&adev->pnp.ids))
return 0;
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_pnp_modalias(adev, &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len < 0)
return len;
env->buflen += len;
if (!adev->data.of_compatible)
return 0;
if (len > 0 && add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_of_modalias(adev, &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len < 0)
return len;
env->buflen += len;
return 0;
} }
/* /*
@ -243,22 +315,41 @@ static bool acpi_companion_match(const struct device *dev)
*/ */
int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
{ {
int len; return __acpi_device_uevent_modalias(acpi_companion_match(dev), env);
if (!acpi_companion_match(dev))
return -ENODEV;
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_modalias(ACPI_COMPANION(dev), &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len <= 0)
return len;
env->buflen += len;
return 0;
} }
EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias);
static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size)
{
int len, count;
if (!adev)
return -ENODEV;
if (list_empty(&adev->pnp.ids))
return 0;
len = create_pnp_modalias(adev, buf, size - 1);
if (len < 0) {
return len;
} else if (len > 0) {
buf[len++] = '\n';
size -= len;
}
if (!adev->data.of_compatible)
return len;
count = create_of_modalias(adev, buf + len, size - 1);
if (count < 0) {
return count;
} else if (count > 0) {
len += count;
buf[len++] = '\n';
}
return len;
}
/* /*
* Creates modalias sysfs attribute for ACPI enumerated devices. * Creates modalias sysfs attribute for ACPI enumerated devices.
* Because the other buses does not support ACPI HIDs & CIDs. * Because the other buses does not support ACPI HIDs & CIDs.
@ -267,29 +358,13 @@ EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias);
*/ */
int acpi_device_modalias(struct device *dev, char *buf, int size) int acpi_device_modalias(struct device *dev, char *buf, int size)
{ {
int len; return __acpi_device_modalias(acpi_companion_match(dev), buf, size);
if (!acpi_companion_match(dev))
return -ENODEV;
len = create_modalias(ACPI_COMPANION(dev), buf, size -1);
if (len <= 0)
return len;
buf[len++] = '\n';
return len;
} }
EXPORT_SYMBOL_GPL(acpi_device_modalias); EXPORT_SYMBOL_GPL(acpi_device_modalias);
static ssize_t static ssize_t
acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) {
struct acpi_device *acpi_dev = to_acpi_device(dev); return __acpi_device_modalias(to_acpi_device(dev), buf, 1024);
int len;
len = create_modalias(acpi_dev, buf, 1024);
if (len <= 0)
return len;
buf[len++] = '\n';
return len;
} }
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
@ -894,8 +969,51 @@ static void acpi_device_remove_files(struct acpi_device *dev)
ACPI Bus operations ACPI Bus operations
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
/**
* acpi_of_match_device - Match device object using the "compatible" property.
* @adev: ACPI device object to match.
* @of_match_table: List of device IDs to match against.
*
* If @dev has an ACPI companion which has the special PRP0001 device ID in its
* list of identifiers and a _DSD object with the "compatible" property, use
* that property to match against the given list of identifiers.
*/
static bool acpi_of_match_device(struct acpi_device *adev,
const struct of_device_id *of_match_table)
{
const union acpi_object *of_compatible, *obj;
int i, nval;
if (!adev)
return false;
of_compatible = adev->data.of_compatible;
if (!of_match_table || !of_compatible)
return false;
if (of_compatible->type == ACPI_TYPE_PACKAGE) {
nval = of_compatible->package.count;
obj = of_compatible->package.elements;
} else { /* Must be ACPI_TYPE_STRING. */
nval = 1;
obj = of_compatible;
}
/* Now we can look for the driver DT compatible strings */
for (i = 0; i < nval; i++, obj++) {
const struct of_device_id *id;
for (id = of_match_table; id->compatible[0]; id++)
if (!strcasecmp(obj->string.pointer, id->compatible))
return true;
}
return false;
}
static const struct acpi_device_id *__acpi_match_device( static const struct acpi_device_id *__acpi_match_device(
struct acpi_device *device, const struct acpi_device_id *ids) struct acpi_device *device,
const struct acpi_device_id *ids,
const struct of_device_id *of_ids)
{ {
const struct acpi_device_id *id; const struct acpi_device_id *id;
struct acpi_hardware_id *hwid; struct acpi_hardware_id *hwid;
@ -904,14 +1022,27 @@ static const struct acpi_device_id *__acpi_match_device(
* If the device is not present, it is unnecessary to load device * If the device is not present, it is unnecessary to load device
* driver for it. * driver for it.
*/ */
if (!device->status.present) if (!device || !device->status.present)
return NULL; return NULL;
for (id = ids; id->id[0]; id++) list_for_each_entry(hwid, &device->pnp.ids, list) {
list_for_each_entry(hwid, &device->pnp.ids, list) /* First, check the ACPI/PNP IDs provided by the caller. */
for (id = ids; id->id[0]; id++)
if (!strcmp((char *) id->id, hwid->id)) if (!strcmp((char *) id->id, hwid->id))
return id; return id;
/*
* Next, check the special "PRP0001" ID and try to match the
* "compatible" property if found.
*
* The id returned by the below is not valid, but the only
* caller passing non-NULL of_ids here is only interested in
* whether or not the return value is NULL.
*/
if (!strcmp("PRP0001", hwid->id)
&& acpi_of_match_device(device, of_ids))
return id;
}
return NULL; return NULL;
} }
@ -929,68 +1060,26 @@ static const struct acpi_device_id *__acpi_match_device(
const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
const struct device *dev) const struct device *dev)
{ {
struct acpi_device *adev; return __acpi_match_device(acpi_companion_match(dev), ids, NULL);
acpi_handle handle = ACPI_HANDLE(dev);
if (!ids || !handle || acpi_bus_get_device(handle, &adev))
return NULL;
if (!acpi_companion_match(dev))
return NULL;
return __acpi_match_device(adev, ids);
} }
EXPORT_SYMBOL_GPL(acpi_match_device); EXPORT_SYMBOL_GPL(acpi_match_device);
int acpi_match_device_ids(struct acpi_device *device, int acpi_match_device_ids(struct acpi_device *device,
const struct acpi_device_id *ids) const struct acpi_device_id *ids)
{ {
return __acpi_match_device(device, ids) ? 0 : -ENOENT; return __acpi_match_device(device, ids, NULL) ? 0 : -ENOENT;
} }
EXPORT_SYMBOL(acpi_match_device_ids); EXPORT_SYMBOL(acpi_match_device_ids);
/* Performs match against special "PRP0001" shoehorn ACPI ID */
static bool acpi_of_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
const union acpi_object *of_compatible, *obj;
struct acpi_device *adev;
int i, nval;
adev = ACPI_COMPANION(dev);
if (!adev)
return false;
of_compatible = adev->data.of_compatible;
if (!drv->of_match_table || !of_compatible)
return false;
if (of_compatible->type == ACPI_TYPE_PACKAGE) {
nval = of_compatible->package.count;
obj = of_compatible->package.elements;
} else { /* Must be ACPI_TYPE_STRING. */
nval = 1;
obj = of_compatible;
}
/* Now we can look for the driver DT compatible strings */
for (i = 0; i < nval; i++, obj++) {
const struct of_device_id *id;
for (id = drv->of_match_table; id->compatible[0]; id++)
if (!strcasecmp(obj->string.pointer, id->compatible))
return true;
}
return false;
}
bool acpi_driver_match_device(struct device *dev, bool acpi_driver_match_device(struct device *dev,
const struct device_driver *drv) const struct device_driver *drv)
{ {
if (!drv->acpi_match_table) if (!drv->acpi_match_table)
return acpi_of_driver_match_device(dev, drv); return acpi_of_match_device(ACPI_COMPANION(dev),
drv->of_match_table);
return !!acpi_match_device(drv->acpi_match_table, dev); return !!__acpi_match_device(acpi_companion_match(dev),
drv->acpi_match_table, drv->of_match_table);
} }
EXPORT_SYMBOL_GPL(acpi_driver_match_device); EXPORT_SYMBOL_GPL(acpi_driver_match_device);
@ -1031,20 +1120,7 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv)
static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{ {
struct acpi_device *acpi_dev = to_acpi_device(dev); return __acpi_device_uevent_modalias(to_acpi_device(dev), env);
int len;
if (list_empty(&acpi_dev->pnp.ids))
return 0;
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len <= 0)
return len;
env->buflen += len;
return 0;
} }
static void acpi_device_notify(acpi_handle handle, u32 event, void *data) static void acpi_device_notify(acpi_handle handle, u32 event, void *data)
@ -1062,10 +1138,10 @@ static void acpi_device_notify_fixed(void *data)
acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device); acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device);
} }
static acpi_status acpi_device_fixed_event(void *data) static u32 acpi_device_fixed_event(void *data)
{ {
acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data); acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data);
return AE_OK; return ACPI_INTERRUPT_HANDLED;
} }
static int acpi_device_install_notify_handler(struct acpi_device *device) static int acpi_device_install_notify_handler(struct acpi_device *device)