📄 ieee1394_core.c
字号:
/* * IEEE 1394 for Linux * * Core support: hpsb_packet management, packet handling and forwarding to * highlevel or lowlevel code * * Copyright (C) 1999, 2000 Andreas E. Bombe * 2002 Manfred Weihs <weihs@ict.tuwien.ac.at> * * This code is licensed under the GPL. See the file COPYING in the root * directory of the kernel sources for details. * * * Contributions: * * Manfred Weihs <weihs@ict.tuwien.ac.at> * loopback functionality in hpsb_send_packet * allow highlevel drivers to disable automatic response generation * and to generate responses themselves (deferred) * */#include <linux/kernel.h>#include <linux/list.h>#include <linux/string.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/bitops.h>#include <linux/kdev_t.h>#include <linux/skbuff.h>#include <linux/suspend.h>#include <linux/kthread.h>#include <linux/preempt.h>#include <linux/time.h>#include <asm/system.h>#include <asm/byteorder.h>#include "ieee1394_types.h"#include "ieee1394.h"#include "hosts.h"#include "ieee1394_core.h"#include "highlevel.h"#include "ieee1394_transactions.h"#include "csr.h"#include "nodemgr.h"#include "dma.h"#include "iso.h"#include "config_roms.h"/* * Disable the nodemgr detection and config rom reading functionality. */static int disable_nodemgr;module_param(disable_nodemgr, int, 0444);MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality.");/* Disable Isochronous Resource Manager functionality */int hpsb_disable_irm = 0;module_param_named(disable_irm, hpsb_disable_irm, bool, 0444);MODULE_PARM_DESC(disable_irm, "Disable Isochronous Resource Manager functionality.");/* We are GPL, so treat us special */MODULE_LICENSE("GPL");/* Some globals used */const char *hpsb_speedto_str[] = { "S100", "S200", "S400", "S800", "S1600", "S3200" };struct class *hpsb_protocol_class;#ifdef CONFIG_IEEE1394_VERBOSEDEBUGstatic void dump_packet(const char *text, quadlet_t *data, int size, int speed){ int i; size /= 4; size = (size > 4 ? 4 : size); printk(KERN_DEBUG "ieee1394: %s", text); if (speed > -1 && speed < 6) printk(" at %s", hpsb_speedto_str[speed]); printk(":"); for (i = 0; i < size; i++) printk(" %08x", data[i]); printk("\n");}#else#define dump_packet(a,b,c,d) do {} while (0)#endifstatic void abort_requests(struct hpsb_host *host);static void queue_packet_complete(struct hpsb_packet *packet);/** * hpsb_set_packet_complete_task - set the task that runs when a packet * completes. You cannot call this more than once on a single packet * before it is sent. * * @packet: the packet whose completion we want the task added to * @routine: function to call * @data: data (if any) to pass to the above function */void hpsb_set_packet_complete_task(struct hpsb_packet *packet, void (*routine)(void *), void *data){ WARN_ON(packet->complete_routine != NULL); packet->complete_routine = routine; packet->complete_data = data; return;}/** * hpsb_alloc_packet - allocate new packet structure * @data_size: size of the data block to be allocated * * This function allocates, initializes and returns a new &struct hpsb_packet. * It can be used in interrupt context. A header block is always included, its * size is big enough to contain all possible 1394 headers. The data block is * only allocated when @data_size is not zero. * * For packets for which responses will be received the @data_size has to be big * enough to contain the response's data block since no further allocation * occurs at response matching time. * * The packet's generation value will be set to the current generation number * for ease of use. Remember to overwrite it with your own recorded generation * number if you can not be sure that your code will not race with a bus reset. * * Return value: A pointer to a &struct hpsb_packet or NULL on allocation * failure. */struct hpsb_packet *hpsb_alloc_packet(size_t data_size){ struct hpsb_packet *packet = NULL; struct sk_buff *skb; data_size = ((data_size + 3) & ~3); skb = alloc_skb(data_size + sizeof(*packet), GFP_ATOMIC); if (skb == NULL) return NULL; memset(skb->data, 0, data_size + sizeof(*packet)); packet = (struct hpsb_packet *)skb->data; packet->skb = skb; packet->header = packet->embedded_header; packet->state = hpsb_unused; packet->generation = -1; INIT_LIST_HEAD(&packet->driver_list); atomic_set(&packet->refcnt, 1); if (data_size) { packet->data = (quadlet_t *)(skb->data + sizeof(*packet)); packet->data_size = data_size; } return packet;}/** * hpsb_free_packet - free packet and data associated with it * @packet: packet to free (is NULL safe) * * This function will free packet->data and finally the packet itself. */void hpsb_free_packet(struct hpsb_packet *packet){ if (packet && atomic_dec_and_test(&packet->refcnt)) { BUG_ON(!list_empty(&packet->driver_list)); kfree_skb(packet->skb); }}int hpsb_reset_bus(struct hpsb_host *host, int type){ if (!host->in_bus_reset) { host->driver->devctl(host, RESET_BUS, type); return 0; } else { return 1; }}/** * hpsb_read_cycle_timer - read cycle timer register and system time * @host: host whose isochronous cycle timer register is read * @cycle_timer: address of bitfield to return the register contents * @local_time: address to return the system time * * The format of * @cycle_timer, is described in OHCI 1.1 clause 5.13. This * format is also read from non-OHCI controllers. * @local_time contains the * system time in microseconds since the Epoch, read at the moment when the * cycle timer was read. * * Return value: 0 for success or error number otherwise. */int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, u64 *local_time){ int ctr; struct timeval tv; unsigned long flags; if (!host || !cycle_timer || !local_time) return -EINVAL; preempt_disable(); local_irq_save(flags); ctr = host->driver->devctl(host, GET_CYCLE_COUNTER, 0); if (ctr) do_gettimeofday(&tv); local_irq_restore(flags); preempt_enable(); if (!ctr) return -EIO; *cycle_timer = ctr; *local_time = tv.tv_sec * 1000000ULL + tv.tv_usec; return 0;}int hpsb_bus_reset(struct hpsb_host *host){ if (host->in_bus_reset) { HPSB_NOTICE("%s called while bus reset already in progress", __FUNCTION__); return 1; } abort_requests(host); host->in_bus_reset = 1; host->irm_id = -1; host->is_irm = 0; host->busmgr_id = -1; host->is_busmgr = 0; host->is_cycmst = 0; host->node_count = 0; host->selfid_count = 0; return 0;}/* * Verify num_of_selfids SelfIDs and return number of nodes. Return zero in * case verification failed. */static int check_selfids(struct hpsb_host *host){ int nodeid = -1; int rest_of_selfids = host->selfid_count; struct selfid *sid = (struct selfid *)host->topology_map; struct ext_selfid *esid; int esid_seq = 23; host->nodes_active = 0; while (rest_of_selfids--) { if (!sid->extended) { nodeid++; esid_seq = 0; if (sid->phy_id != nodeid) { HPSB_INFO("SelfIDs failed monotony check with " "%d", sid->phy_id); return 0; } if (sid->link_active) { host->nodes_active++; if (sid->contender) host->irm_id = LOCAL_BUS | sid->phy_id; } } else { esid = (struct ext_selfid *)sid; if ((esid->phy_id != nodeid) || (esid->seq_nr != esid_seq)) { HPSB_INFO("SelfIDs failed monotony check with " "%d/%d", esid->phy_id, esid->seq_nr); return 0; } esid_seq++; } sid++; } esid = (struct ext_selfid *)(sid - 1); while (esid->extended) { if ((esid->porta == SELFID_PORT_PARENT) || (esid->portb == SELFID_PORT_PARENT) || (esid->portc == SELFID_PORT_PARENT) || (esid->portd == SELFID_PORT_PARENT) || (esid->porte == SELFID_PORT_PARENT) || (esid->portf == SELFID_PORT_PARENT) || (esid->portg == SELFID_PORT_PARENT) || (esid->porth == SELFID_PORT_PARENT)) { HPSB_INFO("SelfIDs failed root check on " "extended SelfID"); return 0; } esid--; } sid = (struct selfid *)esid; if ((sid->port0 == SELFID_PORT_PARENT) || (sid->port1 == SELFID_PORT_PARENT) || (sid->port2 == SELFID_PORT_PARENT)) { HPSB_INFO("SelfIDs failed root check"); return 0; } host->node_count = nodeid + 1; return 1;}static void build_speed_map(struct hpsb_host *host, int nodecount){ u8 cldcnt[nodecount]; u8 *map = host->speed_map; u8 *speedcap = host->speed; struct selfid *sid; struct ext_selfid *esid; int i, j, n; for (i = 0; i < (nodecount * 64); i += 64) { for (j = 0; j < nodecount; j++) { map[i+j] = IEEE1394_SPEED_MAX; } } for (i = 0; i < nodecount; i++) { cldcnt[i] = 0; } /* find direct children count and speed */ for (sid = (struct selfid *)&host->topology_map[host->selfid_count-1], n = nodecount - 1; (void *)sid >= (void *)host->topology_map; sid--) { if (sid->extended) { esid = (struct ext_selfid *)sid; if (esid->porta == SELFID_PORT_CHILD) cldcnt[n]++; if (esid->portb == SELFID_PORT_CHILD) cldcnt[n]++; if (esid->portc == SELFID_PORT_CHILD) cldcnt[n]++; if (esid->portd == SELFID_PORT_CHILD) cldcnt[n]++; if (esid->porte == SELFID_PORT_CHILD) cldcnt[n]++; if (esid->portf == SELFID_PORT_CHILD) cldcnt[n]++; if (esid->portg == SELFID_PORT_CHILD) cldcnt[n]++; if (esid->porth == SELFID_PORT_CHILD) cldcnt[n]++; } else { if (sid->port0 == SELFID_PORT_CHILD) cldcnt[n]++; if (sid->port1 == SELFID_PORT_CHILD) cldcnt[n]++; if (sid->port2 == SELFID_PORT_CHILD) cldcnt[n]++; speedcap[n] = sid->speed; n--; } } /* set self mapping */ for (i = 0; i < nodecount; i++) { map[64*i + i] = speedcap[i]; } /* fix up direct children count to total children count; * also fix up speedcaps for sibling and parent communication */ for (i = 1; i < nodecount; i++) { for (j = cldcnt[i], n = i - 1; j > 0; j--) { cldcnt[i] += cldcnt[n]; speedcap[n] = min(speedcap[n], speedcap[i]); n -= cldcnt[n] + 1; } } for (n = 0; n < nodecount; n++) { for (i = n - cldcnt[n]; i <= n; i++) { for (j = 0; j < (n - cldcnt[n]); j++) { map[j*64 + i] = map[i*64 + j] = min(map[i*64 + j], speedcap[n]); } for (j = n + 1; j < nodecount; j++) { map[j*64 + i] = map[i*64 + j] = min(map[i*64 + j], speedcap[n]); } } }#if SELFID_SPEED_UNKNOWN != IEEE1394_SPEED_MAX /* assume maximum speed for 1394b PHYs, nodemgr will correct it */ for (n = 0; n < nodecount; n++) if (speedcap[n] == SELFID_SPEED_UNKNOWN) speedcap[n] = IEEE1394_SPEED_MAX;#endif}void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid){ if (host->in_bus_reset) { HPSB_VERBOSE("Including SelfID 0x%x", sid); host->topology_map[host->selfid_count++] = sid; } else { HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from bus %d", sid, NODEID_TO_BUS(host->node_id)); }}void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot){ if (!host->in_bus_reset) HPSB_NOTICE("SelfID completion called outside of bus reset!"); host->node_id = LOCAL_BUS | phyid; host->is_root = isroot; if (!check_selfids(host)) { if (host->reset_retries++ < 20) { /* selfid stage did not complete without error */ HPSB_NOTICE("Error in SelfID stage, resetting"); host->in_bus_reset = 0; /* this should work from ohci1394 now... */ hpsb_reset_bus(host, LONG_RESET); return; } else { HPSB_NOTICE("Stopping out-of-control reset loop"); HPSB_NOTICE("Warning - topology map and speed map will not be valid"); host->reset_retries = 0; } } else { host->reset_retries = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -