?? atp.c
字號:
write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); write_reg_high(ioaddr, IMR, ISRh_RxErr); lp->tx_unit_busy = 0; lp->pac_cnt_in_tx_buf = 0; lp->saved_tx_size = 0; dev->tbusy = 0; dev->interrupt = 0;}static void trigger_send(short ioaddr, int length){ write_reg_byte(ioaddr, TxCNT0, length & 0xff); write_reg(ioaddr, TxCNT1, length >> 8); write_reg(ioaddr, CMR1, CMR1_Xmit);}static void write_packet(short ioaddr, int length, unsigned char *packet, int data_mode){ length = (length + 1) & ~1; /* Round up to word length. */ outb(EOC+MAR, ioaddr + PAR_DATA); if ((data_mode & 1) == 0) { /* Write the packet out, starting with the write addr. */ outb(WrAddr+MAR, ioaddr + PAR_DATA); do { write_byte_mode0(ioaddr, *packet++); } while (--length > 0) ; } else { /* Write the packet out in slow mode. */ unsigned char outbyte = *packet++; outb(Ctrl_LNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL); outb(WrAddr+MAR, ioaddr + PAR_DATA); outb((outbyte & 0x0f)|0x40, ioaddr + PAR_DATA); outb(outbyte & 0x0f, ioaddr + PAR_DATA); outbyte >>= 4; outb(outbyte & 0x0f, ioaddr + PAR_DATA); outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL); while (--length > 0) write_byte_mode1(ioaddr, *packet++); } /* Terminate the Tx frame. End of write: ECB. */ outb(0xff, ioaddr + PAR_DATA); outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);}static intnet_send_packet(struct sk_buff *skb, struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; 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; printk("%s: transmit timed out, %s?\n", dev->name, inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem" : "IRQ conflict"); lp->stats.tx_errors++; /* Try to restart the adaptor. */ hardware_init(dev); 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. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ 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; int flags; /* Disable interrupts by writing 0x00 to the Interrupt Mask Register. This sequence must not be interrupted by an incoming packet. */ save_flags(flags); cli(); write_reg(ioaddr, IMR, 0); write_reg_high(ioaddr, IMR, 0); restore_flags(flags); write_packet(ioaddr, length, buf, dev->if_port); lp->pac_cnt_in_tx_buf++; if (lp->tx_unit_busy == 0) { trigger_send(ioaddr, length); lp->saved_tx_size = 0; /* Redundent */ lp->re_tx = 0; lp->tx_unit_busy = 1; } else lp->saved_tx_size = length; dev->trans_start = jiffies; /* Re-enable the LPT interrupts. */ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); write_reg_high(ioaddr, IMR, ISRh_RxErr); } if (skb->free) kfree_skb (skb, FREE_WRITE); return 0;}/* The typical workload of the driver: Handle the network interface interrupts. */static voidnet_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 = 20; static int num_tx_since_rx = 0; if (dev == NULL) { printk ("ATP_interrupt(): irq %d for unknown device.\n", irq); return; } dev->interrupt = 1; ioaddr = dev->base_addr; lp = (struct net_local *)dev->priv; /* Disable additional spurious interrupts. */ outb(Ctrl_SelData, ioaddr + PAR_CONTROL); /* The adaptor's output is currently the IRQ line, switch it to data. */ write_reg(ioaddr, CMR2, CMR2_NULL); write_reg(ioaddr, IMR, 0); if (net_debug > 5) printk("%s: In interrupt ", dev->name); while (--boguscount > 0) { status = read_nibble(ioaddr, ISR); if (net_debug > 5) printk("loop status %02x..", status); if (status & (ISR_RxOK<<3)) { write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */ do { int read_status = read_nibble(ioaddr, CMR1); if (net_debug > 6) printk("handling Rx packet %02x..", read_status); /* We acknowledged the normal Rx interrupt, so if the interrupt is still outstanding we must have a Rx error. */ if (read_status & (CMR1_IRQ << 3)) { /* Overrun. */ lp->stats.rx_over_errors++; /* Set to no-accept mode long enough to remove a packet. */ write_reg_high(ioaddr, CMR2, CMR2h_OFF); net_rx(dev); /* Clear the interrupt and return to normal Rx mode. */ write_reg_high(ioaddr, ISR, ISRh_RxErr); write_reg_high(ioaddr, CMR2, lp->addr_mode); } else if ((read_status & (CMR1_BufEnb << 3)) == 0) { net_rx(dev); dev->last_rx = jiffies; num_tx_since_rx = 0; } else break; } while (--boguscount > 0); } else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) { if (net_debug > 6) printk("handling Tx done.."); /* Clear the Tx interrupt. We should check for too many failures and reinitialize the adaptor. */ write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK); if (status & (ISR_TxErr<<3)) { lp->stats.collisions++; if (++lp->re_tx > 15) { lp->stats.tx_aborted_errors++; hardware_init(dev); break; } /* Attempt to retransmit. */ if (net_debug > 6) printk("attempting to ReTx"); write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit); } else { /* Finish up the transmit. */ lp->stats.tx_packets++; lp->pac_cnt_in_tx_buf--; if ( lp->saved_tx_size) { trigger_send(ioaddr, lp->saved_tx_size); lp->saved_tx_size = 0; lp->re_tx = 0; } else lp->tx_unit_busy = 0; dev->tbusy = 0; mark_bh(INET_BH); /* Inform upper layers. */ } num_tx_since_rx++; } else if (num_tx_since_rx > 8 && jiffies > dev->last_rx + 100) { if (net_debug > 2) printk("%s: Missed packet? No Rx after %d Tx and %ld jiffies" " status %02x CMR1 %02x.\n", dev->name, num_tx_since_rx, jiffies - dev->last_rx, status, (read_nibble(ioaddr, CMR1) >> 3) & 15); lp->stats.rx_missed_errors++; hardware_init(dev); num_tx_since_rx = 0; break; } else break; } /* This following code fixes a rare (and very difficult to track down) problem where the adaptor forgets its ethernet address. */ { int i; for (i = 0; i < 6; i++) write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); } /* Tell the adaptor that it can go back to using the output line as IRQ. */ write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Enable the physical interrupt line, which is sure to be low until.. */ outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL); /* .. we enable the interrupt sources. */ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); write_reg_high(ioaddr, IMR, ISRh_RxErr); /* Hmmm, really needed? */ if (net_debug > 5) printk("exiting interrupt.\n"); dev->interrupt = 0; return;}/* We have a good packet(s), get it/them out of the buffers. */static void net_rx(struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr;#ifdef notdef ushort header[4];#else struct rx_header rx_head;#endif /* Process the received packet. */ outb(EOC+MAR, ioaddr + PAR_DATA); read_block(ioaddr, 8, (unsigned char*)&rx_head, dev->if_port); if (net_debug > 5) printk(" rx_count %04x %04x %04x %04x..", rx_head.pad, rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr); if ((rx_head.rx_status & 0x77) != 0x01) { lp->stats.rx_errors++; /* Ackkk! I don't have any documentation on what the error bits mean! The best I can do is slap the device around a bit. */ if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n", dev->name, rx_head.rx_status); hardware_init(dev); return; } else { /* Malloc up new buffer. */ int pkt_len = (rx_head.rx_count & 0x7ff) - 4; /* The "-4" is omits the FCS (CRC). */ int sksize = sizeof(struct sk_buff) + pkt_len; struct sk_buff *skb; skb = alloc_skb(sksize, GFP_ATOMIC); if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; goto done; } skb->mem_len = sksize; skb->mem_addr = skb; skb->len = pkt_len; skb->dev = dev; read_block(ioaddr, pkt_len, skb->data, dev->if_port); if (net_debug > 6) { unsigned char *data = skb->data; printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x" "%02x%02x%02x %02x%02x..", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13]); } #ifdef HAVE_NETIF_RX netif_rx(skb);#else skb->lock = 0; if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) { kfree_s(skb, sksize); lp->stats.rx_dropped++; break; }#endif lp->stats.rx_packets++; } done: write_reg(ioaddr, CMR1, CMR1_NextPkt); return;}static void read_block(short ioaddr, int length, unsigned char *p, int data_mode){ if (data_mode <= 3) { /* Mode 0 or 1 */ outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL); outb(length == 8 ? RdAddr | HNib | MAR : RdAddr | MAR, ioaddr + PAR_DATA); if (data_mode <= 1) { /* Mode 0 or 1 */ do *p++ = read_byte_mode0(ioaddr); while (--length > 0); } else /* Mode 2 or 3 */ do *p++ = read_byte_mode2(ioaddr); while (--length > 0); } else if (data_mode <= 5) do *p++ = read_byte_mode4(ioaddr); while (--length > 0); else do *p++ = read_byte_mode6(ioaddr); while (--length > 0); outb(EOC+HNib+MAR, ioaddr + PAR_DATA); outb(Ctrl_SelData, ioaddr + PAR_CONTROL);}/* The inverse routine to net_open(). */static intnet_close(struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; dev->tbusy = 1; dev->start = 0; /* Flush the Tx and disable Rx here. */ lp->addr_mode = CMR2h_OFF; write_reg_high(ioaddr, CMR2, CMR2h_OFF); /* Free the IRQ line. */ outb(0x00, ioaddr + PAR_CONTROL); free_irq(dev->irq); irq2dev_map[dev->irq] = 0; /* Leave the hardware in a reset state. */ write_reg_high(ioaddr, CMR1, CMR1h_RESET); return 0;}/* Get the current statistics. This may be called with the card open or closed. */static struct enet_statistics *net_get_stats(struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; return &lp->stats;}#ifdef HAVE_MULTICAST/* Set or clear the multicast filter for this adaptor. num_addrs == -1 Promiscuous mode, receive all packets num_addrs == 0 Normal mode, clear multicast list num_addrs > 0 Multicast mode, receive normal and MC packets, and do best-effort filtering. */static voidset_multicast_list(struct device *dev, int num_addrs, void *addrs){ struct net_local *lp = (struct net_local *)dev->priv; short ioaddr = dev->base_addr; lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal; write_reg_high(ioaddr, CMR2, lp->addr_mode);}#endif/* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c" * version-control: t * kept-new-versions: 5 * tab-width: 4 * End: */
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -