[PATCH one more update] firewire: fw-sbp2: add support for multiple logical units per target

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <stefanr@xxxxxxxxxxxxxxxxx>
---
update 2:
   - rectify and refactor sbp2_probe

 drivers/firewire/fw-sbp2.c |  207 ++++++++++++++++++++++++-------------
 1 file changed, 135 insertions(+), 72 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,97 @@ 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;
 
-	lu = tgt->lu;
-	lu->tgt = tgt;
-	lu->lun = 0;
+	if (fw_core_add_address_handler(&lu->address_handler,
+					&fw_high_memory_region) < 0) {
+		kfree(lu);
+		return -ENOMEM;
+	}
+
+	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;
-
-	error = fw_device_enable_phys_dma(device);
-	if (error < 0)
-		goto out_remove_address_handler;
+static int sbp2_scan_logical_unit_dir(struct sbp2_target *tgt, u32 *directory)
+{
+	struct fw_csr_iterator ci;
+	int key, value;
 
-	/*
-	 * 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))
+		if (key == SBP2_CSR_LOGICAL_UNIT_NUMBER &&
+		    sbp2_add_logical_unit(tgt, value) < 0)
+			return -ENOMEM;
+	return 0;
+}
+
+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;
+
+	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 void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model,
+				  u32 firmware_revision)
+{
+	int i;
 
+	tgt->workarounds = 0;
 	for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) {
 		if (sbp2_workarounds_table[i].firmware_revision !=
 		    (firmware_revision & 0xffffff00))
@@ -701,8 +734,40 @@ static int sbp2_probe(struct device *dev
 	if (tgt->workarounds)
 		fw_notify("Workarounds for node %s: 0x%x "
 			  "(firmware_revision 0x%06x, model_id 0x%06x)\n",
-			  unit->device.bus_id,
+			  tgt->unit->device.bus_id,
 			  tgt->workarounds, firmware_revision, model);
+}
+
+static atomic_t sbp2_starget_id = ATOMIC_INIT(-1);
+
+static int sbp2_probe(struct device *dev)
+{
+	struct fw_unit *unit = fw_unit(dev);
+	struct sbp2_target *tgt;
+	struct sbp2_logical_unit *lu;
+	u32 model, firmware_revision;
+
+	tgt = kmalloc(sizeof(*tgt), GFP_KERNEL);
+	if (!tgt)
+		return -ENOMEM;
+
+	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 = 0xff000000;
+	firmware_revision = 0xff000000;
+	if (sbp2_scan_unit_dir(tgt, unit->directory, &model,
+			       &firmware_revision) < 0)
+		goto out;
+
+	if (fw_device_enable_phys_dma(fw_device(unit->device.parent)) < 0)
+		goto out;
+
+	sbp2_init_workarounds(tgt, model, firmware_revision);
+	tgt->starget_id = atomic_inc_return(&sbp2_starget_id);
 
 	get_device(&unit->device);
 
@@ -710,18 +775,15 @@ static int sbp2_probe(struct device *dev
 	 * 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:
+	kref_put(&tgt->kref, release_sbp2_device);
 
- out_remove_address_handler:
-	fw_core_remove_address_handler(&lu->address_handler);
- out_free:
-	kfree(tgt);
- out_error:
-	return error;
+	return -ENOMEM;
 }
 
 static int sbp2_remove(struct device *dev)
@@ -771,17 +833,18 @@ 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);
+	fw_device_enable_phys_dma(fw_device(unit->device.parent));
+
+	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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux