usb: allow more flexible endpoint allocation

the capabilities of endpoint of several devices such as dwc2 change during
runtime, so they cannot be determined during driver initialization.
therefore, allocation using ep_specs is inappropriate.

to support these devices, add functions to the driver that determine whether
endpoints are available and make allocation more flexible.

tested with ipodvideo(arc) and erosqnative(designware)

Change-Id: I8005c17f3d763cd17306bf49918e1cd8084bdeff
This commit is contained in:
mojyack 2025-12-28 15:48:53 +09:00 committed by Solomon Peachy
parent 42841d493f
commit 41caf678fe
14 changed files with 348 additions and 283 deletions

View file

@ -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)

View file

@ -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 */

View file

@ -24,6 +24,8 @@
#include <inttypes.h>
#include <string.h>
#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)

View file

@ -25,6 +25,7 @@
#include <inttypes.h>
#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__ */

View file

@ -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)

View file

@ -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<<i);
if (epdir == DIR_IN) {
USB_IEP_CTRL(epnum) = USB_EP_CTRL_FLUSH |
USB_EP_CTRL_SNAK |
USB_EP_CTRL_ACT |
(type << 4);
USB_DEV_EP_INTR_MASK &= ~(1<<epnum);
} else {
USB_OEP_CTRL(i) = USB_EP_CTRL_FLUSH |
USB_EP_CTRL_SNAK |
USB_EP_CTRL_ACT |
(type << 4);
USB_DEV_EP_INTR_MASK &= ~(1<<(16+i));
USB_OEP_CTRL(epnum) = USB_EP_CTRL_FLUSH |
USB_EP_CTRL_SNAK |
USB_EP_CTRL_ACT |
(type << 4);
USB_DEV_EP_INTR_MASK &= ~(1<<(16+epnum));
}
return 0;
}
int usb_drv_deinit_endpoint(int endpoint) {
int i = EP_NUM(endpoint);
int d = EP_DIR(endpoint) == DIR_IN ? 0 : 1;
void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
{
(void)ctx;
int i = EP_NUM(ep);
int d = EP_DIR(ep) == DIR_IN ? 0 : 1;
endpoints[i][d].state = 0;
USB_DEV_EP_INTR_MASK |= (1<<(16*d+i));
USB_EP_CTRL(i, !d) = USB_EP_CTRL_FLUSH | USB_EP_CTRL_SNAK;
return 0;
}
void usb_drv_cancel_all_transfers(void)

View file

@ -280,27 +280,30 @@ int usb_drv_port_speed(void)
return (DEV_INFO & DEV_SPEED) ? 0 : 1;
}
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);
(void)type;
const int epnum = EP_NUM(ep);
const int epdir = EP_DIR(ep);
struct endpoint_t *endp = &endpoints[num];
struct endpoint_t *endp = &endpoints[epnum];
if(EP_DIR(endpoint) == DIR_IN)
TXCON(endp) = (num << 8) | TXEPEN | TXNAK | TXACKINTEN | TXCFINTE;
if(epdir == DIR_IN)
TXCON(endp) = (epnum << 8) | TXEPEN | TXNAK | TXACKINTEN | TXCFINTE;
else
RXCON(endp) = (num << 8) | RXEPEN | RXNAK | RXACKINTEN | RXCFINTE | RXERRINTEN;
EN_INT |= 1 << (num + 7);
RXCON(endp) = (epnum << 8) | RXEPEN | RXNAK | RXACKINTEN | RXCFINTE | RXERRINTEN;
EN_INT |= 1 << (epnum + 7);
return 0;
}
int usb_drv_deinit_endpoint(int endpoint) {
int num = EP_NUM(endpoint);
// struct endpoint_t *endp = &endpoints[num];
void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
{
(void)ctx;
int num = EP_NUM(ep);
/* disable interrupt from this endpoint */
EN_INT &= ~(1 << (num + 7));

View file

@ -1489,17 +1489,21 @@ void usb_drv_set_test_mode(int mode)
tnetv_usb_reg_write(TNETV_USB_CTRL, usbCtrl.val);
}
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;
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);
int num = EP_NUM(ep);
int dir = EP_DIR(ep);
return tnetv_gadget_ep_enable(num, dir == DIR_IN);
}
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);
return tnetv_gadget_ep_disable(num, dir == DIR_IN);
}

View file

@ -451,6 +451,42 @@ void usb_drv_startup(void)
((type) == USB_ENDPOINT_XFER_INT ? "INTR" : "INVL"))))
#endif
static void init_endpoint(int ep, int type, int mps) {
const int ep_num = EP_NUM(ep);
const int ep_dir = EP_DIR(ep);
logf("ep init: %d %s %s", ep_num, XFER_DIR_STR(ep_dir), XFER_TYPE_STR(type));
struct queue_head* qh;
unsigned int ctrl = REG_ENDPTCTRL(ep_num);
if(ep_dir == DIR_IN) {
ctrl &= ~EPCTRL_TX_TYPE;
ctrl |= EPCTRL_TX_DATA_TOGGLE_RST | EPCTRL_TX_ENABLE | type << EPCTRL_TX_EP_TYPE_SHIFT;
qh = &qh_array[ep_num * 2 + 1];
} else {
ctrl &= ~EPCTRL_RX_TYPE;
ctrl |= EPCTRL_RX_DATA_TOGGLE_RST | EPCTRL_RX_ENABLE | type << EPCTRL_RX_EP_TYPE_SHIFT;
qh = &qh_array[ep_num * 2];
}
REG_ENDPTCTRL(ep_num) = ctrl;
if(mps == -1) {
if(type == USB_ENDPOINT_XFER_ISOC) {
mps = 1024;
} else {
mps = usb_drv_port_speed() ? 512 : 64;
}
}
if(type == USB_ENDPOINT_XFER_ISOC)
/* FIXME: we can adjust the number of packets per frame, currently use one */
qh->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_num<USB_NUM_ENDPOINTS;ep_num++) {
usb_drv_init_endpoint(ep_num | USB_DIR_IN, USB_ENDPOINT_XFER_BULK, -1);
usb_drv_init_endpoint(ep_num | USB_DIR_OUT, USB_ENDPOINT_XFER_BULK, -1);
init_endpoint(ep_num | USB_DIR_IN, USB_ENDPOINT_XFER_BULK, -1);
init_endpoint(ep_num | USB_DIR_OUT, USB_ENDPOINT_XFER_BULK, -1);
}
}
@ -984,46 +1020,16 @@ void usb_drv_cancel_all_transfers(void)
}
}
int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
int ep_num = EP_NUM(endpoint);
int ep_dir = EP_DIR(endpoint);
logf("ep init: %d %s %s", ep_num, XFER_DIR_STR(ep_dir), XFER_TYPE_STR(type));
struct queue_head* qh;
unsigned int ctrl = REG_ENDPTCTRL(ep_num);
if(ep_dir == DIR_IN) {
ctrl &= ~EPCTRL_TX_TYPE;
ctrl |= EPCTRL_TX_DATA_TOGGLE_RST | EPCTRL_TX_ENABLE | type << EPCTRL_TX_EP_TYPE_SHIFT;
qh = &qh_array[ep_num * 2 + 1];
} else {
ctrl &= ~EPCTRL_RX_TYPE;
ctrl |= EPCTRL_RX_DATA_TOGGLE_RST | EPCTRL_RX_ENABLE | type << EPCTRL_RX_EP_TYPE_SHIFT;
qh = &qh_array[ep_num * 2];
}
REG_ENDPTCTRL(ep_num) = ctrl;
if(max_packet_size == -1) {
if(type == USB_ENDPOINT_XFER_ISOC) {
max_packet_size = 1024;
} else {
max_packet_size = usb_drv_port_speed() ? 512 : 64;
}
}
if(type == USB_ENDPOINT_XFER_ISOC)
/* FIXME: we can adjust the number of packets per frame, currently use one */
qh->max_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,

View file

@ -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()

View file

@ -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)

View file

@ -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;
}

View file

@ -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)

View file

@ -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;