[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>
This commit is contained in:
@@ -100,6 +100,8 @@ static const char driver_desc [] = DRIVER_DESC;
|
|||||||
|
|
||||||
/* CDC and RNDIS support the same host-chosen outgoing packet filters. */
|
/* CDC and RNDIS support the same host-chosen outgoing packet filters. */
|
||||||
#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
|
#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
|
||||||
|
|USB_CDC_PACKET_TYPE_ALL_MULTICAST \
|
||||||
|
|USB_CDC_PACKET_TYPE_PROMISCUOUS \
|
||||||
|USB_CDC_PACKET_TYPE_DIRECTED)
|
|USB_CDC_PACKET_TYPE_DIRECTED)
|
||||||
|
|
||||||
|
|
||||||
@@ -322,12 +324,18 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR);
|
|||||||
/* also defer IRQs on highspeed TX */
|
/* also defer IRQs on highspeed TX */
|
||||||
#define TX_DELAY qmult
|
#define TX_DELAY qmult
|
||||||
|
|
||||||
#define BITRATE(g) (((g)->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS)
|
static inline int BITRATE(struct usb_gadget *g)
|
||||||
|
{
|
||||||
|
return (g->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS;
|
||||||
|
}
|
||||||
|
|
||||||
#else /* full speed (low speed doesn't do bulk) */
|
#else /* full speed (low speed doesn't do bulk) */
|
||||||
#define qlen(gadget) DEFAULT_QLEN
|
#define qlen(gadget) DEFAULT_QLEN
|
||||||
|
|
||||||
#define BITRATE(g) FS_BPS
|
static inline int BITRATE(struct usb_gadget *g)
|
||||||
|
{
|
||||||
|
return FS_BPS;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -1167,7 +1175,7 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
|
|||||||
eth_reset_config (dev);
|
eth_reset_config (dev);
|
||||||
|
|
||||||
/* default: pass all packets, no multicast filtering */
|
/* default: pass all packets, no multicast filtering */
|
||||||
dev->cdc_filter = 0x000f;
|
dev->cdc_filter = DEFAULT_FILTER;
|
||||||
|
|
||||||
switch (number) {
|
switch (number) {
|
||||||
case DEV_CONFIG_VALUE:
|
case DEV_CONFIG_VALUE:
|
||||||
@@ -1343,9 +1351,9 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||||||
struct eth_dev *dev = get_gadget_data (gadget);
|
struct eth_dev *dev = get_gadget_data (gadget);
|
||||||
struct usb_request *req = dev->req;
|
struct usb_request *req = dev->req;
|
||||||
int value = -EOPNOTSUPP;
|
int value = -EOPNOTSUPP;
|
||||||
u16 wIndex = ctrl->wIndex;
|
u16 wIndex = (__force u16) ctrl->wIndex;
|
||||||
u16 wValue = ctrl->wValue;
|
u16 wValue = (__force u16) ctrl->wValue;
|
||||||
u16 wLength = ctrl->wLength;
|
u16 wLength = (__force u16) ctrl->wLength;
|
||||||
|
|
||||||
/* descriptors just go into the pre-allocated ep0 buffer,
|
/* descriptors just go into the pre-allocated ep0 buffer,
|
||||||
* while config change events may enable network traffic.
|
* 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)
|
|||||||
|
|
||||||
/* Some platforms perform better when IP packets are aligned,
|
/* Some platforms perform better when IP packets are aligned,
|
||||||
* but on at least one, checksumming fails otherwise. Note:
|
* but on at least one, checksumming fails otherwise. Note:
|
||||||
* this doesn't account for variable-sized RNDIS headers.
|
* RNDIS headers involve variable numbers of LE32 values.
|
||||||
*/
|
*/
|
||||||
skb_reserve(skb, NET_IP_ALIGN);
|
skb_reserve(skb, NET_IP_ALIGN);
|
||||||
|
|
||||||
@@ -1730,9 +1738,11 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req)
|
|||||||
#ifdef CONFIG_USB_ETH_RNDIS
|
#ifdef CONFIG_USB_ETH_RNDIS
|
||||||
/* we know MaxPacketsPerTransfer == 1 here */
|
/* we know MaxPacketsPerTransfer == 1 here */
|
||||||
if (dev->rndis)
|
if (dev->rndis)
|
||||||
rndis_rm_hdr (req->buf, &(skb->len));
|
status = rndis_rm_hdr (skb);
|
||||||
#endif
|
#endif
|
||||||
if (ETH_HLEN > skb->len || skb->len > ETH_FRAME_LEN) {
|
if (status < 0
|
||||||
|
|| ETH_HLEN > skb->len
|
||||||
|
|| skb->len > ETH_FRAME_LEN) {
|
||||||
dev->stats.rx_errors++;
|
dev->stats.rx_errors++;
|
||||||
dev->stats.rx_length_errors++;
|
dev->stats.rx_length_errors++;
|
||||||
DEBUG (dev, "rx length %d\n", skb->len);
|
DEBUG (dev, "rx length %d\n", skb->len);
|
||||||
@@ -2047,38 +2057,20 @@ rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
|
|||||||
DEBUG ((struct eth_dev *) ep->driver_data,
|
DEBUG ((struct eth_dev *) ep->driver_data,
|
||||||
"rndis control ack complete --> %d, %d/%d\n",
|
"rndis control ack complete --> %d, %d/%d\n",
|
||||||
req->status, req->actual, req->length);
|
req->status, req->actual, req->length);
|
||||||
|
|
||||||
usb_ep_free_buffer(ep, req->buf, req->dma, 8);
|
|
||||||
usb_ep_free_request(ep, req);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rndis_control_ack (struct net_device *net)
|
static int rndis_control_ack (struct net_device *net)
|
||||||
{
|
{
|
||||||
struct eth_dev *dev = netdev_priv(net);
|
struct eth_dev *dev = netdev_priv(net);
|
||||||
u32 length;
|
u32 length;
|
||||||
struct usb_request *resp;
|
struct usb_request *resp = dev->stat_req;
|
||||||
|
|
||||||
/* in case RNDIS calls this after disconnect */
|
/* in case RNDIS calls this after disconnect */
|
||||||
if (!dev->status_ep) {
|
if (!dev->status) {
|
||||||
DEBUG (dev, "status ENODEV\n");
|
DEBUG (dev, "status ENODEV\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate memory for notification ie. ACK */
|
|
||||||
resp = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC);
|
|
||||||
if (!resp) {
|
|
||||||
DEBUG (dev, "status ENOMEM\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
resp->buf = usb_ep_alloc_buffer (dev->status_ep, 8,
|
|
||||||
&resp->dma, GFP_ATOMIC);
|
|
||||||
if (!resp->buf) {
|
|
||||||
DEBUG (dev, "status buf ENOMEM\n");
|
|
||||||
usb_ep_free_request (dev->status_ep, resp);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send RNDIS RESPONSE_AVAILABLE notification;
|
/* Send RNDIS RESPONSE_AVAILABLE notification;
|
||||||
* USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too
|
* USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too
|
||||||
*/
|
*/
|
||||||
@@ -2113,7 +2105,7 @@ static void eth_start (struct eth_dev *dev, int gfp_flags)
|
|||||||
if (dev->rndis) {
|
if (dev->rndis) {
|
||||||
rndis_set_param_medium (dev->rndis_config,
|
rndis_set_param_medium (dev->rndis_config,
|
||||||
NDIS_MEDIUM_802_3,
|
NDIS_MEDIUM_802_3,
|
||||||
BITRATE(dev->gadget));
|
BITRATE(dev->gadget)/100);
|
||||||
rndis_send_media_state (dev, 1);
|
rndis_send_media_state (dev, 1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -37,6 +37,7 @@
|
|||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
|
||||||
#undef RNDIS_PM
|
#undef RNDIS_PM
|
||||||
@@ -165,7 +166,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
|
|||||||
|
|
||||||
/* mandatory */
|
/* mandatory */
|
||||||
case OID_GEN_LINK_SPEED:
|
case OID_GEN_LINK_SPEED:
|
||||||
DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
|
// DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
|
||||||
length = 4;
|
length = 4;
|
||||||
if (rndis_per_dev_params [configNr].media_state
|
if (rndis_per_dev_params [configNr].media_state
|
||||||
== NDIS_MEDIA_STATE_DISCONNECTED)
|
== NDIS_MEDIA_STATE_DISCONNECTED)
|
||||||
@@ -729,7 +730,7 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
|
|||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
||||||
/* FIXME use these NDIS_PACKET_TYPE_* bitflags to
|
/* FIXME use these NDIS_PACKET_TYPE_* bitflags to
|
||||||
* filter packets in hard_start_xmit()
|
* set the cdc_filter; it's not RNDIS-specific
|
||||||
* NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
|
* NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
|
||||||
* PROMISCUOUS, DIRECTED,
|
* PROMISCUOUS, DIRECTED,
|
||||||
* MULTICAST, ALL_MULTICAST, BROADCAST
|
* MULTICAST, ALL_MULTICAST, BROADCAST
|
||||||
@@ -1194,10 +1195,10 @@ void rndis_add_hdr (struct sk_buff *skb)
|
|||||||
return;
|
return;
|
||||||
header = (void *) skb_push (skb, sizeof *header);
|
header = (void *) skb_push (skb, sizeof *header);
|
||||||
memset (header, 0, sizeof *header);
|
memset (header, 0, sizeof *header);
|
||||||
header->MessageType = __constant_cpu_to_le32 (1);
|
header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG);
|
||||||
header->MessageLength = cpu_to_le32(skb->len);
|
header->MessageLength = cpu_to_le32(skb->len);
|
||||||
header->DataOffset = __constant_cpu_to_le32 (36);
|
header->DataOffset = __constant_cpu_to_le32 (36);
|
||||||
header->OOBDataOffset = cpu_to_le32(skb->len - 44);
|
header->DataLength = cpu_to_le32(skb->len - sizeof *header);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rndis_free_response (int configNr, u8 *buf)
|
void rndis_free_response (int configNr, u8 *buf)
|
||||||
@@ -1253,25 +1254,22 @@ static rndis_resp_t *rndis_add_response (int configNr, u32 length)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rndis_rm_hdr (u8 *buf, u32 *length)
|
int rndis_rm_hdr(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
u32 i, messageLen, dataOffset;
|
/* tmp points to a struct rndis_packet_msg_type */
|
||||||
__le32 *tmp;
|
__le32 *tmp = (void *) skb->data;
|
||||||
|
|
||||||
tmp = (__le32 *) buf;
|
/* MessageType, MessageLength */
|
||||||
|
if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
|
||||||
|
!= get_unaligned(tmp++))
|
||||||
|
return -EINVAL;
|
||||||
|
tmp++;
|
||||||
|
|
||||||
if (!buf || !length) return -1;
|
/* DataOffset, DataLength */
|
||||||
if (le32_to_cpup(tmp++) != 1) return -1;
|
if (!skb_pull(skb, le32_to_cpu(get_unaligned(tmp++))
|
||||||
|
+ 8 /* offset of DataOffset */))
|
||||||
messageLen = le32_to_cpup(tmp++);
|
return -EOVERFLOW;
|
||||||
dataOffset = le32_to_cpup(tmp++) + 8;
|
skb_trim(skb, le32_to_cpu(get_unaligned(tmp++)));
|
||||||
|
|
||||||
if (messageLen < dataOffset || messageLen > *length) return -1;
|
|
||||||
|
|
||||||
for (i = dataOffset; i < messageLen; i++)
|
|
||||||
buf [i - dataOffset] = buf [i];
|
|
||||||
|
|
||||||
*length = messageLen - dataOffset;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -38,6 +38,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* Message Set for Connectionless (802.3) Devices */
|
/* Message Set for Connectionless (802.3) Devices */
|
||||||
|
#define REMOTE_NDIS_PACKET_MSG 0x00000001U
|
||||||
#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */
|
#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */
|
||||||
#define REMOTE_NDIS_HALT_MSG 0x00000003U
|
#define REMOTE_NDIS_HALT_MSG 0x00000003U
|
||||||
#define REMOTE_NDIS_QUERY_MSG 0x00000004U
|
#define REMOTE_NDIS_QUERY_MSG 0x00000004U
|
||||||
@@ -333,7 +334,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID,
|
|||||||
const char *vendorDescr);
|
const char *vendorDescr);
|
||||||
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
|
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
|
||||||
void rndis_add_hdr (struct sk_buff *skb);
|
void rndis_add_hdr (struct sk_buff *skb);
|
||||||
int rndis_rm_hdr (u8 *buf, u32 *length);
|
int rndis_rm_hdr (struct sk_buff *skb);
|
||||||
u8 *rndis_get_next_response (int configNr, u32 *length);
|
u8 *rndis_get_next_response (int configNr, u32 *length);
|
||||||
void rndis_free_response (int configNr, u8 *buf);
|
void rndis_free_response (int configNr, u8 *buf);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user