]> Git Repo - qemu.git/blob - hw/usb/combined-packet.c
Merge branch 'trivial-patches' of git://github.com/stefanha/qemu
[qemu.git] / hw / usb / combined-packet.c
1 /*
2  * QEMU USB packet combining code (for input pipelining)
3  *
4  * Copyright(c) 2012 Red Hat, Inc.
5  *
6  * Red Hat Authors:
7  * Hans de Goede <[email protected]>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or(at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22 #include "qemu-common.h"
23 #include "hw/usb.h"
24 #include "iov.h"
25 #include "trace.h"
26
27 static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p)
28 {
29     qemu_iovec_concat(&combined->iov, &p->iov, 0, p->iov.size);
30     QTAILQ_INSERT_TAIL(&combined->packets, p, combined_entry);
31     p->combined = combined;
32 }
33
34 static void usb_combined_packet_remove(USBCombinedPacket *combined,
35                                        USBPacket *p)
36 {
37     assert(p->combined == combined);
38     p->combined = NULL;
39     QTAILQ_REMOVE(&combined->packets, p, combined_entry);
40 }
41
42 /* Also handles completion of non combined packets for pipelined input eps */
43 void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
44 {
45     USBCombinedPacket *combined = p->combined;
46     USBEndpoint *ep = p->ep;
47     USBPacket *next;
48     enum { completing, complete, leftover };
49     int result, state = completing;
50     bool short_not_ok;
51
52     if (combined == NULL) {
53         usb_packet_complete_one(dev, p);
54         goto leave;
55     }
56
57     assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets));
58
59     result = combined->first->result;
60     short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok;
61
62     QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) {
63         if (state == completing) {
64             /* Distribute data over uncombined packets */
65             if (result >= p->iov.size) {
66                 p->result = p->iov.size;
67             } else {
68                 /* Send short or error packet to complete the transfer */
69                 p->result = result;
70                 state = complete;
71             }
72             p->short_not_ok = short_not_ok;
73             usb_combined_packet_remove(combined, p);
74             usb_packet_complete_one(dev, p);
75             result -= p->result;
76         } else {
77             /* Remove any leftover packets from the queue */
78             state = leftover;
79             p->result = USB_RET_REMOVE_FROM_QUEUE;
80             dev->port->ops->complete(dev->port, p);
81         }
82     }
83     /*
84      * If we had leftover packets the hcd driver will have cancelled them
85      * and usb_combined_packet_cancel has already freed combined!
86      */
87     if (state != leftover) {
88         g_free(combined);
89     }
90 leave:
91     /* Check if there are packets in the queue waiting for our completion */
92     usb_ep_combine_input_packets(ep);
93 }
94
95 /* May only be called for combined packets! */
96 void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p)
97 {
98     USBCombinedPacket *combined = p->combined;
99     assert(combined != NULL);
100
101     usb_combined_packet_remove(combined, p);
102     if (p == combined->first) {
103         usb_device_cancel_packet(dev, p);
104     }
105     if (QTAILQ_EMPTY(&combined->packets)) {
106         g_free(combined);
107     }
108 }
109
110 /*
111  * Large input transfers can get split into multiple input packets, this
112  * function recombines them, removing the short_not_ok checks which all but
113  * the last packet of such splits transfers have, thereby allowing input
114  * transfer pipelining (which we cannot do on short_not_ok transfers)
115  */
116 void usb_ep_combine_input_packets(USBEndpoint *ep)
117 {
118     USBPacket *p, *u, *next, *prev = NULL, *first = NULL;
119     USBPort *port = ep->dev->port;
120     int ret, totalsize;
121
122     assert(ep->pipeline);
123     assert(ep->pid == USB_TOKEN_IN);
124
125     QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) {
126         /* Empty the queue on a halt */
127         if (ep->halted) {
128             p->result = USB_RET_REMOVE_FROM_QUEUE;
129             port->ops->complete(port, p);
130             continue;
131         }
132
133         /* Skip packets already submitted to the device */
134         if (p->state == USB_PACKET_ASYNC) {
135             prev = p;
136             continue;
137         }
138         usb_packet_check_state(p, USB_PACKET_QUEUED);
139
140         /*
141          * If the previous (combined) packet has the short_not_ok flag set
142          * stop, as we must not submit packets to the device after a transfer
143          * ending with short_not_ok packet.
144          */
145         if (prev && prev->short_not_ok) {
146             break;
147         }
148
149         if (first) {
150             if (first->combined == NULL) {
151                 USBCombinedPacket *combined = g_new0(USBCombinedPacket, 1);
152
153                 combined->first = first;
154                 QTAILQ_INIT(&combined->packets);
155                 qemu_iovec_init(&combined->iov, 2);
156                 usb_combined_packet_add(combined, first);
157             }
158             usb_combined_packet_add(first->combined, p);
159         } else {
160             first = p;
161         }
162
163         /* Is this packet the last one of a (combined) transfer? */
164         totalsize = (p->combined) ? p->combined->iov.size : p->iov.size;
165         if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok ||
166                 next == NULL ||
167                 /* Work around for Linux usbfs bulk splitting + migration */
168                 (totalsize == 16348 && p->int_req)) {
169             ret = usb_device_handle_data(ep->dev, first);
170             assert(ret == USB_RET_ASYNC);
171             if (first->combined) {
172                 QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) {
173                     usb_packet_set_state(u, USB_PACKET_ASYNC);
174                 }
175             } else {
176                 usb_packet_set_state(first, USB_PACKET_ASYNC);
177             }
178             first = NULL;
179             prev = p;
180         }
181     }
182 }
This page took 0.035429 seconds and 4 git commands to generate.