📄 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/freezer.h>#include <asm/atomic.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 (CSR1212_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. */static DEFINE_MUTEX(nodemgr_serialize);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 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);struct bus_type ieee1394_bus_type = { .name = "ieee1394", .match = nodemgr_bus_match,};static void host_cls_release(struct class_device *class_dev){ put_device(&container_of((class_dev), struct hpsb_host, class_dev)->device);}struct class hpsb_host_class = { .name = "ieee1394_host", .release = host_cls_release,};static void ne_cls_release(struct class_device *class_dev){ put_device(&container_of((class_dev), struct node_entry, class_dev)->device);}static struct class nodemgr_ne_class = { .name = "ieee1394_node", .release = ne_cls_release,};static void ud_cls_release(struct class_device *class_dev){ put_device(&container_of((class_dev), struct unit_directory, class_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", .release = ud_cls_release, .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_TLABELSstatic 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; down_write(&ieee1394_bus_type.subsys.rwsem); device_release_driver(dev); up_write(&ieee1394_bus_type.subsys.rwsem); } 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -