diff options
author | David Brownell <david-b@pacbell.net> | 2005-04-18 20:39:34 -0400 |
---|---|---|
committer | Greg K-H <gregkh@suse.de> | 2005-04-18 20:39:34 -0400 |
commit | 6cdee106e7571751ecc0e9f96606322f88b64a8d (patch) | |
tree | edc75e42c3f3e9bbfecabc12a51b0f6d1bb37930 | |
parent | 1bc3c9e1e44c2059fe2ffa6ff70ad0a925d7b05f (diff) |
[PATCH] usb gadget: ethernet/rndis updates
Updates to the Ethernet/RNDIS gadget driver (mostly for RNDIS):
- Fix brown-paper bag goof with RNDIS packet TX ... the wrong length
field got set, so Windows would ignore data packets it received.
- More consistent handling of CDC output filters (but not yet hooking
things up so RNDIS uses the mechanism).
- Zerocopy RX for RNDIS packets too (saving CPU cycles).
- Use the pre-allocated interrupt/status request and buffer, rather
than allocating and freeing one of each every few seconds (which
could fail).
- Some more "sparse" tweaks, making both dual-speed and single-speed
configurations happier.
- RNDIS speeds are reported in units of 100bps, not bps.
Plus two minor cleanups (whitespace, messaging).
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/gadget/ether.c | 56 | ||||
-rw-r--r-- | drivers/usb/gadget/rndis.c | 40 | ||||
-rw-r--r-- | drivers/usb/gadget/rndis.h | 3 |
3 files changed, 45 insertions, 54 deletions
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index cff9fb0b73cc..3993156c2e82 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c | |||
@@ -100,6 +100,8 @@ static const char driver_desc [] = DRIVER_DESC; | |||
100 | 100 | ||
101 | /* CDC and RNDIS support the same host-chosen outgoing packet filters. */ | 101 | /* CDC and RNDIS support the same host-chosen outgoing packet filters. */ |
102 | #define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ | 102 | #define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ |
103 | |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ | ||
104 | |USB_CDC_PACKET_TYPE_PROMISCUOUS \ | ||
103 | |USB_CDC_PACKET_TYPE_DIRECTED) | 105 | |USB_CDC_PACKET_TYPE_DIRECTED) |
104 | 106 | ||
105 | 107 | ||
@@ -322,12 +324,18 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR); | |||
322 | /* also defer IRQs on highspeed TX */ | 324 | /* also defer IRQs on highspeed TX */ |
323 | #define TX_DELAY qmult | 325 | #define TX_DELAY qmult |
324 | 326 | ||
325 | #define BITRATE(g) (((g)->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS) | 327 | static inline int BITRATE(struct usb_gadget *g) |
328 | { | ||
329 | return (g->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS; | ||
330 | } | ||
326 | 331 | ||
327 | #else /* full speed (low speed doesn't do bulk) */ | 332 | #else /* full speed (low speed doesn't do bulk) */ |
328 | #define qlen(gadget) DEFAULT_QLEN | 333 | #define qlen(gadget) DEFAULT_QLEN |
329 | 334 | ||
330 | #define BITRATE(g) FS_BPS | 335 | static inline int BITRATE(struct usb_gadget *g) |
336 | { | ||
337 | return FS_BPS; | ||
338 | } | ||
331 | #endif | 339 | #endif |
332 | 340 | ||
333 | 341 | ||
@@ -1167,7 +1175,7 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) | |||
1167 | eth_reset_config (dev); | 1175 | eth_reset_config (dev); |
1168 | 1176 | ||
1169 | /* default: pass all packets, no multicast filtering */ | 1177 | /* default: pass all packets, no multicast filtering */ |
1170 | dev->cdc_filter = 0x000f; | 1178 | dev->cdc_filter = DEFAULT_FILTER; |
1171 | 1179 | ||
1172 | switch (number) { | 1180 | switch (number) { |
1173 | case DEV_CONFIG_VALUE: | 1181 | case DEV_CONFIG_VALUE: |
@@ -1343,9 +1351,9 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) | |||
1343 | struct eth_dev *dev = get_gadget_data (gadget); | 1351 | struct eth_dev *dev = get_gadget_data (gadget); |
1344 | struct usb_request *req = dev->req; | 1352 | struct usb_request *req = dev->req; |
1345 | int value = -EOPNOTSUPP; | 1353 | int value = -EOPNOTSUPP; |
1346 | u16 wIndex = ctrl->wIndex; | 1354 | u16 wIndex = (__force u16) ctrl->wIndex; |
1347 | u16 wValue = ctrl->wValue; | 1355 | u16 wValue = (__force u16) ctrl->wValue; |
1348 | u16 wLength = ctrl->wLength; | 1356 | u16 wLength = (__force u16) ctrl->wLength; |
1349 | 1357 | ||
1350 | /* descriptors just go into the pre-allocated ep0 buffer, | 1358 | /* descriptors just go into the pre-allocated ep0 buffer, |
1351 | * while config change events may enable network traffic. | 1359 | * while config change events may enable network traffic. |
@@ -1693,7 +1701,7 @@ rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags) | |||
1693 | 1701 | ||
1694 | /* Some platforms perform better when IP packets are aligned, | 1702 | /* Some platforms perform better when IP packets are aligned, |
1695 | * but on at least one, checksumming fails otherwise. Note: | 1703 | * but on at least one, checksumming fails otherwise. Note: |
1696 | * this doesn't account for variable-sized RNDIS headers. | 1704 | * RNDIS headers involve variable numbers of LE32 values. |
1697 | */ | 1705 | */ |
1698 | skb_reserve(skb, NET_IP_ALIGN); | 1706 | skb_reserve(skb, NET_IP_ALIGN); |
1699 | 1707 | ||
@@ -1730,9 +1738,11 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req) | |||
1730 | #ifdef CONFIG_USB_ETH_RNDIS | 1738 | #ifdef CONFIG_USB_ETH_RNDIS |
1731 | /* we know MaxPacketsPerTransfer == 1 here */ | 1739 | /* we know MaxPacketsPerTransfer == 1 here */ |
1732 | if (dev->rndis) | 1740 | if (dev->rndis) |
1733 | rndis_rm_hdr (req->buf, &(skb->len)); | 1741 | status = rndis_rm_hdr (skb); |
1734 | #endif | 1742 | #endif |
1735 | if (ETH_HLEN > skb->len || skb->len > ETH_FRAME_LEN) { | 1743 | if (status < 0 |
1744 | || ETH_HLEN > skb->len | ||
1745 | || skb->len > ETH_FRAME_LEN) { | ||
1736 | dev->stats.rx_errors++; | 1746 | dev->stats.rx_errors++; |
1737 | dev->stats.rx_length_errors++; | 1747 | dev->stats.rx_length_errors++; |
1738 | DEBUG (dev, "rx length %d\n", skb->len); | 1748 | DEBUG (dev, "rx length %d\n", skb->len); |
@@ -2047,38 +2057,20 @@ rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) | |||
2047 | DEBUG ((struct eth_dev *) ep->driver_data, | 2057 | DEBUG ((struct eth_dev *) ep->driver_data, |
2048 | "rndis control ack complete --> %d, %d/%d\n", | 2058 | "rndis control ack complete --> %d, %d/%d\n", |
2049 | req->status, req->actual, req->length); | 2059 | req->status, req->actual, req->length); |
2050 | |||
2051 | usb_ep_free_buffer(ep, req->buf, req->dma, 8); | ||
2052 | usb_ep_free_request(ep, req); | ||
2053 | } | 2060 | } |
2054 | 2061 | ||
2055 | static int rndis_control_ack (struct net_device *net) | 2062 | static int rndis_control_ack (struct net_device *net) |
2056 | { | 2063 | { |
2057 | struct eth_dev *dev = netdev_priv(net); | 2064 | struct eth_dev *dev = netdev_priv(net); |
2058 | u32 length; | 2065 | u32 length; |
2059 | struct usb_request *resp; | 2066 | struct usb_request *resp = dev->stat_req; |
2060 | 2067 | ||
2061 | /* in case RNDIS calls this after disconnect */ | 2068 | /* in case RNDIS calls this after disconnect */ |
2062 | if (!dev->status_ep) { | 2069 | if (!dev->status) { |
2063 | DEBUG (dev, "status ENODEV\n"); | 2070 | DEBUG (dev, "status ENODEV\n"); |
2064 | return -ENODEV; | 2071 | return -ENODEV; |
2065 | } | 2072 | } |
2066 | 2073 | ||
2067 | /* Allocate memory for notification ie. ACK */ | ||
2068 | resp = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC); | ||
2069 | if (!resp) { | ||
2070 | DEBUG (dev, "status ENOMEM\n"); | ||
2071 | return -ENOMEM; | ||
2072 | } | ||
2073 | |||
2074 | resp->buf = usb_ep_alloc_buffer (dev->status_ep, 8, | ||
2075 | &resp->dma, GFP_ATOMIC); | ||
2076 | if (!resp->buf) { | ||
2077 | DEBUG (dev, "status buf ENOMEM\n"); | ||
2078 | usb_ep_free_request (dev->status_ep, resp); | ||
2079 | return -ENOMEM; | ||
2080 | } | ||
2081 | |||
2082 | /* Send RNDIS RESPONSE_AVAILABLE notification; | 2074 | /* Send RNDIS RESPONSE_AVAILABLE notification; |
2083 | * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too | 2075 | * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too |
2084 | */ | 2076 | */ |
@@ -2113,7 +2105,7 @@ static void eth_start (struct eth_dev *dev, int gfp_flags) | |||
2113 | if (dev->rndis) { | 2105 | if (dev->rndis) { |
2114 | rndis_set_param_medium (dev->rndis_config, | 2106 | rndis_set_param_medium (dev->rndis_config, |
2115 | NDIS_MEDIUM_802_3, | 2107 | NDIS_MEDIUM_802_3, |
2116 | BITRATE(dev->gadget)); | 2108 | BITRATE(dev->gadget)/100); |
2117 | rndis_send_media_state (dev, 1); | 2109 | rndis_send_media_state (dev, 1); |
2118 | } | 2110 | } |
2119 | #endif | 2111 | #endif |
@@ -2307,8 +2299,8 @@ eth_bind (struct usb_gadget *gadget) | |||
2307 | device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210); | 2299 | device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210); |
2308 | } else if (gadget_is_pxa27x(gadget)) { | 2300 | } else if (gadget_is_pxa27x(gadget)) { |
2309 | device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211); | 2301 | device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211); |
2310 | } else if (gadget_is_s3c2410(gadget)) { | 2302 | } else if (gadget_is_s3c2410(gadget)) { |
2311 | device_desc.bcdDevice = __constant_cpu_to_le16 (0x0212); | 2303 | device_desc.bcdDevice = __constant_cpu_to_le16 (0x0212); |
2312 | } else if (gadget_is_at91(gadget)) { | 2304 | } else if (gadget_is_at91(gadget)) { |
2313 | device_desc.bcdDevice = __constant_cpu_to_le16 (0x0213); | 2305 | device_desc.bcdDevice = __constant_cpu_to_le16 (0x0213); |
2314 | } else { | 2306 | } else { |
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 6c5197850edc..7457268d5f28 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <asm/io.h> | 37 | #include <asm/io.h> |
38 | #include <asm/byteorder.h> | 38 | #include <asm/byteorder.h> |
39 | #include <asm/system.h> | 39 | #include <asm/system.h> |
40 | #include <asm/unaligned.h> | ||
40 | 41 | ||
41 | 42 | ||
42 | #undef RNDIS_PM | 43 | #undef RNDIS_PM |
@@ -165,7 +166,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) | |||
165 | 166 | ||
166 | /* mandatory */ | 167 | /* mandatory */ |
167 | case OID_GEN_LINK_SPEED: | 168 | case OID_GEN_LINK_SPEED: |
168 | DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); | 169 | // DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); |
169 | length = 4; | 170 | length = 4; |
170 | if (rndis_per_dev_params [configNr].media_state | 171 | if (rndis_per_dev_params [configNr].media_state |
171 | == NDIS_MEDIA_STATE_DISCONNECTED) | 172 | == NDIS_MEDIA_STATE_DISCONNECTED) |
@@ -729,7 +730,7 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, | |||
729 | retval = 0; | 730 | retval = 0; |
730 | 731 | ||
731 | /* FIXME use these NDIS_PACKET_TYPE_* bitflags to | 732 | /* FIXME use these NDIS_PACKET_TYPE_* bitflags to |
732 | * filter packets in hard_start_xmit() | 733 | * set the cdc_filter; it's not RNDIS-specific |
733 | * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in: | 734 | * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in: |
734 | * PROMISCUOUS, DIRECTED, | 735 | * PROMISCUOUS, DIRECTED, |
735 | * MULTICAST, ALL_MULTICAST, BROADCAST | 736 | * MULTICAST, ALL_MULTICAST, BROADCAST |
@@ -1194,10 +1195,10 @@ void rndis_add_hdr (struct sk_buff *skb) | |||
1194 | return; | 1195 | return; |
1195 | header = (void *) skb_push (skb, sizeof *header); | 1196 | header = (void *) skb_push (skb, sizeof *header); |
1196 | memset (header, 0, sizeof *header); | 1197 | memset (header, 0, sizeof *header); |
1197 | header->MessageType = __constant_cpu_to_le32 (1); | 1198 | header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG); |
1198 | header->MessageLength = cpu_to_le32(skb->len); | 1199 | header->MessageLength = cpu_to_le32(skb->len); |
1199 | header->DataOffset = __constant_cpu_to_le32 (36); | 1200 | header->DataOffset = __constant_cpu_to_le32 (36); |
1200 | header->OOBDataOffset = cpu_to_le32(skb->len - 44); | 1201 | header->DataLength = cpu_to_le32(skb->len - sizeof *header); |
1201 | } | 1202 | } |
1202 | 1203 | ||
1203 | void rndis_free_response (int configNr, u8 *buf) | 1204 | void rndis_free_response (int configNr, u8 *buf) |
@@ -1253,26 +1254,23 @@ static rndis_resp_t *rndis_add_response (int configNr, u32 length) | |||
1253 | return r; | 1254 | return r; |
1254 | } | 1255 | } |
1255 | 1256 | ||
1256 | int rndis_rm_hdr (u8 *buf, u32 *length) | 1257 | int rndis_rm_hdr(struct sk_buff *skb) |
1257 | { | 1258 | { |
1258 | u32 i, messageLen, dataOffset; | 1259 | /* tmp points to a struct rndis_packet_msg_type */ |
1259 | __le32 *tmp; | 1260 | __le32 *tmp = (void *) skb->data; |
1260 | |||
1261 | tmp = (__le32 *) buf; | ||
1262 | 1261 | ||
1263 | if (!buf || !length) return -1; | 1262 | /* MessageType, MessageLength */ |
1264 | if (le32_to_cpup(tmp++) != 1) return -1; | 1263 | if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG) |
1265 | 1264 | != get_unaligned(tmp++)) | |
1266 | messageLen = le32_to_cpup(tmp++); | 1265 | return -EINVAL; |
1267 | dataOffset = le32_to_cpup(tmp++) + 8; | 1266 | tmp++; |
1267 | |||
1268 | /* DataOffset, DataLength */ | ||
1269 | if (!skb_pull(skb, le32_to_cpu(get_unaligned(tmp++)) | ||
1270 | + 8 /* offset of DataOffset */)) | ||
1271 | return -EOVERFLOW; | ||
1272 | skb_trim(skb, le32_to_cpu(get_unaligned(tmp++))); | ||
1268 | 1273 | ||
1269 | if (messageLen < dataOffset || messageLen > *length) return -1; | ||
1270 | |||
1271 | for (i = dataOffset; i < messageLen; i++) | ||
1272 | buf [i - dataOffset] = buf [i]; | ||
1273 | |||
1274 | *length = messageLen - dataOffset; | ||
1275 | |||
1276 | return 0; | 1274 | return 0; |
1277 | } | 1275 | } |
1278 | 1276 | ||
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 822501852c50..2b5b55df3cfd 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h | |||
@@ -38,6 +38,7 @@ | |||
38 | */ | 38 | */ |
39 | 39 | ||
40 | /* Message Set for Connectionless (802.3) Devices */ | 40 | /* Message Set for Connectionless (802.3) Devices */ |
41 | #define REMOTE_NDIS_PACKET_MSG 0x00000001U | ||
41 | #define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */ | 42 | #define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */ |
42 | #define REMOTE_NDIS_HALT_MSG 0x00000003U | 43 | #define REMOTE_NDIS_HALT_MSG 0x00000003U |
43 | #define REMOTE_NDIS_QUERY_MSG 0x00000004U | 44 | #define REMOTE_NDIS_QUERY_MSG 0x00000004U |
@@ -333,7 +334,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, | |||
333 | const char *vendorDescr); | 334 | const char *vendorDescr); |
334 | int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); | 335 | int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); |
335 | void rndis_add_hdr (struct sk_buff *skb); | 336 | void rndis_add_hdr (struct sk_buff *skb); |
336 | int rndis_rm_hdr (u8 *buf, u32 *length); | 337 | int rndis_rm_hdr (struct sk_buff *skb); |
337 | u8 *rndis_get_next_response (int configNr, u32 *length); | 338 | u8 *rndis_get_next_response (int configNr, u32 *length); |
338 | void rndis_free_response (int configNr, u8 *buf); | 339 | void rndis_free_response (int configNr, u8 *buf); |
339 | 340 | ||