patch-2.4.19 linux-2.4.19/drivers/ieee1394/nodemgr.c
Next file: linux-2.4.19/drivers/ieee1394/nodemgr.h
Previous file: linux-2.4.19/drivers/ieee1394/ieee1394_types.h
Back to the patch index
Back to the overall index
- Lines: 1353
- Date:
Fri Aug 2 17:39:44 2002
- Orig file:
linux-2.4.18/drivers/ieee1394/nodemgr.c
- Orig date:
Fri Dec 21 09:41:54 2001
diff -urN linux-2.4.18/drivers/ieee1394/nodemgr.c linux-2.4.19/drivers/ieee1394/nodemgr.c
@@ -12,10 +12,14 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
-#include <asm/atomic.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/kmod.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
#include "ieee1394_types.h"
#include "ieee1394.h"
@@ -41,39 +45,277 @@
* a way that's easy to parse by the protocol interface.
*/
-static LIST_HEAD(node_list);
-static rwlock_t node_lock = RW_LOCK_UNLOCKED;
+/* The nodemgr maintains a number of data structures: the node list,
+ * the driver list, unit directory list and the host info list. The
+ * first three lists are accessed from process context only: /proc
+ * readers, insmod and rmmod, and the nodemgr thread. Access to these
+ * lists are serialized by means of the nodemgr_serialize mutex, which
+ * must be taken before accessing the structures and released
+ * afterwards. The host info list is only accessed during insmod,
+ * rmmod and from interrupt and allways only for a short period of
+ * time, so a spinlock is used to protect this list.
+ */
+static DECLARE_MUTEX(nodemgr_serialize);
+static LIST_HEAD(node_list);
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;
+/* Disables use of the hotplug calls. */
+static int nodemgr_disable_hotplug = 0;
+
struct host_info {
struct hpsb_host *host;
- struct tq_struct task;
struct list_head list;
+ struct completion exited;
+ struct semaphore reset_sem;
+ int pid;
};
+#ifdef CONFIG_PROC_FS
+
+#define PUTF(fmt, args...) out += sprintf(out, fmt, ## args)
+
+static int raw1394_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct list_head *lh;
+ struct node_entry *ne;
+ int len;
+ char *out = page;
+
+ if (down_interruptible(&nodemgr_serialize))
+ return -EINTR;
+
+ list_for_each(lh, &node_list) {
+ struct list_head *l;
+ int ud_count = 0;
+
+ ne = list_entry(lh, struct node_entry, list);
+ if (!ne)
+ continue;
+
+ PUTF("Node[" NODE_BUS_FMT "] GUID[%016Lx]:\n",
+ NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid);
+
+ /* Generic Node information */
+ PUTF(" Vendor ID: `%s' [0x%06x]\n",
+ ne->vendor_name ?: "Unknown", ne->vendor_id);
+ PUTF(" Capabilities: 0x%06x\n", ne->capabilities);
+ PUTF(" Bus Options:\n");
+ PUTF(" IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d)\n"
+ " LSPD(%d) MAX_REC(%d) CYC_CLK_ACC(%d)\n",
+ ne->busopt.irmc, ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
+ ne->busopt.pmc, ne->busopt.generation, ne->busopt.lnkspd,
+ ne->busopt.max_rec, ne->busopt.cyc_clk_acc);
+
+ /* If this is the host entry, output some info about it aswell */
+ if (ne->host != NULL && ne->host->node_id == ne->nodeid) {
+ PUTF(" Host Node Status:\n");
+ PUTF(" Host Driver : %s\n", ne->host->driver->name);
+ PUTF(" Nodes connected : %d\n", ne->host->node_count);
+ PUTF(" Nodes active : %d\n", ne->host->nodes_active);
+ PUTF(" SelfIDs received: %d\n", ne->host->selfid_count);
+ PUTF(" Irm ID : [" NODE_BUS_FMT "]\n",
+ NODE_BUS_ARGS(ne->host->irm_id));
+ PUTF(" BusMgr ID : [" NODE_BUS_FMT "]\n",
+ NODE_BUS_ARGS(ne->host->busmgr_id));
+ PUTF(" In Bus Reset : %s\n", ne->host->in_bus_reset ? "yes" : "no");
+ PUTF(" Root : %s\n", ne->host->is_root ? "yes" : "no");
+ PUTF(" Cycle Master : %s\n", ne->host->is_cycmst ? "yes" : "no");
+ PUTF(" IRM : %s\n", ne->host->is_irm ? "yes" : "no");
+ PUTF(" Bus Manager : %s\n", ne->host->is_busmgr ? "yes" : "no");
+ }
+
+ /* Now the unit directories */
+ list_for_each (l, &ne->unit_directories) {
+ struct unit_directory *ud = list_entry (l, struct unit_directory, node_list);
+ PUTF(" 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);
+ else if (ud->flags & UNIT_DIRECTORY_MODEL_ID) /* Have to put something */
+ PUTF(" Vendor/Model ID: %s [%06x]",
+ ne->vendor_name ?: "Unknown", ne->vendor_id);
+ if (ud->flags & UNIT_DIRECTORY_MODEL_ID)
+ PUTF(" / %s [%06x]", ud->model_name ?: "Unknown", ud->model_id);
+ PUTF("\n");
+ if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
+ PUTF(" Software Specifier ID: %06x\n", ud->specifier_id);
+ if (ud->flags & UNIT_DIRECTORY_VERSION)
+ 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);
+ }
+
+ }
+
+ up(&nodemgr_serialize);
+
+ len = out - page;
+ len -= off;
+ if (len < count) {
+ *eof = 1;
+ if (len <= 0)
+ return 0;
+ } else
+ len = count;
+
+ *start = page + off;
+
+ return len;
+}
+
+#undef PUTF
+#endif /* CONFIG_PROC_FS */
+
static void nodemgr_process_config_rom(struct node_entry *ne,
quadlet_t busoptions);
+static int nodemgr_read_quadlet(struct hpsb_host *host,
+ nodeid_t nodeid, unsigned int generation,
+ octlet_t address, quadlet_t *quad)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < 3; i++) {
+ ret = hpsb_read(host, nodeid, generation, address, quad, 4);
+ if (!ret)
+ break;
+ }
+ *quad = be32_to_cpu(*quad);
+
+ return ret;
+}
+
+static int nodemgr_size_text_leaf(struct hpsb_host *host,
+ nodeid_t nodeid, unsigned int generation,
+ octlet_t address)
+{
+ quadlet_t quad;
+ int size = 0;
+ if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
+ return -1;
+ if (CONFIG_ROM_KEY(quad) == CONFIG_ROM_DESCRIPTOR_LEAF) {
+ /* This is the offset. */
+ address += 4 * CONFIG_ROM_VALUE(quad);
+ if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
+ return -1;
+ /* Now we got the size of the text descriptor leaf. */
+ size = CONFIG_ROM_LEAF_LENGTH(quad);
+ }
+ return size;
+}
+
+static int nodemgr_read_text_leaf(struct node_entry *ne,
+ octlet_t address,
+ quadlet_t *quadp)
+{
+ quadlet_t quad;
+ int i, size, ret;
+
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad)
+ && CONFIG_ROM_KEY(quad) != CONFIG_ROM_DESCRIPTOR_LEAF)
+ return -1;
+
+ /* This is the offset. */
+ address += 4 * CONFIG_ROM_VALUE(quad);
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad))
+ return -1;
+
+ /* Now we got the size of the text descriptor leaf. */
+ size = CONFIG_ROM_LEAF_LENGTH(quad) - 2;
+ if (size <= 0)
+ return -1;
+
+ address += 4;
+ for (i = 0; i < 2; i++, address += 4, quadp++) {
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, quadp))
+ return -1;
+ }
+
+ /* Now read the text string. */
+ 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);
+ if (ret != -EAGAIN)
+ break;
+ }
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static struct node_entry *nodemgr_scan_root_directory
+ (struct hpsb_host *host, nodeid_t nodeid, unsigned int generation)
+{
+ octlet_t address;
+ quadlet_t quad;
+ int length;
+ int code, size, total_size;
+ struct node_entry *ne;
+
+ address = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
+
+ if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
+ return NULL;
+ address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
+
+ if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
+ return NULL;
+ length = CONFIG_ROM_ROOT_LENGTH(quad);
+ address += 4;
+
+ size = 0;
+ total_size = sizeof(struct node_entry);
+ for (; length > 0; length--, address += 4) {
+ if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
+ return NULL;
+ code = CONFIG_ROM_KEY(quad);
+
+ if (code == CONFIG_ROM_VENDOR_ID && length > 0) {
+ /* Check if there is a text descriptor leaf
+ immediately after this. */
+ size = nodemgr_size_text_leaf(host, nodeid, generation,
+ address + 4);
+ if (size > 0) {
+ address += 4;
+ length--;
+ total_size += (size + 1) * sizeof (quadlet_t);
+ }
+ 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;
+ }
+ }
+ return ne;
+}
static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions,
- struct hpsb_host *host, nodeid_t nodeid)
+ struct hpsb_host *host,
+ nodeid_t nodeid, unsigned int generation)
{
struct node_entry *ne;
- unsigned long flags;
- ne = kmalloc(sizeof(struct node_entry), SLAB_ATOMIC);
+ ne = nodemgr_scan_root_directory (host, nodeid, generation);
if (!ne) return NULL;
INIT_LIST_HEAD(&ne->list);
@@ -81,17 +323,16 @@
ne->host = host;
ne->nodeid = nodeid;
ne->guid = guid;
- atomic_set(&ne->generation, get_hpsb_generation(ne->host));
+ ne->generation = generation;
- 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);
+ HPSB_DEBUG("%s added: Node[" NODE_BUS_FMT "] GUID[%016Lx] [%s]",
+ (host->node_id == nodeid) ? "Host" : "Device",
+ NODE_BUS_ARGS(nodeid), (unsigned long long)guid,
+ ne->vendor_name ?: "Unknown");
return ne;
}
@@ -122,30 +363,102 @@
return NULL;
}
-int nodemgr_read_quadlet(struct node_entry *ne,
- octlet_t address, quadlet_t *quad)
+static struct unit_directory *nodemgr_scan_unit_directory
+ (struct node_entry *ne, octlet_t address)
{
- int i;
- int ret = 0;
+ struct unit_directory *ud;
+ quadlet_t quad;
+ u8 flags, todo;
+ int length, size, total_size, count;
+ int vendor_name_size, model_name_size;
- for (i = 0; i < 3; i++) {
- ret = hpsb_read(ne->host, ne->nodeid, address, quad, 4);
- if (ret != -EAGAIN)
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad))
+ return NULL;
+ length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ;
+ address += 4;
+
+ size = 0;
+ total_size = sizeof (struct unit_directory);
+ flags = 0;
+ count = 0;
+ vendor_name_size = 0;
+ model_name_size = 0;
+ for (; length > 0; length--, address += 4) {
+ int code;
+ quadlet_t value;
+
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
+ address, &quad))
+ return NULL;
+ code = CONFIG_ROM_KEY(quad);
+ value = CONFIG_ROM_VALUE(quad);
+
+ todo = 0;
+ switch (code) {
+ case CONFIG_ROM_VENDOR_ID:
+ todo = UNIT_DIRECTORY_VENDOR_TEXT;
break;
- }
- *quad = be32_to_cpu(*quad);
- return ret;
-}
+ case CONFIG_ROM_MODEL_ID:
+ todo = UNIT_DIRECTORY_MODEL_TEXT;
+ break;
+
+ case CONFIG_ROM_SPECIFIER_ID:
+ case CONFIG_ROM_UNIT_SW_VERSION:
+ break;
-#define CONFIG_ROM_VENDOR_ID 0x03
-#define CONFIG_ROM_MODEL_ID 0x17
-#define CONFIG_ROM_NODE_CAPABILITES 0x0C
-#define CONFIG_ROM_UNIT_DIRECTORY 0xd1
-#define CONFIG_ROM_SPECIFIER_ID 0x12
-#define CONFIG_ROM_VERSION 0x13
-#define CONFIG_ROM_DESCRIPTOR_LEAF 0x81
-#define CONFIG_ROM_DESCRIPTOR_DIRECTORY 0xc1
+ case CONFIG_ROM_DESCRIPTOR_LEAF:
+ case CONFIG_ROM_DESCRIPTOR_DIRECTORY:
+ /* TODO: read strings... icons? */
+ 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)
+ count++;
+ break;
+ }
+
+ if (todo && length > 0) {
+ /* Check if there is a text descriptor leaf
+ immediately after this. */
+ size = nodemgr_size_text_leaf(ne->host,
+ ne->nodeid,
+ ne->generation,
+ address + 4);
+
+ if (todo == UNIT_DIRECTORY_VENDOR_TEXT)
+ vendor_name_size = size;
+ else
+ model_name_size = size;
+
+ if (size > 0) {
+ address += 4;
+ length--;
+ flags |= todo;
+ total_size += (size + 1) * sizeof (quadlet_t);
+ }
+ else if (size < 0)
+ return NULL;
+ }
+ }
+ total_size += count * sizeof (quadlet_t);
+ ud = kmalloc (total_size, GFP_KERNEL);
+ if (ud != NULL) {
+ memset (ud, 0, sizeof *ud);
+ ud->flags = flags;
+ ud->count = 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
@@ -156,41 +469,73 @@
octlet_t address)
{
struct unit_directory *ud;
- octlet_t a;
quadlet_t quad;
- int length, i;
+ quadlet_t *infop;
+ int length;
- if (!(ud = kmalloc (sizeof *ud, GFP_KERNEL)))
+ if (!(ud = nodemgr_scan_unit_directory(ne, address)))
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))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
+ address, &quad))
goto unit_directory_error;
- length = quad >> 16;
- a = address + 4;
+ length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ;
+ address += 4;
- for (i = 0; i < length; i++, a += 4) {
+ infop = (quadlet_t *) ud->quadlets;
+ for (; length > 0; length--, address += 4, infop++) {
int code;
quadlet_t value;
+ quadlet_t *quadp;
- if (nodemgr_read_quadlet(ne, a, &quad))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
+ address, &quad))
goto unit_directory_error;
- code = quad >> 24;
- value = quad & 0xffffff;
+ code = CONFIG_ROM_KEY(quad) ;
+ value = CONFIG_ROM_VALUE(quad);
switch (code) {
case CONFIG_ROM_VENDOR_ID:
ud->vendor_id = value;
ud->flags |= UNIT_DIRECTORY_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) {
+ /* We only support minimal
+ ASCII and English. */
+ quadp[ud->vendor_name_size] = 0;
+ ud->vendor_name
+ = (const char *) &(quadp[2]);
+ }
+ }
break;
case CONFIG_ROM_MODEL_ID:
ud->model_id = value;
ud->flags |= UNIT_DIRECTORY_MODEL_ID;
+ 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) {
+ /* We only support minimal
+ ASCII and English. */
+ quadp[ud->model_name_size] = 0;
+ ud->model_name
+ = (const char *) &(quadp[2]);
+ }
+ }
break;
case CONFIG_ROM_SPECIFIER_ID:
@@ -198,7 +543,7 @@
ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
break;
- case CONFIG_ROM_VERSION:
+ case CONFIG_ROM_UNIT_SW_VERSION:
ud->version = value;
ud->flags |= UNIT_DIRECTORY_VERSION;
break;
@@ -209,12 +554,13 @@
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++;
- }
+ /* 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;
+ break;
}
}
@@ -233,53 +579,74 @@
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
struct list_head *l;
- HPSB_DEBUG("vendor_id=0x%06x, capabilities=0x%06x",
- ne->vendor_id, ne->capabilities);
+ 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 ", ud->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 ", ud->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);
}
-#else
- return;
#endif
+ return;
}
static void nodemgr_process_root_directory(struct node_entry *ne)
{
octlet_t address;
quadlet_t quad;
- int length, i;
+ int length;
address = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
- if (nodemgr_read_quadlet(ne, address, &quad))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
+ address, &quad))
return;
- address += 4 + (quad >> 24) * 4;
+ address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
- if (nodemgr_read_quadlet(ne, address, &quad))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
+ address, &quad))
return;
- length = quad >> 16;
+ length = CONFIG_ROM_ROOT_LENGTH(quad);
address += 4;
- for (i = 0; i < length; i++, address += 4) {
+ for (; length > 0; length--, address += 4) {
int code, value;
- if (nodemgr_read_quadlet(ne, address, &quad))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
+ address, &quad))
return;
- code = quad >> 24;
- value = quad & 0xffffff;
+ code = CONFIG_ROM_KEY(quad);
+ value = CONFIG_ROM_VALUE(quad);
switch (code) {
case CONFIG_ROM_VENDOR_ID:
ne->vendor_id = value;
+ /* 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)
+ /* We only support minimal
+ ASCII and English. */
+ ne->vendor_name = NULL;
+ }
break;
case CONFIG_ROM_NODE_CAPABILITES:
@@ -307,6 +674,10 @@
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)
@@ -392,11 +763,9 @@
void hpsb_release_unit_directory(struct unit_directory *ud)
{
- unsigned long flags;
-
- write_lock_irqsave(&unit_directory_lock, flags);
+ down(&nodemgr_serialize);
nodemgr_release_unit_directory(ud);
- write_unlock_irqrestore(&unit_directory_lock, flags);
+ up(&nodemgr_serialize);
}
static void nodemgr_free_unit_directories(struct node_entry *ne)
@@ -486,13 +855,12 @@
{
struct unit_directory *ud;
struct list_head *lh;
- unsigned long flags;
- write_lock_irqsave(&driver_lock, flags);
+ if (down_interruptible(&nodemgr_serialize))
+ return -EINTR;
+
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) {
@@ -501,7 +869,8 @@
if (nodemgr_match_driver(driver, ud) && driver->probe(ud) == 0)
nodemgr_claim_unit_directory(ud, driver);
}
- write_unlock_irqrestore(&unit_directory_lock, flags);
+
+ up(&nodemgr_serialize);
/*
* Right now registration always succeeds, but maybe we should
@@ -515,13 +884,10 @@
{
struct list_head *lh;
struct unit_directory *ud;
- unsigned long flags;
- write_lock_irqsave(&driver_lock, flags);
- list_del(&driver->list);
- write_unlock_irqrestore(&driver_lock, flags);
+ down(&nodemgr_serialize);
- write_lock_irqsave(&unit_directory_lock, flags);
+ list_del(&driver->list);
lh = driver->unit_directories.next;
while (lh != &driver->unit_directories) {
ud = list_entry(lh, struct unit_directory, driver_list);
@@ -530,14 +896,13 @@
ud->driver->disconnect(ud);
nodemgr_release_unit_directory(ud);
}
- write_unlock_irqrestore(&unit_directory_lock, flags);
+
+ up(&nodemgr_serialize);
}
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;
@@ -563,11 +928,9 @@
* 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);
}
/*
@@ -578,7 +941,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)
+ struct hpsb_host *host,
+ nodeid_t nodeid, unsigned int generation)
{
struct list_head *lh;
struct unit_directory *ud;
@@ -593,7 +957,7 @@
nodemgr_process_config_rom (ne, busoptions);
/* Since that's done, we can declare this record current */
- atomic_set(&ne->generation, get_hpsb_generation(ne->host));
+ ne->generation = generation;
list_for_each (lh, &ne->unit_directories) {
ud = list_entry (lh, struct unit_directory, node_list);
@@ -602,93 +966,86 @@
}
}
-static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid,
+static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid, unsigned int generation,
quadlet_t *buffer, int buffer_length)
{
- octlet_t base = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
- int retries = 3;
- int header_count;
+ octlet_t addr = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
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;
- }
+ int i;
- header_count = 0;
- header_size = 0;
+ /* IEEE 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. */
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
HPSB_INFO("Initiating ConfigROM request for node " NODE_BUS_FMT,
NODE_BUS_ARGS(nodeid));
#endif
+ /*
+ * Must retry a few times if config rom read returns zero (how long?). Will
+ * not normally occur, but we should do the right thing. For example, with
+ * some sbp2 devices, the bridge chipset cannot return valid config rom reads
+ * immediately after power-on, since they need to detect the type of
+ * device attached (disk or CD-ROM).
+ */
+ for (i = 0; i < 4; i++) {
+ if (nodemgr_read_quadlet(host, nodeid, generation,
+ addr, &buffer[0]) < 0) {
+ HPSB_ERR("ConfigROM quadlet transaction error for node "
+ NODE_BUS_FMT, NODE_BUS_ARGS(nodeid));
+ return -1;
+ }
+ if (buffer[0])
+ break;
- /* 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;
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (schedule_timeout (HZ/4))
+ return -1;
}
- buffer[header_count++] = be32_to_cpu(quad);
header_size = buffer[0] >> 24;
+ addr += 4;
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);
+ 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,
+ for (i = 1; i < buffer_length; i++, addr += 4) {
+ if (nodemgr_read_quadlet(host, nodeid, generation,
+ addr, &buffer[i]) < 0) {
+ HPSB_ERR("ConfigROM quadlet transaction "
+ "error for node " NODE_BUS_FMT,
NODE_BUS_ARGS(nodeid));
- goto retry_configrom;
+ return -1;
}
- buffer[header_count++] = be32_to_cpu(quad);
}
return 0;
-}
+}
static void nodemgr_remove_node(struct node_entry *ne)
{
- unsigned long flags;
-
- HPSB_DEBUG("Device removed: node " NODE_BUS_FMT ", GUID %016Lx",
- NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid);
+ HPSB_DEBUG("%s removed: Node[" NODE_BUS_FMT "] GUID[%016Lx] [%s]",
+ (ne->host->node_id == ne->nodeid) ? "Host" : "Device",
+ NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid,
+ ne->vendor_name ?: "Unknown");
- 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);
return;
}
-/* Used to schedule each nodes config rom probe */
-struct node_probe_task {
- nodeid_t nodeid;
- struct hpsb_host *host;
- atomic_t *count;
- struct tq_struct task;
-};
-
/* This is where we probe the nodes for their information and provided
* features. */
-static void nodemgr_node_probe_one(void *__npt)
+static void nodemgr_node_probe_one(struct hpsb_host *host,
+ nodeid_t nodeid, int generation)
{
- struct node_probe_task *npt = (struct node_probe_task *)__npt;
struct node_entry *ne;
quadlet_t buffer[5];
octlet_t guid;
@@ -696,173 +1053,200 @@
/* We need to detect when the ConfigROM's generation has changed,
* so we only update the node's info when it needs to be. */
- if (read_businfo_block (npt->host, npt->nodeid, buffer, sizeof(buffer) >> 2))
- goto probe_complete;
+ if (read_businfo_block (host, nodeid, generation,
+ buffer, sizeof(buffer) >> 2))
+ return;
if (buffer[1] != IEEE1394_BUSID_MAGIC) {
- /* This isn't a 1394 device */
- HPSB_ERR("Node " NODE_BUS_FMT " isn't an IEEE 1394 device",
- NODE_BUS_ARGS(npt->nodeid));
- goto probe_complete;
+ /* This isn't a 1394 device, but we let it slide. There
+ * was a report of a device with broken firmware which
+ * reported '2394' instead of '1394', which is obviously a
+ * mistake. One would hope that a non-1394 device never
+ * gets connected to Firewire bus. If someone does, we
+ * shouldn't be held responsible, so we'll allow it with a
+ * warning. */
+ HPSB_WARN("Node " NODE_BUS_FMT " has invalid busID magic [0x%08x]",
+ NODE_BUS_ARGS(nodeid), buffer[1]);
}
guid = ((u64)buffer[3] << 32) | buffer[4];
- ne = hpsb_guid_get_entry(guid);
+ ne = find_entry_by_guid(guid);
if (!ne)
- nodemgr_create_node(guid, buffer[2], npt->host, npt->nodeid);
+ nodemgr_create_node(guid, buffer[2], host, nodeid, generation);
else
- nodemgr_update_node(ne, buffer[2], npt->host, npt->nodeid);
-
-probe_complete:
- atomic_dec(npt->count);
-
- kfree(npt);
+ nodemgr_update_node(ne, buffer[2], host, nodeid, generation);
return;
}
-static void nodemgr_node_probe_cleanup(void *__npt)
+static void nodemgr_node_probe_cleanup(struct hpsb_host *host, unsigned int generation)
{
- struct node_probe_task *npt = (struct node_probe_task *)__npt;
- unsigned long flags;
struct list_head *lh, *next;
struct node_entry *ne;
- /* If things aren't done yet, reschedule ourselves. */
- if (atomic_read(npt->count)) {
- schedule_task(&npt->task);
- return;
- }
-
- kfree(npt->count);
-
/* Now check to see if we have any nodes that aren't referenced
* any longer. */
- write_lock_irqsave(&node_lock, flags);
- for (lh = node_list.next; lh != &node_list; lh = next) {
+ list_for_each_safe(lh, next, &node_list) {
ne = list_entry(lh, struct node_entry, list);
- next = lh->next;
/* Only checking this host */
- if (ne->host != npt->host)
+ if (ne->host != host)
continue;
/* If the generation didn't get updated, then either the
* node was removed, or it failed the above probe. Either
* way, we remove references to it, since they are
* invalid. */
- if (!hpsb_node_entry_valid(ne))
+ if (ne->generation != generation)
nodemgr_remove_node(ne);
}
- write_unlock_irqrestore(&node_lock, flags);
-
- kfree(npt);
return;
}
-static void nodemgr_node_probe(void *__host)
+static void nodemgr_node_probe(struct hpsb_host *host)
{
- struct hpsb_host *host = (struct hpsb_host *)__host;
- int nodecount = host->node_count;
+ int count;
struct selfid *sid = (struct selfid *)host->topology_map;
nodeid_t nodeid = LOCAL_BUS;
- struct node_probe_task *npt;
- atomic_t *count;
+ unsigned int generation;
- count = kmalloc(sizeof (*count), GFP_KERNEL);
-
- if (count == NULL) {
- HPSB_ERR ("NodeMgr: out of memory in nodemgr_node_probe");
+ /* 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;
- }
- atomic_set(count, 0);
-
- for (; nodecount; nodecount--, nodeid++, sid++) {
- while (sid->extended)
- sid++;
- if (!sid->link_active || nodeid == host->node_id)
+ /* 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++) {
+ if (sid->extended)
continue;
- npt = kmalloc(sizeof (*npt), GFP_KERNEL);
-
- if (npt == NULL) {
- HPSB_ERR ("NodeMgr: out of memory in nodemgr_node_probe");
- break;
+ if (!sid->link_active) {
+ nodeid++;
+ continue;
}
- INIT_TQUEUE(&npt->task, nodemgr_node_probe_one, npt);
- npt->host = host;
- npt->nodeid = nodeid;
- npt->count = count;
-
- atomic_inc(count);
-
- schedule_task(&npt->task);
+ nodemgr_node_probe_one(host, nodeid++, generation);
}
- /* Now schedule a task to clean things up after the node probes
- * are done. */
- npt = kmalloc (sizeof (*npt), GFP_KERNEL);
+ /* 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. */
+ if (generation == get_hpsb_generation(host))
+ nodemgr_node_probe_cleanup(host, generation);
- if (npt == NULL) {
- HPSB_ERR ("NodeMgr: out of memory in nodemgr_node_probe");
- return;
- }
+ return;
+}
+
+static int nodemgr_host_thread(void *__hi)
+{
+ struct host_info *hi = (struct host_info *)__hi;
- INIT_TQUEUE(&npt->task, nodemgr_node_probe_cleanup, npt);
- npt->host = host;
- npt->nodeid = 0;
- npt->count = count;
+ /* No userlevel access needed */
+ daemonize();
- schedule_task(&npt->task);
+ strcpy(current->comm, "knodemgrd");
+
+ /* 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);
+ up(&nodemgr_serialize);
+ }
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+ HPSB_DEBUG ("NodeMgr: Exiting thread for %s", hi->host->driver->name);
+#endif
- return;
+ complete_and_exit(&hi->exited, 0);
}
struct node_entry *hpsb_guid_get_entry(u64 guid)
{
- unsigned long flags;
struct node_entry *ne;
- read_lock_irqsave(&node_lock, flags);
+ down(&nodemgr_serialize);
ne = find_entry_by_guid(guid);
- read_unlock_irqrestore(&node_lock, flags);
+ up(&nodemgr_serialize);
return ne;
}
struct node_entry *hpsb_nodeid_get_entry(nodeid_t nodeid)
{
- unsigned long flags;
struct node_entry *ne;
- read_lock_irqsave(&node_lock, flags);
+ down(&nodemgr_serialize);
ne = find_entry_by_nodeid(nodeid);
- read_unlock_irqrestore(&node_lock, flags);
+ up(&nodemgr_serialize);
return ne;
}
-struct hpsb_host *hpsb_get_host_by_ne(struct node_entry *ne)
+/* The following four convenience functions use a struct node_entry
+ * for addressing a node on the bus. They are intended for use by any
+ * process context, not just the nodemgr thread, so we need to be a
+ * little careful when reading out the node ID and generation. The
+ * thing that can go wrong is that we get the node ID, then a bus
+ * reset occurs, and then we read the generation. The node ID is
+ * possibly invalid, but the generation is current, and we end up
+ * sending a packet to a the wrong node.
+ *
+ * The solution is to make sure we read the generation first, so that
+ * if a reset occurs in the process, we end up with a stale generation
+ * and the transactions will fail instead of silently using wrong node
+ * ID's.
+ */
+
+void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt)
{
- if (atomic_read(&ne->generation) != get_hpsb_generation(ne->host))
- return NULL;
- if (ne->nodeid == ne->host->node_id) return ne->host;
- return NULL;
+ pkt->host = ne->host;
+ pkt->generation = ne->generation;
+ barrier();
+ pkt->node_id = ne->nodeid;
}
-int hpsb_guid_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt)
+int hpsb_node_read(struct node_entry *ne, u64 addr,
+ quadlet_t *buffer, size_t length)
{
- if (atomic_read(&ne->generation) != get_hpsb_generation(ne->host))
- return 0;
+ unsigned int generation = ne->generation;
- pkt->host = ne->host;
- pkt->node_id = ne->nodeid;
- pkt->generation = atomic_read(&ne->generation);
- return 1;
+ barrier();
+ return hpsb_read(ne->host, ne->nodeid, generation,
+ addr, buffer, length);
+}
+
+int hpsb_node_write(struct node_entry *ne, u64 addr,
+ quadlet_t *buffer, size_t length)
+{
+ unsigned int generation = ne->generation;
+
+ barrier();
+ return hpsb_write(ne->host, ne->nodeid, generation,
+ addr, buffer, length);
+}
+
+int hpsb_node_lock(struct node_entry *ne, u64 addr,
+ int extcode, quadlet_t *data, quadlet_t arg)
+{
+ unsigned int generation = ne->generation;
+
+ barrier();
+ return hpsb_lock(ne->host, ne->nodeid, generation,
+ addr, extcode, data, arg);
}
static void nodemgr_add_host(struct hpsb_host *host)
@@ -875,11 +1259,23 @@
return;
}
- /* We simply initialize the struct here. We don't start the thread
- * until the first bus reset. */
+ /* 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_TQUEUE(&hi->task, nodemgr_node_probe, host);
+ init_completion(&hi->exited);
+ sema_init(&hi->reset_sem, 0);
+
+ 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);
+ return;
+ }
spin_lock_irqsave (&host_info_lock, flags);
list_add_tail (&hi->list, &host_info_list);
@@ -903,14 +1299,14 @@
}
}
- if (hi == NULL) {
+ if (hi != NULL)
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+ HPSB_DEBUG ("NodeMgr: Processing host reset for %s", host->driver->name);
+#endif
+ up(&hi->reset_sem);
+ else
HPSB_ERR ("NodeMgr: could not process reset of non-existent host");
- goto done_reset_host;
- }
-
- schedule_task(&hi->task);
-done_reset_host:
spin_unlock_irqrestore (&host_info_lock, flags);
return;
@@ -921,53 +1317,63 @@
struct list_head *lh, *next;
struct node_entry *ne;
unsigned long flags;
-
- /* Make sure we have no active scans */
- flush_scheduled_tasks();
-
- /* First remove all node entries for this host */
- write_lock_irqsave(&node_lock, flags);
-
- 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)
- continue;
-
- nodemgr_remove_node(ne);
- }
- write_unlock_irqrestore(&node_lock, 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 *hi = list_entry(lh, struct host_info, list);
- if (hi->host == host) {
- list_del(&hi->list);
- kfree (hi);
+ 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);
+
+ if (hi) {
+ if (hi->pid >= 0) {
+ kill_proc(hi->pid, SIGTERM, 1);
+ wait_for_completion(&hi->exited);
+ }
+ kfree(hi);
+ }
+ else
+ HPSB_ERR("NodeMgr: host %s does not exist, cannot remove",
+ host->driver->name);
- if (lh == host_info_list.next)
- HPSB_ERR ("NodeMgr: could not remove non-existent host");
+ down(&nodemgr_serialize);
- spin_unlock_irqrestore (&host_info_lock, flags);
+ /* Even if we fail the host_info part, remove all the node
+ * entries. */
+ list_for_each_safe(lh, next, &node_list) {
+ ne = list_entry(lh, struct node_entry, list);
+
+ if (ne->host == host)
+ nodemgr_remove_node(ne);
+ }
+
+ up(&nodemgr_serialize);
return;
}
static struct hpsb_highlevel_ops nodemgr_ops = {
- add_host: nodemgr_add_host,
- host_reset: nodemgr_host_reset,
- remove_host: nodemgr_remove_host,
+ .add_host = nodemgr_add_host,
+ .host_reset = nodemgr_host_reset,
+ .remove_host = nodemgr_remove_host,
};
static struct hpsb_highlevel *hl;
-void init_ieee1394_nodemgr(void)
+#define PROC_ENTRY "devices"
+
+void init_ieee1394_nodemgr(int disable_hotplug)
{
+ 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");
@@ -977,4 +1383,7 @@
void cleanup_ieee1394_nodemgr(void)
{
hpsb_unregister_highlevel(hl);
+#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)