]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/usb/usbnet.c
usbnet: introduce usbnet_{read|write}_cmd_nopm
[karo-tx-linux.git] / drivers / net / usb / usbnet.c
index fc9f578a1e253a781b9406b1e4a43ed2de2688d1..0ac1ff3571cb92e671ddbdbf5c9fee038f875cd6 100644 (file)
@@ -1588,11 +1588,212 @@ int usbnet_resume (struct usb_interface *intf)
                        tasklet_schedule (&dev->bh);
                }
        }
+
+       if (test_and_clear_bit(EVENT_DEVICE_REPORT_IDLE, &dev->flags))
+               usb_autopm_get_interface_no_resume(intf);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(usbnet_resume);
 
+/*
+ * Either a subdriver implements manage_power, then it is assumed to always
+ * be ready to be suspended or it reports the readiness to be suspended
+ * explicitly
+ */
+void usbnet_device_suggests_idle(struct usbnet *dev)
+{
+       if (!test_and_set_bit(EVENT_DEVICE_REPORT_IDLE, &dev->flags)) {
+               dev->intf->needs_remote_wakeup = 1;
+               usb_autopm_put_interface_async(dev->intf);
+       }
+}
+EXPORT_SYMBOL(usbnet_device_suggests_idle);
+
+/*-------------------------------------------------------------------------*/
+static 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;
+}
+
+static 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;
+}
 
+/*
+ * The function can't be called inside suspend/resume callback,
+ * otherwise deadlock will be caused.
+ */
+int usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
+                   u16 value, u16 index, void *data, u16 size)
+{
+       return __usbnet_read_cmd(dev, cmd, reqtype, value, index,
+                                data, size);
+}
+EXPORT_SYMBOL_GPL(usbnet_read_cmd);
+
+/*
+ * The function can't be called inside suspend/resume callback,
+ * otherwise deadlock will be caused.
+ */
+int usbnet_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
+                    u16 value, u16 index, const void *data, u16 size)
+{
+       return __usbnet_write_cmd(dev, cmd, reqtype, value, index,
+                                 data, size);
+}
+EXPORT_SYMBOL_GPL(usbnet_write_cmd);
+
+/*
+ * The function can be called inside suspend/resume callback safely
+ * and should only be called by suspend/resume callback generally.
+ */
+int usbnet_read_cmd_nopm(struct usbnet *dev, u8 cmd, u8 reqtype,
+                         u16 value, u16 index, void *data, u16 size)
+{
+       return __usbnet_read_cmd(dev, cmd, reqtype, value, index,
+                                data, size);
+}
+EXPORT_SYMBOL_GPL(usbnet_read_cmd_nopm);
+
+/*
+ * The function can be called inside suspend/resume callback safely
+ * and should only be called by suspend/resume callback generally.
+ */
+int usbnet_write_cmd_nopm(struct usbnet *dev, u8 cmd, u8 reqtype,
+                         u16 value, u16 index, const void *data,
+                         u16 size)
+{
+       return __usbnet_write_cmd(dev, cmd, reqtype, value, index,
+                                 data, size);
+}
+EXPORT_SYMBOL_GPL(usbnet_write_cmd_nopm);
+
+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);
+}
+
+/*
+ * The caller must make sure that device can't be put into suspend
+ * state until the control URB completes.
+ */
+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)