📄 usb.c
字号:
* In:
* addr - endpoint address
* type - endpoint type (control, bulk, interrupt, iso). This is the value
* of the endpoint type filed of the endpoint descriptor.
* ep - number of endpoint
* psize - maximum packet size allowed for this endpoint.
* db - nonzer of endpoint shall be double buffered. Note: only iso and
* bulk endpoints can be double buffered (this is hardware
* specific).
* Out:
* 0 - all ok
* !0 - initialisation failed
*
* Description:
* Configures the spcified endpoint.
*****************************************************************************/
static hcc_u8 usb_setup_ep(hcc_u8 addr, hcc_u8 type, hcc_u8 ep, hcc_u16 psize)
{
hcc_u8 endpt;
/* Disable endpoint. */
disable_ep_tx(ep);
disable_ep_rx(ep);
if (ep_info[ep].state!=EPST_IDLE)
{
ep_info[ep].error=USBEPERR_HOST_ABORT;
}
else
{
ep_info[ep].error=USBEPERR_NONE;
}
ep_info[ep].state=EPST_IDLE;
ep_info[ep].flags=0;
ep_info[ep].data_func=(void*)0;
ep_info[ep].address=0;
ep_info[ep].tlength=0;
ep_info[ep].maxlength=0;
ep_info[ep].psize=psize;
ep_info[ep].data0_tx=0;
ep_info[ep].data0_rx=0;
if (type == EP_TYPE_DISABLE)
{
return(0);
}
endpt =0;
/* if RX side of the endpoint needs to be configured */
if (!(addr & DIR_TX) || type == EP_TYPE_CONTROL)
{
endpt |=MCF_USB_ENDPT_EP_RX_EN;
/* Set BDT_CTL to default value. Set packet size, buffer is owned by the CPU,
enable data toggle synchronisation, expect data0 packet. Note: data0/1
will be set when the buffer is given to the USB. */
WR_LE32(&BDT_CTL_RX(ep, 0), (hcc_u32)((psize << 16) | BDT_CTL_DTS));
/* Set BDT_CTL to default value. */
WR_LE32(&BDT_CTL_RX(ep, 1), (hcc_u32)((psize << 16) | BDT_CTL_DTS));
/* Set RX buffer address. */
WR_LE32(&BDT_ADR_RX(ep, 0), (hcc_u32)get_ep_rx_buffer(ep, 0));
WR_LE32(&BDT_ADR_RX(ep, 1), (hcc_u32)get_ep_rx_buffer(ep, 1));
}
if ((addr & DIR_TX) || type == EP_TYPE_CONTROL)
{
endpt |= MCF_USB_ENDPT_EP_TX_EN;
/* Set BDT_CTL to default value. Set buffer owned by the CPU. Value of other
fileds is not important. Those will get a correct value when the buffer is
made ready for transmission. */
WR_LE32(&BDT_CTL_TX(ep, 0), 0);
/* Set BDT_CTL to default value. */
WR_LE32(&BDT_CTL_TX(ep, 1), 0);
/* Set TX buffer address. */
}
if (type != EP_TYPE_ISO)
{ /* Non ISO endpoints need handhaking. */
endpt |= MCF_USB_ENDPT_EP_HSHK;
}
else
{ /* ISO endpoints do not need handshaking, and retry. */
endpt |= MCF_USB_ENDPT_RETRY_DIS;
}
/* If a non control endpoint uses both directions (e.g. two endpoint is
implemented in the same hardware slot), then CTL_DIS shall be set. */
if (type != EP_TYPE_CONTROL)
{
endpt |= MCF_USB_ENDPT_EP_CTL_DIS;
}
else
{ /* Reception on control endpoints shall be enabled by default. */
/* Set maximum packet size. */
WR_LE32(&BDT_CTL_RX(ep, ep_info[ep].next_rx), (hcc_u32)ep_info[ep].psize<<16u);
ready_ep_rx(ep, ep_info[ep].next_rx);
}
MCF_USB_ENDPT(ep)=endpt;
return(0);
}
/*****************************************************************************
* Name:
* set_config
* In:
* cfg_ndx - index of the configuration to be activated. The value shall
* shall equal to one defined in a configuration descriptor.
* Out:
* N/A
*
* Description:
* Configures the USB module according to the specifyed configuration.
* Assumptions:
* the spefified configuration exists.
* the first interface descriptor is for the default alternate setting.
* interfaces must be numbered from 0 increasing continously (0,1,2,3...)
* configurations must be numbered from 0 increasing continously
*****************************************************************************/
static void set_config(hcc_u8 cfg_ndx)
{
hcc_u8 cfg_ep=0;
usb_current_config=cfg_ndx;
/* All endpoint shall use buffer 0 now. */
if (cfg_ndx != 0)
{
/* For all interfaces in this configuration. */
hcc_u8 ifc=0;
while(is_ifc_ndx(cfg_ndx, ifc, 0))
{ /* Emdpoint descriptor index in configuration descriptor. */
hcc_u8 ifc_ep=0;
while(is_ep_ndx(cfg_ndx, ifc, 0, ifc_ep))
{
const hcc_u8 *epd=get_ep_descriptor(cfg_ndx, ifc, 0, ifc_ep);
/* Endpoint index and address is the same. */
usb_setup_ep(epd[2], epd[3], (hcc_u8)(epd[2] & 0x7fu), RD_LE16(&epd[4]));
ifc_ep++;
cfg_ep++;
}
ifc++;
}
usb_state = USBST_CONFIGURED;
}
else
{
usb_state=USBST_ADDRESSED;
/* No endpoints to configure. The loop below will disable all except 0. */
}
cfg_ep++;
while(cfg_ep < 16)
{
usb_setup_ep(0, EP_TYPE_DISABLE, cfg_ep++, 0);
}
}
/*****************************************************************************
* Name:
* enter_default_state
* In:
* N/A
* Out:
* N/A
*
* Description:
* Configure USB driver to reflect the default state. In this state only
* standard requests on the default pipe are answered, all other endpoints
* are disabled.
*****************************************************************************/
static void enter_default_state(void)
{
int ep;
/* In this state the USB module responds to the default address.
Only EP0 is configured. */
MCF_USB_CTL |= MCF_USB_CTL_ODD_RST;
for(ep=0; ep<sizeof(ep_info)/sizeof(ep_info[0]); ep++)
{
ep_info[ep].next_rx=0;
ep_info[ep].next_tx=0;
}
MCF_USB_CTL &= ~MCF_USB_CTL_ODD_RST;
/* Configure ep 0. */
usb_setup_ep(0, EP_TYPE_CONTROL, 0, EP0_PACKET_SIZE);
/* Disable all other endpoints. */
set_config(0);
/* Set address to default address. */
MCF_USB_ADDR = 0;
usb_state=USBST_DEFAULT;
new_address=0;
}
/*****************************************************************************
* Name:
* usb_stop_ep_tx
* In:
* ep - endpoint number
* Out:
* N/A
* Description:
* Will stall a tx endpoint (endpoint will not transmit any more packages,
* all IN request from the host will be denyed with error handsake).
*****************************************************************************/
void usb_stop_ep_tx(hcc_u8 ep)
{
/* This cal needs to be protected againt USB interrupts, to
make BDT_CTL assecc atomic. */
hcc_imask im=_irq_disable();
/* To enforce stall handshake, we stop both buffers. */
WR_LE32(&BDT_CTL_TX(ep, 0), BDT_CTL_OWN | BDT_CTL_STALL);
WR_LE32(&BDT_CTL_TX(ep, 1), BDT_CTL_OWN | BDT_CTL_STALL);
_irq_restore(im);
}
/*****************************************************************************
* Name:
* usb_stop_ep_rx
* In:
* ep - endpoint number
* Out:
* N/A
* Description:
* Will stall a rx endpoint (endpoint will not treceive any more packages,
* all OUT request from the host will be denyed with error handsake).
*****************************************************************************/
void usb_stop_ep_rx(hcc_u8 ep)
{
/* This cal needs to be protected againt USB interrupts, to
make BDT_CTL assecc atomic. */
hcc_imask im=_irq_disable();
/* To enforce stall handshake, we stop both buffers. */
WR_LE32(&BDT_CTL_RX(ep, 0), BDT_CTL_OWN | BDT_CTL_STALL);
WR_LE32(&BDT_CTL_RX(ep, 1), BDT_CTL_OWN | BDT_CTL_STALL);
_irq_restore(im);
}
/*****************************************************************************
* Name:
* ready_ep_tx
* In:
* ep - endpoint number
* Out:
* N/A
* Description:
* Make tx endpoint ready for transmission.
*****************************************************************************/
static void ready_ep_tx(hcc_u8 ep, hcc_u8 buf)
{ /* Simply give buffer to USB. */
hcc_u32 ctl;
/* Give buffer to USB, set correct data0/1 flag, configure if data toggle
synchronisation shall be used or not. */
ctl=RD_LE32(&BDT_CTL_TX(ep, buf)) & ~0xff;
ctl |= BDT_CTL_OWN | ep_info[ep].data0_tx | BDT_CTL_DTS;
WR_LE32(&BDT_CTL_TX(ep, buf), ctl);
}
/*****************************************************************************
* Name:
* ready_ep_rx
* In:
* ep - endpoint number
* Out:
* N/A
* Description:
* Make rx endpoint ready for reception.
*****************************************************************************/
static void ready_ep_rx(hcc_u8 ep, hcc_u8 buf)
{ /* Simply give buffer to USB. */
hcc_u32 ctl;
/* Give buffer to USB, set correct data0/1 flag, configure if data toggle
synchronisation shall be used or not. */
ctl = (ep_info[ep].psize << 16) | BDT_CTL_OWN | ep_info[ep].data0_rx | BDT_CTL_DTS;
ep_info[ep].data0_rx ^= BDT_CTL_DATA;
WR_LE32(&BDT_CTL_RX(ep, buf), ctl);
}
/*****************************************************************************
* Name:
* disable_ep_tx
* In:
* ep - endpoint number
* Out:
* N/A
* Description:
* Disable TX endpoint. Endpoint behaves as it would not exist (it will not
* affect the USB and will not generate any events).
*****************************************************************************/
static void disable_ep_tx(hcc_u8 ep)
{
MCF_USB_ENDPT(ep) &= ~MCF_USB_ENDPT_EP_TX_EN;
while(MCF_USB_ENDPT(ep) & MCF_USB_ENDPT_EP_TX_EN)
;
WR_LE32(&BDT_CTL_TX(ep, 0), 0);
WR_LE32(&BDT_CTL_TX(ep, 1), 0);
}
/*****************************************************************************
* Name:
* disable_ep_rx
* In:
* ep - endpoint number
* Out:
* N/A
* Description:
* Disable RX endpoint. Endpoint behaves as it would not exist (it will not
* affect the USB and will not generate any events).
*****************************************************************************/
static void disable_ep_rx(hcc_u8 ep)
{
MCF_USB_ENDPT(ep) &= ~MCF_USB_ENDPT_EP_RX_EN;
}
/*****************************************************************************
* Name:
* _usb_send
* In:
* ep - endpoint number
* Out:
* N/A
* Description:
* This fucntion inmplements the basic state machine for transmit (IN)
* endpoints. It will
* - call user callback functions if neccessary,
* - set endpoint specific error codes
* - split data to be sent to packet sized pieces
* Note: it is called from the interrupt handler routine and from "user
* space" too. The function needs to be reentrant!
*****************************************************************************/
static void _usb_send(hcc_u8 ep)
{
hcc_u32 l;
hcc_u8 buf;
hcc_u32 length;
switch (ep_info[ep].state)
{
case EPST_DATA_TX:
l=MIN(ep_info[ep].tlength, ep_info[ep].psize);
CMX_ASSERT(l == ep_info[ep].psize || l==ep_info[ep].tlength);
/* Select next ep buffer. */
buf=select_tx_buf(ep);
/* Set-up UBS module to directly send from applications buffer. */
WR_LE32(&BDT_ADR_TX(ep, buf), (hcc_u32)ep_info[ep].address);
/* Set tx packet length. */
length=MIN(ep_info[ep].psize, ep_info[ep].tlength);
WR_LE32(&BDT_CTL_TX(ep, buf), (hcc_u32)(length<<16u));
/* Make buffer ready for transmission. */
ready_ep_tx(ep, buf);
/* Invert the data toggle bit. */
ep_info[ep].data0_tx ^= BDT_CTL_DATA;
/* Calculate transfer status. */
ep_info[ep].tlength -= l;
ep_info[ep].address = (hcc_u8*)ep_info[ep].address + l;
/* Is the just created packet the last one? */
if (ep_info[ep].tlength == 0)
{ /* Do we need to send a zero length packet to terminate the transmission? */
if ((l == ep_info[ep].psize) && (ep_info[ep].flags & EPFL_ZPACKET))
{
ep_info[ep].state=EPST_TX_STOP;
}
else
{
ep_info[ep].state=EPST_DATA_TX_LAST;
}
/* Warning: when getting there, an additional interrupt is needed to advance the
state succesfully. */
break;
}
break;
case EPST_DATA_TX_WAIT_DB:
/* Let second buffer get empty while waiting for a new buffer. */
ep_info[ep].state=EPST_DATA_TX_EMPTY_DB;
break;
case EPST_TX_STOP:
/* Send a sort packet. */
ep_info[ep].state=EPST_DATA_TX_LAST;
send_zero_packet(ep);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -