📄 znet.c
字号:
*zn.tx_cur++ = length; if (zn.tx_cur + rnd_len + 1 > zn.tx_end) { int semi_cnt = (zn.tx_end - zn.tx_cur)<<1; /* Cvrt to byte cnt. */ memcpy(zn.tx_cur, buf, semi_cnt); rnd_len -= semi_cnt>>1; memcpy(zn.tx_start, buf + semi_cnt, length - semi_cnt); zn.tx_cur = zn.tx_start + rnd_len; } else { memcpy(zn.tx_cur, buf, skb->len); zn.tx_cur += rnd_len; } *zn.tx_cur++ = 0; spin_lock_irqsave(&lp->lock, flags); { *tx_link = CMD0_TRANSMIT + CMD0_CHNL_1; /* Is this always safe to do? */ outb(CMD0_TRANSMIT + CMD0_CHNL_1,ioaddr); } spin_unlock_irqrestore (&lp->lock, flags); dev->trans_start = jiffies; netif_start_queue (dev); if (znet_debug > 4) printk(KERN_DEBUG "%s: Transmitter queued, length %d.\n", dev->name, length); } dev_kfree_skb(skb); return 0;}/* The ZNET interrupt handler. */static void znet_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct net_device *dev = dev_id; struct net_local *lp = (struct net_local *)dev->priv; int ioaddr; int boguscnt = 20; if (dev == NULL) { printk(KERN_WARNING "znet_interrupt(): IRQ %d for unknown device.\n", irq); return; } spin_lock (&lp->lock); ioaddr = dev->base_addr; outb(CMD0_STAT0, ioaddr); do { ushort status = inb(ioaddr); if (znet_debug > 5) { ushort result, rx_ptr, running; outb(CMD0_STAT1, ioaddr); result = inw(ioaddr); outb(CMD0_STAT2, ioaddr); rx_ptr = inw(ioaddr); outb(CMD0_STAT3, ioaddr); running = inb(ioaddr); printk(KERN_DEBUG "%s: interrupt, status %02x, %04x %04x %02x serial %d.\n", dev->name, status, result, rx_ptr, running, boguscnt); } if ((status & 0x80) == 0) break; if ((status & 0x0F) == 4) { /* Transmit done. */ int tx_status; outb(CMD0_STAT1, ioaddr); tx_status = inw(ioaddr); /* It's undocumented, but tx_status seems to match the i82586. */ if (tx_status & 0x2000) { lp->stats.tx_packets++; lp->stats.collisions += tx_status & 0xf; } else { if (tx_status & 0x0600) lp->stats.tx_carrier_errors++; if (tx_status & 0x0100) lp->stats.tx_fifo_errors++; if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++; if (tx_status & 0x0020) lp->stats.tx_aborted_errors++; /* ...and the catch-all. */ if ((tx_status | 0x0760) != 0x0760) lp->stats.tx_errors++; } netif_wake_queue (dev); } if ((status & 0x40) || (status & 0x0f) == 11) { znet_rx(dev); } /* Clear the interrupts we've handled. */ outb(CMD0_ACK,ioaddr); } while (boguscnt--); spin_unlock (&lp->lock); return;}static void znet_rx(struct net_device *dev){ struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; int boguscount = 1; short next_frame_end_offset = 0; /* Offset of next frame start. */ short *cur_frame_end; short cur_frame_end_offset; outb(CMD0_STAT2, ioaddr); cur_frame_end_offset = inw(ioaddr); if (cur_frame_end_offset == zn.rx_cur - zn.rx_start) { printk(KERN_WARNING "%s: Interrupted, but nothing to receive, offset %03x.\n", dev->name, cur_frame_end_offset); return; } /* Use same method as the Crynwr driver: construct a forward list in the same area of the backwards links we now have. This allows us to pass packets to the upper layers in the order they were received -- important for fast-path sequential operations. */ while (zn.rx_start + cur_frame_end_offset != zn.rx_cur && ++boguscount < 5) { unsigned short hi_cnt, lo_cnt, hi_status, lo_status; int count, status; if (cur_frame_end_offset < 4) { /* Oh no, we have a special case: the frame trailer wraps around the end of the ring buffer. We've saved space at the end of the ring buffer for just this problem. */ memcpy(zn.rx_end, zn.rx_start, 8); cur_frame_end_offset += (RX_BUF_SIZE/2); } cur_frame_end = zn.rx_start + cur_frame_end_offset - 4; lo_status = *cur_frame_end++; hi_status = *cur_frame_end++; status = ((hi_status & 0xff) << 8) + (lo_status & 0xff); lo_cnt = *cur_frame_end++; hi_cnt = *cur_frame_end++; count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff); if (znet_debug > 5) printk(KERN_DEBUG "Constructing trailer at location %03x, %04x %04x %04x %04x" " count %#x status %04x.\n", cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt, count, status); cur_frame_end[-4] = status; cur_frame_end[-3] = next_frame_end_offset; cur_frame_end[-2] = count; next_frame_end_offset = cur_frame_end_offset; cur_frame_end_offset -= ((count + 1)>>1) + 3; if (cur_frame_end_offset < 0) cur_frame_end_offset += RX_BUF_SIZE/2; }; /* Now step forward through the list. */ do { ushort *this_rfp_ptr = zn.rx_start + next_frame_end_offset; int status = this_rfp_ptr[-4]; int pkt_len = this_rfp_ptr[-2]; if (znet_debug > 5) printk(KERN_DEBUG "Looking at trailer ending at %04x status %04x length %03x" " next %04x.\n", next_frame_end_offset<<1, status, pkt_len, this_rfp_ptr[-3]<<1); /* Once again we must assume that the i82586 docs apply. */ if ( ! (status & 0x2000)) { /* There was an error. */ lp->stats.rx_errors++; if (status & 0x0800) lp->stats.rx_crc_errors++; if (status & 0x0400) lp->stats.rx_frame_errors++; if (status & 0x0200) lp->stats.rx_over_errors++; /* Wrong. */ if (status & 0x0100) lp->stats.rx_fifo_errors++; if (status & 0x0080) lp->stats.rx_length_errors++; } else if (pkt_len > 1536) { lp->stats.rx_length_errors++; } else { /* Malloc up new buffer. */ struct sk_buff *skb; skb = dev_alloc_skb(pkt_len); if (skb == NULL) { if (znet_debug) printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; break; } skb->dev = dev; if (&zn.rx_cur[(pkt_len+1)>>1] > zn.rx_end) { int semi_cnt = (zn.rx_end - zn.rx_cur)<<1; memcpy(skb_put(skb,semi_cnt), zn.rx_cur, semi_cnt); memcpy(skb_put(skb,pkt_len-semi_cnt), zn.rx_start, pkt_len - semi_cnt); } else { memcpy(skb_put(skb,pkt_len), zn.rx_cur, pkt_len); if (znet_debug > 6) { unsigned int *packet = (unsigned int *) skb->data; printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0], packet[1], packet[2], packet[3]); } } skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); lp->stats.rx_packets++; } zn.rx_cur = this_rfp_ptr; if (zn.rx_cur >= zn.rx_end) zn.rx_cur -= RX_BUF_SIZE/2; update_stop_hit(ioaddr, (zn.rx_cur - zn.rx_start)<<1); next_frame_end_offset = this_rfp_ptr[-3]; if (next_frame_end_offset == 0) /* Read all the frames? */ break; /* Done for now */ this_rfp_ptr = zn.rx_start + next_frame_end_offset; } while (--boguscount); /* If any worth-while packets have been received, dev_rint() has done a mark_bh(INET_BH) for us and will work on them when we get to the bottom-half routine. */ return;}/* The inverse routine to znet_open(). */static int znet_close(struct net_device *dev){ unsigned long flags; int ioaddr = dev->base_addr; netif_stop_queue (dev); outb(CMD0_RESET, ioaddr); /* CMD0_RESET */ flags=claim_dma_lock(); disable_dma(zn.rx_dma); disable_dma(zn.tx_dma); release_dma_lock(flags); free_irq(dev->irq, dev); if (znet_debug > 1) printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name); /* Turn off transceiver power. */ outb(0x10, 0xe6); /* Select LAN control register */ outb(inb(0xe7) & ~0x84, 0xe7); /* Turn on LAN power (bit 2). */ return 0;}/* Get the current statistics. This may be called with the card open or closed. */static struct net_device_stats *net_get_stats(struct net_device *dev){ struct net_local *lp = (struct net_local *)dev->priv; return &lp->stats;}/* Set or clear the multicast filter for this adaptor. As a side effect this routine must also initialize the device parameters. This is taken advantage of in open(). N.B. that we change i593_init[] in place. This (properly) makes the mode change persistent, but must be changed if this code is moved to a multiple adaptor environment. */static void set_multicast_list(struct net_device *dev){ short ioaddr = dev->base_addr; if (dev->flags&IFF_PROMISC) { /* Enable promiscuous mode */ i593_init[7] &= ~3; i593_init[7] |= 1; i593_init[13] &= ~8; i593_init[13] |= 8; } else if (dev->mc_list || (dev->flags&IFF_ALLMULTI)) { /* Enable accept-all-multicast mode */ i593_init[7] &= ~3; i593_init[7] |= 0; i593_init[13] &= ~8; i593_init[13] |= 8; } else { /* Enable normal mode. */ i593_init[7] &= ~3; i593_init[7] |= 0; i593_init[13] &= ~8; i593_init[13] |= 0; } *zn.tx_cur++ = sizeof(i593_init); memcpy(zn.tx_cur, i593_init, sizeof(i593_init)); zn.tx_cur += sizeof(i593_init)/2; outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);#ifdef not_tested if (num_addrs > 0) { int addrs_len = 6*num_addrs; *zn.tx_cur++ = addrs_len; memcpy(zn.tx_cur, addrs, addrs_len); outb(CMD0_MULTICAST_LIST+CMD0_CHNL_1, ioaddr); zn.tx_cur += addrs_len>>1; }#endif}void show_dma(void){ unsigned long flags; short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE; unsigned addr = inb(dma_port); addr |= inb(dma_port) << 8; flags=claim_dma_lock(); printk("Addr: %04x cnt:%3x...", addr<<1, get_dma_residue(zn.tx_dma)); release_dma_lock(flags);}/* Initialize the hardware. We have to do this when the board is open()ed or when we come out of suspend mode. */static void hardware_init(struct net_device *dev){ unsigned long flags; short ioaddr = dev->base_addr; zn.rx_cur = zn.rx_start; zn.tx_cur = zn.tx_start; /* Reset the chip, and start it up. */ outb(CMD0_RESET, ioaddr); flags=claim_dma_lock(); disable_dma(zn.rx_dma); /* reset by an interrupting task. */ clear_dma_ff(zn.rx_dma); set_dma_mode(zn.rx_dma, DMA_RX_MODE); set_dma_addr(zn.rx_dma, (unsigned int) zn.rx_start); set_dma_count(zn.rx_dma, RX_BUF_SIZE); enable_dma(zn.rx_dma); /* Now set up the Tx channel. */ disable_dma(zn.tx_dma); clear_dma_ff(zn.tx_dma); set_dma_mode(zn.tx_dma, DMA_TX_MODE); set_dma_addr(zn.tx_dma, (unsigned int) zn.tx_start); set_dma_count(zn.tx_dma, zn.tx_buf_len<<1); enable_dma(zn.tx_dma); release_dma_lock(flags); if (znet_debug > 1) printk(KERN_DEBUG "%s: Initializing the i82593, tx buf %p... ", dev->name, zn.tx_start); /* Do an empty configure command, just like the Crynwr driver. This resets to chip to its default values. */ *zn.tx_cur++ = 0; *zn.tx_cur++ = 0; printk("stat:%02x ", inb(ioaddr)); show_dma(); outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr); *zn.tx_cur++ = sizeof(i593_init); memcpy(zn.tx_cur, i593_init, sizeof(i593_init)); zn.tx_cur += sizeof(i593_init)/2; printk("stat:%02x ", inb(ioaddr)); show_dma(); outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr); *zn.tx_cur++ = 6; memcpy(zn.tx_cur, dev->dev_addr, 6); zn.tx_cur += 3; printk("stat:%02x ", inb(ioaddr)); show_dma(); outb(CMD0_IA_SETUP + CMD0_CHNL_1, ioaddr); printk("stat:%02x ", inb(ioaddr)); show_dma(); update_stop_hit(ioaddr, 8192); if (znet_debug > 1) printk("enabling Rx.\n"); outb(CMD0_Rx_ENABLE+CMD0_CHNL_0, ioaddr); netif_start_queue (dev);}static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset){ outb(CMD0_PORT_1, ioaddr); if (znet_debug > 5) printk(KERN_DEBUG "Updating stop hit with value %02x.\n", (rx_stop_offset >> 6) | 0x80); outb((rx_stop_offset >> 6) | 0x80, ioaddr); outb(CMD1_PORT_0, ioaddr);}/* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c znet.c" * version-control: t * kept-new-versions: 5 * c-indent-level: 4 * tab-width: 4 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -