Statistics
| Branch: | Revision:

root / hw / usb / combined-packet.c @ 1de7afc9

History | View | Annotate | Download (6.4 kB)

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 <hdegoede@redhat.com>
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 "qemu/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
/* Note will free combined when the last packet gets removed */
35
static void usb_combined_packet_remove(USBCombinedPacket *combined,
36
                                       USBPacket *p)
37
{
38
    assert(p->combined == combined);
39
    p->combined = NULL;
40
    QTAILQ_REMOVE(&combined->packets, p, combined_entry);
41
    if (QTAILQ_EMPTY(&combined->packets)) {
42
        g_free(combined);
43
    }
44
}
45

    
46
/* Also handles completion of non combined packets for pipelined input eps */
47
void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
48
{
49
    USBCombinedPacket *combined = p->combined;
50
    USBEndpoint *ep = p->ep;
51
    USBPacket *next;
52
    int status, actual_length;
53
    bool short_not_ok, done = false;
54

    
55
    if (combined == NULL) {
56
        usb_packet_complete_one(dev, p);
57
        goto leave;
58
    }
59

    
60
    assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets));
61

    
62
    status = combined->first->status;
63
    actual_length = combined->first->actual_length;
64
    short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok;
65

    
66
    QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) {
67
        if (!done) {
68
            /* Distribute data over uncombined packets */
69
            if (actual_length >= p->iov.size) {
70
                p->actual_length = p->iov.size;
71
            } else {
72
                /* Send short or error packet to complete the transfer */
73
                p->actual_length = actual_length;
74
                done = true;
75
            }
76
            /* Report status on the last packet */
77
            if (done || next == NULL) {
78
                p->status = status;
79
            } else {
80
                p->status = USB_RET_SUCCESS;
81
            }
82
            p->short_not_ok = short_not_ok;
83
            /* Note will free combined when the last packet gets removed! */
84
            usb_combined_packet_remove(combined, p);
85
            usb_packet_complete_one(dev, p);
86
            actual_length -= p->actual_length;
87
        } else {
88
            /* Remove any leftover packets from the queue */
89
            p->status = USB_RET_REMOVE_FROM_QUEUE;
90
            /* Note will free combined on the last packet! */
91
            dev->port->ops->complete(dev->port, p);
92
        }
93
    }
94
    /* Do not use combined here, it has been freed! */
95
leave:
96
    /* Check if there are packets in the queue waiting for our completion */
97
    usb_ep_combine_input_packets(ep);
98
}
99

    
100
/* May only be called for combined packets! */
101
void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p)
102
{
103
    USBCombinedPacket *combined = p->combined;
104
    assert(combined != NULL);
105
    USBPacket *first = p->combined->first;
106

    
107
    /* Note will free combined on the last packet! */
108
    usb_combined_packet_remove(combined, p);
109
    if (p == first) {
110
        usb_device_cancel_packet(dev, p);
111
    }
112
}
113

    
114
/*
115
 * Large input transfers can get split into multiple input packets, this
116
 * function recombines them, removing the short_not_ok checks which all but
117
 * the last packet of such splits transfers have, thereby allowing input
118
 * transfer pipelining (which we cannot do on short_not_ok transfers)
119
 */
120
void usb_ep_combine_input_packets(USBEndpoint *ep)
121
{
122
    USBPacket *p, *u, *next, *prev = NULL, *first = NULL;
123
    USBPort *port = ep->dev->port;
124
    int totalsize;
125

    
126
    assert(ep->pipeline);
127
    assert(ep->pid == USB_TOKEN_IN);
128

    
129
    QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) {
130
        /* Empty the queue on a halt */
131
        if (ep->halted) {
132
            p->status = USB_RET_REMOVE_FROM_QUEUE;
133
            port->ops->complete(port, p);
134
            continue;
135
        }
136

    
137
        /* Skip packets already submitted to the device */
138
        if (p->state == USB_PACKET_ASYNC) {
139
            prev = p;
140
            continue;
141
        }
142
        usb_packet_check_state(p, USB_PACKET_QUEUED);
143

    
144
        /*
145
         * If the previous (combined) packet has the short_not_ok flag set
146
         * stop, as we must not submit packets to the device after a transfer
147
         * ending with short_not_ok packet.
148
         */
149
        if (prev && prev->short_not_ok) {
150
            break;
151
        }
152

    
153
        if (first) {
154
            if (first->combined == NULL) {
155
                USBCombinedPacket *combined = g_new0(USBCombinedPacket, 1);
156

    
157
                combined->first = first;
158
                QTAILQ_INIT(&combined->packets);
159
                qemu_iovec_init(&combined->iov, 2);
160
                usb_combined_packet_add(combined, first);
161
            }
162
            usb_combined_packet_add(first->combined, p);
163
        } else {
164
            first = p;
165
        }
166

    
167
        /* Is this packet the last one of a (combined) transfer? */
168
        totalsize = (p->combined) ? p->combined->iov.size : p->iov.size;
169
        if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok ||
170
                next == NULL ||
171
                /* Work around for Linux usbfs bulk splitting + migration */
172
                (totalsize == 16348 && p->int_req)) {
173
            usb_device_handle_data(ep->dev, first);
174
            assert(first->status == USB_RET_ASYNC);
175
            if (first->combined) {
176
                QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) {
177
                    usb_packet_set_state(u, USB_PACKET_ASYNC);
178
                }
179
            } else {
180
                usb_packet_set_state(first, USB_PACKET_ASYNC);
181
            }
182
            first = NULL;
183
            prev = p;
184
        }
185
    }
186
}