extern struct acpi_device *acpi_root;
#define ACPI_BUS_CLASS "system_bus"
-#define ACPI_BUS_HID "ACPI_BUS"
+#define ACPI_BUS_HID "LNXSYBUS"
#define ACPI_BUS_DEVICE_NAME "System Bus"
static LIST_HEAD(acpi_device_list);
unsigned int instance_no;
struct list_head node;
};
+
+/*
+ * Creates hid/cid(s) string needed for modalias and uevent
+ * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
+ * char *modalias: "acpi:IBM0001:ACPI0001"
+*/
+static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
+ int size)
+{
+ int len;
+
+ if (!acpi_dev->flags.hardware_id)
+ return -ENODEV;
+
+ len = snprintf(modalias, size, "acpi:%s:",
+ acpi_dev->pnp.hardware_id);
+ if (len < 0 || len >= size)
+ return -EINVAL;
+ size -= len;
+
+ if (acpi_dev->flags.compatible_ids) {
+ struct acpi_compatible_id_list *cid_list;
+ int i;
+ int count;
+
+ cid_list = acpi_dev->pnp.cid_list;
+ for (i = 0; i < cid_list->count; i++) {
+ count = snprintf(&modalias[len], size, "%s:",
+ cid_list->id[i].value);
+ if (count < 0 || count >= size) {
+ printk(KERN_ERR PREFIX "%s cid[%i] exceeds event buffer size",
+ acpi_dev->pnp.device_name, i);
+ break;
+ }
+ len += count;
+ size -= count;
+ }
+ }
+
+ modalias[len] = '\0';
+ return len;
+}
+
+static ssize_t
+acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) {
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ int len;
+
+ /* Device has no HID and no CID or string is >1024 */
+ len = create_modalias(acpi_dev, buf, 1024);
+ if (len <= 0)
+ return 0;
+ buf[len++] = '\n';
+ return len;
+}
+static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
+
static int acpi_eject_operation(acpi_handle handle, int lockable)
{
struct acpi_object_list arg_list;
goto end;
}
+ if (dev->flags.hardware_id || dev->flags.compatible_ids){
+ result = device_create_file(&dev->dev, &dev_attr_modalias);
+ if(result)
+ goto end;
+ }
+
/*
* If device has _EJ0, 'eject' file is created that is used to trigger
* hot-removal function from userland.
if (ACPI_SUCCESS(status))
device_remove_file(&dev->dev, &dev_attr_eject);
+ if (dev->flags.hardware_id || dev->flags.compatible_ids)
+ device_remove_file(&dev->dev, &dev_attr_modalias);
+
if(dev->flags.hardware_id)
device_remove_file(&dev->dev, &dev_attr_hid);
if(dev->handle)
/* --------------------------------------------------------------------------
ACPI Bus operations
-------------------------------------------------------------------------- */
+
+int acpi_match_device_ids(struct acpi_device *device,
+ const struct acpi_device_id *ids)
+{
+ const struct acpi_device_id *id;
+
+ if (device->flags.hardware_id) {
+ for (id = ids; id->id[0]; id++) {
+ if (!strcmp((char*)id->id, device->pnp.hardware_id))
+ return 0;
+ }
+ }
+
+ if (device->flags.compatible_ids) {
+ struct acpi_compatible_id_list *cid_list = device->pnp.cid_list;
+ int i;
+
+ for (id = ids; id->id[0]; id++) {
+ /* compare multiple _CID entries against driver ids */
+ for (i = 0; i < cid_list->count; i++) {
+ if (!strcmp((char*)id->id,
+ cid_list->id[i].value))
+ return 0;
+ }
+ }
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(acpi_match_device_ids);
+
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);
struct acpi_driver *acpi_drv = to_acpi_driver(drv);
- return !acpi_match_ids(acpi_dev, acpi_drv->ids);
+ return !acpi_match_device_ids(acpi_dev, acpi_drv->ids);
}
-static int acpi_device_uevent(struct device *dev, char **envp, int num_envp,
- char *buffer, int buffer_size)
+static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
- int i = 0, length = 0, ret = 0;
+ int len;
- if (acpi_dev->flags.hardware_id)
- ret = add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "HWID=%s", acpi_dev->pnp.hardware_id);
- if (ret)
+ if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
- if (acpi_dev->flags.compatible_ids) {
- int j;
- struct acpi_compatible_id_list *cid_list;
-
- cid_list = acpi_dev->pnp.cid_list;
-
- for (j = 0; j < cid_list->count; j++) {
- ret = add_uevent_var(envp, num_envp, &i, buffer,
- buffer_size, &length, "COMPTID=%s",
- cid_list->id[j].value);
- if (ret)
- return -ENOMEM;
- }
- }
-
- envp[i] = NULL;
+ len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
+ sizeof(env->buf) - env->buflen);
+ if (len >= (sizeof(env->buf) - env->buflen))
+ return -ENOMEM;
+ env->buflen += len;
return 0;
}
return ;
}
-static struct bus_type acpi_bus_type = {
+struct bus_type acpi_bus_type = {
.name = "acpi",
.suspend = acpi_device_suspend,
.resume = acpi_device_resume,
device->dev.release = &acpi_device_release;
result = device_add(&device->dev);
if(result) {
- printk("Error adding device %s", device->dev.bus_id);
+ printk(KERN_ERR PREFIX "Error adding device %s", device->dev.bus_id);
goto end;
}
status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
if (ACPI_SUCCESS(status)) {
obj = buffer.pointer;
- status = acpi_get_handle(NULL, obj->string.pointer, ejd);
+ status = acpi_get_handle(ACPI_ROOT_OBJECT, obj->string.pointer,
+ ejd);
kfree(buffer.pointer);
}
return status;
return;
}
-int acpi_match_ids(struct acpi_device *device, char *ids)
-{
- if (device->flags.hardware_id)
- if (strstr(ids, device->pnp.hardware_id))
- return 0;
-
- if (device->flags.compatible_ids) {
- struct acpi_compatible_id_list *cid_list = device->pnp.cid_list;
- int i;
-
- /* compare multiple _CID entries against driver ids */
- for (i = 0; i < cid_list->count; i++) {
- if (strstr(ids, cid_list->id[i].value))
- return 0;
- }
- }
- return -ENOENT;
-}
-
static int acpi_bus_get_perf_flags(struct acpi_device *device)
{
device->performance.state = ACPI_STATE_UNKNOWN;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *package = NULL;
+ struct acpi_device_id button_device_ids[] = {
+ {"PNP0C0D", 0},
+ {"PNP0C0C", 0},
+ {"PNP0C0E", 0},
+ {"", 0},
+ };
+
/* _PRW */
status = acpi_evaluate_object(device->handle, "_PRW", NULL, &buffer);
device->wakeup.flags.valid = 1;
/* Power button, Lid switch always enable wakeup */
- if (!acpi_match_ids(device, "PNP0C0D,PNP0C0C,PNP0C0E"))
+ if (!acpi_match_device_ids(device, button_device_ids))
device->wakeup.flags.run_wake = 1;
end:
if (ACPI_SUCCESS(status))
device->flags.wake_capable = 1;
- /* TBD: Peformance management */
+ /* TBD: Performance management */
return 0;
}
return -ENODEV;
}
+/*
+ * acpi_dock_match - see if a device has a _DCK method
+ */
+static int acpi_dock_match(struct acpi_device *device)
+{
+ acpi_handle tmp;
+ return acpi_get_handle(device->handle, "_DCK", &tmp);
+}
+
static void acpi_device_set_id(struct acpi_device *device,
struct acpi_device *parent, acpi_handle handle,
int type)
char *hid = NULL;
char *uid = NULL;
struct acpi_compatible_id_list *cid_list = NULL;
+ const char *cid_add = NULL;
acpi_status status;
switch (type) {
case ACPI_BUS_TYPE_DEVICE:
status = acpi_get_object_info(handle, &buffer);
if (ACPI_FAILURE(status)) {
- printk("%s: Error reading device info\n", __FUNCTION__);
+ printk(KERN_ERR PREFIX "%s: Error reading device info\n", __func__);
return;
}
device->flags.bus_address = 1;
}
- if(!(info->valid & (ACPI_VALID_HID | ACPI_VALID_CID))){
- status = acpi_video_bus_match(device);
- if(ACPI_SUCCESS(status))
- hid = ACPI_VIDEO_HID;
+ /* If we have a video/bay/dock device, add our selfdefined
+ HID to the CID list. Like that the video/bay/dock drivers
+ will get autoloaded and the device might still match
+ against another driver.
+ */
+ if (ACPI_SUCCESS(acpi_video_bus_match(device)))
+ cid_add = ACPI_VIDEO_HID;
+ else if (ACPI_SUCCESS(acpi_bay_match(device)))
+ cid_add = ACPI_BAY_HID;
+ else if (ACPI_SUCCESS(acpi_dock_match(device)))
+ cid_add = ACPI_DOCK_HID;
- status = acpi_bay_match(device);
- if (ACPI_SUCCESS(status))
- hid = ACPI_BAY_HID;
- }
break;
case ACPI_BUS_TYPE_POWER:
hid = ACPI_POWER_HID;
strcpy(device->pnp.unique_id, uid);
device->flags.unique_id = 1;
}
- if (cid_list) {
- device->pnp.cid_list = kmalloc(cid_list->size, GFP_KERNEL);
- if (device->pnp.cid_list)
- memcpy(device->pnp.cid_list, cid_list, cid_list->size);
- else
- printk(KERN_ERR "Memory allocation error\n");
+ if (cid_list || cid_add) {
+ struct acpi_compatible_id_list *list;
+ int size = 0;
+ int count = 0;
+
+ if (cid_list) {
+ size = cid_list->size;
+ } else if (cid_add) {
+ size = sizeof(struct acpi_compatible_id_list);
+ cid_list = ACPI_ALLOCATE_ZEROED((acpi_size) size);
+ if (!cid_list) {
+ printk(KERN_ERR "Memory allocation error\n");
+ kfree(buffer.pointer);
+ return;
+ } else {
+ cid_list->count = 0;
+ cid_list->size = size;
+ }
+ }
+ if (cid_add)
+ size += sizeof(struct acpi_compatible_id);
+ list = kmalloc(size, GFP_KERNEL);
+
+ if (list) {
+ if (cid_list) {
+ memcpy(list, cid_list, cid_list->size);
+ count = cid_list->count;
+ }
+ if (cid_add) {
+ strncpy(list->id[count].value, cid_add,
+ ACPI_MAX_CID_LENGTH);
+ count++;
+ device->flags.compatible_ids = 1;
+ }
+ list->size = size;
+ list->count = count;
+ device->pnp.cid_list = list;
+ } else
+ printk(KERN_ERR PREFIX "Memory allocation error\n");
}
kfree(buffer.pointer);
acpi_bus_data_handler, device);
if (ACPI_FAILURE(status)) {
- printk("Error attaching device data\n");
+ printk(KERN_ERR PREFIX "Error attaching device data\n");
result = -ENODEV;
}
}
}
static int
+acpi_is_child_device(struct acpi_device *device,
+ int (*matcher)(struct acpi_device *))
+{
+ int result = -ENODEV;
+
+ do {
+ if (ACPI_SUCCESS(matcher(device)))
+ return AE_OK;
+ } while ((device = device->parent));
+
+ return result;
+}
+
+static int
acpi_add_single_object(struct acpi_device **child,
struct acpi_device *parent, acpi_handle handle, int type,
struct acpi_bus_ops *ops)
case ACPI_BUS_TYPE_PROCESSOR:
case ACPI_BUS_TYPE_DEVICE:
result = acpi_bus_get_status(device);
- if (ACPI_FAILURE(result) || !device->status.present) {
- result = -ENOENT;
+ if (ACPI_FAILURE(result)) {
+ result = -ENODEV;
goto end;
}
+ if (!device->status.present) {
+ /* Bay and dock should be handled even if absent */
+ if (!ACPI_SUCCESS(
+ acpi_is_child_device(device, acpi_bay_match)) &&
+ !ACPI_SUCCESS(
+ acpi_is_child_device(device, acpi_dock_match))) {
+ result = -ENODEV;
+ goto end;
+ }
+ }
break;
default:
STRUCT_TO_INT(device->status) =
return result;
}
+int __init acpi_boot_ec_enable(void);
+
static int __init acpi_scan_init(void)
{
int result;
* Enumerate devices in the ACPI namespace.
*/
result = acpi_bus_scan_fixed(acpi_root);
+
+ /* EC region might be needed at bus_scan, so enable it now */
+ acpi_boot_ec_enable();
+
if (!result)
result = acpi_bus_scan(acpi_root, &ops);