From dc8c9d1df01a10c54e4915046509d9dfcd66ee83 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 16 Oct 2009 15:34:41 -0500 Subject: [PATCH] gadget: gadgetfs changes for MTP support MTP protocol support using gadgetfs requires the ability to cancel I/O transactions. Signed-off-by: Jun Li --- drivers/usb/gadget/inode.c | 102 +++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index febadaa2a80d..87c878b6a46c 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -40,6 +40,8 @@ #include #include +#include +#include /* * 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; } -- 2.39.5