📄 olympic.c
字号:
/* * olympic.c (c) 1999 Peter De Schrijver All Rights Reserved * 1999 Mike Phillips (phillim@amtrak.com) * * Linux driver for IBM PCI tokenring cards based on the Pit/Pit-Phy/Olympic * chipset. * * Base Driver Skeleton: * Written 1993-94 by Donald Becker. * * Copyright 1993 United States Government as represented by the * Director, National Security Agency. * * Thanks to Erik De Cock, Adrian Bridgett and Frank Fiene for their * assistance and perserverance with the testing of this driver. * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. * * 4/27/99 - Alpha Release 0.1.0 * First release to the public * * 6/8/99 - Official Release 0.2.0 * Merged into the kernel code * 8/18/99 - Updated driver for 2.3.13 kernel to use new pci * resource. Driver also reports the card name returned by * the pci resource. * 1/11/00 - Added spinlocks for smp * 2/23/00 - Updated to dev_kfree_irq * 3/10/00 - Fixed FDX enable which triggered other bugs also * squashed. * 5/20/00 - Changes to handle Olympic on LinuxPPC. Endian changes. * The odd thing about the changes is that the fix for * endian issues with the big-endian data in the arb, asb... * was to always swab() the bytes, no matter what CPU. * That's because the read[wl]() functions always swap the * bytes on the way in on PPC. * Fixing the hardware descriptors was another matter, * because they weren't going through read[wl](), there all * the results had to be in memory in le32 values. kdaaker * * * To Do: * * If Problems do Occur * Most problems can be rectified by either closing and opening the interface * (ifconfig down and up) or rmmod and insmod'ing the driver (a bit difficult * if compiled into the kernel). *//* Change OLYMPIC_DEBUG to 1 to get verbose, and I mean really verbose, messages */#define OLYMPIC_DEBUG 0/* Change OLYMPIC_NETWORK_MONITOR to receive mac frames through the arb channel. * Will also create a /proc/net/olympic_tr entry if proc_fs is compiled into the * kernel. * Intended to be used to create a ring-error reporting network module * i.e. it will give you the source address of beaconers on the ring */#define OLYMPIC_NETWORK_MONITOR 0#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/in.h>#include <linux/ioport.h>#include <linux/string.h>#include <linux/proc_fs.h>#include <linux/ptrace.h>#include <linux/skbuff.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/trdevice.h>#include <linux/stddef.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/spinlock.h>#include <net/checksum.h>#include <asm/io.h>#include <asm/system.h>#include <asm/bitops.h>#include "olympic.h"/* I've got to put some intelligence into the version number so that Peter and I know * which version of the code somebody has got. * Version Number = a.b.c.d where a.b.c is the level of code and d is the latest author. * So 0.0.1.pds = Peter, 0.0.1.mlp = Mike * * Official releases will only have an a.b.c version number format. */static char *version = "Olympic.c v0.5.0 3/10/00 - Peter De Schrijver & Mike Phillips" ; static struct pci_device_id olympic_pci_tbl[] __initdata = { { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR_WAKE, PCI_ANY_ID, PCI_ANY_ID, }, { } /* Terminating entry */};MODULE_DEVICE_TABLE(pci, olympic_pci_tbl);static char *open_maj_error[] = {"No error", "Lobe Media Test", "Physical Insertion", "Address Verification", "Neighbor Notification (Ring Poll)", "Request Parameters","FDX Registration Request", "FDX Duplicate Address Check", "Station registration Query Wait", "Unknown stage"};static char *open_min_error[] = {"No error", "Function Failure", "Signal Lost", "Wire Fault", "Ring Speed Mismatch", "Timeout","Ring Failure","Ring Beaconing", "Duplicate Node Address","Request Parameters","Remove Received", "Reserved", "Reserved", "No Monitor Detected for RPL", "Monitor Contention failer for RPL", "FDX Protocol Error"};/* Module paramters *//* Ring Speed 0,4,16,100 * 0 = Autosense * 4,16 = Selected speed only, no autosense * This allows the card to be the first on the ring * and become the active monitor. * 100 = Nothing at present, 100mbps is autodetected * if FDX is turned on. May be implemented in the future to * fail if 100mpbs is not detected. * * WARNING: Some hubs will allow you to insert * at the wrong speed */static int ringspeed[OLYMPIC_MAX_ADAPTERS] = {0,} ;MODULE_PARM(ringspeed, "1-" __MODULE_STRING(OLYMPIC_MAX_ADAPTERS) "i");/* Packet buffer size */static int pkt_buf_sz[OLYMPIC_MAX_ADAPTERS] = {0,} ; MODULE_PARM(pkt_buf_sz, "1-" __MODULE_STRING(OLYMPIC_MAX_ADAPTERS) "i") ; /* Message Level */static int message_level[OLYMPIC_MAX_ADAPTERS] = {0,} ; MODULE_PARM(message_level, "1-" __MODULE_STRING(OLYMPIC_MAX_ADAPTERS) "i") ; static int olympic_scan(struct net_device *dev);static int olympic_init(struct net_device *dev);static int olympic_open(struct net_device *dev);static int olympic_xmit(struct sk_buff *skb, struct net_device *dev);static int olympic_close(struct net_device *dev);static void olympic_set_rx_mode(struct net_device *dev);static void olympic_interrupt(int irq, void *dev_id, struct pt_regs *regs);static struct net_device_stats * olympic_get_stats(struct net_device *dev);static int olympic_set_mac_address(struct net_device *dev, void *addr) ; static void olympic_arb_cmd(struct net_device *dev);static int olympic_change_mtu(struct net_device *dev, int mtu);static void olympic_srb_bh(struct net_device *dev) ; static void olympic_asb_bh(struct net_device *dev) ; #if OLYMPIC_NETWORK_MONITOR#ifdef CONFIG_PROC_FSstatic int sprintf_info(char *buffer, struct net_device *dev) ; #endif#endifint __init olympic_probe(struct net_device *dev){ int cards_found; cards_found=olympic_scan(dev); return cards_found ? 0 : -ENODEV;}static int __init olympic_scan(struct net_device *dev){ struct pci_dev *pci_device = NULL ; struct olympic_private *olympic_priv; int card_no = 0 ; if (pci_present()) { while((pci_device=pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR_WAKE, pci_device))) { __u16 pci_command ; if (pci_enable_device(pci_device)) continue; /* These lines are needed by the PowerPC, it appearsthat these flags * are not being set properly for the PPC, this maywell be fixed with * the new PCI code */ pci_read_config_word(pci_device, PCI_COMMAND, &pci_command); pci_command |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY; pci_write_config_word(pci_device, PCI_COMMAND,pci_command); pci_set_master(pci_device); /* Check to see if io has been allocated, if so, we've already done this card, so continue on the card discovery loop */ if (check_region(pci_resource_start(pci_device, 0), OLYMPIC_IO_SPACE)) { card_no++ ; continue ; } olympic_priv=kmalloc(sizeof (struct olympic_private), GFP_KERNEL); memset(olympic_priv, 0, sizeof(struct olympic_private)); init_waitqueue_head(&olympic_priv->srb_wait); init_waitqueue_head(&olympic_priv->trb_wait);#ifndef MODULE dev=init_trdev(dev, 0);#endif dev->priv=(void *)olympic_priv;#if OLYMPIC_DEBUG printk("pci_device: %p, dev:%p, dev->priv: %p\n", pci_device, dev, dev->priv);#endif dev->irq=pci_device->irq; dev->base_addr=pci_resource_start(pci_device, 0); dev->init=&olympic_init; olympic_priv->olympic_card_name = (char *)pci_device->resource[0].name ; olympic_priv->olympic_mmio = ioremap(pci_resource_start(pci_device,1),256); olympic_priv->olympic_lap = ioremap(pci_resource_start(pci_device,2),2048); if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000) ) olympic_priv->pkt_buf_sz = PKT_BUF_SZ ; else olympic_priv->pkt_buf_sz = pkt_buf_sz[card_no] ; olympic_priv->olympic_ring_speed = ringspeed[card_no] ; olympic_priv->olympic_message_level = message_level[card_no] ; if(olympic_init(dev)==-1) { unregister_netdevice(dev); kfree(dev->priv); return 0; } dev->open=&olympic_open; dev->hard_start_xmit=&olympic_xmit; dev->change_mtu=&olympic_change_mtu; dev->stop=&olympic_close; dev->do_ioctl=NULL; dev->set_multicast_list=&olympic_set_rx_mode; dev->get_stats=&olympic_get_stats ; dev->set_mac_address=&olympic_set_mac_address ; return 1; } } return 0 ;}static int __init olympic_init(struct net_device *dev){ struct olympic_private *olympic_priv; __u8 *olympic_mmio, *init_srb,*adapter_addr; unsigned long t; unsigned int uaa_addr; olympic_priv=(struct olympic_private *)dev->priv; olympic_mmio=olympic_priv->olympic_mmio; printk("%s \n", version); printk("%s: %s. I/O at %hx, MMIO at %p, LAP at %p, using irq %d\n",dev->name, olympic_priv->olympic_card_name, (unsigned int) dev->base_addr,olympic_priv->olympic_mmio, olympic_priv->olympic_lap, dev->irq); request_region(dev->base_addr, OLYMPIC_IO_SPACE, "olympic"); writel(readl(olympic_mmio+BCTL) | BCTL_SOFTRESET,olympic_mmio+BCTL); t=jiffies; while((readl(olympic_priv->olympic_mmio+BCTL)) & BCTL_SOFTRESET) { schedule(); if(jiffies-t > 40*HZ) { printk(KERN_ERR "IBM PCI tokenring card not responding.\n"); release_region(dev->base_addr, OLYMPIC_IO_SPACE) ; return -1; } } spin_lock_init(&olympic_priv->olympic_lock) ; #if OLYMPIC_DEBUG printk("BCTL: %x\n",readl(olympic_mmio+BCTL)); printk("GPR: %x\n",readw(olympic_mmio+GPR)); printk("SISRMASK: %x\n",readl(olympic_mmio+SISR_MASK));#endif /* Aaaahhh, You have got to be real careful setting GPR, the card holds the previous values from flash memory, including autosense and ring speed */ writel(readl(olympic_mmio+BCTL)|BCTL_MIMREB,olympic_mmio+BCTL); if (olympic_priv->olympic_ring_speed == 0) { /* Autosense */ writel(readl(olympic_mmio+GPR)|GPR_AUTOSENSE,olympic_mmio+GPR); if (olympic_priv->olympic_message_level) printk(KERN_INFO "%s: Ringspeed autosense mode on\n",dev->name); } else if (olympic_priv->olympic_ring_speed == 16) { if (olympic_priv->olympic_message_level) printk(KERN_INFO "%s: Trying to open at 16 Mbps as requested\n", dev->name); writel(GPR_16MBPS, olympic_mmio+GPR); } else if (olympic_priv->olympic_ring_speed == 4) { if (olympic_priv->olympic_message_level) printk(KERN_INFO "%s: Trying to open at 4 Mbps as requested\n", dev->name) ; writel(0, olympic_mmio+GPR); } writel(readl(olympic_mmio+GPR)|GPR_NEPTUNE_BF,olympic_mmio+GPR);#if OLYMPIC_DEBUG printk("GPR = %x\n",readw(olympic_mmio + GPR) ) ; #endif /* start solo init */ writel((1<<15),olympic_mmio+SISR_MASK_SUM); t=jiffies; while(!((readl(olympic_mmio+SISR_RR)) & SISR_SRB_REPLY)) { schedule(); if(jiffies-t > 40*HZ) { printk(KERN_ERR "IBM PCI tokenring card not responding.\n"); release_region(dev->base_addr, OLYMPIC_IO_SPACE); return -1; } } writel(readl(olympic_mmio+LAPWWO),olympic_mmio+LAPA);#if OLYMPIC_DEBUG printk("LAPWWO: %x, LAPA: %x\n",readl(olympic_mmio+LAPWWO), readl(olympic_mmio+LAPA));#endif init_srb=olympic_priv->olympic_lap + ((readl(olympic_mmio+LAPWWO)) & (~0xf800));#if OLYMPIC_DEBUG { int i; printk("init_srb(%p): ",init_srb); for(i=0;i<20;i++) printk("%x ",readb(init_srb+i)); printk("\n");}#endif if(readw(init_srb+6)) { printk(KERN_INFO "tokenring card intialization failed. errorcode : %x\n",readw(init_srb+6)); release_region(dev->base_addr, OLYMPIC_IO_SPACE); return -1; } if (olympic_priv->olympic_message_level) { if ( readb(init_srb +2) & 0x40) { printk(KERN_INFO "Olympic: Adapter is FDX capable.\n") ; } else { printk(KERN_INFO "Olympic: Adapter cannot do FDX.\n"); } } uaa_addr=swab16(readw(init_srb+8));#if OLYMPIC_DEBUG printk("UAA resides at %x\n",uaa_addr);#endif writel(uaa_addr,olympic_mmio+LAPA); adapter_addr=olympic_priv->olympic_lap + (uaa_addr & (~0xf800));#if OLYMPIC_DEBUG printk("adapter address: %02x:%02x:%02x:%02x:%02x:%02x\n", readb(adapter_addr), readb(adapter_addr+1),readb(adapter_addr+2), readb(adapter_addr+3),readb(adapter_addr+4),readb(adapter_addr+5));#endif memcpy_fromio(&dev->dev_addr[0], adapter_addr,6); olympic_priv->olympic_addr_table_addr = swab16(readw(init_srb + 12)); olympic_priv->olympic_parms_addr = swab16(readw(init_srb + 14)); return 0;}static int olympic_open(struct net_device *dev) { struct olympic_private *olympic_priv=(struct olympic_private *)dev->priv; __u8 *olympic_mmio=olympic_priv->olympic_mmio,*init_srb; unsigned long flags; char open_error[255] ; int i, open_finished = 1 ;#if OLYMPIC_NETWORK_MONITOR __u8 *oat ; __u8 *opt ; #endif if(request_irq(dev->irq, &olympic_interrupt, SA_SHIRQ , "olympic", dev)) { return -EAGAIN; }#if OLYMPIC_DEBUG printk("BMCTL: %x\n",readl(olympic_mmio+BMCTL_SUM)); printk("pending ints: %x\n",readl(olympic_mmio+SISR_RR));#endif writel(SISR_MI,olympic_mmio+SISR_MASK_SUM); writel(SISR_MI | SISR_SRB_REPLY, olympic_mmio+SISR_MASK); /* more ints later, doesn't stop arb cmd interrupt */ writel(LISR_LIE,olympic_mmio+LISR); /* more ints later */ /* adapter is closed, so SRB is pointed to by LAPWWO */ writel(readl(olympic_mmio+LAPWWO),olympic_mmio+LAPA); init_srb=olympic_priv->olympic_lap + ((readl(olympic_mmio+LAPWWO)) & (~0xf800));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -