catc.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,005 行 · 第 1/2 页
C
1,005 行
if (q->dir) { if (q->buf && q->len) memcpy(q->buf, catc->ctrl_buf, q->len); else q->buf = catc->ctrl_buf; } if (q->callback) q->callback(catc, q); catc->ctrl_tail = (catc->ctrl_tail + 1) & (CTRL_QUEUE - 1); if (catc->ctrl_head != catc->ctrl_tail) catc_ctrl_run(catc); else clear_bit(CTRL_RUNNING, &catc->flags); spin_unlock_irqrestore(&catc->ctrl_lock, flags);}static int catc_ctrl_async(struct catc *catc, u8 dir, u8 request, u16 value, u16 index, void *buf, int len, void (*callback)(struct catc *catc, struct ctrl_queue *q)){ struct ctrl_queue *q; int retval = 0; unsigned long flags; spin_lock_irqsave(&catc->ctrl_lock, flags); q = catc->ctrl_queue + catc->ctrl_head; q->dir = dir; q->request = request; q->value = value; q->index = index; q->buf = buf; q->len = len; q->callback = callback; catc->ctrl_head = (catc->ctrl_head + 1) & (CTRL_QUEUE - 1); if (catc->ctrl_head == catc->ctrl_tail) { err("ctrl queue full"); catc->ctrl_tail = (catc->ctrl_tail + 1) & (CTRL_QUEUE - 1); retval = -1; } if (!test_and_set_bit(CTRL_RUNNING, &catc->flags)) catc_ctrl_run(catc); spin_unlock_irqrestore(&catc->ctrl_lock, flags); return retval;}/* * Statistics. */static void catc_stats_done(struct catc *catc, struct ctrl_queue *q){ int index = q->index - EthStats; u16 data, last; catc->stats_buf[index] = *((char *)q->buf); if (index & 1) return; data = ((u16)catc->stats_buf[index] << 8) | catc->stats_buf[index + 1]; last = catc->stats_vals[index >> 1]; switch (index) { case TxSingleColl: case TxMultiColl: catc->stats.collisions += data - last; break; case TxExcessColl: catc->stats.tx_aborted_errors += data - last; catc->stats.tx_errors += data - last; break; case RxFramErr: catc->stats.rx_frame_errors += data - last; catc->stats.rx_errors += data - last; break; } catc->stats_vals[index >> 1] = data;}static void catc_stats_timer(unsigned long data){ struct catc *catc = (void *) data; int i; for (i = 0; i < 8; i++) catc_get_reg_async(catc, EthStats + 7 - i, catc_stats_done); mod_timer(&catc->timer, jiffies + STATS_UPDATE);}static struct net_device_stats *catc_get_stats(struct net_device *netdev){ struct catc *catc = netdev->priv; return &catc->stats;}/* * Receive modes. Broadcast, Multicast, Promisc. */static void catc_multicast(unsigned char *addr, u8 *multicast){ u32 crc; crc = ether_crc_le(6, addr); multicast[(crc >> 3) & 0x3f] |= 1 << (crc & 7);}static void catc_set_multicast_list(struct net_device *netdev){ struct catc *catc = netdev->priv; struct dev_mc_list *mc; u8 broadcast[6]; u8 rx = RxEnable | RxPolarity | RxMultiCast; int i; memset(broadcast, 0xff, 6); memset(catc->multicast, 0, 64); catc_multicast(broadcast, catc->multicast); catc_multicast(netdev->dev_addr, catc->multicast); if (netdev->flags & IFF_PROMISC) { memset(catc->multicast, 0xff, 64); rx |= (!catc->is_f5u011) ? RxPromisc : AltRxPromisc; } if (netdev->flags & IFF_ALLMULTI) { memset(catc->multicast, 0xff, 64); } else { for (i = 0, mc = netdev->mc_list; mc && i < netdev->mc_count; i++, mc = mc->next) { u32 crc = ether_crc_le(6, mc->dmi_addr); if (!catc->is_f5u011) { catc->multicast[(crc >> 3) & 0x3f] |= 1 << (crc & 7); } else { catc->multicast[7-(crc >> 29)] |= 1 << ((crc >> 26) & 7); } } } if (!catc->is_f5u011) { catc_set_reg_async(catc, RxUnit, rx); catc_write_mem_async(catc, 0xfa80, catc->multicast, 64); } else { f5u011_mchash_async(catc, catc->multicast); if (catc->rxmode[0] != rx) { catc->rxmode[0] = rx; dbg("Setting RX mode to %2.2X %2.2X", catc->rxmode[0], catc->rxmode[1]); f5u011_rxmode_async(catc, catc->rxmode); } }}/* * ioctl's */static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr){ struct catc *catc = dev->priv; u32 cmd; if (get_user(cmd, (u32 __user *)useraddr)) return -EFAULT; switch (cmd) { /* get driver info */ case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; strncpy(info.driver, driver_name, ETHTOOL_BUSINFO_LEN); strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); usb_make_path (catc->usbdev, info.bus_info, sizeof info.bus_info); if (copy_to_user(useraddr, &info, sizeof(info))) return -EFAULT; return 0; } /* get settings */ case ETHTOOL_GSET: if (catc->is_f5u011) { struct ethtool_cmd ecmd = { ETHTOOL_GSET, SUPPORTED_10baseT_Half | SUPPORTED_TP, ADVERTISED_10baseT_Half | ADVERTISED_TP, SPEED_10, DUPLEX_HALF, PORT_TP, 0, XCVR_INTERNAL, AUTONEG_DISABLE, 1, 1 }; if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; } else { return -EOPNOTSUPP; } /* get link status */ case ETHTOOL_GLINK: { struct ethtool_value edata = {ETHTOOL_GLINK}; edata.data = netif_carrier_ok(dev); if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0; } } return -EOPNOTSUPP;}static int catc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ switch(cmd) { case SIOCETHTOOL: return netdev_ethtool_ioctl(dev, rq->ifr_data); default: return -EOPNOTSUPP; }}/* * Open, close. */static int catc_open(struct net_device *netdev){ struct catc *catc = netdev->priv; int status; catc->irq_urb->dev = catc->usbdev; if ((status = usb_submit_urb(catc->irq_urb, GFP_KERNEL)) < 0) { err("submit(irq_urb) status %d", status); return -1; } netif_start_queue(netdev); if (!catc->is_f5u011) mod_timer(&catc->timer, jiffies + STATS_UPDATE); return 0;}static int catc_stop(struct net_device *netdev){ struct catc *catc = netdev->priv; netif_stop_queue(netdev); if (!catc->is_f5u011) del_timer_sync(&catc->timer); usb_unlink_urb(catc->rx_urb); usb_unlink_urb(catc->tx_urb); usb_unlink_urb(catc->irq_urb); usb_unlink_urb(catc->ctrl_urb); return 0;}/* * USB probe, disconnect. */static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id){ struct usb_device *usbdev = interface_to_usbdev(intf); struct net_device *netdev; struct catc *catc; u8 broadcast[6]; int i, pktsz; if (usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 1)) { err("Can't set altsetting 1."); return -EIO; } catc = kmalloc(sizeof(struct catc), GFP_KERNEL); if (!catc) return -ENOMEM; memset(catc, 0, sizeof(struct catc)); netdev = alloc_etherdev(0); if (!netdev) { kfree(catc); return -EIO; } netdev->open = catc_open; netdev->hard_start_xmit = catc_hard_start_xmit; netdev->stop = catc_stop; netdev->get_stats = catc_get_stats; netdev->tx_timeout = catc_tx_timeout; netdev->watchdog_timeo = TX_TIMEOUT; netdev->set_multicast_list = catc_set_multicast_list; netdev->do_ioctl = catc_ioctl; netdev->priv = catc; catc->usbdev = usbdev; catc->netdev = netdev; catc->tx_lock = SPIN_LOCK_UNLOCKED; catc->ctrl_lock = SPIN_LOCK_UNLOCKED; init_timer(&catc->timer); catc->timer.data = (long) catc; catc->timer.function = catc_stats_timer; catc->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); catc->tx_urb = usb_alloc_urb(0, GFP_KERNEL); catc->rx_urb = usb_alloc_urb(0, GFP_KERNEL); catc->irq_urb = usb_alloc_urb(0, GFP_KERNEL); if ((!catc->ctrl_urb) || (!catc->tx_urb) || (!catc->rx_urb) || (!catc->irq_urb)) { err("No free urbs available."); if (catc->ctrl_urb) usb_free_urb(catc->ctrl_urb); if (catc->tx_urb) usb_free_urb(catc->tx_urb); if (catc->rx_urb) usb_free_urb(catc->rx_urb); if (catc->irq_urb) usb_free_urb(catc->irq_urb); free_netdev(netdev); kfree(catc); return -ENOMEM; } /* The F5U011 has the same vendor/product as the netmate but a device version of 0x130 */ if (usbdev->descriptor.idVendor == 0x0423 && usbdev->descriptor.idProduct == 0xa && catc->usbdev->descriptor.bcdDevice == 0x0130 ) { dbg("Testing for f5u011"); catc->is_f5u011 = 1; atomic_set(&catc->recq_sz, 0); pktsz = RX_PKT_SZ; } else { pktsz = RX_MAX_BURST * (PKT_SZ + 2); } usb_fill_control_urb(catc->ctrl_urb, usbdev, usb_sndctrlpipe(usbdev, 0), NULL, NULL, 0, catc_ctrl_done, catc); usb_fill_bulk_urb(catc->tx_urb, usbdev, usb_sndbulkpipe(usbdev, 1), NULL, 0, catc_tx_done, catc); usb_fill_bulk_urb(catc->rx_urb, usbdev, usb_rcvbulkpipe(usbdev, 1), catc->rx_buf, pktsz, catc_rx_done, catc); usb_fill_int_urb(catc->irq_urb, usbdev, usb_rcvintpipe(usbdev, 2), catc->irq_buf, 2, catc_irq_done, catc, 1); if (!catc->is_f5u011) { dbg("Checking memory size\n"); i = 0x12345678; catc_write_mem(catc, 0x7a80, &i, 4); i = 0x87654321; catc_write_mem(catc, 0xfa80, &i, 4); catc_read_mem(catc, 0x7a80, &i, 4); switch (i) { case 0x12345678: catc_set_reg(catc, TxBufCount, 8); catc_set_reg(catc, RxBufCount, 32); dbg("64k Memory\n"); break; default: warn("Couldn't detect memory size, assuming 32k"); case 0x87654321: catc_set_reg(catc, TxBufCount, 4); catc_set_reg(catc, RxBufCount, 16); dbg("32k Memory\n"); break; } dbg("Getting MAC from SEEROM."); catc_get_mac(catc, netdev->dev_addr); dbg("Setting MAC into registers."); for (i = 0; i < 6; i++) catc_set_reg(catc, StationAddr0 - i, netdev->dev_addr[i]); dbg("Filling the multicast list."); memset(broadcast, 0xff, 6); catc_multicast(broadcast, catc->multicast); catc_multicast(netdev->dev_addr, catc->multicast); catc_write_mem(catc, 0xfa80, catc->multicast, 64); dbg("Clearing error counters."); for (i = 0; i < 8; i++) catc_set_reg(catc, EthStats + i, 0); catc->last_stats = jiffies; dbg("Enabling."); catc_set_reg(catc, MaxBurst, RX_MAX_BURST); catc_set_reg(catc, OpModes, OpTxMerge | OpRxMerge | OpLenInclude | Op3MemWaits); catc_set_reg(catc, LEDCtrl, LEDLink); catc_set_reg(catc, RxUnit, RxEnable | RxPolarity | RxMultiCast); } else { dbg("Performing reset\n"); catc_reset(catc); catc_get_mac(catc, netdev->dev_addr); dbg("Setting RX Mode"); catc->rxmode[0] = RxEnable | RxPolarity | RxMultiCast; catc->rxmode[1] = 0; f5u011_rxmode(catc, catc->rxmode); } dbg("Init done."); printk(KERN_INFO "%s: %s USB Ethernet at usb-%s-%s, ", netdev->name, (catc->is_f5u011) ? "Belkin F5U011" : "CATC EL1210A NetMate", usbdev->bus->bus_name, usbdev->devpath); for (i = 0; i < 5; i++) printk("%2.2x:", netdev->dev_addr[i]); printk("%2.2x.\n", netdev->dev_addr[i]); usb_set_intfdata(intf, catc); SET_NETDEV_DEV(netdev, &intf->dev); if (register_netdev(netdev) != 0) { usb_set_intfdata(intf, NULL); usb_free_urb(catc->ctrl_urb); usb_free_urb(catc->tx_urb); usb_free_urb(catc->rx_urb); usb_free_urb(catc->irq_urb); free_netdev(netdev); kfree(catc); return -EIO; } return 0;}static void catc_disconnect(struct usb_interface *intf){ struct catc *catc = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); if (catc) { unregister_netdev(catc->netdev); usb_free_urb(catc->ctrl_urb); usb_free_urb(catc->tx_urb); usb_free_urb(catc->rx_urb); usb_free_urb(catc->irq_urb); free_netdev(catc->netdev); kfree(catc); }}/* * Module functions and tables. */static struct usb_device_id catc_id_table [] = { { USB_DEVICE(0x0423, 0xa) }, /* CATC Netmate, Belkin F5U011 */ { USB_DEVICE(0x0423, 0xc) }, /* CATC Netmate II, Belkin F5U111 */ { USB_DEVICE(0x08d1, 0x1) }, /* smartBridges smartNIC */ { }};MODULE_DEVICE_TABLE(usb, catc_id_table);static struct usb_driver catc_driver = { .owner = THIS_MODULE, .name = driver_name, .probe = catc_probe, .disconnect = catc_disconnect, .id_table = catc_id_table,};static int __init catc_init(void){ int result = usb_register(&catc_driver); if (result == 0) info(DRIVER_VERSION " " DRIVER_DESC); return result;}static void __exit catc_exit(void){ usb_deregister(&catc_driver);}module_init(catc_init);module_exit(catc_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?