summaryrefslogtreecommitdiff
path: root/drivers/usb/usbip/usbip_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/usbip/usbip_common.c')
-rw-r--r--drivers/usb/usbip/usbip_common.c169
1 files changed, 135 insertions, 34 deletions
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index 45da3e01c7b0..a2b2da1255dd 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -309,12 +309,13 @@ int usbip_recv(struct socket *sock, void *buf, int size)
if (!sock || !buf || !size)
return -EINVAL;
- iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, size);
+ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, size);
usbip_dbg_xmit("enter\n");
do {
sock->sk->sk_allocation = GFP_NOIO;
+ sock->sk->sk_use_task_frag = false;
result = sock_recvmsg(sock, &msg, MSG_WAITALL);
if (result <= 0)
@@ -324,11 +325,6 @@ int usbip_recv(struct socket *sock, void *buf, int size)
} while (msg_data_left(&msg));
if (usbip_dbg_flag_xmit) {
- if (!in_interrupt())
- pr_debug("%-10s:", current->comm);
- else
- pr_debug("interrupt :");
-
pr_debug("receiving....\n");
usbip_dump_buffer(buf, size);
pr_debug("received, osize %d ret %d size %zd total %d\n",
@@ -349,6 +345,91 @@ static unsigned int tweak_transfer_flags(unsigned int flags)
return flags;
}
+/*
+ * USBIP driver packs URB transfer flags in PDUs that are exchanged
+ * between Server (usbip_host) and Client (vhci_hcd). URB_* flags
+ * are internal to kernel and could change. Where as USBIP URB flags
+ * exchanged in PDUs are USBIP user API must not change.
+ *
+ * USBIP_URB* flags are exported as explicit API and client and server
+ * do mapping from kernel flags to USBIP_URB*. Details as follows:
+ *
+ * Client tx path (USBIP_CMD_SUBMIT):
+ * - Maps URB_* to USBIP_URB_* when it sends USBIP_CMD_SUBMIT packet.
+ *
+ * Server rx path (USBIP_CMD_SUBMIT):
+ * - Maps USBIP_URB_* to URB_* when it receives USBIP_CMD_SUBMIT packet.
+ *
+ * Flags aren't included in USBIP_CMD_UNLINK and USBIP_RET_SUBMIT packets
+ * and no special handling is needed for them in the following cases:
+ * - Server rx path (USBIP_CMD_UNLINK)
+ * - Client rx path & Server tx path (USBIP_RET_SUBMIT)
+ *
+ * Code paths:
+ * usbip_pack_pdu() is the common routine that handles packing pdu from
+ * urb and unpack pdu to an urb.
+ *
+ * usbip_pack_cmd_submit() and usbip_pack_ret_submit() handle
+ * USBIP_CMD_SUBMIT and USBIP_RET_SUBMIT respectively.
+ *
+ * usbip_map_urb_to_usbip() and usbip_map_usbip_to_urb() are used
+ * by usbip_pack_cmd_submit() and usbip_pack_ret_submit() to map
+ * flags.
+ */
+
+struct urb_to_usbip_flags {
+ u32 urb_flag;
+ u32 usbip_flag;
+};
+
+#define NUM_USBIP_FLAGS 17
+
+static const struct urb_to_usbip_flags flag_map[NUM_USBIP_FLAGS] = {
+ {URB_SHORT_NOT_OK, USBIP_URB_SHORT_NOT_OK},
+ {URB_ISO_ASAP, USBIP_URB_ISO_ASAP},
+ {URB_NO_TRANSFER_DMA_MAP, USBIP_URB_NO_TRANSFER_DMA_MAP},
+ {URB_ZERO_PACKET, USBIP_URB_ZERO_PACKET},
+ {URB_NO_INTERRUPT, USBIP_URB_NO_INTERRUPT},
+ {URB_FREE_BUFFER, USBIP_URB_FREE_BUFFER},
+ {URB_DIR_IN, USBIP_URB_DIR_IN},
+ {URB_DIR_OUT, USBIP_URB_DIR_OUT},
+ {URB_DIR_MASK, USBIP_URB_DIR_MASK},
+ {URB_DMA_MAP_SINGLE, USBIP_URB_DMA_MAP_SINGLE},
+ {URB_DMA_MAP_PAGE, USBIP_URB_DMA_MAP_PAGE},
+ {URB_DMA_MAP_SG, USBIP_URB_DMA_MAP_SG},
+ {URB_MAP_LOCAL, USBIP_URB_MAP_LOCAL},
+ {URB_SETUP_MAP_SINGLE, USBIP_URB_SETUP_MAP_SINGLE},
+ {URB_SETUP_MAP_LOCAL, USBIP_URB_SETUP_MAP_LOCAL},
+ {URB_DMA_SG_COMBINED, USBIP_URB_DMA_SG_COMBINED},
+ {URB_ALIGNED_TEMP_BUFFER, USBIP_URB_ALIGNED_TEMP_BUFFER},
+};
+
+static unsigned int urb_to_usbip(unsigned int flags)
+{
+ unsigned int map_flags = 0;
+ int loop;
+
+ for (loop = 0; loop < NUM_USBIP_FLAGS; loop++) {
+ if (flags & flag_map[loop].urb_flag)
+ map_flags |= flag_map[loop].usbip_flag;
+ }
+
+ return map_flags;
+}
+
+static unsigned int usbip_to_urb(unsigned int flags)
+{
+ unsigned int map_flags = 0;
+ int loop;
+
+ for (loop = 0; loop < NUM_USBIP_FLAGS; loop++) {
+ if (flags & flag_map[loop].usbip_flag)
+ map_flags |= flag_map[loop].urb_flag;
+ }
+
+ return map_flags;
+}
+
static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb,
int pack)
{
@@ -359,14 +440,14 @@ static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb,
* will be discussed when usbip is ported to other operating systems.
*/
if (pack) {
- spdu->transfer_flags =
- tweak_transfer_flags(urb->transfer_flags);
+ /* map after tweaking the urb flags */
+ spdu->transfer_flags = urb_to_usbip(tweak_transfer_flags(urb->transfer_flags));
spdu->transfer_buffer_length = urb->transfer_buffer_length;
spdu->start_frame = urb->start_frame;
spdu->number_of_packets = urb->number_of_packets;
spdu->interval = urb->interval;
} else {
- urb->transfer_flags = spdu->transfer_flags;
+ urb->transfer_flags = usbip_to_urb(spdu->transfer_flags);
urb->transfer_buffer_length = spdu->transfer_buffer_length;
urb->start_frame = spdu->start_frame;
urb->number_of_packets = spdu->number_of_packets;
@@ -680,8 +761,12 @@ EXPORT_SYMBOL_GPL(usbip_pad_iso);
/* some members of urb must be substituted before. */
int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
{
- int ret;
+ struct scatterlist *sg;
+ int ret = 0;
+ int recv;
int size;
+ int copy;
+ int i;
if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) {
/* the direction of urb must be OUT. */
@@ -701,41 +786,57 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
if (!(size > 0))
return 0;
- if (size > urb->transfer_buffer_length) {
+ if (size > urb->transfer_buffer_length)
/* should not happen, probably malicious packet */
- if (ud->side == USBIP_STUB) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- return 0;
- } else {
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return -EPIPE;
- }
- }
+ goto error;
- ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
- if (ret != size) {
- dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
- if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- } else {
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return -EPIPE;
+ if (urb->num_sgs) {
+ copy = size;
+ for_each_sg(urb->sg, sg, urb->num_sgs, i) {
+ int recv_size;
+
+ if (copy < sg->length)
+ recv_size = copy;
+ else
+ recv_size = sg->length;
+
+ recv = usbip_recv(ud->tcp_socket, sg_virt(sg),
+ recv_size);
+
+ if (recv != recv_size)
+ goto error;
+
+ copy -= recv;
+ ret += recv;
+
+ if (!copy)
+ break;
}
+
+ if (ret != size)
+ goto error;
+ } else {
+ ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
+ if (ret != size)
+ goto error;
}
return ret;
+
+error:
+ dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
+ if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC)
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ else
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+
+ return -EPIPE;
}
EXPORT_SYMBOL_GPL(usbip_recv_xbuff);
static int __init usbip_core_init(void)
{
- int ret;
-
- ret = usbip_init_eh();
- if (ret)
- return ret;
-
- return 0;
+ return usbip_init_eh();
}
static void __exit usbip_core_exit(void)