-/*----------------------------------------------------------------------*/
-
-/* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */
-
-struct kiocb_priv {
- struct usb_request *req;
- struct ep_data *epdata;
- void *buf;
- char __user *ubuf;
- unsigned actual;
-};
-
-static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e)
-{
- struct kiocb_priv *priv = (void *) &iocb->private;
- struct ep_data *epdata;
- int value;
-
- local_irq_disable();
- epdata = priv->epdata;
- // spin_lock(&epdata->dev->lock);
- kiocbSetCancelled(iocb);
- if (likely(epdata && epdata->ep && priv->req))
- value = usb_ep_dequeue (epdata->ep, priv->req);
- else
- value = -EINVAL;
- // spin_unlock(&epdata->dev->lock);
- local_irq_enable();
-
- aio_put_req(iocb);
- return value;
-}
-
-static long ep_aio_read_retry(struct kiocb *iocb)
-{
- struct kiocb_priv *priv = (void *) &iocb->private;
- int status = priv->actual;
-
- /* we "retry" to get the right mm context for this: */
- status = copy_to_user(priv->ubuf, priv->buf, priv->actual);
- if (unlikely(0 != status))
- status = -EFAULT;
- else
- status = priv->actual;
- kfree(priv->buf);
- aio_put_req(iocb);
- return status;
-}
-
-static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
-{
- struct kiocb *iocb = req->context;
- struct kiocb_priv *priv = (void *) &iocb->private;
- struct ep_data *epdata = priv->epdata;
-
- /* lock against disconnect (and ideally, cancel) */
- spin_lock(&epdata->dev->lock);
- priv->req = NULL;
- priv->epdata = NULL;
- if (NULL == iocb->ki_retry
- || unlikely(0 == req->actual)
- || unlikely(kiocbIsCancelled(iocb))) {
- kfree(req->buf);
- /* aio_complete() reports bytes-transferred _and_ faults */
- if (unlikely(kiocbIsCancelled(iocb)))
- aio_put_req(iocb);
- else
- aio_complete(iocb,
- req->actual ? req->actual : req->status,
- req->status);
- } else {
- /* retry() won't report both; so we hide some faults */
- if (unlikely(0 != req->status))
- DBG(epdata->dev, "%s fault %d len %d\n",
- ep->name, req->status, req->actual);
-
- priv->buf = req->buf;
- priv->actual = req->actual;
- kick_iocb(iocb);
- }
- spin_unlock(&epdata->dev->lock);
-
- usb_ep_free_request(ep, req);
- put_ep(epdata);
-}
-
-static ssize_t
-ep_aio_rwtail(struct kiocb *iocb, char *buf, size_t len, struct ep_data *epdata)
-{
- struct kiocb_priv *priv = (void *) &iocb->private;
- struct usb_request *req;
- ssize_t value;
-
- value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
- if (unlikely(value < 0)) {
- kfree(buf);
- return value;
- }
-
- iocb->ki_cancel = ep_aio_cancel;
- get_ep(epdata);
- priv->epdata = epdata;
- priv->actual = 0;
-
- /* each kiocb is coupled to one usb_request, but we can't
- * allocate or submit those if the host disconnected.
- */
- spin_lock_irq(&epdata->dev->lock);
- if (likely(epdata->ep)) {
- req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
- if (likely(req)) {
- priv->req = req;
- req->buf = buf;
- req->length = len;
- req->complete = ep_aio_complete;
- req->context = iocb;
- value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
- if (unlikely(0 != value))
- usb_ep_free_request(epdata->ep, req);
- } else
- value = -EAGAIN;
- } else
- value = -ENODEV;
- spin_unlock_irq(&epdata->dev->lock);
-
- up(&epdata->lock);
-
- if (unlikely(value))
- put_ep(epdata);
- else
- value = -EIOCBQUEUED;
- return value;
-}
-
-static ssize_t
-ep_aio_read(struct kiocb *iocb, char __user *ubuf, size_t len, loff_t o)
-{
- struct kiocb_priv *priv = (void *) &iocb->private;
- struct ep_data *epdata = iocb->ki_filp->private_data;
- char *buf;
-
- if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN))
- return -EINVAL;
- buf = kmalloc(len, GFP_KERNEL);
- if (unlikely(!buf))
- return -ENOMEM;
- iocb->ki_retry = ep_aio_read_retry;
- priv->ubuf = ubuf;
- return ep_aio_rwtail(iocb, buf, len, epdata);
-}
-
-static ssize_t
-ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o)
-{
- struct ep_data *epdata = iocb->ki_filp->private_data;
- char *buf;
-
- if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN)))
- return -EINVAL;
- buf = kmalloc(len, GFP_KERNEL);
- if (unlikely(!buf))
- return -ENOMEM;
- if (unlikely(copy_from_user(buf, ubuf, len) != 0)) {
- kfree(buf);
- return -EFAULT;
- }
- return ep_aio_rwtail(iocb, buf, len, epdata);
-}
-
-/*----------------------------------------------------------------------*/
-