aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/fw-sbp2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firewire/fw-sbp2.c')
-rw-r--r--drivers/firewire/fw-sbp2.c180
1 files changed, 109 insertions, 71 deletions
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c
index c196333e1de9..432c35aefc12 100644
--- a/drivers/firewire/fw-sbp2.c
+++ b/drivers/firewire/fw-sbp2.c
@@ -62,7 +62,8 @@ struct sbp2_device {
62 /* Timer for flushing ORBs. */ 62 /* Timer for flushing ORBs. */
63 struct timer_list orb_timer; 63 struct timer_list orb_timer;
64 64
65 struct work_struct work; 65 int retries;
66 struct delayed_work work;
66 struct Scsi_Host *scsi_host; 67 struct Scsi_Host *scsi_host;
67}; 68};
68 69
@@ -511,6 +512,75 @@ static int sbp2_agent_reset(struct fw_unit *unit)
511 512
512static int add_scsi_devices(struct fw_unit *unit); 513static int add_scsi_devices(struct fw_unit *unit);
513static void remove_scsi_devices(struct fw_unit *unit); 514static void remove_scsi_devices(struct fw_unit *unit);
515static void sbp2_reconnect(struct work_struct *work);
516
517static void sbp2_login(struct work_struct *work)
518{
519 struct sbp2_device *sd =
520 container_of(work, struct sbp2_device, work.work);
521 struct fw_unit *unit = sd->unit;
522 struct fw_device *device = fw_device(unit->device.parent);
523 struct sbp2_login_response response;
524 int generation, node_id, local_node_id, lun, retval;
525
526 /* FIXME: Make this work for multi-lun devices. */
527 lun = 0;
528
529 generation = device->card->generation;
530 node_id = device->node->node_id;
531 local_node_id = device->card->local_node->node_id;
532
533 if (sbp2_send_management_orb(unit, node_id, generation,
534 SBP2_LOGIN_REQUEST, lun, &response) < 0) {
535 if (sd->retries++ < 5) {
536 fw_error("login attempt %d for %s failed, "
537 "rescheduling\n",
538 sd->retries, unit->device.bus_id);
539 schedule_delayed_work(&sd->work, DIV_ROUND_UP(HZ, 5));
540 } else {
541 fw_error("failed to login to %s\n",
542 unit->device.bus_id);
543 remove_scsi_devices(unit);
544 }
545 return;
546 }
547
548 sd->generation = generation;
549 sd->node_id = node_id;
550 sd->address_high = local_node_id << 16;
551
552 /* Get command block agent offset and login id. */
553 sd->command_block_agent_address =
554 ((u64) response.command_block_agent.high << 32) |
555 response.command_block_agent.low;
556 sd->login_id = login_response_get_login_id(response);
557
558 fw_notify("logged in to sbp2 unit %s\n", unit->device.bus_id);
559 fw_notify(" - management_agent_address: 0x%012llx\n",
560 (unsigned long long) sd->management_agent_address);
561 fw_notify(" - command_block_agent_address: 0x%012llx\n",
562 (unsigned long long) sd->command_block_agent_address);
563 fw_notify(" - status write address: 0x%012llx\n",
564 (unsigned long long) sd->address_handler.offset);
565
566#if 0
567 /* FIXME: The linux1394 sbp2 does this last step. */
568 sbp2_set_busy_timeout(scsi_id);
569#endif
570
571 INIT_DELAYED_WORK(&sd->work, sbp2_reconnect);
572 sbp2_agent_reset(unit);
573
574 retval = add_scsi_devices(unit);
575 if (retval < 0) {
576 sbp2_send_management_orb(unit, sd->node_id, sd->generation,
577 SBP2_LOGOUT_REQUEST, sd->login_id,
578 NULL);
579 /* Set this back to sbp2_login so we fall back and
580 * retry login on bus reset. */
581 INIT_DELAYED_WORK(&sd->work, sbp2_login);
582 }
583}
514 584
515static int sbp2_probe(struct device *dev) 585static int sbp2_probe(struct device *dev)
516{ 586{
@@ -518,9 +588,7 @@ static int sbp2_probe(struct device *dev)
518 struct fw_device *device = fw_device(unit->device.parent); 588 struct fw_device *device = fw_device(unit->device.parent);
519 struct sbp2_device *sd; 589 struct sbp2_device *sd;
520 struct fw_csr_iterator ci; 590 struct fw_csr_iterator ci;
521 int i, key, value, lun, retval; 591 int i, key, value;
522 int node_id, generation, local_node_id;
523 struct sbp2_login_response response;
524 u32 model, firmware_revision; 592 u32 model, firmware_revision;
525 593
526 sd = kzalloc(sizeof *sd, GFP_KERNEL); 594 sd = kzalloc(sizeof *sd, GFP_KERNEL);
@@ -586,58 +654,10 @@ static int sbp2_probe(struct device *dev)
586 unit->device.bus_id, 654 unit->device.bus_id,
587 sd->workarounds, firmware_revision, model); 655 sd->workarounds, firmware_revision, model);
588 656
589 /* FIXME: Make this work for multi-lun devices. */ 657 /* We schedule work to do the login so we can easily
590 lun = 0; 658 * reschedule retries. */
591 659 INIT_DELAYED_WORK(&sd->work, sbp2_login);
592 generation = device->card->generation; 660 schedule_delayed_work(&sd->work, 0);
593 node_id = device->node->node_id;
594 local_node_id = device->card->local_node->node_id;
595
596 /* FIXME: We should probably do this from a keventd callback
597 * and handle retries by rescheduling the work. */
598 if (sbp2_send_management_orb(unit, node_id, generation,
599 SBP2_LOGIN_REQUEST, lun, &response) < 0) {
600 fw_core_remove_address_handler(&sd->address_handler);
601 del_timer_sync(&sd->orb_timer);
602 kfree(sd);
603 return -EBUSY;
604 }
605
606 sd->generation = generation;
607 sd->node_id = node_id;
608 sd->address_high = local_node_id << 16;
609
610 /* Get command block agent offset and login id. */
611 sd->command_block_agent_address =
612 ((u64) response.command_block_agent.high << 32) |
613 response.command_block_agent.low;
614 sd->login_id = login_response_get_login_id(response);
615
616 fw_notify("logged in to sbp2 unit %s\n", unit->device.bus_id);
617 fw_notify(" - management_agent_address: 0x%012llx\n",
618 (unsigned long long) sd->management_agent_address);
619 fw_notify(" - command_block_agent_address: 0x%012llx\n",
620 (unsigned long long) sd->command_block_agent_address);
621 fw_notify(" - status write address: 0x%012llx\n",
622 (unsigned long long) sd->address_handler.offset);
623
624#if 0
625 /* FIXME: The linux1394 sbp2 does this last step. */
626 sbp2_set_busy_timeout(scsi_id);
627#endif
628
629 sbp2_agent_reset(unit);
630
631 retval = add_scsi_devices(unit);
632 if (retval < 0) {
633 sbp2_send_management_orb(unit, sd->node_id, sd->generation,
634 SBP2_LOGOUT_REQUEST, sd->login_id,
635 NULL);
636 fw_core_remove_address_handler(&sd->address_handler);
637 del_timer_sync(&sd->orb_timer);
638 kfree(sd);
639 return retval;
640 }
641 661
642 return 0; 662 return 0;
643} 663}
@@ -663,28 +683,41 @@ static int sbp2_remove(struct device *dev)
663 683
664static void sbp2_reconnect(struct work_struct *work) 684static void sbp2_reconnect(struct work_struct *work)
665{ 685{
666 struct sbp2_device *sd = container_of(work, struct sbp2_device, work); 686 struct sbp2_device *sd =
687 container_of(work, struct sbp2_device, work.work);
667 struct fw_unit *unit = sd->unit; 688 struct fw_unit *unit = sd->unit;
668 struct fw_device *device = fw_device(unit->device.parent); 689 struct fw_device *device = fw_device(unit->device.parent);
669 int generation, node_id, local_node_id; 690 int generation, node_id, local_node_id;
670 691
671 fw_notify("in sbp2_reconnect, reconnecting to unit %s\n",
672 unit->device.bus_id);
673
674 generation = device->card->generation; 692 generation = device->card->generation;
675 node_id = device->node->node_id; 693 node_id = device->node->node_id;
676 local_node_id = device->card->local_node->node_id; 694 local_node_id = device->card->local_node->node_id;
677 695
678 sbp2_send_management_orb(unit, node_id, generation, 696 if (sbp2_send_management_orb(unit, node_id, generation,
679 SBP2_RECONNECT_REQUEST, sd->login_id, NULL); 697 SBP2_RECONNECT_REQUEST,
680 698 sd->login_id, NULL) < 0) {
681 /* FIXME: handle reconnect failures. */ 699 if (sd->retries++ < 5) {
682 700 fw_error("reconnect attempt %d for %s failed, "
683 sbp2_cancel_orbs(unit); 701 "rescheduling\n",
702 sd->retries, unit->device.bus_id);
703 } else {
704 fw_error("failed to reconnect to %s\n",
705 unit->device.bus_id);
706 /* Fall back and try to log in again. */
707 sd->retries = 0;
708 INIT_DELAYED_WORK(&sd->work, sbp2_login);
709 }
710 schedule_delayed_work(&sd->work, DIV_ROUND_UP(HZ, 5));
711 return;
712 }
684 713
685 sd->generation = generation; 714 sd->generation = generation;
686 sd->node_id = node_id; 715 sd->node_id = node_id;
687 sd->address_high = local_node_id << 16; 716 sd->address_high = local_node_id << 16;
717
718 fw_notify("reconnected to unit %s\n", unit->device.bus_id);
719 sbp2_agent_reset(unit);
720 sbp2_cancel_orbs(unit);
688} 721}
689 722
690static void sbp2_update(struct fw_unit *unit) 723static void sbp2_update(struct fw_unit *unit)
@@ -692,10 +725,9 @@ static void sbp2_update(struct fw_unit *unit)
692 struct fw_device *device = fw_device(unit->device.parent); 725 struct fw_device *device = fw_device(unit->device.parent);
693 struct sbp2_device *sd = unit->device.driver_data; 726 struct sbp2_device *sd = unit->device.driver_data;
694 727
728 sd->retries = 0;
695 fw_device_enable_phys_dma(device); 729 fw_device_enable_phys_dma(device);
696 730 schedule_delayed_work(&sd->work, 0);
697 INIT_WORK(&sd->work, sbp2_reconnect);
698 schedule_work(&sd->work);
699} 731}
700 732
701#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e 733#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e
@@ -1056,6 +1088,9 @@ static int add_scsi_devices(struct fw_unit *unit)
1056 struct sbp2_device *sd = unit->device.driver_data; 1088 struct sbp2_device *sd = unit->device.driver_data;
1057 int retval, lun; 1089 int retval, lun;
1058 1090
1091 if (sd->scsi_host != NULL)
1092 return 0;
1093
1059 sd->scsi_host = scsi_host_alloc(&scsi_driver_template, 1094 sd->scsi_host = scsi_host_alloc(&scsi_driver_template,
1060 sizeof(unsigned long)); 1095 sizeof(unsigned long));
1061 if (sd->scsi_host == NULL) { 1096 if (sd->scsi_host == NULL) {
@@ -1088,8 +1123,11 @@ static void remove_scsi_devices(struct fw_unit *unit)
1088{ 1123{
1089 struct sbp2_device *sd = unit->device.driver_data; 1124 struct sbp2_device *sd = unit->device.driver_data;
1090 1125
1091 scsi_remove_host(sd->scsi_host); 1126 if (sd->scsi_host != NULL) {
1092 scsi_host_put(sd->scsi_host); 1127 scsi_remove_host(sd->scsi_host);
1128 scsi_host_put(sd->scsi_host);
1129 }
1130 sd->scsi_host = NULL;
1093} 1131}
1094 1132
1095MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>"); 1133MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>");