📄 sdla.c
字号:
/* * SDLA An implementation of a driver for the Sangoma S502/S508 series * multi-protocol PC interface card. Initial offering is with * the DLCI driver, providing Frame Relay support for linux. * * Global definitions for the Frame relay interface. * * Version: @(#)sdla.c 0.30 12 Sep 1996 * * Credits: Sangoma Technologies, for the use of 2 cards for an extended * period of time. * David Mandelstam <dm@sangoma.com> for getting me started on * this project, and incentive to complete it. * Gene Kozen <74604.152@compuserve.com> for providing me with * important information about the cards. * * Author: Mike McLagan <mike.mclagan@linux.org> * * Changes: * 0.15 Mike McLagan Improved error handling, packet dropping * 0.20 Mike McLagan New transmit/receive flags for config * If in FR mode, don't accept packets from * non DLCI devices. * 0.25 Mike McLagan Fixed problem with rejecting packets * from non DLCI devices. * 0.30 Mike McLagan Fixed kernel panic when used with modified * ifconfig * * 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 <linux/config.h> /* for CONFIG_DLCI_MAX */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/in.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/skbuff.h>#include <linux/if_arp.h>#include <linux/if_frad.h>#include <linux/sdla.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/uaccess.h>static const char* version = "SDLA driver v0.30, 12 Sep 1996, mike.mclagan@linux.org";static unsigned int valid_port[] __initdata = { 0x250, 0x270, 0x280, 0x300, 0x350, 0x360, 0x380, 0x390};static unsigned int valid_mem[] __initdata = { 0xA0000, 0xA2000, 0xA4000, 0xA6000, 0xA8000, 0xAA000, 0xAC000, 0xAE000, 0xB0000, 0xB2000, 0xB4000, 0xB6000, 0xB8000, 0xBA000, 0xBC000, 0xBE000, 0xC0000, 0xC2000, 0xC4000, 0xC6000, 0xC8000, 0xCA000, 0xCC000, 0xCE000, 0xD0000, 0xD2000, 0xD4000, 0xD6000, 0xD8000, 0xDA000, 0xDC000, 0xDE000, 0xE0000, 0xE2000, 0xE4000, 0xE6000, 0xE8000, 0xEA000, 0xEC000, 0xEE000}; static spinlock_t sdla_lock = SPIN_LOCK_UNLOCKED;/********************************************************* * * these are the core routines that access the card itself * *********************************************************/#define SDLA_WINDOW(dev,addr) outb((((addr) >> 13) & 0x1F), (dev)->base_addr + SDLA_REG_Z80_WINDOW)static void __sdla_read(struct net_device *dev, int addr, void *buf, short len){ char *temp; const void *base; int offset, bytes; temp = buf; while(len) { offset = addr & SDLA_ADDR_MASK; bytes = offset + len > SDLA_WINDOW_SIZE ? SDLA_WINDOW_SIZE - offset : len; base = (const void *) (dev->mem_start + offset); SDLA_WINDOW(dev, addr); memcpy(temp, base, bytes); addr += bytes; temp += bytes; len -= bytes; } }static void sdla_read(struct net_device *dev, int addr, void *buf, short len){ unsigned long flags; spin_lock_irqsave(&sdla_lock, flags); __sdla_read(dev, addr, buf, len); spin_unlock_irqrestore(&sdla_lock, flags);}static void __sdla_write(struct net_device *dev, int addr, const void *buf, short len){ const char *temp; void *base; int offset, bytes; temp = buf; while(len) { offset = addr & SDLA_ADDR_MASK; bytes = offset + len > SDLA_WINDOW_SIZE ? SDLA_WINDOW_SIZE - offset : len; base = (void *) (dev->mem_start + offset); SDLA_WINDOW(dev, addr); memcpy(base, temp, bytes); addr += bytes; temp += bytes; len -= bytes; }}static void sdla_write(struct net_device *dev, int addr, const void *buf, short len){ unsigned long flags; spin_lock_irqsave(&sdla_lock, flags); __sdla_write(dev, addr, buf, len); spin_unlock_irqrestore(&sdla_lock, flags);}static void sdla_clear(struct net_device *dev){ unsigned long flags; char *base; int len, addr, bytes; len = 65536; addr = 0; bytes = SDLA_WINDOW_SIZE; base = (void *) dev->mem_start; spin_lock_irqsave(&sdla_lock, flags); while(len) { SDLA_WINDOW(dev, addr); memset(base, 0, bytes); addr += bytes; len -= bytes; } spin_unlock_irqrestore(&sdla_lock, flags);}static char sdla_byte(struct net_device *dev, int addr){ unsigned long flags; char byte, *temp; temp = (void *) (dev->mem_start + (addr & SDLA_ADDR_MASK)); spin_lock_irqsave(&sdla_lock, flags); SDLA_WINDOW(dev, addr); byte = *temp; spin_unlock_irqrestore(&sdla_lock, flags); return(byte);}void sdla_stop(struct net_device *dev){ struct frad_local *flp; flp = dev->priv; switch(flp->type) { case SDLA_S502A: outb(SDLA_S502A_HALT, dev->base_addr + SDLA_REG_CONTROL); flp->state = SDLA_HALT; break; case SDLA_S502E: outb(SDLA_HALT, dev->base_addr + SDLA_REG_Z80_CONTROL); outb(SDLA_S502E_ENABLE, dev->base_addr + SDLA_REG_CONTROL); flp->state = SDLA_S502E_ENABLE; break; case SDLA_S507: flp->state &= ~SDLA_CPUEN; outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); break; case SDLA_S508: flp->state &= ~SDLA_CPUEN; outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); break; }}void sdla_start(struct net_device *dev){ struct frad_local *flp; flp = dev->priv; switch(flp->type) { case SDLA_S502A: outb(SDLA_S502A_NMI, dev->base_addr + SDLA_REG_CONTROL); outb(SDLA_S502A_START, dev->base_addr + SDLA_REG_CONTROL); flp->state = SDLA_S502A_START; break; case SDLA_S502E: outb(SDLA_S502E_CPUEN, dev->base_addr + SDLA_REG_Z80_CONTROL); outb(0x00, dev->base_addr + SDLA_REG_CONTROL); flp->state = 0; break; case SDLA_S507: flp->state |= SDLA_CPUEN; outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); break; case SDLA_S508: flp->state |= SDLA_CPUEN; outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); break; }}/**************************************************** * * this is used for the S502A/E cards to determine * the speed of the onboard CPU. Calibration is * necessary for the Frame Relay code uploaded * later. Incorrect results cause timing problems * with link checks & status messages * ***************************************************/int sdla_z80_poll(struct net_device *dev, int z80_addr, int jiffs, char resp1, char resp2){ unsigned long start, done, now; char resp, *temp; start = now = jiffies; done = jiffies + jiffs; temp = (void *)dev->mem_start; temp += z80_addr & SDLA_ADDR_MASK; resp = ~resp1; while (time_before(jiffies, done) && (resp != resp1) && (!resp2 || (resp != resp2))) { if (jiffies != now) { SDLA_WINDOW(dev, z80_addr); now = jiffies; resp = *temp; } } return(time_before(jiffies, done) ? jiffies - start : -1);}/* constants for Z80 CPU speed */#define Z80_READY '1' /* Z80 is ready to begin */#define LOADER_READY '2' /* driver is ready to begin */#define Z80_SCC_OK '3' /* SCC is on board */#define Z80_SCC_BAD '4' /* SCC was not found */static int sdla_cpuspeed(struct net_device *dev, struct ifreq *ifr){ int jiffs; char data; sdla_start(dev); if (sdla_z80_poll(dev, 0, 3*HZ, Z80_READY, 0) < 0) return(-EIO); data = LOADER_READY; sdla_write(dev, 0, &data, 1); if ((jiffs = sdla_z80_poll(dev, 0, 8*HZ, Z80_SCC_OK, Z80_SCC_BAD)) < 0) return(-EIO); sdla_stop(dev); sdla_read(dev, 0, &data, 1); if (data == Z80_SCC_BAD) { printk("%s: SCC bad\n", dev->name); return(-EIO); } if (data != Z80_SCC_OK) return(-EINVAL); if (jiffs < 165) ifr->ifr_mtu = SDLA_CPU_16M; else if (jiffs < 220) ifr->ifr_mtu = SDLA_CPU_10M; else if (jiffs < 258) ifr->ifr_mtu = SDLA_CPU_8M; else if (jiffs < 357) ifr->ifr_mtu = SDLA_CPU_7M; else if (jiffs < 467) ifr->ifr_mtu = SDLA_CPU_5M; else ifr->ifr_mtu = SDLA_CPU_3M; return(0);}/************************************************ * * Direct interaction with the Frame Relay code * starts here. * ************************************************/struct _dlci_stat { short dlci __attribute__((packed)); char flags __attribute__((packed));};struct _frad_stat { char flags; struct _dlci_stat dlcis[SDLA_MAX_DLCI];};static void sdla_errors(struct net_device *dev, int cmd, int dlci, int ret, int len, void *data) { struct _dlci_stat *pstatus; short *pdlci; int i; char *state, line[30]; switch (ret) { case SDLA_RET_MODEM: state = data; if (*state & SDLA_MODEM_DCD_LOW) printk(KERN_INFO "%s: Modem DCD unexpectedly low!\n", dev->name); if (*state & SDLA_MODEM_CTS_LOW) printk(KERN_INFO "%s: Modem CTS unexpectedly low!\n", dev->name); /* I should probably do something about this! */ break; case SDLA_RET_CHANNEL_OFF: printk(KERN_INFO "%s: Channel became inoperative!\n", dev->name); /* same here */ break; case SDLA_RET_CHANNEL_ON: printk(KERN_INFO "%s: Channel became operative!\n", dev->name); /* same here */ break; case SDLA_RET_DLCI_STATUS: printk(KERN_INFO "%s: Status change reported by Access Node.\n", dev->name); len /= sizeof(struct _dlci_stat); for(pstatus = data, i=0;i < len;i++,pstatus++) { if (pstatus->flags & SDLA_DLCI_NEW) state = "new"; else if (pstatus->flags & SDLA_DLCI_DELETED) state = "deleted"; else if (pstatus->flags & SDLA_DLCI_ACTIVE) state = "active"; else { sprintf(line, "unknown status: %02X", pstatus->flags); state = line; } printk(KERN_INFO "%s: DLCI %i: %s.\n", dev->name, pstatus->dlci, state); /* same here */ } break; case SDLA_RET_DLCI_UNKNOWN: printk(KERN_INFO "%s: Received unknown DLCIs:", dev->name); len /= sizeof(short); for(pdlci = data,i=0;i < len;i++,pdlci++) printk(" %i", *pdlci); printk("\n"); break; case SDLA_RET_TIMEOUT: printk(KERN_ERR "%s: Command timed out!\n", dev->name); break; case SDLA_RET_BUF_OVERSIZE: printk(KERN_INFO "%s: Bc/CIR overflow, acceptable size is %i\n", dev->name, len); break; case SDLA_RET_BUF_TOO_BIG: printk(KERN_INFO "%s: Buffer size over specified max of %i\n", dev->name, len); break; case SDLA_RET_CHANNEL_INACTIVE: case SDLA_RET_DLCI_INACTIVE: case SDLA_RET_CIR_OVERFLOW: case SDLA_RET_NO_BUFS: if (cmd == SDLA_INFORMATION_WRITE) break; default: printk(KERN_DEBUG "%s: Cmd 0x%2.2X generated return code 0x%2.2X\n", dev->name, cmd, ret); /* Further processing could be done here */ break; }}static int sdla_cmd(struct net_device *dev, int cmd, short dlci, short flags, void *inbuf, short inlen, void *outbuf, short *outlen){ static struct _frad_stat status; struct frad_local *flp; struct sdla_cmd *cmd_buf; unsigned long pflags; unsigned long jiffs; int ret, waiting, len; long window; flp = dev->priv; window = flp->type == SDLA_S508 ? SDLA_508_CMD_BUF : SDLA_502_CMD_BUF; cmd_buf = (struct sdla_cmd *)(dev->mem_start + (window & SDLA_ADDR_MASK)); ret = 0; len = 0; jiffs = jiffies + HZ; /* 1 second is plenty */ spin_lock_irqsave(&sdla_lock, pflags); SDLA_WINDOW(dev, window); cmd_buf->cmd = cmd; cmd_buf->dlci = dlci; cmd_buf->flags = flags; if (inbuf) memcpy(cmd_buf->data, inbuf, inlen); cmd_buf->length = inlen; cmd_buf->opp_flag = 1; spin_unlock_irqrestore(&sdla_lock, pflags); waiting = 1; len = 0; while (waiting && time_before_eq(jiffies, jiffs)) { if (waiting++ % 3) { spin_lock_irqsave(&sdla_lock, pflags); SDLA_WINDOW(dev, window); waiting = ((volatile int)(cmd_buf->opp_flag)); spin_unlock_irqrestore(&sdla_lock, pflags); } } if (!waiting) { spin_lock_irqsave(&sdla_lock, pflags); SDLA_WINDOW(dev, window); ret = cmd_buf->retval; len = cmd_buf->length; if (outbuf && outlen) { *outlen = *outlen >= len ? len : *outlen; if (*outlen) memcpy(outbuf, cmd_buf->data, *outlen); } /* This is a local copy that's used for error handling */ if (ret) memcpy(&status, cmd_buf->data, len > sizeof(status) ? sizeof(status) : len); spin_unlock_irqrestore(&sdla_lock, pflags); } else ret = SDLA_RET_TIMEOUT; if (ret != SDLA_RET_OK) sdla_errors(dev, cmd, dlci, ret, len, &status); return(ret);}/*********************************************** * * these functions are called by the DLCI driver * ***********************************************/static int sdla_reconfig(struct net_device *dev);int sdla_activate(struct net_device *slave, struct net_device *master){ struct frad_local *flp; int i; flp = slave->priv; for(i=0;i<CONFIG_DLCI_MAX;i++) if (flp->master[i] == master) break; if (i == CONFIG_DLCI_MAX) return(-ENODEV); flp->dlci[i] = abs(flp->dlci[i]); if (netif_running(slave) && (flp->config.station == FRAD_STATION_NODE)) sdla_cmd(slave, SDLA_ACTIVATE_DLCI, 0, 0, &flp->dlci[i], sizeof(short), NULL, NULL); return(0);}int sdla_deactivate(struct net_device *slave, struct net_device *master){ struct frad_local *flp; int i; flp = slave->priv; for(i=0;i<CONFIG_DLCI_MAX;i++) if (flp->master[i] == master) break; if (i == CONFIG_DLCI_MAX) return(-ENODEV); flp->dlci[i] = -abs(flp->dlci[i]); if (netif_running(slave) && (flp->config.station == FRAD_STATION_NODE)) sdla_cmd(slave, SDLA_DEACTIVATE_DLCI, 0, 0, &flp->dlci[i], sizeof(short), NULL, NULL); return(0);}int sdla_assoc(struct net_device *slave, struct net_device *master){ struct frad_local *flp; int i; if (master->type != ARPHRD_DLCI) return(-EINVAL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -