root/drivers/usb/usbip/vudc_transfer.c
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2015 Karol Kosik <karo9@interia.eu>
 * Copyright (C) 2015-2016 Samsung Electronics
 *               Igor Kotrasinski <i.kotrasinsk@samsung.com>
 *
 * Based on dummy_hcd.c, which is:
 * Copyright (C) 2003 David Brownell
 * Copyright (C) 2003-2005 Alan Stern
 */

#include <linux/usb.h>
#include <linux/timer.h>
#include <linux/usb/ch9.h>

#include "vudc.h"

#define DEV_REQUEST     (USB_TYPE_STANDARD | USB_RECIP_DEVICE)
#define DEV_INREQUEST   (DEV_REQUEST | USB_DIR_IN)
#define INTF_REQUEST    (USB_TYPE_STANDARD | USB_RECIP_INTERFACE)
#define INTF_INREQUEST  (INTF_REQUEST | USB_DIR_IN)
#define EP_REQUEST      (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)
#define EP_INREQUEST    (EP_REQUEST | USB_DIR_IN)

static int get_frame_limit(enum usb_device_speed speed)
{
        switch (speed) {
        case USB_SPEED_LOW:
                return 8 /*bytes*/ * 12 /*packets*/;
        case USB_SPEED_FULL:
                return 64 /*bytes*/ * 19 /*packets*/;
        case USB_SPEED_HIGH:
                return 512 /*bytes*/ * 13 /*packets*/ * 8 /*uframes*/;
        case USB_SPEED_SUPER:
                /* Bus speed is 500000 bytes/ms, so use a little less */
                return 490000;
        default:
                /* error */
                return -1;
        }

}

/*
 * handle_control_request() - handles all control transfers
 * @udc: pointer to vudc
 * @urb: the urb request to handle
 * @setup: pointer to the setup data for a USB device control
 *       request
 * @status: pointer to request handling status
 *
 * Return 0 - if the request was handled
 *        1 - if the request wasn't handles
 *        error code on error
 *
 * Adapted from drivers/usb/gadget/udc/dummy_hcd.c
 */
static int handle_control_request(struct vudc *udc, struct urb *urb,
                                  struct usb_ctrlrequest *setup,
                                  int *status)
{
        struct vep      *ep2;
        int             ret_val = 1;
        unsigned int    w_index;
        unsigned int    w_value;

        w_index = le16_to_cpu(setup->wIndex);
        w_value = le16_to_cpu(setup->wValue);
        switch (setup->bRequest) {
        case USB_REQ_SET_ADDRESS:
                if (setup->bRequestType != DEV_REQUEST)
                        break;
                udc->address = w_value;
                ret_val = 0;
                *status = 0;
                break;
        case USB_REQ_SET_FEATURE:
                if (setup->bRequestType == DEV_REQUEST) {
                        ret_val = 0;
                        switch (w_value) {
                        case USB_DEVICE_REMOTE_WAKEUP:
                                break;
                        case USB_DEVICE_B_HNP_ENABLE:
                                udc->gadget.b_hnp_enable = 1;
                                break;
                        case USB_DEVICE_A_HNP_SUPPORT:
                                udc->gadget.a_hnp_support = 1;
                                break;
                        case USB_DEVICE_A_ALT_HNP_SUPPORT:
                                udc->gadget.a_alt_hnp_support = 1;
                                break;
                        default:
                                ret_val = -EOPNOTSUPP;
                        }
                        if (ret_val == 0) {
                                udc->devstatus |= (1 << w_value);
                                *status = 0;
                        }
                } else if (setup->bRequestType == EP_REQUEST) {
                        /* endpoint halt */
                        ep2 = vudc_find_endpoint(udc, w_index);
                        if (!ep2 || ep2->ep.name == udc->ep[0].ep.name) {
                                ret_val = -EOPNOTSUPP;
                                break;
                        }
                        ep2->halted = 1;
                        ret_val = 0;
                        *status = 0;
                }
                break;
        case USB_REQ_CLEAR_FEATURE:
                if (setup->bRequestType == DEV_REQUEST) {
                        ret_val = 0;
                        switch (w_value) {
                        case USB_DEVICE_REMOTE_WAKEUP:
                                w_value = USB_DEVICE_REMOTE_WAKEUP;
                                break;

                        case USB_DEVICE_U1_ENABLE:
                        case USB_DEVICE_U2_ENABLE:
                        case USB_DEVICE_LTM_ENABLE:
                                ret_val = -EOPNOTSUPP;
                                break;
                        default:
                                ret_val = -EOPNOTSUPP;
                                break;
                        }
                        if (ret_val == 0) {
                                udc->devstatus &= ~(1 << w_value);
                                *status = 0;
                        }
                } else if (setup->bRequestType == EP_REQUEST) {
                        /* endpoint halt */
                        ep2 = vudc_find_endpoint(udc, w_index);
                        if (!ep2) {
                                ret_val = -EOPNOTSUPP;
                                break;
                        }
                        if (!ep2->wedged)
                                ep2->halted = 0;
                        ret_val = 0;
                        *status = 0;
                }
                break;
        case USB_REQ_GET_STATUS:
                if (setup->bRequestType == DEV_INREQUEST
                                || setup->bRequestType == INTF_INREQUEST
                                || setup->bRequestType == EP_INREQUEST) {
                        char *buf;
                        /*
                         * device: remote wakeup, selfpowered
                         * interface: nothing
                         * endpoint: halt
                         */
                        buf = (char *)urb->transfer_buffer;
                        if (urb->transfer_buffer_length > 0) {
                                if (setup->bRequestType == EP_INREQUEST) {
                                        ep2 = vudc_find_endpoint(udc, w_index);
                                        if (!ep2) {
                                                ret_val = -EOPNOTSUPP;
                                                break;
                                        }
                                        buf[0] = ep2->halted;
                                } else if (setup->bRequestType ==
                                           DEV_INREQUEST) {
                                        buf[0] = (u8)udc->devstatus;
                                } else
                                        buf[0] = 0;
                        }
                        if (urb->transfer_buffer_length > 1)
                                buf[1] = 0;
                        urb->actual_length = min_t(u32, 2,
                                urb->transfer_buffer_length);
                        ret_val = 0;
                        *status = 0;
                }
                break;
        }
        return ret_val;
}

/* Adapted from dummy_hcd.c ; caller must hold lock */
static int transfer(struct vudc *udc,
                struct urb *urb, struct vep *ep, int limit)
{
        struct vrequest *req;
        int sent = 0;
top:
        /* if there's no request queued, the device is NAKing; return */
        list_for_each_entry(req, &ep->req_queue, req_entry) {
                unsigned int    host_len, dev_len, len;
                void            *ubuf_pos, *rbuf_pos;
                int             is_short, to_host;
                int             rescan = 0;

                /*
                 * 1..N packets of ep->ep.maxpacket each ... the last one
                 * may be short (including zero length).
                 *
                 * writer can send a zlp explicitly (length 0) or implicitly
                 * (length mod maxpacket zero, and 'zero' flag); they always
                 * terminate reads.
                 */
                host_len = urb->transfer_buffer_length - urb->actual_length;
                dev_len = req->req.length - req->req.actual;
                len = min(host_len, dev_len);

                to_host = usb_pipein(urb->pipe);
                if (unlikely(len == 0))
                        is_short = 1;
                else {
                        /* send multiple of maxpacket first, then remainder */
                        if (len >= ep->ep.maxpacket) {
                                is_short = 0;
                                if (len % ep->ep.maxpacket > 0)
                                        rescan = 1;
                                len -= len % ep->ep.maxpacket;
                        } else {
                                is_short = 1;
                        }

                        ubuf_pos = urb->transfer_buffer + urb->actual_length;
                        rbuf_pos = req->req.buf + req->req.actual;

                        if (urb->pipe & USB_DIR_IN)
                                memcpy(ubuf_pos, rbuf_pos, len);
                        else
                                memcpy(rbuf_pos, ubuf_pos, len);

                        urb->actual_length += len;
                        req->req.actual += len;
                        sent += len;
                }

                /*
                 * short packets terminate, maybe with overflow/underflow.
                 * it's only really an error to write too much.
                 *
                 * partially filling a buffer optionally blocks queue advances
                 * (so completion handlers can clean up the queue) but we don't
                 * need to emulate such data-in-flight.
                 */
                if (is_short) {
                        if (host_len == dev_len) {
                                req->req.status = 0;
                                urb->status = 0;
                        } else if (to_host) {
                                req->req.status = 0;
                                if (dev_len > host_len)
                                        urb->status = -EOVERFLOW;
                                else
                                        urb->status = 0;
                        } else {
                                urb->status = 0;
                                if (host_len > dev_len)
                                        req->req.status = -EOVERFLOW;
                                else
                                        req->req.status = 0;
                        }

                /* many requests terminate without a short packet */
                /* also check if we need to send zlp */
                } else {
                        if (req->req.length == req->req.actual) {
                                if (req->req.zero && to_host)
                                        rescan = 1;
                                else
                                        req->req.status = 0;
                        }
                        if (urb->transfer_buffer_length == urb->actual_length) {
                                if (urb->transfer_flags & URB_ZERO_PACKET &&
                                    !to_host)
                                        rescan = 1;
                                else
                                        urb->status = 0;
                        }
                }

                /* device side completion --> continuable */
                if (req->req.status != -EINPROGRESS) {

                        list_del_init(&req->req_entry);
                        spin_unlock(&udc->lock);
                        usb_gadget_giveback_request(&ep->ep, &req->req);
                        spin_lock(&udc->lock);

                        /* requests might have been unlinked... */
                        rescan = 1;
                }

                /* host side completion --> terminate */
                if (urb->status != -EINPROGRESS)
                        break;

                /* rescan to continue with any other queued i/o */
                if (rescan)
                        goto top;
        }
        return sent;
}

static void v_timer(struct timer_list *t)
{
        struct vudc *udc = timer_container_of(udc, t, tr_timer.timer);
        struct transfer_timer *timer = &udc->tr_timer;
        struct urbp *urb_p, *tmp;
        unsigned long flags;
        struct usb_ep *_ep;
        struct vep *ep;
        int ret = 0;
        int total, limit;

        spin_lock_irqsave(&udc->lock, flags);

        total = get_frame_limit(udc->gadget.speed);
        if (total < 0) {        /* unknown speed, or not set yet */
                timer->state = VUDC_TR_IDLE;
                spin_unlock_irqrestore(&udc->lock, flags);
                return;
        }
        /* is it next frame now? */
        if (time_after(jiffies, timer->frame_start + msecs_to_jiffies(1))) {
                timer->frame_limit = total;
                /* FIXME: how to make it accurate? */
                timer->frame_start = jiffies;
        } else {
                total = timer->frame_limit;
        }

        /* We have to clear ep0 flags separately as it's not on the list */
        udc->ep[0].already_seen = 0;
        list_for_each_entry(_ep, &udc->gadget.ep_list, ep_list) {
                ep = to_vep(_ep);
                ep->already_seen = 0;
        }

restart:
        list_for_each_entry_safe(urb_p, tmp, &udc->urb_queue, urb_entry) {
                struct urb *urb = urb_p->urb;

                ep = urb_p->ep;
                if (urb->unlinked)
                        goto return_urb;
                if (timer->state != VUDC_TR_RUNNING)
                        continue;

                if (!ep) {
                        urb->status = -EPROTO;
                        goto return_urb;
                }

                /* Used up bandwidth? */
                if (total <= 0 && ep->type == USB_ENDPOINT_XFER_BULK)
                        continue;

                if (ep->already_seen)
                        continue;
                ep->already_seen = 1;
                if (ep == &udc->ep[0] && urb_p->new) {
                        ep->setup_stage = 1;
                        urb_p->new = 0;
                }
                if (ep->halted && !ep->setup_stage) {
                        urb->status = -EPIPE;
                        goto return_urb;
                }

                if (ep == &udc->ep[0] && ep->setup_stage) {
                        /* TODO - flush any stale requests */
                        ep->setup_stage = 0;
                        ep->halted = 0;

                        ret = handle_control_request(udc, urb,
                                (struct usb_ctrlrequest *) urb->setup_packet,
                                (&urb->status));
                        if (ret > 0) {
                                spin_unlock(&udc->lock);
                                ret = udc->driver->setup(&udc->gadget,
                                        (struct usb_ctrlrequest *)
                                        urb->setup_packet);
                                spin_lock(&udc->lock);
                        }
                        if (ret >= 0) {
                                /* no delays (max 64kb data stage) */
                                limit = 64 * 1024;
                                goto treat_control_like_bulk;
                        } else {
                                urb->status = -EPIPE;
                                urb->actual_length = 0;
                                goto return_urb;
                        }
                }

                limit = total;
                switch (ep->type) {
                case USB_ENDPOINT_XFER_ISOC:
                        /* TODO: support */
                        urb->status = -EXDEV;
                        break;

                case USB_ENDPOINT_XFER_INT:
                        /*
                         * TODO: figure out bandwidth guarantees
                         * for now, give unlimited bandwidth
                         */
                        limit += urb->transfer_buffer_length;
                        fallthrough;
                default:
treat_control_like_bulk:
                        total -= transfer(udc, urb, ep, limit);
                }
                if (urb->status == -EINPROGRESS)
                        continue;

return_urb:
                if (ep)
                        ep->already_seen = ep->setup_stage = 0;

                spin_lock(&udc->lock_tx);
                list_del(&urb_p->urb_entry);
                if (!urb->unlinked) {
                        v_enqueue_ret_submit(udc, urb_p);
                } else {
                        v_enqueue_ret_unlink(udc, urb_p->seqnum,
                                             urb->unlinked);
                        free_urbp_and_urb(urb_p);
                }
                wake_up(&udc->tx_waitq);
                spin_unlock(&udc->lock_tx);

                goto restart;
        }

        /* TODO - also wait on empty usb_request queues? */
        if (list_empty(&udc->urb_queue))
                timer->state = VUDC_TR_IDLE;
        else
                mod_timer(&timer->timer,
                          timer->frame_start + msecs_to_jiffies(1));

        spin_unlock_irqrestore(&udc->lock, flags);
}

/* All timer functions are run with udc->lock held */

void v_init_timer(struct vudc *udc)
{
        struct transfer_timer *t = &udc->tr_timer;

        timer_setup(&t->timer, v_timer, 0);
        t->state = VUDC_TR_STOPPED;
}

void v_start_timer(struct vudc *udc)
{
        struct transfer_timer *t = &udc->tr_timer;

        dev_dbg(&udc->pdev->dev, "timer start");
        switch (t->state) {
        case VUDC_TR_RUNNING:
                return;
        case VUDC_TR_IDLE:
                return v_kick_timer(udc, jiffies);
        case VUDC_TR_STOPPED:
                t->state = VUDC_TR_IDLE;
                t->frame_start = jiffies;
                t->frame_limit = get_frame_limit(udc->gadget.speed);
                return v_kick_timer(udc, jiffies);
        }
}

void v_kick_timer(struct vudc *udc, unsigned long time)
{
        struct transfer_timer *t = &udc->tr_timer;

        dev_dbg(&udc->pdev->dev, "timer kick");
        switch (t->state) {
        case VUDC_TR_RUNNING:
                return;
        case VUDC_TR_IDLE:
                t->state = VUDC_TR_RUNNING;
                fallthrough;
        case VUDC_TR_STOPPED:
                /* we may want to kick timer to unqueue urbs */
                mod_timer(&t->timer, time);
        }
}

void v_stop_timer(struct vudc *udc)
{
        struct transfer_timer *t = &udc->tr_timer;

        /* timer itself will take care of stopping */
        dev_dbg(&udc->pdev->dev, "timer stop");
        t->state = VUDC_TR_STOPPED;
}