* [PATCH net 0/2] r8152: rx patches @ 2016-11-11 7:15 Hayes Wang 2016-11-11 7:15 ` [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable Hayes Wang 2016-11-11 7:15 ` [PATCH net 2/2] r8152: rx descriptor check Hayes Wang 0 siblings, 2 replies; 80+ messages in thread From: Hayes Wang @ 2016-11-11 7:15 UTC (permalink / raw) To: netdev; +Cc: nic_swsd, linux-kernel, linux-usb, mlord, Hayes Wang Let the rx sw checksum available and add some checks for rx desc. Hayes Wang (2): r8152: fix the sw rx checksum is unavailable r8152: rx descriptor check drivers/net/usb/r8152.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) -- 2.7.4 ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-11 7:15 [PATCH net 0/2] r8152: rx patches Hayes Wang @ 2016-11-11 7:15 ` Hayes Wang 2016-11-17 3:36 ` Hayes Wang 2016-11-11 7:15 ` [PATCH net 2/2] r8152: rx descriptor check Hayes Wang 1 sibling, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-11 7:15 UTC (permalink / raw) To: netdev; +Cc: nic_swsd, linux-kernel, linux-usb, mlord, Hayes Wang Fix the hw rx checksum is always enabled, and the user couldn't switch it to sw rx checksum. Note that the RTL_VER_01 only supports sw rx checksum only. Besides, the hw rx checksum for RTL_VER_02 is disabled after commit b9a321b48af4 ("r8152: Fix broken RX checksums."). Re-enable it. Signed-off-by: Hayes Wang <hayeswang@realtek.com> --- drivers/net/usb/r8152.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 75c5168..0e42a78 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1730,7 +1730,7 @@ static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc) u8 checksum = CHECKSUM_NONE; u32 opts2, opts3; - if (tp->version == RTL_VER_01 || tp->version == RTL_VER_02) + if (!(tp->netdev->features & NETIF_F_RXCSUM)) goto return_result; opts2 = le32_to_cpu(rx_desc->opts2); @@ -4307,6 +4307,11 @@ static int rtl8152_probe(struct usb_interface *intf, NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6; + if (tp->version == RTL_VER_01) { + netdev->features &= ~NETIF_F_RXCSUM; + netdev->hw_features &= ~NETIF_F_RXCSUM; + } + netdev->ethtool_ops = &ops; netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE); -- 2.7.4 ^ permalink raw reply related [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-11 7:15 ` [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable Hayes Wang @ 2016-11-17 3:36 ` Hayes Wang 2016-11-17 14:14 ` Mark Lord [not found] ` <d683c019-4e0f-6fe6-368c-c4fc86c72fe6@pobox.com> 0 siblings, 2 replies; 80+ messages in thread From: Hayes Wang @ 2016-11-17 3:36 UTC (permalink / raw) To: netdev; +Cc: nic_swsd, linux-kernel, linux-usb, mlord [...] > Fix the hw rx checksum is always enabled, and the user couldn't switch > it to sw rx checksum. > > Note that the RTL_VER_01 only supports sw rx checksum only. Besides, > the hw rx checksum for RTL_VER_02 is disabled after > commit b9a321b48af4 ("r8152: Fix broken RX checksums."). Re-enable it. Excuse me. If I want to re-send this one patch, should I let RTL_VER_02 use rx hw checksum? Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-17 3:36 ` Hayes Wang @ 2016-11-17 14:14 ` Mark Lord 2016-11-17 14:25 ` Mark Lord [not found] ` <d683c019-4e0f-6fe6-368c-c4fc86c72fe6@pobox.com> 1 sibling, 1 reply; 80+ messages in thread From: Mark Lord @ 2016-11-17 14:14 UTC (permalink / raw) To: Hayes Wang, netdev; +Cc: nic_swsd, linux-kernel, linux-usb (resending.. not sure if the original had mailer errors) On 16-11-16 10:36 PM, Hayes Wang wrote: > [...] >> Fix the hw rx checksum is always enabled, and the user couldn't switch >> it to sw rx checksum. >> >> Note that the RTL_VER_01 only supports sw rx checksum only. Besides, >> the hw rx checksum for RTL_VER_02 is disabled after >> commit b9a321b48af4 ("r8152: Fix broken RX checksums."). Re-enable it. > > Excuse me. If I want to re-send this one patch, should I let > RTL_VER_02 use rx hw checksum? Definitely NOT. I am still doing low-level tracing through the driver as time permits, and just now found some really interesting evidence. Using coherent buffers (non-cacheable, allocated with usb_alloc_coherent), I can get it to fail extremely regularly by simply reducing the buffer size (agg_buf_sz) from 16KB down to 4KB. This makes reproducing the issue much much easier -- the same problems do happen with the larger 16KB size, but much less often than with smaller sizes. So.. with a 4KB URB transfer_buffer size, along with a ton of added error-checking, I see this behaviour every 10 (rx) URBs or so: First URB (number 593): [ 34.260667] r8152_rx_bottom: 593 corrupted urb: head=bf014000 urb_offset=2856/4096 pkt_len(1518) exceeds remainder(1216) [ 34.271931] r8152_dump_rx_desc: 044805ee 40080000 006005dc 06020000 00000000 00000000 rx_len=1518 Next URB (number 594): [ 34.281172] r8152_check_rx_desc: rx_desc looks bad. [ 34.286228] r8152_rx_bottom: 594 corrupted urb. head=bf018000 urb_offset=0/304 len_used=24 [ 34.294774] r8152_dump_rx_desc: 00008300 00008400 00008500 00008600 00008700 00008800 rx_len=768 What the above sample shows, is the URB transfer buffer ran out of space in the middle of a packet, and the hardware then tried to just continue that same packet in the next URB, without an rx_desc header inserted. The r8152.c driver always assumes the URB buffer begins with an rx_desc, so of course this behaviour produces really weird effects, and system crashes, etc.. So until that driver bug is addressed, I would advise disabling hardware RX checksums for all chip versions, not only for version 02. It is not clear to me how the chip decides when to forward an rx URB to the host. If you could describe how that part works for us, then it would help in further understanding why fast systems (eg. a PC) don't generally notice the issue, while much slower embedded systems do see the issue regularly. Thanks Mark ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-17 14:14 ` Mark Lord @ 2016-11-17 14:25 ` Mark Lord 0 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-17 14:25 UTC (permalink / raw) To: Hayes Wang, netdev; +Cc: nic_swsd, linux-kernel, linux-usb, David Miller On 16-11-17 09:14 AM, Mark Lord wrote: .. > Using coherent buffers (non-cacheable, allocated with usb_alloc_coherent), Note that the same behaviour also happens with the original kmalloc'd buffers. > I can get it to fail extremely regularly by simply reducing the buffer size > (agg_buf_sz) from 16KB down to 4KB. This makes reproducing the issue > much much easier -- the same problems do happen with the larger 16KB size, > but much less often than with smaller sizes. Increasing the buffer size to 64KB makes the problem much less frequent, as one might expect. Thus far I haven't seen it happen at all, but a longer run (1-3 days) is needed to make sure. This however is NOT a "fix". > So.. with a 4KB URB transfer_buffer size, along with a ton of added error-checking, > I see this behaviour every 10 (rx) URBs or so: > > First URB (number 593): > [ 34.260667] r8152_rx_bottom: 593 corrupted urb: head=bf014000 urb_offset=2856/4096 pkt_len(1518) exceeds remainder(1216) > [ 34.271931] r8152_dump_rx_desc: 044805ee 40080000 006005dc 06020000 00000000 00000000 rx_len=1518 > > Next URB (number 594): > [ 34.281172] r8152_check_rx_desc: rx_desc looks bad. > [ 34.286228] r8152_rx_bottom: 594 corrupted urb. head=bf018000 urb_offset=0/304 len_used=24 > [ 34.294774] r8152_dump_rx_desc: 00008300 00008400 00008500 00008600 00008700 00008800 rx_len=768 > > What the above sample shows, is the URB transfer buffer ran out of space in the middle > of a packet, and the hardware then tried to just continue that same packet in the next URB, > without an rx_desc header inserted. The r8152.c driver always assumes the URB buffer begins > with an rx_desc, so of course this behaviour produces really weird effects, and system crashes, etc.. > > So until that driver bug is addressed, I would advise disabling hardware RX checksums > for all chip versions, not only for version 02. > > It is not clear to me how the chip decides when to forward an rx URB to the host. > If you could describe how that part works for us, then it would help in further > understanding why fast systems (eg. a PC) don't generally notice the issue, > while much slower embedded systems do see the issue regularly. That last part is critical to understanding things: How does the chip decide that a URB is "full enough" before sending it to the host? Why does a really fast host see fewer packets jammed together into a single URB than a slower host? The answers will help understand if there are more bugs to be found/fixed, or if everything is explained by what has been observed thus far. To recap: the hardware sometimes fills a URB to the very end, and then continues the current packet at the first byte of the following URB. The r8152.c driver does NOT handle this situation; instead it always interprets the first 24 bytes of every URB as an "rx_desc" structure, without any kind of sanity/validation. This results in buffer overruns (it trusts the packet length field, even though the URB is too small to hold such a packet), and other semi-random behaviour. Using software rx checksums prevents Bad Things(tm) happening from most of this, but even that is not perfect given the severity of the bug. Cheers ^ permalink raw reply [flat|nested] 80+ messages in thread
[parent not found: <d683c019-4e0f-6fe6-368c-c4fc86c72fe6@pobox.com>]
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable [not found] ` <d683c019-4e0f-6fe6-368c-c4fc86c72fe6@pobox.com> @ 2016-11-18 7:57 ` Hayes Wang 2016-11-18 12:03 ` Mark Lord 0 siblings, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-18 7:57 UTC (permalink / raw) To: Mark Lord, netdev; +Cc: nic_swsd, linux-kernel, linux-usb Mark Lord [mailto:mlord@pobox.com] > Sent: Thursday, November 17, 2016 9:42 PM [...] > What the above sample shows, is the URB transfer buffer ran out of space in the > middle > of a packet, and the hardware then tried to just continue that same packet in the > next URB, > without an rx_desc header inserted. The r8152.c driver always assumes the URB > buffer begins > with an rx_desc, so of course this behaviour produces really weird effects, and > system crashes, etc.. The USB device wouldn't know the address and size of buffer. Only the USB host controller knows. Therefore, the device sends the data to host, and the host fills the memory. According to your description, it seems the host splits the data from the device into two different buffers (or URB transfers). I wonder if it would occur. As far as I know, the host wouldn't allow the buffer size less than the data length. Our hw engineers need the log from the USB analyzer to confirm what the device sends to the host. However, I don't think you have USB analyzer to do this. I would try to reproduce the issue. But, I am busy, so I don't think I would response quickly. Besides, the maximum data length which the RTL8152 would send to the host is 16KB. That is, if the agg_buf_sz is 16KB, the host wouldn't split it. However, you still see problems for it. [...] > It is not clear to me how the chip decides when to forward an rx URB to the host. > If you could describe how that part works for us, then it would help in further > understanding why fast systems (eg. a PC) don't generally notice the issue, > while much slower embedded systems do see the issue regularly. The driver expects the rx buffer would be rx_desc + a packet + padding to 8 alignment + rx_desc + a packet + padding to 8 alignment + ... Therefore, when a urb transfer is completed, the driver parsers the buffer by this way. After the buffer is handled, it would be submitted to the host, until the transfer is completed again. If the submitting fail, the driver would try again later. The urb->actual_length means how much data the host fills. The drive uses it to check the end of the data. The urb->status mean if the transfer is successful. The driver submits the urb to the host directly if the status is not successful. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-18 7:57 ` Hayes Wang @ 2016-11-18 12:03 ` Mark Lord 2016-11-22 13:12 ` Mark Lord 2016-11-23 3:52 ` Hayes Wang 0 siblings, 2 replies; 80+ messages in thread From: Mark Lord @ 2016-11-18 12:03 UTC (permalink / raw) To: Hayes Wang, netdev; +Cc: nic_swsd, linux-kernel, linux-usb On 16-11-18 02:57 AM, Hayes Wang wrote: .. > Besides, the maximum data length which the RTL8152 would send to > the host is 16KB. That is, if the agg_buf_sz is 16KB, the host > wouldn't split it. However, you still see problems for it. How does the RTL8152 know that the limit is 16KB, rather than some other number? Is this a hardwired number in the hardware, or is it a parameter that the software sends to the chip during initialization? I have a USB analyzer, but it is difficult to figure out how to program an appropriate trigger point for the capture, since the problem (with 16KB URBs) takes minutes to hours or even days to trigger. And the output from the analyzer is in some proprietary format. The in-kernel software analzer could be useful, but I have never figured out how to use it. :) Since my earlier email, I have figured out another piece of the puzzle with this dongle. The first issue is that a packet sometimes begins in one URB, and completes in the next URB, without an rx_desc at the start of the second URB. This I have already reported earlier. But the driver, as written, sometimes accesses bytes outside of the 16KB URB buffer, because it trusts the non-existent rx_desc in these cases, and also because it accesses bytes from the rx_desc without first checking whether there is sufficient remaining space in the URB to hold an rx_desc. These incorrect accesses sometimes touch memory outside of the URB buffer. Since the driver allocates all of its rx URB buffers at once, they are highly likely to be physically (and therefore virtually) adjacent in memory. So mistakenly accessing beyond the end of one buffer will often result in a read from memory of the next URB buffer. Which causes a portion of it to be loaded in the the D-cache. When that URB is subsequently filled by DMA, there then exists a data-consistency issue: the D-cache contains stale information from before the latest DMA cycle. So this explains the strange memory behaviour observed earlier on. When I add a call to invalidate_dcache_range() to the driver just before it begins examining a new rx URB, the problems go away. So this confirms the observations. Using non-cacheable RAM also makes the problem go away. But neither is a fix for the real buffer overrun accesses in the driver. Fix the "packet spans URBs" bug, and fix the driver to ALWAYS test lengths/ranges before accessing the actual buffer, and everything should begin working reliably. Cheers -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-18 12:03 ` Mark Lord @ 2016-11-22 13:12 ` Mark Lord 2016-11-23 3:52 ` Hayes Wang 1 sibling, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-22 13:12 UTC (permalink / raw) To: Hayes Wang, netdev; +Cc: nic_swsd, linux-kernel, linux-usb On 16-11-18 07:03 AM, Mark Lord wrote: > On 16-11-18 02:57 AM, Hayes Wang wrote: > .. >> Besides, the maximum data length which the RTL8152 would send to >> the host is 16KB. That is, if the agg_buf_sz is 16KB, the host >> wouldn't split it. However, you still see problems for it. > > How does the RTL8152 know that the limit is 16KB, > rather than some other number? Is this a hardwired number > in the hardware, or is it a parameter that the software > sends to the chip during initialization? .. > The first issue is that a packet sometimes begins in one URB, > and completes in the next URB, without an rx_desc at the start > of the second URB. This I have already reported earlier. Long run tests over the weekend, with the invalidate_dcache_range() call before the inner loop of r8152_rx_bottom(), turned up a few instances where packets were truncated inside a 16384 byte URB buffer, without filling the URB. [10.293228] r8152_rx_bottom: 4278 corrupted urb: head=9d210000 urb_offset=2856/3376 pkt_len(1518) exceeds remainder(496) [10.304523] r8152_dump_rx_desc: 044805ee 40080000 006005dc 06020000 00000000 00000000 rx_len=1518 .. [ 16.660431] r8152_rx_bottom: 7802 corrupted urb: head=9d1f8000 urb_offset=1544/2064 pkt_len(1518) exceeds remainder(496) [ 16.671719] r8152_dump_rx_desc: 044805ee 40480000 004005dc 46020006 00000000 00000000 rx_len=1518 The r8152.c driver attempted to build skb's for the entire packet size, even though the 1518-byte packets had only 496-bytes of data in the URB. It is not clear what the chip did with the rest of the packets in question, but the next URBs in each case began with a new/real rx_desc and new packet. There were also unconnected events during the test runs where the test code noticed totally invalid rx_desc structs in the middles of URBs. The stock driver would again have attempted to treat those as "valid" (ugh). .. [ 10.273906] r8152_check_rx_desc: rx_desc looks bad. [ 10.279012] r8152_rx_bottom: 4338 corrupted urb. head=9d210000 urb_offset=2856/3376 len_used=2880 [ 10.288196] r8152_dump_rx_desc: 312e3239 382e3836 0a20382e 3d435253 3034336d 202f3a30 rx_len=12857 .. [ 7.184565] r8152_check_rx_desc: rx_desc looks bad. [ 7.189657] r8152_rx_bottom: 1678 corrupted urb. head=9d210000 urb_offset=2856/3376 len_used=2880 [ 7.198852] r8152_dump_rx_desc: a1388402 803c9001 84380810 a67c5c4c a77c782b c64c782b rx_len=1026 .. [ 10.351251] r8152_check_rx_desc: rx_desc looks bad. [ 10.356356] r8152_rx_bottom: 4397 corrupted urb. head=9d20c000 urb_offset=4400/7984 len_used=4424 [ 10.365543] r8152_dump_rx_desc: 312e3239 382e3836 0a20382e 3d435253 3034336d 202f3a30 rx_len=12857 .. [ 10.518119] r8152_check_rx_desc: rx_desc looks bad. [ 10.523204] r8152_rx_bottom: 4458 corrupted urb. head=9d210000 urb_offset=4400/7984 len_used=4424 [ 10.532416] r8152_dump_rx_desc: 54544120 6e3d5352 636f6c6f 65762c6b 343d7372 6464612c rx_len=16672 .. > But the driver, as written, sometimes accesses bytes outside > of the 16KB URB buffer, because it trusts the non-existent > rx_desc in these cases, and also because it accesses bytes > from the rx_desc without first checking whether there is > sufficient remaining space in the URB to hold an rx_desc. > > These incorrect accesses sometimes touch memory outside > of the URB buffer. Since the driver allocates all of its > rx URB buffers at once, they are highly likely to be > physically (and therefore virtually) adjacent in memory. > > So mistakenly accessing beyond the end of one buffer will > often result in a read from memory of the next URB buffer. > Which causes a portion of it to be loaded in the the D-cache. > > When that URB is subsequently filled by DMA, there then exists > a data-consistency issue: the D-cache contains stale information > from before the latest DMA cycle. > > So this explains the strange memory behaviour observed earlier on. > When I add a call to invalidate_dcache_range() to the driver > just before it begins examining a new rx URB, the problems go away. > So this confirms the observations. > > Using non-cacheable RAM also makes the problem go away. > But neither is a fix for the real buffer overrun accesses in the driver. > > Fix the "packet spans URBs" bug, and fix the driver to ALWAYS > test lengths/ranges before accessing the actual buffer, > and everything should begin working reliably. ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-18 12:03 ` Mark Lord 2016-11-22 13:12 ` Mark Lord @ 2016-11-23 3:52 ` Hayes Wang 2016-11-23 13:41 ` Mark Lord 1 sibling, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-23 3:52 UTC (permalink / raw) To: Mark Lord, netdev; +Cc: nic_swsd, linux-kernel, linux-usb Mark Lord [mailto:mlord@pobox.com] > Sent: Friday, November 18, 2016 8:03 PM [..] > How does the RTL8152 know that the limit is 16KB, > rather than some other number? Is this a hardwired number > in the hardware, or is it a parameter that the software > sends to the chip during initialization? It is the limitation of the hardware. > I have a USB analyzer, but it is difficult to figure out how > to program an appropriate trigger point for the capture, > since the problem (with 16KB URBs) takes minutes to hours > or even days to trigger. It is good. Our hw engineers real want it. Maybe you could send a specific packet, and trigger it. You could allocate a skb and fill the data which you prefer, and call skb_queue_tail(&tp->tx_queue, skb); [...] > The first issue is that a packet sometimes begins in one URB, > and completes in the next URB, without an rx_desc at the start > of the second URB. This I have already reported earlier. However, our hw engineer says it wouldn't happen. Our hw always sends rx_desc + packet + padding. The hw wouldn't split it to two or more transmission. That is why I wonder who does it. > But the driver, as written, sometimes accesses bytes outside > of the 16KB URB buffer, because it trusts the non-existent > rx_desc in these cases, and also because it accesses bytes > from the rx_desc without first checking whether there is > sufficient remaining space in the URB to hold an rx_desc. I think I check them. According to the followning code, list_for_each_safe(cursor, next, &rx_queue) { struct rx_desc *rx_desc; struct rx_agg *agg; int len_used = 0; struct urb *urb; u8 *rx_data; ... rx_desc = agg->head; rx_data = agg->head; len_used += sizeof(struct rx_desc); //<-- add the size of next rx_desc while (urb->actual_length > len_used) { struct net_device *netdev = tp->netdev; struct net_device_stats *stats = &netdev->stats; unsigned int pkt_len; struct sk_buff *skb; pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; if (pkt_len < ETH_ZLEN) break; len_used += pkt_len; if (urb->actual_length < len_used) break; pkt_len -= CRC_SIZE; rx_data += sizeof(struct rx_desc); ... find_next_rx: rx_data = rx_agg_align(rx_data + pkt_len + CRC_SIZE); rx_desc = (struct rx_desc *)rx_data; len_used = (int)(rx_data - (u8 *)agg->head); len_used += sizeof(struct rx_desc); //<-- add the size of next rx_desc } submit: ... } The while loop would check if the next rx_desc is inside the urb buffer, because the len_used includes the size of the next rx_desc. Then, in the while loop, the len_used adds the packet size and check with urb->actual_length again. These make sure the rx_desc and the packet are inside the urb buffer. Except the urb->actual_length is more than agg_buf_sz. However, I don't think it would happen. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-23 3:52 ` Hayes Wang @ 2016-11-23 13:41 ` Mark Lord 2016-11-23 15:12 ` Hayes Wang 2016-11-24 12:37 ` Hayes Wang 0 siblings, 2 replies; 80+ messages in thread From: Mark Lord @ 2016-11-23 13:41 UTC (permalink / raw) To: Hayes Wang, netdev; +Cc: nic_swsd, linux-kernel, linux-usb What does this code do: >static void r8153_set_rx_early_size(struct r8152 *tp) >{ > u32 mtu = tp->netdev->mtu; > u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 4; > > ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data); >} How is ocp_data used by the hardware? Shouldn't the calculation also include sizeof(rx_desc) in there somewhere? Thanks -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-23 13:41 ` Mark Lord @ 2016-11-23 15:12 ` Hayes Wang 2016-11-23 19:29 ` Mark Lord 2016-11-24 12:37 ` Hayes Wang 1 sibling, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-23 15:12 UTC (permalink / raw) To: Mark Lord, netdev; +Cc: nic_swsd, linux-kernel, linux-usb Mark Lord [mlord@pobox.com] [...] > What does this code do: > >static void r8153_set_rx_early_size(struct r8152 *tp) > >{ > > u32 mtu = tp->netdev->mtu; > > u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 4; > > > > ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data); > >} This only works for RTL8153. However, what you use is RTL8152. It is like delay completion. It is used to reduce the loading of CPU by letting a transfer contain more data to reduce the number of transfers. > How is ocp_data used by the hardware? > Shouldn't the calculation also include sizeof(rx_desc) in there somewhere? The algorithm is from our hw engineers, and it should be (agg_buf_sz - packet size) / 8 You could refer to commit a59e6d815226 ("r8152: correct the rx early size"). ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-23 15:12 ` Hayes Wang @ 2016-11-23 19:29 ` Mark Lord 2016-11-24 3:24 ` Hayes Wang 2016-11-24 12:31 ` Mark Lord 0 siblings, 2 replies; 80+ messages in thread From: Mark Lord @ 2016-11-23 19:29 UTC (permalink / raw) To: Hayes Wang, netdev; +Cc: nic_swsd, linux-kernel, linux-usb On 16-11-23 10:12 AM, Hayes Wang wrote: > Mark Lord [mlord@pobox.com] > [...] >> What does this code do: > >>> static void r8153_set_rx_early_size(struct r8152 *tp) >>> { >>> u32 mtu = tp->netdev->mtu; >>> u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 4; >>> >>> ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data); >>> } > > This only works for RTL8153. However, what you use is RTL8152. > It is like delay completion. It is used to reduce the loading of CPU > by letting a transfer contain more data to reduce the number of > transfers. > >> How is ocp_data used by the hardware? >> Shouldn't the calculation also include sizeof(rx_desc) in there somewhere? > > The algorithm is from our hw engineers, and it should be > > (agg_buf_sz - packet size) / 8 > > You could refer to commit a59e6d815226 ("r8152: correct the rx early size"). Thanks. Right now I am working quite hard trying to narrow things down exactly. You are correct that the driver does appear to be careful about accesses beyond the filled portion of a URB buffer -- for some reason I thought the original driver had issues there, but looking again it does not seem to. One idea that is now looking more likely: Things could be suffering from speculative CPU accesses to RAM (the system here has non-coherent d-cache/RAM). This could incorrectly pre-load data from adjacent URB buffers into the d-cache, creating coherency issues. I am testing now with cacheline-sized guard zones between the buffers to see if that is the issue or not. Worth repeating: other dongles we have tried, eg. those using the asix driver, do not cause us any troubles here. Only the r8152 dongles do. The other drivers do not use hardware checksums, so even if they did incur similar bad packets, whatever the reason, those bad packets would be detected/rejected by the Linux network stack (software checksums). So everything appears to behave fine with them, as it does with the r8152 driver when hardware checksums are disabled. Still trying to understand exactly how these errors are happening. It takes a very long time to do a conclusive test of anything here, and I only have the hardware for a day or two a week. So my apologies if I am slow in getting back to you on stuff. Cheers ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-23 19:29 ` Mark Lord @ 2016-11-24 3:24 ` Hayes Wang 2016-11-24 12:31 ` Mark Lord 1 sibling, 0 replies; 80+ messages in thread From: Hayes Wang @ 2016-11-24 3:24 UTC (permalink / raw) To: Mark Lord, netdev; +Cc: nic_swsd, linux-kernel, linux-usb Mark Lord [mailto:mlord@pobox.com] > Sent: Thursday, November 24, 2016 3:30 AM [...] > Worth repeating: other dongles we have tried, eg. those using the asix driver, > do not cause us any troubles here. Only the r8152 dongles do. I couldn't tell you why you would see the problem. I have tested the RTL8152 on raspberry pi platform with iperf more than 17 hours. And I don't see any invalid rx descriptor. I don't think it really is the issue about our hw. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-23 19:29 ` Mark Lord 2016-11-24 3:24 ` Hayes Wang @ 2016-11-24 12:31 ` Mark Lord 2016-11-24 13:26 ` Hayes Wang 2016-11-24 16:19 ` David Miller 1 sibling, 2 replies; 80+ messages in thread From: Mark Lord @ 2016-11-24 12:31 UTC (permalink / raw) To: Hayes Wang, netdev; +Cc: nic_swsd, linux-kernel, linux-usb On 16-11-23 02:29 PM, Mark Lord wrote: > On 16-11-23 10:12 AM, Hayes Wang wrote: >> Mark Lord [mlord@pobox.com] >> [...] >>> What does this code do: >> >>>> static void r8153_set_rx_early_size(struct r8152 *tp) >>>> { >>>> u32 mtu = tp->netdev->mtu; >>>> u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 4; >>>> >>>> ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data); >>>> } >> >> This only works for RTL8153. However, what you use is RTL8152. >> It is like delay completion. It is used to reduce the loading of CPU >> by letting a transfer contain more data to reduce the number of >> transfers. >> >>> How is ocp_data used by the hardware? >>> Shouldn't the calculation also include sizeof(rx_desc) in there somewhere? >> >> The algorithm is from our hw engineers, and it should be >> >> (agg_buf_sz - packet size) / 8 >> >> You could refer to commit a59e6d815226 ("r8152: correct the rx early size"). > > Thanks. > > Right now I am working quite hard trying to narrow things down exactly. > You are correct that the driver does appear to be careful about accesses > beyond the filled portion of a URB buffer -- for some reason I thought > the original driver had issues there, but looking again it does not seem to. > > One idea that is now looking more likely: > Things could be suffering from speculative CPU accesses to RAM > (the system here has non-coherent d-cache/RAM). > This could incorrectly pre-load data from adjacent URB buffers > into the d-cache, creating coherency issues. I am testing now > with cacheline-sized guard zones between the buffers to see if > that is the issue or not. Nope. Guard zones did not fix it, so it's probably not a prefetch issue. Oddly, adding a couple of memory barriers to specific places in the driver does help, A LOT. Still not 100%, but it did pass 1800 reboot tests over night with only three bad rx_desc's reported. That's a new record here for the driver using kmalloc'd buffers, and put reliability on par with using non-cacheable buffers. Any way we look at it though, the chip/driver are simply unreliable, and relying upon hardware checksums (which fail due to the driver looking at garbage rather than the checksum bits) leads to data corruption. Cheers ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 12:31 ` Mark Lord @ 2016-11-24 13:26 ` Hayes Wang 2016-11-24 15:24 ` Mark Lord 2016-11-24 16:21 ` David Miller 2016-11-24 16:19 ` David Miller 1 sibling, 2 replies; 80+ messages in thread From: Hayes Wang @ 2016-11-24 13:26 UTC (permalink / raw) To: Mark Lord, netdev; +Cc: nic_swsd, linux-kernel, linux-usb Mark Lord [mailto:mlord@pobox.com] > Sent: Thursday, November 24, 2016 8:31 PM [...] > Nope. Guard zones did not fix it, so it's probably not a prefetch issue. > Oddly, adding a couple of memory barriers to specific places in the driver > does help, A LOT. Still not 100%, but it did pass 1800 reboot tests over night > with only three bad rx_desc's reported. > > That's a new record here for the driver using kmalloc'd buffers, > and put reliability on par with using non-cacheable buffers. > > Any way we look at it though, the chip/driver are simply unreliable, > and relying upon hardware checksums (which fail due to the driver > looking at garbage rather than the checksum bits) leads to data corruption. I don't think the garbage results from our driver or device. If it is the issue about memory, I think the host driver ought to deal with it, because it handles the DMA. Besides, it doesn't seem to occur for all platforms. I have tested the iperf more than 26 hours, and it still works fine. I think I would get the same result on x86 or x86_64 platform. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 13:26 ` Hayes Wang @ 2016-11-24 15:24 ` Mark Lord 2016-11-25 6:11 ` Hayes Wang 2016-11-24 16:21 ` David Miller 1 sibling, 1 reply; 80+ messages in thread From: Mark Lord @ 2016-11-24 15:24 UTC (permalink / raw) To: Hayes Wang, netdev; +Cc: nic_swsd, linux-kernel, linux-usb [-- Attachment #1: Type: text/plain, Size: 1958 bytes --] On 16-11-24 08:26 AM, Hayes Wang wrote: .. > Besides, it doesn't seem to occur for all platforms. I have > tested the iperf more than 26 hours, and it still works fine. > I think I would get the same result on x86 or x86_64 platform. .. x86 has near fully-coherent memory, so it is the "easy" platform to get things working on. But Linux supports a very diverse number of platforms, with varying degrees of cache/memory coherency, and it can be tricky for things to work correctly on all of them. If you are testing with the driver as currently in 4.4.34, then you won't even notice when things are screwing up, because the driver just silently drops packets. Or it passes them on without noticing that they have bad data. Here (attached) is the instrumented driver I am using here now. I suggest you use it or something similar when testing, and not the stock driver. This one has also been converted to use non-cacheable RAM for the receive buffers -- something that is probably a Good Thing for it to do regardless of this investigation. It also never drops a packet without logging the event, so we can see just how often there's an issue. This version behaves almost perfectly here, but I am still experimenting to see what is actually necessary, and what is not. In particular, there are some mb() calls I had put in there that shouldn't be required, so I have yet to try removing them again and see what changes. It takes at least an overnight run to pop up one or two errors, so do expect to hear back again until after the weekend at this point. Also, unrelated, but inside r8152_submit_rx() there is this code: /* The rx would be stopped, so skip submitting */ if (test_bit(RTL8152_UNPLUG, &tp->flags) || !test_bit(WORK_ENABLE, &tp->flags) || !netif_carrier_ok(tp->netdev)) return 0; If that "return 0" statement is ever executed, doesn't it result in the loss/leak of a buffer? Thanks [-- Attachment #2: r8152.c --] [-- Type: text/x-csrc, Size: 102577 bytes --] /* * Copyright (c) 2014 Realtek Semiconductor Corp. All rights reserved. * * 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. * */ #include <linux/signal.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/usb.h> #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> #include <net/ip6_checksum.h> #include <uapi/linux/mdio.h> #include <linux/mdio.h> #include <linux/usb/cdc.h> /* Information for net-next */ #define NETNEXT_VERSION "08" /* Information for net */ #define NET_VERSION "2" #define DRIVER_VERSION "v1." NETNEXT_VERSION "." NET_VERSION #define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>" #define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters" #define MODULENAME "r8152" #define R8152_PHY_ID 32 #define PLA_IDR 0xc000 #define PLA_RCR 0xc010 #define PLA_RMS 0xc016 #define PLA_RXFIFO_CTRL0 0xc0a0 #define PLA_RXFIFO_CTRL1 0xc0a4 #define PLA_RXFIFO_CTRL2 0xc0a8 #define PLA_DMY_REG0 0xc0b0 #define PLA_FMC 0xc0b4 #define PLA_CFG_WOL 0xc0b6 #define PLA_TEREDO_CFG 0xc0bc #define PLA_MAR 0xcd00 #define PLA_BACKUP 0xd000 #define PAL_BDC_CR 0xd1a0 #define PLA_TEREDO_TIMER 0xd2cc #define PLA_REALWOW_TIMER 0xd2e8 #define PLA_LEDSEL 0xdd90 #define PLA_LED_FEATURE 0xdd92 #define PLA_PHYAR 0xde00 #define PLA_BOOT_CTRL 0xe004 #define PLA_GPHY_INTR_IMR 0xe022 #define PLA_EEE_CR 0xe040 #define PLA_EEEP_CR 0xe080 #define PLA_MAC_PWR_CTRL 0xe0c0 #define PLA_MAC_PWR_CTRL2 0xe0ca #define PLA_MAC_PWR_CTRL3 0xe0cc #define PLA_MAC_PWR_CTRL4 0xe0ce #define PLA_WDT6_CTRL 0xe428 #define PLA_TCR0 0xe610 #define PLA_TCR1 0xe612 #define PLA_MTPS 0xe615 #define PLA_TXFIFO_CTRL 0xe618 #define PLA_RSTTALLY 0xe800 #define PLA_CR 0xe813 #define PLA_CRWECR 0xe81c #define PLA_CONFIG12 0xe81e /* CONFIG1, CONFIG2 */ #define PLA_CONFIG34 0xe820 /* CONFIG3, CONFIG4 */ #define PLA_CONFIG5 0xe822 #define PLA_PHY_PWR 0xe84c #define PLA_OOB_CTRL 0xe84f #define PLA_CPCR 0xe854 #define PLA_MISC_0 0xe858 #define PLA_MISC_1 0xe85a #define PLA_OCP_GPHY_BASE 0xe86c #define PLA_TALLYCNT 0xe890 #define PLA_SFF_STS_7 0xe8de #define PLA_PHYSTATUS 0xe908 #define PLA_BP_BA 0xfc26 #define PLA_BP_0 0xfc28 #define PLA_BP_1 0xfc2a #define PLA_BP_2 0xfc2c #define PLA_BP_3 0xfc2e #define PLA_BP_4 0xfc30 #define PLA_BP_5 0xfc32 #define PLA_BP_6 0xfc34 #define PLA_BP_7 0xfc36 #define PLA_BP_EN 0xfc38 #define USB_USB2PHY 0xb41e #define USB_SSPHYLINK2 0xb428 #define USB_U2P3_CTRL 0xb460 #define USB_CSR_DUMMY1 0xb464 #define USB_CSR_DUMMY2 0xb466 #define USB_DEV_STAT 0xb808 #define USB_CONNECT_TIMER 0xcbf8 #define USB_BURST_SIZE 0xcfc0 #define USB_USB_CTRL 0xd406 #define USB_PHY_CTRL 0xd408 #define USB_TX_AGG 0xd40a #define USB_RX_BUF_TH 0xd40c #define USB_USB_TIMER 0xd428 #define USB_RX_EARLY_TIMEOUT 0xd42c #define USB_RX_EARLY_SIZE 0xd42e #define USB_PM_CTRL_STATUS 0xd432 #define USB_TX_DMA 0xd434 #define USB_TOLERANCE 0xd490 #define USB_LPM_CTRL 0xd41a #define USB_UPS_CTRL 0xd800 #define USB_MISC_0 0xd81a #define USB_POWER_CUT 0xd80a #define USB_AFE_CTRL2 0xd824 #define USB_WDT11_CTRL 0xe43c #define USB_BP_BA 0xfc26 #define USB_BP_0 0xfc28 #define USB_BP_1 0xfc2a #define USB_BP_2 0xfc2c #define USB_BP_3 0xfc2e #define USB_BP_4 0xfc30 #define USB_BP_5 0xfc32 #define USB_BP_6 0xfc34 #define USB_BP_7 0xfc36 #define USB_BP_EN 0xfc38 /* OCP Registers */ #define OCP_ALDPS_CONFIG 0x2010 #define OCP_EEE_CONFIG1 0x2080 #define OCP_EEE_CONFIG2 0x2092 #define OCP_EEE_CONFIG3 0x2094 #define OCP_BASE_MII 0xa400 #define OCP_EEE_AR 0xa41a #define OCP_EEE_DATA 0xa41c #define OCP_PHY_STATUS 0xa420 #define OCP_POWER_CFG 0xa430 #define OCP_EEE_CFG 0xa432 #define OCP_SRAM_ADDR 0xa436 #define OCP_SRAM_DATA 0xa438 #define OCP_DOWN_SPEED 0xa442 #define OCP_EEE_ABLE 0xa5c4 #define OCP_EEE_ADV 0xa5d0 #define OCP_EEE_LPABLE 0xa5d2 #define OCP_PHY_STATE 0xa708 /* nway state for 8153 */ #define OCP_ADC_CFG 0xbc06 /* SRAM Register */ #define SRAM_LPF_CFG 0x8012 #define SRAM_10M_AMP1 0x8080 #define SRAM_10M_AMP2 0x8082 #define SRAM_IMPEDANCE 0x8084 /* PLA_RCR */ #define RCR_AAP 0x00000001 #define RCR_APM 0x00000002 #define RCR_AM 0x00000004 #define RCR_AB 0x00000008 #define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB) /* PLA_RXFIFO_CTRL0 */ #define RXFIFO_THR1_NORMAL 0x00080002 #define RXFIFO_THR1_OOB 0x01800003 /* PLA_RXFIFO_CTRL1 */ #define RXFIFO_THR2_FULL 0x00000060 #define RXFIFO_THR2_HIGH 0x00000038 #define RXFIFO_THR2_OOB 0x0000004a #define RXFIFO_THR2_NORMAL 0x00a0 /* PLA_RXFIFO_CTRL2 */ #define RXFIFO_THR3_FULL 0x00000078 #define RXFIFO_THR3_HIGH 0x00000048 #define RXFIFO_THR3_OOB 0x0000005a #define RXFIFO_THR3_NORMAL 0x0110 /* PLA_TXFIFO_CTRL */ #define TXFIFO_THR_NORMAL 0x00400008 #define TXFIFO_THR_NORMAL2 0x01000008 /* PLA_DMY_REG0 */ #define ECM_ALDPS 0x0002 /* PLA_FMC */ #define FMC_FCR_MCU_EN 0x0001 /* PLA_EEEP_CR */ #define EEEP_CR_EEEP_TX 0x0002 /* PLA_WDT6_CTRL */ #define WDT6_SET_MODE 0x0010 /* PLA_TCR0 */ #define TCR0_TX_EMPTY 0x0800 #define TCR0_AUTO_FIFO 0x0080 /* PLA_TCR1 */ #define VERSION_MASK 0x7cf0 /* PLA_MTPS */ #define MTPS_JUMBO (12 * 1024 / 64) #define MTPS_DEFAULT (6 * 1024 / 64) /* PLA_RSTTALLY */ #define TALLY_RESET 0x0001 /* PLA_CR */ #define CR_RST 0x10 #define CR_RE 0x08 #define CR_TE 0x04 /* PLA_CRWECR */ #define CRWECR_NORAML 0x00 #define CRWECR_CONFIG 0xc0 /* PLA_OOB_CTRL */ #define NOW_IS_OOB 0x80 #define TXFIFO_EMPTY 0x20 #define RXFIFO_EMPTY 0x10 #define LINK_LIST_READY 0x02 #define DIS_MCU_CLROOB 0x01 #define FIFO_EMPTY (TXFIFO_EMPTY | RXFIFO_EMPTY) /* PLA_MISC_1 */ #define RXDY_GATED_EN 0x0008 /* PLA_SFF_STS_7 */ #define RE_INIT_LL 0x8000 #define MCU_BORW_EN 0x4000 /* PLA_CPCR */ #define CPCR_RX_VLAN 0x0040 /* PLA_CFG_WOL */ #define MAGIC_EN 0x0001 /* PLA_TEREDO_CFG */ #define TEREDO_SEL 0x8000 #define TEREDO_WAKE_MASK 0x7f00 #define TEREDO_RS_EVENT_MASK 0x00fe #define OOB_TEREDO_EN 0x0001 /* PAL_BDC_CR */ #define ALDPS_PROXY_MODE 0x0001 /* PLA_CONFIG34 */ #define LINK_ON_WAKE_EN 0x0010 #define LINK_OFF_WAKE_EN 0x0008 /* PLA_CONFIG5 */ #define BWF_EN 0x0040 #define MWF_EN 0x0020 #define UWF_EN 0x0010 #define LAN_WAKE_EN 0x0002 /* PLA_LED_FEATURE */ #define LED_MODE_MASK 0x0700 /* PLA_PHY_PWR */ #define TX_10M_IDLE_EN 0x0080 #define PFM_PWM_SWITCH 0x0040 /* PLA_MAC_PWR_CTRL */ #define D3_CLK_GATED_EN 0x00004000 #define MCU_CLK_RATIO 0x07010f07 #define MCU_CLK_RATIO_MASK 0x0f0f0f0f #define ALDPS_SPDWN_RATIO 0x0f87 /* PLA_MAC_PWR_CTRL2 */ #define EEE_SPDWN_RATIO 0x8007 /* PLA_MAC_PWR_CTRL3 */ #define PKT_AVAIL_SPDWN_EN 0x0100 #define SUSPEND_SPDWN_EN 0x0004 #define U1U2_SPDWN_EN 0x0002 #define L1_SPDWN_EN 0x0001 /* PLA_MAC_PWR_CTRL4 */ #define PWRSAVE_SPDWN_EN 0x1000 #define RXDV_SPDWN_EN 0x0800 #define TX10MIDLE_EN 0x0100 #define TP100_SPDWN_EN 0x0020 #define TP500_SPDWN_EN 0x0010 #define TP1000_SPDWN_EN 0x0008 #define EEE_SPDWN_EN 0x0001 /* PLA_GPHY_INTR_IMR */ #define GPHY_STS_MSK 0x0001 #define SPEED_DOWN_MSK 0x0002 #define SPDWN_RXDV_MSK 0x0004 #define SPDWN_LINKCHG_MSK 0x0008 /* PLA_PHYAR */ #define PHYAR_FLAG 0x80000000 /* PLA_EEE_CR */ #define EEE_RX_EN 0x0001 #define EEE_TX_EN 0x0002 /* PLA_BOOT_CTRL */ #define AUTOLOAD_DONE 0x0002 /* USB_USB2PHY */ #define USB2PHY_SUSPEND 0x0001 #define USB2PHY_L1 0x0002 /* USB_SSPHYLINK2 */ #define pwd_dn_scale_mask 0x3ffe #define pwd_dn_scale(x) ((x) << 1) /* USB_CSR_DUMMY1 */ #define DYNAMIC_BURST 0x0001 /* USB_CSR_DUMMY2 */ #define EP4_FULL_FC 0x0001 /* USB_DEV_STAT */ #define STAT_SPEED_MASK 0x0006 #define STAT_SPEED_HIGH 0x0000 #define STAT_SPEED_FULL 0x0002 /* USB_TX_AGG */ #define TX_AGG_MAX_THRESHOLD 0x03 /* USB_RX_BUF_TH */ #define RX_THR_SUPPER 0x0c350180 #define RX_THR_HIGH 0x7a120180 #define RX_THR_SLOW 0xffff0180 /* USB_TX_DMA */ #define TEST_MODE_DISABLE 0x00000001 #define TX_SIZE_ADJUST1 0x00000100 /* USB_UPS_CTRL */ #define POWER_CUT 0x0100 /* USB_PM_CTRL_STATUS */ #define RESUME_INDICATE 0x0001 /* USB_USB_CTRL */ #define RX_AGG_DISABLE 0x0010 #define RX_ZERO_EN 0x0080 /* USB_U2P3_CTRL */ #define U2P3_ENABLE 0x0001 /* USB_POWER_CUT */ #define PWR_EN 0x0001 #define PHASE2_EN 0x0008 /* USB_MISC_0 */ #define PCUT_STATUS 0x0001 /* USB_RX_EARLY_TIMEOUT */ #define COALESCE_SUPER 85000U #define COALESCE_HIGH 250000U #define COALESCE_SLOW 524280U /* USB_WDT11_CTRL */ #define TIMER11_EN 0x0001 /* USB_LPM_CTRL */ /* bit 4 ~ 5: fifo empty boundary */ #define FIFO_EMPTY_1FB 0x30 /* 0x1fb * 64 = 32448 bytes */ /* bit 2 ~ 3: LMP timer */ #define LPM_TIMER_MASK 0x0c #define LPM_TIMER_500MS 0x04 /* 500 ms */ #define LPM_TIMER_500US 0x0c /* 500 us */ #define ROK_EXIT_LPM 0x02 /* USB_AFE_CTRL2 */ #define SEN_VAL_MASK 0xf800 #define SEN_VAL_NORMAL 0xa000 #define SEL_RXIDLE 0x0100 /* OCP_ALDPS_CONFIG */ #define ENPWRSAVE 0x8000 #define ENPDNPS 0x0200 #define LINKENA 0x0100 #define DIS_SDSAVE 0x0010 /* OCP_PHY_STATUS */ #define PHY_STAT_MASK 0x0007 #define PHY_STAT_LAN_ON 3 #define PHY_STAT_PWRDN 5 /* OCP_POWER_CFG */ #define EEE_CLKDIV_EN 0x8000 #define EN_ALDPS 0x0004 #define EN_10M_PLLOFF 0x0001 /* OCP_EEE_CONFIG1 */ #define RG_TXLPI_MSK_HFDUP 0x8000 #define RG_MATCLR_EN 0x4000 #define EEE_10_CAP 0x2000 #define EEE_NWAY_EN 0x1000 #define TX_QUIET_EN 0x0200 #define RX_QUIET_EN 0x0100 #define sd_rise_time_mask 0x0070 #define sd_rise_time(x) (min(x, 7) << 4) /* bit 4 ~ 6 */ #define RG_RXLPI_MSK_HFDUP 0x0008 #define SDFALLTIME 0x0007 /* bit 0 ~ 2 */ /* OCP_EEE_CONFIG2 */ #define RG_LPIHYS_NUM 0x7000 /* bit 12 ~ 15 */ #define RG_DACQUIET_EN 0x0400 #define RG_LDVQUIET_EN 0x0200 #define RG_CKRSEL 0x0020 #define RG_EEEPRG_EN 0x0010 /* OCP_EEE_CONFIG3 */ #define fast_snr_mask 0xff80 #define fast_snr(x) (min(x, 0x1ff) << 7) /* bit 7 ~ 15 */ #define RG_LFS_SEL 0x0060 /* bit 6 ~ 5 */ #define MSK_PH 0x0006 /* bit 0 ~ 3 */ /* OCP_EEE_AR */ /* bit[15:14] function */ #define FUN_ADDR 0x0000 #define FUN_DATA 0x4000 /* bit[4:0] device addr */ /* OCP_EEE_CFG */ #define CTAP_SHORT_EN 0x0040 #define EEE10_EN 0x0010 /* OCP_DOWN_SPEED */ #define EN_10M_BGOFF 0x0080 /* OCP_PHY_STATE */ #define TXDIS_STATE 0x01 #define ABD_STATE 0x02 /* OCP_ADC_CFG */ #define CKADSEL_L 0x0100 #define ADC_EN 0x0080 #define EN_EMI_L 0x0040 /* SRAM_LPF_CFG */ #define LPF_AUTO_TUNE 0x8000 /* SRAM_10M_AMP1 */ #define GDAC_IB_UPALL 0x0008 /* SRAM_10M_AMP2 */ #define AMP_DN 0x0200 /* SRAM_IMPEDANCE */ #define RX_DRIVING_MASK 0x6000 enum rtl_register_content { _1000bps = 0x10, _100bps = 0x08, _10bps = 0x04, LINK_STATUS = 0x02, FULL_DUP = 0x01, }; #define RTL8152_MAX_TX 4 #define RTL8152_MAX_RX 10 #define INTBUFSIZE 2 #define CRC_SIZE 4 #define TX_ALIGN 4 #define RX_ALIGN 8 #define INTR_LINK 0x0004 #define RTL8152_REQT_READ 0xc0 #define RTL8152_REQT_WRITE 0x40 #define RTL8152_REQ_GET_REGS 0x05 #define RTL8152_REQ_SET_REGS 0x05 #define BYTE_EN_DWORD 0xff #define BYTE_EN_WORD 0x33 #define BYTE_EN_BYTE 0x11 #define BYTE_EN_SIX_BYTES 0x3f #define BYTE_EN_START_MASK 0x0f #define BYTE_EN_END_MASK 0xf0 #define RTL8153_MAX_PACKET 9216 /* 9K */ #define RTL8153_MAX_MTU (RTL8153_MAX_PACKET - VLAN_ETH_HLEN - VLAN_HLEN) #define RTL8152_RMS (VLAN_ETH_FRAME_LEN + VLAN_HLEN) #define RTL8153_RMS RTL8153_MAX_PACKET #define RTL8152_TX_TIMEOUT (5 * HZ) #define RTL8152_NAPI_WEIGHT 64 /* rtl8152 flags */ enum rtl8152_flags { RTL8152_UNPLUG = 0, RTL8152_SET_RX_MODE, WORK_ENABLE, RTL8152_LINK_CHG, SELECTIVE_SUSPEND, PHY_RESET, SCHEDULE_NAPI, }; /* Define these values to match your device */ #define VENDOR_ID_REALTEK 0x0bda #define VENDOR_ID_SAMSUNG 0x04e8 #define VENDOR_ID_LENOVO 0x17ef #define VENDOR_ID_NVIDIA 0x0955 #define MCU_TYPE_PLA 0x0100 #define MCU_TYPE_USB 0x0000 struct tally_counter { __le64 tx_packets; __le64 rx_packets; __le64 tx_errors; __le32 rx_errors; __le16 rx_missed; __le16 align_errors; __le32 tx_one_collision; __le32 tx_multi_collision; __le64 rx_unicast; __le64 rx_broadcast; __le32 rx_multicast; __le16 tx_aborted; __le16 tx_underrun; }; struct rx_desc { __le32 opts1; #define RX_LEN_MASK 0x7fff __le32 opts2; #define RD_UDP_CS BIT(23) #define RD_TCP_CS BIT(22) #define RD_IPV6_CS BIT(20) #define RD_IPV4_CS BIT(19) __le32 opts3; #define IPF BIT(23) /* IP checksum fail */ #define UDPF BIT(22) /* UDP checksum fail */ #define TCPF BIT(21) /* TCP checksum fail */ #define RX_VLAN_TAG BIT(16) __le32 opts4; __le32 opts5; __le32 opts6; }; struct tx_desc { __le32 opts1; #define TX_FS BIT(31) /* First segment of a packet */ #define TX_LS BIT(30) /* Final segment of a packet */ #define GTSENDV4 BIT(28) #define GTSENDV6 BIT(27) #define GTTCPHO_SHIFT 18 #define GTTCPHO_MAX 0x7fU #define TX_LEN_MAX 0x3ffffU __le32 opts2; #define UDP_CS BIT(31) /* Calculate UDP/IP checksum */ #define TCP_CS BIT(30) /* Calculate TCP/IP checksum */ #define IPV4_CS BIT(29) /* Calculate IPv4 checksum */ #define IPV6_CS BIT(28) /* Calculate IPv6 checksum */ #define MSS_SHIFT 17 #define MSS_MAX 0x7ffU #define TCPHO_SHIFT 17 #define TCPHO_MAX 0x7ffU #define TX_VLAN_TAG BIT(16) }; struct r8152; struct rx_agg { struct list_head list; struct urb *urb; struct r8152 *context; dma_addr_t transfer_dma; 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 napi_struct napi; struct usb_interface *intf; struct net_device *netdev; 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, rx_queue; spinlock_t rx_lock, tx_lock; struct delayed_work schedule; struct mii_if_info mii; struct mutex control; /* use for hw setting */ struct rtl_ops { void (*init)(struct r8152 *); int (*enable)(struct r8152 *); void (*disable)(struct r8152 *); void (*up)(struct r8152 *); void (*down)(struct r8152 *); void (*unload)(struct r8152 *); int (*eee_get)(struct r8152 *, struct ethtool_eee *); int (*eee_set)(struct r8152 *, struct ethtool_eee *); bool (*in_nway)(struct r8152 *); } rtl_ops; int intr_interval; u32 saved_wolopts; u32 msg_enable; u32 tx_qlen; u32 coalesce; u16 ocp_base; u8 *intr_buff; u8 version; }; enum rtl_version { RTL_VER_UNKNOWN = 0, RTL_VER_01, RTL_VER_02, RTL_VER_03, RTL_VER_04, RTL_VER_05, RTL_VER_06, RTL_VER_MAX }; enum tx_csum_stat { TX_CSUM_SUCCESS = 0, TX_CSUM_TSO, TX_CSUM_NONE }; /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). * The RTL chips use a 64 element hash table based on the Ethernet CRC. */ static const int multicast_filter_limit = 32; static unsigned int agg_buf_sz = 16384; #define RTL_LIMITED_TSO_SIZE (agg_buf_sz - sizeof(struct tx_desc) - \ VLAN_ETH_HLEN - VLAN_HLEN) static int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) { 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, 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) { int ret; void *tmp; tmp = kmemdup(data, size, GFP_KERNEL); if (!tmp) return -ENOMEM; ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0), RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, value, index, tmp, size, 500); kfree(tmp); return ret; } static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data, u16 type) { u16 limit = 64; int ret = 0; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; /* both size and indix must be 4 bytes align */ if ((size & 3) || !size || (index & 3) || !data) return -EPERM; if ((u32)index + (u32)size > 0xffff) return -EPERM; while (size) { if (size > limit) { ret = get_registers(tp, index, type, limit, data); if (ret < 0) break; index += limit; data += limit; size -= limit; } else { ret = get_registers(tp, index, type, size, data); if (ret < 0) break; index += size; data += size; size = 0; break; } } if (ret == -ENODEV) set_bit(RTL8152_UNPLUG, &tp->flags); return ret; } static int generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data, u16 type) { int ret; u16 byteen_start, byteen_end, byen; u16 limit = 512; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; /* both size and indix must be 4 bytes align */ if ((size & 3) || !size || (index & 3) || !data) return -EPERM; if ((u32)index + (u32)size > 0xffff) return -EPERM; byteen_start = byteen & BYTE_EN_START_MASK; byteen_end = byteen & BYTE_EN_END_MASK; byen = byteen_start | (byteen_start << 4); ret = set_registers(tp, index, type | byen, 4, data); if (ret < 0) goto error1; index += 4; data += 4; size -= 4; if (size) { size -= 4; while (size) { if (size > limit) { ret = set_registers(tp, index, type | BYTE_EN_DWORD, limit, data); if (ret < 0) goto error1; index += limit; data += limit; size -= limit; } else { ret = set_registers(tp, index, type | BYTE_EN_DWORD, size, data); if (ret < 0) goto error1; index += size; data += size; size = 0; break; } } byen = byteen_end | (byteen_end >> 4); ret = set_registers(tp, index, type | byen, 4, data); if (ret < 0) goto error1; } error1: if (ret == -ENODEV) set_bit(RTL8152_UNPLUG, &tp->flags); return ret; } static inline int pla_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) { return generic_ocp_read(tp, index, size, data, MCU_TYPE_PLA); } static inline int pla_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) { return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_PLA); } static inline int usb_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) { return generic_ocp_read(tp, index, size, data, MCU_TYPE_USB); } static inline int usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) { return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_USB); } static u32 ocp_read_dword(struct r8152 *tp, u16 type, u16 index) { __le32 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) { __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; generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); data = __le32_to_cpu(tmp); data >>= (shift * 8); data &= 0xffff; return (u16)data; } static void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data) { u32 mask = 0xffff; __le32 tmp; u16 byen = BYTE_EN_WORD; u8 shift = index & 2; data &= mask; if (index & 2) { byen <<= shift; mask <<= (shift * 8); data <<= (shift * 8); index &= ~3; } tmp = __cpu_to_le32(data); 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; generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); data = __le32_to_cpu(tmp); data >>= (shift * 8); data &= 0xff; return (u8)data; } static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data) { u32 mask = 0xff; __le32 tmp; u16 byen = BYTE_EN_BYTE; u8 shift = index & 3; data &= mask; if (index & 3) { byen <<= shift; mask <<= (shift * 8); data <<= (shift * 8); index &= ~3; } tmp = __cpu_to_le32(data); generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); } static u16 ocp_reg_read(struct r8152 *tp, u16 addr) { u16 ocp_base, ocp_index; ocp_base = addr & 0xf000; if (ocp_base != tp->ocp_base) { ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); tp->ocp_base = ocp_base; } ocp_index = (addr & 0x0fff) | 0xb000; return ocp_read_word(tp, MCU_TYPE_PLA, ocp_index); } static void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data) { u16 ocp_base, ocp_index; ocp_base = addr & 0xf000; if (ocp_base != tp->ocp_base) { ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); tp->ocp_base = ocp_base; } ocp_index = (addr & 0x0fff) | 0xb000; ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data); } static inline void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value) { ocp_reg_write(tp, OCP_BASE_MII + reg_addr * 2, value); } static inline int r8152_mdio_read(struct r8152 *tp, u32 reg_addr) { return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2); } static void sram_write(struct r8152 *tp, u16 addr, u16 data) { ocp_reg_write(tp, OCP_SRAM_ADDR, addr); ocp_reg_write(tp, OCP_SRAM_DATA, data); } static int read_mii_word(struct net_device *netdev, int phy_id, int reg) { struct r8152 *tp = netdev_priv(netdev); int ret; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; if (phy_id != R8152_PHY_ID) return -EINVAL; ret = r8152_mdio_read(tp, reg); return ret; } static void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val) { struct r8152 *tp = netdev_priv(netdev); if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; if (phy_id != R8152_PHY_ID) return; r8152_mdio_write(tp, reg, val); } static int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags); static int rtl8152_set_mac_address(struct net_device *netdev, void *p) { struct r8152 *tp = netdev_priv(netdev); struct sockaddr *addr = p; int ret = -EADDRNOTAVAIL; if (!is_valid_ether_addr(addr->sa_data)) goto out1; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out1; mutex_lock(&tp->control); memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, 8, addr->sa_data); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out1: return ret; } static int set_ethernet_addr(struct r8152 *tp) { struct net_device *dev = tp->netdev; struct sockaddr sa; int ret; if (tp->version == RTL_VER_01) ret = pla_ocp_read(tp, PLA_IDR, 8, sa.sa_data); else ret = pla_ocp_read(tp, PLA_BACKUP, 8, sa.sa_data); if (ret < 0) { netif_err(tp, probe, dev, "Get ether addr fail\n"); } else if (!is_valid_ether_addr(sa.sa_data)) { netif_err(tp, probe, dev, "Invalid ether addr %pM\n", sa.sa_data); eth_hw_addr_random(dev); ether_addr_copy(sa.sa_data, dev->dev_addr); ret = rtl8152_set_mac_address(dev, &sa); netif_info(tp, probe, dev, "Random ether addr %pM\n", sa.sa_data); } else { if (tp->version == RTL_VER_01) ether_addr_copy(dev->dev_addr, sa.sa_data); else ret = rtl8152_set_mac_address(dev, &sa); } return ret; } static void read_bulk_callback(struct urb *urb) { struct net_device *netdev; int status = urb->status; struct rx_agg *agg; struct r8152 *tp; 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; /* When link down, the driver would cancel all bulks. */ /* This avoid the re-submitting bulk */ if (!netif_carrier_ok(netdev)) return; usb_mark_last_busy(tp->udev); switch (status) { case 0: mb(); if (urb->actual_length < (sizeof(struct rx_desc) + ETH_ZLEN)) { printk(KERN_INFO "r8152_read_bulk_callback: actual_length (%u) too short\n", urb->actual_length); break; } spin_lock(&tp->rx_lock); list_add_tail(&agg->list, &tp->rx_done); spin_unlock(&tp->rx_lock); napi_schedule(&tp->napi); 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: if (net_ratelimit()) netdev_warn(netdev, "maybe reset is needed?\n"); break; default: if (net_ratelimit()) netdev_warn(netdev, "Rx status %d\n", status); break; } r8152_submit_rx(tp, agg, GFP_ATOMIC); } static void write_bulk_callback(struct urb *urb) { struct net_device_stats *stats; struct net_device *netdev; struct tx_agg *agg; struct r8152 *tp; int status = urb->status; agg = urb->context; if (!agg) return; tp = agg->context; if (!tp) return; netdev = tp->netdev; stats = &netdev->stats; if (status) { if (net_ratelimit()) netdev_warn(netdev, "Tx status %d\n", status); stats->tx_errors += agg->skb_num; } else { stats->tx_packets += agg->skb_num; stats->tx_bytes += agg->skb_len; } spin_lock(&tp->tx_lock); list_add_tail(&agg->list, &tp->tx_free); spin_unlock(&tp->tx_lock); usb_autopm_put_interface_async(tp->intf); if (!netif_carrier_ok(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)) napi_schedule(&tp->napi); } static void intr_callback(struct urb *urb) { struct r8152 *tp; __le16 *d; int status = urb->status; int res; tp = urb->context; if (!tp) return; if (!test_bit(WORK_ENABLE, &tp->flags)) return; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; switch (status) { case 0: /* success */ break; case -ECONNRESET: /* unlink */ case -ESHUTDOWN: netif_device_detach(tp->netdev); case -ENOENT: case -EPROTO: netif_info(tp, intr, tp->netdev, "Stop submitting intr, status %d\n", status); 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 (!netif_carrier_ok(tp->netdev)) { set_bit(RTL8152_LINK_CHG, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } } else { if (netif_carrier_ok(tp->netdev)) { set_bit(RTL8152_LINK_CHG, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } } resubmit: res = usb_submit_urb(urb, GFP_ATOMIC); if (res == -ENODEV) { set_bit(RTL8152_UNPLUG, &tp->flags); netif_device_detach(tp->netdev); } else if (res) { netif_err(tp, intr, tp->netdev, "can't resubmit intr, status %d\n", res); } } static inline void *rx_agg_align(void *data) { return (void *)ALIGN((uintptr_t)data, RX_ALIGN); } static inline void *tx_agg_align(void *data) { return (void *)ALIGN((uintptr_t)data, TX_ALIGN); } static void free_all_mem(struct r8152 *tp) { int i; for (i = 0; i < RTL8152_MAX_RX; i++) { usb_free_urb(tp->rx_info[i].urb); tp->rx_info[i].urb = NULL; usb_free_coherent(tp->udev, agg_buf_sz, tp->rx_info[i].buffer, tp->rx_info[i].transfer_dma); tp->rx_info[i].buffer = NULL; tp->rx_info[i].head = NULL; } for (i = 0; i < RTL8152_MAX_TX; i++) { usb_free_urb(tp->tx_info[i].urb); tp->tx_info[i].urb = NULL; kfree(tp->tx_info[i].buffer); tp->tx_info[i].buffer = NULL; tp->tx_info[i].head = NULL; } usb_free_urb(tp->intr_urb); tp->intr_urb = NULL; 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->tx_free); skb_queue_head_init(&tp->tx_queue); skb_queue_head_init(&tp->rx_queue); for (i = 0; i < RTL8152_MAX_RX; i++) { dma_addr_t transfer_dma = 0; buf = usb_alloc_coherent(tp->udev, agg_buf_sz, GFP_KERNEL, &transfer_dma); if (!buf) goto err1; if (buf != rx_agg_align(buf)) { kfree(buf); buf = kmalloc_node(agg_buf_sz + RX_ALIGN, 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].transfer_dma = transfer_dma; 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(agg_buf_sz, GFP_KERNEL, node); if (!buf) goto err1; if (buf != tx_agg_align(buf)) { kfree(buf); buf = kmalloc_node(agg_buf_sz + TX_ALIGN, 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; if (list_empty(&tp->tx_free)) return NULL; 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; } /* r8152_csum_workaround() * The hw limites the value the transport offset. When the offset is out of the * range, calculate the checksum by sw. */ static void r8152_csum_workaround(struct r8152 *tp, struct sk_buff *skb, struct sk_buff_head *list) { if (skb_shinfo(skb)->gso_size) { netdev_features_t features = tp->netdev->features; struct sk_buff_head seg_list; struct sk_buff *segs, *nskb; features &= ~(NETIF_F_SG | NETIF_F_IPV6_CSUM | NETIF_F_TSO6); segs = skb_gso_segment(skb, features); if (IS_ERR(segs) || !segs) goto drop; __skb_queue_head_init(&seg_list); do { nskb = segs; segs = segs->next; nskb->next = NULL; __skb_queue_tail(&seg_list, nskb); } while (segs); skb_queue_splice(&seg_list, list); dev_kfree_skb(skb); } else if (skb->ip_summed == CHECKSUM_PARTIAL) { if (skb_checksum_help(skb) < 0) goto drop; __skb_queue_head(list, skb); } else { struct net_device_stats *stats; drop: stats = &tp->netdev->stats; stats->tx_dropped++; dev_kfree_skb(skb); } } /* msdn_giant_send_check() * According to the document of microsoft, the TCP Pseudo Header excludes the * packet length for IPv6 TCP large packets. */ static int msdn_giant_send_check(struct sk_buff *skb) { const struct ipv6hdr *ipv6h; struct tcphdr *th; int ret; ret = skb_cow_head(skb, 0); if (ret) return ret; ipv6h = ipv6_hdr(skb); th = tcp_hdr(skb); th->check = 0; th->check = ~tcp_v6_check(0, &ipv6h->saddr, &ipv6h->daddr, 0); return ret; } static inline void rtl_tx_vlan_tag(struct tx_desc *desc, struct sk_buff *skb) { if (skb_vlan_tag_present(skb)) { u32 opts2; opts2 = TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb)); desc->opts2 |= cpu_to_le32(opts2); } } static inline void rtl_rx_vlan_tag(struct rx_desc *desc, struct sk_buff *skb) { u32 opts2 = le32_to_cpu(desc->opts2); if (opts2 & RX_VLAN_TAG) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff)); } static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, struct sk_buff *skb, u32 len, u32 transport_offset) { u32 mss = skb_shinfo(skb)->gso_size; u32 opts1, opts2 = 0; int ret = TX_CSUM_SUCCESS; WARN_ON_ONCE(len > TX_LEN_MAX); opts1 = len | TX_FS | TX_LS; if (mss) { if (transport_offset > GTTCPHO_MAX) { netif_warn(tp, tx_err, tp->netdev, "Invalid transport offset 0x%x for TSO\n", transport_offset); ret = TX_CSUM_TSO; goto unavailable; } switch (vlan_get_protocol(skb)) { case htons(ETH_P_IP): opts1 |= GTSENDV4; break; case htons(ETH_P_IPV6): if (msdn_giant_send_check(skb)) { ret = TX_CSUM_TSO; goto unavailable; } opts1 |= GTSENDV6; break; default: WARN_ON_ONCE(1); break; } opts1 |= transport_offset << GTTCPHO_SHIFT; opts2 |= min(mss, MSS_MAX) << MSS_SHIFT; } else if (skb->ip_summed == CHECKSUM_PARTIAL) { u8 ip_protocol; if (transport_offset > TCPHO_MAX) { netif_warn(tp, tx_err, tp->netdev, "Invalid transport offset 0x%x\n", transport_offset); ret = TX_CSUM_NONE; goto unavailable; } switch (vlan_get_protocol(skb)) { 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; else if (ip_protocol == IPPROTO_UDP) opts2 |= UDP_CS; else WARN_ON_ONCE(1); opts2 |= transport_offset << TCPHO_SHIFT; } desc->opts2 = cpu_to_le32(opts2); desc->opts1 = cpu_to_le32(opts1); unavailable: return ret; } static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg) { struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue; int remain, ret; u8 *tx_data; __skb_queue_head_init(&skb_head); spin_lock(&tx_queue->lock); skb_queue_splice_init(tx_queue, &skb_head); spin_unlock(&tx_queue->lock); tx_data = agg->head; agg->skb_num = 0; agg->skb_len = 0; remain = agg_buf_sz; while (remain >= ETH_ZLEN + sizeof(struct tx_desc)) { struct tx_desc *tx_desc; struct sk_buff *skb; unsigned int len; u32 offset; skb = __skb_dequeue(&skb_head); if (!skb) break; len = skb->len + sizeof(*tx_desc); if (len > remain) { __skb_queue_head(&skb_head, skb); break; } tx_data = tx_agg_align(tx_data); tx_desc = (struct tx_desc *)tx_data; offset = (u32)skb_transport_offset(skb); if (r8152_tx_csum(tp, tx_desc, skb, skb->len, offset)) { r8152_csum_workaround(tp, skb, &skb_head); continue; } rtl_tx_vlan_tag(tx_desc, skb); tx_data += sizeof(*tx_desc); len = skb->len; if (skb_copy_bits(skb, 0, tx_data, len) < 0) { struct net_device_stats *stats = &tp->netdev->stats; stats->tx_dropped++; dev_kfree_skb_any(skb); tx_data -= sizeof(*tx_desc); continue; } tx_data += len; agg->skb_len += len; agg->skb_num++; dev_kfree_skb_any(skb); remain = agg_buf_sz - (int)(tx_agg_align(tx_data) - agg->head); } if (!skb_queue_empty(&skb_head)) { spin_lock(&tx_queue->lock); skb_queue_splice(&skb_head, tx_queue); spin_unlock(&tx_queue->lock); } netif_tx_lock(tp->netdev); if (netif_queue_stopped(tp->netdev) && skb_queue_len(&tp->tx_queue) < tp->tx_qlen) netif_wake_queue(tp->netdev); netif_tx_unlock(tp->netdev); ret = usb_autopm_get_interface_async(tp->intf); if (ret < 0) goto out_tx_fill; 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); ret = usb_submit_urb(agg->urb, GFP_ATOMIC); if (ret < 0) usb_autopm_put_interface_async(tp->intf); out_tx_fill: return ret; } static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc) { u8 checksum = CHECKSUM_NONE; u32 opts2, opts3; if (tp->version == RTL_VER_01) goto return_result; opts2 = le32_to_cpu(rx_desc->opts2); opts3 = le32_to_cpu(rx_desc->opts3); if (opts2 & RD_IPV4_CS) { if (opts3 & IPF) checksum = CHECKSUM_NONE; else if ((opts2 & RD_UDP_CS) && (opts3 & UDPF)) checksum = CHECKSUM_NONE; else if ((opts2 & RD_TCP_CS) && (opts3 & TCPF)) checksum = CHECKSUM_NONE; else checksum = CHECKSUM_UNNECESSARY; } else if (opts2 & RD_IPV6_CS) { if ((opts2 & RD_UDP_CS) && !(opts3 & UDPF)) checksum = CHECKSUM_UNNECESSARY; else if ((opts2 & RD_TCP_CS) && !(opts3 & TCPF)) checksum = CHECKSUM_UNNECESSARY; } return_result: return checksum; } static void r8152_dump_rx_desc(struct rx_desc *rx_desc) { int rx_len = (le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK); printk(KERN_INFO "%s: %08x %08x %08x %08x %08x %08x rx_len=%d\n", __func__, le32_to_cpu(rx_desc->opts1), le32_to_cpu(rx_desc->opts2), le32_to_cpu(rx_desc->opts3), le32_to_cpu(rx_desc->opts4), le32_to_cpu(rx_desc->opts5), le32_to_cpu(rx_desc->opts6), rx_len); } static int r8152_check_rx_desc(struct r8152 *tp, struct rx_desc *rx_desc) { u32 opts1, opts2, opts3, opts4, opts5, opts6; int pkt_len; if (tp->version == RTL_VER_01) return 0; /* rx_desc looks okay */ opts1 = le32_to_cpu(rx_desc->opts1); opts2 = le32_to_cpu(rx_desc->opts2); opts3 = le32_to_cpu(rx_desc->opts3); opts4 = le32_to_cpu(rx_desc->opts4); opts5 = le32_to_cpu(rx_desc->opts5); opts6 = le32_to_cpu(rx_desc->opts6); pkt_len = (opts1 & RX_LEN_MASK) - CRC_SIZE; if ( !opts1 || ((opts1 & 0x0ff3f000) != 0x04400000 && (opts1 & 0xffff0000) != 0x00040000 && (opts1 & 0xffff0000) != 0x00080000) || (opts2 & ~(BIT(30)|RD_UDP_CS|RD_TCP_CS|RD_IPV6_CS|RD_IPV4_CS)) || ((opts2 & RD_IPV6_CS) && (opts2 & RD_IPV4_CS)) || ((opts3 & 0xffff0000) & ~(IPF|UDPF|TCPF|RX_VLAN_TAG)) // 0xff170000 || (opts4 & 0x060cfff8) != 0x06000000 || (opts5 | opts6) || pkt_len > (tp->netdev->mtu + 42) ){ printk(KERN_WARNING "%s: rx_desc looks bad.\n", __func__); return -EIO; /* rx_desc looks bad */ } return 0; /* rx_desc looks okay */ } static int rx_bottom(struct r8152 *tp, int budget) { unsigned long flags; struct list_head *cursor, *next, rx_queue; int ret = 0, work_done = 0; if (!skb_queue_empty(&tp->rx_queue)) { while (work_done < budget) { struct sk_buff *skb = __skb_dequeue(&tp->rx_queue); struct net_device *netdev = tp->netdev; struct net_device_stats *stats = &netdev->stats; unsigned int pkt_len; if (!skb) break; pkt_len = skb->len; napi_gro_receive(&tp->napi, skb); work_done++; stats->rx_packets++; stats->rx_bytes += pkt_len; } } if (list_empty(&tp->rx_done)) goto out1; INIT_LIST_HEAD(&rx_queue); spin_lock_irqsave(&tp->rx_lock, flags); list_splice_init(&tp->rx_done, &rx_queue); spin_unlock_irqrestore(&tp->rx_lock, flags); list_for_each_safe(cursor, next, &rx_queue) { struct rx_desc *rx_desc; struct rx_agg *agg; int len_used = 0; struct urb *urb; u8 *rx_data; list_del_init(cursor); agg = list_entry(cursor, struct rx_agg, list); urb = agg->urb; if (urb->actual_length < (sizeof(struct rx_desc) + ETH_ZLEN)) { printk(KERN_WARNING "r8152_rx_bottom: URB too small: actual_length=%u\n", urb->actual_length); goto submit; } rx_desc = agg->head; rx_data = agg->head; mb(); while (urb->actual_length > len_used) { struct net_device *netdev = tp->netdev; struct net_device_stats *stats = &netdev->stats; unsigned int pkt_len; struct sk_buff *skb; if ((len_used + sizeof(struct rx_desc)) > urb->actual_length) { printk(KERN_WARNING "r8152_rx_bottom: offset=%u/%u too small for rx_desc\n", len_used, urb->actual_length); break; } len_used += sizeof(struct rx_desc); if (r8152_check_rx_desc(tp, rx_desc)) { printk(KERN_WARNING "r8152_rx_bottom: offset=%u/%u bad rx_desc\n", len_used - sizeof(struct rx_desc), urb->actual_length); r8152_dump_rx_desc(rx_desc); } pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; if (pkt_len < ETH_ZLEN) { printk(KERN_WARNING "r8152_rx_bottom: offset=%u/%u pkt_len(%u) < ETH_ZLEN\n", len_used, urb->actual_length, pkt_len); r8152_dump_rx_desc(rx_desc); break; } len_used += pkt_len; if (urb->actual_length < len_used) { printk(KERN_WARNING "r8152_rx_bottom: offset=%u/%u pkt_len(%u) exceeds buffer\n", len_used - pkt_len, urb->actual_length, pkt_len); r8152_dump_rx_desc(rx_desc); break; } pkt_len -= CRC_SIZE; rx_data += sizeof(struct rx_desc); skb = netdev_alloc_skb_ip_align(netdev, pkt_len); if (!skb) { printk(KERN_WARNING "r8152_rx_bottom: netdev_alloc_skb_ip_align(%u) failed\n", pkt_len); stats->rx_dropped++; goto find_next_rx; } skb->ip_summed = r8152_rx_csum(tp, rx_desc); memcpy(skb->data, rx_data, pkt_len); skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, netdev); rtl_rx_vlan_tag(rx_desc, skb); if (work_done < budget) { napi_gro_receive(&tp->napi, skb); work_done++; stats->rx_packets++; stats->rx_bytes += pkt_len; } else { __skb_queue_tail(&tp->rx_queue, skb); } find_next_rx: rx_data = rx_agg_align(rx_data + pkt_len + CRC_SIZE); rx_desc = (struct rx_desc *)rx_data; len_used = (int)(rx_data - (u8 *)agg->head); } submit: if (!ret) { ret = r8152_submit_rx(tp, agg, GFP_ATOMIC); } else { urb->actual_length = 0; list_add_tail(&agg->list, next); } } if (!list_empty(&rx_queue)) { spin_lock_irqsave(&tp->rx_lock, flags); list_splice_tail(&rx_queue, &tp->rx_done); spin_unlock_irqrestore(&tp->rx_lock, flags); } out1: return work_done; } 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 *netdev = tp->netdev; if (res == -ENODEV) { set_bit(RTL8152_UNPLUG, &tp->flags); netif_device_detach(netdev); } else { struct net_device_stats *stats = &netdev->stats; unsigned long flags; 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(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; 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; clear_bit(SCHEDULE_NAPI, &tp->flags); tx_bottom(tp); } static int r8152_poll(struct napi_struct *napi, int budget) { struct r8152 *tp = container_of(napi, struct r8152, napi); int work_done; work_done = rx_bottom(tp, budget); bottom_half(tp); if (work_done < budget) { napi_complete(napi); if (!list_empty(&tp->rx_done)) napi_schedule(napi); } return work_done; } static int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags) { int ret; /* The rx would be stopped, so skip submitting */ if (test_bit(RTL8152_UNPLUG, &tp->flags) || !test_bit(WORK_ENABLE, &tp->flags) || !netif_carrier_ok(tp->netdev)) return 0; /* FIXME: memory leak? */ usb_fill_bulk_urb(agg->urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1), agg->head, agg_buf_sz, (usb_complete_t)read_bulk_callback, agg); agg->urb->transfer_dma = agg->transfer_dma; agg->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; mb(); ret = usb_submit_urb(agg->urb, mem_flags); if (ret == -ENODEV) { set_bit(RTL8152_UNPLUG, &tp->flags); netif_device_detach(tp->netdev); } else if (ret) { struct urb *urb = agg->urb; unsigned long flags; urb->actual_length = 0; spin_lock_irqsave(&tp->rx_lock, flags); list_add_tail(&agg->list, &tp->rx_done); spin_unlock_irqrestore(&tp->rx_lock, flags); netif_err(tp, rx_err, tp->netdev, "Couldn't submit rx[%p], ret = %d\n", agg, ret); napi_schedule(&tp->napi); } return ret; } static void rtl_drop_queued_tx(struct r8152 *tp) { struct net_device_stats *stats = &tp->netdev->stats; struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue; struct sk_buff *skb; if (skb_queue_empty(tx_queue)) return; __skb_queue_head_init(&skb_head); spin_lock_bh(&tx_queue->lock); skb_queue_splice_init(tx_queue, &skb_head); spin_unlock_bh(&tx_queue->lock); while ((skb = __skb_dequeue(&skb_head))) { dev_kfree_skb(skb); stats->tx_dropped++; } } static void rtl8152_tx_timeout(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); netif_warn(tp, tx_err, netdev, "Tx timeout\n"); usb_queue_reset_device(tp->intf); } static void rtl8152_set_rx_mode(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); if (netif_carrier_ok(netdev)) { 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 mc_filter[2]; /* Multicast hash filter */ __le32 tmp[2]; u32 ocp_data; clear_bit(RTL8152_SET_RX_MODE, &tp->flags); netif_stop_queue(netdev); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_data &= ~RCR_ACPT_ALL; ocp_data |= RCR_AB | RCR_APM; if (netdev->flags & IFF_PROMISC) { /* Unconditionally log net taps. */ netif_notice(tp, link, netdev, "Promiscuous mode enabled\n"); ocp_data |= RCR_AM | RCR_AAP; mc_filter[1] = 0xffffffff; mc_filter[0] = 0xffffffff; } else if ((netdev_mc_count(netdev) > multicast_filter_limit) || (netdev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ ocp_data |= RCR_AM; mc_filter[1] = 0xffffffff; mc_filter[0] = 0xffffffff; } else { struct netdev_hw_addr *ha; mc_filter[1] = 0; mc_filter[0] = 0; netdev_for_each_mc_addr(ha, netdev) { int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); ocp_data |= RCR_AM; } } 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(tmp), tmp); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); netif_wake_queue(netdev); } static netdev_features_t rtl8152_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { u32 mss = skb_shinfo(skb)->gso_size; int max_offset = mss ? GTTCPHO_MAX : TCPHO_MAX; int offset = skb_transport_offset(skb); if ((mss || skb->ip_summed == CHECKSUM_PARTIAL) && offset > max_offset) features &= ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); else if ((skb->len + sizeof(struct tx_desc)) > agg_buf_sz) features &= ~NETIF_F_GSO_MASK; return features; } static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); skb_tx_timestamp(skb); skb_queue_tail(&tp->tx_queue, skb); if (!list_empty(&tp->tx_free)) { if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { set_bit(SCHEDULE_NAPI, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } else { usb_mark_last_busy(tp->udev); napi_schedule(&tp->napi); } } else if (skb_queue_len(&tp->tx_queue) > tp->tx_qlen) { netif_stop_queue(netdev); } return NETDEV_TX_OK; } static void r8152b_reset_packet_filter(struct r8152 *tp) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC); ocp_data &= ~FMC_FCR_MCU_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); ocp_data |= FMC_FCR_MCU_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); } static void rtl8152_nic_reset(struct r8152 *tp) { int i; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST); for (i = 0; i < 1000; i++) { if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST)) break; usleep_range(100, 400); } } static void set_tx_qlen(struct r8152 *tp) { struct net_device *netdev = tp->netdev; tp->tx_qlen = agg_buf_sz / (netdev->mtu + VLAN_ETH_HLEN + VLAN_HLEN + sizeof(struct tx_desc)); } static inline u8 rtl8152_get_speed(struct r8152 *tp) { return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS); } static void rtl_set_eee_plus(struct r8152 *tp) { u32 ocp_data; u8 speed; speed = rtl8152_get_speed(tp); if (speed & _10bps) { ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); 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_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); } } static void rxdy_gated_en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); if (enable) ocp_data |= RXDY_GATED_EN; else ocp_data &= ~RXDY_GATED_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); } static int rtl_start_rx(struct r8152 *tp) { int i, ret = 0; INIT_LIST_HEAD(&tp->rx_done); 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); if (ret) break; } if (ret && ++i < RTL8152_MAX_RX) { struct list_head rx_queue; unsigned long flags; INIT_LIST_HEAD(&rx_queue); do { struct rx_agg *agg = &tp->rx_info[i++]; struct urb *urb = agg->urb; urb->actual_length = 0; list_add_tail(&agg->list, &rx_queue); } while (i < RTL8152_MAX_RX); spin_lock_irqsave(&tp->rx_lock, flags); list_splice_tail(&rx_queue, &tp->rx_done); spin_unlock_irqrestore(&tp->rx_lock, flags); } return ret; } static int rtl_stop_rx(struct r8152 *tp) { int i; for (i = 0; i < RTL8152_MAX_RX; i++) usb_kill_urb(tp->rx_info[i].urb); while (!skb_queue_empty(&tp->rx_queue)) dev_kfree_skb(__skb_dequeue(&tp->rx_queue)); return 0; } static int rtl_enable(struct r8152 *tp) { u32 ocp_data; r8152b_reset_packet_filter(tp); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR); ocp_data |= CR_RE | CR_TE; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data); rxdy_gated_en(tp, false); return 0; } static int rtl8152_enable(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; set_tx_qlen(tp); rtl_set_eee_plus(tp); return rtl_enable(tp); } static void r8153_set_rx_early_timeout(struct r8152 *tp) { u32 ocp_data = tp->coalesce / 8; ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, ocp_data); } static void r8153_set_rx_early_size(struct r8152 *tp) { u32 mtu = tp->netdev->mtu; u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 4; ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data); } static int rtl8153_enable(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; usb_disable_lpm(tp->udev); set_tx_qlen(tp); rtl_set_eee_plus(tp); r8153_set_rx_early_timeout(tp); r8153_set_rx_early_size(tp); return rtl_enable(tp); } static void rtl_disable(struct r8152 *tp) { u32 ocp_data; int i; if (test_bit(RTL8152_UNPLUG, &tp->flags)) { rtl_drop_queued_tx(tp); return; } 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); rtl_drop_queued_tx(tp); for (i = 0; i < RTL8152_MAX_TX; i++) usb_kill_urb(tp->tx_info[i].urb); rxdy_gated_en(tp, true); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY) break; usleep_range(1000, 2000); } for (i = 0; i < 1000; i++) { if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY) break; usleep_range(1000, 2000); } rtl_stop_rx(tp); rtl8152_nic_reset(tp); } static void r8152_power_cut_en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); if (enable) ocp_data |= POWER_CUT; else ocp_data &= ~POWER_CUT; ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS); ocp_data &= ~RESUME_INDICATE; ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); } static void rtl_rx_vlan_en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); if (enable) ocp_data |= CPCR_RX_VLAN; else ocp_data &= ~CPCR_RX_VLAN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); } static int rtl8152_set_features(struct net_device *dev, netdev_features_t features) { netdev_features_t changed = features ^ dev->features; struct r8152 *tp = netdev_priv(dev); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); if (changed & NETIF_F_HW_VLAN_CTAG_RX) { if (features & NETIF_F_HW_VLAN_CTAG_RX) rtl_rx_vlan_en(tp, true); else rtl_rx_vlan_en(tp, false); } mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } #define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) static u32 __rtl_get_wol(struct r8152 *tp) { u32 ocp_data; u32 wolopts = 0; ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5); if (!(ocp_data & LAN_WAKE_EN)) return 0; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); if (ocp_data & LINK_ON_WAKE_EN) wolopts |= WAKE_PHY; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5); if (ocp_data & UWF_EN) wolopts |= WAKE_UCAST; if (ocp_data & BWF_EN) wolopts |= WAKE_BCAST; if (ocp_data & MWF_EN) wolopts |= WAKE_MCAST; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); if (ocp_data & MAGIC_EN) wolopts |= WAKE_MAGIC; return wolopts; } static void __rtl_set_wol(struct r8152 *tp, u32 wolopts) { u32 ocp_data; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); ocp_data &= ~LINK_ON_WAKE_EN; if (wolopts & WAKE_PHY) ocp_data |= LINK_ON_WAKE_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5); ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN | LAN_WAKE_EN); if (wolopts & WAKE_UCAST) ocp_data |= UWF_EN; if (wolopts & WAKE_BCAST) ocp_data |= BWF_EN; if (wolopts & WAKE_MCAST) ocp_data |= MWF_EN; if (wolopts & WAKE_ANY) ocp_data |= LAN_WAKE_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); ocp_data &= ~MAGIC_EN; if (wolopts & WAKE_MAGIC) ocp_data |= MAGIC_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data); if (wolopts & WAKE_ANY) device_set_wakeup_enable(&tp->udev->dev, true); else device_set_wakeup_enable(&tp->udev->dev, false); } static void r8153_u1u2en(struct r8152 *tp, bool enable) { u8 u1u2[8]; if (enable) memset(u1u2, 0xff, sizeof(u1u2)); else memset(u1u2, 0x00, sizeof(u1u2)); usb_ocp_write(tp, USB_TOLERANCE, BYTE_EN_SIX_BYTES, sizeof(u1u2), u1u2); } static void r8153_u2p3en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL); if (enable && tp->version != RTL_VER_03 && tp->version != RTL_VER_04) ocp_data |= U2P3_ENABLE; else ocp_data &= ~U2P3_ENABLE; ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data); } static void r8153_power_cut_en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_POWER_CUT); if (enable) ocp_data |= PWR_EN | PHASE2_EN; else ocp_data &= ~(PWR_EN | PHASE2_EN); ocp_write_word(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); ocp_data &= ~PCUT_STATUS; ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); } static bool rtl_can_wakeup(struct r8152 *tp) { struct usb_device *udev = tp->udev; return (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP); } static void rtl_runtime_suspend_enable(struct r8152 *tp, bool enable) { if (enable) { u32 ocp_data; r8153_u1u2en(tp, false); r8153_u2p3en(tp, false); __rtl_set_wol(tp, WAKE_ANY); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); ocp_data |= LINK_OFF_WAKE_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); } else { __rtl_set_wol(tp, tp->saved_wolopts); r8153_u2p3en(tp, true); r8153_u1u2en(tp, true); } } static void rtl_phy_reset(struct r8152 *tp) { u16 data; int i; clear_bit(PHY_RESET, &tp->flags); data = r8152_mdio_read(tp, MII_BMCR); /* don't reset again before the previous one complete */ if (data & BMCR_RESET) return; data |= BMCR_RESET; r8152_mdio_write(tp, MII_BMCR, data); for (i = 0; i < 50; i++) { msleep(20); if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0) break; } } static void r8153_teredo_off(struct r8152 *tp) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG); ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK | OOB_TEREDO_EN); ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE); ocp_write_word(tp, MCU_TYPE_PLA, PLA_REALWOW_TIMER, 0); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0); } static void r8152b_disable_aldps(struct r8152 *tp) { ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE); msleep(20); } static inline void r8152b_enable_aldps(struct r8152 *tp) { ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | LINKENA | DIS_SDSAVE); } static void rtl8152_disable(struct r8152 *tp) { r8152b_disable_aldps(tp); rtl_disable(tp); r8152b_enable_aldps(tp); } static void r8152b_hw_phy_cfg(struct r8152 *tp) { u16 data; data = r8152_mdio_read(tp, MII_BMCR); if (data & BMCR_PDOWN) { data &= ~BMCR_PDOWN; r8152_mdio_write(tp, MII_BMCR, data); } set_bit(PHY_RESET, &tp->flags); } static void r8152b_exit_oob(struct r8152 *tp) { 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); rxdy_gated_en(tp, true); r8153_teredo_off(tp); r8152b_hw_phy_cfg(tp); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, 0x00); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data &= ~MCU_BORW_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } rtl8152_nic_reset(tp); /* rx share fifo credit full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); if (tp->udev->speed == USB_SPEED_FULL || tp->udev->speed == USB_SPEED_LOW) { /* rx share fifo credit near full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_FULL); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_FULL); } else { /* rx share fifo credit near full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_HIGH); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_HIGH); } /* TX share fifo free credit full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL); ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD); ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH); ocp_write_dword(tp, MCU_TYPE_USB, USB_TX_DMA, TEST_MODE_DISABLE | TX_SIZE_ADJUST1); rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); ocp_data |= TCR0_AUTO_FIFO; ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); } static void r8152b_enter_oob(struct r8152 *tp) { u32 ocp_data; int i; ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_OOB); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_OOB); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_OOB); rtl_disable(tp); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); rtl_rx_vlan_en(tp, true); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); ocp_data |= ALDPS_PROXY_MODE; ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); rxdy_gated_en(tp, false); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_data |= RCR_APM | RCR_AM | RCR_AB; ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); } static void r8153_hw_phy_cfg(struct r8152 *tp) { u32 ocp_data; u16 data; if (tp->version == RTL_VER_03 || tp->version == RTL_VER_04 || tp->version == RTL_VER_05) ocp_reg_write(tp, OCP_ADC_CFG, CKADSEL_L | ADC_EN | EN_EMI_L); data = r8152_mdio_read(tp, MII_BMCR); if (data & BMCR_PDOWN) { data &= ~BMCR_PDOWN; r8152_mdio_write(tp, MII_BMCR, data); } if (tp->version == RTL_VER_03) { data = ocp_reg_read(tp, OCP_EEE_CFG); data &= ~CTAP_SHORT_EN; ocp_reg_write(tp, OCP_EEE_CFG, data); } data = ocp_reg_read(tp, OCP_POWER_CFG); data |= EEE_CLKDIV_EN; ocp_reg_write(tp, OCP_POWER_CFG, data); data = ocp_reg_read(tp, OCP_DOWN_SPEED); data |= EN_10M_BGOFF; ocp_reg_write(tp, OCP_DOWN_SPEED, data); data = ocp_reg_read(tp, OCP_POWER_CFG); data |= EN_10M_PLLOFF; ocp_reg_write(tp, OCP_POWER_CFG, data); sram_write(tp, SRAM_IMPEDANCE, 0x0b13); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); ocp_data |= PFM_PWM_SWITCH; ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); /* Enable LPF corner auto tune */ sram_write(tp, SRAM_LPF_CFG, 0xf70f); /* Adjust 10M Amplitude */ sram_write(tp, SRAM_10M_AMP1, 0x00af); sram_write(tp, SRAM_10M_AMP2, 0x0208); set_bit(PHY_RESET, &tp->flags); } static void r8153_first_init(struct r8152 *tp) { u32 ocp_data; int i; rxdy_gated_en(tp, true); r8153_teredo_off(tp); 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); r8153_hw_phy_cfg(tp); rtl8152_nic_reset(tp); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data &= ~MCU_BORW_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8153_RMS); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); ocp_data |= TCR0_AUTO_FIFO; ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); rtl8152_nic_reset(tp); /* rx share fifo credit full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_NORMAL); ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_NORMAL); /* TX share fifo free credit full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL2); /* rx aggregation */ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); } static void r8153_enter_oob(struct r8152 *tp) { u32 ocp_data; int i; ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); rtl_disable(tp); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8153_RMS); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG); ocp_data &= ~TEREDO_WAKE_MASK; ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); rtl_rx_vlan_en(tp, true); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); ocp_data |= ALDPS_PROXY_MODE; ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); rxdy_gated_en(tp, false); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_data |= RCR_APM | RCR_AM | RCR_AB; ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); } static void r8153_disable_aldps(struct r8152 *tp) { u16 data; data = ocp_reg_read(tp, OCP_POWER_CFG); data &= ~EN_ALDPS; ocp_reg_write(tp, OCP_POWER_CFG, data); msleep(20); } static void r8153_enable_aldps(struct r8152 *tp) { u16 data; data = ocp_reg_read(tp, OCP_POWER_CFG); data |= EN_ALDPS; ocp_reg_write(tp, OCP_POWER_CFG, data); } static void rtl8153_disable(struct r8152 *tp) { r8153_disable_aldps(tp); rtl_disable(tp); r8153_enable_aldps(tp); usb_enable_lpm(tp->udev); } static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) { u16 bmcr, anar, gbcr; int ret = 0; cancel_delayed_work_sync(&tp->schedule); anar = r8152_mdio_read(tp, MII_ADVERTISE); anar &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL); if (tp->mii.supports_gmii) { gbcr = r8152_mdio_read(tp, MII_CTRL1000); gbcr &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); } else { gbcr = 0; } if (autoneg == AUTONEG_DISABLE) { if (speed == SPEED_10) { bmcr = 0; anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; } else if (speed == SPEED_100) { bmcr = BMCR_SPEED100; anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; } else if (speed == SPEED_1000 && tp->mii.supports_gmii) { bmcr = BMCR_SPEED1000; gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; } else { ret = -EINVAL; goto out; } if (duplex == DUPLEX_FULL) bmcr |= BMCR_FULLDPLX; } else { if (speed == SPEED_10) { if (duplex == DUPLEX_FULL) anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; else anar |= ADVERTISE_10HALF; } else if (speed == SPEED_100) { if (duplex == DUPLEX_FULL) { anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; } else { anar |= ADVERTISE_10HALF; anar |= ADVERTISE_100HALF; } } else if (speed == SPEED_1000 && tp->mii.supports_gmii) { if (duplex == DUPLEX_FULL) { anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; } else { anar |= ADVERTISE_10HALF; anar |= ADVERTISE_100HALF; gbcr |= ADVERTISE_1000HALF; } } else { ret = -EINVAL; goto out; } bmcr = BMCR_ANENABLE | BMCR_ANRESTART; } if (test_bit(PHY_RESET, &tp->flags)) bmcr |= BMCR_RESET; if (tp->mii.supports_gmii) r8152_mdio_write(tp, MII_CTRL1000, gbcr); r8152_mdio_write(tp, MII_ADVERTISE, anar); r8152_mdio_write(tp, MII_BMCR, bmcr); if (test_bit(PHY_RESET, &tp->flags)) { int i; clear_bit(PHY_RESET, &tp->flags); for (i = 0; i < 50; i++) { msleep(20); if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0) break; } } out: return ret; } static void rtl8152_up(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8152b_disable_aldps(tp); r8152b_exit_oob(tp); r8152b_enable_aldps(tp); } static void rtl8152_down(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) { rtl_drop_queued_tx(tp); return; } r8152_power_cut_en(tp, false); r8152b_disable_aldps(tp); r8152b_enter_oob(tp); r8152b_enable_aldps(tp); } static void rtl8153_up(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8153_u1u2en(tp, false); r8153_disable_aldps(tp); r8153_first_init(tp); r8153_enable_aldps(tp); r8153_u2p3en(tp, true); r8153_u1u2en(tp, true); usb_enable_lpm(tp->udev); } static void rtl8153_down(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) { rtl_drop_queued_tx(tp); return; } r8153_u1u2en(tp, false); r8153_u2p3en(tp, false); r8153_power_cut_en(tp, false); r8153_disable_aldps(tp); r8153_enter_oob(tp); r8153_enable_aldps(tp); } static bool rtl8152_in_nway(struct r8152 *tp) { u16 nway_state; ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, 0x2000); tp->ocp_base = 0x2000; ocp_write_byte(tp, MCU_TYPE_PLA, 0xb014, 0x4c); /* phy state */ nway_state = ocp_read_word(tp, MCU_TYPE_PLA, 0xb01a); /* bit 15: TXDIS_STATE, bit 14: ABD_STATE */ if (nway_state & 0xc000) return false; else return true; } static bool rtl8153_in_nway(struct r8152 *tp) { u16 phy_state = ocp_reg_read(tp, OCP_PHY_STATE) & 0xff; if (phy_state == TXDIS_STATE || phy_state == ABD_STATE) return false; else return true; } 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) { if (!netif_carrier_ok(netdev)) { tp->rtl_ops.enable(tp); set_bit(RTL8152_SET_RX_MODE, &tp->flags); napi_disable(&tp->napi); netif_carrier_on(netdev); rtl_start_rx(tp); napi_enable(&tp->napi); } } else { if (netif_carrier_ok(netdev)) { netif_carrier_off(netdev); napi_disable(&tp->napi); tp->rtl_ops.disable(tp); napi_enable(&tp->napi); } } } static void rtl_work_func_t(struct work_struct *work) { struct r8152 *tp = container_of(work, struct r8152, schedule.work); /* If the device is unplugged or !netif_running(), the workqueue * doesn't need to wake the device, and could return directly. */ if (test_bit(RTL8152_UNPLUG, &tp->flags) || !netif_running(tp->netdev)) return; if (usb_autopm_get_interface(tp->intf) < 0) return; if (!test_bit(WORK_ENABLE, &tp->flags)) goto out1; if (!mutex_trylock(&tp->control)) { schedule_delayed_work(&tp->schedule, 0); goto out1; } 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); /* don't schedule napi before linking */ if (test_bit(SCHEDULE_NAPI, &tp->flags) && netif_carrier_ok(tp->netdev)) { clear_bit(SCHEDULE_NAPI, &tp->flags); napi_schedule(&tp->napi); } if (test_bit(PHY_RESET, &tp->flags)) rtl_phy_reset(tp); mutex_unlock(&tp->control); out1: usb_autopm_put_interface(tp->intf); } static int rtl8152_open(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); int res = 0; res = alloc_all_mem(tp); if (res) goto out; netif_carrier_off(netdev); res = usb_autopm_get_interface(tp->intf); if (res < 0) { free_all_mem(tp); goto out; } mutex_lock(&tp->control); tp->rtl_ops.up(tp); rtl8152_set_speed(tp, AUTONEG_ENABLE, tp->mii.supports_gmii ? SPEED_1000 : SPEED_100, DUPLEX_FULL); netif_carrier_off(netdev); netif_start_queue(netdev); set_bit(WORK_ENABLE, &tp->flags); 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); free_all_mem(tp); } else { napi_enable(&tp->napi); } mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return res; } static int rtl8152_close(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); int res = 0; napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); netif_stop_queue(netdev); res = usb_autopm_get_interface(tp->intf); if (res < 0 || test_bit(RTL8152_UNPLUG, &tp->flags)) { rtl_drop_queued_tx(tp); rtl_stop_rx(tp); } else { mutex_lock(&tp->control); tp->rtl_ops.down(tp); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); } free_all_mem(tp); return res; } static inline void r8152_mmd_indirect(struct r8152 *tp, u16 dev, u16 reg) { ocp_reg_write(tp, OCP_EEE_AR, FUN_ADDR | dev); ocp_reg_write(tp, OCP_EEE_DATA, reg); ocp_reg_write(tp, OCP_EEE_AR, FUN_DATA | dev); } static u16 r8152_mmd_read(struct r8152 *tp, u16 dev, u16 reg) { u16 data; r8152_mmd_indirect(tp, dev, reg); data = ocp_reg_read(tp, OCP_EEE_DATA); ocp_reg_write(tp, OCP_EEE_AR, 0x0000); return data; } static void r8152_mmd_write(struct r8152 *tp, u16 dev, u16 reg, u16 data) { r8152_mmd_indirect(tp, dev, reg); ocp_reg_write(tp, OCP_EEE_DATA, data); ocp_reg_write(tp, OCP_EEE_AR, 0x0000); } static void r8152_eee_en(struct r8152 *tp, bool enable) { u16 config1, config2, config3; u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); config1 = ocp_reg_read(tp, OCP_EEE_CONFIG1) & ~sd_rise_time_mask; config2 = ocp_reg_read(tp, OCP_EEE_CONFIG2); config3 = ocp_reg_read(tp, OCP_EEE_CONFIG3) & ~fast_snr_mask; if (enable) { ocp_data |= EEE_RX_EN | EEE_TX_EN; config1 |= EEE_10_CAP | EEE_NWAY_EN | TX_QUIET_EN | RX_QUIET_EN; config1 |= sd_rise_time(1); config2 |= RG_DACQUIET_EN | RG_LDVQUIET_EN; config3 |= fast_snr(42); } else { ocp_data &= ~(EEE_RX_EN | EEE_TX_EN); config1 &= ~(EEE_10_CAP | EEE_NWAY_EN | TX_QUIET_EN | RX_QUIET_EN); config1 |= sd_rise_time(7); config2 &= ~(RG_DACQUIET_EN | RG_LDVQUIET_EN); config3 |= fast_snr(511); } ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data); ocp_reg_write(tp, OCP_EEE_CONFIG1, config1); ocp_reg_write(tp, OCP_EEE_CONFIG2, config2); ocp_reg_write(tp, OCP_EEE_CONFIG3, config3); } static void r8152b_enable_eee(struct r8152 *tp) { r8152_eee_en(tp, true); r8152_mmd_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, MDIO_EEE_100TX); } static void r8153_eee_en(struct r8152 *tp, bool enable) { u32 ocp_data; u16 config; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); config = ocp_reg_read(tp, OCP_EEE_CFG); if (enable) { ocp_data |= EEE_RX_EN | EEE_TX_EN; config |= EEE10_EN; } else { ocp_data &= ~(EEE_RX_EN | EEE_TX_EN); config &= ~EEE10_EN; } ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data); ocp_reg_write(tp, OCP_EEE_CFG, config); } static void r8153_enable_eee(struct r8152 *tp) { r8153_eee_en(tp, true); ocp_reg_write(tp, OCP_EEE_ADV, MDIO_EEE_1000T | MDIO_EEE_100TX); } static void r8152b_enable_fc(struct r8152 *tp) { u16 anar; anar = r8152_mdio_read(tp, MII_ADVERTISE); anar |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; r8152_mdio_write(tp, MII_ADVERTISE, anar); } static void rtl_tally_reset(struct r8152 *tp) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY); ocp_data |= TALLY_RESET; ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); } static void r8152b_init(struct r8152 *tp) { u32 ocp_data; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8152b_disable_aldps(tp); if (tp->version == RTL_VER_01) { ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); ocp_data &= ~LED_MODE_MASK; ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); } r8152_power_cut_en(tp, false); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH; ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL); ocp_data &= ~MCU_CLK_RATIO_MASK; ocp_data |= MCU_CLK_RATIO | D3_CLK_GATED_EN; ocp_write_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ocp_data); ocp_data = GPHY_STS_MSK | SPEED_DOWN_MSK | SPDWN_RXDV_MSK | SPDWN_LINKCHG_MSK; ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data); r8152b_enable_eee(tp); r8152b_enable_aldps(tp); r8152b_enable_fc(tp); rtl_tally_reset(tp); /* enable rx aggregation */ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); } static void r8153_init(struct r8152 *tp) { u32 ocp_data; int i; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8153_disable_aldps(tp); r8153_u1u2en(tp, false); for (i = 0; i < 500; i++) { if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) & AUTOLOAD_DONE) break; msleep(20); } for (i = 0; i < 500; i++) { ocp_data = ocp_reg_read(tp, OCP_PHY_STATUS) & PHY_STAT_MASK; if (ocp_data == PHY_STAT_LAN_ON || ocp_data == PHY_STAT_PWRDN) break; msleep(20); } usb_disable_lpm(tp->udev); r8153_u2p3en(tp, false); if (tp->version == RTL_VER_04) { ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_SSPHYLINK2); ocp_data &= ~pwd_dn_scale_mask; ocp_data |= pwd_dn_scale(96); ocp_write_word(tp, MCU_TYPE_USB, USB_SSPHYLINK2, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); } else if (tp->version == RTL_VER_05) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0); ocp_data &= ~ECM_ALDPS; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1); if (ocp_read_word(tp, MCU_TYPE_USB, USB_BURST_SIZE) == 0) ocp_data &= ~DYNAMIC_BURST; else ocp_data |= DYNAMIC_BURST; ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1, ocp_data); } else if (tp->version == RTL_VER_06) { ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1); if (ocp_read_word(tp, MCU_TYPE_USB, USB_BURST_SIZE) == 0) ocp_data &= ~DYNAMIC_BURST; else ocp_data |= DYNAMIC_BURST; ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1, ocp_data); } ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2); ocp_data |= EP4_FULL_FC; ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL); ocp_data &= ~TIMER11_EN; ocp_write_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); ocp_data &= ~LED_MODE_MASK; ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); ocp_data = FIFO_EMPTY_1FB | ROK_EXIT_LPM; if (tp->version == RTL_VER_04 && tp->udev->speed != USB_SPEED_SUPER) ocp_data |= LPM_TIMER_500MS; else ocp_data |= LPM_TIMER_500US; ocp_write_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2); ocp_data &= ~SEN_VAL_MASK; ocp_data |= SEN_VAL_NORMAL | SEL_RXIDLE; ocp_write_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2, ocp_data); ocp_write_word(tp, MCU_TYPE_USB, USB_CONNECT_TIMER, 0x0001); r8153_power_cut_en(tp, false); r8153_u1u2en(tp, true); ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ALDPS_SPDWN_RATIO); ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, EEE_SPDWN_RATIO); ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PKT_AVAIL_SPDWN_EN | SUSPEND_SPDWN_EN | U1U2_SPDWN_EN | L1_SPDWN_EN); ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, PWRSAVE_SPDWN_EN | RXDV_SPDWN_EN | TX10MIDLE_EN | TP100_SPDWN_EN | TP500_SPDWN_EN | TP1000_SPDWN_EN | EEE_SPDWN_EN); r8153_enable_eee(tp); r8153_enable_aldps(tp); r8152b_enable_fc(tp); rtl_tally_reset(tp); r8153_u2p3en(tp, true); } static int rtl8152_pre_reset(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); struct net_device *netdev; if (!tp) return 0; netdev = tp->netdev; if (!netif_running(netdev)) return 0; napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); if (netif_carrier_ok(netdev)) { netif_stop_queue(netdev); mutex_lock(&tp->control); tp->rtl_ops.disable(tp); mutex_unlock(&tp->control); } return 0; } static int rtl8152_post_reset(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); struct net_device *netdev; if (!tp) return 0; netdev = tp->netdev; if (!netif_running(netdev)) return 0; set_bit(WORK_ENABLE, &tp->flags); if (netif_carrier_ok(netdev)) { mutex_lock(&tp->control); tp->rtl_ops.enable(tp); rtl8152_set_rx_mode(netdev); mutex_unlock(&tp->control); netif_wake_queue(netdev); } napi_enable(&tp->napi); return 0; } static bool delay_autosuspend(struct r8152 *tp) { bool sw_linking = !!netif_carrier_ok(tp->netdev); bool hw_linking = !!(rtl8152_get_speed(tp) & LINK_STATUS); /* This means a linking change occurs and the driver doesn't detect it, * yet. If the driver has disabled tx/rx and hw is linking on, the * device wouldn't wake up by receiving any packet. */ if (work_busy(&tp->schedule.work) || sw_linking != hw_linking) return true; /* If the linking down is occurred by nway, the device may miss the * linking change event. And it wouldn't wake when linking on. */ if (!sw_linking && tp->rtl_ops.in_nway(tp)) return true; else return false; } static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) { struct r8152 *tp = usb_get_intfdata(intf); struct net_device *netdev = tp->netdev; int ret = 0; mutex_lock(&tp->control); if (PMSG_IS_AUTO(message)) { if (netif_running(netdev) && delay_autosuspend(tp)) { ret = -EBUSY; goto out1; } set_bit(SELECTIVE_SUSPEND, &tp->flags); } else { netif_device_detach(netdev); } if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) { clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); napi_disable(&tp->napi); if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { rtl_stop_rx(tp); rtl_runtime_suspend_enable(tp, true); } else { cancel_delayed_work_sync(&tp->schedule); tp->rtl_ops.down(tp); } napi_enable(&tp->napi); } out1: mutex_unlock(&tp->control); return ret; } static int rtl8152_resume(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); mutex_lock(&tp->control); if (!test_bit(SELECTIVE_SUSPEND, &tp->flags)) { tp->rtl_ops.init(tp); netif_device_attach(tp->netdev); } if (netif_running(tp->netdev) && tp->netdev->flags & IFF_UP) { if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { rtl_runtime_suspend_enable(tp, false); clear_bit(SELECTIVE_SUSPEND, &tp->flags); napi_disable(&tp->napi); set_bit(WORK_ENABLE, &tp->flags); if (netif_carrier_ok(tp->netdev)) rtl_start_rx(tp); napi_enable(&tp->napi); } else { tp->rtl_ops.up(tp); rtl8152_set_speed(tp, AUTONEG_ENABLE, tp->mii.supports_gmii ? SPEED_1000 : SPEED_100, DUPLEX_FULL); netif_carrier_off(tp->netdev); set_bit(WORK_ENABLE, &tp->flags); } usb_submit_urb(tp->intr_urb, GFP_KERNEL); } else if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { if (tp->netdev->flags & IFF_UP) rtl_runtime_suspend_enable(tp, false); clear_bit(SELECTIVE_SUSPEND, &tp->flags); } mutex_unlock(&tp->control); return 0; } static int rtl8152_reset_resume(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); clear_bit(SELECTIVE_SUSPEND, &tp->flags); return rtl8152_resume(intf); } static void rtl8152_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct r8152 *tp = netdev_priv(dev); if (usb_autopm_get_interface(tp->intf) < 0) return; if (!rtl_can_wakeup(tp)) { wol->supported = 0; wol->wolopts = 0; } else { mutex_lock(&tp->control); wol->supported = WAKE_ANY; wol->wolopts = __rtl_get_wol(tp); mutex_unlock(&tp->control); } usb_autopm_put_interface(tp->intf); } static int rtl8152_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct r8152 *tp = netdev_priv(dev); int ret; if (!rtl_can_wakeup(tp)) return -EOPNOTSUPP; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out_set_wol; mutex_lock(&tp->control); __rtl_set_wol(tp, wol->wolopts); tp->saved_wolopts = wol->wolopts & WAKE_ANY; mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out_set_wol: return ret; } static u32 rtl8152_get_msglevel(struct net_device *dev) { struct r8152 *tp = netdev_priv(dev); return tp->msg_enable; } static void rtl8152_set_msglevel(struct net_device *dev, u32 value) { struct r8152 *tp = netdev_priv(dev); tp->msg_enable = value; } static void rtl8152_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { struct r8152 *tp = netdev_priv(netdev); strlcpy(info->driver, MODULENAME, sizeof(info->driver)); strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); usb_make_path(tp->udev, info->bus_info, sizeof(info->bus_info)); } static int rtl8152_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) { struct r8152 *tp = netdev_priv(netdev); int ret; if (!tp->mii.mdio_read) return -EOPNOTSUPP; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = mii_ethtool_gset(&tp->mii, cmd); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static int rtl8152_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct r8152 *tp = netdev_priv(dev); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static const char rtl8152_gstrings[][ETH_GSTRING_LEN] = { "tx_packets", "rx_packets", "tx_errors", "rx_errors", "rx_missed", "align_errors", "tx_single_collisions", "tx_multi_collisions", "rx_unicast", "rx_broadcast", "rx_multicast", "tx_aborted", "tx_underrun", }; static int rtl8152_get_sset_count(struct net_device *dev, int sset) { switch (sset) { case ETH_SS_STATS: return ARRAY_SIZE(rtl8152_gstrings); default: return -EOPNOTSUPP; } } static void rtl8152_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct r8152 *tp = netdev_priv(dev); struct tally_counter tally; if (usb_autopm_get_interface(tp->intf) < 0) return; generic_ocp_read(tp, PLA_TALLYCNT, sizeof(tally), &tally, MCU_TYPE_PLA); usb_autopm_put_interface(tp->intf); data[0] = le64_to_cpu(tally.tx_packets); data[1] = le64_to_cpu(tally.rx_packets); data[2] = le64_to_cpu(tally.tx_errors); data[3] = le32_to_cpu(tally.rx_errors); data[4] = le16_to_cpu(tally.rx_missed); data[5] = le16_to_cpu(tally.align_errors); data[6] = le32_to_cpu(tally.tx_one_collision); data[7] = le32_to_cpu(tally.tx_multi_collision); data[8] = le64_to_cpu(tally.rx_unicast); data[9] = le64_to_cpu(tally.rx_broadcast); data[10] = le32_to_cpu(tally.rx_multicast); data[11] = le16_to_cpu(tally.tx_aborted); data[12] = le16_to_cpu(tally.tx_underrun); } static void rtl8152_get_strings(struct net_device *dev, u32 stringset, u8 *data) { switch (stringset) { case ETH_SS_STATS: memcpy(data, *rtl8152_gstrings, sizeof(rtl8152_gstrings)); break; } } static int r8152_get_eee(struct r8152 *tp, struct ethtool_eee *eee) { u32 ocp_data, lp, adv, supported = 0; u16 val; val = r8152_mmd_read(tp, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); supported = mmd_eee_cap_to_ethtool_sup_t(val); val = r8152_mmd_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV); adv = mmd_eee_adv_to_ethtool_adv_t(val); val = r8152_mmd_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); lp = mmd_eee_adv_to_ethtool_adv_t(val); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); ocp_data &= EEE_RX_EN | EEE_TX_EN; eee->eee_enabled = !!ocp_data; eee->eee_active = !!(supported & adv & lp); eee->supported = supported; eee->advertised = adv; eee->lp_advertised = lp; return 0; } static int r8152_set_eee(struct r8152 *tp, struct ethtool_eee *eee) { u16 val = ethtool_adv_to_mmd_eee_adv_t(eee->advertised); r8152_eee_en(tp, eee->eee_enabled); if (!eee->eee_enabled) val = 0; r8152_mmd_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); return 0; } static int r8153_get_eee(struct r8152 *tp, struct ethtool_eee *eee) { u32 ocp_data, lp, adv, supported = 0; u16 val; val = ocp_reg_read(tp, OCP_EEE_ABLE); supported = mmd_eee_cap_to_ethtool_sup_t(val); val = ocp_reg_read(tp, OCP_EEE_ADV); adv = mmd_eee_adv_to_ethtool_adv_t(val); val = ocp_reg_read(tp, OCP_EEE_LPABLE); lp = mmd_eee_adv_to_ethtool_adv_t(val); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); ocp_data &= EEE_RX_EN | EEE_TX_EN; eee->eee_enabled = !!ocp_data; eee->eee_active = !!(supported & adv & lp); eee->supported = supported; eee->advertised = adv; eee->lp_advertised = lp; return 0; } static int r8153_set_eee(struct r8152 *tp, struct ethtool_eee *eee) { u16 val = ethtool_adv_to_mmd_eee_adv_t(eee->advertised); r8153_eee_en(tp, eee->eee_enabled); if (!eee->eee_enabled) val = 0; ocp_reg_write(tp, OCP_EEE_ADV, val); return 0; } static int rtl_ethtool_get_eee(struct net_device *net, struct ethtool_eee *edata) { struct r8152 *tp = netdev_priv(net); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = tp->rtl_ops.eee_get(tp, edata); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static int rtl_ethtool_set_eee(struct net_device *net, struct ethtool_eee *edata) { struct r8152 *tp = netdev_priv(net); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = tp->rtl_ops.eee_set(tp, edata); if (!ret) ret = mii_nway_restart(&tp->mii); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static int rtl8152_nway_reset(struct net_device *dev) { struct r8152 *tp = netdev_priv(dev); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = mii_nway_restart(&tp->mii); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static int rtl8152_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce) { struct r8152 *tp = netdev_priv(netdev); switch (tp->version) { case RTL_VER_01: case RTL_VER_02: return -EOPNOTSUPP; default: break; } coalesce->rx_coalesce_usecs = tp->coalesce; return 0; } static int rtl8152_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce) { struct r8152 *tp = netdev_priv(netdev); int ret; switch (tp->version) { case RTL_VER_01: case RTL_VER_02: return -EOPNOTSUPP; default: break; } if (coalesce->rx_coalesce_usecs > COALESCE_SLOW) return -EINVAL; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) return ret; mutex_lock(&tp->control); if (tp->coalesce != coalesce->rx_coalesce_usecs) { tp->coalesce = coalesce->rx_coalesce_usecs; if (netif_running(tp->netdev) && netif_carrier_ok(netdev)) r8153_set_rx_early_timeout(tp); } mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); return ret; } static struct ethtool_ops ops = { .get_drvinfo = rtl8152_get_drvinfo, .get_settings = rtl8152_get_settings, .set_settings = rtl8152_set_settings, .get_link = ethtool_op_get_link, .nway_reset = rtl8152_nway_reset, .get_msglevel = rtl8152_get_msglevel, .set_msglevel = rtl8152_set_msglevel, .get_wol = rtl8152_get_wol, .set_wol = rtl8152_set_wol, .get_strings = rtl8152_get_strings, .get_sset_count = rtl8152_get_sset_count, .get_ethtool_stats = rtl8152_get_ethtool_stats, .get_coalesce = rtl8152_get_coalesce, .set_coalesce = rtl8152_set_coalesce, .get_eee = rtl_ethtool_get_eee, .set_eee = rtl_ethtool_set_eee, }; static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) { struct r8152 *tp = netdev_priv(netdev); struct mii_ioctl_data *data = if_mii(rq); int res; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; res = usb_autopm_get_interface(tp->intf); if (res < 0) goto out; switch (cmd) { case SIOCGMIIPHY: data->phy_id = R8152_PHY_ID; /* Internal PHY */ break; case SIOCGMIIREG: mutex_lock(&tp->control); data->val_out = r8152_mdio_read(tp, data->reg_num); mutex_unlock(&tp->control); break; case SIOCSMIIREG: if (!capable(CAP_NET_ADMIN)) { res = -EPERM; break; } mutex_lock(&tp->control); r8152_mdio_write(tp, data->reg_num, data->val_in); mutex_unlock(&tp->control); break; default: res = -EOPNOTSUPP; } usb_autopm_put_interface(tp->intf); out: return res; } static int rtl8152_change_mtu(struct net_device *dev, int new_mtu) { struct r8152 *tp = netdev_priv(dev); int ret; switch (tp->version) { case RTL_VER_01: case RTL_VER_02: return eth_change_mtu(dev, new_mtu); default: break; } if (new_mtu < 68 || new_mtu > RTL8153_MAX_MTU) return -EINVAL; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) return ret; mutex_lock(&tp->control); dev->mtu = new_mtu; if (netif_running(dev) && netif_carrier_ok(dev)) r8153_set_rx_early_size(tp); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); return ret; } static const struct net_device_ops rtl8152_netdev_ops = { .ndo_open = rtl8152_open, .ndo_stop = rtl8152_close, .ndo_do_ioctl = rtl8152_ioctl, .ndo_start_xmit = rtl8152_start_xmit, .ndo_tx_timeout = rtl8152_tx_timeout, .ndo_set_features = rtl8152_set_features, .ndo_set_rx_mode = rtl8152_set_rx_mode, .ndo_set_mac_address = rtl8152_set_mac_address, .ndo_change_mtu = rtl8152_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_features_check = rtl8152_features_check, }; static void r8152b_get_version(struct r8152 *tp) { u32 ocp_data; u16 version; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1); version = (u16)(ocp_data & VERSION_MASK); switch (version) { case 0x4c00: tp->version = RTL_VER_01; break; case 0x4c10: tp->version = RTL_VER_02; break; case 0x5c00: tp->version = RTL_VER_03; tp->mii.supports_gmii = 1; break; case 0x5c10: tp->version = RTL_VER_04; tp->mii.supports_gmii = 1; break; case 0x5c20: tp->version = RTL_VER_05; tp->mii.supports_gmii = 1; break; case 0x5c30: tp->version = RTL_VER_06; tp->mii.supports_gmii = 1; break; default: netif_info(tp, probe, tp->netdev, "Unknown version 0x%04x\n", version); break; } } static void rtl8152_unload(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; if (tp->version != RTL_VER_01) r8152_power_cut_en(tp, true); } static void rtl8153_unload(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8153_power_cut_en(tp, false); } static int rtl_ops_init(struct r8152 *tp) { struct rtl_ops *ops = &tp->rtl_ops; int ret = 0; switch (tp->version) { case RTL_VER_01: case RTL_VER_02: ops->init = r8152b_init; ops->enable = rtl8152_enable; ops->disable = rtl8152_disable; ops->up = rtl8152_up; ops->down = rtl8152_down; ops->unload = rtl8152_unload; ops->eee_get = r8152_get_eee; ops->eee_set = r8152_set_eee; ops->in_nway = rtl8152_in_nway; break; case RTL_VER_03: case RTL_VER_04: case RTL_VER_05: case RTL_VER_06: ops->init = r8153_init; ops->enable = rtl8153_enable; ops->disable = rtl8153_disable; ops->up = rtl8153_up; ops->down = rtl8153_down; ops->unload = rtl8153_unload; ops->eee_get = r8153_get_eee; ops->eee_set = r8153_set_eee; ops->in_nway = rtl8153_in_nway; break; default: ret = -ENODEV; netif_err(tp, probe, tp->netdev, "Unknown Device\n"); break; } return ret; } static int rtl8152_probe(struct usb_interface *intf, const struct usb_device_id *id) { 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); return -ENODEV; } usb_reset_device(udev); netdev = alloc_etherdev(sizeof(struct r8152)); if (!netdev) { dev_err(&intf->dev, "Out of memory\n"); return -ENOMEM; } SET_NETDEV_DEV(netdev, &intf->dev); tp = netdev_priv(netdev); tp->msg_enable = 0x7FFF; tp->udev = udev; tp->netdev = netdev; tp->intf = intf; r8152b_get_version(tp); ret = rtl_ops_init(tp); if (ret) goto out; mutex_init(&tp->control); INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t); netdev->netdev_ops = &rtl8152_netdev_ops; netdev->watchdog_timeo = RTL8152_TX_TIMEOUT; netdev->features |= NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6; netdev->ethtool_ops = &ops; netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE); tp->mii.dev = netdev; tp->mii.mdio_read = read_mii_word; tp->mii.mdio_write = write_mii_word; tp->mii.phy_id_mask = 0x3f; tp->mii.reg_num_mask = 0x1f; tp->mii.phy_id = R8152_PHY_ID; switch (udev->speed) { case USB_SPEED_SUPER: tp->coalesce = COALESCE_SUPER; break; case USB_SPEED_HIGH: tp->coalesce = COALESCE_HIGH; break; default: tp->coalesce = COALESCE_SLOW; break; } intf->needs_remote_wakeup = 1; tp->rtl_ops.init(tp); set_ethernet_addr(tp); usb_set_intfdata(intf, tp); netif_napi_add(netdev, &tp->napi, r8152_poll, RTL8152_NAPI_WEIGHT); ret = register_netdev(netdev); if (ret != 0) { netif_err(tp, probe, netdev, "couldn't register the device\n"); goto out1; } if (!rtl_can_wakeup(tp)) __rtl_set_wol(tp, 0); tp->saved_wolopts = __rtl_get_wol(tp); if (tp->saved_wolopts) device_set_wakeup_enable(&udev->dev, true); else device_set_wakeup_enable(&udev->dev, false); netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION); return 0; out1: netif_napi_del(&tp->napi); usb_set_intfdata(intf, NULL); out: free_netdev(netdev); return ret; } static void rtl8152_disconnect(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); if (tp) { struct usb_device *udev = tp->udev; if (udev->state == USB_STATE_NOTATTACHED) set_bit(RTL8152_UNPLUG, &tp->flags); netif_napi_del(&tp->napi); unregister_netdev(tp->netdev); tp->rtl_ops.unload(tp); free_netdev(tp->netdev); } } #define REALTEK_USB_DEVICE(vend, prod) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ USB_DEVICE_ID_MATCH_INT_CLASS, \ .idVendor = (vend), \ .idProduct = (prod), \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC \ }, \ { \ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | \ USB_DEVICE_ID_MATCH_DEVICE, \ .idVendor = (vend), \ .idProduct = (prod), \ .bInterfaceClass = USB_CLASS_COMM, \ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \ .bInterfaceProtocol = USB_CDC_PROTO_NONE /* table of devices that work with this driver */ static struct usb_device_id rtl8152_table[] = { {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)}, {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)}, {REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)}, {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)}, {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x304f)}, {REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA, 0x09ff)}, {} }; MODULE_DEVICE_TABLE(usb, rtl8152_table); static struct usb_driver rtl8152_driver = { .name = MODULENAME, .id_table = rtl8152_table, .probe = rtl8152_probe, .disconnect = rtl8152_disconnect, .suspend = rtl8152_suspend, .resume = rtl8152_resume, .reset_resume = rtl8152_reset_resume, .pre_reset = rtl8152_pre_reset, .post_reset = rtl8152_post_reset, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, }; module_usb_driver(rtl8152_driver); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 15:24 ` Mark Lord @ 2016-11-25 6:11 ` Hayes Wang 2016-11-25 12:36 ` Mark Lord 0 siblings, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-25 6:11 UTC (permalink / raw) To: Mark Lord, netdev; +Cc: nic_swsd, linux-kernel, linux-usb Mark Lord [mailto:mlord@pobox.com] > Sent: Thursday, November 24, 2016 11:25 PM [...] > x86 has near fully-coherent memory, so it is the "easy" platform > to get things working on. But Linux supports a very diverse number > of platforms, with varying degrees of cache/memory coherency, > and it can be tricky for things to work correctly on all of them. However, I have test iperf on raspberry pi v1 which you suggest for more than one day. I still couldn't reproduce your issue. > If you are testing with the driver as currently in 4.4.34, > then you won't even notice when things are screwing up, > because the driver just silently drops packets. > Or it passes them on without noticing that they have bad data. I only drop the packet silently when the rx descriptor outside the urb buffer. Then, I check the rx descriptor before checking the length of the packet. > Here (attached) is the instrumented driver I am using here now. > I suggest you use it or something similar when testing, > and not the stock driver. I would test it again with your driver. [...] > Also, unrelated, but inside r8152_submit_rx() there is this code: > > /* The rx would be stopped, so skip submitting */ > if (test_bit(RTL8152_UNPLUG, &tp->flags) || > !test_bit(WORK_ENABLE, &tp->flags) > || !netif_carrier_ok(tp->netdev)) > return 0; > > If that "return 0" statement is ever executed, doesn't it result > in the loss/leak of a buffer? They would be found back by calling rtl_start_rx(), when the rx is restarted. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 6:11 ` Hayes Wang @ 2016-11-25 12:36 ` Mark Lord 0 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-25 12:36 UTC (permalink / raw) To: Hayes Wang, netdev; +Cc: nic_swsd, linux-kernel, linux-usb On 16-11-25 01:11 AM, Hayes Wang wrote: > Mark Lord [mailto:mlord@pobox.com] .. >> If that "return 0" statement is ever executed, doesn't it result >> in the loss/leak of a buffer? > > They would be found back by calling rtl_start_rx(), when the rx > is restarted. Good. I figured it was probably something like that, but wasn't entirely sure about the control flow around stop/restart there. Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 13:26 ` Hayes Wang 2016-11-24 15:24 ` Mark Lord @ 2016-11-24 16:21 ` David Miller 2016-11-24 16:43 ` Mark Lord 1 sibling, 1 reply; 80+ messages in thread From: David Miller @ 2016-11-24 16:21 UTC (permalink / raw) To: hayeswang; +Cc: mlord, netdev, nic_swsd, linux-kernel, linux-usb From: Hayes Wang <hayeswang@realtek.com> Date: Thu, 24 Nov 2016 13:26:55 +0000 > I don't think the garbage results from our driver or device. This is my impression with what has been presented so far as well. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 16:21 ` David Miller @ 2016-11-24 16:43 ` Mark Lord 2016-11-24 17:00 ` Mark Lord ` (3 more replies) 0 siblings, 4 replies; 80+ messages in thread From: Mark Lord @ 2016-11-24 16:43 UTC (permalink / raw) To: David Miller, hayeswang; +Cc: netdev, nic_swsd, linux-kernel, linux-usb On 16-11-24 11:21 AM, David Miller wrote: > From: Hayes Wang <hayeswang@realtek.com> > Date: Thu, 24 Nov 2016 13:26:55 +0000 > >> I don't think the garbage results from our driver or device. > This is my impression with what has been presented so far as well. It's not garbage. The latest run with the debug code I posted here earlier just spat out this below. Using coherent (guarded, non-cacheable) RX buffers, with mb() calls: [ 15.199157] r8152_check_rx_desc: rx_desc looks bad. [ 15.204270] r8152_rx_bottom: offset=0/3376 bad rx_desc [ 15.209584] r8152_dump_rx_desc: 3d435253 3034336d 202f3a30 47524154 2f3d5445 3034336d rx_len=21075 The bad data in this case is ASCII: "SRC=m3400:/ TARGET=/m340" This data is what is seen in /run/mount/utab, a file that is read/written over NFS on each boot. "SRC=m3400:/ TARGET=/m3400 ROOT=/ ATTRS=nolock,addr=192.168.8.1\n" But how does this ASCII data end up at offset zero of the rx buffer?? Not possible -- this isn't even stale data, because only an rx_desc could be at that offset in that buffer. So even if this were a platform memory coherency issue, one should still never see ASCII data at the beginning of an rx buffer. The driver NEVER writes anything to the rx buffers. Only the USB hardware ever does. And only the r8152 dongle/driver exhibits this issue. Other USB dongles do not. They *might* still have such issues, but because they use software checksums, the bad packets are caught/rejected. The r8152 driver, without the debug/error-checking additions, would have tried to interpret that ASCII data as an "rx_desc", and would have interpreted the "checksum bits" therein as "valid checksum", and the packet would have passed through the network stack, corrupting data. This driver worked without noticeable issues in 3.12.xx. It hasn't worked since. Because it now trusts the hardware checksums, without first checking to see if noise-on-the-line or something else has corrupted the data before receipt in the rx buffer. Based on the above capture, I suspect a bug in the chip itself, which perhaps is only manifest on a very slow CPU. Nobody here tests with slow CPUs, but they are very prevalent in embedded space. And very few people use USB network dongles nowadays either, as nearly all "computers" have built-in networking. The market for USB network dongles is mostly embedded space. Ergo. Cheers ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 16:43 ` Mark Lord @ 2016-11-24 17:00 ` Mark Lord 2016-11-24 17:13 ` David Miller 2016-11-24 17:11 ` David Miller ` (2 subsequent siblings) 3 siblings, 1 reply; 80+ messages in thread From: Mark Lord @ 2016-11-24 17:00 UTC (permalink / raw) To: David Miller, hayeswang; +Cc: netdev, nic_swsd, linux-kernel, linux-usb On 16-11-24 11:43 AM, Mark Lord wrote: .. > But how does this ASCII data end up at offset zero of the rx buffer?? > Not possible -- this isn't even stale data, because only an rx_desc could > be at that offset in that buffer. Answering my own question here, I suspect it ends up there as a result of overrunning the previous URB. So I have updated the test copy of the driver here now to check for that exact situation. It's running now, but could take hours or a day for the bug to occur again. It seems I am being overly helpful here. Perhaps I should have just stopped with the original regression report (driver works in 3.12.xx, fails on all newer kernels, as a result of enabling hardware checksums). Had I left it there, one might reasonably expect the onus to be on the driver developer to sort it out, with me providing retests of supplied patches as need be. But I've gone WAY BEYOND that, even questioning the sanity of the platform on which it is being used, just to avoid blaming a buggy USB dongle for some other issue. And this is leading people to suspect that I really think the platform is buggy. It isn't. It's been running for years, with a variety of USB hardware attached, and nary a problem. Except with this r8152 dongle on kernels > 3.12. So, yeah, the driver is fixed in our local tree, and has been for some time now. I just was hoping that perhaps others might be interested in it too, since the bug (whatever it is) corrupts data on the NFS server. Cheers ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 17:00 ` Mark Lord @ 2016-11-24 17:13 ` David Miller 0 siblings, 0 replies; 80+ messages in thread From: David Miller @ 2016-11-24 17:13 UTC (permalink / raw) To: mlord; +Cc: hayeswang, netdev, nic_swsd, linux-kernel, linux-usb From: Mark Lord <mlord@pobox.com> Date: Thu, 24 Nov 2016 12:00:15 -0500 > It seems I am being overly helpful here. Either you want to cry or you want to keep helping us track down this problem. It is your choice, and your choice alone. Please do not pretend otherwise, everyone else in this thread is operating with the best intentions and wants to see this through to a full analysis and a proper solution for the corruptions. Thank you. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 16:43 ` Mark Lord 2016-11-24 17:00 ` Mark Lord @ 2016-11-24 17:11 ` David Miller 2016-11-24 18:34 ` Mark Lord 2016-11-24 18:42 ` [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable Greg KH 2016-11-25 6:31 ` Hayes Wang 3 siblings, 1 reply; 80+ messages in thread From: David Miller @ 2016-11-24 17:11 UTC (permalink / raw) To: mlord; +Cc: hayeswang, netdev, nic_swsd, linux-kernel, linux-usb From: Mark Lord <mlord@pobox.com> Date: Thu, 24 Nov 2016 11:43:53 -0500 > So even if this were a platform memory coherency issue, one should > still never see ASCII data at the beginning of an rx buffer. I'm not so convinced, since this is the kind of random corruption one would expect to see when dealing with virtual caches that have aliasing or similar issues. Writes to address X that show up at address Y or not at all are precisely the signature of virtual cache aliasing problems. Is it a case of the chip writing to X but the cpu is still seeing stale data from a previous CPU store? For NFS the cpu is writing into the page cache, so we know that cpu side stores are where the ASCII text is coming from. Now is the r8152 buffer one that the USB host controller is DMA'ing into directly, or is it one that SWIOMMU or similar bounce buffering is copying into? In the latter case we are doing cpu stores into the area and the writes aren't coming from the device. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 17:11 ` David Miller @ 2016-11-24 18:34 ` Mark Lord 2016-11-24 18:49 ` Mark Lord ` (2 more replies) 0 siblings, 3 replies; 80+ messages in thread From: Mark Lord @ 2016-11-24 18:34 UTC (permalink / raw) To: David Miller; +Cc: hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-24 12:11 PM, David Miller wrote: > From: Mark Lord <mlord@pobox.com> > Date: Thu, 24 Nov 2016 11:43:53 -0500 > >> So even if this were a platform memory coherency issue, one should >> still never see ASCII data at the beginning of an rx buffer. > > I'm not so convinced, since this is the kind of random corruption one > would expect to see when dealing with virtual caches that have > aliasing or similar issues. > > Writes to address X that show up at address Y or not at all are > precisely the signature of virtual cache aliasing problems. > > Is it a case of the chip writing to X but the cpu is still seeing > stale data from a previous CPU store? > > For NFS the cpu is writing into the page cache, so we know that > cpu side stores are where the ASCII text is coming from. > > Now is the r8152 buffer one that the USB host controller is DMA'ing > into directly, or is it one that SWIOMMU or similar bounce buffering > is copying into? In the latter case we are doing cpu stores into > the area and the writes aren't coming from the device. >From tracing through the powerpc arch code, this is the buffer that is being directly DMA'd into. And the USB layer does an invalidate_dcache on that entire buffer before initiating the DMA (confirmed via printk). The driver itself NEVER writes anything to that buffer, and nobody else has a pointer to it other than the USB host controller, so there's nothing else that can write to it either. According to the driver writer, the chip should only ever write a fresh rx_desc struct at the beginning of a buffer, never ASCII data. So how does that buffer end up containing ASCII data from the NFS transfers? The only explanation I can see, is if the URB itself contains the data that we see in the URB buffer. Which is what one would expect. So for that to happen, the ethernet chip must be transferring that data. The thing that is special about the situation here, is that the processor is very slow (800Mhz 32-bit powerpc), and very busy elsewhere. So it can easily fall way behind in servicing the ethernet dongle, something that never happens with most modern faster machines. So perhaps this results in a FIFO overflow somewhere in the chip. We can boot/run this same machine from a USB memory stick, and nary a problem. Ditto for other types of ethernet dongles. But boot/run from that specific ethernet dongle, and we get regular random segfaults from corrupted page fetches over NFS. The only end-to-end data integrity available here is the rx checksum, when verified by software rather than trusting it to the chip/driver. One thought: bulk data streams are byte streams, not packets. Scheduling on the USB bus can break up larger transfers across multiple in-kernel buffers. A "real" URB buffer on USB2 is max 512 bytes. The driver is providing 16384-byte buffers, and assumes that data will never spill over from one such buffer to the next. Yet the observations here consistently show otherwise. Cheers -- Mark Lord ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 18:34 ` Mark Lord @ 2016-11-24 18:49 ` Mark Lord 2016-11-24 19:00 ` Greg KH 2016-11-25 0:27 ` Francois Romieu 2 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-24 18:49 UTC (permalink / raw) To: David Miller; +Cc: hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-24 01:34 PM, Mark Lord wrote: >From tracing through the powerpc arch code, this is the buffer that > is being directly DMA'd into. And the USB layer does an invalidate_dcache > on that entire buffer before initiating the DMA (confirmed via printk). Slight correction: the invalidate_dcache_range() is only done when using kmalloc'd buffers. I have converted the driver here to use usb_alloc_coherent() instead, so that now gets skipped since the memory is never cached. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 18:34 ` Mark Lord 2016-11-24 18:49 ` Mark Lord @ 2016-11-24 19:00 ` Greg KH 2016-11-24 19:10 ` Mark Lord 2016-11-25 0:27 ` Francois Romieu 2 siblings, 1 reply; 80+ messages in thread From: Greg KH @ 2016-11-24 19:00 UTC (permalink / raw) To: Mark Lord Cc: David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On Thu, Nov 24, 2016 at 01:34:08PM -0500, Mark Lord wrote: > One thought: bulk data streams are byte streams, not packets. > Scheduling on the USB bus can break up larger transfers across > multiple in-kernel buffers. A "real" URB buffer on USB2 is max 512 bytes. > The driver is providing 16384-byte buffers, and assumes that data will > never spill over from one such buffer to the next. > Yet the observations here consistently show otherwise. Wait, how do you know that data will not spill over? What is making that guarantee? Will the USB device send a "zero packet" in order to show that all of the "logical" data is now sent for this specific endpoint? Is there some sort of "framing" that the device does with the USB data so that the driver "knows" where the end of packet is? Check the zero-packet stuff for this device, that's tripped up many a USB driver writer over the years, myself included. thanks, greg k-h ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 19:00 ` Greg KH @ 2016-11-24 19:10 ` Mark Lord 2016-11-24 19:17 ` Greg KH 2016-11-25 9:52 ` Hayes Wang 0 siblings, 2 replies; 80+ messages in thread From: Mark Lord @ 2016-11-24 19:10 UTC (permalink / raw) To: Greg KH Cc: David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-24 02:00 PM, Greg KH wrote: > On Thu, Nov 24, 2016 at 01:34:08PM -0500, Mark Lord wrote: >> One thought: bulk data streams are byte streams, not packets. >> Scheduling on the USB bus can break up larger transfers across >> multiple in-kernel buffers. A "real" URB buffer on USB2 is max 512 bytes. >> The driver is providing 16384-byte buffers, and assumes that data will >> never spill over from one such buffer to the next. >> Yet the observations here consistently show otherwise. > > Wait, how do you know that data will not spill over? What is making > that guarantee? Will the USB device send a "zero packet" in order to > show that all of the "logical" data is now sent for this specific > endpoint? Is there some sort of "framing" that the device does with the > USB data so that the driver "knows" where the end of packet is? Exactly my point. > Check the zero-packet stuff for this device, that's tripped up many a > USB driver writer over the years, myself included. I haven't tripped over it myself, but only because we were careful to allow for such in the USB drivers I have worked on. The r8152 driver just assumes it never happens. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 19:10 ` Mark Lord @ 2016-11-24 19:17 ` Greg KH 2016-11-25 9:52 ` Hayes Wang 1 sibling, 0 replies; 80+ messages in thread From: Greg KH @ 2016-11-24 19:17 UTC (permalink / raw) To: Mark Lord Cc: David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On Thu, Nov 24, 2016 at 02:10:36PM -0500, Mark Lord wrote: > On 16-11-24 02:00 PM, Greg KH wrote: > > On Thu, Nov 24, 2016 at 01:34:08PM -0500, Mark Lord wrote: > >> One thought: bulk data streams are byte streams, not packets. > >> Scheduling on the USB bus can break up larger transfers across > >> multiple in-kernel buffers. A "real" URB buffer on USB2 is max 512 bytes. > >> The driver is providing 16384-byte buffers, and assumes that data will > >> never spill over from one such buffer to the next. > >> Yet the observations here consistently show otherwise. > > > > Wait, how do you know that data will not spill over? What is making > > that guarantee? Will the USB device send a "zero packet" in order to > > show that all of the "logical" data is now sent for this specific > > endpoint? Is there some sort of "framing" that the device does with the > > USB data so that the driver "knows" where the end of packet is? > > Exactly my point. > > > Check the zero-packet stuff for this device, that's tripped up many a > > USB driver writer over the years, myself included. > > I haven't tripped over it myself, but only because we were careful > to allow for such in the USB drivers I have worked on. > > The r8152 driver just assumes it never happens. Assumes what? That the host will always consume data faster than the device can create it? If so, that sounds like your real problem there... good luck! greg k-h ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 19:10 ` Mark Lord 2016-11-24 19:17 ` Greg KH @ 2016-11-25 9:52 ` Hayes Wang 2016-11-25 13:32 ` Mark Lord 1 sibling, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-25 9:52 UTC (permalink / raw) To: Mark Lord, Greg KH Cc: David Miller, netdev, nic_swsd, linux-kernel, linux-usb Mark Lord [mailto:mlord@pobox.com] > Sent: Friday, November 25, 2016 3:11 AM [...] > On 16-11-24 02:00 PM, Greg KH wrote: > > On Thu, Nov 24, 2016 at 01:34:08PM -0500, Mark Lord wrote: > >> One thought: bulk data streams are byte streams, not packets. > >> Scheduling on the USB bus can break up larger transfers across > >> multiple in-kernel buffers. A "real" URB buffer on USB2 is max 512 bytes. > >> The driver is providing 16384-byte buffers, and assumes that data will > >> never spill over from one such buffer to the next. > >> Yet the observations here consistently show otherwise. > > > > Wait, how do you know that data will not spill over? What is making > > that guarantee? Will the USB device send a "zero packet" in order to > > show that all of the "logical" data is now sent for this specific > > endpoint? Is there some sort of "framing" that the device does with the > > USB data so that the driver "knows" where the end of packet is? > > Exactly my point. > > > Check the zero-packet stuff for this device, that's tripped up many a > > USB driver writer over the years, myself included. > > I haven't tripped over it myself, but only because we were careful > to allow for such in the USB drivers I have worked on. > > The r8152 driver just assumes it never happens. What is the value of /sys/bus/usb/devices/.../power/control ? Could you make sure it is "on" and try again? Or you could call usb_disable_autosuspend() in probe(). Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 9:52 ` Hayes Wang @ 2016-11-25 13:32 ` Mark Lord 0 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-25 13:32 UTC (permalink / raw) To: Hayes Wang, Greg KH Cc: David Miller, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-25 04:52 AM, Hayes Wang wrote: .. > What is the value of /sys/bus/usb/devices/.../power/control ? That entry does not exist -- power control is completely disabled on this board. Good try, though -- USB power control still causes me trouble on PCs with mice and remote controls. But not here. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 18:34 ` Mark Lord 2016-11-24 18:49 ` Mark Lord 2016-11-24 19:00 ` Greg KH @ 2016-11-25 0:27 ` Francois Romieu 2016-11-25 3:49 ` Mark Lord 2 siblings, 1 reply; 80+ messages in thread From: Francois Romieu @ 2016-11-25 0:27 UTC (permalink / raw) To: Mark Lord Cc: David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb Mark Lord <mlord@pobox.com> : [...] > >From tracing through the powerpc arch code, this is the buffer that > is being directly DMA'd into. And the USB layer does an invalidate_dcache > on that entire buffer before initiating the DMA (confirmed via printk). > > The driver itself NEVER writes anything to that buffer, > and nobody else has a pointer to it other than the USB host controller, > so there's nothing else that can write to it either. > > According to the driver writer, the chip should only ever write a fresh > rx_desc struct at the beginning of a buffer, never ASCII data. > > So how does that buffer end up containing ASCII data from the NFS transfers? Through aliasing the URB was given a page that contains said (previously) received file. The ethernet chip/usb host does not write anything in it. There could be a device or a driver problem but it may not be the real problem. So far the analysis focused on "how was this corrupted content written into this receive buffer page ?". If I read David correctly (?) the "nobody else has a pointer to it other than the USB host controller" point may be replaced with "the pointer to it aliases some already used page". -- Ueimor ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 0:27 ` Francois Romieu @ 2016-11-25 3:49 ` Mark Lord 2016-11-25 9:53 ` Greg KH 0 siblings, 1 reply; 80+ messages in thread From: Mark Lord @ 2016-11-25 3:49 UTC (permalink / raw) To: Francois Romieu Cc: David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-24 07:27 PM, Francois Romieu wrote: > > Through aliasing the URB was given a page that contains said (previously) > received file. The ethernet chip/usb host does not write anything in it. I don't see how that could be possible. Please elaborate. The URB buffers are statically allocated by the driver at probe time, ten of them in all, allocated with usb_alloc_coherent() in the copy of the driver I am testing with. There is no possibility for them to be used for anything other than USB receive buffers, for this driver only. Nothing in the driver or kernel ever writes to those buffers after initial allocation, and only the driver and USB host controller ever have pointers to the buffers. -- Mark Lord ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 3:49 ` Mark Lord @ 2016-11-25 9:53 ` Greg KH 2016-11-25 12:34 ` Mark Lord 2016-11-25 12:49 ` Mark Lord 0 siblings, 2 replies; 80+ messages in thread From: Greg KH @ 2016-11-25 9:53 UTC (permalink / raw) To: Mark Lord Cc: Francois Romieu, David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On Thu, Nov 24, 2016 at 10:49:33PM -0500, Mark Lord wrote: > There is no possibility for them to be used for anything other than > USB receive buffers, for this driver only. Nothing in the driver > or kernel ever writes to those buffers after initial allocation, > and only the driver and USB host controller ever have pointers to the buffers. You really are going to have to break out that USB monitor to verify that this is the data coming across the wire. Note, there are "cheap" USB monitors that can be quite handy and that work on Linux: http://www.totalphase.com/products/beagle-usb12/ Or most high-end scopes have a USB mode that you can use to catch stuff like this (but they are usually harder to use/trigger and only store a very limited buffer). good luck! greg k-h ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 9:53 ` Greg KH @ 2016-11-25 12:34 ` Mark Lord 2016-11-25 12:41 ` Mark Lord 2016-11-25 12:49 ` Mark Lord 1 sibling, 1 reply; 80+ messages in thread From: Mark Lord @ 2016-11-25 12:34 UTC (permalink / raw) To: Greg KH Cc: Francois Romieu, David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-25 04:53 AM, Greg KH wrote: > Note, there are "cheap" USB monitors that can be quite handy and that work on Linux: > http://www.totalphase.com/products/beagle-usb12/ USD$455/each in quantity, vs. USD$8 for the USB ethernet dongle. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 12:34 ` Mark Lord @ 2016-11-25 12:41 ` Mark Lord 2016-11-25 14:22 ` Greg KH 0 siblings, 1 reply; 80+ messages in thread From: Mark Lord @ 2016-11-25 12:41 UTC (permalink / raw) To: Greg KH Cc: Francois Romieu, David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-25 07:34 AM, Mark Lord wrote: > On 16-11-25 04:53 AM, Greg KH wrote: >> Note, there are "cheap" USB monitors that can be quite handy and that work on Linux: >> http://www.totalphase.com/products/beagle-usb12/ > > USD$455/each in quantity, vs. USD$8 for the USB ethernet dongle. Oh, wrong model. That one doesn't do USB2. The USB2 version is a mere USD$1300 in quantity. Seems like rather a lot of money just to report a bug in a USB driver. Perhaps the Linux Foundation might purchase one and loan it for this task? ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 12:41 ` Mark Lord @ 2016-11-25 14:22 ` Greg KH 2016-11-25 14:35 ` Mark Lord 0 siblings, 1 reply; 80+ messages in thread From: Greg KH @ 2016-11-25 14:22 UTC (permalink / raw) To: Mark Lord Cc: Francois Romieu, David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On Fri, Nov 25, 2016 at 07:41:42AM -0500, Mark Lord wrote: > On 16-11-25 07:34 AM, Mark Lord wrote: > > On 16-11-25 04:53 AM, Greg KH wrote: > >> Note, there are "cheap" USB monitors that can be quite handy and that work on Linux: > >> http://www.totalphase.com/products/beagle-usb12/ > > > > USD$455/each in quantity, vs. USD$8 for the USB ethernet dongle. > > Oh, wrong model. That one doesn't do USB2. > The USB2 version is a mere USD$1300 in quantity. > > Seems like rather a lot of money just to report a bug in a USB driver. > Perhaps the Linux Foundation might purchase one and loan it for this task? You already have access to a USB analyzer you said, why would I try to buy one and ship it around the world instead? Makes no sense... greg k-h ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 14:22 ` Greg KH @ 2016-11-25 14:35 ` Mark Lord 0 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-25 14:35 UTC (permalink / raw) To: Greg KH Cc: Francois Romieu, David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-25 09:22 AM, Greg KH wrote: > On Fri, Nov 25, 2016 at 07:41:42AM -0500, Mark Lord wrote: >> On 16-11-25 07:34 AM, Mark Lord wrote: >>> On 16-11-25 04:53 AM, Greg KH wrote: >>>> Note, there are "cheap" USB monitors that can be quite handy and that work on Linux: >>>> http://www.totalphase.com/products/beagle-usb12/ >>> >>> USD$455/each in quantity, vs. USD$8 for the USB ethernet dongle. >> >> Oh, wrong model. That one doesn't do USB2. >> The USB2 version is a mere USD$1300 in quantity. >> >> Seems like rather a lot of money just to report a bug in a USB driver. >> Perhaps the Linux Foundation might purchase one and loan it for this task? > > You already have access to a USB analyzer you said, why would I try to > buy one and ship it around the world instead? Makes no sense... No, the company where I am consulting has a paperweight called a "USB analyzer". It doesn't work with Linux machines. You are the one who suggested purchase of a working Linux compatible unit, so I was just following up to see if you were serious about that. No worries. I'll see if the paperweight can be converted into something useful next week. Cheers ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 9:53 ` Greg KH 2016-11-25 12:34 ` Mark Lord @ 2016-11-25 12:49 ` Mark Lord 2016-11-25 14:24 ` Greg KH 2016-11-25 16:58 ` David Miller 1 sibling, 2 replies; 80+ messages in thread From: Mark Lord @ 2016-11-25 12:49 UTC (permalink / raw) To: Greg KH Cc: Francois Romieu, David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-25 04:53 AM, Greg KH wrote: > On Thu, Nov 24, 2016 at 10:49:33PM -0500, Mark Lord wrote: >> There is no possibility for them to be used for anything other than >> USB receive buffers, for this driver only. Nothing in the driver >> or kernel ever writes to those buffers after initial allocation, >> and only the driver and USB host controller ever have pointers to the buffers. > > You really are going to have to break out that USB monitor to verify > that this is the data coming across the wire. Not sure why, because there really is no other way for the data to appear where it does at the beginning of that URB buffer. This does seem a rather unexpected burden to place upon someone reporting a regression in a USB network driver that corrupts user data. I have already spent about 50 hours looking at this issue, and everything now points firmly at some kind of FIFO overflow within the dongle itself. There is no evidence to the contrary. I am very happy to test any driver updates, or data collection mods provided by the author, to help the author find/fix the issue. One idea, might be to have the author try testing with the dongle connected through a USB1.1 hub, forcing it to slower speeds. This might make reproducing the issue (if indeed a FIFO overflow) easier, as the host transfers will then be slower than the ethernet wire speed. I have access to the hardware here next Tuesday. If we can scrounge up the USB analyzer, cables, and a suitable MS-Windows (ugh) machine of some kind, then I'll see if it can be programmed to somewhow capture the event. Probably just set it in continuous capture mode, and have the target system halt when it sees bad data at offset zero. This can take days to reproduce, so don't hold your breaths. Something useful to do in the meanwhile, is to then think about "what next" after the analyzer confirms the issue. -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 12:49 ` Mark Lord @ 2016-11-25 14:24 ` Greg KH 2016-11-25 16:58 ` David Miller 1 sibling, 0 replies; 80+ messages in thread From: Greg KH @ 2016-11-25 14:24 UTC (permalink / raw) To: Mark Lord Cc: Francois Romieu, David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On Fri, Nov 25, 2016 at 07:49:35AM -0500, Mark Lord wrote: > On 16-11-25 04:53 AM, Greg KH wrote: > > On Thu, Nov 24, 2016 at 10:49:33PM -0500, Mark Lord wrote: > >> There is no possibility for them to be used for anything other than > >> USB receive buffers, for this driver only. Nothing in the driver > >> or kernel ever writes to those buffers after initial allocation, > >> and only the driver and USB host controller ever have pointers to the buffers. > > > > You really are going to have to break out that USB monitor to verify > > that this is the data coming across the wire. > > Not sure why, because there really is no other way for the data to > appear where it does at the beginning of that URB buffer. Broken USB host controller driver, or the device really is sending that data to the host. It's either one or the other, and the only way you can rule one of them out is to look at the data on the wire. best of luck, greg k-h ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 12:49 ` Mark Lord 2016-11-25 14:24 ` Greg KH @ 2016-11-25 16:58 ` David Miller 2016-11-30 11:58 ` Hayes Wang 1 sibling, 1 reply; 80+ messages in thread From: David Miller @ 2016-11-25 16:58 UTC (permalink / raw) To: mlord; +Cc: greg, romieu, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb From: Mark Lord <mlord@pobox.com> Date: Fri, 25 Nov 2016 07:49:35 -0500 > On 16-11-25 04:53 AM, Greg KH wrote: >> On Thu, Nov 24, 2016 at 10:49:33PM -0500, Mark Lord wrote: >>> There is no possibility for them to be used for anything other than >>> USB receive buffers, for this driver only. Nothing in the driver >>> or kernel ever writes to those buffers after initial allocation, >>> and only the driver and USB host controller ever have pointers to the buffers. >> >> You really are going to have to break out that USB monitor to verify >> that this is the data coming across the wire. > > Not sure why, because there really is no other way for the data to > appear where it does at the beginning of that URB buffer. > > This does seem a rather unexpected burden to place upon someone > reporting a regression in a USB network driver that corrupts user data. If you are the only person who can actively reproduce this, which seems to be the case right now, this is unfortunately the only way to reach a proper analysis and fix. ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 16:58 ` David Miller @ 2016-11-30 11:58 ` Hayes Wang 2016-12-09 3:23 ` Hayes Wang 2017-01-01 0:07 ` Ansis Atteka 0 siblings, 2 replies; 80+ messages in thread From: Hayes Wang @ 2016-11-30 11:58 UTC (permalink / raw) To: David Miller, mlord Cc: greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb Mark Lord <mlord@pobox.com> [...] > > Not sure why, because there really is no other way for the data to > > appear where it does at the beginning of that URB buffer. > > > > This does seem a rather unexpected burden to place upon someone > > reporting a regression in a USB network driver that corrupts user data. > > If you are the only person who can actively reproduce this, which > seems to be the case right now, this is unfortunately the only way to > reach a proper analysis and fix. I have tested it with iperf more than five days without any error. I would think if there is any other way to reproduce it. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-30 11:58 ` Hayes Wang @ 2016-12-09 3:23 ` Hayes Wang 2016-12-09 13:05 ` Mark Lord 2017-01-01 0:07 ` Ansis Atteka 1 sibling, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-12-09 3:23 UTC (permalink / raw) To: mlord; +Cc: netdev, nic_swsd, linux-kernel, linux-usb [-- Attachment #1: Type: text/plain, Size: 237 bytes --] Mark Lord <mlord@pobox.com> I find an issue about autosuspend, and it may result in the same problem with you. I don't sure if this is helpful to you, because it only occurs when enabling the autosuspend. Best Regards, Hayes [-- Attachment #2: r8152.c --] [-- Type: text/plain, Size: 107439 bytes --] /* * Copyright (c) 2014 Realtek Semiconductor Corp. All rights reserved. * * 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. * */ #include <linux/signal.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/usb.h> #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> #include <net/ip6_checksum.h> #include <uapi/linux/mdio.h> #include <linux/mdio.h> #include <linux/usb/cdc.h> /* Information for net-next */ #define NETNEXT_VERSION "08" /* Information for net */ #define NET_VERSION "2" #define DRIVER_VERSION "v1." NETNEXT_VERSION "." NET_VERSION #define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>" #define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters" #define MODULENAME "r8152" #define R8152_PHY_ID 32 #define PLA_IDR 0xc000 #define PLA_RCR 0xc010 #define PLA_RMS 0xc016 #define PLA_RXFIFO_CTRL0 0xc0a0 #define PLA_RXFIFO_CTRL1 0xc0a4 #define PLA_RXFIFO_CTRL2 0xc0a8 #define PLA_DMY_REG0 0xc0b0 #define PLA_FMC 0xc0b4 #define PLA_CFG_WOL 0xc0b6 #define PLA_TEREDO_CFG 0xc0bc #define PLA_MAR 0xcd00 #define PLA_BACKUP 0xd000 #define PAL_BDC_CR 0xd1a0 #define PLA_TEREDO_TIMER 0xd2cc #define PLA_REALWOW_TIMER 0xd2e8 #define PLA_LEDSEL 0xdd90 #define PLA_LED_FEATURE 0xdd92 #define PLA_PHYAR 0xde00 #define PLA_BOOT_CTRL 0xe004 #define PLA_GPHY_INTR_IMR 0xe022 #define PLA_EEE_CR 0xe040 #define PLA_EEEP_CR 0xe080 #define PLA_MAC_PWR_CTRL 0xe0c0 #define PLA_MAC_PWR_CTRL2 0xe0ca #define PLA_MAC_PWR_CTRL3 0xe0cc #define PLA_MAC_PWR_CTRL4 0xe0ce #define PLA_WDT6_CTRL 0xe428 #define PLA_TCR0 0xe610 #define PLA_TCR1 0xe612 #define PLA_MTPS 0xe615 #define PLA_TXFIFO_CTRL 0xe618 #define PLA_RSTTALLY 0xe800 #define PLA_CR 0xe813 #define PLA_CRWECR 0xe81c #define PLA_CONFIG12 0xe81e /* CONFIG1, CONFIG2 */ #define PLA_CONFIG34 0xe820 /* CONFIG3, CONFIG4 */ #define PLA_CONFIG5 0xe822 #define PLA_PHY_PWR 0xe84c #define PLA_OOB_CTRL 0xe84f #define PLA_CPCR 0xe854 #define PLA_MISC_0 0xe858 #define PLA_MISC_1 0xe85a #define PLA_OCP_GPHY_BASE 0xe86c #define PLA_TALLYCNT 0xe890 #define PLA_SFF_STS_7 0xe8de #define PLA_PHYSTATUS 0xe908 #define PLA_BP_BA 0xfc26 #define PLA_BP_0 0xfc28 #define PLA_BP_1 0xfc2a #define PLA_BP_2 0xfc2c #define PLA_BP_3 0xfc2e #define PLA_BP_4 0xfc30 #define PLA_BP_5 0xfc32 #define PLA_BP_6 0xfc34 #define PLA_BP_7 0xfc36 #define PLA_BP_EN 0xfc38 #define USB_USB2PHY 0xb41e #define USB_SSPHYLINK2 0xb428 #define USB_U2P3_CTRL 0xb460 #define USB_CSR_DUMMY1 0xb464 #define USB_CSR_DUMMY2 0xb466 #define USB_DEV_STAT 0xb808 #define USB_CONNECT_TIMER 0xcbf8 #define USB_BURST_SIZE 0xcfc0 #define USB_USB_CTRL 0xd406 #define USB_PHY_CTRL 0xd408 #define USB_TX_AGG 0xd40a #define USB_RX_BUF_TH 0xd40c #define USB_USB_TIMER 0xd428 #define USB_RX_EARLY_TIMEOUT 0xd42c #define USB_RX_EARLY_SIZE 0xd42e #define USB_PM_CTRL_STATUS 0xd432 #define USB_TX_DMA 0xd434 #define USB_TOLERANCE 0xd490 #define USB_LPM_CTRL 0xd41a #define USB_UPS_CTRL 0xd800 #define USB_MISC_0 0xd81a #define USB_POWER_CUT 0xd80a #define USB_AFE_CTRL2 0xd824 #define USB_WDT11_CTRL 0xe43c #define USB_BP_BA 0xfc26 #define USB_BP_0 0xfc28 #define USB_BP_1 0xfc2a #define USB_BP_2 0xfc2c #define USB_BP_3 0xfc2e #define USB_BP_4 0xfc30 #define USB_BP_5 0xfc32 #define USB_BP_6 0xfc34 #define USB_BP_7 0xfc36 #define USB_BP_EN 0xfc38 /* OCP Registers */ #define OCP_ALDPS_CONFIG 0x2010 #define OCP_EEE_CONFIG1 0x2080 #define OCP_EEE_CONFIG2 0x2092 #define OCP_EEE_CONFIG3 0x2094 #define OCP_BASE_MII 0xa400 #define OCP_EEE_AR 0xa41a #define OCP_EEE_DATA 0xa41c #define OCP_PHY_STATUS 0xa420 #define OCP_POWER_CFG 0xa430 #define OCP_EEE_CFG 0xa432 #define OCP_SRAM_ADDR 0xa436 #define OCP_SRAM_DATA 0xa438 #define OCP_DOWN_SPEED 0xa442 #define OCP_EEE_ABLE 0xa5c4 #define OCP_EEE_ADV 0xa5d0 #define OCP_EEE_LPABLE 0xa5d2 #define OCP_PHY_STATE 0xa708 /* nway state for 8153 */ #define OCP_ADC_CFG 0xbc06 /* SRAM Register */ #define SRAM_LPF_CFG 0x8012 #define SRAM_10M_AMP1 0x8080 #define SRAM_10M_AMP2 0x8082 #define SRAM_IMPEDANCE 0x8084 /* PLA_RCR */ #define RCR_AAP 0x00000001 #define RCR_APM 0x00000002 #define RCR_AM 0x00000004 #define RCR_AB 0x00000008 #define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB) /* PLA_RXFIFO_CTRL0 */ #define RXFIFO_THR1_NORMAL 0x00080002 #define RXFIFO_THR1_OOB 0x01800003 /* PLA_RXFIFO_CTRL1 */ #define RXFIFO_THR2_FULL 0x00000060 #define RXFIFO_THR2_HIGH 0x00000038 #define RXFIFO_THR2_OOB 0x0000004a #define RXFIFO_THR2_NORMAL 0x00a0 /* PLA_RXFIFO_CTRL2 */ #define RXFIFO_THR3_FULL 0x00000078 #define RXFIFO_THR3_HIGH 0x00000048 #define RXFIFO_THR3_OOB 0x0000005a #define RXFIFO_THR3_NORMAL 0x0110 /* PLA_TXFIFO_CTRL */ #define TXFIFO_THR_NORMAL 0x00400008 #define TXFIFO_THR_NORMAL2 0x01000008 /* PLA_DMY_REG0 */ #define ECM_ALDPS 0x0002 /* PLA_FMC */ #define FMC_FCR_MCU_EN 0x0001 /* PLA_EEEP_CR */ #define EEEP_CR_EEEP_TX 0x0002 /* PLA_WDT6_CTRL */ #define WDT6_SET_MODE 0x0010 /* PLA_TCR0 */ #define TCR0_TX_EMPTY 0x0800 #define TCR0_AUTO_FIFO 0x0080 /* PLA_TCR1 */ #define VERSION_MASK 0x7cf0 /* PLA_MTPS */ #define MTPS_JUMBO (12 * 1024 / 64) #define MTPS_DEFAULT (6 * 1024 / 64) /* PLA_RSTTALLY */ #define TALLY_RESET 0x0001 /* PLA_CR */ #define CR_RST 0x10 #define CR_RE 0x08 #define CR_TE 0x04 /* PLA_CRWECR */ #define CRWECR_NORAML 0x00 #define CRWECR_CONFIG 0xc0 /* PLA_OOB_CTRL */ #define NOW_IS_OOB 0x80 #define TXFIFO_EMPTY 0x20 #define RXFIFO_EMPTY 0x10 #define LINK_LIST_READY 0x02 #define DIS_MCU_CLROOB 0x01 #define FIFO_EMPTY (TXFIFO_EMPTY | RXFIFO_EMPTY) /* PLA_MISC_1 */ #define RXDY_GATED_EN 0x0008 /* PLA_SFF_STS_7 */ #define RE_INIT_LL 0x8000 #define MCU_BORW_EN 0x4000 /* PLA_CPCR */ #define CPCR_RX_VLAN 0x0040 /* PLA_CFG_WOL */ #define MAGIC_EN 0x0001 /* PLA_TEREDO_CFG */ #define TEREDO_SEL 0x8000 #define TEREDO_WAKE_MASK 0x7f00 #define TEREDO_RS_EVENT_MASK 0x00fe #define OOB_TEREDO_EN 0x0001 /* PAL_BDC_CR */ #define ALDPS_PROXY_MODE 0x0001 /* PLA_CONFIG34 */ #define LINK_ON_WAKE_EN 0x0010 #define LINK_OFF_WAKE_EN 0x0008 /* PLA_CONFIG5 */ #define BWF_EN 0x0040 #define MWF_EN 0x0020 #define UWF_EN 0x0010 #define LAN_WAKE_EN 0x0002 /* PLA_LED_FEATURE */ #define LED_MODE_MASK 0x0700 /* PLA_PHY_PWR */ #define TX_10M_IDLE_EN 0x0080 #define PFM_PWM_SWITCH 0x0040 /* PLA_MAC_PWR_CTRL */ #define D3_CLK_GATED_EN 0x00004000 #define MCU_CLK_RATIO 0x07010f07 #define MCU_CLK_RATIO_MASK 0x0f0f0f0f #define ALDPS_SPDWN_RATIO 0x0f87 /* PLA_MAC_PWR_CTRL2 */ #define EEE_SPDWN_RATIO 0x8007 /* PLA_MAC_PWR_CTRL3 */ #define PKT_AVAIL_SPDWN_EN 0x0100 #define SUSPEND_SPDWN_EN 0x0004 #define U1U2_SPDWN_EN 0x0002 #define L1_SPDWN_EN 0x0001 /* PLA_MAC_PWR_CTRL4 */ #define PWRSAVE_SPDWN_EN 0x1000 #define RXDV_SPDWN_EN 0x0800 #define TX10MIDLE_EN 0x0100 #define TP100_SPDWN_EN 0x0020 #define TP500_SPDWN_EN 0x0010 #define TP1000_SPDWN_EN 0x0008 #define EEE_SPDWN_EN 0x0001 /* PLA_GPHY_INTR_IMR */ #define GPHY_STS_MSK 0x0001 #define SPEED_DOWN_MSK 0x0002 #define SPDWN_RXDV_MSK 0x0004 #define SPDWN_LINKCHG_MSK 0x0008 /* PLA_PHYAR */ #define PHYAR_FLAG 0x80000000 /* PLA_EEE_CR */ #define EEE_RX_EN 0x0001 #define EEE_TX_EN 0x0002 /* PLA_BOOT_CTRL */ #define AUTOLOAD_DONE 0x0002 /* USB_USB2PHY */ #define USB2PHY_SUSPEND 0x0001 #define USB2PHY_L1 0x0002 /* USB_SSPHYLINK2 */ #define pwd_dn_scale_mask 0x3ffe #define pwd_dn_scale(x) ((x) << 1) /* USB_CSR_DUMMY1 */ #define DYNAMIC_BURST 0x0001 /* USB_CSR_DUMMY2 */ #define EP4_FULL_FC 0x0001 /* USB_DEV_STAT */ #define STAT_SPEED_MASK 0x0006 #define STAT_SPEED_HIGH 0x0000 #define STAT_SPEED_FULL 0x0002 /* USB_TX_AGG */ #define TX_AGG_MAX_THRESHOLD 0x03 /* USB_RX_BUF_TH */ #define RX_THR_SUPPER 0x0c350180 #define RX_THR_HIGH 0x7a120180 #define RX_THR_SLOW 0xffff0180 /* USB_TX_DMA */ #define TEST_MODE_DISABLE 0x00000001 #define TX_SIZE_ADJUST1 0x00000100 /* USB_UPS_CTRL */ #define POWER_CUT 0x0100 /* USB_PM_CTRL_STATUS */ #define RESUME_INDICATE 0x0001 /* USB_USB_CTRL */ #define RX_AGG_DISABLE 0x0010 #define RX_ZERO_EN 0x0080 /* USB_U2P3_CTRL */ #define U2P3_ENABLE 0x0001 /* USB_POWER_CUT */ #define PWR_EN 0x0001 #define PHASE2_EN 0x0008 /* USB_MISC_0 */ #define PCUT_STATUS 0x0001 /* USB_RX_EARLY_TIMEOUT */ #define COALESCE_SUPER 85000U #define COALESCE_HIGH 250000U #define COALESCE_SLOW 524280U /* USB_WDT11_CTRL */ #define TIMER11_EN 0x0001 /* USB_LPM_CTRL */ /* bit 4 ~ 5: fifo empty boundary */ #define FIFO_EMPTY_1FB 0x30 /* 0x1fb * 64 = 32448 bytes */ /* bit 2 ~ 3: LMP timer */ #define LPM_TIMER_MASK 0x0c #define LPM_TIMER_500MS 0x04 /* 500 ms */ #define LPM_TIMER_500US 0x0c /* 500 us */ #define ROK_EXIT_LPM 0x02 /* USB_AFE_CTRL2 */ #define SEN_VAL_MASK 0xf800 #define SEN_VAL_NORMAL 0xa000 #define SEL_RXIDLE 0x0100 /* OCP_ALDPS_CONFIG */ #define ENPWRSAVE 0x8000 #define ENPDNPS 0x0200 #define LINKENA 0x0100 #define DIS_SDSAVE 0x0010 /* OCP_PHY_STATUS */ #define PHY_STAT_MASK 0x0007 #define PHY_STAT_LAN_ON 3 #define PHY_STAT_PWRDN 5 /* OCP_POWER_CFG */ #define EEE_CLKDIV_EN 0x8000 #define EN_ALDPS 0x0004 #define EN_10M_PLLOFF 0x0001 /* OCP_EEE_CONFIG1 */ #define RG_TXLPI_MSK_HFDUP 0x8000 #define RG_MATCLR_EN 0x4000 #define EEE_10_CAP 0x2000 #define EEE_NWAY_EN 0x1000 #define TX_QUIET_EN 0x0200 #define RX_QUIET_EN 0x0100 #define sd_rise_time_mask 0x0070 #define sd_rise_time(x) (min(x, 7) << 4) /* bit 4 ~ 6 */ #define RG_RXLPI_MSK_HFDUP 0x0008 #define SDFALLTIME 0x0007 /* bit 0 ~ 2 */ /* OCP_EEE_CONFIG2 */ #define RG_LPIHYS_NUM 0x7000 /* bit 12 ~ 15 */ #define RG_DACQUIET_EN 0x0400 #define RG_LDVQUIET_EN 0x0200 #define RG_CKRSEL 0x0020 #define RG_EEEPRG_EN 0x0010 /* OCP_EEE_CONFIG3 */ #define fast_snr_mask 0xff80 #define fast_snr(x) (min(x, 0x1ff) << 7) /* bit 7 ~ 15 */ #define RG_LFS_SEL 0x0060 /* bit 6 ~ 5 */ #define MSK_PH 0x0006 /* bit 0 ~ 3 */ /* OCP_EEE_AR */ /* bit[15:14] function */ #define FUN_ADDR 0x0000 #define FUN_DATA 0x4000 /* bit[4:0] device addr */ /* OCP_EEE_CFG */ #define CTAP_SHORT_EN 0x0040 #define EEE10_EN 0x0010 /* OCP_DOWN_SPEED */ #define EN_10M_BGOFF 0x0080 /* OCP_PHY_STATE */ #define TXDIS_STATE 0x01 #define ABD_STATE 0x02 /* OCP_ADC_CFG */ #define CKADSEL_L 0x0100 #define ADC_EN 0x0080 #define EN_EMI_L 0x0040 /* SRAM_LPF_CFG */ #define LPF_AUTO_TUNE 0x8000 /* SRAM_10M_AMP1 */ #define GDAC_IB_UPALL 0x0008 /* SRAM_10M_AMP2 */ #define AMP_DN 0x0200 /* SRAM_IMPEDANCE */ #define RX_DRIVING_MASK 0x6000 enum rtl_register_content { _1000bps = 0x10, _100bps = 0x08, _10bps = 0x04, LINK_STATUS = 0x02, FULL_DUP = 0x01, }; #define RTL8152_MAX_TX 4 #define RTL8152_MAX_RX 10 #define INTBUFSIZE 2 #define CRC_SIZE 4 #define TX_ALIGN 4 #define RX_ALIGN 8 #define INTR_LINK 0x0004 #define RTL8152_REQT_READ 0xc0 #define RTL8152_REQT_WRITE 0x40 #define RTL8152_REQ_GET_REGS 0x05 #define RTL8152_REQ_SET_REGS 0x05 #define BYTE_EN_DWORD 0xff #define BYTE_EN_WORD 0x33 #define BYTE_EN_BYTE 0x11 #define BYTE_EN_SIX_BYTES 0x3f #define BYTE_EN_START_MASK 0x0f #define BYTE_EN_END_MASK 0xf0 #define RTL8153_MAX_PACKET 9216 /* 9K */ #define RTL8153_MAX_MTU (RTL8153_MAX_PACKET - VLAN_ETH_HLEN - VLAN_HLEN) #define RTL8152_RMS (VLAN_ETH_FRAME_LEN + VLAN_HLEN) #define RTL8153_RMS RTL8153_MAX_PACKET #define RTL8152_TX_TIMEOUT (5 * HZ) #define RTL8152_NAPI_WEIGHT 64 /* rtl8152 flags */ enum rtl8152_flags { RTL8152_UNPLUG = 0, RTL8152_SET_RX_MODE, WORK_ENABLE, RTL8152_LINK_CHG, SELECTIVE_SUSPEND, PHY_RESET, SCHEDULE_NAPI, }; /* Define these values to match your device */ #define VENDOR_ID_REALTEK 0x0bda #define VENDOR_ID_SAMSUNG 0x04e8 #define VENDOR_ID_LENOVO 0x17ef #define VENDOR_ID_NVIDIA 0x0955 #define MCU_TYPE_PLA 0x0100 #define MCU_TYPE_USB 0x0000 struct tally_counter { __le64 tx_packets; __le64 rx_packets; __le64 tx_errors; __le32 rx_errors; __le16 rx_missed; __le16 align_errors; __le32 tx_one_collision; __le32 tx_multi_collision; __le64 rx_unicast; __le64 rx_broadcast; __le32 rx_multicast; __le16 tx_aborted; __le16 tx_underrun; }; struct rx_desc { __le32 opts1; #define RX_LEN_MASK 0x7fff __le32 opts2; #define RD_UDP_CS BIT(23) #define RD_TCP_CS BIT(22) #define RD_IPV6_CS BIT(20) #define RD_IPV4_CS BIT(19) __le32 opts3; #define IPF BIT(23) /* IP checksum fail */ #define UDPF BIT(22) /* UDP checksum fail */ #define TCPF BIT(21) /* TCP checksum fail */ #define RX_VLAN_TAG BIT(16) __le32 opts4; __le32 opts5; __le32 opts6; }; struct tx_desc { __le32 opts1; #define TX_FS BIT(31) /* First segment of a packet */ #define TX_LS BIT(30) /* Final segment of a packet */ #define GTSENDV4 BIT(28) #define GTSENDV6 BIT(27) #define GTTCPHO_SHIFT 18 #define GTTCPHO_MAX 0x7fU #define TX_LEN_MAX 0x3ffffU __le32 opts2; #define UDP_CS BIT(31) /* Calculate UDP/IP checksum */ #define TCP_CS BIT(30) /* Calculate TCP/IP checksum */ #define IPV4_CS BIT(29) /* Calculate IPv4 checksum */ #define IPV6_CS BIT(28) /* Calculate IPv6 checksum */ #define MSS_SHIFT 17 #define MSS_MAX 0x7ffU #define TCPHO_SHIFT 17 #define TCPHO_MAX 0x7ffU #define TX_VLAN_TAG BIT(16) }; struct r8152; struct rx_agg { struct list_head list; struct urb *urb; struct r8152 *context; dma_addr_t transfer_dma; 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 napi_struct napi; struct usb_interface *intf; struct net_device *netdev; 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, rx_queue; spinlock_t rx_lock, tx_lock; struct delayed_work schedule; struct mii_if_info mii; struct mutex control; /* use for hw setting */ struct rtl_ops { void (*init)(struct r8152 *); int (*enable)(struct r8152 *); void (*disable)(struct r8152 *); void (*up)(struct r8152 *); void (*down)(struct r8152 *); void (*unload)(struct r8152 *); int (*eee_get)(struct r8152 *, struct ethtool_eee *); int (*eee_set)(struct r8152 *, struct ethtool_eee *); bool (*in_nway)(struct r8152 *); } rtl_ops; int intr_interval; u32 saved_wolopts; u32 msg_enable; u32 tx_qlen; u32 coalesce; u16 ocp_base; u8 *intr_buff; u8 version; }; enum rtl_version { RTL_VER_UNKNOWN = 0, RTL_VER_01, RTL_VER_02, RTL_VER_03, RTL_VER_04, RTL_VER_05, RTL_VER_06, RTL_VER_MAX }; enum tx_csum_stat { TX_CSUM_SUCCESS = 0, TX_CSUM_TSO, TX_CSUM_NONE }; /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). * The RTL chips use a 64 element hash table based on the Ethernet CRC. */ static const int multicast_filter_limit = 32; static unsigned int agg_buf_sz = 16384; #define RTL_LIMITED_TSO_SIZE (agg_buf_sz - sizeof(struct tx_desc) - \ VLAN_ETH_HLEN - VLAN_HLEN) static int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) { 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, 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) { int ret; void *tmp; tmp = kmemdup(data, size, GFP_KERNEL); if (!tmp) return -ENOMEM; ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0), RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, value, index, tmp, size, 500); kfree(tmp); return ret; } static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data, u16 type) { u16 limit = 64; int ret = 0; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; /* both size and indix must be 4 bytes align */ if ((size & 3) || !size || (index & 3) || !data) return -EPERM; if ((u32)index + (u32)size > 0xffff) return -EPERM; while (size) { if (size > limit) { ret = get_registers(tp, index, type, limit, data); if (ret < 0) break; index += limit; data += limit; size -= limit; } else { ret = get_registers(tp, index, type, size, data); if (ret < 0) break; index += size; data += size; size = 0; break; } } if (ret == -ENODEV) set_bit(RTL8152_UNPLUG, &tp->flags); return ret; } static int generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data, u16 type) { int ret; u16 byteen_start, byteen_end, byen; u16 limit = 512; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; /* both size and indix must be 4 bytes align */ if ((size & 3) || !size || (index & 3) || !data) return -EPERM; if ((u32)index + (u32)size > 0xffff) return -EPERM; byteen_start = byteen & BYTE_EN_START_MASK; byteen_end = byteen & BYTE_EN_END_MASK; byen = byteen_start | (byteen_start << 4); ret = set_registers(tp, index, type | byen, 4, data); if (ret < 0) goto error1; index += 4; data += 4; size -= 4; if (size) { size -= 4; while (size) { if (size > limit) { ret = set_registers(tp, index, type | BYTE_EN_DWORD, limit, data); if (ret < 0) goto error1; index += limit; data += limit; size -= limit; } else { ret = set_registers(tp, index, type | BYTE_EN_DWORD, size, data); if (ret < 0) goto error1; index += size; data += size; size = 0; break; } } byen = byteen_end | (byteen_end >> 4); ret = set_registers(tp, index, type | byen, 4, data); if (ret < 0) goto error1; } error1: if (ret == -ENODEV) set_bit(RTL8152_UNPLUG, &tp->flags); return ret; } static inline int pla_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) { return generic_ocp_read(tp, index, size, data, MCU_TYPE_PLA); } static inline int pla_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) { return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_PLA); } static inline int usb_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) { return generic_ocp_read(tp, index, size, data, MCU_TYPE_USB); } static inline int usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) { return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_USB); } static u32 ocp_read_dword(struct r8152 *tp, u16 type, u16 index) { __le32 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) { __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; generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); data = __le32_to_cpu(tmp); data >>= (shift * 8); data &= 0xffff; return (u16)data; } static void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data) { u32 mask = 0xffff; __le32 tmp; u16 byen = BYTE_EN_WORD; u8 shift = index & 2; data &= mask; if (index & 2) { byen <<= shift; mask <<= (shift * 8); data <<= (shift * 8); index &= ~3; } tmp = __cpu_to_le32(data); 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; generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); data = __le32_to_cpu(tmp); data >>= (shift * 8); data &= 0xff; return (u8)data; } static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data) { u32 mask = 0xff; __le32 tmp; u16 byen = BYTE_EN_BYTE; u8 shift = index & 3; data &= mask; if (index & 3) { byen <<= shift; mask <<= (shift * 8); data <<= (shift * 8); index &= ~3; } tmp = __cpu_to_le32(data); generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); } static u16 ocp_reg_read(struct r8152 *tp, u16 addr) { u16 ocp_base, ocp_index; ocp_base = addr & 0xf000; if (ocp_base != tp->ocp_base) { ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); tp->ocp_base = ocp_base; } ocp_index = (addr & 0x0fff) | 0xb000; return ocp_read_word(tp, MCU_TYPE_PLA, ocp_index); } static void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data) { u16 ocp_base, ocp_index; ocp_base = addr & 0xf000; if (ocp_base != tp->ocp_base) { ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); tp->ocp_base = ocp_base; } ocp_index = (addr & 0x0fff) | 0xb000; ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data); } static inline void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value) { ocp_reg_write(tp, OCP_BASE_MII + reg_addr * 2, value); } static inline int r8152_mdio_read(struct r8152 *tp, u32 reg_addr) { return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2); } static void sram_write(struct r8152 *tp, u16 addr, u16 data) { ocp_reg_write(tp, OCP_SRAM_ADDR, addr); ocp_reg_write(tp, OCP_SRAM_DATA, data); } static int read_mii_word(struct net_device *netdev, int phy_id, int reg) { struct r8152 *tp = netdev_priv(netdev); int ret; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; if (phy_id != R8152_PHY_ID) return -EINVAL; ret = r8152_mdio_read(tp, reg); return ret; } static void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val) { struct r8152 *tp = netdev_priv(netdev); if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; if (phy_id != R8152_PHY_ID) return; r8152_mdio_write(tp, reg, val); } static int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags); static int rtl8152_set_mac_address(struct net_device *netdev, void *p) { struct r8152 *tp = netdev_priv(netdev); struct sockaddr *addr = p; int ret = -EADDRNOTAVAIL; if (!is_valid_ether_addr(addr->sa_data)) goto out1; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out1; mutex_lock(&tp->control); memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, 8, addr->sa_data); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out1: return ret; } static int set_ethernet_addr(struct r8152 *tp) { struct net_device *dev = tp->netdev; struct sockaddr sa; int ret; if (tp->version == RTL_VER_01) ret = pla_ocp_read(tp, PLA_IDR, 8, sa.sa_data); else ret = pla_ocp_read(tp, PLA_BACKUP, 8, sa.sa_data); if (ret < 0) { netif_err(tp, probe, dev, "Get ether addr fail\n"); } else if (!is_valid_ether_addr(sa.sa_data)) { netif_err(tp, probe, dev, "Invalid ether addr %pM\n", sa.sa_data); eth_hw_addr_random(dev); ether_addr_copy(sa.sa_data, dev->dev_addr); ret = rtl8152_set_mac_address(dev, &sa); netif_info(tp, probe, dev, "Random ether addr %pM\n", sa.sa_data); } else { if (tp->version == RTL_VER_01) ether_addr_copy(dev->dev_addr, sa.sa_data); else ret = rtl8152_set_mac_address(dev, &sa); } return ret; } static void read_bulk_callback(struct urb *urb) { struct net_device *netdev; int status = urb->status; struct rx_agg *agg; struct r8152 *tp; 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; /* When link down, the driver would cancel all bulks. */ /* This avoid the re-submitting bulk */ if (!netif_carrier_ok(netdev)) return; usb_mark_last_busy(tp->udev); switch (status) { case 0: mb(); if (urb->actual_length < (sizeof(struct rx_desc) + ETH_ZLEN)) { printk(KERN_INFO "r8152_read_bulk_callback: actual_length (%u) too short\n", urb->actual_length); break; } spin_lock(&tp->rx_lock); list_add_tail(&agg->list, &tp->rx_done); spin_unlock(&tp->rx_lock); napi_schedule(&tp->napi); 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: if (net_ratelimit()) netdev_warn(netdev, "maybe reset is needed?\n"); break; default: if (net_ratelimit()) netdev_warn(netdev, "Rx status %d\n", status); break; } r8152_submit_rx(tp, agg, GFP_ATOMIC); } static void write_bulk_callback(struct urb *urb) { struct net_device_stats *stats; struct net_device *netdev; struct tx_agg *agg; struct r8152 *tp; int status = urb->status; agg = urb->context; if (!agg) return; tp = agg->context; if (!tp) return; netdev = tp->netdev; stats = &netdev->stats; if (status) { if (net_ratelimit()) netdev_warn(netdev, "Tx status %d\n", status); stats->tx_errors += agg->skb_num; } else { stats->tx_packets += agg->skb_num; stats->tx_bytes += agg->skb_len; } spin_lock(&tp->tx_lock); list_add_tail(&agg->list, &tp->tx_free); spin_unlock(&tp->tx_lock); usb_autopm_put_interface_async(tp->intf); if (!netif_carrier_ok(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)) napi_schedule(&tp->napi); } static void intr_callback(struct urb *urb) { struct r8152 *tp; __le16 *d; int status = urb->status; int res; tp = urb->context; if (!tp) return; if (!test_bit(WORK_ENABLE, &tp->flags)) return; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; switch (status) { case 0: /* success */ break; case -ECONNRESET: /* unlink */ case -ESHUTDOWN: netif_device_detach(tp->netdev); case -ENOENT: case -EPROTO: netif_info(tp, intr, tp->netdev, "Stop submitting intr, status %d\n", status); 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 (!netif_carrier_ok(tp->netdev)) { set_bit(RTL8152_LINK_CHG, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } } else { if (netif_carrier_ok(tp->netdev)) { set_bit(RTL8152_LINK_CHG, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } } resubmit: res = usb_submit_urb(urb, GFP_ATOMIC); if (res == -ENODEV) { set_bit(RTL8152_UNPLUG, &tp->flags); netif_device_detach(tp->netdev); } else if (res) { netif_err(tp, intr, tp->netdev, "can't resubmit intr, status %d\n", res); } } static inline void *rx_agg_align(void *data) { return (void *)ALIGN((uintptr_t)data, RX_ALIGN); } static inline void *tx_agg_align(void *data) { return (void *)ALIGN((uintptr_t)data, TX_ALIGN); } static void free_all_mem(struct r8152 *tp) { int i; for (i = 0; i < RTL8152_MAX_RX; i++) { usb_free_urb(tp->rx_info[i].urb); tp->rx_info[i].urb = NULL; usb_free_coherent(tp->udev, agg_buf_sz, tp->rx_info[i].buffer, tp->rx_info[i].transfer_dma); tp->rx_info[i].buffer = NULL; tp->rx_info[i].head = NULL; } for (i = 0; i < RTL8152_MAX_TX; i++) { usb_free_urb(tp->tx_info[i].urb); tp->tx_info[i].urb = NULL; kfree(tp->tx_info[i].buffer); tp->tx_info[i].buffer = NULL; tp->tx_info[i].head = NULL; } usb_free_urb(tp->intr_urb); tp->intr_urb = NULL; 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->tx_free); skb_queue_head_init(&tp->tx_queue); skb_queue_head_init(&tp->rx_queue); for (i = 0; i < RTL8152_MAX_RX; i++) { dma_addr_t transfer_dma = 0; buf = usb_alloc_coherent(tp->udev, agg_buf_sz, GFP_KERNEL, &transfer_dma); if (!buf) goto err1; if (buf != rx_agg_align(buf)) { kfree(buf); buf = kmalloc_node(agg_buf_sz + RX_ALIGN, 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].transfer_dma = transfer_dma; 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(agg_buf_sz, GFP_KERNEL, node); if (!buf) goto err1; if (buf != tx_agg_align(buf)) { kfree(buf); buf = kmalloc_node(agg_buf_sz + TX_ALIGN, 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; if (list_empty(&tp->tx_free)) return NULL; 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; } /* r8152_csum_workaround() * The hw limites the value the transport offset. When the offset is out of the * range, calculate the checksum by sw. */ static void r8152_csum_workaround(struct r8152 *tp, struct sk_buff *skb, struct sk_buff_head *list) { if (skb_shinfo(skb)->gso_size) { netdev_features_t features = tp->netdev->features; struct sk_buff_head seg_list; struct sk_buff *segs, *nskb; features &= ~(NETIF_F_SG | NETIF_F_IPV6_CSUM | NETIF_F_TSO6); segs = skb_gso_segment(skb, features); if (IS_ERR(segs) || !segs) goto drop; __skb_queue_head_init(&seg_list); do { nskb = segs; segs = segs->next; nskb->next = NULL; __skb_queue_tail(&seg_list, nskb); } while (segs); skb_queue_splice(&seg_list, list); dev_kfree_skb(skb); } else if (skb->ip_summed == CHECKSUM_PARTIAL) { if (skb_checksum_help(skb) < 0) goto drop; __skb_queue_head(list, skb); } else { struct net_device_stats *stats; drop: stats = &tp->netdev->stats; stats->tx_dropped++; dev_kfree_skb(skb); } } /* msdn_giant_send_check() * According to the document of microsoft, the TCP Pseudo Header excludes the * packet length for IPv6 TCP large packets. */ static int msdn_giant_send_check(struct sk_buff *skb) { const struct ipv6hdr *ipv6h; struct tcphdr *th; int ret; ret = skb_cow_head(skb, 0); if (ret) return ret; ipv6h = ipv6_hdr(skb); th = tcp_hdr(skb); th->check = 0; th->check = ~tcp_v6_check(0, &ipv6h->saddr, &ipv6h->daddr, 0); return ret; } static inline void rtl_tx_vlan_tag(struct tx_desc *desc, struct sk_buff *skb) { if (skb_vlan_tag_present(skb)) { u32 opts2; opts2 = TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb)); desc->opts2 |= cpu_to_le32(opts2); } } static inline void rtl_rx_vlan_tag(struct rx_desc *desc, struct sk_buff *skb) { u32 opts2 = le32_to_cpu(desc->opts2); if (opts2 & RX_VLAN_TAG) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff)); } static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, struct sk_buff *skb, u32 len, u32 transport_offset) { u32 mss = skb_shinfo(skb)->gso_size; u32 opts1, opts2 = 0; int ret = TX_CSUM_SUCCESS; WARN_ON_ONCE(len > TX_LEN_MAX); opts1 = len | TX_FS | TX_LS; if (mss) { if (transport_offset > GTTCPHO_MAX) { netif_warn(tp, tx_err, tp->netdev, "Invalid transport offset 0x%x for TSO\n", transport_offset); ret = TX_CSUM_TSO; goto unavailable; } switch (vlan_get_protocol(skb)) { case htons(ETH_P_IP): opts1 |= GTSENDV4; break; case htons(ETH_P_IPV6): if (msdn_giant_send_check(skb)) { ret = TX_CSUM_TSO; goto unavailable; } opts1 |= GTSENDV6; break; default: WARN_ON_ONCE(1); break; } opts1 |= transport_offset << GTTCPHO_SHIFT; opts2 |= min(mss, MSS_MAX) << MSS_SHIFT; } else if (skb->ip_summed == CHECKSUM_PARTIAL) { u8 ip_protocol; if (transport_offset > TCPHO_MAX) { netif_warn(tp, tx_err, tp->netdev, "Invalid transport offset 0x%x\n", transport_offset); ret = TX_CSUM_NONE; goto unavailable; } switch (vlan_get_protocol(skb)) { 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; else if (ip_protocol == IPPROTO_UDP) opts2 |= UDP_CS; else WARN_ON_ONCE(1); opts2 |= transport_offset << TCPHO_SHIFT; } desc->opts2 = cpu_to_le32(opts2); desc->opts1 = cpu_to_le32(opts1); unavailable: return ret; } static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg) { struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue; int remain, ret; u8 *tx_data; __skb_queue_head_init(&skb_head); spin_lock(&tx_queue->lock); skb_queue_splice_init(tx_queue, &skb_head); spin_unlock(&tx_queue->lock); tx_data = agg->head; agg->skb_num = 0; agg->skb_len = 0; remain = agg_buf_sz; while (remain >= ETH_ZLEN + sizeof(struct tx_desc)) { struct tx_desc *tx_desc; struct sk_buff *skb; unsigned int len; u32 offset; skb = __skb_dequeue(&skb_head); if (!skb) break; len = skb->len + sizeof(*tx_desc); if (len > remain) { __skb_queue_head(&skb_head, skb); break; } tx_data = tx_agg_align(tx_data); tx_desc = (struct tx_desc *)tx_data; offset = (u32)skb_transport_offset(skb); if (r8152_tx_csum(tp, tx_desc, skb, skb->len, offset)) { r8152_csum_workaround(tp, skb, &skb_head); continue; } rtl_tx_vlan_tag(tx_desc, skb); tx_data += sizeof(*tx_desc); len = skb->len; if (skb_copy_bits(skb, 0, tx_data, len) < 0) { struct net_device_stats *stats = &tp->netdev->stats; stats->tx_dropped++; dev_kfree_skb_any(skb); tx_data -= sizeof(*tx_desc); continue; } tx_data += len; agg->skb_len += len; agg->skb_num++; dev_kfree_skb_any(skb); remain = agg_buf_sz - (int)(tx_agg_align(tx_data) - agg->head); } if (!skb_queue_empty(&skb_head)) { spin_lock(&tx_queue->lock); skb_queue_splice(&skb_head, tx_queue); spin_unlock(&tx_queue->lock); } netif_tx_lock(tp->netdev); if (netif_queue_stopped(tp->netdev) && skb_queue_len(&tp->tx_queue) < tp->tx_qlen) netif_wake_queue(tp->netdev); netif_tx_unlock(tp->netdev); ret = usb_autopm_get_interface_async(tp->intf); if (ret < 0) goto out_tx_fill; 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); ret = usb_submit_urb(agg->urb, GFP_ATOMIC); if (ret < 0) usb_autopm_put_interface_async(tp->intf); out_tx_fill: return ret; } static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc) { u8 checksum = CHECKSUM_NONE; u32 opts2, opts3; if (tp->version == RTL_VER_01) goto return_result; opts2 = le32_to_cpu(rx_desc->opts2); opts3 = le32_to_cpu(rx_desc->opts3); if (opts2 & RD_IPV4_CS) { if (opts3 & IPF) checksum = CHECKSUM_NONE; else if ((opts2 & RD_UDP_CS) && (opts3 & UDPF)) checksum = CHECKSUM_NONE; else if ((opts2 & RD_TCP_CS) && (opts3 & TCPF)) checksum = CHECKSUM_NONE; else checksum = CHECKSUM_UNNECESSARY; } else if (opts2 & RD_IPV6_CS) { if ((opts2 & RD_UDP_CS) && !(opts3 & UDPF)) checksum = CHECKSUM_UNNECESSARY; else if ((opts2 & RD_TCP_CS) && !(opts3 & TCPF)) checksum = CHECKSUM_UNNECESSARY; } return_result: return checksum; } static void r8152_dump_rx_desc(struct rx_desc *rx_desc) { int rx_len = (le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK); printk(KERN_INFO "%s: %08x %08x %08x %08x %08x %08x rx_len=%d\n", __func__, le32_to_cpu(rx_desc->opts1), le32_to_cpu(rx_desc->opts2), le32_to_cpu(rx_desc->opts3), le32_to_cpu(rx_desc->opts4), le32_to_cpu(rx_desc->opts5), le32_to_cpu(rx_desc->opts6), rx_len); } static int r8152_check_rx_desc(struct r8152 *tp, struct rx_desc *rx_desc) { u32 opts1, opts2, opts3, opts4, opts5, opts6; int pkt_len; if (tp->version == RTL_VER_01) return 0; /* rx_desc looks okay */ opts1 = le32_to_cpu(rx_desc->opts1); opts2 = le32_to_cpu(rx_desc->opts2); opts3 = le32_to_cpu(rx_desc->opts3); opts4 = le32_to_cpu(rx_desc->opts4); opts5 = le32_to_cpu(rx_desc->opts5); opts6 = le32_to_cpu(rx_desc->opts6); pkt_len = (opts1 & RX_LEN_MASK) - CRC_SIZE; if ( !opts1 || ((opts1 & 0x0ff3f000) != 0x04400000 && (opts1 & 0xffff0000) != 0x00040000 && (opts1 & 0xffff0000) != 0x00080000) || (opts2 & ~(BIT(30)|RD_UDP_CS|RD_TCP_CS|RD_IPV6_CS|RD_IPV4_CS)) || ((opts2 & RD_IPV6_CS) && (opts2 & RD_IPV4_CS)) || ((opts3 & 0xffff0000) & ~(IPF|UDPF|TCPF|RX_VLAN_TAG)) // 0xff170000 || (opts4 & 0x060cfff8) != 0x06000000 || (opts5 | opts6) || pkt_len > (tp->netdev->mtu + 42) ){ printk(KERN_WARNING "%s: rx_desc looks bad.\n", __func__); return -EIO; /* rx_desc looks bad */ } return 0; /* rx_desc looks okay */ } static int rx_bottom(struct r8152 *tp, int budget) { unsigned long flags; struct list_head *cursor, *next, rx_queue; int ret = 0, work_done = 0; if (!skb_queue_empty(&tp->rx_queue)) { while (work_done < budget) { struct sk_buff *skb = __skb_dequeue(&tp->rx_queue); struct net_device *netdev = tp->netdev; struct net_device_stats *stats = &netdev->stats; unsigned int pkt_len; if (!skb) break; pkt_len = skb->len; napi_gro_receive(&tp->napi, skb); work_done++; stats->rx_packets++; stats->rx_bytes += pkt_len; } } if (list_empty(&tp->rx_done)) goto out1; INIT_LIST_HEAD(&rx_queue); spin_lock_irqsave(&tp->rx_lock, flags); list_splice_init(&tp->rx_done, &rx_queue); spin_unlock_irqrestore(&tp->rx_lock, flags); list_for_each_safe(cursor, next, &rx_queue) { struct rx_desc *rx_desc; struct rx_agg *agg; int len_used = 0; struct urb *urb; u8 *rx_data; list_del_init(cursor); agg = list_entry(cursor, struct rx_agg, list); urb = agg->urb; if (urb->actual_length < (sizeof(struct rx_desc) + ETH_ZLEN)) { printk(KERN_WARNING "r8152_rx_bottom: URB too small: actual_length=%u\n", urb->actual_length); goto submit; } rx_desc = agg->head; rx_data = agg->head; mb(); while (urb->actual_length > len_used) { struct net_device *netdev = tp->netdev; struct net_device_stats *stats = &netdev->stats; unsigned int pkt_len; struct sk_buff *skb; if ((len_used + sizeof(struct rx_desc)) > urb->actual_length) { printk(KERN_WARNING "r8152_rx_bottom: offset=%u/%u too small for rx_desc\n", len_used, urb->actual_length); break; } len_used += sizeof(struct rx_desc); if (r8152_check_rx_desc(tp, rx_desc)) { printk(KERN_WARNING "r8152_rx_bottom: offset=%u/%u bad rx_desc\n", len_used - sizeof(struct rx_desc), urb->actual_length); r8152_dump_rx_desc(rx_desc); } pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; if (pkt_len < ETH_ZLEN) { printk(KERN_WARNING "r8152_rx_bottom: offset=%u/%u pkt_len(%u) < ETH_ZLEN\n", len_used, urb->actual_length, pkt_len); r8152_dump_rx_desc(rx_desc); break; } len_used += pkt_len; if (urb->actual_length < len_used) { printk(KERN_WARNING "r8152_rx_bottom: offset=%u/%u pkt_len(%u) exceeds buffer\n", len_used - pkt_len, urb->actual_length, pkt_len); r8152_dump_rx_desc(rx_desc); break; } pkt_len -= CRC_SIZE; rx_data += sizeof(struct rx_desc); skb = netdev_alloc_skb_ip_align(netdev, pkt_len); if (!skb) { printk(KERN_WARNING "r8152_rx_bottom: netdev_alloc_skb_ip_align(%u) failed\n", pkt_len); stats->rx_dropped++; goto find_next_rx; } skb->ip_summed = r8152_rx_csum(tp, rx_desc); memcpy(skb->data, rx_data, pkt_len); skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, netdev); rtl_rx_vlan_tag(rx_desc, skb); if (work_done < budget) { napi_gro_receive(&tp->napi, skb); work_done++; stats->rx_packets++; stats->rx_bytes += pkt_len; } else { __skb_queue_tail(&tp->rx_queue, skb); } find_next_rx: rx_data = rx_agg_align(rx_data + pkt_len + CRC_SIZE); rx_desc = (struct rx_desc *)rx_data; len_used = (int)(rx_data - (u8 *)agg->head); } submit: if (!ret) { ret = r8152_submit_rx(tp, agg, GFP_ATOMIC); } else { urb->actual_length = 0; list_add_tail(&agg->list, next); } } if (!list_empty(&rx_queue)) { spin_lock_irqsave(&tp->rx_lock, flags); list_splice_tail(&rx_queue, &tp->rx_done); spin_unlock_irqrestore(&tp->rx_lock, flags); } out1: return work_done; } 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 *netdev = tp->netdev; if (res == -ENODEV) { set_bit(RTL8152_UNPLUG, &tp->flags); netif_device_detach(netdev); } else { struct net_device_stats *stats = &netdev->stats; unsigned long flags; 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(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; 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; clear_bit(SCHEDULE_NAPI, &tp->flags); tx_bottom(tp); } static int r8152_poll(struct napi_struct *napi, int budget) { struct r8152 *tp = container_of(napi, struct r8152, napi); int work_done; work_done = rx_bottom(tp, budget); bottom_half(tp); if (work_done < budget) { napi_complete(napi); if (!list_empty(&tp->rx_done)) napi_schedule(napi); } return work_done; } static int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags) { int ret; /* The rx would be stopped, so skip submitting */ if (test_bit(RTL8152_UNPLUG, &tp->flags) || !test_bit(WORK_ENABLE, &tp->flags) || !netif_carrier_ok(tp->netdev)) return 0; /* FIXME: memory leak? */ usb_fill_bulk_urb(agg->urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1), agg->head, agg_buf_sz, (usb_complete_t)read_bulk_callback, agg); agg->urb->transfer_dma = agg->transfer_dma; agg->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; mb(); ret = usb_submit_urb(agg->urb, mem_flags); if (ret == -ENODEV) { set_bit(RTL8152_UNPLUG, &tp->flags); netif_device_detach(tp->netdev); } else if (ret) { struct urb *urb = agg->urb; unsigned long flags; urb->actual_length = 0; spin_lock_irqsave(&tp->rx_lock, flags); list_add_tail(&agg->list, &tp->rx_done); spin_unlock_irqrestore(&tp->rx_lock, flags); netif_err(tp, rx_err, tp->netdev, "Couldn't submit rx[%p], ret = %d\n", agg, ret); napi_schedule(&tp->napi); } return ret; } static void rtl_drop_queued_tx(struct r8152 *tp) { struct net_device_stats *stats = &tp->netdev->stats; struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue; struct sk_buff *skb; if (skb_queue_empty(tx_queue)) return; __skb_queue_head_init(&skb_head); spin_lock_bh(&tx_queue->lock); skb_queue_splice_init(tx_queue, &skb_head); spin_unlock_bh(&tx_queue->lock); while ((skb = __skb_dequeue(&skb_head))) { dev_kfree_skb(skb); stats->tx_dropped++; } } static void rtl8152_tx_timeout(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); netif_warn(tp, tx_err, netdev, "Tx timeout\n"); usb_queue_reset_device(tp->intf); } static void rtl8152_set_rx_mode(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); if (netif_carrier_ok(netdev)) { 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 mc_filter[2]; /* Multicast hash filter */ __le32 tmp[2]; u32 ocp_data; clear_bit(RTL8152_SET_RX_MODE, &tp->flags); netif_stop_queue(netdev); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_data &= ~RCR_ACPT_ALL; ocp_data |= RCR_AB | RCR_APM; if (netdev->flags & IFF_PROMISC) { /* Unconditionally log net taps. */ netif_notice(tp, link, netdev, "Promiscuous mode enabled\n"); ocp_data |= RCR_AM | RCR_AAP; mc_filter[1] = 0xffffffff; mc_filter[0] = 0xffffffff; } else if ((netdev_mc_count(netdev) > multicast_filter_limit) || (netdev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ ocp_data |= RCR_AM; mc_filter[1] = 0xffffffff; mc_filter[0] = 0xffffffff; } else { struct netdev_hw_addr *ha; mc_filter[1] = 0; mc_filter[0] = 0; netdev_for_each_mc_addr(ha, netdev) { int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); ocp_data |= RCR_AM; } } 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(tmp), tmp); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); netif_wake_queue(netdev); } static netdev_features_t rtl8152_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { u32 mss = skb_shinfo(skb)->gso_size; int max_offset = mss ? GTTCPHO_MAX : TCPHO_MAX; int offset = skb_transport_offset(skb); if ((mss || skb->ip_summed == CHECKSUM_PARTIAL) && offset > max_offset) features &= ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); else if ((skb->len + sizeof(struct tx_desc)) > agg_buf_sz) features &= ~NETIF_F_GSO_MASK; return features; } static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); skb_tx_timestamp(skb); skb_queue_tail(&tp->tx_queue, skb); if (!list_empty(&tp->tx_free)) { if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { set_bit(SCHEDULE_NAPI, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } else { usb_mark_last_busy(tp->udev); napi_schedule(&tp->napi); } } else if (skb_queue_len(&tp->tx_queue) > tp->tx_qlen) { netif_stop_queue(netdev); } return NETDEV_TX_OK; } static void r8152b_reset_packet_filter(struct r8152 *tp) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC); ocp_data &= ~FMC_FCR_MCU_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); ocp_data |= FMC_FCR_MCU_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); } static void rtl8152_nic_reset(struct r8152 *tp) { int i; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST); for (i = 0; i < 1000; i++) { if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST)) break; usleep_range(100, 400); } } static void set_tx_qlen(struct r8152 *tp) { struct net_device *netdev = tp->netdev; tp->tx_qlen = agg_buf_sz / (netdev->mtu + VLAN_ETH_HLEN + VLAN_HLEN + sizeof(struct tx_desc)); } static inline u8 rtl8152_get_speed(struct r8152 *tp) { return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS); } static void rtl_set_eee_plus(struct r8152 *tp) { u32 ocp_data; u8 speed; speed = rtl8152_get_speed(tp); if (speed & _10bps) { ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); 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_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); } } static void rxdy_gated_en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); if (enable) ocp_data |= RXDY_GATED_EN; else ocp_data &= ~RXDY_GATED_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); } static int rtl_start_rx(struct r8152 *tp) { int i, ret = 0; INIT_LIST_HEAD(&tp->rx_done); 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); if (ret) break; } if (ret && ++i < RTL8152_MAX_RX) { struct list_head rx_queue; unsigned long flags; INIT_LIST_HEAD(&rx_queue); do { struct rx_agg *agg = &tp->rx_info[i++]; struct urb *urb = agg->urb; urb->actual_length = 0; list_add_tail(&agg->list, &rx_queue); } while (i < RTL8152_MAX_RX); spin_lock_irqsave(&tp->rx_lock, flags); list_splice_tail(&rx_queue, &tp->rx_done); spin_unlock_irqrestore(&tp->rx_lock, flags); } return ret; } static int rtl_stop_rx(struct r8152 *tp) { int i; for (i = 0; i < RTL8152_MAX_RX; i++) usb_kill_urb(tp->rx_info[i].urb); while (!skb_queue_empty(&tp->rx_queue)) dev_kfree_skb(__skb_dequeue(&tp->rx_queue)); return 0; } static int rtl_enable(struct r8152 *tp) { u32 ocp_data; r8152b_reset_packet_filter(tp); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR); ocp_data |= CR_RE | CR_TE; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data); rxdy_gated_en(tp, false); return 0; } static int rtl8152_enable(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; set_tx_qlen(tp); rtl_set_eee_plus(tp); return rtl_enable(tp); } static void r8153_set_rx_early_timeout(struct r8152 *tp) { u32 ocp_data = tp->coalesce / 8; ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, ocp_data); } static void r8153_set_rx_early_size(struct r8152 *tp) { u32 mtu = tp->netdev->mtu; u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 4; ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data); } static int rtl8153_enable(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; usb_disable_lpm(tp->udev); set_tx_qlen(tp); rtl_set_eee_plus(tp); r8153_set_rx_early_timeout(tp); r8153_set_rx_early_size(tp); return rtl_enable(tp); } static void rtl_disable(struct r8152 *tp) { u32 ocp_data; int i; if (test_bit(RTL8152_UNPLUG, &tp->flags)) { rtl_drop_queued_tx(tp); return; } 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); rtl_drop_queued_tx(tp); for (i = 0; i < RTL8152_MAX_TX; i++) usb_kill_urb(tp->tx_info[i].urb); rxdy_gated_en(tp, true); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY) break; usleep_range(1000, 2000); } for (i = 0; i < 1000; i++) { if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY) break; usleep_range(1000, 2000); } rtl_stop_rx(tp); rtl8152_nic_reset(tp); } static void r8152_power_cut_en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); if (enable) ocp_data |= POWER_CUT; else ocp_data &= ~POWER_CUT; ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS); ocp_data &= ~RESUME_INDICATE; ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); } static void rtl_rx_vlan_en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); if (enable) ocp_data |= CPCR_RX_VLAN; else ocp_data &= ~CPCR_RX_VLAN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); } static int rtl8152_set_features(struct net_device *dev, netdev_features_t features) { netdev_features_t changed = features ^ dev->features; struct r8152 *tp = netdev_priv(dev); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); if (changed & NETIF_F_HW_VLAN_CTAG_RX) { if (features & NETIF_F_HW_VLAN_CTAG_RX) rtl_rx_vlan_en(tp, true); else rtl_rx_vlan_en(tp, false); } mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } #define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) static u32 __rtl_get_wol(struct r8152 *tp) { u32 ocp_data; u32 wolopts = 0; ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5); if (!(ocp_data & LAN_WAKE_EN)) return 0; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); if (ocp_data & LINK_ON_WAKE_EN) wolopts |= WAKE_PHY; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5); if (ocp_data & UWF_EN) wolopts |= WAKE_UCAST; if (ocp_data & BWF_EN) wolopts |= WAKE_BCAST; if (ocp_data & MWF_EN) wolopts |= WAKE_MCAST; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); if (ocp_data & MAGIC_EN) wolopts |= WAKE_MAGIC; return wolopts; } static void __rtl_set_wol(struct r8152 *tp, u32 wolopts) { u32 ocp_data; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); ocp_data &= ~LINK_ON_WAKE_EN; if (wolopts & WAKE_PHY) ocp_data |= LINK_ON_WAKE_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5); ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN | LAN_WAKE_EN); if (wolopts & WAKE_UCAST) ocp_data |= UWF_EN; if (wolopts & WAKE_BCAST) ocp_data |= BWF_EN; if (wolopts & WAKE_MCAST) ocp_data |= MWF_EN; if (wolopts & WAKE_ANY) ocp_data |= LAN_WAKE_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); ocp_data &= ~MAGIC_EN; if (wolopts & WAKE_MAGIC) ocp_data |= MAGIC_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data); if (wolopts & WAKE_ANY) device_set_wakeup_enable(&tp->udev->dev, true); else device_set_wakeup_enable(&tp->udev->dev, false); } static void r8153_u1u2en(struct r8152 *tp, bool enable) { u8 u1u2[8]; if (enable) memset(u1u2, 0xff, sizeof(u1u2)); else memset(u1u2, 0x00, sizeof(u1u2)); usb_ocp_write(tp, USB_TOLERANCE, BYTE_EN_SIX_BYTES, sizeof(u1u2), u1u2); } static void r8153_u2p3en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL); if (enable && tp->version != RTL_VER_03 && tp->version != RTL_VER_04) ocp_data |= U2P3_ENABLE; else ocp_data &= ~U2P3_ENABLE; ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data); } static void r8153_power_cut_en(struct r8152 *tp, bool enable) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_POWER_CUT); if (enable) ocp_data |= PWR_EN | PHASE2_EN; else ocp_data &= ~(PWR_EN | PHASE2_EN); ocp_write_word(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); ocp_data &= ~PCUT_STATUS; ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); } static bool rtl_can_wakeup(struct r8152 *tp) { struct usb_device *udev = tp->udev; return (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP); } static void rtl_runtime_suspend_enable(struct r8152 *tp, bool enable) { if (enable) { u32 ocp_data; r8153_u1u2en(tp, false); r8153_u2p3en(tp, false); __rtl_set_wol(tp, WAKE_ANY); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); ocp_data |= LINK_OFF_WAKE_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); } else { __rtl_set_wol(tp, tp->saved_wolopts); r8153_u2p3en(tp, true); r8153_u1u2en(tp, true); } } static void rtl_phy_reset(struct r8152 *tp) { u16 data; int i; clear_bit(PHY_RESET, &tp->flags); data = r8152_mdio_read(tp, MII_BMCR); /* don't reset again before the previous one complete */ if (data & BMCR_RESET) return; data |= BMCR_RESET; r8152_mdio_write(tp, MII_BMCR, data); for (i = 0; i < 50; i++) { msleep(20); if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0) break; } } static void r8153_teredo_off(struct r8152 *tp) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG); ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK | OOB_TEREDO_EN); ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE); ocp_write_word(tp, MCU_TYPE_PLA, PLA_REALWOW_TIMER, 0); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0); } static void r8152b_disable_aldps(struct r8152 *tp) { ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE); msleep(20); } static inline void r8152b_enable_aldps(struct r8152 *tp) { ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | LINKENA | DIS_SDSAVE); } static void rtl8152_disable(struct r8152 *tp) { r8152b_disable_aldps(tp); rtl_disable(tp); r8152b_enable_aldps(tp); } static void r8152b_hw_phy_cfg(struct r8152 *tp) { u16 data; data = r8152_mdio_read(tp, MII_BMCR); if (data & BMCR_PDOWN) { data &= ~BMCR_PDOWN; r8152_mdio_write(tp, MII_BMCR, data); } set_bit(PHY_RESET, &tp->flags); } static void r8152b_exit_oob(struct r8152 *tp) { 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); rxdy_gated_en(tp, true); r8153_teredo_off(tp); r8152b_hw_phy_cfg(tp); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, 0x00); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data &= ~MCU_BORW_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } rtl8152_nic_reset(tp); /* rx share fifo credit full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); if (tp->udev->speed == USB_SPEED_FULL || tp->udev->speed == USB_SPEED_LOW) { /* rx share fifo credit near full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_FULL); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_FULL); } else { /* rx share fifo credit near full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_HIGH); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_HIGH); } /* TX share fifo free credit full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL); ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD); ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH); ocp_write_dword(tp, MCU_TYPE_USB, USB_TX_DMA, TEST_MODE_DISABLE | TX_SIZE_ADJUST1); rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); ocp_data |= TCR0_AUTO_FIFO; ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); } static void r8152b_enter_oob(struct r8152 *tp) { u32 ocp_data; int i; ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_OOB); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_OOB); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_OOB); rtl_disable(tp); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); rtl_rx_vlan_en(tp, true); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); ocp_data |= ALDPS_PROXY_MODE; ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); rxdy_gated_en(tp, false); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_data |= RCR_APM | RCR_AM | RCR_AB; ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); } static void r8153_hw_phy_cfg(struct r8152 *tp) { u32 ocp_data; u16 data; if (tp->version == RTL_VER_03 || tp->version == RTL_VER_04 || tp->version == RTL_VER_05) ocp_reg_write(tp, OCP_ADC_CFG, CKADSEL_L | ADC_EN | EN_EMI_L); data = r8152_mdio_read(tp, MII_BMCR); if (data & BMCR_PDOWN) { data &= ~BMCR_PDOWN; r8152_mdio_write(tp, MII_BMCR, data); } if (tp->version == RTL_VER_03) { data = ocp_reg_read(tp, OCP_EEE_CFG); data &= ~CTAP_SHORT_EN; ocp_reg_write(tp, OCP_EEE_CFG, data); } data = ocp_reg_read(tp, OCP_POWER_CFG); data |= EEE_CLKDIV_EN; ocp_reg_write(tp, OCP_POWER_CFG, data); data = ocp_reg_read(tp, OCP_DOWN_SPEED); data |= EN_10M_BGOFF; ocp_reg_write(tp, OCP_DOWN_SPEED, data); data = ocp_reg_read(tp, OCP_POWER_CFG); data |= EN_10M_PLLOFF; ocp_reg_write(tp, OCP_POWER_CFG, data); sram_write(tp, SRAM_IMPEDANCE, 0x0b13); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); ocp_data |= PFM_PWM_SWITCH; ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); /* Enable LPF corner auto tune */ sram_write(tp, SRAM_LPF_CFG, 0xf70f); /* Adjust 10M Amplitude */ sram_write(tp, SRAM_10M_AMP1, 0x00af); sram_write(tp, SRAM_10M_AMP2, 0x0208); set_bit(PHY_RESET, &tp->flags); } static void r8153_first_init(struct r8152 *tp) { u32 ocp_data; int i; rxdy_gated_en(tp, true); r8153_teredo_off(tp); 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); r8153_hw_phy_cfg(tp); rtl8152_nic_reset(tp); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data &= ~MCU_BORW_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8153_RMS); ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); ocp_data |= TCR0_AUTO_FIFO; ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); rtl8152_nic_reset(tp); /* rx share fifo credit full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_NORMAL); ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_NORMAL); /* TX share fifo free credit full threshold */ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL2); /* rx aggregation */ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); } static void r8153_enter_oob(struct r8152 *tp) { u32 ocp_data; int i; ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); rtl_disable(tp); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; usleep_range(1000, 2000); } ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8153_RMS); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG); ocp_data &= ~TEREDO_WAKE_MASK; ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); rtl_rx_vlan_en(tp, true); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); ocp_data |= ALDPS_PROXY_MODE; ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); rxdy_gated_en(tp, false); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_data |= RCR_APM | RCR_AM | RCR_AB; ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); } static void r8153_disable_aldps(struct r8152 *tp) { u16 data; data = ocp_reg_read(tp, OCP_POWER_CFG); data &= ~EN_ALDPS; ocp_reg_write(tp, OCP_POWER_CFG, data); msleep(20); } static void r8153_enable_aldps(struct r8152 *tp) { u16 data; data = ocp_reg_read(tp, OCP_POWER_CFG); data |= EN_ALDPS; ocp_reg_write(tp, OCP_POWER_CFG, data); } static void rtl8153_disable(struct r8152 *tp) { r8153_disable_aldps(tp); rtl_disable(tp); r8153_enable_aldps(tp); usb_enable_lpm(tp->udev); } static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) { u16 bmcr, anar, gbcr; int ret = 0; cancel_delayed_work_sync(&tp->schedule); anar = r8152_mdio_read(tp, MII_ADVERTISE); anar &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL); if (tp->mii.supports_gmii) { gbcr = r8152_mdio_read(tp, MII_CTRL1000); gbcr &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); } else { gbcr = 0; } if (autoneg == AUTONEG_DISABLE) { if (speed == SPEED_10) { bmcr = 0; anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; } else if (speed == SPEED_100) { bmcr = BMCR_SPEED100; anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; } else if (speed == SPEED_1000 && tp->mii.supports_gmii) { bmcr = BMCR_SPEED1000; gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; } else { ret = -EINVAL; goto out; } if (duplex == DUPLEX_FULL) bmcr |= BMCR_FULLDPLX; } else { if (speed == SPEED_10) { if (duplex == DUPLEX_FULL) anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; else anar |= ADVERTISE_10HALF; } else if (speed == SPEED_100) { if (duplex == DUPLEX_FULL) { anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; } else { anar |= ADVERTISE_10HALF; anar |= ADVERTISE_100HALF; } } else if (speed == SPEED_1000 && tp->mii.supports_gmii) { if (duplex == DUPLEX_FULL) { anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; } else { anar |= ADVERTISE_10HALF; anar |= ADVERTISE_100HALF; gbcr |= ADVERTISE_1000HALF; } } else { ret = -EINVAL; goto out; } bmcr = BMCR_ANENABLE | BMCR_ANRESTART; } if (test_bit(PHY_RESET, &tp->flags)) bmcr |= BMCR_RESET; if (tp->mii.supports_gmii) r8152_mdio_write(tp, MII_CTRL1000, gbcr); r8152_mdio_write(tp, MII_ADVERTISE, anar); r8152_mdio_write(tp, MII_BMCR, bmcr); if (test_bit(PHY_RESET, &tp->flags)) { int i; clear_bit(PHY_RESET, &tp->flags); for (i = 0; i < 50; i++) { msleep(20); if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0) break; } } out: return ret; } static void rtl8152_up(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8152b_disable_aldps(tp); r8152b_exit_oob(tp); r8152b_enable_aldps(tp); } static void rtl8152_down(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) { rtl_drop_queued_tx(tp); return; } r8152_power_cut_en(tp, false); r8152b_disable_aldps(tp); r8152b_enter_oob(tp); r8152b_enable_aldps(tp); } static void rtl8153_up(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8153_u1u2en(tp, false); r8153_disable_aldps(tp); r8153_first_init(tp); r8153_enable_aldps(tp); r8153_u2p3en(tp, true); r8153_u1u2en(tp, true); usb_enable_lpm(tp->udev); } static void rtl8153_down(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) { rtl_drop_queued_tx(tp); return; } r8153_u1u2en(tp, false); r8153_u2p3en(tp, false); r8153_power_cut_en(tp, false); r8153_disable_aldps(tp); r8153_enter_oob(tp); r8153_enable_aldps(tp); } static bool rtl8152_in_nway(struct r8152 *tp) { u16 nway_state; ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, 0x2000); tp->ocp_base = 0x2000; ocp_write_byte(tp, MCU_TYPE_PLA, 0xb014, 0x4c); /* phy state */ nway_state = ocp_read_word(tp, MCU_TYPE_PLA, 0xb01a); /* bit 15: TXDIS_STATE, bit 14: ABD_STATE */ if (nway_state & 0xc000) return false; else return true; } static bool rtl8153_in_nway(struct r8152 *tp) { u16 phy_state = ocp_reg_read(tp, OCP_PHY_STATE) & 0xff; if (phy_state == TXDIS_STATE || phy_state == ABD_STATE) return false; else return true; } 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) { if (!netif_carrier_ok(netdev)) { tp->rtl_ops.enable(tp); set_bit(RTL8152_SET_RX_MODE, &tp->flags); napi_disable(&tp->napi); netif_carrier_on(netdev); rtl_start_rx(tp); napi_enable(&tp->napi); } } else { if (netif_carrier_ok(netdev)) { netif_carrier_off(netdev); napi_disable(&tp->napi); tp->rtl_ops.disable(tp); napi_enable(&tp->napi); } } } static void rtl_work_func_t(struct work_struct *work) { struct r8152 *tp = container_of(work, struct r8152, schedule.work); /* If the device is unplugged or !netif_running(), the workqueue * doesn't need to wake the device, and could return directly. */ if (test_bit(RTL8152_UNPLUG, &tp->flags) || !netif_running(tp->netdev)) return; if (usb_autopm_get_interface(tp->intf) < 0) return; if (!test_bit(WORK_ENABLE, &tp->flags)) goto out1; if (!mutex_trylock(&tp->control)) { schedule_delayed_work(&tp->schedule, 0); goto out1; } 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); /* don't schedule napi before linking */ if (test_bit(SCHEDULE_NAPI, &tp->flags) && netif_carrier_ok(tp->netdev)) { clear_bit(SCHEDULE_NAPI, &tp->flags); napi_schedule(&tp->napi); } if (test_bit(PHY_RESET, &tp->flags)) rtl_phy_reset(tp); mutex_unlock(&tp->control); out1: usb_autopm_put_interface(tp->intf); } static int rtl8152_open(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); int res = 0; res = alloc_all_mem(tp); if (res) goto out; netif_carrier_off(netdev); res = usb_autopm_get_interface(tp->intf); if (res < 0) { free_all_mem(tp); goto out; } mutex_lock(&tp->control); tp->rtl_ops.up(tp); rtl8152_set_speed(tp, AUTONEG_ENABLE, tp->mii.supports_gmii ? SPEED_1000 : SPEED_100, DUPLEX_FULL); netif_carrier_off(netdev); netif_start_queue(netdev); set_bit(WORK_ENABLE, &tp->flags); 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); free_all_mem(tp); } else { napi_enable(&tp->napi); } mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return res; } static int rtl8152_close(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); int res = 0; napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); netif_stop_queue(netdev); res = usb_autopm_get_interface(tp->intf); if (res < 0 || test_bit(RTL8152_UNPLUG, &tp->flags)) { rtl_drop_queued_tx(tp); rtl_stop_rx(tp); } else { mutex_lock(&tp->control); tp->rtl_ops.down(tp); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); } free_all_mem(tp); return res; } static inline void r8152_mmd_indirect(struct r8152 *tp, u16 dev, u16 reg) { ocp_reg_write(tp, OCP_EEE_AR, FUN_ADDR | dev); ocp_reg_write(tp, OCP_EEE_DATA, reg); ocp_reg_write(tp, OCP_EEE_AR, FUN_DATA | dev); } static u16 r8152_mmd_read(struct r8152 *tp, u16 dev, u16 reg) { u16 data; r8152_mmd_indirect(tp, dev, reg); data = ocp_reg_read(tp, OCP_EEE_DATA); ocp_reg_write(tp, OCP_EEE_AR, 0x0000); return data; } static void r8152_mmd_write(struct r8152 *tp, u16 dev, u16 reg, u16 data) { r8152_mmd_indirect(tp, dev, reg); ocp_reg_write(tp, OCP_EEE_DATA, data); ocp_reg_write(tp, OCP_EEE_AR, 0x0000); } static void r8152_eee_en(struct r8152 *tp, bool enable) { u16 config1, config2, config3; u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); config1 = ocp_reg_read(tp, OCP_EEE_CONFIG1) & ~sd_rise_time_mask; config2 = ocp_reg_read(tp, OCP_EEE_CONFIG2); config3 = ocp_reg_read(tp, OCP_EEE_CONFIG3) & ~fast_snr_mask; if (enable) { ocp_data |= EEE_RX_EN | EEE_TX_EN; config1 |= EEE_10_CAP | EEE_NWAY_EN | TX_QUIET_EN | RX_QUIET_EN; config1 |= sd_rise_time(1); config2 |= RG_DACQUIET_EN | RG_LDVQUIET_EN; config3 |= fast_snr(42); } else { ocp_data &= ~(EEE_RX_EN | EEE_TX_EN); config1 &= ~(EEE_10_CAP | EEE_NWAY_EN | TX_QUIET_EN | RX_QUIET_EN); config1 |= sd_rise_time(7); config2 &= ~(RG_DACQUIET_EN | RG_LDVQUIET_EN); config3 |= fast_snr(511); } ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data); ocp_reg_write(tp, OCP_EEE_CONFIG1, config1); ocp_reg_write(tp, OCP_EEE_CONFIG2, config2); ocp_reg_write(tp, OCP_EEE_CONFIG3, config3); } static void r8152b_enable_eee(struct r8152 *tp) { r8152_eee_en(tp, true); r8152_mmd_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, MDIO_EEE_100TX); } static void r8153_eee_en(struct r8152 *tp, bool enable) { u32 ocp_data; u16 config; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); config = ocp_reg_read(tp, OCP_EEE_CFG); if (enable) { ocp_data |= EEE_RX_EN | EEE_TX_EN; config |= EEE10_EN; } else { ocp_data &= ~(EEE_RX_EN | EEE_TX_EN); config &= ~EEE10_EN; } ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data); ocp_reg_write(tp, OCP_EEE_CFG, config); } static void r8153_enable_eee(struct r8152 *tp) { r8153_eee_en(tp, true); ocp_reg_write(tp, OCP_EEE_ADV, MDIO_EEE_1000T | MDIO_EEE_100TX); } static void r8152b_enable_fc(struct r8152 *tp) { u16 anar; anar = r8152_mdio_read(tp, MII_ADVERTISE); anar |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; r8152_mdio_write(tp, MII_ADVERTISE, anar); } static void rtl_tally_reset(struct r8152 *tp) { u32 ocp_data; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY); ocp_data |= TALLY_RESET; ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); } static void r8152b_init(struct r8152 *tp) { u32 ocp_data; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8152b_disable_aldps(tp); if (tp->version == RTL_VER_01) { ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); ocp_data &= ~LED_MODE_MASK; ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); } r8152_power_cut_en(tp, false); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH; ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL); ocp_data &= ~MCU_CLK_RATIO_MASK; ocp_data |= MCU_CLK_RATIO | D3_CLK_GATED_EN; ocp_write_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ocp_data); ocp_data = GPHY_STS_MSK | SPEED_DOWN_MSK | SPDWN_RXDV_MSK | SPDWN_LINKCHG_MSK; ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data); r8152b_enable_eee(tp); r8152b_enable_aldps(tp); r8152b_enable_fc(tp); rtl_tally_reset(tp); /* enable rx aggregation */ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); } static void r8153_init(struct r8152 *tp) { u32 ocp_data; int i; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8153_disable_aldps(tp); r8153_u1u2en(tp, false); for (i = 0; i < 500; i++) { if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) & AUTOLOAD_DONE) break; msleep(20); } for (i = 0; i < 500; i++) { ocp_data = ocp_reg_read(tp, OCP_PHY_STATUS) & PHY_STAT_MASK; if (ocp_data == PHY_STAT_LAN_ON || ocp_data == PHY_STAT_PWRDN) break; msleep(20); } usb_disable_lpm(tp->udev); r8153_u2p3en(tp, false); if (tp->version == RTL_VER_04) { ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_SSPHYLINK2); ocp_data &= ~pwd_dn_scale_mask; ocp_data |= pwd_dn_scale(96); ocp_write_word(tp, MCU_TYPE_USB, USB_SSPHYLINK2, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); } else if (tp->version == RTL_VER_05) { ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0); ocp_data &= ~ECM_ALDPS; ocp_write_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1); if (ocp_read_word(tp, MCU_TYPE_USB, USB_BURST_SIZE) == 0) ocp_data &= ~DYNAMIC_BURST; else ocp_data |= DYNAMIC_BURST; ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1, ocp_data); } else if (tp->version == RTL_VER_06) { ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1); if (ocp_read_word(tp, MCU_TYPE_USB, USB_BURST_SIZE) == 0) ocp_data &= ~DYNAMIC_BURST; else ocp_data |= DYNAMIC_BURST; ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1, ocp_data); } ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2); ocp_data |= EP4_FULL_FC; ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL); ocp_data &= ~TIMER11_EN; ocp_write_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); ocp_data &= ~LED_MODE_MASK; ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); ocp_data = FIFO_EMPTY_1FB | ROK_EXIT_LPM; if (tp->version == RTL_VER_04 && tp->udev->speed != USB_SPEED_SUPER) ocp_data |= LPM_TIMER_500MS; else ocp_data |= LPM_TIMER_500US; ocp_write_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2); ocp_data &= ~SEN_VAL_MASK; ocp_data |= SEN_VAL_NORMAL | SEL_RXIDLE; ocp_write_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2, ocp_data); ocp_write_word(tp, MCU_TYPE_USB, USB_CONNECT_TIMER, 0x0001); r8153_power_cut_en(tp, false); r8153_u1u2en(tp, true); ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ALDPS_SPDWN_RATIO); ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, EEE_SPDWN_RATIO); ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PKT_AVAIL_SPDWN_EN | SUSPEND_SPDWN_EN | U1U2_SPDWN_EN | L1_SPDWN_EN); ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, PWRSAVE_SPDWN_EN | RXDV_SPDWN_EN | TX10MIDLE_EN | TP100_SPDWN_EN | TP500_SPDWN_EN | TP1000_SPDWN_EN | EEE_SPDWN_EN); r8153_enable_eee(tp); r8153_enable_aldps(tp); r8152b_enable_fc(tp); rtl_tally_reset(tp); r8153_u2p3en(tp, true); } static int rtl8152_pre_reset(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); struct net_device *netdev; if (!tp) return 0; netdev = tp->netdev; if (!netif_running(netdev)) return 0; napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); if (netif_carrier_ok(netdev)) { netif_stop_queue(netdev); mutex_lock(&tp->control); tp->rtl_ops.disable(tp); mutex_unlock(&tp->control); } return 0; } static int rtl8152_post_reset(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); struct net_device *netdev; if (!tp) return 0; netdev = tp->netdev; if (!netif_running(netdev)) return 0; set_bit(WORK_ENABLE, &tp->flags); if (netif_carrier_ok(netdev)) { mutex_lock(&tp->control); tp->rtl_ops.enable(tp); rtl8152_set_rx_mode(netdev); mutex_unlock(&tp->control); netif_wake_queue(netdev); } napi_enable(&tp->napi); return 0; } static bool delay_autosuspend(struct r8152 *tp) { bool sw_linking = !!netif_carrier_ok(tp->netdev); bool hw_linking = !!(rtl8152_get_speed(tp) & LINK_STATUS); /* This means a linking change occurs and the driver doesn't detect it, * yet. If the driver has disabled tx/rx and hw is linking on, the * device wouldn't wake up by receiving any packet. */ if (work_busy(&tp->schedule.work) || sw_linking != hw_linking) return true; /* If the linking down is occurred by nway, the device may miss the * linking change event. And it wouldn't wake when linking on. */ if (!sw_linking && tp->rtl_ops.in_nway(tp)) return true; else return false; } static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) { struct r8152 *tp = usb_get_intfdata(intf); struct net_device *netdev = tp->netdev; int ret = 0; u32 rcr = 0; mutex_lock(&tp->control); if (PMSG_IS_AUTO(message)) { u32 ocp_data; if (netif_running(netdev) && delay_autosuspend(tp)) { ret = -EBUSY; goto out1; } rcr = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr & ~RCR_ACPT_ALL); rxdy_gated_en(tp, true); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); if (!(ocp_data & RXFIFO_EMPTY)) { rxdy_gated_en(tp, false); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr); ret = -EBUSY; goto out1; } set_bit(SELECTIVE_SUSPEND, &tp->flags); } else { netif_device_detach(netdev); } if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) { clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); napi_disable(&tp->napi); if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { rtl_stop_rx(tp); rtl_runtime_suspend_enable(tp, true); rxdy_gated_en(tp, false); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr); } else { cancel_delayed_work_sync(&tp->schedule); tp->rtl_ops.down(tp); } napi_enable(&tp->napi); } out1: mutex_unlock(&tp->control); return ret; } static int rtl8152_resume(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); mutex_lock(&tp->control); if (!test_bit(SELECTIVE_SUSPEND, &tp->flags)) { tp->rtl_ops.init(tp); netif_device_attach(tp->netdev); } if (netif_running(tp->netdev) && tp->netdev->flags & IFF_UP) { if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { rtl_runtime_suspend_enable(tp, false); clear_bit(SELECTIVE_SUSPEND, &tp->flags); napi_disable(&tp->napi); set_bit(WORK_ENABLE, &tp->flags); if (netif_carrier_ok(tp->netdev)) rtl_start_rx(tp); napi_enable(&tp->napi); } else { tp->rtl_ops.up(tp); rtl8152_set_speed(tp, AUTONEG_ENABLE, tp->mii.supports_gmii ? SPEED_1000 : SPEED_100, DUPLEX_FULL); netif_carrier_off(tp->netdev); set_bit(WORK_ENABLE, &tp->flags); } usb_submit_urb(tp->intr_urb, GFP_KERNEL); } else if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { if (tp->netdev->flags & IFF_UP) rtl_runtime_suspend_enable(tp, false); clear_bit(SELECTIVE_SUSPEND, &tp->flags); } mutex_unlock(&tp->control); return 0; } static int rtl8152_reset_resume(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); clear_bit(SELECTIVE_SUSPEND, &tp->flags); return rtl8152_resume(intf); } static void rtl8152_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct r8152 *tp = netdev_priv(dev); if (usb_autopm_get_interface(tp->intf) < 0) return; if (!rtl_can_wakeup(tp)) { wol->supported = 0; wol->wolopts = 0; } else { mutex_lock(&tp->control); wol->supported = WAKE_ANY; wol->wolopts = __rtl_get_wol(tp); mutex_unlock(&tp->control); } usb_autopm_put_interface(tp->intf); } static int rtl8152_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct r8152 *tp = netdev_priv(dev); int ret; if (!rtl_can_wakeup(tp)) return -EOPNOTSUPP; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out_set_wol; mutex_lock(&tp->control); __rtl_set_wol(tp, wol->wolopts); tp->saved_wolopts = wol->wolopts & WAKE_ANY; mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out_set_wol: return ret; } static u32 rtl8152_get_msglevel(struct net_device *dev) { struct r8152 *tp = netdev_priv(dev); return tp->msg_enable; } static void rtl8152_set_msglevel(struct net_device *dev, u32 value) { struct r8152 *tp = netdev_priv(dev); tp->msg_enable = value; } static void rtl8152_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { struct r8152 *tp = netdev_priv(netdev); strlcpy(info->driver, MODULENAME, sizeof(info->driver)); strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); usb_make_path(tp->udev, info->bus_info, sizeof(info->bus_info)); } static int rtl8152_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) { struct r8152 *tp = netdev_priv(netdev); int ret; if (!tp->mii.mdio_read) return -EOPNOTSUPP; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = mii_ethtool_gset(&tp->mii, cmd); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static int rtl8152_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct r8152 *tp = netdev_priv(dev); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static const char rtl8152_gstrings[][ETH_GSTRING_LEN] = { "tx_packets", "rx_packets", "tx_errors", "rx_errors", "rx_missed", "align_errors", "tx_single_collisions", "tx_multi_collisions", "rx_unicast", "rx_broadcast", "rx_multicast", "tx_aborted", "tx_underrun", }; static int rtl8152_get_sset_count(struct net_device *dev, int sset) { switch (sset) { case ETH_SS_STATS: return ARRAY_SIZE(rtl8152_gstrings); default: return -EOPNOTSUPP; } } static void rtl8152_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct r8152 *tp = netdev_priv(dev); struct tally_counter tally; if (usb_autopm_get_interface(tp->intf) < 0) return; generic_ocp_read(tp, PLA_TALLYCNT, sizeof(tally), &tally, MCU_TYPE_PLA); usb_autopm_put_interface(tp->intf); data[0] = le64_to_cpu(tally.tx_packets); data[1] = le64_to_cpu(tally.rx_packets); data[2] = le64_to_cpu(tally.tx_errors); data[3] = le32_to_cpu(tally.rx_errors); data[4] = le16_to_cpu(tally.rx_missed); data[5] = le16_to_cpu(tally.align_errors); data[6] = le32_to_cpu(tally.tx_one_collision); data[7] = le32_to_cpu(tally.tx_multi_collision); data[8] = le64_to_cpu(tally.rx_unicast); data[9] = le64_to_cpu(tally.rx_broadcast); data[10] = le32_to_cpu(tally.rx_multicast); data[11] = le16_to_cpu(tally.tx_aborted); data[12] = le16_to_cpu(tally.tx_underrun); } static void rtl8152_get_strings(struct net_device *dev, u32 stringset, u8 *data) { switch (stringset) { case ETH_SS_STATS: memcpy(data, *rtl8152_gstrings, sizeof(rtl8152_gstrings)); break; } } static int r8152_get_eee(struct r8152 *tp, struct ethtool_eee *eee) { u32 ocp_data, lp, adv, supported = 0; u16 val; val = r8152_mmd_read(tp, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); supported = mmd_eee_cap_to_ethtool_sup_t(val); val = r8152_mmd_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV); adv = mmd_eee_adv_to_ethtool_adv_t(val); val = r8152_mmd_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); lp = mmd_eee_adv_to_ethtool_adv_t(val); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); ocp_data &= EEE_RX_EN | EEE_TX_EN; eee->eee_enabled = !!ocp_data; eee->eee_active = !!(supported & adv & lp); eee->supported = supported; eee->advertised = adv; eee->lp_advertised = lp; return 0; } static int r8152_set_eee(struct r8152 *tp, struct ethtool_eee *eee) { u16 val = ethtool_adv_to_mmd_eee_adv_t(eee->advertised); r8152_eee_en(tp, eee->eee_enabled); if (!eee->eee_enabled) val = 0; r8152_mmd_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); return 0; } static int r8153_get_eee(struct r8152 *tp, struct ethtool_eee *eee) { u32 ocp_data, lp, adv, supported = 0; u16 val; val = ocp_reg_read(tp, OCP_EEE_ABLE); supported = mmd_eee_cap_to_ethtool_sup_t(val); val = ocp_reg_read(tp, OCP_EEE_ADV); adv = mmd_eee_adv_to_ethtool_adv_t(val); val = ocp_reg_read(tp, OCP_EEE_LPABLE); lp = mmd_eee_adv_to_ethtool_adv_t(val); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); ocp_data &= EEE_RX_EN | EEE_TX_EN; eee->eee_enabled = !!ocp_data; eee->eee_active = !!(supported & adv & lp); eee->supported = supported; eee->advertised = adv; eee->lp_advertised = lp; return 0; } static int r8153_set_eee(struct r8152 *tp, struct ethtool_eee *eee) { u16 val = ethtool_adv_to_mmd_eee_adv_t(eee->advertised); r8153_eee_en(tp, eee->eee_enabled); if (!eee->eee_enabled) val = 0; ocp_reg_write(tp, OCP_EEE_ADV, val); return 0; } static int rtl_ethtool_get_eee(struct net_device *net, struct ethtool_eee *edata) { struct r8152 *tp = netdev_priv(net); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = tp->rtl_ops.eee_get(tp, edata); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static int rtl_ethtool_set_eee(struct net_device *net, struct ethtool_eee *edata) { struct r8152 *tp = netdev_priv(net); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = tp->rtl_ops.eee_set(tp, edata); if (!ret) ret = mii_nway_restart(&tp->mii); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static int rtl8152_nway_reset(struct net_device *dev) { struct r8152 *tp = netdev_priv(dev); int ret; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) goto out; mutex_lock(&tp->control); ret = mii_nway_restart(&tp->mii); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); out: return ret; } static int rtl8152_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce) { struct r8152 *tp = netdev_priv(netdev); switch (tp->version) { case RTL_VER_01: case RTL_VER_02: return -EOPNOTSUPP; default: break; } coalesce->rx_coalesce_usecs = tp->coalesce; return 0; } static int rtl8152_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce) { struct r8152 *tp = netdev_priv(netdev); int ret; switch (tp->version) { case RTL_VER_01: case RTL_VER_02: return -EOPNOTSUPP; default: break; } if (coalesce->rx_coalesce_usecs > COALESCE_SLOW) return -EINVAL; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) return ret; mutex_lock(&tp->control); if (tp->coalesce != coalesce->rx_coalesce_usecs) { tp->coalesce = coalesce->rx_coalesce_usecs; if (netif_running(tp->netdev) && netif_carrier_ok(netdev)) r8153_set_rx_early_timeout(tp); } mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); return ret; } static struct ethtool_ops ops = { .get_drvinfo = rtl8152_get_drvinfo, .get_settings = rtl8152_get_settings, .set_settings = rtl8152_set_settings, .get_link = ethtool_op_get_link, .nway_reset = rtl8152_nway_reset, .get_msglevel = rtl8152_get_msglevel, .set_msglevel = rtl8152_set_msglevel, .get_wol = rtl8152_get_wol, .set_wol = rtl8152_set_wol, .get_strings = rtl8152_get_strings, .get_sset_count = rtl8152_get_sset_count, .get_ethtool_stats = rtl8152_get_ethtool_stats, .get_coalesce = rtl8152_get_coalesce, .set_coalesce = rtl8152_set_coalesce, .get_eee = rtl_ethtool_get_eee, .set_eee = rtl_ethtool_set_eee, }; static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) { struct r8152 *tp = netdev_priv(netdev); struct mii_ioctl_data *data = if_mii(rq); int res; if (test_bit(RTL8152_UNPLUG, &tp->flags)) return -ENODEV; res = usb_autopm_get_interface(tp->intf); if (res < 0) goto out; switch (cmd) { case SIOCGMIIPHY: data->phy_id = R8152_PHY_ID; /* Internal PHY */ break; case SIOCGMIIREG: mutex_lock(&tp->control); data->val_out = r8152_mdio_read(tp, data->reg_num); mutex_unlock(&tp->control); break; case SIOCSMIIREG: if (!capable(CAP_NET_ADMIN)) { res = -EPERM; break; } mutex_lock(&tp->control); r8152_mdio_write(tp, data->reg_num, data->val_in); mutex_unlock(&tp->control); break; default: res = -EOPNOTSUPP; } usb_autopm_put_interface(tp->intf); out: return res; } static int rtl8152_change_mtu(struct net_device *dev, int new_mtu) { struct r8152 *tp = netdev_priv(dev); int ret; switch (tp->version) { case RTL_VER_01: case RTL_VER_02: return eth_change_mtu(dev, new_mtu); default: break; } if (new_mtu < 68 || new_mtu > RTL8153_MAX_MTU) return -EINVAL; ret = usb_autopm_get_interface(tp->intf); if (ret < 0) return ret; mutex_lock(&tp->control); dev->mtu = new_mtu; if (netif_running(dev) && netif_carrier_ok(dev)) r8153_set_rx_early_size(tp); mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); return ret; } static const struct net_device_ops rtl8152_netdev_ops = { .ndo_open = rtl8152_open, .ndo_stop = rtl8152_close, .ndo_do_ioctl = rtl8152_ioctl, .ndo_start_xmit = rtl8152_start_xmit, .ndo_tx_timeout = rtl8152_tx_timeout, .ndo_set_features = rtl8152_set_features, .ndo_set_rx_mode = rtl8152_set_rx_mode, .ndo_set_mac_address = rtl8152_set_mac_address, .ndo_change_mtu = rtl8152_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_features_check = rtl8152_features_check, }; static void r8152b_get_version(struct r8152 *tp) { u32 ocp_data; u16 version; ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1); version = (u16)(ocp_data & VERSION_MASK); switch (version) { case 0x4c00: tp->version = RTL_VER_01; break; case 0x4c10: tp->version = RTL_VER_02; break; case 0x5c00: tp->version = RTL_VER_03; tp->mii.supports_gmii = 1; break; case 0x5c10: tp->version = RTL_VER_04; tp->mii.supports_gmii = 1; break; case 0x5c20: tp->version = RTL_VER_05; tp->mii.supports_gmii = 1; break; case 0x5c30: tp->version = RTL_VER_06; tp->mii.supports_gmii = 1; break; default: netif_info(tp, probe, tp->netdev, "Unknown version 0x%04x\n", version); break; } } static void rtl8152_unload(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; if (tp->version != RTL_VER_01) r8152_power_cut_en(tp, true); } static void rtl8153_unload(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; r8153_power_cut_en(tp, false); } static int rtl_ops_init(struct r8152 *tp) { struct rtl_ops *ops = &tp->rtl_ops; int ret = 0; switch (tp->version) { case RTL_VER_01: case RTL_VER_02: ops->init = r8152b_init; ops->enable = rtl8152_enable; ops->disable = rtl8152_disable; ops->up = rtl8152_up; ops->down = rtl8152_down; ops->unload = rtl8152_unload; ops->eee_get = r8152_get_eee; ops->eee_set = r8152_set_eee; ops->in_nway = rtl8152_in_nway; break; case RTL_VER_03: case RTL_VER_04: case RTL_VER_05: case RTL_VER_06: ops->init = r8153_init; ops->enable = rtl8153_enable; ops->disable = rtl8153_disable; ops->up = rtl8153_up; ops->down = rtl8153_down; ops->unload = rtl8153_unload; ops->eee_get = r8153_get_eee; ops->eee_set = r8153_set_eee; ops->in_nway = rtl8153_in_nway; break; default: ret = -ENODEV; netif_err(tp, probe, tp->netdev, "Unknown Device\n"); break; } return ret; } static int rtl8152_probe(struct usb_interface *intf, const struct usb_device_id *id) { 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); return -ENODEV; } usb_reset_device(udev); netdev = alloc_etherdev(sizeof(struct r8152)); if (!netdev) { dev_err(&intf->dev, "Out of memory\n"); return -ENOMEM; } SET_NETDEV_DEV(netdev, &intf->dev); tp = netdev_priv(netdev); tp->msg_enable = 0x7FFF; tp->udev = udev; tp->netdev = netdev; tp->intf = intf; r8152b_get_version(tp); ret = rtl_ops_init(tp); if (ret) goto out; mutex_init(&tp->control); INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t); netdev->netdev_ops = &rtl8152_netdev_ops; netdev->watchdog_timeo = RTL8152_TX_TIMEOUT; netdev->features |= NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6; netdev->ethtool_ops = &ops; netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE); tp->mii.dev = netdev; tp->mii.mdio_read = read_mii_word; tp->mii.mdio_write = write_mii_word; tp->mii.phy_id_mask = 0x3f; tp->mii.reg_num_mask = 0x1f; tp->mii.phy_id = R8152_PHY_ID; switch (udev->speed) { case USB_SPEED_SUPER: tp->coalesce = COALESCE_SUPER; break; case USB_SPEED_HIGH: tp->coalesce = COALESCE_HIGH; break; default: tp->coalesce = COALESCE_SLOW; break; } intf->needs_remote_wakeup = 1; tp->rtl_ops.init(tp); set_ethernet_addr(tp); usb_set_intfdata(intf, tp); netif_napi_add(netdev, &tp->napi, r8152_poll, RTL8152_NAPI_WEIGHT); ret = register_netdev(netdev); if (ret != 0) { netif_err(tp, probe, netdev, "couldn't register the device\n"); goto out1; } if (!rtl_can_wakeup(tp)) __rtl_set_wol(tp, 0); tp->saved_wolopts = __rtl_get_wol(tp); if (tp->saved_wolopts) device_set_wakeup_enable(&udev->dev, true); else device_set_wakeup_enable(&udev->dev, false); netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION); return 0; out1: netif_napi_del(&tp->napi); usb_set_intfdata(intf, NULL); out: free_netdev(netdev); return ret; } static void rtl8152_disconnect(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); if (tp) { struct usb_device *udev = tp->udev; if (udev->state == USB_STATE_NOTATTACHED) set_bit(RTL8152_UNPLUG, &tp->flags); netif_napi_del(&tp->napi); unregister_netdev(tp->netdev); tp->rtl_ops.unload(tp); free_netdev(tp->netdev); } } #define REALTEK_USB_DEVICE(vend, prod) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ USB_DEVICE_ID_MATCH_INT_CLASS, \ .idVendor = (vend), \ .idProduct = (prod), \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC \ }, \ { \ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | \ USB_DEVICE_ID_MATCH_DEVICE, \ .idVendor = (vend), \ .idProduct = (prod), \ .bInterfaceClass = USB_CLASS_COMM, \ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \ .bInterfaceProtocol = USB_CDC_PROTO_NONE /* table of devices that work with this driver */ static struct usb_device_id rtl8152_table[] = { {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)}, {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)}, {REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)}, {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)}, {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x304f)}, {REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA, 0x09ff)}, {} }; MODULE_DEVICE_TABLE(usb, rtl8152_table); static struct usb_driver rtl8152_driver = { .name = MODULENAME, .id_table = rtl8152_table, .probe = rtl8152_probe, .disconnect = rtl8152_disconnect, .suspend = rtl8152_suspend, .resume = rtl8152_resume, .reset_resume = rtl8152_reset_resume, .pre_reset = rtl8152_pre_reset, .post_reset = rtl8152_post_reset, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, }; module_usb_driver(rtl8152_driver); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-12-09 3:23 ` Hayes Wang @ 2016-12-09 13:05 ` Mark Lord 0 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-12-09 13:05 UTC (permalink / raw) To: Hayes Wang; +Cc: netdev, nic_swsd, linux-kernel, linux-usb On 16-12-08 10:23 PM, Hayes Wang wrote: > Mark Lord <mlord@pobox.com> > > I find an issue about autosuspend, and it may result in the same > problem with you. I don't sure if this is helpful to you, because > it only occurs when enabling the autosuspend. Thanks. I am using ASIX adapters now. I did try the latest 4.9-rc8, and 4.8.12 kernels with the r8152 dongle yesterday, in hope that perhaps the many EHCI fixes from those kernels might help out. The dongle was unusable with those newer kernels. Most of the time it failed with "Get ether addr fail\n" at startup. On the occasions where it got past that point, it often failed the DHCP negotiation, but this looks more like a bug elsewhere in the kernel, possibly racing against initialization of the random number generators. Adding a 2-second sleep the the r8151 probe function made this error mostly go away. Cheers -- Mark Lord ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-30 11:58 ` Hayes Wang 2016-12-09 3:23 ` Hayes Wang @ 2017-01-01 0:07 ` Ansis Atteka 2017-01-03 0:40 ` Ansis Atteka 2019-01-05 14:14 ` r8152: data corruption in various scenarios Mark Lord 1 sibling, 2 replies; 80+ messages in thread From: Ansis Atteka @ 2017-01-01 0:07 UTC (permalink / raw) To: Hayes Wang Cc: David Miller, mlord, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb On Wed, Nov 30, 2016 at 3:58 AM, Hayes Wang <hayeswang@realtek.com> wrote: > Mark Lord <mlord@pobox.com> > [...] >> > Not sure why, because there really is no other way for the data to >> > appear where it does at the beginning of that URB buffer. >> > >> > This does seem a rather unexpected burden to place upon someone >> > reporting a regression in a USB network driver that corrupts user data. >> >> If you are the only person who can actively reproduce this, which >> seems to be the case right now, this is unfortunately the only way to >> reach a proper analysis and fix. > > I have tested it with iperf more than five days without any error. > I would think if there is any other way to reproduce it. > For the past few days I have been debugging a similar data corruption bug related to r8152 driver, but on x86-64 platform. Also, I think that this data corruption bug has some serious security implications, because it appears that "corrupted data" is actually 530 byte fragment from one of the previous Ethernet frames that Realtek device just received. See the ping test in the bottom of my email that demonstrates this. Besides the data corruption problem I am also experiencing another serious problem that could be related and manifests itself in XHCI module when Realtek Ethernet port receives packets at "high" rate (ie 10Mbps or higher). This second problem correlates with error messages in kern.log printed by xhci-hcd. Ethernet connectivity is completely lost at this time until I reload r8152 driver: [ 2540.426240] xhci_hcd 0000:0e:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13 [ 2540.426258] xhci_hcd 0000:0e:00.0: Looking for event-dma 00000000fff0f010 trb-start 00000000ff5c9fe0 trb-end 00000000ff5c9fe0 seg-start 00000000ff5c9000 seg-end 00000000ff5c9ff0 [ 2540.426259] xhci_hcd 0000:0e:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13 [ 2540.426260] xhci_hcd 0000:0e:00.0: Looking for event-dma 00000000fff0f020 trb-start 00000000ff5c9fe0 trb-end 00000000ff5c9fe0 seg-start 00000000ff5c9000 seg-end 00000000ff5c9ff0 [ 2540.426334] xhci_hcd 0000:0e:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13 [ 2540.426336] xhci_hcd 0000:0e:00.0: Looking for event-dma 00000000fff0f030 trb-start 00000000ff5c9fe0 trb-end 00000000ff5c9fe0 seg-start 00000000ff5c9000 seg-end 00000000ff5c9ff0 [ 2540.426372] xhci_hcd 0000:0e:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13 [ 2540.426373] xhci_hcd 0000:0e:00.0: Looking for event-dma 00000000fff0f040 trb-start 00000000ff5c9fe0 trb-end 00000000ff5c9fe0 seg-start 00000000ff5c9000 seg-end 00000000ff5c9ff0 [ 2540.426488] xhci_hcd 0000:0e:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13 [ 2540.426491] xhci_hcd 0000:0e:00.0: Looking for event-dma 00000000fff0f050 trb-start 00000000ff5c9fe0 trb-end 00000000ff5c9fe0 seg-start 00000000ff5c9000 seg-end 00000000ff5c9ff0 [ 2540.437020] xhci_hcd 0000:0e:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13 [ 2540.437024] xhci_hcd 0000:0e:00.0: Looking for event-dma 00000000fff0f060 trb-start 00000000ff5c9fe0 trb-end 00000000ff5c9fe0 seg-start 00000000ff5c9000 seg-end 00000000ff5c9ff0 [ 2540.438239] xhci_hcd 0000:0e:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13 [ 2540.438246] xhci_hcd 0000:0e:00.0: Looking for event-dma 00000000fff0f070 trb-start 00000000ff5c9fe0 trb-end 00000000ff5c9fe0 seg-start 00000000ff5c9000 seg-end 00000000ff5c9ff0 [ 2540.438493] xhci_hcd 0000:0e:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13 [ 2540.438495] xhci_hcd 0000:0e:00.0: Looking for event-dma 00000000fff0f080 trb-start 00000000ff5c9fe0 trb-end 00000000ff5c9fe0 seg-start 00000000ff5c9000 seg-end 00000000ff5c9ff0 All of that is happening on my X86-64 Dell XPS15 9550 laptop that is connected to Ethernet via Dell TB15 dock. This Dell TB 15 Dock uses Realtek chip to provide Ethernet connectivity to laptop: # lsusb ... Bus 004 Device 003: ID 0bda:8153 Realtek Semiconductor Corp. Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 3.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 9 idVendor 0x0bda Realtek Semiconductor Corp. idProduct 0x8153 bcdDevice 30.01 iManufacturer 1 Realtek iProduct 2 USB 10/100/1000 LAN iSerial 6 000001000000 bNumConfigurations 2 This Realtek Ethernet port is connected to a XHCI ASMedia host controller that also resides on Dell TB15 Dock. The dock itself is connected via Thunderbolt 3 cable to laptop: # lspci .... 0e:00.0 USB controller: ASMedia Technology Inc. ASM1042A USB 3.0 Host Controller In my case it is easy to reproduce either of those two issues. Here are my observations: 1. The Ethernet controller on Dell TB15 dock was working completely fine while I had Windows 10 installed on my Laptop. 2. I have tried various Linux distributions - Ubuntu 16.10, Ubuntu 14.04, CentOS 7. All of them fail with "ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13" error message under high load. 3. I have tried Ubuntu 16.10 and Ubuntu 16.04. Both of them are affected by this data corruption bug. I did not test for data corruption on CentOS or other Linux distributions that come with older Linux kernels than Ubuntu. 4. If I start two ping instances at the same time then it appears that 530 bytes from the first ping instance are occasionally "injected" into ping payload of the second ping instance. Also, I was able to reproduce this exact same issue with TCP. sudo ping -i 0.05 -p ff -s 15000 10.33.75.80 # Sending 0xff as payload .... 15008 bytes from 10.33.75.80: icmp_seq=39 ttl=64 time=104 ms wrong data byte #9822 should be 0xff but was 0x0 #16 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #9776 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #9808 ff ff ff ff ff ff ff ff ff ff ff ff ff ff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #9840 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #9872 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #9904 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #9936 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #9968 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10032 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10064 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10096 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10160 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10192 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10224 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10256 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10288 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10320 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #10352 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #10384 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ... sudo ping -i 0.05 -p 00 -s 15000 10.33.75.80 # Sending 0x00 as payload ... 15008 bytes from 10.33.75.80: icmp_seq=164 ttl=64 time=95.4 ms wrong data byte #11302 should be 0x0 but was 0xff #16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... #11248 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #11280 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ff ff ff ff ff ff ff ff ff ff #11312 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11344 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11376 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11408 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11440 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11472 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11504 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11536 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11568 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11600 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11632 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11664 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11696 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11728 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11760 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11792 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff #11824 ff ff ff ff ff ff ff ff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #11856 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #11888 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2017-01-01 0:07 ` Ansis Atteka @ 2017-01-03 0:40 ` Ansis Atteka 2017-01-03 13:19 ` Mark Lord 2017-01-09 7:58 ` Hayes Wang 2019-01-05 14:14 ` r8152: data corruption in various scenarios Mark Lord 1 sibling, 2 replies; 80+ messages in thread From: Ansis Atteka @ 2017-01-03 0:40 UTC (permalink / raw) To: Hayes Wang Cc: David Miller, mlord, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Ansis Atteka On Sat, Dec 31, 2016 at 4:07 PM, Ansis Atteka <aatteka@nicira.com> wrote: > On Wed, Nov 30, 2016 at 3:58 AM, Hayes Wang <hayeswang@realtek.com> wrote: >> Mark Lord <mlord@pobox.com> >> [...] >>> > Not sure why, because there really is no other way for the data to >>> > appear where it does at the beginning of that URB buffer. >>> > >>> > This does seem a rather unexpected burden to place upon someone >>> > reporting a regression in a USB network driver that corrupts user data. >>> >>> If you are the only person who can actively reproduce this, which >>> seems to be the case right now, this is unfortunately the only way to >>> reach a proper analysis and fix. >> >> I have tested it with iperf more than five days without any error. >> I would think if there is any other way to reproduce it. >> I think that I am getting closer to the root cause of this bug. Also, I have a workaround that at least makes r8152 functionally stable in my Dell TB15 dock. Mark, would you mind giving a chance to the patch that I have in the bottom of this email to see if it helps your issue too (you might have to tweak those settings slightly differently if you use something else than USB 3.0) Long story short - what I observed in Wireshark is that if there are more than ~10 Ethernet frames *close together to each other* then the data corruption bug starts to express itself. If there are ~15 or more Ethernet frames close together to each other then the XHCI starts to emit the "ERROR Transfer event TRB DMA ptr not part of current TD ep_index 2 comp_code 13" error message and r8152 driver gets toasted. Hayes, in your iperf reproduction environment did you 1) connect sender and receiver directly with an Ethernet cable? 2) use iperf's TCP mode instead of UDP mode, because I believe that with UDP mode packets are more likely to be sparsely distributed? Also, this bug is way easier to reproduce when IP fragmentation kicks in because IP fragments are typically sent out very close to each other. 3) were you plugging your USB Ethernet dongle in USB 3.0 port or whatever Mark was using? It seems that each USB mode has different coalesce parameters and yours might have work "out of box"? While I would not call this a proper fix, because it simply reduces coalescing timeouts by order of 10X and most likely does not eliminate security aspects of the bug, it at least made my system functionally stable and I don't see either of those two bugs in my setup anymore: git diff diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index c254248..4979690 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -365,9 +365,9 @@ #define PCUT_STATUS 0x0001 /* USB_RX_EARLY_TIMEOUT */ -#define COALESCE_SUPER 85000U -#define COALESCE_HIGH 250000U -#define COALESCE_SLOW 524280U +#define COALESCE_SUPER 8500U +#define COALESCE_HIGH 25000U +#define COALESCE_SLOW 52428U /* USB_WDT11_CTRL */ #define TIMER11_EN 0x0001 ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2017-01-03 0:40 ` Ansis Atteka @ 2017-01-03 13:19 ` Mark Lord 2017-01-09 7:58 ` Hayes Wang 1 sibling, 0 replies; 80+ messages in thread From: Mark Lord @ 2017-01-03 13:19 UTC (permalink / raw) To: Ansis Atteka, Hayes Wang Cc: David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Ansis Atteka On 17-01-02 07:40 PM, Ansis Atteka wrote: .. > I think that I am getting closer to the root cause of this bug. Also, > I have a workaround that at least makes r8152 functionally stable in > my Dell TB15 dock. Mark, would you mind giving a chance to the patch > that I have in the bottom of this email to see if it helps your issue > too (you might have to tweak those settings slightly differently if > you use something else than USB 3.0) /* USB_RX_EARLY_TIMEOUT */ -#define COALESCE_SUPER 85000U -#define COALESCE_HIGH 250000U -#define COALESCE_SLOW 524280U +#define COALESCE_SUPER 8500U +#define COALESCE_HIGH 25000U +#define COALESCE_SLOW 52428U The RTL_VER_02 chip that I was using does not support interrupt coalescing in the driver [see the rtl8152_set_coalesce() function]. So that workaround would not help here. -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2017-01-03 0:40 ` Ansis Atteka 2017-01-03 13:19 ` Mark Lord @ 2017-01-09 7:58 ` Hayes Wang 1 sibling, 0 replies; 80+ messages in thread From: Hayes Wang @ 2017-01-09 7:58 UTC (permalink / raw) To: Ansis Atteka Cc: David Miller, mlord, netdev, nic_swsd, linux-kernel, linux-usb, Ansis Atteka Ansis Atteka [mailto:aatteka@nicira.com] > Sent: Tuesday, January 03, 2017 8:41 AM [...] > Hayes, in your iperf reproduction environment did you > 1) connect sender and receiver directly with an Ethernet cable? > 2) use iperf's TCP mode instead of UDP mode, because I believe that > with UDP mode packets are more likely to be sparsely distributed? > Also, this bug is way easier to reproduce when IP fragmentation kicks > in because IP fragments are typically sent out very close to each > other. > 3) were you plugging your USB Ethernet dongle in USB 3.0 port or > whatever Mark was using? It seems that each USB mode has different > coalesce parameters and yours might have work "out of box"? Yes. I connect them directly and use iperf's TCP mode. However, I test the RTL8152 which only support USB 2.0. Therefore, I don't think it occurs with different coalesce parameters. > While I would not call this a proper fix, because it simply reduces > coalescing timeouts by order of 10X and most likely does not eliminate > security aspects of the bug, it at least made my system functionally > stable and I don't see either of those two bugs in my setup anymore: Do you try commit a59e6d815226 ("r8152: correct the rx early size"), or you have used it? Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* r8152: data corruption in various scenarios 2017-01-01 0:07 ` Ansis Atteka 2017-01-03 0:40 ` Ansis Atteka @ 2019-01-05 14:14 ` Mark Lord 2019-01-05 14:22 ` Mark Lord 2019-01-06 19:14 ` Kai Heng Feng 1 sibling, 2 replies; 80+ messages in thread From: Mark Lord @ 2019-01-05 14:14 UTC (permalink / raw) To: Ansis Atteka, Hayes Wang Cc: David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Kai-Heng Feng A couple of years back, I reported data corruption resulting from a change in kernel 3.16 which enabled hardware checksums in the r8152 driver. This was happening on an embedded system that was using a r8152 USB dongle. At the time, it was very difficult to figure out what could possibly be causing it, other than that re-enabling software checksums prevented corrupted packets from resulting in more serious issues. Since that time, more and more reports of similar corruption and issues have been trickling in. Eg. https://lore.kernel.org/patchwork/patch/873920/ Note that there are reports in the thread above that the issues are not limited to only the built-in ethernet chip of the dock. There is even now a special hack in the upstream r8152.c to attempt to detect a Dell TB16 dock and disable RX Aggregation in the driver to prevent such issues. Well.. I have a WD15 dock, not a TB16, and that same hack also catches my dock in its net: [5.794641] usb 4-1.2: Dell TB16 Dock, disable RX aggregation So one issue is that the code is not correctly identifying the dock, and the WD15 is claimed to be immune from the r8152 issues. One of the symptoms of the r8152 issue, reported by Ansis Atteka, were messages like this: xhci_hcd 0000:39:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 13 comp_code 1 I just got that exact message above, with the r8152 in my 1-day old WD15 dock, with the TB16 "workaround" enabled in Linux kernel 4.20.0. From this I conclude that the workaround is not 100% complete yet. -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-05 14:14 ` r8152: data corruption in various scenarios Mark Lord @ 2019-01-05 14:22 ` Mark Lord 2019-01-06 19:14 ` Kai Heng Feng 1 sibling, 0 replies; 80+ messages in thread From: Mark Lord @ 2019-01-05 14:22 UTC (permalink / raw) To: Ansis Atteka, Hayes Wang Cc: David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Kai-Heng Feng On 2019-01-05 9:14 a.m., Mark Lord wrote: > A couple of years back, I reported data corruption resulting from > a change in kernel 3.16 which enabled hardware checksums in the r8152 driver. > This was happening on an embedded system that was using a r8152 USB dongle. > > At the time, it was very difficult to figure out what could possibly be causing it, > other than that re-enabling software checksums prevented corrupted packets from > resulting in more serious issues. > > Since that time, more and more reports of similar corruption and issues > have been trickling in. Eg. > > https://lore.kernel.org/patchwork/patch/873920/ Forgot to include this link (below) where people still have the issue even with the driver workaround. Switching to software checksums "fixes" it: https://bugzilla.redhat.com/show_bug.cgi?id=1460789 > > Note that there are reports in the thread above that the issues > are not limited to only the built-in ethernet chip of the dock. > > There is even now a special hack in the upstream r8152.c to attempt to detect > a Dell TB16 dock and disable RX Aggregation in the driver to prevent such issues. > > Well.. I have a WD15 dock, not a TB16, and that same hack also catches my dock > in its net: > > [5.794641] usb 4-1.2: Dell TB16 Dock, disable RX aggregation > > So one issue is that the code is not correctly identifying the dock, > and the WD15 is claimed to be immune from the r8152 issues. > > One of the symptoms of the r8152 issue, reported by Ansis Atteka, > were messages like this: > > xhci_hcd 0000:39:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 13 > comp_code 1 > > I just got that exact message above, with the r8152 in my 1-day old WD15 dock, > with the TB16 "workaround" enabled in Linux kernel 4.20.0. > >>From this I conclude that the workaround is not 100% complete yet. > -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-05 14:14 ` r8152: data corruption in various scenarios Mark Lord 2019-01-05 14:22 ` Mark Lord @ 2019-01-06 19:14 ` Kai Heng Feng 2019-01-06 21:13 ` Mark Lord 1 sibling, 1 reply; 80+ messages in thread From: Kai Heng Feng @ 2019-01-06 19:14 UTC (permalink / raw) To: Mark Lord Cc: Ansis Atteka, Hayes Wang, David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Mario Limonciello > On Jan 5, 2019, at 10:14 PM, Mark Lord <mlord@pobox.com> wrote: > > A couple of years back, I reported data corruption resulting from > a change in kernel 3.16 which enabled hardware checksums in the r8152 driver. > This was happening on an embedded system that was using a r8152 USB dongle. > > At the time, it was very difficult to figure out what could possibly be causing it, > other than that re-enabling software checksums prevented corrupted packets from > resulting in more serious issues. > > Since that time, more and more reports of similar corruption and issues > have been trickling in. Eg. > > https://lore.kernel.org/patchwork/patch/873920/ > > Note that there are reports in the thread above that the issues > are not limited to only the built-in ethernet chip of the dock. > > There is even now a special hack in the upstream r8152.c to attempt to detect > a Dell TB16 dock and disable RX Aggregation in the driver to prevent such issues. > > Well.. I have a WD15 dock, not a TB16, and that same hack also catches my dock > in its net: > > [5.794641] usb 4-1.2: Dell TB16 Dock, disable RX aggregation The serial should be unique according to Dell. > > So one issue is that the code is not correctly identifying the dock, > and the WD15 is claimed to be immune from the r8152 issues. The WD15 I tested didn't use that serial number though... > > One of the symptoms of the r8152 issue, reported by Ansis Atteka, > were messages like this: > > xhci_hcd 0000:39:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 13 > comp_code 1 This is probably an xHC bug. A similar issue is fixed by commit 9da5a1092b13 ("xhci: Bad Ethernet performance plugged in ASM1042A host”). > > I just got that exact message above, with the r8152 in my 1-day old WD15 dock, > with the TB16 "workaround" enabled in Linux kernel 4.20.0. Is the xHC WD15 connected an ASMedia one? Kai-Heng > > From this I conclude that the workaround is not 100% complete yet. > -- > Mark Lord > Real-Time Remedies Inc. > mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-06 19:14 ` Kai Heng Feng @ 2019-01-06 21:13 ` Mark Lord 2019-01-06 21:16 ` Mark Lord 0 siblings, 1 reply; 80+ messages in thread From: Mark Lord @ 2019-01-06 21:13 UTC (permalink / raw) To: Kai Heng Feng Cc: Ansis Atteka, Hayes Wang, David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Mario Limonciello On 2019-01-06 2:14 p.m., Kai Heng Feng wrote:>> On Jan 5, 2019, at 10:14 PM, Mark Lord <mlord@pobox.com> wrote: .. >> There is even now a special hack in the upstream r8152.c to attempt to detect >> a Dell TB16 dock and disable RX Aggregation in the driver to prevent such issues. >> >> Well.. I have a WD15 dock, not a TB16, and that same hack also catches my dock >> in its net: >> >> [5.794641] usb 4-1.2: Dell TB16 Dock, disable RX aggregation > > The serial should be unique according to Dell. > >> So one issue is that the code is not correctly identifying the dock, >> and the WD15 is claimed to be immune from the r8152 issues. > > The WD15 I tested didn't use that serial number though... What info do you need from me about the WD15 so this can be corrected? >> xhci_hcd 0000:39:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 13 >> comp_code 1 > > This is probably an xHC bug. A similar issue is fixed by commit 9da5a1092b13 > ("xhci: Bad Ethernet performance plugged in ASM1042A host”). > >> I just got that exact message above, with the r8152 in my 1-day old WD15 dock, >> with the TB16 "workaround" enabled in Linux kernel 4.20.0. > > Is the xHC WD15 connected an ASMedia one? I don't know. I *think* it identifies as a DSL6340 (see below). Here is lspci and lsusb: $ lspci -vt -[0000:00]-+-00.0 Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers +-02.0 Intel Corporation UHD Graphics 620 +-04.0 Intel Corporation Skylake Processor Thermal Subsystem +-14.0 Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller +-14.2 Intel Corporation Sunrise Point-LP Thermal subsystem +-15.0 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 +-15.1 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 +-16.0 Intel Corporation Sunrise Point-LP CSME HECI #1 +-1c.0-[01-39]----00.0-[02-39]--+-00.0-[03]-- | +-01.0-[04-38]-- | \-02.0-[39]----00.0 Intel Corporation DSL6340 USB 3.1 Controller [Alpine Ridge] +-1c.4-[3a]----00.0 Qualcomm Atheros QCA6174 802.11ac Wireless Network Adapter +-1d.0-[3b]----00.0 Samsung Electronics Co Ltd Device a808 +-1f.0 Intel Corporation Device 9d4e +-1f.2 Intel Corporation Sunrise Point-LP PMC +-1f.3 Intel Corporation Sunrise Point-LP HD Audio \-1f.4 Intel Corporation Sunrise Point-LP SMBus $ lsusb -t /: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 10000M |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/7p, 5000M |__ Port 2: Dev 3, If 0, Class=Vendor Specific Class, Driver=r8152, 5000M /: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 480M |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/7p, 480M |__ Port 5: Dev 3, If 1, Class=Audio, Driver=snd-usb-audio, 480M |__ Port 5: Dev 3, If 2, Class=Audio, Driver=snd-usb-audio, 480M |__ Port 5: Dev 3, If 0, Class=Audio, Driver=snd-usb-audio, 480M |__ Port 5: Dev 3, If 3, Class=Audio, Driver=snd-usb-audio, 480M |__ Port 6: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 12M |__ Port 6: Dev 4, If 1, Class=Human Interface Device, Driver=usbhid, 12M |__ Port 6: Dev 4, If 2, Class=Human Interface Device, Driver=usbhid, 12M |__ Port 7: Dev 5, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/6p, 5000M /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M |__ Port 3: Dev 2, If 0, Class=Wireless, Driver=btusb, 12M |__ Port 3: Dev 2, If 1, Class=Wireless, Driver=btusb, 12M Thanks for having a look. -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-06 21:13 ` Mark Lord @ 2019-01-06 21:16 ` Mark Lord 2019-01-07 3:53 ` Hayes Wang 2019-01-07 4:09 ` Kai Heng Feng 0 siblings, 2 replies; 80+ messages in thread From: Mark Lord @ 2019-01-06 21:16 UTC (permalink / raw) To: Kai Heng Feng Cc: Ansis Atteka, Hayes Wang, David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Mario Limonciello On 2019-01-06 4:13 p.m., Mark Lord wrote: > On 2019-01-06 2:14 p.m., Kai Heng Feng wrote:>> On Jan 5, 2019, at 10:14 PM, Mark Lord > <mlord@pobox.com> wrote: > .. >>> There is even now a special hack in the upstream r8152.c to attempt to detect >>> a Dell TB16 dock and disable RX Aggregation in the driver to prevent such issues. >>> >>> Well.. I have a WD15 dock, not a TB16, and that same hack also catches my dock >>> in its net: >>> >>> [5.794641] usb 4-1.2: Dell TB16 Dock, disable RX aggregation >> >> The serial should be unique according to Dell. >> >>> So one issue is that the code is not correctly identifying the dock, >>> and the WD15 is claimed to be immune from the r8152 issues. >> >> The WD15 I tested didn't use that serial number though... > > What info do you need from me about the WD15 so this can be corrected? > >>> xhci_hcd 0000:39:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 13 >>> comp_code 1 >> >> This is probably an xHC bug. A similar issue is fixed by commit 9da5a1092b13 >> ("xhci: Bad Ethernet performance plugged in ASM1042A host”). >> >>> I just got that exact message above, with the r8152 in my 1-day old WD15 dock, >>> with the TB16 "workaround" enabled in Linux kernel 4.20.0. >> >> Is the xHC WD15 connected an ASMedia one? > > I don't know. I *think* it identifies as a DSL6340 (see below). > > Here is lspci and lsusb: > > $ lspci -vt > -[0000:00]-+-00.0 Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers > +-02.0 Intel Corporation UHD Graphics 620 > +-04.0 Intel Corporation Skylake Processor Thermal Subsystem > +-14.0 Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller > +-14.2 Intel Corporation Sunrise Point-LP Thermal subsystem > +-15.0 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 > +-15.1 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 > +-16.0 Intel Corporation Sunrise Point-LP CSME HECI #1 > +-1c.0-[01-39]----00.0-[02-39]--+-00.0-[03]-- > | +-01.0-[04-38]-- > | \-02.0-[39]----00.0 Intel Corporation DSL6340 USB 3.1 > Controller [Alpine Ridge] > +-1c.4-[3a]----00.0 Qualcomm Atheros QCA6174 802.11ac Wireless Network Adapter > +-1d.0-[3b]----00.0 Samsung Electronics Co Ltd Device a808 > +-1f.0 Intel Corporation Device 9d4e > +-1f.2 Intel Corporation Sunrise Point-LP PMC > +-1f.3 Intel Corporation Sunrise Point-LP HD Audio > \-1f.4 Intel Corporation Sunrise Point-LP SMBus Mmm.. lspci -vt isn't as verbose as I thought, so here is plain lspci to fill in the blanks: $ lspci 00:00.0 Host bridge: Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers (rev 08) 00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 620 (rev 07) 00:04.0 Signal processing controller: Intel Corporation Skylake Processor Thermal Subsystem (rev 08) 00:14.0 USB controller: Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller (rev 21) 00:14.2 Signal processing controller: Intel Corporation Sunrise Point-LP Thermal subsystem (rev 21) 00:15.0 Signal processing controller: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 (rev 21) 00:15.1 Signal processing controller: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 (rev 21) 00:16.0 Communication controller: Intel Corporation Sunrise Point-LP CSME HECI #1 (rev 21) 00:1c.0 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port (rev f1) 00:1c.4 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port #5 (rev f1) 00:1d.0 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port #9 (rev f1) 00:1f.0 ISA bridge: Intel Corporation Device 9d4e (rev 21) 00:1f.2 Memory controller: Intel Corporation Sunrise Point-LP PMC (rev 21) 00:1f.3 Audio device: Intel Corporation Sunrise Point-LP HD Audio (rev 21) 00:1f.4 SMBus: Intel Corporation Sunrise Point-LP SMBus (rev 21) 01:00.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] 02:00.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] 02:01.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] 02:02.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] 39:00.0 USB controller: Intel Corporation DSL6340 USB 3.1 Controller [Alpine Ridge] 3a:00.0 Network controller: Qualcomm Atheros QCA6174 802.11ac Wireless Network Adapter (rev 32) 3b:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd Device a808 -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: r8152: data corruption in various scenarios 2019-01-06 21:16 ` Mark Lord @ 2019-01-07 3:53 ` Hayes Wang 2019-01-07 16:01 ` Mario.Limonciello 2019-01-07 4:09 ` Kai Heng Feng 1 sibling, 1 reply; 80+ messages in thread From: Hayes Wang @ 2019-01-07 3:53 UTC (permalink / raw) To: Mark Lord, Kai Heng Feng Cc: Ansis Atteka, David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Mario Limonciello, Ryankao Monday, January 07, 2019 5:17 AM [...] >> This is probably an xHC bug. A similar issue is fixed by commit 9da5a1092b13 >> ("xhci: Bad Ethernet performance plugged in ASM1042A host”). >> >>> I just got that exact message above, with the r8152 in my 1-day old WD15 dock, >>> with the TB16 "workaround" enabled in Linux kernel 4.20.0. >> >> Is the xHC WD15 connected an ASMedia one? > > I don't know. I *think* it identifies as a DSL6340 (see below). > According to our record, it is relative to the asmedia. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: r8152: data corruption in various scenarios 2019-01-07 3:53 ` Hayes Wang @ 2019-01-07 16:01 ` Mario.Limonciello 2019-01-07 18:06 ` Mark Lord 0 siblings, 1 reply; 80+ messages in thread From: Mario.Limonciello @ 2019-01-07 16:01 UTC (permalink / raw) To: hayeswang, mlord, kai.heng.feng Cc: aatteka, davem, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, ryankao > -----Original Message----- > From: Hayes Wang <hayeswang@realtek.com> > Sent: Sunday, January 6, 2019 9:54 PM > To: Mark Lord; Kai Heng Feng > Cc: Ansis Atteka; David Miller; greg@kroah.com; romieu@fr.zoreil.com; > netdev@vger.kernel.org; nic_swsd; linux-kernel@vger.kernel.org; linux- > usb@vger.kernel.org; Limonciello, Mario; Ryankao > Subject: RE: r8152: data corruption in various scenarios > > > [EXTERNAL EMAIL] > > Monday, January 07, 2019 5:17 AM > [...] > >> This is probably an xHC bug. A similar issue is fixed by commit 9da5a1092b13 > >> ("xhci: Bad Ethernet performance plugged in ASM1042A host”). > >> > >>> I just got that exact message above, with the r8152 in my 1-day old WD15 dock, > >>> with the TB16 "workaround" enabled in Linux kernel 4.20.0. > >> > >> Is the xHC WD15 connected an ASMedia one? > > > > I don't know. I *think* it identifies as a DSL6340 (see below). > > > > According to our record, it is relative to the asmedia. > DSL6430 should be referring to the Alpine Ridge controller in the system. TB16 contains ASMedia host controller. It's a Thunderbolt dock and all USB devices are connected to ASMedia host controller in the dock. WD15 does not contain an ASMedia host controller, it connected to system's USB host controller. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-07 16:01 ` Mario.Limonciello @ 2019-01-07 18:06 ` Mark Lord 2019-01-07 18:27 ` Mario.Limonciello 0 siblings, 1 reply; 80+ messages in thread From: Mark Lord @ 2019-01-07 18:06 UTC (permalink / raw) To: Mario.Limonciello, hayeswang, kai.heng.feng Cc: aatteka, davem, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, ryankao On 2019-01-07 11:01 a.m., Mario.Limonciello@dell.com wrote: > > TB16 contains ASMedia host controller. It's a Thunderbolt dock and all USB devices > are connected to ASMedia host controller in the dock. > > WD15 does not contain an ASMedia host controller, it connected to system's > USB host controller. Thank-you, Mario. So.. why are we enabling the r8153 (USB-ethernet) workaround on this WD15 dock? The discussion back in 2017 was that only the TB15/TB16 were affected by the XHCI overruns it produces? -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: r8152: data corruption in various scenarios 2019-01-07 18:06 ` Mark Lord @ 2019-01-07 18:27 ` Mario.Limonciello 2019-01-07 19:24 ` Mark Lord 0 siblings, 1 reply; 80+ messages in thread From: Mario.Limonciello @ 2019-01-07 18:27 UTC (permalink / raw) To: mlord, hayeswang, kai.heng.feng Cc: aatteka, davem, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, ryankao > -----Original Message----- > From: Mark Lord <mlord@pobox.com> > Sent: Monday, January 7, 2019 12:06 PM > To: Limonciello, Mario; hayeswang@realtek.com; kai.heng.feng@canonical.com > Cc: aatteka@nicira.com; davem@davemloft.net; greg@kroah.com; > romieu@fr.zoreil.com; netdev@vger.kernel.org; nic_swsd@realtek.com; linux- > kernel@vger.kernel.org; linux-usb@vger.kernel.org; ryankao@realtek.com > Subject: Re: r8152: data corruption in various scenarios > > > [EXTERNAL EMAIL] > > On 2019-01-07 11:01 a.m., Mario.Limonciello@dell.com wrote: > > > > TB16 contains ASMedia host controller. It's a Thunderbolt dock and all USB > devices > > are connected to ASMedia host controller in the dock. > > > > WD15 does not contain an ASMedia host controller, it connected to system's > > USB host controller. > > > Thank-you, Mario. > > So.. why are we enabling the r8153 (USB-ethernet) workaround on this WD15 > dock? > The discussion back in 2017 was that only the TB15/TB16 were affected by > the XHCI overruns it produces? > > -- The xHCI overrun workaround should only be applied on TB16/TB16, correct. Can you double check the verbose information from lsusb for the r8153 device on your WD15? I just double checked on my on hand WD15 with an XPS 9380 and it's not activating the quirk (bcdDevice was different). If it's the same information as the TB16 (which it sounds like it is) Kai Heng and I will check around internally to find out why they're looking the same. I can hypothesize a few guesses of what happened. My first guess would be a comparison issue with the logic in 176eb614b. Looking at that commit, I guess I would ask on the compiler behavior of !strcmp(). Would that be matching the less than case as well as the zero case? If so, it might need to be changed to strcmp() == 0. My second guess would be maybe newer ethernet NVM in manufacturing. My third guess would be a manufacturing issue putting wrong NVM image on your WD15. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-07 18:27 ` Mario.Limonciello @ 2019-01-07 19:24 ` Mark Lord 0 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2019-01-07 19:24 UTC (permalink / raw) To: Mario.Limonciello, hayeswang, kai.heng.feng Cc: aatteka, davem, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, ryankao On 2019-01-07 1:27 p.m., Mario.Limonciello@dell.com wrote: .. > The xHCI overrun workaround should only be applied on TB16/TB16, correct. > > Can you double check the verbose information from lsusb for the r8153 device > on your WD15? Sure, see below for the full output. > If it's the same information as the TB16 (which it sounds like it is) Kai Heng and I will check > around internally to find out why they're looking the same. Thanks. > My second guess would be maybe newer ethernet NVM in manufacturing. > My third guess would be a manufacturing issue putting wrong NVM image on your WD15. It could be one of those two things. Let us know what you discover. Thanks Bus 004 Device 003: ID 0bda:8153 Realtek Semiconductor Corp. Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 3.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 9 idVendor 0x0bda Realtek Semiconductor Corp. idProduct 0x8153 bcdDevice 30.11 iManufacturer 1 Realtek iProduct 2 USB 10/100/1000 LAN iSerial 6 000002000000 bNumConfigurations 2 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 57 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 64mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 3 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 255 Vendor Specific Subclass bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0400 1x 1024 bytes bInterval 0 bMaxBurst 3 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0400 1x 1024 bytes bInterval 0 bMaxBurst 3 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0002 1x 2 bytes bInterval 8 bMaxBurst 0 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 98 bNumInterfaces 2 bConfigurationValue 2 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 64mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 6 Ethernet Networking bInterfaceProtocol 0 iInterface 5 CDC Communications Control CDC Header: bcdCDC 1.10 CDC Union: bMasterInterface 0 bSlaveInterface 1 CDC Ethernet: iMacAddress 3 54BF6450FC4F bmEthernetStatistics 0x00000000 wMaxSegmentSize 1514 wNumberMCFilters 0x0000 bNumberPowerFilters 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0010 1x 16 bytes bInterval 8 bMaxBurst 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 Unused bInterfaceProtocol 0 iInterface 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 1 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 Unused bInterfaceProtocol 0 iInterface 4 Ethernet Data Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0400 1x 1024 bytes bInterval 0 bMaxBurst 3 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0400 1x 1024 bytes bInterval 0 bMaxBurst 3 Binary Object Store Descriptor: bLength 5 bDescriptorType 15 wTotalLength 22 bNumDeviceCaps 2 USB 2.0 Extension Device Capability: bLength 7 bDescriptorType 16 bDevCapabilityType 2 bmAttributes 0x00000002 Link Power Management (LPM) Supported SuperSpeed USB Device Capability: bLength 10 bDescriptorType 16 bDevCapabilityType 3 bmAttributes 0x00 wSpeedsSupported 0x000e Device can operate at Full Speed (12Mbps) Device can operate at High Speed (480Mbps) Device can operate at SuperSpeed (5Gbps) bFunctionalitySupport 2 Lowest fully-functional device speed is High Speed (480Mbps) bU1DevExitLat 10 micro seconds bU2DevExitLat 2047 micro seconds Device Status: 0x000c (Bus Powered) U1 Enabled U2 Enabled -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-06 21:16 ` Mark Lord 2019-01-07 3:53 ` Hayes Wang @ 2019-01-07 4:09 ` Kai Heng Feng 2019-01-07 4:13 ` Mark Lord 1 sibling, 1 reply; 80+ messages in thread From: Kai Heng Feng @ 2019-01-07 4:09 UTC (permalink / raw) To: Mark Lord Cc: Ansis Atteka, Hayes Wang, David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Mario Limonciello > On Jan 7, 2019, at 05:16, Mark Lord <mlord@pobox.com> wrote: > > On 2019-01-06 4:13 p.m., Mark Lord wrote: >> On 2019-01-06 2:14 p.m., Kai Heng Feng wrote:>> On Jan 5, 2019, at 10:14 PM, Mark Lord >> <mlord@pobox.com> wrote: >> .. >>>> There is even now a special hack in the upstream r8152.c to attempt to detect >>>> a Dell TB16 dock and disable RX Aggregation in the driver to prevent such issues. >>>> >>>> Well.. I have a WD15 dock, not a TB16, and that same hack also catches my dock >>>> in its net: >>>> >>>> [5.794641] usb 4-1.2: Dell TB16 Dock, disable RX aggregation >>> >>> The serial should be unique according to Dell. >>> >>>> So one issue is that the code is not correctly identifying the dock, >>>> and the WD15 is claimed to be immune from the r8152 issues. >>> >>> The WD15 I tested didn't use that serial number though... >> >> What info do you need from me about the WD15 so this can be corrected? >> >>>> xhci_hcd 0000:39:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 13 >>>> comp_code 1 >>> >>> This is probably an xHC bug. A similar issue is fixed by commit 9da5a1092b13 >>> ("xhci: Bad Ethernet performance plugged in ASM1042A host”). >>> >>>> I just got that exact message above, with the r8152 in my 1-day old WD15 dock, >>>> with the TB16 "workaround" enabled in Linux kernel 4.20.0. >>> >>> Is the xHC WD15 connected an ASMedia one? >> >> I don't know. I *think* it identifies as a DSL6340 (see below). >> >> Here is lspci and lsusb: >> >> $ lspci -vt >> -[0000:00]-+-00.0 Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers >> +-02.0 Intel Corporation UHD Graphics 620 >> +-04.0 Intel Corporation Skylake Processor Thermal Subsystem >> +-14.0 Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller >> +-14.2 Intel Corporation Sunrise Point-LP Thermal subsystem >> +-15.0 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 >> +-15.1 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 >> +-16.0 Intel Corporation Sunrise Point-LP CSME HECI #1 >> +-1c.0-[01-39]----00.0-[02-39]--+-00.0-[03]-- >> | +-01.0-[04-38]-- >> | \-02.0-[39]----00.0 Intel Corporation DSL6340 USB 3.1 >> Controller [Alpine Ridge] >> +-1c.4-[3a]----00.0 Qualcomm Atheros QCA6174 802.11ac Wireless Network Adapter >> +-1d.0-[3b]----00.0 Samsung Electronics Co Ltd Device a808 >> +-1f.0 Intel Corporation Device 9d4e >> +-1f.2 Intel Corporation Sunrise Point-LP PMC >> +-1f.3 Intel Corporation Sunrise Point-LP HD Audio >> \-1f.4 Intel Corporation Sunrise Point-LP SMBus > > > Mmm.. lspci -vt isn't as verbose as I thought, so here is plain lspci to fill in the blanks: > > $ lspci > 00:00.0 Host bridge: Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM > Registers (rev 08) > 00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 620 (rev 07) > > 00:04.0 Signal processing controller: Intel Corporation Skylake Processor Thermal Subsystem (rev 08) > > 00:14.0 USB controller: Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller (rev 21) > > 00:14.2 Signal processing controller: Intel Corporation Sunrise Point-LP Thermal subsystem (rev 21) > > 00:15.0 Signal processing controller: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 > (rev 21) > 00:15.1 Signal processing controller: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 > (rev 21) > 00:16.0 Communication controller: Intel Corporation Sunrise Point-LP CSME HECI #1 (rev 21) > > 00:1c.0 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port (rev f1) > > 00:1c.4 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port #5 (rev f1) > > 00:1d.0 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port #9 (rev f1) > > 00:1f.0 ISA bridge: Intel Corporation Device 9d4e (rev 21) > > 00:1f.2 Memory controller: Intel Corporation Sunrise Point-LP PMC (rev 21) > > 00:1f.3 Audio device: Intel Corporation Sunrise Point-LP HD Audio (rev 21) > > 00:1f.4 SMBus: Intel Corporation Sunrise Point-LP SMBus (rev 21) > > 01:00.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] > > 02:00.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] > > 02:01.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] > > 02:02.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] > > 39:00.0 USB controller: Intel Corporation DSL6340 USB 3.1 Controller [Alpine Ridge] So it’s not an ASMedia one. Before digging further, please make sure the system firmware (BIOS), Thunderbolt controller NVM and WD15 firmware are all up-to-date. Kai-Heng > > 3a:00.0 Network controller: Qualcomm Atheros QCA6174 802.11ac Wireless Network Adapter (rev 32) > > 3b:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd Device a808 > > > -- > Mark Lord > Real-Time Remedies Inc. > mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-07 4:09 ` Kai Heng Feng @ 2019-01-07 4:13 ` Mark Lord 2019-01-07 6:46 ` Kai Heng Feng 0 siblings, 1 reply; 80+ messages in thread From: Mark Lord @ 2019-01-07 4:13 UTC (permalink / raw) To: Kai Heng Feng Cc: Ansis Atteka, Hayes Wang, David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Mario Limonciello On 2019-01-06 11:09 p.m., Kai Heng Feng wrote: > > >> On Jan 7, 2019, at 05:16, Mark Lord <mlord@pobox.com> wrote: >> >> On 2019-01-06 4:13 p.m., Mark Lord wrote: >>> On 2019-01-06 2:14 p.m., Kai Heng Feng wrote:>> On Jan 5, 2019, at 10:14 PM, Mark Lord >>> <mlord@pobox.com> wrote: >>> .. >>>>> There is even now a special hack in the upstream r8152.c to attempt to detect >>>>> a Dell TB16 dock and disable RX Aggregation in the driver to prevent such issues. >>>>> >>>>> Well.. I have a WD15 dock, not a TB16, and that same hack also catches my dock >>>>> in its net: >>>>> >>>>> [5.794641] usb 4-1.2: Dell TB16 Dock, disable RX aggregation >>>> >>>> The serial should be unique according to Dell. >>>> >>>>> So one issue is that the code is not correctly identifying the dock, >>>>> and the WD15 is claimed to be immune from the r8152 issues. >>>> >>>> The WD15 I tested didn't use that serial number though... >>> >>> What info do you need from me about the WD15 so this can be corrected? >>> >>>>> xhci_hcd 0000:39:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 13 >>>>> comp_code 1 >>>> >>>> This is probably an xHC bug. A similar issue is fixed by commit 9da5a1092b13 >>>> ("xhci: Bad Ethernet performance plugged in ASM1042A host”). >>>> >>>>> I just got that exact message above, with the r8152 in my 1-day old WD15 dock, >>>>> with the TB16 "workaround" enabled in Linux kernel 4.20.0. >>>> >>>> Is the xHC WD15 connected an ASMedia one? >>> >>> I don't know. I *think* it identifies as a DSL6340 (see below). >>> >>> Here is lspci and lsusb: >>> >>> $ lspci -vt >>> -[0000:00]-+-00.0 Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers >>> +-02.0 Intel Corporation UHD Graphics 620 >>> +-04.0 Intel Corporation Skylake Processor Thermal Subsystem >>> +-14.0 Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller >>> +-14.2 Intel Corporation Sunrise Point-LP Thermal subsystem >>> +-15.0 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 >>> +-15.1 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 >>> +-16.0 Intel Corporation Sunrise Point-LP CSME HECI #1 >>> +-1c.0-[01-39]----00.0-[02-39]--+-00.0-[03]-- >>> | +-01.0-[04-38]-- >>> | \-02.0-[39]----00.0 Intel Corporation DSL6340 USB 3.1 >>> Controller [Alpine Ridge] >>> +-1c.4-[3a]----00.0 Qualcomm Atheros QCA6174 802.11ac Wireless Network Adapter >>> +-1d.0-[3b]----00.0 Samsung Electronics Co Ltd Device a808 >>> +-1f.0 Intel Corporation Device 9d4e >>> +-1f.2 Intel Corporation Sunrise Point-LP PMC >>> +-1f.3 Intel Corporation Sunrise Point-LP HD Audio >>> \-1f.4 Intel Corporation Sunrise Point-LP SMBus >> >> >> Mmm.. lspci -vt isn't as verbose as I thought, so here is plain lspci to fill in the blanks: >> >> $ lspci >> 00:00.0 Host bridge: Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM >> Registers (rev 08) >> 00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 620 (rev 07) >> >> 00:04.0 Signal processing controller: Intel Corporation Skylake Processor Thermal Subsystem (rev 08) >> >> 00:14.0 USB controller: Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller (rev 21) >> >> 00:14.2 Signal processing controller: Intel Corporation Sunrise Point-LP Thermal subsystem (rev 21) >> >> 00:15.0 Signal processing controller: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 >> (rev 21) >> 00:15.1 Signal processing controller: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 >> (rev 21) >> 00:16.0 Communication controller: Intel Corporation Sunrise Point-LP CSME HECI #1 (rev 21) >> >> 00:1c.0 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port (rev f1) >> >> 00:1c.4 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port #5 (rev f1) >> >> 00:1d.0 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port #9 (rev f1) >> >> 00:1f.0 ISA bridge: Intel Corporation Device 9d4e (rev 21) >> >> 00:1f.2 Memory controller: Intel Corporation Sunrise Point-LP PMC (rev 21) >> >> 00:1f.3 Audio device: Intel Corporation Sunrise Point-LP HD Audio (rev 21) >> >> 00:1f.4 SMBus: Intel Corporation Sunrise Point-LP SMBus (rev 21) >> >> 01:00.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] >> >> 02:00.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] >> >> 02:01.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] >> >> 02:02.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] >> >> 39:00.0 USB controller: Intel Corporation DSL6340 USB 3.1 Controller [Alpine Ridge] > > So it’s not an ASMedia one. > > Before digging further, please make sure the system firmware (BIOS), Thunderbolt controller NVM and WD15 firmware are all up-to-date. Everything is completely up to date. -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-07 4:13 ` Mark Lord @ 2019-01-07 6:46 ` Kai Heng Feng 2019-01-07 7:01 ` Mark Lord 0 siblings, 1 reply; 80+ messages in thread From: Kai Heng Feng @ 2019-01-07 6:46 UTC (permalink / raw) To: Mark Lord Cc: Ansis Atteka, Hayes Wang, David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Mario Limonciello > On Jan 7, 2019, at 12:13, Mark Lord <mlord@pobox.com> wrote: > > On 2019-01-06 11:09 p.m., Kai Heng Feng wrote: >> >> >>> On Jan 7, 2019, at 05:16, Mark Lord <mlord@pobox.com> wrote: >>> >>> On 2019-01-06 4:13 p.m., Mark Lord wrote: >>>> On 2019-01-06 2:14 p.m., Kai Heng Feng wrote:>> On Jan 5, 2019, at 10:14 PM, Mark Lord >>>> <mlord@pobox.com> wrote: >>>> .. >>>>>> There is even now a special hack in the upstream r8152.c to attempt to detect >>>>>> a Dell TB16 dock and disable RX Aggregation in the driver to prevent such issues. >>>>>> >>>>>> Well.. I have a WD15 dock, not a TB16, and that same hack also catches my dock >>>>>> in its net: >>>>>> >>>>>> [5.794641] usb 4-1.2: Dell TB16 Dock, disable RX aggregation >>>>> >>>>> The serial should be unique according to Dell. >>>>> >>>>>> So one issue is that the code is not correctly identifying the dock, >>>>>> and the WD15 is claimed to be immune from the r8152 issues. >>>>> >>>>> The WD15 I tested didn't use that serial number though... >>>> >>>> What info do you need from me about the WD15 so this can be corrected? >>>> >>>>>> xhci_hcd 0000:39:00.0: ERROR Transfer event TRB DMA ptr not part of current TD ep_index 13 >>>>>> comp_code 1 >>>>> >>>>> This is probably an xHC bug. A similar issue is fixed by commit 9da5a1092b13 >>>>> ("xhci: Bad Ethernet performance plugged in ASM1042A host”). >>>>> >>>>>> I just got that exact message above, with the r8152 in my 1-day old WD15 dock, >>>>>> with the TB16 "workaround" enabled in Linux kernel 4.20.0. >>>>> >>>>> Is the xHC WD15 connected an ASMedia one? >>>> >>>> I don't know. I *think* it identifies as a DSL6340 (see below). >>>> >>>> Here is lspci and lsusb: >>>> >>>> $ lspci -vt >>>> -[0000:00]-+-00.0 Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers >>>> +-02.0 Intel Corporation UHD Graphics 620 >>>> +-04.0 Intel Corporation Skylake Processor Thermal Subsystem >>>> +-14.0 Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller >>>> +-14.2 Intel Corporation Sunrise Point-LP Thermal subsystem >>>> +-15.0 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 >>>> +-15.1 Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 >>>> +-16.0 Intel Corporation Sunrise Point-LP CSME HECI #1 >>>> +-1c.0-[01-39]----00.0-[02-39]--+-00.0-[03]-- >>>> | +-01.0-[04-38]-- >>>> | \-02.0-[39]----00.0 Intel Corporation DSL6340 USB 3.1 >>>> Controller [Alpine Ridge] >>>> +-1c.4-[3a]----00.0 Qualcomm Atheros QCA6174 802.11ac Wireless Network Adapter >>>> +-1d.0-[3b]----00.0 Samsung Electronics Co Ltd Device a808 >>>> +-1f.0 Intel Corporation Device 9d4e >>>> +-1f.2 Intel Corporation Sunrise Point-LP PMC >>>> +-1f.3 Intel Corporation Sunrise Point-LP HD Audio >>>> \-1f.4 Intel Corporation Sunrise Point-LP SMBus >>> >>> >>> Mmm.. lspci -vt isn't as verbose as I thought, so here is plain lspci to fill in the blanks: >>> >>> $ lspci >>> 00:00.0 Host bridge: Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM >>> Registers (rev 08) >>> 00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 620 (rev 07) >>> >>> 00:04.0 Signal processing controller: Intel Corporation Skylake Processor Thermal Subsystem (rev 08) >>> >>> 00:14.0 USB controller: Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller (rev 21) >>> >>> 00:14.2 Signal processing controller: Intel Corporation Sunrise Point-LP Thermal subsystem (rev 21) >>> >>> 00:15.0 Signal processing controller: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 >>> (rev 21) >>> 00:15.1 Signal processing controller: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 >>> (rev 21) >>> 00:16.0 Communication controller: Intel Corporation Sunrise Point-LP CSME HECI #1 (rev 21) >>> >>> 00:1c.0 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port (rev f1) >>> >>> 00:1c.4 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port #5 (rev f1) >>> >>> 00:1d.0 PCI bridge: Intel Corporation Sunrise Point-LP PCI Express Root Port #9 (rev f1) >>> >>> 00:1f.0 ISA bridge: Intel Corporation Device 9d4e (rev 21) >>> >>> 00:1f.2 Memory controller: Intel Corporation Sunrise Point-LP PMC (rev 21) >>> >>> 00:1f.3 Audio device: Intel Corporation Sunrise Point-LP HD Audio (rev 21) >>> >>> 00:1f.4 SMBus: Intel Corporation Sunrise Point-LP SMBus (rev 21) >>> >>> 01:00.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] >>> >>> 02:00.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] >>> >>> 02:01.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] >>> >>> 02:02.0 PCI bridge: Intel Corporation DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] >>> >>> 39:00.0 USB controller: Intel Corporation DSL6340 USB 3.1 Controller [Alpine Ridge] >> >> So it’s not an ASMedia one. >> >> Before digging further, please make sure the system firmware (BIOS), Thunderbolt controller NVM and WD15 firmware are all up-to-date. > > Everything is completely up to date. Do you happen to use a Dell system? We can do some test here. Kai-Heng > > > -- > Mark Lord > Real-Time Remedies Inc. > mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: r8152: data corruption in various scenarios 2019-01-07 6:46 ` Kai Heng Feng @ 2019-01-07 7:01 ` Mark Lord 0 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2019-01-07 7:01 UTC (permalink / raw) To: Kai Heng Feng Cc: Ansis Atteka, Hayes Wang, David Miller, greg, romieu, netdev, nic_swsd, linux-kernel, linux-usb, Mario Limonciello On 2019-01-07 1:46 a.m., Kai Heng Feng wrote: > > Do you happen to use a Dell system? We can do some test here. Yes. It is a Dell XPS 13 9360 i7-8550U notebook, with the Dell WD15 USB-C dock. -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 16:43 ` Mark Lord 2016-11-24 17:00 ` Mark Lord 2016-11-24 17:11 ` David Miller @ 2016-11-24 18:42 ` Greg KH 2016-11-24 18:58 ` Mark Lord 2016-11-25 6:31 ` Hayes Wang 3 siblings, 1 reply; 80+ messages in thread From: Greg KH @ 2016-11-24 18:42 UTC (permalink / raw) To: Mark Lord Cc: David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On Thu, Nov 24, 2016 at 11:43:53AM -0500, Mark Lord wrote: > On 16-11-24 11:21 AM, David Miller wrote: > > From: Hayes Wang <hayeswang@realtek.com> > > Date: Thu, 24 Nov 2016 13:26:55 +0000 > > > > > I don't think the garbage results from our driver or device. > > This is my impression with what has been presented so far as well. > > It's not garbage. > > The latest run with the debug code I posted here earlier just spat out this below. > Using coherent (guarded, non-cacheable) RX buffers, with mb() calls: > > [ 15.199157] r8152_check_rx_desc: rx_desc looks bad. > [ 15.204270] r8152_rx_bottom: offset=0/3376 bad rx_desc > [ 15.209584] r8152_dump_rx_desc: 3d435253 3034336d 202f3a30 47524154 2f3d5445 3034336d rx_len=21075 > > The bad data in this case is ASCII: > > "SRC=m3400:/ TARGET=/m340" Have you tried using usbmon? Details for how to use it is in Documentation/usbmon.txt and it might help you rule out the driver vs. the USB host controller issues as it sees the raw data the USB host controller sees before it sends it to the driver. thanks, greg k-h ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 18:42 ` [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable Greg KH @ 2016-11-24 18:58 ` Mark Lord 0 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-24 18:58 UTC (permalink / raw) To: Greg KH Cc: David Miller, hayeswang, netdev, nic_swsd, linux-kernel, linux-usb On 16-11-24 01:42 PM, Greg KH wrote: > > Have you tried using usbmon? This system is running rootfs over NFS, so usbmon isn't realistically going to be usable in that scenario without a lot of reconfiguration of the setup (which in itself might obscure the original problem). There is a hardware USB analyzer in the building though. But it requires a MS-Windows machine (very scarce here, I don't have one) for the incredibly user-unfriendly software. I'm not sure if it can be setup to stop the trace somehow at the right point either, as it takes overnight runs usually to catch an occurrence of the issue. I also seem to recall that it only exports data captures in a proprietary format that only that brand of software/device can read, but perhaps that might not be true. Would still need to find a MS-Windows machine/license to even check it out though. ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 16:43 ` Mark Lord ` (2 preceding siblings ...) 2016-11-24 18:42 ` [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable Greg KH @ 2016-11-25 6:31 ` Hayes Wang 2016-11-25 6:51 ` Hayes Wang 3 siblings, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-25 6:31 UTC (permalink / raw) To: Mark Lord, David Miller; +Cc: netdev, nic_swsd, linux-kernel, linux-usb Mark Lord [mailto:mlord@pobox.com] > Sent: Friday, November 25, 2016 12:44 AM [...] > The bad data in this case is ASCII: > > "SRC=m3400:/ TARGET=/m340" > > This data is what is seen in /run/mount/utab, a file that is read/written over NFS on > each boot. > > "SRC=m3400:/ TARGET=/m3400 ROOT=/ > ATTRS=nolock,addr=192.168.8.1\n" > > But how does this ASCII data end up at offset zero of the rx buffer?? > Not possible -- this isn't even stale data, because only an rx_desc could > be at that offset in that buffer. > > So even if this were a platform memory coherency issue, one should still > never see ASCII data at the beginning of an rx buffer. The driver NEVER > writes anything to the rx buffers. Only the USB hardware ever does. > > And only the r8152 dongle/driver exhibits this issue. > Other USB dongles do not. They *might* still have such issues, > but because they use software checksums, the bad packets are caught/rejected. Do you test it by rebooting? Maybe you could try a patch commit 93fe9b183840 ("r8152: reset the bmu"). However, it should only occur for the first urb buffer after rx is reset. I don't think you would reset the rx frequently, so the situation seems to be different. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 6:31 ` Hayes Wang @ 2016-11-25 6:51 ` Hayes Wang 2016-11-25 12:35 ` Mark Lord 0 siblings, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-25 6:51 UTC (permalink / raw) To: Mark Lord, David Miller; +Cc: netdev, nic_swsd, linux-kernel, linux-usb > Mark Lord [mailto:mlord@pobox.com] > > Sent: Friday, November 25, 2016 12:44 AM > [...] > > The bad data in this case is ASCII: > > > > "SRC=m3400:/ TARGET=/m340" > > > > This data is what is seen in /run/mount/utab, a file that is read/written over NFS > on > > each boot. > > > > "SRC=m3400:/ TARGET=/m3400 ROOT=/ > > ATTRS=nolock,addr=192.168.8.1\n" > > > > But how does this ASCII data end up at offset zero of the rx buffer?? > > Not possible -- this isn't even stale data, because only an rx_desc could > > be at that offset in that buffer. > > > > So even if this were a platform memory coherency issue, one should still > > never see ASCII data at the beginning of an rx buffer. The driver NEVER > > writes anything to the rx buffers. Only the USB hardware ever does. > > > > And only the r8152 dongle/driver exhibits this issue. > > Other USB dongles do not. They *might* still have such issues, > > but because they use software checksums, the bad packets are caught/rejected. > > Do you test it by rebooting? Maybe you could try a patch > commit 93fe9b183840 ("r8152: reset the bmu"). However, it should > only occur for the first urb buffer after rx is reset. I don't > think you would reset the rx frequently, so the situation seems > to be different. Forgive me. I provide wrong information. This is about RTL8153, not RTL8152. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-25 6:51 ` Hayes Wang @ 2016-11-25 12:35 ` Mark Lord 0 siblings, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-25 12:35 UTC (permalink / raw) To: Hayes Wang, David Miller; +Cc: netdev, nic_swsd, linux-kernel, linux-usb On 16-11-25 01:51 AM, Hayes Wang wrote: > > Forgive me. I provide wrong information. This is about RTL8153, not RTL8152. No problem. Thanks for trying though. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-24 12:31 ` Mark Lord 2016-11-24 13:26 ` Hayes Wang @ 2016-11-24 16:19 ` David Miller 1 sibling, 0 replies; 80+ messages in thread From: David Miller @ 2016-11-24 16:19 UTC (permalink / raw) To: mlord; +Cc: hayeswang, netdev, nic_swsd, linux-kernel, linux-usb From: Mark Lord <mlord@pobox.com> Date: Thu, 24 Nov 2016 07:31:17 -0500 > Any way we look at it though, the chip/driver are simply unreliable, > and relying upon hardware checksums (which fail due to the driver > looking at garbage rather than the checksum bits) leads to data > corruption. If the cpu/DMA implementation is the problem, then turning off checksums is not an appropriate fix at all. In fact, we have no idea what the cause is yet. That makes turning off random features no more than grasping at straws and makes no sense at all upstream. It may make sense for you to do such a change locally in _your_ tree to fix your situation temporarily. But upstream we shouldn't be doing it. ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable 2016-11-23 13:41 ` Mark Lord 2016-11-23 15:12 ` Hayes Wang @ 2016-11-24 12:37 ` Hayes Wang 1 sibling, 0 replies; 80+ messages in thread From: Hayes Wang @ 2016-11-24 12:37 UTC (permalink / raw) To: Mark Lord, netdev; +Cc: nic_swsd, linux-kernel, linux-usb Mark Lord [mailto:mlord@pobox.com] > Sent: Wednesday, November 23, 2016 9:41 PM [...] > >static void r8153_set_rx_early_size(struct r8152 *tp) > >{ > > u32 mtu = tp->netdev->mtu; > > u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 4; > > > > ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data); > >} > > How is ocp_data used by the hardware? > Shouldn't the calculation also include sizeof(rx_desc) in there somewhere? I check your question with our hw engineers, and you are right. The size of rx descriptor should be calculated, too. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH net 2/2] r8152: rx descriptor check 2016-11-11 7:15 [PATCH net 0/2] r8152: rx patches Hayes Wang 2016-11-11 7:15 ` [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable Hayes Wang @ 2016-11-11 7:15 ` Hayes Wang 2016-11-11 12:13 ` Francois Romieu 2016-11-13 17:39 ` David Miller 1 sibling, 2 replies; 80+ messages in thread From: Hayes Wang @ 2016-11-11 7:15 UTC (permalink / raw) To: netdev; +Cc: nic_swsd, linux-kernel, linux-usb, mlord, Hayes Wang For some platforms, the data in memory is not the same with the one from the device. That is, the data of memory is unbelievable. The check is used to find out this situation. Signed-off-by: Hayes Wang <hayeswang@realtek.com> --- drivers/net/usb/r8152.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 0e42a78..e766121 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1756,6 +1756,43 @@ static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc) return checksum; } +static int invalid_rx_desc(struct r8152 *tp, struct rx_desc *rx_desc) +{ + u32 opts1 = le32_to_cpu(rx_desc->opts1); + u32 opts2 = le32_to_cpu(rx_desc->opts2); + unsigned int pkt_len = opts1 & RX_LEN_MASK; + + switch (tp->version) { + case RTL_VER_01: + case RTL_VER_02: + if (pkt_len > RTL8152_RMS) + return -EIO; + break; + default: + if (pkt_len > RTL8153_RMS) + return -EIO; + break; + } + + switch (opts2 & (RD_IPV4_CS | RD_IPV6_CS)) { + case (RD_IPV4_CS | RD_IPV6_CS): + return -EIO; + case RD_IPV4_CS: + case RD_IPV6_CS: + switch (opts2 & (RD_UDP_CS | RD_TCP_CS)) { + case (RD_UDP_CS | RD_TCP_CS): + return -EIO; + default: + break; + } + break; + default: + break; + } + + return 0; +} + static int rx_bottom(struct r8152 *tp, int budget) { unsigned long flags; @@ -1812,6 +1849,18 @@ static int rx_bottom(struct r8152 *tp, int budget) unsigned int pkt_len; struct sk_buff *skb; + if (unlikely(invalid_rx_desc(tp, rx_desc))) { + if (net_ratelimit()) + netif_err(tp, rx_err, netdev, + "Memory unbelievable\n"); + if (tp->netdev->features & NETIF_F_RXCSUM) { + tp->netdev->features &= ~NETIF_F_RXCSUM; + netif_err(tp, rx_err, netdev, + "rx checksum off\n"); + } + break; + } + pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; if (pkt_len < ETH_ZLEN) break; -- 2.7.4 ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH net 2/2] r8152: rx descriptor check 2016-11-11 7:15 ` [PATCH net 2/2] r8152: rx descriptor check Hayes Wang @ 2016-11-11 12:13 ` Francois Romieu 2016-11-12 13:21 ` Mark Lord 2016-11-14 6:43 ` Hayes Wang 2016-11-13 17:39 ` David Miller 1 sibling, 2 replies; 80+ messages in thread From: Francois Romieu @ 2016-11-11 12:13 UTC (permalink / raw) To: Hayes Wang; +Cc: netdev, nic_swsd, linux-kernel, linux-usb, mlord Hayes Wang <hayeswang@realtek.com> : > For some platforms, the data in memory is not the same with the one > from the device. That is, the data of memory is unbelievable. The > check is used to find out this situation. Invalid packet size corrupted receive descriptors in Realtek's device reminds of CVE-2009-4537. Is the silicium of both devices different enough to prevent the same exploit to happen ? -- Ueimor ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 2/2] r8152: rx descriptor check 2016-11-11 12:13 ` Francois Romieu @ 2016-11-12 13:21 ` Mark Lord 2016-11-14 6:43 ` Hayes Wang 1 sibling, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-12 13:21 UTC (permalink / raw) To: Francois Romieu, Hayes Wang; +Cc: netdev, nic_swsd, linux-kernel, linux-usb On 16-11-11 07:13 AM, Francois Romieu wrote: > Hayes Wang <hayeswang@realtek.com> : >> For some platforms, the data in memory is not the same with the one >> from the device. That is, the data of memory is unbelievable. The >> check is used to find out this situation. > > Invalid packet size corrupted receive descriptors in Realtek's device > reminds of CVE-2009-4537. > > Is the silicium of both devices different enough to prevent the same > exploit to happen ? I don't know if the hardware can do it, but the existing Linux device driver regularly attempts to process huge unreal packet sizes here. I've had to patch it to reject "packets" larger than the configured MRU. -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 2/2] r8152: rx descriptor check 2016-11-11 12:13 ` Francois Romieu 2016-11-12 13:21 ` Mark Lord @ 2016-11-14 6:43 ` Hayes Wang 2016-11-15 1:10 ` Francois Romieu 1 sibling, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-14 6:43 UTC (permalink / raw) To: Francois Romieu; +Cc: netdev, nic_swsd, linux-kernel, linux-usb, mlord Francois Romieu [mailto:romieu@fr.zoreil.com] > Sent: Friday, November 11, 2016 8:13 PM [...] > Invalid packet size corrupted receive descriptors in Realtek's device > reminds of CVE-2009-4537. Do you mean that the driver would get a packet exceed the size which is set to RxMaxSize? I check it with our hw engineers. They don't get any issue about RxMaxSize. And their test for RxMaxSize register is fine. > Is the silicium of both devices different enough to prevent the same > exploit to happen ? For this case, I don't think the device provide a invalid value for the receive descriptors. However, the driver sees a different value. That is why I say the memory is unbelievable. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 2/2] r8152: rx descriptor check 2016-11-14 6:43 ` Hayes Wang @ 2016-11-15 1:10 ` Francois Romieu 2016-11-17 3:05 ` Hayes Wang 0 siblings, 1 reply; 80+ messages in thread From: Francois Romieu @ 2016-11-15 1:10 UTC (permalink / raw) To: Hayes Wang; +Cc: netdev, nic_swsd, linux-kernel, linux-usb, mlord Hayes Wang <hayeswang@realtek.com> : > Francois Romieu [mailto:romieu@fr.zoreil.com] > > Sent: Friday, November 11, 2016 8:13 PM > [...] > > Invalid packet size corrupted receive descriptors in Realtek's device > > reminds of CVE-2009-4537. > > Do you mean that the driver would get a packet exceed the size > which is set to RxMaxSize ? If it was possible to get it wrong once, it should be possible to get it wrong twice, especially if some part of the hardware design is recycled. I don't mean anything else. I won't speculate about some cache consistency issue or some badly aborted dma transaction to explain the memory corruption. -- Ueimor ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 2/2] r8152: rx descriptor check 2016-11-15 1:10 ` Francois Romieu @ 2016-11-17 3:05 ` Hayes Wang 0 siblings, 0 replies; 80+ messages in thread From: Hayes Wang @ 2016-11-17 3:05 UTC (permalink / raw) To: Francois Romieu; +Cc: netdev, nic_swsd, linux-kernel, linux-usb, mlord Francois Romieu [mailto:romieu@fr.zoreil.com] > Sent: Tuesday, November 15, 2016 9:11 AM [...] > If it was possible to get it wrong once, it should be possible to > get it wrong twice, especially if some part of the hardware design > is recycled. I don't mean anything else. I agree with you. However, I have to let it could be reproduced for confirming it. Besides, the behavior is different for PCIe and USB device. There is no action of DMA for USB device. It is done by the USB host controller. And, the USB host controller wouldn't allow the device sends a data which is more than the size of the buffer. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 2/2] r8152: rx descriptor check 2016-11-11 7:15 ` [PATCH net 2/2] r8152: rx descriptor check Hayes Wang 2016-11-11 12:13 ` Francois Romieu @ 2016-11-13 17:39 ` David Miller 2016-11-13 20:34 ` Mark Lord 2016-11-14 7:03 ` Hayes Wang 1 sibling, 2 replies; 80+ messages in thread From: David Miller @ 2016-11-13 17:39 UTC (permalink / raw) To: hayeswang; +Cc: netdev, nic_swsd, linux-kernel, linux-usb, mlord From: Hayes Wang <hayeswang@realtek.com> Date: Fri, 11 Nov 2016 15:15:41 +0800 > For some platforms, the data in memory is not the same with the one > from the device. That is, the data of memory is unbelievable. The > check is used to find out this situation. > > Signed-off-by: Hayes Wang <hayeswang@realtek.com> I'm all for adding consistency checks, but I disagree with proceeding in this manner for this. If you add this patch now, there is a much smaller likelyhood that you will work with a high priority to figure out _why_ this is happening. For all we know this could be a platform bug in the DMA API for the systems in question. It could also be a bug elsewhere in the driver, either in setting up the descriptor DMA mappings or how the chip is programmed. Either way the true cause must be found before we start throwing changes like this into the driver. I'm not applying this series, sorry. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 2/2] r8152: rx descriptor check 2016-11-13 17:39 ` David Miller @ 2016-11-13 20:34 ` Mark Lord 2016-11-13 20:38 ` Mark Lord 2016-11-14 7:23 ` Hayes Wang 2016-11-14 7:03 ` Hayes Wang 1 sibling, 2 replies; 80+ messages in thread From: Mark Lord @ 2016-11-13 20:34 UTC (permalink / raw) To: David Miller, hayeswang; +Cc: netdev, nic_swsd, linux-kernel, linux-usb On 16-11-13 12:39 PM, David Miller wrote: > From: Hayes Wang <hayeswang@realtek.com> > Date: Fri, 11 Nov 2016 15:15:41 +0800 > >> For some platforms, the data in memory is not the same with the one >> from the device. That is, the data of memory is unbelievable. The >> check is used to find out this situation. >> >> Signed-off-by: Hayes Wang <hayeswang@realtek.com> > > I'm all for adding consistency checks, but I disagree with proceeding > in this manner for this. > > If you add this patch now, there is a much smaller likelyhood that you > will work with a high priority to figure out _why_ this is happening. > > For all we know this could be a platform bug in the DMA API for the > systems in question. > > It could also be a bug elsewhere in the driver, either in setting up > the descriptor DMA mappings or how the chip is programmed. > > Either way the true cause must be found before we start throwing > changes like this into the driver. I agree. The system I use it with is a 32-bit ppc476, with non-coherent RAM, and using 16KB page sizes. The dongle instantly becomes a lot more reliable when r8152.c is updated to use usb_alloc_coherent() for URB buffers, rather than kmalloc(). Not sure why that would be though, as the USB stack normally would handle kmalloc'd buffers just fine. It is calling the appropriate routines, which boil down to invalidating the dcache lines (for inbound bulk xfers) as part of usb_submit_urb(), and yet the problem there persists. It could be caused by cache-line sharing with other allocations, but that seems unlikely as the kmalloc() size is 16384 bytes per buffer. Perhaps the driver is somehow accessing the buffer space again after doing usb_submit_urb()? That would certainly produce this kind of behaviour. Or maybe there's just a memory barrier missing somewhere in path. The really weird thing is that ASIX-based dongles (which use a different driver) don't have this problem, and yet they also use kmalloc'd buffers. I have access to the test system only for a day or two a week, and it takes a few hours to do a good test as to whether something helps or not. I'll continue to poke at it as time and New Ideas permit. New Ideas welcome! -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 2/2] r8152: rx descriptor check 2016-11-13 20:34 ` Mark Lord @ 2016-11-13 20:38 ` Mark Lord 2016-11-14 7:23 ` Hayes Wang 1 sibling, 0 replies; 80+ messages in thread From: Mark Lord @ 2016-11-13 20:38 UTC (permalink / raw) To: David Miller, hayeswang; +Cc: netdev, nic_swsd, linux-kernel, linux-usb On 16-11-13 03:34 PM, Mark Lord wrote: > > The system I use it with is a 32-bit ppc476, with non-coherent RAM, > and using 16KB page sizes. > > The dongle instantly becomes a lot more reliable when r8152.c is updated > to use usb_alloc_coherent() for URB buffers, rather than kmalloc(). > > Not sure why that would be though, as the USB stack normally would handle > kmalloc'd buffers just fine. It is calling the appropriate routines, > which boil down to invalidating the dcache lines (for inbound bulk xfers) > as part of usb_submit_urb(), and yet the problem there persists. > > It could be caused by cache-line sharing with other allocations, but that seems > unlikely as the kmalloc() size is 16384 bytes per buffer. Perhaps the driver > is somehow accessing the buffer space again after doing usb_submit_urb()? > That would certainly produce this kind of behaviour. > > Or maybe there's just a memory barrier missing somewhere in path. > > The really weird thing is that ASIX-based dongles (which use a different driver) > don't have this problem, and yet they also use kmalloc'd buffers. > > I have access to the test system only for a day or two a week, > and it takes a few hours to do a good test as to whether something helps or not. > I'll continue to poke at it as time and New Ideas permit. Oh, and the problems did not exist with the 3.14.xx kernels and earlier. They began to show up when we tried 3.16.xx and all newer kernels. The difference there is that RX checksums were enabled in hardware as of 3.16.xx, and thus the network stack began accepting bad packets from the r8152 driver. I don't know if the ASIX driver uses hardware checksums or just software checksums. That might explain why it is more reliable here. -- Mark Lord Real-Time Remedies Inc. mlord@pobox.com ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 2/2] r8152: rx descriptor check 2016-11-13 20:34 ` Mark Lord 2016-11-13 20:38 ` Mark Lord @ 2016-11-14 7:23 ` Hayes Wang 2016-11-14 17:27 ` David Miller 1 sibling, 1 reply; 80+ messages in thread From: Hayes Wang @ 2016-11-14 7:23 UTC (permalink / raw) To: Mark Lord, David Miller; +Cc: netdev, nic_swsd, linux-kernel, linux-usb Mark Lord [mailto:mlord@pobox.com] > Sent: Monday, November 14, 2016 4:34 AM [...] > Perhaps the driver > is somehow accessing the buffer space again after doing usb_submit_urb()? > That would certainly produce this kind of behaviour. I don't think so. First, the driver only read the received buffer. That is, the driver would not change (or write) the data. Second, The driver would lose the point address of the received buffer after submitting the urb to the USB host controller, until the transfer is completed by the USB host controller. That is, the driver doesn't how to access the buffer after calling usb_submit_urb(). Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH net 2/2] r8152: rx descriptor check 2016-11-14 7:23 ` Hayes Wang @ 2016-11-14 17:27 ` David Miller 0 siblings, 0 replies; 80+ messages in thread From: David Miller @ 2016-11-14 17:27 UTC (permalink / raw) To: hayeswang; +Cc: mlord, netdev, nic_swsd, linux-kernel, linux-usb From: Hayes Wang <hayeswang@realtek.com> Date: Mon, 14 Nov 2016 07:23:51 +0000 > Mark Lord [mailto:mlord@pobox.com] >> Sent: Monday, November 14, 2016 4:34 AM > [...] >> Perhaps the driver >> is somehow accessing the buffer space again after doing usb_submit_urb()? >> That would certainly produce this kind of behaviour. > > I don't think so. First, the driver only read the received buffer. > That is, the driver would not change (or write) the data. Second, > The driver would lose the point address of the received buffer > after submitting the urb to the USB host controller, until the > transfer is completed by the USB host controller. That is, the > driver doesn't how to access the buffer after calling usb_submit_urb(). This is why it's most likely some DMA implementation issue or similar. ^ permalink raw reply [flat|nested] 80+ messages in thread
* RE: [PATCH net 2/2] r8152: rx descriptor check 2016-11-13 17:39 ` David Miller 2016-11-13 20:34 ` Mark Lord @ 2016-11-14 7:03 ` Hayes Wang 1 sibling, 0 replies; 80+ messages in thread From: Hayes Wang @ 2016-11-14 7:03 UTC (permalink / raw) To: David Miller; +Cc: netdev, nic_swsd, linux-kernel, linux-usb, mlord David Miller [mailto:davem@davemloft.net] > Sent: Monday, November 14, 2016 1:40 AM [...] > If you add this patch now, there is a much smaller likelyhood that you > will work with a high priority to figure out _why_ this is happening. > > For all we know this could be a platform bug in the DMA API for the > systems in question. > > It could also be a bug elsewhere in the driver, either in setting up > the descriptor DMA mappings or how the chip is programmed. > > Either way the true cause must be found before we start throwing > changes like this into the driver. Our hw engineer could check our device, and I could check the driver. However, for the other parts, such as the USB host controller or memory, it is difficult for me to make sure whether they are correct or not. I could only promise our devices and driver work fine. Best Regards, Hayes ^ permalink raw reply [flat|nested] 80+ messages in thread
end of thread, other threads:[~2019-01-07 19:24 UTC | newest] Thread overview: 80+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-11-11 7:15 [PATCH net 0/2] r8152: rx patches Hayes Wang 2016-11-11 7:15 ` [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable Hayes Wang 2016-11-17 3:36 ` Hayes Wang 2016-11-17 14:14 ` Mark Lord 2016-11-17 14:25 ` Mark Lord [not found] ` <d683c019-4e0f-6fe6-368c-c4fc86c72fe6@pobox.com> 2016-11-18 7:57 ` Hayes Wang 2016-11-18 12:03 ` Mark Lord 2016-11-22 13:12 ` Mark Lord 2016-11-23 3:52 ` Hayes Wang 2016-11-23 13:41 ` Mark Lord 2016-11-23 15:12 ` Hayes Wang 2016-11-23 19:29 ` Mark Lord 2016-11-24 3:24 ` Hayes Wang 2016-11-24 12:31 ` Mark Lord 2016-11-24 13:26 ` Hayes Wang 2016-11-24 15:24 ` Mark Lord 2016-11-25 6:11 ` Hayes Wang 2016-11-25 12:36 ` Mark Lord 2016-11-24 16:21 ` David Miller 2016-11-24 16:43 ` Mark Lord 2016-11-24 17:00 ` Mark Lord 2016-11-24 17:13 ` David Miller 2016-11-24 17:11 ` David Miller 2016-11-24 18:34 ` Mark Lord 2016-11-24 18:49 ` Mark Lord 2016-11-24 19:00 ` Greg KH 2016-11-24 19:10 ` Mark Lord 2016-11-24 19:17 ` Greg KH 2016-11-25 9:52 ` Hayes Wang 2016-11-25 13:32 ` Mark Lord 2016-11-25 0:27 ` Francois Romieu 2016-11-25 3:49 ` Mark Lord 2016-11-25 9:53 ` Greg KH 2016-11-25 12:34 ` Mark Lord 2016-11-25 12:41 ` Mark Lord 2016-11-25 14:22 ` Greg KH 2016-11-25 14:35 ` Mark Lord 2016-11-25 12:49 ` Mark Lord 2016-11-25 14:24 ` Greg KH 2016-11-25 16:58 ` David Miller 2016-11-30 11:58 ` Hayes Wang 2016-12-09 3:23 ` Hayes Wang 2016-12-09 13:05 ` Mark Lord 2017-01-01 0:07 ` Ansis Atteka 2017-01-03 0:40 ` Ansis Atteka 2017-01-03 13:19 ` Mark Lord 2017-01-09 7:58 ` Hayes Wang 2019-01-05 14:14 ` r8152: data corruption in various scenarios Mark Lord 2019-01-05 14:22 ` Mark Lord 2019-01-06 19:14 ` Kai Heng Feng 2019-01-06 21:13 ` Mark Lord 2019-01-06 21:16 ` Mark Lord 2019-01-07 3:53 ` Hayes Wang 2019-01-07 16:01 ` Mario.Limonciello 2019-01-07 18:06 ` Mark Lord 2019-01-07 18:27 ` Mario.Limonciello 2019-01-07 19:24 ` Mark Lord 2019-01-07 4:09 ` Kai Heng Feng 2019-01-07 4:13 ` Mark Lord 2019-01-07 6:46 ` Kai Heng Feng 2019-01-07 7:01 ` Mark Lord 2016-11-24 18:42 ` [PATCH net 1/2] r8152: fix the sw rx checksum is unavailable Greg KH 2016-11-24 18:58 ` Mark Lord 2016-11-25 6:31 ` Hayes Wang 2016-11-25 6:51 ` Hayes Wang 2016-11-25 12:35 ` Mark Lord 2016-11-24 16:19 ` David Miller 2016-11-24 12:37 ` Hayes Wang 2016-11-11 7:15 ` [PATCH net 2/2] r8152: rx descriptor check Hayes Wang 2016-11-11 12:13 ` Francois Romieu 2016-11-12 13:21 ` Mark Lord 2016-11-14 6:43 ` Hayes Wang 2016-11-15 1:10 ` Francois Romieu 2016-11-17 3:05 ` Hayes Wang 2016-11-13 17:39 ` David Miller 2016-11-13 20:34 ` Mark Lord 2016-11-13 20:38 ` Mark Lord 2016-11-14 7:23 ` Hayes Wang 2016-11-14 17:27 ` David Miller 2016-11-14 7:03 ` Hayes Wang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).