Commit 6cdee106 authored by David Brownell's avatar David Brownell Committed by Greg K-H
Browse files

[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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 1bc3c9e1
Loading
Loading
Loading
Loading
+24 −32
Original line number Diff line number Diff line
@@ -100,6 +100,8 @@ static const char driver_desc [] = DRIVER_DESC;

/* CDC and RNDIS support the same host-chosen outgoing packet filters. */
#define	DEFAULT_FILTER	(USB_CDC_PACKET_TYPE_BROADCAST \
 			|USB_CDC_PACKET_TYPE_ALL_MULTICAST \
 			|USB_CDC_PACKET_TYPE_PROMISCUOUS \
 			|USB_CDC_PACKET_TYPE_DIRECTED)


@@ -322,12 +324,18 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR);
/* also defer IRQs on highspeed TX */
#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) */
#define qlen(gadget) DEFAULT_QLEN

#define	BITRATE(g)	FS_BPS
static inline int BITRATE(struct usb_gadget *g)
{
	return FS_BPS;
}
#endif


@@ -1167,7 +1175,7 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
	eth_reset_config (dev);

	/* default:  pass all packets, no multicast filtering */
	dev->cdc_filter = 0x000f;
	dev->cdc_filter = DEFAULT_FILTER;

	switch (number) {
	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 usb_request	*req = dev->req;
	int			value = -EOPNOTSUPP;
	u16			wIndex = ctrl->wIndex;
	u16			wValue = ctrl->wValue;
	u16			wLength = ctrl->wLength;
	u16			wIndex = (__force u16) ctrl->wIndex;
	u16			wValue = (__force u16) ctrl->wValue;
	u16			wLength = (__force u16) ctrl->wLength;

	/* descriptors just go into the pre-allocated ep0 buffer,
	 * 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,
	 * 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);

@@ -1730,9 +1738,11 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req)
#ifdef CONFIG_USB_ETH_RNDIS
		/* we know MaxPacketsPerTransfer == 1 here */
		if (dev->rndis)
			rndis_rm_hdr (req->buf, &(skb->len));
			status = rndis_rm_hdr (skb);
#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_length_errors++;
			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,
			"rndis control ack complete --> %d, %d/%d\n",
			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)
{
	struct eth_dev          *dev = netdev_priv(net);
	u32                     length;
	struct usb_request      *resp;
	struct usb_request      *resp = dev->stat_req;
	
	/* in case RNDIS calls this after disconnect */
	if (!dev->status_ep) {
	if (!dev->status) {
		DEBUG (dev, "status ENODEV\n");
		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;
	 * 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) {
		rndis_set_param_medium (dev->rndis_config,
					NDIS_MEDIUM_802_3,
					BITRATE(dev->gadget));
					BITRATE(dev->gadget)/100);
		rndis_send_media_state (dev, 1);
	}
#endif	
+19 −21
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/system.h>
#include <asm/unaligned.h>


#undef	RNDIS_PM
@@ -165,7 +166,7 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
		
	/* mandatory */
	case OID_GEN_LINK_SPEED:
		DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
//		DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
		length = 4;
		if (rndis_per_dev_params [configNr].media_state
			== 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;

		/* 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:
		 *	PROMISCUOUS, DIRECTED,
		 *	MULTICAST, ALL_MULTICAST, BROADCAST
@@ -1194,10 +1195,10 @@ void rndis_add_hdr (struct sk_buff *skb)
		return;
	header = (void *) skb_push (skb, 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->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)
@@ -1253,25 +1254,22 @@ static rndis_resp_t *rndis_add_response (int configNr, u32 length)
	return r;
}

int rndis_rm_hdr (u8 *buf, u32 *length)
int rndis_rm_hdr(struct sk_buff *skb)
{
	u32 i, messageLen, dataOffset;
	__le32 *tmp;
	
	tmp = (__le32 *) buf; 

	if (!buf || !length) return -1;
	if (le32_to_cpup(tmp++) != 1) return -1;
	
	messageLen = le32_to_cpup(tmp++);
	dataOffset = le32_to_cpup(tmp++) + 8;

	if (messageLen < dataOffset || messageLen > *length) return -1;
	
	for (i = dataOffset; i < messageLen; i++)
		buf [i - dataOffset] = buf [i];
		
	*length = messageLen - dataOffset;
	/* tmp points to a struct rndis_packet_msg_type */
	__le32		*tmp = (void *) skb->data;

	/* MessageType, MessageLength */
	if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
			!= get_unaligned(tmp++))
		return -EINVAL;
	tmp++;

	/* DataOffset, DataLength */
	if (!skb_pull(skb, le32_to_cpu(get_unaligned(tmp++))
			+ 8 /* offset of DataOffset */))
		return -EOVERFLOW;
	skb_trim(skb, le32_to_cpu(get_unaligned(tmp++)));

	return 0;
}
+2 −1
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@
 */

/* Message Set for Connectionless (802.3) Devices */
#define REMOTE_NDIS_PACKET_MSG		0x00000001U
#define REMOTE_NDIS_INITIALIZE_MSG	0x00000002U	/* Initialize device */
#define REMOTE_NDIS_HALT_MSG		0x00000003U
#define REMOTE_NDIS_QUERY_MSG		0x00000004U
@@ -333,7 +334,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID,
			    const char *vendorDescr);
int  rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
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);
void rndis_free_response (int configNr, u8 *buf);