📄 smc91c92_cs.c
字号:
printk(KERN_NOTICE "smc91c92_cs: Unable to find hardware address.\n"); link->state &= ~DEV_CONFIG_PENDING; goto config_undo; } strcpy(smc->node.dev_name, dev->name); link->dev = &smc->node; link->state &= ~DEV_CONFIG_PENDING; rev = check_sig(link); name = "???"; if (rev > 0) switch (rev >> 4) { case 3: name = "92"; break; case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break; case 5: name = "95"; break; case 7: name = "100"; break; case 8: name = "100-FD"; break; case 9: name = "110"; break; } printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, " "hw_addr ", dev->name, name, (rev & 0x0f), dev->base_addr, dev->irq); for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); if (rev > 0) { u_long mir, mcr; ioaddr_t ioaddr = dev->base_addr; SMC_SELECT_BANK(0); mir = inw(ioaddr + MEMINFO) & 0xff; if (mir == 0xff) mir++; /* Get scale factor for memory size */ mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200; mir *= 128 * (1<<((mcr >> 9) & 7)); if (mir & 0x3ff) printk(KERN_INFO " %lu byte", mir); else printk(KERN_INFO " %lu kb", mir>>10); SMC_SELECT_BANK(1); smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT; smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC; if (smc->manfid == MANFID_OSITECH) smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0; if ((rev >> 4) >= 7) smc->cfg |= CFG_MII_SELECT; printk(" buffer, %s xcvr\n", (smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]); } return; config_undo: unregister_netdev(dev);config_failed: /* CS_EXIT_TEST() calls jump to here... */ smc91c92_release((u_long)link); } /* smc91c92_config *//*====================================================================== After a card is removed, smc91c92_release() will unregister the net device, and release the PCMCIA configuration. If the device is still open, this will be postponed until it is closed.======================================================================*/static void smc91c92_release(u_long arg){ dev_link_t *link = (dev_link_t *)arg; struct smc_private *smc = link->priv; DEBUG(0, "smc91c92_release(0x%p)\n", link); if (link->open) { DEBUG(1, "smc91c92_cs: release postponed, '%s' still open\n", link->dev->dev_name); link->state |= DEV_STALE_CONFIG; return; } CardServices(ReleaseConfiguration, link->handle); CardServices(ReleaseIO, link->handle, &link->io); CardServices(ReleaseIRQ, link->handle, &link->irq); if (link->win) { iounmap(smc->base); CardServices(ReleaseWindow, link->win); } link->state &= ~DEV_CONFIG;} /* smc91c92_release *//*====================================================================== The card status event handler. Mostly, this schedules other stuff to run after an event is received. A CARD_REMOVAL event also sets some flags to discourage the net drivers from trying to talk to the card any more.======================================================================*/static int smc91c92_event(event_t event, int priority, event_callback_args_t *args){ dev_link_t *link = args->client_data; struct smc_private *smc = link->priv; struct net_device *dev = &smc->dev; DEBUG(1, "smc91c92_event(0x%06x)\n", event); switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { netif_device_detach(dev); mod_timer(&link->release, jiffies + HZ/20); } break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT; smc91c92_config(link); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: if (link->state & DEV_CONFIG) { if (link->open) netif_device_detach(dev); CardServices(ReleaseConfiguration, link->handle); } break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: if (link->state & DEV_CONFIG) { if ((smc->manfid == MANFID_MEGAHERTZ) && (smc->cardid == PRODID_MEGAHERTZ_EM3288)) mhz_3288_power(link); CardServices(RequestConfiguration, link->handle, &link->conf); if (smc->manfid == MANFID_MOTOROLA) mot_config(link); if ((smc->manfid == MANFID_OSITECH) && (smc->cardid != PRODID_OSITECH_SEVEN)) { /* Power up the card and enable interrupts */ set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR); set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR); } if (link->open) { smc_reset(dev); netif_device_attach(dev); } } break; } return 0;} /* smc91c92_event *//*====================================================================== The driver core code, most of which should be common with a non-PCMCIA implementation. ======================================================================*/#ifdef PCMCIA_DEBUGstatic void smc_dump(struct net_device *dev){ ioaddr_t ioaddr = dev->base_addr; u_short i, w, save; save = inw(ioaddr + BANK_SELECT); for (w = 0; w < 4; w++) { SMC_SELECT_BANK(w); printk(KERN_DEBUG "bank %d: ", w); for (i = 0; i < 14; i += 2) printk(" %04x", inw(ioaddr + i)); printk("\n"); } outw(save, ioaddr + BANK_SELECT);}#endifstatic int smc91c92_open(struct net_device *dev){ struct smc_private *smc = dev->priv; dev_link_t *link = &smc->link;#ifdef PCMCIA_DEBUG DEBUG(0, "%s: smc91c92_open(%p), ID/Window %4.4x.\n", dev->name, dev, inw(dev->base_addr + BANK_SELECT)); if (pc_debug > 1) smc_dump(dev);#endif /* Check that the PCMCIA card is still here. */ if (!DEV_OK(link)) return -ENODEV; /* Physical device present signature. */ if (check_sig(link) < 0) { printk("smc91c92_cs: Yikes! Bad chip signature!\n"); return -ENODEV; } link->open++; MOD_INC_USE_COUNT; netif_start_queue(dev); smc->saved_skb = 0; smc->packets_waiting = 0; smc_reset(dev); smc->media.function = &media_check; smc->media.data = (u_long)smc; smc->media.expires = jiffies + HZ; add_timer(&smc->media); return 0;} /* smc91c92_open *//*====================================================================*/static int smc91c92_close(struct net_device *dev){ struct smc_private *smc = dev->priv; dev_link_t *link = &smc->link; ioaddr_t ioaddr = dev->base_addr; DEBUG(0, "%s: smc91c92_close(), status %4.4x.\n", dev->name, inw(ioaddr + BANK_SELECT)); netif_stop_queue(dev); /* Shut off all interrupts, and turn off the Tx and Rx sections. Don't bother to check for chip present. */ SMC_SELECT_BANK(2); /* Nominally paranoia, but do no assume... */ outw(0, ioaddr + INTERRUPT); SMC_SELECT_BANK(0); mask_bits(0xff00, ioaddr + RCR); mask_bits(0xff00, ioaddr + TCR); /* Put the chip into power-down mode. */ SMC_SELECT_BANK(1); outw(CTL_POWERDOWN, ioaddr + CONTROL ); link->open--; del_timer(&smc->media); if (link->state & DEV_STALE_CONFIG) mod_timer(&link->release, jiffies + HZ/20); MOD_DEC_USE_COUNT; return 0;} /* smc91c92_close *//*====================================================================== Transfer a packet to the hardware and trigger the packet send. This may be called at either from either the Tx queue code or the interrupt handler. ======================================================================*/static void smc_hardware_send_packet(struct net_device * dev){ struct smc_private *smc = dev->priv; struct sk_buff *skb = smc->saved_skb; ioaddr_t ioaddr = dev->base_addr; u_char packet_no; if (!skb) { printk(KERN_ERR "%s: In XMIT with no packet to send.\n", dev->name); return; } /* There should be a packet slot waiting. */ packet_no = inw(ioaddr + PNR_ARR) >> 8; if (packet_no & 0x80) { /* If not, there is a hardware problem! Likely an ejected card. */ printk(KERN_WARNING "%s: 91c92 hardware Tx buffer allocation" " failed, status %#2.2x.\n", dev->name, packet_no); dev_kfree_skb_irq(skb); smc->saved_skb = NULL; netif_start_queue(dev); return; } smc->stats.tx_bytes += skb->len; /* The card should use the just-allocated buffer. */ outw(packet_no, ioaddr + PNR_ARR); /* point to the beginning of the packet */ outw(PTR_AUTOINC , ioaddr + POINTER); /* Send the packet length (+6 for status, length and ctl byte) and the status word (set to zeros). */ { u_char *buf = skb->data; u_int length = skb->len; /* The chip will pad to ethernet min. */ DEBUG(2, "%s: Trying to xmit packet of length %d.\n", dev->name, length); /* send the packet length: +6 for status word, length, and ctl */ outw(0, ioaddr + DATA_1); outw(length + 6, ioaddr + DATA_1); outsw(ioaddr + DATA_1, buf, length >> 1); /* The odd last byte, if there is one, goes in the control word. */ outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1); } /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */ outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) | (inw(ioaddr + INTERRUPT) & 0xff00), ioaddr + INTERRUPT); /* The chip does the rest of the work. */ outw(MC_ENQUEUE , ioaddr + MMU_CMD); smc->saved_skb = NULL; dev_kfree_skb_irq(skb); dev->trans_start = jiffies; netif_start_queue(dev); return;}/*====================================================================*/static void smc_tx_timeout(struct net_device *dev){ struct smc_private *smc = dev->priv; ioaddr_t ioaddr = dev->base_addr; printk(KERN_NOTICE "%s: SMC91c92 transmit timed out, " "Tx_status %2.2x status %4.4x.\n", dev->name, inw(ioaddr)&0xff, inw(ioaddr + 2)); smc->stats.tx_errors++; smc_reset(dev); dev->trans_start = jiffies; smc->saved_skb = NULL; netif_start_queue(dev);}static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct smc_private *smc = dev->priv; ioaddr_t ioaddr = dev->base_addr; u_short num_pages; short time_out, ir; netif_stop_queue(dev); DEBUG(2, "%s: smc91c92_start_xmit(length = %d) called," " status %4.4x.\n", dev->name, skb->len, inw(ioaddr + 2)); if (smc->saved_skb) { /* THIS SHOULD NEVER HAPPEN. */ smc->stats.tx_aborted_errors++; printk(KERN_DEBUG "%s: Internal error -- sent packet while busy.\n", dev->name); return 1; } smc->saved_skb = skb; num_pages = skb->len >> 8; if (num_pages > 7) { printk(KERN_ERR "%s: Far too big packet error.\n", dev->name); dev_kfree_skb (skb); smc->saved_skb = NULL; smc->stats.tx_dropped++; return 0; /* Do not re-queue this packet. */ } /* A packet is now waiting. */ smc->packets_waiting++; SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */ /* Allocate the memory; send the packet now if we win. */ outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD); for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) { ir = inw(ioaddr+INTERRUPT); if (ir & IM_ALLOC_INT) { /* Acknowledge the interrupt, send the packet. */ outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT); smc_hardware_send_packet(dev); /* Send the packet now.. */ return 0; } } /* Otherwise defer until the Tx-space-allocated interrupt. */ DEBUG(2, "%s: memory allocation deferred.\n", dev->name); outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT); return 0;}/*====================================================================== Handle a Tx anomolous event. Entered while in Window 2. ======================================================================*/static void smc_tx_err(struct net_device * dev){ struct smc_private *smc = (struct smc_private *)dev->priv; ioaddr_t ioaddr = dev->base_addr; int saved_packet = inw(ioaddr + PNR_ARR) & 0xff; int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f; int tx_status; /* select this as the packet to read from */ outw(packet_no, ioaddr + PNR_ARR); /* read the first word from this packet */ outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER); tx_status = inw(ioaddr + DATA_1); smc->stats.tx_errors++; if (tx_status & TS_LOSTCAR) smc->stats.tx_carrier_errors++; if (tx_status & TS_LATCOL) smc->stats.tx_window_errors++; if (tx_status & TS_16COL) { smc->stats.tx_aborted_errors++; smc->tx_err++; } if (tx_status & TS_SUCCESS) { printk(KERN_NOTICE "%s: Successful packet caused error " "interrupt?\n", dev->name); } /* re-enable transmit */ SMC_SELECT_BANK(0); outw(inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR); SMC_SELECT_BANK(2); outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */ /* one less packet waiting for me */ smc->packets_waiting--; outw(saved_packet, ioaddr + PNR_ARR); return;}/*====================================================================*/static void smc_eph_irq(struct net_device *dev){ struct smc_private *smc = dev->priv; ioaddr_t ioaddr = dev->base_addr; u_short card_stats, ephs; SMC_SELECT_BANK(0); ephs = inw(ioaddr + EPH); DEBUG(2, "%s: Ethernet protocol handler interrupt, status" " %4.4x.\n", dev->name, ephs); /* Could be a counter roll-over warning: update stats. */ card_stats = inw(ioaddr + COUNTER); /* single collisions */ smc->stats.collisions += card_stats & 0xF; card_stats >>= 4; /* multiple collisions */ smc->stats.collisions += card_stats & 0xF;#if 0 /* These are for when linux supports these statistics */ card_stats >>= 4; /* deferred */ card_stats >>= 4; /* excess deferred */#endif /* If we had a transmit error we must re-enable the transmitter. */ outw(inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR); /* Clear a link error interrupt. */ SMC_SELECT_BANK(1); outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL); outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, ioaddr + CONTROL); SMC_SELECT_BANK(2);}/*====================================================================*/ static void smc_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct smc_private *smc = dev_id; struct net_device *dev = &smc->dev; ioaddr_t ioaddr; u_short saved_bank, saved_pointer, mask, status; char bogus_cnt = INTR_WORK; /* Work we are willing to do. */ if (!netif_device_present(dev)) return; ioaddr = dev->base_addr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -