patch-2.4.21 linux-2.4.21/drivers/ieee1394/nodemgr.c

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

diff -urN linux-2.4.20/drivers/ieee1394/nodemgr.c linux-2.4.21/drivers/ieee1394/nodemgr.c
@@ -1,17 +1,17 @@
 /*
  * Node information (ConfigROM) collection and management.
  *
- * Copyright (C) 2000 Andreas E. Bombe
- *               2001 Ben Collins <bcollins@debian.net>
+ * Copyright (C) 2000		Andreas E. Bombe
+ *               2001-2003	Ben Collins <bcollins@debian.net>
  *
  * This code is licensed under the GPL.  See the file COPYING in the root
  * directory of the kernel sources for details.
  */
 
 #include <linux/kernel.h>
+#include <linux/config.h>
 #include <linux/list.h>
 #include <linux/slab.h>
-#include <asm/byteorder.h>
 #include <linux/smp_lock.h>
 #include <linux/interrupt.h>
 #include <linux/kmod.h>
@@ -31,6 +31,24 @@
 #include "nodemgr.h"
 
 
+
+static char *nodemgr_find_oui_name(int oui)
+{
+#ifdef CONFIG_IEEE1394_OUI_DB
+	extern struct oui_list_struct {
+		int oui;
+		char *name;
+	} oui_list[];
+	int i;
+
+	for (i = 0; oui_list[i].name; i++)
+		if (oui_list[i].oui == oui)
+			return oui_list[i].name;
+#endif
+	return NULL;
+}
+
+
 /* 
  * Basically what we do here is start off retrieving the bus_info block.
  * From there will fill in some info about the node, verify it is of IEEE
@@ -40,9 +58,9 @@
  * complete directory entry (be it a leaf or a directory). We then process
  * it and add the info to our structure for that particular node.
  *
- * We verify CRC's along the way for each directory/block/leaf. The
- * entire node structure is generic, and simply stores the information in
- * a way that's easy to parse by the protocol interface.
+ * We verify CRC's along the way for each directory/block/leaf. The entire
+ * node structure is generic, and simply stores the information in a way
+ * that's easy to parse by the protocol interface.
  */
 
 /* The nodemgr maintains a number of data structures: the node list,
@@ -61,20 +79,17 @@
 static LIST_HEAD(driver_list);
 static LIST_HEAD(unit_directory_list);
 
-static LIST_HEAD(host_info_list);
-static spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED;
-
-/* Disables use of the hotplug calls.  */
-static int nodemgr_disable_hotplug = 0;
 
 struct host_info {
 	struct hpsb_host *host;
-	struct list_head list;
 	struct completion exited;
 	struct semaphore reset_sem;
 	int pid;
+	char daemon_name[15];
 };
 
+static struct hpsb_highlevel nodemgr_highlevel;
+
 #ifdef CONFIG_PROC_FS
 
 #define PUTF(fmt, args...) out += sprintf(out, fmt, ## args)
@@ -92,7 +107,7 @@
 
 	list_for_each(lh, &node_list) {
 		struct list_head *l;
-		int ud_count = 0;
+		int ud_count = 0, lud_count = 0;
 
 		ne = list_entry(lh, struct node_entry, list);
 		if (!ne)
@@ -135,7 +150,10 @@
 			struct unit_directory *ud = list_entry (l, struct unit_directory, node_list);
 			int printed = 0; // small hack
 
-			PUTF("  Unit Directory %d:\n", ud_count++);
+			if (ud->parent == NULL)
+				PUTF("  Unit Directory %d:\n", lud_count++);
+			else
+				PUTF("  Logical Unit Directory %d:\n", ud_count++);
 			if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) {
 				PUTF("    Vendor/Model ID: %s [%06x]",
 				     ud->vendor_name ?: "Unknown", ud->vendor_id);
@@ -157,7 +175,7 @@
 				PUTF("    Software Version: %06x\n", ud->version);
 			if (ud->driver)
 				PUTF("    Driver: %s\n", ud->driver->name);
-			PUTF("    Length (in quads): %d\n", ud->count);
+			PUTF("    Length (in quads): %d\n", ud->length);
 		}
 
 	}
@@ -258,7 +276,7 @@
 	ret = -ENXIO;
 	for (; size > 0; size--, address += 4, quadp++) {
 		for (i = 0; i < 3; i++) {
-			ret = hpsb_read(ne->host, ne->nodeid, ne->generation, address, quadp, 4);
+			ret = hpsb_node_read(ne, address, quadp, 4);
 			if (ret != -EAGAIN)
 				break;
 		}
@@ -282,6 +300,10 @@
 	
 	if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
 		return NULL;
+
+	if (CONFIG_ROM_BUS_INFO_LENGTH(quad) == 1)  /* minimal config rom */
+		return NULL;
+
 	address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
 
 	if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
@@ -305,29 +327,32 @@
 				address += 4;
 				length--;
 				total_size += (size + 1) * sizeof (quadlet_t);
-			}
-			else if (size < 0)
+			} else if (size < 0)
 				return NULL;
 		}
 	}
-	ne = kmalloc(total_size, SLAB_ATOMIC);
-	if (ne != NULL) {
-		if (size != 0) {
-			ne->vendor_name
-				= (const char *) &(ne->quadlets[2]);
-			ne->quadlets[size] = 0;
-		}
-		else {
-			ne->vendor_name = NULL;
-		}
+	ne = kmalloc(total_size, GFP_KERNEL);
+
+	if (!ne)
+		return NULL;
+
+	memset(ne, 0, total_size);
+
+	if (size != 0) {
+		ne->vendor_name = (const char *) &(ne->quadlets[2]);
+		ne->quadlets[size] = 0;
+	} else {
+		ne->vendor_name = NULL;
 	}
+
 	return ne; 
 }
 
 static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions,
-					      struct hpsb_host *host,
+					      struct host_info *hi,
 					      nodeid_t nodeid, unsigned int generation)
 {
+	struct hpsb_host *host = hi->host;
         struct node_entry *ne;
 
 	ne = nodemgr_scan_root_directory (host, nodeid, generation);
@@ -337,8 +362,10 @@
 	INIT_LIST_HEAD(&ne->unit_directories);
         ne->host = host;
         ne->nodeid = nodeid;
-        ne->guid = guid;
 	ne->generation = generation;
+	ne->guid = guid;
+	ne->guid_vendor_id = (guid >> 40) & 0xffffff;
+	ne->guid_vendor_oui = nodemgr_find_oui_name(ne->guid_vendor_id);
 
         list_add_tail(&ne->list, &node_list);
 
@@ -365,14 +392,15 @@
         return NULL;
 }
 
-static struct node_entry *find_entry_by_nodeid(nodeid_t nodeid)
+static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t nodeid)
 {
 	struct list_head *lh;
 	struct node_entry *ne;
 
 	list_for_each(lh, &node_list) {
 		ne = list_entry(lh, struct node_entry, list);
-		if (ne->nodeid == nodeid) return ne;
+		if (ne->nodeid == nodeid && ne->host == host)
+			return ne;
 	}
 
 	return NULL;
@@ -432,7 +460,7 @@
 			   store?  Only count immediate values and
 			   CSR offsets for now.  */
 			code &= CONFIG_ROM_KEY_TYPE_MASK;
-			if ((code & 0x80) == 0)
+			if ((code & CONFIG_ROM_KEY_TYPE_LEAF) == 0)
 				count++;
 			break;
 		}
@@ -460,33 +488,33 @@
 				return NULL;
 		}
 	}
+
 	total_size += count * sizeof (quadlet_t);
 	ud = kmalloc (total_size, GFP_KERNEL);
+
 	if (ud != NULL) {
-		memset (ud, 0, sizeof *ud);
+		memset (ud, 0, total_size);
 		ud->flags = flags;
-		ud->count = count;
+		ud->length = count;
 		ud->vendor_name_size = vendor_name_size;
 		ud->model_name_size = model_name_size;
-		/* If there is no vendor name in the unit directory,
-		   use the one in the root directory.  */
-		ud->vendor_name = ne->vendor_name;
 	}
+
 	return ud;
 }
 
+
 /* This implementation currently only scans the config rom and its
  * immediate unit directories looking for software_id and
- * software_version entries, in order to get driver autoloading working.
- */
-
-static void nodemgr_process_unit_directory(struct node_entry *ne, 
-					   octlet_t address)
+ * software_version entries, in order to get driver autoloading working. */
+static struct unit_directory * nodemgr_process_unit_directory
+	(struct node_entry *ne, octlet_t address, struct unit_directory *parent)
 {
 	struct unit_directory *ud;
 	quadlet_t quad;
 	quadlet_t *infop;
 	int length;
+	struct unit_directory *ud_temp = NULL;
 
 	if (!(ud = nodemgr_scan_unit_directory(ne, address)))
 		goto unit_directory_error;
@@ -494,6 +522,9 @@
 	ud->ne = ne;
 	ud->address = address;
 
+	if (parent != NULL)
+		ud->parent = parent;
+
 	if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
 				 address, &quad))
 		goto unit_directory_error;
@@ -501,7 +532,7 @@
 	address += 4;
 
 	infop = (quadlet_t *) ud->quadlets;
-	for (; length > 0; length--, address += 4, infop++) {
+	for (; length > 0; length--, address += 4) {
 		int code;
 		quadlet_t value;
 		quadlet_t *quadp;
@@ -516,14 +547,16 @@
 		case CONFIG_ROM_VENDOR_ID:
 			ud->vendor_id = value;
 			ud->flags |= UNIT_DIRECTORY_VENDOR_ID;
+
+			if (ud->vendor_id)
+				ud->vendor_oui = nodemgr_find_oui_name(ud->vendor_id);
+
 			if ((ud->flags & UNIT_DIRECTORY_VENDOR_TEXT) != 0) {
 				length--;
 				address += 4;
-				quadp = &(ud->quadlets[ud->count]);
-				if (nodemgr_read_text_leaf(ne, address,
-							   quadp) == 0
-				    && quadp[0] == 0
-				    && quadp[1] == 0) {
+				quadp = &(ud->quadlets[ud->length]);
+				if (nodemgr_read_text_leaf(ne, address, quadp) == 0
+				    && quadp[0] == 0 && quadp[1] == 0) {
 				    	/* We only support minimal
 					   ASCII and English. */
 					quadp[ud->vendor_name_size] = 0;
@@ -539,11 +572,9 @@
 			if ((ud->flags & UNIT_DIRECTORY_MODEL_TEXT) != 0) {
 				length--;
 				address += 4;
-				quadp = &(ud->quadlets[ud->count + ud->vendor_name_size + 1]);
-				if (nodemgr_read_text_leaf(ne, address,
-							   quadp) == 0
-				    && quadp[0] == 0
-				    && quadp[1] == 0) {
+				quadp = &(ud->quadlets[ud->length + ud->vendor_name_size + 1]);
+				if (nodemgr_read_text_leaf(ne, address, quadp) == 0
+				    && quadp[0] == 0 && quadp[1] == 0) {
 				    	/* We only support minimal
 					   ASCII and English. */
 					quadp[ud->model_name_size] = 0;
@@ -568,13 +599,47 @@
 			/* TODO: read strings... icons? */
 			break;
 
+		case CONFIG_ROM_LOGICAL_UNIT_DIRECTORY:
+			ud_temp = nodemgr_process_unit_directory(ne, address + value * 4, ud);
+
+			/* inherit unspecified values */
+			if (ud_temp != NULL)
+			{
+				if ((ud->flags & UNIT_DIRECTORY_VENDOR_ID) &&
+					!(ud_temp->flags & UNIT_DIRECTORY_VENDOR_ID))
+				{
+					ud_temp->flags |=  UNIT_DIRECTORY_VENDOR_ID;
+					ud_temp->vendor_id = ud->vendor_id;
+				}
+				if ((ud->flags & UNIT_DIRECTORY_MODEL_ID) &&
+					!(ud_temp->flags & UNIT_DIRECTORY_MODEL_ID))
+				{
+					ud_temp->flags |=  UNIT_DIRECTORY_MODEL_ID;
+					ud_temp->model_id = ud->model_id;
+				}
+				if ((ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) &&
+					!(ud_temp->flags & UNIT_DIRECTORY_SPECIFIER_ID))
+				{
+					ud_temp->flags |=  UNIT_DIRECTORY_SPECIFIER_ID;
+					ud_temp->specifier_id = ud->specifier_id;
+				}
+				if ((ud->flags & UNIT_DIRECTORY_VERSION) &&
+					!(ud_temp->flags & UNIT_DIRECTORY_VERSION))
+				{
+					ud_temp->flags |=  UNIT_DIRECTORY_VERSION;
+					ud_temp->version = ud->version;
+				}
+			}
+
+			break;
+
 		default:
 			/* Which types of quadlets do we want to
 			   store?  Only count immediate values and
 			   CSR offsets for now.  */
 			code &= CONFIG_ROM_KEY_TYPE_MASK;
-			if ((code & 0x80) == 0)
-				*infop = quad;
+			if ((code & CONFIG_ROM_KEY_TYPE_LEAF) == 0)
+				*infop++ = quad;
 			break;
 		}
 	}
@@ -582,40 +647,14 @@
 	list_add_tail(&ud->node_list, &ne->unit_directories);
 	list_add_tail(&ud->driver_list, &unit_directory_list);
 
-	return;
+	return ud;
 
 unit_directory_error:	
 	if (ud != NULL)
 		kfree(ud);
+	return NULL;
 }
 
-static void dump_directories (struct node_entry *ne)
-{
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-	struct list_head *l;
-
-	HPSB_DEBUG("vendor_id=0x%06x [%s], capabilities=0x%06x",
-		   ne->vendor_id, ne->vendor_name ?: "Unknown",
-		   ne->capabilities);
-	list_for_each (l, &ne->unit_directories) {
-		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 [%s]",
-				   ud->vendor_id,
-				   ud->vendor_name ?: "Unknown");
-		if (ud->flags & UNIT_DIRECTORY_MODEL_ID)
-			HPSB_DEBUG("  model_id=0x%06x [%s]",
-				   ud->model_id,
-				   ud->model_name ?: "Unknown");
-		if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
-			HPSB_DEBUG("  sw_specifier_id=0x%06x ", ud->specifier_id);
-		if (ud->flags & UNIT_DIRECTORY_VERSION)
-			HPSB_DEBUG("  sw_version=0x%06x ", ud->version);
-	}
-#endif
-	return;
-}
 
 static void nodemgr_process_root_directory(struct node_entry *ne)
 {
@@ -648,16 +687,17 @@
 		switch (code) {
 		case CONFIG_ROM_VENDOR_ID:
 			ne->vendor_id = value;
+
+			if (ne->vendor_id)
+				ne->vendor_oui = nodemgr_find_oui_name(ne->vendor_id);
+
 			/* Now check if there is a vendor name text
 			   string.  */
 			if (ne->vendor_name != NULL) {
 				length--;
 				address += 4;
-				if (nodemgr_read_text_leaf(ne, address,
-							   ne->quadlets)
-				    != 0
-				    || ne->quadlets [0] != 0
-				    || ne->quadlets [1] != 0)
+				if (nodemgr_read_text_leaf(ne, address, ne->quadlets) != 0
+				    || ne->quadlets[0] != 0 || ne->quadlets[1] != 0)
 				    	/* We only support minimal
 					   ASCII and English. */
 					ne->vendor_name = NULL;
@@ -669,7 +709,7 @@
 			break;
 
 		case CONFIG_ROM_UNIT_DIRECTORY:
-			nodemgr_process_unit_directory(ne, address + value * 4);
+			nodemgr_process_unit_directory(ne, address + value * 4, NULL);
 			break;			
 
 		case CONFIG_ROM_DESCRIPTOR_LEAF:
@@ -678,8 +718,6 @@
 			break;
 		}
 	}
-
-	dump_directories(ne);
 }
 
 #ifdef CONFIG_HOTPLUG
@@ -689,10 +727,6 @@
 	char *argv [3], **envp, *buf, *scratch;
 	int i = 0, value;
 
-	/* User requested to disable hotplug when module was loaded. */
-	if (nodemgr_disable_hotplug)
-		return;
-
 	if (!hotplug_path [0])
 		return;
 	if (!current->fs->root)
@@ -728,7 +762,7 @@
 	envp[i++] = scratch;
 	scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
 	envp[i++] = scratch;
-	scratch += sprintf(scratch, "VENDOR_ID=%06x", ud->ne->vendor_id) + 1;
+	scratch += sprintf(scratch, "VENDOR_ID=%06x", ud->vendor_id) + 1;
 	envp[i++] = scratch;
 	scratch += sprintf(scratch, "GUID=%016Lx", (long long unsigned)ud->ne->guid) + 1;
 	envp[i++] = scratch;
@@ -765,15 +799,13 @@
 					 struct hpsb_protocol_driver *driver)
 {
 	ud->driver = driver;
-	list_del(&ud->driver_list);
-	list_add_tail(&ud->driver_list, &driver->unit_directories);
+	list_move_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);
+	list_move_tail(&ud->driver_list, &unit_directory_list);
 }
 
 void hpsb_release_unit_directory(struct unit_directory *ud)
@@ -785,18 +817,21 @@
 
 static void nodemgr_free_unit_directories(struct node_entry *ne)
 {
-	struct list_head *lh;
+	struct list_head *lh, *next;
 	struct unit_directory *ud;
 
-	lh = ne->unit_directories.next;
-	while (lh != &ne->unit_directories) {
+	list_for_each_safe(lh, next, &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);
+		list_del(&ud->node_list);
+
 		kfree(ud);
 	}
 }
@@ -820,8 +855,9 @@
 		    id->specifier_id != ud->specifier_id)
 			continue;
 
+		/* software version does a bitwise comparison instead of equality */
 		if ((id->match_flags & IEEE1394_MATCH_VERSION) &&
-		    id->version != ud->version)
+		    !(id->version & ud->version))
 			continue;
 
 		return id;
@@ -860,16 +896,17 @@
 	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)
+		if (driver && (!driver->probe || 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;
+	struct list_head *lh, *next;
 
 	if (down_interruptible(&nodemgr_serialize))
 		return -EINTR;
@@ -877,11 +914,12 @@
 	list_add_tail(&driver->list, &driver_list);
 
 	INIT_LIST_HEAD(&driver->unit_directories);
-	lh = unit_directory_list.next;
-	while (lh != &unit_directory_list) {
+
+	list_for_each_safe (lh, next, &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)
+
+		if (nodemgr_match_driver(driver, ud) &&
+		    (!driver->probe || driver->probe(ud) == 0))
 			nodemgr_claim_unit_directory(ud, driver);
 	}
 
@@ -890,6 +928,18 @@
 	/*
 	 * Right now registration always succeeds, but maybe we should
 	 * detect clashes in protocols handled by other drivers.
+     * DRD> No because multiple drivers are needed to handle certain devices.
+     * For example, a DV camera is an IEC 61883 device (dv1394) and AV/C (raw1394).
+     * This will become less an issue with libiec61883 using raw1394.
+     *
+     * BenC: But can we handle this with an ALLOW_SHARED flag for a
+     * protocol? When we get an SBP-3 driver, it will be nice if they were
+     * mutually exclusive, since SBP-3 can handle SBP-2 protocol.
+     *
+     * Not to mention that we currently do not seem to support multiple
+     * drivers claiming the same unitdirectory. If we implement both of
+     * those, then we'll need to keep probing when a driver claims a
+     * unitdirectory, but is sharable.
 	 */
 
 	return 0;
@@ -897,18 +947,19 @@
 
 void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver)
 {
-	struct list_head *lh;
+	struct list_head *lh, *next;
 	struct unit_directory *ud;
 
 	down(&nodemgr_serialize);
 
 	list_del(&driver->list);
-	lh = driver->unit_directories.next;
-	while (lh != &driver->unit_directories) {
+
+	list_for_each_safe (lh, next, &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);
 	}
 
@@ -956,8 +1007,8 @@
  * 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, unsigned int generation)
+				struct host_info *hi, nodeid_t nodeid,
+				unsigned int generation)
 {
 	struct list_head *lh;
 	struct unit_directory *ud;
@@ -976,7 +1027,7 @@
 
 	list_for_each (lh, &ne->unit_directories) {
 		ud = list_entry (lh, struct unit_directory, node_list);
-		if (ud->driver != NULL && ud->driver->update != NULL)
+		if (ud->driver && ud->driver->update != NULL)
 			ud->driver->update(ud);
 	}
 }
@@ -1022,6 +1073,13 @@
 	header_size = buffer[0] >> 24;
 	addr += 4;
 
+	if (header_size == 1) {
+		HPSB_INFO("Node " NODE_BUS_FMT " has a minimal ROM.  "
+			  "Vendor is %08x",
+			  NODE_BUS_ARGS(nodeid), buffer[0] & 0x00ffffff);
+		return -1;
+	}
+
 	if (header_size < 4) {
 		HPSB_INFO("Node " NODE_BUS_FMT " has non-standard ROM "
 			  "format (%d quads), cannot parse",
@@ -1058,9 +1116,10 @@
 
 /* This is where we probe the nodes for their information and provided
  * features.  */
-static void nodemgr_node_probe_one(struct hpsb_host *host,
+static void nodemgr_node_probe_one(struct host_info *hi,
 				   nodeid_t nodeid, int generation)
 {
+	struct hpsb_host *host = hi->host;
 	struct node_entry *ne;
 	quadlet_t buffer[5];
 	octlet_t guid;
@@ -1088,9 +1147,9 @@
 	ne = find_entry_by_guid(guid);
 
 	if (!ne)
-		nodemgr_create_node(guid, buffer[2], host, nodeid, generation);
+		nodemgr_create_node(guid, buffer[2], hi, nodeid, generation);
 	else
-		nodemgr_update_node(ne, buffer[2], host, nodeid, generation);
+		nodemgr_update_node(ne, buffer[2], hi, nodeid, generation);
 
 	return;
 }
@@ -1120,26 +1179,12 @@
 	return;
 }
 
-static void nodemgr_node_probe(struct hpsb_host *host)
+static void nodemgr_node_probe(struct host_info *hi, int generation)
 {
 	int count;
+	struct hpsb_host *host = hi->host;
 	struct selfid *sid = (struct selfid *)host->topology_map;
 	nodeid_t nodeid = LOCAL_BUS;
-	unsigned int generation;
-
-	/* Pause for 1/4 second, to make sure things settle down. If
-	 * schedule_timeout returns non-zero, it means we caught a signal
-	 * and need to return. */
-	set_current_state(TASK_INTERRUPTIBLE);
-	if (schedule_timeout (HZ/4))
-		return;
-
-	/* Now get the generation in which the node ID's we collect
-	 * are valid.  During the bus scan we will use this generation
-	 * for the read transactions, so that if another reset occurs
-	 * during the scan the transactions will fail instead of
-	 * returning bogus data. */
-	generation = get_hpsb_generation(host);
 
 	/* Scan each node on the bus */
 	for (count = host->selfid_count; count; count--, sid++) {
@@ -1150,40 +1195,148 @@
 			nodeid++;
 			continue;
 		}
-
-		nodemgr_node_probe_one(host, nodeid++, generation);
+		nodemgr_node_probe_one(hi, nodeid++, generation);
 	}
 
 	/* If we had a bus reset while we were scanning the bus, it is
 	 * possible that we did not probe all nodes.  In that case, we
 	 * skip the clean up for now, since we could remove nodes that
-	 * were still on the bus.  The bus reset increased
-	 * hi->reset_sem, so there's a bus scan pending which will do
-	 * the clean up eventually. */
+	 * were still on the bus.  The bus reset increased hi->reset_sem,
+	 * so there's a bus scan pending which will do the clean up
+	 * eventually. */
 	if (generation == get_hpsb_generation(host))
 		nodemgr_node_probe_cleanup(host, generation);
 
 	return;
 }
 
+/* Because we are a 1394a-2000 compliant IRM, we need to inform all the other
+ * nodes of the broadcast channel.  (Really we're only setting the validity
+ * bit). Other IRM responsibilities go in here as well. */
+static void nodemgr_do_irm_duties(struct hpsb_host *host)
+{
+	quadlet_t bc;
+        
+	if (!host->is_irm)
+		return;
+
+	host->csr.broadcast_channel |= 0x40000000;  /* set validity bit */
+
+	bc = cpu_to_be32(host->csr.broadcast_channel);
+
+	hpsb_write(host, LOCAL_BUS | ALL_NODES, get_hpsb_generation(host),
+		   (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL),
+		   &bc, sizeof(quadlet_t));
+
+	/* If there is no bus manager then we should set the root node's
+	 * force_root bit to promote bus stability per the 1394
+	 * spec. (8.4.2.6) */
+	if (host->busmgr_id == 0x3f && host->node_count > 1)
+	{
+		u16 root_node = host->node_count - 1;
+		struct node_entry *ne = hpsb_nodeid_get_entry(host, root_node);
+
+		if (ne->busopt.cmc)
+			hpsb_send_phy_config(host, root_node, -1);
+		else {
+			HPSB_DEBUG("The root node is not cycle master capable; "
+				   "selecting a new root node and resetting...");
+			hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1);
+			hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT);
+		}
+	}
+}
+
+/* We need to ensure that if we are not the IRM, that the IRM node is capable of
+ * everything we can do, otherwise issue a bus reset and try to become the IRM
+ * ourselves. */
+static int nodemgr_check_irm_capability(struct hpsb_host *host, int cycles)
+{
+	quadlet_t bc;
+	int status;
+
+	if (host->is_irm)
+		return 1;
+
+	status = hpsb_read(host, LOCAL_BUS | (host->irm_id),
+			   get_hpsb_generation(host),
+			   (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL),
+			   &bc, sizeof(quadlet_t));
+
+	if (status < 0 || !(be32_to_cpu(bc) & 0x80000000)) {
+		/* The current irm node does not have a valid BROADCAST_CHANNEL
+		 * register and we do, so reset the bus with force_root set */
+		HPSB_DEBUG("Current remote IRM is not 1394a-2000 compliant, resetting...");
+
+		if (cycles >= 5) {
+			/* Oh screw it! Just leave the bus as it is */
+			HPSB_DEBUG("Stopping reset loop for IRM sanity");
+			return 1;
+		}
+
+		hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1);
+		hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT);
+
+		return 0;
+	}
+
+	return 1;
+}
+
 static int nodemgr_host_thread(void *__hi)
 {
 	struct host_info *hi = (struct host_info *)__hi;
+	struct hpsb_host *host = hi->host;
+	int reset_cycles = 0;
 
 	/* No userlevel access needed */
 	daemonize();
 
-	strcpy(current->comm, "knodemgrd");
+	strcpy(current->comm, hi->daemon_name);
 	
 	/* Sit and wait for a signal to probe the nodes on the bus. This
 	 * happens when we get a bus reset. */
 	while (!down_interruptible(&hi->reset_sem) &&
 	       !down_interruptible(&nodemgr_serialize)) {
-		nodemgr_node_probe(hi->host);
+		unsigned int generation;
+		int i;
+
+		/* Pause for 1/4 second, to make sure things settle down. */
+		for (i = HZ/4; i > 0; i-= HZ/16) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (schedule_timeout(HZ/16))
+				goto caught_signal;
+
+			/* Now get the generation in which the node ID's we collect
+			 * are valid.  During the bus scan we will use this generation
+			 * for the read transactions, so that if another reset occurs
+			 * during the scan the transactions will fail instead of
+			 * returning bogus data. */
+			generation = get_hpsb_generation(host);
+
+			/* If we get a reset before we are done waiting, then
+			 * start the the waiting over again */
+			while (!down_trylock(&hi->reset_sem))
+				i = HZ/4;
+		}
+
+		if (!nodemgr_check_irm_capability(host, reset_cycles++)) {
+			/* Do nothing, we are resetting */
+			up(&nodemgr_serialize);
+			continue;
+		}
+
+		reset_cycles = 0;
+
+		nodemgr_node_probe(hi, generation);
+		nodemgr_do_irm_duties(host);
+
 		up(&nodemgr_serialize);
 	}
+
+caught_signal:
 #ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-	HPSB_DEBUG ("NodeMgr: Exiting thread for %s", hi->host->driver->name);
+	HPSB_DEBUG ("NodeMgr: Exiting thread");
 #endif
 
 	complete_and_exit(&hi->exited, 0);
@@ -1200,12 +1353,12 @@
         return ne;
 }
 
-struct node_entry *hpsb_nodeid_get_entry(nodeid_t nodeid)
+struct node_entry *hpsb_nodeid_get_entry(struct hpsb_host *host, nodeid_t nodeid)
 {
 	struct node_entry *ne;
 
 	down(&nodemgr_serialize);
-	ne = find_entry_by_nodeid(nodeid);
+	ne = find_entry_by_nodeid(host, nodeid);
 	up(&nodemgr_serialize);
 
 	return ne;
@@ -1266,53 +1419,37 @@
 
 static void nodemgr_add_host(struct hpsb_host *host)
 {
-	struct host_info *hi = kmalloc (sizeof (struct host_info), GFP_KERNEL);
-	unsigned long flags;
+	struct host_info *hi;
+
+	hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi));
 
 	if (!hi) {
 		HPSB_ERR ("NodeMgr: out of memory in add host");
 		return;
 	}
 
-	/* Initialize the hostinfo here and start the thread.  The
-	 * thread blocks on the reset semaphore until a bus reset
-	 * happens. */
 	hi->host = host;
-	INIT_LIST_HEAD(&hi->list);
 	init_completion(&hi->exited);
         sema_init(&hi->reset_sem, 0);
 
+	sprintf(hi->daemon_name, "knodemgrd_%d", host->id);
+
 	hi->pid = kernel_thread(nodemgr_host_thread, hi,
 				CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-	
+
 	if (hi->pid < 0) {
-		HPSB_ERR ("NodeMgr: failed to start NodeMgr thread for %s",
-			  host->driver->name);
-		kfree(hi);
+		HPSB_ERR ("NodeMgr: failed to start %s thread for %s",
+			  hi->daemon_name, host->driver->name);
+		hpsb_destroy_hostinfo(&nodemgr_highlevel, host);
 		return;
 	}
 
-	spin_lock_irqsave (&host_info_lock, flags);
-	list_add_tail (&hi->list, &host_info_list);
-	spin_unlock_irqrestore (&host_info_lock, flags);
-
 	return;
 }
 
 static void nodemgr_host_reset(struct hpsb_host *host)
 {
-	struct list_head *lh;
-	struct host_info *hi = NULL;
-	unsigned long flags;
-
-	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;
-			break;
-		}
-	}
+	struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
 
 	if (hi != NULL) {
 #ifdef CONFIG_IEEE1394_VERBOSEDEBUG
@@ -1320,9 +1457,7 @@
 #endif
 		up(&hi->reset_sem);
 	} else
-		HPSB_ERR ("NodeMgr: could not process reset of non-existent host");
-
-	spin_unlock_irqrestore (&host_info_lock, flags);
+		HPSB_ERR ("NodeMgr: could not process reset of unused host");
 
 	return;
 }
@@ -1331,28 +1466,14 @@
 {
 	struct list_head *lh, *next;
 	struct node_entry *ne;
-	unsigned long flags;
-	struct host_info *hi = NULL;
-
-	spin_lock_irqsave (&host_info_lock, flags);
-	list_for_each_safe(lh, next, &host_info_list) {
-		struct host_info *myhi = list_entry(lh, struct host_info, list);
-		if (myhi->host == host) {
-			list_del(&myhi->list);
-			hi = myhi;
-			break;
-		}
-	}
-	spin_unlock_irqrestore (&host_info_lock, flags);
+	struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
 
 	if (hi) {
 		if (hi->pid >= 0) {
 			kill_proc(hi->pid, SIGTERM, 1);
 			wait_for_completion(&hi->exited);
 		}
-		kfree(hi);
-	}
-	else
+	} else
 		HPSB_ERR("NodeMgr: host %s does not exist, cannot remove",
 			 host->driver->name);
 
@@ -1372,32 +1493,27 @@
 	return;
 }
 
-static struct hpsb_highlevel_ops nodemgr_ops = {
+static struct hpsb_highlevel nodemgr_highlevel = {
+	.name =		"Node manager",
 	.add_host =	nodemgr_add_host,
 	.host_reset =	nodemgr_host_reset,
 	.remove_host =	nodemgr_remove_host,
 };
 
-static struct hpsb_highlevel *hl;
-
 #define PROC_ENTRY "devices"
 
-void init_ieee1394_nodemgr(int disable_hotplug)
+void init_ieee1394_nodemgr(void)
 {
-	nodemgr_disable_hotplug = disable_hotplug;
 #ifdef CONFIG_PROC_FS
 	if (!create_proc_read_entry(PROC_ENTRY, 0444, ieee1394_procfs_entry, raw1394_read_proc, NULL))
 		HPSB_ERR("Can't create devices procfs entry");
 #endif
-        hl = hpsb_register_highlevel("Node manager", &nodemgr_ops);
-        if (!hl) {
-		HPSB_ERR("NodeMgr: out of memory during ieee1394 initialization");
-        }
+	hpsb_register_highlevel(&nodemgr_highlevel);
 }
 
 void cleanup_ieee1394_nodemgr(void)
 {
-        hpsb_unregister_highlevel(hl);
+        hpsb_unregister_highlevel(&nodemgr_highlevel);
 #ifdef CONFIG_PROC_FS
 	remove_proc_entry(PROC_ENTRY, ieee1394_procfs_entry);
 #endif

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