aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging
diff options
context:
space:
mode:
authorArjan Mels <arjan.mels@gmx.net>2011-04-05 14:26:59 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-04-06 18:51:14 -0400
commit28276a28d8b3cd19f4449991faad4945fe557656 (patch)
treee3c5c6ceb249faf8a90e03519a198836235746a5 /drivers/staging
parent1325f85fa49f57df034869de430f7c302ae23109 (diff)
staging: usbip: bugfix for isochronous packets and optimization
For isochronous packets the actual_length is the sum of the actual length of each of the packets, however between the packets might be padding, so it is not sufficient to just send the first actual_length bytes of the buffer. To fix this and simultanesouly optimize the bandwidth the content of the isochronous packets are send without the padding, the padding is restored on the receiving end. Signed-off-by: Arjan Mels <arjan.mels@gmx.net> Cc: Takahiro Hirofuchi <hirofuchi@users.sourceforge.net> Cc: Max Vozeler <max@vozeler.com> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/usbip/stub_tx.c74
-rw-r--r--drivers/staging/usbip/usbip_common.c57
-rw-r--r--drivers/staging/usbip/usbip_common.h2
-rw-r--r--drivers/staging/usbip/vhci_rx.c3
4 files changed, 122 insertions, 14 deletions
diff --git a/drivers/staging/usbip/stub_tx.c b/drivers/staging/usbip/stub_tx.c
index 5523f25998e6..64a52b26dcf6 100644
--- a/drivers/staging/usbip/stub_tx.c
+++ b/drivers/staging/usbip/stub_tx.c
@@ -170,7 +170,6 @@ static int stub_send_ret_submit(struct stub_device *sdev)
170 struct stub_priv *priv, *tmp; 170 struct stub_priv *priv, *tmp;
171 171
172 struct msghdr msg; 172 struct msghdr msg;
173 struct kvec iov[3];
174 size_t txsize; 173 size_t txsize;
175 174
176 size_t total_size = 0; 175 size_t total_size = 0;
@@ -180,28 +179,73 @@ static int stub_send_ret_submit(struct stub_device *sdev)
180 struct urb *urb = priv->urb; 179 struct urb *urb = priv->urb;
181 struct usbip_header pdu_header; 180 struct usbip_header pdu_header;
182 void *iso_buffer = NULL; 181 void *iso_buffer = NULL;
182 struct kvec *iov = NULL;
183 int iovnum = 0;
183 184
184 txsize = 0; 185 txsize = 0;
185 memset(&pdu_header, 0, sizeof(pdu_header)); 186 memset(&pdu_header, 0, sizeof(pdu_header));
186 memset(&msg, 0, sizeof(msg)); 187 memset(&msg, 0, sizeof(msg));
187 memset(&iov, 0, sizeof(iov));
188 188
189 usbip_dbg_stub_tx("setup txdata urb %p\n", urb); 189 if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
190 iovnum = 2 + urb->number_of_packets;
191 else
192 iovnum = 2;
193
194 iov = kzalloc(iovnum * sizeof(struct kvec), GFP_KERNEL);
190 195
196 if (!iov) {
197 usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC);
198 return -1;
199 }
200
201 iovnum = 0;
191 202
192 /* 1. setup usbip_header */ 203 /* 1. setup usbip_header */
193 setup_ret_submit_pdu(&pdu_header, urb); 204 setup_ret_submit_pdu(&pdu_header, urb);
205 usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n",
206 pdu_header.base.seqnum, urb);
207 /*usbip_dump_header(pdu_header);*/
194 usbip_header_correct_endian(&pdu_header, 1); 208 usbip_header_correct_endian(&pdu_header, 1);
195 209
196 iov[0].iov_base = &pdu_header; 210 iov[iovnum].iov_base = &pdu_header;
197 iov[0].iov_len = sizeof(pdu_header); 211 iov[iovnum].iov_len = sizeof(pdu_header);
212 iovnum++;
198 txsize += sizeof(pdu_header); 213 txsize += sizeof(pdu_header);
199 214
200 /* 2. setup transfer buffer */ 215 /* 2. setup transfer buffer */
201 if (usb_pipein(urb->pipe) && urb->actual_length > 0) { 216 if (usb_pipein(urb->pipe) &&
202 iov[1].iov_base = urb->transfer_buffer; 217 usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS &&
203 iov[1].iov_len = urb->actual_length; 218 urb->actual_length > 0) {
219 iov[iovnum].iov_base = urb->transfer_buffer;
220 iov[iovnum].iov_len = urb->actual_length;
221 iovnum++;
204 txsize += urb->actual_length; 222 txsize += urb->actual_length;
223 } else if (usb_pipein(urb->pipe) &&
224 usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
225 /*
226 * For isochronous packets: actual length is the sum of
227 * the actual length of the individual, packets, but as
228 * the packet offsets are not changed there will be
229 * padding between the packets. To optimally use the
230 * bandwidth the padding is not transmitted.
231 */
232
233 int i;
234 for (i = 0; i < urb->number_of_packets; i++) {
235 iov[iovnum].iov_base = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
236 iov[iovnum].iov_len = urb->iso_frame_desc[i].actual_length;
237 iovnum++;
238 txsize += urb->iso_frame_desc[i].actual_length;
239 }
240
241 if (txsize != sizeof(pdu_header) + urb->actual_length) {
242 dev_err(&sdev->interface->dev,
243 "actual length of urb (%d) does not match iso packet sizes (%d)\n",
244 urb->actual_length, txsize-sizeof(pdu_header));
245 kfree(iov);
246 usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
247 return -1;
248 }
205 } 249 }
206 250
207 /* 3. setup iso_packet_descriptor */ 251 /* 3. setup iso_packet_descriptor */
@@ -212,32 +256,34 @@ static int stub_send_ret_submit(struct stub_device *sdev)
212 if (!iso_buffer) { 256 if (!iso_buffer) {
213 usbip_event_add(&sdev->ud, 257 usbip_event_add(&sdev->ud,
214 SDEV_EVENT_ERROR_MALLOC); 258 SDEV_EVENT_ERROR_MALLOC);
259 kfree(iov);
215 return -1; 260 return -1;
216 } 261 }
217 262
218 iov[2].iov_base = iso_buffer; 263 iov[iovnum].iov_base = iso_buffer;
219 iov[2].iov_len = len; 264 iov[iovnum].iov_len = len;
220 txsize += len; 265 txsize += len;
266 iovnum++;
221 } 267 }
222 268
223 ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov, 269 ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg,
224 3, txsize); 270 iov, iovnum, txsize);
225 if (ret != txsize) { 271 if (ret != txsize) {
226 dev_err(&sdev->interface->dev, 272 dev_err(&sdev->interface->dev,
227 "sendmsg failed!, retval %d for %zd\n", 273 "sendmsg failed!, retval %d for %zd\n",
228 ret, txsize); 274 ret, txsize);
275 kfree(iov);
229 kfree(iso_buffer); 276 kfree(iso_buffer);
230 usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); 277 usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
231 return -1; 278 return -1;
232 } 279 }
233 280
281 kfree(iov);
234 kfree(iso_buffer); 282 kfree(iso_buffer);
235 usbip_dbg_stub_tx("send txdata\n");
236 283
237 total_size += txsize; 284 total_size += txsize;
238 } 285 }
239 286
240
241 spin_lock_irqsave(&sdev->priv_lock, flags); 287 spin_lock_irqsave(&sdev->priv_lock, flags);
242 288
243 list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) { 289 list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) {
diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
index 85622153ea7c..7b1fe45bf7dd 100644
--- a/drivers/staging/usbip/usbip_common.c
+++ b/drivers/staging/usbip/usbip_common.c
@@ -730,6 +730,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
730 int size = np * sizeof(*iso); 730 int size = np * sizeof(*iso);
731 int i; 731 int i;
732 int ret; 732 int ret;
733 int total_length = 0;
733 734
734 if (!usb_pipeisoc(urb->pipe)) 735 if (!usb_pipeisoc(urb->pipe))
735 return 0; 736 return 0;
@@ -759,19 +760,75 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
759 return -EPIPE; 760 return -EPIPE;
760 } 761 }
761 762
763
762 for (i = 0; i < np; i++) { 764 for (i = 0; i < np; i++) {
763 iso = buff + (i * sizeof(*iso)); 765 iso = buff + (i * sizeof(*iso));
764 766
765 usbip_iso_pakcet_correct_endian(iso, 0); 767 usbip_iso_pakcet_correct_endian(iso, 0);
766 usbip_pack_iso(iso, &urb->iso_frame_desc[i], 0); 768 usbip_pack_iso(iso, &urb->iso_frame_desc[i], 0);
769 total_length += urb->iso_frame_desc[i].actual_length;
767 } 770 }
768 771
769 kfree(buff); 772 kfree(buff);
770 773
774 if (total_length != urb->actual_length) {
775 dev_err(&urb->dev->dev,
776 "total length of iso packets (%d) not equal to actual length of buffer (%d)\n",
777 total_length, urb->actual_length);
778
779 if (ud->side == USBIP_STUB)
780 usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
781 else
782 usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
783
784 return -EPIPE;
785 }
786
771 return ret; 787 return ret;
772} 788}
773EXPORT_SYMBOL_GPL(usbip_recv_iso); 789EXPORT_SYMBOL_GPL(usbip_recv_iso);
774 790
791/*
792 * This functions restores the padding which was removed for optimizing
793 * the bandwidth during transfer over tcp/ip
794 *
795 * buffer and iso packets need to be stored and be in propeper endian in urb
796 * before calling this function
797 */
798int usbip_pad_iso(struct usbip_device *ud, struct urb *urb)
799{
800 int np = urb->number_of_packets;
801 int i;
802 int ret;
803 int actualoffset = urb->actual_length;
804
805 if (!usb_pipeisoc(urb->pipe))
806 return 0;
807
808 /* if no packets or length of data is 0, then nothing to unpack */
809 if (np == 0 || urb->actual_length == 0)
810 return 0;
811
812 /*
813 * if actual_length is transfer_buffer_length then no padding is
814 * present.
815 */
816 if (urb->actual_length == urb->transfer_buffer_length)
817 return 0;
818
819 /*
820 * loop over all packets from last to first (to prevent overwritting
821 * memory when padding) and move them into the proper place
822 */
823 for (i = np-1; i > 0; i--) {
824 actualoffset -= urb->iso_frame_desc[i].actual_length;
825 memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset,
826 urb->transfer_buffer + actualoffset,
827 urb->iso_frame_desc[i].actual_length);
828 }
829 return ret;
830}
831EXPORT_SYMBOL_GPL(usbip_pad_iso);
775 832
776/* some members of urb must be substituted before. */ 833/* some members of urb must be substituted before. */
777int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) 834int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index 9f809c315d92..c767f52be5fb 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -379,6 +379,8 @@ void usbip_header_correct_endian(struct usbip_header *pdu, int send);
379int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb); 379int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb);
380/* some members of urb must be substituted before. */ 380/* some members of urb must be substituted before. */
381int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); 381int usbip_recv_iso(struct usbip_device *ud, struct urb *urb);
382/* some members of urb must be substituted before. */
383int usbip_pad_iso(struct usbip_device *ud, struct urb *urb);
382void *usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); 384void *usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen);
383 385
384 386
diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c
index 09bf2355934b..2ffc96a4c0d4 100644
--- a/drivers/staging/usbip/vhci_rx.c
+++ b/drivers/staging/usbip/vhci_rx.c
@@ -100,6 +100,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
100 if (usbip_recv_iso(ud, urb) < 0) 100 if (usbip_recv_iso(ud, urb) < 0)
101 return; 101 return;
102 102
103 /* restore the padding in iso packets */
104 if (usbip_pad_iso(ud, urb) < 0)
105 return;
103 106
104 if (usbip_dbg_flag_vhci_rx) 107 if (usbip_dbg_flag_vhci_rx)
105 usbip_dump_urb(urb); 108 usbip_dump_urb(urb);