|
@@ -253,6 +253,7 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */
|
|
|
* of that mess as possible.
|
|
|
*/
|
|
|
#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101)
|
|
|
+#define OID_GEN_MAXIMUM_FRAME_SIZE ccpu2(0x00010106)
|
|
|
#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 */
|
|
|
// struct rndis_indicate *msg = (void *)buf;
|
|
|
dev_info(&info->control->dev,
|
|
|
- "rndis fault indication\n");
|
|
|
+ "rndis fault indication\n");
|
|
|
}
|
|
|
break;
|
|
|
case RNDIS_MSG_KEEPALIVE: { /* ping */
|
|
@@ -387,6 +388,71 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
|
|
|
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)
|
|
|
{
|
|
|
int retval;
|
|
@@ -403,6 +469,8 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
|
|
|
struct rndis_set_c *set_c;
|
|
|
} u;
|
|
|
u32 tmp;
|
|
|
+ int reply_len;
|
|
|
+ unsigned char *bp;
|
|
|
|
|
|
/* we can't rely on i/o from stack working, or stack allocation */
|
|
|
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.
|
|
|
* For RX we handle drivers that zero-pad to end-of-packet.
|
|
|
* 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);
|
|
|
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)) {
|
|
|
/* it might not even be an RNDIS device!! */
|
|
|
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);
|
|
|
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,
|
|
|
1 << le32_to_cpu(u.init_c->packet_alignment));
|
|
|
|
|
|
- /* Get designated host ethernet address.
|
|
|
- *
|
|
|
- * Adding a payload exactly the same size as the expected response
|
|
|
- * payload is an evident requirement MSFT added for ActiveSync.
|
|
|
- * This undocumented (and nonsensical) issue was found by sniffing
|
|
|
- * 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)) {
|
|
|
+ /* Get designated host ethernet address */
|
|
|
+ reply_len = ETH_ALEN;
|
|
|
+ retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
|
|
|
+ 48, (void **) &bp, &reply_len);
|
|
|
+ if (unlikely(retval< 0)) {
|
|
|
dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
|
|
|
goto fail_and_release;
|
|
|
}
|
|
|
- tmp = le32_to_cpu(u.get_c->offset);
|
|
|
- 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);
|
|
|
+ memcpy(net->dev_addr, bp, ETH_ALEN);
|
|
|
|
|
|
/* set a nonzero filter to enable data transfers */
|
|
|
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:
|
|
|
usb_set_intfdata(info->data, NULL);
|
|
|
usb_driver_release_interface(driver_of(intf), info->data);
|
|
|
+ info->data = NULL;
|
|
|
fail:
|
|
|
kfree(u.buf);
|
|
|
return retval;
|
|
@@ -618,7 +674,7 @@ fill:
|
|
|
|
|
|
static const struct driver_info rndis_info = {
|
|
|
.description = "RNDIS device",
|
|
|
- .flags = FLAG_ETHER | FLAG_FRAMING_RN,
|
|
|
+ .flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
|
|
|
.bind = rndis_bind,
|
|
|
.unbind = rndis_unbind,
|
|
|
.status = rndis_status,
|