From 2a18d6873b6f3b22e9900197f2ea480d1e0fa4f4 Mon Sep 17 00:00:00 2001 From: hydevcode Date: Fri, 24 Jan 2025 23:12:25 +0800 Subject: [PATCH] [bsp][nrf5x]added the cherryusb adapter for nrf52840 (#9939) --- .../.ci/attachconfig/ci.attachconfig.yml | 11 +- bsp/nrf5x/nrf52840/board/SConscript | 6 + bsp/nrf5x/nrf52840/board/port/SConscript | 21 + .../nrf52840/board/port/cherryusb/cherryusb.c | 70 + .../board/port/cherryusb/usb_config.h | 137 ++ bsp/nrf5x/nrf52840/board/sdk_config.h | 2 +- components/drivers/usb/cherryusb/Kconfig | 2 + components/drivers/usb/cherryusb/SConscript | 2 + .../usb/cherryusb/port/nrf5x/README.md | 9 + .../usb/cherryusb/port/nrf5x/usb_dc_nrf5x.c | 1321 +++++++++++++++++ 10 files changed, 1579 insertions(+), 2 deletions(-) create mode 100644 bsp/nrf5x/nrf52840/board/port/SConscript create mode 100644 bsp/nrf5x/nrf52840/board/port/cherryusb/cherryusb.c create mode 100644 bsp/nrf5x/nrf52840/board/port/cherryusb/usb_config.h create mode 100644 components/drivers/usb/cherryusb/port/nrf5x/README.md create mode 100644 components/drivers/usb/cherryusb/port/nrf5x/usb_dc_nrf5x.c diff --git a/bsp/nrf5x/nrf52840/.ci/attachconfig/ci.attachconfig.yml b/bsp/nrf5x/nrf52840/.ci/attachconfig/ci.attachconfig.yml index e923d7a470..a170defb5a 100644 --- a/bsp/nrf5x/nrf52840/.ci/attachconfig/ci.attachconfig.yml +++ b/bsp/nrf5x/nrf52840/.ci/attachconfig/ci.attachconfig.yml @@ -98,4 +98,13 @@ nimble.uart: segger: kconfig: - CONFIG_PKG_USING_SEGGER_RTT=y - - CONFIG_RT_USING_SERIAL_V2=y \ No newline at end of file + - CONFIG_RT_USING_SERIAL_V2=y +# ------ component CI ------ +component.cherryusb_hid_keyboard: + kconfig: + - CONFIG_RT_USING_CHERRYUSB=y + - CONFIG_RT_CHERRYUSB_DEVICE=y + - CONFIG_RT_CHERRYUSB_DEVICE_NRF5X=y + - CONFIG_RT_CHERRYUSB_DEVICE_HID=y + - CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_HID_KEYBOARD=y + - CONFIG_RT_USING_MESSAGEQUEUE=y \ No newline at end of file diff --git a/bsp/nrf5x/nrf52840/board/SConscript b/bsp/nrf5x/nrf52840/board/SConscript index d74f1a8861..3b5a2f0940 100644 --- a/bsp/nrf5x/nrf52840/board/SConscript +++ b/bsp/nrf5x/nrf52840/board/SConscript @@ -7,4 +7,10 @@ src = Glob('*.c') CPPPATH = [cwd] group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH) + +list = os.listdir(cwd) +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + group = group + SConscript(os.path.join(item, 'SConscript')) + Return('group') diff --git a/bsp/nrf5x/nrf52840/board/port/SConscript b/bsp/nrf5x/nrf52840/board/port/SConscript new file mode 100644 index 0000000000..d15a67f358 --- /dev/null +++ b/bsp/nrf5x/nrf52840/board/port/SConscript @@ -0,0 +1,21 @@ +import os +from building import * + +cwd = GetCurrentDir() + +# add general drivers +src = [] +path = [] + +if GetDepend(['RT_USING_CHERRYUSB']): + src += Glob('cherryusb/cherryusb.c') + path += [cwd + '/cherryusb'] + +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = path) + +list = os.listdir(cwd) +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + group = group + SConscript(os.path.join(item, 'SConscript')) + +Return('group') \ No newline at end of file diff --git a/bsp/nrf5x/nrf52840/board/port/cherryusb/cherryusb.c b/bsp/nrf5x/nrf52840/board/port/cherryusb/cherryusb.c new file mode 100644 index 0000000000..3212e3ecc9 --- /dev/null +++ b/bsp/nrf5x/nrf52840/board/port/cherryusb/cherryusb.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include "nrf.h" +#include "nrfx_usbd.h" +#include "nrfx_clock.h" +#include "nrfx_power.h" + +void usb_dc_low_level_post_init(void) +{ + /* Enable interrupt globally */ + NRFX_IRQ_PRIORITY_SET(USBD_IRQn, NRFX_USBD_CONFIG_IRQ_PRIORITY); + NRFX_IRQ_ENABLE(USBD_IRQn); +} + +extern void cherry_usb_hal_nrf_power_event(uint32_t event); +static void power_event_handler(nrfx_power_usb_evt_t event) +{ + cherry_usb_hal_nrf_power_event((uint32_t)event); +} + +void usb_dc_low_level_pre_init(void) +{ + uint32_t usb_reg; + const nrfx_power_usbevt_config_t config = {.handler = power_event_handler}; + nrfx_power_usbevt_init(&config); + nrfx_power_usbevt_enable(); + usb_reg = NRF_POWER->USBREGSTATUS; + + if (usb_reg & POWER_USBREGSTATUS_VBUSDETECT_Msk) + { + cherry_usb_hal_nrf_power_event(NRFX_POWER_USB_EVT_DETECTED); + } + + if (usb_reg & POWER_USBREGSTATUS_OUTPUTRDY_Msk) + { + cherry_usb_hal_nrf_power_event(NRFX_POWER_USB_EVT_READY); + } +} + +void usb_low_clear_pending_irq(void) +{ + NVIC_ClearPendingIRQ(USBD_IRQn); +} + +void usb_low_disable_irq(void) +{ + NVIC_DisableIRQ(USBD_IRQn); +} + +int cherryusb_protocol_stack_init(void) +{ +#ifdef RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM + extern void cdc_acm_init(void); + cdc_acm_init(); + rt_kprintf("cdc acm example started. \r\n"); +#elif defined RT_CHERRYUSB_DEVICE_TEMPLATE_MSC + extern void msc_ram_init(void); + msc_ram_init(); + rt_kprintf("msc ram example started. \r\n"); +#elif defined RT_CHERRYUSB_DEVICE_TEMPLATE_HID_KEYBOARD + extern void hid_keyboard_init(uint8_t busid, uintptr_t reg_base); + hid_keyboard_init(0,NULL); + rt_kprintf("hid keyboard example started. \r\n"); +#endif +} + +INIT_APP_EXPORT(cherryusb_protocol_stack_init); \ No newline at end of file diff --git a/bsp/nrf5x/nrf52840/board/port/cherryusb/usb_config.h b/bsp/nrf5x/nrf52840/board/port/cherryusb/usb_config.h new file mode 100644 index 0000000000..84acee78d0 --- /dev/null +++ b/bsp/nrf5x/nrf52840/board/port/cherryusb/usb_config.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2022, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef CHERRYUSB_CONFIG_H +#define CHERRYUSB_CONFIG_H + + +#include + +/* ================ USB common Configuration ================ */ +#define CONFIG_USB_PRINTF(...) rt_kprintf(__VA_ARGS__) +#define usb_malloc(size) malloc(size) +#define usb_free(ptr) free(ptr) + +#ifndef CONFIG_USB_DBG_LEVEL +//#define CONFIG_USB_DBG_LEVEL USB_DBG_INFO +#define CONFIG_USB_DBG_LEVEL 3 +#endif + +/* Enable print with color */ +#define CONFIG_USB_PRINTF_COLOR_ENABLE + +/* data align size when use dma */ +#ifndef CONFIG_USB_ALIGN_SIZE +#define CONFIG_USB_ALIGN_SIZE 4 +#endif + +/* attribute data into no cache ram */ +#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable"))) + +/* ================= USB Device Stack Configuration ================ */ + +/* Ep0 max transfer buffer, specially for receiving data from ep0 out */ +#define CONFIG_USBDEV_REQUEST_BUFFER_LEN 256 + +#ifndef CONFIG_USBDEV_MSC_MAX_LUN +#define CONFIG_USBDEV_MSC_MAX_LUN 1 +#endif + +#ifndef CONFIG_USBDEV_MSC_MAX_BUFSIZE +#define CONFIG_USBDEV_MSC_MAX_BUFSIZE 512 +#endif + +#ifndef CONFIG_USBDEV_MSC_MANUFACTURER_STRING +#define CONFIG_USBDEV_MSC_MANUFACTURER_STRING "" +#endif + +#ifndef CONFIG_USBDEV_MSC_PRODUCT_STRING +#define CONFIG_USBDEV_MSC_PRODUCT_STRING "" +#endif + +#ifndef CONFIG_USBDEV_MSC_VERSION_STRING +#define CONFIG_USBDEV_MSC_VERSION_STRING "0.01" +#endif + +#ifndef CONFIG_USBDEV_MSC_PRIO +#define CONFIG_USBDEV_MSC_PRIO 4 +#endif + +#ifndef CONFIG_USBDEV_MSC_STACKSIZE +#define CONFIG_USBDEV_MSC_STACKSIZE 2048 +#endif + +#ifndef CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE +#define CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE 156 +#endif + +#ifndef CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE +#define CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE 1536 +#endif + +#ifndef CONFIG_USBDEV_RNDIS_VENDOR_ID +#define CONFIG_USBDEV_RNDIS_VENDOR_ID 0x0000ffff +#endif + +#ifndef CONFIG_USBDEV_RNDIS_VENDOR_DESC +#define CONFIG_USBDEV_RNDIS_VENDOR_DESC "CherryUSB" +#endif + +#define CONFIG_USBDEV_RNDIS_USING_LWIP + +/* ================ USB HOST Stack Configuration ================== */ + +#define CONFIG_USBHOST_MAX_RHPORTS 1 +#define CONFIG_USBHOST_MAX_EXTHUBS 1 +#define CONFIG_USBHOST_MAX_EHPORTS 4 +#define CONFIG_USBHOST_MAX_INTERFACES 8 +#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 8 +#define CONFIG_USBHOST_MAX_ENDPOINTS 4 + +#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4 +#define CONFIG_USBHOST_MAX_HID_CLASS 4 +#define CONFIG_USBHOST_MAX_MSC_CLASS 2 +#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1 +#define CONFIG_USBHOST_MAX_VIDEO_CLASS 1 + +#define CONFIG_USBHOST_DEV_NAMELEN 16 + +#ifndef CONFIG_USBHOST_PSC_PRIO +#define CONFIG_USBHOST_PSC_PRIO 0 +#endif +#ifndef CONFIG_USBHOST_PSC_STACKSIZE +#define CONFIG_USBHOST_PSC_STACKSIZE 2048 +#endif + +#define CONFIG_USBHOST_MSOS_VENDOR_CODE 0x00 + +/* Ep0 max transfer buffer */ +#define CONFIG_USBHOST_REQUEST_BUFFER_LEN 512 + +#ifndef CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT +#define CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT 500 +#endif + +#ifndef CONFIG_USBHOST_MSC_TIMEOUT +#define CONFIG_USBHOST_MSC_TIMEOUT 5000 +#endif + +/* ================ USB Device Port Configuration ================*/ +#define CONFIG_USBDEV_MAX_BUS 1 + +#define CONFIG_USBDEV_EP_NUM 8 +#define CONFIG_USBDEV_FSDEV_PMA_ACCESS 2 + +/* ================ USB Host Port Configuration ==================*/ + +#define CONFIG_USBHOST_PIPE_NUM 10 + +/* ================ EHCI Configuration ================ */ + +#define CONFIG_USB_EHCI_HCCR_BASE (0x20072000) +#define CONFIG_USB_EHCI_HCOR_BASE (0x20072000 + 0x10) +#define CONFIG_USB_EHCI_FRAME_LIST_SIZE 1024 + +#endif \ No newline at end of file diff --git a/bsp/nrf5x/nrf52840/board/sdk_config.h b/bsp/nrf5x/nrf52840/board/sdk_config.h index 25fa4938fa..0f0d9c1854 100644 --- a/bsp/nrf5x/nrf52840/board/sdk_config.h +++ b/bsp/nrf5x/nrf52840/board/sdk_config.h @@ -2472,7 +2472,7 @@ // NRFX_POWER_ENABLED - nrfx_power - POWER peripheral driver //========================================================== #ifndef NRFX_POWER_ENABLED -#define NRFX_POWER_ENABLED 0 +#define NRFX_POWER_ENABLED 1 #endif // NRFX_POWER_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority diff --git a/components/drivers/usb/cherryusb/Kconfig b/components/drivers/usb/cherryusb/Kconfig index 67c6ad543a..256690cbab 100644 --- a/components/drivers/usb/cherryusb/Kconfig +++ b/components/drivers/usb/cherryusb/Kconfig @@ -68,6 +68,8 @@ if RT_USING_CHERRYUSB bool "aic" config RT_CHERRYUSB_DEVICE_PUSB2 bool "pusb2" + config RT_CHERRYUSB_DEVICE_NRF5X + bool "nrf5x" endchoice config RT_CHERRYUSB_DEVICE_CDC_ACM diff --git a/components/drivers/usb/cherryusb/SConscript b/components/drivers/usb/cherryusb/SConscript index 58d0aa01c2..29cbd4ae80 100644 --- a/components/drivers/usb/cherryusb/SConscript +++ b/components/drivers/usb/cherryusb/SConscript @@ -28,6 +28,8 @@ if GetDepend(['RT_CHERRYUSB_DEVICE']): if GetDepend(['RT_CHERRYUSB_DEVICE_SPEED_HS']): CPPDEFINES+=['CONFIG_USB_HS'] + if GetDepend(['RT_CHERRYUSB_DEVICE_NRF5X']): + src += Glob('port/nrf5x/usb_dc_nrf5x.c') if GetDepend(['RT_CHERRYUSB_DEVICE_FSDEV']): src += Glob('port/fsdev/usb_dc_fsdev.c') if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_ST']): diff --git a/components/drivers/usb/cherryusb/port/nrf5x/README.md b/components/drivers/usb/cherryusb/port/nrf5x/README.md new file mode 100644 index 0000000000..5f976a0272 --- /dev/null +++ b/components/drivers/usb/cherryusb/port/nrf5x/README.md @@ -0,0 +1,9 @@ +# Note + +## Support Chip List + +- NRF5x + +## Before Use + +- Your should implement `usb_dc_low_level_pre_init`,`usb_dc_low_level_post_init`,`usb_dc_low_level_deinit`. \ No newline at end of file diff --git a/components/drivers/usb/cherryusb/port/nrf5x/usb_dc_nrf5x.c b/components/drivers/usb/cherryusb/port/nrf5x/usb_dc_nrf5x.c new file mode 100644 index 0000000000..6e42c690c6 --- /dev/null +++ b/components/drivers/usb/cherryusb/port/nrf5x/usb_dc_nrf5x.c @@ -0,0 +1,1321 @@ +#include +#include +#include "nrf52840.h" +#include "usb_def.h" +#include "usb_dc.h" +#include "usbd_core.h" + +#ifndef USBD_IRQHandler +#define USBD_IRQHandler USBD_IRQHandler /*!< Use actual usb irq name instead */ +#endif + +#ifndef USBD_CONFIG_ISO_IN_ZLP +#define USBD_CONFIG_ISO_IN_ZLP 0 +#endif + +/*!< The default platform is NRF52840 */ +#define NRF52_SERIES +/*!< Ep nums */ +#define EP_NUMS 9 +/*!< Ep mps */ +#define EP_MPS 64 +/*!< Nrf5x special */ +#define EP_ISO_NUM 8 + +/*!< USBD peripheral address base */ +#define NRF_USBD_BASE 0x40027000UL +/*!< Clock peripheral address base */ +#define NRF_CLOCK_BASE 0x40000000UL + +#define USBD_IRQn 39 + +#ifndef EP_ISO_MPS +#define EP_ISO_MPS 64 +#endif + +#define CHECK_ADD_IS_RAM(address) ((((uint32_t)address) & 0xE0000000u) == 0x20000000u) ? 1 : 0 + +/** + * @brief Endpoint information structure + */ +typedef struct _usbd_ep_info +{ + uint16_t mps; /*!< Maximum packet length of endpoint */ + uint8_t eptype; /*!< Endpoint Type */ + uint8_t ep_stalled; /* Endpoint stall flag */ + uint8_t ep_enable; /* Endpoint enable */ + uint8_t *xfer_buf; + uint32_t xfer_len; + uint32_t actual_xfer_len; + uint8_t ep_buffer[EP_MPS]; + /*!< Other endpoint parameters that may be used */ + volatile uint8_t is_using_dma; + volatile uint8_t add_flag; +} usbd_ep_info; + +/*!< nrf52840 usb */ +struct _nrf52840_core_prvi +{ + uint8_t address; /*!< address */ + usbd_ep_info ep_in[EP_NUMS]; /*!< ep in */ + usbd_ep_info ep_out[EP_NUMS]; /*!< ep out */ + struct usb_setup_packet setup; /*!< Setup package that may be used in interrupt processing (outside the protocol stack) */ + volatile uint8_t dma_running; + int8_t in_count; + volatile uint8_t iso_turn; + volatile uint8_t iso_tx_is_ready; + /** + * For nrf5x, easydma will not move the setup packet into RAM. + * We use a flag bit to judge whether the host sends setup, + * and then notify usbd_ep_read to and from the register to read the setup package + */ + volatile uint8_t is_setup_packet; +} usb_dc_cfg; + +__WEAK void usb_dc_low_level_pre_init(void) +{ +} + +__WEAK void usb_dc_low_level_post_init(void) +{ +} + +__WEAK void usb_dc_low_level_deinit(void) +{ +} + + +/**@brief Usbds the set remote wakeup. + * + * @param[in] busid do not used + * + * @retval + */ +int usbd_set_remote_wakeup (uint8_t busid) +{ + //TOGGLE_GPIOA9(); + //#TASK 1.唤醒使能+挂起标志+休眠标志 + // USB->CNTR |= (uint16_t)USB_CNTR_RESUME; + return -1; +} + +/** + * @brief Get setup package + * @pre None + * @param[in] setup Pointer to the address where the setup package is stored + * @retval None + */ +static inline void get_setup_packet(struct usb_setup_packet *setup) +{ + setup->bmRequestType = (uint8_t)(NRF_USBD->BMREQUESTTYPE); + setup->bRequest = (uint8_t)(NRF_USBD->BREQUEST); + setup->wIndex = (uint16_t)(NRF_USBD->WINDEXL | ((NRF_USBD->WINDEXH) << 8)); + setup->wLength = (uint16_t)(NRF_USBD->WLENGTHL | ((NRF_USBD->WLENGTHH) << 8)); + setup->wValue = (uint16_t)(NRF_USBD->WVALUEL | ((NRF_USBD->WVALUEH) << 8)); +} + +/** + * @brief Set tx easydma + * @pre None + * @param[in] ep End point address + * @param[in] ptr Data ram ptr + * @param[in] maxcnt Max length + * @retval None + */ +static void nrf_usbd_ep_easydma_set_tx(uint8_t ep, uint32_t ptr, uint32_t maxcnt) +{ + uint8_t epid = USB_EP_GET_IDX(ep); + if (epid == EP_ISO_NUM) + { + NRF_USBD->ISOIN.PTR = ptr; + NRF_USBD->ISOIN.MAXCNT = maxcnt; + return; + } + NRF_USBD->EPIN[epid].PTR = ptr; + NRF_USBD->EPIN[epid].MAXCNT = maxcnt; +} + +/** + * @brief Set rx easydma + * @pre None + * @param[in] ep End point address + * @param[in] ptr Data ram ptr + * @param[in] maxcnt Max length + * @retval None + */ +static void nrf_usbd_ep_easydma_set_rx(uint8_t ep, uint32_t ptr, uint32_t maxcnt) +{ + uint8_t epid = USB_EP_GET_IDX(ep); + if (epid == EP_ISO_NUM) + { + NRF_USBD->ISOOUT.PTR = ptr; + NRF_USBD->ISOOUT.MAXCNT = maxcnt; + return; + } + NRF_USBD->EPOUT[epid].PTR = ptr; + NRF_USBD->EPOUT[epid].MAXCNT = maxcnt; +} + +/**@brief Usbds the set address. + * + * @param[in] usbid do not used + * @param[in] address 8-bit valid address + * + * @retval >=0 success otherwise failureSet addressNone + */ +int usbd_set_address(uint8_t usbid, const uint8_t address) +{ + if (address == 0) + { + /*!< init 0 address */ + } + else + { + /*!< For non-0 addresses, write the address to the register in the state phase of setting the address */ + } + + NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE; + NRF_USBD->EVENTS_USBEVENT = 0; + + NRF_USBD->INTENSET = USBD_INTEN_USBEVENT_Msk; + /*!< nothing to do, handled by hardware; but don't STALL */ + usb_dc_cfg.address = address; + return 0; +} + +uint8_t usbd_get_port_speed(const uint8_t port) +{ + return USB_SPEED_FULL; +} + +/**@brief Usbds the ep open. + * + * @param[in] busid do not used + * @param[in] ep_cfg Endpoint configuration structure pointer + * + * @retval >=0 success otherwise failureOpen endpointNone + */ +int usbd_ep_open(uint8_t busid, const struct usb_endpoint_descriptor *ep_cfg) +{ + /*!< ep id */ + uint8_t epid = USB_EP_GET_IDX(ep_cfg->bEndpointAddress); + /*!< ep max packet length */ + uint16_t mps = ep_cfg->wMaxPacketSize; + if (USB_EP_DIR_IS_IN(ep_cfg->bEndpointAddress)) + { + /*!< In */ + usb_dc_cfg.ep_in[epid].mps = mps; + usb_dc_cfg.ep_in[epid].eptype = ep_cfg->bmAttributes; + usb_dc_cfg.ep_in[epid].ep_enable = true; + /*!< Open ep */ + if (ep_cfg->bmAttributes != USB_ENDPOINT_TYPE_ISOCHRONOUS) + { + /*!< Enable endpoint interrupt */ + NRF_USBD->INTENSET = (1 << (USBD_INTEN_ENDEPIN0_Pos + epid)); + /*!< Enable the in endpoint host to respond when sending in token */ + NRF_USBD->EPINEN |= (1 << (epid)); + __ISB(); + __DSB(); + } + else + { + NRF_USBD->EVENTS_ENDISOIN = 0; + /*!< SPLIT ISO buffer when ISO OUT endpoint is already opened. */ + if (usb_dc_cfg.ep_out[EP_ISO_NUM].mps) + NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN; + + /*!< Clear SOF event in case interrupt was not enabled yet. */ + if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) + NRF_USBD->EVENTS_SOF = 0; + + /*!< Enable SOF and ISOIN interrupts, and ISOIN endpoint. */ + NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk; + NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk; + } + } + else if (USB_EP_DIR_IS_OUT(ep_cfg->bEndpointAddress)) + { + /*!< Out */ + usb_dc_cfg.ep_out[epid].mps = mps; + usb_dc_cfg.ep_out[epid].eptype = ep_cfg->bmAttributes; + usb_dc_cfg.ep_out[epid].ep_enable = true; + /*!< Open ep */ + if (ep_cfg->bmAttributes != USB_ENDPOINT_TYPE_ISOCHRONOUS) + { + NRF_USBD->INTENSET = (1 << (USBD_INTEN_ENDEPOUT0_Pos + epid)); + NRF_USBD->EPOUTEN |= (1 << (epid)); + __ISB(); + __DSB(); + /*!< Write any value to SIZE register will allow nRF to ACK/accept data */ + NRF_USBD->SIZE.EPOUT[epid] = 0; + } + else + { + /*!< SPLIT ISO buffer when ISO IN endpoint is already opened. */ + if (usb_dc_cfg.ep_in[EP_ISO_NUM].mps) + NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN; + + /*!< Clear old events */ + NRF_USBD->EVENTS_ENDISOOUT = 0; + + /*!< Clear SOF event in case interrupt was not enabled yet. */ + if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) + NRF_USBD->EVENTS_SOF = 0; + + /*!< Enable SOF and ISOOUT interrupts, and ISOOUT endpoint. */ + NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk; + NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk; + } + } + + /*!< Clear stall and reset DataToggle */ + NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | (ep_cfg->bEndpointAddress); + NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | (ep_cfg->bEndpointAddress); + + __ISB(); + __DSB(); + + return 0; +} + +/**@brief Usbds the ep close. + * + * @param[in] busid do not used + * @param[in] ep ep : Endpoint address + * + * @retval >=0 success otherwise failureClose endpointNone + */ +int usbd_ep_close(uint8_t busid, const uint8_t ep) +{ + /*!< ep id */ + uint8_t epid = USB_EP_GET_IDX(ep); + if (epid != EP_ISO_NUM) + { + + if (USB_EP_DIR_IS_OUT(ep)) + { + usb_dc_cfg.ep_out[epid].ep_enable = false; + NRF_USBD->INTENCLR = (1 << (USBD_INTEN_ENDEPOUT0_Pos + epid)); + NRF_USBD->EPOUTEN &= ~(1 << (epid)); + } + else + { + usb_dc_cfg.ep_in[epid].ep_enable = false; + NRF_USBD->INTENCLR = (1 << (USBD_INTEN_ENDEPIN0_Pos + epid)); + NRF_USBD->EPINEN &= ~(1 << (epid)); + } + } + else + { + /*!< ISO EP */ + if (USB_EP_DIR_IS_OUT(ep)) + { + usb_dc_cfg.ep_out[epid].ep_enable = false; + usb_dc_cfg.ep_out[EP_ISO_NUM].mps = 0; + NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOOUT_Msk; + NRF_USBD->EPOUTEN &= ~USBD_EPOUTEN_ISOOUT_Msk; + NRF_USBD->EVENTS_ENDISOOUT = 0; + } + else + { + usb_dc_cfg.ep_in[epid].ep_enable = false; + usb_dc_cfg.ep_in[EP_ISO_NUM].mps = 0; + NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOIN_Msk; + NRF_USBD->EPINEN &= ~USBD_EPINEN_ISOIN_Msk; + } + /*!< One of the ISO endpoints closed, no need to split buffers any more. */ + NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir; + /*!< When both ISO endpoint are close there is no need for SOF any more. */ + if (usb_dc_cfg.ep_in[EP_ISO_NUM].mps + usb_dc_cfg.ep_out[EP_ISO_NUM].mps == 0) + { + NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk; + } + } + __ISB(); + __DSB(); + + return 0; +} + +/** + * @brief Setup in ep transfer setting and start transfer. + * + * This function is asynchronous. + * This function is similar to uart with tx dma. + * + * This function is called to write data to the specified endpoint. The + * supplied usbd_endpoint_callback function will be called when data is transmitted + * out. + * + * @param[in] busid do not used + * @param[in] ep Endpoint address corresponding to the one + * listed in the device configuration table + * @param[in] data Pointer to data to write + * @param[in] data_len Length of the data requested to write. This may + * be zero for a zero length status packet. + * @return 0 on success, negative errno code on fail. + */ +int usbd_ep_start_write(uint8_t busid, const uint8_t ep, const uint8_t *data, uint32_t data_len) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (!data && data_len) + { + return -1; + } + if (!usb_dc_cfg.ep_in[ep_idx].ep_enable) + { + return -2; + } + if ((uint32_t)data & 0x03) + { + return -3; + } + + usb_dc_cfg.ep_in[ep_idx].xfer_buf = (uint8_t *)data; + usb_dc_cfg.ep_in[ep_idx].xfer_len = data_len; + usb_dc_cfg.ep_in[ep_idx].actual_xfer_len = 0; + + if (data_len == 0) + { + /*!< write 0 len data */ + nrf_usbd_ep_easydma_set_tx(ep_idx, (uint32_t)NULL, 0); + NRF_USBD->TASKS_STARTEPIN[ep_idx] = 1; + } + else + { + /*!< Not zlp */ + data_len = MIN(data_len, usb_dc_cfg.ep_in[ep_idx].mps); + if (!CHECK_ADD_IS_RAM(data)) + { + /*!< Data is not in ram */ + /*!< Memcpy data to ram */ + memcpy(usb_dc_cfg.ep_in[ep_idx].ep_buffer, data, data_len); + nrf_usbd_ep_easydma_set_tx(ep_idx, (uint32_t)usb_dc_cfg.ep_in[ep_idx].ep_buffer, data_len); + } + else + { + nrf_usbd_ep_easydma_set_tx(ep_idx, (uint32_t)data, data_len); + } + /** + * Note that starting DMA transmission is to transmit data to USB peripherals, + * and then wait for the host to get it + */ + /*!< Start dma transfer */ + if (ep_idx != EP_ISO_NUM) + { + NRF_USBD->TASKS_STARTEPIN[ep_idx] = 1; + } + else + { + // NRF_USBD->TASKS_STARTISOIN = 1; + usb_dc_cfg.iso_tx_is_ready = 1; + } + } + return 0; +} + +/** + * @brief Setup out ep transfer setting and start transfer. + * + * This function is asynchronous. + * This function is similar to uart with rx dma. + * + * This function is called to read data to the specified endpoint. The + * supplied usbd_endpoint_callback function will be called when data is received + * in. + * + * @param[in] busid do not used + * @param[in] ep Endpoint address corresponding to the one + * listed in the device configuration table + * @param[in] data Pointer to data to read + * @param[in] data_len Max length of the data requested to read. + * + * @return 0 on success, negative errno code on fail. + */ +int usbd_ep_start_read(uint8_t busid, const uint8_t ep, uint8_t *data, uint32_t data_len) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (!data && data_len) + { + return -1; + } + if (!usb_dc_cfg.ep_out[ep_idx].ep_enable) + { + return -2; + } + if ((uint32_t)data & 0x03) + { + return -3; + } + + usb_dc_cfg.ep_out[ep_idx].xfer_buf = (uint8_t *)data; + usb_dc_cfg.ep_out[ep_idx].xfer_len = data_len; + usb_dc_cfg.ep_out[ep_idx].actual_xfer_len = 0; + + if (data_len == 0) + { + return 0; + } + else + { + data_len = MIN(data_len, usb_dc_cfg.ep_out[ep_idx].mps); + if (!CHECK_ADD_IS_RAM(data)) + { + /*!< Data address is not in ram */ + // TODO: + } + else + { + if (ep_idx == 0) + { + NRF_USBD->TASKS_EP0RCVOUT = 1; + } + nrf_usbd_ep_easydma_set_rx(ep_idx, (uint32_t)data, data_len); + } + } + return 0; +} + +/** + * @brief Endpoint setting pause + * @pre None + * @param[in] busid do not used + * @param[in] ep : Endpoint address + * @retval >=0 success otherwise failure + */ +int usbd_ep_set_stall(uint8_t busid, const uint8_t ep) +{ + /*!< ep id */ + uint8_t epid = USB_EP_GET_IDX(ep); + if (epid == 0) + { + NRF_USBD->TASKS_EP0STALL = 1; + } + else if (epid != EP_ISO_NUM) + { + NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | (ep); + } + __ISB(); + __DSB(); + return 0; +} + +/** + * @brief Endpoint clear pause + * @pre None + * @param[in] busid do not used + * @param[in] ep : Endpoint address + * @retval >=0 success otherwise failure + */ +int usbd_ep_clear_stall(uint8_t usbid, const uint8_t ep) +{ + /*!< ep id */ + uint8_t epid = USB_EP_GET_IDX(ep); + + if (epid != 0 && epid != EP_ISO_NUM) + { + /** + * reset data toggle to DATA0 + * First write this register with VALUE=Nop to select the endpoint, then either read it to get the status from + * VALUE, or write it again with VALUE=Data0 or Data1 + */ + NRF_USBD->DTOGGLE = ep; + NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep; + + /*!< Clear stall */ + NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep; + + /*!< Write any value to SIZE register will allow nRF to ACK/accept data */ + if (USB_EP_DIR_IS_OUT(ep)) + NRF_USBD->SIZE.EPOUT[epid] = 0; + + __ISB(); + __DSB(); + } + + return 0; +} + +/** + * @brief Check endpoint status + * @pre None + * @param[in] busid do not used + * @param[in] ep : Endpoint address + * @param[out] stalled : Outgoing endpoint status + * @retval >=0 success otherwise failure + */ +int usbd_ep_is_stalled(uint8_t busid, const uint8_t ep, uint8_t *stalled) +{ + return 0; +} + +/** + * @brief USB initialization + * @pre None + * @param[in] busid do not used + * @retval >=0 success otherwise failure + */ +int usb_dc_init(uint8_t busid) +{ + /*!< dc init */ + usb_dc_low_level_pre_init(); + + memset(&usb_dc_cfg, 0, sizeof(usb_dc_cfg)); + /*!< Clear USB Event Interrupt */ + NRF_USBD->EVENTS_USBEVENT = 0; + NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE; + + /*!< Reset interrupt */ + NRF_USBD->INTENCLR = NRF_USBD->INTEN; + NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk | + USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk | USBD_INTEN_STARTED_Msk; + + usb_dc_low_level_post_init(); + return 0; +} + +/** + * @brief USB deinit + * @pre None + * @param[in] busid do not used + * @retval >=0 success otherwise failure + */ +int usb_dc_deinit(uint8_t busid) +{ + /*!< dc deinit */ + return 0; +} + +/** + * @brief USB interrupt processing function + * @pre None + * @param[in] None + * @retval None + */ +void USBD_IRQHandler(void) +{ + uint32_t const inten = NRF_USBD->INTEN; + uint32_t int_status = 0; + volatile uint32_t usb_event = 0; + volatile uint32_t *regevt = &NRF_USBD->EVENTS_USBRESET; + + /*!< Traverse USB events */ + for (uint8_t i = 0; i < USBD_INTEN_EPDATA_Pos + 1; i++) + { + if ((inten & (1 << i)) && regevt[i]) + { + int_status |= (1 << (i)); + /*!< event clear */ + regevt[i] = 0; + __ISB(); + __DSB(); + } + } + + /*!< bit 24 */ + if (int_status & USBD_INTEN_EPDATA_Msk) + { + /*!< out ep */ + for (uint8_t ep_out_ct = 1; ep_out_ct <= 7; ep_out_ct++) + { + if ((NRF_USBD->EPDATASTATUS) & (1 << (16 + ep_out_ct))) + { + NRF_USBD->EPDATASTATUS |= (1 << (16 + ep_out_ct)); + /*!< The data arrives at the usb fifo, starts the dma transmission, and transfers it to the user ram */ + NRF_USBD->TASKS_STARTEPOUT[ep_out_ct] = 1; + } + } + /*!< in ep */ + for (uint8_t ep_in_ct = 1; ep_in_ct <= 7; ep_in_ct++) + { + if ((NRF_USBD->EPDATASTATUS) & (1 << (0 + ep_in_ct))) + { + /*!< in ep tranfer to host successfully */ + NRF_USBD->EPDATASTATUS |= (1 << (0 + ep_in_ct)); + if (usb_dc_cfg.ep_in[ep_in_ct].xfer_len > usb_dc_cfg.ep_in[ep_in_ct].mps) + { + /*!< Need start in again */ + usb_dc_cfg.ep_in[ep_in_ct].xfer_buf += usb_dc_cfg.ep_in[ep_in_ct].mps; + usb_dc_cfg.ep_in[ep_in_ct].xfer_len -= usb_dc_cfg.ep_in[ep_in_ct].mps; + usb_dc_cfg.ep_in[ep_in_ct].actual_xfer_len += usb_dc_cfg.ep_in[ep_in_ct].mps; + if (usb_dc_cfg.ep_in[ep_in_ct].xfer_len > usb_dc_cfg.ep_in[ep_in_ct].mps) + { + nrf_usbd_ep_easydma_set_tx(ep_in_ct, (uint32_t)usb_dc_cfg.ep_in[ep_in_ct].xfer_buf, usb_dc_cfg.ep_in[ep_in_ct].mps); + } + else + { + nrf_usbd_ep_easydma_set_tx(ep_in_ct, (uint32_t)usb_dc_cfg.ep_in[ep_in_ct].xfer_buf, usb_dc_cfg.ep_in[ep_in_ct].xfer_len); + } + NRF_USBD->TASKS_STARTEPIN[ep_in_ct] = 1; + } + else + { + usb_dc_cfg.ep_in[ep_in_ct].actual_xfer_len += usb_dc_cfg.ep_in[ep_in_ct].xfer_len; + usb_dc_cfg.ep_in[ep_in_ct].xfer_len = 0; + usbd_event_ep_in_complete_handler(0, ep_in_ct | 0x80, usb_dc_cfg.ep_in[ep_in_ct].actual_xfer_len); + } + } + } + } + + /*!< bit 23 */ + if (int_status & USBD_INTEN_EP0SETUP_Msk) + { + /* Setup */ + /*!< Storing this setup package will help the following procedures */ + get_setup_packet(&(usb_dc_cfg.setup)); + /*!< Nrf52840 will set the address automatically by hardware, + so the protocol stack of the address setting command sent by the host does not need to be processed */ + + if (usb_dc_cfg.setup.wLength == 0) + { + NRF_USBD->TASKS_EP0STATUS = 1; + } + + if (usb_dc_cfg.setup.bRequest != USB_REQUEST_SET_ADDRESS) + { + usbd_event_ep0_setup_complete_handler(0, (uint8_t *)&(usb_dc_cfg.setup)); + } + } + + /*!< bit 22 */ + if (int_status & USBD_INTEN_USBEVENT_Msk) + { + usb_event = NRF_USBD->EVENTCAUSE; + NRF_USBD->EVENTCAUSE = usb_event; + if (usb_event & USBD_EVENTCAUSE_SUSPEND_Msk) + { + NRF_USBD->LOWPOWER = 1; + /*!< */ + } + if (usb_event & USBD_EVENTCAUSE_RESUME_Msk) + { + /*!< */ + } + if (usb_event & USBD_EVENTCAUSE_USBWUALLOWED_Msk) + { + NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume; + NRF_USBD->TASKS_DPDMDRIVE = 1; + /** + * There is no Resume interrupt for remote wakeup, enable SOF for to report bus ready state + * Clear SOF event in case interrupt was not enabled yet. + */ + if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) + NRF_USBD->EVENTS_SOF = 0; + NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk; + } + } + + /*!< bit 21 */ + if (int_status & USBD_INTEN_SOF_Msk) + { + bool iso_enabled = false; + /*!< ISOOUT: Transfer data gathered in previous frame from buffer to RAM */ + if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk) + { + iso_enabled = true; + /*!< If ZERO bit is set, ignore ISOOUT length */ + if ((NRF_USBD->SIZE.ISOOUT) & USBD_SIZE_ISOOUT_ZERO_Msk) + { + } + else + { + /*!< Trigger DMA move data from Endpoint -> SRAM */ + NRF_USBD->TASKS_STARTISOOUT = 1; + /*!< EP_ISO_NUM out using dma */ + usb_dc_cfg.ep_out[EP_ISO_NUM].is_using_dma = 1; + } + } + + /*!< ISOIN: Notify client that data was transferred */ + if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk) + { + iso_enabled = true; + if (usb_dc_cfg.iso_tx_is_ready == 1) + { + usb_dc_cfg.iso_tx_is_ready = 0; + NRF_USBD->TASKS_STARTISOIN = 1; + } + } + + if (!iso_enabled) + { + /** + * ISO endpoint is not used, SOF is only enabled one-time for remote wakeup + * so we disable it now + */ + NRF_USBD->INTENCLR = USBD_INTENSET_SOF_Msk; + } + } + + /*!< bit 20 */ + if (int_status & USBD_INTEN_ENDISOOUT_Msk) + { + if (usb_dc_cfg.ep_out[EP_ISO_NUM].is_using_dma == 1) + { + usb_dc_cfg.ep_out[EP_ISO_NUM].is_using_dma = 0; + uint32_t read_count = NRF_USBD->SIZE.ISOOUT; + usb_dc_cfg.ep_out[EP_ISO_NUM].xfer_buf += read_count; + usb_dc_cfg.ep_out[EP_ISO_NUM].actual_xfer_len += read_count; + usb_dc_cfg.ep_out[EP_ISO_NUM].xfer_len -= read_count; + + if ((read_count < usb_dc_cfg.ep_out[EP_ISO_NUM].mps) || (usb_dc_cfg.ep_out[EP_ISO_NUM].xfer_len == 0)) + { + usbd_event_ep_out_complete_handler(0, ((EP_ISO_NUM)&0x7f), usb_dc_cfg.ep_out[EP_ISO_NUM].actual_xfer_len); + } + else + { + if (usb_dc_cfg.ep_out[EP_ISO_NUM].xfer_len > usb_dc_cfg.ep_out[EP_ISO_NUM].mps) + { + nrf_usbd_ep_easydma_set_rx(((EP_ISO_NUM)&0x7f), (uint32_t)usb_dc_cfg.ep_out[EP_ISO_NUM].xfer_buf, usb_dc_cfg.ep_out[EP_ISO_NUM].mps); + } + else + { + nrf_usbd_ep_easydma_set_rx(((EP_ISO_NUM)&0x7f), (uint32_t)usb_dc_cfg.ep_out[EP_ISO_NUM].xfer_buf, usb_dc_cfg.ep_out[EP_ISO_NUM].xfer_len); + } + } + } + } + + /** + * Traverse ordinary out endpoint events, starting from endpoint 1 to endpoint 7, + * and end 0 for special processing + */ + for (uint8_t offset = 0; offset < 7; offset++) + { + if (int_status & (USBD_INTEN_ENDEPOUT1_Msk << offset)) + { + /*!< Out 'offset' transfer complete */ + uint32_t read_count = NRF_USBD->SIZE.EPOUT[offset + 1]; + usb_dc_cfg.ep_out[offset + 1].xfer_buf += read_count; + usb_dc_cfg.ep_out[offset + 1].actual_xfer_len += read_count; + usb_dc_cfg.ep_out[offset + 1].xfer_len -= read_count; + + if ((read_count < usb_dc_cfg.ep_out[offset + 1].mps) || (usb_dc_cfg.ep_out[offset + 1].xfer_len == 0)) + { + usbd_event_ep_out_complete_handler(0, ((offset + 1) & 0x7f), usb_dc_cfg.ep_out[offset + 1].actual_xfer_len); + } + else + { + if (usb_dc_cfg.ep_out[offset + 1].xfer_len > usb_dc_cfg.ep_out[offset + 1].mps) + { + nrf_usbd_ep_easydma_set_rx(((offset + 1) & 0x7f), (uint32_t)usb_dc_cfg.ep_out[offset + 1].xfer_buf, usb_dc_cfg.ep_out[offset + 1].mps); + } + else + { + nrf_usbd_ep_easydma_set_rx(((offset + 1) & 0x7f), (uint32_t)usb_dc_cfg.ep_out[offset + 1].xfer_buf, usb_dc_cfg.ep_out[offset + 1].xfer_len); + } + } + } + } + + /*!< bit 12 */ + if (int_status & USBD_INTEN_ENDEPOUT0_Msk) + { + uint32_t read_count = NRF_USBD->SIZE.EPOUT[0]; + usb_dc_cfg.ep_out[0].actual_xfer_len += read_count; + usb_dc_cfg.ep_out[0].xfer_len -= read_count; + + if (usb_dc_cfg.ep_out[0].xfer_len == 0) + { + /*!< Enable the state phase of endpoint 0 */ + NRF_USBD->TASKS_EP0STATUS = 1; + } + + usbd_event_ep_out_complete_handler(0, 0x00, usb_dc_cfg.ep_out[0].actual_xfer_len); + } + + /*!< bit 11 */ + if (int_status & USBD_INTEN_ENDISOIN_Msk) + { + } + + /*!< bit 10 */ + if (int_status & USBD_INTEN_EP0DATADONE_Msk) + { + switch (usb_dc_cfg.setup.bmRequestType >> USB_REQUEST_DIR_SHIFT) + { + case 1: + /*!< IN */ + if (usb_dc_cfg.ep_in[0].xfer_len > usb_dc_cfg.ep_in[0].mps) + { + usb_dc_cfg.ep_in[0].xfer_len -= usb_dc_cfg.ep_in[0].mps; + usb_dc_cfg.ep_in[0].actual_xfer_len += usb_dc_cfg.ep_in[0].mps; + usbd_event_ep_in_complete_handler(0, 0 | 0x80, usb_dc_cfg.ep_in[0].actual_xfer_len); + } + else + { + usb_dc_cfg.ep_in[0].actual_xfer_len += usb_dc_cfg.ep_in[0].xfer_len; + usb_dc_cfg.ep_in[0].xfer_len = 0; + /*!< Enable the state phase of endpoint 0 */ + usbd_event_ep_in_complete_handler(0, 0 | 0x80, usb_dc_cfg.ep_in[0].actual_xfer_len); + NRF_USBD->TASKS_EP0STATUS = 1; + } + break; + case 0: + if (usb_dc_cfg.setup.bRequest != USB_REQUEST_SET_ADDRESS) + { + /*!< The data arrives at the usb fifo, starts the dma transmission, and transfers it to the user ram */ + NRF_USBD->TASKS_STARTEPOUT[0] = 1; + } + break; + } + } + + /** + * Traversing ordinary in endpoint events, starting from endpoint 1 to endpoint 7, + * endpoint 0 special processing + */ + for (uint8_t offset = 0; offset < 7; offset++) + { + if (int_status & (USBD_INTEN_ENDEPIN1_Msk << offset)) + { + /*!< DMA move data completed */ + } + } + + /*!< bit 1 */ + if (int_status & USBD_INTEN_STARTED_Msk) + { + /*!< Easy dma start transfer data */ + } + + /*!< bit 2 */ + if (int_status & USBD_INTEN_ENDEPIN0_Msk) + { + /*!< EP0 IN DMA move data completed */ + } + + /*!< bit 0 */ + if (int_status & USBD_INTEN_USBRESET_Msk) + { + NRF_USBD->EPOUTEN = 1UL; + NRF_USBD->EPINEN = 1UL; + + for (int i = 0; i < 8; i++) + { + NRF_USBD->TASKS_STARTEPIN[i] = 0; + NRF_USBD->TASKS_STARTEPOUT[i] = 0; + } + + NRF_USBD->TASKS_STARTISOIN = 0; + NRF_USBD->TASKS_STARTISOOUT = 0; + + /*!< Clear USB Event Interrupt */ + NRF_USBD->EVENTS_USBEVENT = 0; + NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE; + + /*!< Reset interrupt */ + NRF_USBD->INTENCLR = NRF_USBD->INTEN; + NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk | + USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk | USBD_INTEN_STARTED_Msk; + + usbd_event_reset_handler(0); + } +} + +/** + * Errata: USB cannot be enabled. + */ +static bool chyu_nrf52_errata_187(void) +{ +#ifndef NRF52_SERIES + return false; +#else +#if defined(NRF52820_XXAA) || defined(DEVELOP_IN_NRF52820) || defined(NRF52833_XXAA) || defined(DEVELOP_IN_NRF52833) || defined(NRF52840_XXAA) || defined(DEVELOP_IN_NRF52840) + uint32_t var1 = *(uint32_t *)0x10000130ul; + uint32_t var2 = *(uint32_t *)0x10000134ul; +#endif +#if defined(NRF52840_XXAA) || defined(DEVELOP_IN_NRF52840) + if (var1 == 0x08) + { + switch (var2) + { + case 0x00ul: + return false; + case 0x01ul: + return true; + case 0x02ul: + return true; + case 0x03ul: + return true; + default: + return true; + } + } +#endif +#if defined(NRF52833_XXAA) || defined(DEVELOP_IN_NRF52833) + if (var1 == 0x0D) + { + switch (var2) + { + case 0x00ul: + return true; + case 0x01ul: + return true; + default: + return true; + } + } +#endif +#if defined(NRF52820_XXAA) || defined(DEVELOP_IN_NRF52820) + if (var1 == 0x10) + { + switch (var2) + { + case 0x00ul: + return true; + case 0x01ul: + return true; + case 0x02ul: + return true; + default: + return true; + } + } +#endif + return false; +#endif +} + +/** + * Errata: USBD might not reach its active state. + */ +static bool chyu_nrf52_errata_171(void) +{ +#ifndef NRF52_SERIES + return false; +#else +#if defined(NRF52840_XXAA) || defined(DEVELOP_IN_NRF52840) + uint32_t var1 = *(uint32_t *)0x10000130ul; + uint32_t var2 = *(uint32_t *)0x10000134ul; +#endif +#if defined(NRF52840_XXAA) || defined(DEVELOP_IN_NRF52840) + if (var1 == 0x08) + { + switch (var2) + { + case 0x00ul: + return true; + case 0x01ul: + return true; + case 0x02ul: + return true; + case 0x03ul: + return true; + default: + return true; + } + } +#endif + return false; +#endif +} + +/** + * Errata: ISO double buffering not functional. + */ +static bool chyu_nrf52_errata_166(void) +{ +#ifndef NRF52_SERIES + return false; +#else +#if defined(NRF52840_XXAA) || defined(DEVELOP_IN_NRF52840) + uint32_t var1 = *(uint32_t *)0x10000130ul; + uint32_t var2 = *(uint32_t *)0x10000134ul; +#endif +#if defined(NRF52840_XXAA) || defined(DEVELOP_IN_NRF52840) + if (var1 == 0x08) + { + switch (var2) + { + case 0x00ul: + return true; + case 0x01ul: + return true; + case 0x02ul: + return true; + case 0x03ul: + return true; + default: + return true; + } + } +#endif + return false; +#endif +} + +#ifdef SOFTDEVICE_PRESENT + +#include "nrf_mbr.h" +#include "nrf_sdm.h" +#include "nrf_soc.h" + +#ifndef SD_MAGIC_NUMBER +#define SD_MAGIC_NUMBER 0x51B1E5DB +#endif + +static inline bool is_sd_existed(void) +{ + return *((uint32_t *)(SOFTDEVICE_INFO_STRUCT_ADDRESS + 4)) == SD_MAGIC_NUMBER; +} + +/** + * check if SD is existed and enabled + */ +static inline bool is_sd_enabled(void) +{ + if (!is_sd_existed()) + return false; + + uint8_t sd_en = false; + (void)sd_softdevice_is_enabled(&sd_en); + return sd_en; +} +#endif + +static bool hfclk_running(void) +{ +#ifdef SOFTDEVICE_PRESENT + if (is_sd_enabled()) + { + uint32_t is_running = 0; + (void)sd_clock_hfclk_is_running(&is_running); + return (is_running ? true : false); + } +#endif + +#if defined(CLOCK_HFCLKSTAT_SRC_Xtal) || defined(__NRFX_DOXYGEN__) + return (NRF_CLOCK->HFCLKSTAT & (CLOCK_HFCLKSTAT_STATE_Msk | CLOCK_HFCLKSTAT_SRC_Msk)) == + (CLOCK_HFCLKSTAT_STATE_Msk | (CLOCK_HFCLKSTAT_SRC_Xtal << CLOCK_HFCLKSTAT_SRC_Pos)); +#else + return (NRF_CLOCK->HFCLKSTAT & (CLOCK_HFCLKSTAT_STATE_Msk | CLOCK_HFCLKSTAT_SRC_Msk)) == + (CLOCK_HFCLKSTAT_STATE_Msk | (CLOCK_HFCLKSTAT_SRC_HFXO << CLOCK_HFCLKSTAT_SRC_Pos)); +#endif +} + +enum +{ + NRF_CLOCK_EVENT_HFCLKSTARTED = offsetof(NRF_CLOCK_Type, EVENTS_HFCLKSTARTED), /*!< HFCLK oscillator started. */ +}; + +enum +{ + NRF_CLOCK_TASK_HFCLKSTART = offsetof(NRF_CLOCK_Type, TASKS_HFCLKSTART), /*!< Start HFCLK clock source. */ + NRF_CLOCK_TASK_HFCLKSTOP = offsetof(NRF_CLOCK_Type, TASKS_HFCLKSTOP), /*!< Stop HFCLK clock source. */ +}; + +static void hfclk_enable(void) +{ + /*!< already running, nothing to do */ + if (hfclk_running()) + return; + +#ifdef SOFTDEVICE_PRESENT + if (is_sd_enabled()) + { + (void)sd_clock_hfclk_request(); + return; + } +#endif + + *((volatile uint32_t *)((uint8_t *)NRF_CLOCK + NRF_CLOCK_EVENT_HFCLKSTARTED)) = 0x0UL; + volatile uint32_t dummy = *((volatile uint32_t *)((uint8_t *)NRF_CLOCK + (uint32_t)NRF_CLOCK_EVENT_HFCLKSTARTED)); + (void)dummy; + *((volatile uint32_t *)((uint8_t *)NRF_CLOCK + NRF_CLOCK_TASK_HFCLKSTART)) = 0x1UL; +} + +static void hfclk_disable(void) +{ +#ifdef SOFTDEVICE_PRESENT + if (is_sd_enabled()) + { + (void)sd_clock_hfclk_release(); + return; + } +#endif + + *((volatile uint32_t *)((uint8_t *)NRF_CLOCK + NRF_CLOCK_TASK_HFCLKSTOP)) = 0x1UL; +} + +/** + * Power & Clock Peripheral on nRF5x to manage USB + * USB Bus power is managed by Power module, there are 3 VBUS power events: + * Detected, Ready, Removed. Upon these power events, This function will + * enable ( or disable ) usb & hfclk peripheral, set the usb pin pull up + * accordingly to the controller Startup/Standby Sequence in USBD 51.4 specs. + * Therefore this function must be called to handle USB power event by + * - nrfx_power_usbevt_init() : if Softdevice is not used or enabled + * - SoftDevice SOC event : if SD is used and enabled + */ +void cherry_usb_hal_nrf_power_event(uint32_t event) +{ + enum + { + USB_EVT_DETECTED = 0, + USB_EVT_REMOVED = 1, + USB_EVT_READY = 2 + }; + + switch (event) + { + case USB_EVT_DETECTED: + if (!NRF_USBD->ENABLE) + { + /*!< Prepare for receiving READY event: disable interrupt since we will blocking wait */ + NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk; + NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk; + __ISB(); + __DSB(); + +#ifdef NRF52_SERIES /*!< NRF53 does not need this errata */ + /*!< ERRATA 171, 187, 166 */ + if (chyu_nrf52_errata_187()) + { + if (*((volatile uint32_t *)(0x4006EC00)) == 0x00000000) + { + *((volatile uint32_t *)(0x4006EC00)) = 0x00009375; + *((volatile uint32_t *)(0x4006ED14)) = 0x00000003; + *((volatile uint32_t *)(0x4006EC00)) = 0x00009375; + } + else + { + *((volatile uint32_t *)(0x4006ED14)) = 0x00000003; + } + } + + if (chyu_nrf52_errata_171()) + { + if (*((volatile uint32_t *)(0x4006EC00)) == 0x00000000) + { + *((volatile uint32_t *)(0x4006EC00)) = 0x00009375; + *((volatile uint32_t *)(0x4006EC14)) = 0x000000C0; + *((volatile uint32_t *)(0x4006EC00)) = 0x00009375; + } + else + { + *((volatile uint32_t *)(0x4006EC14)) = 0x000000C0; + } + } +#endif + + /*!< Enable the peripheral (will cause Ready event) */ + NRF_USBD->ENABLE = 1; + __ISB(); + __DSB(); + + /*!< Enable HFCLK */ + hfclk_enable(); + } + break; + + case USB_EVT_READY: + /** + * Skip if pull-up is enabled and HCLK is already running. + * Application probably call this more than necessary. + */ + if (NRF_USBD->USBPULLUP && hfclk_running()) + break; + + /*!< Waiting for USBD peripheral enabled */ + while (!(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE)) + { + } + + NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk; + __ISB(); + __DSB(); + +#ifdef NRF52_SERIES + if (chyu_nrf52_errata_171()) + { + if (*((volatile uint32_t *)(0x4006EC00)) == 0x00000000) + { + *((volatile uint32_t *)(0x4006EC00)) = 0x00009375; + *((volatile uint32_t *)(0x4006EC14)) = 0x00000000; + *((volatile uint32_t *)(0x4006EC00)) = 0x00009375; + } + else + { + *((volatile uint32_t *)(0x4006EC14)) = 0x00000000; + } + } + + if (chyu_nrf52_errata_187()) + { + if (*((volatile uint32_t *)(0x4006EC00)) == 0x00000000) + { + *((volatile uint32_t *)(0x4006EC00)) = 0x00009375; + *((volatile uint32_t *)(0x4006ED14)) = 0x00000000; + *((volatile uint32_t *)(0x4006EC00)) = 0x00009375; + } + else + { + *((volatile uint32_t *)(0x4006ED14)) = 0x00000000; + } + } + + if (chyu_nrf52_errata_166()) + { + *((volatile uint32_t *)(NRF_USBD_BASE + 0x800)) = 0x7E3; + *((volatile uint32_t *)(NRF_USBD_BASE + 0x804)) = 0x40; + + __ISB(); + __DSB(); + } +#endif + + /*!< ISO buffer Lower half for IN, upper half for OUT */ + NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN; + + /*!< Enable bus-reset interrupt */ + NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk; + + /*!< Enable interrupt, priorities should be set by application */ + /*!< clear pending irq */ + NVIC->ICPR[(((uint32_t)USBD_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)USBD_IRQn) & 0x1FUL)); + /** + * Don't enable USBD interrupt yet, if dcd_init() did not finish yet + * Interrupt will be enabled by tud_init(), when USB stack is ready + * to handle interrupts. + */ + /*!< Wait for HFCLK */ + while (!hfclk_running()) + { + } + + /*!< Enable pull up */ + NRF_USBD->USBPULLUP = 1; + __ISB(); + __DSB(); + break; + + case USB_EVT_REMOVED: + if (NRF_USBD->ENABLE) + { + /*!< Disable pull up */ + NRF_USBD->USBPULLUP = 0; + __ISB(); + __DSB(); + + /*!< Disable Interrupt */ + NVIC->ICER[(((uint32_t)USBD_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)USBD_IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + /*!< disable all interrupt */ + NRF_USBD->INTENCLR = NRF_USBD->INTEN; + + NRF_USBD->ENABLE = 0; + __ISB(); + __DSB(); + hfclk_disable(); + } + break; + + default: + break; + } +} \ No newline at end of file