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

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.33-rc3 258 lines 6.0 kB view raw
1/* 2 * Link physical devices with ACPI devices support 3 * 4 * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com> 5 * Copyright (c) 2005 Intel Corp. 6 * 7 * This file is released under the GPLv2. 8 */ 9#include <linux/init.h> 10#include <linux/list.h> 11#include <linux/device.h> 12#include <linux/rwsem.h> 13#include <linux/acpi.h> 14 15#include "internal.h" 16 17#define ACPI_GLUE_DEBUG 0 18#if ACPI_GLUE_DEBUG 19#define DBG(x...) printk(PREFIX x) 20#else 21#define DBG(x...) do { } while(0) 22#endif 23static LIST_HEAD(bus_type_list); 24static DECLARE_RWSEM(bus_type_sem); 25 26int register_acpi_bus_type(struct acpi_bus_type *type) 27{ 28 if (acpi_disabled) 29 return -ENODEV; 30 if (type && type->bus && type->find_device) { 31 down_write(&bus_type_sem); 32 list_add_tail(&type->list, &bus_type_list); 33 up_write(&bus_type_sem); 34 printk(KERN_INFO PREFIX "bus type %s registered\n", 35 type->bus->name); 36 return 0; 37 } 38 return -ENODEV; 39} 40 41int unregister_acpi_bus_type(struct acpi_bus_type *type) 42{ 43 if (acpi_disabled) 44 return 0; 45 if (type) { 46 down_write(&bus_type_sem); 47 list_del_init(&type->list); 48 up_write(&bus_type_sem); 49 printk(KERN_INFO PREFIX "ACPI bus type %s unregistered\n", 50 type->bus->name); 51 return 0; 52 } 53 return -ENODEV; 54} 55 56static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type) 57{ 58 struct acpi_bus_type *tmp, *ret = NULL; 59 60 down_read(&bus_type_sem); 61 list_for_each_entry(tmp, &bus_type_list, list) { 62 if (tmp->bus == type) { 63 ret = tmp; 64 break; 65 } 66 } 67 up_read(&bus_type_sem); 68 return ret; 69} 70 71static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle) 72{ 73 struct acpi_bus_type *tmp; 74 int ret = -ENODEV; 75 76 down_read(&bus_type_sem); 77 list_for_each_entry(tmp, &bus_type_list, list) { 78 if (tmp->find_bridge && !tmp->find_bridge(dev, handle)) { 79 ret = 0; 80 break; 81 } 82 } 83 up_read(&bus_type_sem); 84 return ret; 85} 86 87/* Get device's handler per its address under its parent */ 88struct acpi_find_child { 89 acpi_handle handle; 90 acpi_integer address; 91}; 92 93static acpi_status 94do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv) 95{ 96 acpi_status status; 97 struct acpi_device_info *info; 98 struct acpi_find_child *find = context; 99 100 status = acpi_get_object_info(handle, &info); 101 if (ACPI_SUCCESS(status)) { 102 if (info->address == find->address) 103 find->handle = handle; 104 kfree(info); 105 } 106 return AE_OK; 107} 108 109acpi_handle acpi_get_child(acpi_handle parent, acpi_integer address) 110{ 111 struct acpi_find_child find = { NULL, address }; 112 113 if (!parent) 114 return NULL; 115 acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 116 1, do_acpi_find_child, NULL, &find, NULL); 117 return find.handle; 118} 119 120EXPORT_SYMBOL(acpi_get_child); 121 122/* Link ACPI devices with physical devices */ 123static void acpi_glue_data_handler(acpi_handle handle, 124 void *context) 125{ 126 /* we provide an empty handler */ 127} 128 129/* Note: a success call will increase reference count by one */ 130struct device *acpi_get_physical_device(acpi_handle handle) 131{ 132 acpi_status status; 133 struct device *dev; 134 135 status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev); 136 if (ACPI_SUCCESS(status)) 137 return get_device(dev); 138 return NULL; 139} 140 141EXPORT_SYMBOL(acpi_get_physical_device); 142 143static int acpi_bind_one(struct device *dev, acpi_handle handle) 144{ 145 struct acpi_device *acpi_dev; 146 acpi_status status; 147 148 if (dev->archdata.acpi_handle) { 149 dev_warn(dev, "Drivers changed 'acpi_handle'\n"); 150 return -EINVAL; 151 } 152 get_device(dev); 153 status = acpi_attach_data(handle, acpi_glue_data_handler, dev); 154 if (ACPI_FAILURE(status)) { 155 put_device(dev); 156 return -EINVAL; 157 } 158 dev->archdata.acpi_handle = handle; 159 160 status = acpi_bus_get_device(handle, &acpi_dev); 161 if (!ACPI_FAILURE(status)) { 162 int ret; 163 164 ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, 165 "firmware_node"); 166 ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, 167 "physical_node"); 168 if (acpi_dev->wakeup.flags.valid) { 169 device_set_wakeup_capable(dev, true); 170 device_set_wakeup_enable(dev, 171 acpi_dev->wakeup.state.enabled); 172 } 173 } 174 175 return 0; 176} 177 178static int acpi_unbind_one(struct device *dev) 179{ 180 if (!dev->archdata.acpi_handle) 181 return 0; 182 if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) { 183 struct acpi_device *acpi_dev; 184 185 /* acpi_get_physical_device increase refcnt by one */ 186 put_device(dev); 187 188 if (!acpi_bus_get_device(dev->archdata.acpi_handle, 189 &acpi_dev)) { 190 sysfs_remove_link(&dev->kobj, "firmware_node"); 191 sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node"); 192 } 193 194 acpi_detach_data(dev->archdata.acpi_handle, 195 acpi_glue_data_handler); 196 dev->archdata.acpi_handle = NULL; 197 /* acpi_bind_one increase refcnt by one */ 198 put_device(dev); 199 } else { 200 dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); 201 } 202 return 0; 203} 204 205static int acpi_platform_notify(struct device *dev) 206{ 207 struct acpi_bus_type *type; 208 acpi_handle handle; 209 int ret = -EINVAL; 210 211 if (!dev->bus || !dev->parent) { 212 /* bridge devices genernally haven't bus or parent */ 213 ret = acpi_find_bridge_device(dev, &handle); 214 goto end; 215 } 216 type = acpi_get_bus_type(dev->bus); 217 if (!type) { 218 DBG("No ACPI bus support for %s\n", dev_name(dev)); 219 ret = -EINVAL; 220 goto end; 221 } 222 if ((ret = type->find_device(dev, &handle)) != 0) 223 DBG("Can't get handler for %s\n", dev_name(dev)); 224 end: 225 if (!ret) 226 acpi_bind_one(dev, handle); 227 228#if ACPI_GLUE_DEBUG 229 if (!ret) { 230 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 231 232 acpi_get_name(dev->archdata.acpi_handle, 233 ACPI_FULL_PATHNAME, &buffer); 234 DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer); 235 kfree(buffer.pointer); 236 } else 237 DBG("Device %s -> No ACPI support\n", dev_name(dev)); 238#endif 239 240 return ret; 241} 242 243static int acpi_platform_notify_remove(struct device *dev) 244{ 245 acpi_unbind_one(dev); 246 return 0; 247} 248 249int __init init_acpi_device_notify(void) 250{ 251 if (platform_notify || platform_notify_remove) { 252 printk(KERN_ERR PREFIX "Can't use platform_notify\n"); 253 return 0; 254 } 255 platform_notify = acpi_platform_notify; 256 platform_notify_remove = acpi_platform_notify_remove; 257 return 0; 258}