USB: rndis_host, various cleanups
Cleanups to the rndis_host code, and a tweak that helps talking to PXA hardware. Mostly from Ole André Vadla Ravnås <oleavr@gmail.com> - Prevent SET_INTERFACE requests, they give PXA hardware bad indigestion - For paranoia, null a pointer after freeing its data - Wrap up ActiveSync oddities for RNDIS_QUERY in one routine - Use that wrapper when getting the Ethernet address - Whitespace fixes Plus add a comment noting the open issues about some RNDIS clients still needing TBD kinds of browbeating to accept non-jumbogram packets. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
aa2ce5ca6b
commit
ddda086240
@ -253,6 +253,7 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */
|
|||||||
* of that mess as possible.
|
* of that mess as possible.
|
||||||
*/
|
*/
|
||||||
#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101)
|
#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101)
|
||||||
|
#define OID_GEN_MAXIMUM_FRAME_SIZE ccpu2(0x00010106)
|
||||||
#define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e)
|
#define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -349,7 +350,7 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
|
|||||||
case RNDIS_MSG_INDICATE: { /* fault */
|
case RNDIS_MSG_INDICATE: { /* fault */
|
||||||
// struct rndis_indicate *msg = (void *)buf;
|
// struct rndis_indicate *msg = (void *)buf;
|
||||||
dev_info(&info->control->dev,
|
dev_info(&info->control->dev,
|
||||||
"rndis fault indication\n");
|
"rndis fault indication\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RNDIS_MSG_KEEPALIVE: { /* ping */
|
case RNDIS_MSG_KEEPALIVE: { /* ping */
|
||||||
@ -387,6 +388,71 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
|
|||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rndis_query:
|
||||||
|
*
|
||||||
|
* Performs a query for @oid along with 0 or more bytes of payload as
|
||||||
|
* specified by @in_len. If @reply_len is not set to -1 then the reply
|
||||||
|
* length is checked against this value, resulting in an error if it
|
||||||
|
* doesn't match.
|
||||||
|
*
|
||||||
|
* NOTE: Adding a payload exactly or greater than the size of the expected
|
||||||
|
* response payload is an evident requirement MSFT added for ActiveSync.
|
||||||
|
*
|
||||||
|
* The only exception is for OIDs that return a variably sized response,
|
||||||
|
* in which case no payload should be added. This undocumented (and
|
||||||
|
* nonsensical!) issue was found by sniffing protocol requests from the
|
||||||
|
* ActiveSync 4.1 Windows driver.
|
||||||
|
*/
|
||||||
|
static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
|
||||||
|
void *buf, u32 oid, u32 in_len,
|
||||||
|
void **reply, int *reply_len)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
union {
|
||||||
|
void *buf;
|
||||||
|
struct rndis_msg_hdr *header;
|
||||||
|
struct rndis_query *get;
|
||||||
|
struct rndis_query_c *get_c;
|
||||||
|
} u;
|
||||||
|
u32 off, len;
|
||||||
|
|
||||||
|
u.buf = buf;
|
||||||
|
|
||||||
|
memset(u.get, 0, sizeof *u.get + in_len);
|
||||||
|
u.get->msg_type = RNDIS_MSG_QUERY;
|
||||||
|
u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
|
||||||
|
u.get->oid = oid;
|
||||||
|
u.get->len = cpu_to_le32(in_len);
|
||||||
|
u.get->offset = ccpu2(20);
|
||||||
|
|
||||||
|
retval = rndis_command(dev, u.header);
|
||||||
|
if (unlikely(retval < 0)) {
|
||||||
|
dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
|
||||||
|
oid, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
off = le32_to_cpu(u.get_c->offset);
|
||||||
|
len = le32_to_cpu(u.get_c->len);
|
||||||
|
if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
|
||||||
|
goto response_error;
|
||||||
|
|
||||||
|
if (*reply_len != -1 && len != *reply_len)
|
||||||
|
goto response_error;
|
||||||
|
|
||||||
|
*reply = (unsigned char *) &u.get_c->request_id + off;
|
||||||
|
*reply_len = len;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
response_error:
|
||||||
|
dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
|
||||||
|
"invalid response - off %d len %d\n",
|
||||||
|
oid, off, len);
|
||||||
|
return -EDOM;
|
||||||
|
}
|
||||||
|
|
||||||
static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
|
static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
@ -403,6 +469,8 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
|
|||||||
struct rndis_set_c *set_c;
|
struct rndis_set_c *set_c;
|
||||||
} u;
|
} u;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
int reply_len;
|
||||||
|
unsigned char *bp;
|
||||||
|
|
||||||
/* we can't rely on i/o from stack working, or stack allocation */
|
/* we can't rely on i/o from stack working, or stack allocation */
|
||||||
u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
|
u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
|
||||||
@ -421,6 +489,12 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
|
|||||||
* TX we'll stick to one Ethernet packet plus RNDIS framing.
|
* TX we'll stick to one Ethernet packet plus RNDIS framing.
|
||||||
* For RX we handle drivers that zero-pad to end-of-packet.
|
* For RX we handle drivers that zero-pad to end-of-packet.
|
||||||
* Don't let userspace change these settings.
|
* Don't let userspace change these settings.
|
||||||
|
*
|
||||||
|
* NOTE: there still seems to be wierdness here, as if we need
|
||||||
|
* to do some more things to make sure WinCE targets accept this.
|
||||||
|
* They default to jumbograms of 8KB or 16KB, which is absurd
|
||||||
|
* for such low data rates and which is also more than Linux
|
||||||
|
* can usually expect to allocate for SKB data...
|
||||||
*/
|
*/
|
||||||
net->hard_header_len += sizeof (struct rndis_data_hdr);
|
net->hard_header_len += sizeof (struct rndis_data_hdr);
|
||||||
dev->hard_mtu = net->mtu + net->hard_header_len;
|
dev->hard_mtu = net->mtu + net->hard_header_len;
|
||||||
@ -434,7 +508,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
|
|||||||
if (unlikely(retval < 0)) {
|
if (unlikely(retval < 0)) {
|
||||||
/* it might not even be an RNDIS device!! */
|
/* it might not even be an RNDIS device!! */
|
||||||
dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
|
dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
|
||||||
goto fail_and_release;
|
goto fail_and_release;
|
||||||
}
|
}
|
||||||
tmp = le32_to_cpu(u.init_c->max_transfer_size);
|
tmp = le32_to_cpu(u.init_c->max_transfer_size);
|
||||||
if (tmp < dev->hard_mtu) {
|
if (tmp < dev->hard_mtu) {
|
||||||
@ -450,34 +524,15 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
|
|||||||
dev->hard_mtu, tmp, dev->rx_urb_size,
|
dev->hard_mtu, tmp, dev->rx_urb_size,
|
||||||
1 << le32_to_cpu(u.init_c->packet_alignment));
|
1 << le32_to_cpu(u.init_c->packet_alignment));
|
||||||
|
|
||||||
/* Get designated host ethernet address.
|
/* Get designated host ethernet address */
|
||||||
*
|
reply_len = ETH_ALEN;
|
||||||
* Adding a payload exactly the same size as the expected response
|
retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
|
||||||
* payload is an evident requirement MSFT added for ActiveSync.
|
48, (void **) &bp, &reply_len);
|
||||||
* This undocumented (and nonsensical) issue was found by sniffing
|
if (unlikely(retval< 0)) {
|
||||||
* protocol requests from the ActiveSync 4.1 Windows driver.
|
|
||||||
*/
|
|
||||||
memset(u.get, 0, sizeof *u.get + 48);
|
|
||||||
u.get->msg_type = RNDIS_MSG_QUERY;
|
|
||||||
u.get->msg_len = ccpu2(sizeof *u.get + 48);
|
|
||||||
u.get->oid = OID_802_3_PERMANENT_ADDRESS;
|
|
||||||
u.get->len = ccpu2(48);
|
|
||||||
u.get->offset = ccpu2(20);
|
|
||||||
|
|
||||||
retval = rndis_command(dev, u.header);
|
|
||||||
if (unlikely(retval < 0)) {
|
|
||||||
dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
|
dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
|
||||||
goto fail_and_release;
|
goto fail_and_release;
|
||||||
}
|
}
|
||||||
tmp = le32_to_cpu(u.get_c->offset);
|
memcpy(net->dev_addr, bp, ETH_ALEN);
|
||||||
if (unlikely((tmp + 8) > (CONTROL_BUFFER_SIZE - ETH_ALEN)
|
|
||||||
|| u.get_c->len != ccpu2(ETH_ALEN))) {
|
|
||||||
dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n",
|
|
||||||
tmp, le32_to_cpu(u.get_c->len));
|
|
||||||
retval = -EDOM;
|
|
||||||
goto fail_and_release;
|
|
||||||
}
|
|
||||||
memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN);
|
|
||||||
|
|
||||||
/* set a nonzero filter to enable data transfers */
|
/* set a nonzero filter to enable data transfers */
|
||||||
memset(u.set, 0, sizeof *u.set);
|
memset(u.set, 0, sizeof *u.set);
|
||||||
@ -502,6 +557,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
|
|||||||
fail_and_release:
|
fail_and_release:
|
||||||
usb_set_intfdata(info->data, NULL);
|
usb_set_intfdata(info->data, NULL);
|
||||||
usb_driver_release_interface(driver_of(intf), info->data);
|
usb_driver_release_interface(driver_of(intf), info->data);
|
||||||
|
info->data = NULL;
|
||||||
fail:
|
fail:
|
||||||
kfree(u.buf);
|
kfree(u.buf);
|
||||||
return retval;
|
return retval;
|
||||||
@ -618,7 +674,7 @@ fill:
|
|||||||
|
|
||||||
static const struct driver_info rndis_info = {
|
static const struct driver_info rndis_info = {
|
||||||
.description = "RNDIS device",
|
.description = "RNDIS device",
|
||||||
.flags = FLAG_ETHER | FLAG_FRAMING_RN,
|
.flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
|
||||||
.bind = rndis_bind,
|
.bind = rndis_bind,
|
||||||
.unbind = rndis_unbind,
|
.unbind = rndis_unbind,
|
||||||
.status = rndis_status,
|
.status = rndis_status,
|
||||||
|
Loading…
Reference in New Issue
Block a user