root/drivers/usb/usbip/vudc_dev.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>
 *               Krzysztof Opasiak <k.opasiak@samsung.com>
 */

#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/usb.h>
#include <linux/usb/gadget.h>
#include <linux/usb/hcd.h>
#include <linux/kthread.h>
#include <linux/file.h>
#include <linux/byteorder/generic.h>

#include "usbip_common.h"
#include "vudc.h"

#define VIRTUAL_ENDPOINTS (1 /* ep0 */ + 15 /* in eps */ + 15 /* out eps */)

/* urb-related structures alloc / free */


static void free_urb(struct urb *urb)
{
        if (!urb)
                return;

        kfree(urb->setup_packet);
        urb->setup_packet = NULL;

        kfree(urb->transfer_buffer);
        urb->transfer_buffer = NULL;

        usb_free_urb(urb);
}

struct urbp *alloc_urbp(void)
{
        struct urbp *urb_p;

        urb_p = kzalloc_obj(*urb_p);
        if (!urb_p)
                return urb_p;

        urb_p->urb = NULL;
        urb_p->ep = NULL;
        INIT_LIST_HEAD(&urb_p->urb_entry);
        return urb_p;
}

static void free_urbp(struct urbp *urb_p)
{
        kfree(urb_p);
}

void free_urbp_and_urb(struct urbp *urb_p)
{
        if (!urb_p)
                return;
        free_urb(urb_p->urb);
        free_urbp(urb_p);
}


/* utilities ; almost verbatim from dummy_hcd.c */

/* called with spinlock held */
static void nuke(struct vudc *udc, struct vep *ep)
{
        struct vrequest *req;

        while (!list_empty(&ep->req_queue)) {
                req = list_first_entry(&ep->req_queue, struct vrequest,
                                       req_entry);
                list_del_init(&req->req_entry);
                req->req.status = -ESHUTDOWN;

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

/* caller must hold lock */
static void stop_activity(struct vudc *udc)
{
        int i;
        struct urbp *urb_p, *tmp;

        udc->address = 0;

        for (i = 0; i < VIRTUAL_ENDPOINTS; i++)
                nuke(udc, &udc->ep[i]);

        list_for_each_entry_safe(urb_p, tmp, &udc->urb_queue, urb_entry) {
                list_del(&urb_p->urb_entry);
                free_urbp_and_urb(urb_p);
        }
}

struct vep *vudc_find_endpoint(struct vudc *udc, u8 address)
{
        int i;

        if ((address & ~USB_DIR_IN) == 0)
                return &udc->ep[0];

        for (i = 1; i < VIRTUAL_ENDPOINTS; i++) {
                struct vep *ep = &udc->ep[i];

                if (!ep->desc)
                        continue;
                if (ep->desc->bEndpointAddress == address)
                        return ep;
        }
        return NULL;
}

/* gadget ops */

static int vgadget_get_frame(struct usb_gadget *_gadget)
{
        struct timespec64 now;
        struct vudc *udc = usb_gadget_to_vudc(_gadget);

        ktime_get_ts64(&now);
        return ((now.tv_sec - udc->start_time.tv_sec) * 1000 +
                (now.tv_nsec - udc->start_time.tv_nsec) / NSEC_PER_MSEC)
                        & 0x7FF;
}

static int vgadget_set_selfpowered(struct usb_gadget *_gadget, int value)
{
        struct vudc *udc = usb_gadget_to_vudc(_gadget);

        if (value)
                udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
        else
                udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
        return 0;
}

static int vgadget_pullup(struct usb_gadget *_gadget, int value)
{
        struct vudc *udc = usb_gadget_to_vudc(_gadget);
        unsigned long flags;
        int ret;


        spin_lock_irqsave(&udc->lock, flags);
        value = !!value;
        if (value == udc->pullup)
                goto unlock;

        udc->pullup = value;
        if (value) {
                udc->gadget.speed = min_t(u8, USB_SPEED_HIGH,
                                           udc->driver->max_speed);
                udc->ep[0].ep.maxpacket = 64;
                /*
                 * This is the first place where we can ask our
                 * gadget driver for descriptors.
                 */
                ret = get_gadget_descs(udc);
                if (ret) {
                        dev_err(&udc->gadget.dev, "Unable go get desc: %d", ret);
                        goto unlock;
                }

                spin_unlock_irqrestore(&udc->lock, flags);
                usbip_start_eh(&udc->ud);
        } else {
                /* Invalidate descriptors */
                udc->desc_cached = 0;

                spin_unlock_irqrestore(&udc->lock, flags);
                usbip_event_add(&udc->ud, VUDC_EVENT_REMOVED);
                usbip_stop_eh(&udc->ud); /* Wait for eh completion */
        }

        return 0;

unlock:
        spin_unlock_irqrestore(&udc->lock, flags);
        return 0;
}

static int vgadget_udc_start(struct usb_gadget *g,
                struct usb_gadget_driver *driver)
{
        struct vudc *udc = usb_gadget_to_vudc(g);
        unsigned long flags;

        spin_lock_irqsave(&udc->lock, flags);
        udc->driver = driver;
        udc->pullup = udc->connected = udc->desc_cached = 0;
        spin_unlock_irqrestore(&udc->lock, flags);

        return 0;
}

static int vgadget_udc_stop(struct usb_gadget *g)
{
        struct vudc *udc = usb_gadget_to_vudc(g);
        unsigned long flags;

        spin_lock_irqsave(&udc->lock, flags);
        udc->driver = NULL;
        spin_unlock_irqrestore(&udc->lock, flags);
        return 0;
}

static const struct usb_gadget_ops vgadget_ops = {
        .get_frame      = vgadget_get_frame,
        .set_selfpowered = vgadget_set_selfpowered,
        .pullup         = vgadget_pullup,
        .udc_start      = vgadget_udc_start,
        .udc_stop       = vgadget_udc_stop,
};


/* endpoint ops */

static int vep_enable(struct usb_ep *_ep,
                const struct usb_endpoint_descriptor *desc)
{
        struct vep      *ep;
        struct vudc     *udc;
        unsigned int    maxp;
        unsigned long   flags;

        ep = to_vep(_ep);
        udc = ep_to_vudc(ep);

        if (!_ep || !desc || ep->desc || _ep->caps.type_control
                        || desc->bDescriptorType != USB_DT_ENDPOINT)
                return -EINVAL;

        if (!udc->driver)
                return -ESHUTDOWN;

        spin_lock_irqsave(&udc->lock, flags);

        maxp = usb_endpoint_maxp(desc);
        _ep->maxpacket = maxp;
        ep->desc = desc;
        ep->type = usb_endpoint_type(desc);
        ep->halted = ep->wedged = 0;

        spin_unlock_irqrestore(&udc->lock, flags);

        return 0;
}

static int vep_disable(struct usb_ep *_ep)
{
        struct vep *ep;
        struct vudc *udc;
        unsigned long flags;

        ep = to_vep(_ep);
        udc = ep_to_vudc(ep);
        if (!_ep || !ep->desc || _ep->caps.type_control)
                return -EINVAL;

        spin_lock_irqsave(&udc->lock, flags);
        ep->desc = NULL;
        nuke(udc, ep);
        spin_unlock_irqrestore(&udc->lock, flags);

        return 0;
}

static struct usb_request *vep_alloc_request(struct usb_ep *_ep,
                gfp_t mem_flags)
{
        struct vrequest *req;

        if (!_ep)
                return NULL;

        req = kzalloc_obj(*req, mem_flags);
        if (!req)
                return NULL;

        INIT_LIST_HEAD(&req->req_entry);

        return &req->req;
}

static void vep_free_request(struct usb_ep *_ep, struct usb_request *_req)
{
        struct vrequest *req;

        /* ep is always valid here - see usb_ep_free_request() */
        if (!_req)
                return;

        req = to_vrequest(_req);
        kfree(req);
}

static int vep_queue(struct usb_ep *_ep, struct usb_request *_req,
                gfp_t mem_flags)
{
        struct vep *ep;
        struct vrequest *req;
        struct vudc *udc;
        unsigned long flags;

        if (!_ep || !_req)
                return -EINVAL;

        ep = to_vep(_ep);
        req = to_vrequest(_req);
        udc = ep_to_vudc(ep);

        spin_lock_irqsave(&udc->lock, flags);
        _req->actual = 0;
        _req->status = -EINPROGRESS;

        list_add_tail(&req->req_entry, &ep->req_queue);
        spin_unlock_irqrestore(&udc->lock, flags);

        return 0;
}

static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
        struct vep *ep;
        struct vrequest *req;
        struct vudc *udc;
        struct vrequest *lst;
        unsigned long flags;
        int ret = -EINVAL;

        if (!_ep || !_req)
                return ret;

        ep = to_vep(_ep);
        req = to_vrequest(_req);
        udc = req->udc;

        if (!udc->driver)
                return -ESHUTDOWN;

        spin_lock_irqsave(&udc->lock, flags);
        list_for_each_entry(lst, &ep->req_queue, req_entry) {
                if (&lst->req == _req) {
                        list_del_init(&lst->req_entry);
                        _req->status = -ECONNRESET;
                        ret = 0;
                        break;
                }
        }
        spin_unlock_irqrestore(&udc->lock, flags);

        if (ret == 0)
                usb_gadget_giveback_request(_ep, _req);

        return ret;
}

static int
vep_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
{
        struct vep *ep;
        struct vudc *udc;
        unsigned long flags;
        int ret = 0;

        ep = to_vep(_ep);
        if (!_ep)
                return -EINVAL;

        udc = ep_to_vudc(ep);
        if (!udc->driver)
                return -ESHUTDOWN;

        spin_lock_irqsave(&udc->lock, flags);
        if (!value)
                ep->halted = ep->wedged = 0;
        else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
                        !list_empty(&ep->req_queue))
                ret = -EAGAIN;
        else {
                ep->halted = 1;
                if (wedged)
                        ep->wedged = 1;
        }

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

static int
vep_set_halt(struct usb_ep *_ep, int value)
{
        return vep_set_halt_and_wedge(_ep, value, 0);
}

static int vep_set_wedge(struct usb_ep *_ep)
{
        return vep_set_halt_and_wedge(_ep, 1, 1);
}

static const struct usb_ep_ops vep_ops = {
        .enable         = vep_enable,
        .disable        = vep_disable,

        .alloc_request  = vep_alloc_request,
        .free_request   = vep_free_request,

        .queue          = vep_queue,
        .dequeue        = vep_dequeue,

        .set_halt       = vep_set_halt,
        .set_wedge      = vep_set_wedge,
};


/* shutdown / reset / error handlers */

static void vudc_shutdown(struct usbip_device *ud)
{
        struct vudc *udc = container_of(ud, struct vudc, ud);
        int call_disconnect = 0;
        unsigned long flags;

        dev_dbg(&udc->pdev->dev, "device shutdown");
        if (ud->tcp_socket)
                kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);

        if (ud->tcp_rx) {
                kthread_stop_put(ud->tcp_rx);
                ud->tcp_rx = NULL;
        }
        if (ud->tcp_tx) {
                kthread_stop_put(ud->tcp_tx);
                ud->tcp_tx = NULL;
        }

        if (ud->tcp_socket) {
                sockfd_put(ud->tcp_socket);
                ud->tcp_socket = NULL;
        }

        spin_lock_irqsave(&udc->lock, flags);
        stop_activity(udc);
        if (udc->connected && udc->driver->disconnect)
                call_disconnect = 1;
        udc->connected = 0;
        spin_unlock_irqrestore(&udc->lock, flags);
        if (call_disconnect)
                udc->driver->disconnect(&udc->gadget);
}

static void vudc_device_reset(struct usbip_device *ud)
{
        struct vudc *udc = container_of(ud, struct vudc, ud);
        unsigned long flags;

        dev_dbg(&udc->pdev->dev, "device reset");
        spin_lock_irqsave(&udc->lock, flags);
        stop_activity(udc);
        spin_unlock_irqrestore(&udc->lock, flags);
        if (udc->driver)
                usb_gadget_udc_reset(&udc->gadget, udc->driver);
        spin_lock_irqsave(&ud->lock, flags);
        ud->status = SDEV_ST_AVAILABLE;
        spin_unlock_irqrestore(&ud->lock, flags);
}

static void vudc_device_unusable(struct usbip_device *ud)
{
        unsigned long flags;

        spin_lock_irqsave(&ud->lock, flags);
        ud->status = SDEV_ST_ERROR;
        spin_unlock_irqrestore(&ud->lock, flags);
}

/* device setup / cleanup */

struct vudc_device *alloc_vudc_device(int devid)
{
        struct vudc_device *udc_dev;

        udc_dev = kzalloc_obj(*udc_dev);
        if (!udc_dev)
                return NULL;

        INIT_LIST_HEAD(&udc_dev->dev_entry);

        udc_dev->pdev = platform_device_alloc(GADGET_NAME, devid);
        if (!udc_dev->pdev) {
                kfree(udc_dev);
                udc_dev = NULL;
        }

        return udc_dev;
}

void put_vudc_device(struct vudc_device *udc_dev)
{
        platform_device_put(udc_dev->pdev);
        kfree(udc_dev);
}

static int init_vudc_hw(struct vudc *udc)
{
        int i;
        struct usbip_device *ud = &udc->ud;
        struct vep *ep;

        udc->ep = kzalloc_objs(*udc->ep, VIRTUAL_ENDPOINTS);
        if (!udc->ep)
                goto nomem_ep;

        INIT_LIST_HEAD(&udc->gadget.ep_list);

        /* create ep0 and 15 in, 15 out general purpose eps */
        for (i = 0; i < VIRTUAL_ENDPOINTS; ++i) {
                int is_out = i % 2;
                int num = (i + 1) / 2;

                ep = &udc->ep[i];

                sprintf(ep->name, "ep%d%s", num,
                        i ? (is_out ? "out" : "in") : "");
                ep->ep.name = ep->name;

                ep->ep.ops = &vep_ops;

                usb_ep_set_maxpacket_limit(&ep->ep, ~0);
                ep->ep.max_streams = 16;
                ep->gadget = &udc->gadget;
                INIT_LIST_HEAD(&ep->req_queue);

                if (i == 0) {
                        /* ep0 */
                        ep->ep.caps.type_control = true;
                        ep->ep.caps.dir_out = true;
                        ep->ep.caps.dir_in = true;

                        udc->gadget.ep0 = &ep->ep;
                } else {
                        /* All other eps */
                        ep->ep.caps.type_iso = true;
                        ep->ep.caps.type_int = true;
                        ep->ep.caps.type_bulk = true;

                        if (is_out)
                                ep->ep.caps.dir_out = true;
                        else
                                ep->ep.caps.dir_in = true;

                        list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
                }
        }

        spin_lock_init(&udc->lock);
        spin_lock_init(&udc->lock_tx);
        INIT_LIST_HEAD(&udc->urb_queue);
        INIT_LIST_HEAD(&udc->tx_queue);
        init_waitqueue_head(&udc->tx_waitq);

        spin_lock_init(&ud->lock);
        mutex_init(&ud->sysfs_lock);
        ud->status = SDEV_ST_AVAILABLE;
        ud->side = USBIP_VUDC;

        ud->eh_ops.shutdown = vudc_shutdown;
        ud->eh_ops.reset    = vudc_device_reset;
        ud->eh_ops.unusable = vudc_device_unusable;

        v_init_timer(udc);
        return 0;

nomem_ep:
                return -ENOMEM;
}

static void cleanup_vudc_hw(struct vudc *udc)
{
        kfree(udc->ep);
}

/* platform driver ops */

int vudc_probe(struct platform_device *pdev)
{
        struct vudc *udc;
        int ret = -ENOMEM;

        udc = kzalloc_obj(*udc);
        if (!udc)
                goto out;

        udc->gadget.name = GADGET_NAME;
        udc->gadget.ops = &vgadget_ops;
        udc->gadget.max_speed = USB_SPEED_HIGH;
        udc->gadget.dev.parent = &pdev->dev;
        udc->pdev = pdev;

        ret = init_vudc_hw(udc);
        if (ret)
                goto err_init_vudc_hw;

        ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
        if (ret < 0)
                goto err_add_udc;

        platform_set_drvdata(pdev, udc);

        return ret;

err_add_udc:
        cleanup_vudc_hw(udc);
err_init_vudc_hw:
        kfree(udc);
out:
        return ret;
}

void vudc_remove(struct platform_device *pdev)
{
        struct vudc *udc = platform_get_drvdata(pdev);

        usb_del_gadget_udc(&udc->gadget);
        cleanup_vudc_hw(udc);
        kfree(udc);
}