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 |
|