📄 usb-musb.c
字号:
}static inline void musb_cb_tick1(void *opaque){ struct musb_ep_s *ep = (struct musb_ep_s *) opaque; ep->delayed_cb[1](&ep->packey[1], opaque);}#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0)static inline void musb_schedule_cb(USBPacket *packey, void *opaque, int dir){ struct musb_ep_s *ep = (struct musb_ep_s *) opaque; int timeout = 0; if (ep->status[dir] == USB_RET_NAK) timeout = ep->timeout[dir]; else if (ep->interrupt[dir]) timeout = 8; else return musb_cb_tick(opaque); if (!ep->intv_timer[dir]) ep->intv_timer[dir] = qemu_new_timer(vm_clock, musb_cb_tick, opaque); qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock(vm_clock) + muldiv64(timeout, ticks_per_sec, 8000));}static void musb_schedule0_cb(USBPacket *packey, void *opaque){ return musb_schedule_cb(packey, opaque, 0);}static void musb_schedule1_cb(USBPacket *packey, void *opaque){ return musb_schedule_cb(packey, opaque, 1);}static int musb_timeout(int ttype, int speed, int val){#if 1 return val << 3;#endif switch (ttype) { case USB_ENDPOINT_XFER_CONTROL: if (val < 2) return 0; else if (speed == USB_SPEED_HIGH) return 1 << (val - 1); else return 8 << (val - 1); case USB_ENDPOINT_XFER_INT: if (speed == USB_SPEED_HIGH) if (val < 2) return 0; else return 1 << (val - 1); else return val << 3; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_ISOC: if (val < 2) return 0; else if (speed == USB_SPEED_HIGH) return 1 << (val - 1); else return 8 << (val - 1); /* TODO: what with low-speed Bulk and Isochronous? */ } cpu_abort(cpu_single_env, "bad interval\n");}static inline void musb_packet(struct musb_s *s, struct musb_ep_s *ep, int epnum, int pid, int len, USBCallback cb, int dir){ int ret; int idx = epnum && dir; int ttype; /* ep->type[0,1] contains: * in bits 7:6 the speed (0 - invalid, 1 - high, 2 - full, 3 - slow) * in bits 5:4 the transfer type (BULK / INT) * in bits 3:0 the EP num */ ttype = epnum ? (ep->type[idx] >> 4) & 3 : 0; ep->timeout[dir] = musb_timeout(ttype, ep->type[idx] >> 6, ep->interval[idx]); ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; ep->delayed_cb[dir] = cb; cb = dir ? musb_schedule1_cb : musb_schedule0_cb; ep->packey[dir].pid = pid; /* A wild guess on the FADDR semantics... */ ep->packey[dir].devaddr = ep->faddr[idx]; ep->packey[dir].devep = ep->type[idx] & 0xf; ep->packey[dir].data = (void *) ep->buf[idx]; ep->packey[dir].len = len; ep->packey[dir].complete_cb = cb; ep->packey[dir].complete_opaque = ep; if (s->port.dev) ret = s->port.dev->handle_packet(s->port.dev, &ep->packey[dir]); else ret = USB_RET_NODEV; if (ret == USB_RET_ASYNC) { ep->status[dir] = len; return; } ep->status[dir] = ret; usb_packet_complete(&ep->packey[dir]);}static void musb_tx_packet_complete(USBPacket *packey, void *opaque){ /* Unfortunately we can't use packey->devep because that's the remote * endpoint number and may be different than our local. */ struct musb_ep_s *ep = (struct musb_ep_s *) opaque; int epnum = ep->epnum; struct musb_s *s = ep->musb; ep->fifostart[0] = 0; ep->fifolen[0] = 0;#ifdef CLEAR_NAK if (ep->status[0] != USB_RET_NAK) {#endif if (epnum) ep->csr[0] &= ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY); else ep->csr[0] &= ~MGC_M_CSR0_TXPKTRDY;#ifdef CLEAR_NAK }#endif /* Clear all of the error bits first */ if (epnum) ep->csr[0] &= ~(MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_H_RXSTALL | MGC_M_TXCSR_H_NAKTIMEOUT); else ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); if (ep->status[0] == USB_RET_STALL) { /* Command not supported by target! */ ep->status[0] = 0; if (epnum) ep->csr[0] |= MGC_M_TXCSR_H_RXSTALL; else ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; } if (ep->status[0] == USB_RET_NAK) { ep->status[0] = 0; /* NAK timeouts are only generated in Bulk transfers and * Data-errors in Isochronous. */ if (ep->interrupt[0]) { return; } if (epnum) ep->csr[0] |= MGC_M_TXCSR_H_NAKTIMEOUT; else ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; } if (ep->status[0] < 0) { if (ep->status[0] == USB_RET_BABBLE) musb_intr_set(s, musb_irq_rst_babble, 1); /* Pretend we've tried three times already and failed (in * case of USB_TOKEN_SETUP). */ if (epnum) ep->csr[0] |= MGC_M_TXCSR_H_ERROR; else ep->csr[0] |= MGC_M_CSR0_H_ERROR; musb_tx_intr_set(s, epnum, 1); return; } /* TODO: check len for over/underruns of an OUT packet? */#ifdef SETUPLEN_HACK if (!epnum && ep->packey[0].pid == USB_TOKEN_SETUP) s->setup_len = ep->packey[0].data[6];#endif /* In DMA mode: if no error, assert DMA request for this EP, * and skip the interrupt. */ musb_tx_intr_set(s, epnum, 1);}static void musb_rx_packet_complete(USBPacket *packey, void *opaque){ /* Unfortunately we can't use packey->devep because that's the remote * endpoint number and may be different than our local. */ struct musb_ep_s *ep = (struct musb_ep_s *) opaque; int epnum = ep->epnum; struct musb_s *s = ep->musb; ep->fifostart[1] = 0; ep->fifolen[1] = 0;#ifdef CLEAR_NAK if (ep->status[1] != USB_RET_NAK) {#endif ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; if (!epnum) ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT;#ifdef CLEAR_NAK }#endif /* Clear all of the imaginable error bits first */ ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR); if (!epnum) ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); if (ep->status[1] == USB_RET_STALL) { ep->status[1] = 0; packey->len = 0; ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; if (!epnum) ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; } if (ep->status[1] == USB_RET_NAK) { ep->status[1] = 0; /* NAK timeouts are only generated in Bulk transfers and * Data-errors in Isochronous. */ if (ep->interrupt[1]) return musb_packet(s, ep, epnum, USB_TOKEN_IN, packey->len, musb_rx_packet_complete, 1); ep->csr[1] |= MGC_M_RXCSR_DATAERROR; if (!epnum) ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; } if (ep->status[1] < 0) { if (ep->status[1] == USB_RET_BABBLE) { musb_intr_set(s, musb_irq_rst_babble, 1); return; } /* Pretend we've tried three times already and failed (in * case of a control transfer). */ ep->csr[1] |= MGC_M_RXCSR_H_ERROR; if (!epnum) ep->csr[0] |= MGC_M_CSR0_H_ERROR; musb_rx_intr_set(s, epnum, 1); return; } /* TODO: check len for over/underruns of an OUT packet? */ /* TODO: perhaps make use of e->ext_size[1] here. */ packey->len = ep->status[1]; if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; if (!epnum) ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; ep->rxcount = packey->len; /* XXX: MIN(packey->len, ep->maxp[1]); */ /* In DMA mode: assert DMA request for this EP */ } /* Only if DMA has not been asserted */ musb_rx_intr_set(s, epnum, 1);}static void musb_tx_rdy(struct musb_s *s, int epnum){ struct musb_ep_s *ep = s->ep + epnum; int pid; int total, valid = 0; ep->fifostart[0] += ep->fifolen[0]; ep->fifolen[0] = 0; /* XXX: how's the total size of the packet retrieved exactly in * the generic case? */ total = ep->maxp[0] & 0x3ff; if (ep->ext_size[0]) { total = ep->ext_size[0]; ep->ext_size[0] = 0; valid = 1; } /* If the packet is not fully ready yet, wait for a next segment. */ if (epnum && (ep->fifostart[0] << 2) < total) return; if (!valid) total = ep->fifostart[0] << 2; pid = USB_TOKEN_OUT; if (!epnum && (ep->csr[0] & MGC_M_CSR0_H_SETUPPKT)) { pid = USB_TOKEN_SETUP; if (total != 8) printf("%s: illegal SETUPPKT length of %i bytes\n", __FUNCTION__, total); /* Controller should retry SETUP packets three times on errors * but it doesn't make sense for us to do that. */ } return musb_packet(s, ep, epnum, pid, total, musb_tx_packet_complete, 0);}static void musb_rx_req(struct musb_s *s, int epnum){ struct musb_ep_s *ep = s->ep + epnum; int total; /* If we already have a packet, which didn't fit into the * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ if (ep->packey[1].pid == USB_TOKEN_IN && ep->status[1] >= 0 && (ep->fifostart[1] << 2) + ep->rxcount < ep->packey[1].len) { ep->fifostart[1] += ep->rxcount >> 2; ep->fifolen[1] = 0; ep->rxcount = MIN(ep->packey[0].len - (ep->fifostart[1] << 2), ep->maxp[1]); ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; if (!epnum) ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT; /* Clear all of the error bits first */ ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR); if (!epnum) ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; if (!epnum) ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; musb_rx_intr_set(s, epnum, 1); return; } /* The driver sets maxp[1] to 64 or less because it knows the hardware * FIFO is this deep. Bigger packets get split in * usb_generic_handle_packet but we can also do the splitting locally * for performance. It turns out we can also have a bigger FIFO and * ignore the limit set in ep->maxp[1]. The Linux MUSB driver deals * OK with single packets of even 32KB and we avoid splitting, however * usb_msd.c sometimes sends a packet bigger than what Linux expects * (e.g. 8192 bytes instead of 4096) and we get an OVERRUN. Splitting * hides this overrun from Linux. Up to 4096 everything is fine * though. Currently this is disabled. * * XXX: mind ep->fifosize. */ total = MIN(ep->maxp[1] & 0x3ff, sizeof(s->buf));#ifdef SETUPLEN_HACK /* Why should *we* do that instead of Linux? */ if (!epnum) { if (ep->packey[0].devaddr == 2) total = MIN(s->setup_len, 8); else total = MIN(s->setup_len, 64); s->setup_len -= total; }#endif return musb_packet(s, ep, epnum, USB_TOKEN_IN, total, musb_rx_packet_complete, 1);}static void musb_ep_frame_cancel(struct musb_ep_s *ep, int dir){ if (ep->intv_timer[dir]) qemu_del_timer(ep->intv_timer[dir]);}/* Bus control */static uint8_t musb_busctl_readb(void *opaque, int ep, int addr){ struct musb_s *s = (struct musb_s *) opaque; switch (addr) { /* For USB2.0 HS hubs only */ case MUSB_HDRC_TXHUBADDR: return s->ep[ep].haddr[0]; case MUSB_HDRC_TXHUBPORT: return s->ep[ep].hport[0]; case MUSB_HDRC_RXHUBADDR: return s->ep[ep].haddr[1]; case MUSB_HDRC_RXHUBPORT: return s->ep[ep].hport[1]; default: printf("%s: unknown register at %02x\n", __FUNCTION__, addr); return 0x00; };}static void musb_busctl_writeb(void *opaque, int ep, int addr, uint8_t value){ struct musb_s *s = (struct musb_s *) opaque; switch (addr) { case MUSB_HDRC_TXHUBADDR: s->ep[ep].haddr[0] = value; break; case MUSB_HDRC_TXHUBPORT: s->ep[ep].hport[0] = value; break; case MUSB_HDRC_RXHUBADDR: s->ep[ep].haddr[1] = value; break; case MUSB_HDRC_RXHUBPORT: s->ep[ep].hport[1] = value; break; default: printf("%s: unknown register at %02x\n", __FUNCTION__, addr); };}static uint16_t musb_busctl_readh(void *opaque, int ep, int addr){ struct musb_s *s = (struct musb_s *) opaque; switch (addr) { case MUSB_HDRC_TXFUNCADDR: return s->ep[ep].faddr[0]; case MUSB_HDRC_RXFUNCADDR: return s->ep[ep].faddr[1]; default: return musb_busctl_readb(s, ep, addr) | (musb_busctl_readb(s, ep, addr | 1) << 8); };}static void musb_busctl_writeh(void *opaque, int ep, int addr, uint16_t value){ struct musb_s *s = (struct musb_s *) opaque; switch (addr) { case MUSB_HDRC_TXFUNCADDR: s->ep[ep].faddr[0] = value; break; case MUSB_HDRC_RXFUNCADDR: s->ep[ep].faddr[1] = value; break; default: musb_busctl_writeb(s, ep, addr, value & 0xff); musb_busctl_writeb(s, ep, addr | 1, value >> 8); };}/* Endpoint control */static uint8_t musb_ep_readb(void *opaque, int ep, int addr){ struct musb_s *s = (struct musb_s *) opaque; switch (addr) { case MUSB_HDRC_TXTYPE: return s->ep[ep].type[0]; case MUSB_HDRC_TXINTERVAL: return s->ep[ep].interval[0]; case MUSB_HDRC_RXTYPE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -