📄 nodemgr.c
字号:
/* 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 struct unit_directory *nodemgr_process_unit_directory
(struct host_info *hi, struct node_entry *ne, struct csr1212_keyval *ud_kv,
unsigned int *id, struct unit_directory *parent)
{
struct unit_directory *ud;
struct unit_directory *ud_child = NULL;
struct csr1212_dentry *dentry;
struct csr1212_keyval *kv;
u8 last_key_id = 0;
ud = kzalloc(sizeof(*ud), GFP_KERNEL);
if (!ud)
goto unit_directory_error;
ud->ne = ne;
ud->ignore_driver = ignore_drivers;
ud->address = ud_kv->offset + CSR1212_REGISTER_SPACE_BASE;
ud->directory_id = ud->address & 0xffffff;
ud->ud_kv = ud_kv;
ud->id = (*id)++;
csr1212_for_each_dir_entry(ne->csr, kv, ud_kv, dentry) {
switch (kv->key.id) {
case CSR1212_KV_ID_VENDOR:
if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) {
ud->vendor_id = kv->value.immediate;
ud->flags |= UNIT_DIRECTORY_VENDOR_ID;
}
break;
case CSR1212_KV_ID_MODEL:
ud->model_id = kv->value.immediate;
ud->flags |= UNIT_DIRECTORY_MODEL_ID;
break;
case CSR1212_KV_ID_SPECIFIER_ID:
ud->specifier_id = kv->value.immediate;
ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
break;
case CSR1212_KV_ID_VERSION:
ud->version = kv->value.immediate;
ud->flags |= UNIT_DIRECTORY_VERSION;
break;
case CSR1212_KV_ID_DESCRIPTOR:
if (kv->key.type == CSR1212_KV_TYPE_LEAF &&
CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 &&
CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
switch (last_key_id) {
case CSR1212_KV_ID_VENDOR:
ud->vendor_name_kv = kv;
csr1212_keep_keyval(kv);
break;
case CSR1212_KV_ID_MODEL:
ud->model_name_kv = kv;
csr1212_keep_keyval(kv);
break;
}
} /* else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) ... */
break;
case CSR1212_KV_ID_DEPENDENT_INFO:
/* Logical Unit Number */
if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) {
if (ud->flags & UNIT_DIRECTORY_HAS_LUN) {
ud_child = kmemdup(ud, sizeof(*ud_child), GFP_KERNEL);
if (!ud_child)
goto unit_directory_error;
nodemgr_register_device(ne, ud_child, &ne->device);
ud_child = NULL;
ud->id = (*id)++;
}
ud->lun = kv->value.immediate;
ud->flags |= UNIT_DIRECTORY_HAS_LUN;
/* Logical Unit Directory */
} else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) {
/* This should really be done in SBP2 as this is
* doing SBP2 specific parsing.
*/
/* first register the parent unit */
ud->flags |= UNIT_DIRECTORY_HAS_LUN_DIRECTORY;
if (ud->device.bus != &ieee1394_bus_type)
nodemgr_register_device(ne, ud, &ne->device);
/* process the child unit */
ud_child = nodemgr_process_unit_directory(hi, ne, kv, id, ud);
if (ud_child == NULL)
break;
/* inherit unspecified values, the driver core picks it up */
if ((ud->flags & UNIT_DIRECTORY_MODEL_ID) &&
!(ud_child->flags & UNIT_DIRECTORY_MODEL_ID))
{
ud_child->flags |= UNIT_DIRECTORY_MODEL_ID;
ud_child->model_id = ud->model_id;
}
if ((ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) &&
!(ud_child->flags & UNIT_DIRECTORY_SPECIFIER_ID))
{
ud_child->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
ud_child->specifier_id = ud->specifier_id;
}
if ((ud->flags & UNIT_DIRECTORY_VERSION) &&
!(ud_child->flags & UNIT_DIRECTORY_VERSION))
{
ud_child->flags |= UNIT_DIRECTORY_VERSION;
ud_child->version = ud->version;
}
/* register the child unit */
ud_child->flags |= UNIT_DIRECTORY_LUN_DIRECTORY;
nodemgr_register_device(ne, ud_child, &ud->device);
}
break;
case CSR1212_KV_ID_DIRECTORY_ID:
ud->directory_id = kv->value.immediate;
break;
default:
break;
}
last_key_id = kv->key.id;
}
/* do not process child units here and only if not already registered */
if (!parent && ud->device.bus != &ieee1394_bus_type)
nodemgr_register_device(ne, ud, &ne->device);
return ud;
unit_directory_error:
kfree(ud);
return NULL;
}
static void nodemgr_process_root_directory(struct host_info *hi, struct node_entry *ne)
{
unsigned int ud_id = 0;
struct csr1212_dentry *dentry;
struct csr1212_keyval *kv;
u8 last_key_id = 0;
ne->needs_probe = 0;
csr1212_for_each_dir_entry(ne->csr, kv, ne->csr->root_kv, dentry) {
switch (kv->key.id) {
case CSR1212_KV_ID_VENDOR:
ne->vendor_id = kv->value.immediate;
break;
case CSR1212_KV_ID_NODE_CAPABILITIES:
ne->capabilities = kv->value.immediate;
break;
case CSR1212_KV_ID_UNIT:
nodemgr_process_unit_directory(hi, ne, kv, &ud_id, NULL);
break;
case CSR1212_KV_ID_DESCRIPTOR:
if (last_key_id == CSR1212_KV_ID_VENDOR) {
if (kv->key.type == CSR1212_KV_TYPE_LEAF &&
CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 &&
CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
ne->vendor_name_kv = kv;
csr1212_keep_keyval(kv);
}
}
break;
}
last_key_id = kv->key.id;
}
if (ne->vendor_name_kv) {
int error = device_create_file(&ne->device,
&dev_attr_ne_vendor_name_kv);
if (error && error != -EEXIST)
HPSB_ERR("Failed to add sysfs attribute");
}
}
#ifdef CONFIG_HOTPLUG
static int nodemgr_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
struct unit_directory *ud;
int i = 0;
int length = 0;
int retval = 0;
/* ieee1394:venNmoNspNverN */
char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1];
if (!dev)
return -ENODEV;
ud = container_of(dev, struct unit_directory, unit_dev);
if (ud->ne->in_limbo || ud->ignore_driver)
return -ENODEV;
#define PUT_ENVP(fmt,val) \
do { \
retval = add_uevent_var(envp, num_envp, &i, \
buffer, buffer_size, &length, \
fmt, val); \
if (retval) \
return retval; \
} while (0)
PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id);
PUT_ENVP("MODEL_ID=%06x", ud->model_id);
PUT_ENVP("GUID=%016Lx", (unsigned long long)ud->ne->guid);
PUT_ENVP("SPECIFIER_ID=%06x", ud->specifier_id);
PUT_ENVP("VERSION=%06x", ud->version);
snprintf(buf, sizeof(buf), "ieee1394:ven%08Xmo%08Xsp%08Xver%08X",
ud->vendor_id,
ud->model_id,
ud->specifier_id,
ud->version);
PUT_ENVP("MODALIAS=%s", buf);
#undef PUT_ENVP
envp[i] = NULL;
return 0;
}
#else
static int nodemgr_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
return -ENODEV;
}
#endif /* CONFIG_HOTPLUG */
int __hpsb_register_protocol(struct hpsb_protocol_driver *drv,
struct module *owner)
{
int error;
drv->driver.bus = &ieee1394_bus_type;
drv->driver.owner = owner;
drv->driver.name = drv->name;
/* This will cause a probe for devices */
error = driver_register(&drv->driver);
if (!error)
nodemgr_create_drv_files(drv);
return error;
}
void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver)
{
nodemgr_remove_drv_files(driver);
/* This will subsequently disconnect all devices that our driver
* is attached to. */
driver_unregister(&driver->driver);
}
/*
* This function updates nodes that were present on the bus before the
* reset and still are after the reset. The nodeid and the config rom
* may have changed, and the drivers managing this device must be
* informed that this device just went through a bus reset, to allow
* the to take whatever actions required.
*/
static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr,
struct host_info *hi, nodeid_t nodeid,
unsigned int generation)
{
if (ne->nodeid != nodeid) {
HPSB_DEBUG("Node changed: " NODE_BUS_FMT " -> " NODE_BUS_FMT,
NODE_BUS_ARGS(ne->host, ne->nodeid),
NODE_BUS_ARGS(ne->host, nodeid));
ne->nodeid = nodeid;
}
if (ne->busopt.generation != ((be32_to_cpu(csr->bus_info_data[2]) >> 4) & 0xf)) {
kfree(ne->csr->private);
csr1212_destroy_csr(ne->csr);
ne->csr = csr;
/* If the node's configrom generation has changed, we
* unregister all the unit directories. */
nodemgr_remove_uds(ne);
nodemgr_update_bus_options(ne);
/* Mark the node as new, so it gets re-probed */
ne->needs_probe = 1;
} else {
/* old cache is valid, so update its generation */
struct nodemgr_csr_info *ci = ne->csr->private;
ci->generation = generation;
/* free the partially filled now unneeded new cache */
kfree(csr->private);
csr1212_destroy_csr(csr);
}
if (ne->in_limbo)
nodemgr_resume_ne(ne);
/* Mark the node current */
ne->generation = generation;
}
static void nodemgr_node_scan_one(struct host_info *hi,
nodeid_t nodeid, int generation)
{
struct hpsb_host *host = hi->host;
struct node_entry *ne;
octlet_t guid;
struct csr1212_csr *csr;
struct nodemgr_csr_info *ci;
u8 *speed;
ci = kmalloc(sizeof(*ci), GFP_KERNEL);
if (!ci)
return;
ci->host = host;
ci->nodeid = nodeid;
ci->generation = generation;
/* Prepare for speed probe which occurs when reading the ROM */
speed = &(host->speed[NODEID_TO_NODE(nodeid)]);
if (*speed > host->csr.lnk_spd)
*speed = host->csr.lnk_spd;
ci->speed_unverified = *speed > 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. */
csr = csr1212_create_csr(&nodemgr_csr_ops, 5 * sizeof(quadlet_t), ci);
if (!csr || csr1212_parse_csr(csr) != CSR1212_SUCCESS) {
HPSB_ERR("Error parsing configrom for node " NODE_BUS_FMT,
NODE_BUS_ARGS(host, nodeid));
if (csr)
csr1212_destroy_csr(csr);
kfree(ci);
return;
}
if (csr->bus_info_data[1] != IEEE1394_BUSID_MAGIC) {
/* 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(host, nodeid), csr->bus_info_data[1]);
}
guid = ((u64)be32_to_cpu(csr->bus_info_data[3]) << 32) | be32_to_cpu(csr->bus_info_data[4]);
ne = find_entry_by_guid(guid);
if (ne && ne->host != host && ne->in_limbo) {
/* Must have moved this device from one host to another */
nodemgr_remove_ne(ne);
ne = NULL;
}
if (!ne)
nodemgr_create_node(guid, csr, hi, nodeid, generation);
else
nodemgr_update_node(ne, csr, hi, nodeid, generation);
}
static void nodemgr_node_scan(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;
/* Scan each node on the bus */
for (count = host->selfid_count; count; count--, sid++) {
if (sid->extended)
continue;
if (!sid->link_active) {
nodeid++;
continue;
}
nodemgr_node_scan_one(hi, nodeid++, generation);
}
}
static void nodemgr_suspend_ne(struct node_entry *ne)
{
struct device *dev;
struct unit_directory *ud;
struct device_driver *drv;
int error;
HPSB_DEBUG("Node suspended: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
ne->in_limbo = 1;
WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo));
down(&nodemgr_ud_class.sem);
list_for_each_entry(dev, &nodemgr_ud_class.devices, node) {
ud = container_of(dev, struct unit_directory, unit_dev);
if (ud->ne != ne)
continue;
drv = get_driver(ud->device.driver);
if (!drv)
continue;
error = 1; /* release if suspend is not implemented */
if (drv->suspend) {
down(&ud->device.sem);
error = drv->suspend(&ud->device, PMSG_SUSPEND);
up(&ud->device.sem);
}
if (error)
device_release_driver(&ud->device);
put_driver(drv);
}
up(&nodemgr_ud_class.sem);
}
static void nodemgr_resume_ne(struct node_entry *ne)
{
struct device *dev;
struct unit_directory *ud;
struct device_driver *drv;
ne->in_limbo = 0;
device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
down(&nodemgr_ud_class.sem);
list_for_each_entry(dev, &nodemgr_ud_class.devices, node) {
ud = container_of(dev, struct unit_directory, unit_dev);
if (ud->ne != ne)
continue;
drv = get_driver(ud->device.driver);
if (!drv)
continue;
if (drv->resume) {
down(&ud->device.sem);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -