Revision f8af1e88

b/hw/usb-uhci.c
95 95
#endif
96 96

  
97 97
typedef struct UHCIState UHCIState;
98
typedef struct UHCIAsync UHCIAsync;
99
typedef struct UHCIQueue UHCIQueue;
98 100

  
99 101
/* 
100 102
 * Pending async transaction.
101 103
 * 'packet' must be the first field because completion
102 104
 * handler does "(UHCIAsync *) pkt" cast.
103 105
 */
104
typedef struct UHCIAsync {
106

  
107
struct UHCIAsync {
105 108
    USBPacket packet;
106 109
    QEMUSGList sgl;
107
    UHCIState *uhci;
110
    UHCIQueue *queue;
108 111
    QTAILQ_ENTRY(UHCIAsync) next;
109 112
    uint32_t  td;
110
    uint32_t  token;
111
    int8_t    valid;
112 113
    uint8_t   isoc;
113 114
    uint8_t   done;
114
} UHCIAsync;
115
};
116

  
117
struct UHCIQueue {
118
    uint32_t  token;
119
    UHCIState *uhci;
120
    QTAILQ_ENTRY(UHCIQueue) next;
121
    QTAILQ_HEAD(, UHCIAsync) asyncs;
122
    int8_t    valid;
123
};
115 124

  
116 125
typedef struct UHCIPort {
117 126
    USBPort port;
......
137 146
    uint32_t pending_int_mask;
138 147

  
139 148
    /* Active packets */
140
    QTAILQ_HEAD(,UHCIAsync) async_pending;
149
    QTAILQ_HEAD(, UHCIQueue) queues;
141 150
    uint8_t num_ports_vmstate;
142 151

  
143 152
    /* Properties */
......
157 166
    uint32_t el_link;
158 167
} UHCI_QH;
159 168

  
160
static UHCIAsync *uhci_async_alloc(UHCIState *s)
169
static inline int32_t uhci_queue_token(UHCI_TD *td)
170
{
171
    /* covers ep, dev, pid -> identifies the endpoint */
172
    return td->token & 0x7ffff;
173
}
174

  
175
static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
176
{
177
    uint32_t token = uhci_queue_token(td);
178
    UHCIQueue *queue;
179

  
180
    QTAILQ_FOREACH(queue, &s->queues, next) {
181
        if (queue->token == token) {
182
            return queue;
183
        }
184
    }
185

  
186
    queue = g_new0(UHCIQueue, 1);
187
    queue->uhci = s;
188
    queue->token = token;
189
    QTAILQ_INIT(&queue->asyncs);
190
    QTAILQ_INSERT_HEAD(&s->queues, queue, next);
191
    return queue;
192
}
193

  
194
static void uhci_queue_free(UHCIQueue *queue)
195
{
196
    UHCIState *s = queue->uhci;
197

  
198
    QTAILQ_REMOVE(&s->queues, queue, next);
199
    g_free(queue);
200
}
201

  
202
static UHCIAsync *uhci_async_alloc(UHCIQueue *queue)
161 203
{
162 204
    UHCIAsync *async = g_new0(UHCIAsync, 1);
163 205

  
164
    async->uhci  = s;
206
    async->queue = queue;
165 207
    usb_packet_init(&async->packet);
166
    pci_dma_sglist_init(&async->sgl, &s->dev, 1);
208
    pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
167 209

  
168 210
    return async;
169 211
}
170 212

  
171
static void uhci_async_free(UHCIState *s, UHCIAsync *async)
213
static void uhci_async_free(UHCIAsync *async)
172 214
{
173 215
    usb_packet_cleanup(&async->packet);
174 216
    qemu_sglist_destroy(&async->sgl);
175 217
    g_free(async);
176 218
}
177 219

  
178
static void uhci_async_link(UHCIState *s, UHCIAsync *async)
220
static void uhci_async_link(UHCIAsync *async)
179 221
{
180
    QTAILQ_INSERT_HEAD(&s->async_pending, async, next);
222
    UHCIQueue *queue = async->queue;
223
    QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
181 224
}
182 225

  
183
static void uhci_async_unlink(UHCIState *s, UHCIAsync *async)
226
static void uhci_async_unlink(UHCIAsync *async)
184 227
{
185
    QTAILQ_REMOVE(&s->async_pending, async, next);
228
    UHCIQueue *queue = async->queue;
229
    QTAILQ_REMOVE(&queue->asyncs, async, next);
186 230
}
187 231

  
188
static void uhci_async_cancel(UHCIState *s, UHCIAsync *async)
232
static void uhci_async_cancel(UHCIAsync *async)
189 233
{
190 234
    DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
191 235
           async->td, async->token, async->done);
192 236

  
193 237
    if (!async->done)
194 238
        usb_cancel_packet(&async->packet);
195
    uhci_async_free(s, async);
239
    uhci_async_free(async);
196 240
}
197 241

  
198 242
/*
199 243
 * Mark all outstanding async packets as invalid.
200 244
 * This is used for canceling them when TDs are removed by the HCD.
201 245
 */
202
static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
246
static void uhci_async_validate_begin(UHCIState *s)
203 247
{
204
    UHCIAsync *async;
248
    UHCIQueue *queue;
205 249

  
206
    QTAILQ_FOREACH(async, &s->async_pending, next) {
207
        async->valid--;
250
    QTAILQ_FOREACH(queue, &s->queues, next) {
251
        queue->valid--;
208 252
    }
209
    return NULL;
210 253
}
211 254

  
212 255
/*
......
214 257
 */
215 258
static void uhci_async_validate_end(UHCIState *s)
216 259
{
217
    UHCIAsync *curr, *n;
260
    UHCIQueue *queue, *n;
261
    UHCIAsync *async;
218 262

  
219
    QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
220
        if (curr->valid > 0) {
263
    QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
264
        if (queue->valid > 0) {
221 265
            continue;
222 266
        }
223
        uhci_async_unlink(s, curr);
224
        uhci_async_cancel(s, curr);
267
        while (!QTAILQ_EMPTY(&queue->asyncs)) {
268
            async = QTAILQ_FIRST(&queue->asyncs);
269
            uhci_async_unlink(async);
270
            uhci_async_cancel(async);
271
        }
272
        uhci_queue_free(queue);
225 273
    }
226 274
}
227 275

  
228 276
static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
229 277
{
278
    UHCIQueue *queue;
230 279
    UHCIAsync *curr, *n;
231 280

  
232
    QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
233
        if (!usb_packet_is_inflight(&curr->packet) ||
234
            curr->packet.ep->dev != dev) {
235
            continue;
281
    QTAILQ_FOREACH(queue, &s->queues, next) {
282
        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
283
            if (!usb_packet_is_inflight(&curr->packet) ||
284
                curr->packet.ep->dev != dev) {
285
                continue;
286
            }
287
            uhci_async_unlink(curr);
288
            uhci_async_cancel(curr);
236 289
        }
237
        uhci_async_unlink(s, curr);
238
        uhci_async_cancel(s, curr);
239 290
    }
240 291
}
241 292

  
242 293
static void uhci_async_cancel_all(UHCIState *s)
243 294
{
295
    UHCIQueue *queue;
244 296
    UHCIAsync *curr, *n;
245 297

  
246
    QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
247
        uhci_async_unlink(s, curr);
248
        uhci_async_cancel(s, curr);
298
    QTAILQ_FOREACH(queue, &s->queues, next) {
299
        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
300
            uhci_async_unlink(curr);
301
            uhci_async_cancel(curr);
302
        }
249 303
    }
250 304
}
251 305

  
252
static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token)
306
static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td)
253 307
{
308
    uint32_t token = uhci_queue_token(td);
309
    UHCIQueue *queue;
254 310
    UHCIAsync *async;
255
    UHCIAsync *match = NULL;
256
    int count = 0;
257

  
258
    /*
259
     * We're looking for the best match here. ie both td addr and token.
260
     * Otherwise we return last good match. ie just token.
261
     * It's ok to match just token because it identifies the transaction
262
     * rather well, token includes: device addr, endpoint, size, etc.
263
     *
264
     * Also since we queue async transactions in reverse order by returning
265
     * last good match we restores the order.
266
     *
267
     * It's expected that we wont have a ton of outstanding transactions.
268
     * If we ever do we'd want to optimize this algorithm.
269
     */
270

  
271
    QTAILQ_FOREACH(async, &s->async_pending, next) {
272
        if (async->token == token) {
273
            /* Good match */
274
            match = async;
275 311

  
276
            if (async->td == addr) {
277
                /* Best match */
278
                break;
279
            }
312
    QTAILQ_FOREACH(queue, &s->queues, next) {
313
        if (queue->token == token) {
314
            break;
280 315
        }
281
        count++;
316
    }
317
    if (queue == NULL) {
318
        return NULL;
282 319
    }
283 320

  
284
    if (count > 64)
285
	fprintf(stderr, "uhci: warning lots of async transactions\n");
321
    QTAILQ_FOREACH(async, &queue->asyncs, next) {
322
        if (async->td == addr) {
323
            return async;
324
        }
325
    }
286 326

  
287
    return match;
327
    return NULL;
288 328
}
289 329

  
290 330
static void uhci_update_irq(UHCIState *s)
......
753 793
{
754 794
    UHCIAsync *async;
755 795
    int len = 0, max_len;
756
    uint8_t pid, isoc;
757
    uint32_t token;
796
    uint8_t pid;
758 797
    USBDevice *dev;
759 798
    USBEndpoint *ep;
760 799

  
......
762 801
    if (!(td->ctrl & TD_CTRL_ACTIVE))
763 802
        return 1;
764 803

  
765
    /* token field is not unique for isochronous requests,
766
     * so use the destination buffer 
767
     */
768
    if (td->ctrl & TD_CTRL_IOS) {
769
        token = td->buffer;
770
        isoc = 1;
771
    } else {
772
        token = td->token;
773
        isoc = 0;
774
    }
775

  
776
    async = uhci_async_find_td(s, addr, token);
804
    async = uhci_async_find_td(s, addr, td);
777 805
    if (async) {
778 806
        /* Already submitted */
779
        async->valid = 32;
807
        async->queue->valid = 32;
780 808

  
781 809
        if (!async->done)
782 810
            return 1;
783 811

  
784
        uhci_async_unlink(s, async);
812
        uhci_async_unlink(async);
785 813
        goto done;
786 814
    }
787 815

  
788 816
    /* Allocate new packet */
789
    async = uhci_async_alloc(s);
817
    async = uhci_async_alloc(uhci_queue_get(s, td));
790 818
    if (!async)
791 819
        return 1;
792 820

  
793 821
    /* valid needs to be large enough to handle 10 frame delay
794 822
     * for initial isochronous requests
795 823
     */
796
    async->valid = 32;
824
    async->queue->valid = 32;
797 825
    async->td    = addr;
798
    async->token = token;
799
    async->isoc  = isoc;
826
    async->isoc  = td->ctrl & TD_CTRL_IOS;
800 827

  
801 828
    max_len = ((td->token >> 21) + 1) & 0x7ff;
802 829
    pid = td->token & 0xff;
......
821 848

  
822 849
    default:
823 850
        /* invalid pid : frame interrupted */
824
        uhci_async_free(s, async);
851
        uhci_async_free(async);
825 852
        s->status |= UHCI_STS_HCPERR;
826 853
        uhci_update_irq(s);
827 854
        return -1;
828 855
    }
829 856
 
830 857
    if (len == USB_RET_ASYNC) {
831
        uhci_async_link(s, async);
858
        uhci_async_link(async);
832 859
        return 2;
833 860
    }
834 861

  
......
837 864
done:
838 865
    len = uhci_complete_td(s, td, async, int_mask);
839 866
    usb_packet_unmap(&async->packet);
840
    uhci_async_free(s, async);
867
    uhci_async_free(async);
841 868
    return len;
842 869
}
843 870

  
844 871
static void uhci_async_complete(USBPort *port, USBPacket *packet)
845 872
{
846 873
    UHCIAsync *async = container_of(packet, UHCIAsync, packet);
847
    UHCIState *s = async->uhci;
874
    UHCIState *s = async->queue->uhci;
848 875

  
849 876
    DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
850 877

  
......
859 886
        le32_to_cpus(&td.token);
860 887
        le32_to_cpus(&td.buffer);
861 888

  
862
        uhci_async_unlink(s, async);
889
        uhci_async_unlink(async);
863 890
        uhci_complete_td(s, &td, async, &int_mask);
864 891
        s->pending_int_mask |= int_mask;
865 892

  
866 893
        /* update the status bits of the TD */
867 894
        val = cpu_to_le32(td.ctrl);
868 895
        pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
869
        uhci_async_free(s, async);
896
        uhci_async_free(async);
870 897
    } else {
871 898
        async->done = 1;
872 899
        uhci_process_frame(s);
......
1142 1169
    }
1143 1170
    s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
1144 1171
    s->num_ports_vmstate = NB_PORTS;
1145
    QTAILQ_INIT(&s->async_pending);
1172
    QTAILQ_INIT(&s->queues);
1146 1173

  
1147 1174
    qemu_register_reset(uhci_reset, s);
1148 1175

  

Also available in: Unified diff