/* * 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 olympic and the PIT/PHY 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 (erik@ibm.be) for his 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 * * Known Problems: * * Ping flooding the broadcast address will cause the generation of an unexpected * interrupt 0x10000 and possibly the de-syncing of the buffers. * * To Do: * * Improve error handling. * Enable MAC frame handling. * And all the other things we haven't thought about. * * 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). * * * 4/28/99 - Version 0.1.1.mlp * We do have to update rx_ring_last_received if we receive a frame error. * Change pkt_buf_sz to 4192. (8 * 2048 < 18000) * Change mtu function works now, how large do you want your packets to be. * * 4/28/99 - Version 0.1.2.mlp * Set pkt_buf_sz back to 2048, increases ring size to 16 (to match pds changes.) * Added check for SISR_MI in interrupt routine. (Why does this look so much like * a chipset workaround ??) This removes the 0x10000 interrupts. * Turned on a whole load of other interrupts we will want to receive. * * 4/29/99 - Version 0.1.3.mlp * Turning on the other interrupts killed the tx queue. Turns out issuing * two SUM's in succession can lead to the first SUM being ignored. (Makes sense * we can do two memory writes quicker than the PCI bus can service the area.) * * 4/29/99 - Version 0.1.4.mlp * Implemented variable sized rx buffers. Changing mtu size will change the rx buffer * size, when the buffers get renewed, i.e. single buffer rx's. * Doh, need to change this to be on a device by device basis. It will work as a global * but then we are using memory for interfaces when we don't need to. Also added * + TR_HLEN to the pkt_buf_sz calculation, I'm pretty convinced its the right thing to do. * If not, all we lose is a bit of memory. * * 4/30/99 - Version 0.1.5.mlp * Removed the do {} while loop from the interrupt routine, of course we are going to get * sisr = 0x10000 (Rx End of Frame Early) if we re-read sisr, the hardware can be faster * than us. Ran over 2,000,000 packets through the broadcast address and didn't get one * 0x10000 interrupt. * * 4/30/99 - Version 0.1.6.mlp * Added multiple module parameters for ringspeed and pkt_buf_sz so each card can be * configured separately. This involves a very nasty hack using a global int, not * good, need to improve this. * Changed some status printk's from KERN_WARNING to KERN_INFO, stops them getting * displayed on the console (still go to /var/log/messages) * * 5/3/99 - Version 0.1.7.mlp * Thought of a way to get rid of the nasty global variable and put it in the init function * Also added the set_multicast function to allow promiscuous mode. Big problem though, * trying to call sleep_on to wait for the interrupt is producing a kernel panic. So for * now we are just ignoring the interrupt. * */ /* Change OLYMPIC_DEBUG to 1 to get verbose, and I mean really verbose, messages */ #define OLYMPIC_DEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "olympic.h" #define OLYMPIC_RX_RING_SIZE 16 /* should be a power of 2 */ #define OLYMPIC_TX_RING_SIZE 8 /* should be a power of 2 */ #define PKT_BUF_SZ 2048 /* Default packet size */ struct olympic_tx_desc { __u32 buffer; __u32 status_length; }; struct olympic_tx_status { __u32 status; }; struct olympic_rx_desc { __u32 buffer; __u32 res_length ; }; struct olympic_rx_status { __u32 fragmentcnt_framelen; __u32 status_buffercnt; }; struct mac_receive_buffer { __u16 next ; __u8 padding ; __u8 frame_status ; __u16 buffer_length ; __u8 *frame_data ; }; struct olympic_private { __u16 srb; __u16 trb; __u16 arb; __u16 asb; __u8 *olympic_mmio; __u8 *olympic_lap; volatile int srb_queued; /* True if an SRB is still posted */ struct wait_queue *srb_wait; volatile int asb_queued; /* True if an ASB is posted */ struct wait_queue *asb_wait; volatile int trb_queued; /* True if a TRB is posted */ struct wait_queue *trb_wait ; struct olympic_rx_desc olympic_rx_ring[OLYMPIC_RX_RING_SIZE]; struct olympic_tx_desc olympic_tx_ring[OLYMPIC_TX_RING_SIZE]; struct olympic_rx_status olympic_rx_status_ring[OLYMPIC_RX_RING_SIZE]; struct olympic_tx_status olympic_tx_status_ring[OLYMPIC_TX_RING_SIZE]; struct sk_buff *tx_ring_skb[OLYMPIC_TX_RING_SIZE], *rx_ring_skb[OLYMPIC_RX_RING_SIZE]; int tx_ring_free, tx_ring_last_status, rx_ring_last_received,rx_status_last_received, free_tx_ring_entries; struct net_device_stats olympic_stats ; __u16 olympic_lan_status ; __u8 olympic_ring_speed ; __u16 pkt_buf_sz ; __u8 olympic_receive_options, olympic_copy_all_options ; }; /* 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.1.7.mlp 5/3/99 - Peter De Schrijver & Mike Phillips" ; 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 * I need some way of passing different parameters to each card in the machine * and to be able to identify each card exactly, moving the cards around on the * pci bus will move around the order pci detects them and therefore the order * we can detect them (so simple arrays don't quite cut it) - MLP * * Okay, I've given up on the idea of directly id'ing the cards, will just do * them in the sequence they are detected on the pci bus for now. * */ /* 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"); /* MODULE_PARM(ringspeed, "1-8i"); */ /* Packet buffer size */ static int pkt_buf_sz[OLYMPIC_MAX_ADAPTERS] = {0,} ; MODULE_PARM(pkt_buf_sz, "1-" __MODULE_STRING(OLYMPIC_MAX_ADAPTERS) "i") ; static int olympic_scan(struct device *dev); static int olympic_init(struct device *dev); static int olympic_open(struct device *dev); static int olympic_xmit(struct sk_buff *skb, struct device *dev); static int olympic_close(struct device *dev); static void olympic_set_rx_mode(struct device *dev); static void olympic_interrupt(int irq, void *dev_id, struct pt_regs *regs); static struct net_device_stats * olympic_get_stats(struct device *dev); static void olympic_arb_cmd(struct device *dev); static int olympic_change_mtu(struct device *dev, int mtu); int olympic_probe(struct device *dev) { int cards_found; cards_found=olympic_scan(dev); /* printk("%d olympic based cards found\n",cards_found); */ return cards_found ? 0 : -ENODEV; } static int olympic_scan(struct 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))) { 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_device->base_address[0] & (~3), OLYMPIC_IO_SPACE)) { card_no++ ; continue ; } olympic_priv=kmalloc(sizeof (struct olympic_private), GFP_KERNEL); memset(olympic_priv, 0, sizeof(*olympic_priv)); #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_device->base_address[0] & (~3); dev->init=&olympic_init; olympic_priv->olympic_mmio=ioremap(pci_device->base_address[1],256); olympic_priv->olympic_lap=ioremap(pci_device->base_address[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] ; 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 ; return 1; } } return 0 ; } static int olympic_init(struct 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: IBM PCI tokenring card. I/O at %hx, MMIO at %p, LAP at %p, using irq %d\n",dev->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"); *(__u32 *)(olympic_mmio+BCTL)|=BCTL_SOFTRESET; t=jiffies; while((*(__u32 *)(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; } } #if OLYMPIC_DEBUG printk("BCTL: %x\n",*(__u32 *)(olympic_mmio+BCTL)); printk("GPR: %x\n",*(__u16 *)(olympic_mmio+GPR)); printk("SISRMASK: %x\n",*(__u32 *)(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 */ *(__u32 *)(olympic_mmio+BCTL)|= BCTL_MIMREB ; if (olympic_priv->olympic_ring_speed == 0) { /* Autosense */ *(__u32 *)(olympic_mmio+GPR) |= GPR_AUTOSENSE ; printk(KERN_INFO "%s: Ringspeed autosense mode on\n",dev->name); } else if (olympic_priv->olympic_ring_speed == 16) { printk(KERN_INFO "%s: Trying to open at 16 Mbps as requested\n", dev->name); *(__u32 *)(olympic_mmio+GPR) = GPR_16MBPS ; } else if (olympic_priv->olympic_ring_speed == 4) { printk(KERN_INFO "%s: Trying to open at 4 Mbps as requested\n", dev->name) ; *(__u32 *)(olympic_mmio+GPR) = 0 ; } *(__u32 *)(olympic_mmio+GPR) |= (GPR_NEPTUNE_BF) ; #if OLYMPIC_DEBUG printk("GPR = %x\n",*(__u16 *)(olympic_mmio + GPR) ) ; #endif /* start solo init */ *(__u32 *)(olympic_mmio+SISR_MASK_SUM)=(1<<15); t=jiffies; while(!((*(__u32 *)(olympic_mmio+SISR)) & 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; } } *(__u32 *)(olympic_mmio+LAPA)=*(__u32 *)(olympic_mmio+LAPWWO); #if OLYMPIC_DEBUG printk("LAPWWO: %x, LAPA: %x\n",*(__u32 *)(olympic_mmio+LAPWWO), *(__u32 *)(olympic_mmio+LAPA)); #endif init_srb=olympic_priv->olympic_lap + ((*(__u32 *)(olympic_mmio+LAPWWO)) & (~0xf800)); #if OLYMPIC_DEBUG printk("init_srb(%p): ",init_srb); for(i=0;i<20;i++) printk("%x ",init_srb[i]); printk("\n"); #endif if(*(__u16 *)(init_srb+6)) { printk(KERN_INFO "tokenring card intialization failed. errorcode : %x\n",*(__u16 *)(init_srb+6)); release_region(dev->base_addr, OLYMPIC_IO_SPACE); return -1; } uaa_addr=ntohs(*(__u16 *)(init_srb+8)); #if OLYMPIC_DEBUG printk("UAA resides at %x\n",uaa_addr); #endif *(__u32 *)(olympic_mmio+LAPA)=uaa_addr; adapter_addr=olympic_priv->olympic_lap + (uaa_addr & (~0xf800)); #if OLYMPIC_DEBUG printk("adapter address: %02x:%02x:%02x:%02x:%02x:%02x\n",adapter_addr[0], adapter_addr[1],adapter_addr[2],adapter_addr[3],adapter_addr[4], adapter_addr[5]); #endif memcpy(&dev->dev_addr[0], adapter_addr,6); return 0; } static int olympic_open(struct 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(request_irq(dev->irq, &olympic_interrupt, SA_SHIRQ , "olympic", dev)) { return -EAGAIN; } #if OLYMPIC_DEBUG printk("BMCTL: %x\n",*(__u32 *)(olympic_mmio+BMCTL_SUM)); printk("pending ints: %x\n",*(__u32 *)(olympic_mmio+SISR_RR)); #endif *(__u32 *)(olympic_mmio+SISR_MASK_SUM)=SISR_MI; *(__u32 *)(olympic_mmio+SISR_MASK)=SISR_MI | SISR_SRB_REPLY; /* more ints later, doesn't stop arb cmd interrupt */ *(__u32 *)(olympic_mmio+LISR)=LISR_LIE; /* more ints later */ /* adapter is closed, so SRB is pointed to by LAPWWO */ *(__u32 *)(olympic_mmio+LAPA)=*(__u32 *)(olympic_mmio+LAPWWO); init_srb=olympic_priv->olympic_lap + ((*(__u32 *)(olympic_mmio+LAPWWO)) & (~0xf800)); #if OLYMPIC_DEBUG printk("LAPWWO: %x, LAPA: %x\n",*(__u32 *)(olympic_mmio+LAPWWO), *(__u32 *)(olympic_mmio+LAPA)); printk("SISR Mask = %04x\n", *( __u32 *)(olympic_mmio+SISR_MASK)); printk("Before the open command \n"); #endif do { save_flags(flags); cli(); memset(init_srb,0,SRB_COMMAND_SIZE); init_srb[0]= SRB_OPEN_ADAPTER ; /* open */ init_srb[2]= OLYMPIC_CLEAR_RET_CODE; init_srb[8]= OPEN_ADAPTER_ENABLE_FDX ; init_srb[30]=0x1; olympic_priv->srb_queued=1; *(__u32 *)(olympic_mmio+LISR_SUM)=LISR_SRB_CMD; while(olympic_priv->srb_queued) { interruptible_sleep_on_timeout(&olympic_priv->srb_wait, jiffies+60*HZ); if(signal_pending(current)) { printk(KERN_WARNING "%s: SRB timed out.\n", dev->name); printk(KERN_WARNING "SISR=%x MISR=%x\n", *(__u32 *)(olympic_mmio+SISR), *(__u32 *)(olympic_mmio+LISR)); olympic_priv->srb_queued=0; break; } } restore_flags(flags); #if OLYMPIC_DEBUG printk("init_srb(%p): ",init_srb); for(i=0;i<20;i++) printk("%x ",init_srb[i]); printk("\n"); #endif if(init_srb[2]!=0) { if (init_srb[2] == 0x07) { if (!olympic_priv->olympic_ring_speed && open_finished) { /* Autosense , first time around */ printk(KERN_WARNING "%s: Retrying at different ring speed \n", dev->name); open_finished = 0 ; } else { strcpy(open_error, open_maj_error[(init_srb[7] & 0xf0) >> 8]) ; strcat(open_error," - ") ; strcat(open_error, open_min_error[(init_srb[7] & 0x0f)]) ; if (!olympic_priv->olympic_ring_speed && ((init_srb[7] & 0x0f) == 0x0d)) { printk(KERN_WARNING "%s: Tried to autosense ring speed with no monitors present\n",dev->name); printk(KERN_WARNING "%s: Please try again with a specified ring speed \n",dev->name); free_irq(dev->irq, dev); return -EIO ; } printk(KERN_WARNING "%s: %s\n",dev->name,open_error); free_irq(dev->irq,dev) ; return -EIO ; } /* if autosense && open_finished */ } else { printk(KERN_WARNING "%s: Bad OPEN response: %x\n", dev->name,init_srb[2]); free_irq(dev->irq, dev); return -EIO; } } else open_finished = 1 ; } while (!(open_finished)) ; /* Will only loop if ring speed mismatch re-open attempted && autosense is on */ if (init_srb[18] & (1<<3)) printk(KERN_INFO "%s: Opened in FDX Mode\n",dev->name); if (init_srb[18] & (1<<1)) olympic_priv->olympic_ring_speed = 100 ; else if (init_srb[18] & 1) olympic_priv->olympic_ring_speed = 16 ; else olympic_priv->olympic_ring_speed = 4 ; printk(KERN_INFO "%s: Opened in %d Mbps mode\n",dev->name, olympic_priv->olympic_ring_speed); olympic_priv->asb=ntohs(*(__u16 *)(init_srb+8)); olympic_priv->srb=ntohs(*(__u16 *)(init_srb+10)); olympic_priv->arb=ntohs(*(__u16 *)(init_srb+12)); olympic_priv->trb=ntohs(*(__u16 *)(init_srb+16)); olympic_priv->olympic_receive_options = 0x01 ; olympic_priv->olympic_copy_all_options = 0 ; /* setup rx ring */ /* *(__u32 *)(olympic_mmio+BMCTL_SUM)=(1<<19); */ /* use fixed length buffers */ *(__u32 *)(olympic_mmio+BMCTL_RWM) = (3<<16) ; /* Ensure end of frame generated interrupts */ *(__u32 *)(olympic_mmio+BMCTL_RWM)=BMCTL_RX_DIS|3; /* Yes, this the enables RX channel */ for(i=0;ipkt_buf_sz); if(skb == NULL) break; skb->dev = dev; olympic_priv->olympic_rx_ring[i].buffer=virt_to_bus(skb->data); olympic_priv->olympic_rx_ring[i].res_length = olympic_priv->pkt_buf_sz ; olympic_priv->rx_ring_skb[i]=skb; } if (i==0) { printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers. Adapter disabled\n",dev->name); free_irq(dev->irq, dev); return -EIO; } *(__u32 *)(olympic_mmio+RXDESCQ)=(__u32)virt_to_bus(&olympic_priv->olympic_rx_ring[0]); *(__u32 *)(olympic_mmio+RXCDA)=(__u32)virt_to_bus(&olympic_priv->olympic_rx_ring[0]); *(__u16 *)(olympic_mmio+RXDESCQCNT)=i; *(__u32 *)(olympic_mmio+RXSTATQ)=(__u32)virt_to_bus(&olympic_priv->olympic_rx_status_ring[0]); *(__u32 *)(olympic_mmio+RXCSA)=(__u32)virt_to_bus(&olympic_priv->olympic_rx_status_ring[0]); /* *(__u16 *)(olympic_mmio+RXCLEN)= pkt_buf_sz; */ olympic_priv->rx_ring_last_received=OLYMPIC_RX_RING_SIZE-1; /* last processed rx status */ olympic_priv->rx_status_last_received = OLYMPIC_RX_RING_SIZE-1; *(__u16 *)(olympic_mmio+RXSTATQCNT)=i; #if OLYMPIC_DEBUG printk("# of rx buffers: %d, RXENQ: %x\n",i, *(__u16 *)(olympic_mmio+RXENQ)); printk("RXCSA: %x, rx_status_ring[0]: %p\n",bus_to_virt(*(__u32 *)(olympic_mmio+RXCSA)),&olympic_priv->olympic_rx_status_ring[0]); printk(" stat_ring[1]: %p, stat_ring[2]: %p, stat_ring[3]: %p\n", &(olympic_priv->olympic_rx_status_ring[1]), &(olympic_priv->olympic_rx_status_ring[2]), &(olympic_priv->olympic_rx_status_ring[3]) ); printk(" stat_ring[4]: %p, stat_ring[5]: %p, stat_ring[6]: %p\n", &(olympic_priv->olympic_rx_status_ring[4]), &(olympic_priv->olympic_rx_status_ring[5]), &(olympic_priv->olympic_rx_status_ring[6]) ); printk(" stat_ring[7]: %p\n", &(olympic_priv->olympic_rx_status_ring[7]) ); printk("RXCDA: %x, rx_ring[0]: %p\n",bus_to_virt(*(__u32 *)(olympic_mmio+RXCDA)),&olympic_priv->olympic_rx_ring[0]); #endif *(__u16 *)(olympic_mmio+RXENQ)=(((*(__u16 *)(olympic_mmio+RXENQ)) & 0x8000) ^ 0x8000) | i; #if OLYMPIC_DEBUG printk("# of rx buffers: %d, RXENQ: %x\n",i, *(__u16 *)(olympic_mmio+RXENQ)); printk("RXCSA: %x, rx_ring[0]: %p\n",bus_to_virt(*(__u32 *)(olympic_mmio+RXCSA)),&olympic_priv->olympic_rx_status_ring[0]); printk("RXCDA: %x, rx_ring[0]: %p\n",bus_to_virt(*(__u32 *)(olympic_mmio+RXCDA)),&olympic_priv->olympic_rx_ring[0]); #endif #if 0 olympic_priv->rx_ring_last_received=(__u32)bus_to_virt(*(__u32 *)(olympic_mmio+RXCSA))- (__u32)&olympic_priv->olympic_rx_ring[0]; printk("olympic_priv->rx_ring_last_received: %x\n",olympic_priv->rx_ring_last_received); olympic_priv->rx_ring_last_received--; olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE-1); printk("olympic_priv->rx_ring_last_received: %x\n",olympic_priv->rx_ring_last_received); #endif *(__u32 *)(olympic_mmio+SISR_MASK_SUM)=SISR_RX_STATUS | SISR_RX_NOBUF; /* setup tx ring */ *(__u32 *)(olympic_mmio+BMCTL_RWM)=BMCTL_TX1_DIS; /* Yes, this enables TX channel 1 */ for(i=0;iolympic_tx_ring[i].buffer=0xdeadbeef; olympic_priv->free_tx_ring_entries=OLYMPIC_TX_RING_SIZE; *(__u32 *)(olympic_mmio+TXDESCQ_1)=virt_to_bus(&olympic_priv->olympic_tx_ring[0]); *(__u32 *)(olympic_mmio+TXCDA_1)=virt_to_bus(&olympic_priv->olympic_tx_ring[0]); *(__u16 *)(olympic_mmio+TXDESCQCNT_1)=OLYMPIC_TX_RING_SIZE; *(__u32 *)(olympic_mmio+TXSTATQ_1)=virt_to_bus(&olympic_priv->olympic_tx_status_ring[0]); *(__u32 *)(olympic_mmio+TXCSA_1)=virt_to_bus(&olympic_priv->olympic_tx_status_ring[0]); *(__u16 *)(olympic_mmio+TXSTATQCNT_1)=OLYMPIC_TX_RING_SIZE; olympic_priv->tx_ring_free=0; /* next entry in tx ring to use */ olympic_priv->tx_ring_last_status=OLYMPIC_TX_RING_SIZE-1; /* last processed tx status */ /* *(__u32 *)(olympic_mmio+SISR_MASK_SUM)=SISR_TX1_EOF; */ *(__u32 *)(olympic_mmio+SISR_MASK_SUM)=SISR_TX1_EOF | SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE ; #if OLYMPIC_DEBUG printk("BMCTL: %x\n",*(__u32 *)(olympic_mmio+BMCTL_SUM)); printk("SISR MASK: %x\n",*(__u32 *)(olympic_mmio+SISR_MASK)); #endif dev->start = 1; dev->interrupt=0; dev->tbusy=0; MOD_INC_USE_COUNT ; return 0; } static void olympic_rx(struct device *dev) { struct olympic_private *olympic_priv=(struct olympic_private *)dev->priv; __u8 *olympic_mmio=olympic_priv->olympic_mmio; struct olympic_rx_status *rx_status; struct olympic_rx_desc *rx_desc ; int rx_ring_last_received,rx_status_last_received,length, buffer_cnt, cpy_length, frag_len; struct sk_buff *skb, *skb2; int i; olympic_priv->rx_status_last_received++ ; olympic_priv->rx_status_last_received &= (OLYMPIC_RX_RING_SIZE -1); rx_status_last_received = olympic_priv->rx_status_last_received ; rx_status = &(olympic_priv->olympic_rx_status_ring[rx_status_last_received]); #if OLYMPIC_DEBUG printk(" stat_ring addr: %x \n", &(olympic_priv->olympic_rx_status_ring[olympic_priv->rx_status_last_received]) ); printk("rx status: %x rx len: %x \n",rx_status->status_buffercnt,rx_status->fragmentcnt_framelen); #endif length=rx_status->fragmentcnt_framelen & 0xffff; buffer_cnt = rx_status->status_buffercnt & 0xffff ; i = buffer_cnt ; /* Need buffer_cnt later for rxenq update */ frag_len = rx_status->fragmentcnt_framelen >> 16 ; #if 0 printk("length: %x, frag_len: %x, buffer_cnt: %x\n",length,frag_len,buffer_cnt); #endif if (length != 0) { if(rx_status->status_buffercnt & 0xC0000000) { if (rx_status->status_buffercnt & 0x3B000000) { if (rx_status->status_buffercnt & (1<<29)) /* Rx Frame Truncated */ printk(KERN_WARNING "%s: Rx Frame Truncated \n",dev->name); if (rx_status->status_buffercnt & (1<<28)) /*Rx receive overrun */ printk(KERN_WARNING "%s: Rx Frame Receive overrun \n",dev->name); if (rx_status->status_buffercnt & (1<<27)) /* No receive buffers */ printk(KERN_WARNING "%s: No receive buffers \n",dev->name); if (rx_status->status_buffercnt & (1<<25)) /* Receive frame error detect */ printk(KERN_WARNING "%s: Receive frame error detect \n",dev->name); if (rx_status->status_buffercnt & (1<<24)) /* Received Error Detect */ printk(KERN_WARNING "%s: Received Error Detect \n",dev->name); olympic_priv->rx_ring_last_received += i ; olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1) ; olympic_priv->olympic_stats.rx_errors++; } else { if (buffer_cnt == 1) { skb = dev_alloc_skb(olympic_priv->pkt_buf_sz) ; } else { skb = dev_alloc_skb(length) ; } if (skb == NULL) { printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n",dev->name) ; olympic_priv->olympic_stats.rx_dropped++ ; /* Update counters even though we don't transfer the frame */ olympic_priv->rx_ring_last_received += i ; olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1) ; } else { skb->dev = dev ; /* Optimise based upon number of buffers used. If only one buffer is used we can simply swap the buffers around. If more than one then we must use the new buffer and copy the information first. Ideally all frames would be in a single buffer, this can be tuned by altering the buffer size. */ if (buffer_cnt==1) { olympic_priv->rx_ring_last_received++ ; olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1); rx_ring_last_received = olympic_priv->rx_ring_last_received ; skb2=olympic_priv->rx_ring_skb[rx_ring_last_received] ; skb_put(skb2,length); skb2->protocol = tr_type_trans(skb2,dev); olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer=virt_to_bus(skb->data); olympic_priv->olympic_rx_ring[rx_ring_last_received].res_length = olympic_priv->pkt_buf_sz ; olympic_priv->rx_ring_skb[rx_ring_last_received] = skb ; netif_rx(skb2) ; } else { do { /* Walk the buffers */ olympic_priv->rx_ring_last_received++ ; olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1); rx_ring_last_received = olympic_priv->rx_ring_last_received ; rx_desc = &(olympic_priv->olympic_rx_ring[rx_ring_last_received]); cpy_length = (i == 1 ? frag_len : rx_desc->res_length); memcpy(skb_put(skb, cpy_length), bus_to_virt(rx_desc->buffer), cpy_length) ; } while (--i) ; skb->protocol = tr_type_trans(skb,dev); netif_rx(skb) ; } olympic_priv->olympic_stats.rx_packets++ ; olympic_priv->olympic_stats.rx_bytes += length ; } /* if skb == null */ } /* If status & 0x3b */ } /*if buffercnt & 0xC */ } /* if length != 0 */ /* I wonder if we could release rxenq after each buffer read. Updating at the end will be faster as it will only require one pci bus write, but releasing each buffer individually will stop buffers running out. Has anybody got 100Mbps TR to test this out ?? */ *(__u16 *)(olympic_mmio+RXENQ)=(((*(__u16 *)(olympic_mmio+RXENQ)) & 0x8000) ^ 0x8000) | buffer_cnt ; } static void olympic_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct device *dev=dev_id; struct olympic_private *olympic_priv=(struct olympic_private *)dev->priv; __u8 *olympic_mmio=olympic_priv->olympic_mmio; __u32 sisr; __u8 in_loop = 0 ; sisr=*(__u32 *)(olympic_mmio+SISR_RR); /* This automatically resets sisr */ /* Only run through code if it is an interrupt we want to deal with. This allows interrupt code to be bypassed if necessary for debuging purpose */ if (!sisr) /* Interrupt is not for us */ return ; if (!(sisr & SISR_MI)) { /* Master Bit Not Set, bogus interrupt */ #if OLYMPIC_DEBUG printk(KERN_WARNING "%s: bogus interrupts, sisr = %x, irq = %x\n",dev->name, sisr, irq) ; #endif return ; } ; if (sisr & (SISR_SRB_REPLY | SISR_TX1_EOF | SISR_RX_STATUS | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_RX_NOBUF)) { if(sisr & SISR_SRB_REPLY) { #if OLYMPIC_DEBUG printk(KERN_INFO "SRB reply posted\n"); #endif if(olympic_priv->srb_queued) { wake_up_interruptible(&olympic_priv->srb_wait); } olympic_priv->srb_queued=0; } /* SISR_SRB_REPLY */ if (sisr & SISR_TX1_EOF) { olympic_priv->tx_ring_last_status++; olympic_priv->tx_ring_last_status &= (OLYMPIC_TX_RING_SIZE-1); olympic_priv->free_tx_ring_entries++; #if 0 printk("xmit status: %x\n",olympic_priv->olympic_tx_status_ring[olympic_priv->tx_ring_last_status].status); #endif olympic_priv->olympic_stats.tx_bytes += olympic_priv->tx_ring_skb[olympic_priv->tx_ring_last_status]->len; olympic_priv->olympic_stats.tx_packets++ ; dev_kfree_skb(olympic_priv->tx_ring_skb[olympic_priv->tx_ring_last_status]); olympic_priv->olympic_tx_ring[olympic_priv->tx_ring_last_status].buffer=0xdeadbeef; olympic_priv->olympic_tx_status_ring[olympic_priv->tx_ring_last_status].status=0; if(dev->tbusy) { dev->tbusy=0; mark_bh(NET_BH); } } /* SISR_TX1_EOF */ if (sisr & SISR_RX_STATUS) { olympic_rx(dev); } /* SISR_RX_STATUS */ if (sisr & SISR_ADAPTER_CHECK) { printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, we're in trouble now\n", dev->name); } /* SISR_ADAPTER_CHECK */ if (sisr & SISR_ASB_FREE) { /* Wake up anything that is waiting for the asb response */ if (olympic_priv->asb_queued) { wake_up_interruptible(&olympic_priv->asb_wait); } /* Docs say we must deal with this no matter what, so if asb_queued = 0 we still have to do something */ olympic_priv->asb_queued = 0 ; } /* SISR_ASB_FREE */ if (sisr & SISR_ARB_CMD) { olympic_arb_cmd(dev) ; } /* SISR_ARB_CMD */ if (sisr & SISR_TRB_REPLY) { /* Wake up anything that is waiting for the trb response */ if (olympic_priv->trb_queued) { wake_up_interruptible(&olympic_priv->trb_wait); } olympic_priv->trb_queued = 0 ; } /* SISR_TRB_REPLY */ if (sisr & SISR_RX_NOBUF) { /* According to the documentation, we don't have to do anything, but trapping it keeps it out of /var/log/messages. */ } /* SISR_RX_NOBUF */ } else { printk(KERN_WARNING "%s: Unexpected interrupt: %x, in loop: %d \n",dev->name, sisr,in_loop); printk(KERN_WARNING "%s: SISR_MASK: %x\n",dev->name, *(__u32 *)(olympic_mmio+SISR_MASK)); } /* One if the interrupts we want */ *(__u32 *)(olympic_mmio+SISR_MASK_SUM)=SISR_MI; } static int olympic_xmit(struct sk_buff *skb, struct device *dev) { struct olympic_private *olympic_priv=(struct olympic_private *)dev->priv; __u8 *olympic_mmio=olympic_priv->olympic_mmio; if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { return 1; } #if 0 printk("olympic_priv->free_tx_ring_entries: %x\n",olympic_priv->free_tx_ring_entries); #endif if(olympic_priv->free_tx_ring_entries) { olympic_priv->olympic_tx_ring[olympic_priv->tx_ring_free].buffer=virt_to_bus(skb->data); olympic_priv->olympic_tx_ring[olympic_priv->tx_ring_free].status_length=skb->len | (0x80000000); olympic_priv->tx_ring_skb[olympic_priv->tx_ring_free]=skb; olympic_priv->free_tx_ring_entries--; olympic_priv->tx_ring_free++; olympic_priv->tx_ring_free&=~OLYMPIC_TX_RING_SIZE; *(__u16 *)(olympic_mmio+TXENQ_1)=(((*(__u16 *)(olympic_mmio+TXENQ_1)) & 0x8000) ^ 0x8000) | 1; dev->tbusy=0; return 0; } else return 1; } static int olympic_close(struct device *dev) { struct olympic_private *olympic_priv=(struct olympic_private *)dev->priv; __u8 *olympic_mmio=olympic_priv->olympic_mmio,*srb; unsigned long flags; int i; *(__u32 *)(olympic_mmio+LAPA)=olympic_priv->srb; srb=olympic_priv->olympic_lap + (olympic_priv->srb & (~0xf800)); srb[0]= SRB_CLOSE_ADAPTER ; srb[1]= 0; srb[2]= OLYMPIC_CLEAR_RET_CODE ; save_flags(flags); cli(); olympic_priv->srb_queued=1; *(__u32 *)(olympic_mmio+LISR_SUM)=LISR_SRB_CMD; while(olympic_priv->srb_queued) { interruptible_sleep_on_timeout(&olympic_priv->srb_wait, jiffies+60*HZ); if(signal_pending(current)) { printk(KERN_WARNING "%s: SRB timed out.\n", dev->name); printk(KERN_WARNING "SISR=%x MISR=%x\n", *(__u32 *)(olympic_mmio+SISR), *(__u32 *)(olympic_mmio+LISR)); olympic_priv->srb_queued=0; break; } } restore_flags(flags) ; olympic_priv->rx_status_last_received++; olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; for(i=0;irx_ring_skb[olympic_priv->rx_status_last_received]); olympic_priv->rx_status_last_received++; olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; } /* reset tx/rx fifo's and busmaster logic */ *(__u32 *)(olympic_mmio+BCTL)|=(3<<13); udelay(1); *(__u32 *)(olympic_mmio+BCTL)&=~(3<<13); #if OLYMPIC_DEBUG printk("srb(%p): ",srb); for(i=0;i<4;i++) printk("%x ",srb[i]); printk("\n"); #endif dev->start = 0; free_irq(dev->irq,dev); MOD_DEC_USE_COUNT ; return 0; } /* 5/3/99 - M.Phillips * For now we are just going to turn promiscuous on and off. * This copies all LLC frames, except those with routing information. * We could turn these options on and get all LLC frames and all MAC * frames, but the traffic would be incredible. (Be interesting to see if * the kernel could keep up though :) * * Ignore the return value for the moment, trying to call sleep_on * is producing a kernel oops. */ static void olympic_set_rx_mode(struct device *dev) { struct olympic_private *olympic_priv = (struct olympic_private *) dev->priv ; __u8 *olympic_mmio = olympic_priv->olympic_mmio ; __u8 options = 0 ; __u8 *srb; unsigned long flags ; *(__u32 *)(olympic_mmio+LAPA)=olympic_priv->srb; srb=olympic_priv->olympic_lap + (olympic_priv->srb & (~0xf800)); if (dev->flags&IFF_PROMISC) options |= (1<<6) ; else options &= ~(1<<6) ; /* Only issue the srb if there is a change in options */ if (!(options ^ olympic_priv->olympic_copy_all_options)) return ; /* Now to issue the srb command to alter the copy.all.options */ srb[0] = SRB_MODIFY_RECEIVE_OPTIONS ; srb[1] = 0 ; srb[2] = OLYMPIC_CLEAR_RET_CODE ; srb[3] = 0 ; srb[4] = olympic_priv->olympic_receive_options ; srb[5] = options ; save_flags(flags) ; cli() ; #if 1 olympic_priv->srb_queued=1; #endif *(__u32 *)(olympic_mmio+LISR_SUM)=LISR_SRB_CMD; #if 1 while(olympic_priv->srb_queued) { interruptible_sleep_on_timeout(&olympic_priv->srb_wait, jiffies+60*HZ); if(signal_pending(current)) { printk(KERN_WARNING "%s: SRB timed out.\n", dev->name); printk(KERN_WARNING "SISR=%x MISR=%x\n", *(__u32 *)(olympic_mmio+SISR), *(__u32 *)(olympic_mmio+LISR)); olympic_priv->srb_queued=0; break; } } #endif restore_flags(flags); /* Read the return code */ switch (srb[2]) { case 0x01: printk(KERN_WARNING "%s: Unrecognized srb command\n",dev->name) ; break ; case 0x04: printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n",dev->name); break ; default: printk(KERN_INFO "%s: Receive Options modified \n",dev->name) ; olympic_priv->olympic_copy_all_options = options ; break ; } /* switch */ } static struct net_device_stats * olympic_get_stats(struct device *dev) { struct olympic_private *olympic_priv ; olympic_priv=(struct olympic_private *) dev->priv; return (struct net_device_stats *) &olympic_priv->olympic_stats; } /* olympic_arb_cmd - M.Phillips 4/14/99 4/26/99 - M.Phillips Lan.change.status works well, just need to flesh out a couple of responses. Receive.data - I'm not sure about this at all, esp. the walking the buffers part. */ static void olympic_arb_cmd(struct device *dev) { struct olympic_private *olympic_priv = (struct olympic_private *) dev->priv; __u8 *olympic_mmio=olympic_priv->olympic_mmio; __u8 *arb_block, *asb_block ; __u16 frame_len, header_len,i; struct sk_buff *mac_frame ; unsigned char *mac_frame_counter ; /* unsigned char in sk_buff definition, to stop compiler warning */ struct mac_receive_buffer *buf_ptr ; __u16 lan_status = 0, lan_status_diff ; /* Initialize to stop compiler warning */ __u8 fdx_prot_error ; arb_block = (__u8 *)(olympic_priv->olympic_lap + olympic_priv->arb) ; asb_block = (__u8 *)(olympic_priv->olympic_lap + olympic_priv->asb) ; if (arb_block[0] == ARB_RECEIVE_DATA) { /* Receive.data, MAC frames */ header_len = *(__u16 *)(arb_block +8) ; /* 802.5 Token-Ring Header Length */ frame_len = *(__u16 *)(arb_block + 10) ; mac_frame = dev_alloc_skb(PKT_BUF_SZ) ; /* Wrong, what is the max MAC frame size ?? */ /* Walk the buffer chain, creating the frame */ /* Commented out for the first public release, I wrote this in a hurry and haven't had chance to test any of it yet - MLP */ buf_ptr = (struct mac_receive_buffer *)(olympic_priv->olympic_lap + *(__u16 *)(arb_block + 6)) ; mac_frame_counter = mac_frame->data ; do { for (i = 0; i< buf_ptr->buffer_length; i++, mac_frame_counter++) { *mac_frame_counter = *(buf_ptr->frame_data+i); } } while ((buf_ptr = (struct mac_receive_buffer *)(olympic_priv->olympic_lap + buf_ptr->next)) != 0); /* Now we have the mac frame, need to decide what to do with it. Do the tr functions deal with this ? Just pass it up the protocol layers for now */ mac_frame->dev = dev ; skb_put(mac_frame,frame_len); mac_frame->protocol = tr_type_trans(mac_frame,dev); netif_rx(mac_frame) ; /* You still need to do all the steps below though otherwise the card will think * that we haven't dealt with the frame. */ /* Set LISR Bit 1 */ *( __u32 *)(olympic_priv->olympic_lap + LISR_SUM) = LISR_ARB_FREE ; /* Is the ASB free ? */ if (!(*(__u32 *)(olympic_priv->olympic_mmio + SISR) & SISR_ASB_FREE)) { /* Request ASB */ olympic_priv->asb_queued = 1 ; *( __u32 *)(olympic_priv->olympic_mmio+LISR_SUM) = LISR_ASB_FREE_REQ ; while (olympic_priv->asb_queued) { interruptible_sleep_on_timeout(&olympic_priv->asb_wait, jiffies+60*HZ) ; if (signal_pending(current)) { printk(KERN_WARNING "%s: ASB Timed out.\n", dev->name); olympic_priv->asb_queued=0 ; break ; } } } asb_block[0] = ASB_RECEIVE_DATA ; /* Receive data */ asb_block[2] = OLYMPIC_CLEAR_RET_CODE ; /* Necessary ?? */ asb_block[6] = arb_block[6] ; /* Must send the address back to the adapter */ asb_block[7] = arb_block[7] ; /* To let it know we have dealt with the data */ *( __u32 *)(olympic_priv->olympic_mmio+LISR_SUM) = LISR_ASB_REPLY ; /* Wait for asb response to ensure that the adapter is happy with the information we sent it */ olympic_priv->asb_queued = 1 ; while (olympic_priv->asb_queued) { interruptible_sleep_on_timeout(&olympic_priv->asb_wait, jiffies+60*HZ); if (signal_pending(current)) { printk(KERN_WARNING "%s: ASB Timed out.\n", dev->name); olympic_priv->asb_queued = 0 ; break ; } } /* According to the documentation any return code except an 0xFF means we are in big trouble. What do we do ? Reset the adapter, halt it ? */ switch (asb_block[2]) { case 0x01: printk(KERN_WARNING "%s: Unrecognized command code \n", dev->name); break ; case 0x26: printk(KERN_WARNING "%s: Unrecognized buffer address \n", dev->name); break ; case 0xFF: /* Valid response, everything should be ok again */ break ; default: printk(KERN_WARNING "%s: Invalid return code in asb\n",dev->name); break ; } } else if (arb_block[0] == ARB_LAN_CHANGE_STATUS) { /* Lan.change.status */ lan_status = *(__u16 *)(arb_block+6); fdx_prot_error = arb_block[8] ; /* Issue ARB Free */ *(__u32 *)(olympic_priv->olympic_mmio+LISR_SUM) = LISR_ARB_FREE ; lan_status_diff = olympic_priv->olympic_lan_status ^ lan_status ; if (lan_status_diff & (LSC_LWF | LSC_ARW | LSC_FPE | LSC_RR) ) { if (lan_status_diff & LSC_LWF) printk(KERN_WARNING "%s: Short circuit detected on the lobe\n",dev->name); if (lan_status_diff & LSC_ARW) printk(KERN_WARNING "%s: Auto removal error\n",dev->name); if (lan_status_diff & LSC_FPE) printk(KERN_WARNING "%s: FDX Protocol Error\n",dev->name); if (lan_status_diff & LSC_RR) printk(KERN_WARNING "%s: Force remove MAC frame received\n",dev->name); /* Adapter has been closed by the hardware */ /* reset tx/rx fifo's and busmaster logic */ *(__u32 *)(olympic_mmio+BCTL)|=(3<<13); udelay(1); *(__u32 *)(olympic_mmio+BCTL)&=~(3<<13); dev->tbusy = 1 ; dev->interrupt = 1 ; dev->start = 0 ; olympic_priv->srb = *(__u16 *)(olympic_priv->olympic_lap + LAPWWO) ; free_irq(dev->irq,dev); printk(KERN_WARNING "%s: Adapter has been closed \n", dev->name) ; } /* If serious error */ if (lan_status_diff & LSC_SIG_LOSS) printk(KERN_WARNING "%s: No receive signal detected \n", dev->name) ; if (lan_status_diff & LSC_HARD_ERR) printk(KERN_INFO "%s: Beaconing \n",dev->name); if (lan_status_diff & LSC_SOFT_ERR) printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame \n",dev->name); if (lan_status_diff & LSC_TRAN_BCN) printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n",dev->name); if (lan_status_diff & LSC_CO) printk(KERN_WARNING "%s: Counter Overflow \n", dev->name); /* READ.LOG command should be issued */ if (lan_status_diff & LSC_SS) printk(KERN_INFO "%s: Single Station on the ring \n", dev->name); if (lan_status_diff & LSC_RING_REC) printk(KERN_INFO "%s: Ring recovery ongoing\n",dev->name); /* Has a different meaning in FDX mode */ if (lan_status_diff & LSC_SR_CO) printk(KERN_WARNING "%s: Source routing counters overflow\n", dev->name); /* If we need source routing, must issue a READ.SR.COUNTERS */ if (lan_status_diff & LSC_FDX_MODE) printk(KERN_INFO "%s: Operating in FDX mode\n",dev->name); olympic_priv->olympic_lan_status = lan_status ; } /* Lan.change.status */ else printk(KERN_WARNING "%s: Unknown arb command \n", dev->name); } /* 4/26/99 M.Phillips Change Mtu routine. We can limit the mtu to the maximum available for Token Ring 4 Mbps = 4500 16, 100 Mbps = 18000 4/28/99 - Change to alter mtu size, but doesn't alter buffers. 4/29/99 - Now alters buffer size as well (alters pkt_buf_sz which changes the rx buffers when a new buffer is received. */ static int olympic_change_mtu(struct device *dev, int mtu) { struct olympic_private *olympic_priv = (struct olympic_private *) dev->priv; __u16 max_mtu ; if (olympic_priv->olympic_ring_speed == 4) max_mtu = 4500 ; else max_mtu = 18000 ; if (mtu > max_mtu) return -EINVAL ; if (mtu < 100) return -EINVAL ; dev->mtu = mtu ; olympic_priv->pkt_buf_sz = mtu + TR_HLEN ; return 0 ; } #ifdef MODULE static struct device* dev_olympic[OLYMPIC_MAX_ADAPTERS]; int init_module(void) { int i; for (i = 0; (iinit = &olympic_probe; if (register_trdev(dev_olympic[i]) != 0) { kfree_s(dev_olympic[i], sizeof(struct device)); dev_olympic[i] = NULL; if (i == 0) { printk("Olympic: No IBM PCI Token Ring cards found in system.\n"); return -EIO; } else { printk("Olympic: %d IBM PCI Token Ring card(s) found in system.\n",i) ; return 0; } } } return 0; } void cleanup_module(void) { int i; for (i = 0; i < OLYMPIC_MAX_ADAPTERS; i++) if (dev_olympic[i]) { unregister_trdev(dev_olympic[i]); release_region(dev_olympic[i]->base_addr, OLYMPIC_IO_SPACE); kfree_s(dev_olympic[i]->priv, sizeof(struct olympic_private)); kfree_s(dev_olympic[i], sizeof(struct device)); dev_olympic[i] = NULL; } } #endif /* MODULE */