📄 tms380tr.c
字号:
/* * tms380tr.c: A network driver library for Texas Instruments TMS380-based * Token Ring Adapters. * * Originally sktr.c: Written 1997 by Christoph Goos * * A fine result of the Linux Systems Network Architecture Project. * http://www.linux-sna.org * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. * * The following modules are currently available for card support: * - tmspci (Generic PCI card support) * - abyss (Madge PCI support) * * The following cards are currently lacking support, even * though they were supported in previous versions, because * their code did not get migrated into a seperate module: * - SysKonnect TR4/16(+) ISA (SK-4190) * They are no longer supported by this driver, at least until * a module gets written for them. * * Sources: * - The hardware related parts of this driver are take from * the SysKonnect Token Ring driver for Windows NT. * - I used the IBM Token Ring driver 'ibmtr.c' as a base for this * driver, as well as the 'skeleton.c' driver by Donald Becker. * - Also various other drivers in the linux source tree were taken * as samples for some tasks. * - TI TMS380 Second-Generation Token Ring User's Guide * - TI datasheets for respective chips * - David Hein at Texas Instruments * - Various Madge employees * * Maintainer(s): * JS Jay Schulist jschlst@turbolinux.com * CG Christoph Goos cgoos@syskonnect.de * AF Adam Fritzler mid@auk.cx * MLP Mike Phillips phillim@amtrak.com * * Modification History: * 29-Aug-97 CG Created * 04-Apr-98 CG Fixed problems caused by tok_timer_check * 10-Apr-98 CG Fixed lockups at cable disconnection * 27-May-98 JS Formated to Linux Kernel Format * 31-May-98 JS Hacked in PCI support * 16-Jun-98 JS Modulized for multiple cards with one driver * Sep-99 AF Renamed to tms380tr (supports more than SK's) * 23-Sep-99 AF Added Compaq and Thomas-Conrad PCI support * Fixed a bug causing double copies on PCI * Fixed for new multicast stuff (2.2/2.3) * 25-Sep-99 AF Uped TPL_NUM from 3 to 9 * Removed extraneous 'No free TPL' * 22-Dec-99 AF Added Madge PCI Mk2 support and generalized * parts of the initilization procedure. * 30-Dec-99 AF Turned tms380tr into a library ala 8390. * Madge support is provided in the abyss module * Generic PCI support is in the tmspci module. * * To do: * 1. Multi/Broadcast packet handling (this may have fixed itself) * 2. Write a sktrisa module that includes the old ISA support * 3. Allow modules to load their own microcode * 4. Speed up the BUD process -- freezing the kernel for 3+sec is * quite unacceptable. * 5. Still a few remaining stalls when the cable is unplugged. */#ifdef MODULEstatic const char *version = "tms380tr.c: v1.07 21/01/2000 by Christoph Goos, Adam Fritzler\n";#endif#ifdef MODULE#include <linux/module.h>#include <linux/version.h>#endif#include <linux/kernel.h>#include <linux/sched.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/malloc.h>#include <linux/string.h>#include <linux/time.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/trdevice.h>#include "tms380tr.h" /* Our Stuff */#include "tms380tr_microcode.h" /* TI microcode for COMMprocessor *//* Use 0 for production, 1 for verification, 2 for debug, and * 3 for very verbose debug. */#ifndef TMS380TR_DEBUG#define TMS380TR_DEBUG 0#endifstatic unsigned int tms380tr_debug = TMS380TR_DEBUG;/* Index to functions, as function prototypes. * Alphabetical by function name. *//* "A" *//* "B" */static int tms380tr_bringup_diags(struct net_device *dev);/* "C" */static void tms380tr_cancel_tx_queue(struct net_local* tp);static int tms380tr_chipset_init(struct net_device *dev);static void tms380tr_chk_irq(struct net_device *dev);#if 0static unsigned char tms380tr_chk_frame(struct net_device *dev, unsigned char *Addr);#endifstatic void tms380tr_chk_outstanding_cmds(struct net_device *dev);static void tms380tr_chk_src_addr(unsigned char *frame, unsigned char *hw_addr);static unsigned char tms380tr_chk_ssb(struct net_local *tp, unsigned short IrqType);int tms380tr_close(struct net_device *dev);static void tms380tr_cmd_status_irq(struct net_device *dev);/* "D" */static void tms380tr_disable_interrupts(struct net_device *dev);#if TMS380TR_DEBUG > 0static void tms380tr_dump(unsigned char *Data, int length);#endif/* "E" */static void tms380tr_enable_interrupts(struct net_device *dev);static void tms380tr_exec_cmd(struct net_device *dev, unsigned short Command);static void tms380tr_exec_sifcmd(struct net_device *dev, unsigned int WriteValue);/* "F" *//* "G" */static struct net_device_stats *tms380tr_get_stats(struct net_device *dev);/* "H" */static void tms380tr_hardware_send_packet(struct net_device *dev, struct net_local* tp);/* "I" */static int tms380tr_init_adapter(struct net_device *dev);static int tms380tr_init_card(struct net_device *dev);static void tms380tr_init_ipb(struct net_local *tp);static void tms380tr_init_net_local(struct net_device *dev);static void tms380tr_init_opb(struct net_device *dev);void tms380tr_interrupt(int irq, void *dev_id, struct pt_regs *regs);/* "M" *//* "O" */int tms380tr_open(struct net_device *dev);static void tms380tr_open_adapter(struct net_device *dev);/* "P" *//* "R" */static void tms380tr_rcv_status_irq(struct net_device *dev);static int tms380tr_read_ptr(struct net_device *dev);static void tms380tr_read_ram(struct net_device *dev, unsigned char *Data, unsigned short Address, int Length);static int tms380tr_reset_adapter(struct net_device *dev);static void tms380tr_reset_interrupt(struct net_device *dev);static void tms380tr_ring_status_irq(struct net_device *dev);/* "S" */static int tms380tr_send_packet(struct sk_buff *skb, struct net_device *dev);static void tms380tr_set_multicast_list(struct net_device *dev);static int tms380tr_set_mac_address(struct net_device *dev, void *addr);/* "T" */static void tms380tr_timer_chk(unsigned long data);static void tms380tr_timer_end_wait(unsigned long data);static void tms380tr_tx_status_irq(struct net_device *dev);/* "U" */static void tms380tr_update_rcv_stats(struct net_local *tp, unsigned char DataPtr[], unsigned int Length);/* "W" */void tms380tr_wait(unsigned long time);static void tms380tr_write_rpl_status(RPL *rpl, unsigned int Status);static void tms380tr_write_tpl_status(TPL *tpl, unsigned int Status);#define SIFREADB(reg) (((struct net_local *)dev->priv)->sifreadb(dev, reg))#define SIFWRITEB(val, reg) (((struct net_local *)dev->priv)->sifwriteb(dev, val, reg))#define SIFREADW(reg) (((struct net_local *)dev->priv)->sifreadw(dev, reg))#define SIFWRITEW(val, reg) (((struct net_local *)dev->priv)->sifwritew(dev, val, reg))#if TMS380TR_DEBUG > 0static int madgemc_sifprobe(struct net_device *dev){ unsigned char old, chk1, chk2; old = SIFREADB(SIFADR); /* Get the old SIFADR value */ chk1 = 0; /* Begin with check value 0 */ do { madgemc_setregpage(dev, 0); /* Write new SIFADR value */ SIFWRITEB(chk1, SIFADR); chk2 = SIFREADB(SIFADR); if (chk2 != chk1) return -1; madgemc_setregpage(dev, 1); /* Read, invert and write */ chk2 = SIFREADB(SIFADD); if (chk2 != chk1) return -1; madgemc_setregpage(dev, 0); chk2 ^= 0x0FE; SIFWRITEB(chk2, SIFADR); /* Read, invert and compare */ madgemc_setregpage(dev, 1); chk2 = SIFREADB(SIFADD); madgemc_setregpage(dev, 0); chk2 ^= 0x0FE; if(chk1 != chk2) return (-1); /* No adapter */ chk1 -= 2; } while(chk1 != 0); /* Repeat 128 times (all byte values) */ madgemc_setregpage(dev, 0); /* sanity */ /* Restore the SIFADR value */ SIFWRITEB(old, SIFADR); return (0);}#endif/* Dummy function */static int __init tms380tr_init_card(struct net_device *dev){ if(tms380tr_debug > 3) printk("%s: tms380tr_init_card\n", dev->name); return (0);}/* * Open/initialize the board. This is called sometime after * booting when the 'ifconfig' program is run. * * This routine should set everything up anew at each open, even * registers that "should" only need to be set once at boot, so that * there is non-reboot way to recover if something goes wrong. */int tms380tr_open(struct net_device *dev){ struct net_local *tp = (struct net_local *)dev->priv; int err; /* Reset the hardware here. Don't forget to set the station address. */ err = tms380tr_chipset_init(dev); if(err) { printk(KERN_INFO "%s: Chipset initialization error\n", dev->name); return (-1); } init_timer(&tp->timer); tp->timer.expires = jiffies + 30*HZ; tp->timer.function = tms380tr_timer_end_wait; tp->timer.data = (unsigned long)dev; add_timer(&tp->timer); printk(KERN_INFO "%s: Adapter RAM size: %dK\n", dev->name, tms380tr_read_ptr(dev)); tms380tr_enable_interrupts(dev); tms380tr_open_adapter(dev); netif_start_queue(dev); /* Wait for interrupt from hardware. If interrupt does not come, * there will be a timeout from the timer. */ tp->Sleeping = 1; interruptible_sleep_on(&tp->wait_for_tok_int); del_timer(&tp->timer); /* If AdapterVirtOpenFlag is 1, the adapter is now open for use */ if(tp->AdapterVirtOpenFlag == 0) { tms380tr_disable_interrupts(dev); return (-1); } tp->StartTime = jiffies; /* Start function control timer */ tp->timer.expires = jiffies + 2*HZ; tp->timer.function = tms380tr_timer_chk; tp->timer.data = (unsigned long)dev; add_timer(&tp->timer); return (0);}/* * Timeout function while waiting for event */static void tms380tr_timer_end_wait(unsigned long data){ struct net_device *dev = (struct net_device*)data; struct net_local *tp = (struct net_local *)dev->priv; if(tp->Sleeping) { tp->Sleeping = 0; wake_up_interruptible(&tp->wait_for_tok_int); } return;}/* * Initialize the chipset */static int tms380tr_chipset_init(struct net_device *dev){ struct net_local *tp = (struct net_local *)dev->priv; int err; tms380tr_init_ipb(tp); tms380tr_init_opb(dev); tms380tr_init_net_local(dev); err = tms380tr_reset_adapter(dev); if(err < 0) return (-1); err = tms380tr_bringup_diags(dev); if(err < 0) return (-1); err = tms380tr_init_adapter(dev); if(err < 0) return (-1); return (0);}/* * Initializes the net_local structure. */static void tms380tr_init_net_local(struct net_device *dev){ struct net_local *tp = (struct net_local *)dev->priv; int i; tp->scb.CMD = 0; tp->scb.Parm[0] = 0; tp->scb.Parm[1] = 0; tp->ssb.STS = 0; tp->ssb.Parm[0] = 0; tp->ssb.Parm[1] = 0; tp->ssb.Parm[2] = 0; tp->CMDqueue = 0; tp->AdapterOpenFlag = 0; tp->AdapterVirtOpenFlag = 0; tp->ScbInUse = 0; tp->OpenCommandIssued = 0; tp->ReOpenInProgress = 0; tp->HaltInProgress = 0; tp->TransmitHaltScheduled = 0; tp->LobeWireFaultLogged = 0; tp->LastOpenStatus = 0; tp->MaxPacketSize = DEFAULT_PACKET_SIZE; skb_queue_head_init(&tp->SendSkbQueue); tp->QueueSkb = MAX_TX_QUEUE; /* Create circular chain of transmit lists */ for (i = 0; i < TPL_NUM; i++) { tp->Tpl[i].NextTPLAddr = htonl((unsigned long) virt_to_bus(&tp->Tpl[(i+1) % TPL_NUM])); tp->Tpl[i].Status = 0; tp->Tpl[i].FrameSize = 0; tp->Tpl[i].FragList[0].DataCount = 0; tp->Tpl[i].FragList[0].DataAddr = 0; tp->Tpl[i].NextTPLPtr = &tp->Tpl[(i+1) % TPL_NUM]; tp->Tpl[i].MData = NULL; tp->Tpl[i].TPLIndex = i; tp->Tpl[i].BusyFlag = 0; } tp->TplFree = tp->TplBusy = &tp->Tpl[0]; /* Create circular chain of receive lists */ for (i = 0; i < RPL_NUM; i++) { tp->Rpl[i].NextRPLAddr = htonl((unsigned long) virt_to_bus(&tp->Rpl[(i+1) % RPL_NUM])); tp->Rpl[i].Status = (RX_VALID | RX_START_FRAME | RX_END_FRAME | RX_FRAME_IRQ); tp->Rpl[i].FrameSize = 0; tp->Rpl[i].FragList[0].DataCount = cpu_to_be16((unsigned short)tp->MaxPacketSize); /* Alloc skb and point adapter to data area */ tp->Rpl[i].Skb = dev_alloc_skb(tp->MaxPacketSize); /* skb == NULL ? then use local buffer */ if(tp->Rpl[i].Skb == NULL) { tp->Rpl[i].SkbStat = SKB_UNAVAILABLE; tp->Rpl[i].FragList[0].DataAddr = htonl(virt_to_bus(tp->LocalRxBuffers[i])); tp->Rpl[i].MData = tp->LocalRxBuffers[i]; } else /* SKB != NULL */ { tp->Rpl[i].Skb->dev = dev; skb_put(tp->Rpl[i].Skb, tp->MaxPacketSize); /* data unreachable for DMA ? then use local buffer */ if(tp->dmalimit && virt_to_bus(tp->Rpl[i].Skb->data) + tp->MaxPacketSize > tp->dmalimit) { tp->Rpl[i].SkbStat = SKB_DATA_COPY; tp->Rpl[i].FragList[0].DataAddr = htonl(virt_to_bus(tp->LocalRxBuffers[i])); tp->Rpl[i].MData = tp->LocalRxBuffers[i]; } else /* DMA directly in skb->data */ { tp->Rpl[i].SkbStat = SKB_DMA_DIRECT; tp->Rpl[i].FragList[0].DataAddr = htonl(virt_to_bus(tp->Rpl[i].Skb->data)); tp->Rpl[i].MData = tp->Rpl[i].Skb->data; } } tp->Rpl[i].NextRPLPtr = &tp->Rpl[(i+1) % RPL_NUM]; tp->Rpl[i].RPLIndex = i; } tp->RplHead = &tp->Rpl[0]; tp->RplTail = &tp->Rpl[RPL_NUM-1]; tp->RplTail->Status = (RX_START_FRAME | RX_END_FRAME | RX_FRAME_IRQ); return;}/* * Initializes the initialisation parameter block. */static void tms380tr_init_ipb(struct net_local *tp){ tp->ipb.Init_Options = BURST_MODE; tp->ipb.CMD_Status_IV = 0; tp->ipb.TX_IV = 0; tp->ipb.RX_IV = 0; tp->ipb.Ring_Status_IV = 0; tp->ipb.SCB_Clear_IV = 0; tp->ipb.Adapter_CHK_IV = 0; tp->ipb.RX_Burst_Size = BURST_SIZE; tp->ipb.TX_Burst_Size = BURST_SIZE; tp->ipb.DMA_Abort_Thrhld = DMA_RETRIES; tp->ipb.SCB_Addr = 0; tp->ipb.SSB_Addr = 0; return;}/* * Initializes the open parameter block. */static void tms380tr_init_opb(struct net_device *dev){ struct net_local *tp; unsigned long Addr; unsigned short RplSize = RPL_SIZE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -