📄 atp.c
字号:
printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);#endif printk(KERN_NOTICE "%s: Pocket adapter found at %#3lx, IRQ %d, SAPROM " "%02X:%02X:%02X:%02X:%02X:%02X.\n", dev->name, dev->base_addr, dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); /* Reset the ethernet hardware and activate the printer pass-through. */ write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); /* Initialize the device structure. */ ether_setup(dev); if (dev->priv == NULL) dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct net_local)); lp = (struct net_local *)dev->priv; lp->chip_type = RTL8002; lp->addr_mode = CMR2h_Normal; spin_lock_init(&lp->lock); lp->next_module = root_atp_dev; root_atp_dev = dev; /* For the ATP adapter the "if_port" is really the data transfer mode. */ if (xcvr[0]) dev->if_port = xcvr[0]; else dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4; if (dev->mem_end & 0xf) net_debug = dev->mem_end & 7; dev->open = net_open; dev->stop = net_close; dev->hard_start_xmit = atp_send_packet; dev->get_stats = net_get_stats; dev->set_multicast_list = lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012; dev->tx_timeout = tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; return 0;}/* Read the station address PROM, usually a word-wide EEPROM. */static void __init get_node_ID(struct net_device *dev){ long ioaddr = dev->base_addr; int sa_offset = 0; int i; write_reg(ioaddr, CMR2, CMR2_EEPROM); /* Point to the EEPROM control registers. */ /* Some adapters have the station address at offset 15 instead of offset zero. Check for it, and fix it if needed. */ if (eeprom_op(ioaddr, EE_READ(0)) == 0xffff) sa_offset = 15; for (i = 0; i < 3; i++) ((u16 *)dev->dev_addr)[i] = be16_to_cpu(eeprom_op(ioaddr, EE_READ(sa_offset + i))); write_reg(ioaddr, CMR2, CMR2_NULL);}/* An EEPROM read command starts by shifting out 0x60+address, and then shifting in the serial data. See the NatSemi databook for details. * ________________ * CS : __| * ___ ___ * CLK: ______| |___| | * __ _______ _______ * DI : __X_______X_______X * DO : _________X_______X */static unsigned short __init eeprom_op(long ioaddr, unsigned int cmd){ unsigned eedata_out = 0; int num_bits = EE_CMD_SIZE; while (--num_bits >= 0) { char outval = test_bit(num_bits, &cmd) ? EE_DATA_WRITE : 0; write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW); write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH); eedata_out <<= 1; if (read_nibble(ioaddr, PROM_DATA) & EE_DATA_READ) eedata_out++; } write_reg_high(ioaddr, PROM_CMD, EE_CLK_LOW & ~EE_CS); return eedata_out;}/* Open/initialize the board. This is called (in the current kernel) sometime after booting when the 'ifconfig' program is run. This routine sets everything up anew at each open, even registers that "should" only need to be set once at boot, so that there is non-reboot way to recover if something goes wrong. This is an attachable device: if there is no dev->priv entry then it wasn't probed for at boot-time, and we need to probe for it again. */static int net_open(struct net_device *dev){ struct net_local *lp = (struct net_local *)dev->priv; int ret; /* The interrupt line is turned off (tri-stated) when the device isn't in use. That's especially important for "attached" interfaces where the port or interrupt may be shared. */ ret = request_irq(dev->irq, &atp_interrupt, 0, dev->name, dev); if (ret) return ret; hardware_init(dev); init_timer(&lp->timer); lp->timer.expires = RUN_AT(TIMED_CHECKER); lp->timer.data = (unsigned long)dev; lp->timer.function = &atp_timed_checker; /* timer handler */ add_timer(&lp->timer); netif_start_queue(dev); return 0;}/* This routine resets the hardware. We initialize everything, assuming that the hardware may have been temporarily detached. */static void hardware_init(struct net_device *dev){ struct net_local *lp = (struct net_local *)dev->priv; long ioaddr = dev->base_addr; int i; /* Turn off the printer multiplexer on the 8012. */ for (i = 0; i < 8; i++) outb(mux_8012[i], ioaddr + PAR_DATA); write_reg_high(ioaddr, CMR1, CMR1h_RESET); for (i = 0; i < 6; i++) write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); write_reg_high(ioaddr, CMR2, lp->addr_mode); if (net_debug > 2) { printk(KERN_DEBUG "%s: Reset: current Rx mode %d.\n", dev->name, (read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f); } 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 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 void tx_timeout(struct net_device *dev){ struct net_local *np = (struct net_local *)dev->priv; 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++; return;}static int atp_send_packet(struct sk_buff *skb, struct net_device *dev){ struct net_local *lp = (struct net_local *)dev->priv; long ioaddr = dev->base_addr; int length; 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, 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 void atp_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 = 0; int boguscount = max_interrupt_work; if (dev == NULL) { printk(KERN_ERR "ATP_interrupt(): irq %d for unknown device.\n", irq); return; } ioaddr = dev->base_addr; lp = (struct net_local *)dev->priv; 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)) { 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -