📄 eepro.c
字号:
for (i=0; i < 6; i++) outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i); temp_reg = inb(ioaddr + REG1); /* Setup Transmit Chaining */ outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */ | RCV_Discard_BadFrame, ioaddr + REG1); temp_reg = inb(ioaddr + REG2); /* Match broadcast */ outb(temp_reg | 0x14, ioaddr + REG2); temp_reg = inb(ioaddr + REG3); outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */ /* Set the receiving mode */ outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */ /* Set the interrupt vector */ temp_reg = inb(ioaddr + INT_NO_REG); if (lp->eepro == 2) outb((temp_reg & 0xf8) | irqrmap2[dev->irq], ioaddr + INT_NO_REG); else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); temp_reg = inb(ioaddr + INT_NO_REG); if (lp->eepro == 2) outb((temp_reg & 0xf0) | irqrmap2[dev->irq] | 0x08,ioaddr+INT_NO_REG); else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); if (net_debug > 3) printk(KERN_DEBUG "eepro_open: content of INT Reg is %x\n", temp_reg); /* Initialize the RCV and XMT upper and lower limits */ outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG); outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG); outb(XMT_LOWER_LIMIT, ioaddr + XMT_LOWER_LIMIT_REG); outb(XMT_UPPER_LIMIT, ioaddr + XMT_UPPER_LIMIT_REG); /* Enable the interrupt line. */ temp_reg = inb(ioaddr + REG1); outb(temp_reg | INT_ENABLE, ioaddr + REG1); outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ /* Let RX and TX events to interrupt */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); /* clear all interrupts */ outb(ALL_MASK, ioaddr + STATUS_REG); /* Initialize RCV */ outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR); lp->rx_start = (RCV_LOWER_LIMIT << 8) ; outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP); /* Initialize XMT */ outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR); /* Check for the i82595TX and i82595FX */ old8 = inb(ioaddr + 8); outb(~old8, ioaddr + 8); if ((temp_reg = inb(ioaddr + 8)) == old8) { if (net_debug > 3) printk(KERN_DEBUG "i82595 detected!\n"); lp->version = LAN595; } else { lp->version = LAN595TX; outb(old8, ioaddr + 8); old9 = inb(ioaddr + 9); /*outb(~old9, ioaddr + 9); if (((temp_reg = inb(ioaddr + 9)) == ( (~old9)&0xff) )) {*/ if (irqMask==ee_FX_INT2IRQ) { enum iftype { AUI=0, BNC=1, TPE=2 }; if (net_debug > 3) { printk(KERN_DEBUG "IrqMask: %#x\n",irqMask); printk(KERN_DEBUG "i82595FX detected!\n"); } lp->version = LAN595FX; outb(old9, ioaddr + 9); if (dev->if_port != TPE) { /* Hopefully, this will fix the problem of using Pentiums and pro/10 w/ BNC. */ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ temp_reg = inb(ioaddr + REG13); /* disable the full duplex mode since it is not applicable with the 10Base2 cable. */ outb(temp_reg & ~(FDX | A_N_ENABLE), REG13); outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */ } } else if (net_debug > 3) { printk(KERN_DEBUG "temp_reg: %#x ~old9: %#x\n",temp_reg,((~old9)&0xff)); printk(KERN_DEBUG "i82595TX detected!\n"); } } outb(SEL_RESET_CMD, ioaddr); /* We are supposed to wait for 2 us after a SEL_RESET */ SLOW_DOWN; SLOW_DOWN; lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */ lp->tx_last = 0; dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; if (net_debug > 3) printk(KERN_DEBUG "%s: exiting eepro_open routine.\n", dev->name); outb(RCV_ENABLE_CMD, ioaddr); MOD_INC_USE_COUNT; return 0;}static int eepro_send_packet(struct sk_buff *skb, struct device *dev){ struct eepro_local *lp = (struct eepro_local *)dev->priv; int ioaddr = dev->base_addr; int rcv_ram = dev->mem_end;#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155 unsigned long flags;#endif if (net_debug > 5) printk(KERN_DEBUG "%s: entering eepro_send_packet routine.\n", dev->name); if (dev->tbusy) { /* If we get here, some higher level has decided we are broken. There should really be a "kick me" function call instead. */ int tickssofar = jiffies - dev->trans_start; if (tickssofar < 40) return 1; /* if (net_debug > 1) */ printk(KERN_ERR "%s: transmit timed out, %s?\n", dev->name, "network cable problem"); /* This is not a duplicate. One message for the console, one for the the log file */ printk(KERN_DEBUG "%s: transmit timed out, %s?\n", dev->name, "network cable problem"); lp->stats.tx_errors++; /* Try to restart the adaptor. */ outb(SEL_RESET_CMD, ioaddr); /* We are supposed to wait for 2 us after a SEL_RESET */ SLOW_DOWN; SLOW_DOWN; /* Do I also need to flush the transmit buffers here? YES? */ lp->tx_start = lp->tx_end = rcv_ram; lp->tx_last = 0; dev->tbusy=0; dev->trans_start = jiffies; outb(RCV_ENABLE_CMD, ioaddr); }#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20155 /* If some higher layer thinks we've missed an tx-done interrupt we are passed NULL. Caution: dev_tint() handles the cli()/sti() itself. */ /* if (skb == NULL) { dev_tint(dev); return 0; }*/ /* according to A. Cox, this is obsolete since 1.0 */#endif#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155 spin_lock_irqsave(&lp->lock, flags);#endif /* Block a timer-based transmit from overlapping. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155 spin_unlock_irqrestore(&lp->lock, flags);#endif } else { short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data;#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155 lp->stats.tx_bytes+=skb->len;#endif hardware_send_packet(dev, buf, length); dev->trans_start = jiffies; } compat_dev_kfree_skb (skb, FREE_WRITE); /* You might need to clean up and record Tx statistics here. */ /* lp->stats.tx_aborted_errors++; */ if (net_debug > 5) printk(KERN_DEBUG "%s: exiting eepro_send_packet routine.\n", dev->name);#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155 spin_unlock_irqrestore(&lp->lock, flags);#endif return 0;}/* The typical workload of the driver: Handle the network interface interrupts. */static voideepro_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct device *dev = (struct device *)dev_id; /* (struct device *)(irq2dev_map[irq]);*/ struct eepro_local *lp = (struct eepro_local *)dev->priv; int ioaddr, status, boguscount = 20; if (dev == NULL) { printk (KERN_ERR "eepro_interrupt(): irq %d for unknown device.\\n", irq); return; }#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155 spin_lock(&lp->lock);#endif if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155 spin_unlock(&lp->lock); /* FIXME : with the lock, could this ever happen ? */#endif return; } dev->interrupt = 1; if (net_debug > 5) printk(KERN_DEBUG "%s: entering eepro_interrupt routine.\n", dev->name); ioaddr = dev->base_addr; do { status = inb(ioaddr + STATUS_REG); if (status & RX_INT) { if (net_debug > 4) printk(KERN_DEBUG "%s: packet received interrupt.\n", dev->name); /* Acknowledge the RX_INT */ outb(RX_INT, ioaddr + STATUS_REG); /* Get the received packets */ eepro_rx(dev); } else if (status & TX_INT) { if (net_debug > 4) printk(KERN_DEBUG "%s: packet transmit interrupt.\n", dev->name); /* Acknowledge the TX_INT */ outb(TX_INT, ioaddr + STATUS_REG); /* Process the status of transmitted packets */ eepro_transmit_interrupt(dev); } } while ((boguscount-- > 0) && (status & 0x06)); dev->interrupt = 0; if (net_debug > 5) printk(KERN_DEBUG "%s: exiting eepro_interrupt routine.\n", dev->name);#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20155 spin_unlock(&lp->lock);#endif return;}static int eepro_close(struct device *dev){ struct eepro_local *lp = (struct eepro_local *)dev->priv; int ioaddr = dev->base_addr; int rcv_ram = dev->mem_end; short temp_reg; dev->tbusy = 1; dev->start = 0; outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */ /* Disable the physical interrupt line. */ temp_reg = inb(ioaddr + REG1); outb(temp_reg & 0x7f, ioaddr + REG1); outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ /* Flush the Tx and disable Rx. */ outb(STOP_RCV_CMD, ioaddr); lp->tx_start = lp->tx_end = rcv_ram ; lp->tx_last = 0; /* Mask all the interrupts. */ outb(ALL_MASK, ioaddr + INT_MASK_REG); /* clear all interrupts */ outb(ALL_MASK, ioaddr + STATUS_REG); /* Reset the 82595 */ outb(RESET_CMD, ioaddr); /* release the interrupt */ free_irq(dev->irq, dev);#ifdef irq2dev_map irq2dev_map[dev->irq] = 0;#endif /* Update the statistics here. What statistics? */ /* We are supposed to wait for 200 us after a RESET */ SLOW_DOWN; SLOW_DOWN; /* May not be enough? */ MOD_DEC_USE_COUNT; return 0;}/* Get the current statistics. This may be called with the card open or closed. */static struct enet_statistics *eepro_get_stats(struct device *dev){ struct eepro_local *lp = (struct eepro_local *)dev->priv; return &lp->stats;}/* Set or clear the multicast filter for this adaptor. */static voidset_multicast_list(struct device *dev){ struct eepro_local *lp = (struct eepro_local *)dev->priv; short ioaddr = dev->base_addr; unsigned short mode; struct dev_mc_list *dmi=dev->mc_list; if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || dev->mc_count > 63) { /* * We must make the kernel realise we had to move * into promisc mode or we start all out war on * the cable. If it was a promisc request the * flag is already set. If not we assert it. */ dev->flags|=IFF_PROMISC; outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ mode = inb(ioaddr + REG2); outb(mode | PRMSC_Mode, ioaddr + REG2); mode = inb(ioaddr + REG3); outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ printk("%s: promiscuous mode enabled.\n", dev->name); } else if (dev->mc_count==0 ) { outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ mode = inb(ioaddr + REG2); outb(mode & 0xd6, ioaddr + REG2); /* Turn off Multi-IA and PRMSC_Mode bits */ mode = inb(ioaddr + REG3); outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ } else { unsigned short status, *eaddrs; int i, boguscount = 0; /* Disable RX and TX interrupts. Necessary to avoid corruption of the HOST_ADDRESS_REG by interrupt service routines. */ outb(ALL_MASK, ioaddr + INT_MASK_REG); outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ mode = inb(ioaddr + REG2); outb(mode | Multi_IA, ioaddr + REG2); mode = inb(ioaddr + REG3); outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ outw(lp->tx_end, ioaddr + HOST_ADDRESS_REG); outw(MC_SETUP, ioaddr + IO_PORT); outw(0, ioaddr + IO_PORT); outw(0, ioaddr + IO_PORT); outw(6*(dev->mc_count + 1), ioaddr + IO_PORT); for (i = 0; i < dev->mc_count; i++) { eaddrs=(unsigned short *)dmi->dmi_addr; dmi=dmi->next; outw(*eaddrs++, ioaddr + IO_PORT); outw(*eaddrs++, ioaddr + IO_PORT); outw(*eaddrs++, ioaddr + IO_PORT); } eaddrs = (unsigned short *) dev->dev_addr; outw(eaddrs[0], ioaddr + IO_PORT); outw(eaddrs[1], ioaddr + IO_PORT); outw(eaddrs[2], ioaddr + IO_PORT); outw(lp->tx_end, ioaddr + XMT_BAR); outb(MC_SETUP, ioaddr); /* Update the transmit queue */ i = lp->tx_end + XMT_HEADER + 6*(dev->mc_count + 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -