Revision 0122f472 hw/usb-ehci.c
b/hw/usb-ehci.c | ||
---|---|---|
334 | 334 |
uint32_t backptr; // Standard next link pointer |
335 | 335 |
} EHCIfstn; |
336 | 336 |
|
337 |
typedef struct { |
|
337 |
typedef struct EHCIQueue EHCIQueue; |
|
338 |
typedef struct EHCIState EHCIState; |
|
339 |
|
|
340 |
enum async_state { |
|
341 |
EHCI_ASYNC_NONE = 0, |
|
342 |
EHCI_ASYNC_INFLIGHT, |
|
343 |
EHCI_ASYNC_FINISHED, |
|
344 |
}; |
|
345 |
|
|
346 |
struct EHCIQueue { |
|
347 |
EHCIState *ehci; |
|
348 |
|
|
349 |
/* cached data from guest - needs to be flushed |
|
350 |
* when guest removes an entry (doorbell, handshake sequence) |
|
351 |
*/ |
|
352 |
EHCIqh qh; // copy of current QH (being worked on) |
|
353 |
uint32_t qhaddr; // address QH read from |
|
354 |
EHCIqtd qtd; // copy of current QTD (being worked on) |
|
355 |
uint32_t qtdaddr; // address QTD read from |
|
356 |
|
|
357 |
USBPacket packet; |
|
358 |
uint8_t buffer[BUFF_SIZE]; |
|
359 |
int pid; |
|
360 |
uint32_t tbytes; |
|
361 |
enum async_state async; |
|
362 |
int usb_status; |
|
363 |
}; |
|
364 |
|
|
365 |
struct EHCIState { |
|
338 | 366 |
PCIDevice dev; |
367 |
USBBus bus; |
|
339 | 368 |
qemu_irq irq; |
340 | 369 |
target_phys_addr_t mem_base; |
341 | 370 |
int mem; |
... | ... | |
360 | 389 |
uint32_t portsc[NB_PORTS]; |
361 | 390 |
}; |
362 | 391 |
}; |
392 |
|
|
363 | 393 |
/* |
364 | 394 |
* Internal states, shadow registers, etc |
365 | 395 |
*/ |
... | ... | |
369 | 399 |
int astate; // Current state in asynchronous schedule |
370 | 400 |
int pstate; // Current state in periodic schedule |
371 | 401 |
USBPort ports[NB_PORTS]; |
372 |
uint8_t buffer[BUFF_SIZE]; |
|
373 | 402 |
uint32_t usbsts_pending; |
403 |
EHCIQueue queue; |
|
374 | 404 |
|
375 |
/* cached data from guest - needs to be flushed |
|
376 |
* when guest removes an entry (doorbell, handshake sequence) |
|
377 |
*/ |
|
378 |
EHCIqh qh; // copy of current QH (being worked on) |
|
379 |
uint32_t qhaddr; // address QH read from |
|
380 |
|
|
381 |
EHCIqtd qtd; // copy of current QTD (being worked on) |
|
382 |
uint32_t qtdaddr; // address QTD read from |
|
383 |
|
|
384 |
uint32_t itdaddr; // current ITD |
|
385 |
|
|
386 |
uint32_t fetch_addr; // which address to look at next |
|
405 |
uint32_t a_fetch_addr; // which address to look at next |
|
406 |
uint32_t p_fetch_addr; // which address to look at next |
|
387 | 407 |
|
388 |
USBBus bus; |
|
389 |
USBPacket usb_packet; |
|
390 |
int async_complete; |
|
391 |
uint32_t tbytes; |
|
392 |
int pid; |
|
393 |
int exec_status; |
|
408 |
USBPacket ipacket; |
|
409 |
uint8_t ibuffer[BUFF_SIZE]; |
|
394 | 410 |
int isoch_pause; |
411 |
|
|
395 | 412 |
uint32_t last_run_usec; |
396 | 413 |
uint32_t frame_end_usec; |
397 |
} EHCIState;
|
|
414 |
}; |
|
398 | 415 |
|
399 | 416 |
#define SET_LAST_RUN_CLOCK(s) \ |
400 | 417 |
(s)->last_run_usec = qemu_get_clock_ns(vm_clock) / 1000; |
... | ... | |
563 | 580 |
return async ? s->astate : s->pstate; |
564 | 581 |
} |
565 | 582 |
|
583 |
static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr) |
|
584 |
{ |
|
585 |
if (async) { |
|
586 |
s->a_fetch_addr = addr; |
|
587 |
} else { |
|
588 |
s->p_fetch_addr = addr; |
|
589 |
} |
|
590 |
} |
|
591 |
|
|
592 |
static int ehci_get_fetch_addr(EHCIState *s, int async) |
|
593 |
{ |
|
594 |
return async ? s->a_fetch_addr : s->p_fetch_addr; |
|
595 |
} |
|
596 |
|
|
566 | 597 |
static void ehci_trace_qh(EHCIState *s, target_phys_addr_t addr, EHCIqh *qh) |
567 | 598 |
{ |
568 | 599 |
trace_usb_ehci_qh(addr, qh->next, |
... | ... | |
656 | 687 |
|
657 | 688 |
s->astate = EST_INACTIVE; |
658 | 689 |
s->pstate = EST_INACTIVE; |
659 |
s->async_complete = 0; |
|
660 | 690 |
s->isoch_pause = -1; |
661 | 691 |
s->attach_poll_counter = 0; |
662 | 692 |
|
... | ... | |
888 | 918 |
|
889 | 919 |
// 4.10.2 |
890 | 920 |
|
891 |
static int ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd)
|
|
921 |
static int ehci_qh_do_overlay(EHCIQueue *q)
|
|
892 | 922 |
{ |
893 | 923 |
int i; |
894 | 924 |
int dtoggle; |
... | ... | |
898 | 928 |
|
899 | 929 |
// remember values in fields to preserve in qh after overlay |
900 | 930 |
|
901 |
dtoggle = qh->token & QTD_TOKEN_DTOGGLE;
|
|
902 |
ping = qh->token & QTD_TOKEN_PING;
|
|
931 |
dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE;
|
|
932 |
ping = q->qh.token & QTD_TOKEN_PING;
|
|
903 | 933 |
|
904 |
qh->current_qtd = ehci->qtdaddr;
|
|
905 |
qh->next_qtd = qtd->next;
|
|
906 |
qh->altnext_qtd = qtd->altnext;
|
|
907 |
qh->token = qtd->token;
|
|
934 |
q->qh.current_qtd = q->qtdaddr;
|
|
935 |
q->qh.next_qtd = q->qtd.next;
|
|
936 |
q->qh.altnext_qtd = q->qtd.altnext;
|
|
937 |
q->qh.token = q->qtd.token;
|
|
908 | 938 |
|
909 | 939 |
|
910 |
eps = get_field(qh->epchar, QH_EPCHAR_EPS);
|
|
940 |
eps = get_field(q->qh.epchar, QH_EPCHAR_EPS);
|
|
911 | 941 |
if (eps == EHCI_QH_EPS_HIGH) { |
912 |
qh->token &= ~QTD_TOKEN_PING;
|
|
913 |
qh->token |= ping;
|
|
942 |
q->qh.token &= ~QTD_TOKEN_PING;
|
|
943 |
q->qh.token |= ping;
|
|
914 | 944 |
} |
915 | 945 |
|
916 |
reload = get_field(qh->epchar, QH_EPCHAR_RL);
|
|
917 |
set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
|
|
946 |
reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
|
|
947 |
set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
|
|
918 | 948 |
|
919 | 949 |
for (i = 0; i < 5; i++) { |
920 |
qh->bufptr[i] = qtd->bufptr[i];
|
|
950 |
q->qh.bufptr[i] = q->qtd.bufptr[i];
|
|
921 | 951 |
} |
922 | 952 |
|
923 |
if (!(qh->epchar & QH_EPCHAR_DTC)) {
|
|
953 |
if (!(q->qh.epchar & QH_EPCHAR_DTC)) {
|
|
924 | 954 |
// preserve QH DT bit |
925 |
qh->token &= ~QTD_TOKEN_DTOGGLE;
|
|
926 |
qh->token |= dtoggle;
|
|
955 |
q->qh.token &= ~QTD_TOKEN_DTOGGLE;
|
|
956 |
q->qh.token |= dtoggle;
|
|
927 | 957 |
} |
928 | 958 |
|
929 |
qh->bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
|
|
930 |
qh->bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
|
|
959 |
q->qh.bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
|
|
960 |
q->qh.bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
|
|
931 | 961 |
|
932 |
put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
|
|
962 |
put_dwords(NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
|
|
933 | 963 |
|
934 | 964 |
return 0; |
935 | 965 |
} |
936 | 966 |
|
937 |
static int ehci_buffer_rw(uint8_t *buffer, EHCIqh *qh, int bytes, int rw)
|
|
967 |
static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw)
|
|
938 | 968 |
{ |
939 | 969 |
int bufpos = 0; |
940 | 970 |
int cpage, offset; |
... | ... | |
946 | 976 |
return 0; |
947 | 977 |
} |
948 | 978 |
|
949 |
cpage = get_field(qh->token, QTD_TOKEN_CPAGE);
|
|
979 |
cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
|
|
950 | 980 |
if (cpage > 4) { |
951 | 981 |
fprintf(stderr, "cpage out of range (%d)\n", cpage); |
952 | 982 |
return USB_RET_PROCERR; |
953 | 983 |
} |
954 | 984 |
|
955 |
offset = qh->bufptr[0] & ~QTD_BUFPTR_MASK;
|
|
985 |
offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
|
|
956 | 986 |
|
957 | 987 |
do { |
958 | 988 |
/* start and end of this page */ |
959 |
head = qh->bufptr[cpage] & QTD_BUFPTR_MASK;
|
|
989 |
head = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
|
|
960 | 990 |
tail = head + ~QTD_BUFPTR_MASK + 1; |
961 | 991 |
/* add offset into page */ |
962 | 992 |
head |= offset; |
... | ... | |
966 | 996 |
} |
967 | 997 |
|
968 | 998 |
trace_usb_ehci_data(rw, cpage, offset, head, tail-head, bufpos); |
969 |
cpu_physical_memory_rw(head, &buffer[bufpos], tail - head, rw);
|
|
999 |
cpu_physical_memory_rw(head, q->buffer + bufpos, tail - head, rw);
|
|
970 | 1000 |
|
971 | 1001 |
bufpos += (tail - head); |
972 | 1002 |
bytes -= (tail - head); |
... | ... | |
978 | 1008 |
} while (bytes > 0); |
979 | 1009 |
|
980 | 1010 |
/* save cpage */ |
981 |
set_field(&qh->token, cpage, QTD_TOKEN_CPAGE);
|
|
1011 |
set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE);
|
|
982 | 1012 |
|
983 | 1013 |
/* save offset into cpage */ |
984 | 1014 |
offset = tail - head; |
985 |
qh->bufptr[0] &= ~QTD_BUFPTR_MASK;
|
|
986 |
qh->bufptr[0] |= offset;
|
|
1015 |
q->qh.bufptr[0] &= ~QTD_BUFPTR_MASK;
|
|
1016 |
q->qh.bufptr[0] |= offset;
|
|
987 | 1017 |
|
988 | 1018 |
return 0; |
989 | 1019 |
} |
990 | 1020 |
|
991 | 1021 |
static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet) |
992 | 1022 |
{ |
993 |
EHCIState *ehci = container_of(packet, EHCIState, usb_packet);
|
|
1023 |
EHCIQueue *q = container_of(packet, EHCIQueue, packet);
|
|
994 | 1024 |
|
995 | 1025 |
DPRINTF("Async packet complete\n"); |
996 |
ehci->async_complete = 1; |
|
997 |
ehci->exec_status = packet->len; |
|
1026 |
assert(q->async == EHIC_ASYNC_INFLIGHT); |
|
1027 |
q->async = EHCI_ASYNC_FINISHED; |
|
1028 |
q->usb_status = packet->len; |
|
998 | 1029 |
} |
999 | 1030 |
|
1000 |
static int ehci_execute_complete(EHCIState *ehci, EHCIqh *qh, int ret)
|
|
1031 |
static void ehci_execute_complete(EHCIQueue *q)
|
|
1001 | 1032 |
{ |
1002 | 1033 |
int c_err, reload; |
1003 | 1034 |
|
1004 |
if (ret == USB_RET_ASYNC && !ehci->async_complete) {
|
|
1035 |
if (q->async == EHCI_ASYNC_INFLIGHT) {
|
|
1005 | 1036 |
DPRINTF("not done yet\n"); |
1006 |
return ret;
|
|
1037 |
return; |
|
1007 | 1038 |
} |
1008 |
|
|
1009 |
ehci->async_complete = 0; |
|
1039 |
q->async = EHCI_ASYNC_NONE; |
|
1010 | 1040 |
|
1011 | 1041 |
DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n", |
1012 |
ehci->qhaddr, qh->next, ehci->qtdaddr, ret);
|
|
1042 |
q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
|
|
1013 | 1043 |
|
1014 |
if (ret < 0) {
|
|
1044 |
if (q->usb_status < 0) {
|
|
1015 | 1045 |
err: |
1016 | 1046 |
/* TO-DO: put this is in a function that can be invoked below as well */ |
1017 |
c_err = get_field(qh->token, QTD_TOKEN_CERR);
|
|
1047 |
c_err = get_field(q->qh.token, QTD_TOKEN_CERR);
|
|
1018 | 1048 |
c_err--; |
1019 |
set_field(&qh->token, c_err, QTD_TOKEN_CERR);
|
|
1049 |
set_field(&q->qh.token, c_err, QTD_TOKEN_CERR);
|
|
1020 | 1050 |
|
1021 |
switch(ret) {
|
|
1051 |
switch(q->usb_status) {
|
|
1022 | 1052 |
case USB_RET_NODEV: |
1023 | 1053 |
fprintf(stderr, "USB no device\n"); |
1024 | 1054 |
break; |
1025 | 1055 |
case USB_RET_STALL: |
1026 | 1056 |
fprintf(stderr, "USB stall\n"); |
1027 |
qh->token |= QTD_TOKEN_HALT;
|
|
1028 |
ehci_record_interrupt(ehci, USBSTS_ERRINT); |
|
1057 |
q->qh.token |= QTD_TOKEN_HALT;
|
|
1058 |
ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
|
|
1029 | 1059 |
break; |
1030 | 1060 |
case USB_RET_NAK: |
1031 | 1061 |
/* 4.10.3 */ |
1032 |
reload = get_field(qh->epchar, QH_EPCHAR_RL);
|
|
1033 |
if ((ehci->pid == USB_TOKEN_IN) && reload) {
|
|
1034 |
int nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
|
|
1062 |
reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
|
|
1063 |
if ((q->pid == USB_TOKEN_IN) && reload) {
|
|
1064 |
int nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT);
|
|
1035 | 1065 |
nakcnt--; |
1036 |
set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
|
|
1066 |
set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
|
|
1037 | 1067 |
} else if (!reload) { |
1038 |
return USB_RET_NAK;
|
|
1068 |
return; |
|
1039 | 1069 |
} |
1040 | 1070 |
break; |
1041 | 1071 |
case USB_RET_BABBLE: |
1042 | 1072 |
fprintf(stderr, "USB babble TODO\n"); |
1043 |
qh->token |= QTD_TOKEN_BABBLE;
|
|
1044 |
ehci_record_interrupt(ehci, USBSTS_ERRINT); |
|
1073 |
q->qh.token |= QTD_TOKEN_BABBLE;
|
|
1074 |
ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
|
|
1045 | 1075 |
break; |
1046 | 1076 |
default: |
1047 |
fprintf(stderr, "USB invalid response %d to handle\n", ret);
|
|
1048 |
/* TO-DO: transaction error */
|
|
1049 |
ret = USB_RET_PROCERR;
|
|
1077 |
/* should not be triggerable */
|
|
1078 |
fprintf(stderr, "USB invalid response %d to handle\n", q->usb_status);
|
|
1079 |
assert(0);
|
|
1050 | 1080 |
break; |
1051 | 1081 |
} |
1052 | 1082 |
} else { |
1053 | 1083 |
// DPRINTF("Short packet condition\n"); |
1054 | 1084 |
// TODO check 4.12 for splits |
1055 | 1085 |
|
1056 |
if ((ret > ehci->tbytes) && (ehci->pid == USB_TOKEN_IN)) {
|
|
1057 |
ret = USB_RET_BABBLE;
|
|
1086 |
if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) {
|
|
1087 |
q->usb_status = USB_RET_BABBLE;
|
|
1058 | 1088 |
goto err; |
1059 | 1089 |
} |
1060 | 1090 |
|
1061 |
if (ehci->tbytes && ehci->pid == USB_TOKEN_IN) { |
|
1062 |
if (ehci_buffer_rw(ehci->buffer, qh, ret, 1) != 0) { |
|
1063 |
return USB_RET_PROCERR; |
|
1091 |
if (q->tbytes && q->pid == USB_TOKEN_IN) { |
|
1092 |
if (ehci_buffer_rw(q, q->usb_status, 1) != 0) { |
|
1093 |
q->usb_status = USB_RET_PROCERR; |
|
1094 |
return; |
|
1064 | 1095 |
} |
1065 |
ehci->tbytes -= ret;
|
|
1096 |
q->tbytes -= q->usb_status;
|
|
1066 | 1097 |
} else { |
1067 |
ehci->tbytes = 0;
|
|
1098 |
q->tbytes = 0;
|
|
1068 | 1099 |
} |
1069 | 1100 |
|
1070 |
DPRINTF("updating tbytes to %d\n", ehci->tbytes);
|
|
1071 |
set_field(&qh->token, ehci->tbytes, QTD_TOKEN_TBYTES);
|
|
1101 |
DPRINTF("updating tbytes to %d\n", q->tbytes);
|
|
1102 |
set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES);
|
|
1072 | 1103 |
} |
1073 | 1104 |
|
1074 |
qh->token ^= QTD_TOKEN_DTOGGLE;
|
|
1075 |
qh->token &= ~QTD_TOKEN_ACTIVE;
|
|
1105 |
q->qh.token ^= QTD_TOKEN_DTOGGLE;
|
|
1106 |
q->qh.token &= ~QTD_TOKEN_ACTIVE;
|
|
1076 | 1107 |
|
1077 |
if ((ret >= 0) && (qh->token & QTD_TOKEN_IOC)) {
|
|
1078 |
ehci_record_interrupt(ehci, USBSTS_INT); |
|
1108 |
if ((q->usb_status >= 0) && (q->qh.token & QTD_TOKEN_IOC)) {
|
|
1109 |
ehci_record_interrupt(q->ehci, USBSTS_INT);
|
|
1079 | 1110 |
} |
1080 |
|
|
1081 |
return ret; |
|
1082 | 1111 |
} |
1083 | 1112 |
|
1084 | 1113 |
// 4.10.3 |
1085 | 1114 |
|
1086 |
static int ehci_execute(EHCIState *ehci, EHCIqh *qh)
|
|
1115 |
static int ehci_execute(EHCIQueue *q)
|
|
1087 | 1116 |
{ |
1088 | 1117 |
USBPort *port; |
1089 | 1118 |
USBDevice *dev; |
... | ... | |
1092 | 1121 |
int endp; |
1093 | 1122 |
int devadr; |
1094 | 1123 |
|
1095 |
if ( !(qh->token & QTD_TOKEN_ACTIVE)) {
|
|
1124 |
if ( !(q->qh.token & QTD_TOKEN_ACTIVE)) {
|
|
1096 | 1125 |
fprintf(stderr, "Attempting to execute inactive QH\n"); |
1097 | 1126 |
return USB_RET_PROCERR; |
1098 | 1127 |
} |
1099 | 1128 |
|
1100 |
ehci->tbytes = (qh->token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
|
|
1101 |
if (ehci->tbytes > BUFF_SIZE) {
|
|
1129 |
q->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
|
|
1130 |
if (q->tbytes > BUFF_SIZE) {
|
|
1102 | 1131 |
fprintf(stderr, "Request for more bytes than allowed\n"); |
1103 | 1132 |
return USB_RET_PROCERR; |
1104 | 1133 |
} |
1105 | 1134 |
|
1106 |
ehci->pid = (qh->token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
|
|
1107 |
switch(ehci->pid) {
|
|
1108 |
case 0: ehci->pid = USB_TOKEN_OUT; break;
|
|
1109 |
case 1: ehci->pid = USB_TOKEN_IN; break;
|
|
1110 |
case 2: ehci->pid = USB_TOKEN_SETUP; break;
|
|
1135 |
q->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
|
|
1136 |
switch(q->pid) {
|
|
1137 |
case 0: q->pid = USB_TOKEN_OUT; break;
|
|
1138 |
case 1: q->pid = USB_TOKEN_IN; break;
|
|
1139 |
case 2: q->pid = USB_TOKEN_SETUP; break;
|
|
1111 | 1140 |
default: fprintf(stderr, "bad token\n"); break; |
1112 | 1141 |
} |
1113 | 1142 |
|
1114 |
if ((ehci->tbytes && ehci->pid != USB_TOKEN_IN) &&
|
|
1115 |
(ehci_buffer_rw(ehci->buffer, qh, ehci->tbytes, 0) != 0)) {
|
|
1143 |
if ((q->tbytes && q->pid != USB_TOKEN_IN) &&
|
|
1144 |
(ehci_buffer_rw(q, q->tbytes, 0) != 0)) {
|
|
1116 | 1145 |
return USB_RET_PROCERR; |
1117 | 1146 |
} |
1118 | 1147 |
|
1119 |
endp = get_field(qh->epchar, QH_EPCHAR_EP);
|
|
1120 |
devadr = get_field(qh->epchar, QH_EPCHAR_DEVADDR);
|
|
1148 |
endp = get_field(q->qh.epchar, QH_EPCHAR_EP);
|
|
1149 |
devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
|
|
1121 | 1150 |
|
1122 | 1151 |
ret = USB_RET_NODEV; |
1123 | 1152 |
|
1124 | 1153 |
// TO-DO: associating device with ehci port |
1125 | 1154 |
for(i = 0; i < NB_PORTS; i++) { |
1126 |
port = &ehci->ports[i]; |
|
1155 |
port = &q->ehci->ports[i];
|
|
1127 | 1156 |
dev = port->dev; |
1128 | 1157 |
|
1129 | 1158 |
// TODO sometime we will also need to check if we are the port owner |
1130 | 1159 |
|
1131 |
if (!(ehci->portsc[i] &(PORTSC_CONNECT))) { |
|
1160 |
if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) {
|
|
1132 | 1161 |
DPRINTF("Port %d, no exec, not connected(%08X)\n", |
1133 |
i, ehci->portsc[i]); |
|
1162 |
i, q->ehci->portsc[i]);
|
|
1134 | 1163 |
continue; |
1135 | 1164 |
} |
1136 | 1165 |
|
1137 |
ehci->usb_packet.pid = ehci->pid;
|
|
1138 |
ehci->usb_packet.devaddr = devadr;
|
|
1139 |
ehci->usb_packet.devep = endp;
|
|
1140 |
ehci->usb_packet.data = ehci->buffer;
|
|
1141 |
ehci->usb_packet.len = ehci->tbytes;
|
|
1166 |
q->packet.pid = q->pid;
|
|
1167 |
q->packet.devaddr = devadr;
|
|
1168 |
q->packet.devep = endp;
|
|
1169 |
q->packet.data = q->buffer;
|
|
1170 |
q->packet.len = q->tbytes;
|
|
1142 | 1171 |
|
1143 |
ret = usb_handle_packet(dev, &ehci->usb_packet);
|
|
1172 |
ret = usb_handle_packet(dev, &q->packet);
|
|
1144 | 1173 |
|
1145 | 1174 |
DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n", |
1146 |
ehci->qhaddr, qh->next, ehci->qtdaddr, ehci->pid,
|
|
1147 |
ehci->usb_packet.len, ehci->tbytes, endp, ret);
|
|
1175 |
q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
|
|
1176 |
q->packet.len, q->tbytes, endp, ret);
|
|
1148 | 1177 |
|
1149 | 1178 |
if (ret != USB_RET_NODEV) { |
1150 | 1179 |
break; |
... | ... | |
1157 | 1186 |
} |
1158 | 1187 |
|
1159 | 1188 |
if (ret == USB_RET_ASYNC) { |
1160 |
ehci->async_complete = 0;
|
|
1189 |
q->async = EHCI_ASYNC_INFLIGHT;
|
|
1161 | 1190 |
} |
1162 | 1191 |
|
1163 | 1192 |
return ret; |
... | ... | |
1204 | 1233 |
DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len); |
1205 | 1234 |
|
1206 | 1235 |
if (!dir) { |
1207 |
cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 0); |
|
1236 |
cpu_physical_memory_rw(ptr, &ehci->ibuffer[0], len, 0);
|
|
1208 | 1237 |
pid = USB_TOKEN_OUT; |
1209 | 1238 |
} else |
1210 | 1239 |
pid = USB_TOKEN_IN; |
... | ... | |
1223 | 1252 |
continue; |
1224 | 1253 |
} |
1225 | 1254 |
|
1226 |
ehci->usb_packet.pid = ehci->pid;
|
|
1227 |
ehci->usb_packet.devaddr = devadr;
|
|
1228 |
ehci->usb_packet.devep = endp;
|
|
1229 |
ehci->usb_packet.data = ehci->buffer;
|
|
1230 |
ehci->usb_packet.len = len;
|
|
1255 |
ehci->ipacket.pid = pid;
|
|
1256 |
ehci->ipacket.devaddr = devadr;
|
|
1257 |
ehci->ipacket.devep = endp;
|
|
1258 |
ehci->ipacket.data = ehci->ibuffer;
|
|
1259 |
ehci->ipacket.len = len;
|
|
1231 | 1260 |
|
1232 | 1261 |
DPRINTF("calling usb_handle_packet\n"); |
1233 |
ret = usb_handle_packet(dev, &ehci->usb_packet);
|
|
1262 |
ret = usb_handle_packet(dev, &ehci->ipacket);
|
|
1234 | 1263 |
|
1235 | 1264 |
if (ret != USB_RET_NODEV) { |
1236 | 1265 |
break; |
... | ... | |
1271 | 1300 |
} |
1272 | 1301 |
|
1273 | 1302 |
if (ret >= 0 && dir) { |
1274 |
cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 1); |
|
1303 |
cpu_physical_memory_rw(ptr, &ehci->ibuffer[0], len, 1);
|
|
1275 | 1304 |
|
1276 | 1305 |
if (ret != len) { |
1277 | 1306 |
DPRINTF("ISOCH IN expected %d, got %d\n", |
... | ... | |
1289 | 1318 |
*/ |
1290 | 1319 |
static int ehci_state_waitlisthead(EHCIState *ehci, int async) |
1291 | 1320 |
{ |
1292 |
EHCIqh *qh = &ehci->qh;
|
|
1321 |
EHCIqh qh; |
|
1293 | 1322 |
int i = 0; |
1294 | 1323 |
int again = 0; |
1295 | 1324 |
uint32_t entry = ehci->asynclistaddr; |
... | ... | |
1301 | 1330 |
|
1302 | 1331 |
/* Find the head of the list (4.9.1.1) */ |
1303 | 1332 |
for(i = 0; i < MAX_QH; i++) { |
1304 |
get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2); |
|
1305 |
ehci_trace_qh(ehci, NLPTR_GET(entry), qh); |
|
1333 |
get_dwords(NLPTR_GET(entry), (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
|
|
1334 |
ehci_trace_qh(ehci, NLPTR_GET(entry), &qh);
|
|
1306 | 1335 |
|
1307 |
if (qh->epchar & QH_EPCHAR_H) {
|
|
1336 |
if (qh.epchar & QH_EPCHAR_H) {
|
|
1308 | 1337 |
if (async) { |
1309 | 1338 |
entry |= (NLPTR_TYPE_QH << 1); |
1310 | 1339 |
} |
1311 | 1340 |
|
1312 |
ehci->fetch_addr = entry;
|
|
1341 |
ehci_set_fetch_addr(ehci, async, entry);
|
|
1313 | 1342 |
ehci_set_state(ehci, async, EST_FETCHENTRY); |
1314 | 1343 |
again = 1; |
1315 | 1344 |
goto out; |
1316 | 1345 |
} |
1317 | 1346 |
|
1318 |
entry = qh->next;
|
|
1347 |
entry = qh.next;
|
|
1319 | 1348 |
if (entry == ehci->asynclistaddr) { |
1320 | 1349 |
break; |
1321 | 1350 |
} |
... | ... | |
1336 | 1365 |
static int ehci_state_fetchentry(EHCIState *ehci, int async) |
1337 | 1366 |
{ |
1338 | 1367 |
int again = 0; |
1339 |
uint32_t entry = ehci->fetch_addr;
|
|
1368 |
uint32_t entry = ehci_get_fetch_addr(ehci, async);
|
|
1340 | 1369 |
|
1341 | 1370 |
#if EHCI_DEBUG == 0 |
1342 | 1371 |
if (qemu_get_clock_ns(vm_clock) / 1000 >= ehci->frame_end_usec) { |
... | ... | |
1364 | 1393 |
switch (NLPTR_TYPE_GET(entry)) { |
1365 | 1394 |
case NLPTR_TYPE_QH: |
1366 | 1395 |
ehci_set_state(ehci, async, EST_FETCHQH); |
1367 |
ehci->qhaddr = entry; |
|
1368 | 1396 |
again = 1; |
1369 | 1397 |
break; |
1370 | 1398 |
|
1371 | 1399 |
case NLPTR_TYPE_ITD: |
1372 | 1400 |
ehci_set_state(ehci, async, EST_FETCHITD); |
1373 |
ehci->itdaddr = entry; |
|
1374 | 1401 |
again = 1; |
1375 | 1402 |
break; |
1376 | 1403 |
|
... | ... | |
1385 | 1412 |
return again; |
1386 | 1413 |
} |
1387 | 1414 |
|
1388 |
static int ehci_state_fetchqh(EHCIState *ehci, int async)
|
|
1415 |
static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
|
|
1389 | 1416 |
{ |
1390 |
EHCIqh *qh = &ehci->qh; |
|
1417 |
uint32_t entry; |
|
1418 |
EHCIQueue *q; |
|
1391 | 1419 |
int reload; |
1392 |
int again = 0; |
|
1393 | 1420 |
|
1394 |
get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2); |
|
1395 |
ehci_trace_qh(ehci, NLPTR_GET(ehci->qhaddr), qh); |
|
1421 |
entry = ehci_get_fetch_addr(ehci, async); |
|
1422 |
q = &ehci->queue; /* temporary */ |
|
1423 |
q->qhaddr = entry; |
|
1396 | 1424 |
|
1397 |
if (async && (qh->epchar & QH_EPCHAR_H)) { |
|
1425 |
get_dwords(NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); |
|
1426 |
ehci_trace_qh(ehci, NLPTR_GET(q->qhaddr), &q->qh); |
|
1427 |
|
|
1428 |
if (async && (q->qh.epchar & QH_EPCHAR_H)) { |
|
1398 | 1429 |
|
1399 | 1430 |
/* EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */ |
1400 | 1431 |
if (ehci->usbsts & USBSTS_REC) { |
1401 | 1432 |
ehci_clear_usbsts(ehci, USBSTS_REC); |
1402 | 1433 |
} else { |
1403 | 1434 |
DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset" |
1404 |
" - done processing\n", ehci->qhaddr);
|
|
1435 |
" - done processing\n", q->qhaddr);
|
|
1405 | 1436 |
ehci_set_state(ehci, async, EST_ACTIVE); |
1437 |
q = NULL; |
|
1406 | 1438 |
goto out; |
1407 | 1439 |
} |
1408 | 1440 |
} |
1409 | 1441 |
|
1410 | 1442 |
#if EHCI_DEBUG |
1411 |
if (ehci->qhaddr != qh->next) {
|
|
1443 |
if (q->qhaddr != q->qh.next) {
|
|
1412 | 1444 |
DPRINTF("FETCHQH: QH 0x%08x (h %x halt %x active %x) next 0x%08x\n", |
1413 |
ehci->qhaddr,
|
|
1414 |
qh->epchar & QH_EPCHAR_H,
|
|
1415 |
qh->token & QTD_TOKEN_HALT,
|
|
1416 |
qh->token & QTD_TOKEN_ACTIVE,
|
|
1417 |
qh->next);
|
|
1445 |
q->qhaddr,
|
|
1446 |
q->qh.epchar & QH_EPCHAR_H,
|
|
1447 |
q->qh.token & QTD_TOKEN_HALT,
|
|
1448 |
q->qh.token & QTD_TOKEN_ACTIVE,
|
|
1449 |
q->qh.next);
|
|
1418 | 1450 |
} |
1419 | 1451 |
#endif |
1420 | 1452 |
|
1421 |
reload = get_field(qh->epchar, QH_EPCHAR_RL);
|
|
1453 |
reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
|
|
1422 | 1454 |
if (reload) { |
1423 |
set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
|
|
1455 |
set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
|
|
1424 | 1456 |
} |
1425 | 1457 |
|
1426 |
if (qh->token & QTD_TOKEN_HALT) {
|
|
1458 |
if (q->qh.token & QTD_TOKEN_HALT) {
|
|
1427 | 1459 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
1428 |
again = 1; |
|
1429 | 1460 |
|
1430 |
} else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_qtd > 0x1000)) {
|
|
1431 |
ehci->qtdaddr = qh->current_qtd;
|
|
1461 |
} else if ((q->qh.token & QTD_TOKEN_ACTIVE) && (q->qh.current_qtd > 0x1000)) {
|
|
1462 |
q->qtdaddr = q->qh.current_qtd;
|
|
1432 | 1463 |
ehci_set_state(ehci, async, EST_FETCHQTD); |
1433 |
again = 1; |
|
1434 | 1464 |
|
1435 | 1465 |
} else { |
1436 | 1466 |
/* EHCI spec version 1.0 Section 4.10.2 */ |
1437 | 1467 |
ehci_set_state(ehci, async, EST_ADVANCEQUEUE); |
1438 |
again = 1; |
|
1439 | 1468 |
} |
1440 | 1469 |
|
1441 | 1470 |
out: |
1442 |
return again;
|
|
1471 |
return q;
|
|
1443 | 1472 |
} |
1444 | 1473 |
|
1445 | 1474 |
static int ehci_state_fetchitd(EHCIState *ehci, int async) |
1446 | 1475 |
{ |
1476 |
uint32_t entry; |
|
1447 | 1477 |
EHCIitd itd; |
1448 | 1478 |
|
1449 |
get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd, |
|
1479 |
assert(!async); |
|
1480 |
entry = ehci_get_fetch_addr(ehci, async); |
|
1481 |
|
|
1482 |
get_dwords(NLPTR_GET(entry),(uint32_t *) &itd, |
|
1450 | 1483 |
sizeof(EHCIitd) >> 2); |
1451 |
ehci_trace_itd(ehci, ehci->itdaddr, &itd);
|
|
1484 |
ehci_trace_itd(ehci, entry, &itd);
|
|
1452 | 1485 |
|
1453 | 1486 |
if (ehci_process_itd(ehci, &itd) != 0) { |
1454 | 1487 |
return -1; |
1455 | 1488 |
} |
1456 | 1489 |
|
1457 |
put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd,
|
|
1490 |
put_dwords(NLPTR_GET(entry), (uint32_t *) &itd,
|
|
1458 | 1491 |
sizeof(EHCIitd) >> 2); |
1459 |
ehci->fetch_addr = itd.next;
|
|
1492 |
ehci_set_fetch_addr(ehci, async, itd.next);
|
|
1460 | 1493 |
ehci_set_state(ehci, async, EST_FETCHENTRY); |
1461 | 1494 |
|
1462 | 1495 |
return 1; |
1463 | 1496 |
} |
1464 | 1497 |
|
1465 | 1498 |
/* Section 4.10.2 - paragraph 3 */ |
1466 |
static int ehci_state_advqueue(EHCIState *ehci, int async)
|
|
1499 |
static int ehci_state_advqueue(EHCIQueue *q, int async)
|
|
1467 | 1500 |
{ |
1468 | 1501 |
#if 0 |
1469 | 1502 |
/* TO-DO: 4.10.2 - paragraph 2 |
... | ... | |
1479 | 1512 |
/* |
1480 | 1513 |
* want data and alt-next qTD is valid |
1481 | 1514 |
*/ |
1482 |
if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
|
|
1483 |
(ehci->qh.altnext_qtd > 0x1000) &&
|
|
1484 |
(NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) {
|
|
1485 |
ehci->qtdaddr = ehci->qh.altnext_qtd;
|
|
1486 |
ehci_set_state(ehci, async, EST_FETCHQTD); |
|
1515 |
if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
|
|
1516 |
(q->qh.altnext_qtd > 0x1000) &&
|
|
1517 |
(NLPTR_TBIT(q->qh.altnext_qtd) == 0)) {
|
|
1518 |
q->qtdaddr = q->qh.altnext_qtd;
|
|
1519 |
ehci_set_state(q->ehci, async, EST_FETCHQTD);
|
|
1487 | 1520 |
|
1488 | 1521 |
/* |
1489 | 1522 |
* next qTD is valid |
1490 | 1523 |
*/ |
1491 |
} else if ((ehci->qh.next_qtd > 0x1000) &&
|
|
1492 |
(NLPTR_TBIT(ehci->qh.next_qtd) == 0)) {
|
|
1493 |
ehci->qtdaddr = ehci->qh.next_qtd;
|
|
1494 |
ehci_set_state(ehci, async, EST_FETCHQTD); |
|
1524 |
} else if ((q->qh.next_qtd > 0x1000) &&
|
|
1525 |
(NLPTR_TBIT(q->qh.next_qtd) == 0)) {
|
|
1526 |
q->qtdaddr = q->qh.next_qtd;
|
|
1527 |
ehci_set_state(q->ehci, async, EST_FETCHQTD);
|
|
1495 | 1528 |
|
1496 | 1529 |
/* |
1497 | 1530 |
* no valid qTD, try next QH |
1498 | 1531 |
*/ |
1499 | 1532 |
} else { |
1500 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
|
1533 |
ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
|
|
1501 | 1534 |
} |
1502 | 1535 |
|
1503 | 1536 |
return 1; |
1504 | 1537 |
} |
1505 | 1538 |
|
1506 | 1539 |
/* Section 4.10.2 - paragraph 4 */ |
1507 |
static int ehci_state_fetchqtd(EHCIState *ehci, int async)
|
|
1540 |
static int ehci_state_fetchqtd(EHCIQueue *q, int async)
|
|
1508 | 1541 |
{ |
1509 |
EHCIqtd *qtd = &ehci->qtd; |
|
1510 | 1542 |
int again = 0; |
1511 | 1543 |
|
1512 |
get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2);
|
|
1513 |
ehci_trace_qtd(ehci, NLPTR_GET(ehci->qtdaddr), qtd);
|
|
1544 |
get_dwords(NLPTR_GET(q->qtdaddr),(uint32_t *) &q->qtd, sizeof(EHCIqtd) >> 2);
|
|
1545 |
ehci_trace_qtd(q->ehci, NLPTR_GET(q->qtdaddr), &q->qtd);
|
|
1514 | 1546 |
|
1515 |
if (qtd->token & QTD_TOKEN_ACTIVE) {
|
|
1516 |
ehci_set_state(ehci, async, EST_EXECUTE); |
|
1547 |
if (q->qtd.token & QTD_TOKEN_ACTIVE) {
|
|
1548 |
ehci_set_state(q->ehci, async, EST_EXECUTE);
|
|
1517 | 1549 |
again = 1; |
1518 | 1550 |
} else { |
1519 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
|
1551 |
ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
|
|
1520 | 1552 |
again = 1; |
1521 | 1553 |
} |
1522 | 1554 |
|
1523 | 1555 |
return again; |
1524 | 1556 |
} |
1525 | 1557 |
|
1526 |
static int ehci_state_horizqh(EHCIState *ehci, int async)
|
|
1558 |
static int ehci_state_horizqh(EHCIQueue *q, int async)
|
|
1527 | 1559 |
{ |
1528 | 1560 |
int again = 0; |
1529 | 1561 |
|
1530 |
if (ehci->fetch_addr != ehci->qh.next) {
|
|
1531 |
ehci->fetch_addr = ehci->qh.next;
|
|
1532 |
ehci_set_state(ehci, async, EST_FETCHENTRY); |
|
1562 |
if (ehci_get_fetch_addr(q->ehci, async) != q->qh.next) {
|
|
1563 |
ehci_set_fetch_addr(q->ehci, async, q->qh.next);
|
|
1564 |
ehci_set_state(q->ehci, async, EST_FETCHENTRY);
|
|
1533 | 1565 |
again = 1; |
1534 | 1566 |
} else { |
1535 |
ehci_set_state(ehci, async, EST_ACTIVE); |
|
1567 |
ehci_set_state(q->ehci, async, EST_ACTIVE);
|
|
1536 | 1568 |
} |
1537 | 1569 |
|
1538 | 1570 |
return again; |
1539 | 1571 |
} |
1540 | 1572 |
|
1541 |
static int ehci_state_execute(EHCIState *ehci, int async)
|
|
1573 |
static int ehci_state_execute(EHCIQueue *q, int async)
|
|
1542 | 1574 |
{ |
1543 |
EHCIqh *qh = &ehci->qh; |
|
1544 |
EHCIqtd *qtd = &ehci->qtd; |
|
1545 | 1575 |
int again = 0; |
1546 | 1576 |
int reload, nakcnt; |
1547 | 1577 |
int smask; |
1548 | 1578 |
|
1549 |
if (ehci_qh_do_overlay(ehci, qh, qtd) != 0) {
|
|
1579 |
if (ehci_qh_do_overlay(q) != 0) {
|
|
1550 | 1580 |
return -1; |
1551 | 1581 |
} |
1552 | 1582 |
|
1553 |
smask = get_field(qh->epcap, QH_EPCAP_SMASK);
|
|
1583 |
smask = get_field(q->qh.epcap, QH_EPCAP_SMASK);
|
|
1554 | 1584 |
|
1555 | 1585 |
if (!smask) { |
1556 |
reload = get_field(qh->epchar, QH_EPCHAR_RL);
|
|
1557 |
nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
|
|
1586 |
reload = get_field(q->qh.epchar, QH_EPCHAR_RL);
|
|
1587 |
nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT);
|
|
1558 | 1588 |
if (reload && !nakcnt) { |
1559 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
|
1589 |
ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
|
|
1560 | 1590 |
again = 1; |
1561 | 1591 |
goto out; |
1562 | 1592 |
} |
... | ... | |
1567 | 1597 |
// TODO Windows does not seem to ever set the MULT field |
1568 | 1598 |
|
1569 | 1599 |
if (!async) { |
1570 |
int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
|
|
1600 |
int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
|
|
1571 | 1601 |
if (!transactCtr) { |
1572 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
|
1602 |
ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
|
|
1573 | 1603 |
again = 1; |
1574 | 1604 |
goto out; |
1575 | 1605 |
} |
1576 | 1606 |
} |
1577 | 1607 |
|
1578 | 1608 |
if (async) { |
1579 |
ehci_set_usbsts(ehci, USBSTS_REC); |
|
1609 |
ehci_set_usbsts(q->ehci, USBSTS_REC);
|
|
1580 | 1610 |
} |
1581 | 1611 |
|
1582 |
ehci->exec_status = ehci_execute(ehci, qh);
|
|
1583 |
if (ehci->exec_status == USB_RET_PROCERR) {
|
|
1612 |
q->usb_status = ehci_execute(q);
|
|
1613 |
if (q->usb_status == USB_RET_PROCERR) {
|
|
1584 | 1614 |
again = -1; |
1585 | 1615 |
goto out; |
1586 | 1616 |
} |
1587 |
ehci_set_state(ehci, async, EST_EXECUTING); |
|
1617 |
ehci_set_state(q->ehci, async, EST_EXECUTING);
|
|
1588 | 1618 |
|
1589 |
if (ehci->exec_status != USB_RET_ASYNC) {
|
|
1619 |
if (q->usb_status != USB_RET_ASYNC) {
|
|
1590 | 1620 |
again = 1; |
1591 | 1621 |
} |
1592 | 1622 |
|
... | ... | |
1594 | 1624 |
return again; |
1595 | 1625 |
} |
1596 | 1626 |
|
1597 |
static int ehci_state_executing(EHCIState *ehci, int async)
|
|
1627 |
static int ehci_state_executing(EHCIQueue *q, int async)
|
|
1598 | 1628 |
{ |
1599 |
EHCIqh *qh = &ehci->qh; |
|
1600 | 1629 |
int again = 0; |
1601 | 1630 |
int reload, nakcnt; |
1602 | 1631 |
|
1603 |
ehci->exec_status = ehci_execute_complete(ehci, qh, ehci->exec_status);
|
|
1604 |
if (ehci->exec_status == USB_RET_ASYNC) {
|
|
1632 |
ehci_execute_complete(q);
|
|
1633 |
if (q->usb_status == USB_RET_ASYNC) {
|
|
1605 | 1634 |
goto out; |
1606 | 1635 |
} |
1607 |
if (ehci->exec_status == USB_RET_PROCERR) {
|
|
1636 |
if (q->usb_status == USB_RET_PROCERR) {
|
|
1608 | 1637 |
again = -1; |
1609 | 1638 |
goto out; |
1610 | 1639 |
} |
1611 | 1640 |
|
1612 | 1641 |
// 4.10.3 |
1613 | 1642 |
if (!async) { |
1614 |
int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
|
|
1643 |
int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
|
|
1615 | 1644 |
transactCtr--; |
1616 |
set_field(&qh->epcap, transactCtr, QH_EPCAP_MULT);
|
|
1645 |
set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT);
|
|
1617 | 1646 |
// 4.10.3, bottom of page 82, should exit this state when transaction |
1618 | 1647 |
// counter decrements to 0 |
1619 | 1648 |
} |
1620 | 1649 |
|
1621 |
|
|
1622 |
reload = get_field(qh->epchar, QH_EPCHAR_RL); |
|
1650 |
reload = get_field(q->qh.epchar, QH_EPCHAR_RL); |
|
1623 | 1651 |
if (reload) { |
1624 |
nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
|
|
1625 |
if (ehci->exec_status == USB_RET_NAK) {
|
|
1652 |
nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT);
|
|
1653 |
if (q->usb_status == USB_RET_NAK) {
|
|
1626 | 1654 |
if (nakcnt) { |
1627 | 1655 |
nakcnt--; |
1628 | 1656 |
} |
1629 | 1657 |
} else { |
1630 | 1658 |
nakcnt = reload; |
1631 | 1659 |
} |
1632 |
set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
|
|
1660 |
set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
|
|
1633 | 1661 |
} |
1634 | 1662 |
|
1635 | 1663 |
/* |
... | ... | |
1637 | 1665 |
* in the EHCI spec but we need to do it since we don't share |
1638 | 1666 |
* physical memory with our guest VM. |
1639 | 1667 |
*/ |
1640 |
put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
|
|
1668 |
put_dwords(NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
|
|
1641 | 1669 |
|
1642 | 1670 |
/* 4.10.5 */ |
1643 |
if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) {
|
|
1644 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
|
1671 |
if ((q->usb_status == USB_RET_NAK) || (q->qh.token & QTD_TOKEN_ACTIVE)) {
|
|
1672 |
ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
|
|
1645 | 1673 |
} else { |
1646 |
ehci_set_state(ehci, async, EST_WRITEBACK); |
|
1674 |
ehci_set_state(q->ehci, async, EST_WRITEBACK);
|
|
1647 | 1675 |
} |
1648 | 1676 |
|
1649 | 1677 |
again = 1; |
... | ... | |
1653 | 1681 |
} |
1654 | 1682 |
|
1655 | 1683 |
|
1656 |
static int ehci_state_writeback(EHCIState *ehci, int async)
|
|
1684 |
static int ehci_state_writeback(EHCIQueue *q, int async)
|
|
1657 | 1685 |
{ |
1658 |
EHCIqh *qh = &ehci->qh; |
|
1659 | 1686 |
int again = 0; |
1660 | 1687 |
|
1661 | 1688 |
/* Write back the QTD from the QH area */ |
1662 |
ehci_trace_qtd(ehci, NLPTR_GET(ehci->qtdaddr), (EHCIqtd*) &qh->next_qtd);
|
|
1663 |
put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd,
|
|
1689 |
ehci_trace_qtd(q->ehci, NLPTR_GET(q->qtdaddr), (EHCIqtd*) &q->qh.next_qtd);
|
|
1690 |
put_dwords(NLPTR_GET(q->qtdaddr),(uint32_t *) &q->qh.next_qtd,
|
|
1664 | 1691 |
sizeof(EHCIqtd) >> 2); |
1665 | 1692 |
|
1666 | 1693 |
/* TODO confirm next state. For now, keep going if async |
1667 | 1694 |
* but stop after one qtd if periodic |
1668 | 1695 |
*/ |
1669 | 1696 |
//if (async) { |
1670 |
ehci_set_state(ehci, async, EST_ADVANCEQUEUE); |
|
1697 |
ehci_set_state(q->ehci, async, EST_ADVANCEQUEUE);
|
|
1671 | 1698 |
again = 1; |
1672 | 1699 |
//} else { |
1673 | 1700 |
// ehci_set_state(ehci, async, EST_ACTIVE); |
... | ... | |
1682 | 1709 |
static void ehci_advance_state(EHCIState *ehci, |
1683 | 1710 |
int async) |
1684 | 1711 |
{ |
1712 |
EHCIQueue *q = NULL; |
|
1685 | 1713 |
int again; |
1686 | 1714 |
int iter = 0; |
1687 | 1715 |
|
... | ... | |
1708 | 1736 |
break; |
1709 | 1737 |
|
1710 | 1738 |
case EST_FETCHQH: |
1711 |
again = ehci_state_fetchqh(ehci, async); |
|
1739 |
q = ehci_state_fetchqh(ehci, async); |
|
1740 |
again = q ? 1 : 0; |
|
1712 | 1741 |
break; |
1713 | 1742 |
|
1714 | 1743 |
case EST_FETCHITD: |
... | ... | |
1716 | 1745 |
break; |
1717 | 1746 |
|
1718 | 1747 |
case EST_ADVANCEQUEUE: |
1719 |
again = ehci_state_advqueue(ehci, async);
|
|
1748 |
again = ehci_state_advqueue(q, async);
|
|
1720 | 1749 |
break; |
1721 | 1750 |
|
1722 | 1751 |
case EST_FETCHQTD: |
1723 |
again = ehci_state_fetchqtd(ehci, async);
|
|
1752 |
again = ehci_state_fetchqtd(q, async);
|
|
1724 | 1753 |
break; |
1725 | 1754 |
|
1726 | 1755 |
case EST_HORIZONTALQH: |
1727 |
again = ehci_state_horizqh(ehci, async);
|
|
1756 |
again = ehci_state_horizqh(q, async);
|
|
1728 | 1757 |
break; |
1729 | 1758 |
|
1730 | 1759 |
case EST_EXECUTE: |
1731 | 1760 |
iter = 0; |
1732 |
again = ehci_state_execute(ehci, async);
|
|
1761 |
again = ehci_state_execute(q, async);
|
|
1733 | 1762 |
break; |
1734 | 1763 |
|
1735 | 1764 |
case EST_EXECUTING: |
1736 |
again = ehci_state_executing(ehci, async); |
|
1765 |
q = &ehci->queue; /* temporary */ |
|
1766 |
again = ehci_state_executing(q, async); |
|
1737 | 1767 |
break; |
1738 | 1768 |
|
1739 | 1769 |
case EST_WRITEBACK: |
1740 |
again = ehci_state_writeback(ehci, async);
|
|
1770 |
again = ehci_state_writeback(q, async);
|
|
1741 | 1771 |
break; |
1742 | 1772 |
|
1743 | 1773 |
default: |
... | ... | |
1759 | 1789 |
|
1760 | 1790 |
static void ehci_advance_async_state(EHCIState *ehci) |
1761 | 1791 |
{ |
1762 |
EHCIqh qh; |
|
1763 | 1792 |
int async = 1; |
1764 | 1793 |
|
1765 | 1794 |
switch(ehci_get_state(ehci, async)) { |
... | ... | |
1808 | 1837 |
/* fall through */ |
1809 | 1838 |
|
1810 | 1839 |
case EST_EXECUTING: |
1811 |
get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh, |
|
1812 |
sizeof(EHCIqh) >> 2); |
|
1813 | 1840 |
ehci_advance_state(ehci, async); |
1814 | 1841 |
break; |
1815 | 1842 |
|
... | ... | |
1817 | 1844 |
/* this should only be due to a developer mistake */ |
1818 | 1845 |
fprintf(stderr, "ehci: Bad asynchronous state %d. " |
1819 | 1846 |
"Resetting to active\n", ehci->astate); |
1820 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1847 |
assert(0);
|
|
1821 | 1848 |
} |
1822 | 1849 |
} |
1823 | 1850 |
|
... | ... | |
1857 | 1884 |
|
1858 | 1885 |
DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", |
1859 | 1886 |
ehci->frindex / 8, list, entry); |
1860 |
ehci->fetch_addr = entry;
|
|
1887 |
ehci_set_fetch_addr(ehci, async,entry);
|
|
1861 | 1888 |
ehci_set_state(ehci, async, EST_FETCHENTRY); |
1862 | 1889 |
ehci_advance_state(ehci, async); |
1863 | 1890 |
break; |
... | ... | |
1871 | 1898 |
/* this should only be due to a developer mistake */ |
1872 | 1899 |
fprintf(stderr, "ehci: Bad periodic state %d. " |
1873 | 1900 |
"Resetting to active\n", ehci->pstate); |
1874 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1901 |
assert(0);
|
|
1875 | 1902 |
} |
1876 | 1903 |
} |
1877 | 1904 |
|
... | ... | |
2043 | 2070 |
} |
2044 | 2071 |
|
2045 | 2072 |
s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s); |
2073 |
s->queue.ehci = s; |
|
2046 | 2074 |
|
2047 | 2075 |
qemu_register_reset(ehci_reset, s); |
2048 | 2076 |
|
Also available in: Unified diff