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
- Lines: 1010
- Date:
2003-06-13 07:51:34.000000000 -0700
- Orig file:
linux-2.4.20/drivers/ieee1394/nodemgr.c
- Orig date:
2002-11-28 15:53:13.000000000 -0800
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)