📄 znet.c
字号:
static int znet_open(struct net_device *dev){ int ioaddr = dev->base_addr; if (znet_debug > 2) printk(KERN_DEBUG "%s: znet_open() called.\n", dev->name); /* These should never fail. You can't add devices to a sealed box! */ if (znet_request_resources (dev)) { printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name); return -EBUSY; } znet_transceiver_power (dev, 1); /* According to the Crynwr driver we should wait 50 msec. for the LAN clock to stabilize. My experiments indicates that the '593 can be initialized immediately. The delay is probably needed for the DC-to-DC converter to come up to full voltage, and for the oscillator to be spot-on at 20Mhz before transmitting. Until this proves to be a problem we rely on the higher layers for the delay and save allocating a timer entry. */ /* maz : Well, I'm getting every time the following message * without the delay on a 486@33. This machine is much too * fast... :-) So maybe the Crynwr driver wasn't wrong after * all, even if the message is completly harmless on my * setup. */ mdelay (50); /* This follows the packet driver's lead, and checks for success. */ if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00) printk(KERN_WARNING "%s: Problem turning on the transceiver power.\n", dev->name); hardware_init(dev); netif_start_queue (dev); return 0;}static void znet_tx_timeout (struct net_device *dev){ int ioaddr = dev->base_addr; ushort event, tx_status, rx_offset, state; outb (CR0_STATUS_0, ioaddr); event = inb (ioaddr); outb (CR0_STATUS_1, ioaddr); tx_status = inw (ioaddr); outb (CR0_STATUS_2, ioaddr); rx_offset = inw (ioaddr); outb (CR0_STATUS_3, ioaddr); state = inb (ioaddr); printk (KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x," " resetting.\n", dev->name, event, tx_status, rx_offset, state); if (tx_status == TX_LOST_CRS) printk (KERN_WARNING "%s: Tx carrier error, check transceiver cable.\n", dev->name); outb (OP0_RESET, ioaddr); hardware_init (dev); netif_wake_queue (dev);}static int znet_send_packet(struct sk_buff *skb, struct net_device *dev){ int ioaddr = dev->base_addr; struct znet_private *znet = dev->priv; unsigned long flags; short length = skb->len; if (znet_debug > 4) printk(KERN_DEBUG "%s: ZNet_send_packet.\n", dev->name); if (length < ETH_ZLEN) { if (skb_padto(skb, ETH_ZLEN)) return 0; length = ETH_ZLEN; } netif_stop_queue (dev); /* Check that the part hasn't reset itself, probably from suspend. */ outb(CR0_STATUS_0, ioaddr); if (inw(ioaddr) == 0x0010 && inw(ioaddr) == 0x0000 && inw(ioaddr) == 0x0010) { if (znet_debug > 1) printk (KERN_WARNING "%s : waking up\n", dev->name); hardware_init(dev); znet_transceiver_power (dev, 1); } if (1) { unsigned char *buf = (void *)skb->data; ushort *tx_link = znet->tx_cur - 1; ushort rnd_len = (length + 1)>>1; dev->stats.tx_bytes+=length; if (znet->tx_cur >= znet->tx_end) znet->tx_cur = znet->tx_start; *znet->tx_cur++ = length; if (znet->tx_cur + rnd_len + 1 > znet->tx_end) { int semi_cnt = (znet->tx_end - znet->tx_cur)<<1; /* Cvrt to byte cnt. */ memcpy(znet->tx_cur, buf, semi_cnt); rnd_len -= semi_cnt>>1; memcpy(znet->tx_start, buf + semi_cnt, length - semi_cnt); znet->tx_cur = znet->tx_start + rnd_len; } else { memcpy(znet->tx_cur, buf, skb->len); znet->tx_cur += rnd_len; } *znet->tx_cur++ = 0; spin_lock_irqsave(&znet->lock, flags); { *tx_link = OP0_TRANSMIT | CR0_CHNL; /* Is this always safe to do? */ outb(OP0_TRANSMIT | CR0_CHNL, ioaddr); } spin_unlock_irqrestore (&znet->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 irqreturn_t znet_interrupt(int irq, void *dev_id){ struct net_device *dev = dev_id; struct znet_private *znet = dev->priv; int ioaddr; int boguscnt = 20; int handled = 0; spin_lock (&znet->lock); ioaddr = dev->base_addr; outb(CR0_STATUS_0, ioaddr); do { ushort status = inb(ioaddr); if (znet_debug > 5) { ushort result, rx_ptr, running; outb(CR0_STATUS_1, ioaddr); result = inw(ioaddr); outb(CR0_STATUS_2, ioaddr); rx_ptr = inw(ioaddr); outb(CR0_STATUS_3, 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 & SR0_INTERRUPT) == 0) break; handled = 1; if ((status & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE || (status & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE || (status & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) { int tx_status; outb(CR0_STATUS_1, ioaddr); tx_status = inw(ioaddr); /* It's undocumented, but tx_status seems to match the i82586. */ if (tx_status & TX_OK) { dev->stats.tx_packets++; dev->stats.collisions += tx_status & TX_NCOL_MASK; } else { if (tx_status & (TX_LOST_CTS | TX_LOST_CRS)) dev->stats.tx_carrier_errors++; if (tx_status & TX_UND_RUN) dev->stats.tx_fifo_errors++; if (!(tx_status & TX_HRT_BEAT)) dev->stats.tx_heartbeat_errors++; if (tx_status & TX_MAX_COL) dev->stats.tx_aborted_errors++; /* ...and the catch-all. */ if ((tx_status | (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL)) != (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL)) dev->stats.tx_errors++; /* Transceiver may be stuck if cable * was removed while emiting a * packet. Flip it off, then on to * reset it. This is very empirical, * but it seems to work. */ znet_transceiver_power (dev, 0); znet_transceiver_power (dev, 1); } netif_wake_queue (dev); } if ((status & SR0_RECEPTION) || (status & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) { znet_rx(dev); } /* Clear the interrupts we've handled. */ outb(CR0_INT_ACK, ioaddr); } while (boguscnt--); spin_unlock (&znet->lock); return IRQ_RETVAL(handled);}static void znet_rx(struct net_device *dev){ struct znet_private *znet = 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(CR0_STATUS_2, ioaddr); cur_frame_end_offset = inw(ioaddr); if (cur_frame_end_offset == znet->rx_cur - znet->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 (znet->rx_start + cur_frame_end_offset != znet->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(znet->rx_end, znet->rx_start, 8); cur_frame_end_offset += (RX_BUF_SIZE/2); } cur_frame_end = znet->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 = znet->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 & RX_RCV_OK)) { /* There was an error. */ dev->stats.rx_errors++; if (status & RX_CRC_ERR) dev->stats.rx_crc_errors++; if (status & RX_ALG_ERR) dev->stats.rx_frame_errors++;#if 0 if (status & 0x0200) dev->stats.rx_over_errors++; /* Wrong. */ if (status & 0x0100) dev->stats.rx_fifo_errors++;#else /* maz : Wild guess... */ if (status & RX_OVRRUN) dev->stats.rx_over_errors++;#endif if (status & RX_SRT_FRM) dev->stats.rx_length_errors++; } else if (pkt_len > 1536) { dev->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); dev->stats.rx_dropped++; break; } if (&znet->rx_cur[(pkt_len+1)>>1] > znet->rx_end) { int semi_cnt = (znet->rx_end - znet->rx_cur)<<1; memcpy(skb_put(skb,semi_cnt), znet->rx_cur, semi_cnt); memcpy(skb_put(skb,pkt_len-semi_cnt), znet->rx_start, pkt_len - semi_cnt); } else { memcpy(skb_put(skb,pkt_len), znet->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); dev->last_rx = jiffies; dev->stats.rx_packets++; dev->stats.rx_bytes += pkt_len; } znet->rx_cur = this_rfp_ptr; if (znet->rx_cur >= znet->rx_end) znet->rx_cur -= RX_BUF_SIZE/2; update_stop_hit(ioaddr, (znet->rx_cur - znet->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 = znet->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){ int ioaddr = dev->base_addr; netif_stop_queue (dev); outb(OP0_RESET, ioaddr); /* CMD0_RESET */ if (znet_debug > 1) printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name); /* Turn off transceiver power. */ znet_transceiver_power (dev, 0); znet_release_resources (dev); return 0;}static void show_dma(struct net_device *dev){ short ioaddr = dev->base_addr; unsigned char stat = inb (ioaddr); struct znet_private *znet = dev->priv; unsigned long flags; short dma_port = ((znet->tx_dma&3)<<2) + IO_DMA2_BASE; unsigned addr = inb(dma_port); short residue; addr |= inb(dma_port) << 8; residue = get_dma_residue(znet->tx_dma); if (znet_debug > 1) { flags=claim_dma_lock(); printk(KERN_DEBUG "Stat:%02x Addr: %04x cnt:%3x\n", stat, addr<<1, residue); 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; struct znet_private *znet = dev->priv; znet->rx_cur = znet->rx_start; znet->tx_cur = znet->tx_start; /* Reset the chip, and start it up. */ outb(OP0_RESET, ioaddr); flags=claim_dma_lock(); disable_dma(znet->rx_dma); /* reset by an interrupting task. */ clear_dma_ff(znet->rx_dma); set_dma_mode(znet->rx_dma, DMA_RX_MODE); set_dma_addr(znet->rx_dma, (unsigned int) znet->rx_start); set_dma_count(znet->rx_dma, RX_BUF_SIZE); enable_dma(znet->rx_dma); /* Now set up the Tx channel. */ disable_dma(znet->tx_dma); clear_dma_ff(znet->tx_dma); set_dma_mode(znet->tx_dma, DMA_TX_MODE); set_dma_addr(znet->tx_dma, (unsigned int) znet->tx_start); set_dma_count(znet->tx_dma, znet->tx_buf_len<<1); enable_dma(znet->tx_dma); release_dma_lock(flags); if (znet_debug > 1) printk(KERN_DEBUG "%s: Initializing the i82593, rx buf %p tx buf %p\n", dev->name, znet->rx_start,znet->tx_start); /* Do an empty configure command, just like the Crynwr driver. This resets to chip to its default values. */ *znet->tx_cur++ = 0; *znet->tx_cur++ = 0; show_dma(dev); outb(OP0_CONFIGURE | CR0_CHNL, ioaddr); znet_set_multicast_list (dev); *znet->tx_cur++ = 6; memcpy(znet->tx_cur, dev->dev_addr, 6); znet->tx_cur += 3; show_dma(dev); outb(OP0_IA_SETUP | CR0_CHNL, ioaddr); show_dma(dev); update_stop_hit(ioaddr, 8192); if (znet_debug > 1) printk(KERN_DEBUG "enabling Rx.\n"); outb(OP0_RCV_ENABLE, ioaddr); netif_start_queue (dev);}static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset){ outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, ioaddr); if (znet_debug > 5) printk(KERN_DEBUG "Updating stop hit with value %02x.\n", (rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE); outb((rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE, ioaddr); outb(OP1_SWIT_TO_PORT_0, ioaddr);}static __exit void znet_cleanup (void){ if (znet_dev) { struct znet_private *znet = znet_dev->priv; unregister_netdev (znet_dev); kfree (znet->rx_start); kfree (znet->tx_start); free_netdev (znet_dev); }}module_init (znet_probe);module_exit (znet_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -