diff options
| author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2008-03-24 15:54:28 -0400 |
|---|---|---|
| committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2008-04-18 11:55:36 -0400 |
| commit | c9755e14a01987ada4063e8b4c50c2b6738d879e (patch) | |
| tree | 41a593c4b9ac10ccc4ad031510438e4551e51b5b | |
| parent | 1dadff71d6356ebb804c3f4f1d3049247e16111c (diff) | |
firewire: reread config ROM when device reset the bus
When a device changes its configuration ROM, it announces this with a
bus reset. firewire-core has to check which node initiated a bus reset
and whether any unit directories went away or were added on this node.
Tested with an IOI FWB-IDE01AB which has its link-on bit set if bus
power is available but does not respond to ROM read requests if self
power is off. This implements
- recognition of the units if self power is switched on after fw-core
gave up the initial attempt to read the config ROM,
- shutdown of the units when self power is switched off.
Also tested with a second PC running Linux/ieee1394. When the eth1394
driver is inserted and removed on that node, fw-core now notices the
addition and removal of the IPv4 unit on the ieee1394 node.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
| -rw-r--r-- | drivers/firewire/fw-card.c | 2 | ||||
| -rw-r--r-- | drivers/firewire/fw-cdev.c | 13 | ||||
| -rw-r--r-- | drivers/firewire/fw-device.c | 222 | ||||
| -rw-r--r-- | drivers/firewire/fw-device.h | 11 | ||||
| -rw-r--r-- | drivers/firewire/fw-sbp2.c | 8 | ||||
| -rw-r--r-- | drivers/firewire/fw-topology.c | 3 | ||||
| -rw-r--r-- | drivers/firewire/fw-topology.h | 11 |
7 files changed, 223 insertions, 47 deletions
diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c index 140b34d477de..7e4012db6ad2 100644 --- a/drivers/firewire/fw-card.c +++ b/drivers/firewire/fw-card.c | |||
| @@ -331,7 +331,7 @@ fw_card_bm_work(struct work_struct *work) | |||
| 331 | */ | 331 | */ |
| 332 | spin_unlock_irqrestore(&card->lock, flags); | 332 | spin_unlock_irqrestore(&card->lock, flags); |
| 333 | goto out; | 333 | goto out; |
| 334 | } else if (root_device->config_rom[2] & BIB_CMC) { | 334 | } else if (root_device->cmc) { |
| 335 | /* | 335 | /* |
| 336 | * FIXME: I suppose we should set the cmstr bit in the | 336 | * FIXME: I suppose we should set the cmstr bit in the |
| 337 | * STATE_CLEAR register of this node, as described in | 337 | * STATE_CLEAR register of this node, as described in |
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c index 46bc197a047f..4a541921a14a 100644 --- a/drivers/firewire/fw-cdev.c +++ b/drivers/firewire/fw-cdev.c | |||
| @@ -269,21 +269,28 @@ static int ioctl_get_info(struct client *client, void *buffer) | |||
| 269 | { | 269 | { |
| 270 | struct fw_cdev_get_info *get_info = buffer; | 270 | struct fw_cdev_get_info *get_info = buffer; |
| 271 | struct fw_cdev_event_bus_reset bus_reset; | 271 | struct fw_cdev_event_bus_reset bus_reset; |
| 272 | unsigned long ret = 0; | ||
| 272 | 273 | ||
| 273 | client->version = get_info->version; | 274 | client->version = get_info->version; |
| 274 | get_info->version = FW_CDEV_VERSION; | 275 | get_info->version = FW_CDEV_VERSION; |
| 275 | 276 | ||
| 277 | down_read(&fw_device_rwsem); | ||
| 278 | |||
| 276 | if (get_info->rom != 0) { | 279 | if (get_info->rom != 0) { |
| 277 | void __user *uptr = u64_to_uptr(get_info->rom); | 280 | void __user *uptr = u64_to_uptr(get_info->rom); |
| 278 | size_t want = get_info->rom_length; | 281 | size_t want = get_info->rom_length; |
| 279 | size_t have = client->device->config_rom_length * 4; | 282 | size_t have = client->device->config_rom_length * 4; |
| 280 | 283 | ||
| 281 | if (copy_to_user(uptr, client->device->config_rom, | 284 | ret = copy_to_user(uptr, client->device->config_rom, |
| 282 | min(want, have))) | 285 | min(want, have)); |
| 283 | return -EFAULT; | ||
| 284 | } | 286 | } |
| 285 | get_info->rom_length = client->device->config_rom_length * 4; | 287 | get_info->rom_length = client->device->config_rom_length * 4; |
| 286 | 288 | ||
| 289 | up_read(&fw_device_rwsem); | ||
| 290 | |||
| 291 | if (ret != 0) | ||
| 292 | return -EFAULT; | ||
| 293 | |||
| 287 | client->bus_reset_closure = get_info->bus_reset_closure; | 294 | client->bus_reset_closure = get_info->bus_reset_closure; |
| 288 | if (get_info->bus_reset != 0) { | 295 | if (get_info->bus_reset != 0) { |
| 289 | void __user *uptr = u64_to_uptr(get_info->bus_reset); | 296 | void __user *uptr = u64_to_uptr(get_info->bus_reset); |
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 20ac9a5afc37..75365cd0008a 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c | |||
| @@ -25,7 +25,7 @@ | |||
| 25 | #include <linux/device.h> | 25 | #include <linux/device.h> |
| 26 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
| 27 | #include <linux/idr.h> | 27 | #include <linux/idr.h> |
| 28 | #include <linux/rwsem.h> | 28 | #include <linux/string.h> |
| 29 | #include <asm/semaphore.h> | 29 | #include <asm/semaphore.h> |
| 30 | #include <asm/system.h> | 30 | #include <asm/system.h> |
| 31 | #include <linux/ctype.h> | 31 | #include <linux/ctype.h> |
| @@ -160,9 +160,9 @@ static void fw_device_release(struct device *dev) | |||
| 160 | * Take the card lock so we don't set this to NULL while a | 160 | * Take the card lock so we don't set this to NULL while a |
| 161 | * FW_NODE_UPDATED callback is being handled. | 161 | * FW_NODE_UPDATED callback is being handled. |
| 162 | */ | 162 | */ |
| 163 | spin_lock_irqsave(&device->card->lock, flags); | 163 | spin_lock_irqsave(&card->lock, flags); |
| 164 | device->node->data = NULL; | 164 | device->node->data = NULL; |
| 165 | spin_unlock_irqrestore(&device->card->lock, flags); | 165 | spin_unlock_irqrestore(&card->lock, flags); |
| 166 | 166 | ||
| 167 | fw_node_put(device->node); | 167 | fw_node_put(device->node); |
| 168 | kfree(device->config_rom); | 168 | kfree(device->config_rom); |
| @@ -195,7 +195,9 @@ show_immediate(struct device *dev, struct device_attribute *dattr, char *buf) | |||
| 195 | container_of(dattr, struct config_rom_attribute, attr); | 195 | container_of(dattr, struct config_rom_attribute, attr); |
| 196 | struct fw_csr_iterator ci; | 196 | struct fw_csr_iterator ci; |
| 197 | u32 *dir; | 197 | u32 *dir; |
| 198 | int key, value; | 198 | int key, value, ret = -ENOENT; |
| 199 | |||
| 200 | down_read(&fw_device_rwsem); | ||
| 199 | 201 | ||
| 200 | if (is_fw_unit(dev)) | 202 | if (is_fw_unit(dev)) |
| 201 | dir = fw_unit(dev)->directory; | 203 | dir = fw_unit(dev)->directory; |
| @@ -204,11 +206,15 @@ show_immediate(struct device *dev, struct device_attribute *dattr, char *buf) | |||
| 204 | 206 | ||
| 205 | fw_csr_iterator_init(&ci, dir); | 207 | fw_csr_iterator_init(&ci, dir); |
| 206 | while (fw_csr_iterator_next(&ci, &key, &value)) | 208 | while (fw_csr_iterator_next(&ci, &key, &value)) |
| 207 | if (attr->key == key) | 209 | if (attr->key == key) { |
| 208 | return snprintf(buf, buf ? PAGE_SIZE : 0, | 210 | ret = snprintf(buf, buf ? PAGE_SIZE : 0, |
| 209 | "0x%06x\n", value); | 211 | "0x%06x\n", value); |
| 212 | break; | ||
| 213 | } | ||
| 214 | |||
| 215 | up_read(&fw_device_rwsem); | ||
| 210 | 216 | ||
| 211 | return -ENOENT; | 217 | return ret; |
| 212 | } | 218 | } |
| 213 | 219 | ||
| 214 | #define IMMEDIATE_ATTR(name, key) \ | 220 | #define IMMEDIATE_ATTR(name, key) \ |
| @@ -221,9 +227,11 @@ show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf) | |||
| 221 | container_of(dattr, struct config_rom_attribute, attr); | 227 | container_of(dattr, struct config_rom_attribute, attr); |
| 222 | struct fw_csr_iterator ci; | 228 | struct fw_csr_iterator ci; |
| 223 | u32 *dir, *block = NULL, *p, *end; | 229 | u32 *dir, *block = NULL, *p, *end; |
| 224 | int length, key, value, last_key = 0; | 230 | int length, key, value, last_key = 0, ret = -ENOENT; |
| 225 | char *b; | 231 | char *b; |
| 226 | 232 | ||
| 233 | down_read(&fw_device_rwsem); | ||
| 234 | |||
| 227 | if (is_fw_unit(dev)) | 235 | if (is_fw_unit(dev)) |
| 228 | dir = fw_unit(dev)->directory; | 236 | dir = fw_unit(dev)->directory; |
| 229 | else | 237 | else |
| @@ -238,18 +246,20 @@ show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf) | |||
| 238 | } | 246 | } |
| 239 | 247 | ||
| 240 | if (block == NULL) | 248 | if (block == NULL) |
| 241 | return -ENOENT; | 249 | goto out; |
| 242 | 250 | ||
| 243 | length = min(block[0] >> 16, 256U); | 251 | length = min(block[0] >> 16, 256U); |
| 244 | if (length < 3) | 252 | if (length < 3) |
| 245 | return -ENOENT; | 253 | goto out; |
| 246 | 254 | ||
| 247 | if (block[1] != 0 || block[2] != 0) | 255 | if (block[1] != 0 || block[2] != 0) |
| 248 | /* Unknown encoding. */ | 256 | /* Unknown encoding. */ |
| 249 | return -ENOENT; | 257 | goto out; |
| 250 | 258 | ||
| 251 | if (buf == NULL) | 259 | if (buf == NULL) { |
| 252 | return length * 4; | 260 | ret = length * 4; |
| 261 | goto out; | ||
| 262 | } | ||
| 253 | 263 | ||
| 254 | b = buf; | 264 | b = buf; |
| 255 | end = &block[length + 1]; | 265 | end = &block[length + 1]; |
| @@ -259,8 +269,11 @@ show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf) | |||
| 259 | /* Strip trailing whitespace and add newline. */ | 269 | /* Strip trailing whitespace and add newline. */ |
| 260 | while (b--, (isspace(*b) || *b == '\0') && b > buf); | 270 | while (b--, (isspace(*b) || *b == '\0') && b > buf); |
| 261 | strcpy(b + 1, "\n"); | 271 | strcpy(b + 1, "\n"); |
| 272 | ret = b + 2 - buf; | ||
| 273 | out: | ||
| 274 | up_read(&fw_device_rwsem); | ||
| 262 | 275 | ||
| 263 | return b + 2 - buf; | 276 | return ret; |
| 264 | } | 277 | } |
| 265 | 278 | ||
| 266 | #define TEXT_LEAF_ATTR(name, key) \ | 279 | #define TEXT_LEAF_ATTR(name, key) \ |
| @@ -337,19 +350,28 @@ static ssize_t | |||
| 337 | config_rom_show(struct device *dev, struct device_attribute *attr, char *buf) | 350 | config_rom_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 338 | { | 351 | { |
| 339 | struct fw_device *device = fw_device(dev); | 352 | struct fw_device *device = fw_device(dev); |
| 353 | size_t length; | ||
| 340 | 354 | ||
| 341 | memcpy(buf, device->config_rom, device->config_rom_length * 4); | 355 | down_read(&fw_device_rwsem); |
| 356 | length = device->config_rom_length * 4; | ||
| 357 | memcpy(buf, device->config_rom, length); | ||
| 358 | up_read(&fw_device_rwsem); | ||
| 342 | 359 | ||
| 343 | return device->config_rom_length * 4; | 360 | return length; |
| 344 | } | 361 | } |
| 345 | 362 | ||
| 346 | static ssize_t | 363 | static ssize_t |
| 347 | guid_show(struct device *dev, struct device_attribute *attr, char *buf) | 364 | guid_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 348 | { | 365 | { |
| 349 | struct fw_device *device = fw_device(dev); | 366 | struct fw_device *device = fw_device(dev); |
| 367 | int ret; | ||
| 368 | |||
| 369 | down_read(&fw_device_rwsem); | ||
| 370 | ret = snprintf(buf, PAGE_SIZE, "0x%08x%08x\n", | ||
| 371 | device->config_rom[3], device->config_rom[4]); | ||
| 372 | up_read(&fw_device_rwsem); | ||
| 350 | 373 | ||
| 351 | return snprintf(buf, PAGE_SIZE, "0x%08x%08x\n", | 374 | return ret; |
| 352 | device->config_rom[3], device->config_rom[4]); | ||
| 353 | } | 375 | } |
| 354 | 376 | ||
| 355 | static struct device_attribute fw_device_attributes[] = { | 377 | static struct device_attribute fw_device_attributes[] = { |
| @@ -412,7 +434,7 @@ read_rom(struct fw_device *device, int generation, int index, u32 *data) | |||
| 412 | */ | 434 | */ |
| 413 | static int read_bus_info_block(struct fw_device *device, int generation) | 435 | static int read_bus_info_block(struct fw_device *device, int generation) |
| 414 | { | 436 | { |
| 415 | u32 *rom, *stack; | 437 | u32 *rom, *stack, *old_rom, *new_rom; |
| 416 | u32 sp, key; | 438 | u32 sp, key; |
| 417 | int i, end, length, ret = -1; | 439 | int i, end, length, ret = -1; |
| 418 | 440 | ||
| @@ -527,12 +549,19 @@ static int read_bus_info_block(struct fw_device *device, int generation) | |||
| 527 | length = i; | 549 | length = i; |
| 528 | } | 550 | } |
| 529 | 551 | ||
| 530 | device->config_rom = kmalloc(length * 4, GFP_KERNEL); | 552 | old_rom = device->config_rom; |
| 531 | if (device->config_rom == NULL) | 553 | new_rom = kmemdup(rom, length * 4, GFP_KERNEL); |
| 554 | if (new_rom == NULL) | ||
| 532 | goto out; | 555 | goto out; |
| 533 | memcpy(device->config_rom, rom, length * 4); | 556 | |
| 557 | down_write(&fw_device_rwsem); | ||
| 558 | device->config_rom = new_rom; | ||
| 534 | device->config_rom_length = length; | 559 | device->config_rom_length = length; |
| 560 | up_write(&fw_device_rwsem); | ||
| 561 | |||
| 562 | kfree(old_rom); | ||
| 535 | ret = 0; | 563 | ret = 0; |
| 564 | device->cmc = rom[2] & 1 << 30; | ||
| 536 | out: | 565 | out: |
| 537 | kfree(rom); | 566 | kfree(rom); |
| 538 | 567 | ||
| @@ -605,7 +634,14 @@ static int shutdown_unit(struct device *device, void *data) | |||
| 605 | return 0; | 634 | return 0; |
| 606 | } | 635 | } |
| 607 | 636 | ||
| 608 | static DECLARE_RWSEM(idr_rwsem); | 637 | /* |
| 638 | * fw_device_rwsem acts as dual purpose mutex: | ||
| 639 | * - serializes accesses to fw_device_idr, | ||
| 640 | * - serializes accesses to fw_device.config_rom/.config_rom_length and | ||
| 641 | * fw_unit.directory, unless those accesses happen at safe occasions | ||
| 642 | */ | ||
| 643 | DECLARE_RWSEM(fw_device_rwsem); | ||
| 644 | |||
| 609 | static DEFINE_IDR(fw_device_idr); | 645 | static DEFINE_IDR(fw_device_idr); |
| 610 | int fw_cdev_major; | 646 | int fw_cdev_major; |
| 611 | 647 | ||
| @@ -613,11 +649,11 @@ struct fw_device *fw_device_get_by_devt(dev_t devt) | |||
| 613 | { | 649 | { |
| 614 | struct fw_device *device; | 650 | struct fw_device *device; |
| 615 | 651 | ||
| 616 | down_read(&idr_rwsem); | 652 | down_read(&fw_device_rwsem); |
| 617 | device = idr_find(&fw_device_idr, MINOR(devt)); | 653 | device = idr_find(&fw_device_idr, MINOR(devt)); |
| 618 | if (device) | 654 | if (device) |
| 619 | fw_device_get(device); | 655 | fw_device_get(device); |
| 620 | up_read(&idr_rwsem); | 656 | up_read(&fw_device_rwsem); |
| 621 | 657 | ||
| 622 | return device; | 658 | return device; |
| 623 | } | 659 | } |
| @@ -632,9 +668,9 @@ static void fw_device_shutdown(struct work_struct *work) | |||
| 632 | device_for_each_child(&device->device, NULL, shutdown_unit); | 668 | device_for_each_child(&device->device, NULL, shutdown_unit); |
| 633 | device_unregister(&device->device); | 669 | device_unregister(&device->device); |
| 634 | 670 | ||
| 635 | down_write(&idr_rwsem); | 671 | down_write(&fw_device_rwsem); |
| 636 | idr_remove(&fw_device_idr, minor); | 672 | idr_remove(&fw_device_idr, minor); |
| 637 | up_write(&idr_rwsem); | 673 | up_write(&fw_device_rwsem); |
| 638 | fw_device_put(device); | 674 | fw_device_put(device); |
| 639 | } | 675 | } |
| 640 | 676 | ||
| @@ -687,10 +723,10 @@ static void fw_device_init(struct work_struct *work) | |||
| 687 | err = -ENOMEM; | 723 | err = -ENOMEM; |
| 688 | 724 | ||
| 689 | fw_device_get(device); | 725 | fw_device_get(device); |
| 690 | down_write(&idr_rwsem); | 726 | down_write(&fw_device_rwsem); |
| 691 | if (idr_pre_get(&fw_device_idr, GFP_KERNEL)) | 727 | if (idr_pre_get(&fw_device_idr, GFP_KERNEL)) |
| 692 | err = idr_get_new(&fw_device_idr, device, &minor); | 728 | err = idr_get_new(&fw_device_idr, device, &minor); |
| 693 | up_write(&idr_rwsem); | 729 | up_write(&fw_device_rwsem); |
| 694 | 730 | ||
| 695 | if (err < 0) | 731 | if (err < 0) |
| 696 | goto error; | 732 | goto error; |
| @@ -724,7 +760,7 @@ static void fw_device_init(struct work_struct *work) | |||
| 724 | if (atomic_cmpxchg(&device->state, | 760 | if (atomic_cmpxchg(&device->state, |
| 725 | FW_DEVICE_INITIALIZING, | 761 | FW_DEVICE_INITIALIZING, |
| 726 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) { | 762 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) { |
| 727 | fw_device_shutdown(&device->work.work); | 763 | fw_device_shutdown(work); |
| 728 | } else { | 764 | } else { |
| 729 | if (device->config_rom_retries) | 765 | if (device->config_rom_retries) |
| 730 | fw_notify("created device %s: GUID %08x%08x, S%d00, " | 766 | fw_notify("created device %s: GUID %08x%08x, S%d00, " |
| @@ -738,6 +774,7 @@ static void fw_device_init(struct work_struct *work) | |||
| 738 | device->device.bus_id, | 774 | device->device.bus_id, |
| 739 | device->config_rom[3], device->config_rom[4], | 775 | device->config_rom[3], device->config_rom[4], |
| 740 | 1 << device->max_speed); | 776 | 1 << device->max_speed); |
| 777 | device->config_rom_retries = 0; | ||
| 741 | } | 778 | } |
| 742 | 779 | ||
| 743 | /* | 780 | /* |
| @@ -752,9 +789,9 @@ static void fw_device_init(struct work_struct *work) | |||
| 752 | return; | 789 | return; |
| 753 | 790 | ||
| 754 | error_with_cdev: | 791 | error_with_cdev: |
| 755 | down_write(&idr_rwsem); | 792 | down_write(&fw_device_rwsem); |
| 756 | idr_remove(&fw_device_idr, minor); | 793 | idr_remove(&fw_device_idr, minor); |
| 757 | up_write(&idr_rwsem); | 794 | up_write(&fw_device_rwsem); |
| 758 | error: | 795 | error: |
| 759 | fw_device_put(device); /* fw_device_idr's reference */ | 796 | fw_device_put(device); /* fw_device_idr's reference */ |
| 760 | 797 | ||
| @@ -784,6 +821,106 @@ static void fw_device_update(struct work_struct *work) | |||
| 784 | device_for_each_child(&device->device, NULL, update_unit); | 821 | device_for_each_child(&device->device, NULL, update_unit); |
| 785 | } | 822 | } |
| 786 | 823 | ||
| 824 | enum { | ||
| 825 | REREAD_BIB_ERROR, | ||
| 826 | REREAD_BIB_GONE, | ||
| 827 | REREAD_BIB_UNCHANGED, | ||
| 828 | REREAD_BIB_CHANGED, | ||
| 829 | }; | ||
| 830 | |||
| 831 | /* Reread and compare bus info block and header of root directory */ | ||
| 832 | static int reread_bus_info_block(struct fw_device *device, int generation) | ||
| 833 | { | ||
| 834 | u32 q; | ||
| 835 | int i; | ||
| 836 | |||
| 837 | for (i = 0; i < 6; i++) { | ||
| 838 | if (read_rom(device, generation, i, &q) != RCODE_COMPLETE) | ||
| 839 | return REREAD_BIB_ERROR; | ||
| 840 | |||
| 841 | if (i == 0 && q == 0) | ||
| 842 | return REREAD_BIB_GONE; | ||
| 843 | |||
| 844 | if (i > device->config_rom_length || q != device->config_rom[i]) | ||
| 845 | return REREAD_BIB_CHANGED; | ||
| 846 | } | ||
| 847 | |||
| 848 | return REREAD_BIB_UNCHANGED; | ||
| 849 | } | ||
| 850 | |||
| 851 | static void fw_device_refresh(struct work_struct *work) | ||
| 852 | { | ||
| 853 | struct fw_device *device = | ||
| 854 | container_of(work, struct fw_device, work.work); | ||
| 855 | struct fw_card *card = device->card; | ||
| 856 | int node_id = device->node_id; | ||
| 857 | |||
| 858 | switch (reread_bus_info_block(device, device->generation)) { | ||
| 859 | case REREAD_BIB_ERROR: | ||
| 860 | if (device->config_rom_retries < MAX_RETRIES / 2 && | ||
| 861 | atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { | ||
| 862 | device->config_rom_retries++; | ||
| 863 | schedule_delayed_work(&device->work, RETRY_DELAY / 2); | ||
| 864 | |||
| 865 | return; | ||
| 866 | } | ||
| 867 | goto give_up; | ||
| 868 | |||
| 869 | case REREAD_BIB_GONE: | ||
| 870 | goto gone; | ||
| 871 | |||
| 872 | case REREAD_BIB_UNCHANGED: | ||
| 873 | if (atomic_cmpxchg(&device->state, | ||
| 874 | FW_DEVICE_INITIALIZING, | ||
| 875 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) | ||
| 876 | goto gone; | ||
| 877 | |||
| 878 | fw_device_update(work); | ||
| 879 | device->config_rom_retries = 0; | ||
| 880 | goto out; | ||
| 881 | |||
| 882 | case REREAD_BIB_CHANGED: | ||
| 883 | break; | ||
| 884 | } | ||
| 885 | |||
| 886 | /* | ||
| 887 | * Something changed. We keep things simple and don't investigate | ||
| 888 | * further. We just destroy all previous units and create new ones. | ||
| 889 | */ | ||
| 890 | device_for_each_child(&device->device, NULL, shutdown_unit); | ||
| 891 | |||
| 892 | if (read_bus_info_block(device, device->generation) < 0) { | ||
| 893 | if (device->config_rom_retries < MAX_RETRIES && | ||
| 894 | atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { | ||
| 895 | device->config_rom_retries++; | ||
| 896 | schedule_delayed_work(&device->work, RETRY_DELAY); | ||
| 897 | |||
| 898 | return; | ||
| 899 | } | ||
| 900 | goto give_up; | ||
| 901 | } | ||
| 902 | |||
| 903 | create_units(device); | ||
| 904 | |||
| 905 | if (atomic_cmpxchg(&device->state, | ||
| 906 | FW_DEVICE_INITIALIZING, | ||
| 907 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) | ||
| 908 | goto gone; | ||
| 909 | |||
| 910 | fw_notify("refreshed device %s\n", device->device.bus_id); | ||
| 911 | device->config_rom_retries = 0; | ||
| 912 | goto out; | ||
| 913 | |||
| 914 | give_up: | ||
| 915 | fw_notify("giving up on refresh of device %s\n", device->device.bus_id); | ||
| 916 | gone: | ||
| 917 | atomic_set(&device->state, FW_DEVICE_SHUTDOWN); | ||
| 918 | fw_device_shutdown(work); | ||
| 919 | out: | ||
| 920 | if (node_id == card->root_node->node_id) | ||
| 921 | schedule_delayed_work(&card->work, 0); | ||
| 922 | } | ||
| 923 | |||
| 787 | void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | 924 | void fw_node_event(struct fw_card *card, struct fw_node *node, int event) |
| 788 | { | 925 | { |
| 789 | struct fw_device *device; | 926 | struct fw_device *device; |
| @@ -793,7 +930,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | |||
| 793 | case FW_NODE_LINK_ON: | 930 | case FW_NODE_LINK_ON: |
| 794 | if (!node->link_on) | 931 | if (!node->link_on) |
| 795 | break; | 932 | break; |
| 796 | 933 | create: | |
| 797 | device = kzalloc(sizeof(*device), GFP_ATOMIC); | 934 | device = kzalloc(sizeof(*device), GFP_ATOMIC); |
| 798 | if (device == NULL) | 935 | if (device == NULL) |
| 799 | break; | 936 | break; |
| @@ -832,6 +969,23 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | |||
| 832 | schedule_delayed_work(&device->work, INITIAL_DELAY); | 969 | schedule_delayed_work(&device->work, INITIAL_DELAY); |
| 833 | break; | 970 | break; |
| 834 | 971 | ||
| 972 | case FW_NODE_INITIATED_RESET: | ||
| 973 | device = node->data; | ||
| 974 | if (device == NULL) | ||
| 975 | goto create; | ||
| 976 | |||
| 977 | device->node_id = node->node_id; | ||
| 978 | smp_wmb(); /* update node_id before generation */ | ||
| 979 | device->generation = card->generation; | ||
| 980 | if (atomic_cmpxchg(&device->state, | ||
| 981 | FW_DEVICE_RUNNING, | ||
| 982 | FW_DEVICE_INITIALIZING) == FW_DEVICE_RUNNING) { | ||
| 983 | PREPARE_DELAYED_WORK(&device->work, fw_device_refresh); | ||
| 984 | schedule_delayed_work(&device->work, | ||
| 985 | node == card->local_node ? 0 : INITIAL_DELAY); | ||
| 986 | } | ||
| 987 | break; | ||
| 988 | |||
| 835 | case FW_NODE_UPDATED: | 989 | case FW_NODE_UPDATED: |
| 836 | if (!node->link_on || node->data == NULL) | 990 | if (!node->link_on || node->data == NULL) |
| 837 | break; | 991 | break; |
diff --git a/drivers/firewire/fw-device.h b/drivers/firewire/fw-device.h index 0d771fda86de..5f131f5129da 100644 --- a/drivers/firewire/fw-device.h +++ b/drivers/firewire/fw-device.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | 21 | ||
| 22 | #include <linux/fs.h> | 22 | #include <linux/fs.h> |
| 23 | #include <linux/cdev.h> | 23 | #include <linux/cdev.h> |
| 24 | #include <linux/rwsem.h> | ||
| 24 | #include <asm/atomic.h> | 25 | #include <asm/atomic.h> |
| 25 | 26 | ||
| 26 | enum fw_device_state { | 27 | enum fw_device_state { |
| @@ -46,6 +47,11 @@ struct fw_attribute_group { | |||
| 46 | * fw_device.node_id is guaranteed to be current too. | 47 | * fw_device.node_id is guaranteed to be current too. |
| 47 | * | 48 | * |
| 48 | * The same applies to fw_device.card->node_id vs. fw_device.generation. | 49 | * The same applies to fw_device.card->node_id vs. fw_device.generation. |
| 50 | * | ||
| 51 | * fw_device.config_rom and fw_device.config_rom_length may be accessed during | ||
| 52 | * the lifetime of any fw_unit belonging to the fw_device, before device_del() | ||
| 53 | * was called on the last fw_unit. Alternatively, they may be accessed while | ||
| 54 | * holding fw_device_rwsem. | ||
| 49 | */ | 55 | */ |
| 50 | struct fw_device { | 56 | struct fw_device { |
| 51 | atomic_t state; | 57 | atomic_t state; |
| @@ -53,6 +59,7 @@ struct fw_device { | |||
| 53 | int node_id; | 59 | int node_id; |
| 54 | int generation; | 60 | int generation; |
| 55 | unsigned max_speed; | 61 | unsigned max_speed; |
| 62 | bool cmc; | ||
| 56 | struct fw_card *card; | 63 | struct fw_card *card; |
| 57 | struct device device; | 64 | struct device device; |
| 58 | struct list_head link; | 65 | struct list_head link; |
| @@ -92,8 +99,12 @@ int fw_device_enable_phys_dma(struct fw_device *device); | |||
| 92 | void fw_device_cdev_update(struct fw_device *device); | 99 | void fw_device_cdev_update(struct fw_device *device); |
| 93 | void fw_device_cdev_remove(struct fw_device *device); | 100 | void fw_device_cdev_remove(struct fw_device *device); |
| 94 | 101 | ||
| 102 | extern struct rw_semaphore fw_device_rwsem; | ||
| 95 | extern int fw_cdev_major; | 103 | extern int fw_cdev_major; |
| 96 | 104 | ||
| 105 | /* | ||
| 106 | * fw_unit.directory must not be accessed after device_del(&fw_unit.device). | ||
| 107 | */ | ||
| 97 | struct fw_unit { | 108 | struct fw_unit { |
| 98 | struct device device; | 109 | struct device device; |
| 99 | u32 *directory; | 110 | u32 *directory; |
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index e99a33fcc923..2a999373863e 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c | |||
| @@ -153,6 +153,7 @@ struct sbp2_target { | |||
| 153 | struct list_head lu_list; | 153 | struct list_head lu_list; |
| 154 | 154 | ||
| 155 | u64 management_agent_address; | 155 | u64 management_agent_address; |
| 156 | u64 guid; | ||
| 156 | int directory_id; | 157 | int directory_id; |
| 157 | int node_id; | 158 | int node_id; |
| 158 | int address_high; | 159 | int address_high; |
| @@ -1114,6 +1115,7 @@ static int sbp2_probe(struct device *dev) | |||
| 1114 | kref_init(&tgt->kref); | 1115 | kref_init(&tgt->kref); |
| 1115 | INIT_LIST_HEAD(&tgt->lu_list); | 1116 | INIT_LIST_HEAD(&tgt->lu_list); |
| 1116 | tgt->bus_id = unit->device.bus_id; | 1117 | tgt->bus_id = unit->device.bus_id; |
| 1118 | tgt->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; | ||
| 1117 | 1119 | ||
| 1118 | if (fw_device_enable_phys_dma(device) < 0) | 1120 | if (fw_device_enable_phys_dma(device) < 0) |
| 1119 | goto fail_shost_put; | 1121 | goto fail_shost_put; |
| @@ -1571,16 +1573,14 @@ sbp2_sysfs_ieee1394_id_show(struct device *dev, struct device_attribute *attr, | |||
| 1571 | { | 1573 | { |
| 1572 | struct scsi_device *sdev = to_scsi_device(dev); | 1574 | struct scsi_device *sdev = to_scsi_device(dev); |
| 1573 | struct sbp2_logical_unit *lu; | 1575 | struct sbp2_logical_unit *lu; |
| 1574 | struct fw_device *device; | ||
| 1575 | 1576 | ||
| 1576 | if (!sdev) | 1577 | if (!sdev) |
| 1577 | return 0; | 1578 | return 0; |
| 1578 | 1579 | ||
| 1579 | lu = sdev->hostdata; | 1580 | lu = sdev->hostdata; |
| 1580 | device = fw_device(lu->tgt->unit->device.parent); | ||
| 1581 | 1581 | ||
| 1582 | return sprintf(buf, "%08x%08x:%06x:%04x\n", | 1582 | return sprintf(buf, "%016llx:%06x:%04x\n", |
| 1583 | device->config_rom[3], device->config_rom[4], | 1583 | (unsigned long long)lu->tgt->guid, |
| 1584 | lu->tgt->directory_id, lu->lun); | 1584 | lu->tgt->directory_id, lu->lun); |
| 1585 | } | 1585 | } |
| 1586 | 1586 | ||
diff --git a/drivers/firewire/fw-topology.c b/drivers/firewire/fw-topology.c index d2c7a3d7e1cb..ebdec4c6c689 100644 --- a/drivers/firewire/fw-topology.c +++ b/drivers/firewire/fw-topology.c | |||
| @@ -108,6 +108,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color) | |||
| 108 | node->node_id = LOCAL_BUS | SELF_ID_PHY_ID(sid); | 108 | node->node_id = LOCAL_BUS | SELF_ID_PHY_ID(sid); |
| 109 | node->link_on = SELF_ID_LINK_ON(sid); | 109 | node->link_on = SELF_ID_LINK_ON(sid); |
| 110 | node->phy_speed = SELF_ID_PHY_SPEED(sid); | 110 | node->phy_speed = SELF_ID_PHY_SPEED(sid); |
| 111 | node->initiated_reset = SELF_ID_PHY_INITIATOR(sid); | ||
| 111 | node->port_count = port_count; | 112 | node->port_count = port_count; |
| 112 | 113 | ||
| 113 | atomic_set(&node->ref_count, 1); | 114 | atomic_set(&node->ref_count, 1); |
| @@ -431,6 +432,8 @@ update_tree(struct fw_card *card, struct fw_node *root) | |||
| 431 | event = FW_NODE_LINK_OFF; | 432 | event = FW_NODE_LINK_OFF; |
| 432 | else if (!node0->link_on && node1->link_on) | 433 | else if (!node0->link_on && node1->link_on) |
| 433 | event = FW_NODE_LINK_ON; | 434 | event = FW_NODE_LINK_ON; |
| 435 | else if (node1->initiated_reset && node1->link_on) | ||
| 436 | event = FW_NODE_INITIATED_RESET; | ||
| 434 | else | 437 | else |
| 435 | event = FW_NODE_UPDATED; | 438 | event = FW_NODE_UPDATED; |
| 436 | 439 | ||
diff --git a/drivers/firewire/fw-topology.h b/drivers/firewire/fw-topology.h index cedc1ec906e9..addb9f8ea776 100644 --- a/drivers/firewire/fw-topology.h +++ b/drivers/firewire/fw-topology.h | |||
| @@ -20,11 +20,12 @@ | |||
| 20 | #define __fw_topology_h | 20 | #define __fw_topology_h |
| 21 | 21 | ||
| 22 | enum { | 22 | enum { |
| 23 | FW_NODE_CREATED = 0x00, | 23 | FW_NODE_CREATED, |
| 24 | FW_NODE_UPDATED = 0x01, | 24 | FW_NODE_UPDATED, |
| 25 | FW_NODE_DESTROYED = 0x02, | 25 | FW_NODE_DESTROYED, |
| 26 | FW_NODE_LINK_ON = 0x03, | 26 | FW_NODE_LINK_ON, |
| 27 | FW_NODE_LINK_OFF = 0x04, | 27 | FW_NODE_LINK_OFF, |
| 28 | FW_NODE_INITIATED_RESET, | ||
| 28 | }; | 29 | }; |
| 29 | 30 | ||
| 30 | struct fw_node { | 31 | struct fw_node { |
