diff options
author | Valentina Manea <valentina.manea.m@gmail.com> | 2014-08-20 00:31:00 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-08-25 13:40:06 -0400 |
commit | 96c2737716d586a218bc795fcb79d2e2b6003081 (patch) | |
tree | d2cad517eece7a5ee6255e028f3263c2e0b1e23c /drivers/usb/usbip/vhci_tx.c | |
parent | 588b48caf65c4a92af567948ec0025065e749ddf (diff) |
usbip: move usbip kernel code out of staging
At this point, USB/IP kernel code is fully functional
and can be moved out of staging.
Signed-off-by: Valentina Manea <valentina.manea.m@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/usbip/vhci_tx.c')
-rw-r--r-- | drivers/usb/usbip/vhci_tx.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c new file mode 100644 index 000000000000..409fd99f3257 --- /dev/null +++ b/drivers/usb/usbip/vhci_tx.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2003-2008 Takahiro Hirofuchi | ||
3 | * | ||
4 | * This is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | ||
17 | * USA. | ||
18 | */ | ||
19 | |||
20 | #include <linux/kthread.h> | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #include "usbip_common.h" | ||
24 | #include "vhci.h" | ||
25 | |||
26 | static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb) | ||
27 | { | ||
28 | struct vhci_priv *priv = ((struct vhci_priv *)urb->hcpriv); | ||
29 | struct vhci_device *vdev = priv->vdev; | ||
30 | |||
31 | usbip_dbg_vhci_tx("URB, local devnum %u, remote devid %u\n", | ||
32 | usb_pipedevice(urb->pipe), vdev->devid); | ||
33 | |||
34 | pdup->base.command = USBIP_CMD_SUBMIT; | ||
35 | pdup->base.seqnum = priv->seqnum; | ||
36 | pdup->base.devid = vdev->devid; | ||
37 | pdup->base.direction = usb_pipein(urb->pipe) ? | ||
38 | USBIP_DIR_IN : USBIP_DIR_OUT; | ||
39 | pdup->base.ep = usb_pipeendpoint(urb->pipe); | ||
40 | |||
41 | usbip_pack_pdu(pdup, urb, USBIP_CMD_SUBMIT, 1); | ||
42 | |||
43 | if (urb->setup_packet) | ||
44 | memcpy(pdup->u.cmd_submit.setup, urb->setup_packet, 8); | ||
45 | } | ||
46 | |||
47 | static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev) | ||
48 | { | ||
49 | struct vhci_priv *priv, *tmp; | ||
50 | |||
51 | spin_lock(&vdev->priv_lock); | ||
52 | |||
53 | list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) { | ||
54 | list_move_tail(&priv->list, &vdev->priv_rx); | ||
55 | spin_unlock(&vdev->priv_lock); | ||
56 | return priv; | ||
57 | } | ||
58 | |||
59 | spin_unlock(&vdev->priv_lock); | ||
60 | |||
61 | return NULL; | ||
62 | } | ||
63 | |||
64 | static int vhci_send_cmd_submit(struct vhci_device *vdev) | ||
65 | { | ||
66 | struct vhci_priv *priv = NULL; | ||
67 | |||
68 | struct msghdr msg; | ||
69 | struct kvec iov[3]; | ||
70 | size_t txsize; | ||
71 | |||
72 | size_t total_size = 0; | ||
73 | |||
74 | while ((priv = dequeue_from_priv_tx(vdev)) != NULL) { | ||
75 | int ret; | ||
76 | struct urb *urb = priv->urb; | ||
77 | struct usbip_header pdu_header; | ||
78 | struct usbip_iso_packet_descriptor *iso_buffer = NULL; | ||
79 | |||
80 | txsize = 0; | ||
81 | memset(&pdu_header, 0, sizeof(pdu_header)); | ||
82 | memset(&msg, 0, sizeof(msg)); | ||
83 | memset(&iov, 0, sizeof(iov)); | ||
84 | |||
85 | usbip_dbg_vhci_tx("setup txdata urb %p\n", urb); | ||
86 | |||
87 | /* 1. setup usbip_header */ | ||
88 | setup_cmd_submit_pdu(&pdu_header, urb); | ||
89 | usbip_header_correct_endian(&pdu_header, 1); | ||
90 | |||
91 | iov[0].iov_base = &pdu_header; | ||
92 | iov[0].iov_len = sizeof(pdu_header); | ||
93 | txsize += sizeof(pdu_header); | ||
94 | |||
95 | /* 2. setup transfer buffer */ | ||
96 | if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) { | ||
97 | iov[1].iov_base = urb->transfer_buffer; | ||
98 | iov[1].iov_len = urb->transfer_buffer_length; | ||
99 | txsize += urb->transfer_buffer_length; | ||
100 | } | ||
101 | |||
102 | /* 3. setup iso_packet_descriptor */ | ||
103 | if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { | ||
104 | ssize_t len = 0; | ||
105 | |||
106 | iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); | ||
107 | if (!iso_buffer) { | ||
108 | usbip_event_add(&vdev->ud, | ||
109 | SDEV_EVENT_ERROR_MALLOC); | ||
110 | return -1; | ||
111 | } | ||
112 | |||
113 | iov[2].iov_base = iso_buffer; | ||
114 | iov[2].iov_len = len; | ||
115 | txsize += len; | ||
116 | } | ||
117 | |||
118 | ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize); | ||
119 | if (ret != txsize) { | ||
120 | pr_err("sendmsg failed!, ret=%d for %zd\n", ret, | ||
121 | txsize); | ||
122 | kfree(iso_buffer); | ||
123 | usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); | ||
124 | return -1; | ||
125 | } | ||
126 | |||
127 | kfree(iso_buffer); | ||
128 | usbip_dbg_vhci_tx("send txdata\n"); | ||
129 | |||
130 | total_size += txsize; | ||
131 | } | ||
132 | |||
133 | return total_size; | ||
134 | } | ||
135 | |||
136 | static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev) | ||
137 | { | ||
138 | struct vhci_unlink *unlink, *tmp; | ||
139 | |||
140 | spin_lock(&vdev->priv_lock); | ||
141 | |||
142 | list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { | ||
143 | list_move_tail(&unlink->list, &vdev->unlink_rx); | ||
144 | spin_unlock(&vdev->priv_lock); | ||
145 | return unlink; | ||
146 | } | ||
147 | |||
148 | spin_unlock(&vdev->priv_lock); | ||
149 | |||
150 | return NULL; | ||
151 | } | ||
152 | |||
153 | static int vhci_send_cmd_unlink(struct vhci_device *vdev) | ||
154 | { | ||
155 | struct vhci_unlink *unlink = NULL; | ||
156 | |||
157 | struct msghdr msg; | ||
158 | struct kvec iov[3]; | ||
159 | size_t txsize; | ||
160 | |||
161 | size_t total_size = 0; | ||
162 | |||
163 | while ((unlink = dequeue_from_unlink_tx(vdev)) != NULL) { | ||
164 | int ret; | ||
165 | struct usbip_header pdu_header; | ||
166 | |||
167 | txsize = 0; | ||
168 | memset(&pdu_header, 0, sizeof(pdu_header)); | ||
169 | memset(&msg, 0, sizeof(msg)); | ||
170 | memset(&iov, 0, sizeof(iov)); | ||
171 | |||
172 | usbip_dbg_vhci_tx("setup cmd unlink, %lu\n", unlink->seqnum); | ||
173 | |||
174 | /* 1. setup usbip_header */ | ||
175 | pdu_header.base.command = USBIP_CMD_UNLINK; | ||
176 | pdu_header.base.seqnum = unlink->seqnum; | ||
177 | pdu_header.base.devid = vdev->devid; | ||
178 | pdu_header.base.ep = 0; | ||
179 | pdu_header.u.cmd_unlink.seqnum = unlink->unlink_seqnum; | ||
180 | |||
181 | usbip_header_correct_endian(&pdu_header, 1); | ||
182 | |||
183 | iov[0].iov_base = &pdu_header; | ||
184 | iov[0].iov_len = sizeof(pdu_header); | ||
185 | txsize += sizeof(pdu_header); | ||
186 | |||
187 | ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize); | ||
188 | if (ret != txsize) { | ||
189 | pr_err("sendmsg failed!, ret=%d for %zd\n", ret, | ||
190 | txsize); | ||
191 | usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); | ||
192 | return -1; | ||
193 | } | ||
194 | |||
195 | usbip_dbg_vhci_tx("send txdata\n"); | ||
196 | |||
197 | total_size += txsize; | ||
198 | } | ||
199 | |||
200 | return total_size; | ||
201 | } | ||
202 | |||
203 | int vhci_tx_loop(void *data) | ||
204 | { | ||
205 | struct usbip_device *ud = data; | ||
206 | struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); | ||
207 | |||
208 | while (!kthread_should_stop()) { | ||
209 | if (vhci_send_cmd_submit(vdev) < 0) | ||
210 | break; | ||
211 | |||
212 | if (vhci_send_cmd_unlink(vdev) < 0) | ||
213 | break; | ||
214 | |||
215 | wait_event_interruptible(vdev->waitq_tx, | ||
216 | (!list_empty(&vdev->priv_tx) || | ||
217 | !list_empty(&vdev->unlink_tx) || | ||
218 | kthread_should_stop())); | ||
219 | |||
220 | usbip_dbg_vhci_tx("pending urbs ?, now wake up\n"); | ||
221 | } | ||
222 | |||
223 | return 0; | ||
224 | } | ||