📄 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/kernel.h>#include <linux/config.h>#include <linux/list.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/interrupt.h>#include <linux/kmod.h>#include <linux/completion.h>#include <linux/delay.h>#ifdef CONFIG_PROC_FS#include <linux/proc_fs.h>#endif#include "ieee1394_types.h"#include "ieee1394.h"#include "nodemgr.h"#include "hosts.h"#include "ieee1394_transactions.h"#include "highlevel.h"#include "csr.h"#include "nodemgr.h"static char *nodemgr_find_oui_name(int oui){#ifdef CONFIG_IEEE1394_OUI_DB extern struct oui_list_struct { int oui; char *name; } oui_list[]; int i; for (i = 0; oui_list[i].name; i++) if (oui_list[i].oui == oui) return oui_list[i].name;#endif return NULL;}/* * 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 maintains a number of data structures: the node list,* the driver list, unit directory list and the host info list. The* first three lists are accessed from process context only: /proc* readers, insmod and rmmod, and the nodemgr thread. Access to these* lists are serialized by means of the nodemgr_serialize mutex, which* must be taken before accessing the structures and released* afterwards. The host info list is only accessed during insmod,* rmmod and from interrupt and allways only for a short period of* time, so a spinlock is used to protect this list.*/static DECLARE_MUTEX(nodemgr_serialize);static LIST_HEAD(node_list);static LIST_HEAD(driver_list);static LIST_HEAD(unit_directory_list);struct host_info { struct hpsb_host *host; struct completion exited; struct semaphore reset_sem; int pid; char daemon_name[15];};static struct hpsb_highlevel nodemgr_highlevel;#ifdef CONFIG_PROC_FS#define PUTF(fmt, args...) \ do { \ len += sprintf(page + len, fmt, ## args); \ pos = begin + len; \ if (pos < off) { \ len = 0; \ begin = pos; \ } \ if (pos > off + count) \ goto done_proc; \ } while (0)static int raw1394_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ struct list_head *lh; struct node_entry *ne; off_t begin = 0, pos = 0; int len = 0; if (down_interruptible(&nodemgr_serialize)) return -EINTR; list_for_each(lh, &node_list) { struct list_head *l; int ud_count = 0, lud_count = 0; ne = list_entry(lh, struct node_entry, list); if (!ne) continue; PUTF("Node[" NODE_BUS_FMT "] GUID[%016Lx]:\n", NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); /* Generic Node information */ PUTF(" Vendor ID: `%s' [0x%06x]\n", ne->vendor_name ?: "Unknown", ne->vendor_id); PUTF(" Capabilities: 0x%06x\n", ne->capabilities); PUTF(" Bus Options:\n"); PUTF(" IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d)\n" " LSPD(%d) MAX_REC(%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.cyc_clk_acc); /* If this is the host entry, output some info about it aswell */ if (ne->host != NULL && ne->host->node_id == ne->nodeid) { PUTF(" Host Node Status:\n"); PUTF(" Host Driver : %s\n", ne->host->driver->name); PUTF(" Nodes connected : %d\n", ne->host->node_count); PUTF(" Nodes active : %d\n", ne->host->nodes_active); PUTF(" SelfIDs received: %d\n", ne->host->selfid_count); PUTF(" Irm ID : [" NODE_BUS_FMT "]\n", NODE_BUS_ARGS(ne->host, ne->host->irm_id)); PUTF(" BusMgr ID : [" NODE_BUS_FMT "]\n", NODE_BUS_ARGS(ne->host, ne->host->busmgr_id)); PUTF(" In Bus Reset : %s\n", ne->host->in_bus_reset ? "yes" : "no"); PUTF(" Root : %s\n", ne->host->is_root ? "yes" : "no"); PUTF(" Cycle Master : %s\n", ne->host->is_cycmst ? "yes" : "no"); PUTF(" IRM : %s\n", ne->host->is_irm ? "yes" : "no"); PUTF(" Bus Manager : %s\n", ne->host->is_busmgr ? "yes" : "no"); } /* Now the unit directories */ list_for_each (l, &ne->unit_directories) { struct unit_directory *ud = list_entry (l, struct unit_directory, node_list); int printed = 0; // small hack if (ud->parent == NULL) PUTF(" Unit Directory %d:\n", lud_count++); else PUTF(" Logical Unit Directory %d:\n", ud_count++); if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) { PUTF(" Vendor/Model ID: %s [%06x]", ud->vendor_name ?: "Unknown", ud->vendor_id); printed = 1; } if (ud->flags & UNIT_DIRECTORY_MODEL_ID) { if (!printed) PUTF(" Vendor/Model ID: %s [%06x]", ne->vendor_name ?: "Unknown", ne->vendor_id); PUTF(" / %s [%06x]", ud->model_name ?: "Unknown", ud->model_id); printed = 1; } if (printed) PUTF("\n"); if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) PUTF(" Software Specifier ID: %06x\n", ud->specifier_id); if (ud->flags & UNIT_DIRECTORY_VERSION) PUTF(" Software Version: %06x\n", ud->version); if (ud->driver) PUTF(" Driver: %s\n", ud->driver->name); PUTF(" Length (in quads): %d\n", ud->length); } } done_proc: up(&nodemgr_serialize); *start = page + (off - begin); len -= (off - begin); if (len > count) len = count; else { *eof = 1; if (len <= 0) return 0; } return len;} #undef PUTF#endif /* CONFIG_PROC_FS */static void nodemgr_process_config_rom(struct node_entry *ne, quadlet_t busoptions);static int nodemgr_read_quadlet(struct hpsb_host *host, nodeid_t nodeid, unsigned int generation, octlet_t address, quadlet_t *quad){ int i; int ret = 0; for (i = 0; i < 3; i++) { ret = hpsb_read(host, nodeid, generation, address, quad, 4); if (!ret) break; set_current_state(TASK_INTERRUPTIBLE); if (schedule_timeout (HZ/3)) return -1; } *quad = be32_to_cpu(*quad); return ret;}static int nodemgr_size_text_leaf(struct hpsb_host *host, nodeid_t nodeid, unsigned int generation, octlet_t address){ quadlet_t quad; int size = 0; if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad)) return -1; if (CONFIG_ROM_KEY(quad) == CONFIG_ROM_DESCRIPTOR_LEAF) { /* This is the offset. */ address += 4 * CONFIG_ROM_VALUE(quad); if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad)) return -1; /* Now we got the size of the text descriptor leaf. */ size = CONFIG_ROM_LEAF_LENGTH(quad); } return size;}static int nodemgr_read_text_leaf(struct node_entry *ne, octlet_t address, quadlet_t *quadp){ quadlet_t quad; int i, size, ret; if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad) || CONFIG_ROM_KEY(quad) != CONFIG_ROM_DESCRIPTOR_LEAF) return -1; /* This is the offset. */ address += 4 * CONFIG_ROM_VALUE(quad); if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad)) return -1; /* Now we got the size of the text descriptor leaf. */ size = CONFIG_ROM_LEAF_LENGTH(quad) - 2; if (size <= 0) return -1; address += 4; for (i = 0; i < 2; i++, address += 4, quadp++) { if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, quadp)) return -1; } /* Now read the text string. */ ret = -ENXIO; for (; size > 0; size--, address += 4, quadp++) { for (i = 0; i < 3; i++) { ret = hpsb_node_read(ne, address, quadp, 4); if (ret != -EAGAIN) break; } if (ret) break; } return ret;}static struct node_entry *nodemgr_scan_root_directory(struct hpsb_host *host, nodeid_t nodeid, unsigned int generation){ octlet_t address; quadlet_t quad; int length; int code, size, total_size; struct node_entry *ne; address = CSR_REGISTER_BASE + CSR_CONFIG_ROM; if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad)) return NULL; if (CONFIG_ROM_BUS_INFO_LENGTH(quad) == 1) /* minimal config rom */ return NULL; address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4; if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad)) return NULL; length = CONFIG_ROM_ROOT_LENGTH(quad); address += 4; size = 0; total_size = sizeof(struct node_entry); for (; length > 0; length--, address += 4) { if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad)) return NULL; code = CONFIG_ROM_KEY(quad); if (code == CONFIG_ROM_VENDOR_ID && length > 0) { /* Check if there is a text descriptor leaf immediately after this. */ size = nodemgr_size_text_leaf(host, nodeid, generation, address + 4); if (size > 0) { address += 4; length--; total_size += (size + 1) * sizeof (quadlet_t); } else if (size < 0) return NULL; } } ne = kmalloc(total_size, GFP_KERNEL); if (!ne) return NULL; memset(ne, 0, total_size); if (size != 0) { ne->vendor_name = (const char *) &(ne->quadlets[2]); ne->quadlets[size] = 0; } else { ne->vendor_name = NULL; } return ne; }static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions, struct host_info *hi, nodeid_t nodeid, unsigned int generation){ struct hpsb_host *host = hi->host; struct node_entry *ne; ne = nodemgr_scan_root_directory (host, nodeid, generation); if (!ne) return NULL; INIT_LIST_HEAD(&ne->list); INIT_LIST_HEAD(&ne->unit_directories); ne->host = host; ne->nodeid = nodeid; ne->generation = generation; ne->guid = guid; ne->guid_vendor_id = (guid >> 40) & 0xffffff; ne->guid_vendor_oui = nodemgr_find_oui_name(ne->guid_vendor_id); list_add_tail(&ne->list, &node_list); nodemgr_process_config_rom (ne, busoptions); HPSB_DEBUG("%s added: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", (host->node_id == nodeid) ? "Host" : "Node", NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid); return ne;}static struct node_entry *find_entry_by_guid(u64 guid){ struct list_head *lh; struct node_entry *ne; list_for_each(lh, &node_list) { ne = list_entry(lh, struct node_entry, list); if (ne->guid == guid) return ne; } return NULL;}static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t nodeid){ struct list_head *lh; struct node_entry *ne; list_for_each(lh, &node_list) { ne = list_entry(lh, struct node_entry, list); if (ne->nodeid == nodeid && ne->host == host) return ne; } return NULL;}static struct unit_directory *nodemgr_scan_unit_directory(struct node_entry *ne, octlet_t address){ struct unit_directory *ud; quadlet_t quad; u8 flags, todo; int length, size, total_size, count; int vendor_name_size, model_name_size; if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad)) return NULL; length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ; address += 4; size = 0; total_size = sizeof (struct unit_directory); flags = 0; count = 0; vendor_name_size = 0; model_name_size = 0; for (; length > 0; length--, address += 4) { int code; quadlet_t value; if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad)) return NULL; code = CONFIG_ROM_KEY(quad); value = CONFIG_ROM_VALUE(quad); todo = 0; switch (code) { case CONFIG_ROM_VENDOR_ID: todo = UNIT_DIRECTORY_VENDOR_TEXT; break; case CONFIG_ROM_MODEL_ID: todo = UNIT_DIRECTORY_MODEL_TEXT; break; case CONFIG_ROM_SPECIFIER_ID: case CONFIG_ROM_UNIT_SW_VERSION: break; case CONFIG_ROM_DESCRIPTOR_LEAF: case CONFIG_ROM_DESCRIPTOR_DIRECTORY: /* TODO: read strings... icons? */ break; default: /* Which types of quadlets do we want to store? Only count immediate values and CSR offsets for now. */ code &= CONFIG_ROM_KEY_TYPE_MASK; if ((code & CONFIG_ROM_KEY_TYPE_LEAF) == 0) count++; break; } if (todo && length > 0) { /* Check if there is a text descriptor leaf immediately after this. */ size = nodemgr_size_text_leaf(ne->host, ne->nodeid, ne->generation, address + 4); if (todo == UNIT_DIRECTORY_VENDOR_TEXT) vendor_name_size = size; else model_name_size = size; if (size > 0) { address += 4; length--; flags |= todo; total_size += (size + 1) * sizeof (quadlet_t); } else if (size < 0) return NULL; } } total_size += count * sizeof (quadlet_t); ud = kmalloc (total_size, GFP_KERNEL); if (ud != NULL) { memset (ud, 0, total_size); ud->flags = flags; ud->length = count; ud->vendor_name_size = vendor_name_size;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -