--- asix.c Fri Aug 18 09:26:24 2006 +++ asix.c.new Thu Aug 24 08:41:01 2006 @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -// #define DEBUG // error path messages, extra info + #define DEBUG // error path messages, extra info // #define VERBOSE // more; success messages #include @@ -807,6 +807,298 @@ return 0; } + +static struct ethtool_ops ax88178_ethtool_ops = { + .get_drvinfo = asix_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_msglevel = usbnet_get_msglevel, + .set_msglevel = usbnet_set_msglevel, + .get_wol = asix_get_wol, + .set_wol = asix_set_wol, + .get_eeprom_len = asix_get_eeprom_len, + .get_eeprom = asix_get_eeprom, + .get_settings = asix_get_settings, + .set_settings = asix_set_settings, +}; + +static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf) +{ + int ret; + void *buf; + u8 data[8]; + u8 phy; + usbnet_get_endpoints(dev,intf); + + buf = kmalloc(6, GFP_KERNEL); + if(!buf) { + dbg ("Cannot allocate memory for buffer"); + ret = -ENOMEM; + goto out1; + } + + if ((ret = asix_sw_reset(dev, 0x004c)) < 0) + goto out2; + + msleep(5); + + memset(buf, 0, ETH_ALEN); + if ((ret = asix_read_cmd(dev, AX88772_CMD_READ_NODE_ID, + 0, 0, ETH_ALEN, buf)) < 0) { + dbg("Failed to read MAC address: %d", ret); + goto out2; + } + memcpy(dev->net->dev_addr, buf, ETH_ALEN); + + msleep(150); + + if ((asix_write_cmd( dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, data )) < 0 ) + { + dbg("Failed to read MAC address: %d", ret); + goto out2; + } + if ((ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, data )) < 0 ) + goto out2; + dbg( "%02x %02x\n", data[0], data[1] ); + + if ((asix_write_cmd( dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, data )) < 0 ) + goto out2; + + if ((asix_write_cmd( dev, AX_CMD_WRITE_GPIOS, 0x008c, 0, 0, data )) < 0 ) + goto out2; + + if ((asix_write_cmd( dev, AX_CMD_WRITE_GPIOS, 0x001c, 0, 0, data )) < 0 ) + goto out2; + + if ((asix_write_cmd( dev, AX_CMD_WRITE_GPIOS, 0x003c, 0, 0, data )) < 0 ) + goto out2; + + if ((asix_write_cmd( dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, data )) < 0 ) + goto out2; + + msleep(150); + + if ((ret = asix_sw_reset(dev, 0x0048)) < 0) + goto out2; + + if ((asix_write_cmd( dev, AX_CMD_WRITE_RX_CTL, 0, 0, 0, data )) < 0 ) + goto out2; + + if ((ret = asix_set_sw_mii(dev)) < 0) + goto out2; + + if ((ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, data )) < 0) + goto out2; + phy = data[1]; + dbg( "%02x %02x\n", data[0], data[1] ); + + if ((ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy, 0x0002, 2, data )) < 0) + goto out2; + dbg( "%02x %02x\n", data[0], data[1] ); + + if ((ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy, 0x001b, 2, data )) < 0) + goto out2; + dbg( "%02x %02x\n", data[0], data[1] ); + + data[0] = 0x00; + data[1] = 0x80; + if ((asix_write_cmd( dev, AX_CMD_WRITE_MII_REG, phy, 0, 2, data)) < 0 ) + goto out2; + if ((ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy, 0x0000, 2, data )) < 0) + goto out2; + dbg( "%02x %02x\n", data[0], data[1] ); + + if ((ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy, 0x0018, 2, data )) < 0) + goto out2; + dbg( "%02x %02x\n", data[0], data[1] ); + + data[0] = 0x41; + data[1] = 0x43; + if ((asix_write_cmd( dev, AX_CMD_WRITE_MII_REG, phy, 0x0018, 2, data )) < 0 ) + goto out2; + data[0] = 0xe1; + data[1] = 0x05; + if ((asix_write_cmd( dev, AX_CMD_WRITE_MII_REG, phy, 0x0004, 2, data )) < 0 ) + goto out2; + data[0] = 0x00; + data[1] = 0x02; + if ((asix_write_cmd( dev, AX_CMD_WRITE_MII_REG,phy, 0x0009, 2, data )) < 0 ) + goto out2; + data[0] = 0x00; + data[1] = 0x12; + if ((asix_write_cmd( dev, AX_CMD_WRITE_MII_REG, phy, 0x0000, 2, data )) < 0 ) + goto out2; + if ((ret = asix_set_hw_mii(dev)) < 0) + goto out2; + + /* Initialize MII structure */ + dev->mii.dev = dev->net; + dev->mii.mdio_read = asix_mdio_read; + dev->mii.mdio_write = asix_mdio_write; + dev->mii.phy_id_mask = 0xff; + dev->mii.reg_num_mask = 0xff; + dev->net->do_ioctl = asix_ioctl; + dev->mii.phy_id = phy; + + msleep(150); + + dev->net->set_multicast_list = asix_set_multicast; + dev->net->ethtool_ops = &ax88178_ethtool_ops; + + mii_nway_restart(&dev->mii); + + + if ((asix_write_cmd( dev, AX_CMD_WRITE_MEDIUM_MODE, 0x013f, 0x0000, 0, data )) < 0 ) + goto out2; + if ((asix_write_cmd( dev, AX_CMD_WRITE_IPG0, 0x0c15, 0x000e, 0, data )) < 0 ) + goto out2; + if ((asix_write_cmd( dev, AX_CMD_WRITE_IPG0, 0x0c15, 0x000e, 0, data )) < 0 ) + goto out2; + if ((asix_write_cmd( dev, AX_CMD_WRITE_RX_CTL, 0x0388, 0, 0, data )) < 0 ) + goto out2; + if ((asix_write_cmd( dev, AX_CMD_WRITE_RX_CTL, 0x0398, 0, 0, data )) < 0 ) + goto out2; + + kfree(buf); + /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ + if (dev->driver_info->flags & FLAG_FRAMING_AX) { + /* hard_mtu is still the default - the device does not support + jumbo eth frames */ + dev->rx_urb_size = 2048; + } + + return 0; + +out2: + kfree(buf); +out1: + return ret; +} + +static int ax88178_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ + u8 *head; + u32 header; + char *packet; + struct sk_buff *ax_skb; + u16 size; + + head = (u8 *) skb->data; + memcpy(&header, head, sizeof(header)); + le32_to_cpus(&header); + packet = head + sizeof(header); + + skb_pull(skb, 4); + + while (skb->len > 0) { + if ((short)(header & 0x0000ffff) != + ~((short)((header & 0xffff0000) >> 16))) { + devdbg(dev,"header length data is error"); + } + /* get the packet length */ + size = (u16) (header & 0x0000ffff); + + if ((skb->len) - ((size + 1) & 0xfffe) == 0) + return 2; + if (size > ETH_FRAME_LEN) { + devdbg(dev,"invalid rx length %d", size); + return 0; + } + ax_skb = skb_clone(skb, GFP_ATOMIC); + if (ax_skb) { + ax_skb->len = size; + ax_skb->data = packet; + ax_skb->tail = packet + size; + usbnet_skb_return(dev, ax_skb); + } else { + return 0; + } + + skb_pull(skb, (size + 1) & 0xfffe); + + if (skb->len == 0) + break; + + head = (u8 *) skb->data; + memcpy(&header, head, sizeof(header)); + le32_to_cpus(&header); + packet = head + sizeof(header); + skb_pull(skb, 4); + } + + if (skb->len < 0) { + devdbg(dev,"invalid rx length %d", skb->len); + return 0; + } + return 1; +} + +static struct sk_buff *ax88178_tx_fixup(struct usbnet *dev, struct sk_buff *skb, + gfp_t flags) +{ + int padlen; + int headroom = skb_headroom(skb); + int tailroom = skb_tailroom(skb); + u32 packet_len; + u32 padbytes = 0xffff0000; + + padlen = ((skb->len + 4) % 512) ? 0 : 4; + + if ((!skb_cloned(skb)) + && ((headroom + tailroom) >= (4 + padlen))) { + if ((headroom < 4) || (tailroom < padlen)) { + skb->data = memmove(skb->head + 4, skb->data, skb->len); + skb->tail = skb->data + skb->len; + } + } else { + struct sk_buff *skb2; + skb2 = skb_copy_expand(skb, 4, padlen, flags); + dev_kfree_skb_any(skb); + skb = skb2; + if (!skb) + return NULL; + } + + skb_push(skb, 4); + packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); + memcpy(skb->data, &packet_len, sizeof(packet_len)); + + if ((skb->len % 512) == 0) { + memcpy( skb->tail, &padbytes, sizeof(padbytes)); + skb_put(skb, sizeof(padbytes)); + } + return skb; +} + +static int ax88178_link_reset(struct usbnet *dev) +{ + u16 lpa; + u16 adv; + u16 res; + u16 mode; + + mode = 0x0; + lpa = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA); + adv = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE); + res = mii_nway_result(lpa|adv); + + if ((res & (LPA_DUPLEX | LPA_1000XFULL) ) == 0) + mode = 0x0002; + if ((res & (LPA_10HALF | LPA_10FULL)) != 0) + mode |= 0x0104; + if ((res & LPA_100) != 0) + mode |= 0x0334; + if ((res & (LPA_1000XFULL | LPA_1000XHALF)) != 0) + mode |= 0x013f; + + asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); + + return 0; +} + + + + + static const struct driver_info ax8817x_info = { .description = "ASIX AX8817x USB 2.0 Ethernet", .bind = ax88172_bind, @@ -858,6 +1150,17 @@ .tx_fixup = ax88772_tx_fixup, }; +static const struct driver_info ax88178_info = { + .description = "ASIX AX88178 USB 2.0 Ethernet", + .bind = ax88178_bind, + .status = asix_status, + .link_reset = ax88178_link_reset, + .reset = ax88178_link_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88178_rx_fixup, + .tx_fixup = ax88178_tx_fixup, +}; + static const struct usb_device_id products [] = { { // Linksys USB200M @@ -919,6 +1222,10 @@ // 0Q0 cable ethernet USB_DEVICE (0x1557, 0x7720), .driver_info = (unsigned long) &ax88772_info, +}, { + // ASIX AX88178 10/100/1000 + USB_DEVICE (0x0b95, 0x1780), + .driver_info = (unsigned long) &ax88178_info, }, { }, // END }; @@ -933,6 +1240,7 @@ .disconnect = usbnet_disconnect, }; + static int __init asix_init(void) { return usb_register(&asix_driver);