diff options
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/Kconfig | 8 | ||||
-rw-r--r-- | drivers/net/usb/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/usb/asix.h | 2 | ||||
-rw-r--r-- | drivers/net/usb/asix_devices.c | 5 | ||||
-rw-r--r-- | drivers/net/usb/ax88172a.c | 8 | ||||
-rw-r--r-- | drivers/net/usb/ax88179_178a.c | 41 | ||||
-rw-r--r-- | drivers/net/usb/cdc_mbim.c | 4 | ||||
-rw-r--r-- | drivers/net/usb/hso.c | 15 | ||||
-rw-r--r-- | drivers/net/usb/qmi_wwan.c | 2 | ||||
-rw-r--r-- | drivers/net/usb/r8152.c | 971 | ||||
-rw-r--r-- | drivers/net/usb/r815x.c | 62 | ||||
-rw-r--r-- | drivers/net/usb/smsc75xx.c | 12 | ||||
-rw-r--r-- | drivers/net/usb/sr9700.c | 560 | ||||
-rw-r--r-- | drivers/net/usb/sr9700.h | 173 | ||||
-rw-r--r-- | drivers/net/usb/usbnet.c | 103 |
15 files changed, 1640 insertions, 327 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index d84bfd4109a4..40db31233313 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -268,6 +268,14 @@ config USB_NET_DM9601 This option adds support for Davicom DM9601 based USB 1.1 10/100 Ethernet adapters. +config USB_NET_SR9700 + tristate "CoreChip-sz SR9700 based USB 1.1 10/100 ethernet devices" + depends on USB_USBNET + select CRC32 + help + This option adds support for CoreChip-sz SR9700 based USB 1.1 + 10/100 Ethernet adapters. + config USB_NET_SMSC75XX tristate "SMSC LAN75XX based USB 2.0 gigabit ethernet devices" depends on USB_USBNET diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index e8171784529d..8b342cf992fd 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_USB_NET_AX88179_178A) += ax88179_178a.o obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o r815x.o obj-$(CONFIG_USB_NET_CDC_EEM) += cdc_eem.o obj-$(CONFIG_USB_NET_DM9601) += dm9601.o +obj-$(CONFIG_USB_NET_SR9700) += sr9700.o obj-$(CONFIG_USB_NET_SMSC75XX) += smsc75xx.o obj-$(CONFIG_USB_NET_SMSC95XX) += smsc95xx.o obj-$(CONFIG_USB_NET_GL620A) += gl620a.o diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h index 346c032aa795..bdaa12d07a12 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -178,6 +178,8 @@ struct asix_common_private { struct asix_rx_fixup_info rx_fixup_info; }; +extern const struct driver_info ax88172a_info; + /* ASIX specific flags */ #define FLAG_EEPROM_MAC (1UL << 0) /* init device MAC from eeprom */ diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index ad5d1e4384db..386a3df53678 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -778,6 +778,9 @@ static int ax88178_change_mtu(struct net_device *net, int new_mtu) dev->hard_mtu = net->mtu + net->hard_header_len; ax88178_set_mfb(dev); + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } @@ -943,8 +946,6 @@ static const struct driver_info hg20f9_info = { .data = FLAG_EEPROM_MAC, }; -extern const struct driver_info ax88172a_info; - static const struct usb_device_id products [] = { { // Linksys USB200M diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index d012203b0f29..723b3879ecc2 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.c @@ -161,7 +161,8 @@ static const struct net_device_ops ax88172a_netdev_ops = { .ndo_set_rx_mode = asix_set_multicast, }; -int ax88172a_get_settings(struct net_device *net, struct ethtool_cmd *cmd) +static int ax88172a_get_settings(struct net_device *net, + struct ethtool_cmd *cmd) { if (!net->phydev) return -ENODEV; @@ -169,7 +170,8 @@ int ax88172a_get_settings(struct net_device *net, struct ethtool_cmd *cmd) return phy_ethtool_gset(net->phydev, cmd); } -int ax88172a_set_settings(struct net_device *net, struct ethtool_cmd *cmd) +static int ax88172a_set_settings(struct net_device *net, + struct ethtool_cmd *cmd) { if (!net->phydev) return -ENODEV; @@ -177,7 +179,7 @@ int ax88172a_set_settings(struct net_device *net, struct ethtool_cmd *cmd) return phy_ethtool_sset(net->phydev, cmd); } -int ax88172a_nway_reset(struct net_device *net) +static int ax88172a_nway_reset(struct net_device *net) { if (!net->phydev) return -ENODEV; diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 1e3c302d94fe..3569293df872 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -688,6 +688,9 @@ static int ax88179_change_mtu(struct net_device *net, int new_mtu) 2, 2, &tmp16); } + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } @@ -1028,11 +1031,19 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) dev->mii.phy_id = 0x03; dev->mii.supports_gmii = 1; + if (usb_device_no_sg_constraint(dev->udev)) + dev->can_dma_sg = 1; + dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO; + NETIF_F_RXCSUM; dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO; + NETIF_F_RXCSUM; + + if (dev->can_dma_sg) { + dev->net->features |= NETIF_F_SG | NETIF_F_TSO; + dev->net->hw_features |= NETIF_F_SG | NETIF_F_TSO; + } /* Enable checksum offload */ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | @@ -1166,32 +1177,18 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) int frame_size = dev->maxpacket; int mss = skb_shinfo(skb)->gso_size; int headroom; - int tailroom; tx_hdr1 = skb->len; tx_hdr2 = mss; if (((skb->len + 8) % frame_size) == 0) tx_hdr2 |= 0x80008000; /* Enable padding */ - skb_linearize(skb); - headroom = skb_headroom(skb); - tailroom = skb_tailroom(skb); - - if (!skb_header_cloned(skb) && - !skb_cloned(skb) && - (headroom + tailroom) >= 8) { - if (headroom < 8) { - skb->data = memmove(skb->head + 8, skb->data, skb->len); - skb_set_tail_pointer(skb, skb->len); - } - } else { - struct sk_buff *skb2; + headroom = skb_headroom(skb) - 8; - skb2 = skb_copy_expand(skb, 8, 0, flags); + if ((skb_header_cloned(skb) || headroom < 0) && + pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) { dev_kfree_skb_any(skb); - skb = skb2; - if (!skb) - return NULL; + return NULL; } skb_push(skb, 4); @@ -1317,10 +1314,10 @@ static int ax88179_reset(struct usbnet *dev) 1, 1, tmp); dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO; + NETIF_F_RXCSUM; dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO; + NETIF_F_RXCSUM; /* Enable checksum offload */ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 872819851aef..25ba7eca9a13 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -400,6 +400,10 @@ static const struct usb_device_id mbim_devs[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info_zlp, }, + /* HP hs2434 Mobile Broadband Module needs ZLPs */ + { USB_DEVICE_AND_INTERFACE_INFO(0x3f0, 0x4b1d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&cdc_mbim_info_zlp, + }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info, }, diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index cba1d46e672e..86292e6aaf49 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2816,13 +2816,16 @@ exit: static int hso_get_config_data(struct usb_interface *interface) { struct usb_device *usbdev = interface_to_usbdev(interface); - u8 config_data[17]; + u8 *config_data = kmalloc(17, GFP_KERNEL); u32 if_num = interface->altsetting->desc.bInterfaceNumber; s32 result; + if (!config_data) + return -ENOMEM; if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x86, 0xC0, 0, 0, config_data, 17, USB_CTRL_SET_TIMEOUT) != 0x11) { + kfree(config_data); return -EIO; } @@ -2873,6 +2876,7 @@ static int hso_get_config_data(struct usb_interface *interface) if (config_data[16] & 0x1) result |= HSO_INFO_CRC_BUG; + kfree(config_data); return result; } @@ -2886,6 +2890,11 @@ static int hso_probe(struct usb_interface *interface, struct hso_shared_int *shared_int; struct hso_device *tmp_dev = NULL; + if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { + dev_err(&interface->dev, "Not our interface\n"); + return -ENODEV; + } + if_num = interface->altsetting->desc.bInterfaceNumber; /* Get the interface/port specification from either driver_info or from @@ -2895,10 +2904,6 @@ static int hso_probe(struct usb_interface *interface, else port_spec = hso_get_config_data(interface); - if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { - dev_err(&interface->dev, "Not our interface\n"); - return -ENODEV; - } /* Check if we need to switch to alt interfaces prior to port * configuration */ if (interface->num_altsetting > 1) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 606eba2872bd..3a8131582e75 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -323,7 +323,7 @@ next_desc: /* Never use the same address on both ends of the link, even * if the buggy firmware told us to. */ - if (!compare_ether_addr(dev->net->dev_addr, default_modem_addr)) + if (ether_addr_equal(dev->net->dev_addr, default_modem_addr)) eth_hw_addr_random(dev->net); /* make MAC addr easily distinguishable from an IP header */ diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index ee13f9eb740c..f3fce412c0c1 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -19,9 +19,12 @@ #include <linux/crc32.h> #include <linux/if_vlan.h> #include <linux/uaccess.h> +#include <linux/list.h> +#include <linux/ip.h> +#include <linux/ipv6.h> /* Version Information */ -#define DRIVER_VERSION "v1.0.0 (2013/05/03)" +#define DRIVER_VERSION "v1.01.0 (2013/08/12)" #define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>" #define DRIVER_DESC "Realtek RTL8152 Based USB 2.0 Ethernet Adapters" #define MODULENAME "r8152" @@ -267,6 +270,12 @@ enum rtl_register_content { FULL_DUP = 0x01, }; +#define RTL8152_MAX_TX 10 +#define RTL8152_MAX_RX 10 +#define INTBUFSIZE 2 + +#define INTR_LINK 0x0004 + #define RTL8152_REQT_READ 0xc0 #define RTL8152_REQT_WRITE 0x40 #define RTL8152_REQ_GET_REGS 0x05 @@ -285,9 +294,9 @@ enum rtl_register_content { /* rtl8152 flags */ enum rtl8152_flags { RTL8152_UNPLUG = 0, - RX_URB_FAIL, RTL8152_SET_RX_MODE, - WORK_ENABLE + WORK_ENABLE, + RTL8152_LINK_CHG, }; /* Define these values to match your device */ @@ -311,21 +320,53 @@ struct tx_desc { u32 opts1; #define TX_FS (1 << 31) /* First segment of a packet */ #define TX_LS (1 << 30) /* Final segment of a packet */ -#define TX_LEN_MASK 0xffff +#define TX_LEN_MASK 0x3ffff + u32 opts2; +#define UDP_CS (1 << 31) /* Calculate UDP/IP checksum */ +#define TCP_CS (1 << 30) /* Calculate TCP/IP checksum */ +#define IPV4_CS (1 << 29) /* Calculate IPv4 checksum */ +#define IPV6_CS (1 << 28) /* Calculate IPv6 checksum */ +}; + +struct r8152; + +struct rx_agg { + struct list_head list; + struct urb *urb; + struct r8152 *context; + void *buffer; + void *head; +}; + +struct tx_agg { + struct list_head list; + struct urb *urb; + struct r8152 *context; + void *buffer; + void *head; + u32 skb_num; + u32 skb_len; }; struct r8152 { unsigned long flags; struct usb_device *udev; struct tasklet_struct tl; + struct usb_interface *intf; struct net_device *netdev; - struct urb *rx_urb, *tx_urb; - struct sk_buff *tx_skb, *rx_skb; + struct urb *intr_urb; + struct tx_agg tx_info[RTL8152_MAX_TX]; + struct rx_agg rx_info[RTL8152_MAX_RX]; + struct list_head rx_done, tx_free; + struct sk_buff_head tx_queue; + spinlock_t rx_lock, tx_lock; struct delayed_work schedule; struct mii_if_info mii; + int intr_interval; u32 msg_enable; u16 ocp_base; + u8 *intr_buff; u8 version; u8 speed; }; @@ -340,21 +381,46 @@ enum rtl_version { * The RTL chips use a 64 element hash table based on the Ethernet CRC. */ static const int multicast_filter_limit = 32; +static unsigned int rx_buf_sz = 16384; static int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) { - return usb_control_msg(tp->udev, usb_rcvctrlpipe(tp->udev, 0), + int ret; + void *tmp; + + tmp = kmalloc(size, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + ret = usb_control_msg(tp->udev, usb_rcvctrlpipe(tp->udev, 0), RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, - value, index, data, size, 500); + value, index, tmp, size, 500); + + memcpy(data, tmp, size); + kfree(tmp); + + return ret; } static int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) { - return usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0), + int ret; + void *tmp; + + tmp = kmalloc(size, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + memcpy(tmp, data, size); + + ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0), RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, - value, index, data, size, 500); + value, index, tmp, size, 500); + + kfree(tmp); + return ret; } static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size, @@ -490,37 +556,31 @@ int usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) static u32 ocp_read_dword(struct r8152 *tp, u16 type, u16 index) { - u32 data; + __le32 data; - if (type == MCU_TYPE_PLA) - pla_ocp_read(tp, index, sizeof(data), &data); - else - usb_ocp_read(tp, index, sizeof(data), &data); + generic_ocp_read(tp, index, sizeof(data), &data, type); return __le32_to_cpu(data); } static void ocp_write_dword(struct r8152 *tp, u16 type, u16 index, u32 data) { - if (type == MCU_TYPE_PLA) - pla_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(data), &data); - else - usb_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(data), &data); + __le32 tmp = __cpu_to_le32(data); + + generic_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(tmp), &tmp, type); } static u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index) { u32 data; + __le32 tmp; u8 shift = index & 2; index &= ~3; - if (type == MCU_TYPE_PLA) - pla_ocp_read(tp, index, sizeof(data), &data); - else - usb_ocp_read(tp, index, sizeof(data), &data); + generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); - data = __le32_to_cpu(data); + data = __le32_to_cpu(tmp); data >>= (shift * 8); data &= 0xffff; @@ -529,7 +589,8 @@ static u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index) static void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data) { - u32 tmp, mask = 0xffff; + u32 mask = 0xffff; + __le32 tmp; u16 byen = BYTE_EN_WORD; u8 shift = index & 2; @@ -542,34 +603,25 @@ static void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data) index &= ~3; } - if (type == MCU_TYPE_PLA) - pla_ocp_read(tp, index, sizeof(tmp), &tmp); - else - usb_ocp_read(tp, index, sizeof(tmp), &tmp); + generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); - tmp = __le32_to_cpu(tmp) & ~mask; - tmp |= data; - tmp = __cpu_to_le32(tmp); + data |= __le32_to_cpu(tmp) & ~mask; + tmp = __cpu_to_le32(data); - if (type == MCU_TYPE_PLA) - pla_ocp_write(tp, index, byen, sizeof(tmp), &tmp); - else - usb_ocp_write(tp, index, byen, sizeof(tmp), &tmp); + generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); } static u8 ocp_read_byte(struct r8152 *tp, u16 type, u16 index) { u32 data; + __le32 tmp; u8 shift = index & 3; index &= ~3; - if (type == MCU_TYPE_PLA) - pla_ocp_read(tp, index, sizeof(data), &data); - else - usb_ocp_read(tp, index, sizeof(data), &data); + generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); - data = __le32_to_cpu(data); + data = __le32_to_cpu(tmp); data >>= (shift * 8); data &= 0xff; @@ -578,7 +630,8 @@ static u8 ocp_read_byte(struct r8152 *tp, u16 type, u16 index) static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data) { - u32 tmp, mask = 0xff; + u32 mask = 0xff; + __le32 tmp; u16 byen = BYTE_EN_BYTE; u8 shift = index & 3; @@ -591,19 +644,12 @@ static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data) index &= ~3; } - if (type == MCU_TYPE_PLA) - pla_ocp_read(tp, index, sizeof(tmp), &tmp); - else - usb_ocp_read(tp, index, sizeof(tmp), &tmp); + generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); - tmp = __le32_to_cpu(tmp) & ~mask; - tmp |= data; - tmp = __cpu_to_le32(tmp); + data |= __le32_to_cpu(tmp) & ~mask; + tmp = __cpu_to_le32(data); - if (type == MCU_TYPE_PLA) - pla_ocp_write(tp, index, byen, sizeof(tmp), &tmp); - else - usb_ocp_write(tp, index, byen, sizeof(tmp), &tmp); + generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); } static void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value) @@ -682,24 +728,20 @@ static void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data) ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data); } +static +int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags); + static inline void set_ethernet_addr(struct r8152 *tp) { struct net_device *dev = tp->netdev; - u8 *node_id; - - node_id = kmalloc(sizeof(u8) * 8, GFP_KERNEL); - if (!node_id) { - netif_err(tp, probe, dev, "out of memory"); - return; - } + u8 node_id[8] = {0}; - if (pla_ocp_read(tp, PLA_IDR, sizeof(u8) * 8, node_id) < 0) + if (pla_ocp_read(tp, PLA_IDR, sizeof(node_id), node_id) < 0) netif_notice(tp, probe, dev, "inet addr fail\n"); else { memcpy(dev->dev_addr, node_id, dev->addr_len); memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); } - kfree(node_id); } static int rtl8152_set_mac_address(struct net_device *netdev, void *p) @@ -719,26 +761,6 @@ static int rtl8152_set_mac_address(struct net_device *netdev, void *p) return 0; } -static int alloc_all_urbs(struct r8152 *tp) -{ - tp->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!tp->rx_urb) - return 0; - tp->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!tp->tx_urb) { - usb_free_urb(tp->rx_urb); - return 0; - } - - return 1; -} - -static void free_all_urbs(struct r8152 *tp) -{ - usb_free_urb(tp->rx_urb); - usb_free_urb(tp->tx_urb); -} - static struct net_device_stats *rtl8152_get_stats(struct net_device *dev) { return &dev->stats; @@ -746,151 +768,583 @@ static struct net_device_stats *rtl8152_get_stats(struct net_device *dev) static void read_bulk_callback(struct urb *urb) { - struct r8152 *tp; - unsigned pkt_len; - struct sk_buff *skb; struct net_device *netdev; - struct net_device_stats *stats; + unsigned long flags; int status = urb->status; + struct rx_agg *agg; + struct r8152 *tp; int result; - struct rx_desc *rx_desc; - tp = urb->context; + agg = urb->context; + if (!agg) + return; + + tp = agg->context; if (!tp) return; + if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; + + if (!test_bit(WORK_ENABLE, &tp->flags)) + return; + netdev = tp->netdev; - if (!netif_device_present(netdev)) + + /* When link down, the driver would cancel all bulks. */ + /* This avoid the re-submitting bulk */ + if (!netif_carrier_ok(netdev)) return; - stats = rtl8152_get_stats(netdev); switch (status) { case 0: - break; + if (urb->actual_length < ETH_ZLEN) + break; + + spin_lock_irqsave(&tp->rx_lock, flags); + list_add_tail(&agg->list, &tp->rx_done); + spin_unlock_irqrestore(&tp->rx_lock, flags); + tasklet_schedule(&tp->tl); + return; case -ESHUTDOWN: set_bit(RTL8152_UNPLUG, &tp->flags); netif_device_detach(tp->netdev); + return; case -ENOENT: return; /* the urb is in unlink state */ case -ETIME: pr_warn_ratelimited("may be reset is needed?..\n"); - goto goon; + break; default: pr_warn_ratelimited("Rx status %d\n", status); - goto goon; + break; } - /* protect against short packets (tell me why we got some?!?) */ - if (urb->actual_length < sizeof(*rx_desc)) - goto goon; - - - rx_desc = (struct rx_desc *)urb->transfer_buffer; - pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; - if (urb->actual_length < sizeof(struct rx_desc) + pkt_len) - goto goon; - - skb = netdev_alloc_skb_ip_align(netdev, pkt_len); - if (!skb) - goto goon; - - memcpy(skb->data, tp->rx_skb->data + sizeof(struct rx_desc), pkt_len); - skb_put(skb, pkt_len); - skb->protocol = eth_type_trans(skb, netdev); - netif_rx(skb); - stats->rx_packets++; - stats->rx_bytes += pkt_len; -goon: - usb_fill_bulk_urb(tp->rx_urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1), - tp->rx_skb->data, RTL8152_RMS + sizeof(struct rx_desc), - (usb_complete_t)read_bulk_callback, tp); - result = usb_submit_urb(tp->rx_urb, GFP_ATOMIC); + result = r8152_submit_rx(tp, agg, GFP_ATOMIC); if (result == -ENODEV) { netif_device_detach(tp->netdev); } else if (result) { - set_bit(RX_URB_FAIL, &tp->flags); - goto resched; + spin_lock_irqsave(&tp->rx_lock, flags); + list_add_tail(&agg->list, &tp->rx_done); + spin_unlock_irqrestore(&tp->rx_lock, flags); + tasklet_schedule(&tp->tl); + } +} + +static void write_bulk_callback(struct urb *urb) +{ + struct net_device_stats *stats; + unsigned long flags; + struct tx_agg *agg; + struct r8152 *tp; + int status = urb->status; + + agg = urb->context; + if (!agg) + return; + + tp = agg->context; + if (!tp) + return; + + stats = rtl8152_get_stats(tp->netdev); + if (status) { + pr_warn_ratelimited("Tx status %d\n", status); + stats->tx_errors += agg->skb_num; } else { - clear_bit(RX_URB_FAIL, &tp->flags); + stats->tx_packets += agg->skb_num; + stats->tx_bytes += agg->skb_len; } - return; -resched: - tasklet_schedule(&tp->tl); + spin_lock_irqsave(&tp->tx_lock, flags); + list_add_tail(&agg->list, &tp->tx_free); + spin_unlock_irqrestore(&tp->tx_lock, flags); + + if (!netif_carrier_ok(tp->netdev)) + return; + + if (!test_bit(WORK_ENABLE, &tp->flags)) + return; + + if (test_bit(RTL8152_UNPLUG, &tp->flags)) + return; + + if (!skb_queue_empty(&tp->tx_queue)) + tasklet_schedule(&tp->tl); } -static void rx_fixup(unsigned long data) +static void intr_callback(struct urb *urb) { struct r8152 *tp; - int status; + __u16 *d; + int status = urb->status; + int res; + + tp = urb->context; + if (!tp) + return; - tp = (struct r8152 *)data; if (!test_bit(WORK_ENABLE, &tp->flags)) return; - status = usb_submit_urb(tp->rx_urb, GFP_ATOMIC); - if (status == -ENODEV) { + if (test_bit(RTL8152_UNPLUG, &tp->flags)) + return; + + switch (status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: netif_device_detach(tp->netdev); - } else if (status) { - set_bit(RX_URB_FAIL, &tp->flags); - goto tlsched; + case -ENOENT: + return; + case -EOVERFLOW: + netif_info(tp, intr, tp->netdev, "intr status -EOVERFLOW\n"); + goto resubmit; + /* -EPIPE: should clear the halt */ + default: + netif_info(tp, intr, tp->netdev, "intr status %d\n", status); + goto resubmit; + } + + d = urb->transfer_buffer; + if (INTR_LINK & __le16_to_cpu(d[0])) { + if (!(tp->speed & LINK_STATUS)) { + set_bit(RTL8152_LINK_CHG, &tp->flags); + schedule_delayed_work(&tp->schedule, 0); + } } else { - clear_bit(RX_URB_FAIL, &tp->flags); + if (tp->speed & LINK_STATUS) { + set_bit(RTL8152_LINK_CHG, &tp->flags); + schedule_delayed_work(&tp->schedule, 0); + } } - return; -tlsched: - tasklet_schedule(&tp->tl); +resubmit: + res = usb_submit_urb(urb, GFP_ATOMIC); + if (res == -ENODEV) + netif_device_detach(tp->netdev); + else if (res) + netif_err(tp, intr, tp->netdev, + "can't resubmit intr, status %d\n", res); } -static void write_bulk_callback(struct urb *urb) +static inline void *rx_agg_align(void *data) +{ + return (void *)ALIGN((uintptr_t)data, 8); +} + +static inline void *tx_agg_align(void *data) +{ + return (void *)ALIGN((uintptr_t)data, 4); +} + +static void free_all_mem(struct r8152 *tp) +{ + int i; + + for (i = 0; i < RTL8152_MAX_RX; i++) { + if (tp->rx_info[i].urb) { + usb_free_urb(tp->rx_info[i].urb); + tp->rx_info[i].urb = NULL; + } + + if (tp->rx_info[i].buffer) { + kfree(tp->rx_info[i].buffer); + tp->rx_info[i].buffer = NULL; + tp->rx_info[i].head = NULL; + } + } + + for (i = 0; i < RTL8152_MAX_TX; i++) { + if (tp->tx_info[i].urb) { + usb_free_urb(tp->tx_info[i].urb); + tp->tx_info[i].urb = NULL; + } + + if (tp->tx_info[i].buffer) { + kfree(tp->tx_info[i].buffer); + tp->tx_info[i].buffer = NULL; + tp->tx_info[i].head = NULL; + } + } + + if (tp->intr_urb) { + usb_free_urb(tp->intr_urb); + tp->intr_urb = NULL; + } + + if (tp->intr_buff) { + kfree(tp->intr_buff); + tp->intr_buff = NULL; + } +} + +static int alloc_all_mem(struct r8152 *tp) +{ + struct net_device *netdev = tp->netdev; + struct usb_interface *intf = tp->intf; + struct usb_host_interface *alt = intf->cur_altsetting; + struct usb_host_endpoint *ep_intr = alt->endpoint + 2; + struct urb *urb; + int node, i; + u8 *buf; + + node = netdev->dev.parent ? dev_to_node(netdev->dev.parent) : -1; + + spin_lock_init(&tp->rx_lock); + spin_lock_init(&tp->tx_lock); + INIT_LIST_HEAD(&tp->rx_done); + INIT_LIST_HEAD(&tp->tx_free); + skb_queue_head_init(&tp->tx_queue); + + for (i = 0; i < RTL8152_MAX_RX; i++) { + buf = kmalloc_node(rx_buf_sz, GFP_KERNEL, node); + if (!buf) + goto err1; + + if (buf != rx_agg_align(buf)) { + kfree(buf); + buf = kmalloc_node(rx_buf_sz + 8, GFP_KERNEL, node); + if (!buf) + goto err1; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + kfree(buf); + goto err1; + } + + INIT_LIST_HEAD(&tp->rx_info[i].list); + tp->rx_info[i].context = tp; + tp->rx_info[i].urb = urb; + tp->rx_info[i].buffer = buf; + tp->rx_info[i].head = rx_agg_align(buf); + } + + for (i = 0; i < RTL8152_MAX_TX; i++) { + buf = kmalloc_node(rx_buf_sz, GFP_KERNEL, node); + if (!buf) + goto err1; + + if (buf != tx_agg_align(buf)) { + kfree(buf); + buf = kmalloc_node(rx_buf_sz + 4, GFP_KERNEL, node); + if (!buf) + goto err1; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + kfree(buf); + goto err1; + } + + INIT_LIST_HEAD(&tp->tx_info[i].list); + tp->tx_info[i].context = tp; + tp->tx_info[i].urb = urb; + tp->tx_info[i].buffer = buf; + tp->tx_info[i].head = tx_agg_align(buf); + + list_add_tail(&tp->tx_info[i].list, &tp->tx_free); + } + + tp->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!tp->intr_urb) + goto err1; + + tp->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL); + if (!tp->intr_buff) + goto err1; + + tp->intr_interval = (int)ep_intr->desc.bInterval; + usb_fill_int_urb(tp->intr_urb, tp->udev, usb_rcvintpipe(tp->udev, 3), + tp->intr_buff, INTBUFSIZE, intr_callback, + tp, tp->intr_interval); + + return 0; + +err1: + free_all_mem(tp); + return -ENOMEM; +} + +static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp) +{ + struct tx_agg *agg = NULL; + unsigned long flags; + + spin_lock_irqsave(&tp->tx_lock, flags); + if (!list_empty(&tp->tx_free)) { + struct list_head *cursor; + + cursor = tp->tx_free.next; + list_del_init(cursor); + agg = list_entry(cursor, struct tx_agg, list); + } + spin_unlock_irqrestore(&tp->tx_lock, flags); + + return agg; +} + +static void +r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, struct sk_buff *skb) +{ + memset(desc, 0, sizeof(*desc)); + + desc->opts1 = cpu_to_le32((skb->len & TX_LEN_MASK) | TX_FS | TX_LS); + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + __be16 protocol; + u8 ip_protocol; + u32 opts2 = 0; + + if (skb->protocol == htons(ETH_P_8021Q)) + protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; + else + protocol = skb->protocol; + + switch (protocol) { + case htons(ETH_P_IP): + opts2 |= IPV4_CS; + ip_protocol = ip_hdr(skb)->protocol; + break; + + case htons(ETH_P_IPV6): + opts2 |= IPV6_CS; + ip_protocol = ipv6_hdr(skb)->nexthdr; + break; + + default: + ip_protocol = IPPROTO_RAW; + break; + } + + if (ip_protocol == IPPROTO_TCP) { + opts2 |= TCP_CS; + opts2 |= (skb_transport_offset(skb) & 0x7fff) << 17; + } else if (ip_protocol == IPPROTO_UDP) { + opts2 |= UDP_CS; + } else { + WARN_ON_ONCE(1); + } + + desc->opts2 = cpu_to_le32(opts2); + } +} + +static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg) +{ + u32 remain; + u8 *tx_data; + + tx_data = agg->head; + agg->skb_num = agg->skb_len = 0; + remain = rx_buf_sz - sizeof(struct tx_desc); + + while (remain >= ETH_ZLEN) { + struct tx_desc *tx_desc; + struct sk_buff *skb; + unsigned int len; + + skb = skb_dequeue(&tp->tx_queue); + if (!skb) + break; + + len = skb->len; + if (remain < len) { + skb_queue_head(&tp->tx_queue, skb); + break; + } + + tx_desc = (struct tx_desc *)tx_data; + tx_data += sizeof(*tx_desc); + + r8152_tx_csum(tp, tx_desc, skb); + memcpy(tx_data, skb->data, len); + agg->skb_num++; + agg->skb_len += len; + dev_kfree_skb_any(skb); + + tx_data = tx_agg_align(tx_data + len); + remain = rx_buf_sz - sizeof(*tx_desc) - + (u32)((void *)tx_data - agg->head); + } + + usb_fill_bulk_urb(agg->urb, tp->udev, usb_sndbulkpipe(tp->udev, 2), + agg->head, (int)(tx_data - (u8 *)agg->head), + (usb_complete_t)write_bulk_callback, agg); + + return usb_submit_urb(agg->urb, GFP_ATOMIC); +} + +static void rx_bottom(struct r8152 *tp) +{ + unsigned long flags; + struct list_head *cursor, *next; + + spin_lock_irqsave(&tp->rx_lock, flags); + list_for_each_safe(cursor, next, &tp->rx_done) { + struct rx_desc *rx_desc; + struct rx_agg *agg; + unsigned pkt_len; + int len_used = 0; + struct urb *urb; + u8 *rx_data; + int ret; + + list_del_init(cursor); + spin_unlock_irqrestore(&tp->rx_lock, flags); + + agg = list_entry(cursor, struct rx_agg, list); + urb = agg->urb; + if (urb->actual_length < ETH_ZLEN) + goto submit; + + rx_desc = agg->head; + rx_data = agg->head; + pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; + len_used += sizeof(struct rx_desc) + pkt_len; + + while (urb->actual_length >= len_used) { + struct net_device *netdev = tp->netdev; + struct net_device_stats *stats; + struct sk_buff *skb; + + if (pkt_len < ETH_ZLEN) + break; + + stats = rtl8152_get_stats(netdev); + + pkt_len -= 4; /* CRC */ + rx_data += sizeof(struct rx_desc); + + skb = netdev_alloc_skb_ip_align(netdev, pkt_len); + if (!skb) { + stats->rx_dropped++; + break; + } + memcpy(skb->data, rx_data, pkt_len); + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, netdev); + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += pkt_len; + + rx_data = rx_agg_align(rx_data + pkt_len + 4); + rx_desc = (struct rx_desc *)rx_data; + pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; + len_used = (int)(rx_data - (u8 *)agg->head); + len_used += sizeof(struct rx_desc) + pkt_len; + } + +submit: + ret = r8152_submit_rx(tp, agg, GFP_ATOMIC); + spin_lock_irqsave(&tp->rx_lock, flags); + if (ret && ret != -ENODEV) { + list_add_tail(&agg->list, next); + tasklet_schedule(&tp->tl); + } + } + spin_unlock_irqrestore(&tp->rx_lock, flags); +} + +static void tx_bottom(struct r8152 *tp) +{ + int res; + + do { + struct tx_agg *agg; + + if (skb_queue_empty(&tp->tx_queue)) + break; + + agg = r8152_get_tx_agg(tp); + if (!agg) + break; + + res = r8152_tx_agg_fill(tp, agg); + if (res) { + struct net_device_stats *stats; + struct net_device *netdev; + unsigned long flags; + + netdev = tp->netdev; + stats = rtl8152_get_stats(netdev); + + if (res == -ENODEV) { + netif_device_detach(netdev); + } else { + netif_warn(tp, tx_err, netdev, + "failed tx_urb %d\n", res); + stats->tx_dropped += agg->skb_num; + spin_lock_irqsave(&tp->tx_lock, flags); + list_add_tail(&agg->list, &tp->tx_free); + spin_unlock_irqrestore(&tp->tx_lock, flags); + } + } + } while (res == 0); +} + +static void bottom_half(unsigned long data) { struct r8152 *tp; - int status = urb->status; - tp = urb->context; - if (!tp) + tp = (struct r8152 *)data; + + if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; - dev_kfree_skb_irq(tp->tx_skb); - if (!netif_device_present(tp->netdev)) + + if (!test_bit(WORK_ENABLE, &tp->flags)) + return; + + /* When link down, the driver would cancel all bulks. */ + /* This avoid the re-submitting bulk */ + if (!netif_carrier_ok(tp->netdev)) return; - if (status) - dev_info(&urb->dev->dev, "%s: Tx status %d\n", - tp->netdev->name, status); - tp->netdev->trans_start = jiffies; - netif_wake_queue(tp->netdev); + + rx_bottom(tp); + tx_bottom(tp); +} + +static +int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags) +{ + usb_fill_bulk_urb(agg->urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1), + agg->head, rx_buf_sz, + (usb_complete_t)read_bulk_callback, agg); + + return usb_submit_urb(agg->urb, mem_flags); } static void rtl8152_tx_timeout(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); - struct net_device_stats *stats = rtl8152_get_stats(netdev); + int i; + netif_warn(tp, tx_err, netdev, "Tx timeout.\n"); - usb_unlink_urb(tp->tx_urb); - stats->tx_errors++; + for (i = 0; i < RTL8152_MAX_TX; i++) + usb_unlink_urb(tp->tx_info[i].urb); } static void rtl8152_set_rx_mode(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); - if (tp->speed & LINK_STATUS) + if (tp->speed & LINK_STATUS) { set_bit(RTL8152_SET_RX_MODE, &tp->flags); + schedule_delayed_work(&tp->schedule, 0); + } } static void _rtl8152_set_rx_mode(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); - u32 tmp, *mc_filter; /* Multicast hash filter */ + u32 mc_filter[2]; /* Multicast hash filter */ + __le32 tmp[2]; u32 ocp_data; - mc_filter = kmalloc(sizeof(u32) * 2, GFP_KERNEL); - if (!mc_filter) { - netif_err(tp, link, netdev, "out of memory"); - return; - } - clear_bit(RTL8152_SET_RX_MODE, &tp->flags); netif_stop_queue(netdev); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); @@ -918,14 +1372,12 @@ static void _rtl8152_set_rx_mode(struct net_device *netdev) } } - tmp = mc_filter[0]; - mc_filter[0] = __cpu_to_le32(swab32(mc_filter[1])); - mc_filter[1] = __cpu_to_le32(swab32(tmp)); + tmp[0] = __cpu_to_le32(swab32(mc_filter[1])); + tmp[1] = __cpu_to_le32(swab32(mc_filter[0])); - pla_ocp_write(tp, PLA_MAR, BYTE_EN_DWORD, sizeof(u32) * 2, mc_filter); + pla_ocp_write(tp, PLA_MAR, BYTE_EN_DWORD, sizeof(tmp), tmp); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); netif_wake_queue(netdev); - kfree(mc_filter); } static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, @@ -933,33 +1385,39 @@ static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, { struct r8152 *tp = netdev_priv(netdev); struct net_device_stats *stats = rtl8152_get_stats(netdev); + unsigned long flags; + struct tx_agg *agg = NULL; struct tx_desc *tx_desc; unsigned int len; + u8 *tx_data; int res; - netif_stop_queue(netdev); - len = skb->len; - if (skb_header_cloned(skb) || skb_headroom(skb) < sizeof(*tx_desc)) { - struct sk_buff *tx_skb; + skb_tx_timestamp(skb); - tx_skb = skb_copy_expand(skb, sizeof(*tx_desc), 0, GFP_ATOMIC); - dev_kfree_skb_any(skb); - if (!tx_skb) { - stats->tx_dropped++; - netif_wake_queue(netdev); - return NETDEV_TX_OK; - } - skb = tx_skb; + /* If tx_queue is not empty, it means at least one previous packt */ + /* is waiting for sending. Don't send current one before it. */ + if (skb_queue_empty(&tp->tx_queue)) + agg = r8152_get_tx_agg(tp); + + if (!agg) { + skb_queue_tail(&tp->tx_queue, skb); + return NETDEV_TX_OK; } - tx_desc = (struct tx_desc *)skb_push(skb, sizeof(*tx_desc)); - memset(tx_desc, 0, sizeof(*tx_desc)); - tx_desc->opts1 = cpu_to_le32((len & TX_LEN_MASK) | TX_FS | TX_LS); - tp->tx_skb = skb; - skb_tx_timestamp(skb); - usb_fill_bulk_urb(tp->tx_urb, tp->udev, usb_sndbulkpipe(tp->udev, 2), - skb->data, skb->len, - (usb_complete_t)write_bulk_callback, tp); - res = usb_submit_urb(tp->tx_urb, GFP_ATOMIC); + + tx_desc = (struct tx_desc *)agg->head; + tx_data = agg->head + sizeof(*tx_desc); + agg->skb_num = agg->skb_len = 0; + + len = skb->len; + r8152_tx_csum(tp, tx_desc, skb); + memcpy(tx_data, skb->data, len); + dev_kfree_skb_any(skb); + agg->skb_num++; + agg->skb_len += len; + usb_fill_bulk_urb(agg->urb, tp->udev, usb_sndbulkpipe(tp->udev, 2), + agg->head, len + sizeof(*tx_desc), + (usb_complete_t)write_bulk_callback, agg); + res = usb_submit_urb(agg->urb, GFP_ATOMIC); if (res) { /* Can we get/handle EPIPE here? */ if (res == -ENODEV) { @@ -967,12 +1425,11 @@ static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, } else { netif_warn(tp, tx_err, netdev, "failed tx_urb %d\n", res); - stats->tx_errors++; - netif_start_queue(netdev); + stats->tx_dropped++; + spin_lock_irqsave(&tp->tx_lock, flags); + list_add_tail(&agg->list, &tp->tx_free); + spin_unlock_irqrestore(&tp->tx_lock, flags); } - } else { - stats->tx_packets++; - stats->tx_bytes += skb->len; } return NETDEV_TX_OK; @@ -1009,17 +1466,18 @@ static inline u8 rtl8152_get_speed(struct r8152 *tp) static int rtl8152_enable(struct r8152 *tp) { - u32 ocp_data; + u32 ocp_data; + int i, ret; u8 speed; speed = rtl8152_get_speed(tp); - if (speed & _100bps) { + if (speed & _10bps) { ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); - ocp_data &= ~EEEP_CR_EEEP_TX; + ocp_data |= EEEP_CR_EEEP_TX; ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); } else { ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); - ocp_data |= EEEP_CR_EEEP_TX; + ocp_data &= ~EEEP_CR_EEEP_TX; ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); } @@ -1033,23 +1491,34 @@ static int rtl8152_enable(struct r8152 *tp) ocp_data &= ~RXDY_GATED_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); - usb_fill_bulk_urb(tp->rx_urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1), - tp->rx_skb->data, RTL8152_RMS + sizeof(struct rx_desc), - (usb_complete_t)read_bulk_callback, tp); + INIT_LIST_HEAD(&tp->rx_done); + ret = 0; + for (i = 0; i < RTL8152_MAX_RX; i++) { + INIT_LIST_HEAD(&tp->rx_info[i].list); + ret |= r8152_submit_rx(tp, &tp->rx_info[i], GFP_KERNEL); + } - return usb_submit_urb(tp->rx_urb, GFP_KERNEL); + return ret; } static void rtl8152_disable(struct r8152 *tp) { - u32 ocp_data; - int i; + struct net_device_stats *stats = rtl8152_get_stats(tp->netdev); + struct sk_buff *skb; + u32 ocp_data; + int i; ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_data &= ~RCR_ACPT_ALL; ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); - usb_kill_urb(tp->tx_urb); + while ((skb = skb_dequeue(&tp->tx_queue))) { + dev_kfree_skb(skb); + stats->tx_dropped++; + } + + for (i = 0; i < RTL8152_MAX_TX; i++) + usb_kill_urb(tp->tx_info[i].urb); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); ocp_data |= RXDY_GATED_EN; @@ -1068,7 +1537,8 @@ static void rtl8152_disable(struct r8152 *tp) mdelay(1); } - usb_kill_urb(tp->rx_urb); + for (i = 0; i < RTL8152_MAX_RX; i++) + usb_kill_urb(tp->rx_info[i].urb); rtl8152_nic_reset(tp); } @@ -1279,7 +1749,6 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) r8152_mdio_write(tp, MII_BMCR, bmcr); out: - schedule_delayed_work(&tp->schedule, 5 * HZ); return ret; } @@ -1302,6 +1771,7 @@ static void set_carrier(struct r8152 *tp) struct net_device *netdev = tp->netdev; u8 speed; + clear_bit(RTL8152_LINK_CHG, &tp->flags); speed = rtl8152_get_speed(tp); if (speed & LINK_STATUS) { @@ -1313,7 +1783,9 @@ static void set_carrier(struct r8152 *tp) } else { if (tp->speed & LINK_STATUS) { netif_carrier_off(netdev); + tasklet_disable(&tp->tl); rtl8152_disable(tp); + tasklet_enable(&tp->tl); } } tp->speed = speed; @@ -1329,13 +1801,12 @@ static void rtl_work_func_t(struct work_struct *work) if (test_bit(RTL8152_UNPLUG, &tp->flags)) goto out1; - set_carrier(tp); + if (test_bit(RTL8152_LINK_CHG, &tp->flags)) + set_carrier(tp); if (test_bit(RTL8152_SET_RX_MODE, &tp->flags)) _rtl8152_set_rx_mode(tp->netdev); - schedule_delayed_work(&tp->schedule, HZ); - out1: return; } @@ -1345,28 +1816,20 @@ static int rtl8152_open(struct net_device *netdev) struct r8152 *tp = netdev_priv(netdev); int res = 0; - tp->speed = rtl8152_get_speed(tp); - if (tp->speed & LINK_STATUS) { - res = rtl8152_enable(tp); - if (res) { - if (res == -ENODEV) - netif_device_detach(tp->netdev); - - netif_err(tp, ifup, netdev, - "rtl8152_open failed: %d\n", res); - return res; - } - - netif_carrier_on(netdev); - } else { - netif_stop_queue(netdev); - netif_carrier_off(netdev); + res = usb_submit_urb(tp->intr_urb, GFP_KERNEL); + if (res) { + if (res == -ENODEV) + netif_device_detach(tp->netdev); + netif_warn(tp, ifup, netdev, + "intr_urb submit failed: %d\n", res); + return res; } rtl8152_set_speed(tp, AUTONEG_ENABLE, SPEED_100, DUPLEX_FULL); + tp->speed = 0; + netif_carrier_off(netdev); netif_start_queue(netdev); set_bit(WORK_ENABLE, &tp->flags); - schedule_delayed_work(&tp->schedule, 0); return res; } @@ -1376,10 +1839,13 @@ static int rtl8152_close(struct net_device *netdev) struct r8152 *tp = netdev_priv(netdev); int res = 0; + usb_kill_urb(tp->intr_urb); clear_bit(WORK_ENABLE, &tp->flags); cancel_delayed_work_sync(&tp->schedule); netif_stop_queue(netdev); + tasklet_disable(&tp->tl); rtl8152_disable(tp); + tasklet_enable(&tp->tl); return res; } @@ -1439,8 +1905,8 @@ static void r8152b_hw_phy_cfg(struct r8152 *tp) static void r8152b_init(struct r8152 *tp) { - u32 ocp_data; - int i; + u32 ocp_data; + int i; rtl_clear_bp(tp); @@ -1485,9 +1951,9 @@ static void r8152b_init(struct r8152 *tp) break; } - /* disable rx aggregation */ + /* enable rx aggregation */ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); - ocp_data |= RX_AGG_DISABLE; + ocp_data &= ~RX_AGG_DISABLE; ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); } @@ -1499,7 +1965,9 @@ static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) if (netif_running(tp->netdev)) { clear_bit(WORK_ENABLE, &tp->flags); + usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); + tasklet_disable(&tp->tl); } rtl8152_down(tp); @@ -1514,10 +1982,12 @@ static int rtl8152_resume(struct usb_interface *intf) r8152b_init(tp); netif_device_attach(tp->netdev); if (netif_running(tp->netdev)) { - rtl8152_enable(tp); + rtl8152_set_speed(tp, AUTONEG_ENABLE, SPEED_100, DUPLEX_FULL); + tp->speed = 0; + netif_carrier_off(tp->netdev); set_bit(WORK_ENABLE, &tp->flags); - set_bit(RTL8152_SET_RX_MODE, &tp->flags); - schedule_delayed_work(&tp->schedule, 0); + usb_submit_urb(tp->intr_urb, GFP_KERNEL); + tasklet_enable(&tp->tl); } return 0; @@ -1629,6 +2099,7 @@ static int rtl8152_probe(struct usb_interface *intf, struct usb_device *udev = interface_to_usbdev(intf); struct r8152 *tp; struct net_device *netdev; + int ret; if (udev->actconfig->desc.bConfigurationValue != 1) { usb_driver_set_configuration(udev, 1); @@ -1641,19 +2112,22 @@ static int rtl8152_probe(struct usb_interface *intf, return -ENOMEM; } + SET_NETDEV_DEV(netdev, &intf->dev); tp = netdev_priv(netdev); tp->msg_enable = 0x7FFF; - tasklet_init(&tp->tl, rx_fixup, (unsigned long)tp); + tasklet_init(&tp->tl, bottom_half, (unsigned long)tp); INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t); tp->udev = udev; tp->netdev = netdev; + tp->intf = intf; netdev->netdev_ops = &rtl8152_netdev_ops; netdev->watchdog_timeo = RTL8152_TX_TIMEOUT; - netdev->features &= ~NETIF_F_IP_CSUM; + + netdev->features |= NETIF_F_IP_CSUM; + netdev->hw_features = NETIF_F_IP_CSUM; SET_ETHTOOL_OPS(netdev, &ops); - tp->speed = 0; tp->mii.dev = netdev; tp->mii.mdio_read = read_mii_word; @@ -1667,37 +2141,27 @@ static int rtl8152_probe(struct usb_interface *intf, r8152b_init(tp); set_ethernet_addr(tp); - if (!alloc_all_urbs(tp)) { - netif_err(tp, probe, netdev, "out of memory"); + ret = alloc_all_mem(tp); + if (ret) goto out; - } - - tp->rx_skb = netdev_alloc_skb(netdev, - RTL8152_RMS + sizeof(struct rx_desc)); - if (!tp->rx_skb) - goto out1; usb_set_intfdata(intf, tp); - SET_NETDEV_DEV(netdev, &intf->dev); - - if (register_netdev(netdev) != 0) { + ret = register_netdev(netdev); + if (ret != 0) { netif_err(tp, probe, netdev, "couldn't register the device"); - goto out2; + goto out1; } netif_info(tp, probe, netdev, "%s", DRIVER_VERSION); return 0; -out2: - usb_set_intfdata(intf, NULL); - dev_kfree_skb(tp->rx_skb); out1: - free_all_urbs(tp); + usb_set_intfdata(intf, NULL); out: free_netdev(netdev); - return -EIO; + return ret; } static void rtl8152_unload(struct r8152 *tp) @@ -1725,9 +2189,7 @@ static void rtl8152_disconnect(struct usb_interface *intf) tasklet_kill(&tp->tl); unregister_netdev(tp->netdev); rtl8152_unload(tp); - free_all_urbs(tp); - if (tp->rx_skb) - dev_kfree_skb(tp->rx_skb); + free_all_mem(tp); free_netdev(tp->netdev); } } @@ -1742,11 +2204,12 @@ MODULE_DEVICE_TABLE(usb, rtl8152_table); static struct usb_driver rtl8152_driver = { .name = MODULENAME, + .id_table = rtl8152_table, .probe = rtl8152_probe, .disconnect = rtl8152_disconnect, - .id_table = rtl8152_table, .suspend = rtl8152_suspend, - .resume = rtl8152_resume + .resume = rtl8152_resume, + .reset_resume = rtl8152_resume, }; module_usb_driver(rtl8152_driver); diff --git a/drivers/net/usb/r815x.c b/drivers/net/usb/r815x.c index 852392269718..2df2f4fb42a7 100644 --- a/drivers/net/usb/r815x.c +++ b/drivers/net/usb/r815x.c @@ -24,34 +24,43 @@ static int pla_read_word(struct usb_device *udev, u16 index) { - int data, ret; + int ret; u8 shift = index & 2; - __le32 ocp_data; + __le32 *tmp; + + tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return -ENOMEM; index &= ~3; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), RTL815x_REQ_GET_REGS, RTL815x_REQT_READ, - index, MCU_TYPE_PLA, &ocp_data, sizeof(ocp_data), - 500); + index, MCU_TYPE_PLA, tmp, sizeof(*tmp), 500); if (ret < 0) - return ret; + goto out2; - data = __le32_to_cpu(ocp_data); - data >>= (shift * 8); - data &= 0xffff; + ret = __le32_to_cpu(*tmp); + ret >>= (shift * 8); + ret &= 0xffff; - return data; +out2: + kfree(tmp); + return ret; } static int pla_write_word(struct usb_device *udev, u16 index, u32 data) { - __le32 ocp_data; + __le32 *tmp; u32 mask = 0xffff; u16 byen = BYTE_EN_WORD; u8 shift = index & 2; int ret; + tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + data &= mask; if (shift) { @@ -63,19 +72,20 @@ static int pla_write_word(struct usb_device *udev, u16 index, u32 data) ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), RTL815x_REQ_GET_REGS, RTL815x_REQT_READ, - index, MCU_TYPE_PLA, &ocp_data, sizeof(ocp_data), - 500); + index, MCU_TYPE_PLA, tmp, sizeof(*tmp), 500); if (ret < 0) - return ret; + goto out3; - data |= __le32_to_cpu(ocp_data) & ~mask; - ocp_data = __cpu_to_le32(data); + data |= __le32_to_cpu(*tmp) & ~mask; + *tmp = __cpu_to_le32(data); ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), RTL815x_REQ_SET_REGS, RTL815x_REQT_WRITE, - index, MCU_TYPE_PLA | byen, &ocp_data, - sizeof(ocp_data), 500); + index, MCU_TYPE_PLA | byen, tmp, sizeof(*tmp), + 500); +out3: + kfree(tmp); return ret; } @@ -116,11 +126,18 @@ out1: static int r815x_mdio_read(struct net_device *netdev, int phy_id, int reg) { struct usbnet *dev = netdev_priv(netdev); + int ret; if (phy_id != R815x_PHY_ID) return -EINVAL; - return ocp_reg_read(dev, BASE_MII + reg * 2); + if (usb_autopm_get_interface(dev->intf) < 0) + return -ENODEV; + + ret = ocp_reg_read(dev, BASE_MII + reg * 2); + + usb_autopm_put_interface(dev->intf); + return ret; } static @@ -131,7 +148,12 @@ void r815x_mdio_write(struct net_device *netdev, int phy_id, int reg, int val) if (phy_id != R815x_PHY_ID) return; + if (usb_autopm_get_interface(dev->intf) < 0) + return; + ocp_reg_write(dev, BASE_MII + reg * 2, val); + + usb_autopm_put_interface(dev->intf); } static int r8153_bind(struct usbnet *dev, struct usb_interface *intf) @@ -150,7 +172,7 @@ static int r8153_bind(struct usbnet *dev, struct usb_interface *intf) dev->mii.phy_id = R815x_PHY_ID; dev->mii.supports_gmii = 1; - return 0; + return status; } static int r8152_bind(struct usbnet *dev, struct usb_interface *intf) @@ -169,7 +191,7 @@ static int r8152_bind(struct usbnet *dev, struct usb_interface *intf) dev->mii.phy_id = R815x_PHY_ID; dev->mii.supports_gmii = 0; - return 0; + return status; } static const struct driver_info r8152_info = { diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 75409748c774..66ebbacf066f 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -45,7 +45,6 @@ #define EEPROM_MAC_OFFSET (0x01) #define DEFAULT_TX_CSUM_ENABLE (true) #define DEFAULT_RX_CSUM_ENABLE (true) -#define DEFAULT_TSO_ENABLE (true) #define SMSC75XX_INTERNAL_PHY_ID (1) #define SMSC75XX_TX_OVERHEAD (8) #define MAX_RX_FIFO_SIZE (20 * 1024) @@ -1410,17 +1409,14 @@ static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write); - if (DEFAULT_TX_CSUM_ENABLE) { + if (DEFAULT_TX_CSUM_ENABLE) dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; - if (DEFAULT_TSO_ENABLE) - dev->net->features |= NETIF_F_SG | - NETIF_F_TSO | NETIF_F_TSO6; - } + if (DEFAULT_RX_CSUM_ENABLE) dev->net->features |= NETIF_F_RXCSUM; dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_RXCSUM; + NETIF_F_RXCSUM; ret = smsc75xx_wait_ready(dev, 0); if (ret < 0) { @@ -2200,8 +2196,6 @@ static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev, { u32 tx_cmd_a, tx_cmd_b; - skb_linearize(skb); - if (skb_headroom(skb) < SMSC75XX_TX_OVERHEAD) { struct sk_buff *skb2 = skb_copy_expand(skb, SMSC75XX_TX_OVERHEAD, 0, flags); diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c new file mode 100644 index 000000000000..7ec3e0ee0783 --- /dev/null +++ b/drivers/net/usb/sr9700.c @@ -0,0 +1,560 @@ +/* + * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices + * + * Author : Liu Junliang <liujunliang_ljl@163.com> + * + * Based on dm9601.c + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <linux/usb/usbnet.h> + +#include "sr9700.h" + +static int sr_read(struct usbnet *dev, u8 reg, u16 length, void *data) +{ + int err; + + err = usbnet_read_cmd(dev, SR_RD_REGS, SR_REQ_RD_REG, 0, reg, data, + length); + if ((err != length) && (err >= 0)) + err = -EINVAL; + return err; +} + +static int sr_write(struct usbnet *dev, u8 reg, u16 length, void *data) +{ + int err; + + err = usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, 0, reg, data, + length); + if ((err >= 0) && (err < length)) + err = -EINVAL; + return err; +} + +static int sr_read_reg(struct usbnet *dev, u8 reg, u8 *value) +{ + return sr_read(dev, reg, 1, value); +} + +static int sr_write_reg(struct usbnet *dev, u8 reg, u8 value) +{ + return usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, + value, reg, NULL, 0); +} + +static void sr_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) +{ + usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, + 0, reg, data, length); +} + +static void sr_write_reg_async(struct usbnet *dev, u8 reg, u8 value) +{ + usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, + value, reg, NULL, 0); +} + +static int wait_phy_eeprom_ready(struct usbnet *dev, int phy) +{ + int i; + + for (i = 0; i < SR_SHARE_TIMEOUT; i++) { + u8 tmp = 0; + int ret; + + udelay(1); + ret = sr_read_reg(dev, EPCR, &tmp); + if (ret < 0) + return ret; + + /* ready */ + if (!(tmp & EPCR_ERRE)) + return 0; + } + + netdev_err(dev->net, "%s write timed out!\n", phy ? "phy" : "eeprom"); + + return -EIO; +} + +static int sr_share_read_word(struct usbnet *dev, int phy, u8 reg, + __le16 *value) +{ + int ret; + + mutex_lock(&dev->phy_mutex); + + sr_write_reg(dev, EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); + sr_write_reg(dev, EPCR, phy ? (EPCR_EPOS | EPCR_ERPRR) : EPCR_ERPRR); + + ret = wait_phy_eeprom_ready(dev, phy); + if (ret < 0) + goto out_unlock; + + sr_write_reg(dev, EPCR, 0x0); + ret = sr_read(dev, EPDR, 2, value); + + netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n", + phy, reg, *value, ret); + +out_unlock: + mutex_unlock(&dev->phy_mutex); + return ret; +} + +static int sr_share_write_word(struct usbnet *dev, int phy, u8 reg, + __le16 value) +{ + int ret; + + mutex_lock(&dev->phy_mutex); + + ret = sr_write(dev, EPDR, 2, &value); + if (ret < 0) + goto out_unlock; + + sr_write_reg(dev, EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); + sr_write_reg(dev, EPCR, phy ? (EPCR_WEP | EPCR_EPOS | EPCR_ERPRW) : + (EPCR_WEP | EPCR_ERPRW)); + + ret = wait_phy_eeprom_ready(dev, phy); + if (ret < 0) + goto out_unlock; + + sr_write_reg(dev, EPCR, 0x0); + +out_unlock: + mutex_unlock(&dev->phy_mutex); + return ret; +} + +static int sr_read_eeprom_word(struct usbnet *dev, u8 offset, void *value) +{ + return sr_share_read_word(dev, 0, offset, value); +} + +static int sr9700_get_eeprom_len(struct net_device *netdev) +{ + return SR_EEPROM_LEN; +} + +static int sr9700_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct usbnet *dev = netdev_priv(netdev); + __le16 *buf = (__le16 *)data; + int ret = 0; + int i; + + /* access is 16bit */ + if ((eeprom->offset & 0x01) || (eeprom->len & 0x01)) + return -EINVAL; + + for (i = 0; i < eeprom->len / 2; i++) { + ret = sr_read_eeprom_word(dev, eeprom->offset / 2 + i, buf + i); + if (ret < 0) + break; + } + + return ret; +} + +static int sr_mdio_read(struct net_device *netdev, int phy_id, int loc) +{ + struct usbnet *dev = netdev_priv(netdev); + __le16 res; + int rc = 0; + + if (phy_id) { + netdev_dbg(netdev, "Only internal phy supported\n"); + return 0; + } + + /* Access NSR_LINKST bit for link status instead of MII_BMSR */ + if (loc == MII_BMSR) { + u8 value; + + sr_read_reg(dev, NSR, &value); + if (value & NSR_LINKST) + rc = 1; + } + sr_share_read_word(dev, 1, loc, &res); + if (rc == 1) + res = le16_to_cpu(res) | BMSR_LSTATUS; + else + res = le16_to_cpu(res) & ~BMSR_LSTATUS; + + netdev_dbg(netdev, "sr_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", + phy_id, loc, res); + + return res; +} + +static void sr_mdio_write(struct net_device *netdev, int phy_id, int loc, + int val) +{ + struct usbnet *dev = netdev_priv(netdev); + __le16 res = cpu_to_le16(val); + + if (phy_id) { + netdev_dbg(netdev, "Only internal phy supported\n"); + return; + } + + netdev_dbg(netdev, "sr_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", + phy_id, loc, val); + + sr_share_write_word(dev, 1, loc, res); +} + +static u32 sr9700_get_link(struct net_device *netdev) +{ + struct usbnet *dev = netdev_priv(netdev); + u8 value = 0; + int rc = 0; + + /* Get the Link Status directly */ + sr_read_reg(dev, NSR, &value); + if (value & NSR_LINKST) + rc = 1; + + return rc; +} + +static int sr9700_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) +{ + struct usbnet *dev = netdev_priv(netdev); + + return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); +} + +static const struct ethtool_ops sr9700_ethtool_ops = { + .get_drvinfo = usbnet_get_drvinfo, + .get_link = sr9700_get_link, + .get_msglevel = usbnet_get_msglevel, + .set_msglevel = usbnet_set_msglevel, + .get_eeprom_len = sr9700_get_eeprom_len, + .get_eeprom = sr9700_get_eeprom, + .get_settings = usbnet_get_settings, + .set_settings = usbnet_set_settings, + .nway_reset = usbnet_nway_reset, +}; + +static void sr9700_set_multicast(struct net_device *netdev) +{ + struct usbnet *dev = netdev_priv(netdev); + /* We use the 20 byte dev->data for our 8 byte filter buffer + * to avoid allocating memory that is tricky to free later + */ + u8 *hashes = (u8 *)&dev->data; + /* rx_ctl setting : enable, disable_long, disable_crc */ + u8 rx_ctl = RCR_RXEN | RCR_DIS_CRC | RCR_DIS_LONG; + + memset(hashes, 0x00, SR_MCAST_SIZE); + /* broadcast address */ + hashes[SR_MCAST_SIZE - 1] |= SR_MCAST_ADDR_FLAG; + if (netdev->flags & IFF_PROMISC) { + rx_ctl |= RCR_PRMSC; + } else if (netdev->flags & IFF_ALLMULTI || + netdev_mc_count(netdev) > SR_MCAST_MAX) { + rx_ctl |= RCR_RUNT; + } else if (!netdev_mc_empty(netdev)) { + struct netdev_hw_addr *ha; + + netdev_for_each_mc_addr(ha, netdev) { + u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26; + hashes[crc >> 3] |= 1 << (crc & 0x7); + } + } + + sr_write_async(dev, MAR, SR_MCAST_SIZE, hashes); + sr_write_reg_async(dev, RCR, rx_ctl); +} + +static int sr9700_set_mac_address(struct net_device *netdev, void *p) +{ + struct usbnet *dev = netdev_priv(netdev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) { + netdev_err(netdev, "not setting invalid mac address %pM\n", + addr->sa_data); + return -EINVAL; + } + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + sr_write_async(dev, PAR, 6, netdev->dev_addr); + + return 0; +} + +static const struct net_device_ops sr9700_netdev_ops = { + .ndo_open = usbnet_open, + .ndo_stop = usbnet_stop, + .ndo_start_xmit = usbnet_start_xmit, + .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_change_mtu = usbnet_change_mtu, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = sr9700_ioctl, + .ndo_set_rx_mode = sr9700_set_multicast, + .ndo_set_mac_address = sr9700_set_mac_address, +}; + +static int sr9700_bind(struct usbnet *dev, struct usb_interface *intf) +{ + struct net_device *netdev; + struct mii_if_info *mii; + int ret; + + ret = usbnet_get_endpoints(dev, intf); + if (ret) + goto out; + + netdev = dev->net; + + netdev->netdev_ops = &sr9700_netdev_ops; + netdev->ethtool_ops = &sr9700_ethtool_ops; + netdev->hard_header_len += SR_TX_OVERHEAD; + dev->hard_mtu = netdev->mtu + netdev->hard_header_len; + /* bulkin buffer is preferably not less than 3K */ + dev->rx_urb_size = 3072; + + mii = &dev->mii; + mii->dev = netdev; + mii->mdio_read = sr_mdio_read; + mii->mdio_write = sr_mdio_write; + mii->phy_id_mask = 0x1f; + mii->reg_num_mask = 0x1f; + + sr_write_reg(dev, NCR, NCR_RST); + udelay(20); + + /* read MAC + * After Chip Power on, the Chip will reload the MAC from + * EEPROM automatically to PAR. In case there is no EEPROM externally, + * a default MAC address is stored in PAR for making chip work properly. + */ + if (sr_read(dev, PAR, ETH_ALEN, netdev->dev_addr) < 0) { + netdev_err(netdev, "Error reading MAC address\n"); + ret = -ENODEV; + goto out; + } + + /* power up and reset phy */ + sr_write_reg(dev, PRR, PRR_PHY_RST); + /* at least 10ms, here 20ms for safe */ + mdelay(20); + sr_write_reg(dev, PRR, 0); + /* at least 1ms, here 2ms for reading right register */ + udelay(2 * 1000); + + /* receive broadcast packets */ + sr9700_set_multicast(netdev); + + sr_mdio_write(netdev, mii->phy_id, MII_BMCR, BMCR_RESET); + sr_mdio_write(netdev, mii->phy_id, MII_ADVERTISE, ADVERTISE_ALL | + ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); + mii_nway_restart(mii); + +out: + return ret; +} + +static int sr9700_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ + struct sk_buff *sr_skb; + int len; + + /* skb content (packets) format : + * p0 p1 p2 ...... pm + * / \ + * / \ + * / \ + * / \ + * p0b0 p0b1 p0b2 p0b3 ...... p0b(n-4) p0b(n-3)...p0bn + * + * p0 : packet 0 + * p0b0 : packet 0 byte 0 + * + * b0: rx status + * b1: packet length (incl crc) low + * b2: packet length (incl crc) high + * b3..n-4: packet data + * bn-3..bn: ethernet packet crc + */ + if (unlikely(skb->len < SR_RX_OVERHEAD)) { + netdev_err(dev->net, "unexpected tiny rx frame\n"); + return 0; + } + + /* one skb may contains multiple packets */ + while (skb->len > SR_RX_OVERHEAD) { + if (skb->data[0] != 0x40) + return 0; + + /* ignore the CRC length */ + len = (skb->data[1] | (skb->data[2] << 8)) - 4; + + if (len > ETH_FRAME_LEN) + return 0; + + /* the last packet of current skb */ + if (skb->len == (len + SR_RX_OVERHEAD)) { + skb_pull(skb, 3); + skb->len = len; + skb_set_tail_pointer(skb, len); + skb->truesize = len + sizeof(struct sk_buff); + return 2; + } + + /* skb_clone is used for address align */ + sr_skb = skb_clone(skb, GFP_ATOMIC); + if (!sr_skb) + return 0; + + sr_skb->len = len; + sr_skb->data = skb->data + 3; + skb_set_tail_pointer(sr_skb, len); + sr_skb->truesize = len + sizeof(struct sk_buff); + usbnet_skb_return(dev, sr_skb); + + skb_pull(skb, len + SR_RX_OVERHEAD); + }; + + return 0; +} + +static struct sk_buff *sr9700_tx_fixup(struct usbnet *dev, struct sk_buff *skb, + gfp_t flags) +{ + int len; + + /* SR9700 can only send out one ethernet packet at once. + * + * b0 b1 b2 b3 ...... b(n-4) b(n-3)...bn + * + * b0: rx status + * b1: packet length (incl crc) low + * b2: packet length (incl crc) high + * b3..n-4: packet data + * bn-3..bn: ethernet packet crc + */ + + len = skb->len; + + if (skb_headroom(skb) < SR_TX_OVERHEAD) { + struct sk_buff *skb2; + + skb2 = skb_copy_expand(skb, SR_TX_OVERHEAD, 0, flags); + dev_kfree_skb_any(skb); + skb = skb2; + if (!skb) + return NULL; + } + + __skb_push(skb, SR_TX_OVERHEAD); + + /* usbnet adds padding if length is a multiple of packet size + * if so, adjust length value in header + */ + if ((skb->len % dev->maxpacket) == 0) + len++; + + skb->data[0] = len; + skb->data[1] = len >> 8; + + return skb; +} + +static void sr9700_status(struct usbnet *dev, struct urb *urb) +{ + int link; + u8 *buf; + + /* format: + b0: net status + b1: tx status 1 + b2: tx status 2 + b3: rx status + b4: rx overflow + b5: rx count + b6: tx count + b7: gpr + */ + + if (urb->actual_length < 8) + return; + + buf = urb->transfer_buffer; + + link = !!(buf[0] & 0x40); + if (netif_carrier_ok(dev->net) != link) { + usbnet_link_change(dev, link, 1); + netdev_dbg(dev->net, "Link Status is: %d\n", link); + } +} + +static int sr9700_link_reset(struct usbnet *dev) +{ + struct ethtool_cmd ecmd; + + mii_check_media(&dev->mii, 1, 1); + mii_ethtool_gset(&dev->mii, &ecmd); + + netdev_dbg(dev->net, "link_reset() speed: %d duplex: %d\n", + ecmd.speed, ecmd.duplex); + + return 0; +} + +static const struct driver_info sr9700_driver_info = { + .description = "CoreChip SR9700 USB Ethernet", + .flags = FLAG_ETHER, + .bind = sr9700_bind, + .rx_fixup = sr9700_rx_fixup, + .tx_fixup = sr9700_tx_fixup, + .status = sr9700_status, + .link_reset = sr9700_link_reset, + .reset = sr9700_link_reset, +}; + +static const struct usb_device_id products[] = { + { + USB_DEVICE(0x0fe6, 0x9700), /* SR9700 device */ + .driver_info = (unsigned long)&sr9700_driver_info, + }, + {}, /* END */ +}; + +MODULE_DEVICE_TABLE(usb, products); + +static struct usb_driver sr9700_usb_driver = { + .name = "sr9700", + .id_table = products, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, + .suspend = usbnet_suspend, + .resume = usbnet_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(sr9700_usb_driver); + +MODULE_AUTHOR("liujl <liujunliang_ljl@163.com>"); +MODULE_DESCRIPTION("SR9700 one chip USB 1.1 USB to Ethernet device from http://www.corechip-sz.com/"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/sr9700.h b/drivers/net/usb/sr9700.h new file mode 100644 index 000000000000..fd687c575e74 --- /dev/null +++ b/drivers/net/usb/sr9700.h @@ -0,0 +1,173 @@ +/* + * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices + * + * Author : Liu Junliang <liujunliang_ljl@163.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef _SR9700_H +#define _SR9700_H + +/* sr9700 spec. register table on Linux platform */ + +/* Network Control Reg */ +#define NCR 0x00 +#define NCR_RST (1 << 0) +#define NCR_LBK (3 << 1) +#define NCR_FDX (1 << 3) +#define NCR_WAKEEN (1 << 6) +/* Network Status Reg */ +#define NSR 0x01 +#define NSR_RXRDY (1 << 0) +#define NSR_RXOV (1 << 1) +#define NSR_TX1END (1 << 2) +#define NSR_TX2END (1 << 3) +#define NSR_TXFULL (1 << 4) +#define NSR_WAKEST (1 << 5) +#define NSR_LINKST (1 << 6) +#define NSR_SPEED (1 << 7) +/* Tx Control Reg */ +#define TCR 0x02 +#define TCR_CRC_DIS (1 << 1) +#define TCR_PAD_DIS (1 << 2) +#define TCR_LC_CARE (1 << 3) +#define TCR_CRS_CARE (1 << 4) +#define TCR_EXCECM (1 << 5) +#define TCR_LF_EN (1 << 6) +/* Tx Status Reg for Packet Index 1 */ +#define TSR1 0x03 +#define TSR1_EC (1 << 2) +#define TSR1_COL (1 << 3) +#define TSR1_LC (1 << 4) +#define TSR1_NC (1 << 5) +#define TSR1_LOC (1 << 6) +#define TSR1_TLF (1 << 7) +/* Tx Status Reg for Packet Index 2 */ +#define TSR2 0x04 +#define TSR2_EC (1 << 2) +#define TSR2_COL (1 << 3) +#define TSR2_LC (1 << 4) +#define TSR2_NC (1 << 5) +#define TSR2_LOC (1 << 6) +#define TSR2_TLF (1 << 7) +/* Rx Control Reg*/ +#define RCR 0x05 +#define RCR_RXEN (1 << 0) +#define RCR_PRMSC (1 << 1) +#define RCR_RUNT (1 << 2) +#define RCR_ALL (1 << 3) +#define RCR_DIS_CRC (1 << 4) +#define RCR_DIS_LONG (1 << 5) +/* Rx Status Reg */ +#define RSR 0x06 +#define RSR_AE (1 << 2) +#define RSR_MF (1 << 6) +#define RSR_RF (1 << 7) +/* Rx Overflow Counter Reg */ +#define ROCR 0x07 +#define ROCR_ROC (0x7F << 0) +#define ROCR_RXFU (1 << 7) +/* Back Pressure Threshold Reg */ +#define BPTR 0x08 +#define BPTR_JPT (0x0F << 0) +#define BPTR_BPHW (0x0F << 4) +/* Flow Control Threshold Reg */ +#define FCTR 0x09 +#define FCTR_LWOT (0x0F << 0) +#define FCTR_HWOT (0x0F << 4) +/* rx/tx Flow Control Reg */ +#define FCR 0x0A +#define FCR_FLCE (1 << 0) +#define FCR_BKPA (1 << 4) +#define FCR_TXPEN (1 << 5) +#define FCR_TXPF (1 << 6) +#define FCR_TXP0 (1 << 7) +/* Eeprom & Phy Control Reg */ +#define EPCR 0x0B +#define EPCR_ERRE (1 << 0) +#define EPCR_ERPRW (1 << 1) +#define EPCR_ERPRR (1 << 2) +#define EPCR_EPOS (1 << 3) +#define EPCR_WEP (1 << 4) +/* Eeprom & Phy Address Reg */ +#define EPAR 0x0C +#define EPAR_EROA (0x3F << 0) +#define EPAR_PHY_ADR_MASK (0x03 << 6) +#define EPAR_PHY_ADR (0x01 << 6) +/* Eeprom & Phy Data Reg */ +#define EPDR 0x0D /* 0x0D ~ 0x0E for Data Reg Low & High */ +/* Wakeup Control Reg */ +#define WCR 0x0F +#define WCR_MAGICST (1 << 0) +#define WCR_LINKST (1 << 2) +#define WCR_MAGICEN (1 << 3) +#define WCR_LINKEN (1 << 5) +/* Physical Address Reg */ +#define PAR 0x10 /* 0x10 ~ 0x15 6 bytes for PAR */ +/* Multicast Address Reg */ +#define MAR 0x16 /* 0x16 ~ 0x1D 8 bytes for MAR */ +/* 0x1e unused */ +/* Phy Reset Reg */ +#define PRR 0x1F +#define PRR_PHY_RST (1 << 0) +/* Tx sdram Write Pointer Address Low */ +#define TWPAL 0x20 +/* Tx sdram Write Pointer Address High */ +#define TWPAH 0x21 +/* Tx sdram Read Pointer Address Low */ +#define TRPAL 0x22 +/* Tx sdram Read Pointer Address High */ +#define TRPAH 0x23 +/* Rx sdram Write Pointer Address Low */ +#define RWPAL 0x24 +/* Rx sdram Write Pointer Address High */ +#define RWPAH 0x25 +/* Rx sdram Read Pointer Address Low */ +#define RRPAL 0x26 +/* Rx sdram Read Pointer Address High */ +#define RRPAH 0x27 +/* Vendor ID register */ +#define VID 0x28 /* 0x28 ~ 0x29 2 bytes for VID */ +/* Product ID register */ +#define PID 0x2A /* 0x2A ~ 0x2B 2 bytes for PID */ +/* CHIP Revision register */ +#define CHIPR 0x2C +/* 0x2D --> 0xEF unused */ +/* USB Device Address */ +#define USBDA 0xF0 +#define USBDA_USBFA (0x7F << 0) +/* RX packet Counter Reg */ +#define RXC 0xF1 +/* Tx packet Counter & USB Status Reg */ +#define TXC_USBS 0xF2 +#define TXC_USBS_TXC0 (1 << 0) +#define TXC_USBS_TXC1 (1 << 1) +#define TXC_USBS_TXC2 (1 << 2) +#define TXC_USBS_EP1RDY (1 << 5) +#define TXC_USBS_SUSFLAG (1 << 6) +#define TXC_USBS_RXFAULT (1 << 7) +/* USB Control register */ +#define USBC 0xF4 +#define USBC_EP3NAK (1 << 4) +#define USBC_EP3ACK (1 << 5) + +/* Register access commands and flags */ +#define SR_RD_REGS 0x00 +#define SR_WR_REGS 0x01 +#define SR_WR_REG 0x03 +#define SR_REQ_RD_REG (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE) +#define SR_REQ_WR_REG (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE) + +/* parameters */ +#define SR_SHARE_TIMEOUT 1000 +#define SR_EEPROM_LEN 256 +#define SR_MCAST_SIZE 8 +#define SR_MCAST_ADDR_FLAG 0x80 +#define SR_MCAST_MAX 64 +#define SR_TX_OVERHEAD 2 /* 2bytes header */ +#define SR_RX_OVERHEAD 7 /* 3bytes header + 4crc tail */ + +#endif /* _SR9700_H */ diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 06ee82f557d4..7b331e613e02 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -59,15 +59,13 @@ * For high speed, each frame comfortably fits almost 36 max size * Ethernet packets (so queues should be bigger). * - * REVISIT qlens should be members of 'struct usbnet'; the goal is to - * let the USB host controller be busy for 5msec or more before an irq - * is required, under load. Jumbograms change the equation. + * The goal is to let the USB host controller be busy for 5msec or + * more before an irq is required, under load. Jumbograms change + * the equation. */ -#define RX_MAX_QUEUE_MEMORY (60 * 1518) -#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ - (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4) -#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ - (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4) +#define MAX_QUEUE_MEMORY (60 * 1518) +#define RX_QLEN(dev) ((dev)->rx_qlen) +#define TX_QLEN(dev) ((dev)->tx_qlen) // reawaken network queue this soon after stopping; else watchdog barks #define TX_TIMEOUT_JIFFIES (5*HZ) @@ -347,6 +345,31 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(usbnet_skb_return); +/* must be called if hard_mtu or rx_urb_size changed */ +void usbnet_update_max_qlen(struct usbnet *dev) +{ + enum usb_device_speed speed = dev->udev->speed; + + switch (speed) { + case USB_SPEED_HIGH: + dev->rx_qlen = MAX_QUEUE_MEMORY / dev->rx_urb_size; + dev->tx_qlen = MAX_QUEUE_MEMORY / dev->hard_mtu; + break; + case USB_SPEED_SUPER: + /* + * Not take default 5ms qlen for super speed HC to + * save memory, and iperf tests show 2.5ms qlen can + * work well + */ + dev->rx_qlen = 5 * MAX_QUEUE_MEMORY / dev->rx_urb_size; + dev->tx_qlen = 5 * MAX_QUEUE_MEMORY / dev->hard_mtu; + break; + default: + dev->rx_qlen = dev->tx_qlen = 4; + } +} +EXPORT_SYMBOL_GPL(usbnet_update_max_qlen); + /*------------------------------------------------------------------------- * @@ -375,6 +398,9 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu) usbnet_unlink_rx_urbs(dev); } + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } EXPORT_SYMBOL_GPL(usbnet_change_mtu); @@ -843,6 +869,9 @@ int usbnet_open (struct net_device *net) goto done; } + /* hard_mtu or rx_urb_size may change in reset() */ + usbnet_update_max_qlen(dev); + // insist peer be connected if (info->check_connect && (retval = info->check_connect (dev)) < 0) { netif_dbg(dev, ifup, dev->net, "can't open; %d\n", retval); @@ -927,6 +956,9 @@ int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd) if (dev->driver_info->link_reset) dev->driver_info->link_reset(dev); + /* hard_mtu or rx_urb_size may change in link_reset() */ + usbnet_update_max_qlen(dev); + return retval; } @@ -1020,6 +1052,9 @@ static void __handle_link_change(struct usbnet *dev) tasklet_schedule(&dev->bh); } + /* hard_mtu or rx_urb_size may change during link change */ + usbnet_update_max_qlen(dev); + clear_bit(EVENT_LINK_CHANGE, &dev->flags); } @@ -1197,6 +1232,37 @@ EXPORT_SYMBOL_GPL(usbnet_tx_timeout); /*-------------------------------------------------------------------------*/ +static int build_dma_sg(const struct sk_buff *skb, struct urb *urb) +{ + unsigned num_sgs, total_len = 0; + int i, s = 0; + + num_sgs = skb_shinfo(skb)->nr_frags + 1; + if (num_sgs == 1) + return 0; + + urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), GFP_ATOMIC); + if (!urb->sg) + return -ENOMEM; + + urb->num_sgs = num_sgs; + sg_init_table(urb->sg, urb->num_sgs); + + sg_set_buf(&urb->sg[s++], skb->data, skb_headlen(skb)); + total_len += skb_headlen(skb); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + struct skb_frag_struct *f = &skb_shinfo(skb)->frags[i]; + + total_len += skb_frag_size(f); + sg_set_page(&urb->sg[i + s], f->page.p, f->size, + f->page_offset); + } + urb->transfer_buffer_length = total_len; + + return 1; +} + netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) { @@ -1223,7 +1289,6 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, goto drop; } } - length = skb->len; if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) { netif_dbg(dev, tx_err, dev->net, "no urb\n"); @@ -1233,10 +1298,14 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; - entry->length = length; usb_fill_bulk_urb (urb, dev->udev, dev->out, skb->data, skb->len, tx_complete, skb); + if (dev->can_dma_sg) { + if (build_dma_sg(skb, urb) < 0) + goto drop; + } + entry->length = length = urb->transfer_buffer_length; /* don't assume the hardware handles USB_ZERO_PACKET * NOTE: strictly conforming cdc-ether devices should expect @@ -1305,7 +1374,10 @@ drop: not_drop: if (skb) dev_kfree_skb_any (skb); - usb_free_urb (urb); + if (urb) { + kfree(urb->sg); + usb_free_urb(urb); + } } else netif_dbg(dev, tx_queued, dev->net, "> tx, len %d, type 0x%x\n", length, skb->protocol); @@ -1356,6 +1428,7 @@ static void usbnet_bh (unsigned long param) rx_process (dev, skb); continue; case tx_done: + kfree(entry->urb->sg); case rx_cleanup: usb_free_urb (entry->urb); dev_kfree_skb (skb); @@ -1594,11 +1667,18 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) dev->rx_urb_size = dev->hard_mtu; dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1); + /* let userspace know we have a random address */ + if (ether_addr_equal(net->dev_addr, node_id)) + net->addr_assign_type = NET_ADDR_RANDOM; + if ((dev->driver_info->flags & FLAG_WLAN) != 0) SET_NETDEV_DEVTYPE(net, &wlan_type); if ((dev->driver_info->flags & FLAG_WWAN) != 0) SET_NETDEV_DEVTYPE(net, &wwan_type); + /* initialize max rx_qlen and tx_qlen */ + usbnet_update_max_qlen(dev); + status = register_netdev (net); if (status) goto out4; @@ -1689,6 +1769,7 @@ int usbnet_resume (struct usb_interface *intf) retval = usb_submit_urb(res, GFP_ATOMIC); if (retval < 0) { dev_kfree_skb_any(skb); + kfree(res->sg); usb_free_urb(res); usb_autopm_put_interface_async(dev->intf); } else { |