diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-05 14:54:29 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-05 14:54:29 -0700 |
commit | cc998ff8811530be521f6b316f37ab7676a07938 (patch) | |
tree | a054b3bf4b2ef406bf756a6cfc9be2f9115f17ae /drivers/net/usb | |
parent | 57d730924d5cc2c3e280af16a9306587c3a511db (diff) | |
parent | 0d40f75bdab241868c0eb6f97aef9f8b3a66f7b3 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking changes from David Miller:
"Noteworthy changes this time around:
1) Multicast rejoin support for team driver, from Jiri Pirko.
2) Centralize and simplify TCP RTT measurement handling in order to
reduce the impact of bad RTO seeding from SYN/ACKs. Also, when
both timestamps and local RTT measurements are available prefer
the later because there are broken middleware devices which
scramble the timestamp.
From Yuchung Cheng.
3) Add TCP_NOTSENT_LOWAT socket option to limit the amount of kernel
memory consumed to queue up unsend user data. From Eric Dumazet.
4) Add a "physical port ID" abstraction for network devices, from
Jiri Pirko.
5) Add a "suppress" operation to influence fib_rules lookups, from
Stefan Tomanek.
6) Add a networking development FAQ, from Paul Gortmaker.
7) Extend the information provided by tcp_probe and add ipv6 support,
from Daniel Borkmann.
8) Use RCU locking more extensively in openvswitch data paths, from
Pravin B Shelar.
9) Add SCTP support to openvswitch, from Joe Stringer.
10) Add EF10 chip support to SFC driver, from Ben Hutchings.
11) Add new SYNPROXY netfilter target, from Patrick McHardy.
12) Compute a rate approximation for sending in TCP sockets, and use
this to more intelligently coalesce TSO frames. Furthermore, add
a new packet scheduler which takes advantage of this estimate when
available. From Eric Dumazet.
13) Allow AF_PACKET fanouts with random selection, from Daniel
Borkmann.
14) Add ipv6 support to vxlan driver, from Cong Wang"
Resolved conflicts as per discussion.
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1218 commits)
openvswitch: Fix alignment of struct sw_flow_key.
netfilter: Fix build errors with xt_socket.c
tcp: Add missing braces to do_tcp_setsockopt
caif: Add missing braces to multiline if in cfctrl_linkup_request
bnx2x: Add missing braces in bnx2x:bnx2x_link_initialize
vxlan: Fix kernel panic on device delete.
net: mvneta: implement ->ndo_do_ioctl() to support PHY ioctls
net: mvneta: properly disable HW PHY polling and ensure adjust_link() works
icplus: Use netif_running to determine device state
ethernet/arc/arc_emac: Fix huge delays in large file copies
tuntap: orphan frags before trying to set tx timestamp
tuntap: purge socket error queue on detach
qlcnic: use standard NAPI weights
ipv6:introduce function to find route for redirect
bnx2x: VF RSS support - VF side
bnx2x: VF RSS support - PF side
vxlan: Notify drivers for listening UDP port changes
net: usbnet: update addr_assign_type if appropriate
driver/net: enic: update enic maintainers and driver
driver/net: enic: Exposing symbols for Cisco's low latency driver
...
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 | 24 | ||||
-rw-r--r-- | drivers/net/usb/qmi_wwan.c | 2 | ||||
-rw-r--r-- | drivers/net/usb/r8152.c | 845 | ||||
-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 | 58 |
11 files changed, 1469 insertions, 217 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index d84bfd4109a..40db3123331 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 e8171784529..8b342cf992f 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 346c032aa79..bdaa12d07a1 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 ad5d1e4384d..386a3df5367 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 d012203b0f2..723b3879ecc 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 4233c05a3e3..3569293df87 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; } @@ -1174,31 +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 */ - 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); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 606eba2872b..3a8131582e7 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 11c51f27536..f3fce412c0c 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,6 +381,7 @@ 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) @@ -686,6 +728,9 @@ 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; @@ -716,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; @@ -743,137 +768,574 @@ 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; - 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); + + /* When link down, the driver would cancel all bulks. */ + /* This avoid the re-submitting bulk */ + if (!netif_carrier_ok(tp->netdev)) + return; + + 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) @@ -923,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) { @@ -957,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; @@ -999,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); } @@ -1023,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; @@ -1058,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); } @@ -1269,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; } @@ -1292,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) { @@ -1303,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; @@ -1319,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; } @@ -1335,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; } @@ -1366,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; } @@ -1429,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); @@ -1475,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); } @@ -1489,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); @@ -1504,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; @@ -1619,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); @@ -1631,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; @@ -1657,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) @@ -1715,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); } } @@ -1732,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/sr9700.c b/drivers/net/usb/sr9700.c new file mode 100644 index 00000000000..7ec3e0ee078 --- /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 00000000000..fd687c575e7 --- /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 27a00b00603..7b331e613e0 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); } @@ -1632,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; |