diff options
Diffstat (limited to 'drivers/firewire/fw-device.c')
-rw-r--r-- | drivers/firewire/fw-device.c | 151 |
1 files changed, 147 insertions, 4 deletions
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 59451f524fc3..71bb9d1e8196 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/idr.h> | 28 | #include <linux/idr.h> |
29 | #include <linux/rwsem.h> | 29 | #include <linux/rwsem.h> |
30 | #include <asm/semaphore.h> | 30 | #include <asm/semaphore.h> |
31 | #include <linux/ctype.h> | ||
31 | #include "fw-transaction.h" | 32 | #include "fw-transaction.h" |
32 | #include "fw-topology.h" | 33 | #include "fw-topology.h" |
33 | #include "fw-device.h" | 34 | #include "fw-device.h" |
@@ -193,6 +194,129 @@ int fw_device_enable_phys_dma(struct fw_device *device) | |||
193 | } | 194 | } |
194 | EXPORT_SYMBOL(fw_device_enable_phys_dma); | 195 | EXPORT_SYMBOL(fw_device_enable_phys_dma); |
195 | 196 | ||
197 | struct config_rom_attribute { | ||
198 | struct device_attribute attr; | ||
199 | u32 key; | ||
200 | }; | ||
201 | |||
202 | static ssize_t | ||
203 | show_immediate(struct device *dev, struct device_attribute *dattr, char *buf) | ||
204 | { | ||
205 | struct config_rom_attribute *attr = | ||
206 | container_of(dattr, struct config_rom_attribute, attr); | ||
207 | struct fw_csr_iterator ci; | ||
208 | u32 *dir; | ||
209 | int key, value; | ||
210 | |||
211 | if (is_fw_unit(dev)) | ||
212 | dir = fw_unit(dev)->directory; | ||
213 | else | ||
214 | dir = fw_device(dev)->config_rom + 5; | ||
215 | |||
216 | fw_csr_iterator_init(&ci, dir); | ||
217 | while (fw_csr_iterator_next(&ci, &key, &value)) | ||
218 | if (attr->key == key) | ||
219 | return snprintf(buf, buf ? PAGE_SIZE : 0, | ||
220 | "0x%06x\n", value); | ||
221 | |||
222 | return -ENOENT; | ||
223 | } | ||
224 | |||
225 | #define IMMEDIATE_ATTR(name, key) \ | ||
226 | { __ATTR(name, S_IRUGO, show_immediate, NULL), key } | ||
227 | |||
228 | static ssize_t | ||
229 | show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf) | ||
230 | { | ||
231 | struct config_rom_attribute *attr = | ||
232 | container_of(dattr, struct config_rom_attribute, attr); | ||
233 | struct fw_csr_iterator ci; | ||
234 | u32 *dir, *block = NULL, *p, *end; | ||
235 | int length, key, value, last_key = 0; | ||
236 | char *b; | ||
237 | |||
238 | if (is_fw_unit(dev)) | ||
239 | dir = fw_unit(dev)->directory; | ||
240 | else | ||
241 | dir = fw_device(dev)->config_rom + 5; | ||
242 | |||
243 | fw_csr_iterator_init(&ci, dir); | ||
244 | while (fw_csr_iterator_next(&ci, &key, &value)) { | ||
245 | if (attr->key == last_key && | ||
246 | key == (CSR_DESCRIPTOR | CSR_LEAF)) | ||
247 | block = ci.p - 1 + value; | ||
248 | last_key = key; | ||
249 | } | ||
250 | |||
251 | if (block == NULL) | ||
252 | return -ENOENT; | ||
253 | |||
254 | length = min(block[0] >> 16, 256U); | ||
255 | if (length < 3) | ||
256 | return -ENOENT; | ||
257 | |||
258 | if (block[1] != 0 || block[2] != 0) | ||
259 | /* Unknown encoding. */ | ||
260 | return -ENOENT; | ||
261 | |||
262 | if (buf == NULL) | ||
263 | return length * 4; | ||
264 | |||
265 | b = buf; | ||
266 | end = &block[length + 1]; | ||
267 | for (p = &block[3]; p < end; p++, b += 4) | ||
268 | * (u32 *) b = (__force u32) __cpu_to_be32(*p); | ||
269 | |||
270 | /* Strip trailing whitespace and add newline. */ | ||
271 | while (b--, (isspace(*b) || *b == '\0') && b > buf); | ||
272 | strcpy(b + 1, "\n"); | ||
273 | |||
274 | return b + 2 - buf; | ||
275 | } | ||
276 | |||
277 | #define TEXT_LEAF_ATTR(name, key) \ | ||
278 | { __ATTR(name, S_IRUGO, show_text_leaf, NULL), key } | ||
279 | |||
280 | static struct config_rom_attribute config_rom_attributes[] = { | ||
281 | IMMEDIATE_ATTR(vendor, CSR_VENDOR), | ||
282 | IMMEDIATE_ATTR(hardware_version, CSR_HARDWARE_VERSION), | ||
283 | IMMEDIATE_ATTR(specifier_id, CSR_SPECIFIER_ID), | ||
284 | IMMEDIATE_ATTR(version, CSR_VERSION), | ||
285 | IMMEDIATE_ATTR(model, CSR_MODEL), | ||
286 | TEXT_LEAF_ATTR(vendor_name, CSR_VENDOR), | ||
287 | TEXT_LEAF_ATTR(model_name, CSR_MODEL), | ||
288 | TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION), | ||
289 | }; | ||
290 | |||
291 | static void | ||
292 | remove_config_rom_attributes(struct device *dev) | ||
293 | { | ||
294 | int i; | ||
295 | |||
296 | for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++) | ||
297 | device_remove_file(dev, &config_rom_attributes[i].attr); | ||
298 | } | ||
299 | |||
300 | static int | ||
301 | add_config_rom_attributes(struct device *dev) | ||
302 | { | ||
303 | struct device_attribute *attr; | ||
304 | int i, err = 0; | ||
305 | |||
306 | for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++) { | ||
307 | attr = &config_rom_attributes[i].attr; | ||
308 | if (attr->show(dev, attr, NULL) < 0) | ||
309 | continue; | ||
310 | err = device_create_file(dev, attr); | ||
311 | if (err < 0) { | ||
312 | remove_config_rom_attributes(dev); | ||
313 | break; | ||
314 | } | ||
315 | } | ||
316 | |||
317 | return err; | ||
318 | } | ||
319 | |||
196 | static ssize_t | 320 | static ssize_t |
197 | modalias_show(struct device *dev, | 321 | modalias_show(struct device *dev, |
198 | struct device_attribute *attr, char *buf) | 322 | struct device_attribute *attr, char *buf) |
@@ -399,15 +523,26 @@ static void create_units(struct fw_device *device) | |||
399 | snprintf(unit->device.bus_id, sizeof unit->device.bus_id, | 523 | snprintf(unit->device.bus_id, sizeof unit->device.bus_id, |
400 | "%s.%d", device->device.bus_id, i++); | 524 | "%s.%d", device->device.bus_id, i++); |
401 | 525 | ||
402 | if (device_register(&unit->device) < 0) { | 526 | if (device_register(&unit->device) < 0) |
403 | kfree(unit); | 527 | goto skip_unit; |
404 | continue; | 528 | |
405 | } | 529 | if (add_config_rom_attributes(&unit->device) < 0) |
530 | goto skip_unregister; | ||
531 | |||
532 | continue; | ||
533 | |||
534 | skip_unregister: | ||
535 | device_unregister(&unit->device); | ||
536 | skip_unit: | ||
537 | kfree(unit); | ||
406 | } | 538 | } |
407 | } | 539 | } |
408 | 540 | ||
409 | static int shutdown_unit(struct device *device, void *data) | 541 | static int shutdown_unit(struct device *device, void *data) |
410 | { | 542 | { |
543 | struct fw_unit *unit = fw_unit(device); | ||
544 | |||
545 | remove_config_rom_attributes(&unit->device); | ||
411 | device_unregister(device); | 546 | device_unregister(device); |
412 | 547 | ||
413 | return 0; | 548 | return 0; |
@@ -437,6 +572,8 @@ static void fw_device_shutdown(struct work_struct *work) | |||
437 | idr_remove(&fw_device_idr, minor); | 572 | idr_remove(&fw_device_idr, minor); |
438 | up_write(&fw_bus_type.subsys.rwsem); | 573 | up_write(&fw_bus_type.subsys.rwsem); |
439 | 574 | ||
575 | remove_config_rom_attributes(&device->device); | ||
576 | |||
440 | fw_device_cdev_remove(device); | 577 | fw_device_cdev_remove(device); |
441 | device_for_each_child(&device->device, NULL, shutdown_unit); | 578 | device_for_each_child(&device->device, NULL, shutdown_unit); |
442 | device_unregister(&device->device); | 579 | device_unregister(&device->device); |
@@ -504,6 +641,10 @@ static void fw_device_init(struct work_struct *work) | |||
504 | goto error_with_cdev; | 641 | goto error_with_cdev; |
505 | } | 642 | } |
506 | 643 | ||
644 | err = add_config_rom_attributes(&device->device); | ||
645 | if (err < 0) | ||
646 | goto error_with_register; | ||
647 | |||
507 | create_units(device); | 648 | create_units(device); |
508 | 649 | ||
509 | /* Transition the device to running state. If it got pulled | 650 | /* Transition the device to running state. If it got pulled |
@@ -530,6 +671,8 @@ static void fw_device_init(struct work_struct *work) | |||
530 | 671 | ||
531 | return; | 672 | return; |
532 | 673 | ||
674 | error_with_register: | ||
675 | device_unregister(&device->device); | ||
533 | error_with_cdev: | 676 | error_with_cdev: |
534 | down_write(&fw_bus_type.subsys.rwsem); | 677 | down_write(&fw_bus_type.subsys.rwsem); |
535 | idr_remove(&fw_device_idr, minor); | 678 | idr_remove(&fw_device_idr, minor); |