15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
279c02cb1SIgor Kotrasinski /*
379c02cb1SIgor Kotrasinski * Copyright (C) 2015 Karol Kosik <karo9@interia.eu>
479c02cb1SIgor Kotrasinski * Copyright (C) 2015-2016 Samsung Electronics
579c02cb1SIgor Kotrasinski * Igor Kotrasinski <i.kotrasinsk@samsung.com>
679c02cb1SIgor Kotrasinski */
779c02cb1SIgor Kotrasinski
879c02cb1SIgor Kotrasinski #include <net/sock.h>
979c02cb1SIgor Kotrasinski #include <linux/list.h>
1079c02cb1SIgor Kotrasinski #include <linux/kthread.h>
1179c02cb1SIgor Kotrasinski
1279c02cb1SIgor Kotrasinski #include "usbip_common.h"
1379c02cb1SIgor Kotrasinski #include "vudc.h"
1479c02cb1SIgor Kotrasinski
alloc_urb_from_cmd(struct urb ** urbp,struct usbip_header * pdu,u8 type)1579c02cb1SIgor Kotrasinski static int alloc_urb_from_cmd(struct urb **urbp,
1679c02cb1SIgor Kotrasinski struct usbip_header *pdu, u8 type)
1779c02cb1SIgor Kotrasinski {
1879c02cb1SIgor Kotrasinski struct urb *urb;
1979c02cb1SIgor Kotrasinski
2079c02cb1SIgor Kotrasinski if (type == USB_ENDPOINT_XFER_ISOC)
2179c02cb1SIgor Kotrasinski urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets,
2279c02cb1SIgor Kotrasinski GFP_KERNEL);
2379c02cb1SIgor Kotrasinski else
2479c02cb1SIgor Kotrasinski urb = usb_alloc_urb(0, GFP_KERNEL);
2579c02cb1SIgor Kotrasinski
2679c02cb1SIgor Kotrasinski if (!urb)
2779c02cb1SIgor Kotrasinski goto err;
2879c02cb1SIgor Kotrasinski
2979c02cb1SIgor Kotrasinski usbip_pack_pdu(pdu, urb, USBIP_CMD_SUBMIT, 0);
3079c02cb1SIgor Kotrasinski
3179c02cb1SIgor Kotrasinski if (urb->transfer_buffer_length > 0) {
3279c02cb1SIgor Kotrasinski urb->transfer_buffer = kzalloc(urb->transfer_buffer_length,
3379c02cb1SIgor Kotrasinski GFP_KERNEL);
3479c02cb1SIgor Kotrasinski if (!urb->transfer_buffer)
3579c02cb1SIgor Kotrasinski goto free_urb;
3679c02cb1SIgor Kotrasinski }
3779c02cb1SIgor Kotrasinski
3879c02cb1SIgor Kotrasinski urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8,
3979c02cb1SIgor Kotrasinski GFP_KERNEL);
4079c02cb1SIgor Kotrasinski if (!urb->setup_packet)
4179c02cb1SIgor Kotrasinski goto free_buffer;
4279c02cb1SIgor Kotrasinski
4379c02cb1SIgor Kotrasinski /*
4479c02cb1SIgor Kotrasinski * FIXME - we only setup pipe enough for usbip functions
4579c02cb1SIgor Kotrasinski * to behave nicely
4679c02cb1SIgor Kotrasinski */
4779c02cb1SIgor Kotrasinski urb->pipe |= pdu->base.direction == USBIP_DIR_IN ?
4879c02cb1SIgor Kotrasinski USB_DIR_IN : USB_DIR_OUT;
4979c02cb1SIgor Kotrasinski
5079c02cb1SIgor Kotrasinski *urbp = urb;
5179c02cb1SIgor Kotrasinski return 0;
5279c02cb1SIgor Kotrasinski
5379c02cb1SIgor Kotrasinski free_buffer:
5479c02cb1SIgor Kotrasinski kfree(urb->transfer_buffer);
5579c02cb1SIgor Kotrasinski urb->transfer_buffer = NULL;
5679c02cb1SIgor Kotrasinski free_urb:
5779c02cb1SIgor Kotrasinski usb_free_urb(urb);
5879c02cb1SIgor Kotrasinski err:
5979c02cb1SIgor Kotrasinski return -ENOMEM;
6079c02cb1SIgor Kotrasinski }
6179c02cb1SIgor Kotrasinski
v_recv_cmd_unlink(struct vudc * udc,struct usbip_header * pdu)6279c02cb1SIgor Kotrasinski static int v_recv_cmd_unlink(struct vudc *udc,
6379c02cb1SIgor Kotrasinski struct usbip_header *pdu)
6479c02cb1SIgor Kotrasinski {
6579c02cb1SIgor Kotrasinski unsigned long flags;
6679c02cb1SIgor Kotrasinski struct urbp *urb_p;
6779c02cb1SIgor Kotrasinski
6879c02cb1SIgor Kotrasinski spin_lock_irqsave(&udc->lock, flags);
6979c02cb1SIgor Kotrasinski list_for_each_entry(urb_p, &udc->urb_queue, urb_entry) {
7079c02cb1SIgor Kotrasinski if (urb_p->seqnum != pdu->u.cmd_unlink.seqnum)
7179c02cb1SIgor Kotrasinski continue;
7279c02cb1SIgor Kotrasinski urb_p->urb->unlinked = -ECONNRESET;
7379c02cb1SIgor Kotrasinski urb_p->seqnum = pdu->base.seqnum;
7479c02cb1SIgor Kotrasinski v_kick_timer(udc, jiffies);
7579c02cb1SIgor Kotrasinski spin_unlock_irqrestore(&udc->lock, flags);
7679c02cb1SIgor Kotrasinski return 0;
7779c02cb1SIgor Kotrasinski }
7879c02cb1SIgor Kotrasinski /* Not found, completed / not queued */
7979c02cb1SIgor Kotrasinski spin_lock(&udc->lock_tx);
8079c02cb1SIgor Kotrasinski v_enqueue_ret_unlink(udc, pdu->base.seqnum, 0);
8179c02cb1SIgor Kotrasinski wake_up(&udc->tx_waitq);
8279c02cb1SIgor Kotrasinski spin_unlock(&udc->lock_tx);
8379c02cb1SIgor Kotrasinski spin_unlock_irqrestore(&udc->lock, flags);
8479c02cb1SIgor Kotrasinski
8579c02cb1SIgor Kotrasinski return 0;
8679c02cb1SIgor Kotrasinski }
8779c02cb1SIgor Kotrasinski
v_recv_cmd_submit(struct vudc * udc,struct usbip_header * pdu)8879c02cb1SIgor Kotrasinski static int v_recv_cmd_submit(struct vudc *udc,
8979c02cb1SIgor Kotrasinski struct usbip_header *pdu)
9079c02cb1SIgor Kotrasinski {
9179c02cb1SIgor Kotrasinski int ret = 0;
9279c02cb1SIgor Kotrasinski struct urbp *urb_p;
9379c02cb1SIgor Kotrasinski u8 address;
9479c02cb1SIgor Kotrasinski unsigned long flags;
9579c02cb1SIgor Kotrasinski
9679c02cb1SIgor Kotrasinski urb_p = alloc_urbp();
9779c02cb1SIgor Kotrasinski if (!urb_p) {
9879c02cb1SIgor Kotrasinski usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_MALLOC);
9979c02cb1SIgor Kotrasinski return -ENOMEM;
10079c02cb1SIgor Kotrasinski }
10179c02cb1SIgor Kotrasinski
10279c02cb1SIgor Kotrasinski /* base.ep is pipeendpoint(pipe) */
10379c02cb1SIgor Kotrasinski address = pdu->base.ep;
10479c02cb1SIgor Kotrasinski if (pdu->base.direction == USBIP_DIR_IN)
10579c02cb1SIgor Kotrasinski address |= USB_DIR_IN;
10679c02cb1SIgor Kotrasinski
107ecf6deddSDan Carpenter spin_lock_irqsave(&udc->lock, flags);
1080255cf9eSKrzysztof Opasiak urb_p->ep = vudc_find_endpoint(udc, address);
10979c02cb1SIgor Kotrasinski if (!urb_p->ep) {
11079c02cb1SIgor Kotrasinski /* we don't know the type, there may be isoc data! */
11179c02cb1SIgor Kotrasinski dev_err(&udc->pdev->dev, "request to nonexistent endpoint");
112ecf6deddSDan Carpenter spin_unlock_irqrestore(&udc->lock, flags);
11379c02cb1SIgor Kotrasinski usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_TCP);
11479c02cb1SIgor Kotrasinski ret = -EPIPE;
11579c02cb1SIgor Kotrasinski goto free_urbp;
11679c02cb1SIgor Kotrasinski }
11779c02cb1SIgor Kotrasinski urb_p->type = urb_p->ep->type;
118ecf6deddSDan Carpenter spin_unlock_irqrestore(&udc->lock, flags);
11979c02cb1SIgor Kotrasinski
12079c02cb1SIgor Kotrasinski urb_p->new = 1;
12179c02cb1SIgor Kotrasinski urb_p->seqnum = pdu->base.seqnum;
12279c02cb1SIgor Kotrasinski
123b78d830fSShuah Khan if (urb_p->ep->type == USB_ENDPOINT_XFER_ISOC) {
124b78d830fSShuah Khan /* validate packet size and number of packets */
125b78d830fSShuah Khan unsigned int maxp, packets, bytes;
126b78d830fSShuah Khan
127b78d830fSShuah Khan maxp = usb_endpoint_maxp(urb_p->ep->desc);
128b78d830fSShuah Khan maxp *= usb_endpoint_maxp_mult(urb_p->ep->desc);
129b78d830fSShuah Khan bytes = pdu->u.cmd_submit.transfer_buffer_length;
130b78d830fSShuah Khan packets = DIV_ROUND_UP(bytes, maxp);
131b78d830fSShuah Khan
132b78d830fSShuah Khan if (pdu->u.cmd_submit.number_of_packets < 0 ||
133b78d830fSShuah Khan pdu->u.cmd_submit.number_of_packets > packets) {
134b78d830fSShuah Khan dev_err(&udc->gadget.dev,
135b78d830fSShuah Khan "CMD_SUBMIT: isoc invalid num packets %d\n",
136b78d830fSShuah Khan pdu->u.cmd_submit.number_of_packets);
137b78d830fSShuah Khan ret = -EMSGSIZE;
138b78d830fSShuah Khan goto free_urbp;
139b78d830fSShuah Khan }
140b78d830fSShuah Khan }
141b78d830fSShuah Khan
14279c02cb1SIgor Kotrasinski ret = alloc_urb_from_cmd(&urb_p->urb, pdu, urb_p->ep->type);
14379c02cb1SIgor Kotrasinski if (ret) {
14479c02cb1SIgor Kotrasinski usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_MALLOC);
14579c02cb1SIgor Kotrasinski ret = -ENOMEM;
14679c02cb1SIgor Kotrasinski goto free_urbp;
14779c02cb1SIgor Kotrasinski }
14879c02cb1SIgor Kotrasinski
14979c02cb1SIgor Kotrasinski urb_p->urb->status = -EINPROGRESS;
15079c02cb1SIgor Kotrasinski
15179c02cb1SIgor Kotrasinski /* FIXME: more pipe setup to please usbip_common */
152*dd65a243SShuah Khan BUILD_BUG_ON_MSG(PIPE_BULK != 3, "PIPE_* doesn't range from 0 to 3");
153*dd65a243SShuah Khan
154*dd65a243SShuah Khan urb_p->urb->pipe &= ~(PIPE_BULK << 30);
15579c02cb1SIgor Kotrasinski switch (urb_p->ep->type) {
15679c02cb1SIgor Kotrasinski case USB_ENDPOINT_XFER_BULK:
15779c02cb1SIgor Kotrasinski urb_p->urb->pipe |= (PIPE_BULK << 30);
15879c02cb1SIgor Kotrasinski break;
15979c02cb1SIgor Kotrasinski case USB_ENDPOINT_XFER_INT:
16079c02cb1SIgor Kotrasinski urb_p->urb->pipe |= (PIPE_INTERRUPT << 30);
16179c02cb1SIgor Kotrasinski break;
16279c02cb1SIgor Kotrasinski case USB_ENDPOINT_XFER_CONTROL:
16379c02cb1SIgor Kotrasinski urb_p->urb->pipe |= (PIPE_CONTROL << 30);
16479c02cb1SIgor Kotrasinski break;
16579c02cb1SIgor Kotrasinski case USB_ENDPOINT_XFER_ISOC:
16679c02cb1SIgor Kotrasinski urb_p->urb->pipe |= (PIPE_ISOCHRONOUS << 30);
16779c02cb1SIgor Kotrasinski break;
16879c02cb1SIgor Kotrasinski }
16979c02cb1SIgor Kotrasinski ret = usbip_recv_xbuff(&udc->ud, urb_p->urb);
17079c02cb1SIgor Kotrasinski if (ret < 0)
17179c02cb1SIgor Kotrasinski goto free_urbp;
17279c02cb1SIgor Kotrasinski
17379c02cb1SIgor Kotrasinski ret = usbip_recv_iso(&udc->ud, urb_p->urb);
17479c02cb1SIgor Kotrasinski if (ret < 0)
17579c02cb1SIgor Kotrasinski goto free_urbp;
17679c02cb1SIgor Kotrasinski
17779c02cb1SIgor Kotrasinski spin_lock_irqsave(&udc->lock, flags);
17879c02cb1SIgor Kotrasinski v_kick_timer(udc, jiffies);
17979c02cb1SIgor Kotrasinski list_add_tail(&urb_p->urb_entry, &udc->urb_queue);
18079c02cb1SIgor Kotrasinski spin_unlock_irqrestore(&udc->lock, flags);
18179c02cb1SIgor Kotrasinski
18279c02cb1SIgor Kotrasinski return 0;
18379c02cb1SIgor Kotrasinski
18479c02cb1SIgor Kotrasinski free_urbp:
18579c02cb1SIgor Kotrasinski free_urbp_and_urb(urb_p);
18679c02cb1SIgor Kotrasinski return ret;
18779c02cb1SIgor Kotrasinski }
18879c02cb1SIgor Kotrasinski
v_rx_pdu(struct usbip_device * ud)18979c02cb1SIgor Kotrasinski static int v_rx_pdu(struct usbip_device *ud)
19079c02cb1SIgor Kotrasinski {
19179c02cb1SIgor Kotrasinski int ret;
19279c02cb1SIgor Kotrasinski struct usbip_header pdu;
19379c02cb1SIgor Kotrasinski struct vudc *udc = container_of(ud, struct vudc, ud);
19479c02cb1SIgor Kotrasinski
19579c02cb1SIgor Kotrasinski memset(&pdu, 0, sizeof(pdu));
19679c02cb1SIgor Kotrasinski ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
19779c02cb1SIgor Kotrasinski if (ret != sizeof(pdu)) {
19879c02cb1SIgor Kotrasinski usbip_event_add(ud, VUDC_EVENT_ERROR_TCP);
19979c02cb1SIgor Kotrasinski if (ret >= 0)
20079c02cb1SIgor Kotrasinski return -EPIPE;
20179c02cb1SIgor Kotrasinski return ret;
20279c02cb1SIgor Kotrasinski }
20379c02cb1SIgor Kotrasinski usbip_header_correct_endian(&pdu, 0);
20479c02cb1SIgor Kotrasinski
20579c02cb1SIgor Kotrasinski spin_lock_irq(&ud->lock);
20679c02cb1SIgor Kotrasinski ret = (ud->status == SDEV_ST_USED);
20779c02cb1SIgor Kotrasinski spin_unlock_irq(&ud->lock);
20879c02cb1SIgor Kotrasinski if (!ret) {
20979c02cb1SIgor Kotrasinski usbip_event_add(ud, VUDC_EVENT_ERROR_TCP);
21079c02cb1SIgor Kotrasinski return -EBUSY;
21179c02cb1SIgor Kotrasinski }
21279c02cb1SIgor Kotrasinski
21379c02cb1SIgor Kotrasinski switch (pdu.base.command) {
21479c02cb1SIgor Kotrasinski case USBIP_CMD_UNLINK:
21579c02cb1SIgor Kotrasinski ret = v_recv_cmd_unlink(udc, &pdu);
21679c02cb1SIgor Kotrasinski break;
21779c02cb1SIgor Kotrasinski case USBIP_CMD_SUBMIT:
21879c02cb1SIgor Kotrasinski ret = v_recv_cmd_submit(udc, &pdu);
21979c02cb1SIgor Kotrasinski break;
22079c02cb1SIgor Kotrasinski default:
22179c02cb1SIgor Kotrasinski ret = -EPIPE;
22279c02cb1SIgor Kotrasinski pr_err("rx: unknown command");
22379c02cb1SIgor Kotrasinski break;
22479c02cb1SIgor Kotrasinski }
22579c02cb1SIgor Kotrasinski return ret;
22679c02cb1SIgor Kotrasinski }
22779c02cb1SIgor Kotrasinski
v_rx_loop(void * data)22879c02cb1SIgor Kotrasinski int v_rx_loop(void *data)
22979c02cb1SIgor Kotrasinski {
23079c02cb1SIgor Kotrasinski struct usbip_device *ud = data;
23179c02cb1SIgor Kotrasinski int ret = 0;
23279c02cb1SIgor Kotrasinski
23379c02cb1SIgor Kotrasinski while (!kthread_should_stop()) {
23479c02cb1SIgor Kotrasinski if (usbip_event_happened(ud))
23579c02cb1SIgor Kotrasinski break;
23679c02cb1SIgor Kotrasinski ret = v_rx_pdu(ud);
23779c02cb1SIgor Kotrasinski if (ret < 0) {
23879c02cb1SIgor Kotrasinski pr_warn("v_rx exit with error %d", ret);
23979c02cb1SIgor Kotrasinski break;
24079c02cb1SIgor Kotrasinski }
24179c02cb1SIgor Kotrasinski }
24279c02cb1SIgor Kotrasinski return ret;
24379c02cb1SIgor Kotrasinski }
244