ieee1394: support for slow links or slow 1394b phy ports
Ben Collins [Mon, 12 Jun 2006 22:12:37 +0000 (18:12 -0400)]
Add support for the following types of hardware:
 + nodes that have a link speed < PHY speed
 + 1394b PHYs that are less than S800 capable
 + 1394b/1394a adapter cable between two 1394b PHYs
Also, S1600 and S3200 are now supported if IEEE1394_SPEED_MAX is raised.

A probing function is added to nodemgr's config ROM fetching routine
which adjusts the allowable speed if an access problem was encountered.
Pros and Cons of the approach:
 + minimum code footprint to support this less widely used hardware
 + nearly no overhead for unaffected hardware
 - ineffective before nodemgr began to read the ROM of affected nodes
 - ineffective if ieee1394 is loaded with disable_nodemgr=1
The speed map CSRs which are published to the bus are not touched by the
patch.

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Cc: Hakan Ardo <hakan@debian.org>
Cc: Calculex <linux@calculex.com>
Cc: Robert J. Kosinski <robk@cmcherald.com>
Signed-off-by: Ben Collins <bcollins@ubuntu.com>

drivers/ieee1394/eth1394.c
drivers/ieee1394/hosts.h
drivers/ieee1394/ieee1394_core.c
drivers/ieee1394/nodemgr.c
drivers/ieee1394/sbp2.c

index 30fa0d4..b2d2c4d 100644 (file)
@@ -502,10 +502,8 @@ static void ether1394_reset_priv (struct net_device *dev, int set_mtu)
 
        /* Determine speed limit */
        for (i = 0; i < host->node_count; i++)
-               if (max_speed > host->speed_map[NODEID_TO_NODE(host->node_id) *
-                                               64 + i])
-                       max_speed = host->speed_map[NODEID_TO_NODE(host->node_id) *
-                                                   64 + i];
+               if (max_speed > host->speed[i])
+                       max_speed = host->speed[i];
        priv->bc_sspd = max_speed;
 
        /* We'll use our maxpayload as the default mtu */
index 07d188c..d1a4760 100644 (file)
@@ -30,12 +30,13 @@ struct hpsb_host {
 
        unsigned char iso_listen_count[64];
 
-       int node_count; /* number of identified nodes on this bus */
-       int selfid_count; /* total number of SelfIDs received */
-       int nodes_active; /* number of nodes that are actually active */
+       int node_count;     /* number of identified nodes on this bus */
+       int selfid_count;   /* total number of SelfIDs received */
+       int nodes_active;   /* number of nodes with active link layer */
+       u8 speed[63];       /* speed between each node and local node */
 
-       nodeid_t node_id; /* node ID of this host */
-       nodeid_t irm_id; /* ID of this bus' isochronous resource manager */
+       nodeid_t node_id;   /* node ID of this host */
+       nodeid_t irm_id;    /* ID of this bus' isochronous resource manager */
        nodeid_t busmgr_id; /* ID of this bus' bus manager */
 
        /* this nodes state */
index be6854e..c83e2b8 100644 (file)
@@ -285,9 +285,9 @@ static int check_selfids(struct hpsb_host *host)
 
 static void build_speed_map(struct hpsb_host *host, int nodecount)
 {
-       u8 speedcap[nodecount];
        u8 cldcnt[nodecount];
        u8 *map = host->speed_map;
+       u8 *speedcap = host->speed;
        struct selfid *sid;
        struct ext_selfid *esid;
        int i, j, n;
@@ -354,6 +354,11 @@ static void build_speed_map(struct hpsb_host *host, int nodecount)
                        }
                }
        }
+
+       /* assume maximum speed for 1394b PHYs, nodemgr will correct it */
+       for (n = 0; n < nodecount; n++)
+               if (speedcap[n] == 3)
+                       speedcap[n] = IEEE1394_SPEED_MAX;
 }
 
 
@@ -554,11 +559,10 @@ int hpsb_send_packet(struct hpsb_packet *packet)
                return 0;
        }
 
-       if (packet->type == hpsb_async && packet->node_id != ALL_NODES) {
+       if (packet->type == hpsb_async &&
+           NODEID_TO_NODE(packet->node_id) != ALL_NODES)
                packet->speed_code =
-                       host->speed_map[NODEID_TO_NODE(host->node_id) * 64
-                                      + NODEID_TO_NODE(packet->node_id)];
-       }
+                       host->speed[NODEID_TO_NODE(packet->node_id)];
 
        dump_packet("send packet", packet->header, packet->header_size, packet->speed_code);
 
index 082c7fd..948f1b8 100644 (file)
@@ -38,6 +38,7 @@ struct nodemgr_csr_info {
        struct hpsb_host *host;
        nodeid_t nodeid;
        unsigned int generation;
+       unsigned int speed_unverified:1;
 };
 
 
@@ -57,23 +58,75 @@ static char *nodemgr_find_oui_name(int oui)
        return NULL;
 }
 
+/*
+ * Correct the speed map entry.  This is necessary
+ *  - for nodes with link speed < phy speed,
+ *  - for 1394b nodes with negotiated phy port speed < IEEE1394_SPEED_MAX.
+ * A possible speed is determined by trial and error, using quadlet reads.
+ */
+static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr,
+                              quadlet_t *buffer)
+{
+       quadlet_t q;
+       u8 i, *speed, old_speed, good_speed;
+       int ret;
+
+       speed = ci->host->speed + NODEID_TO_NODE(ci->nodeid);
+       old_speed = *speed;
+       good_speed = IEEE1394_SPEED_MAX + 1;
+
+       /* Try every speed from S100 to old_speed.
+        * If we did it the other way around, a too low speed could be caught
+        * if the retry succeeded for some other reason, e.g. because the link
+        * just finished its initialization. */
+       for (i = IEEE1394_SPEED_100; i <= old_speed; i++) {
+               *speed = i;
+               ret = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
+                               &q, sizeof(quadlet_t));
+               if (ret)
+                       break;
+               *buffer = q;
+               good_speed = i;
+       }
+       if (good_speed <= IEEE1394_SPEED_MAX) {
+               HPSB_DEBUG("Speed probe of node " NODE_BUS_FMT " yields %s",
+                          NODE_BUS_ARGS(ci->host, ci->nodeid),
+                          hpsb_speedto_str[good_speed]);
+               *speed = good_speed;
+               ci->speed_unverified = 0;
+               return 0;
+       }
+       *speed = old_speed;
+       return ret;
+}
 
 static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, u16 length,
                             void *buffer, void *__ci)
 {
        struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci;
-       int i, ret = 0;
+       int i, ret;
 
        for (i = 1; ; i++) {
                ret = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
                                buffer, length);
-               if (!ret || i == 3)
+               if (!ret) {
+                       ci->speed_unverified = 0;
+                       break;
+               }
+               /* Give up after 3rd failure. */
+               if (i == 3)
                        break;
 
+               /* The ieee1394_core guessed the node's speed capability from
+                * the self ID.  Check whether a lower speed works. */
+               if (ci->speed_unverified && length == sizeof(quadlet_t)) {
+                       ret = nodemgr_check_speed(ci, addr, buffer);
+                       if (!ret)
+                               break;
+               }
                if (msleep_interruptible(334))
                        return -EINTR;
        }
-
        return ret;
 }
 
@@ -1204,6 +1257,8 @@ static void nodemgr_node_scan_one(struct host_info *hi,
        ci->host = host;
        ci->nodeid = nodeid;
        ci->generation = generation;
+       ci->speed_unverified =
+               host->speed[NODEID_TO_NODE(nodeid)] > IEEE1394_SPEED_100;
 
        /* We need to detect when the ConfigROM's generation has changed,
         * so we only update the node's info when it needs to be.  */
index 24cd1e2..c30e03a 100644 (file)
@@ -1664,10 +1664,8 @@ static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id)
 
        SBP2_DEBUG_ENTER();
 
-       /* Initial setting comes from the hosts speed map */
        scsi_id->speed_code =
-           hi->host->speed_map[NODEID_TO_NODE(hi->host->node_id) * 64 +
-                               NODEID_TO_NODE(scsi_id->ne->nodeid)];
+           hi->host->speed[NODEID_TO_NODE(scsi_id->ne->nodeid)];
 
        /* Bump down our speed if the user requested it */
        if (scsi_id->speed_code > max_speed) {