usb/ohci: Don't use packet from OHCIState for isochronous transfers

Since isochronous transfers cannot be handled async (the function
returns error in that case) we don't need to remember the packet.
Avoid using the usb_packet field in OHCIState (as that can be a
waiting async packet on another endpoint) and allocate and use a local
USBPacket for the iso transfer instead. After this we don't have to
care if we're called from a completion callback or not so we can drop
that parameter as well.

Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
Message-Id: <bf523d40f8088a84383cb00ffd2e6e82fa47790d.1643117600.git.balaton@eik.bme.hu>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
BALATON Zoltan 2022-01-25 14:33:20 +01:00 committed by Gerd Hoffmann
parent b6b0c066f5
commit 3a4d06f26f

View file

@ -548,8 +548,7 @@ static int ohci_copy_iso_td(OHCIState *ohci,
#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
int completion)
static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed)
{
int dir;
size_t len = 0;
@ -559,6 +558,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
int i;
USBDevice *dev;
USBEndpoint *ep;
USBPacket *pkt;
uint8_t buf[8192];
bool int_req;
struct ohci_iso_td iso_td;
uint32_t addr;
uint16_t starting_frame;
@ -693,40 +695,42 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
} else {
len = end_addr - start_addr + 1;
}
if (len > sizeof(ohci->usb_buf)) {
len = sizeof(ohci->usb_buf);
if (len > sizeof(buf)) {
len = sizeof(buf);
}
if (len && dir != OHCI_TD_DIR_IN) {
if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len,
if (ohci_copy_iso_td(ohci, start_addr, end_addr, buf, len,
DMA_DIRECTION_TO_DEVICE)) {
ohci_die(ohci);
return 1;
}
}
if (!completion) {
bool int_req = relative_frame_number == frame_count &&
OHCI_BM(iso_td.flags, TD_DI) == 0;
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
if (dev == NULL) {
trace_usb_ohci_td_dev_error();
return 1;
}
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
usb_handle_packet(dev, &ohci->usb_packet);
if (ohci->usb_packet.status == USB_RET_ASYNC) {
usb_device_flush_ep_queue(dev, ep);
return 1;
}
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
if (dev == NULL) {
trace_usb_ohci_td_dev_error();
return 1;
}
if (ohci->usb_packet.status == USB_RET_SUCCESS) {
ret = ohci->usb_packet.actual_length;
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
pkt = g_new0(USBPacket, 1);
usb_packet_init(pkt);
int_req = relative_frame_number == frame_count &&
OHCI_BM(iso_td.flags, TD_DI) == 0;
usb_packet_setup(pkt, pid, ep, 0, addr, false, int_req);
usb_packet_addbuf(pkt, buf, len);
usb_handle_packet(dev, pkt);
if (pkt->status == USB_RET_ASYNC) {
usb_device_flush_ep_queue(dev, ep);
g_free(pkt);
return 1;
}
if (pkt->status == USB_RET_SUCCESS) {
ret = pkt->actual_length;
} else {
ret = ohci->usb_packet.status;
ret = pkt->status;
}
g_free(pkt);
trace_usb_ohci_iso_td_so(start_offset, end_offset, start_addr, end_addr,
str, len, ret);
@ -734,7 +738,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
/* Writeback */
if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
/* IN transfer succeeded */
if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret,
if (ohci_copy_iso_td(ohci, start_addr, end_addr, buf, ret,
DMA_DIRECTION_FROM_DEVICE)) {
ohci_die(ohci);
return 1;
@ -1057,7 +1061,7 @@ exit_no_retire:
}
/* Service an endpoint list. Returns nonzero if active TD were found. */
static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
{
struct ohci_ed ed;
uint32_t next_ed;
@ -1108,8 +1112,9 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
break;
} else {
/* Handle isochronous endpoints */
if (ohci_service_iso_td(ohci, &ed, completion))
if (ohci_service_iso_td(ohci, &ed)) {
break;
}
}
}
@ -1136,20 +1141,20 @@ static void ohci_sof(OHCIState *ohci)
}
/* Process Control and Bulk lists. */
static void ohci_process_lists(OHCIState *ohci, int completion)
static void ohci_process_lists(OHCIState *ohci)
{
if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) {
trace_usb_ohci_process_lists(ohci->ctrl_head, ohci->ctrl_cur);
}
if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
if (!ohci_service_ed_list(ohci, ohci->ctrl_head)) {
ohci->ctrl_cur = 0;
ohci->status &= ~OHCI_STATUS_CLF;
}
}
if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) {
if (!ohci_service_ed_list(ohci, ohci->bulk_head)) {
ohci->bulk_cur = 0;
ohci->status &= ~OHCI_STATUS_BLF;
}
@ -1173,7 +1178,7 @@ static void ohci_frame_boundary(void *opaque)
int n;
n = ohci->frame_number & 0x1f;
ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]));
}
/* Cancel all pending packets if either of the lists has been disabled. */
@ -1181,7 +1186,7 @@ static void ohci_frame_boundary(void *opaque)
ohci_stop_endpoints(ohci);
}
ohci->old_ctl = ohci->ctl;
ohci_process_lists(ohci, 0);
ohci_process_lists(ohci);
/* Stop if UnrecoverableError happened or ohci_sof will crash */
if (ohci->intr_status & OHCI_INTR_UE) {
@ -1794,7 +1799,7 @@ static void ohci_async_complete_packet(USBPort *port, USBPacket *packet)
trace_usb_ohci_async_complete();
ohci->async_complete = true;
ohci_process_lists(ohci, 1);
ohci_process_lists(ohci);
}
static USBPortOps ohci_port_ops = {