Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI: add module autoloading support for ACPI enumerated devices

An ACPI enumerated device may have its compatible id strings.

To support the compatible ACPI ids (acpi_device->pnp.ids),
we introduced acpi_driver_match_device() to match
the driver->acpi_match_table and acpi_device->pnp.ids.

For those drivers, MODULE_DEVICE_TABLE(acpi, xxx) is used to
exports the driver module alias in the format of
"acpi:device_compatible_ids".
But in the mean time, the current code does not export the
ACPI compatible strings as part of the module_alias for the
ACPI enumerated devices, which will break the module autoloading.

Take the following piece of code for example,
static const struct acpi_device_id xxx_acpi_match[] = {
{ "INTABCD", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, xxx_acpi_match);

If this piece of code is used in a platform driver for
an ACPI enumerated platform device, the platform driver module_alias
is "acpi:INTABCD", but the uevent attribute of its platform device node
is "platform:INTABCD:00" (PREFIX:platform_device->name).
If this piece of code is used in an i2c driver for an ACPI enumerated
i2c device, the i2c driver module_alias is "acpi:INTABCD", but
the uevent of its i2c device node is "i2c:INTABCD:00" (PREFIX:i2c_client->name).
If this piece of code is used in an spi driver for an ACPI enumerated
spi device, the spi driver module_alias is "acpi:INTABCD", but
the uevent of its spi device node is "spi:INTABCD" (PREFIX:spi_device->modalias).

The reason why the module autoloading is not broken for now is that
the uevent file of the ACPI device node is "acpi:INTABCD".
Thus it is the ACPI device node creation that loads the platform/i2c/spi driver.

So this is a problem that will affect us the day when the ACPI bus
is removed from device model.

This patch introduces two new APIs,
one for exporting ACPI ids in uevent MODALIAS field,
and another for exporting ACPI ids in device' modalias sysfs attribute.

For any bus that supports ACPI enumerated devices, it needs to invoke
these two functions for their uevent and modalias attribute.

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Zhang Rui and committed by
Rafael J. Wysocki
6eb2451f 3d8e0090

+72
+57
drivers/acpi/scan.c
··· 116 116 return len; 117 117 } 118 118 119 + /* 120 + * Creates uevent modalias field for ACPI enumerated devices. 121 + * Because the other buses does not support ACPI HIDs & CIDs. 122 + * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: 123 + * "acpi:IBM0001:ACPI0001" 124 + */ 125 + int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) 126 + { 127 + struct acpi_device *acpi_dev; 128 + int len; 129 + 130 + acpi_dev = ACPI_COMPANION(dev); 131 + if (!acpi_dev) 132 + return -ENODEV; 133 + 134 + /* Fall back to bus specific way of modalias exporting */ 135 + if (list_empty(&acpi_dev->pnp.ids)) 136 + return -ENODEV; 137 + 138 + if (add_uevent_var(env, "MODALIAS=")) 139 + return -ENOMEM; 140 + len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], 141 + sizeof(env->buf) - env->buflen); 142 + if (len <= 0) 143 + return len; 144 + env->buflen += len; 145 + return 0; 146 + } 147 + EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); 148 + 149 + /* 150 + * Creates modalias sysfs attribute for ACPI enumerated devices. 151 + * Because the other buses does not support ACPI HIDs & CIDs. 152 + * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: 153 + * "acpi:IBM0001:ACPI0001" 154 + */ 155 + int acpi_device_modalias(struct device *dev, char *buf, int size) 156 + { 157 + struct acpi_device *acpi_dev; 158 + int len; 159 + 160 + acpi_dev = ACPI_COMPANION(dev); 161 + if (!acpi_dev) 162 + return -ENODEV; 163 + 164 + /* Fall back to bus specific way of modalias exporting */ 165 + if (list_empty(&acpi_dev->pnp.ids)) 166 + return -ENODEV; 167 + 168 + len = create_modalias(acpi_dev, buf, size -1); 169 + if (len <= 0) 170 + return len; 171 + buf[len++] = '\n'; 172 + return len; 173 + } 174 + EXPORT_SYMBOL_GPL(acpi_device_modalias); 175 + 119 176 static ssize_t 120 177 acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { 121 178 struct acpi_device *acpi_dev = to_acpi_device(dev);
+15
include/linux/acpi.h
··· 409 409 return !!acpi_match_device(drv->acpi_match_table, dev); 410 410 } 411 411 412 + int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *); 413 + int acpi_device_modalias(struct device *, char *, int); 414 + 412 415 #define ACPI_PTR(_ptr) (_ptr) 413 416 414 417 #else /* !CONFIG_ACPI */ ··· 489 486 const struct device_driver *drv) 490 487 { 491 488 return false; 489 + } 490 + 491 + static inline int acpi_device_uevent_modalias(struct device *dev, 492 + struct kobj_uevent_env *env) 493 + { 494 + return -ENODEV; 495 + } 496 + 497 + static inline int acpi_device_modalias(struct device *dev, 498 + char *buf, int size) 499 + { 500 + return -ENODEV; 492 501 } 493 502 494 503 #define ACPI_PTR(_ptr) (NULL)