Commit 7bfe5777023bb88e8f63a3e80a836f3eb7b13fdc
1 parent
046833ea
OHCI USB isochronous transfers support (Arnon Gilboa).
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3493 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
340 additions
and
17 deletions
hw/usb-ohci.c
| @@ -32,6 +32,7 @@ | @@ -32,6 +32,7 @@ | ||
| 32 | //#define DEBUG_OHCI | 32 | //#define DEBUG_OHCI |
| 33 | /* Dump packet contents. */ | 33 | /* Dump packet contents. */ |
| 34 | //#define DEBUG_PACKET | 34 | //#define DEBUG_PACKET |
| 35 | +//#define DEBUG_ISOCH | ||
| 35 | /* This causes frames to occur 1000x slower */ | 36 | /* This causes frames to occur 1000x slower */ |
| 36 | //#define OHCI_TIME_WARP 1 | 37 | //#define OHCI_TIME_WARP 1 |
| 37 | 38 | ||
| @@ -132,8 +133,8 @@ static void ohci_bus_stop(OHCIState *ohci); | @@ -132,8 +133,8 @@ static void ohci_bus_stop(OHCIState *ohci); | ||
| 132 | #define OHCI_ED_S (1<<13) | 133 | #define OHCI_ED_S (1<<13) |
| 133 | #define OHCI_ED_K (1<<14) | 134 | #define OHCI_ED_K (1<<14) |
| 134 | #define OHCI_ED_F (1<<15) | 135 | #define OHCI_ED_F (1<<15) |
| 135 | -#define OHCI_ED_MPS_SHIFT 7 | ||
| 136 | -#define OHCI_ED_MPS_MASK (0xf<<OHCI_ED_FA_SHIFT) | 136 | +#define OHCI_ED_MPS_SHIFT 16 |
| 137 | +#define OHCI_ED_MPS_MASK (0x7ff<<OHCI_ED_MPS_SHIFT) | ||
| 137 | 138 | ||
| 138 | /* Flags in the head field of an Endpoint Desciptor. */ | 139 | /* Flags in the head field of an Endpoint Desciptor. */ |
| 139 | #define OHCI_ED_H 1 | 140 | #define OHCI_ED_H 1 |
| @@ -152,6 +153,22 @@ static void ohci_bus_stop(OHCIState *ohci); | @@ -152,6 +153,22 @@ static void ohci_bus_stop(OHCIState *ohci); | ||
| 152 | #define OHCI_TD_CC_SHIFT 28 | 153 | #define OHCI_TD_CC_SHIFT 28 |
| 153 | #define OHCI_TD_CC_MASK (0xf<<OHCI_TD_CC_SHIFT) | 154 | #define OHCI_TD_CC_MASK (0xf<<OHCI_TD_CC_SHIFT) |
| 154 | 155 | ||
| 156 | +/* Bitfields for the first word of an Isochronous Transfer Desciptor. */ | ||
| 157 | +/* CC & DI - same as in the General Transfer Desciptor */ | ||
| 158 | +#define OHCI_TD_SF_SHIFT 0 | ||
| 159 | +#define OHCI_TD_SF_MASK (0xffff<<OHCI_TD_SF_SHIFT) | ||
| 160 | +#define OHCI_TD_FC_SHIFT 24 | ||
| 161 | +#define OHCI_TD_FC_MASK (7<<OHCI_TD_FC_SHIFT) | ||
| 162 | + | ||
| 163 | +/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */ | ||
| 164 | +#define OHCI_TD_PSW_CC_SHIFT 12 | ||
| 165 | +#define OHCI_TD_PSW_CC_MASK (0xf<<OHCI_TD_PSW_CC_SHIFT) | ||
| 166 | +#define OHCI_TD_PSW_SIZE_SHIFT 0 | ||
| 167 | +#define OHCI_TD_PSW_SIZE_MASK (0xfff<<OHCI_TD_PSW_SIZE_SHIFT) | ||
| 168 | + | ||
| 169 | +#define OHCI_PAGE_MASK 0xfffff000 | ||
| 170 | +#define OHCI_OFFSET_MASK 0xfff | ||
| 171 | + | ||
| 155 | #define OHCI_DPTR_MASK 0xfffffff0 | 172 | #define OHCI_DPTR_MASK 0xfffffff0 |
| 156 | 173 | ||
| 157 | #define OHCI_BM(val, field) \ | 174 | #define OHCI_BM(val, field) \ |
| @@ -178,6 +195,15 @@ struct ohci_td { | @@ -178,6 +195,15 @@ struct ohci_td { | ||
| 178 | uint32_t be; | 195 | uint32_t be; |
| 179 | }; | 196 | }; |
| 180 | 197 | ||
| 198 | +/* Isochronous transfer descriptor */ | ||
| 199 | +struct ohci_iso_td { | ||
| 200 | + uint32_t flags; | ||
| 201 | + uint32_t bp; | ||
| 202 | + uint32_t next; | ||
| 203 | + uint32_t be; | ||
| 204 | + uint16_t offset[8]; | ||
| 205 | +}; | ||
| 206 | + | ||
| 181 | #define USB_HZ 12000000 | 207 | #define USB_HZ 12000000 |
| 182 | 208 | ||
| 183 | /* OHCI Local stuff */ | 209 | /* OHCI Local stuff */ |
| @@ -421,6 +447,32 @@ static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) | @@ -421,6 +447,32 @@ static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) | ||
| 421 | return 1; | 447 | return 1; |
| 422 | } | 448 | } |
| 423 | 449 | ||
| 450 | +/* Get an array of words from main memory */ | ||
| 451 | +static inline int get_words(uint32_t addr, uint16_t *buf, int num) | ||
| 452 | +{ | ||
| 453 | + int i; | ||
| 454 | + | ||
| 455 | + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { | ||
| 456 | + cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0); | ||
| 457 | + *buf = le16_to_cpu(*buf); | ||
| 458 | + } | ||
| 459 | + | ||
| 460 | + return 1; | ||
| 461 | +} | ||
| 462 | + | ||
| 463 | +/* Put an array of words in to main memory */ | ||
| 464 | +static inline int put_words(uint32_t addr, uint16_t *buf, int num) | ||
| 465 | +{ | ||
| 466 | + int i; | ||
| 467 | + | ||
| 468 | + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { | ||
| 469 | + uint16_t tmp = cpu_to_le16(*buf); | ||
| 470 | + cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1); | ||
| 471 | + } | ||
| 472 | + | ||
| 473 | + return 1; | ||
| 474 | +} | ||
| 475 | + | ||
| 424 | static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed) | 476 | static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed) |
| 425 | { | 477 | { |
| 426 | return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); | 478 | return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); |
| @@ -431,6 +483,12 @@ static inline int ohci_read_td(uint32_t addr, struct ohci_td *td) | @@ -431,6 +483,12 @@ static inline int ohci_read_td(uint32_t addr, struct ohci_td *td) | ||
| 431 | return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); | 483 | return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); |
| 432 | } | 484 | } |
| 433 | 485 | ||
| 486 | +static inline int ohci_read_iso_td(uint32_t addr, struct ohci_iso_td *td) | ||
| 487 | +{ | ||
| 488 | + return (get_dwords(addr, (uint32_t *)td, 4) && | ||
| 489 | + get_words(addr + 16, td->offset, 8)); | ||
| 490 | +} | ||
| 491 | + | ||
| 434 | static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed) | 492 | static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed) |
| 435 | { | 493 | { |
| 436 | return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); | 494 | return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); |
| @@ -441,6 +499,12 @@ static inline int ohci_put_td(uint32_t addr, struct ohci_td *td) | @@ -441,6 +499,12 @@ static inline int ohci_put_td(uint32_t addr, struct ohci_td *td) | ||
| 441 | return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); | 499 | return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); |
| 442 | } | 500 | } |
| 443 | 501 | ||
| 502 | +static inline int ohci_put_iso_td(uint32_t addr, struct ohci_iso_td *td) | ||
| 503 | +{ | ||
| 504 | + return (put_dwords(addr, (uint32_t *)td, 4) && | ||
| 505 | + put_words(addr + 16, td->offset, 8)); | ||
| 506 | +} | ||
| 507 | + | ||
| 444 | /* Read/Write the contents of a TD from/to main memory. */ | 508 | /* Read/Write the contents of a TD from/to main memory. */ |
| 445 | static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write) | 509 | static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write) |
| 446 | { | 510 | { |
| @@ -459,16 +523,270 @@ static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write) | @@ -459,16 +523,270 @@ static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write) | ||
| 459 | cpu_physical_memory_rw(ptr, buf, len - n, write); | 523 | cpu_physical_memory_rw(ptr, buf, len - n, write); |
| 460 | } | 524 | } |
| 461 | 525 | ||
| 462 | -static void ohci_process_lists(OHCIState *ohci); | 526 | +/* Read/Write the contents of an ISO TD from/to main memory. */ |
| 527 | +static void ohci_copy_iso_td(uint32_t start_addr, uint32_t end_addr, | ||
| 528 | + uint8_t *buf, int len, int write) | ||
| 529 | +{ | ||
| 530 | + uint32_t ptr; | ||
| 531 | + uint32_t n; | ||
| 463 | 532 | ||
| 464 | -static void ohci_async_complete_packet(USBPacket * packet, void *opaque) | 533 | + ptr = start_addr; |
| 534 | + n = 0x1000 - (ptr & 0xfff); | ||
| 535 | + if (n > len) | ||
| 536 | + n = len; | ||
| 537 | + cpu_physical_memory_rw(ptr, buf, n, write); | ||
| 538 | + if (n == len) | ||
| 539 | + return; | ||
| 540 | + ptr = end_addr & ~0xfffu; | ||
| 541 | + buf += n; | ||
| 542 | + cpu_physical_memory_rw(ptr, buf, len - n, write); | ||
| 543 | +} | ||
| 544 | + | ||
| 545 | +static void ohci_process_lists(OHCIState *ohci, int completion); | ||
| 546 | + | ||
| 547 | +static void ohci_async_complete_packet(USBPacket *packet, void *opaque) | ||
| 465 | { | 548 | { |
| 466 | OHCIState *ohci = opaque; | 549 | OHCIState *ohci = opaque; |
| 467 | #ifdef DEBUG_PACKET | 550 | #ifdef DEBUG_PACKET |
| 468 | dprintf("Async packet complete\n"); | 551 | dprintf("Async packet complete\n"); |
| 469 | #endif | 552 | #endif |
| 470 | ohci->async_complete = 1; | 553 | ohci->async_complete = 1; |
| 471 | - ohci_process_lists(ohci); | 554 | + ohci_process_lists(ohci, 1); |
| 555 | +} | ||
| 556 | + | ||
| 557 | +#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b))) | ||
| 558 | + | ||
| 559 | +static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, | ||
| 560 | + int completion) | ||
| 561 | +{ | ||
| 562 | + int dir; | ||
| 563 | + size_t len = 0; | ||
| 564 | + char *str = NULL; | ||
| 565 | + int pid; | ||
| 566 | + int ret; | ||
| 567 | + int i; | ||
| 568 | + USBDevice *dev; | ||
| 569 | + struct ohci_iso_td iso_td; | ||
| 570 | + uint32_t addr; | ||
| 571 | + uint16_t starting_frame; | ||
| 572 | + int16_t relative_frame_number; | ||
| 573 | + int frame_count; | ||
| 574 | + uint32_t start_offset, next_offset, end_offset = 0; | ||
| 575 | + uint32_t start_addr, end_addr; | ||
| 576 | + | ||
| 577 | + addr = ed->head & OHCI_DPTR_MASK; | ||
| 578 | + | ||
| 579 | + if (!ohci_read_iso_td(addr, &iso_td)) { | ||
| 580 | + printf("usb-ohci: ISO_TD read error at %x\n", addr); | ||
| 581 | + return 0; | ||
| 582 | + } | ||
| 583 | + | ||
| 584 | + starting_frame = OHCI_BM(iso_td.flags, TD_SF); | ||
| 585 | + frame_count = OHCI_BM(iso_td.flags, TD_FC); | ||
| 586 | + relative_frame_number = USUB(ohci->frame_number, starting_frame); | ||
| 587 | + | ||
| 588 | +#ifdef DEBUG_ISOCH | ||
| 589 | + printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n" | ||
| 590 | + "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n" | ||
| 591 | + "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n" | ||
| 592 | + "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n" | ||
| 593 | + "frame_number 0x%.8x starting_frame 0x%.8x\n" | ||
| 594 | + "frame_count 0x%.8x relative %d\n" | ||
| 595 | + "di 0x%.8x cc 0x%.8x\n", | ||
| 596 | + ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK, | ||
| 597 | + iso_td.flags, iso_td.bp, iso_td.next, iso_td.be, | ||
| 598 | + iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3], | ||
| 599 | + iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7], | ||
| 600 | + ohci->frame_number, starting_frame, | ||
| 601 | + frame_count, relative_frame_number, | ||
| 602 | + OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC)); | ||
| 603 | +#endif | ||
| 604 | + | ||
| 605 | + if (relative_frame_number < 0) { | ||
| 606 | + dprintf("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number); | ||
| 607 | + return 1; | ||
| 608 | + } else if (relative_frame_number > frame_count) { | ||
| 609 | + /* ISO TD expired - retire the TD to the Done Queue and continue with | ||
| 610 | + the next ISO TD of the same ED */ | ||
| 611 | + dprintf("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, | ||
| 612 | + frame_count); | ||
| 613 | + OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN); | ||
| 614 | + ed->head &= ~OHCI_DPTR_MASK; | ||
| 615 | + ed->head |= (iso_td.next & OHCI_DPTR_MASK); | ||
| 616 | + iso_td.next = ohci->done; | ||
| 617 | + ohci->done = addr; | ||
| 618 | + i = OHCI_BM(iso_td.flags, TD_DI); | ||
| 619 | + if (i < ohci->done_count) | ||
| 620 | + ohci->done_count = i; | ||
| 621 | + ohci_put_iso_td(addr, &iso_td); | ||
| 622 | + return 0; | ||
| 623 | + } | ||
| 624 | + | ||
| 625 | + dir = OHCI_BM(ed->flags, ED_D); | ||
| 626 | + switch (dir) { | ||
| 627 | + case OHCI_TD_DIR_IN: | ||
| 628 | + str = "in"; | ||
| 629 | + pid = USB_TOKEN_IN; | ||
| 630 | + break; | ||
| 631 | + case OHCI_TD_DIR_OUT: | ||
| 632 | + str = "out"; | ||
| 633 | + pid = USB_TOKEN_OUT; | ||
| 634 | + break; | ||
| 635 | + case OHCI_TD_DIR_SETUP: | ||
| 636 | + str = "setup"; | ||
| 637 | + pid = USB_TOKEN_SETUP; | ||
| 638 | + break; | ||
| 639 | + default: | ||
| 640 | + printf("usb-ohci: Bad direction %d\n", dir); | ||
| 641 | + return 1; | ||
| 642 | + } | ||
| 643 | + | ||
| 644 | + if (!iso_td.bp || !iso_td.be) { | ||
| 645 | + printf("usb-ohci: ISO_TD bp 0x%.8x be 0x%.8x\n", iso_td.bp, iso_td.be); | ||
| 646 | + return 1; | ||
| 647 | + } | ||
| 648 | + | ||
| 649 | + start_offset = iso_td.offset[relative_frame_number]; | ||
| 650 | + next_offset = iso_td.offset[relative_frame_number + 1]; | ||
| 651 | + | ||
| 652 | + if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || | ||
| 653 | + ((relative_frame_number < frame_count) && | ||
| 654 | + !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) { | ||
| 655 | + printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n", | ||
| 656 | + start_offset, next_offset); | ||
| 657 | + return 1; | ||
| 658 | + } | ||
| 659 | + | ||
| 660 | + if ((relative_frame_number < frame_count) && (start_offset > next_offset)) { | ||
| 661 | + printf("usb-ohci: ISO_TD start_offset=0x%.8x > next_offset=0x%.8x\n", | ||
| 662 | + start_offset, next_offset); | ||
| 663 | + return 1; | ||
| 664 | + } | ||
| 665 | + | ||
| 666 | + if ((start_offset & 0x1000) == 0) { | ||
| 667 | + start_addr = (iso_td.bp & OHCI_PAGE_MASK) | | ||
| 668 | + (start_offset & OHCI_OFFSET_MASK); | ||
| 669 | + } else { | ||
| 670 | + start_addr = (iso_td.be & OHCI_PAGE_MASK) | | ||
| 671 | + (start_offset & OHCI_OFFSET_MASK); | ||
| 672 | + } | ||
| 673 | + | ||
| 674 | + if (relative_frame_number < frame_count) { | ||
| 675 | + end_offset = next_offset - 1; | ||
| 676 | + if ((end_offset & 0x1000) == 0) { | ||
| 677 | + end_addr = (iso_td.bp & OHCI_PAGE_MASK) | | ||
| 678 | + (end_offset & OHCI_OFFSET_MASK); | ||
| 679 | + } else { | ||
| 680 | + end_addr = (iso_td.be & OHCI_PAGE_MASK) | | ||
| 681 | + (end_offset & OHCI_OFFSET_MASK); | ||
| 682 | + } | ||
| 683 | + } else { | ||
| 684 | + /* Last packet in the ISO TD */ | ||
| 685 | + end_addr = iso_td.be; | ||
| 686 | + } | ||
| 687 | + | ||
| 688 | + if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) { | ||
| 689 | + len = (end_addr & OHCI_OFFSET_MASK) + 0x1001 | ||
| 690 | + - (start_addr & OHCI_OFFSET_MASK); | ||
| 691 | + } else { | ||
| 692 | + len = end_addr - start_addr + 1; | ||
| 693 | + } | ||
| 694 | + | ||
| 695 | + if (len && dir != OHCI_TD_DIR_IN) { | ||
| 696 | + ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, len, 0); | ||
| 697 | + } | ||
| 698 | + | ||
| 699 | + if (completion) { | ||
| 700 | + ret = ohci->usb_packet.len; | ||
| 701 | + } else { | ||
| 702 | + ret = USB_RET_NODEV; | ||
| 703 | + for (i = 0; i < ohci->num_ports; i++) { | ||
| 704 | + dev = ohci->rhport[i].port.dev; | ||
| 705 | + if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) | ||
| 706 | + continue; | ||
| 707 | + ohci->usb_packet.pid = pid; | ||
| 708 | + ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA); | ||
| 709 | + ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); | ||
| 710 | + ohci->usb_packet.data = ohci->usb_buf; | ||
| 711 | + ohci->usb_packet.len = len; | ||
| 712 | + ohci->usb_packet.complete_cb = ohci_async_complete_packet; | ||
| 713 | + ohci->usb_packet.complete_opaque = ohci; | ||
| 714 | + ret = dev->handle_packet(dev, &ohci->usb_packet); | ||
| 715 | + if (ret != USB_RET_NODEV) | ||
| 716 | + break; | ||
| 717 | + } | ||
| 718 | + | ||
| 719 | + if (ret == USB_RET_ASYNC) { | ||
| 720 | + return 1; | ||
| 721 | + } | ||
| 722 | + } | ||
| 723 | + | ||
| 724 | +#ifdef DEBUG_ISOCH | ||
| 725 | + printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n", | ||
| 726 | + start_offset, end_offset, start_addr, end_addr, str, len, ret); | ||
| 727 | +#endif | ||
| 728 | + | ||
| 729 | + /* Writeback */ | ||
| 730 | + if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) { | ||
| 731 | + /* IN transfer succeeded */ | ||
| 732 | + ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, ret, 1); | ||
| 733 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, | ||
| 734 | + OHCI_CC_NOERROR); | ||
| 735 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret); | ||
| 736 | + } else if (dir == OHCI_TD_DIR_OUT && ret == len) { | ||
| 737 | + /* OUT transfer succeeded */ | ||
| 738 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, | ||
| 739 | + OHCI_CC_NOERROR); | ||
| 740 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0); | ||
| 741 | + } else { | ||
| 742 | + if (ret > len) { | ||
| 743 | + printf("usb-ohci: DataOverrun %d > %zu\n", ret, len); | ||
| 744 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, | ||
| 745 | + OHCI_CC_DATAOVERRUN); | ||
| 746 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, | ||
| 747 | + len); | ||
| 748 | + } else if (ret >= 0) { | ||
| 749 | + printf("usb-ohci: DataUnderrun %d\n", ret); | ||
| 750 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, | ||
| 751 | + OHCI_CC_DATAUNDERRUN); | ||
| 752 | + } else { | ||
| 753 | + switch (ret) { | ||
| 754 | + case USB_RET_NODEV: | ||
| 755 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, | ||
| 756 | + OHCI_CC_DEVICENOTRESPONDING); | ||
| 757 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, | ||
| 758 | + 0); | ||
| 759 | + break; | ||
| 760 | + case USB_RET_NAK: | ||
| 761 | + case USB_RET_STALL: | ||
| 762 | + printf("usb-ohci: got NAK/STALL %d\n", ret); | ||
| 763 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, | ||
| 764 | + OHCI_CC_STALL); | ||
| 765 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, | ||
| 766 | + 0); | ||
| 767 | + break; | ||
| 768 | + default: | ||
| 769 | + printf("usb-ohci: Bad device response %d\n", ret); | ||
| 770 | + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, | ||
| 771 | + OHCI_CC_UNDEXPETEDPID); | ||
| 772 | + break; | ||
| 773 | + } | ||
| 774 | + } | ||
| 775 | + } | ||
| 776 | + | ||
| 777 | + if (relative_frame_number == frame_count) { | ||
| 778 | + /* Last data packet of ISO TD - retire the TD to the Done Queue */ | ||
| 779 | + OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR); | ||
| 780 | + ed->head &= ~OHCI_DPTR_MASK; | ||
| 781 | + ed->head |= (iso_td.next & OHCI_DPTR_MASK); | ||
| 782 | + iso_td.next = ohci->done; | ||
| 783 | + ohci->done = addr; | ||
| 784 | + i = OHCI_BM(iso_td.flags, TD_DI); | ||
| 785 | + if (i < ohci->done_count) | ||
| 786 | + ohci->done_count = i; | ||
| 787 | + } | ||
| 788 | + ohci_put_iso_td(addr, &iso_td); | ||
| 789 | + return 1; | ||
| 472 | } | 790 | } |
| 473 | 791 | ||
| 474 | /* Service a transport descriptor. | 792 | /* Service a transport descriptor. |
| @@ -671,7 +989,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) | @@ -671,7 +989,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) | ||
| 671 | } | 989 | } |
| 672 | 990 | ||
| 673 | /* Service an endpoint list. Returns nonzero if active TD were found. */ | 991 | /* Service an endpoint list. Returns nonzero if active TD were found. */ |
| 674 | -static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) | 992 | +static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) |
| 675 | { | 993 | { |
| 676 | struct ohci_ed ed; | 994 | struct ohci_ed ed; |
| 677 | uint32_t next_ed; | 995 | uint32_t next_ed; |
| @@ -702,10 +1020,6 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) | @@ -702,10 +1020,6 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) | ||
| 702 | continue; | 1020 | continue; |
| 703 | } | 1021 | } |
| 704 | 1022 | ||
| 705 | - /* Skip isochronous endpoints. */ | ||
| 706 | - if (ed.flags & OHCI_ED_F) | ||
| 707 | - continue; | ||
| 708 | - | ||
| 709 | while ((ed.head & OHCI_DPTR_MASK) != ed.tail) { | 1023 | while ((ed.head & OHCI_DPTR_MASK) != ed.tail) { |
| 710 | #ifdef DEBUG_PACKET | 1024 | #ifdef DEBUG_PACKET |
| 711 | dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u " | 1025 | dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u " |
| @@ -719,8 +1033,14 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) | @@ -719,8 +1033,14 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) | ||
| 719 | #endif | 1033 | #endif |
| 720 | active = 1; | 1034 | active = 1; |
| 721 | 1035 | ||
| 722 | - if (ohci_service_td(ohci, &ed)) | ||
| 723 | - break; | 1036 | + if ((ed.flags & OHCI_ED_F) == 0) { |
| 1037 | + if (ohci_service_td(ohci, &ed)) | ||
| 1038 | + break; | ||
| 1039 | + } else { | ||
| 1040 | + /* Handle isochronous endpoints */ | ||
| 1041 | + if (ohci_service_iso_td(ohci, &ed, completion)) | ||
| 1042 | + break; | ||
| 1043 | + } | ||
| 724 | } | 1044 | } |
| 725 | 1045 | ||
| 726 | ohci_put_ed(cur, &ed); | 1046 | ohci_put_ed(cur, &ed); |
| @@ -738,19 +1058,19 @@ static void ohci_sof(OHCIState *ohci) | @@ -738,19 +1058,19 @@ static void ohci_sof(OHCIState *ohci) | ||
| 738 | } | 1058 | } |
| 739 | 1059 | ||
| 740 | /* Process Control and Bulk lists. */ | 1060 | /* Process Control and Bulk lists. */ |
| 741 | -static void ohci_process_lists(OHCIState *ohci) | 1061 | +static void ohci_process_lists(OHCIState *ohci, int completion) |
| 742 | { | 1062 | { |
| 743 | if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { | 1063 | if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { |
| 744 | if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) | 1064 | if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) |
| 745 | dprintf("usb-ohci: head %x, cur %x\n", ohci->ctrl_head, ohci->ctrl_cur); | 1065 | dprintf("usb-ohci: head %x, cur %x\n", ohci->ctrl_head, ohci->ctrl_cur); |
| 746 | - if (!ohci_service_ed_list(ohci, ohci->ctrl_head)) { | 1066 | + if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) { |
| 747 | ohci->ctrl_cur = 0; | 1067 | ohci->ctrl_cur = 0; |
| 748 | ohci->status &= ~OHCI_STATUS_CLF; | 1068 | ohci->status &= ~OHCI_STATUS_CLF; |
| 749 | } | 1069 | } |
| 750 | } | 1070 | } |
| 751 | 1071 | ||
| 752 | if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) { | 1072 | if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) { |
| 753 | - if (!ohci_service_ed_list(ohci, ohci->bulk_head)) { | 1073 | + if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) { |
| 754 | ohci->bulk_cur = 0; | 1074 | ohci->bulk_cur = 0; |
| 755 | ohci->status &= ~OHCI_STATUS_BLF; | 1075 | ohci->status &= ~OHCI_STATUS_BLF; |
| 756 | } | 1076 | } |
| @@ -770,7 +1090,7 @@ static void ohci_frame_boundary(void *opaque) | @@ -770,7 +1090,7 @@ static void ohci_frame_boundary(void *opaque) | ||
| 770 | int n; | 1090 | int n; |
| 771 | 1091 | ||
| 772 | n = ohci->frame_number & 0x1f; | 1092 | n = ohci->frame_number & 0x1f; |
| 773 | - ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n])); | 1093 | + ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0); |
| 774 | } | 1094 | } |
| 775 | 1095 | ||
| 776 | /* Cancel all pending packets if either of the lists has been disabled. */ | 1096 | /* Cancel all pending packets if either of the lists has been disabled. */ |
| @@ -780,7 +1100,7 @@ static void ohci_frame_boundary(void *opaque) | @@ -780,7 +1100,7 @@ static void ohci_frame_boundary(void *opaque) | ||
| 780 | ohci->async_td = 0; | 1100 | ohci->async_td = 0; |
| 781 | } | 1101 | } |
| 782 | ohci->old_ctl = ohci->ctl; | 1102 | ohci->old_ctl = ohci->ctl; |
| 783 | - ohci_process_lists(ohci); | 1103 | + ohci_process_lists(ohci, 0); |
| 784 | 1104 | ||
| 785 | /* Frame boundary, so do EOF stuf here */ | 1105 | /* Frame boundary, so do EOF stuf here */ |
| 786 | ohci->frt = ohci->fit; | 1106 | ohci->frt = ohci->fit; |
| @@ -1206,6 +1526,9 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val) | @@ -1206,6 +1526,9 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val) | ||
| 1206 | ohci_set_frame_interval(ohci, val); | 1526 | ohci_set_frame_interval(ohci, val); |
| 1207 | break; | 1527 | break; |
| 1208 | 1528 | ||
| 1529 | + case 15: /* HcFmNumber */ | ||
| 1530 | + break; | ||
| 1531 | + | ||
| 1209 | case 16: /* HcPeriodicStart */ | 1532 | case 16: /* HcPeriodicStart */ |
| 1210 | ohci->pstart = val & 0xffff; | 1533 | ohci->pstart = val & 0xffff; |
| 1211 | break; | 1534 | break; |