patch-2.4.11-dontuse linux/drivers/ieee1394/nodemgr.c

Next file: linux/drivers/ieee1394/nodemgr.h
Previous file: linux/drivers/ieee1394/ieee1394_types.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.10/linux/drivers/ieee1394/nodemgr.c linux/drivers/ieee1394/nodemgr.c
@@ -15,11 +15,13 @@
 #include <asm/atomic.h>
 #include <linux/smp_lock.h>
 #include <linux/interrupt.h>
+#include <linux/kmod.h>
 
 #include "ieee1394_types.h"
 #include "ieee1394.h"
 #include "hosts.h"
 #include "ieee1394_transactions.h"
+#include "ieee1394_hotplug.h"
 #include "highlevel.h"
 #include "csr.h"
 #include "nodemgr.h"
@@ -47,6 +49,16 @@
 static LIST_HEAD(node_list);
 static rwlock_t node_lock = RW_LOCK_UNLOCKED;
 
+static LIST_HEAD(driver_list);
+static rwlock_t driver_lock = RW_LOCK_UNLOCKED;
+
+/* The rwlock unit_directory_lock is always held when manipulating the
+ * global unit_directory_list, but this also protects access to the
+ * lists of unit directories stored in the protocol drivers.
+ */
+static LIST_HEAD(unit_directory_list);
+static rwlock_t unit_directory_lock = RW_LOCK_UNLOCKED;
+
 static LIST_HEAD(host_info_list);
 static spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED;
 
@@ -56,7 +68,12 @@
 	struct list_head list;
 };
 
-static struct node_entry *create_node_entry(void)
+static void nodemgr_process_config_rom(struct node_entry *ne, 
+				       quadlet_t busoptions);
+
+
+static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions,
+					      struct hpsb_host *host, nodeid_t nodeid)
 {
         struct node_entry *ne;
         unsigned long flags;
@@ -66,15 +83,21 @@
 
         INIT_LIST_HEAD(&ne->list);
 	INIT_LIST_HEAD(&ne->unit_directories);
-        ne->guid = (u64) -1;
-        ne->host = NULL;
-        ne->nodeid = 0;
-        atomic_set(&ne->generation, -1);
+        ne->host = host;
+        ne->nodeid = nodeid;
+        ne->guid = guid;
+	atomic_set(&ne->generation, get_hpsb_generation(ne->host));
 
         write_lock_irqsave(&node_lock, flags);
         list_add_tail(&ne->list, &node_list);
         write_unlock_irqrestore(&node_lock, flags);
 
+	nodemgr_process_config_rom (ne, busoptions);
+
+	HPSB_DEBUG("%s added: node " NODE_BUS_FMT ", GUID %016Lx",
+		   (host->node_id == nodeid) ? "Local host" : "Device",
+		   NODE_BUS_ARGS(nodeid), (unsigned long long)guid);
+
         return ne;
 }
 
@@ -104,16 +127,20 @@
 	return NULL;
 }
 
-int nodemgr_read_quadlet(struct node_entry *ne, 
+int nodemgr_read_quadlet(struct node_entry *ne,
 			 octlet_t address, quadlet_t *quad)
 {
-	if (hpsb_read(ne->host, ne->nodeid, address, quad, 4)) {
-		HPSB_DEBUG("read of address %Lx failed", address);
-		return -EAGAIN;
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < 3; i++) {
+		ret = hpsb_read(ne->host, ne->nodeid, address, quad, 4);
+		if (ret != -EAGAIN)
+			break;
 	}
 	*quad = be32_to_cpu(*quad);
 
-	return 0;
+	return ret;
 }
 
 #define CONFIG_ROM_VENDOR_ID		0x03
@@ -137,13 +164,14 @@
 	octlet_t a;
 	quadlet_t quad;
 	int length, i;
-	
 
 	if (!(ud = kmalloc (sizeof *ud, GFP_KERNEL)))
 		goto unit_directory_error;
 
 	memset (ud, 0, sizeof *ud);
+	ud->ne = ne;
 	ud->address = address;
+	ud->arb_count = 0;
 
 	if (nodemgr_read_quadlet(ne, address, &quad))
 		goto unit_directory_error;
@@ -151,7 +179,8 @@
 	a = address + 4;
 
 	for (i = 0; i < length; i++, a += 4) {
-		int code, value;
+		int code;
+		quadlet_t value;
 
 		if (nodemgr_read_quadlet(ne, a, &quad))
 			goto unit_directory_error;
@@ -183,10 +212,20 @@
 		case CONFIG_ROM_DESCRIPTOR_DIRECTORY:
 			/* TODO: read strings... icons? */
 			break;
+
+		default:
+			if (ud->arb_count < 16) {
+				/* Place them in the arbitrary pairs */
+				ud->arb_keys[ud->arb_count] = code;
+				ud->arb_values[ud->arb_count] = value;
+				ud->arb_count++;
+			}
 		}
 	}
 
-	list_add_tail (&ud->list, &ne->unit_directories);
+	list_add_tail(&ud->node_list, &ne->unit_directories);
+	list_add_tail(&ud->driver_list, &unit_directory_list);
+
 	return;
 
 unit_directory_error:	
@@ -194,27 +233,29 @@
 		kfree(ud);
 }
 
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
 static void dump_directories (struct node_entry *ne)
 {
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
 	struct list_head *l;
 
 	HPSB_DEBUG("vendor_id=0x%06x, capabilities=0x%06x",
 		   ne->vendor_id, ne->capabilities);
 	list_for_each (l, &ne->unit_directories) {
-		struct unit_directory *ud = list_entry (l, struct unit_directory, list);
+		struct unit_directory *ud = list_entry (l, struct unit_directory, node_list);
 		HPSB_DEBUG("unit directory:");
 		if (ud->flags & UNIT_DIRECTORY_VENDOR_ID)
 			HPSB_DEBUG("  vendor_id=0x%06x ", ud->vendor_id);
 		if (ud->flags & UNIT_DIRECTORY_MODEL_ID)
 			HPSB_DEBUG("  model_id=0x%06x ", ud->model_id);
 		if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
-			HPSB_DEBUG("  specifier_id=0x%06x ", ud->specifier_id);
+			HPSB_DEBUG("  sw_specifier_id=0x%06x ", ud->specifier_id);
 		if (ud->flags & UNIT_DIRECTORY_VERSION)
-			HPSB_DEBUG("  version=0x%06x ", ud->version);
+			HPSB_DEBUG("  sw_version=0x%06x ", ud->version);
 	}
-}
+#else
+	return;
 #endif
+}
 
 static void nodemgr_process_root_directory(struct node_entry *ne)
 {
@@ -260,86 +301,383 @@
 			break;
 		}
 	}
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+
 	dump_directories(ne);
+}
+
+#ifdef CONFIG_HOTPLUG
+
+static void nodemgr_call_policy(char *verb, struct unit_directory *ud)
+{
+	char *argv [3], **envp, *buf, *scratch;
+	int i = 0, value;
+
+	if (!hotplug_path [0])
+		return;
+	if (!current->fs->root)
+		return;
+	if (!(envp = (char **) kmalloc(20 * sizeof (char *), GFP_KERNEL))) {
+		HPSB_DEBUG ("ENOMEM");
+		return;
+	}
+	if (!(buf = kmalloc(256, GFP_KERNEL))) {
+		kfree(envp);
+		HPSB_DEBUG("ENOMEM2");
+		return;
+	}
+
+	/* only one standardized param to hotplug command: type */
+	argv[0] = hotplug_path;
+	argv[1] = "ieee1394";
+	argv[2] = 0;
+
+	/* minimal command environment */
+	envp[i++] = "HOME=/";
+	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+	/* hint that policy agent should enter no-stdout debug mode */
+	envp[i++] = "DEBUG=kernel";
+#endif
+	/* extensible set of named bus-specific parameters,
+	 * supporting multiple driver selection algorithms.
+	 */
+	scratch = buf;
+
+	envp[i++] = scratch;
+	scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
+	envp[i++] = scratch;
+	scratch += sprintf(scratch, "VENDOR_ID=%06x", ud->ne->vendor_id) + 1;
+	envp[i++] = scratch;
+	scratch += sprintf(scratch, "GUID=%016Lx", (long long unsigned)ud->ne->guid) + 1;
+	envp[i++] = scratch;
+	scratch += sprintf(scratch, "SPECIFIER_ID=%06x", ud->specifier_id) + 1;
+	envp[i++] = scratch;
+	scratch += sprintf(scratch, "VERSION=%06x", ud->version) + 1;
+	envp[i++] = 0;
+
+	/* NOTE: user mode daemons can call the agents too */
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+	HPSB_DEBUG("NodeMgr: %s %s %016Lx", argv[0], verb, (long long unsigned)ud->ne->guid);
 #endif
+	value = call_usermodehelper(argv[0], argv, envp);
+	kfree(buf);
+	kfree(envp);
+	if (value != 0)
+		HPSB_DEBUG("NodeMgr: hotplug policy returned 0x%x", value);
 }
 
-static void register_node(struct hpsb_host *host, nodeid_t nodeid, u64 guid,
-			  quadlet_t busoptions)
+#else
+
+static inline void
+nodemgr_call_policy(char *verb, struct unit_directory *ud)
 {
-        struct node_entry *ne;
-        unsigned long flags, new = 0;
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+	HPSB_DEBUG("NodeMgr: nodemgr_call_policy(): hotplug not enabled");
+#else
+	return;
+#endif
+} 
 
-        read_lock_irqsave(&node_lock, flags);
-        ne = find_entry_by_guid(guid);
-        read_unlock_irqrestore(&node_lock, flags);
+#endif /* CONFIG_HOTPLUG */
 
-	/* New entry */
-	if (!ne) {
-		if ((ne = create_node_entry()) == NULL)
-			return;
+static void nodemgr_claim_unit_directory(struct unit_directory *ud,
+					 struct hpsb_protocol_driver *driver)
+{
+	ud->driver = driver;
+	list_del(&ud->driver_list);
+	list_add_tail(&ud->driver_list, &driver->unit_directories);
+}
+
+static void nodemgr_release_unit_directory(struct unit_directory *ud)
+{
+	ud->driver = NULL;
+	list_del(&ud->driver_list);
+	list_add_tail(&ud->driver_list, &unit_directory_list);
+}
+
+void hpsb_release_unit_directory(struct unit_directory *ud)
+{
+	unsigned long flags;
+
+	write_lock_irqsave(&unit_directory_lock, flags);
+	nodemgr_release_unit_directory(ud);
+	write_unlock_irqrestore(&unit_directory_lock, flags);
+}
+
+static void nodemgr_free_unit_directories(struct node_entry *ne)
+{
+	struct list_head *lh;
+	struct unit_directory *ud;
+
+	lh = ne->unit_directories.next;
+	while (lh != &ne->unit_directories) {
+		ud = list_entry(lh, struct unit_directory, node_list);
+		lh = lh->next;
+		if (ud->driver && ud->driver->disconnect)
+			ud->driver->disconnect(ud);
+		nodemgr_release_unit_directory(ud);
+		nodemgr_call_policy("remove", ud);
+		list_del(&ud->driver_list);
+		kfree(ud);
+	}
+}
+
+static struct ieee1394_device_id *
+nodemgr_match_driver(struct hpsb_protocol_driver *driver, 
+		     struct unit_directory *ud)
+{
+	struct ieee1394_device_id *id;
+
+	for (id = driver->id_table; id->match_flags != 0; id++) {
+		if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) &&
+		    id->vendor_id != ud->vendor_id)
+			continue;
+
+		if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) &&
+		    id->model_id != ud->model_id)
+			continue;
+
+		if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) &&
+		    id->specifier_id != ud->specifier_id)
+			continue;
+
+		if ((id->match_flags & IEEE1394_MATCH_VERSION) &&
+		    id->version != ud->version)
+			continue;
+
+		return id;
+	}
+
+	return NULL;
+}
+
+static struct hpsb_protocol_driver *
+nodemgr_find_driver(struct unit_directory *ud)
+{
+	struct list_head *l;
+	struct hpsb_protocol_driver *match, *driver;
+	struct ieee1394_device_id *device_id;
+
+	match = NULL;
+	list_for_each(l, &driver_list) {
+		driver = list_entry(l, struct hpsb_protocol_driver, list);
+		device_id = nodemgr_match_driver(driver, ud);
+
+		if (device_id != NULL) {
+			match = driver;
+			break;
+		}
+	}
+
+	return match;
+}
+
+static void nodemgr_bind_drivers (struct node_entry *ne)
+{
+	struct list_head *lh;
+	struct hpsb_protocol_driver *driver;
+	struct unit_directory *ud;
 
-		HPSB_DEBUG("%s added: node " NODE_BUS_FMT
-			   ", GUID %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-			   (host->node_id == nodeid) ? "Local host" : "Device",
-			   NODE_BUS_ARGS(nodeid), ((u8 *)&guid)[0],
-			   ((u8 *)&guid)[1], ((u8 *)&guid)[2], ((u8 *)&guid)[3],
-			   ((u8 *)&guid)[4], ((u8 *)&guid)[5], ((u8 *)&guid)[6],
-			   ((u8 *)&guid)[7]);
+	list_for_each(lh, &ne->unit_directories) {
+		ud = list_entry(lh, struct unit_directory, node_list);
+		driver = nodemgr_find_driver(ud);
+		if (driver != NULL && driver->probe(ud) == 0)
+			nodemgr_claim_unit_directory(ud, driver);
+		nodemgr_call_policy("add", ud);
+	}
+}
+
+int hpsb_register_protocol(struct hpsb_protocol_driver *driver)
+{
+	struct unit_directory *ud;
+	struct list_head *lh;
+	unsigned long flags;
+
+        write_lock_irqsave(&driver_lock, flags);
+	list_add_tail(&driver->list, &driver_list);
+	write_unlock_irqrestore(&driver_lock, flags);
+
+	write_lock_irqsave(&unit_directory_lock, flags);
+	INIT_LIST_HEAD(&driver->unit_directories);
+	lh = unit_directory_list.next;
+	while (lh != &unit_directory_list) {
+		ud = list_entry(lh, struct unit_directory, driver_list);
+		lh = lh->next;
+		if (nodemgr_match_driver(driver, ud) && driver->probe(ud) == 0)
+			nodemgr_claim_unit_directory(ud, driver);
+	}
+	write_unlock_irqrestore(&unit_directory_lock, flags);
+
+	/*
+	 * Right now registration always succeeds, but maybe we should
+	 * detect clashes in protocols handled by other drivers.
+	 */
+
+	return 0;
+}
+
+void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver)
+{
+	struct list_head *lh;
+	struct unit_directory *ud;
+	unsigned long flags;
 
-		ne->guid = guid;
-		new = 1;
+        write_lock_irqsave(&driver_lock, flags);
+	list_del(&driver->list);
+	write_unlock_irqrestore(&driver_lock, flags);
+
+	write_lock_irqsave(&unit_directory_lock, flags);
+	lh = driver->unit_directories.next;
+	while (lh != &driver->unit_directories) {
+		ud = list_entry(lh, struct unit_directory, driver_list);
+		lh = lh->next;
+		if (ud->driver && ud->driver->disconnect)
+			ud->driver->disconnect(ud);
+		nodemgr_release_unit_directory(ud);
 	}
+	write_unlock_irqrestore(&unit_directory_lock, flags);
+}
+
+static void nodemgr_process_config_rom(struct node_entry *ne, 
+				       quadlet_t busoptions)
+{
+	unsigned long flags;
+
+	ne->busopt.irmc		= (busoptions >> 31) & 1;
+	ne->busopt.cmc		= (busoptions >> 30) & 1;
+	ne->busopt.isc		= (busoptions >> 29) & 1;
+	ne->busopt.bmc		= (busoptions >> 28) & 1;
+	ne->busopt.pmc		= (busoptions >> 27) & 1;
+	ne->busopt.cyc_clk_acc	= (busoptions >> 16) & 0xff;
+	ne->busopt.max_rec	= 1 << (((busoptions >> 12) & 0xf) + 1);
+	ne->busopt.generation	= (busoptions >> 4) & 0xf;
+	ne->busopt.lnkspd	= busoptions & 0x7;
 
-	if (!new && ne->nodeid != nodeid)
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+	HPSB_DEBUG("NodeMgr: raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d "
+		   "cyc_clk_acc=%d max_rec=%d gen=%d lspd=%d",
+		   busoptions, ne->busopt.irmc, ne->busopt.cmc,
+		   ne->busopt.isc, ne->busopt.bmc, ne->busopt.pmc,
+		   ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
+		   ne->busopt.generation, ne->busopt.lnkspd);
+#endif
+
+	/*
+	 * When the config rom changes we disconnect all drivers and
+	 * free the cached unit directories and reread the whole
+	 * thing.  If this was a new device, the call to
+	 * nodemgr_disconnect_drivers is a no-op and all is well.
+	 */
+	write_lock_irqsave(&unit_directory_lock, flags);
+	nodemgr_free_unit_directories(ne);
+	nodemgr_process_root_directory(ne);
+	nodemgr_bind_drivers(ne);
+	write_unlock_irqrestore(&unit_directory_lock, flags);
+}
+
+/*
+ * This function updates nodes that were present on the bus before the
+ * reset and still are after the reset.  The nodeid and the config rom
+ * may have changed, and the drivers managing this device must be
+ * informed that this device just went through a bus reset, to allow
+ * the to take whatever actions required.
+ */
+static void nodemgr_update_node(struct node_entry *ne, quadlet_t busoptions,
+                               struct hpsb_host *host, nodeid_t nodeid)
+{
+	struct list_head *lh;
+
+	if (ne->nodeid != nodeid)
 		HPSB_DEBUG("Node " NODE_BUS_FMT " changed to " NODE_BUS_FMT,
 			   NODE_BUS_ARGS(ne->nodeid), NODE_BUS_ARGS(nodeid));
 
 	ne->host = host;
-        ne->nodeid = nodeid;
+	ne->nodeid = nodeid;
 
-	/* Now set the bus options. Only do all this crap if this is a new
-	 * node, or if the generation number has changed.  */
-	if (new || ne->busopt.generation != ((busoptions >> 6) & 0x3)) {
-		ne->busopt.irmc		= (busoptions >> 31) & 1;
-		ne->busopt.cmc		= (busoptions >> 30) & 1;
-		ne->busopt.isc		= (busoptions >> 29) & 1;
-		ne->busopt.bmc		= (busoptions >> 28) & 1;
-		ne->busopt.pmc		= (busoptions >> 27) & 1;
-		ne->busopt.cyc_clk_acc	= (busoptions >> 16) & 0xff;
-		ne->busopt.max_rec	= 1 << (((busoptions >> 12) & 0xf) + 1);
-		ne->busopt.generation	= (busoptions >> 6) & 0x3;
-		ne->busopt.lnkspd	= busoptions & 0x7;
+	if (ne->busopt.generation != ((busoptions >> 4) & 0xf))
+		nodemgr_process_config_rom (ne, busoptions);
 
-		/* Now, process the rest of the tree */
-		nodemgr_process_root_directory(ne);
+	/* Since that's done, we can declare this record current */
+	atomic_set(&ne->generation, get_hpsb_generation(ne->host));
+
+	list_for_each (lh, &ne->unit_directories) {
+		struct unit_directory *ud;
+
+		ud = list_entry (lh, struct unit_directory, node_list);
+		if (ud->driver != NULL && ud->driver->update != NULL)
+			ud->driver->update(ud);
 	}
+}
 
-	/* Since that's done, we can declare this record current */
-	atomic_set(&ne->generation, get_hpsb_generation(host));
+static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid,
+			      quadlet_t *buffer, int buffer_length)
+{
+	octlet_t base = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
+	int retries = 3;
+	int header_count;
+	unsigned header_size;
+	quadlet_t quad;
+
+retry_configrom:
+
+	if (!retries--) {
+		HPSB_ERR("Giving up on node " NODE_BUS_FMT
+			 " for ConfigROM probe, too many errors",
+			 NODE_BUS_ARGS(nodeid));
+		return -1;
+	}
+
+	header_count = 0;
+	header_size = 0;
 
 #ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-	HPSB_DEBUG("raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d cyc_clk_acc=%d "
-		   "max_rec=%d gen=%d lspd=%d", busoptions,
-		   ne->busopt.irmc, ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
-		   ne->busopt.pmc, ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
-		   ne->busopt.generation, ne->busopt.lnkspd);
+	HPSB_INFO("Initiating ConfigROM request for node " NODE_BUS_FMT,
+		  NODE_BUS_ARGS(nodeid));
 #endif
 
-	return;
+	/* Now, P1212 says that devices should support 64byte block
+	 * reads, aligned on 64byte boundaries. That doesn't seem
+	 * to work though, and we are forced to doing quadlet
+	 * sized reads.  */
+
+	if (hpsb_read(host, nodeid, base, &quad, 4)) {
+		HPSB_ERR("ConfigROM quadlet transaction error for node " NODE_BUS_FMT,
+			 NODE_BUS_ARGS(nodeid));
+		goto retry_configrom;
+	}
+	buffer[header_count++] = be32_to_cpu(quad);
+
+	header_size = buffer[0] >> 24;
+
+	if (header_size < 4) {
+		HPSB_INFO("Node " NODE_BUS_FMT " has non-standard ROM format (%d quads), "
+			  "cannot parse", NODE_BUS_ARGS(nodeid), header_size);
+		return -1;
+	}
+
+	while (header_count <= header_size && header_count < buffer_length) {
+		if (hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4)) {
+			HPSB_ERR("ConfigROM quadlet transaction error for " NODE_BUS_FMT,
+				 NODE_BUS_ARGS(nodeid));
+			goto retry_configrom;
+		}
+		buffer[header_count++] = be32_to_cpu(quad);
+	}
+
+	return 0;
 }
 
 static void nodemgr_remove_node(struct node_entry *ne)
 {
-	HPSB_DEBUG("Device removed: node " NODE_BUS_FMT ", GUID "
-		   "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-		   NODE_BUS_ARGS(ne->nodeid),
-		   ((u8 *)&ne->guid)[0], ((u8 *)&ne->guid)[1],
-		   ((u8 *)&ne->guid)[2], ((u8 *)&ne->guid)[3],
-		   ((u8 *)&ne->guid)[4], ((u8 *)&ne->guid)[5],
-		   ((u8 *)&ne->guid)[6], ((u8 *)&ne->guid)[7]);
+	unsigned long flags;
 
+	HPSB_DEBUG("Device removed: node " NODE_BUS_FMT ", GUID %016Lx",
+		   NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid);
+
+	write_lock_irqsave(&unit_directory_lock, flags);
+	nodemgr_free_unit_directories(ne);
+	write_unlock_irqrestore(&unit_directory_lock, flags);
 	list_del(&ne->list);
 	kfree(ne);
 
@@ -352,82 +690,26 @@
 {
 	struct hpsb_host *host = (struct hpsb_host *)data;
         struct selfid *sid = (struct selfid *)host->topology_map;
-	struct list_head *lh,*spare;
+	struct list_head *lh, *next;
 	struct node_entry *ne;
         int nodecount = host->node_count;
         nodeid_t nodeid = LOCAL_BUS;
-	quadlet_t buffer[5], quad;
-	octlet_t base = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
+	quadlet_t buffer[5];
+	octlet_t guid;
 	unsigned long flags;
 
 	/* We need to detect when the ConfigROM's generation has changed,
 	 * so we only update the node's info when it needs to be.  */
         for (; nodecount; nodecount--, nodeid++, sid++) {
-		int retries = 3;
-		int header_count;
-		unsigned header_size;
-		octlet_t guid;
-
 		/* Skip extended, and non-active node's */
                 while (sid->extended)
 			sid++;
                 if (!sid->link_active)
 			continue;
 
-		/* Just use our local ROM */
-		if (nodeid == host->node_id) {
-			int i;
-			for (i = 0; i < 5; i++)
-				buffer[i] = be32_to_cpu(host->csr.rom[i]);
-			goto set_options;
-		}
-
-retry_configrom:
-		
-		if (!retries--) {
-			HPSB_ERR("Giving up on node " NODE_BUS_FMT
-				 " for ConfigROM probe, too many errors",
-				 NODE_BUS_ARGS(nodeid));
+		if (read_businfo_block (host, nodeid, buffer, sizeof(buffer) >> 2))
 			continue;
-		}
-
-		header_count = 0;
-		header_size = 0;
-
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-		HPSB_INFO("Initiating ConfigROM request for node " NODE_BUS_FMT,
-			  NODE_BUS_ARGS(nodeid));
-#endif
 
-		/* Now, P1212 says that devices should support 64byte block
-		 * reads, aligned on 64byte boundaries. That doesn't seem
-		 * to work though, and we are forced to doing quadlet
-		 * sized reads.  */
-
-		if (hpsb_read(host, nodeid, base, &quad, 4)) {
-			HPSB_ERR("ConfigROM quadlet transaction error for node " NODE_BUS_FMT,
-				 NODE_BUS_ARGS(nodeid));
-			goto retry_configrom;
-		}
-		buffer[header_count++] = be32_to_cpu(quad);
-
-		header_size = buffer[0] >> 24;
-
-		if (header_size < 4) {
-			HPSB_INFO("Node " NODE_BUS_FMT " has non-standard ROM format (%d quads), "
-				  "cannot parse", NODE_BUS_ARGS(nodeid), header_size);
-			continue;
-		}
-
-		while (header_count <= header_size && (header_count<<2) < sizeof(buffer)) {
-			if (hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4)) {
-				HPSB_ERR("ConfigROM quadlet transaction error for " NODE_BUS_FMT,
-					 NODE_BUS_ARGS(nodeid));
-				goto retry_configrom;
-			}
-			buffer[header_count++] = be32_to_cpu(quad);
-		}
-set_options:
 		if (buffer[1] != IEEE1394_BUSID_MAGIC) {
 			/* This isn't a 1394 device */
 			HPSB_ERR("Node " NODE_BUS_FMT " isn't an IEEE 1394 device",
@@ -435,15 +717,21 @@
 			continue;
 		}
 
-		guid = be64_to_cpu(((u64)buffer[3] << 32) | buffer[4]);
-		register_node(host, nodeid, guid, buffer[2]);
+		guid = ((u64)buffer[3] << 32) | buffer[4];
+		ne = hpsb_guid_get_entry(guid);
+
+		if (!ne)
+			nodemgr_create_node(guid, buffer[2], host, nodeid);
+		else
+			nodemgr_update_node(ne, buffer[2], host, nodeid);
         }
 
 	/* Now check to see if we have any nodes that aren't referenced
 	 * any longer.  */
         write_lock_irqsave(&node_lock, flags);
-	list_for_each_safe(lh, spare, &node_list) {
+	for (lh = node_list.next; lh != &node_list; lh = next) {
 		ne = list_entry(lh, struct node_entry, list);
+		next = lh->next;
 
 		/* Only checking this host */
 		if (ne->host != host)
@@ -453,7 +741,7 @@
 		 * node was removed, or it failed the above probe. Either
 		 * way, we remove references to it, since they are
 		 * invalid.  */
-		if (atomic_read(&ne->generation) != get_hpsb_generation(host))
+		if (!hpsb_node_entry_valid(ne))
 			nodemgr_remove_node(ne);
 	}
 	write_unlock_irqrestore(&node_lock, flags);
@@ -511,7 +799,7 @@
 	unsigned long flags;
 
 	if (!hi) {
-		HPSB_ERR ("Out of memory in Node Manager");
+		HPSB_ERR ("NodeMgr: out of memory in add host");
 		return;
 	}
 
@@ -544,7 +832,7 @@
 	}
 
 	if (hi == NULL) {
-		HPSB_ERR ("Could not process reset of non-existent host in Node Manager");
+		HPSB_ERR ("NodeMgr: could not process reset of non-existent host");
 		goto done_reset_host;
 	}
 
@@ -558,8 +846,7 @@
 
 static void nodemgr_remove_host(struct hpsb_host *host)
 {
-	struct list_head *lh;
-	struct host_info *hi = NULL;
+	struct list_head *lh, *next;
 	struct node_entry *ne;
 	unsigned long flags;
 
@@ -568,8 +855,10 @@
 
 	/* First remove all node entries for this host */
 	write_lock_irqsave(&node_lock, flags);
-	list_for_each(lh, &node_list) {
+
+	for (lh = node_list.next; lh != &node_list; lh = next) {
 		ne = list_entry(lh, struct node_entry, list);
+		next = lh->next;
 
 		/* Only checking this host */
 		if (ne->host != host)
@@ -581,22 +870,17 @@
 
 	spin_lock_irqsave (&host_info_lock, flags);
 	list_for_each(lh, &host_info_list) {
-		struct host_info *myhi = list_entry(lh, struct host_info, list);
-		if (myhi->host == host) {
-			hi = myhi;
+		struct host_info *hi = list_entry(lh, struct host_info, list);
+		if (hi->host == host) {
+			list_del(&hi->list);
+			kfree (hi);
 			break;
 		}
 	}
 
-	if (hi == NULL) {
-		HPSB_ERR ("Could not remove non-existent host in Node Manager");
-		goto done_remove_host;
-	}
-
-	list_del(&hi->list);
-	kfree (hi);
+	if (lh == host_info_list.next)
+		HPSB_ERR ("NodeMgr: could not remove non-existent host");
 
-done_remove_host:
 	spin_unlock_irqrestore (&host_info_lock, flags);
 
 	return;
@@ -614,7 +898,7 @@
 {
         hl = hpsb_register_highlevel("Node manager", &nodemgr_ops);
         if (!hl) {
-                HPSB_ERR("Out of memory during ieee1394 initialization");
+		HPSB_ERR("NodeMgr: out of memory during ieee1394 initialization");
         }
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)