Revision 4d611c9a hw/usb-uhci.c
b/hw/usb-uhci.c | ||
---|---|---|
76 | 76 |
uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ |
77 | 77 |
QEMUTimer *frame_timer; |
78 | 78 |
UHCIPort ports[NB_PORTS]; |
79 |
|
|
80 |
/* Interrupts that should be raised at the end of the current frame. */ |
|
81 |
uint32_t pending_int_mask; |
|
82 |
/* For simplicity of implementation we only allow a single pending USB |
|
83 |
request. This means all usb traffic on this controller is effectively |
|
84 |
suspended until that transfer completes. When the transfer completes |
|
85 |
the next transfer from that queue will be processed. However |
|
86 |
other queues will not be processed until the next frame. The solution |
|
87 |
is to allow multiple pending requests. */ |
|
88 |
uint32_t async_qh; |
|
89 |
USBPacket usb_packet; |
|
90 |
uint8_t usb_buf[1280]; |
|
79 | 91 |
} UHCIState; |
80 | 92 |
|
81 | 93 |
typedef struct UHCI_TD { |
... | ... | |
188 | 200 |
port = &s->ports[i]; |
189 | 201 |
dev = port->port.dev; |
190 | 202 |
if (dev) { |
191 |
dev->handle_packet(dev, |
|
192 |
USB_MSG_RESET, 0, 0, NULL, 0); |
|
203 |
usb_send_msg(dev, USB_MSG_RESET); |
|
193 | 204 |
} |
194 | 205 |
} |
195 | 206 |
uhci_reset(s); |
... | ... | |
232 | 243 |
/* port reset */ |
233 | 244 |
if ( (val & UHCI_PORT_RESET) && |
234 | 245 |
!(port->ctrl & UHCI_PORT_RESET) ) { |
235 |
dev->handle_packet(dev, |
|
236 |
USB_MSG_RESET, 0, 0, NULL, 0); |
|
246 |
usb_send_msg(dev, USB_MSG_RESET); |
|
237 | 247 |
} |
238 | 248 |
} |
239 | 249 |
port->ctrl = (port->ctrl & 0x01fb) | (val & ~0x01fb); |
... | ... | |
336 | 346 |
port->ctrl &= ~UHCI_PORT_LSDA; |
337 | 347 |
port->port.dev = dev; |
338 | 348 |
/* send the attach message */ |
339 |
dev->handle_packet(dev, |
|
340 |
USB_MSG_ATTACH, 0, 0, NULL, 0); |
|
349 |
usb_send_msg(dev, USB_MSG_ATTACH); |
|
341 | 350 |
} else { |
342 | 351 |
/* set connect status */ |
343 | 352 |
if (port->ctrl & UHCI_PORT_CCS) { |
... | ... | |
352 | 361 |
dev = port->port.dev; |
353 | 362 |
if (dev) { |
354 | 363 |
/* send the detach message */ |
355 |
dev->handle_packet(dev, |
|
356 |
USB_MSG_DETACH, 0, 0, NULL, 0); |
|
364 |
usb_send_msg(dev, USB_MSG_DETACH); |
|
357 | 365 |
} |
358 | 366 |
port->port.dev = NULL; |
359 | 367 |
} |
360 | 368 |
} |
361 | 369 |
|
362 |
static int uhci_broadcast_packet(UHCIState *s, uint8_t pid, |
|
363 |
uint8_t devaddr, uint8_t devep, |
|
364 |
uint8_t *data, int len) |
|
370 |
static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) |
|
365 | 371 |
{ |
366 | 372 |
UHCIPort *port; |
367 | 373 |
USBDevice *dev; |
... | ... | |
370 | 376 |
#ifdef DEBUG_PACKET |
371 | 377 |
{ |
372 | 378 |
const char *pidstr; |
373 |
switch(pid) { |
|
379 |
switch(p->pid) {
|
|
374 | 380 |
case USB_TOKEN_SETUP: pidstr = "SETUP"; break; |
375 | 381 |
case USB_TOKEN_IN: pidstr = "IN"; break; |
376 | 382 |
case USB_TOKEN_OUT: pidstr = "OUT"; break; |
377 | 383 |
default: pidstr = "?"; break; |
378 | 384 |
} |
379 | 385 |
printf("frame %d: pid=%s addr=0x%02x ep=%d len=%d\n", |
380 |
s->frnum, pidstr, devaddr, devep, len);
|
|
381 |
if (pid != USB_TOKEN_IN) { |
|
386 |
s->frnum, pidstr, p->devaddr, p->devep, p->len);
|
|
387 |
if (p->pid != USB_TOKEN_IN) {
|
|
382 | 388 |
printf(" data_out="); |
383 |
for(i = 0; i < len; i++) { |
|
384 |
printf(" %02x", data[i]); |
|
389 |
for(i = 0; i < p->len; i++) {
|
|
390 |
printf(" %02x", p->data[i]);
|
|
385 | 391 |
} |
386 | 392 |
printf("\n"); |
387 | 393 |
} |
... | ... | |
391 | 397 |
port = &s->ports[i]; |
392 | 398 |
dev = port->port.dev; |
393 | 399 |
if (dev && (port->ctrl & UHCI_PORT_EN)) { |
394 |
ret = dev->handle_packet(dev, pid, |
|
395 |
devaddr, devep, |
|
396 |
data, len); |
|
400 |
ret = dev->handle_packet(dev, p); |
|
397 | 401 |
if (ret != USB_RET_NODEV) { |
398 | 402 |
#ifdef DEBUG_PACKET |
399 |
{ |
|
403 |
if (ret == USB_RET_ASYNC) { |
|
404 |
printf("usb-uhci: Async packet\n"); |
|
405 |
} else { |
|
400 | 406 |
printf(" ret=%d ", ret); |
401 |
if (pid == USB_TOKEN_IN && ret > 0) { |
|
407 |
if (p->pid == USB_TOKEN_IN && ret > 0) {
|
|
402 | 408 |
printf("data_in="); |
403 | 409 |
for(i = 0; i < ret; i++) { |
404 |
printf(" %02x", data[i]); |
|
410 |
printf(" %02x", p->data[i]);
|
|
405 | 411 |
} |
406 | 412 |
} |
407 | 413 |
printf("\n"); |
... | ... | |
414 | 420 |
return USB_RET_NODEV; |
415 | 421 |
} |
416 | 422 |
|
423 |
static void uhci_async_complete_packet(USBPacket * packet, void *opaque); |
|
424 |
|
|
417 | 425 |
/* return -1 if fatal error (frame must be stopped) |
418 | 426 |
0 if TD successful |
419 | 427 |
1 if TD unsuccessful or inactive |
... | ... | |
421 | 429 |
static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) |
422 | 430 |
{ |
423 | 431 |
uint8_t pid; |
424 |
uint8_t buf[1280]; |
|
425 | 432 |
int len, max_len, err, ret; |
426 | 433 |
|
434 |
/* ??? This is wrong for async completion. */ |
|
427 | 435 |
if (td->ctrl & TD_CTRL_IOC) { |
428 | 436 |
*int_mask |= 0x01; |
429 | 437 |
} |
... | ... | |
434 | 442 |
/* TD is active */ |
435 | 443 |
max_len = ((td->token >> 21) + 1) & 0x7ff; |
436 | 444 |
pid = td->token & 0xff; |
437 |
switch(pid) { |
|
438 |
case USB_TOKEN_OUT: |
|
439 |
case USB_TOKEN_SETUP: |
|
440 |
cpu_physical_memory_read(td->buffer, buf, max_len); |
|
441 |
ret = uhci_broadcast_packet(s, pid, |
|
442 |
(td->token >> 8) & 0x7f, |
|
443 |
(td->token >> 15) & 0xf, |
|
444 |
buf, max_len); |
|
445 |
len = max_len; |
|
446 |
break; |
|
447 |
case USB_TOKEN_IN: |
|
448 |
ret = uhci_broadcast_packet(s, pid, |
|
449 |
(td->token >> 8) & 0x7f, |
|
450 |
(td->token >> 15) & 0xf, |
|
451 |
buf, max_len); |
|
445 |
if (s->async_qh) { |
|
446 |
ret = s->usb_packet.len; |
|
452 | 447 |
if (ret >= 0) { |
453 | 448 |
len = ret; |
454 | 449 |
if (len > max_len) { |
... | ... | |
457 | 452 |
} |
458 | 453 |
if (len > 0) { |
459 | 454 |
/* write the data back */ |
460 |
cpu_physical_memory_write(td->buffer, buf, len); |
|
455 |
cpu_physical_memory_write(td->buffer, s->usb_buf, len);
|
|
461 | 456 |
} |
462 | 457 |
} else { |
463 | 458 |
len = 0; |
464 | 459 |
} |
465 |
break; |
|
466 |
default: |
|
467 |
/* invalid pid : frame interrupted */ |
|
468 |
s->status |= UHCI_STS_HCPERR; |
|
469 |
uhci_update_irq(s); |
|
470 |
return -1; |
|
460 |
s->async_qh = 0; |
|
461 |
} else { |
|
462 |
s->usb_packet.pid = pid; |
|
463 |
s->usb_packet.devaddr = (td->token >> 8) & 0x7f; |
|
464 |
s->usb_packet.devep = (td->token >> 15) & 0xf; |
|
465 |
s->usb_packet.data = s->usb_buf; |
|
466 |
s->usb_packet.len = max_len; |
|
467 |
s->usb_packet.complete_cb = uhci_async_complete_packet; |
|
468 |
s->usb_packet.complete_opaque = s; |
|
469 |
switch(pid) { |
|
470 |
case USB_TOKEN_OUT: |
|
471 |
case USB_TOKEN_SETUP: |
|
472 |
cpu_physical_memory_read(td->buffer, s->usb_buf, max_len); |
|
473 |
ret = uhci_broadcast_packet(s, &s->usb_packet); |
|
474 |
len = max_len; |
|
475 |
break; |
|
476 |
case USB_TOKEN_IN: |
|
477 |
ret = uhci_broadcast_packet(s, &s->usb_packet); |
|
478 |
if (ret >= 0) { |
|
479 |
len = ret; |
|
480 |
if (len > max_len) { |
|
481 |
len = max_len; |
|
482 |
ret = USB_RET_BABBLE; |
|
483 |
} |
|
484 |
if (len > 0) { |
|
485 |
/* write the data back */ |
|
486 |
cpu_physical_memory_write(td->buffer, s->usb_buf, len); |
|
487 |
} |
|
488 |
} else { |
|
489 |
len = 0; |
|
490 |
} |
|
491 |
break; |
|
492 |
default: |
|
493 |
/* invalid pid : frame interrupted */ |
|
494 |
s->status |= UHCI_STS_HCPERR; |
|
495 |
uhci_update_irq(s); |
|
496 |
return -1; |
|
497 |
} |
|
498 |
} |
|
499 |
if (ret == USB_RET_ASYNC) { |
|
500 |
return 2; |
|
471 | 501 |
} |
472 | 502 |
if (td->ctrl & TD_CTRL_IOS) |
473 | 503 |
td->ctrl &= ~TD_CTRL_ACTIVE; |
... | ... | |
520 | 550 |
} |
521 | 551 |
} |
522 | 552 |
|
553 |
static void uhci_async_complete_packet(USBPacket * packet, void *opaque) |
|
554 |
{ |
|
555 |
UHCIState *s = opaque; |
|
556 |
UHCI_QH qh; |
|
557 |
UHCI_TD td; |
|
558 |
uint32_t link; |
|
559 |
uint32_t old_td_ctrl; |
|
560 |
uint32_t val; |
|
561 |
int ret; |
|
562 |
|
|
563 |
link = s->async_qh; |
|
564 |
if (!link) { |
|
565 |
/* This should never happen. It means a TD somehow got removed |
|
566 |
without cancelling the associated async IO request. */ |
|
567 |
return; |
|
568 |
} |
|
569 |
cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh)); |
|
570 |
le32_to_cpus(&qh.link); |
|
571 |
le32_to_cpus(&qh.el_link); |
|
572 |
/* Re-process the queue containing the async packet. */ |
|
573 |
while (1) { |
|
574 |
cpu_physical_memory_read(qh.el_link & ~0xf, |
|
575 |
(uint8_t *)&td, sizeof(td)); |
|
576 |
le32_to_cpus(&td.link); |
|
577 |
le32_to_cpus(&td.ctrl); |
|
578 |
le32_to_cpus(&td.token); |
|
579 |
le32_to_cpus(&td.buffer); |
|
580 |
old_td_ctrl = td.ctrl; |
|
581 |
ret = uhci_handle_td(s, &td, &s->pending_int_mask); |
|
582 |
/* update the status bits of the TD */ |
|
583 |
if (old_td_ctrl != td.ctrl) { |
|
584 |
val = cpu_to_le32(td.ctrl); |
|
585 |
cpu_physical_memory_write((qh.el_link & ~0xf) + 4, |
|
586 |
(const uint8_t *)&val, |
|
587 |
sizeof(val)); |
|
588 |
} |
|
589 |
if (ret < 0) |
|
590 |
break; /* interrupted frame */ |
|
591 |
if (ret == 2) { |
|
592 |
s->async_qh = link; |
|
593 |
break; |
|
594 |
} else if (ret == 0) { |
|
595 |
/* update qh element link */ |
|
596 |
qh.el_link = td.link; |
|
597 |
val = cpu_to_le32(qh.el_link); |
|
598 |
cpu_physical_memory_write((link & ~0xf) + 4, |
|
599 |
(const uint8_t *)&val, |
|
600 |
sizeof(val)); |
|
601 |
if (!(qh.el_link & 4)) |
|
602 |
break; |
|
603 |
} |
|
604 |
break; |
|
605 |
} |
|
606 |
} |
|
607 |
|
|
523 | 608 |
static void uhci_frame_timer(void *opaque) |
524 | 609 |
{ |
525 | 610 |
UHCIState *s = opaque; |
... | ... | |
528 | 613 |
int int_mask, cnt, ret; |
529 | 614 |
UHCI_TD td; |
530 | 615 |
UHCI_QH qh; |
616 |
uint32_t old_async_qh; |
|
531 | 617 |
|
532 | 618 |
if (!(s->cmd & UHCI_CMD_RS)) { |
533 | 619 |
qemu_del_timer(s->frame_timer); |
... | ... | |
535 | 621 |
s->status |= UHCI_STS_HCHALTED; |
536 | 622 |
return; |
537 | 623 |
} |
624 |
/* Complete the previous frame. */ |
|
625 |
s->frnum = (s->frnum + 1) & 0x7ff; |
|
626 |
if (s->pending_int_mask) { |
|
627 |
s->status2 |= s->pending_int_mask; |
|
628 |
s->status |= UHCI_STS_USBINT; |
|
629 |
uhci_update_irq(s); |
|
630 |
} |
|
631 |
old_async_qh = s->async_qh; |
|
538 | 632 |
frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); |
539 | 633 |
cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4); |
540 | 634 |
le32_to_cpus(&link); |
... | ... | |
546 | 640 |
/* valid frame */ |
547 | 641 |
if (link & 2) { |
548 | 642 |
/* QH */ |
643 |
if (link == s->async_qh) { |
|
644 |
/* We've found a previously issues packet. |
|
645 |
Nothing else to do. */ |
|
646 |
old_async_qh = 0; |
|
647 |
break; |
|
648 |
} |
|
549 | 649 |
cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh)); |
550 | 650 |
le32_to_cpus(&qh.link); |
551 | 651 |
le32_to_cpus(&qh.el_link); |
... | ... | |
556 | 656 |
} else if (qh.el_link & 2) { |
557 | 657 |
/* QH */ |
558 | 658 |
link = qh.el_link; |
659 |
} else if (s->async_qh) { |
|
660 |
/* We can only cope with one pending packet. Keep looking |
|
661 |
for the previously issued packet. */ |
|
662 |
link = qh.link; |
|
559 | 663 |
} else { |
560 | 664 |
/* TD */ |
561 | 665 |
if (--cnt == 0) |
... | ... | |
577 | 681 |
} |
578 | 682 |
if (ret < 0) |
579 | 683 |
break; /* interrupted frame */ |
580 |
if (ret == 0) { |
|
684 |
if (ret == 2) { |
|
685 |
s->async_qh = link; |
|
686 |
} else if (ret == 0) { |
|
581 | 687 |
/* update qh element link */ |
582 | 688 |
qh.el_link = td.link; |
583 | 689 |
val = cpu_to_le32(qh.el_link); |
... | ... | |
599 | 705 |
le32_to_cpus(&td.ctrl); |
600 | 706 |
le32_to_cpus(&td.token); |
601 | 707 |
le32_to_cpus(&td.buffer); |
602 |
old_td_ctrl = td.ctrl; |
|
603 |
ret = uhci_handle_td(s, &td, &int_mask); |
|
604 |
/* update the status bits of the TD */ |
|
605 |
if (old_td_ctrl != td.ctrl) { |
|
606 |
val = cpu_to_le32(td.ctrl); |
|
607 |
cpu_physical_memory_write((link & ~0xf) + 4, |
|
608 |
(const uint8_t *)&val, |
|
609 |
sizeof(val)); |
|
708 |
/* Ignore isochonous transfers while there is an async packet |
|
709 |
pending. This is wrong, but we don't implement isochronous |
|
710 |
transfers anyway. */ |
|
711 |
if (s->async_qh == 0) { |
|
712 |
old_td_ctrl = td.ctrl; |
|
713 |
ret = uhci_handle_td(s, &td, &int_mask); |
|
714 |
/* update the status bits of the TD */ |
|
715 |
if (old_td_ctrl != td.ctrl) { |
|
716 |
val = cpu_to_le32(td.ctrl); |
|
717 |
cpu_physical_memory_write((link & ~0xf) + 4, |
|
718 |
(const uint8_t *)&val, |
|
719 |
sizeof(val)); |
|
720 |
} |
|
721 |
if (ret < 0) |
|
722 |
break; /* interrupted frame */ |
|
723 |
if (ret == 2) { |
|
724 |
/* We can't handle async isochronous transfers. |
|
725 |
Cancel The packet. */ |
|
726 |
fprintf(stderr, "usb-uhci: Unimplemented async packet\n"); |
|
727 |
usb_cancel_packet(&s->usb_packet); |
|
728 |
} |
|
610 | 729 |
} |
611 |
if (ret < 0) |
|
612 |
break; /* interrupted frame */ |
|
613 | 730 |
link = td.link; |
614 | 731 |
} |
615 | 732 |
} |
616 |
s->frnum = (s->frnum + 1) & 0x7ff; |
|
617 |
if (int_mask) { |
|
618 |
s->status2 |= int_mask; |
|
619 |
s->status |= UHCI_STS_USBINT; |
|
620 |
uhci_update_irq(s); |
|
733 |
s->pending_int_mask = int_mask; |
|
734 |
if (old_async_qh) { |
|
735 |
/* A previously started transfer has disappeared from the transfer |
|
736 |
list. There's nothing useful we can do with it now, so just |
|
737 |
discard the packet and hope it wasn't too important. */ |
|
738 |
#ifdef DEBUG |
|
739 |
printf("Discarding USB packet\n"); |
|
740 |
#endif |
|
741 |
usb_cancel_packet(&s->usb_packet); |
|
742 |
s->async_qh = 0; |
|
621 | 743 |
} |
622 | 744 |
/* prepare the timer for the next frame */ |
623 | 745 |
expire_time = qemu_get_clock(vm_clock) + |
Also available in: Unified diff