diff --git a/firmware/drivers/isp1583.c b/firmware/drivers/isp1583.c index 39b9d1190e..93aba17a7b 100644 --- a/firmware/drivers/isp1583.c +++ b/firmware/drivers/isp1583.c @@ -626,18 +626,17 @@ void usb_drv_cancel_all_transfers(void) endpoints[i].halt[0] = endpoints[i].halt[1] = 1; } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { - (void)max_packet_size; /* FIXME: support max packet size override */ - (void)type; - (void)endpoint; - return 0; + /* FIXME: support max packet size override */ + (void)ctx; + (void)ep; } -int usb_drv_deinit_endpoint(int endpoint) +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { - (void)endpoint; - return 0; + (void)ctx; + (void)ep; } static void bus_reset(void) diff --git a/firmware/drivers/m66591.c b/firmware/drivers/m66591.c index cf41aced31..6095e085e3 100644 --- a/firmware/drivers/m66591.c +++ b/firmware/drivers/m66591.c @@ -640,8 +640,11 @@ void usb_drv_set_test_mode(int mode) { M66591_TESTMODE |= mode; } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; /* FIXME: support max packet size override */ +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { + /* FIXME: support max packet size override */ + const int epnum = EP_NUM(ep); + const int epdir = EP_DIR(ep); + const int type = ctx->type[epnum][epdir]; int pipecfg = 0; @@ -651,41 +654,38 @@ int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { } else if(type == USB_ENDPOINT_XFER_BULK) { pipecfg |= 1<<13; } else { - /* Not a supported type */ - return -1; + panicf("mxx: unsupported type %d", type); } - int num = endpoint & USB_ENDPOINT_NUMBER_MASK; - int dir = endpoint & USB_ENDPOINT_DIR_MASK; - if (dir == USB_DIR_IN) { + if (epdir == DIR_IN) { pipecfg |= (1<<4); } - M66591_eps[num].dir = dir; + M66591_eps[epnum].dir = epdir == DIR_IN ? USB_DIR_IN : USB_DIR_OUT; - M66591_PIPE_CFGSEL=num; + M66591_PIPE_CFGSEL = epnum; /* Enable pipe (15) */ pipecfg |= 1<<15; - pipe_handshake(num, PIPE_SHAKE_NAK); + pipe_handshake(epnum, PIPE_SHAKE_NAK); /* Setup the flags */ M66591_PIPE_CFGWND=pipecfg; - pipe_init(num); + pipe_init(epnum); - logf("mxx: ep req ep#: %d config: 0x%04x", num, M66591_PIPE_CFGWND); - - return 0; + logf("mxx: ep req ep#: %d config: 0x%04x", epnum, M66591_PIPE_CFGWND); } /* Used by stack to tell the helper functions that the pipe is not in use */ -int usb_drv_deinit_endpoint(int endpoint) { - int num = endpoint & USB_ENDPOINT_NUMBER_MASK; +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { + (void)ctx; + + int num = ep & USB_ENDPOINT_NUMBER_MASK; if (num < 1 || num > USB_NUM_ENDPOINTS) { - return -1; + return; } int flags = disable_irq_save(); @@ -695,8 +695,6 @@ int usb_drv_deinit_endpoint(int endpoint) { M66591_eps[num].dir = -1; restore_irq(flags); - - return 0; } /* Periodically called to check if a cable was plugged into the device */ diff --git a/firmware/drivers/usb-designware.c b/firmware/drivers/usb-designware.c index d1274f5e41..61fa4acc70 100644 --- a/firmware/drivers/usb-designware.c +++ b/firmware/drivers/usb-designware.c @@ -24,6 +24,8 @@ #include #include +#include "usb-designware.h" + #include "config.h" #include "cpu.h" #include "system.h" @@ -35,8 +37,6 @@ #include "usb_ch9.h" #include "usb_core.h" -#include "usb-designware.h" - /* Define LOGF_ENABLE to enable logf output in this file */ /*#define LOGF_ENABLE*/ #include "logf.h" @@ -155,9 +155,6 @@ struct usb_dw_ep0 struct usb_ctrlrequest pending_req; }; -struct usb_drv_ep_spec usb_drv_ep_specs[USB_NUM_ENDPOINTS]; /* filled in usb_drv_init */ -uint8_t usb_drv_ep_specs_flags = 0; - static const char* const dw_dir_str[USB_DW_NUM_DIRS] = { [USB_DW_EPDIR_IN] = "IN", @@ -195,7 +192,6 @@ static uint32_t usb_endpoints; /* available EPs mask */ (usually 1), otherwise it is the number of dedicated Tx FIFOs (not counting NPTX FIFO that is always dedicated for IN0). */ static int n_ptxfifos; -static uint16_t ptxfifo_usage; static uint32_t hw_maxbytes; static uint32_t hw_maxpackets; @@ -672,47 +668,28 @@ static void usb_dw_unconfigure_ep(int epnum, enum usb_dw_epdir epdir) #endif ep_periodic_msk &= ~(1 << epnum); #endif - ptxfifo_usage &= ~(1 << GET_DTXFNUM(epnum)); } usb_dw_flush_endpoint(epnum, epdir); DWC_EPCTL(epnum, epdir) = epctl; } -static int usb_dw_configure_ep(int epnum, - enum usb_dw_epdir epdir, int type, int maxpktsize) +static void usb_dw_configure_ep(const struct usb_drv_ep_alloc_ctx* ctx, int epnum, enum usb_dw_epdir epdir, int type, int maxpktsize) { uint32_t epctl = SETD0PIDEF|EPTYP(type)|USBAEP|maxpktsize; - if (epdir == USB_DW_EPDIR_IN) + if (epdir == USB_DW_EPDIR_IN && ctx->assigned_txfifos[epnum] > 0) { - /* - * If the hardware has dedicated fifos, we must give each - * IN EP a unique tx-fifo even if it is non-periodic. - */ #ifdef USB_DW_SHARED_FIFO + ep_periodic_msk |= (1 << epnum); #ifndef USB_DW_ARCH_SLAVE epctl |= DWC_DIEPCTL(epnum) & NEXTEP(0xf); #endif - if (type == USB_ENDPOINT_XFER_INT) #endif - { - int fnum; - for (fnum = 1; fnum <= n_ptxfifos; fnum++) - if (~ptxfifo_usage & (1 << fnum)) - break; - if (fnum > n_ptxfifos) - return -1; /* no available fifos */ - ptxfifo_usage |= (1 << fnum); - epctl |= DTXFNUM(fnum); -#ifdef USB_DW_SHARED_FIFO - ep_periodic_msk |= (1 << epnum); -#endif - } + epctl |= DTXFNUM(ctx->assigned_txfifos[epnum]); } DWC_EPCTL(epnum, epdir) = epctl; - return 0; /* ok */ } static void usb_dw_reset_endpoints(void) @@ -753,7 +730,6 @@ static void usb_dw_reset_endpoints(void) usb_dw_unconfigure_ep(ep, USB_DW_EPDIR_IN); } - ptxfifo_usage = 0; #ifdef USB_DW_SHARED_FIFO ep_periodic_msk = 0; #endif @@ -1509,17 +1485,6 @@ static void usb_dw_init(void) /* Soft reconnect */ udelay(3000); DWC_DCTL &= ~SDIS; - - /* Fill endpoint spec table FIXME: should be done in usb_drv_startup() */ - usb_drv_ep_specs[0].type[DIR_OUT] = USB_ENDPOINT_XFER_CONTROL; - usb_drv_ep_specs[0].type[DIR_IN] = USB_ENDPOINT_XFER_CONTROL; - for(int i = 1; i < USB_NUM_ENDPOINTS; i += 1) { - bool out_avail = usb_endpoints & (1 << (i + USB_DW_DIR_OFF(USB_DW_EPDIR_OUT))); - usb_drv_ep_specs[i].type[DIR_OUT] = out_avail ? USB_ENDPOINT_TYPE_ANY : USB_ENDPOINT_TYPE_NONE; - - bool in_avail = usb_endpoints & (1 << (i + USB_DW_DIR_OFF(USB_DW_EPDIR_IN))); - usb_drv_ep_specs[i].type[DIR_IN] = in_avail ? USB_ENDPOINT_TYPE_ANY : USB_ENDPOINT_TYPE_NONE; - } } static void usb_dw_exit(void) @@ -1617,12 +1582,65 @@ void INT_USB_FUNC(void) usb_dw_irq(); } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) +void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size) { (void)max_packet_size; /* FIXME: support max packet size override */ - enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; - struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(endpoint), epdir); + const uint8_t epnum = EP_NUM(ep); + const uint8_t epdir = EP_DIR(ep); + + if(ep == EP_CONTROL) + { + return false; + } + + enum usb_dw_epdir dwdir = epdir == DIR_IN ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; + if(!(usb_endpoints & (1 << (epnum + USB_DW_DIR_OFF(dwdir))))) + { + return false; + } + + bool need_fifo = epdir == DIR_IN; +#ifdef USB_DW_SHARED_FIFO + /* in shared fifo mode, only periodic endpoints need dedicated fifo */ + need_fifo &= type == USB_ENDPOINT_XFER_ISOC || type == USB_ENDPOINT_XFER_INT; +#endif + if(!need_fifo) + { + goto ok; + } + + for (int fnum = 1; fnum <= n_ptxfifos; fnum++) + { + if (~ctx->txfifo_usage & (1 << fnum)) + { + ctx->txfifo_usage |= 1 << fnum; + ctx->assigned_txfifos[epnum] = fnum; + goto ok; + } + } + + return false; + +ok: + ctx->type[epnum][epdir] = type; + return true; +} + +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + const int epnum = EP_NUM(ep); + const int epdir_ = EP_DIR(ep); + const int type = ctx->type[epnum][epdir_]; + + enum usb_dw_epdir epdir = (epdir_ == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; + struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(ep), epdir); int maxpktsize; if(type == EPTYP_ISOCHRONOUS) @@ -1635,32 +1653,23 @@ int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) } usb_dw_target_disable_irq(); - int res = usb_dw_configure_ep(EP_NUM(endpoint), epdir, type, maxpktsize); + usb_dw_configure_ep(ctx, epnum, epdir, type, maxpktsize); usb_dw_target_enable_irq(); - if(res >= 0) - { - dw_ep->active = true; - return 0; - } - else - { - return -1; - } + dw_ep->active = true; } -int usb_drv_deinit_endpoint(int endpoint) +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { - enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; - struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(endpoint), epdir); + (void)ctx; + enum usb_dw_epdir epdir = (EP_DIR(ep) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT; + struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(ep), epdir); usb_dw_target_disable_irq(); - usb_dw_unconfigure_ep(EP_NUM(endpoint), epdir); + usb_dw_unconfigure_ep(EP_NUM(ep), epdir); usb_dw_target_enable_irq(); dw_ep->active = false; - - return 0; } int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) diff --git a/firmware/export/usb-designware.h b/firmware/export/usb-designware.h index ccef6d3d0d..424390e3b5 100644 --- a/firmware/export/usb-designware.h +++ b/firmware/export/usb-designware.h @@ -25,6 +25,7 @@ #include #include "config.h" +#include "cpu.h" #ifndef REG32_PTR_T #define REG32_PTR_T volatile uint32_t * @@ -288,4 +289,13 @@ extern void usb_dw_target_enable_irq(void); extern void usb_dw_target_disable_irq(void); extern void usb_dw_target_clear_irq(void); +/* endpoint allocation */ +struct usb_drv_ep_alloc_ctx_dw +{ + int8_t type[USB_NUM_ENDPOINTS][2]; + uint16_t txfifo_usage; + uint8_t assigned_txfifos[USB_NUM_ENDPOINTS]; +}; +#define usb_drv_ep_alloc_ctx usb_drv_ep_alloc_ctx_dw + #endif /* __USB_DESIGNWARE_H__ */ diff --git a/firmware/export/usb_drv.h b/firmware/export/usb_drv.h index 6c8949200d..405ea1d9dd 100644 --- a/firmware/export/usb_drv.h +++ b/firmware/export/usb_drv.h @@ -62,19 +62,42 @@ enum usb_control_response { USB_CONTROL_RECEIVE, }; +/* endpoint allocation: + * there are two ways to implement endpoint allocation. + * 1. define usb_drv_ep_specs and usb_drv_ep_specs_flags in the driver. + * this is the simplest option when the types accepted by each endpoint are mutually independent. + * these variables are set only once during driver initialization and should not be modified afterward. + * 2. define usb_drv_ep_alloc_ctx and implement usb_drv_ep_reset_alloc_ctx and usb_drv_ep_allocate in the driver. + * if the available endpoint types change based on allocation status, + * these functions can be overridden to allow the driver to track the endpoint state. + * */ + +#ifndef usb_drv_ep_alloc_ctx +/* option 1 */ #define USB_ENDPOINT_TYPE_ANY (-1) #define USB_ENDPOINT_TYPE_NONE (-2) - struct usb_drv_ep_spec { int8_t type[2]; /* USB_ENDPOINT_TYPE_{ANY,NONE} USB_ENDPOINT_XFER_* */ }; - extern struct usb_drv_ep_spec usb_drv_ep_specs[USB_NUM_ENDPOINTS]; #define USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH (1 << 0) #define USB_ENDPOINT_SPEC_IO_EXCLUSIVE (1 << 1) extern uint8_t usb_drv_ep_specs_flags; +struct usb_drv_ep_alloc_ctx { + int8_t type[USB_NUM_ENDPOINTS][2]; + int max_packet_size[USB_NUM_ENDPOINTS][2]; +}; +#else +/* option 2 */ +void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx); +bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size); +#endif + +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep); +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep); + /* one-time initialisation of the USB driver */ void usb_drv_startup(void); void usb_drv_int_enable(bool enable); /* Target implemented */ @@ -98,8 +121,6 @@ int usb_drv_port_speed(void); void usb_drv_cancel_all_transfers(void); void usb_drv_set_test_mode(int mode); bool usb_drv_connected(void); -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size); -int usb_drv_deinit_endpoint(int endpoint); #ifdef USB_HAS_ISOCHRONOUS /* returns the last received frame number (the 11-bit number contained in the last SOF): * - full-speed: the host sends one SOF every 1ms (so 1000 SOF/s) diff --git a/firmware/target/arm/as3525/usb-drv-as3525.c b/firmware/target/arm/as3525/usb-drv-as3525.c index 863b553164..56216bec5e 100644 --- a/firmware/target/arm/as3525/usb-drv-as3525.c +++ b/firmware/target/arm/as3525/usb-drv-as3525.c @@ -343,37 +343,37 @@ int usb_drv_port_speed(void) return (USB_DEV_STS & USB_DEV_STS_MASK_SPD) ? 0 : 1; } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + const int epnum = EP_NUM(ep); + const int epdir = EP_DIR(ep); + const int type = ctx->type[epnum][epdir]; - int i = EP_NUM(endpoint); -// int d = EP_DIR(endpoint) == DIR_IN ? 0 : 1; - - if (EP_DIR(endpoint) == DIR_IN) { - USB_IEP_CTRL(i) = USB_EP_CTRL_FLUSH | - USB_EP_CTRL_SNAK | - USB_EP_CTRL_ACT | - (type << 4); - USB_DEV_EP_INTR_MASK &= ~(1<max_pkt_length = mps << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS; + else + qh->max_pkt_length = mps << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; + + qh->dtd.next_td_ptr = QH_NEXT_TERMINATE; +} + + /* manual: 32.14.1 Device Controller Initialization */ void usb_drv_init(void) { @@ -494,8 +530,8 @@ void usb_drv_init(void) * will cause undefined behavior for the data pid tracking on the active * endpoint/direction. */ for(int ep_num=1;ep_nummax_pkt_length = max_packet_size << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS; - else - qh->max_pkt_length = max_packet_size << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; - - qh->dtd.next_td_ptr = QH_NEXT_TERMINATE; - - return 0; +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { + const int ep_num = EP_NUM(ep); + const int ep_dir = EP_DIR(ep); + init_endpoint(ep, ctx->type[ep_num][ep_dir], ctx->max_packet_size[ep_num][ep_dir]); } -int usb_drv_deinit_endpoint(int endpoint) { - int ep_num = EP_NUM(endpoint); - int ep_dir = EP_DIR(endpoint); +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) { + (void)ctx; + int ep_num = EP_NUM(ep); + int ep_dir = EP_DIR(ep); logf("ep deinit: %d %s", ep_num, XFER_DIR_STR(ep_dir)); @@ -1032,8 +1038,6 @@ int usb_drv_deinit_endpoint(int endpoint) { } else { REG_ENDPTCTRL(ep_num) &= ~EPCTRL_RX_ENABLE & ~EPCTRL_RX_TYPE; } - - return 0; } static void prepare_td(struct transfer_descriptor* td, diff --git a/firmware/target/arm/usb-s3c6400x.c b/firmware/target/arm/usb-s3c6400x.c index e570d07e31..c26072d70c 100644 --- a/firmware/target/arm/usb-s3c6400x.c +++ b/firmware/target/arm/usb-s3c6400x.c @@ -588,19 +588,23 @@ void INT_USB_FUNC(void) GINTSTS = sts; } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; /* FIXME: support max packet size override */ +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + (void)ctx; - int num = EP_NUM(endpoint); - int dir = EP_DIR(endpoint); - bool out = dir == DIR_OUT; - DEPCTL(num, out) = (DEPCTL(num, out) & ~(DEPCTL_eptype_bits << DEPCTL_eptype_bitp)) + const int epnum = EP_NUM(ep); + const int epdir = EP_DIR(ep); + const int type = ctx->type[epnum][epdir]; + + bool out = epdir == DIR_OUT; + DEPCTL(epnum, out) = (DEPCTL(epnum, out) & ~(DEPCTL_eptype_bits << DEPCTL_eptype_bitp)) | DEPCTL_setd0pid | (type << DEPCTL_eptype_bitp) | DEPCTL_usbactep; - return 0; } -int usb_drv_deinit_endpoint(int endpoint) { - return 0; +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; } void usb_drv_cancel_all_transfers() diff --git a/firmware/target/arm/usb-tcc.c b/firmware/target/arm/usb-tcc.c index ba055b4cb7..ab2260e48e 100644 --- a/firmware/target/arm/usb-tcc.c +++ b/firmware/target/arm/usb-tcc.c @@ -100,16 +100,17 @@ static struct tcc_ep tcc_endpoints[] = { static bool usb_drv_write_ep(struct tcc_ep *ep); static void usb_set_speed(int); -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; /* FIXME: support max packet size override */ - - tcc_endpoints[EP_NUM(endpoint)].dir = EP_DIR(endpoint) == DIR_IN ? USB_DIR_IN : USB_DIR_OUT; - return 0; +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + (void)ctx; + tcc_endpoints[EP_NUM(ep)].dir = EP_DIR(ep) == DIR_IN ? USB_DIR_IN : USB_DIR_OUT; } -int usb_drv_deinit_endpoint(int endpoint) { - tcc_endpoints[EP_NUM(endpoint)].dir = -1; - return 0; +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; + tcc_endpoints[EP_NUM(ep)].dir = -1; } static inline void pullup_on(void) diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c index c923d8d97a..13542b0a5d 100644 --- a/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c +++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c @@ -836,14 +836,15 @@ void usb_drv_cancel_all_transfers(void) restore_irq(flags); } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)endpoint; - (void)type; - (void)max_packet_size; /* FIXME: support max packet size override */ - return 0; +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + (void)ctx; + (void)ep; } -int usb_drv_deinit_endpoint(int endpoint) { - (void)endpoint; - return 0; +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; + (void)ep; } diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c index 1e44a0a7aa..b8f4e05070 100644 --- a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c +++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c @@ -1192,12 +1192,12 @@ void usb_drv_cancel_all_transfers(void) restore_irq(flags); } -int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { - (void)max_packet_size; /* FIXME: support max packet size override */ - (void)type; - - int num = EP_NUM(endpoint); - int dir = EP_DIR(endpoint); +void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + /* FIXME: support max packet size override */ + (void)ctx; + int num = EP_NUM(ep); + int dir = EP_DIR(ep); int index = num * 2 + (dir == DIR_OUT ? 1 : 0); endpoints[index].allocated = true; if(dir == DIR_IN) @@ -1207,9 +1207,11 @@ int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) { return 0; } -int usb_drv_deinit_endpoint(int endpoint) { - int num = EP_NUM(endpoint); - int dir = EP_DIR(endpoint); +void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) +{ + (void)ctx; + int num = EP_NUM(ep); + int dir = EP_DIR(ep); int index = num * 2 + (dir == DIR_OUT ? 1 : 0); endpoints[index].allocated = false; if(dir == DIR_IN) diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c index 8b7e4510fd..c498ee4cbf 100644 --- a/firmware/usbstack/usb_core.c +++ b/firmware/usbstack/usb_core.c @@ -28,7 +28,6 @@ #include "usb.h" #include "usb_ch9.h" -#include "usb_drv.h" #include "usb_core.h" #include "usb_class_driver.h" @@ -57,6 +56,12 @@ #include "usb_iap.h" #endif +/* include order matters, include driver header before usb_drv.h */ +#if CONFIG_USBOTG == USBOTG_DESIGNWARE +#include "usb-designware.h" +#endif +#include "usb_drv.h" + /* TODO: Move target-specific stuff somewhere else (serial number reading) */ #if defined(IPOD_ARCH) && defined(CPU_PP) @@ -179,8 +184,6 @@ static int usb_no_host_callback(struct timeout *tmo) } #endif -static int usb_core_num_interfaces[NUM_CONFIGS]; - typedef void (*completion_handler_t)(int ep, int dir, int status, int length); typedef bool (*fast_completion_handler_t)(int ep, int dir, int status, int length); typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, void* reqdata, @@ -199,7 +202,13 @@ struct ep_alloc_state { struct usb_class_driver* owner[2]; }; -static struct ep_alloc_state ep_alloc_states[NUM_CONFIGS][USB_NUM_ENDPOINTS]; +struct config_state { + struct usb_drv_ep_alloc_ctx ep_alloc_ctx; + struct ep_alloc_state ep_alloc_states[USB_NUM_ENDPOINTS]; + uint8_t num_interfaces; +}; + +struct config_state config_states[NUM_CONFIGS]; static struct usb_class_driver* drivers[USB_NUM_DRIVERS] = { @@ -407,9 +416,6 @@ void usb_core_init(void) } } - /* clear endpoint allocation state */ - memset(ep_alloc_states, 0, sizeof(ep_alloc_states)); - initialized = true; usb_state = DEFAULT; usb_config = 0; @@ -548,52 +554,86 @@ static void usb_core_set_serial_function_id(void) } /* synchronize endpoint initialization state to allocation state */ -static void init_deinit_endpoints(uint8_t conf_index, bool init) { +static void init_deinit_endpoints(int config, bool init) { for(int epnum = 0; epnum < USB_NUM_ENDPOINTS; epnum += 1) { for(int dir = 0; dir < 2; dir += 1) { - struct ep_alloc_state* alloc = &ep_alloc_states[conf_index][epnum]; - struct usb_class_driver* driver = alloc->owner[dir]; + struct config_state* cstate = &config_states[config - 1]; + struct ep_alloc_state* astate = &cstate->ep_alloc_states[epnum]; + struct usb_class_driver* driver = astate->owner[dir]; if(driver == NULL) { continue; } int ep = epnum | (dir == DIR_OUT ? USB_DIR_OUT : USB_DIR_IN); - int ret; - if(init) { - int ps = driver->get_max_packet_size ? driver->get_max_packet_size(ep) : -1; - ret = usb_drv_init_endpoint(ep, alloc->type[dir], ps); - } else { - ret = usb_drv_deinit_endpoint(ep); - } - if(ret) { - logf("usb_core: usb_drv_%s_endpoint failed ep=%d dir=%d", init ? "init" : "deinit", epnum, dir); - continue; - } if(init) { + usb_drv_ep_init(&cstate->ep_alloc_ctx, ep); ep_data[epnum].completion_handler[dir] = driver->transfer_complete; ep_data[epnum].fast_completion_handler[dir] = driver->fast_transfer_complete; ep_data[epnum].control_handler[dir] = driver->control_request; + } else { + usb_drv_ep_deinit(&cstate->ep_alloc_ctx, ep); } } } } +#ifndef usb_drv_ep_alloc_ctx +/* default endpoint allocator using usb_drv_ep_specs table */ +static void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx) { + for(int i = 0; i < USB_NUM_ENDPOINTS; i += 1) { + ctx->type[i][0] = -1; + ctx->type[i][1] = -1; + } +} + +static bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size) { + const uint8_t epnum = EP_NUM(ep); + const uint8_t epdir = EP_DIR(ep); + + struct usb_drv_ep_spec* spec = &usb_drv_ep_specs[epnum]; + + const int8_t spec_type = spec->type[epdir]; + if(spec_type != type && spec_type != USB_ENDPOINT_TYPE_ANY) { + return false; + } + + const int8_t other_type = ctx->type[epnum][!epdir]; + if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_IO_EXCLUSIVE && other_type != -1) { + /* the other side is allocated */ + return false; + } + + if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH && other_type != -1 && other_type != type) { + /* the other side is allocated with another type */ + return false; + } + + ctx->type[epnum][epdir] = type; + ctx->max_packet_size[epnum][epdir] = max_packet_size; + + return true; +} +#endif + static void allocate_interfaces_and_endpoints(void) { if(usb_config != 0) { /* deinit currently used endpoints */ - init_deinit_endpoints(usb_config - 1, false); + init_deinit_endpoints(usb_config, false); } +retry: /* reset allocations */ - memset(ep_alloc_states, 0, sizeof(ep_alloc_states)); - - int interface[NUM_CONFIGS] = {0}; + for(int i = 0; i < NUM_CONFIGS; i += 1) { + config_states[i].num_interfaces = 0; + memset(config_states[i].ep_alloc_states, 0, sizeof(config_states[i].ep_alloc_states)); + usb_drv_ep_reset_alloc_ctx(&config_states[i].ep_alloc_ctx); + } for(int i = 0; i < USB_NUM_DRIVERS; i++) { struct usb_class_driver* driver = drivers[i]; - const uint8_t conf_index = driver->config - 1; + struct config_state* cstate = &config_states[driver->config - 1]; - if(!driver->enabled) { + if(!driver->enabled || driver->error) { continue; } @@ -603,69 +643,38 @@ static void allocate_interfaces_and_endpoints(void) struct usb_class_driver_ep_allocation* req = &driver->ep_allocs[reqnum]; req->ep = 0; for(int epnum = 1; epnum < USB_NUM_ENDPOINTS; epnum += 1) { - struct usb_drv_ep_spec* spec = &usb_drv_ep_specs[epnum]; - /* ep type check */ - const int8_t spec_type = spec->type[req->dir]; - if(spec_type != req->type && spec_type != USB_ENDPOINT_TYPE_ANY) { - continue; - } /* free check */ - struct ep_alloc_state* alloc = &ep_alloc_states[conf_index][epnum]; + struct ep_alloc_state* alloc = &cstate->ep_alloc_states[epnum]; if(alloc->owner[req->dir] != NULL) { continue; } - /* this ep's requested direction is free */ - - /* another checks */ - if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_IO_EXCLUSIVE) { - /* check for the other direction type */ - if(alloc->owner[!req->dir] != NULL) { - /* the other side is allocated */ - continue; - } - } - if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH) { - /* check for other direction type */ - if(alloc->owner[!req->dir] != NULL && alloc->type[!req->dir] != req->type) { - /* the other side is allocated with another type */ - continue; - } + /* driver specific check */ + const int ep = epnum | (req->dir == DIR_OUT ? USB_DIR_OUT : USB_DIR_IN); + const int ps = driver->get_max_packet_size ? driver->get_max_packet_size(ep) : -1; + if(!usb_drv_ep_allocate(&cstate->ep_alloc_ctx, ep, req->type, ps)) { + continue; } /* all checks passed, assign it */ - const int ep = epnum | (req->dir == DIR_OUT ? USB_DIR_OUT : USB_DIR_IN); req->ep = ep; alloc->owner[req->dir] = driver; alloc->type[req->dir] = req->type; break; } if(req->ep == 0 && !req->optional) { - /* no matching ep found, disable the driver */ + /* no matching ep found, retry allocation excluding this driver */ + logf("usb_core: no endpoint allocated for driver %d", i); driver->enabled = false; - /* also revert all allocations for this driver */ - for(reqnum = reqnum - 1; reqnum >= 0; reqnum -= 1) { - const uint8_t ep = driver->ep_allocs[reqnum].ep; - const uint8_t epnum = EP_NUM(ep); - const uint8_t epdir = EP_DIR(ep); - const uint8_t dir = epdir == USB_DIR_OUT ? DIR_OUT : DIR_IN; - ep_alloc_states[conf_index][epnum].owner[dir] = NULL; - } - break; + goto retry; } } - if(!driver->enabled) { - continue; - } - /* assign interfaces */ - driver->first_interface = interface[conf_index]; - interface[conf_index] = driver->set_first_interface(interface[conf_index]); - driver->last_interface = interface[conf_index]; + driver->first_interface = cstate->num_interfaces; + cstate->num_interfaces = driver->set_first_interface(cstate->num_interfaces); + driver->last_interface = cstate->num_interfaces; } - - memcpy(usb_core_num_interfaces, interface, sizeof(interface)); } @@ -765,7 +774,7 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, v } } - config_descriptor.bNumInterfaces = usb_core_num_interfaces[index]; + config_descriptor.bNumInterfaces = config_states[index].num_interfaces; config_descriptor.bConfigurationValue = index + 1; config_descriptor.wTotalLength = (uint16_t)size; memcpy(&response_data[0], &config_descriptor, sizeof(struct usb_config_descriptor)); @@ -844,7 +853,7 @@ static int usb_core_do_set_config(uint8_t new_config) drivers[i]->disconnect(); } } - init_deinit_endpoints(usb_config - 1, false); + init_deinit_endpoints(usb_config, false); /* clear any pending transfer completions, * because they are depend on contents of ep_data */ @@ -861,7 +870,7 @@ static int usb_core_do_set_config(uint8_t new_config) /* activate new config */ if(usb_config != 0) { - init_deinit_endpoints(usb_config - 1, true); + init_deinit_endpoints(usb_config, true); for(int i = 0; i < USB_NUM_DRIVERS; i++) { if(!is_active(drivers[i])) { continue;