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 { |