?? pcnet_cs.c
字號:
}static void ei_watchdog(u_long arg){ struct net_device *dev = (struct net_device *)arg; pcnet_dev_t *info = PRIV(dev); kio_addr_t nic_base = dev->base_addr; kio_addr_t mii_addr = nic_base + DLINK_GPIO; u_short link; if (!netif_device_present(dev)) goto reschedule; /* Check for pending interrupt with expired latency timer: with this, we can limp along even if the interrupt is blocked */ if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) { if (!info->fast_poll) printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); ei_irq_wrapper(dev->irq, dev); info->fast_poll = HZ; } if (info->fast_poll) { info->fast_poll--; info->watchdog.expires = jiffies + 1; add_timer(&info->watchdog); return; } if (!(info->flags & HAS_MII)) goto reschedule; mdio_read(mii_addr, info->phy_id, 1); link = mdio_read(mii_addr, info->phy_id, 1); if (!link || (link == 0xffff)) { if (info->eth_phy) { info->phy_id = info->eth_phy = 0; } else { printk(KERN_INFO "%s: MII is missing!\n", dev->name); info->flags &= ~HAS_MII; } goto reschedule; } link &= 0x0004; if (link != info->link_status) { u_short p = mdio_read(mii_addr, info->phy_id, 5); printk(KERN_INFO "%s: %s link beat\n", dev->name, (link) ? "found" : "lost"); if (link && (info->flags & IS_DL10022)) { /* Disable collision detection on full duplex links */ outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG); } else if (link && (info->flags & IS_DL10019)) { /* Disable collision detection on full duplex links */ write_asic(dev->base_addr, 4, (p & 0x140) ? DL19FDUPLX : 0); } if (link) { if (info->phy_id == info->eth_phy) { if (p) printk(KERN_INFO "%s: autonegotiation complete: " "%sbaseT-%cD selected\n", dev->name, ((p & 0x0180) ? "100" : "10"), ((p & 0x0140) ? 'F' : 'H')); else printk(KERN_INFO "%s: link partner did not " "autonegotiate\n", dev->name); } NS8390_init(dev, 1); } info->link_status = link; } if (info->pna_phy && time_after(jiffies, info->mii_reset + 6*HZ)) { link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004; if (((info->phy_id == info->pna_phy) && link) || ((info->phy_id != info->pna_phy) && !link)) { /* isolate this MII and try flipping to the other one */ mdio_write(mii_addr, info->phy_id, 0, 0x0400); info->phy_id ^= info->pna_phy ^ info->eth_phy; printk(KERN_INFO "%s: switched to %s transceiver\n", dev->name, (info->phy_id == info->eth_phy) ? "ethernet" : "PNA"); mdio_write(mii_addr, info->phy_id, 0, (info->phy_id == info->eth_phy) ? 0x1000 : 0); info->link_status = 0; info->mii_reset = jiffies; } }reschedule: info->watchdog.expires = jiffies + HZ; add_timer(&info->watchdog);}/*====================================================================*/static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strcpy(info->driver, "pcnet_cs");}static const struct ethtool_ops netdev_ethtool_ops = { .get_drvinfo = netdev_get_drvinfo,};/*====================================================================*/static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ pcnet_dev_t *info = PRIV(dev); u16 *data = (u16 *)&rq->ifr_ifru; kio_addr_t mii_addr = dev->base_addr + DLINK_GPIO; switch (cmd) { case SIOCGMIIPHY: data[0] = info->phy_id; case SIOCGMIIREG: /* Read MII PHY register. */ data[3] = mdio_read(mii_addr, data[0], data[1] & 0x1f); return 0; case SIOCSMIIREG: /* Write MII PHY register. */ if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(mii_addr, data[0], data[1] & 0x1f, data[2]); return 0; } return -EOPNOTSUPP;}/*====================================================================*/static void dma_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page){ kio_addr_t nic_base = dev->base_addr; if (ei_status.dmaing) { printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input." "[DMAstat:%1x][irqlock:%1x]\n", dev->name, ei_status.dmaing, ei_status.irqlock); return; } ei_status.dmaing |= 0x01; outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD); outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); outb_p(0, nic_base + EN0_RCNTHI); outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */ outb_p(ring_page, nic_base + EN0_RSARHI); outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD); insw(nic_base + PCNET_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); /* Fix for big endian systems */ hdr->count = le16_to_cpu(hdr->count); outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ ei_status.dmaing &= ~0x01;}/*====================================================================*/static void dma_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset){ kio_addr_t nic_base = dev->base_addr; int xfer_count = count; char *buf = skb->data;#ifdef PCMCIA_DEBUG if ((ei_debug > 4) && (count != 4)) printk(KERN_DEBUG "%s: [bi=%d]\n", dev->name, count+4);#endif if (ei_status.dmaing) { printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input." "[DMAstat:%1x][irqlock:%1x]\n", dev->name, ei_status.dmaing, ei_status.irqlock); return; } ei_status.dmaing |= 0x01; outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD); outb_p(count & 0xff, nic_base + EN0_RCNTLO); outb_p(count >> 8, nic_base + EN0_RCNTHI); outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD); insw(nic_base + PCNET_DATAPORT,buf,count>>1); if (count & 0x01) buf[count-1] = inb(nic_base + PCNET_DATAPORT), xfer_count++; /* This was for the ALPHA version only, but enough people have been encountering problems that it is still here. */#ifdef PCMCIA_DEBUG if (ei_debug > 4) { /* DMA termination address check... */ int addr, tries = 20; do { /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken for Rx on some cards! */ int high = inb_p(nic_base + EN0_RSARHI); int low = inb_p(nic_base + EN0_RSARLO); addr = (high << 8) + low; if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff)) break; } while (--tries > 0); if (tries <= 0) printk(KERN_NOTICE "%s: RX transfer address mismatch," "%#4.4x (expected) vs. %#4.4x (actual).\n", dev->name, ring_offset + xfer_count, addr); }#endif outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ ei_status.dmaing &= ~0x01;} /* dma_block_input *//*====================================================================*/static void dma_block_output(struct net_device *dev, int count, const u_char *buf, const int start_page){ kio_addr_t nic_base = dev->base_addr; pcnet_dev_t *info = PRIV(dev);#ifdef PCMCIA_DEBUG int retries = 0;#endif u_long dma_start;#ifdef PCMCIA_DEBUG if (ei_debug > 4) printk(KERN_DEBUG "%s: [bo=%d]\n", dev->name, count);#endif /* Round the count up for word writes. Do we need to do this? What effect will an odd byte count have on the 8390? I should check someday. */ if (count & 0x01) count++; if (ei_status.dmaing) { printk(KERN_NOTICE "%s: DMAing conflict in dma_block_output." "[DMAstat:%1x][irqlock:%1x]\n", dev->name, ei_status.dmaing, ei_status.irqlock); return; } ei_status.dmaing |= 0x01; /* We should already be in page 0, but to be safe... */ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base+PCNET_CMD);#ifdef PCMCIA_DEBUG retry:#endif outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Now the normal output. */ outb_p(count & 0xff, nic_base + EN0_RCNTLO); outb_p(count >> 8, nic_base + EN0_RCNTHI); outb_p(0x00, nic_base + EN0_RSARLO); outb_p(start_page, nic_base + EN0_RSARHI); outb_p(E8390_RWRITE+E8390_START, nic_base + PCNET_CMD); outsw(nic_base + PCNET_DATAPORT, buf, count>>1); dma_start = jiffies;#ifdef PCMCIA_DEBUG /* This was for the ALPHA version only, but enough people have been encountering problems that it is still here. */ if (ei_debug > 4) { /* DMA termination address check... */ int addr, tries = 20; do { int high = inb_p(nic_base + EN0_RSARHI); int low = inb_p(nic_base + EN0_RSARLO); addr = (high << 8) + low; if ((start_page << 8) + count == addr) break; } while (--tries > 0); if (tries <= 0) { printk(KERN_NOTICE "%s: Tx packet transfer address mismatch," "%#4.4x (expected) vs. %#4.4x (actual).\n", dev->name, (start_page << 8) + count, addr); if (retries++ == 0) goto retry; } }#endif while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0) if (time_after(jiffies, dma_start + PCNET_RDC_TIMEOUT)) { printk(KERN_NOTICE "%s: timeout waiting for Tx RDC.\n", dev->name); pcnet_reset_8390(dev); NS8390_init(dev, 1); break; } outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ if (info->flags & DELAY_OUTPUT) udelay((long)delay_time); ei_status.dmaing &= ~0x01;}/*====================================================================*/static int setup_dma_config(struct pcmcia_device *link, int start_pg, int stop_pg){ struct net_device *dev = link->priv; ei_status.tx_start_page = start_pg; ei_status.rx_start_page = start_pg + TX_PAGES; ei_status.stop_page = stop_pg; /* set up block i/o functions */ ei_status.get_8390_hdr = &dma_get_8390_hdr; ei_status.block_input = &dma_block_input; ei_status.block_output = &dma_block_output; return 0;}/*====================================================================*/static void copyin(void *dest, void __iomem *src, int c){ u_short *d = dest; u_short __iomem *s = src; int odd; if (c <= 0) return; odd = (c & 1); c >>= 1; if (c) { do { *d++ = __raw_readw(s++); } while (--c); } /* get last byte by fetching a word and masking */ if (odd) *((u_char *)d) = readw(s) & 0xff;}static void copyout(void __iomem *dest, const void *src, int c){ u_short __iomem *d = dest; const u_short *s = src; int odd; if (c <= 0) return; odd = (c & 1); c >>= 1; if (c) { do { __raw_writew(*s++, d++); } while (--c); } /* copy last byte doing a read-modify-write */ if (odd) writew((readw(d) & 0xff00) | *(u_char *)s, d);}/*====================================================================*/
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -