]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
gadget: gadgetfs changes for MTP support
authorRob Herring <r.herring@freescale.com>
Fri, 16 Oct 2009 20:34:41 +0000 (15:34 -0500)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:32:28 +0000 (08:32 +0200)
MTP protocol support using gadgetfs requires the ability to cancel
I/O transactions.

Signed-off-by: Jun Li <r65092@freescale.com>
drivers/usb/gadget/inode.c

index febadaa2a80d8a32fbc1040ace61979134b9e20b..87c878b6a46ce19da0f3f6d1bf0a2dd176f5fceb 100644 (file)
@@ -40,6 +40,8 @@
 #include <linux/usb/gadgetfs.h>
 #include <linux/usb/gadget.h>
 
+#include <linux/delay.h>
+#include <linux/time.h>
 
 /*
  * The gadgetfs API maps each endpoint to a file descriptor so that you
@@ -81,6 +83,9 @@ MODULE_DESCRIPTION (DRIVER_DESC);
 MODULE_AUTHOR ("David Brownell");
 MODULE_LICENSE ("GPL");
 
+/* Cancel IO, To store the bulkin and bulkout ep data. */
+static struct ep_data *gp_ep_bulkin_data;
+static struct ep_data *gp_ep_bulkout_data;
 
 /*----------------------------------------------------------------------*/
 
@@ -265,6 +270,10 @@ static const char *CHIP;
 #define INFO(dev,fmt,args...) \
        xprintk(dev , KERN_INFO , fmt , ## args)
 
+/* Cancel IO */
+static int mtp_ctrl_cmd;
+static int gbCancelFlag;
+static unsigned long mtptimestamp;
 
 /*----------------------------------------------------------------------*/
 
@@ -275,6 +284,17 @@ static const char *CHIP;
  * precise FIFO status when recovering from cancellation.
  */
 
+/* Cancel IO */
+static void cancel_io_process(struct work_struct *work)
+{
+       if (gp_ep_bulkout_data->req->status == -EINPROGRESS)
+               usb_ep_dequeue(gp_ep_bulkout_data->ep, gp_ep_bulkout_data->req);
+
+       if (gp_ep_bulkin_data->req->status == -EINPROGRESS)
+               usb_ep_dequeue(gp_ep_bulkin_data->ep, gp_ep_bulkin_data->req);
+}
+static DECLARE_DELAYED_WORK(cancel_work, cancel_io_process);
+
 static void epio_complete (struct usb_ep *ep, struct usb_request *req)
 {
        struct ep_data  *epdata = ep->driver_data;
@@ -875,6 +895,8 @@ ep_open (struct inode *inode, struct file *fd)
 {
        struct ep_data          *data = inode->i_private;
        int                     value = -EBUSY;
+       char *epin = "ep1in";
+       char *epout = "ep1out";
 
        if (mutex_lock_interruptible(&data->lock) != 0)
                return -EINTR;
@@ -887,6 +909,12 @@ ep_open (struct inode *inode, struct file *fd)
                get_ep (data);
                fd->private_data = data;
                VDEBUG (data->dev, "%s ready\n", data->name);
+               /* Cancel IO */
+               if (0 == strcmp(data->name, epin))
+                       gp_ep_bulkin_data = fd->private_data;
+
+               if (0 == strcmp(data->name, epout))
+                       gp_ep_bulkout_data = fd->private_data;
        } else
                DBG (data->dev, "%s state %d\n",
                        data->name, data->state);
@@ -1054,6 +1082,7 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
                                        retval = len;
                                clean_req (dev->gadget->ep0, dev->req);
                                /* NOTE userspace can't yet choose to stall */
+                               dev->state = STATE_DEV_CONNECTED;       /* Cancel IO */
                        }
                }
                goto done;
@@ -1067,6 +1096,12 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
        len -= len % sizeof (struct usb_gadgetfs_event);
        dev->usermode_setup = 1;
 
+    /* Cancel IO, signal abort blocked IO. */
+       if (mtp_ctrl_cmd == 1) {
+               mtp_ctrl_cmd = 0;
+               schedule_delayed_work(&cancel_work, HZ / 100);
+       }
+
 scan:
        /* return queued events right away */
        if (dev->ev_next != 0) {
@@ -1391,6 +1426,16 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
        struct usb_gadgetfs_event       *event;
        u16                             w_value = le16_to_cpu(ctrl->wValue);
        u16                             w_length = le16_to_cpu(ctrl->wLength);
+       struct timeval tv;
+
+    /* Cancel IO */
+       if (0x67 == ctrl->bRequest && 1 == gbCancelFlag
+           && dev->state == STATE_DEV_SETUP)
+               dev->state = STATE_DEV_CONNECTED;
+
+       if (0x67 == ctrl->bRequest && 2 == mtp_ctrl_cmd
+           && dev->state == STATE_DEV_SETUP)
+               dev->state = STATE_DEV_CONNECTED;
 
        spin_lock (&dev->lock);
        dev->setup_abort = 0;
@@ -1418,6 +1463,11 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
         */
        } else if (dev->state == STATE_DEV_SETUP)
                dev->setup_abort = 1;
+       /*Cancel IO */
+       if (mtp_ctrl_cmd == 1 && gbCancelFlag == 1 && dev->setup_abort == 1) {
+               INFO(dev, "0x64->setup\n");
+               dev->setup_abort = 0;
+       }
 
        req->buf = dev->rbuf;
        req->dma = DMA_ADDR_INVALID;
@@ -1550,11 +1600,63 @@ delegate:
                                /* we can't currently stall these */
                                dev->setup_can_stall = 0;
                        }
+                       /* Cancel IO */
+                       if (0x67 == ctrl->bRequest && 1 == gbCancelFlag) {
+                               gbCancelFlag = 0;
+
+                               setup_req(gadget->ep0, dev->req, 4);
+                               *(unsigned long *)dev->req->buf = 0x20190004;
+                               usb_ep_queue(gadget->ep0, dev->req, GFP_ATOMIC);
+
+                               spin_unlock(&dev->lock);
+                               return 0;
+                       }
+                       if (ctrl->bRequest == 0x67 && mtp_ctrl_cmd == 2) {
+                               /* get status */
+                               mtp_ctrl_cmd = 0;
+                       }
 
                        /* state changes when reader collects event */
                        event = next_event (dev, GADGETFS_SETUP);
                        event->u.setup = *ctrl;
+                       /*  Cancel IO */
+                       if (0x64 == ctrl->bRequest) {
+                               mtp_ctrl_cmd = 1;
+                               gbCancelFlag = 1;
+
+                               /* get the timestamp */
+                               do_gettimeofday(&tv);
+                               mtptimestamp = tv.tv_usec;
+                               event->u.setup.wValue =
+                                   (unsigned short)tv.tv_usec;
+                       }
+                       if (0x66 == ctrl->bRequest) {
+                               /* get the timestamp */
+                               do_gettimeofday(&tv);
+                               mtptimestamp = tv.tv_usec;
+                               event->u.setup.wValue =
+                                   (unsigned short)tv.tv_usec;
+                       }
+
                        ep0_readable (dev);
+                       /* Reset request. */
+                       if (ctrl->bRequest == 0x66) {   /* reset ,send ZLP */
+                               mtp_ctrl_cmd = 2;
+
+                               if (gp_ep_bulkout_data->req->status ==
+                                   -EINPROGRESS) {
+                                       usb_ep_dequeue(gp_ep_bulkout_data->ep,
+                                                      gp_ep_bulkout_data->req);
+                               }
+                               if (gp_ep_bulkin_data->req->status ==
+                                   -EINPROGRESS) {
+                                       usb_ep_dequeue(gp_ep_bulkin_data->ep,
+                                                      gp_ep_bulkin_data->req);
+                               }
+                       }
+                       if (ctrl->bRequest == 0x65)
+                               pr_debug("i:0x65,not supported\n");
+
                        spin_unlock (&dev->lock);
                        return 0;
                }