📄 nodemgr.c
字号:
/*
* Node information (ConfigROM) collection and management.
*
* 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/bitmap.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
#include <asm/atomic.h>
#include <asm/semaphore.h>
#include "csr.h"
#include "highlevel.h"
#include "hosts.h"
#include "ieee1394.h"
#include "ieee1394_core.h"
#include "ieee1394_hotplug.h"
#include "ieee1394_types.h"
#include "ieee1394_transactions.h"
#include "nodemgr.h"
static int ignore_drivers;
module_param(ignore_drivers, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers.");
struct nodemgr_csr_info {
struct hpsb_host *host;
nodeid_t nodeid;
unsigned int generation;
unsigned int speed_unverified:1;
};
/*
* 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 error;
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;
error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
&q, sizeof(quadlet_t));
if (error)
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 error;
}
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, error;
for (i = 1; ; i++) {
error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
buffer, length);
if (!error) {
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)) {
error = nodemgr_check_speed(ci, addr, buffer);
if (!error)
break;
}
if (msleep_interruptible(334))
return -EINTR;
}
return error;
}
static int nodemgr_get_max_rom(quadlet_t *bus_info_data, void *__ci)
{
return (be32_to_cpu(bus_info_data[2]) >> 8) & 0x3;
}
static struct csr1212_bus_ops nodemgr_csr_ops = {
.bus_read = nodemgr_bus_read,
.get_max_rom = nodemgr_get_max_rom
};
/*
* 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
* 1394 type, and that the crc checks out ok. After that we start off with
* the root directory, and subdirectories. To do this, we retrieve the
* quadlet header for a directory, find out the length, and retrieve the
* 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.
*/
/*
* The nodemgr relies heavily on the Driver Model for device callbacks and
* driver/device mappings. The old nodemgr used to handle all this itself,
* but now we are much simpler because of the LDM.
*/
struct host_info {
struct hpsb_host *host;
struct list_head list;
struct task_struct *thread;
};
static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
static int nodemgr_uevent(struct device *dev, 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);
struct bus_type ieee1394_bus_type = {
.name = "ieee1394",
.match = nodemgr_bus_match,
};
static void host_cls_release(struct device *dev)
{
put_device(&container_of((dev), struct hpsb_host, host_dev)->device);
}
struct class hpsb_host_class = {
.name = "ieee1394_host",
.dev_release = host_cls_release,
};
static void ne_cls_release(struct device *dev)
{
put_device(&container_of((dev), struct node_entry, node_dev)->device);
}
static struct class nodemgr_ne_class = {
.name = "ieee1394_node",
.dev_release = ne_cls_release,
};
static void ud_cls_release(struct device *dev)
{
put_device(&container_of((dev), struct unit_directory, unit_dev)->device);
}
/* The name here is only so that unit directory hotplug works with old
* style hotplug, which only ever did unit directories anyway.
*/
static struct class nodemgr_ud_class = {
.name = "ieee1394",
.dev_release = ud_cls_release,
.dev_uevent = nodemgr_uevent,
};
static struct hpsb_highlevel nodemgr_highlevel;
static void nodemgr_release_ud(struct device *dev)
{
struct unit_directory *ud = container_of(dev, struct unit_directory, device);
if (ud->vendor_name_kv)
csr1212_release_keyval(ud->vendor_name_kv);
if (ud->model_name_kv)
csr1212_release_keyval(ud->model_name_kv);
kfree(ud);
}
static void nodemgr_release_ne(struct device *dev)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
if (ne->vendor_name_kv)
csr1212_release_keyval(ne->vendor_name_kv);
kfree(ne);
}
static void nodemgr_release_host(struct device *dev)
{
struct hpsb_host *host = container_of(dev, struct hpsb_host, device);
csr1212_destroy_csr(host->csr.rom);
kfree(host);
}
static int nodemgr_ud_platform_data;
static struct device nodemgr_dev_template_ud = {
.bus = &ieee1394_bus_type,
.release = nodemgr_release_ud,
.platform_data = &nodemgr_ud_platform_data,
};
static struct device nodemgr_dev_template_ne = {
.bus = &ieee1394_bus_type,
.release = nodemgr_release_ne,
};
/* This dummy driver prevents the host devices from being scanned. We have no
* useful drivers for them yet, and there would be a deadlock possible if the
* driver core scans the host device while the host's low-level driver (i.e.
* the host's parent device) is being removed. */
static struct device_driver nodemgr_mid_layer_driver = {
.bus = &ieee1394_bus_type,
.name = "nodemgr",
.owner = THIS_MODULE,
};
struct device nodemgr_dev_template_host = {
.bus = &ieee1394_bus_type,
.release = nodemgr_release_host,
};
#define fw_attr(class, class_type, field, type, format_string) \
static ssize_t fw_show_##class##_##field (struct device *dev, struct device_attribute *attr, char *buf)\
{ \
class_type *class; \
class = container_of(dev, class_type, device); \
return sprintf(buf, format_string, (type)class->field); \
} \
static struct device_attribute dev_attr_##class##_##field = { \
.attr = {.name = __stringify(field), .mode = S_IRUGO }, \
.show = fw_show_##class##_##field, \
};
#define fw_attr_td(class, class_type, td_kv) \
static ssize_t fw_show_##class##_##td_kv (struct device *dev, struct device_attribute *attr, char *buf)\
{ \
int len; \
class_type *class = container_of(dev, class_type, device); \
len = (class->td_kv->value.leaf.len - 2) * sizeof(quadlet_t); \
memcpy(buf, \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(class->td_kv), \
len); \
while ((buf + len - 1) == '\0') \
len--; \
buf[len++] = '\n'; \
buf[len] = '\0'; \
return len; \
} \
static struct device_attribute dev_attr_##class##_##td_kv = { \
.attr = {.name = __stringify(td_kv), .mode = S_IRUGO }, \
.show = fw_show_##class##_##td_kv, \
};
#define fw_drv_attr(field, type, format_string) \
static ssize_t fw_drv_show_##field (struct device_driver *drv, char *buf) \
{ \
struct hpsb_protocol_driver *driver; \
driver = container_of(drv, struct hpsb_protocol_driver, driver); \
return sprintf(buf, format_string, (type)driver->field);\
} \
static struct driver_attribute driver_attr_drv_##field = { \
.attr = {.name = __stringify(field), .mode = S_IRUGO }, \
.show = fw_drv_show_##field, \
};
static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribute *attr, char *buf)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
return sprintf(buf, "IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d) "
"LSPD(%d) MAX_REC(%d) MAX_ROM(%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.max_rom,
ne->busopt.cyc_clk_acc);
}
static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
#ifdef HPSB_DEBUG_TLABELS
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);
unsigned long flags;
unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
int tf;
spin_lock_irqsave(&hpsb_tlabel_lock, flags);
tf = 64 - bitmap_weight(tp, 64);
spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
return sprintf(buf, "%d\n", tf);
}
static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
static ssize_t fw_show_ne_tlabels_mask(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
unsigned long flags;
unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
u64 tm;
spin_lock_irqsave(&hpsb_tlabel_lock, flags);
#if (BITS_PER_LONG <= 32)
tm = ((u64)tp[0] << 32) + tp[1];
#else
tm = tp[0];
#endif
spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
return sprintf(buf, "0x%016llx\n", (unsigned long long)tm);
}
static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL);
#endif /* HPSB_DEBUG_TLABELS */
static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct unit_directory *ud = container_of(dev, struct unit_directory, device);
int state = simple_strtoul(buf, NULL, 10);
if (state == 1) {
ud->ignore_driver = 1;
device_release_driver(dev);
} else if (state == 0)
ud->ignore_driver = 0;
return count;
}
static ssize_t fw_get_ignore_driver(struct device *dev, struct device_attribute *attr, char *buf)
{
struct unit_directory *ud = container_of(dev, struct unit_directory, device);
return sprintf(buf, "%d\n", ud->ignore_driver);
}
static DEVICE_ATTR(ignore_driver, S_IWUSR | S_IRUGO, fw_get_ignore_driver, fw_set_ignore_driver);
static ssize_t fw_set_destroy_node(struct bus_type *bus, const char *buf, size_t count)
{
struct node_entry *ne;
u64 guid = (u64)simple_strtoull(buf, NULL, 16);
ne = find_entry_by_guid(guid);
if (ne == NULL || !ne->in_limbo)
return -EINVAL;
nodemgr_remove_ne(ne);
return count;
}
static ssize_t fw_get_destroy_node(struct bus_type *bus, char *buf)
{
return sprintf(buf, "You can destroy in_limbo nodes by writing their GUID to this file\n");
}
static BUS_ATTR(destroy_node, S_IWUSR | S_IRUGO, fw_get_destroy_node, fw_set_destroy_node);
static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf,
size_t count)
{
int error = 0;
if (simple_strtoul(buf, NULL, 10) == 1)
error = bus_rescan_devices(&ieee1394_bus_type);
return error ? error : count;
}
static ssize_t fw_get_rescan(struct bus_type *bus, char *buf)
{
return sprintf(buf, "You can force a rescan of the bus for "
"drivers by writing a 1 to this file\n");
}
static BUS_ATTR(rescan, S_IWUSR | S_IRUGO, fw_get_rescan, fw_set_rescan);
static ssize_t fw_set_ignore_drivers(struct bus_type *bus, const char *buf, size_t count)
{
int state = simple_strtoul(buf, NULL, 10);
if (state == 1)
ignore_drivers = 1;
else if (state == 0)
ignore_drivers = 0;
return count;
}
static ssize_t fw_get_ignore_drivers(struct bus_type *bus, char *buf)
{
return sprintf(buf, "%d\n", ignore_drivers);
}
static BUS_ATTR(ignore_drivers, S_IWUSR | S_IRUGO, fw_get_ignore_drivers, fw_set_ignore_drivers);
struct bus_attribute *const fw_bus_attrs[] = {
&bus_attr_destroy_node,
&bus_attr_rescan,
&bus_attr_ignore_drivers,
NULL
};
fw_attr(ne, struct node_entry, capabilities, unsigned int, "0x%06x\n")
fw_attr(ne, struct node_entry, nodeid, unsigned int, "0x%04x\n")
fw_attr(ne, struct node_entry, vendor_id, unsigned int, "0x%06x\n")
fw_attr_td(ne, struct node_entry, vendor_name_kv)
fw_attr(ne, struct node_entry, guid, unsigned long long, "0x%016Lx\n")
fw_attr(ne, struct node_entry, guid_vendor_id, unsigned int, "0x%06x\n")
fw_attr(ne, struct node_entry, in_limbo, int, "%d\n");
static struct device_attribute *const fw_ne_attrs[] = {
&dev_attr_ne_guid,
&dev_attr_ne_guid_vendor_id,
&dev_attr_ne_capabilities,
&dev_attr_ne_vendor_id,
&dev_attr_ne_nodeid,
&dev_attr_bus_options,
#ifdef HPSB_DEBUG_TLABELS
&dev_attr_tlabels_free,
&dev_attr_tlabels_mask,
#endif
};
fw_attr(ud, struct unit_directory, address, unsigned long long, "0x%016Lx\n")
fw_attr(ud, struct unit_directory, length, int, "%d\n")
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -