# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.526 -> 1.527 # drivers/usb/Config.help 1.5 -> 1.6 # drivers/usb/catc.c 1.9 -> 1.10 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/03/17 spse@secret.org.uk 1.527 # USB catc driver # # Here is a patch to add support for F5U011 to catc.c driver. The # patch has been compile tested against 2.5.6 and 2.5.7pre1 # (and tested against 2.5.5-dj1) and should apply cleanly. # -------------------------------------------- # diff -Nru a/drivers/usb/Config.help b/drivers/usb/Config.help --- a/drivers/usb/Config.help Sun Mar 17 11:01:36 2002 +++ b/drivers/usb/Config.help Sun Mar 17 11:01:36 2002 @@ -382,6 +382,7 @@ CONFIG_USB_CATC Say Y if you want to use one of the following 10Mbps USB Ethernet device based on the EL1210A chip. Supported devices are: + Belkin F5U011 Belkin F5U111 CATC NetMate CATC NetMate II diff -Nru a/drivers/usb/catc.c b/drivers/usb/catc.c --- a/drivers/usb/catc.c Sun Mar 17 11:01:36 2002 +++ b/drivers/usb/catc.c Sun Mar 17 11:01:36 2002 @@ -7,6 +7,9 @@ * * Based on the work of * Donald Becker + * + * Old chipset support added by Simon Evans 2002 + * - adds support for Belkin F5U011 */ /* @@ -70,6 +73,7 @@ #define RX_MAX_BURST 15 /* Max packets per rx buffer (> 0, < 16) */ #define TX_MAX_BURST 15 /* Max full sized packets per tx buffer (> 0) */ #define CTRL_QUEUE 16 /* Max control requests in flight (power of two) */ +#define RX_PKT_SZ 1600 /* Max size of receive packet for F5U011 */ /* * Control requests. @@ -80,6 +84,7 @@ GetMac = 0xf2, Reset = 0xf4, SetMac = 0xf5, + SetRxMode = 0xf5, /* F5U011 only */ WriteROM = 0xf8, SetReg = 0xfa, GetReg = 0xfb, @@ -127,6 +132,7 @@ RxForceOK = 0x04, RxMultiCast = 0x08, RxPromisc = 0x10, + AltRxPromisc = 0x20, /* F5U011 uses different bit */ }; enum led_values { @@ -137,6 +143,12 @@ LEDLink = 0x08, }; +enum link_status { + LinkNoChange = 0, + LinkGood = 1, + LinkBad = 2 +}; + /* * The catc struct. */ @@ -180,6 +192,10 @@ } ctrl_queue[CTRL_QUEUE]; struct urb *tx_urb, *rx_urb, *irq_urb, *ctrl_urb; + + u8 is_f5u011; /* Set if device is an F5U011 */ + u8 rxmode[2]; /* Used for F5U011 */ + atomic_t recq_sz; /* Used for F5U011 - counter of waiting rx packets */ }; /* @@ -193,6 +209,10 @@ #define catc_write_mem(catc, addr, buf, size) catc_ctrl_msg(catc, USB_DIR_OUT, WriteMem, 0, addr, buf, size) #define catc_read_mem(catc, addr, buf, size) catc_ctrl_msg(catc, USB_DIR_IN, ReadMem, 0, addr, buf, size) +#define f5u011_rxmode(catc, rxmode) catc_ctrl_msg(catc, USB_DIR_OUT, SetRxMode, 0, 1, rxmode, 2) +#define f5u011_rxmode_async(catc, rxmode) catc_ctrl_async(catc, USB_DIR_OUT, SetRxMode, 0, 1, &rxmode, 2, NULL) +#define f5u011_mchash_async(catc, hash) catc_ctrl_async(catc, USB_DIR_OUT, SetRxMode, 0, 2, &hash, 8, NULL) + #define catc_set_reg_async(catc, reg, val) catc_ctrl_async(catc, USB_DIR_OUT, SetReg, val, reg, NULL, 0, NULL) #define catc_get_reg_async(catc, reg, cb) catc_ctrl_async(catc, USB_DIR_IN, GetReg, 0, reg, NULL, 1, cb) #define catc_write_mem_async(catc, addr, buf, size) catc_ctrl_async(catc, USB_DIR_OUT, WriteMem, 0, addr, buf, size, NULL) @@ -206,9 +226,12 @@ struct catc *catc = urb->context; u8 *pkt_start = urb->transfer_buffer; struct sk_buff *skb; - int pkt_len; + int pkt_len, pkt_offset = 0; - clear_bit(RX_RUNNING, &catc->flags); + if (!catc->is_f5u011) { + clear_bit(RX_RUNNING, &catc->flags); + pkt_offset = 2; + } if (urb->status) { dbg("rx_done, status %d, length %d", urb->status, urb->actual_length); @@ -216,19 +239,22 @@ } do { - pkt_len = le16_to_cpup((u16*)pkt_start); - - if (pkt_len > urb->actual_length) { - catc->stats.rx_length_errors++; - catc->stats.rx_errors++; - break; + if(!catc->is_f5u011) { + pkt_len = le16_to_cpup((u16*)pkt_start); + if (pkt_len > urb->actual_length) { + catc->stats.rx_length_errors++; + catc->stats.rx_errors++; + break; + } + } else { + pkt_len = urb->actual_length; } if (!(skb = dev_alloc_skb(pkt_len))) return; skb->dev = catc->netdev; - eth_copy_and_sum(skb, pkt_start + 2, pkt_len, 0); + eth_copy_and_sum(skb, pkt_start + pkt_offset, pkt_len, 0); skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, catc->netdev); @@ -237,11 +263,28 @@ catc->stats.rx_packets++; catc->stats.rx_bytes += pkt_len; + /* F5U011 only does one packet per RX */ + if (catc->is_f5u011) + break; pkt_start += (((pkt_len + 1) >> 6) + 1) << 6; } while (pkt_start - (u8 *) urb->transfer_buffer < urb->actual_length); catc->netdev->last_rx = jiffies; + + if (catc->is_f5u011) { + if (atomic_read(&catc->recq_sz)) { + int status; + atomic_dec(&catc->recq_sz); + dbg("getting extra packet"); + urb->dev = catc->usbdev; + if ((status = usb_submit_urb(urb, GFP_KERNEL)) < 0) { + dbg("submit(rx_urb) status %d", status); + } + } else { + clear_bit(RX_RUNNING, &catc->flags); + } + } } static void catc_irq_done(struct urb *urb) @@ -249,29 +292,48 @@ struct catc *catc = urb->context; u8 *data = urb->transfer_buffer; int status; + unsigned int hasdata = 0, linksts = LinkNoChange; + + if (!catc->is_f5u011) { + hasdata = data[1] & 0x80; + if (data[1] & 0x40) + linksts = LinkGood; + else if (data[1] & 0x20) + linksts = LinkBad; + } else { + hasdata = (unsigned int)(be16_to_cpup((u16*)data) & 0x0fff); + if (data[0] == 0x90) + linksts = LinkGood; + else if (data[0] == 0xA0) + linksts = LinkBad; + } if (urb->status) { dbg("irq_done, status %d, data %02x %02x.", urb->status, data[0], data[1]); return; } - if ((data[1] & 0x80) && !test_and_set_bit(RX_RUNNING, &catc->flags)) { - catc->rx_urb->dev = catc->usbdev; - if ((status = usb_submit_urb(catc->rx_urb, GFP_KERNEL)) < 0) { - err("submit(rx_urb) status %d", status); - return; - } - } - - if (data[1] & 0x40) { + if (linksts == LinkGood) { netif_carrier_on(catc->netdev); dbg("link ok"); } - if (data[1] & 0x20) { + if (linksts == LinkBad) { netif_carrier_off(catc->netdev); dbg("link bad"); } + + if (hasdata) { + if (test_and_set_bit(RX_RUNNING, &catc->flags)) { + if (catc->is_f5u011) + atomic_inc(&catc->recq_sz); + } else { + catc->rx_urb->dev = catc->usbdev; + if ((status = usb_submit_urb(catc->rx_urb, GFP_KERNEL)) < 0) { + err("submit(rx_urb) status %d", status); + } + } + } } /* @@ -282,6 +344,9 @@ { int status; + if (catc->is_f5u011) + catc->tx_ptr = (catc->tx_ptr + 63) & ~63; + catc->tx_urb->transfer_buffer_length = catc->tx_ptr; catc->tx_urb->transfer_buffer = catc->tx_buf[catc->tx_idx]; catc->tx_urb->dev = catc->usbdev; @@ -338,14 +403,15 @@ catc->tx_ptr = (((catc->tx_ptr - 1) >> 6) + 1) << 6; tx_buf = catc->tx_buf[catc->tx_idx] + catc->tx_ptr; - *((u16*)tx_buf) = cpu_to_le16((u16)skb->len); + *((u16*)tx_buf) = (catc->is_f5u011) ? cpu_to_be16((u16)skb->len) : cpu_to_le16((u16)skb->len); memcpy(tx_buf + 2, skb->data, skb->len); catc->tx_ptr += skb->len + 2; if (!test_and_set_bit(TX_RUNNING, &catc->flags)) catc_tx_run(catc); - if (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2))) + if ((catc->is_f5u011 && catc->tx_ptr) + || (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2)))) netif_stop_queue(netdev); spin_unlock_irqrestore(&catc->tx_lock, flags); @@ -554,17 +620,32 @@ if (netdev->flags & IFF_PROMISC) { memset(catc->multicast, 0xff, 64); - rx |= RxPromisc; + rx |= (!catc->is_f5u011) ? RxPromisc : AltRxPromisc; } - if (netdev->flags & IFF_ALLMULTI) + if (netdev->flags & IFF_ALLMULTI) { memset(catc->multicast, 0xff, 64); - - for (i = 0, mc = netdev->mc_list; mc && i < netdev->mc_count; i++, mc = mc->next) - catc_multicast(mc->dmi_addr, catc->multicast); - - catc_set_reg_async(catc, RxUnit, rx); - catc_write_mem_async(catc, 0xfa80, catc->multicast, 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); + } + } } /* @@ -591,6 +672,29 @@ 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}; @@ -632,7 +736,8 @@ netif_start_queue(netdev); - mod_timer(&catc->timer, jiffies + STATS_UPDATE); + if (!catc->is_f5u011) + mod_timer(&catc->timer, jiffies + STATS_UPDATE); return 0; } @@ -643,7 +748,8 @@ netif_stop_queue(netdev); - del_timer_sync(&catc->timer); + if (!catc->is_f5u011) + del_timer_sync(&catc->timer); usb_unlink_urb(catc->rx_urb); usb_unlink_urb(catc->tx_urb); @@ -662,7 +768,7 @@ struct net_device *netdev; struct catc *catc; u8 broadcast[6]; - int i; + int i, pktsz; if (usb_set_interface(usbdev, ifnum, 1)) { err("Can't set altsetting 1."); @@ -670,9 +776,16 @@ } catc = kmalloc(sizeof(struct catc), GFP_KERNEL); + if (!catc) + return NULL; + memset(catc, 0, sizeof(struct catc)); netdev = init_etherdev(0, 0); + if (!netdev) { + kfree(catc); + return NULL; + } netdev->open = catc_open; netdev->hard_start_xmit = catc_hard_start_xmit; @@ -701,9 +814,26 @@ 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); + kfree(netdev); + kfree(catc); return NULL; } + /* 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); + } + FILL_CONTROL_URB(catc->ctrl_urb, usbdev, usb_sndctrlpipe(usbdev, 0), NULL, NULL, 0, catc_ctrl_done, catc); @@ -711,20 +841,21 @@ NULL, 0, catc_tx_done, catc); FILL_BULK_URB(catc->rx_urb, usbdev, usb_rcvbulkpipe(usbdev, 1), - catc->rx_buf, RX_MAX_BURST * (PKT_SZ + 2), catc_rx_done, catc); + catc->rx_buf, pktsz, catc_rx_done, catc); FILL_INT_URB(catc->irq_urb, usbdev, usb_rcvintpipe(usbdev, 2), catc->irq_buf, 2, catc_irq_done, catc, 1); - 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); + if (!catc->is_f5u011) { + dbg("Checking memory size\n"); - switch (i) { + 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); @@ -737,44 +868,52 @@ 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("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, 8); - 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); - dbg("Init done."); - - printk(KERN_INFO "%s: CATC EL1210A NetMate USB Ethernet at usb%d:%d.%d, ", - netdev->name, usbdev->bus->busnum, usbdev->devnum, ifnum); + printk(KERN_INFO "%s: %s USB Ethernet at usb%d:%d.%d, ", + netdev->name, (catc->is_f5u011) ? "Belkin F5U011" : "CATC EL1210A NetMate", + usbdev->bus->busnum, usbdev->devnum, ifnum); for (i = 0; i < 5; i++) printk("%2.2x:", netdev->dev_addr[i]); printk("%2.2x.\n", netdev->dev_addr[i]); - return catc; } @@ -795,7 +934,7 @@ */ static struct usb_device_id catc_id_table [] = { - { USB_DEVICE(0x0423, 0xa) }, /* CATC Netmate */ + { USB_DEVICE(0x0423, 0xa) }, /* CATC Netmate, Belkin F5U011 */ { USB_DEVICE(0x0423, 0xc) }, /* CATC Netmate II, Belkin F5U111 */ { USB_DEVICE(0x08d1, 0x1) }, /* smartBridges smartNIC */ { }