📄 atp.c
字号:
} write_reg(ioaddr, CMR2, CMR2_IRQOUT); write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); /* Enable the interrupt line from the serial port. */ outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL); /* Unmask the interesting interrupts. */ 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;}static void trigger_send(long 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(long ioaddr, int length, unsigned char *packet, int pad_len, int data_mode){ if (length & 1) { length++; pad_len++; } 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 > pad_len) ; do { write_byte_mode0(ioaddr, 0); } 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 > pad_len) write_byte_mode1(ioaddr, *packet++); while (--length > 0) write_byte_mode1(ioaddr, 0); } /* Terminate the Tx frame. End of write: ECB. */ outb(0xff, ioaddr + PAR_DATA); outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);}static void tx_timeout(struct net_device *dev){ struct net_local *np = netdev_priv(dev); long ioaddr = dev->base_addr; printk(KERN_WARNING "%s: Transmit timed out, %s?\n", dev->name, inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem" : "IRQ conflict"); np->stats.tx_errors++; /* Try to restart the adapter. */ hardware_init(dev); dev->trans_start = jiffies; netif_wake_queue(dev); np->stats.tx_errors++;}static int atp_send_packet(struct sk_buff *skb, struct net_device *dev){ struct net_local *lp = netdev_priv(dev); long ioaddr = dev->base_addr; int length; unsigned long flags; length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; netif_stop_queue(dev); /* Disable interrupts by writing 0x00 to the Interrupt Mask Register. This sequence must not be interrupted by an incoming packet. */ spin_lock_irqsave(&lp->lock, flags); write_reg(ioaddr, IMR, 0); write_reg_high(ioaddr, IMR, 0); spin_unlock_irqrestore(&lp->lock, flags); write_packet(ioaddr, length, skb->data, length-skb->len, dev->if_port); lp->pac_cnt_in_tx_buf++; if (lp->tx_unit_busy == 0) { trigger_send(ioaddr, length); lp->saved_tx_size = 0; /* Redundant */ lp->re_tx = 0; lp->tx_unit_busy = 1; } else lp->saved_tx_size = length; /* Re-enable the LPT interrupts. */ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); write_reg_high(ioaddr, IMR, ISRh_RxErr); dev->trans_start = jiffies; dev_kfree_skb (skb); return 0;}/* The typical workload of the driver: Handle the network interface interrupts. */static irqreturn_tatp_interrupt(int irq, void *dev_instance, struct pt_regs * regs){ struct net_device *dev = (struct net_device *)dev_instance; struct net_local *lp; long ioaddr; static int num_tx_since_rx; int boguscount = max_interrupt_work; int handled = 0; if (dev == NULL) { printk(KERN_ERR "ATP_interrupt(): irq %d for unknown device.\n", irq); return IRQ_NONE; } ioaddr = dev->base_addr; lp = netdev_priv(dev); spin_lock(&lp->lock); /* Disable additional spurious interrupts. */ outb(Ctrl_SelData, ioaddr + PAR_CONTROL); /* The adapter'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(KERN_DEBUG "%s: In interrupt ", dev->name); while (--boguscount > 0) { int status = read_nibble(ioaddr, ISR); if (net_debug > 5) printk("loop status %02x..", status); if (status & (ISR_RxOK<<3)) { handled = 1; 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); num_tx_since_rx = 0; } else break; } while (--boguscount > 0); } else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) { handled = 1; if (net_debug > 6) printk("handling Tx done.."); /* Clear the Tx interrupt. We should check for too many failures and reinitialize the adapter. */ 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; netif_wake_queue(dev); /* Inform upper layers. */ } num_tx_since_rx++; } else if (num_tx_since_rx > 8 && time_after(jiffies, dev->last_rx + HZ)) { if (net_debug > 2) printk(KERN_DEBUG "%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 adapter forgets its ethernet address. */ { int i; for (i = 0; i < 6; i++) write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);#if 0 && defined(TIMED_CHECKER) mod_timer(&lp->timer, jiffies + TIMED_CHECKER);#endif } /* Tell the adapter 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? */ spin_unlock(&lp->lock); if (net_debug > 5) printk("exiting interrupt.\n"); return IRQ_RETVAL(handled);}#ifdef TIMED_CHECKER/* This following code fixes a rare (and very difficult to track down) problem where the adapter forgets its ethernet address. */static void atp_timed_checker(unsigned long data){ struct net_device *dev = (struct net_device *)data; long ioaddr = dev->base_addr; struct net_local *lp = netdev_priv(dev); int tickssofar = jiffies - lp->last_rx_time; int i; spin_lock(&lp->lock); if (tickssofar > 2*HZ) {#if 1 for (i = 0; i < 6; i++) write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); lp->last_rx_time = jiffies;#else for (i = 0; i < 6; i++) if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i]) { struct net_local *lp = netdev_priv(atp_timed_dev); write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]); if (i == 2) lp->stats.tx_errors++; else if (i == 3) lp->stats.tx_dropped++; else if (i == 4) lp->stats.collisions++; else lp->stats.rx_errors++; }#endif } spin_unlock(&lp->lock); lp->timer.expires = jiffies + TIMED_CHECKER; add_timer(&lp->timer);}#endif/* We have a good packet(s), get it/them out of the buffers. */static void net_rx(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); long ioaddr = dev->base_addr; struct rx_header rx_head; /* 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(KERN_DEBUG " 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++; if (rx_head.rx_status & 0x0004) lp->stats.rx_frame_errors++; else if (rx_head.rx_status & 0x0002) lp->stats.rx_crc_errors++; if (net_debug > 3) printk(KERN_DEBUG "%s: Unknown ATP Rx error %04x.\n", dev->name, rx_head.rx_status); if (rx_head.rx_status & 0x0020) { lp->stats.rx_fifo_errors++; write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE); write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); } else if (rx_head.rx_status & 0x0050) hardware_init(dev); return; } else { /* Malloc up new buffer. The "-4" omits the FCS (CRC). */ int pkt_len = (rx_head.rx_count & 0x7ff) - 4; struct sk_buff *skb; skb = dev_alloc_skb(pkt_len + 2); if (skb == NULL) { printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; goto done; } skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; lp->stats.rx_bytes += pkt_len; } done: write_reg(ioaddr, CMR1, CMR1_NextPkt); lp->last_rx_time = jiffies; return;}static void read_block(long 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 net_device *dev){ struct net_local *lp = netdev_priv(dev); long ioaddr = dev->base_addr; netif_stop_queue(dev); del_timer_sync(&lp->timer); /* 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, dev); /* Reset the ethernet hardware and activate the printer pass-through. */ write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); 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 = netdev_priv(dev); return &lp->stats;}/* * Set or clear the multicast filter for this adapter. */static void set_rx_mode_8002(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); long ioaddr = dev->base_addr; if ( dev->mc_count > 0 || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) { /* We must make the kernel realise we had to move * into promisc mode or we start all out war on * the cable. - AC */ dev->flags|=IFF_PROMISC; lp->addr_mode = CMR2h_PROMISC; } else lp->addr_mode = CMR2h_Normal; write_reg_high(ioaddr, CMR2, lp->addr_mode);}static void set_rx_mode_8012(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); long ioaddr = dev->base_addr; unsigned char new_mode, mc_filter[8]; /* Multicast hash filter */ int i; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ new_mode = CMR2h_PROMISC; } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); new_mode = CMR2h_Normal; } else { struct dev_mc_list *mclist; memset(mc_filter, 0, sizeof(mc_filter)); for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { int filterbit = ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f; mc_filter[filterbit >> 5] |= cpu_to_le32(1 << (filterbit & 31)); } new_mode = CMR2h_Normal; } lp->addr_mode = new_mode; write_reg(ioaddr, CMR2, CMR2_IRQOUT | 0x04); /* Switch to page 1. */ for (i = 0; i < 8; i++) write_reg_byte(ioaddr, i, mc_filter[i]); if (net_debug > 2 || 1) { lp->addr_mode = 1; printk(KERN_DEBUG "%s: Mode %d, setting multicast filter to", dev->name, lp->addr_mode); for (i = 0; i < 8; i++) printk(" %2.2x", mc_filter[i]); printk(".\n"); } write_reg_high(ioaddr, CMR2, lp->addr_mode); write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */}static int __init atp_init_module(void) { if (debug) /* Emit version even if no cards detected. */ printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); return atp_init();}static void __exit atp_cleanup_module(void) { struct net_device *next_dev; while (root_atp_dev) { next_dev = ((struct net_local *)root_atp_dev->priv)->next_module; unregister_netdev(root_atp_dev); /* No need to release_region(), since we never snarf it. */ free_netdev(root_atp_dev); root_atp_dev = next_dev; }}module_init(atp_init_module);module_exit(atp_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -