📄 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 * * This code is licensed under the GPL. See the file COPYING in the root * directory of the kernel sources for details. */#include <linux/config.h>#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 <asm/bitops.h>#include <asm/byteorder.h>#include <asm/semaphore.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 "ieee1394_hotplug.h"/* * Disable the nodemgr detection and config rom reading functionality. */MODULE_PARM(disable_nodemgr, "i");MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality.");static int disable_nodemgr = 0;/* We are GPL, so treat us special */MODULE_LICENSE("GPL");static kmem_cache_t *hpsb_packet_cache;/* Some globals used */const char *hpsb_speedto_str[] = { "S100", "S200", "S400" };static void dump_packet(const char *text, quadlet_t *data, int size){ int i; size /= 4; size = (size > 4 ? 4 : size); printk(KERN_DEBUG "ieee1394: %s", text); for (i = 0; i < size; i++) { printk(" %8.8x", data[i]); } printk("\n");}/** * alloc_hpsb_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 *alloc_hpsb_packet(size_t data_size){ struct hpsb_packet *packet = NULL; void *data = NULL; int kmflags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; packet = kmem_cache_alloc(hpsb_packet_cache, kmflags); if (packet == NULL) return NULL; memset(packet, 0, sizeof(struct hpsb_packet)); packet->header = packet->embedded_header; if (data_size) { data = kmalloc(data_size + 8, kmflags); if (data == NULL) { kmem_cache_free(hpsb_packet_cache, packet); return NULL; } packet->data = data; packet->data_size = data_size; } INIT_TQ_HEAD(packet->complete_tq); INIT_LIST_HEAD(&packet->list); sema_init(&packet->state_change, 0); packet->state = hpsb_unused; packet->generation = -1; packet->data_be = 1; return packet;}/** * free_hpsb_packet - free packet and data associated with it * @packet: packet to free (is NULL safe) * * This function will free packet->data, packet->header and finally the packet * itself. */void free_hpsb_packet(struct hpsb_packet *packet){ if (!packet) return; kfree(packet->data); kmem_cache_free(hpsb_packet_cache, packet);}int hpsb_reset_bus(struct hpsb_host *host, int type){ if (!host->initialized) { return 1; } if (!host->in_bus_reset) { host->template->devctl(host, RESET_BUS, type); return 0; } else { return 1; }}int hpsb_bus_reset(struct hpsb_host *host){ if (host->in_bus_reset) { HPSB_NOTICE(__FUNCTION__ " called while bus reset already in progress"); return 1; } abort_requests(host); host->in_bus_reset = 1; host->irm_id = -1; host->busmgr_id = -1; 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, unsigned int num_of_selfids){ int nodeid = -1; int rest_of_selfids = num_of_selfids; struct selfid *sid = (struct selfid *)host->topology_map; struct ext_selfid *esid; int esid_seq = 23; 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->contender && sid->link_active) { 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 == 0x2) || (esid->portb == 0x2) || (esid->portc == 0x2) || (esid->portd == 0x2) || (esid->porte == 0x2) || (esid->portf == 0x2) || (esid->portg == 0x2) || (esid->porth == 0x2)) { HPSB_INFO("SelfIDs failed root check on " "extended SelfID"); return 0; } esid--; } sid = (struct selfid *)esid; if ((sid->port0 == 0x2) || (sid->port1 == 0x2) || (sid->port2 == 0x2)) { HPSB_INFO("SelfIDs failed root check"); return 0; } return nodeid + 1;}static void build_speed_map(struct hpsb_host *host, int nodecount){ char speedcap[nodecount]; char cldcnt[nodecount]; u8 *map = host->speed_map; 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] = SPEED_400; } } 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 == 0x3) cldcnt[n]++; if (esid->portb == 0x3) cldcnt[n]++; if (esid->portc == 0x3) cldcnt[n]++; if (esid->portd == 0x3) cldcnt[n]++; if (esid->porte == 0x3) cldcnt[n]++; if (esid->portf == 0x3) cldcnt[n]++; if (esid->portg == 0x3) cldcnt[n]++; if (esid->porth == 0x3) cldcnt[n]++; } else { if (sid->port0 == 0x3) cldcnt[n]++; if (sid->port1 == 0x3) cldcnt[n]++; if (sid->port2 == 0x3) 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]); } } }}void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid){ if (host->in_bus_reset) {#ifdef CONFIG_IEEE1394_VERBOSEDEBUG HPSB_INFO("Including SelfID 0x%x", sid);#endif host->topology_map[host->selfid_count++] = sid; } else { HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from bus %d", sid, (host->node_id & BUS_MASK) >> 6); }}void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot){ host->node_id = LOCAL_BUS | phyid; host->in_bus_reset = 0; host->is_root = isroot; host->node_count = check_selfids(host, host->selfid_count); if (!host->node_count) { if (host->reset_retries++ < 20) { /* selfid stage did not complete without error */ HPSB_NOTICE("Error in SelfID stage, resetting"); 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"); } } else { build_speed_map(host, host->node_count); } /* irm_id is kept up to date by check_selfids() */ if (host->irm_id == host->node_id) { host->is_irm = 1; host->is_busmgr = 1; host->busmgr_id = host->node_id; host->csr.bus_manager_id = host->node_id; } host->reset_retries = 0; atomic_inc(&host->generation); if (isroot) host->template->devctl(host, ACT_CYCLE_MASTER, 1); highlevel_host_reset(host);}void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, int ackcode){ unsigned long flags; packet->ack_code = ackcode; if (packet->no_waiter) { /* must not have a tlabel allocated */ free_hpsb_packet(packet); return; } if (ackcode != ACK_PENDING || !packet->expect_response) { packet->state = hpsb_complete; up(&packet->state_change); up(&packet->state_change); run_task_queue(&packet->complete_tq); return; } packet->state = hpsb_pending; packet->sendtime = jiffies; spin_lock_irqsave(&host->pending_pkt_lock, flags); list_add_tail(&packet->list, &host->pending_packets); spin_unlock_irqrestore(&host->pending_pkt_lock, flags); up(&packet->state_change); queue_task(&host->timeout_tq, &tq_timer);}/** * hpsb_send_packet - transmit a packet on the bus * @packet: packet to send * * The packet is sent through the host specified in the packet->host field. * Before sending, the packet's transmit speed is automatically determined using * the local speed map when it is an async, non-broadcast packet. * * Possibilities for failure are that host is either not initialized, in bus * reset, the packet's generation number doesn't match the current generation * number or the host reports a transmit error. * * Return value: False (0) on failure, true (1) otherwise. */int hpsb_send_packet(struct hpsb_packet *packet){ struct hpsb_host *host = packet->host; if (!host->initialized || host->in_bus_reset || (packet->generation != get_hpsb_generation(host))) { return 0; } packet->state = hpsb_queued; if (packet->type == hpsb_async && packet->node_id != ALL_NODES) { packet->speed_code = host->speed_map[(host->node_id & NODE_MASK) * 64 + (packet->node_id & NODE_MASK)]; }#ifdef CONFIG_IEEE1394_VERBOSEDEBUG switch (packet->speed_code) { case 2: dump_packet("send packet 400:", packet->header, packet->header_size); break; case 1: dump_packet("send packet 200:", packet->header, packet->header_size); break; default: dump_packet("send packet 100:", packet->header, packet->header_size); }#endif return host->template->transmit_packet(host, packet);}static void send_packet_nocare(struct hpsb_packet *packet){ if (!hpsb_send_packet(packet)) { free_hpsb_packet(packet);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -