📄 sktr.c
字号:
/* * sktr.c: A network driver for the SysKonnect Token Ring ISA/PCI Adapters. * * Written 1997 by Christoph Goos * * A fine result of the Linux Systems Network Architecture Project. * http://samba.anu.edu.au/linux-sna/ * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. * * This device driver works with the following SysKonnect adapters: * - SysKonnect TR4/16(+) ISA (SK-4190) * - SysKonnect TR4/16(+) PCI (SK-4590) * - SysKonnect TR4/16 PCI (SK-4591) * * 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. * * Maintainer(s): * JS Jay Schulist jschlst@samba.anu.edu.au * CG Christoph Goos cgoos@syskonnect.de * * 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 * * To do: * 1. Selectable 16 Mbps or 4Mbps * 2. Multi/Broadcast packet handling * */static const char *version = "sktr.c: v1.01 08/29/97 by Christoph Goos\n";#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 <linux/errno.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/trdevice.h>#include "sktr.h" /* Our Stuff */#include "sktr_firmware.h" /* SysKonnect adapter firmware *//* A zero-terminated list of I/O addresses to be probed. */static unsigned int sktr_portlist[] __initdata = { 0x0A20, 0x1A20, 0x0B20, 0x1B20, 0x0980, 0x1980, 0x0900, 0x1900, 0};/* A zero-terminated list of IRQs to be probed. * Used again after initial probe for sktr_chipset_init, called from sktr_open. */static unsigned short sktr_irqlist[] = { 3, 5, 9, 10, 11, 12, 15, 0};/* A zero-terminated list of DMAs to be probed. */static int sktr_dmalist[] __initdata = { 5, 6, 7, 0};/* Card names */static char *pci_cardname = "SK NET TR 4/16 PCI\0";static char *isa_cardname = "SK NET TR 4/16 ISA\0";static char *AdapterName;/* Use 0 for production, 1 for verification, 2 for debug, and * 3 for very verbose debug. */#ifndef SKTR_DEBUG#define SKTR_DEBUG 1#endifstatic unsigned int sktr_debug = SKTR_DEBUG;/* The number of low I/O ports used by the tokencard. */#define SKTR_IO_EXTENT 32/* Index to functions, as function prototypes. * Alphabetical by function name. *//* "B" */static int sktr_bringup_diags(struct device *dev);/* "C" */static void sktr_cancel_tx_queue(struct net_local* tp);static int sktr_chipset_init(struct device *dev);static void sktr_chk_irq(struct device *dev);static unsigned char sktr_chk_frame(struct device *dev, unsigned char *Addr);static void sktr_chk_outstanding_cmds(struct device *dev);static void sktr_chk_src_addr(unsigned char *frame, unsigned char *hw_addr);static unsigned char sktr_chk_ssb(struct net_local *tp, unsigned short IrqType);static int sktr_close(struct device *dev);static void sktr_cmd_status_irq(struct device *dev);/* "D" */static void sktr_disable_interrupts(struct device *dev);static void sktr_dump(unsigned char *Data, int length);/* "E" */static void sktr_enable_interrupts(struct device *dev);static void sktr_exec_cmd(struct device *dev, unsigned short Command);static void sktr_exec_sifcmd(struct device *dev, unsigned int WriteValue);/* "F" */static unsigned char *sktr_fix_srouting(unsigned char *buf, short *FrameLen);/* "G" */static struct enet_statistics *sktr_get_stats(struct device *dev);/* "H" */static void sktr_hardware_send_packet(struct device *dev, struct net_local* tp);/* "I" */static int sktr_init_adapter(struct device *dev);static int sktr_init_card(struct device *dev);static void sktr_init_ipb(struct net_local *tp);static void sktr_init_net_local(struct device *dev);static void sktr_init_opb(struct net_local *tp);static void sktr_interrupt(int irq, void *dev_id, struct pt_regs *regs);static int sktr_isa_chk_card(struct device *dev, int ioaddr);static int sktr_isa_chk_ioaddr(int ioaddr);/* "O" */static int sktr_open(struct device *dev);static void sktr_open_adapter(struct device *dev);/* "P" */static int sktr_pci_chk_card(struct device *dev);int sktr_probe(struct device *dev);static int sktr_probe1(struct device *dev, int ioaddr);/* "R" */static void sktr_rcv_status_irq(struct device *dev);static void sktr_read_addr(struct device *dev, unsigned char *Address);static void sktr_read_ptr(struct device *dev);static void sktr_read_ram(struct device *dev, unsigned char *Data, unsigned short Address, int Length);static int sktr_reset_adapter(struct device *dev);static void sktr_reset_interrupt(struct device *dev);static void sktr_ring_status_irq(struct device *dev);/* "S" */static int sktr_send_packet(struct sk_buff *skb, struct device *dev);static void sktr_set_multicast_list(struct device *dev);/* "T" */static void sktr_timer_chk(unsigned long data);static void sktr_timer_end_wait(unsigned long data);static void sktr_tx_status_irq(struct device *dev);/* "U" */static void sktr_update_rcv_stats(struct net_local *tp, unsigned char DataPtr[], unsigned int Length);/* "W" */static void sktr_wait(unsigned long time);static void sktr_write_rpl_status(RPL *rpl, unsigned int Status);static void sktr_write_tpl_status(TPL *tpl, unsigned int Status);/* * Check for a network adapter of this type, and return '0' if one exists. * If dev->base_addr == 0, probe all likely locations. * If dev->base_addr == 1, always return failure. */__initfunc(int sktr_probe(struct device *dev)){ int i; int base_addr = dev ? dev->base_addr : 0; if(base_addr > 0x1ff) /* Check a single specified location. */ return (sktr_probe1(dev, base_addr)); else if(base_addr != 0) /* Don't probe at all. */ return (-ENXIO); for(i = 0; sktr_portlist[i]; i++) { int ioaddr = sktr_portlist[i]; if(check_region(ioaddr, SKTR_IO_EXTENT)) continue; if(sktr_probe1(dev, ioaddr)) {#ifndef MODULE tr_freedev(dev);#endif } else return (0); } return (-ENODEV);}/* * Detect and setup the PCI SysKonnect TR cards in slot order. */__initfunc(static int sktr_pci_chk_card(struct device *dev)){ static int pci_index = 0; unsigned char pci_bus, pci_device_fn; if(!pci_present()) return (-1); /* No PCI present. */ for(; pci_index < 0xff; pci_index++) { unsigned int pci_irq_line; struct pci_dev *pdev; unsigned short pci_command, new_command, vendor, device; unsigned int pci_ioaddr; if(pcibios_find_class(PCI_CLASS_NETWORK_TOKEN_RING << 8, pci_index, &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) { break; } pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); pdev = pci_find_slot(pci_bus, pci_device_fn); pci_irq_line = pdev->irq; pci_ioaddr = pdev->base_address[0]; pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); /* Remove I/O space marker in bit 0. */ pci_ioaddr &= ~3; if(vendor != PCI_VENDOR_ID_SK) continue; if(device != PCI_DEVICE_ID_SK_TR) continue; if(check_region(pci_ioaddr, SKTR_IO_EXTENT)) continue; request_region(pci_ioaddr, SKTR_IO_EXTENT, pci_cardname); if(request_irq(pdev->irq, sktr_interrupt, SA_SHIRQ, pci_cardname, dev)) return (-ENODEV); /* continue; ?? */ AdapterName = pci_cardname; new_command = (pci_command|PCI_COMMAND_MASTER|PCI_COMMAND_IO); if(pci_command != new_command) { printk("The PCI BIOS has not enabled this" "device! Updating PCI command %4.4x->%4.4x.\n", pci_command, new_command); pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, new_command); } /* At this point we have found a valid PCI TR card. */ dev->base_addr = pci_ioaddr; dev->irq = pci_irq_line; dev->dma = 0; printk("%s: %s found at %#4x, using IRQ %d.\n", dev->name, AdapterName, pci_ioaddr, dev->irq); return (0); } return (-1);}/* * Detect and setup the ISA SysKonnect TR cards. */__initfunc(static int sktr_isa_chk_card(struct device *dev, int ioaddr)){ int i, err; unsigned long flags; err = sktr_isa_chk_ioaddr(ioaddr); if(err < 0) return (-ENODEV); if(virt_to_bus((void*)((unsigned long)dev->priv+sizeof(struct net_local))) > ISA_MAX_ADDRESS) { printk("%s: Memory not accessible for DMA\n", dev->name); kfree(dev->priv); return (-EAGAIN); } AdapterName = isa_cardname; /* Grab the region so that no one else tries to probe our ioports. */ request_region(ioaddr, SKTR_IO_EXTENT, AdapterName); dev->base_addr = ioaddr; /* Autoselect IRQ and DMA if dev->irq == 0 */ if(dev->irq == 0) { for(i = 0; sktr_irqlist[i] != 0; i++) { dev->irq = sktr_irqlist[i]; err = request_irq(dev->irq, &sktr_interrupt, 0, AdapterName, dev); if(!err) break; } if(sktr_irqlist[i] == 0) { printk("%s: AutoSelect no IRQ available\n", dev->name); return (-EAGAIN); } } else { err = request_irq(dev->irq, &sktr_interrupt, 0, AdapterName, dev); if(err) { printk("%s: Selected IRQ not available\n", dev->name); return (-EAGAIN); } } /* Always allocate the DMA channel after IRQ and clean up on failure */ if(dev->dma == 0) { for(i = 0; sktr_dmalist[i] != 0; i++) { dev->dma = sktr_dmalist[i]; err = request_dma(dev->dma, AdapterName); if(!err) break; } if(dev->dma == 0) { printk("%s: AutoSelect no DMA available\n", dev->name); free_irq(dev->irq, NULL); return (-EAGAIN); } } else { err = request_dma(dev->dma, AdapterName); if(err) { printk("%s: Selected DMA not available\n", dev->name); free_irq(dev->irq, NULL); return (-EAGAIN); } } flags=claim_dma_lock(); disable_dma(dev->dma); set_dma_mode(dev->dma, DMA_MODE_CASCADE); enable_dma(dev->dma); release_dma_lock(flags); printk("%s: %s found at %#4x, using IRQ %d and DMA %d.\n", dev->name, AdapterName, ioaddr, dev->irq, dev->dma); return (0);}__initfunc(static int sktr_probe1(struct device *dev, int ioaddr)){ static unsigned version_printed = 0; struct net_local *tp; int err; if(sktr_debug && version_printed++ == 0) printk("%s", version);#ifndef MODULE dev = init_trdev(dev, 0); if(dev == NULL) return (-ENOMEM);#endif err = sktr_pci_chk_card(dev); if(err < 0) { err = sktr_isa_chk_card(dev, ioaddr); if(err < 0) return (-ENODEV); } /* Setup this devices private information structure */ tp = (struct net_local *)kmalloc(sizeof(struct net_local), GFP_KERNEL | GFP_DMA); if(tp == NULL) return (-ENOMEM); memset(tp, 0, sizeof(struct net_local)); dev->priv = tp; dev->init = sktr_init_card; dev->open = sktr_open; dev->stop = sktr_close; dev->hard_start_xmit = sktr_send_packet; dev->get_stats = sktr_get_stats; dev->set_multicast_list = &sktr_set_multicast_list; return (0);}/* Dummy function */__initfunc(static int sktr_init_card(struct device *dev)){ if(sktr_debug > 3) printk("%s: sktr_init_card\n", dev->name); return (0);}/* * This function tests if an adapter is really installed at the * given I/O address. Return negative if no adapter at IO addr. */__initfunc(static int sktr_isa_chk_ioaddr(int ioaddr)){ unsigned char old, chk1, chk2; old = inb(ioaddr + SIFADR); /* Get the old SIFADR value */ chk1 = 0; /* Begin with check value 0 */ do { /* Write new SIFADR value */ outb(chk1, ioaddr + SIFADR); /* Read, invert and write */ chk2 = inb(ioaddr + SIFADD); chk2 ^= 0x0FE; outb(chk2, ioaddr + SIFADR); /* Read, invert and compare */ chk2 = inb(ioaddr + SIFADD); chk2 ^= 0x0FE; if(chk1 != chk2) return (-1); /* No adapter */ chk1 -= 2; } while(chk1 != 0); /* Repeat 128 times (all byte values) */ /* Restore the SIFADR value */ outb(old, ioaddr + SIFADR); 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. */static int sktr_open(struct 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 = sktr_chipset_init(dev); if(err) { printk(KERN_INFO "%s: Chipset initialization error\n", dev->name); return (-1); } dev->addr_len = 6; sktr_read_addr(dev, (unsigned char*)dev->dev_addr); init_timer(&tp->timer); tp->timer.expires = jiffies + 30*HZ; tp->timer.function = sktr_timer_end_wait; tp->timer.data = (unsigned long)dev; tp->timer.next = NULL; tp->timer.prev = NULL; add_timer(&tp->timer); sktr_read_ptr(dev); sktr_enable_interrupts(dev); sktr_open_adapter(dev); dev->tbusy = 0; dev->interrupt = 0; dev->start = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -