📄 3c589_cs.c
字号:
dev->base_addr = link->io.BasePort1; if (register_netdev(dev) != 0) { printk(KERN_NOTICE "3c589_cs: register_netdev() failed\n"); goto failed; } ioaddr = dev->base_addr; EL3WINDOW(0); /* The 3c589 has an extra EEPROM for configuration info, including the hardware address. The 3c562 puts the address in the CIS. */ tuple.DesiredTuple = 0x88; if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) { CardServices(GetTupleData, handle, &tuple); for (i = 0; i < 3; i++) phys_addr[i] = htons(buf[i]); } else { for (i = 0; i < 3; i++) phys_addr[i] = htons(read_eeprom(ioaddr, i)); if (phys_addr[0] == 0x6060) { printk(KERN_NOTICE "3c589_cs: IO port conflict at 0x%03lx" "-0x%03lx\n", dev->base_addr, dev->base_addr+15); goto failed; } } strcpy(lp->node.dev_name, dev->name); link->dev = &lp->node; link->state &= ~DEV_CONFIG_PENDING; /* The address and resource configuration register aren't loaded from the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. */ outw(0x3f00, ioaddr + 8); /* The if_port symbol can be set when the module is loaded */ if ((if_port >= 0) && (if_port <= 3)) dev->if_port = if_port; else printk(KERN_NOTICE "3c589_cs: invalid if_port requested\n"); printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, hw_addr ", dev->name, (multi ? "562" : "589"), dev->base_addr, dev->irq); for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); i = inl(ioaddr); printk(KERN_INFO " %dK FIFO split %s Rx:Tx, %s xcvr\n", (i & 7) ? 32 : 8, ram_split[(i >> 16) & 3], if_names[dev->if_port]); return;cs_failed: cs_error(link->handle, last_fn, last_ret);failed: tc589_release((u_long)link); return; } /* tc589_config *//*====================================================================== After a card is removed, tc589_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 tc589_release(u_long arg){ dev_link_t *link = (dev_link_t *)arg; DEBUG(0, "3c589_release(0x%p)\n", link); if (link->open) { DEBUG(1, "3c589_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); link->state &= ~DEV_CONFIG; } /* tc589_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 tc589_event(event_t event, int priority, event_callback_args_t *args){ dev_link_t *link = args->client_data; struct el3_private *lp = link->priv; struct net_device *dev = &lp->dev; DEBUG(1, "3c589_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 | DEV_CONFIG_PENDING; tc589_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) { CardServices(RequestConfiguration, link->handle, &link->conf); if (link->open) { tc589_reset(dev); netif_device_attach(dev); } } break; } return 0;} /* tc589_event *//*====================================================================*//* Use this for commands that may take time to finish*/static void tc589_wait_for_completion(struct net_device *dev, int cmd){ int i = 100; outw(cmd, dev->base_addr + EL3_CMD); while (--i > 0) if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; if (i == 0) printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n", dev->name, cmd);}/* Read a word from the EEPROM using the regular EEPROM access register. Assume that we are in register window zero.*/static u_short read_eeprom(ioaddr_t ioaddr, int index){ int i; outw(EEPROM_READ + index, ioaddr + 10); /* Reading the eeprom takes 162 us */ for (i = 1620; i >= 0; i--) if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0) break; return inw(ioaddr + 12);}/* Set transceiver type, perhaps to something other than what the user specified in dev->if_port.*/static void tc589_set_xcvr(struct net_device *dev, int if_port){ struct el3_private *lp = (struct el3_private *)dev->priv; ioaddr_t ioaddr = dev->base_addr; EL3WINDOW(0); switch (if_port) { case 0: case 1: outw(0, ioaddr + 6); break; case 2: outw(3<<14, ioaddr + 6); break; case 3: outw(1<<14, ioaddr + 6); break; } /* On PCMCIA, this just turns on the LED */ outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD); /* 10baseT interface, enable link beat and jabber check. */ EL3WINDOW(4); outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA); EL3WINDOW(1); if (if_port == 2) lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000); else lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);}static void dump_status(struct net_device *dev){ ioaddr_t ioaddr = dev->base_addr; EL3WINDOW(1); printk(KERN_INFO " irq status %04x, rx status %04x, tx status " "%02x tx free %04x\n", inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS), inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE)); EL3WINDOW(4); printk(KERN_INFO " diagnostics: fifo %04x net %04x ethernet %04x" " media %04x\n", inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08), inw(ioaddr+0x0a)); EL3WINDOW(1);}/* Reset and restore all of the 3c589 registers. */static void tc589_reset(struct net_device *dev){ ioaddr_t ioaddr = dev->base_addr; int i; EL3WINDOW(0); outw(0x0001, ioaddr + 4); /* Activate board. */ outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */ /* Set the station address in window 2. */ EL3WINDOW(2); for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + i); tc589_set_xcvr(dev, dev->if_port); /* Switch to the stats window, and clear all stats by reading. */ outw(StatsDisable, ioaddr + EL3_CMD); EL3WINDOW(6); for (i = 0; i < 9; i++) inb(ioaddr+i); inw(ioaddr + 10); inw(ioaddr + 12); /* Switch to register set 1 for normal use. */ EL3WINDOW(1); /* Accept b-cast and phys addr only. */ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ /* Allow status bits to be seen. */ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull | AdapterFailure, ioaddr + EL3_CMD);}static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr){ u32 ethcmd; /* dev_ioctl() in ../../net/core/dev.c has already checked capable(CAP_NET_ADMIN), so don't bother with that here. */ if (get_user(ethcmd, (u32 *)useraddr)) return -EFAULT; switch (ethcmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; strcpy (info.driver, DRV_NAME); strcpy (info.version, DRV_VERSION); sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr); if (copy_to_user (useraddr, &info, sizeof (info))) return -EFAULT; return 0; }#ifdef PCMCIA_DEBUG /* get message-level */ case ETHTOOL_GMSGLVL: { struct ethtool_value edata = {ETHTOOL_GMSGLVL}; edata.data = pc_debug; if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0; } /* set message-level */ case ETHTOOL_SMSGLVL: { struct ethtool_value edata; if (copy_from_user(&edata, useraddr, sizeof(edata))) return -EFAULT; pc_debug = edata.data; return 0; }#endif default: break; } return -EOPNOTSUPP;}static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd){ int rc; switch (cmd) { case SIOCETHTOOL: rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); break; default: rc = -EOPNOTSUPP; break; } return rc;}static int el3_config(struct net_device *dev, struct ifmap *map){ if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { if (map->port <= 3) { dev->if_port = map->port; printk(KERN_INFO "%s: switched to %s port\n", dev->name, if_names[dev->if_port]); tc589_set_xcvr(dev, dev->if_port); } else return -EINVAL; } return 0;}static int el3_open(struct net_device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; dev_link_t *link = &lp->link; if (!DEV_OK(link)) return -ENODEV; link->open++; MOD_INC_USE_COUNT; netif_start_queue(dev); tc589_reset(dev); lp->media.function = &media_check; lp->media.data = (u_long)lp; lp->media.expires = jiffies + HZ; add_timer(&lp->media); DEBUG(1, "%s: opened, status %4.4x.\n", dev->name, inw(dev->base_addr + EL3_STATUS)); return 0;}static void el3_tx_timeout(struct net_device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; ioaddr_t ioaddr = dev->base_addr; printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name); dump_status(dev); lp->stats.tx_errors++; dev->trans_start = jiffies; /* Issue TX_RESET and TX_START commands. */ tc589_wait_for_completion(dev, TxReset); outw(TxEnable, ioaddr + EL3_CMD); netif_wake_queue(dev);}static void pop_tx_status(struct net_device *dev){ struct el3_private *lp = (struct el3_private *)dev->priv; ioaddr_t ioaddr = dev->base_addr; int i; /* Clear the Tx status stack. */ for (i = 32; i > 0; i--) { u_char tx_status = inb(ioaddr + TX_STATUS); if (!(tx_status & 0x84)) break; /* reset transmitter on jabber error or underrun */ if (tx_status & 0x30) tc589_wait_for_completion(dev, TxReset); if (tx_status & 0x38) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -