summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/inode.c')
-rw-r--r--drivers/usb/gadget/inode.c105
1 files changed, 105 insertions, 0 deletions
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index de8a83803505..946507e2527a 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -41,6 +41,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
@@ -82,6 +84,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;
/*----------------------------------------------------------------------*/
@@ -266,6 +271,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;
/*----------------------------------------------------------------------*/
@@ -276,6 +285,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;
@@ -872,6 +892,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;
@@ -884,6 +906,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);
@@ -1047,8 +1075,12 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
// FIXME don't call this with the spinlock held ...
if (copy_to_user (buf, dev->req->buf, len))
retval = -EFAULT;
+ else
+ /* Bug of Cancel IO 6 bytes read. */
+ 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;
@@ -1062,6 +1094,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) {
@@ -1388,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;
@@ -1415,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;
@@ -1547,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;
}