#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _IBM_LANA_DRIVER_ #include "ibmlana.h" #undef DEBUG #define DRV_NAME "ibmlana" /* ------------------------------------------------------------------------ * global static data - not more since we can handle multiple boards and * have to pack all state info into the device struct! * ------------------------------------------------------------------------ */ static char *MediaNames[Media_Count] = { "10BaseT", "10Base5", "Unknown", "10Base2" }; /* ------------------------------------------------------------------------ * private subfunctions * ------------------------------------------------------------------------ */ #ifdef DEBUG /* dump all registers */ static void dumpregs(struct net_device *dev) { int z; for (z = 0; z < 160; z += 2) { if (!(z & 15)) printk("REGS: %04x:", z); printk(" %04x", inw(dev->base_addr + z)); if ((z & 15) == 14) printk("\n"); } } /* dump parts of shared memory - only needed during debugging */ static void dumpmem(struct net_device *dev, u32 start, u32 len) { ibmlana_priv *priv = netdev_priv(dev); int z; printk("Address %04x:\n", start); for (z = 0; z < len; z++) { if ((z & 15) == 0) printk("%04x:", z); printk(" %02x", readb(priv->base + start + z)); if ((z & 15) == 15) printk("\n"); } if ((z & 15) != 0) printk("\n"); } /* print exact time - ditto */ static void PrTime(void) { struct timeval tv; do_gettimeofday(&tv); printk("%9d:%06d: ", (int) tv.tv_sec, (int) tv.tv_usec); } #endif /* DEBUG */ /* deduce resources out of POS registers */ static void getaddrs(int slot, int *base, int *memlen, int *iobase, int *irq, ibmlana_medium * medium) { u_char pos0, pos1; pos0 = mca_read_stored_pos(slot, 2); pos1 = mca_read_stored_pos(slot, 3); *base = 0xc0000 + ((pos1 & 0xf0) << 9); *memlen = (pos1 & 0x01) ? 0x8000 : 0x4000; *iobase = (pos0 & 0xe0) << 7; switch (pos0 & 0x06) { case 0: *irq = 5; break; case 2: *irq = 15; break; case 4: *irq = 10; break; case 6: *irq = 11; break; } *medium = (pos0 & 0x18) >> 3; } /* wait on register value with mask and timeout */ static int wait_timeout(struct net_device *dev, int regoffs, u16 mask, u16 value, int timeout) { unsigned long fin = jiffies + timeout; while (time_before(jiffies,fin)) if ((inw(dev->base_addr + regoffs) & mask) == value) return 1; return 0; } /* reset the whole board */ static void ResetBoard(struct net_device *dev) { unsigned char bcmval; /* read original board control value */ bcmval = inb(dev->base_addr + BCMREG); /* set reset bit for a while */ bcmval |= BCMREG_RESET; outb(bcmval, dev->base_addr + BCMREG); udelay(10); bcmval &= ~BCMREG_RESET; outb(bcmval, dev->base_addr + BCMREG); /* switch over to RAM again */ bcmval |= BCMREG_RAMEN | BCMREG_RAMWIN; outb(bcmval, dev->base_addr + BCMREG); } /* calculate RAM layout & set up descriptors in RAM */ static void InitDscrs(struct net_device *dev) { ibmlana_priv *priv = netdev_priv(dev); u32 addr, baddr, raddr; int z; tda_t tda; rda_t rda; rra_t rra; /* initialize RAM */ memset_io(priv->base, 0xaa, dev->mem_start - dev->mem_start); /* setup n TX descriptors - independent of RAM size */ priv->tdastart = addr = 0; priv->txbufstart = baddr = sizeof(tda_t) * TXBUFCNT; for (z = 0; z < TXBUFCNT; z++) { tda.status = 0; tda.config = 0; tda.length = 0; tda.fragcount = 1; tda.startlo = baddr; tda.starthi = 0; tda.fraglength = 0; if (z == TXBUFCNT - 1) tda.link = priv->tdastart; else tda.link = addr + sizeof(tda_t); tda.link |= 1; memcpy_toio(priv->base + addr, &tda, sizeof(tda_t)); addr += sizeof(tda_t); baddr += PKTSIZE; } /* calculate how many receive buffers fit into remaining memory */ priv->rxbufcnt = (dev->mem_end - dev->mem_start - baddr) / (sizeof(rra_t) + sizeof(rda_t) + PKTSIZE); /* calculate receive addresses */ priv->rrastart = raddr = priv->txbufstart + (TXBUFCNT * PKTSIZE); priv->rdastart = addr = priv->rrastart + (priv->rxbufcnt * sizeof(rra_t)); priv->rxbufstart = baddr = priv->rdastart + (priv->rxbufcnt * sizeof(rda_t)); for (z = 0; z < priv->rxbufcnt; z++) { rra.startlo = baddr; rra.starthi = 0; rra.cntlo = PKTSIZE >> 1; rra.cnthi = 0; memcpy_toio(priv->base + raddr, &rra, sizeof(rra_t)); rda.status = 0; rda.length = 0; rda.startlo = 0; rda.starthi = 0; rda.seqno = 0; if (z < priv->rxbufcnt - 1) rda.link = addr + sizeof(rda_t); else rda.link = 1; rda.inuse = 1; memcpy_toio(priv->base + addr, &rda, sizeof(rda_t)); baddr += PKTSIZE; raddr += sizeof(rra_t); addr += sizeof(rda_t); } /* initialize current pointers */ priv->nextrxdescr = 0; priv->lastrxdescr = priv->rxbufcnt - 1; priv->nexttxdescr = 0; priv->currtxdescr = 0; priv->txusedcnt = 0; memset(priv->txused, 0, sizeof(priv->txused)); } /* set up Rx + Tx descriptors in SONIC */ static int InitSONIC(struct net_device *dev) { ibmlana_priv *priv = netdev_priv(dev); /* set up start & end of resource area */ outw(0, SONIC_URRA); outw(priv->rrastart, dev->base_addr + SONIC_RSA); outw(priv->rrastart + (priv->rxbufcnt * sizeof(rra_t)), dev->base_addr + SONIC_REA); outw(priv->rrastart, dev->base_addr + SONIC_RRP); outw(priv->rrastart, dev->base_addr + SONIC_RWP); /* set EOBC so that only one packet goes into one buffer */ outw((PKTSIZE - 4) >> 1, dev->base_addr + SONIC_EOBC); /* let SONIC read the first RRA descriptor */ outw(CMDREG_RRRA, dev->base_addr + SONIC_CMDREG); if (!wait_timeout(dev, SONIC_CMDREG, CMDREG_RRRA, 0, 2)) { printk(KERN_ERR "%s: SONIC did not respond on RRRA command - giving up.", dev->name); return 0; } /* point SONIC to the first RDA */ outw(0, dev->base_addr + SONIC_URDA); outw(priv->rdastart, dev->base_addr + SONIC_CRDA); /* set upper half of TDA address */ outw(0, dev->base_addr + SONIC_UTDA); return 1; } /* stop SONIC so we can reinitialize it */ static void StopSONIC(struct net_device *dev) { /* disable interrupts */ outb(inb(dev->base_addr + BCMREG) & (~BCMREG_IEN), dev->base_addr + BCMREG); outb(0, dev->base_addr + SONIC_IMREG); /* reset the SONIC */ outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG); udelay(10); outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG); } /* initialize card and SONIC for proper operation */ static void putcam(camentry_t * cams, int *camcnt, char *addr) { camentry_t *pcam = cams + (*camcnt); u8 *uaddr = (u8 *) addr; pcam->index = *camcnt; pcam->addr0 = (((u16) uaddr[1]) << 8) | uaddr[0]; pcam->addr1 = (((u16) uaddr[3]) << 8) | uaddr[2]; pcam->addr2 = (((u16) uaddr[5]) << 8) | uaddr[4]; (*camcnt)++; } static void InitBoard(struct net_device *dev) { ibmlana_priv *priv = netdev_priv(dev); int camcnt; camentry_t cams[16]; u32 cammask; struct dev_mc_list *mcptr; u16 rcrval; /* reset the SONIC */ outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG); udelay(10); /* clear all spurious interrupts */ outw(inw(dev->base_addr + SONIC_ISREG), dev->base_addr + SONIC_ISREG); /* set up the SONIC's bus interface - constant for this adapter - must be done while the SONIC is in reset */ outw(DCREG_USR1 | DCREG_USR0 | DCREG_WC1 | DCREG_DW32, dev->base_addr + SONIC_DCREG); outw(0, dev->base_addr + SONIC_DCREG2); /* remove reset form the SONIC */ outw(0, dev->base_addr + SONIC_CMDREG); udelay(10); /* data sheet requires URRA to be programmed before setting up the CAM contents */ outw(0, dev->base_addr + SONIC_URRA); /* program the CAM entry 0 to the device address */ camcnt = 0; putcam(cams, &camcnt, dev->dev_addr); /* start putting the multicast addresses into the CAM list. Stop if it is full. */ for (mcptr = dev->mc_list; mcptr != NULL; mcptr = mcptr->next) { putcam(cams, &camcnt, mcptr->dmi_addr); if (camcnt == 16) break; } /* calculate CAM mask */ cammask = (1 << camcnt) - 1; /* feed CDA into SONIC, initialize RCR value (always get broadcasts) */ memcpy_toio(priv->base, cams, sizeof(camentry_t) * camcnt); memcpy_toio(priv->base + (sizeof(camentry_t) * camcnt), &cammask, sizeof(cammask)); #ifdef DEBUG printk("CAM setup:\n"); dumpmem(dev, 0, sizeof(camentry_t) * camcnt + sizeof(cammask)); #endif outw(0, dev->base_addr + SONIC_CAMPTR); outw(camcnt, dev->base_addr + SONIC_CAMCNT); outw(CMDREG_LCAM, dev->base_addr + SONIC_CMDREG); if (!wait_timeout(dev, SONIC_CMDREG, CMDREG_LCAM, 0, 2)) { printk(KERN_ERR "%s:SONIC did not respond on LCAM command - giving up.", dev->name); return; } else { /* clear interrupt condition */ outw(ISREG_LCD, dev->base_addr + SONIC_ISREG); #ifdef DEBUG printk("Loading CAM done, address pointers %04x:%04x\n", inw(dev->base_addr + SONIC_URRA), inw(dev->base_addr + SONIC_CAMPTR)); { int z; printk("\n-->CAM: PTR %04x CNT %04x\n", inw(dev->base_addr + SONIC_CAMPTR), inw(dev->base_addr + SONIC_CAMCNT)); outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG); for (z = 0; z < camcnt; z++) { outw(z, dev->base_addr + SONIC_CAMEPTR); printk("Entry %d: %04x %04x %04x\n", z, inw(dev->base_addr + SONIC_CAMADDR0), inw(dev->base_addr + SONIC_CAMADDR1), inw(dev->base_addr + SONIC_CAMADDR2)); } outw(0, dev->base_addr + SONIC_CMDREG); } #endif } rcrval = RCREG_BRD | RCREG_LB_NONE; /* if still multicast addresses left or ALLMULTI is set, set the multicast enable bit */ if ((dev->flags & IFF_ALLMULTI) || (mcptr != NULL)) rcrval |= RCREG_AMC; /* promiscous mode ? */ if (dev->flags & IFF_PROMISC) rcrval |= RCREG_PRO; /* program receive mode */ outw(rcrval, dev->base_addr + SONIC_RCREG); #ifdef DEBUG printk("\nRCRVAL: %04x\n", rcrval); #endif /* set up descriptors in shared memory + feed them into SONIC registers */ InitDscrs(dev); if (!InitSONIC(dev)) return; /* reset all pending interrupts */ outw(0xffff, dev->base_addr + SONIC_ISREG); /* enable transmitter + receiver interrupts */ outw(CMDREG_RXEN, dev->base_addr + SONIC_CMDREG); outw(IMREG_PRXEN | IMREG_RBEEN | IMREG_PTXEN | IMREG_TXEREN, dev->base_addr + SONIC_IMREG); /* turn on card interrupts */ outb(inb(dev->base_addr + BCMREG) | BCMREG_IEN, dev->base_addr + BCMREG); #ifdef DEBUG printk("Register dump after initialization:\n"); dumpregs(dev); #endif } /* start transmission of a descriptor */ static void StartTx(struct net_device *dev, int descr) { ibmlana_priv *priv = netdev_priv(dev); int addr; addr = priv->tdastart + (descr * sizeof(tda_t)); /* put descriptor address into SONIC */ outw(addr, dev->base_addr + SONIC_CTDA); /* trigger transmitter */ priv->currtxdescr = descr; outw(CMDREG_TXP, dev->base_addr + SONIC_CMDREG); } /* ------------------------------------------------------------------------ * interrupt handler(s) * ------------------------------------------------------------------------ */ /* receive buffer area exhausted */ static void irqrbe_handler(struct net_device *dev) { ibmlana_priv *priv = netdev_priv(dev); /* point the SONIC back to the RRA start */ outw(priv->rrastart, dev->base_addr + SONIC_RRP); outw(priv->rrastart, dev->base_addr + SONIC_RWP); } /* receive interrupt */ static void irqrx_handler(struct net_device *dev) { ibmlana_priv *priv = netdev_priv(dev); rda_t rda; u32 rdaaddr, lrdaaddr; /* loop until ... */ while (1) { /* read descriptor that was next to be filled by SONIC */ rdaaddr = priv->rdastart + (priv->nextrxdescr * sizeof(rda_t)); lrdaaddr = priv->rdastart + (priv->lastrxdescr * sizeof(rda_t)); memcpy_fromio(&rda, priv->base + rdaaddr, sizeof(rda_t)); /* iron out upper word halves of fields we use - SONIC will duplicate bits 0..15 to 16..31 */ rda.status &= 0xffff; rda.length &= 0xffff; rda.startlo &= 0xffff; /* stop if the SONIC still owns it, i.e. there is no data for us */ if (rda.inuse) break; /* good packet? */ else if (rda.status & RCREG_PRX) { struct sk_buff *skb; /* fetch buffer */ skb = dev_alloc_skb(rda.length + 2); if (skb == NULL) priv->stat.rx_dropped++; else { /* copy out data */ memcpy_fromio(skb_put(skb, rda.length), priv->base + rda.startlo, rda.length); /* set up skb fields */ skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; /* bookkeeping */ dev->last_rx = jiffies; priv->stat.rx_packets++; priv->stat.rx_bytes += rda.length; /* pass to the upper layers */ netif_rx(skb); } } /* otherwise check error status bits and increase statistics */ else { priv->stat.rx_errors++; if (rda.status & RCREG_FAER) priv->stat.rx_frame_errors++; if (rda.status & RCREG_CRCR) priv->stat.rx_crc_errors++; } /* descriptor processed, will become new last descriptor in queue */ rda.link = 1; rda.inuse = 1; memcpy_toio(priv->base + rdaaddr, &rda, sizeof(rda_t)); /* set up link and EOL = 0 in currently last descriptor. Only write the link field since the SONIC may currently already access the other fields. */ memcpy_toio(priv->base + lrdaaddr + 20, &rdaaddr, 4); /* advance indices */ priv->lastrxdescr = priv->nextrxdescr; if ((++priv->nextrxdescr) >= priv->rxbufcnt) priv->nextrxdescr = 0; } } /* transmit interrupt */ static void irqtx_handler(struct net_device *dev) { ibmlana_priv *priv = netdev_priv(dev); tda_t tda; /* fetch descriptor (we forgot the size ;-) */ memcpy_fromio(&tda, priv->base + priv->tdastart + (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t)); /* update statistics */ priv->stat.tx_packets++; priv->stat.tx_bytes += tda.length; /* update our pointers */ priv->txused[priv->currtxdescr] = 0; priv->txusedcnt--; /* if there are more descriptors present in RAM, start them */ if (priv->txusedcnt > 0) StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT); /* tell the upper layer we can go on transmitting */ netif_wake_queue(dev); } static void irqtxerr_handler(struct net_device *dev) { ibmlana_priv *priv = netdev_priv(dev); tda_t tda; /* fetch descriptor to check status */ memcpy_fromio(&tda, priv->base + priv->tdastart + (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t)); /* update statistics */ priv->stat.tx_errors++; if (tda.status & (TCREG_NCRS | TCREG_CRSL)) priv->stat.tx_carrier_errors++; if (tda.status & TCREG_EXC) priv->stat.tx_aborted_errors++; if (tda.status & TCREG_OWC) priv->stat.tx_window_errors++; if (tda.status & TCREG_FU) priv->stat.tx_fifo_errors++; /* update our pointers */ priv->txused[priv->currtxdescr] = 0; priv->txusedcnt--; /* if there are more descriptors present in RAM, start them */ if (priv->txusedcnt > 0) StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT); /* tell the upper layer we can go on transmitting */ netif_wake_queue(dev); } /* general interrupt entry */ static irqreturn_t irq_handler(int irq, void *device) { struct net_device *dev = (struct net_device *) device; u16 ival; /* in case we're not meant... */ if (!(inb(dev->base_addr + BCMREG) & BCMREG_IPEND)) return IRQ_NONE; /* loop through the interrupt bits until everything is clear */ while (1) { ival = inw(dev->base_addr + SONIC_ISREG); if (ival & ISREG_RBE) { irqrbe_handler(dev); outw(ISREG_RBE, dev->base_addr + SONIC_ISREG); } if (ival & ISREG_PKTRX) { irqrx_handler(dev); outw(ISREG_PKTRX, dev->base_addr + SONIC_ISREG); } if (ival & ISREG_TXDN) { irqtx_handler(dev); outw(ISREG_TXDN, dev->base_addr + SONIC_ISREG); } if (ival & ISREG_TXER) { irqtxerr_handler(dev); outw(ISREG_TXER, dev->base_addr + SONIC_ISREG); } break; } return IRQ_HANDLED; } /* ------------------------------------------------------------------------ * driver methods * ------------------------------------------------------------------------ */ /* MCA info */ static int ibmlana_getinfo(char *buf, int slot, void *d) { int len = 0, i; struct net_device *dev = (struct net_device *) d; ibmlana_priv *priv; /* can't say anything about an uninitialized device... */ if (dev == NULL) return len; priv = netdev_priv(dev); /* print info */ len += sprintf(buf + len, "IRQ: %d\n", priv->realirq); len += sprintf(buf + len, "I/O: %#lx\n", dev->base_addr); len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start, dev->mem_end - 1); len += sprintf(buf + len, "Transceiver: %s\n", MediaNames[priv->medium]); len += sprintf(buf + len, "Device: %s\n", dev->name); len += sprintf(buf + len, "MAC address:"); for (i = 0; i < 6; i++) len += sprintf(buf + len, " %02x", dev->dev_addr[i]); buf[len++] = '\n'; buf[len] = 0; return len; } /* open driver. Means also initialization and start of LANCE */ static int ibmlana_open(struct net_device *dev) { int result; ibmlana_priv *priv = netdev_priv(dev); /* register resources - only necessary for IRQ */ result = request_irq(priv->realirq, irq_handler, IRQF_SHARED | IRQF_SAMPLE_RANDOM, dev->name, dev); if (result != 0) { printk(KERN_ERR "%s: failed to register irq %d\n", dev->name, dev->irq); return result; } dev->irq = priv->realirq; /* set up the card and SONIC */ InitBoard(dev); /* initialize operational flags */ netif_start_queue(dev); return 0; } /* close driver. Shut down board and free allocated resources */ static int ibmlana_close(struct net_device *dev) { /* turn off board */ /* release resources */ if (dev->irq != 0) free_irq(dev->irq, dev); dev->irq = 0; return 0; } /* transmit a block. */ static int ibmlana_tx(struct sk_buff *skb, struct net_device *dev) { ibmlana_priv *priv = netdev_priv(dev); int retval = 0, tmplen, addr; unsigned long flags; tda_t tda; int baddr; /* find out if there are free slots for a frame to transmit. If not, the upper layer is in deep desperation and we simply ignore the frame. */ if (priv->txusedcnt >= TXBUFCNT) { retval = -EIO; priv->stat.tx_dropped++; goto tx_done; } /* copy the frame data into the next free transmit buffer - fillup missing */ tmplen = skb->len; if (tmplen < 60) tmplen = 60; baddr = priv->txbufstart + (priv->nexttxdescr * PKTSIZE); memcpy_toio(priv->base + baddr, skb->data, skb->len); /* copy filler into RAM - in case we're filling up... we're filling a bit more than necessary, but that doesn't harm since the buffer is far larger... Sorry Linus for the filler string but I couldn't resist ;-) */ if (tmplen > skb->len) { char *fill = "NetBSD is a nice OS too! "; unsigned int destoffs = skb->len, l = strlen(fill); while (destoffs < tmplen) { memcpy_toio(priv->base + baddr + destoffs, fill, l); destoffs += l; } } /* set up the new frame descriptor */ addr = priv->tdastart + (priv->nexttxdescr * sizeof(tda_t)); memcpy_fromio(&tda, priv->base + addr, sizeof(tda_t)); tda.length = tda.fraglength = tmplen; memcpy_toio(priv->base + addr, &tda, sizeof(tda_t)); /* if there were no active descriptors, trigger the SONIC */ spin_lock_irqsave(&priv->lock, flags); priv->txusedcnt++; priv->txused[priv->nexttxdescr] = 1; /* are all transmission slots used up ? */ if (priv->txusedcnt >= TXBUFCNT) netif_stop_queue(dev); if (priv->txusedcnt == 1) StartTx(dev, priv->nexttxdescr); priv->nexttxdescr = (priv->nexttxdescr + 1) % TXBUFCNT; spin_unlock_irqrestore(&priv->lock, flags); tx_done: dev_kfree_skb(skb); return retval; } /* return pointer to Ethernet statistics */ static struct net_device_stats *ibmlana_stats(struct net_device *dev) { ibmlana_priv *priv = netdev_priv(dev); return &priv->stat; } /* switch receiver mode. */ static void ibmlana_set_multicast_list(struct net_device *dev) { /* first stop the SONIC... */ StopSONIC(dev); /* ...then reinit it with the new flags */ InitBoard(dev); } /* ------------------------------------------------------------------------ * hardware check * ------------------------------------------------------------------------ */ static int startslot; /* counts through slots when probing multiple devices */ static int ibmlana_probe(struct net_device *dev) { int slot, z; int base = 0, irq = 0, iobase = 0, memlen = 0; ibmlana_priv *priv; ibmlana_medium medium; SET_MODULE_OWNER(dev); /* can't work without an MCA bus ;-) */ if (MCA_bus == 0) return -ENODEV; base = dev->mem_start; irq = dev->irq; for (slot = startslot; (slot = mca_find_adapter(IBM_LANA_ID, slot)) != -1; slot++) { /* deduce card addresses */ getaddrs(slot, &base, &memlen, &iobase, &irq, &medium); /* slot already in use ? */ if (mca_is_adapter_used(slot)) continue; /* were we looking for something different ? */ if (dev->irq && dev->irq != irq) continue; if (dev->mem_start && dev->mem_start != base) continue; /* found something that matches */ break; } /* nothing found ? */ if (slot == -1) return (base != 0 || irq != 0) ? -ENXIO : -ENODEV; /* announce success */ printk(KERN_INFO "%s: IBM LAN Adapter/A found in slot %d\n", dev->name, slot + 1); /* try to obtain I/O range */ if (!request_region(iobase, IBM_LANA_IORANGE, DRV_NAME)) { printk(KERN_ERR "%s: cannot allocate I/O range at %#x!\n", DRV_NAME, iobase); startslot = slot + 1; return -EBUSY; } priv = netdev_priv(dev); priv->slot = slot; priv->realirq = irq; priv->medium = medium; spin_lock_init(&priv->lock); /* set base + irq for this device (irq not allocated so far) */ dev->irq = 0; dev->mem_start = base; dev->mem_end = base + memlen; dev->base_addr = iobase; priv->base = ioremap(base, memlen); if (!priv->base) { printk(KERN_ERR "%s: cannot remap memory!\n", DRV_NAME); startslot = slot + 1; release_region(iobase, IBM_LANA_IORANGE); return -EBUSY; } /* make procfs entries */ mca_set_adapter_name(slot, "IBM LAN Adapter/A"); mca_set_adapter_procfn(slot, (MCA_ProcFn) ibmlana_getinfo, dev); mca_mark_as_used(slot); /* set methods */ dev->open = ibmlana_open; dev->stop = ibmlana_close; dev->hard_start_xmit = ibmlana_tx; dev->do_ioctl = NULL; dev->get_stats = ibmlana_stats; dev->set_multicast_list = ibmlana_set_multicast_list; dev->flags |= IFF_MULTICAST; /* copy out MAC address */ for (z = 0; z < sizeof(dev->dev_addr); z++) dev->dev_addr[z] = inb(dev->base_addr + MACADDRPROM + z); /* print config */ printk(KERN_INFO "%s: IRQ %d, I/O %#lx, memory %#lx-%#lx, " "MAC address %02x:%02x:%02x:%02x:%02x:%02x.\n", dev->name, priv->realirq, dev->base_addr, dev->mem_start, dev->mem_end - 1, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); printk(KERN_INFO "%s: %s medium\n", dev->name, MediaNames[priv->medium]); /* reset board */ ResetBoard(dev); /* next probe will start at next slot */ startslot = slot + 1; return 0; } /* ------------------------------------------------------------------------ * modularization support * ------------------------------------------------------------------------ */ #ifdef MODULE #define DEVMAX 5 static struct net_device *moddevs[DEVMAX]; static int irq; static int io; module_param(irq, int, 0); module_param(io, int, 0); MODULE_PARM_DESC(irq, "IBM LAN/A IRQ number"); MODULE_PARM_DESC(io, "IBM LAN/A I/O base address"); MODULE_LICENSE("GPL"); int init_module(void) { int z; startslot = 0; for (z = 0; z < DEVMAX; z++) { struct net_device *dev = alloc_etherdev(sizeof(ibmlana_priv)); if (!dev) break; dev->irq = irq; dev->base_addr = io; if (ibmlana_probe(dev)) { free_netdev(dev); break; } if (register_netdev(dev)) { ibmlana_priv *priv = netdev_priv(dev); release_region(dev->base_addr, IBM_LANA_IORANGE); mca_mark_as_unused(priv->slot); mca_set_adapter_name(priv->slot, ""); mca_set_adapter_procfn(priv->slot, NULL, NULL); iounmap(priv->base); free_netdev(dev); break; } moddevs[z] = dev; } return (z > 0) ? 0 : -EIO; } void cleanup_module(void) { int z; for (z = 0; z < DEVMAX; z++) { struct net_device *dev = moddevs[z]; if (dev) { ibmlana_priv *priv = netdev_priv(dev); unregister_netdev(dev); /*DeinitBoard(dev); */ release_region(dev->base_addr, IBM_LANA_IORANGE); mca_mark_as_unused(priv->slot); mca_set_adapter_name(priv->slot, ""); mca_set_adapter_procfn(priv->slot, NULL, NULL); iounmap(priv->base); free_netdev(dev); } } } #endif /* MODULE */