Revision 26d53979 hw/usb-ehci.c
b/hw/usb-ehci.c | ||
---|---|---|
33 | 33 |
#include "trace.h" |
34 | 34 |
|
35 | 35 |
#define EHCI_DEBUG 0 |
36 |
#define STATE_DEBUG 0 /* state transitions */ |
|
37 | 36 |
|
38 |
#if EHCI_DEBUG || STATE_DEBUG
|
|
37 |
#if EHCI_DEBUG |
|
39 | 38 |
#define DPRINTF printf |
40 | 39 |
#else |
41 | 40 |
#define DPRINTF(...) |
42 | 41 |
#endif |
43 | 42 |
|
44 |
#if STATE_DEBUG |
|
45 |
#define DPRINTF_ST DPRINTF |
|
46 |
#else |
|
47 |
#define DPRINTF_ST(...) |
|
48 |
#endif |
|
49 |
|
|
50 | 43 |
/* internal processing - reset HC to try and recover */ |
51 | 44 |
#define USB_RET_PROCERR (-99) |
52 | 45 |
|
... | ... | |
417 | 410 |
*data = val; \ |
418 | 411 |
} while(0) |
419 | 412 |
|
413 |
static const char *ehci_state_names[] = { |
|
414 |
[ EST_INACTIVE ] = "INACTIVE", |
|
415 |
[ EST_ACTIVE ] = "ACTIVE", |
|
416 |
[ EST_EXECUTING ] = "EXECUTING", |
|
417 |
[ EST_SLEEPING ] = "SLEEPING", |
|
418 |
[ EST_WAITLISTHEAD ] = "WAITLISTHEAD", |
|
419 |
[ EST_FETCHENTRY ] = "FETCH ENTRY", |
|
420 |
[ EST_FETCHQH ] = "FETCH QH", |
|
421 |
[ EST_FETCHITD ] = "FETCH ITD", |
|
422 |
[ EST_ADVANCEQUEUE ] = "ADVANCEQUEUE", |
|
423 |
[ EST_FETCHQTD ] = "FETCH QTD", |
|
424 |
[ EST_EXECUTE ] = "EXECUTE", |
|
425 |
[ EST_WRITEBACK ] = "WRITEBACK", |
|
426 |
[ EST_HORIZONTALQH ] = "HORIZONTALQH", |
|
427 |
}; |
|
428 |
|
|
429 |
static const char *ehci_mmio_names[] = { |
|
430 |
[ CAPLENGTH ] = "CAPLENGTH", |
|
431 |
[ HCIVERSION ] = "HCIVERSION", |
|
432 |
[ HCSPARAMS ] = "HCSPARAMS", |
|
433 |
[ HCCPARAMS ] = "HCCPARAMS", |
|
434 |
[ USBCMD ] = "USBCMD", |
|
435 |
[ USBSTS ] = "USBSTS", |
|
436 |
[ USBINTR ] = "USBINTR", |
|
437 |
[ FRINDEX ] = "FRINDEX", |
|
438 |
[ PERIODICLISTBASE ] = "P-LIST BASE", |
|
439 |
[ ASYNCLISTADDR ] = "A-LIST ADDR", |
|
440 |
[ PORTSC_BEGIN ] = "PORTSC #0", |
|
441 |
[ PORTSC_BEGIN + 4] = "PORTSC #1", |
|
442 |
[ PORTSC_BEGIN + 8] = "PORTSC #2", |
|
443 |
[ PORTSC_BEGIN + 12] = "PORTSC #3", |
|
444 |
[ CONFIGFLAG ] = "CONFIGFLAG", |
|
445 |
}; |
|
420 | 446 |
|
421 |
static const char *addr2str(target_phys_addr_t addr)
|
|
447 |
static const char *nr2str(const char **n, size_t len, uint32_t nr)
|
|
422 | 448 |
{ |
423 |
const char *r = "unknown"; |
|
424 |
const char *n[] = { |
|
425 |
[ CAPLENGTH ] = "CAPLENGTH", |
|
426 |
[ HCIVERSION ] = "HCIVERSION", |
|
427 |
[ HCSPARAMS ] = "HCSPARAMS", |
|
428 |
[ HCCPARAMS ] = "HCCPARAMS", |
|
429 |
[ USBCMD ] = "USBCMD", |
|
430 |
[ USBSTS ] = "USBSTS", |
|
431 |
[ USBINTR ] = "USBINTR", |
|
432 |
[ FRINDEX ] = "FRINDEX", |
|
433 |
[ PERIODICLISTBASE ] = "P-LIST BASE", |
|
434 |
[ ASYNCLISTADDR ] = "A-LIST ADDR", |
|
435 |
[ PORTSC_BEGIN ... |
|
436 |
PORTSC_END ] = "PORTSC", |
|
437 |
[ CONFIGFLAG ] = "CONFIGFLAG", |
|
438 |
}; |
|
439 |
|
|
440 |
if (addr < ARRAY_SIZE(n) && n[addr] != NULL) { |
|
441 |
return n[addr]; |
|
449 |
if (nr < len && n[nr] != NULL) { |
|
450 |
return n[nr]; |
|
442 | 451 |
} else { |
443 |
return r;
|
|
452 |
return "unknown";
|
|
444 | 453 |
} |
445 | 454 |
} |
446 | 455 |
|
456 |
static const char *state2str(uint32_t state) |
|
457 |
{ |
|
458 |
return nr2str(ehci_state_names, ARRAY_SIZE(ehci_state_names), state); |
|
459 |
} |
|
460 |
|
|
461 |
static const char *addr2str(target_phys_addr_t addr) |
|
462 |
{ |
|
463 |
return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); |
|
464 |
} |
|
465 |
|
|
447 | 466 |
static void ehci_trace_usbsts(uint32_t mask, int state) |
448 | 467 |
{ |
449 | 468 |
/* interrupts */ |
... | ... | |
528 | 547 |
s->usbsts_pending = 0; |
529 | 548 |
} |
530 | 549 |
|
550 |
static void ehci_set_state(EHCIState *s, int async, int state) |
|
551 |
{ |
|
552 |
if (async) { |
|
553 |
trace_usb_ehci_state("async", state2str(state)); |
|
554 |
s->astate = state; |
|
555 |
} else { |
|
556 |
trace_usb_ehci_state("periodic", state2str(state)); |
|
557 |
s->pstate = state; |
|
558 |
} |
|
559 |
} |
|
560 |
|
|
561 |
static int ehci_get_state(EHCIState *s, int async) |
|
562 |
{ |
|
563 |
return async ? s->astate : s->pstate; |
|
564 |
} |
|
565 |
|
|
566 |
static void ehci_trace_qh(EHCIState *s, target_phys_addr_t addr, EHCIqh *qh) |
|
567 |
{ |
|
568 |
trace_usb_ehci_qh(addr, qh->next, |
|
569 |
qh->current_qtd, qh->next_qtd, qh->altnext_qtd, |
|
570 |
get_field(qh->epchar, QH_EPCHAR_RL), |
|
571 |
get_field(qh->epchar, QH_EPCHAR_MPLEN), |
|
572 |
get_field(qh->epchar, QH_EPCHAR_EPS), |
|
573 |
get_field(qh->epchar, QH_EPCHAR_EP), |
|
574 |
get_field(qh->epchar, QH_EPCHAR_DEVADDR), |
|
575 |
(bool)(qh->epchar & QH_EPCHAR_C), |
|
576 |
(bool)(qh->epchar & QH_EPCHAR_H), |
|
577 |
(bool)(qh->epchar & QH_EPCHAR_DTC), |
|
578 |
(bool)(qh->epchar & QH_EPCHAR_I)); |
|
579 |
} |
|
580 |
|
|
581 |
static void ehci_trace_qtd(EHCIState *s, target_phys_addr_t addr, EHCIqtd *qtd) |
|
582 |
{ |
|
583 |
trace_usb_ehci_qtd(addr, qtd->next, qtd->altnext, |
|
584 |
get_field(qtd->token, QTD_TOKEN_TBYTES), |
|
585 |
get_field(qtd->token, QTD_TOKEN_CPAGE), |
|
586 |
get_field(qtd->token, QTD_TOKEN_CERR), |
|
587 |
get_field(qtd->token, QTD_TOKEN_PID), |
|
588 |
(bool)(qtd->token & QTD_TOKEN_IOC), |
|
589 |
(bool)(qtd->token & QTD_TOKEN_ACTIVE), |
|
590 |
(bool)(qtd->token & QTD_TOKEN_HALT), |
|
591 |
(bool)(qtd->token & QTD_TOKEN_BABBLE), |
|
592 |
(bool)(qtd->token & QTD_TOKEN_XACTERR)); |
|
593 |
} |
|
594 |
|
|
595 |
static void ehci_trace_itd(EHCIState *s, target_phys_addr_t addr, EHCIitd *itd) |
|
596 |
{ |
|
597 |
trace_usb_ehci_itd(addr, itd->next); |
|
598 |
} |
|
599 |
|
|
531 | 600 |
/* Attach or detach a device on root hub */ |
532 | 601 |
|
533 | 602 |
static void ehci_attach(USBPort *port) |
... | ... | |
1230 | 1299 |
/* This state is the entry point for asynchronous schedule |
1231 | 1300 |
* processing. Entry here consitutes a EHCI start event state (4.8.5) |
1232 | 1301 |
*/ |
1233 |
static int ehci_state_waitlisthead(EHCIState *ehci, int async, int *state)
|
|
1302 |
static int ehci_state_waitlisthead(EHCIState *ehci, int async) |
|
1234 | 1303 |
{ |
1235 | 1304 |
EHCIqh *qh = &ehci->qh; |
1236 | 1305 |
int i = 0; |
... | ... | |
1245 | 1314 |
/* Find the head of the list (4.9.1.1) */ |
1246 | 1315 |
for(i = 0; i < MAX_QH; i++) { |
1247 | 1316 |
get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2); |
1317 |
ehci_trace_qh(ehci, NLPTR_GET(entry), qh); |
|
1248 | 1318 |
|
1249 | 1319 |
if (qh->epchar & QH_EPCHAR_H) { |
1250 |
DPRINTF_ST("WAITLISTHEAD: QH %08X is the HEAD of the list\n", |
|
1251 |
entry); |
|
1252 | 1320 |
if (async) { |
1253 | 1321 |
entry |= (NLPTR_TYPE_QH << 1); |
1254 | 1322 |
} |
1255 | 1323 |
|
1256 | 1324 |
ehci->fetch_addr = entry; |
1257 |
*state = EST_FETCHENTRY;
|
|
1325 |
ehci_set_state(ehci, async, EST_FETCHENTRY);
|
|
1258 | 1326 |
again = 1; |
1259 | 1327 |
goto out; |
1260 | 1328 |
} |
1261 | 1329 |
|
1262 |
DPRINTF_ST("WAITLISTHEAD: QH %08X is NOT the HEAD of the list\n", |
|
1263 |
entry); |
|
1264 | 1330 |
entry = qh->next; |
1265 | 1331 |
if (entry == ehci->asynclistaddr) { |
1266 |
DPRINTF("WAITLISTHEAD: reached beginning of QH list\n"); |
|
1267 | 1332 |
break; |
1268 | 1333 |
} |
1269 | 1334 |
} |
1270 | 1335 |
|
1271 | 1336 |
/* no head found for list. */ |
1272 | 1337 |
|
1273 |
*state = EST_ACTIVE;
|
|
1338 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1274 | 1339 |
|
1275 | 1340 |
out: |
1276 | 1341 |
return again; |
... | ... | |
1280 | 1345 |
/* This state is the entry point for periodic schedule processing as |
1281 | 1346 |
* well as being a continuation state for async processing. |
1282 | 1347 |
*/ |
1283 |
static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state)
|
|
1348 |
static int ehci_state_fetchentry(EHCIState *ehci, int async) |
|
1284 | 1349 |
{ |
1285 | 1350 |
int again = 0; |
1286 | 1351 |
uint32_t entry = ehci->fetch_addr; |
... | ... | |
1298 | 1363 |
#endif |
1299 | 1364 |
if (entry < 0x1000) { |
1300 | 1365 |
DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry); |
1301 |
*state = EST_ACTIVE;
|
|
1366 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1302 | 1367 |
goto out; |
1303 | 1368 |
} |
1304 | 1369 |
|
... | ... | |
1310 | 1375 |
|
1311 | 1376 |
switch (NLPTR_TYPE_GET(entry)) { |
1312 | 1377 |
case NLPTR_TYPE_QH: |
1313 |
DPRINTF_ST("FETCHENTRY: entry %X is a Queue Head\n", entry); |
|
1314 |
*state = EST_FETCHQH; |
|
1378 |
ehci_set_state(ehci, async, EST_FETCHQH); |
|
1315 | 1379 |
ehci->qhaddr = entry; |
1316 | 1380 |
again = 1; |
1317 | 1381 |
break; |
1318 | 1382 |
|
1319 | 1383 |
case NLPTR_TYPE_ITD: |
1320 |
DPRINTF_ST("FETCHENTRY: entry %X is an ITD\n", entry); |
|
1321 |
*state = EST_FETCHITD; |
|
1384 |
ehci_set_state(ehci, async, EST_FETCHITD); |
|
1322 | 1385 |
ehci->itdaddr = entry; |
1323 | 1386 |
again = 1; |
1324 | 1387 |
break; |
... | ... | |
1334 | 1397 |
return again; |
1335 | 1398 |
} |
1336 | 1399 |
|
1337 |
static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state)
|
|
1400 |
static int ehci_state_fetchqh(EHCIState *ehci, int async) |
|
1338 | 1401 |
{ |
1339 | 1402 |
EHCIqh *qh = &ehci->qh; |
1340 | 1403 |
int reload; |
1341 | 1404 |
int again = 0; |
1342 | 1405 |
|
1343 | 1406 |
get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2); |
1407 |
ehci_trace_qh(ehci, NLPTR_GET(ehci->qhaddr), qh); |
|
1344 | 1408 |
|
1345 | 1409 |
if (async && (qh->epchar & QH_EPCHAR_H)) { |
1346 | 1410 |
|
... | ... | |
1350 | 1414 |
} else { |
1351 | 1415 |
DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset" |
1352 | 1416 |
" - done processing\n", ehci->qhaddr); |
1353 |
*state = EST_ACTIVE;
|
|
1417 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1354 | 1418 |
goto out; |
1355 | 1419 |
} |
1356 | 1420 |
} |
... | ... | |
1368 | 1432 |
|
1369 | 1433 |
reload = get_field(qh->epchar, QH_EPCHAR_RL); |
1370 | 1434 |
if (reload) { |
1371 |
DPRINTF_ST("FETCHQH: reloading nakcnt to %d\n", reload); |
|
1372 | 1435 |
set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT); |
1373 | 1436 |
} |
1374 | 1437 |
|
1375 | 1438 |
if (qh->token & QTD_TOKEN_HALT) { |
1376 |
DPRINTF_ST("FETCHQH: QH Halted, go horizontal\n"); |
|
1377 |
*state = EST_HORIZONTALQH; |
|
1439 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
|
1378 | 1440 |
again = 1; |
1379 | 1441 |
|
1380 | 1442 |
} else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_qtd > 0x1000)) { |
1381 |
DPRINTF_ST("FETCHQH: Active, !Halt, execute - fetch qTD\n"); |
|
1382 | 1443 |
ehci->qtdaddr = qh->current_qtd; |
1383 |
*state = EST_FETCHQTD;
|
|
1444 |
ehci_set_state(ehci, async, EST_FETCHQTD);
|
|
1384 | 1445 |
again = 1; |
1385 | 1446 |
|
1386 | 1447 |
} else { |
1387 | 1448 |
/* EHCI spec version 1.0 Section 4.10.2 */ |
1388 |
DPRINTF_ST("FETCHQH: !Active, !Halt, advance queue\n"); |
|
1389 |
*state = EST_ADVANCEQUEUE; |
|
1449 |
ehci_set_state(ehci, async, EST_ADVANCEQUEUE); |
|
1390 | 1450 |
again = 1; |
1391 | 1451 |
} |
1392 | 1452 |
|
... | ... | |
1394 | 1454 |
return again; |
1395 | 1455 |
} |
1396 | 1456 |
|
1397 |
static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state)
|
|
1457 |
static int ehci_state_fetchitd(EHCIState *ehci, int async) |
|
1398 | 1458 |
{ |
1399 | 1459 |
EHCIitd itd; |
1400 | 1460 |
|
1401 | 1461 |
get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd, |
1402 | 1462 |
sizeof(EHCIitd) >> 2); |
1403 |
DPRINTF_ST("FETCHITD: Fetched ITD at address %08X " "(next is %08X)\n", |
|
1404 |
ehci->itdaddr, itd.next); |
|
1463 |
ehci_trace_itd(ehci, ehci->itdaddr, &itd); |
|
1405 | 1464 |
|
1406 | 1465 |
if (ehci_process_itd(ehci, &itd) != 0) { |
1407 | 1466 |
return -1; |
... | ... | |
1410 | 1469 |
put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd, |
1411 | 1470 |
sizeof(EHCIitd) >> 2); |
1412 | 1471 |
ehci->fetch_addr = itd.next; |
1413 |
*state = EST_FETCHENTRY;
|
|
1472 |
ehci_set_state(ehci, async, EST_FETCHENTRY);
|
|
1414 | 1473 |
|
1415 | 1474 |
return 1; |
1416 | 1475 |
} |
1417 | 1476 |
|
1418 | 1477 |
/* Section 4.10.2 - paragraph 3 */ |
1419 |
static int ehci_state_advqueue(EHCIState *ehci, int async, int *state)
|
|
1478 |
static int ehci_state_advqueue(EHCIState *ehci, int async) |
|
1420 | 1479 |
{ |
1421 | 1480 |
#if 0 |
1422 | 1481 |
/* TO-DO: 4.10.2 - paragraph 2 |
... | ... | |
1424 | 1483 |
* go to horizontal QH |
1425 | 1484 |
*/ |
1426 | 1485 |
if (I-bit set) { |
1427 |
*state = EST_HORIZONTALQH;
|
|
1486 |
ehci_set_state(ehci, async, EST_HORIZONTALQH);
|
|
1428 | 1487 |
goto out; |
1429 | 1488 |
} |
1430 | 1489 |
#endif |
... | ... | |
1435 | 1494 |
if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && |
1436 | 1495 |
(ehci->qh.altnext_qtd > 0x1000) && |
1437 | 1496 |
(NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) { |
1438 |
DPRINTF_ST("ADVQUEUE: goto alt next qTD. " |
|
1439 |
"curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n", |
|
1440 |
ehci->qh.current_qtd, ehci->qh.altnext_qtd, |
|
1441 |
ehci->qh.next_qtd, ehci->qh.next); |
|
1442 | 1497 |
ehci->qtdaddr = ehci->qh.altnext_qtd; |
1443 |
*state = EST_FETCHQTD;
|
|
1498 |
ehci_set_state(ehci, async, EST_FETCHQTD);
|
|
1444 | 1499 |
|
1445 | 1500 |
/* |
1446 | 1501 |
* next qTD is valid |
1447 | 1502 |
*/ |
1448 | 1503 |
} else if ((ehci->qh.next_qtd > 0x1000) && |
1449 | 1504 |
(NLPTR_TBIT(ehci->qh.next_qtd) == 0)) { |
1450 |
DPRINTF_ST("ADVQUEUE: next qTD. " |
|
1451 |
"curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n", |
|
1452 |
ehci->qh.current_qtd, ehci->qh.altnext_qtd, |
|
1453 |
ehci->qh.next_qtd, ehci->qh.next); |
|
1454 | 1505 |
ehci->qtdaddr = ehci->qh.next_qtd; |
1455 |
*state = EST_FETCHQTD;
|
|
1506 |
ehci_set_state(ehci, async, EST_FETCHQTD);
|
|
1456 | 1507 |
|
1457 | 1508 |
/* |
1458 | 1509 |
* no valid qTD, try next QH |
1459 | 1510 |
*/ |
1460 | 1511 |
} else { |
1461 |
DPRINTF_ST("ADVQUEUE: go to horizontal QH\n"); |
|
1462 |
*state = EST_HORIZONTALQH; |
|
1512 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
|
1463 | 1513 |
} |
1464 | 1514 |
|
1465 | 1515 |
return 1; |
1466 | 1516 |
} |
1467 | 1517 |
|
1468 | 1518 |
/* Section 4.10.2 - paragraph 4 */ |
1469 |
static int ehci_state_fetchqtd(EHCIState *ehci, int async, int *state)
|
|
1519 |
static int ehci_state_fetchqtd(EHCIState *ehci, int async) |
|
1470 | 1520 |
{ |
1471 | 1521 |
EHCIqtd *qtd = &ehci->qtd; |
1472 | 1522 |
int again = 0; |
1473 | 1523 |
|
1474 | 1524 |
get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2); |
1525 |
ehci_trace_qtd(ehci, NLPTR_GET(ehci->qtdaddr), qtd); |
|
1475 | 1526 |
|
1476 | 1527 |
if (qtd->token & QTD_TOKEN_ACTIVE) { |
1477 |
*state = EST_EXECUTE;
|
|
1528 |
ehci_set_state(ehci, async, EST_EXECUTE);
|
|
1478 | 1529 |
again = 1; |
1479 | 1530 |
} else { |
1480 |
*state = EST_HORIZONTALQH;
|
|
1531 |
ehci_set_state(ehci, async, EST_HORIZONTALQH);
|
|
1481 | 1532 |
again = 1; |
1482 | 1533 |
} |
1483 | 1534 |
|
1484 | 1535 |
return again; |
1485 | 1536 |
} |
1486 | 1537 |
|
1487 |
static int ehci_state_horizqh(EHCIState *ehci, int async, int *state)
|
|
1538 |
static int ehci_state_horizqh(EHCIState *ehci, int async) |
|
1488 | 1539 |
{ |
1489 | 1540 |
int again = 0; |
1490 | 1541 |
|
1491 | 1542 |
if (ehci->fetch_addr != ehci->qh.next) { |
1492 | 1543 |
ehci->fetch_addr = ehci->qh.next; |
1493 |
*state = EST_FETCHENTRY;
|
|
1544 |
ehci_set_state(ehci, async, EST_FETCHENTRY);
|
|
1494 | 1545 |
again = 1; |
1495 | 1546 |
} else { |
1496 |
*state = EST_ACTIVE;
|
|
1547 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1497 | 1548 |
} |
1498 | 1549 |
|
1499 | 1550 |
return again; |
1500 | 1551 |
} |
1501 | 1552 |
|
1502 |
static int ehci_state_execute(EHCIState *ehci, int async, int *state)
|
|
1553 |
static int ehci_state_execute(EHCIState *ehci, int async) |
|
1503 | 1554 |
{ |
1504 | 1555 |
EHCIqh *qh = &ehci->qh; |
1505 | 1556 |
EHCIqtd *qtd = &ehci->qtd; |
... | ... | |
1507 | 1558 |
int reload, nakcnt; |
1508 | 1559 |
int smask; |
1509 | 1560 |
|
1510 |
if (async) { |
|
1511 |
DPRINTF_ST(">>>>> ASYNC STATE MACHINE execute QH 0x%08x, QTD 0x%08x\n", |
|
1512 |
ehci->qhaddr, ehci->qtdaddr); |
|
1513 |
} else { |
|
1514 |
DPRINTF_ST(">>>>> PERIODIC STATE MACHINE execute\n"); |
|
1515 |
} |
|
1516 |
|
|
1517 | 1561 |
if (ehci_qh_do_overlay(ehci, qh, qtd) != 0) { |
1518 | 1562 |
return -1; |
1519 | 1563 |
} |
... | ... | |
1524 | 1568 |
reload = get_field(qh->epchar, QH_EPCHAR_RL); |
1525 | 1569 |
nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT); |
1526 | 1570 |
if (reload && !nakcnt) { |
1527 |
DPRINTF_ST("EXECUTE: RL != 0 but NakCnt == 0 -- no execute\n"); |
|
1528 |
*state = EST_HORIZONTALQH; |
|
1571 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
|
1529 | 1572 |
again = 1; |
1530 | 1573 |
goto out; |
1531 | 1574 |
} |
... | ... | |
1538 | 1581 |
if (!async) { |
1539 | 1582 |
int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT); |
1540 | 1583 |
if (!transactCtr) { |
1541 |
DPRINTF("ZERO transactctr for int qh, go HORIZ\n"); |
|
1542 |
*state = EST_HORIZONTALQH; |
|
1584 |
ehci_set_state(ehci, async, EST_HORIZONTALQH); |
|
1543 | 1585 |
again = 1; |
1544 | 1586 |
goto out; |
1545 | 1587 |
} |
... | ... | |
1554 | 1596 |
again = -1; |
1555 | 1597 |
goto out; |
1556 | 1598 |
} |
1557 |
*state = EST_EXECUTING;
|
|
1599 |
ehci_set_state(ehci, async, EST_EXECUTING);
|
|
1558 | 1600 |
|
1559 | 1601 |
if (ehci->exec_status != USB_RET_ASYNC) { |
1560 | 1602 |
again = 1; |
... | ... | |
1564 | 1606 |
return again; |
1565 | 1607 |
} |
1566 | 1608 |
|
1567 |
static int ehci_state_executing(EHCIState *ehci, int async, int *state)
|
|
1609 |
static int ehci_state_executing(EHCIState *ehci, int async) |
|
1568 | 1610 |
{ |
1569 | 1611 |
EHCIqh *qh = &ehci->qh; |
1570 | 1612 |
int again = 0; |
... | ... | |
1596 | 1638 |
if (nakcnt) { |
1597 | 1639 |
nakcnt--; |
1598 | 1640 |
} |
1599 |
DPRINTF_ST("EXECUTING: Nak occured and RL != 0, dec NakCnt to %d\n", |
|
1600 |
nakcnt); |
|
1601 | 1641 |
} else { |
1602 | 1642 |
nakcnt = reload; |
1603 |
DPRINTF_ST("EXECUTING: Nak didn't occur, reloading to %d\n", |
|
1604 |
nakcnt); |
|
1605 | 1643 |
} |
1606 | 1644 |
set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); |
1607 | 1645 |
} |
... | ... | |
1611 | 1649 |
* in the EHCI spec but we need to do it since we don't share |
1612 | 1650 |
* physical memory with our guest VM. |
1613 | 1651 |
*/ |
1614 |
|
|
1615 |
DPRINTF("EXECUTING: write QH to VM memory: qhaddr 0x%x, next 0x%x\n", |
|
1616 |
ehci->qhaddr, qh->next); |
|
1617 | 1652 |
put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2); |
1618 | 1653 |
|
1619 | 1654 |
/* 4.10.5 */ |
1620 | 1655 |
if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) { |
1621 |
*state = EST_HORIZONTALQH;
|
|
1656 |
ehci_set_state(ehci, async, EST_HORIZONTALQH);
|
|
1622 | 1657 |
} else { |
1623 |
*state = EST_WRITEBACK;
|
|
1658 |
ehci_set_state(ehci, async, EST_WRITEBACK);
|
|
1624 | 1659 |
} |
1625 | 1660 |
|
1626 | 1661 |
again = 1; |
... | ... | |
1630 | 1665 |
} |
1631 | 1666 |
|
1632 | 1667 |
|
1633 |
static int ehci_state_writeback(EHCIState *ehci, int async, int *state)
|
|
1668 |
static int ehci_state_writeback(EHCIState *ehci, int async) |
|
1634 | 1669 |
{ |
1635 | 1670 |
EHCIqh *qh = &ehci->qh; |
1636 | 1671 |
int again = 0; |
1637 | 1672 |
|
1638 | 1673 |
/* Write back the QTD from the QH area */ |
1639 |
DPRINTF_ST("WRITEBACK: write QTD to VM memory\n");
|
|
1674 |
ehci_trace_qtd(ehci, NLPTR_GET(ehci->qtdaddr), (EHCIqtd*) &qh->next_qtd);
|
|
1640 | 1675 |
put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd, |
1641 | 1676 |
sizeof(EHCIqtd) >> 2); |
1642 | 1677 |
|
... | ... | |
1644 | 1679 |
* but stop after one qtd if periodic |
1645 | 1680 |
*/ |
1646 | 1681 |
//if (async) { |
1647 |
*state = EST_ADVANCEQUEUE;
|
|
1682 |
ehci_set_state(ehci, async, EST_ADVANCEQUEUE);
|
|
1648 | 1683 |
again = 1; |
1649 | 1684 |
//} else { |
1650 |
// *state = EST_ACTIVE;
|
|
1685 |
// ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1651 | 1686 |
//} |
1652 | 1687 |
return again; |
1653 | 1688 |
} |
... | ... | |
1656 | 1691 |
* This is the state machine that is common to both async and periodic |
1657 | 1692 |
*/ |
1658 | 1693 |
|
1659 |
static int ehci_advance_state(EHCIState *ehci, |
|
1660 |
int async, |
|
1661 |
int state) |
|
1694 |
static void ehci_advance_state(EHCIState *ehci, |
|
1695 |
int async) |
|
1662 | 1696 |
{ |
1663 | 1697 |
int again; |
1664 | 1698 |
int iter = 0; |
1665 | 1699 |
|
1666 | 1700 |
do { |
1667 |
if (state == EST_FETCHQH) {
|
|
1701 |
if (ehci_get_state(ehci, async) == EST_FETCHQH) {
|
|
1668 | 1702 |
iter++; |
1669 | 1703 |
/* if we are roaming a lot of QH without executing a qTD |
1670 | 1704 |
* something is wrong with the linked list. TO-DO: why is |
... | ... | |
1672 | 1706 |
*/ |
1673 | 1707 |
if (iter > MAX_ITERATIONS) { |
1674 | 1708 |
DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n"); |
1675 |
state = EST_ACTIVE;
|
|
1709 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1676 | 1710 |
break; |
1677 | 1711 |
} |
1678 | 1712 |
} |
1679 |
switch(state) {
|
|
1713 |
switch(ehci_get_state(ehci, async)) {
|
|
1680 | 1714 |
case EST_WAITLISTHEAD: |
1681 |
again = ehci_state_waitlisthead(ehci, async, &state);
|
|
1715 |
again = ehci_state_waitlisthead(ehci, async); |
|
1682 | 1716 |
break; |
1683 | 1717 |
|
1684 | 1718 |
case EST_FETCHENTRY: |
1685 |
again = ehci_state_fetchentry(ehci, async, &state);
|
|
1719 |
again = ehci_state_fetchentry(ehci, async); |
|
1686 | 1720 |
break; |
1687 | 1721 |
|
1688 | 1722 |
case EST_FETCHQH: |
1689 |
again = ehci_state_fetchqh(ehci, async, &state);
|
|
1723 |
again = ehci_state_fetchqh(ehci, async); |
|
1690 | 1724 |
break; |
1691 | 1725 |
|
1692 | 1726 |
case EST_FETCHITD: |
1693 |
again = ehci_state_fetchitd(ehci, async, &state);
|
|
1727 |
again = ehci_state_fetchitd(ehci, async); |
|
1694 | 1728 |
break; |
1695 | 1729 |
|
1696 | 1730 |
case EST_ADVANCEQUEUE: |
1697 |
again = ehci_state_advqueue(ehci, async, &state);
|
|
1731 |
again = ehci_state_advqueue(ehci, async); |
|
1698 | 1732 |
break; |
1699 | 1733 |
|
1700 | 1734 |
case EST_FETCHQTD: |
1701 |
again = ehci_state_fetchqtd(ehci, async, &state);
|
|
1735 |
again = ehci_state_fetchqtd(ehci, async); |
|
1702 | 1736 |
break; |
1703 | 1737 |
|
1704 | 1738 |
case EST_HORIZONTALQH: |
1705 |
again = ehci_state_horizqh(ehci, async, &state);
|
|
1739 |
again = ehci_state_horizqh(ehci, async); |
|
1706 | 1740 |
break; |
1707 | 1741 |
|
1708 | 1742 |
case EST_EXECUTE: |
1709 | 1743 |
iter = 0; |
1710 |
again = ehci_state_execute(ehci, async, &state);
|
|
1744 |
again = ehci_state_execute(ehci, async); |
|
1711 | 1745 |
break; |
1712 | 1746 |
|
1713 | 1747 |
case EST_EXECUTING: |
1714 |
again = ehci_state_executing(ehci, async, &state);
|
|
1748 |
again = ehci_state_executing(ehci, async); |
|
1715 | 1749 |
break; |
1716 | 1750 |
|
1717 | 1751 |
case EST_WRITEBACK: |
1718 |
again = ehci_state_writeback(ehci, async, &state);
|
|
1752 |
again = ehci_state_writeback(ehci, async); |
|
1719 | 1753 |
break; |
1720 | 1754 |
|
1721 | 1755 |
default: |
... | ... | |
1733 | 1767 |
while (again); |
1734 | 1768 |
|
1735 | 1769 |
ehci_commit_interrupt(ehci); |
1736 |
return state; |
|
1737 | 1770 |
} |
1738 | 1771 |
|
1739 | 1772 |
static void ehci_advance_async_state(EHCIState *ehci) |
1740 | 1773 |
{ |
1741 | 1774 |
EHCIqh qh; |
1742 |
int state = ehci->astate;
|
|
1775 |
int async = 1;
|
|
1743 | 1776 |
|
1744 |
switch(state) {
|
|
1777 |
switch(ehci_get_state(ehci, async)) {
|
|
1745 | 1778 |
case EST_INACTIVE: |
1746 | 1779 |
if (!(ehci->usbcmd & USBCMD_ASE)) { |
1747 | 1780 |
break; |
1748 | 1781 |
} |
1749 | 1782 |
ehci_set_usbsts(ehci, USBSTS_ASS); |
1750 |
ehci->astate = EST_ACTIVE;
|
|
1783 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1751 | 1784 |
// No break, fall through to ACTIVE |
1752 | 1785 |
|
1753 | 1786 |
case EST_ACTIVE: |
1754 | 1787 |
if ( !(ehci->usbcmd & USBCMD_ASE)) { |
1755 | 1788 |
ehci_clear_usbsts(ehci, USBSTS_ASS); |
1756 |
ehci->astate = EST_INACTIVE;
|
|
1789 |
ehci_set_state(ehci, async, EST_INACTIVE);
|
|
1757 | 1790 |
break; |
1758 | 1791 |
} |
1759 | 1792 |
|
... | ... | |
1775 | 1808 |
break; |
1776 | 1809 |
} |
1777 | 1810 |
|
1778 |
DPRINTF_ST("ASYNC: waiting for listhead, starting at %08x\n", |
|
1779 |
ehci->asynclistaddr); |
|
1780 | 1811 |
/* check that address register has been set */ |
1781 | 1812 |
if (ehci->asynclistaddr == 0) { |
1782 | 1813 |
break; |
1783 | 1814 |
} |
1784 | 1815 |
|
1785 |
state = EST_WAITLISTHEAD;
|
|
1816 |
ehci_set_state(ehci, async, EST_WAITLISTHEAD);
|
|
1786 | 1817 |
/* fall through */ |
1787 | 1818 |
|
1788 | 1819 |
case EST_FETCHENTRY: |
... | ... | |
1791 | 1822 |
case EST_EXECUTING: |
1792 | 1823 |
get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh, |
1793 | 1824 |
sizeof(EHCIqh) >> 2); |
1794 |
ehci->astate = ehci_advance_state(ehci, 1, state);
|
|
1825 |
ehci_advance_state(ehci, async);
|
|
1795 | 1826 |
break; |
1796 | 1827 |
|
1797 | 1828 |
default: |
1798 | 1829 |
/* this should only be due to a developer mistake */ |
1799 | 1830 |
fprintf(stderr, "ehci: Bad asynchronous state %d. " |
1800 | 1831 |
"Resetting to active\n", ehci->astate); |
1801 |
ehci->astate = EST_ACTIVE;
|
|
1832 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1802 | 1833 |
} |
1803 | 1834 |
} |
1804 | 1835 |
|
... | ... | |
1806 | 1837 |
{ |
1807 | 1838 |
uint32_t entry; |
1808 | 1839 |
uint32_t list; |
1840 |
int async = 0; |
|
1809 | 1841 |
|
1810 | 1842 |
// 4.6 |
1811 | 1843 |
|
1812 |
switch(ehci->pstate) {
|
|
1844 |
switch(ehci_get_state(ehci, async)) {
|
|
1813 | 1845 |
case EST_INACTIVE: |
1814 | 1846 |
if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) { |
1815 | 1847 |
ehci_set_usbsts(ehci, USBSTS_PSS); |
1816 |
ehci->pstate = EST_ACTIVE;
|
|
1848 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1817 | 1849 |
// No break, fall through to ACTIVE |
1818 | 1850 |
} else |
1819 | 1851 |
break; |
... | ... | |
1821 | 1853 |
case EST_ACTIVE: |
1822 | 1854 |
if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) { |
1823 | 1855 |
ehci_clear_usbsts(ehci, USBSTS_PSS); |
1824 |
ehci->pstate = EST_INACTIVE;
|
|
1856 |
ehci_set_state(ehci, async, EST_INACTIVE);
|
|
1825 | 1857 |
break; |
1826 | 1858 |
} |
1827 | 1859 |
|
... | ... | |
1838 | 1870 |
DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", |
1839 | 1871 |
ehci->frindex / 8, list, entry); |
1840 | 1872 |
ehci->fetch_addr = entry; |
1841 |
ehci->pstate = ehci_advance_state(ehci, 0, EST_FETCHENTRY); |
|
1873 |
ehci_set_state(ehci, async, EST_FETCHENTRY); |
|
1874 |
ehci_advance_state(ehci, async); |
|
1842 | 1875 |
break; |
1843 | 1876 |
|
1844 | 1877 |
case EST_EXECUTING: |
1845 | 1878 |
DPRINTF("PERIODIC state adv for executing\n"); |
1846 |
ehci->pstate = ehci_advance_state(ehci, 0, EST_EXECUTING);
|
|
1879 |
ehci_advance_state(ehci, async);
|
|
1847 | 1880 |
break; |
1848 | 1881 |
|
1849 | 1882 |
default: |
1850 | 1883 |
/* this should only be due to a developer mistake */ |
1851 | 1884 |
fprintf(stderr, "ehci: Bad periodic state %d. " |
1852 | 1885 |
"Resetting to active\n", ehci->pstate); |
1853 |
ehci->pstate = EST_ACTIVE;
|
|
1886 |
ehci_set_state(ehci, async, EST_ACTIVE);
|
|
1854 | 1887 |
} |
1855 | 1888 |
} |
1856 | 1889 |
|
... | ... | |
1896 | 1929 |
} else { |
1897 | 1930 |
// TODO could this cause periodic frames to get skipped if async |
1898 | 1931 |
// active? |
1899 |
if (ehci->astate != EST_EXECUTING) {
|
|
1932 |
if (ehci_get_state(ehci, 1) != EST_EXECUTING) {
|
|
1900 | 1933 |
ehci_advance_periodic_state(ehci); |
1901 | 1934 |
} |
1902 | 1935 |
} |
... | ... | |
1913 | 1946 |
/* Async is not inside loop since it executes everything it can once |
1914 | 1947 |
* called |
1915 | 1948 |
*/ |
1916 |
if (ehci->pstate != EST_EXECUTING) {
|
|
1949 |
if (ehci_get_state(ehci, 0) != EST_EXECUTING) {
|
|
1917 | 1950 |
ehci_advance_async_state(ehci); |
1918 | 1951 |
} |
1919 | 1952 |
|
Also available in: Unified diff