This makes multiple logical units on a single target accessible to
fw-sbp2.  Successfully tested with the IOI FWB-IDE01AB dual LU bridge.

Signed-off-by: Stefan Richter <[EMAIL PROTECTED]>
---
update:
   - dynamically append logical units in a list
   - simplify config ROM parsing

 drivers/firewire/fw-sbp2.c |  195 ++++++++++++++++++++++++-------------
 1 file changed, 130 insertions(+), 65 deletions(-)

Index: linux/drivers/firewire/fw-sbp2.c
===================================================================
--- linux.orig/drivers/firewire/fw-sbp2.c
+++ linux/drivers/firewire/fw-sbp2.c
@@ -71,6 +71,7 @@ static const char sbp2_driver_name[] = "
 
 struct sbp2_logical_unit {
        struct sbp2_target *tgt;
+       struct list_head tgt_list;
        struct scsi_device *sdev;
        struct fw_address_handler address_handler;
        struct list_head orb_list;
@@ -100,7 +101,7 @@ struct sbp2_target {
 
        unsigned workarounds;
        int starget_id;
-       struct sbp2_logical_unit lu[1];
+       struct list_head lu_list;
 };
 
 #define SBP2_MAX_SG_ELEMENT_LENGTH     0xf000
@@ -113,7 +114,9 @@ struct sbp2_target {
 #define SBP2_DIRECTION_FROM_MEDIA      0x1
 
 /* Unit directory keys */
-#define SBP2_FIRMWARE_REVISION         0x3c
+#define SBP2_CSR_FIRMWARE_REVISION     0x3c
+#define SBP2_CSR_LOGICAL_UNIT_NUMBER   0x14
+#define SBP2_CSR_LOGICAL_UNIT_DIRECTORY        0xd4
 
 /* Flags for detected oddities and brokeness */
 #define SBP2_WORKAROUND_128K_MAX_TRANS 0x1
@@ -531,18 +534,21 @@ static int sbp2_agent_reset(struct sbp2_
        return 0;
 }
 
-/*  FIXME: Loop over logical units */
 static void release_sbp2_device(struct kref *kref)
 {
        struct sbp2_target *tgt = container_of(kref, struct sbp2_target, kref);
-       struct sbp2_logical_unit *lu = tgt->lu;
+       struct sbp2_logical_unit *lu, *next;
 
-       if (lu->sdev)
-               scsi_remove_device(lu->sdev);
+       list_for_each_entry_safe(lu, next, &tgt->lu_list, tgt_list) {
+               if (lu->sdev)
+                       scsi_remove_device(lu->sdev);
 
-       sbp2_send_management_orb(lu, tgt->node_id, lu->generation,
-                                SBP2_LOGOUT_REQUEST, lu->login_id, NULL);
-       fw_core_remove_address_handler(&lu->address_handler);
+               sbp2_send_management_orb(lu, tgt->node_id, lu->generation,
+                               SBP2_LOGOUT_REQUEST, lu->login_id, NULL);
+               fw_core_remove_address_handler(&lu->address_handler);
+               list_del(&lu->tgt_list);
+               kfree(lu);
+       }
        fw_notify("removed sbp2 unit %s\n", tgt->unit->device.bus_id);
        put_device(&tgt->unit->device);
        kfree(tgt);
@@ -623,70 +629,122 @@ static void sbp2_login(struct work_struc
        kref_put(&lu->tgt->kref, release_sbp2_device);
 }
 
-static atomic_t sbp2_starget_id = ATOMIC_INIT(-1);
-
-/* FIXME: Loop over luns here. */
-static int sbp2_probe(struct device *dev)
+static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry)
 {
-       struct fw_unit *unit = fw_unit(dev);
-       struct fw_device *device = fw_device(unit->device.parent);
-       struct sbp2_target *tgt;
        struct sbp2_logical_unit *lu;
-       struct fw_csr_iterator ci;
-       int i, key, value, error;
-       u32 model, firmware_revision;
 
-       error = -ENOMEM;
-       tgt = kzalloc(sizeof(*tgt), GFP_KERNEL);
-       if (!tgt)
-               goto out_error;
+       lu = kmalloc(sizeof(*lu), GFP_KERNEL);
+       if (!lu)
+               return -ENOMEM;
 
-       unit->device.driver_data = tgt;
-       tgt->unit = unit;
-       kref_init(&tgt->kref);
-       tgt->starget_id = atomic_inc_return(&sbp2_starget_id);
+       lu->address_handler.length           = 0x100;
+       lu->address_handler.address_callback = sbp2_status_write;
+       lu->address_handler.callback_data    = lu;
+
+       if (fw_core_add_address_handler(&lu->address_handler,
+                                       &fw_high_memory_region) < 0) {
+               kfree(lu);
+               return -ENOMEM;
+       }
 
-       lu = tgt->lu;
-       lu->tgt = tgt;
-       lu->lun = 0;
+       lu->tgt  = tgt;
+       lu->sdev = NULL;
+       lu->lun  = lun_entry & 0xffff;
+       lu->retries = 0;
        INIT_LIST_HEAD(&lu->orb_list);
+       INIT_DELAYED_WORK(&lu->work, sbp2_login);
 
-       lu->address_handler.length = 0x100;
-       lu->address_handler.address_callback = sbp2_status_write;
-       lu->address_handler.callback_data = lu;
+       list_add_tail(&lu->tgt_list, &tgt->lu_list);
+       return 0;
+}
 
-       error = fw_core_add_address_handler(&lu->address_handler,
-                                           &fw_high_memory_region);
-       if (error < 0)
-               goto out_free;
+static int sbp2_scan_logical_unit_dir(struct sbp2_target *tgt, u32 *directory)
+{
+       struct fw_csr_iterator ci;
+       int key, value;
+
+       fw_csr_iterator_init(&ci, directory);
+       while (fw_csr_iterator_next(&ci, &key, &value))
+               if (key == SBP2_CSR_LOGICAL_UNIT_NUMBER &&
+                   sbp2_add_logical_unit(tgt, value) < 0)
+                       return -ENOMEM;
+       return 0;
+}
 
-       error = fw_device_enable_phys_dma(device);
-       if (error < 0)
-               goto out_remove_address_handler;
+static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory,
+                             u32 *model, u32 *firmware_revision)
+{
+       struct fw_device *dev = fw_device(tgt->unit->device.parent);
+       struct fw_csr_iterator ci;
+       int key, value;
+       u32 *subdir;
 
-       /*
-        * Scan unit directory to get management agent address,
-        * firmware revison and model.  Initialize firmware_revision
-        * and model to values that wont match anything in our table.
-        */
-       firmware_revision = 0xff000000;
-       model = 0xff000000;
-       fw_csr_iterator_init(&ci, unit->directory);
+       fw_csr_iterator_init(&ci, directory);
        while (fw_csr_iterator_next(&ci, &key, &value)) {
                switch (key) {
+
                case CSR_DEPENDENT_INFO | CSR_OFFSET:
-                       tgt->management_agent_address =
-                               0xfffff0000000ULL + 4 * value;
-                       break;
-               case SBP2_FIRMWARE_REVISION:
-                       firmware_revision = value;
+                       tgt->management_agent_address
+                                       = 0xfffff0000000ULL + 4 * value;
                        break;
+
                case CSR_MODEL:
-                       model = value;
+                       *model = value;
+                       break;
+
+               case SBP2_CSR_FIRMWARE_REVISION:
+                       *firmware_revision = value;
+                       break;
+
+               case SBP2_CSR_LOGICAL_UNIT_NUMBER:
+                       if (sbp2_add_logical_unit(tgt, value) < 0)
+                               return -ENOMEM;
+                       break;
+
+               case SBP2_CSR_LOGICAL_UNIT_DIRECTORY:
+                       subdir = ci.p + value;
+                       if (subdir <= dev->config_rom + 5 ||
+                           subdir >= dev->config_rom + dev->config_rom_length)
+                               fw_error("logical unit dir out of bounds\n");
+                       else if (sbp2_scan_logical_unit_dir(tgt, subdir) < 0)
+                               return -ENOMEM;
                        break;
                }
        }
+       return 0;
+}
+
+static atomic_t sbp2_starget_id = ATOMIC_INIT(-1);
+
+static int sbp2_probe(struct device *dev)
+{
+       struct fw_unit *unit = fw_unit(dev);
+       struct fw_device *device = fw_device(unit->device.parent);
+       struct sbp2_target *tgt;
+       struct sbp2_logical_unit *lu, *next;
+       int i;
+       u32 model, firmware_revision;
+
+       tgt = kmalloc(sizeof(*tgt), GFP_KERNEL);
+       if (!tgt)
+               goto out_error;
+
+       unit->device.driver_data = tgt;
+       tgt->unit = unit;
+       kref_init(&tgt->kref);
+       INIT_LIST_HEAD(&tgt->lu_list);
+
+       /* Initialize to values that won't match anything in our table. */
+       model = ~0;
+       firmware_revision = ~0;
+       if (sbp2_scan_unit_dir(tgt, unit->directory, &model,
+                              &firmware_revision) < 0)
+               goto out_free;
+
+       if (fw_device_enable_phys_dma(device) < 0)
+               goto out_free;
 
+       tgt->workarounds = 0;
        for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) {
                if (sbp2_workarounds_table[i].firmware_revision !=
                    (firmware_revision & 0xffffff00))
@@ -704,24 +762,29 @@ static int sbp2_probe(struct device *dev
                          unit->device.bus_id,
                          tgt->workarounds, firmware_revision, model);
 
+       tgt->starget_id = atomic_inc_return(&sbp2_starget_id);
        get_device(&unit->device);
 
        /*
         * We schedule work to do the login so we can easily reschedule retries.
         * Always get the ref when scheduling work.
         */
-       INIT_DELAYED_WORK(&lu->work, sbp2_login);
-       if (schedule_delayed_work(&lu->work, 0))
-               kref_get(&tgt->kref);
+       list_for_each_entry(lu, &tgt->lu_list, tgt_list)
+               if (schedule_delayed_work(&lu->work, 0))
+                       kref_get(&tgt->kref);
 
        return 0;
 
- out_remove_address_handler:
-       fw_core_remove_address_handler(&lu->address_handler);
  out_free:
+       list_for_each_entry_safe(lu, next, &tgt->lu_list, tgt_list) {
+               fw_core_remove_address_handler(&lu->address_handler);
+               list_del(&lu->tgt_list);
+               kfree(lu);
+       }
        kfree(tgt);
+
  out_error:
-       return error;
+       return -ENOMEM;
 }
 
 static int sbp2_remove(struct device *dev)
@@ -771,17 +834,19 @@ static void sbp2_reconnect(struct work_s
        kref_put(&lu->tgt->kref, release_sbp2_device);
 }
 
-/*  FIXME: Loop over logical units */
 static void sbp2_update(struct fw_unit *unit)
 {
        struct fw_device *device = fw_device(unit->device.parent);
        struct sbp2_target *tgt = unit->device.driver_data;
-       struct sbp2_logical_unit *lu = tgt->lu;
+       struct sbp2_logical_unit *lu;
 
-       lu->retries = 0;
        fw_device_enable_phys_dma(device);
-       if (schedule_delayed_work(&lu->work, 0))
-               kref_get(&tgt->kref);
+
+       list_for_each_entry(lu, &tgt->lu_list, tgt_list) {
+               lu->retries = 0;
+               if (schedule_delayed_work(&lu->work, 0))
+                       kref_get(&tgt->kref);
+       }
 }
 
 #define SBP2_UNIT_SPEC_ID_ENTRY        0x0000609e

-- 
Stefan Richter
-=====-=-=== -=== ===-=
http://arcgraph.de/sr/

-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to