Merge branches 'misc', 'eeepc-laptop' and 'bugzilla-14445' into release
[safe/jmp/linux-2.6] / drivers / net / usb / rndis_host.c
index 1d6bf0a..f56dec6 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-
-// #define     DEBUG                   // error path messages, extra info
-// #define     VERBOSE                 // more; success messages
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/netdevice.h>
@@ -29,9 +25,8 @@
 #include <linux/mii.h>
 #include <linux/usb.h>
 #include <linux/usb/cdc.h>
-
-#include "usbnet.h"
-#include "rndis_host.h"
+#include <linux/usb/usbnet.h>
+#include <linux/usb/rndis_host.h>
 
 
 /*
@@ -70,6 +65,32 @@ void rndis_status(struct usbnet *dev, struct urb *urb)
 EXPORT_SYMBOL_GPL(rndis_status);
 
 /*
+ * RNDIS indicate messages.
+ */
+static void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg,
+                               int buflen)
+{
+       struct cdc_state *info = (void *)&dev->data;
+       struct device *udev = &info->control->dev;
+
+       if (dev->driver_info->indication) {
+               dev->driver_info->indication(dev, msg, buflen);
+       } else {
+               switch (msg->status) {
+               case RNDIS_STATUS_MEDIA_CONNECT:
+                       dev_info(udev, "rndis media connect\n");
+                       break;
+               case RNDIS_STATUS_MEDIA_DISCONNECT:
+                       dev_info(udev, "rndis media disconnect\n");
+                       break;
+               default:
+                       dev_info(udev, "rndis indication: 0x%08x\n",
+                                       le32_to_cpu(msg->status));
+               }
+       }
+}
+
+/*
  * RPC done RNDIS-style.  Caller guarantees:
  * - message is properly byteswapped
  * - there's no other request pending
@@ -79,7 +100,7 @@ EXPORT_SYMBOL_GPL(rndis_status);
  * Call context is likely probe(), before interface name is known,
  * which is why we won't try to use it in the diagnostics.
  */
-int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
+int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)
 {
        struct cdc_state        *info = (void *) &dev->data;
        int                     master_ifnum;
@@ -126,7 +147,7 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
                        USB_CDC_GET_ENCAPSULATED_RESPONSE,
                        USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
                        0, master_ifnum,
-                       buf, CONTROL_BUFFER_SIZE,
+                       buf, buflen,
                        RNDIS_CONTROL_TIMEOUT_MS);
                if (likely(retval >= 8)) {
                        msg_len = le32_to_cpu(buf->msg_len);
@@ -148,17 +169,15 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
                                        request_id, xid);
                                /* then likely retry */
                        } else switch (buf->msg_type) {
-                       case RNDIS_MSG_INDICATE: {      /* fault */
-                               // struct rndis_indicate *msg = (void *)buf;
-                               dev_info(&info->control->dev,
-                                       "rndis fault indication\n");
-                               }
+                       case RNDIS_MSG_INDICATE:        /* fault/event */
+                               rndis_msg_indicate(dev, (void *)buf, buflen);
+
                                break;
                        case RNDIS_MSG_KEEPALIVE: {     /* ping */
                                struct rndis_keepalive_c *msg = (void *)buf;
 
                                msg->msg_type = RNDIS_MSG_KEEPALIVE_C;
-                               msg->msg_len = ccpu2(sizeof *msg);
+                               msg->msg_len = cpu_to_le32(sizeof *msg);
                                msg->status = RNDIS_STATUS_SUCCESS;
                                retval = usb_control_msg(dev->udev,
                                        usb_sndctrlpipe(dev->udev, 0),
@@ -183,7 +202,7 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
                        dev_dbg(&info->control->dev,
                                "rndis response error, code %d\n", retval);
                }
-               msleep(2);
+               msleep(20);
        }
        dev_dbg(&info->control->dev, "rndis response timeout\n");
        return -ETIMEDOUT;
@@ -207,7 +226,7 @@ EXPORT_SYMBOL_GPL(rndis_command);
  * ActiveSync 4.1 Windows driver.
  */
 static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
-               void *buf, u32 oid, u32 in_len,
+               void *buf, __le32 oid, u32 in_len,
                void **reply, int *reply_len)
 {
        int retval;
@@ -226,9 +245,9 @@ static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
        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);
+       u.get->offset = cpu_to_le32(20);
 
-       retval = rndis_command(dev, u.header);
+       retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE);
        if (unlikely(retval < 0)) {
                dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
                                oid, retval);
@@ -255,7 +274,18 @@ response_error:
        return -EDOM;
 }
 
-int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf)
+/* same as usbnet_netdev_ops but MTU change not allowed */
+static const struct net_device_ops rndis_netdev_ops = {
+       .ndo_open               = usbnet_open,
+       .ndo_stop               = usbnet_stop,
+       .ndo_start_xmit         = usbnet_start_xmit,
+       .ndo_tx_timeout         = usbnet_tx_timeout,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
+int
+generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags)
 {
        int                     retval;
        struct net_device       *net = dev->net;
@@ -272,6 +302,7 @@ int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf)
                struct rndis_halt       *halt;
        } u;
        u32                     tmp;
+       __le32                  phym_unspec, *phym;
        int                     reply_len;
        unsigned char           *bp;
 
@@ -284,9 +315,9 @@ int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf)
                goto fail;
 
        u.init->msg_type = RNDIS_MSG_INIT;
-       u.init->msg_len = ccpu2(sizeof *u.init);
-       u.init->major_version = ccpu2(1);
-       u.init->minor_version = ccpu2(0);
+       u.init->msg_len = cpu_to_le32(sizeof *u.init);
+       u.init->major_version = cpu_to_le32(1);
+       u.init->minor_version = cpu_to_le32(0);
 
        /* max transfer (in spec) is 0x4000 at full speed, but for
         * TX we'll stick to one Ethernet packet plus RNDIS framing.
@@ -302,12 +333,21 @@ int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf)
        net->hard_header_len += sizeof (struct rndis_data_hdr);
        dev->hard_mtu = net->mtu + net->hard_header_len;
 
+       dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1);
+       if (dev->maxpacket == 0) {
+               if (netif_msg_probe(dev))
+                       dev_dbg(&intf->dev, "dev->maxpacket can't be 0\n");
+               retval = -EINVAL;
+               goto fail_and_release;
+       }
+
        dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1);
        dev->rx_urb_size &= ~(dev->maxpacket - 1);
        u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size);
 
-       net->change_mtu = NULL;
-       retval = rndis_command(dev, u.header);
+       net->netdev_ops = &rndis_netdev_ops;
+
+       retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE);
        if (unlikely(retval < 0)) {
                /* it might not even be an RNDIS device!! */
                dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
@@ -322,12 +362,12 @@ int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf)
                        retval = -EINVAL;
                        goto halt_fail_and_release;
                }
-               dev->hard_mtu = tmp;
-               net->mtu = dev->hard_mtu - net->hard_header_len;
                dev_warn(&intf->dev,
                         "dev can't take %u byte packets (max %u), "
                         "adjusting MTU to %u\n",
-                        dev->hard_mtu, tmp, net->mtu);
+                        dev->hard_mtu, tmp, tmp - net->hard_header_len);
+               dev->hard_mtu = tmp;
+               net->mtu = dev->hard_mtu - net->hard_header_len;
        }
 
        /* REVISIT:  peripheral "alignment" request is ignored ... */
@@ -336,6 +376,39 @@ int generic_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));
 
+       /* module has some device initialization code needs to be done right
+        * after RNDIS_INIT */
+       if (dev->driver_info->early_init &&
+                       dev->driver_info->early_init(dev) != 0)
+               goto halt_fail_and_release;
+
+       /* Check physical medium */
+       phym = NULL;
+       reply_len = sizeof *phym;
+       retval = rndis_query(dev, intf, u.buf, OID_GEN_PHYSICAL_MEDIUM,
+                       0, (void **) &phym, &reply_len);
+       if (retval != 0 || !phym) {
+               /* OID is optional so don't fail here. */
+               phym_unspec = RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED;
+               phym = &phym_unspec;
+       }
+       if ((flags & FLAG_RNDIS_PHYM_WIRELESS) &&
+                       *phym != RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {
+               if (netif_msg_probe(dev))
+                       dev_dbg(&intf->dev, "driver requires wireless "
+                               "physical medium, but device is not.\n");
+               retval = -ENODEV;
+               goto halt_fail_and_release;
+       }
+       if ((flags & FLAG_RNDIS_PHYM_NOT_WIRELESS) &&
+                       *phym == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {
+               if (netif_msg_probe(dev))
+                       dev_dbg(&intf->dev, "driver requires non-wireless "
+                               "physical medium, but device is wireless.\n");
+               retval = -ENODEV;
+               goto halt_fail_and_release;
+       }
+
        /* Get designated host ethernet address */
        reply_len = ETH_ALEN;
        retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
@@ -345,17 +418,18 @@ int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf)
                goto halt_fail_and_release;
        }
        memcpy(net->dev_addr, bp, ETH_ALEN);
+       memcpy(net->perm_addr, bp, ETH_ALEN);
 
        /* set a nonzero filter to enable data transfers */
        memset(u.set, 0, sizeof *u.set);
        u.set->msg_type = RNDIS_MSG_SET;
-       u.set->msg_len = ccpu2(4 + sizeof *u.set);
+       u.set->msg_len = cpu_to_le32(4 + sizeof *u.set);
        u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
-       u.set->len = ccpu2(4);
-       u.set->offset = ccpu2((sizeof *u.set) - 8);
+       u.set->len = cpu_to_le32(4);
+       u.set->offset = cpu_to_le32((sizeof *u.set) - 8);
        *(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER;
 
-       retval = rndis_command(dev, u.header);
+       retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE);
        if (unlikely(retval < 0)) {
                dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
                goto halt_fail_and_release;
@@ -369,8 +443,8 @@ int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 halt_fail_and_release:
        memset(u.halt, 0, sizeof *u.halt);
        u.halt->msg_type = RNDIS_MSG_HALT;
-       u.halt->msg_len = ccpu2(sizeof *u.halt);
-       (void) rndis_command(dev, (void *)u.halt);
+       u.halt->msg_len = cpu_to_le32(sizeof *u.halt);
+       (void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE);
 fail_and_release:
        usb_set_intfdata(info->data, NULL);
        usb_driver_release_interface(driver_of(intf), info->data);
@@ -381,6 +455,11 @@ fail:
 }
 EXPORT_SYMBOL_GPL(generic_rndis_bind);
 
+static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       return generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_NOT_WIRELESS);
+}
+
 void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
 {
        struct rndis_halt       *halt;
@@ -389,8 +468,8 @@ void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
        halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
        if (halt) {
                halt->msg_type = RNDIS_MSG_HALT;
-               halt->msg_len = ccpu2(sizeof *halt);
-               (void) rndis_command(dev, (void *)halt);
+               halt->msg_len = cpu_to_le32(sizeof *halt);
+               (void) rndis_command(dev, (void *)halt, CONTROL_BUFFER_SIZE);
                kfree(halt);
        }
 
@@ -417,7 +496,7 @@ int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET
                                || skb->len < msg_len
                                || (data_offset + data_len + 8) > msg_len)) {
-                       dev->stats.rx_frame_errors++;
+                       dev->net->stats.rx_frame_errors++;
                        devdbg(dev, "bad rndis message %d/%d/%d/%d, len %d",
                                le32_to_cpu(hdr->msg_type),
                                msg_len, data_offset, data_len, skb->len);
@@ -484,7 +563,7 @@ fill:
        memset(hdr, 0, sizeof *hdr);
        hdr->msg_type = RNDIS_MSG_PACKET;
        hdr->msg_len = cpu_to_le32(skb->len);
-       hdr->data_offset = ccpu2(sizeof(*hdr) - 8);
+       hdr->data_offset = cpu_to_le32(sizeof(*hdr) - 8);
        hdr->data_len = cpu_to_le32(len);
 
        /* FIXME make the last packet always be short ... */
@@ -496,16 +575,13 @@ EXPORT_SYMBOL_GPL(rndis_tx_fixup);
 static const struct driver_info        rndis_info = {
        .description =  "RNDIS device",
        .flags =        FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
-       .bind =         generic_rndis_bind,
+       .bind =         rndis_bind,
        .unbind =       rndis_unbind,
        .status =       rndis_status,
        .rx_fixup =     rndis_rx_fixup,
        .tx_fixup =     rndis_tx_fixup,
 };
 
-#undef ccpu2
-
-
 /*-------------------------------------------------------------------------*/
 
 static const struct usb_device_id      products [] = {
@@ -517,6 +593,10 @@ static const struct usb_device_id  products [] = {
        /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
        USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1),
        .driver_info = (unsigned long) &rndis_info,
+}, {
+       /* RNDIS for tethering */
+       USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
+       .driver_info = (unsigned long) &rndis_info,
 },
        { },            // END
 };