usb: implement endpoint allocation

this commit has following changes:
- introduce `usb_drv_ep_spec` table to udc drivers, which represents
  endpoint characteristics.
- introduce 'ep_allocs' table to class drivers, which represents what
  endpoint type does the class driver want.
- implement endpoint matching logic to usb core.

this is a required step to implement usb config switching, because we
need to create config descriptors without actually initializing endpoints.

Change-Id: I11c324cd35189ab636744488f6259d0cdb2179f0
This commit is contained in:
mojyack 2025-12-17 11:13:43 +09:00 committed by Solomon Peachy
parent f13f80c506
commit 350a2250b1
27 changed files with 579 additions and 714 deletions

View file

@ -29,6 +29,9 @@
#include "logf.h"
#include "stdio.h"
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;
struct usb_endpoint
{
unsigned char *out_buf;
@ -48,7 +51,6 @@ struct usb_endpoint
unsigned char enabled[2];
short max_pkt_size[2];
short type;
char allocation;
};
static unsigned char setup_pkt_buf[8];
@ -470,6 +472,14 @@ void usb_drv_init(void)
//tick_add_task(usb_helper);
/* 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) {
usb_drv_ep_specs[i].type[DIR_OUT] = USB_ENDPOINT_XFER_BULK;
usb_drv_ep_specs[i].type[DIR_IN] = USB_ENDPOINT_XFER_BULK;
}
logf("usb_init_device() finished");
}
@ -616,29 +626,15 @@ void usb_drv_cancel_all_transfers(void)
endpoints[i].halt[0] = endpoints[i].halt[1] = 1;
}
int usb_drv_request_endpoint(int type, int dir)
int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size)
{
int i, bit;
if (type != USB_ENDPOINT_XFER_BULK)
return -1;
bit=(dir & USB_DIR_IN)? 2:1;
for (i=1; i < USB_NUM_ENDPOINTS; i++) {
if((endpoints[i].allocation & bit)!=0)
continue;
endpoints[i].allocation |= bit;
return i | dir;
}
return -1;
(void)max_packet_size; /* FIXME: support max packet size override */
return 0;
}
void usb_drv_release_endpoint(int ep)
int usb_drv_deinit_endpoint(int endpoint)
{
int mask = (ep & USB_DIR_IN)? ~2:~1;
endpoints[ep & 0x7f].allocation &= mask;
return 0;
}
static void bus_reset(void)

View file

@ -59,6 +59,9 @@
* stack visible functions.
******************************************************************************/
struct usb_drv_ep_spec usb_drv_ep_specs[USB_NUM_ENDPOINTS]; /* filled in usb_drv_init */
uint8_t usb_drv_ep_specs_flags = USB_ENDPOINT_SPEC_IO_EXCLUSIVE;
static volatile unsigned short * pipe_ctrl_addr(int pipe);
static void pipe_handshake(int pipe, int handshake);
static void pipe_c_select (int pipe, bool dir);
@ -78,7 +81,6 @@ struct M66591_epstat {
int length; /* how match data will fit */
volatile int count; /* actual data count */
bool waiting; /* is there data to transfer? */
bool busy; /* has the pipe been requested for use? */
} ;
static struct M66591_epstat M66591_eps[USB_NUM_ENDPOINTS];
@ -638,82 +640,63 @@ void usb_drv_set_test_mode(int mode) {
M66591_TESTMODE |= mode;
}
/* Request an unused endpoint */
int usb_drv_request_endpoint(int type, int dir) {
int ep;
int pipecfg = 0;
int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
(void)max_packet_size; /* FIXME: support max packet size override */
if (type == USB_ENDPOINT_XFER_BULK) {
int pipecfg;
if(type == USB_ENDPOINT_XFER_BULK) {
/* Enable double buffer mode (only used for ep 1 and 2) */
pipecfg |= 1<<9 | 1<<8;
/* Bulk endpoints must be between 1 and 4 inclusive */
ep=1;
while(M66591_eps[ep].busy && ep++<5);
/* If this reached 5 the endpoints were all busy */
if(ep==5) {
logf("mxx: ep %d busy", ep);
return -1;
}
} else if (type == USB_ENDPOINT_XFER_INT) {
ep=5;
} else if(type == USB_ENDPOINT_XFER_BULK) {
pipecfg |= 1<<13;
while(M66591_eps[ep].busy && ++ep<USB_NUM_ENDPOINTS);
/* If this reached USB_NUM_ENDPOINTS the endpoints were all busy */
if(ep==USB_NUM_ENDPOINTS) {
logf("mxx: ep %d busy", ep);
return -1;
}
} else {
/* Not a supported type */
return -1;
}
int num = endpoint & USB_ENDPOINT_NUMBER_MASK;
int dir = endpoint & USB_ENDPOINT_DIR_MASK;
if (dir == USB_DIR_IN) {
pipecfg |= (1<<4);
}
M66591_eps[num].dir = dir;
M66591_eps[ep].busy = true;
M66591_eps[ep].dir = dir;
M66591_PIPE_CFGSEL=ep;
M66591_PIPE_CFGSEL=num;
/* Enable pipe (15) */
pipecfg |= 1<<15;
pipe_handshake(ep, PIPE_SHAKE_NAK);
pipe_handshake(num, PIPE_SHAKE_NAK);
/* Setup the flags */
M66591_PIPE_CFGWND=pipecfg;
pipe_init(ep);
pipe_init(num);
logf("mxx: ep req ep#: %d config: 0x%04x", ep, M66591_PIPE_CFGWND);
logf("mxx: ep req ep#: %d config: 0x%04x", num, M66591_PIPE_CFGWND);
return ep | dir;
return 0;
}
/* Used by stack to tell the helper functions that the pipe is not in use */
void usb_drv_release_endpoint(int ep) {
int flags;
ep &= 0x7f;
int usb_drv_deinit_endpoint(int endpoint) {
int num = endpoint & USB_ENDPOINT_NUMBER_MASK;
if (ep < 1 || ep > USB_NUM_ENDPOINTS || M66591_eps[ep].busy == false)
return ;
if (num < 1 || num > USB_NUM_ENDPOINTS) {
return -1;
}
flags = disable_irq_save();
int flags = disable_irq_save();
logf("mxx: ep %d release", ep);
logf("mxx: ep %d release", num);
M66591_eps[ep].busy = false;
M66591_eps[ep].dir = -1;
M66591_eps[num].dir = -1;
restore_irq(flags);
return 0;
}
/* Periodically called to check if a cable was plugged into the device */
@ -744,6 +727,18 @@ void usb_drv_init(void) {
M66591_TRN_CTRL |=0x0001;
M66591_INTCFG_MAIN |=0x8000; /* Enable VBUS interrupt */
/* 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 < 5; i += 1) {
usb_drv_ep_specs[i].type[DIR_OUT] = USB_ENDPOINT_XFER_BULK;
usb_drv_ep_specs[i].type[DIR_IN] = USB_ENDPOINT_XFER_BULK;
}
for(int i = 5; i < USB_NUM_ENDPOINTS; i += 1) {
usb_drv_ep_specs[i].type[DIR_OUT] = USB_ENDPOINT_XFER_INT;
usb_drv_ep_specs[i].type[DIR_IN] = USB_ENDPOINT_XFER_INT;
}
}
/* fully enable driver */
@ -757,7 +752,6 @@ void usb_attach(void) {
M66591_eps[i].length = 0;
M66591_eps[i].count = 0;
M66591_eps[i].waiting = false;
M66591_eps[i].busy = false;
}
/* Issue a h/w reset */

View file

@ -144,6 +144,9 @@ 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",
@ -1495,6 +1498,17 @@ 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)
@ -1592,56 +1606,50 @@ void INT_USB_FUNC(void)
usb_dw_irq();
}
int usb_drv_request_endpoint(int type, int dir)
int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size)
{
int request_ep = -1;
enum usb_dw_epdir epdir = (EP_DIR(dir) == DIR_IN) ?
USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
(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);
int maxpktsize;
if(type == EPTYP_ISOCHRONOUS)
{
maxpktsize = 1023;
}
else
{
maxpktsize = usb_drv_port_speed() ? 512 : 64;
}
usb_dw_target_disable_irq();
for (int ep = 1; ep < USB_NUM_ENDPOINTS; ep++)
{
if (usb_endpoints & (1 << (ep + USB_DW_DIR_OFF(epdir))))
{
struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, epdir);
if (!dw_ep->active)
{
int maxpktsize = 64;
if (type == EPTYP_ISOCHRONOUS){
maxpktsize = 1023;
} else {
maxpktsize = usb_drv_port_speed() ? 512 : 64;
}
if (usb_dw_configure_ep(ep, epdir, type,
maxpktsize) >= 0)
{
dw_ep->active = true;
request_ep = ep | dir;
}
break;
}
}
}
int res = usb_dw_configure_ep(EP_NUM(endpoint), epdir, type, maxpktsize);
usb_dw_target_enable_irq();
return request_ep;
if(res >= 0)
{
dw_ep->active = true;
return 0;
}
else
{
return -1;
}
}
void usb_drv_release_endpoint(int endpoint)
int usb_drv_deinit_endpoint(int endpoint)
{
int epnum = EP_NUM(endpoint);
if (!epnum) return;
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(epnum, epdir);
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);
usb_dw_target_disable_irq();
if (dw_ep->active)
{
usb_dw_unconfigure_ep(epnum, epdir);
dw_ep->active = false;
}
usb_dw_unconfigure_ep(EP_NUM(endpoint), epdir);
usb_dw_target_enable_irq();
dw_ep->active = false;
return 0;
}
int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)