aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple/usb/usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmaple/usb/usb.c')
-rwxr-xr-xlibmaple/usb/usb.c334
1 files changed, 244 insertions, 90 deletions
diff --git a/libmaple/usb/usb.c b/libmaple/usb/usb.c
index 4f31c33..29d07d0 100755
--- a/libmaple/usb/usb.c
+++ b/libmaple/usb/usb.c
@@ -25,28 +25,31 @@
*****************************************************************************/
/**
- * @brief usb-specific hardware setup, NVIC, clocks, and usb activities
- * in the pre-attached state. includes some of the lower level callbacks
- * needed by the usb library, like suspend,resume,init,etc
+ * @file usb.c
+ * @brief USB support.
*/
#include "usb.h"
+
#include "libmaple.h"
-#include "usb_lib.h"
#include "gpio.h"
#include "delay.h"
#include "nvic.h"
#include "rcc.h"
+#include "usb_reg_map.h"
#include "usb_config.h"
#include "usb_callbacks.h"
-#include "usb_lib.h"
-/* persistent usb structs */
+#include "usb_type.h"
+#include "usb_core.h"
-volatile uint32 bDeviceState = UNCONNECTED;
-volatile uint16 wIstr = 0;
-volatile uint32 bIntPackSOF = 0;
+static void usb_init(void);
+static void dispatch_ctr_lp(void);
+
+/*
+ * Globals required by usb_lib/
+ */
DEVICE Device_Table =
{NUM_ENDPTS,
@@ -95,20 +98,39 @@ void (*pEpInt_OUT[7])(void) =
NOP_Process,
NOP_Process};
+volatile uint16 wIstr = 0;
+uint8 EPindex; /* current endpoint */
+DEVICE_INFO *pInformation;
+DEVICE_PROP *pProperty;
+uint16 SaveTState; /* caches TX status for later use */
+uint16 SaveRState; /* caches RX status for later use */
+uint16 wInterrupt_Mask;
+DEVICE_INFO Device_Info;
+USER_STANDARD_REQUESTS *pUser_Standard_Requests;
+
+/*
+ * Other state
+ */
+
+volatile uint32 bDeviceState = UNCONNECTED;
+volatile uint32 bIntPackSOF = 0;
+
struct {
volatile RESUME_STATE eState;
volatile uint8 bESOFcnt;
} ResumeS;
+/*
+ * Routines
+ */
+
void usb_cdcacm_enable(gpio_dev *disc_dev, uint8 disc_bit) {
+ /* Present ourselves to the host */
gpio_set_mode(disc_dev, disc_bit, GPIO_OUTPUT_PP);
-
- /* setup the apb1 clock for USB */
- RCC_BASE->APB1ENR |= 0x00800000;
-
- /* initialize the usb application */
gpio_write_bit(disc_dev, disc_bit, 0); // presents us to the host
- USB_Init(); // low level init routine provided by the ST library
+
+ /* initialize USB peripheral */
+ usb_init();
}
void usb_cdcacm_disable(gpio_dev *disc_dev, uint8 disc_bit) {
@@ -119,31 +141,32 @@ void usb_cdcacm_disable(gpio_dev *disc_dev, uint8 disc_bit) {
}
void usbSuspend(void) {
- u16 wCNTR;
- wCNTR = _GetCNTR();
- wCNTR |= CNTR_FSUSP | CNTR_LPMODE;
- _SetCNTR(wCNTR);
+ uint16 cntr;
+
+ /* TODO decide if read/modify/write is really what we want
+ * (e.g. usbResumeInit() reconfigures CNTR). */
+ cntr = USB_BASE->CNTR;
+ cntr |= USB_CNTR_FSUSP;
+ USB_BASE->CNTR = cntr;
+ cntr |= USB_CNTR_LP_MODE;
+ USB_BASE->CNTR = cntr;
- /* run any power reduction handlers */
bDeviceState = SUSPENDED;
}
void usbResumeInit(void) {
- u16 wCNTR;
-
- /* restart any clocks that had been stopped */
-
- wCNTR = _GetCNTR();
- wCNTR &= (~CNTR_LPMODE);
- _SetCNTR(wCNTR);
+ uint16 cntr;
- /* undo power reduction handlers here */
- _SetCNTR(ISR_MSK);
+ cntr = USB_BASE->CNTR;
+ cntr &= ~USB_CNTR_LP_MODE;
+ USB_BASE->CNTR = cntr;
+ /* Enable interrupt lines */
+ USB_BASE->CNTR = ISR_MSK;
}
void usbResume(RESUME_STATE eResumeSetVal) {
- u16 wCNTR;
+ uint16 cntr;
if (eResumeSetVal != RESUME_ESOF)
ResumeS.eState = eResumeSetVal;
@@ -168,18 +191,18 @@ void usbResume(RESUME_STATE eResumeSetVal) {
ResumeS.eState = RESUME_START;
break;
case RESUME_START:
- wCNTR = _GetCNTR();
- wCNTR |= CNTR_RESUME;
- _SetCNTR(wCNTR);
+ cntr = USB_BASE->CNTR;
+ cntr |= USB_CNTR_RESUME;
+ USB_BASE->CNTR = cntr;
ResumeS.eState = RESUME_ON;
ResumeS.bESOFcnt = 10;
break;
case RESUME_ON:
ResumeS.bESOFcnt--;
if (ResumeS.bESOFcnt == 0) {
- wCNTR = _GetCNTR();
- wCNTR &= (~CNTR_RESUME);
- _SetCNTR(wCNTR);
+ cntr = USB_BASE->CNTR;
+ cntr &= ~USB_CNTR_RESUME;
+ USB_BASE->CNTR = cntr;
ResumeS.eState = RESUME_OFF;
}
break;
@@ -191,46 +214,39 @@ void usbResume(RESUME_STATE eResumeSetVal) {
}
}
-/* overloaded ISR routine, this is the main usb ISR */
void __irq_usb_lp_can_rx0(void) {
-wIstr = _GetISTR();
+ wIstr = USB_BASE->ISTR;
-/* go nuts with the preproc switches since this is an ISTR and must be FAST */
-#if (ISR_MSK & ISTR_RESET)
-if (wIstr & ISTR_RESET & wInterrupt_Mask)
- {
- _SetISTR((u16)CLR_RESET);
+ /* Use ISR_MSK to only include code for bits we care about. */
+
+#if (ISR_MSK & USB_ISTR_RESET)
+ if (wIstr & USB_ISTR_RESET & wInterrupt_Mask) {
+ USB_BASE->ISTR = ~USB_ISTR_RESET;
Device_Property.Reset();
}
#endif
-
-#if (ISR_MSK & ISTR_DOVR)
-if (wIstr & ISTR_DOVR & wInterrupt_Mask)
- {
- _SetISTR((u16)CLR_DOVR);
+#if (ISR_MSK & USB_ISTR_PMAOVR)
+ if (wIstr & ISTR_PMAOVR & wInterrupt_Mask) {
+ USB_BASE->ISTR = ~USB_ISTR_PMAOVR;
}
#endif
-
-#if (ISR_MSK & ISTR_ERR)
-if (wIstr & ISTR_ERR & wInterrupt_Mask)
- {
- _SetISTR((u16)CLR_ERR);
+#if (ISR_MSK & USB_ISTR_ERR)
+ if (wIstr & USB_ISTR_ERR & wInterrupt_Mask) {
+ USB_BASE->ISTR = ~USB_ISTR_ERR;
}
#endif
-
-#if (ISR_MSK & ISTR_WKUP)
-if (wIstr & ISTR_WKUP & wInterrupt_Mask) {
- _SetISTR((u16)CLR_WKUP);
+#if (ISR_MSK & USB_ISTR_WKUP)
+ if (wIstr & USB_ISTR_WKUP & wInterrupt_Mask) {
+ USB_BASE->ISTR = ~USB_ISTR_WKUP;
usbResume(RESUME_EXTERNAL);
-}
+ }
#endif
-/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
-#if (ISR_MSK & ISTR_SUSP)
-if (wIstr & ISTR_SUSP & wInterrupt_Mask) {
+#if (ISR_MSK & USB_ISTR_SUSP)
+ if (wIstr & USB_ISTR_SUSP & wInterrupt_Mask) {
/* check if SUSPEND is possible */
if (F_SUSPEND_ENABLED) {
usbSuspend();
@@ -239,36 +255,34 @@ if (wIstr & ISTR_SUSP & wInterrupt_Mask) {
usbResume(RESUME_LATER);
}
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
- _SetISTR((u16)CLR_SUSP);
+ USB_BASE->ISTR = ~USB_ISTR_SUSP;
}
#endif
-
-#if (ISR_MSK & ISTR_SOF)
-if (wIstr & ISTR_SOF & wInterrupt_Mask) {
- _SetISTR((u16)CLR_SOF);
+#if (ISR_MSK & USB_ISTR_SOF)
+ if (wIstr & USB_ISTR_SOF & wInterrupt_Mask) {
+ USB_BASE->ISTR = ~USB_ISTR_SOF;
bIntPackSOF++;
- }
+ }
#endif
-
-#if (ISR_MSK & ISTR_ESOF)
-if (wIstr & ISTR_ESOF & wInterrupt_Mask) {
- _SetISTR((u16)CLR_ESOF);
+#if (ISR_MSK & USB_ISTR_ESOF)
+ if (wIstr & USB_ISTR_ESOF & wInterrupt_Mask) {
+ USB_BASE->ISTR = ~USB_ISTR_ESOF;
/* resume handling timing is made with ESOFs */
usbResume(RESUME_ESOF); /* request without change of the machine state */
- }
+ }
#endif
-/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
-#if (ISR_MSK & ISTR_CTR)
-if (wIstr & ISTR_CTR & wInterrupt_Mask) {
- /* servicing of the endpoint correct transfer interrupt */
- /* clear of the CTR flag into the sub */
- CTR_LP(); /* low priority ISR defined in the usb core lib */
- }
-#endif
+ /*
+ * Service the correct transfer interrupt.
+ */
+#if (ISR_MSK & USB_ISTR_CTR)
+ if (wIstr & USB_ISTR_CTR & wInterrupt_Mask) {
+ dispatch_ctr_lp();
+ }
+#endif
}
void usbWaitReset(void) {
@@ -303,16 +317,17 @@ uint32 usbSendBytes(const uint8* sendBuf, uint32 len) {
}
// We can only put VCOM_TX_EPSIZE bytes in the buffer
+ /* FIXME XXX then why are we only copying half as many? */
if (len > VCOM_TX_EPSIZE / 2) {
len = VCOM_TX_EPSIZE / 2;
}
// Try to load some bytes if we can
if (len) {
- UserToPMABufferCopy(sendBuf, VCOM_TX_ADDR, len);
- _SetEPTxCount(VCOM_TX_ENDP, len);
+ usb_copy_to_pma(sendBuf, len, VCOM_TX_ADDR);
+ usb_set_ep_tx_count(VCOM_TX_ENDP, len);
countTx += len;
- _SetEPTxValid(VCOM_TX_ENDP);
+ usb_set_ep_tx_stat(VCOM_TX_ENDP, USB_EP_STAT_TX_VALID);
}
return len;
@@ -323,10 +338,10 @@ uint32 usbBytesAvailable(void) {
return newBytes;
}
-/* copies len bytes from the local recieve FIFO (not
- usb packet buffer) into recvBuf and deq's the fifo.
- will only copy the minimum of len or the available
- bytes. returns the number of bytes copied */
+/* Nonblocking byte receive.
+ *
+ * Copies up to len bytes from the local recieve FIFO (*NOT* the PMA)
+ * into recvBuf and deq's the FIFO. */
uint32 usbReceiveBytes(uint8* recvBuf, uint32 len) {
static int offset = 0;
@@ -342,10 +357,10 @@ uint32 usbReceiveBytes(uint8* recvBuf, uint32 len) {
newBytes -= len;
offset += len;
- /* re-enable the rx endpoint which we had set to receive 0 bytes */
+ /* Re-enable the RX endpoint, which we had set to receive 0 bytes */
if (newBytes == 0) {
- SetEPRxCount(VCOM_RX_ENDP,VCOM_RX_EPSIZE);
- SetEPRxStatus(VCOM_RX_ENDP,EP_RX_VALID);
+ usb_set_ep_rx_count(VCOM_RX_ENDP,VCOM_RX_EPSIZE);
+ usb_set_ep_rx_stat(VCOM_RX_ENDP, USB_EP_STAT_RX_VALID);
offset = 0;
}
@@ -372,3 +387,142 @@ uint16 usbGetPending() {
return countTx;
}
+/*
+ * Auxiliary routines
+ */
+
+static void usb_init(void) { /* TODO make a public, improved version */
+ rcc_clk_enable(RCC_USB);
+
+ pInformation = &Device_Info;
+ pInformation->ControlState = 2;
+ pProperty = &Device_Property;
+ pUser_Standard_Requests = &User_Standard_Requests;
+ pProperty->Init();
+}
+
+static inline uint8 dispatch_endpt_zero(void);
+static inline void dispatch_endpt(uint8 ep);
+static inline void set_rx_tx_status0(uint16 rx, uint16 tx);
+
+static void handle_setup0(void);
+static void handle_in0(void);
+static void handle_out0(void);
+
+static void dispatch_ctr_lp(void) {
+ while (((wIstr = USB_BASE->ISTR) & USB_ISTR_CTR) != 0) {
+ /* TODO WTF, figure this out: RM0008 says CTR is read-only,
+ * but ST's firmware claims it's clear-only, and emphasizes
+ * the importance of clearing it in more than one place. */
+ USB_BASE->ISTR = ~USB_ISTR_CTR;
+ uint8 ep_id = wIstr & USB_ISTR_EP_ID;
+ if (ep_id == 0) {
+ /* TODO figure out why it's OK to break out of the loop
+ * once we're done serving endpoint zero, but not okay if
+ * there are multiple nonzero endpoint transfers to
+ * handle. */
+ if (dispatch_endpt_zero())
+ return;
+ } else {
+ dispatch_endpt(ep_id);
+ }
+ }
+}
+
+/* FIXME Dataflow on endpoint 0 RX/TX status is based off of ST's
+ * code, and is ugly/confusing in its use of SaveRState/SaveTState.
+ * Fixing this requires filling in handle_in0(), handle_setup0(),
+ * handle_out0(). */
+static inline uint8 dispatch_endpt_zero(void) {
+ uint32 epr = (uint16)USB_BASE->EP[0];
+
+ if (!(epr & (USB_EP_CTR_TX | USB_EP_SETUP | USB_EP_CTR_RX))) {
+ return 0;
+ }
+
+ /* Cache RX/TX statuses in SaveRState/SaveTState, respectively.
+ * The various handle_foo0() may clobber these values
+ * before we reset them at the end of this routine. */
+ SaveRState = epr & USB_EP_STAT_RX;
+ SaveTState = epr & USB_EP_STAT_TX;
+
+ /* Set actual RX/TX statuses to NAK while we're thinking */
+ set_rx_tx_status0(USB_EP_STAT_RX_NAK, USB_EP_STAT_TX_NAK);
+
+ if ((wIstr & USB_ISTR_DIR) == 0) {
+ /* ST RM0008: "If DIR bit=0, CTR_TX bit is set in the USB_EPnR
+ * register related to the interrupting endpoint. The
+ * interrupting transaction is of IN type (data transmitted by
+ * the USB peripheral to the host PC)." */
+ ASSERT_FAULT(epr & USB_EP_CTR_TX);
+ usb_clear_ctr_tx(USB_EP0);
+ handle_in0();
+ } else {
+ /* RM0008: "If DIR bit=1, CTR_RX bit or both CTR_TX/CTR_RX
+ * are set in the USB_EPnR register related to the
+ * interrupting endpoint. The interrupting transaction is of
+ * OUT type (data received by the USB peripheral from the host
+ * PC) or two pending transactions are waiting to be
+ * processed."
+ *
+ * [mbolivar] Note how the following control flow (which
+ * replicates ST's) doesn't seem to actually handle both
+ * interrupts that are ostensibly pending when both CTR_RX and
+ * CTR_TX are set.
+ *
+ * TODO sort this mess out.
+ */
+ if (epr & USB_EP_CTR_TX) {
+ usb_clear_ctr_tx(USB_EP0);
+ handle_in0();
+ } else { /* SETUP or CTR_RX */
+ /* SETUP is held constant while CTR_RX is set, so clear it
+ * either way */
+ usb_clear_ctr_rx(USB_EP0);
+ if (epr & USB_EP_SETUP) {
+ handle_setup0();
+ } else { /* CTR_RX */
+ handle_out0();
+ }
+ }
+ }
+
+ set_rx_tx_status0(SaveRState, SaveTState);
+ return 1;
+}
+
+static inline void dispatch_endpt(uint8 ep) {
+ uint32 epr = USB_BASE->EP[ep];
+ /* If ISTR_CTR is set and the ISTR gave us this EP_ID to handle,
+ * then presumably at least one of CTR_RX and CTR_TX is set, but
+ * again, ST's control flow allows for the possibility of neither.
+ *
+ * TODO try to find out if neither being set is possible. */
+ if (epr & USB_EP_CTR_RX) {
+ usb_clear_ctr_rx(ep);
+ (pEpInt_OUT[ep - 1])();
+ }
+ if (epr & USB_EP_CTR_TX) {
+ usb_clear_ctr_tx(ep);
+ (pEpInt_IN[ep - 1])();
+ }
+}
+
+static inline void set_rx_tx_status0(uint16 rx, uint16 tx) {
+ usb_set_ep_rx_stat(USB_EP0, rx);
+ usb_set_ep_tx_stat(USB_EP0, tx);
+}
+
+/* TODO Rip out usb_lib/ dependency from the following functions: */
+
+static void handle_setup0(void) {
+ Setup0_Process();
+}
+
+static void handle_in0(void) {
+ In0_Process();
+}
+
+static void handle_out0(void) {
+ Out0_Process();
+}