diff options
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 118 |
1 files changed, 109 insertions, 9 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9efe3e9dbf21..769e54bc9226 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -21,9 +21,15 @@ extern struct acpi_device *acpi_root; | |||
21 | #define ACPI_BUS_DEVICE_NAME "System Bus" | 21 | #define ACPI_BUS_DEVICE_NAME "System Bus" |
22 | 22 | ||
23 | static LIST_HEAD(acpi_device_list); | 23 | static LIST_HEAD(acpi_device_list); |
24 | static LIST_HEAD(acpi_bus_id_list); | ||
24 | DEFINE_SPINLOCK(acpi_device_lock); | 25 | DEFINE_SPINLOCK(acpi_device_lock); |
25 | LIST_HEAD(acpi_wakeup_device_list); | 26 | LIST_HEAD(acpi_wakeup_device_list); |
26 | 27 | ||
28 | struct acpi_device_bus_id{ | ||
29 | char bus_id[9]; | ||
30 | unsigned int instance_no; | ||
31 | struct list_head node; | ||
32 | }; | ||
27 | static int acpi_eject_operation(acpi_handle handle, int lockable) | 33 | static int acpi_eject_operation(acpi_handle handle, int lockable) |
28 | { | 34 | { |
29 | struct acpi_object_list arg_list; | 35 | struct acpi_object_list arg_list; |
@@ -103,18 +109,61 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, | |||
103 | 109 | ||
104 | static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); | 110 | static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); |
105 | 111 | ||
106 | static void acpi_device_setup_files(struct acpi_device *dev) | 112 | static ssize_t |
113 | acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) { | ||
114 | struct acpi_device *acpi_dev = to_acpi_device(dev); | ||
115 | |||
116 | return sprintf(buf, "%s\n", acpi_dev->pnp.hardware_id); | ||
117 | } | ||
118 | static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL); | ||
119 | |||
120 | static ssize_t | ||
121 | acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) { | ||
122 | struct acpi_device *acpi_dev = to_acpi_device(dev); | ||
123 | struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
124 | int result; | ||
125 | |||
126 | result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path); | ||
127 | if(result) | ||
128 | goto end; | ||
129 | |||
130 | result = sprintf(buf, "%s\n", (char*)path.pointer); | ||
131 | kfree(path.pointer); | ||
132 | end: | ||
133 | return result; | ||
134 | } | ||
135 | static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); | ||
136 | |||
137 | static int acpi_device_setup_files(struct acpi_device *dev) | ||
107 | { | 138 | { |
108 | acpi_status status; | 139 | acpi_status status; |
109 | acpi_handle temp; | 140 | acpi_handle temp; |
141 | int result = 0; | ||
110 | 142 | ||
111 | /* | 143 | /* |
112 | * If device has _EJ0, 'eject' file is created that is used to trigger | 144 | * Devices gotten from FADT don't have a "path" attribute |
113 | * hot-removal function from userland. | ||
114 | */ | 145 | */ |
146 | if(dev->handle) { | ||
147 | result = device_create_file(&dev->dev, &dev_attr_path); | ||
148 | if(result) | ||
149 | goto end; | ||
150 | } | ||
151 | |||
152 | if(dev->flags.hardware_id) { | ||
153 | result = device_create_file(&dev->dev, &dev_attr_hid); | ||
154 | if(result) | ||
155 | goto end; | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * If device has _EJ0, 'eject' file is created that is used to trigger | ||
160 | * hot-removal function from userland. | ||
161 | */ | ||
115 | status = acpi_get_handle(dev->handle, "_EJ0", &temp); | 162 | status = acpi_get_handle(dev->handle, "_EJ0", &temp); |
116 | if (ACPI_SUCCESS(status)) | 163 | if (ACPI_SUCCESS(status)) |
117 | device_create_file(&dev->dev, &dev_attr_eject); | 164 | result = device_create_file(&dev->dev, &dev_attr_eject); |
165 | end: | ||
166 | return result; | ||
118 | } | 167 | } |
119 | 168 | ||
120 | static void acpi_device_remove_files(struct acpi_device *dev) | 169 | static void acpi_device_remove_files(struct acpi_device *dev) |
@@ -129,6 +178,11 @@ static void acpi_device_remove_files(struct acpi_device *dev) | |||
129 | status = acpi_get_handle(dev->handle, "_EJ0", &temp); | 178 | status = acpi_get_handle(dev->handle, "_EJ0", &temp); |
130 | if (ACPI_SUCCESS(status)) | 179 | if (ACPI_SUCCESS(status)) |
131 | device_remove_file(&dev->dev, &dev_attr_eject); | 180 | device_remove_file(&dev->dev, &dev_attr_eject); |
181 | |||
182 | if(dev->flags.hardware_id) | ||
183 | device_remove_file(&dev->dev, &dev_attr_hid); | ||
184 | if(dev->handle) | ||
185 | device_remove_file(&dev->dev, &dev_attr_path); | ||
132 | } | 186 | } |
133 | /* -------------------------------------------------------------------------- | 187 | /* -------------------------------------------------------------------------- |
134 | ACPI Bus operations | 188 | ACPI Bus operations |
@@ -260,9 +314,12 @@ static struct bus_type acpi_bus_type = { | |||
260 | .uevent = acpi_device_uevent, | 314 | .uevent = acpi_device_uevent, |
261 | }; | 315 | }; |
262 | 316 | ||
263 | static void acpi_device_register(struct acpi_device *device, | 317 | static int acpi_device_register(struct acpi_device *device, |
264 | struct acpi_device *parent) | 318 | struct acpi_device *parent) |
265 | { | 319 | { |
320 | int result; | ||
321 | struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; | ||
322 | int found = 0; | ||
266 | /* | 323 | /* |
267 | * Linkage | 324 | * Linkage |
268 | * ------- | 325 | * ------- |
@@ -273,7 +330,33 @@ static void acpi_device_register(struct acpi_device *device, | |||
273 | INIT_LIST_HEAD(&device->g_list); | 330 | INIT_LIST_HEAD(&device->g_list); |
274 | INIT_LIST_HEAD(&device->wakeup_list); | 331 | INIT_LIST_HEAD(&device->wakeup_list); |
275 | 332 | ||
333 | new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); | ||
334 | if (!new_bus_id) { | ||
335 | printk(KERN_ERR PREFIX "Memory allocation error\n"); | ||
336 | return -ENOMEM; | ||
337 | } | ||
338 | |||
276 | spin_lock(&acpi_device_lock); | 339 | spin_lock(&acpi_device_lock); |
340 | /* | ||
341 | * Find suitable bus_id and instance number in acpi_bus_id_list | ||
342 | * If failed, create one and link it into acpi_bus_id_list | ||
343 | */ | ||
344 | list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) { | ||
345 | if(!strcmp(acpi_device_bus_id->bus_id, device->flags.hardware_id? device->pnp.hardware_id : "PNPIDNON")) { | ||
346 | acpi_device_bus_id->instance_no ++; | ||
347 | found = 1; | ||
348 | kfree(new_bus_id); | ||
349 | break; | ||
350 | } | ||
351 | } | ||
352 | if(!found) { | ||
353 | acpi_device_bus_id = new_bus_id; | ||
354 | strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "PNPIDNON"); | ||
355 | acpi_device_bus_id->instance_no = 0; | ||
356 | list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); | ||
357 | } | ||
358 | sprintf(device->dev.bus_id, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no); | ||
359 | |||
277 | if (device->parent) { | 360 | if (device->parent) { |
278 | list_add_tail(&device->node, &device->parent->children); | 361 | list_add_tail(&device->node, &device->parent->children); |
279 | list_add_tail(&device->g_list, &device->parent->g_list); | 362 | list_add_tail(&device->g_list, &device->parent->g_list); |
@@ -287,12 +370,29 @@ static void acpi_device_register(struct acpi_device *device, | |||
287 | device->dev.parent = &parent->dev; | 370 | device->dev.parent = &parent->dev; |
288 | device->dev.bus = &acpi_bus_type; | 371 | device->dev.bus = &acpi_bus_type; |
289 | device_initialize(&device->dev); | 372 | device_initialize(&device->dev); |
290 | sprintf(device->dev.bus_id, "%s", device->pnp.bus_id); | ||
291 | device->dev.release = &acpi_device_release; | 373 | device->dev.release = &acpi_device_release; |
292 | device_add(&device->dev); | 374 | result = device_add(&device->dev); |
375 | if(result) { | ||
376 | printk("Error adding device %s", device->dev.bus_id); | ||
377 | goto end; | ||
378 | } | ||
379 | |||
380 | result = acpi_device_setup_files(device); | ||
381 | if(result) | ||
382 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error creating sysfs interface for device %s\n", device->dev.bus_id)); | ||
293 | 383 | ||
294 | acpi_device_setup_files(device); | ||
295 | device->removal_type = ACPI_BUS_REMOVAL_NORMAL; | 384 | device->removal_type = ACPI_BUS_REMOVAL_NORMAL; |
385 | return 0; | ||
386 | end: | ||
387 | spin_lock(&acpi_device_lock); | ||
388 | if (device->parent) { | ||
389 | list_del(&device->node); | ||
390 | list_del(&device->g_list); | ||
391 | } else | ||
392 | list_del(&device->g_list); | ||
393 | list_del(&device->wakeup_list); | ||
394 | spin_unlock(&acpi_device_lock); | ||
395 | return result; | ||
296 | } | 396 | } |
297 | 397 | ||
298 | static void acpi_device_unregister(struct acpi_device *device, int type) | 398 | static void acpi_device_unregister(struct acpi_device *device, int type) |
@@ -1035,7 +1135,7 @@ acpi_add_single_object(struct acpi_device **child, | |||
1035 | 1135 | ||
1036 | acpi_device_get_debug_info(device, handle, type); | 1136 | acpi_device_get_debug_info(device, handle, type); |
1037 | 1137 | ||
1038 | acpi_device_register(device, parent); | 1138 | result = acpi_device_register(device, parent); |
1039 | 1139 | ||
1040 | end: | 1140 | end: |
1041 | if (!result) | 1141 | if (!result) |