📄 ib-pcmcia.c
字号:
/* iBurst (TM) compatible driver for 2.6 Linux kernel. * based on the original ArrayComm (TM) iBurst (TM) driver. * Nicholas Jefferson <nicholas@pythontraining.com.au> * 11 May 2005 * * Ported to 2.6 Linux kernel by Nik Trevallyn-Jones. * Patches for 2.6.13 Linux kernel by Greg Cockburn and Scott McKenzie. * Patch for 2.6.16 Linux kernel by Daniel Burr. * Patch for 2.6.17 Linux kernel by Damian Ivereigh * Patch for sysfs by Scott McKenzie. * Fixes to support new hardware by Shane MacPhillamy. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */#include "ib-net.h"#include <asm/io.h>#include <linux/version.h>#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/cisreg.h>#include <pcmcia/cistpl.h>#include <pcmcia/ds.h>static int debug = 0;module_param(debug, int, 0);#define DEBUG(n, args...) if (debug < (n)) ; else printk(KERN_INFO args)/** * interval - poll interval (in milliseconds). */static int interval = 4;module_param(interval, int, 0);/** * io16 - PCMCIA 16 bit mode. */static int io16 = 0;module_param(io16, int, 0);/** * enum ib_pcmcia_ifstate - hardware interface state. */enum ib_pcmcia_ifstate{ IFSTATE_RESET = 0, IFSTATE_WAIT = 1, IFSTATE_NORMAL = 2,};/** * ib_pcmcia_offset - i/o mapped memory offsets. */enum ib_pcmcia_offset{ OFFSET_UT_MAGIC1 = 0x00, OFFSET_UT_MAGIC2 = 0x01, OFFSET_UT_SEQUENCE = 0x02, OFFSET_UT_FEEDBACK = 0x03, OFFSET_UT_JRX = 0x04, OFFSET_UT_ITX = 0x05, OFFSET_UT_PACKET = 0x06, OFFSET_UT_STATUS = 0x07, OFFSET_PC_MAGIC1 = 0x08, OFFSET_PC_MAGIC2 = 0x09, OFFSET_PC_SEQUENCE = 0x0a, OFFSET_PC_FEEDBACK = 0x0b, OFFSET_PC_JTX = 0x0c, OFFSET_PC_IRX = 0x0d, OFFSET_PC_PACKET = 0x0e, OFFSET_PC_STATUS = 0x0f, OFFSET_UT_ICHUNK = 0x10, OFFSET_UT_NCHUNK = 0x11, OFFSET_PC_JCHUNK = 0x12, OFFSET_PC_NCHUNK = 0x13, OFFSET_UT_ETHERNET = 0x1a,};/** * ib_pcmcia_local_t - PCMCIA device private state. * @dev: PCMCIA device state. * @node: network node. * @mem: i/o mapped memory. * @modem: corresponding modem state. * @timer: poll hardware. * @release: release configuration timer. * @ifstate: hardware state. * @sequence: hardware sync protocol. * @feedback: hardware sync protocol. * @rx_echunk: expected chunks (32 bytes) to receive. * @rx_ibuf: next index into rx_buf to use. * @rx_nbuf: packet length. * @tx_echunk: expected chunks (32 bytes) to transmit. * @tx_jbuf: next index into tx_buf to use. * @irx: most recent index into i/o mapped receive buffer. * @jtx: most recent index into i/o mapped transmit buffer. * @nreset: number of hardware resets. */struct ib_pcmcia_local_t { struct pcmcia_device *dev; struct dev_node_t node; unsigned char *mem; struct ib_net_modem_t *modem; struct timer_list timer; struct timer_list release; enum ib_pcmcia_ifstate ifstate; unsigned int sequence, feedback; int rx_echunk, rx_ibuf, rx_nbuf; int tx_echunk, tx_jbuf; int irx, jtx; int nreset;};/** * ib_pcmcia_dev_info - identifier for this driver. */static dev_info_t ib_pcmcia_dev_info = "iburst_cs";/** * ib_pcmcia_blit - the selected blit function. */static void (*ib_pcmcia_blit)(unsigned char*, unsigned char*, int);/** * ib_pcmcia_timer - poll hardware. * @_local: device private state. */static void ib_pcmcia_timer(unsigned long _local){ struct ib_pcmcia_local_t *local = (struct ib_pcmcia_local_t*) _local; ib_net_schedule(local->modem); local->timer.expires = jiffies + msecs_to_jiffies(interval); local->timer.function = ib_pcmcia_timer; local->timer.data = _local; add_timer(&local->timer);}/** * ib_pcmcia_blit16 - blit chunks (32 bytes) 16 bits at a time. * @_dst: destination buffer. * @_src: source buffer. * @nchunk: chunk count. */static void ib_pcmcia_blit16(unsigned char *_dst, unsigned char *_src, int nchunk){ uint16_t *dst = (uint16_t*) _dst; uint16_t *src = (uint16_t*) _src; while (nchunk--) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7]; dst[8] = src[8]; dst[9] = src[9]; dst[10] = src[10]; dst[11] = src[11]; dst[12] = src[12]; dst[13] = src[13]; dst[14] = src[14]; dst[15] = src[15]; dst += 16; src += 16; }}/** * ib_pcmcia_blit32 - blit chunks (32 bytes) 32 bits at a time. * @_dst: destination buffer. * @_src: source buffer. * @nchunk: chunk count. */static void ib_pcmcia_blit32(unsigned char *_dst, unsigned char *_src, int nchunk){ uint32_t *dst = (uint32_t*) _dst; uint32_t *src = (uint32_t*) _src; while (1 < nchunk) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7]; dst[8] = src[8]; dst[9] = src[9]; dst[10] = src[10]; dst[11] = src[11]; dst[12] = src[12]; dst[13] = src[13]; dst[14] = src[14]; dst[15] = src[15]; dst += 16; src += 16; nchunk -= 2; } if (nchunk) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7]; }}/** * ib_pcmcia_release - release configuration. * @_local: device private state. */static void ib_pcmcia_release(unsigned long _local){ struct ib_pcmcia_local_t *local = (struct ib_pcmcia_local_t *) _local; struct pcmcia_device *link = local->dev; del_timer_sync(&local->timer); ib_net_flush(local->modem); iounmap(local->mem); pcmcia_disable_device(link);}/** * ib_pcmcia_detach - detach device. * @link: device state. */static void ib_pcmcia_detach(struct pcmcia_device *link){ struct ib_pcmcia_local_t *local = link->priv; del_timer_sync(&local->release); ib_pcmcia_release((unsigned long) local); if (local->modem) ib_net_deregister(local->modem); kfree(local);}/** * ib_pcmcia_reset - reset hardware protocol. * called under ib_lock * @local: device private state. */static void ib_pcmcia_reset(struct ib_pcmcia_local_t *local){ struct ib_net_modem_t *modem = local->modem; unsigned char *mem = local->mem; unsigned int sequence, feedback; mem[OFFSET_PC_MAGIC2] = 0x00; mem[OFFSET_PC_MAGIC1] = 0x00; sequence = mem[OFFSET_UT_SEQUENCE]; feedback = mem[OFFSET_UT_FEEDBACK]; if (local->nreset) local->sequence = (feedback + 1) & 0xff; else local->sequence = (feedback + 7) & 0xff; local->feedback = sequence; mem[OFFSET_PC_SEQUENCE] = local->sequence; mem[OFFSET_PC_FEEDBACK] = local->feedback; mem[OFFSET_PC_IRX] = local->irx = 0; mem[OFFSET_PC_JTX] = local->jtx = 0; mem[OFFSET_PC_PACKET] = modem->stats.tx_packets & 0xff; mem[OFFSET_PC_STATUS] = modem->pc_status; mem[OFFSET_PC_MAGIC1] = 0xac; mem[OFFSET_PC_MAGIC2] = 0x02; local->ifstate = IFSTATE_WAIT; local->nreset += 1;}/** * ib_pcmcia_check - check hardware consistent. * called under ib_lock * @local: device private state. */static int ib_pcmcia_check(struct ib_pcmcia_local_t *local){ unsigned char *mem = local->mem; if (mem[OFFSET_PC_MAGIC1] != 0xac || mem[OFFSET_PC_MAGIC2] != 0x02 || mem[OFFSET_PC_SEQUENCE] != local->sequence || mem[OFFSET_PC_FEEDBACK] != local->feedback || mem[OFFSET_PC_IRX] != local->irx || mem[OFFSET_PC_JTX] != local->jtx) { DEBUG(8, "ib-pcmcia: check %02X %02X %02X %02X " "%02X %02X %02X %02X %02X %02X %02X %02X\n", mem[OFFSET_PC_MAGIC1], mem[OFFSET_PC_MAGIC2], mem[OFFSET_PC_PACKET], mem[OFFSET_PC_STATUS], mem[OFFSET_PC_SEQUENCE], local->sequence, mem[OFFSET_PC_FEEDBACK], local->feedback, mem[OFFSET_PC_IRX], local->irx, mem[OFFSET_PC_JTX], local->jtx); ib_pcmcia_reset(local); return 1; } return 0;}/** * ib_pcmcia_normal - normal operation; transfer chunks. * called under ib_lock * @local: device private state. */static void ib_pcmcia_normal(struct ib_pcmcia_local_t *local){ struct ib_net_modem_t *modem = local->modem; struct ib_net_radio_t *radio = (struct ib_net_radio_t*) modem->rx_buf; unsigned char *mem = local->mem; int irx, jrx, rx_ichunk, rx_nchunk; int itx, jtx, tx_jchunk, tx_nchunk; int nchunk, nbuf; int packet; if (ib_pcmcia_check(local)) return; if (mem[OFFSET_UT_MAGIC1] != 0xac || mem[OFFSET_UT_MAGIC2] != 0x02 || mem[OFFSET_UT_SEQUENCE] != local->feedback || mem[OFFSET_UT_FEEDBACK] != local->sequence) { DEBUG(8, "ib-pcmcia: normal %02X %02X " "%02X %02X %02X %02X\n", mem[OFFSET_UT_MAGIC1], mem[OFFSET_UT_MAGIC2], mem[OFFSET_UT_SEQUENCE], local->feedback, mem[OFFSET_UT_FEEDBACK], local->sequence); mem[OFFSET_PC_IRX] = local->irx = 0; mem[OFFSET_PC_JTX] = local->jtx = 0; local->ifstate = IFSTATE_WAIT; return; } packet = modem->stats.tx_packets & 0xff; ib_net_ut_status(modem, mem[OFFSET_UT_STATUS]); if (mem[OFFSET_PC_PACKET] != packet) mem[OFFSET_PC_PACKET] = packet; if (mem[OFFSET_PC_STATUS] != modem->pc_status) mem[OFFSET_PC_STATUS] = modem->pc_status; rx_ichunk = mem[OFFSET_UT_ICHUNK]; rx_nchunk = mem[OFFSET_UT_NCHUNK]; tx_jchunk = mem[OFFSET_PC_JCHUNK]; tx_nchunk = mem[OFFSET_PC_NCHUNK]; if (rx_ichunk == 0 || rx_nchunk == 0) goto failed; if (tx_jchunk == 0 || tx_nchunk == 0) goto failed; if (tx_jchunk <= rx_ichunk && rx_ichunk < tx_jchunk + tx_nchunk) goto failed; if (rx_ichunk <= tx_jchunk && tx_jchunk < rx_ichunk + rx_nchunk) goto failed; if (0x80 < rx_ichunk + rx_nchunk || 0x80 < tx_jchunk + tx_nchunk) goto failed; while (1) { irx = local->irx; if (mem[OFFSET_PC_IRX] != irx) goto failed; jrx = mem[OFFSET_UT_JRX]; if (2 * rx_nchunk <= jrx) goto failed; if (irx == jrx) break; if (jrx < irx) nchunk = jrx - irx + 2 * rx_nchunk; else nchunk = jrx - irx; if (rx_nchunk < nchunk) goto failed; if (rx_nchunk <= irx) irx -= rx_nchunk; if (rx_nchunk - irx < nchunk) nchunk = rx_nchunk - irx; if (local->rx_echunk == 0) { ib_pcmcia_blit(modem->rx_buf, mem + ((rx_ichunk + irx) << 5), 1); if (radio->word[0] & FLAG_EXTENSION) goto failed; nbuf = IB_NET_NBUF(radio->word[0], radio->word[1]); if (nbuf < IB_NET_RADIO_HEAD) goto failed; if (IB_NET_RADIO_HEAD + ETH_DATA_LEN < nbuf) goto failed; local->irx += 1; if (local->irx == 2 * rx_nchunk) local->irx = 0; mem[OFFSET_PC_IRX] = local->irx; if (nbuf <= 32) { ib_net_rx_parse(modem, nbuf); continue; } local->rx_echunk = ((nbuf + 31) >> 5) - 1; local->rx_ibuf = 32;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -