📄 3c507.c
字号:
el16_send_packet(struct sk_buff *skb, struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; short *shmem = (short*)dev->mem_start; 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 < 5) return 1; if (net_debug > 1) printk("%s: transmit timed out, %s? ", dev->name, shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" : "network cable problem"); /* Try to restart the adaptor. */ if (lp->last_restart == lp->stats.tx_packets) { if (net_debug > 1) printk("Resetting board.\n"); /* Completely reset the adaptor. */ init_82586_mem(dev); } else { /* Issue the channel attention signal and hope it "gets better". */ if (net_debug > 1) printk("Kicking board.\n"); shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START; outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */ lp->last_restart = lp->stats.tx_packets; } dev->tbusy=0; dev->trans_start = jiffies; } /* 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; } /* For ethernet, fill in the header. This should really be done by a higher level, rather than duplicated for each ethernet adaptor. */ if (!skb->arp && dev->rebuild_header(skb->data, dev)) { skb->dev = dev; arp_queue (skb); return 0; } skb->arp=1; /* Block a timer-based transmit from overlapping. */ if (set_bit(0, (void*)&dev->tbusy) != 0) printk("%s: Transmitter access conflict.\n", dev->name); else { short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; /* Disable the 82586's input to the interrupt line. */ outb(0x80, ioaddr + MISC_CTRL); hardware_send_packet(dev, buf, length); dev->trans_start = jiffies; /* Enable the 82586 interrupt input. */ outb(0x84, ioaddr + MISC_CTRL); } if (skb->free) kfree_skb (skb, FREE_WRITE); /* You might need to clean up and record Tx statistics here. */ return 0;}/* The typical workload of the driver: Handle the network interface interrupts. */static voidel16_interrupt(int reg_ptr){ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); struct device *dev = (struct device *)(irq2dev_map[irq]); struct net_local *lp; int ioaddr, status, boguscount = 0; ushort ack_cmd = 0; ushort *shmem; if (dev == NULL) { printk ("net_interrupt(): irq %d for unknown device.\n", irq); return; } dev->interrupt = 1; ioaddr = dev->base_addr; lp = (struct net_local *)dev->priv; shmem = ((ushort*)dev->mem_start); status = shmem[iSCB_STATUS>>1]; if (net_debug > 4) { printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status); } /* Disable the 82586's input to the interrupt line. */ outb(0x80, ioaddr + MISC_CTRL); /* Reap the Tx packet buffers. */ while (lp->tx_reap != lp->tx_head) { unsigned short tx_status = shmem[lp->tx_reap>>1]; if (tx_status == 0) { if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap); break; } if (tx_status & 0x2000) { lp->stats.tx_packets++; lp->stats.collisions += tx_status & 0xf; dev->tbusy = 0; mark_bh(INET_BH); /* Inform upper layers. */ } else { lp->stats.tx_errors++; 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++; } if (net_debug > 5) printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status); lp->tx_reap += TX_BUF_SIZE; if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE) lp->tx_reap = TX_BUF_START; if (++boguscount > 4) break; } if (status & 0x4000) { /* Packet received. */ if (net_debug > 5) printk("Received packet, rx_head %04x.\n", lp->rx_head); el16_rx(dev); } /* Acknowledge the interrupt sources. */ ack_cmd = status & 0xf000; if ((status & 0x0700) != 0x0200 && dev->start) { if (net_debug) printk("%s: Command unit stopped, status %04x, restarting.\n", dev->name, status); /* If this ever occurs we should really re-write the idle loop, reset the Tx list, and do a complete restart of the command unit. For now we rely on the Tx timeout if the resume doesn't work. */ ack_cmd |= CUC_RESUME; } if ((status & 0x0070) != 0x0040 && dev->start) { static void init_rx_bufs(struct device *); /* The Rx unit is not ready, it must be hung. Restart the receiver by initializing the rx buffers, and issuing an Rx start command. */ if (net_debug) printk("%s: Rx unit stopped, status %04x, restarting.\n", dev->name, status); init_rx_bufs(dev); shmem[iSCB_RFA >> 1] = RX_BUF_START; ack_cmd |= RX_START; } shmem[iSCB_CMD>>1] = ack_cmd; outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */ /* Clear the latched interrupt. */ outb(0, ioaddr + RESET_IRQ); /* Enable the 82586's interrupt input. */ outb(0x84, ioaddr + MISC_CTRL); return;}static intel16_close(struct device *dev){ int ioaddr = dev->base_addr; ushort *shmem = (short*)dev->mem_start; dev->tbusy = 1; dev->start = 0; /* Flush the Tx and disable Rx. */ shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND; outb(0, ioaddr + SIGNAL_CA); /* Disable the 82586's input to the interrupt line. */ outb(0x80, ioaddr + MISC_CTRL); /* We always physically use the IRQ line, so we don't do free_irq(). We do remove ourselves from the map. */ irq2dev_map[dev->irq] = 0; /* Update the statistics here. */ return 0;}/* Get the current statistics. This may be called with the card open or closed. */static struct enet_statistics *el16_get_stats(struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; /* ToDo: decide if there are any useful statistics from the SCB. */ return &lp->stats;}/* Initialize the Rx-block list. */static voidinit_rx_bufs(struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; unsigned short *write_ptr; unsigned short SCB_base = SCB_BASE; int cur_rxbuf = lp->rx_head = RX_BUF_START; /* Initialize each Rx frame + data buffer. */ do { /* While there is room for one more. */ write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf); *write_ptr++ = 0x0000; /* Status */ *write_ptr++ = 0x0000; /* Command */ *write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */ *write_ptr++ = cur_rxbuf + 22; /* Buffer offset */ *write_ptr++ = 0x0000; /* Pad for dest addr. */ *write_ptr++ = 0x0000; *write_ptr++ = 0x0000; *write_ptr++ = 0x0000; /* Pad for source addr. */ *write_ptr++ = 0x0000; *write_ptr++ = 0x0000; *write_ptr++ = 0x0000; /* Pad for protocol. */ *write_ptr++ = 0x0000; /* Buffer: Actual count */ *write_ptr++ = -1; /* Buffer: Next (none). */ *write_ptr++ = cur_rxbuf + 0x20 + SCB_base; /* Buffer: Address low */ *write_ptr++ = 0x0000; /* Finally, the number of bytes in the buffer. */ *write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20; lp->rx_tail = cur_rxbuf; cur_rxbuf += RX_BUF_SIZE; } while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE); /* Terminate the list by setting the EOL bit, and wrap the pointer to make the list a ring. */ write_ptr = (unsigned short *) (dev->mem_start + lp->rx_tail + 2); *write_ptr++ = 0xC000; /* Command, mark as last. */ *write_ptr++ = lp->rx_head; /* Link */}voidinit_82586_mem(struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; short ioaddr = dev->base_addr; ushort *shmem = (short*)dev->mem_start; /* Enable loopback to protect the wire while starting up, and hold the 586 in reset during the memory initialization. */ outb(0x20, ioaddr + MISC_CTRL); /* Fix the ISCP address and base. */ init_words[3] = SCB_BASE; init_words[7] = SCB_BASE; /* Write the words at 0xfff6 (address-aliased to 0xfffff6). */ memcpy((void*)dev->mem_end-10, init_words, 10); /* Write the words at 0x0000. */ memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10); /* Fill in the station address. */ memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr, sizeof(dev->dev_addr)); /* The Tx-block list is written as needed. We just set up the values. */ lp->tx_cmd_link = IDLELOOP + 4; lp->tx_head = lp->tx_reap = TX_BUF_START; init_rx_bufs(dev); /* Start the 586 by releasing the reset line, but leave loopback. */ outb(0xA0, ioaddr + MISC_CTRL); /* This was time consuming to track down: you need to give two channel attention signals to reliably start up the i82586. */ outb(0, ioaddr + SIGNAL_CA); { int boguscnt = 50; while (shmem[iSCB_STATUS>>1] == 0) if (--boguscnt == 0) { printk("%s: i82586 initialization timed out with status %04x," "cmd %04x.\n", dev->name, shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]); break; } /* Issue channel-attn -- the 82586 won't start. */ outb(0, ioaddr + SIGNAL_CA); } /* Disable loopback and enable interrupts. */ outb(0x84, ioaddr + MISC_CTRL); if (net_debug > 4) printk("%s: Initialized 82586, status %04x.\n", dev->name, shmem[iSCB_STATUS>>1]); return;}static voidhardware_send_packet(struct device *dev, void *buf, short length){ struct net_local *lp = (struct net_local *)dev->priv; short ioaddr = dev->base_addr; ushort tx_block = lp->tx_head; ushort *write_ptr = (ushort *)(dev->mem_start + tx_block); /* Set the write pointer to the Tx block, and put out the header. */ *write_ptr++ = 0x0000; /* Tx status */ *write_ptr++ = CMD_INTR|CmdTx; /* Tx command */ *write_ptr++ = tx_block+16; /* Next command is a NoOp. */ *write_ptr++ = tx_block+8; /* Data Buffer offset. */ /* Output the data buffer descriptor. */ *write_ptr++ = length | 0x8000; /* Byte count parameter. */ *write_ptr++ = -1; /* No next data buffer. */ *write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */ *write_ptr++ = 0x0000; /* Buffer address high bits (always zero). */ /* Output the Loop-back NoOp command. */ *write_ptr++ = 0x0000; /* Tx status */ *write_ptr++ = CmdNOp; /* Tx command */ *write_ptr++ = tx_block+16; /* Next is myself. */ /* Output the packet at the write pointer. */ memcpy(write_ptr, buf, length); /* Set the old command link pointing to this send packet. */ *(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block; lp->tx_cmd_link = tx_block + 20; /* Set the next free tx region. */ lp->tx_head = tx_block + TX_BUF_SIZE; if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE) lp->tx_head = TX_BUF_START; if (net_debug > 4) { printk("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n", dev->name, ioaddr, length, tx_block, lp->tx_head); } if (lp->tx_head != lp->tx_reap) dev->tbusy = 0;}static voidel16_rx(struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; short *shmem = (short*)dev->mem_start; ushort rx_head = lp->rx_head; ushort rx_tail = lp->rx_tail; ushort boguscount = 10; short frame_status; while ((frame_status = shmem[rx_head>>1]) < 0) { /* Command complete */ ushort *read_frame = (short *)(dev->mem_start + rx_head); ushort rfd_cmd = read_frame[1]; ushort next_rx_frame = read_frame[2]; ushort data_buffer_addr = read_frame[3]; ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr); ushort pkt_len = data_frame[0]; if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 || pkt_len & 0xC000 != 0xC000) { printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x" "next %04x data-buf @%04x %04x.\n", dev->name, rx_head, frame_status, rfd_cmd, next_rx_frame, data_buffer_addr, pkt_len); } else if ((frame_status & 0x2000) == 0) { /* Frame Rxed, but with error. */ lp->stats.rx_errors++; if (frame_status & 0x0800) lp->stats.rx_crc_errors++; if (frame_status & 0x0400) lp->stats.rx_frame_errors++; if (frame_status & 0x0200) lp->stats.rx_fifo_errors++; if (frame_status & 0x0100) lp->stats.rx_over_errors++; if (frame_status & 0x0080) lp->stats.rx_length_errors++; } else { /* Malloc up new buffer. */ int sksize; struct sk_buff *skb; pkt_len &= 0x3fff; sksize = sizeof(struct sk_buff) + pkt_len; skb = alloc_skb(sksize, GFP_ATOMIC); if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; break; } skb->mem_len = sksize; skb->mem_addr = skb; skb->len = pkt_len; skb->dev = dev; /* 'skb->data' points to the start of sk_buff data area. */ memcpy(skb->data, data_frame + 5, pkt_len); #ifdef HAVE_NETIF_RX netif_rx(skb);#else skb->lock = 0; if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) { kfree_skbmem(skb, sksize); lp->stats.rx_dropped++; break; }#endif lp->stats.rx_packets++; } /* Clear the status word and set End-of-List on the rx frame. */ read_frame[0] = 0; read_frame[1] = 0xC000; /* Clear the end-of-list on the prev. RFD. */ *(short*)(dev->mem_start + rx_tail + 2) = 0x0000; rx_tail = rx_head; rx_head = next_rx_frame; if (--boguscount == 0) break; } lp->rx_head = rx_head; lp->rx_tail = rx_tail;}/* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c 3c507.c" * version-control: t * kept-new-versions: 5 * tab-width: 4 * c-indent-level: 4 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -