linux/drivers/usb/usbip/vudc_rx.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Karol Kosik <karo9@interia.eu>
   3 * Copyright (C) 2015-2016 Samsung Electronics
   4 *               Igor Kotrasinski <i.kotrasinsk@samsung.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <net/sock.h>
  21#include <linux/list.h>
  22#include <linux/kthread.h>
  23
  24#include "usbip_common.h"
  25#include "vudc.h"
  26
  27static int alloc_urb_from_cmd(struct urb **urbp,
  28                              struct usbip_header *pdu, u8 type)
  29{
  30        struct urb *urb;
  31
  32        if (type == USB_ENDPOINT_XFER_ISOC)
  33                urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets,
  34                                          GFP_KERNEL);
  35        else
  36                urb = usb_alloc_urb(0, GFP_KERNEL);
  37
  38        if (!urb)
  39                goto err;
  40
  41        usbip_pack_pdu(pdu, urb, USBIP_CMD_SUBMIT, 0);
  42
  43        if (urb->transfer_buffer_length > 0) {
  44                urb->transfer_buffer = kzalloc(urb->transfer_buffer_length,
  45                        GFP_KERNEL);
  46                if (!urb->transfer_buffer)
  47                        goto free_urb;
  48        }
  49
  50        urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8,
  51                            GFP_KERNEL);
  52        if (!urb->setup_packet)
  53                goto free_buffer;
  54
  55        /*
  56         * FIXME - we only setup pipe enough for usbip functions
  57         * to behave nicely
  58         */
  59        urb->pipe |= pdu->base.direction == USBIP_DIR_IN ?
  60                        USB_DIR_IN : USB_DIR_OUT;
  61
  62        *urbp = urb;
  63        return 0;
  64
  65free_buffer:
  66        kfree(urb->transfer_buffer);
  67        urb->transfer_buffer = NULL;
  68free_urb:
  69        usb_free_urb(urb);
  70err:
  71        return -ENOMEM;
  72}
  73
  74static int v_recv_cmd_unlink(struct vudc *udc,
  75                                struct usbip_header *pdu)
  76{
  77        unsigned long flags;
  78        struct urbp *urb_p;
  79
  80        spin_lock_irqsave(&udc->lock, flags);
  81        list_for_each_entry(urb_p, &udc->urb_queue, urb_entry) {
  82                if (urb_p->seqnum != pdu->u.cmd_unlink.seqnum)
  83                        continue;
  84                urb_p->urb->unlinked = -ECONNRESET;
  85                urb_p->seqnum = pdu->base.seqnum;
  86                v_kick_timer(udc, jiffies);
  87                spin_unlock_irqrestore(&udc->lock, flags);
  88                return 0;
  89        }
  90        /* Not found, completed / not queued */
  91        spin_lock(&udc->lock_tx);
  92        v_enqueue_ret_unlink(udc, pdu->base.seqnum, 0);
  93        wake_up(&udc->tx_waitq);
  94        spin_unlock(&udc->lock_tx);
  95        spin_unlock_irqrestore(&udc->lock, flags);
  96
  97        return 0;
  98}
  99
 100static int v_recv_cmd_submit(struct vudc *udc,
 101                                 struct usbip_header *pdu)
 102{
 103        int ret = 0;
 104        struct urbp *urb_p;
 105        u8 address;
 106        unsigned long flags;
 107
 108        urb_p = alloc_urbp();
 109        if (!urb_p) {
 110                usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_MALLOC);
 111                return -ENOMEM;
 112        }
 113
 114        /* base.ep is pipeendpoint(pipe) */
 115        address = pdu->base.ep;
 116        if (pdu->base.direction == USBIP_DIR_IN)
 117                address |= USB_DIR_IN;
 118
 119        spin_lock_irq(&udc->lock);
 120        urb_p->ep = vudc_find_endpoint(udc, address);
 121        if (!urb_p->ep) {
 122                /* we don't know the type, there may be isoc data! */
 123                dev_err(&udc->pdev->dev, "request to nonexistent endpoint");
 124                spin_unlock_irq(&udc->lock);
 125                usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_TCP);
 126                ret = -EPIPE;
 127                goto free_urbp;
 128        }
 129        urb_p->type = urb_p->ep->type;
 130        spin_unlock_irq(&udc->lock);
 131
 132        urb_p->new = 1;
 133        urb_p->seqnum = pdu->base.seqnum;
 134
 135        ret = alloc_urb_from_cmd(&urb_p->urb, pdu, urb_p->ep->type);
 136        if (ret) {
 137                usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_MALLOC);
 138                ret = -ENOMEM;
 139                goto free_urbp;
 140        }
 141
 142        urb_p->urb->status = -EINPROGRESS;
 143
 144        /* FIXME: more pipe setup to please usbip_common */
 145        urb_p->urb->pipe &= ~(3 << 30);
 146        switch (urb_p->ep->type) {
 147        case USB_ENDPOINT_XFER_BULK:
 148                urb_p->urb->pipe |= (PIPE_BULK << 30);
 149                break;
 150        case USB_ENDPOINT_XFER_INT:
 151                urb_p->urb->pipe |= (PIPE_INTERRUPT << 30);
 152                break;
 153        case USB_ENDPOINT_XFER_CONTROL:
 154                urb_p->urb->pipe |= (PIPE_CONTROL << 30);
 155                break;
 156        case USB_ENDPOINT_XFER_ISOC:
 157                urb_p->urb->pipe |= (PIPE_ISOCHRONOUS << 30);
 158                break;
 159        }
 160        ret = usbip_recv_xbuff(&udc->ud, urb_p->urb);
 161        if (ret < 0)
 162                goto free_urbp;
 163
 164        ret = usbip_recv_iso(&udc->ud, urb_p->urb);
 165        if (ret < 0)
 166                goto free_urbp;
 167
 168        spin_lock_irqsave(&udc->lock, flags);
 169        v_kick_timer(udc, jiffies);
 170        list_add_tail(&urb_p->urb_entry, &udc->urb_queue);
 171        spin_unlock_irqrestore(&udc->lock, flags);
 172
 173        return 0;
 174
 175free_urbp:
 176        free_urbp_and_urb(urb_p);
 177        return ret;
 178}
 179
 180static int v_rx_pdu(struct usbip_device *ud)
 181{
 182        int ret;
 183        struct usbip_header pdu;
 184        struct vudc *udc = container_of(ud, struct vudc, ud);
 185
 186        memset(&pdu, 0, sizeof(pdu));
 187        ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
 188        if (ret != sizeof(pdu)) {
 189                usbip_event_add(ud, VUDC_EVENT_ERROR_TCP);
 190                if (ret >= 0)
 191                        return -EPIPE;
 192                return ret;
 193        }
 194        usbip_header_correct_endian(&pdu, 0);
 195
 196        spin_lock_irq(&ud->lock);
 197        ret = (ud->status == SDEV_ST_USED);
 198        spin_unlock_irq(&ud->lock);
 199        if (!ret) {
 200                usbip_event_add(ud, VUDC_EVENT_ERROR_TCP);
 201                return -EBUSY;
 202        }
 203
 204        switch (pdu.base.command) {
 205        case USBIP_CMD_UNLINK:
 206                ret = v_recv_cmd_unlink(udc, &pdu);
 207                break;
 208        case USBIP_CMD_SUBMIT:
 209                ret = v_recv_cmd_submit(udc, &pdu);
 210                break;
 211        default:
 212                ret = -EPIPE;
 213                pr_err("rx: unknown command");
 214                break;
 215        }
 216        return ret;
 217}
 218
 219int v_rx_loop(void *data)
 220{
 221        struct usbip_device *ud = data;
 222        int ret = 0;
 223
 224        while (!kthread_should_stop()) {
 225                if (usbip_event_happened(ud))
 226                        break;
 227                ret = v_rx_pdu(ud);
 228                if (ret < 0) {
 229                        pr_warn("v_rx exit with error %d", ret);
 230                        break;
 231                }
 232        }
 233        return ret;
 234}
 235