diff options
| -rw-r--r-- | drivers/acpi/scan.c | 249 |
1 files changed, 155 insertions, 94 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8f3adf924e83..d2e3c3e3f9c9 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
| @@ -114,7 +114,12 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, | |||
| 114 | return 0; | 114 | return 0; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | /* | 117 | /** |
| 118 | * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent | ||
| 119 | * @acpi_dev: ACPI device object. | ||
| 120 | * @modalias: Buffer to print into. | ||
| 121 | * @size: Size of the buffer. | ||
| 122 | * | ||
| 118 | * Creates hid/cid(s) string needed for modalias and uevent | 123 | * Creates hid/cid(s) string needed for modalias and uevent |
| 119 | * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: | 124 | * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: |
| 120 | * char *modalias: "acpi:IBM0001:ACPI0001" | 125 | * char *modalias: "acpi:IBM0001:ACPI0001" |
| @@ -122,68 +127,98 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, | |||
| 122 | * -EINVAL: output error | 127 | * -EINVAL: output error |
| 123 | * -ENOMEM: output is truncated | 128 | * -ENOMEM: output is truncated |
| 124 | */ | 129 | */ |
| 125 | static int create_modalias(struct acpi_device *acpi_dev, char *modalias, | 130 | static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias, |
| 126 | int size) | 131 | int size) |
| 127 | { | 132 | { |
| 128 | int len; | 133 | int len; |
| 129 | int count; | 134 | int count; |
| 130 | struct acpi_hardware_id *id; | 135 | struct acpi_hardware_id *id; |
| 131 | 136 | ||
| 132 | if (list_empty(&acpi_dev->pnp.ids)) | ||
| 133 | return 0; | ||
| 134 | |||
| 135 | /* | 137 | /* |
| 136 | * If the device has PRP0001 we expose DT compatible modalias | 138 | * Since we skip PRP0001 from the modalias below, 0 should be returned |
| 137 | * instead in form of of:NnameTCcompatible. | 139 | * if PRP0001 is the only ACPI/PNP ID in the device's list. |
| 138 | */ | 140 | */ |
| 139 | if (acpi_dev->data.of_compatible) { | 141 | count = 0; |
| 140 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; | 142 | list_for_each_entry(id, &acpi_dev->pnp.ids, list) |
| 141 | const union acpi_object *of_compatible, *obj; | 143 | if (strcmp(id->id, "PRP0001")) |
| 142 | int i, nval; | 144 | count++; |
| 143 | char *c; | ||
| 144 | |||
| 145 | acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); | ||
| 146 | /* DT strings are all in lower case */ | ||
| 147 | for (c = buf.pointer; *c != '\0'; c++) | ||
| 148 | *c = tolower(*c); | ||
| 149 | |||
| 150 | len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); | ||
| 151 | ACPI_FREE(buf.pointer); | ||
| 152 | |||
| 153 | of_compatible = acpi_dev->data.of_compatible; | ||
| 154 | if (of_compatible->type == ACPI_TYPE_PACKAGE) { | ||
| 155 | nval = of_compatible->package.count; | ||
| 156 | obj = of_compatible->package.elements; | ||
| 157 | } else { /* Must be ACPI_TYPE_STRING. */ | ||
| 158 | nval = 1; | ||
| 159 | obj = of_compatible; | ||
| 160 | } | ||
| 161 | for (i = 0; i < nval; i++, obj++) { | ||
| 162 | count = snprintf(&modalias[len], size, "C%s", | ||
| 163 | obj->string.pointer); | ||
| 164 | if (count < 0) | ||
| 165 | return -EINVAL; | ||
| 166 | if (count >= size) | ||
| 167 | return -ENOMEM; | ||
| 168 | |||
| 169 | len += count; | ||
| 170 | size -= count; | ||
| 171 | } | ||
| 172 | } else { | ||
| 173 | len = snprintf(modalias, size, "acpi:"); | ||
| 174 | size -= len; | ||
| 175 | 145 | ||
| 176 | list_for_each_entry(id, &acpi_dev->pnp.ids, list) { | 146 | if (!count) |
| 177 | count = snprintf(&modalias[len], size, "%s:", id->id); | 147 | return 0; |
| 178 | if (count < 0) | 148 | |
| 179 | return -EINVAL; | 149 | len = snprintf(modalias, size, "acpi:"); |
| 180 | if (count >= size) | 150 | if (len <= 0) |
| 181 | return -ENOMEM; | 151 | return len; |
| 182 | len += count; | 152 | |
| 183 | size -= count; | 153 | size -= len; |
| 184 | } | 154 | |
| 155 | list_for_each_entry(id, &acpi_dev->pnp.ids, list) { | ||
| 156 | if (!strcmp(id->id, "PRP0001")) | ||
| 157 | continue; | ||
| 158 | |||
| 159 | count = snprintf(&modalias[len], size, "%s:", id->id); | ||
| 160 | if (count < 0) | ||
| 161 | return -EINVAL; | ||
| 162 | |||
| 163 | if (count >= size) | ||
| 164 | return -ENOMEM; | ||
| 165 | |||
| 166 | len += count; | ||
| 167 | size -= count; | ||
| 185 | } | 168 | } |
| 169 | modalias[len] = '\0'; | ||
| 170 | return len; | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * create_of_modalias - Creates DT compatible string for modalias and uevent | ||
| 175 | * @acpi_dev: ACPI device object. | ||
| 176 | * @modalias: Buffer to print into. | ||
| 177 | * @size: Size of the buffer. | ||
| 178 | * | ||
| 179 | * Expose DT compatible modalias as of:NnameTCcompatible. This function should | ||
| 180 | * only be called for devices having PRP0001 in their list of ACPI/PNP IDs. | ||
| 181 | */ | ||
| 182 | static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias, | ||
| 183 | int size) | ||
| 184 | { | ||
| 185 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; | ||
| 186 | const union acpi_object *of_compatible, *obj; | ||
| 187 | int len, count; | ||
| 188 | int i, nval; | ||
| 189 | char *c; | ||
| 186 | 190 | ||
| 191 | acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); | ||
| 192 | /* DT strings are all in lower case */ | ||
| 193 | for (c = buf.pointer; *c != '\0'; c++) | ||
| 194 | *c = tolower(*c); | ||
| 195 | |||
| 196 | len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); | ||
| 197 | ACPI_FREE(buf.pointer); | ||
| 198 | |||
| 199 | if (len <= 0) | ||
| 200 | return len; | ||
| 201 | |||
| 202 | of_compatible = acpi_dev->data.of_compatible; | ||
| 203 | if (of_compatible->type == ACPI_TYPE_PACKAGE) { | ||
| 204 | nval = of_compatible->package.count; | ||
| 205 | obj = of_compatible->package.elements; | ||
| 206 | } else { /* Must be ACPI_TYPE_STRING. */ | ||
| 207 | nval = 1; | ||
| 208 | obj = of_compatible; | ||
| 209 | } | ||
| 210 | for (i = 0; i < nval; i++, obj++) { | ||
| 211 | count = snprintf(&modalias[len], size, "C%s", | ||
| 212 | obj->string.pointer); | ||
| 213 | if (count < 0) | ||
| 214 | return -EINVAL; | ||
| 215 | |||
| 216 | if (count >= size) | ||
| 217 | return -ENOMEM; | ||
| 218 | |||
| 219 | len += count; | ||
| 220 | size -= count; | ||
| 221 | } | ||
| 187 | modalias[len] = '\0'; | 222 | modalias[len] = '\0'; |
| 188 | return len; | 223 | return len; |
| 189 | } | 224 | } |
| @@ -236,61 +271,100 @@ static struct acpi_device *acpi_companion_match(const struct device *dev) | |||
| 236 | return adev; | 271 | return adev; |
| 237 | } | 272 | } |
| 238 | 273 | ||
| 239 | /* | 274 | static int __acpi_device_uevent_modalias(struct acpi_device *adev, |
| 240 | * Creates uevent modalias field for ACPI enumerated devices. | 275 | struct kobj_uevent_env *env) |
| 241 | * Because the other buses does not support ACPI HIDs & CIDs. | ||
| 242 | * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: | ||
| 243 | * "acpi:IBM0001:ACPI0001" | ||
| 244 | */ | ||
| 245 | int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) | ||
| 246 | { | 276 | { |
| 247 | int len; | 277 | int len; |
| 248 | 278 | ||
| 249 | if (!acpi_companion_match(dev)) | 279 | if (!adev) |
| 250 | return -ENODEV; | 280 | return -ENODEV; |
| 251 | 281 | ||
| 282 | if (list_empty(&adev->pnp.ids)) | ||
| 283 | return 0; | ||
| 284 | |||
| 252 | if (add_uevent_var(env, "MODALIAS=")) | 285 | if (add_uevent_var(env, "MODALIAS=")) |
| 253 | return -ENOMEM; | 286 | return -ENOMEM; |
| 254 | len = create_modalias(ACPI_COMPANION(dev), &env->buf[env->buflen - 1], | 287 | |
| 255 | sizeof(env->buf) - env->buflen); | 288 | len = create_pnp_modalias(adev, &env->buf[env->buflen - 1], |
| 256 | if (len <= 0) | 289 | sizeof(env->buf) - env->buflen); |
| 290 | if (len < 0) | ||
| 257 | return len; | 291 | return len; |
| 292 | |||
| 293 | env->buflen += len; | ||
| 294 | if (!adev->data.of_compatible) | ||
| 295 | return 0; | ||
| 296 | |||
| 297 | if (len > 0 && add_uevent_var(env, "MODALIAS=")) | ||
| 298 | return -ENOMEM; | ||
| 299 | |||
| 300 | len = create_of_modalias(adev, &env->buf[env->buflen - 1], | ||
| 301 | sizeof(env->buf) - env->buflen); | ||
| 302 | if (len < 0) | ||
| 303 | return len; | ||
| 304 | |||
| 258 | env->buflen += len; | 305 | env->buflen += len; |
| 306 | |||
| 259 | return 0; | 307 | return 0; |
| 260 | } | 308 | } |
| 261 | EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); | ||
| 262 | 309 | ||
| 263 | /* | 310 | /* |
| 264 | * Creates modalias sysfs attribute for ACPI enumerated devices. | 311 | * Creates uevent modalias field for ACPI enumerated devices. |
| 265 | * Because the other buses does not support ACPI HIDs & CIDs. | 312 | * Because the other buses does not support ACPI HIDs & CIDs. |
| 266 | * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: | 313 | * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: |
| 267 | * "acpi:IBM0001:ACPI0001" | 314 | * "acpi:IBM0001:ACPI0001" |
| 268 | */ | 315 | */ |
| 269 | int acpi_device_modalias(struct device *dev, char *buf, int size) | 316 | int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) |
| 270 | { | 317 | { |
| 271 | int len; | 318 | return __acpi_device_uevent_modalias(acpi_companion_match(dev), env); |
| 319 | } | ||
| 320 | EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); | ||
| 321 | |||
| 322 | static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size) | ||
| 323 | { | ||
| 324 | int len, count; | ||
| 272 | 325 | ||
| 273 | if (!acpi_companion_match(dev)) | 326 | if (!adev) |
| 274 | return -ENODEV; | 327 | return -ENODEV; |
| 275 | 328 | ||
| 276 | len = create_modalias(ACPI_COMPANION(dev), buf, size -1); | 329 | if (list_empty(&adev->pnp.ids)) |
| 277 | if (len <= 0) | 330 | return 0; |
| 331 | |||
| 332 | len = create_pnp_modalias(adev, buf, size - 1); | ||
| 333 | if (len < 0) { | ||
| 278 | return len; | 334 | return len; |
| 279 | buf[len++] = '\n'; | 335 | } else if (len > 0) { |
| 336 | buf[len++] = '\n'; | ||
| 337 | size -= len; | ||
| 338 | } | ||
| 339 | if (!adev->data.of_compatible) | ||
| 340 | return len; | ||
| 341 | |||
| 342 | count = create_of_modalias(adev, buf + len, size - 1); | ||
| 343 | if (count < 0) { | ||
| 344 | return count; | ||
| 345 | } else if (count > 0) { | ||
| 346 | len += count; | ||
| 347 | buf[len++] = '\n'; | ||
| 348 | } | ||
| 349 | |||
| 280 | return len; | 350 | return len; |
| 281 | } | 351 | } |
| 352 | |||
| 353 | /* | ||
| 354 | * Creates modalias sysfs attribute for ACPI enumerated devices. | ||
| 355 | * Because the other buses does not support ACPI HIDs & CIDs. | ||
| 356 | * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: | ||
| 357 | * "acpi:IBM0001:ACPI0001" | ||
| 358 | */ | ||
| 359 | int acpi_device_modalias(struct device *dev, char *buf, int size) | ||
| 360 | { | ||
| 361 | return __acpi_device_modalias(acpi_companion_match(dev), buf, size); | ||
| 362 | } | ||
| 282 | EXPORT_SYMBOL_GPL(acpi_device_modalias); | 363 | EXPORT_SYMBOL_GPL(acpi_device_modalias); |
| 283 | 364 | ||
| 284 | static ssize_t | 365 | static ssize_t |
| 285 | acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { | 366 | acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { |
| 286 | struct acpi_device *acpi_dev = to_acpi_device(dev); | 367 | return __acpi_device_modalias(to_acpi_device(dev), buf, 1024); |
| 287 | int len; | ||
| 288 | |||
| 289 | len = create_modalias(acpi_dev, buf, 1024); | ||
| 290 | if (len <= 0) | ||
| 291 | return len; | ||
| 292 | buf[len++] = '\n'; | ||
| 293 | return len; | ||
| 294 | } | 368 | } |
| 295 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); | 369 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); |
| 296 | 370 | ||
| @@ -1046,20 +1120,7 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv) | |||
| 1046 | 1120 | ||
| 1047 | static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) | 1121 | static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) |
| 1048 | { | 1122 | { |
| 1049 | struct acpi_device *acpi_dev = to_acpi_device(dev); | 1123 | return __acpi_device_uevent_modalias(to_acpi_device(dev), env); |
| 1050 | int len; | ||
| 1051 | |||
| 1052 | if (list_empty(&acpi_dev->pnp.ids)) | ||
| 1053 | return 0; | ||
| 1054 | |||
| 1055 | if (add_uevent_var(env, "MODALIAS=")) | ||
| 1056 | return -ENOMEM; | ||
| 1057 | len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], | ||
| 1058 | sizeof(env->buf) - env->buflen); | ||
| 1059 | if (len <= 0) | ||
| 1060 | return len; | ||
| 1061 | env->buflen += len; | ||
| 1062 | return 0; | ||
| 1063 | } | 1124 | } |
| 1064 | 1125 | ||
| 1065 | static void acpi_device_notify(acpi_handle handle, u32 event, void *data) | 1126 | static void acpi_device_notify(acpi_handle handle, u32 event, void *data) |
