usbnet: introduce usbnet 3 command helpers
authorMing Lei <ming.lei@canonical.com>
Wed, 24 Oct 2012 19:46:54 +0000 (19:46 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 26 Oct 2012 07:36:50 +0000 (03:36 -0400)
This patch introduces the below 3 usb command helpers:

usbnet_read_cmd / usbnet_write_cmd / usbnet_write_cmd_async

so that each low level driver doesn't need to implement them
by itself, and the dma buffer allocation for usb transfer and
runtime PM things can be handled just in one place.

Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/usb/usbnet.c
include/linux/usb/usbnet.h

index f9819d10b1f9d41f864dc710e6799ffed9f9d5f0..447d20d22b7cdec6e1cb7ffdd383cb2ec34531b2 100644 (file)
@@ -1610,6 +1610,138 @@ void usbnet_device_suggests_idle(struct usbnet *dev)
 }
 EXPORT_SYMBOL(usbnet_device_suggests_idle);
 
+/*-------------------------------------------------------------------------*/
+int usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
+                   u16 value, u16 index, void *data, u16 size)
+{
+       void *buf = NULL;
+       int err = -ENOMEM;
+
+       netdev_dbg(dev->net, "usbnet_read_cmd cmd=0x%02x reqtype=%02x"
+                  " value=0x%04x index=0x%04x size=%d\n",
+                  cmd, reqtype, value, index, size);
+
+       if (data) {
+               buf = kmalloc(size, GFP_KERNEL);
+               if (!buf)
+                       goto out;
+       }
+
+       err = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+                             cmd, reqtype, value, index, buf, size,
+                             USB_CTRL_GET_TIMEOUT);
+       if (err > 0 && err <= size)
+               memcpy(data, buf, err);
+       kfree(buf);
+out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(usbnet_read_cmd);
+
+int usbnet_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
+                    u16 value, u16 index, const void *data, u16 size)
+{
+       void *buf = NULL;
+       int err = -ENOMEM;
+
+       netdev_dbg(dev->net, "usbnet_write_cmd cmd=0x%02x reqtype=%02x"
+                  " value=0x%04x index=0x%04x size=%d\n",
+                  cmd, reqtype, value, index, size);
+
+       if (data) {
+               buf = kmemdup(data, size, GFP_KERNEL);
+               if (!buf)
+                       goto out;
+       }
+
+       err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+                             cmd, reqtype, value, index, buf, size,
+                             USB_CTRL_SET_TIMEOUT);
+       kfree(buf);
+
+out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(usbnet_write_cmd);
+
+static void usbnet_async_cmd_cb(struct urb *urb)
+{
+       struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+       int status = urb->status;
+
+       if (status < 0)
+               dev_dbg(&urb->dev->dev, "%s failed with %d",
+                       __func__, status);
+
+       kfree(req);
+       usb_free_urb(urb);
+}
+
+int usbnet_write_cmd_async(struct usbnet *dev, u8 cmd, u8 reqtype,
+                          u16 value, u16 index, const void *data, u16 size)
+{
+       struct usb_ctrlrequest *req = NULL;
+       struct urb *urb;
+       int err = -ENOMEM;
+       void *buf = NULL;
+
+       netdev_dbg(dev->net, "usbnet_write_cmd cmd=0x%02x reqtype=%02x"
+                  " value=0x%04x index=0x%04x size=%d\n",
+                  cmd, reqtype, value, index, size);
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               netdev_err(dev->net, "Error allocating URB in"
+                          " %s!\n", __func__);
+               goto fail;
+       }
+
+       if (data) {
+               buf = kmemdup(data, size, GFP_ATOMIC);
+               if (!buf) {
+                       netdev_err(dev->net, "Error allocating buffer"
+                                  " in %s!\n", __func__);
+                       goto fail_free;
+               }
+       }
+
+       req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+       if (!req) {
+               netdev_err(dev->net, "Failed to allocate memory for %s\n",
+                          __func__);
+               goto fail_free_buf;
+       }
+
+       req->bRequestType = reqtype;
+       req->bRequest = cmd;
+       req->wValue = cpu_to_le16(value);
+       req->wIndex = cpu_to_le16(index);
+       req->wLength = cpu_to_le16(size);
+
+       usb_fill_control_urb(urb, dev->udev,
+                            usb_sndctrlpipe(dev->udev, 0),
+                            (void *)req, buf, size,
+                            usbnet_async_cmd_cb, req);
+       urb->transfer_flags |= URB_FREE_BUFFER;
+
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (err < 0) {
+               netdev_err(dev->net, "Error submitting the control"
+                          " message: status=%d\n", err);
+               goto fail_free;
+       }
+       return 0;
+
+fail_free_buf:
+       kfree(buf);
+fail_free:
+       kfree(req);
+       usb_free_urb(urb);
+fail:
+       return err;
+
+}
+EXPORT_SYMBOL_GPL(usbnet_write_cmd_async);
 /*-------------------------------------------------------------------------*/
 
 static int __init usbnet_init(void)
index ddbbb7de894b4039d70003197737d5591649ad15..4410e4166c6cb3d26ca2cdb1d697997fd61281a6 100644 (file)
@@ -163,6 +163,12 @@ extern int usbnet_resume(struct usb_interface *);
 extern void usbnet_disconnect(struct usb_interface *);
 extern void usbnet_device_suggests_idle(struct usbnet *dev);
 
+extern int usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
+                   u16 value, u16 index, void *data, u16 size);
+extern int usbnet_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
+                   u16 value, u16 index, const void *data, u16 size);
+extern int usbnet_write_cmd_async(struct usbnet *dev, u8 cmd, u8 reqtype,
+                   u16 value, u16 index, const void *data, u16 size);
 
 /* Drivers that reuse some of the standard USB CDC infrastructure
  * (notably, using multiple interfaces according to the CDC