/* * _______ _ _ _____ ____ * |__ __| | | | |/ ____| _ \ * | | ___ ___ _ __ _ _| | | | (___ | |_) | * | |/ _ \/ _ \ '_ \| | | | | | |\___ \| _ < * | | __/ __/ | | | |_| | |__| |____) | |_) | * |_|\___|\___|_| |_|\__, |\____/|_____/|____/ * __/ | * |___/ * * TeenyUSB - light weight usb stack for STM32 micro controllers * * Copyright (c) 2019 XToolBox - admin@xtoolbox.org * www.tusb.org * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ // teeny usb platform header for STM32F1/STM32F0 device #ifndef __STM32_FS_PLATFORM_H__ #define __STM32_FS_PLATFORM_H__ #include "tusb_def.h" //////////////// HAL PCD define //////////////// #define PCD_ENDP0 0U #define PCD_ENDP1 1U #define PCD_ENDP2 2U #define PCD_ENDP3 3U #define PCD_ENDP4 4U #define PCD_ENDP5 5U #define PCD_ENDP6 6U #define PCD_ENDP7 7U /* SetENDPOINT */ #define PCD_SET_ENDPOINT(USBx, bEpNum, wRegValue) (*(__IO uint16_t *)(&(USBx)->EP0R + ((bEpNum) * 2U)) = (uint16_t)(wRegValue)) /* GetENDPOINT */ #define PCD_GET_ENDPOINT(USBx, bEpNum) (*(__IO uint16_t *)(&(USBx)->EP0R + ((bEpNum) * 2U))) /** * @brief sets the status for tx transfer (bits STAT_TX[1:0]). * @param USBx USB peripheral instance register address. * @param bEpNum Endpoint Number. * @param wState new state * @retval None */ #define PCD_SET_EP_TX_STATUS(USBx, bEpNum, wState) do { \ register uint16_t _wRegVal; \ \ _wRegVal = PCD_GET_ENDPOINT((USBx), (bEpNum)) & USB_EPTX_DTOGMASK; \ /* toggle first bit ? */ \ if ((USB_EPTX_DTOG1 & (wState))!= 0U) \ { \ _wRegVal ^= USB_EPTX_DTOG1; \ } \ /* toggle second bit ? */ \ if ((USB_EPTX_DTOG2 & (wState))!= 0U) \ { \ _wRegVal ^= USB_EPTX_DTOG2; \ } \ PCD_SET_ENDPOINT((USBx), (bEpNum), (_wRegVal | USB_EP_CTR_RX | USB_EP_CTR_TX)); \ } while(0) /* PCD_SET_EP_TX_STATUS */ /** * @brief sets the status for rx transfer (bits STAT_TX[1:0]) * @param USBx USB peripheral instance register address. * @param bEpNum Endpoint Number. * @param wState new state * @retval None */ #define PCD_SET_EP_RX_STATUS(USBx, bEpNum,wState) do { \ register uint16_t _wRegVal; \ \ _wRegVal = PCD_GET_ENDPOINT((USBx), (bEpNum)) & USB_EPRX_DTOGMASK; \ /* toggle first bit ? */ \ if ((USB_EPRX_DTOG1 & (wState))!= 0U) \ { \ _wRegVal ^= USB_EPRX_DTOG1; \ } \ /* toggle second bit ? */ \ if ((USB_EPRX_DTOG2 & (wState))!= 0U) \ { \ _wRegVal ^= USB_EPRX_DTOG2; \ } \ PCD_SET_ENDPOINT((USBx), (bEpNum), (_wRegVal | USB_EP_CTR_RX | USB_EP_CTR_TX)); \ } while(0) /* PCD_SET_EP_RX_STATUS */ /** * @brief Toggles DTOG_RX / DTOG_TX bit in the endpoint register. * @param USBx USB peripheral instance register address. * @param bEpNum Endpoint Number. * @retval None */ #define PCD_RX_DTOG(USBx, bEpNum) do { \ register uint16_t _wEPVal; \ \ _wEPVal = PCD_GET_ENDPOINT((USBx), (bEpNum)) & USB_EPREG_MASK; \ \ PCD_SET_ENDPOINT((USBx), (bEpNum), (_wEPVal | USB_EP_CTR_RX | USB_EP_CTR_TX | USB_EP_DTOG_RX)); \ } while(0) /* PCD_RX_DTOG */ #define PCD_TX_DTOG(USBx, bEpNum) do { \ register uint16_t _wEPVal; \ \ _wEPVal = PCD_GET_ENDPOINT((USBx), (bEpNum)) & USB_EPREG_MASK; \ \ PCD_SET_ENDPOINT((USBx), (bEpNum), (_wEPVal | USB_EP_CTR_RX | USB_EP_CTR_TX | USB_EP_DTOG_TX)); \ } while(0) /* PCD_TX_DTOG */ /** * @brief Clears DTOG_RX / DTOG_TX bit in the endpoint register. * @param USBx USB peripheral instance register address. * @param bEpNum Endpoint Number. * @retval None */ #define PCD_CLEAR_RX_DTOG(USBx, bEpNum) do { \ register uint16_t _wRegVal; \ \ _wRegVal = PCD_GET_ENDPOINT((USBx), (bEpNum)); \ \ if ((_wRegVal & USB_EP_DTOG_RX) != 0U)\ { \ PCD_RX_DTOG((USBx), (bEpNum)); \ } \ } while(0) /* PCD_CLEAR_RX_DTOG */ #define PCD_CLEAR_TX_DTOG(USBx, bEpNum) do { \ register uint16_t _wRegVal; \ \ _wRegVal = PCD_GET_ENDPOINT((USBx), (bEpNum)); \ \ if ((_wRegVal & USB_EP_DTOG_TX) != 0U)\ { \ PCD_TX_DTOG((USBx), (bEpNum)); \ } \ } while(0) /* PCD_CLEAR_TX_DTOG */ //////////////////////////////////////////////// #if defined(STM32F0) || defined(STM32F302xE) || defined(STM32F303xE) || defined(STM32F302x8) // Define PMA buffer layout for STM32F0xx typedef struct _pma_data{ uint16_t data; }pma_data; typedef struct _pma_record{ uint16_t addr; // TX/RX address uint16_t cnt:10; // TX/RX data count uint16_t block:6; // RX buffer size }pma_record; #elif defined(STM32F1) || defined(STM32F302xC) || defined(STM32F303xC) || defined(STM32F373xC) // Define PMA buffer layout for STM32F1xx typedef struct _pma_data{ uint16_t data; uint16_t padding; }pma_data; typedef struct _pma_record{ uint16_t addr; // TX/RX address uint16_t padding1; uint16_t cnt:10; // TX/RX data count uint16_t block:6; // RX buffer size uint16_t padding2; }pma_record; #endif // There is only one USB device in STM32F0/STM32F1, just return the USB directly #define GetUSB(dev) (USB) #ifndef NEED_MAX_PACKET #define NEED_MAX_PACKET #endif #define GetInMaxPacket(dev, EPn) ((dev)->tx_max_size[(EPn)]) #define GetOutMaxPacket(dev, EPn) ((dev)->rx_max_size[(EPn)]) #define PMATable(dev) ( (pma_data*) ((uint32_t)GetUSB(dev) + 0x400U) ) #define GetPMAAddr(dev, offset) \ (PMATable(dev) + ( (offset) / sizeof(((pma_data*)0)->data)) ) #ifndef BTABLE_ADDRESS #define BTABLE_ADDRESS (0) #endif // BTABLE address will not change in this kind device, so just return the fixed value #define GetBTABLE(dev) (BTABLE_ADDRESS) typedef union _pma_ep_desc { struct _normal{ pma_record tx; pma_record rx; }normal; struct _dbl_tx{ pma_record tx0; pma_record tx1; }dbl_tx; struct _dlb_rx{ pma_record rx0; pma_record rx1; }dbl_rx; }pma_ep_desc; // easy way to aceess the field #define pma_tx normal.tx #define pma_rx normal.rx #define pma_tx0 dbl_tx.tx0 #define pma_tx1 dbl_tx.tx1 #define pma_rx0 dbl_rx.rx0 #define pma_rx1 dbl_rx.rx1 #define tx_addr normal.tx.addr #define tx_cnt normal.tx.cnt #define rx_addr normal.rx.addr #define rx_cnt normal.rx.cnt #define rx_block normal.rx.block #define tx0_addr dbl_tx.tx0.addr #define tx0_cnt dbl_tx.tx0.cnt #define tx1_addr dbl_tx.tx1.addr #define tx1_cnt dbl_tx.tx1.cnt #define rx0_addr dbl_rx.rx0.addr #define rx0_cnt dbl_rx.rx0.cnt #define rx0_block dbl_rx.rx0.block #define rx1_addr dbl_rx.rx1.addr #define rx1_cnt dbl_rx.rx1.cnt #define rx1_block dbl_rx.rx1.block #define PMA_DESC(dev) ((pma_ep_desc*) (GetBTABLE(dev) + PMATable(dev)) ) #define EPT(dev, bEpNum) (PMA_DESC(dev)[bEpNum]) #define PMA_TX(dev, bEpNum) (&(PMA_DESC(dev)[bEpNum].pma_tx)) #define PMA_RX(dev, bEpNum) (&(PMA_DESC(dev)[bEpNum].pma_rx)) #define PMA_TX0(dev, bEpNum) (&(PMA_DESC(dev)[bEpNum].pma_tx0)) #define PMA_TX1(dev, bEpNum) (&(PMA_DESC(dev)[bEpNum].pma_tx1)) #define PMA_RX0(dev, bEpNum) (&(PMA_DESC(dev)[bEpNum].pma_rx0)) #define PMA_RX1(dev, bEpNum) (&(PMA_DESC(dev)[bEpNum].pma_rx1)) #define SET_TX_ADDR(dev, bEpNum, addr) do { EPT(dev, bEpNum).tx_addr = (addr); }while(0) #define SET_RX_ADDR(dev, bEpNum, addr) do { EPT(dev, bEpNum).rx_addr = (addr); }while(0) #define SET_DOUBLE_ADDR(dev, bEpNum, addr0, addr1) \ do{\ EPT(dev, bEpNum).tx0_addr = addr0;\ EPT(dev, bEpNum).tx1_addr = addr1;\ }while(0) #define SET_RX_CNT(dev, bEpNum, cnt)\ do{\ uint16_t block = (cnt)>62 ? ((((cnt)+31)/32) | (1<<5)): (((cnt)+1)/2);\ EPT(dev, bEpNum).rx_cnt = 0;\ EPT(dev, bEpNum).rx_block = block;\ }while(0) #define SET_TX_CNT(dev, bEpNum, cnt) \ do{ EPT(dev, bEpNum).tx_cnt = (cnt); }while(0) #define SET_DBL_TX_CNT(dev, bEpNum, cnt)\ do{\ EPT(dev, bEpNum).tx0_cnt = (cnt);\ EPT(dev, bEpNum).tx1_cnt = (cnt);\ }while(0) #define SET_DBL_RX_CNT(dev, bEpNum, cnt)\ do{\ uint16_t block = (cnt)>62 ? ((((cnt)+31)/32) | (1<<5)): (((cnt)+1)/2);\ EPT(dev, bEpNum).rx0_cnt = 0;\ EPT(dev, bEpNum).rx0_block = block;\ EPT(dev, bEpNum).rx1_cnt = 0;\ EPT(dev, bEpNum).rx1_block = block;\ }while(0) // Macro used to build ep setting, Tx/Rx CTR not changed, Tx/Rx TOG bits forced to 0 #define BUILD_EP_SETTING(dev, bEpNum, type, rx_state, tx_state, kind)\ /* Get end point current value */ \ ((( PCD_GET_ENDPOINT(GetUSB(dev), bEpNum) \ /* TOG bits is write 1 to toggle, mask them with current value */\ /* then write them back will set the TOG bits to 0*/\ & (USB_EP_DTOG_RX | USB_EP_DTOG_TX | USB_EPTX_STAT | USB_EPRX_STAT)) \ /* tx/rx state is wirte 1 to toggle, xor them with current value */ \ /* then write them back, will set them to desired value */ \ ^ ((tx_state) | (rx_state)) ) \ /* Write 1 to CTR has no effect, ored them with 1 makes no change */ \ | (USB_EP_CTR_RX|USB_EP_CTR_TX) \ /* Write ep num, tpye and kind to the register */ \ | (bEpNum) | (type) | (kind) ) // Init ep in tx/tx mode, and set tx to NAK, set rx to VALID #define INIT_EP_BiDirection(dev, bEpNum, type) \ PCD_SET_ENDPOINT(GetUSB(dev), bEpNum, BUILD_EP_SETTING(dev, bEpNum, type, USB_EP_RX_VALID, USB_EP_TX_NAK, 0) ) // Init ep in tx mode, and set tx to NAK state #define INIT_EP_TxOnly(dev, bEpNum, type) \ PCD_SET_ENDPOINT(GetUSB(dev), bEpNum, BUILD_EP_SETTING(dev, bEpNum, type, USB_EP_RX_DIS, USB_EP_TX_NAK, 0) ) // Init ep in rx mode, and set rx to VALID state #define INIT_EP_RxOnly(dev, bEpNum, type) \ PCD_SET_ENDPOINT(GetUSB(dev), bEpNum, BUILD_EP_SETTING(dev, bEpNum, type, USB_EP_RX_VALID, USB_EP_TX_DIS, 0) ) // Init ep in tx double buffer mode, and set tx to NAK state #define INIT_EP_TxDouble(dev, bEpNum, type) \ /* Keep to Rx/Tx TOG bit to 0, the double buffer tx ep is NAK even tx state is VALID */ \ PCD_SET_ENDPOINT(GetUSB(dev), bEpNum, BUILD_EP_SETTING(dev, bEpNum, type, USB_EP_RX_DIS, USB_EP_TX_VALID, USB_EP_KIND) ) // Init ep in rx double buffer mode, and set rx to VALID state #define INIT_EP_RxDouble(dev, bEpNum, type) \ PCD_SET_ENDPOINT(GetUSB(dev), bEpNum, BUILD_EP_SETTING(dev, bEpNum, type, USB_EP_RX_VALID, USB_EP_TX_DIS, USB_EP_KIND) \ /* Toggle the SW_BUF bits, to make sure the rx state is real valid */ \ ^ USB_EP_DTOG_TX ) // Teeny USB style tog, set, clear macros, they all required old ep value #define TUSB_RX_DTOG(USBx, EPn, EPOld) \ PCD_SET_ENDPOINT(USBx, EPn, (EPOld & USB_EPREG_MASK) | (USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX) ) #define TUSB_TX_DTOG(USBx, EPn, EPOld) \ PCD_SET_ENDPOINT(USBx, EPn, (EPOld & USB_EPREG_MASK) | (USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX) ) #define TUSB_SET_TX_STATUS(USBx, EPn, EPOld, Status) \ PCD_SET_ENDPOINT( USBx, EPn, \ ((EPOld & USB_EPTX_DTOGMASK) ^ Status) | USB_EP_CTR_RX|USB_EP_CTR_TX) #define TUSB_SET_RX_STATUS(USBx, EPn, EPOld, Status) \ PCD_SET_ENDPOINT( USBx, EPn, \ ((EPOld & USB_EPRX_DTOGMASK) ^ Status) | USB_EP_CTR_RX|USB_EP_CTR_TX) #define TUSB_CLEAR_TX_CTR(USBx, EPn, EPOld) \ PCD_SET_ENDPOINT((USBx), (EPn), \ EPOld & 0xFF7FU & USB_EPREG_MASK) #define TUSB_CLEAR_RX_CTR(USBx, EPn, EPOld) \ PCD_SET_ENDPOINT((USBx), (EPn), \ EPOld & 0x7FFFU & USB_EPREG_MASK) #endif