Revision e654887f hw/usb-ehci.c
b/hw/usb-ehci.c | ||
---|---|---|
198 | 198 |
#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff |
199 | 199 |
#define ITD_BUFPTR_MAXPKT_SH 0 |
200 | 200 |
#define ITD_BUFPTR_MULT_MASK 0x00000003 |
201 |
#define ITD_BUFPTR_MULT_SH 0 |
|
201 | 202 |
} EHCIitd; |
202 | 203 |
|
203 | 204 |
/* EHCI spec version 1.0 Section 3.4 |
... | ... | |
628 | 629 |
|
629 | 630 |
static void ehci_trace_itd(EHCIState *s, target_phys_addr_t addr, EHCIitd *itd) |
630 | 631 |
{ |
631 |
trace_usb_ehci_itd(addr, itd->next); |
|
632 |
trace_usb_ehci_itd(addr, itd->next, |
|
633 |
get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT), |
|
634 |
get_field(itd->bufptr[2], ITD_BUFPTR_MULT), |
|
635 |
get_field(itd->bufptr[0], ITD_BUFPTR_EP), |
|
636 |
get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR)); |
|
632 | 637 |
} |
633 | 638 |
|
634 | 639 |
/* queue management */ |
... | ... | |
1270 | 1275 |
USBPort *port; |
1271 | 1276 |
USBDevice *dev; |
1272 | 1277 |
int ret; |
1273 |
int i, j; |
|
1274 |
int ptr; |
|
1275 |
int pid; |
|
1276 |
int pg; |
|
1277 |
int len; |
|
1278 |
int dir; |
|
1279 |
int devadr; |
|
1280 |
int endp; |
|
1278 |
uint32_t i, j, len, len1, len2, pid, dir, devaddr, endp; |
|
1279 |
uint32_t pg, off, ptr1, ptr2, max, mult; |
|
1281 | 1280 |
|
1282 | 1281 |
dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); |
1283 |
devadr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR); |
|
1282 |
devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
|
|
1284 | 1283 |
endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP); |
1285 |
/* maxpkt = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT); */ |
|
1284 |
max = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT); |
|
1285 |
mult = get_field(itd->bufptr[2], ITD_BUFPTR_MULT); |
|
1286 | 1286 |
|
1287 | 1287 |
for(i = 0; i < 8; i++) { |
1288 | 1288 |
if (itd->transact[i] & ITD_XACT_ACTIVE) { |
1289 |
DPRINTF("ISOCHRONOUS active for frame %d, interval %d\n", |
|
1290 |
ehci->frindex >> 3, i); |
|
1291 |
|
|
1292 |
pg = get_field(itd->transact[i], ITD_XACT_PGSEL); |
|
1293 |
ptr = (itd->bufptr[pg] & ITD_BUFPTR_MASK) | |
|
1294 |
(itd->transact[i] & ITD_XACT_OFFSET_MASK); |
|
1295 |
len = get_field(itd->transact[i], ITD_XACT_LENGTH); |
|
1289 |
pg = get_field(itd->transact[i], ITD_XACT_PGSEL); |
|
1290 |
off = itd->transact[i] & ITD_XACT_OFFSET_MASK; |
|
1291 |
ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); |
|
1292 |
ptr2 = (itd->bufptr[pg+1] & ITD_BUFPTR_MASK); |
|
1293 |
len = get_field(itd->transact[i], ITD_XACT_LENGTH); |
|
1294 |
|
|
1295 |
if (len > max * mult) { |
|
1296 |
len = max * mult; |
|
1297 |
} |
|
1296 | 1298 |
|
1297 | 1299 |
if (len > BUFF_SIZE) { |
1298 | 1300 |
return USB_RET_PROCERR; |
1299 | 1301 |
} |
1300 | 1302 |
|
1301 |
DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len); |
|
1303 |
if (off + len > 4096) { |
|
1304 |
/* transfer crosses page border */ |
|
1305 |
len2 = off + len - 4096; |
|
1306 |
len1 = len - len2; |
|
1307 |
} else { |
|
1308 |
len1 = len; |
|
1309 |
len2 = 0; |
|
1310 |
} |
|
1302 | 1311 |
|
1303 | 1312 |
if (!dir) { |
1304 |
cpu_physical_memory_rw(ptr, &ehci->ibuffer[0], len, 0); |
|
1305 | 1313 |
pid = USB_TOKEN_OUT; |
1306 |
} else |
|
1314 |
trace_usb_ehci_data(0, pg, off, ptr1 + off, len1, 0); |
|
1315 |
cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 0); |
|
1316 |
if (len2) { |
|
1317 |
trace_usb_ehci_data(0, pg+1, 0, ptr2, len2, len1); |
|
1318 |
cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 0); |
|
1319 |
} |
|
1320 |
} else { |
|
1307 | 1321 |
pid = USB_TOKEN_IN; |
1322 |
} |
|
1308 | 1323 |
|
1309 | 1324 |
ret = USB_RET_NODEV; |
1310 | 1325 |
|
... | ... | |
1315 | 1330 |
// TODO sometime we will also need to check if we are the port owner |
1316 | 1331 |
|
1317 | 1332 |
if (!(ehci->portsc[j] &(PORTSC_CONNECT))) { |
1318 |
DPRINTF("Port %d, no exec, not connected(%08X)\n", |
|
1319 |
j, ehci->portsc[j]); |
|
1320 | 1333 |
continue; |
1321 | 1334 |
} |
1322 | 1335 |
|
1323 | 1336 |
ehci->ipacket.pid = pid; |
1324 |
ehci->ipacket.devaddr = devadr; |
|
1337 |
ehci->ipacket.devaddr = devaddr;
|
|
1325 | 1338 |
ehci->ipacket.devep = endp; |
1326 | 1339 |
ehci->ipacket.data = ehci->ibuffer; |
1327 | 1340 |
ehci->ipacket.len = len; |
1328 | 1341 |
|
1329 |
DPRINTF("calling usb_handle_packet\n"); |
|
1330 | 1342 |
ret = usb_handle_packet(dev, &ehci->ipacket); |
1331 | 1343 |
|
1332 | 1344 |
if (ret != USB_RET_NODEV) { |
... | ... | |
1334 | 1346 |
} |
1335 | 1347 |
} |
1336 | 1348 |
|
1349 |
#if 0 |
|
1337 | 1350 |
/* In isoch, there is no facility to indicate a NAK so let's |
1338 | 1351 |
* instead just complete a zero-byte transaction. Setting |
1339 | 1352 |
* DBERR seems too draconian. |
... | ... | |
1358 | 1371 |
DPRINTF("ISOCH: received ACK, clearing pause\n"); |
1359 | 1372 |
ehci->isoch_pause = -1; |
1360 | 1373 |
} |
1374 |
#else |
|
1375 |
if (ret == USB_RET_NAK) { |
|
1376 |
ret = 0; |
|
1377 |
} |
|
1378 |
#endif |
|
1361 | 1379 |
|
1362 | 1380 |
if (ret >= 0) { |
1363 |
itd->transact[i] &= ~ITD_XACT_ACTIVE; |
|
1381 |
if (!dir) { |
|
1382 |
/* OUT */ |
|
1383 |
set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH); |
|
1384 |
} else { |
|
1385 |
/* IN */ |
|
1386 |
if (len1 > ret) { |
|
1387 |
len1 = ret; |
|
1388 |
} |
|
1389 |
if (len2 > ret - len1) { |
|
1390 |
len2 = ret - len1; |
|
1391 |
} |
|
1392 |
if (len1) { |
|
1393 |
trace_usb_ehci_data(1, pg, off, ptr1 + off, len1, 0); |
|
1394 |
cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 1); |
|
1395 |
} |
|
1396 |
if (len2) { |
|
1397 |
trace_usb_ehci_data(1, pg+1, 0, ptr2, len2, len1); |
|
1398 |
cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 1); |
|
1399 |
} |
|
1400 |
set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); |
|
1401 |
} |
|
1364 | 1402 |
|
1365 | 1403 |
if (itd->transact[i] & ITD_XACT_IOC) { |
1366 | 1404 |
ehci_record_interrupt(ehci, USBSTS_INT); |
1367 | 1405 |
} |
1368 | 1406 |
} |
1369 |
|
|
1370 |
if (ret >= 0 && dir) { |
|
1371 |
cpu_physical_memory_rw(ptr, &ehci->ibuffer[0], len, 1); |
|
1372 |
|
|
1373 |
if (ret != len) { |
|
1374 |
DPRINTF("ISOCH IN expected %d, got %d\n", |
|
1375 |
len, ret); |
|
1376 |
set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); |
|
1377 |
} |
|
1378 |
} |
|
1407 |
itd->transact[i] &= ~ITD_XACT_ACTIVE; |
|
1379 | 1408 |
} |
1380 | 1409 |
} |
1381 | 1410 |
return 0; |
Also available in: Unified diff