diff --git a/Makefile b/Makefile index 55082e4..a979bfb 100644 --- a/Makefile +++ b/Makefile @@ -108,7 +108,7 @@ C_SOURCE_FILES += $(SRC_PATH)/dfu_init.c # nrfx C_SOURCE_FILES += $(NRFX_PATH)/drivers/src/nrfx_power.c -C_SOURCE_FILES += $(NRFX_PATH)/hal/nrf_nvmc.c +C_SOURCE_FILES += $(NRFX_PATH)/drivers/src/nrfx_nvmc.c # SDK 11 files C_SOURCE_FILES += $(SDK11_PATH)/libraries/bootloader_dfu/bootloader.c @@ -159,13 +159,11 @@ C_SOURCE_FILES += $(NRFX_PATH)/mdk/system_nrf52840.c # Tinyusb stack C_SOURCE_FILES += $(TUSB_PATH)/portable/nordic/nrf5x/dcd_nrf5x.c -C_SOURCE_FILES += $(TUSB_PATH)/portable/nordic/nrf5x/hal_nrf5x.c C_SOURCE_FILES += $(TUSB_PATH)/common/tusb_fifo.c C_SOURCE_FILES += $(TUSB_PATH)/device/usbd.c C_SOURCE_FILES += $(TUSB_PATH)/device/usbd_control.c C_SOURCE_FILES += $(TUSB_PATH)/class/cdc/cdc_device.c C_SOURCE_FILES += $(TUSB_PATH)/class/msc/msc_device.c -C_SOURCE_FILES += $(TUSB_PATH)/class/custom/custom_device.c C_SOURCE_FILES += $(TUSB_PATH)/tusb.c endif @@ -198,6 +196,7 @@ IPATH += $(NRFX_PATH) IPATH += $(NRFX_PATH)/mdk IPATH += $(NRFX_PATH)/hal IPATH += $(NRFX_PATH)/drivers/include +IPATH += $(NRFX_PATH)/drivers/src IPATH += $(SDK11_PATH)/libraries/bootloader_dfu/hci_transport IPATH += $(SDK11_PATH)/libraries/bootloader_dfu diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index c3342b8..1245622 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -119,9 +119,9 @@ static void wait_for_events(void) // Feed all Watchdog just in case application enable it // WDT cannot be disabled once started. It even last through soft reset (NVIC Reset) - if ( nrf_wdt_started() ) + if ( nrf_wdt_started(NRF_WDT) ) { - for (uint8_t i=0; i<8; i++) nrf_wdt_reload_request_set(i); + for (uint8_t i=0; i<8; i++) nrf_wdt_reload_request_set(NRF_WDT, i); } // Event received. Process it from the scheduler. @@ -194,8 +194,8 @@ static void bootloader_settings_save(bootloader_settings_t * p_settings) } else { - nrf_nvmc_page_erase(BOOTLOADER_SETTINGS_ADDRESS); - nrf_nvmc_write_words(BOOTLOADER_SETTINGS_ADDRESS, (uint32_t *) p_settings, sizeof(bootloader_settings_t) / 4); + nrfx_nvmc_page_erase(BOOTLOADER_SETTINGS_ADDRESS); + nrfx_nvmc_words_write(BOOTLOADER_SETTINGS_ADDRESS, (uint32_t *) p_settings, sizeof(bootloader_settings_t) / 4); pstorage_callback_handler(&m_bootsettings_handle, PSTORAGE_STORE_OP_CODE, NRF_SUCCESS, (uint8_t *) p_settings, sizeof(bootloader_settings_t)); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index ce3d995..60a1729 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -151,7 +151,7 @@ static void dfu_prepare_func_app_erase(uint32_t image_size) for ( uint32_t i = 0; i < page_count; i++ ) { - nrf_nvmc_page_erase(DFU_BANK_0_REGION_START + i * CODE_PAGE_SIZE); + nrfx_nvmc_page_erase(DFU_BANK_0_REGION_START + i * CODE_PAGE_SIZE); } // invoke complete callback diff --git a/src/flash_nrf5x.c b/src/flash_nrf5x.c index 931b89e..fe563ac 100644 --- a/src/flash_nrf5x.c +++ b/src/flash_nrf5x.c @@ -44,9 +44,9 @@ void flash_nrf5x_flush (bool need_erase) // - nRF52840 dfu serial/uf2 are USB-based which are DMA and should have no problems. // // Note: MSC uf2 does not erase page in advance like dfu serial - if ( need_erase ) nrf_nvmc_page_erase(_fl_addr); + if ( need_erase ) nrfx_nvmc_page_erase(_fl_addr); - nrf_nvmc_write_words(_fl_addr, (uint32_t *) _fl_buf, FLASH_PAGE_SIZE / 4); + nrfx_nvmc_words_write(_fl_addr, (uint32_t *) _fl_buf, FLASH_PAGE_SIZE / 4); } _fl_addr = FLASH_CACHE_INVALID_ADDR; diff --git a/src/flash_nrf5x.h b/src/flash_nrf5x.h index ef17adf..9ab4619 100644 --- a/src/flash_nrf5x.h +++ b/src/flash_nrf5x.h @@ -28,7 +28,7 @@ #include #include -#include "nrf_nvmc.h" +#include "nrfx_nvmc.h" #ifdef __cplusplus extern "C" { diff --git a/src/main.c b/src/main.c index 38581d4..fcc7831 100644 --- a/src/main.c +++ b/src/main.c @@ -65,8 +65,8 @@ #include "pstorage_platform.h" #include "nrf_mbr.h" #include "pstorage.h" +#include "nrfx_nvmc.h" -#include "nrf_nvmc.h" #ifdef NRF52840_XXAA #include "nrf_usbd.h" @@ -275,11 +275,11 @@ void adafruit_factory_reset(void) // clear all App Data if any if ( DFU_APP_DATA_RESERVED ) { - nrf_nvmc_page_erase(APPDATA_ADDR_START); + nrfx_nvmc_page_erase(APPDATA_ADDR_START); } // Only need to erase the 1st page of Application code to make it invalid - nrf_nvmc_page_erase(DFU_BANK_0_REGION_START); + nrfx_nvmc_page_erase(DFU_BANK_0_REGION_START); // back to normal led_state(STATE_FACTORY_RESET_FINISHED); diff --git a/src/nrfx_config.h b/src/nrfx_config.h index 118fcf6..419dc19 100644 --- a/src/nrfx_config.h +++ b/src/nrfx_config.h @@ -2,13 +2,17 @@ #define NRFX_CONFIG_H__ // Power -#define NRFX_POWER_ENABLED 1 -#define NRFX_POWER_CONFIG_IRQ_PRIORITY 7 +#define NRFX_POWER_ENABLED 1 +#define NRFX_POWER_DEFAULT_CONFIG_IRQ_PRIORITY 7 + +#define NRFX_CLOCK_ENABLED 0 + +#define NRFX_NVMC_ENABLED 1 // UART #ifdef NRF52832_XXAA -#define NRFX_UART_ENABLED 1 -#define NRFX_UART0_ENABLED 1 +#define NRFX_UART_ENABLED 1 +#define NRFX_UART0_ENABLED 1 #define NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY 7 #define NRFX_UART_DEFAULT_CONFIG_HWFC NRF_UART_HWFC_DISABLED diff --git a/src/usb/msc_uf2.c b/src/usb/msc_uf2.c index 11876b1..8db296c 100644 --- a/src/usb/msc_uf2.c +++ b/src/usb/msc_uf2.c @@ -45,38 +45,47 @@ int write_block(uint32_t block_no, uint8_t *data, bool quiet, WriteState *state) // tinyusb callbacks //--------------------------------------------------------------------+ +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) +{ + (void) lun; + + const char vid[] = "Adafruit"; + const char pid[] = "Bluefruit UF2"; + const char rev[] = "1.0"; + + memcpy(vendor_id , vid, strlen(vid)); + memcpy(product_id , pid, strlen(pid)); + memcpy(product_rev, rev, strlen(rev)); +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun) +{ + (void) lun; + return true; +} + // Callback invoked when received an SCSI command not in built-in list below // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE // - READ10 and WRITE10 has their own callbacks int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { void const* response = NULL; - int32_t resplen = 0; - memset(buffer, 0, bufsize); + uint16_t resplen = 0; - switch ( scsi_cmd[0] ) + // most scsi handled is input + bool in_xfer = true; + + switch (scsi_cmd[0]) { - case SCSI_CMD_TEST_UNIT_READY: - // Command that host uses to check our readiness before sending other commands - resplen = 0; - break; - case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: // Host is about to read/write etc ... better not to disconnect disk resplen = 0; break; - case SCSI_CMD_START_STOP_UNIT: - // Host try to eject/safe remove/poweroff us. We could safely disconnect with disk storage, or go into lower power - /* scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd; - // Start bit = 0 : low power mode, if load_eject = 1 : unmount disk storage as well - // Start bit = 1 : Ready mode, if load_eject = 1 : mount disk storage - start_stop->start; - start_stop->load_eject; - */ - resplen = 0; - break; - default: // Set Sense = Invalid Command Operation tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); @@ -86,13 +95,18 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, break; } - // return len must not larger than bufsize - if ( resplen > (int32_t)bufsize ) resplen = bufsize; + // return resplen must not larger than bufsize + if ( resplen > bufsize ) resplen = bufsize; - // copy response to stack's buffer if any - if ( response && resplen ) + if ( response && (resplen > 0) ) { - memcpy(buffer, response, resplen); + if(in_xfer) + { + memcpy(buffer, response, resplen); + }else + { + // SCSI output + } } return resplen; @@ -161,6 +175,8 @@ void tud_msc_write10_complete_cb(uint8_t lun) } } +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size +// Application update block count and block size void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) { (void) lun; @@ -169,4 +185,26 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_siz *block_size = 512; } +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) +{ + (void) lun; + (void) power_condition; + + if ( load_eject ) + { + if (start) + { + // load disk storage + }else + { + // unload disk storage + } + } + + return true; +} + #endif diff --git a/src/usb/tusb_config.h b/src/usb/tusb_config.h index 59f06fb..22611b2 100644 --- a/src/usb/tusb_config.h +++ b/src/usb/tusb_config.h @@ -58,11 +58,6 @@ //------------- Class enabled -------------// #define CFG_TUD_CDC 1 #define CFG_TUD_MSC 1 -#define CFG_TUD_HID_KEYBOARD 0 -#define CFG_TUD_HID_MOUSE 0 -#define CFG_TUD_HID_GENERIC 0 // not supported yet -#define CFG_TUD_CUSTOM_CLASS 0 - /*------------------------------------------------------------------*/ /* CLASS DRIVER @@ -72,34 +67,15 @@ #define CFG_TUD_CDC_RX_BUFSIZE 1024 #define CFG_TUD_CDC_TX_BUFSIZE 1024 -/* TX is sent automatically on every Start of Frame event ~ 1ms. - * If not enabled, application must call tud_cdc_flush() periodically - * Note: Enabled this could overflow device task, if it does, define - * CFG_TUD_TASK_QUEUE_SZ with large value - */ -#define CFG_TUD_CDC_FLUSH_ON_SOF 0 - -// Number of supported Logical Unit Number -#define CFG_TUD_MSC_MAXLUN 1 - // Buffer size for each read/write transfer, the more the better #define CFG_TUD_MSC_BUFSIZE (4*1024) -// Vendor name included in Inquiry response, max 8 bytes -#define CFG_TUD_MSC_VENDOR "Adafruit" - -// Product name included in Inquiry response, max 16 bytes -#define CFG_TUD_MSC_PRODUCT "Feather nRF52840" - -// Product revision string included in Inquiry response, max 4 bytes -#define CFG_TUD_MSC_PRODUCT_REV "1.0" - //--------------------------------------------------------------------+ // USB RAM PLACEMENT //--------------------------------------------------------------------+ #define CFG_TUSB_ATTR_USBRAM -#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4) +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) #define BREAKPOINT_IGNORE_COUNT(n) \ diff --git a/src/usb/usb.c b/src/usb/usb.c index c9dbc30..1137fbf 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -40,12 +40,8 @@ // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ -// from usb_desc.c for dynamic descriptor -extern tusb_desc_device_t usb_desc_dev; -extern usb_desc_cfg_t usb_desc_cfg; - // Serial string using unique Device ID -extern uint16_t usb_desc_str_serial[1+16]; +extern char usb_desc_str_serial[1+16]; /* tinyusb function that handles power event (detected, ready, removed) * We must call it within SD's SOC event handler, or set it as power event handler if SD is not enabled. */ @@ -94,24 +90,10 @@ void usb_init(bool cdc_only) tusb_hal_nrf_power_event(NRFX_POWER_USB_EVT_READY); } - if ( cdc_only ) - { - // Change PID to CDC only - usb_desc_dev.idProduct = USB_DESC_CDC_ONLY_PID; - - // Remove MSC interface = reduce total interface + adjust config desc length - usb_desc_cfg.config.bNumInterfaces--; - usb_desc_cfg.config.wTotalLength -= sizeof(usb_desc_cfg.msc); - } + usb_desc_set_mode(cdc_only); // Create Serial string descriptor - char tmp_serial[17]; - sprintf(tmp_serial, "%08lX%08lX", NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0]); - - for(uint8_t i=0; i<16; i++) - { - usb_desc_str_serial[1+i] = tmp_serial[i]; - } + sprintf(usb_desc_str_serial, "%08lX%08lX", NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0]); // Init tusb stack tusb_init(); @@ -124,7 +106,7 @@ void usb_teardown(void) // Abort all transfers // Disable pull up - nrf_usbd_pullup_disable(); + nrf_usbd_pullup_disable(NRF_USBD); // Disable Interrupt NVIC_DisableIRQ(USBD_IRQn); @@ -132,7 +114,7 @@ void usb_teardown(void) // disable all interrupt NRF_USBD->INTENCLR = NRF_USBD->INTEN; - nrf_usbd_disable(); + nrf_usbd_disable(NRF_USBD); sd_clock_hfclk_release(); sd_power_usbdetected_enable(false); diff --git a/src/usb/usb_desc.c b/src/usb/usb_desc.c index 18f4321..5ca1856 100644 --- a/src/usb/usb_desc.c +++ b/src/usb/usb_desc.c @@ -24,15 +24,6 @@ #include "usb_desc.h" - -/*------------- Interface Numbering -------------*/ -enum { - ITF_NUM_CDC = 0 , - ITF_NUM_CDC_DATA , - ITF_NUM_MSC , - ITF_NUM_TOTAL -}; - enum { ITF_STR_LANGUAGE = 0 , ITF_STR_MANUFACTURER , @@ -42,22 +33,95 @@ enum { ITF_STR_MSC }; -/*------------- Endpoint Numbering & Size -------------*/ -#define _EP_IN(x) (0x80 | (x)) -#define _EP_OUT(x) (x) +static bool _cdc_only = false; -// CDC -#define EP_CDC_NOTIF _EP_IN ( ITF_NUM_CDC+1 ) -#define EP_CDC_NOTIF_SIZE 8 +//--------------------------------------------------------------------+ +// Device Descriptor +//--------------------------------------------------------------------+ +tusb_desc_device_t desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, -#define EP_CDC_OUT _EP_OUT( ITF_NUM_CDC+2 ) -#define EP_CDC_IN _EP_IN ( ITF_NUM_CDC+2 ) + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, -// Mass Storage -#define EP_MSC_OUT _EP_OUT( ITF_NUM_MSC+1 ) -#define EP_MSC_IN _EP_IN ( ITF_NUM_MSC+1 ) + .idVendor = USB_DESC_VID, + .idProduct = USB_DESC_UF2_PID, + .bcdDevice = 0x0100, -#define EP_MSC_SIZE 64 + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum { + ITF_NUM_CDC = 0 , + ITF_NUM_CDC_DATA , + ITF_NUM_MSC , + ITF_NUM_TOTAL +}; + +uint8_t const desc_configuration_cdc_msc[] = +{ + // Interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, ITF_STR_CDC, 0x81, 8, 0x02, 0x82, 64), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, ITF_STR_MSC, 0x03, 0x83, 64), +}; + +uint8_t const desc_configuration_cdc_only[] = +{ + // Interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL-1, 0, TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, ITF_STR_CDC, 0x81, 8, 0x02, 0x82, 64), +}; + + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + return _cdc_only ? desc_configuration_cdc_only : desc_configuration_cdc_msc; +} + +// Enumerate as CDC + MSC or CDC only +void usb_desc_set_mode(bool cdc_only) +{ + _cdc_only = cdc_only; + + if ( cdc_only ) + { + // Change PID to CDC only + desc_device.idProduct = USB_DESC_CDC_ONLY_PID; + } +} //--------------------------------------------------------------------+ // STRING DESCRIPTORS @@ -86,219 +150,51 @@ enum { #endif // Serial is 64-bit DeviceID -> 16 chars len -uint16_t usb_desc_str_serial[1+16] = { TUD_DESC_STR_HEADER(16) }; +char usb_desc_str_serial[1+16]; // array of pointer to string descriptors -uint16_t const * const string_desc_arr [] = USB_STRING_DESCRIPTORS; - -//--------------------------------------------------------------------+ -// Device Descriptor -//--------------------------------------------------------------------+ -tusb_desc_device_t usb_desc_dev = +char const* string_desc_arr [] = { - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - - // Use Interface Association Descriptor (IAD) for CDC - // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, - - .idVendor = USB_DESC_VID, - .idProduct = USB_DESC_UF2_PID, - .bcdDevice = 0x0100, - - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - - .bNumConfigurations = 0x01 + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "Adafruit Industries", // 1: Manufacturer + "Bluefruit DFU", // 2: Product + usb_desc_str_serial, // 3: Serials, should use chip ID + "Bluefruit Serial", // 4: CDC Interface + "Bluefruit UF2", // 5: MSC Interface }; -//--------------------------------------------------------------------+ -// Configuration Descriptor -//--------------------------------------------------------------------+ -usb_desc_cfg_t usb_desc_cfg = +// up to 32 unicode characters (header make it 33) +static uint16_t _desc_str[33]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index) { - .config = + uint8_t chr_count; + + if ( index == 0) + { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + }else + { + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + // Convert ASCII string into UTF-16 + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + if ( chr_count > 31 ) chr_count = 31; + + for(uint8_t i=0; i