Skip to content

Commit

Permalink
USB isochronous transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
Cacodemon345 committed Dec 12, 2024
1 parent 4dc844f commit 62c4891
Showing 2 changed files with 225 additions and 11 deletions.
228 changes: 223 additions & 5 deletions src/usb/usb_ohci_bochs.c
Original file line number Diff line number Diff line change
@@ -118,16 +118,24 @@ struct OHCI_TD {
#define ISO_TD_GET_PSW6(x) ((x)->dword7 & 0x0000FFFF)
#define ISO_TD_GET_PSW7(x) (((x)->dword7 & 0xFFFF0000) >> 16)

#pragma pack(push, 2)
struct OHCI_ISO_TD {
uint32_t dword0;
uint32_t dword1;
uint32_t dword2;
uint32_t dword3;
uint32_t dword4;
uint32_t dword5;
uint32_t dword6;
uint32_t dword7;
union
{
struct {
uint32_t dword4;
uint32_t dword5;
uint32_t dword6;
uint32_t dword7;
};
uint16_t offset[8];
};
};
#pragma pack(pop)


typedef struct {
@@ -1249,6 +1257,211 @@ int usb_ohci_broadcast_packet(bx_ohci_core_t* hub, USBPacket *p)
return ret;
}

/* Read/Write the contents of an ISO TD from/to main memory. */
static int ohci_copy_iso_td(uint32_t start_addr, uint32_t end_addr,
uint8_t *buf, int len, bool read)
{
uint32_t ptr, n;

ptr = start_addr;
n = 0x1000 - (ptr & 0xfff);
if (n > len) {
n = len;
}
if (read)
dma_bm_read(ptr, buf, n, 4);
else
dma_bm_write(ptr, buf, n, 4);

if (n == len) {
return 0;
}
ptr = end_addr & ~0xfffu;
buf += n;
if (read)
dma_bm_read(ptr, buf, len - n, 4);
else
dma_bm_write(ptr, buf, len - n, 4);

return 0;
}


#define OHCI_PAGE_MASK 0xfffff000
#define OHCI_OFFSET_MASK 0xfff
int usb_ohci_process_iso_td(bx_ohci_core_t* hub, struct OHCI_ISO_TD *td, struct OHCI_ED *ed)
{
unsigned pid = 0, len = 0;
int ret2 = 1;
uint8_t buf[8192];
int ilen, ret = 0;
uint32_t addr;
USBAsync *p;
bool completion;
uint16_t starting_frame;
int16_t relative_frame_number;
int frame_count;
uint32_t start_offset, next_offset, end_offset = 0;
uint32_t start_addr, end_addr;

addr = ED_GET_HEADP(ed);

if (addr == 0)
return 1;

p = find_async_packet(&hub->packets, addr);
completion = (p != NULL);
if (completion && !p->done) {
return 0;
}

starting_frame = ISO_TD_GET_SF(td);
frame_count = ISO_TD_GET_FC(td);
relative_frame_number = (int16_t)((uint16_t)(hub->op_regs.HcFmNumber) - (uint16_t)(starting_frame));

if (relative_frame_number < 0)
return 1;
else if (relative_frame_number > frame_count) {
const uint32_t temp = ED_GET_HEADP(ed);
if (ISO_TD_GET_CC(td) == DataOverrun)
return 1;
else {
TD_SET_CC(td, DataOverrun);
ED_SET_HEADP(ed, ISO_TD_GET_NEXTTD(td));
TD_SET_NEXTTD(td, hub->op_regs.HcDoneHead);
hub->op_regs.HcDoneHead = temp;
if (ISO_TD_GET_DI(td) < hub->ohci_done_count)
hub->ohci_done_count = ISO_TD_GET_DI(td);
return 0;
}
}

if (ED_GET_D(ed) == 1)
pid = USB_TOKEN_OUT;
else if (ED_GET_D(ed) == 2)
pid = USB_TOKEN_IN;
else if (ED_GET_D(ed) == 0)
pid = USB_TOKEN_SETUP;
else
return 1;

if (ISO_TD_GET_BE(td) == 0 || ISO_TD_GET_BP0(td) == 0)
return 1;

start_offset = td->offset[relative_frame_number];
if (relative_frame_number < frame_count) {
next_offset = td->offset[relative_frame_number + 1];
} else {
next_offset = ISO_TD_GET_BE(td);
}

if (!((start_offset >> 12) & 0xe) ||
((relative_frame_number < frame_count) &&
!((start_offset >> 12) & 0xe))) {
return 1;
}

if ((relative_frame_number < frame_count) && (start_offset > next_offset)) {
return 1;
}

if ((start_offset & 0x1000) == 0) {
start_addr = (td->dword1 & OHCI_PAGE_MASK) |
(start_offset & OHCI_OFFSET_MASK);
} else {
start_addr = (td->dword2 & OHCI_PAGE_MASK) |
(start_offset & OHCI_OFFSET_MASK);
}

if (relative_frame_number < frame_count) {
end_offset = next_offset - 1;
if ((end_offset & 0x1000) == 0) {
end_addr = (td->dword1 & OHCI_PAGE_MASK) |
(end_offset & OHCI_OFFSET_MASK);
} else {
end_addr = (td->dword2 & OHCI_PAGE_MASK) |
(end_offset & OHCI_OFFSET_MASK);
}
} else {
/* Last packet in the ISO TD */
end_addr = next_offset;
}

if (start_addr > end_addr) {
return 1;
}

if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) {
len = (end_addr & OHCI_OFFSET_MASK) + 0x1001
- (start_addr & OHCI_OFFSET_MASK);
} else {
len = end_addr - start_addr + 1;
}

if (len > sizeof(buf)) {
len = sizeof(buf);
}

if (len && pid != USB_TOKEN_IN) {
ohci_copy_iso_td(start_addr, end_addr, buf, len, true);
}

if (completion) {
ret = p->packet.len;
} else {
p = create_async_packet(&hub->packets, addr, len);
if (pid != USB_TOKEN_IN)
memcpy(p->packet.data, buf, len);
p->packet.pid = pid;
p->packet.devaddr = ED_GET_FA(ed);
p->packet.devep = ED_GET_EN(ed);
p->packet.speed = ED_GET_S(ed) ? USB_SPEED_LOW : USB_SPEED_FULL;
#if HANDLE_TOGGLE_CONTROL
p->packet.toggle = ED_GET_C(ed) ^ (relative_frame_number & 1);
#endif
p->packet.complete_cb = usb_ohci_event_handler;
p->packet.complete_dev = hub;
ret = usb_ohci_broadcast_packet(hub, &p->packet);

if (ret == USB_RET_ASYNC)
return 1;

if (ret >= 0 && pid == USB_TOKEN_SETUP)
ret = len = 8;
}


if (pid == USB_TOKEN_IN && ret >= 0 && ret <= len) {
ohci_copy_iso_td(start_addr, end_addr, buf, len, false);

td->offset[relative_frame_number] = (NoError << 12) | (len & 0xfff);
} else if (pid == USB_TOKEN_OUT && ret == len) {
td->offset[relative_frame_number] = (NoError << 12);
} else {
if (ret > len)
td->offset[relative_frame_number] = (DataOverrun << 12) | (len & 0xfff);
else if (ret >= 0) {
td->offset[relative_frame_number] &= 0xFFF;
td->offset[relative_frame_number] |= (DataUnderrun << 12);
} else {
td->offset[relative_frame_number] = (ret == USB_RET_IOERROR || ret == USB_RET_NODEV) ? DeviceNotResponding : Stall;
}
}

if (relative_frame_number == frame_count) {
const uint32_t temp = ED_GET_HEADP(ed);
TD_SET_CC(td, NoError);
ED_SET_HEADP(ed, ISO_TD_GET_NEXTTD(td));
TD_SET_NEXTTD(td, hub->op_regs.HcDoneHead);
hub->op_regs.HcDoneHead = temp;
if (ISO_TD_GET_DI(td) < hub->ohci_done_count)
hub->ohci_done_count = ISO_TD_GET_DI(td);
}

dma_bm_write(addr, (uint8_t*)td, sizeof(struct OHCI_ISO_TD), 4);
return 1;
}

int usb_ohci_process_td(bx_ohci_core_t* hub, struct OHCI_TD *td, struct OHCI_ED *ed, int toggle)
{
unsigned pid = 0, len = 0, len1, len2;
@@ -1434,6 +1647,7 @@ int usb_ohci_process_td(bx_ohci_core_t* hub, struct OHCI_TD *td, struct OHCI_ED
bool usb_ohci_process_ed(bx_ohci_core_t* hub, struct OHCI_ED *ed, const uint32_t ed_address)
{
struct OHCI_TD cur_td;
struct OHCI_ISO_TD cur_iso_td;
int toggle;
bool ret = 0;

@@ -1443,7 +1657,11 @@ bool usb_ohci_process_ed(bx_ohci_core_t* hub, struct OHCI_ED *ed, const uint32_t
if (hub->op_regs.HcControl.ie) {
// load and do a isochronous TD list
BX_DEBUG(("Found a valid ED that points to an isochronous TD"));
// we currently ignore ISO TD's
while (!ED_GET_H(ed) && (ED_GET_HEADP(ed) != ED_GET_TAILP(ed))) {
dma_bm_read(ED_GET_HEADP(ed), (uint8_t*)&cur_iso_td, sizeof(struct OHCI_ISO_TD), 4);
if (usb_ohci_process_iso_td(hub, &cur_iso_td, ed))
break;
}
}
} else {
BX_DEBUG(("Found a valid ED that points to an control/bulk/int TD"));
8 changes: 2 additions & 6 deletions src/usb/usb_uhci_bochs.c
Original file line number Diff line number Diff line change
@@ -648,11 +648,6 @@ usb_uhci_do_transfer(bx_uhci_core_t *hub, uint32_t address, struct TD *td)
(td->dword1 & (0x7F << 16)) >> 16));
}

// we don't support ISO transfers, so if the IOS bit is set, give an error
if (td->dword1 & (1 << 25)) {
BX_ERROR(("UHCI Core: ISO bit is set..."));
}

// the reserved bit in the Link Pointer should be zero
if (td->dword0 & (1 << 3)) {
BX_INFO(("UHCI Core: Reserved bit in the Link Pointer is not zero."));
@@ -722,7 +717,8 @@ usb_uhci_do_transfer(bx_uhci_core_t *hub, uint32_t address, struct TD *td)
set_status(td, 0, 0, 0, 0, 0, 0, len - 1);
} else if (ret == USB_RET_NAK) {
set_status(td, 0, 0, 0, 1, 0, 0, len - 1); // NAK
td->dword1 |= (1 << 23);
if (!(td->dword1 & (1 << 25)))
td->dword1 |= (1 << 23);
} else {
set_status(td, 1, 0, 0, 0, 0, 0, 0x007); // stalled
}

0 comments on commit 62c4891

Please sign in to comment.