* directory of the kernel sources for details.
*/
+#include <linux/bitmap.h>
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/list.h>
struct hpsb_host *host;
nodeid_t nodeid;
unsigned int generation;
+ unsigned int speed_unverified:1;
};
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;
}
};
static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
-static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp,
- char *buffer, int buffer_size);
+static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp,
+ char *buffer, int buffer_size);
static void nodemgr_resume_ne(struct node_entry *ne);
static void nodemgr_remove_ne(struct node_entry *ne);
static struct node_entry *find_entry_by_guid(u64 guid);
static struct class nodemgr_ud_class = {
.name = "ieee1394",
.release = ud_cls_release,
- .hotplug = nodemgr_hotplug,
+ .uevent = nodemgr_uevent,
};
static struct hpsb_highlevel nodemgr_highlevel;
static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
+/* tlabels_free, tlabels_allocations, tlabels_mask are read non-atomically
+ * here, therefore displayed values may be occasionally wrong. */
static ssize_t fw_show_ne_tlabels_free(struct device *dev, struct device_attribute *attr, char *buf)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
- return sprintf(buf, "%d\n", atomic_read(&ne->tpool->count.count) + 1);
+ return sprintf(buf, "%d\n", 64 - bitmap_weight(ne->tpool->pool, 64));
}
static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
unsigned int generation)
{
struct hpsb_host *host = hi->host;
- struct node_entry *ne;
-
- ne = kmalloc(sizeof(struct node_entry), GFP_KERNEL);
- if (!ne) return NULL;
+ struct node_entry *ne;
- memset(ne, 0, sizeof(struct node_entry));
+ ne = kzalloc(sizeof(*ne), GFP_KERNEL);
+ if (!ne)
+ return NULL;
ne->tpool = &host->tpool[nodeid & NODE_MASK];
- ne->host = host;
- ne->nodeid = nodeid;
+ ne->host = host;
+ ne->nodeid = nodeid;
ne->generation = generation;
ne->needs_probe = 1;
- ne->guid = guid;
+ ne->guid = guid;
ne->guid_vendor_id = (guid >> 40) & 0xffffff;
ne->guid_vendor_oui = nodemgr_find_oui_name(ne->guid_vendor_id);
ne->csr = csr;
(host->node_id == nodeid) ? "Host" : "Node",
NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid);
- return ne;
+ return ne;
}
struct csr1212_keyval *kv;
u8 last_key_id = 0;
- ud = kmalloc(sizeof(struct unit_directory), GFP_KERNEL);
+ ud = kzalloc(sizeof(*ud), GFP_KERNEL);
if (!ud)
goto unit_directory_error;
- memset (ud, 0, sizeof(struct unit_directory));
-
ud->ne = ne;
ud->ignore_driver = ignore_drivers;
ud->address = ud_kv->offset + CSR1212_CONFIG_ROM_SPACE_BASE;
/* Logical Unit Number */
if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) {
if (ud->flags & UNIT_DIRECTORY_HAS_LUN) {
- ud_child = kmalloc(sizeof(struct unit_directory), GFP_KERNEL);
+ ud_child = kmalloc(sizeof(*ud_child), GFP_KERNEL);
if (!ud_child)
goto unit_directory_error;
- memcpy(ud_child, ud, sizeof(struct unit_directory));
+ memcpy(ud_child, ud, sizeof(*ud_child));
nodemgr_register_device(ne, ud_child, &ne->device);
ud_child = NULL;
if (ud_child == NULL)
break;
- /* inherit unspecified values so hotplug picks it up */
+ /* inherit unspecified values, the driver core picks it up */
if ((ud->flags & UNIT_DIRECTORY_MODEL_ID) &&
!(ud_child->flags & UNIT_DIRECTORY_MODEL_ID))
{
#ifdef CONFIG_HOTPLUG
-static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp,
- char *buffer, int buffer_size)
+static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
{
struct unit_directory *ud;
int i = 0;
#else
-static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp,
- char *buffer, int buffer_size)
+static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
{
return -ENODEV;
}
struct csr1212_csr *csr;
struct nodemgr_csr_info *ci;
- ci = kmalloc(sizeof(struct nodemgr_csr_info), GFP_KERNEL);
+ ci = kmalloc(sizeof(*ci), GFP_KERNEL);
if (!ci)
return;
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. */
}
+/* Write the BROADCAST_CHANNEL as per IEEE1394a 8.3.2.3.11 and 8.4.2.3. This
+ * seems like an optional service but in the end it is practically mandatory
+ * as a consequence of these clauses.
+ *
+ * Note that we cannot do a broadcast write to all nodes at once because some
+ * pre-1394a devices would hang. */
+static void nodemgr_irm_write_bc(struct node_entry *ne, int generation)
+{
+ const u64 bc_addr = (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL);
+ quadlet_t bc_remote, bc_local;
+ int ret;
+
+ if (!ne->host->is_irm || ne->generation != generation ||
+ ne->nodeid == ne->host->node_id)
+ return;
+
+ bc_local = cpu_to_be32(ne->host->csr.broadcast_channel);
+
+ /* Check if the register is implemented and 1394a compliant. */
+ ret = hpsb_read(ne->host, ne->nodeid, generation, bc_addr, &bc_remote,
+ sizeof(bc_remote));
+ if (!ret && bc_remote & cpu_to_be32(0x80000000) &&
+ bc_remote != bc_local)
+ hpsb_node_write(ne, bc_addr, &bc_local, sizeof(bc_local));
+}
+
+
static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int generation)
{
struct device *dev;
if (!dev)
return;
+ nodemgr_irm_write_bc(ne, generation);
+
/* If "needs_probe", then this is either a new or changed node we
* rescan totally. If the generation matches for an existing node
* (one that existed prior to the bus reset) we send update calls
struct hpsb_host *host = hi->host;
struct class *class = &nodemgr_ne_class;
struct class_device *cdev;
+ struct node_entry *ne;
/* Do some processing of the nodes we've probed. This pulls them
* into the sysfs layer if needed, and can result in processing of
* unit-directories, or just updating the node and it's
- * unit-directories. */
+ * unit-directories.
+ *
+ * Run updates before probes. Usually, updates are time-critical
+ * while probes are time-consuming. (Well, those probes need some
+ * improvement...) */
+
down_read(&class->subsys.rwsem);
- list_for_each_entry(cdev, &class->children, node)
- nodemgr_probe_ne(hi, container_of(cdev, struct node_entry, class_dev), generation);
+ list_for_each_entry(cdev, &class->children, node) {
+ ne = container_of(cdev, struct node_entry, class_dev);
+ if (!ne->needs_probe)
+ nodemgr_probe_ne(hi, ne, generation);
+ }
+ list_for_each_entry(cdev, &class->children, node) {
+ ne = container_of(cdev, struct node_entry, class_dev);
+ if (ne->needs_probe)
+ nodemgr_probe_ne(hi, ne, generation);
+ }
up_read(&class->subsys.rwsem);
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 int nodemgr_send_resume_packet(struct hpsb_host *host)
+{
+ struct hpsb_packet *packet;
+ int ret = 1;
+
+ packet = hpsb_make_phypacket(host,
+ EXTPHYPACKET_TYPE_RESUME |
+ NODEID_TO_NODE(host->node_id) << PHYPACKET_PORT_SHIFT);
+ if (packet) {
+ packet->no_waiter = 1;
+ packet->generation = get_hpsb_generation(host);
+ ret = hpsb_send_packet(packet);
+ }
+ if (ret)
+ HPSB_WARN("fw-host%d: Failed to broadcast resume packet",
+ host->id);
+ return ret;
+}
+
+/* Perform a few high-level IRM responsibilities. */
static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
{
quadlet_t bc;
if (!host->is_irm || host->irm_id == (nodeid_t)-1)
return 1;
- 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));
+ /* We are a 1394a-2000 compliant IRM. Set the validity bit. */
+ host->csr.broadcast_channel |= 0x40000000;
/* If there is no bus manager then we should set the root node's
* force_root bit to promote bus stability per the 1394
}
}
+ /* Some devices suspend their ports while being connected to an inactive
+ * host adapter, i.e. if connected before the low-level driver is
+ * loaded. They become visible either when physically unplugged and
+ * replugged, or when receiving a resume packet. Send one once. */
+ if (!host->resume_packet_sent && !nodemgr_send_resume_packet(host))
+ host->resume_packet_sent = 1;
+
return 1;
}
/* Scan our nodes to get the bus options and create node
* entries. This does not do the sysfs stuff, since that
- * would trigger hotplug callbacks and such, which is a
- * bad idea at this point. */
+ * would trigger uevents and such, which is a bad idea at
+ * this point. */
nodemgr_node_scan(hi, generation);
/* This actually does the full probe, with sysfs