forked from espressif/arduino-esp32
Add initial support for USB MSC (#5466)
* Add initial support for USB MSC * Add Firmware Upload/Download With MSC Current running firmware is available as file inside the MSC Disk. To update the firmware on the ESP, just copy a regular firmware bin into the drive * Support overwriting of the firmware file Overwriting a file is done totally differently on MacOS, Windows and Linux. This change supports it on all of them. * Allow CDC, FirmwareMSC and DFU to be enabled on boot * Add example ESP32-S2 USB-ONLY board * Various device code optimizations Added `end()` methods to MSC classes Made begin() methods safe to be called multiple times Optimized CDC class * Fix CDC Connect/Disconnect detection in Arduino IDE on Windows * Rework cdc_write * Update ESP32-S2 board configs
This commit is contained in:
423
cores/esp32/FirmwareMSC.cpp
Normal file
423
cores/esp32/FirmwareMSC.cpp
Normal file
@ -0,0 +1,423 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cstring>
|
||||
#include "FirmwareMSC.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "pins_arduino.h"
|
||||
#include "firmware_msc_fat.h"
|
||||
|
||||
#if CONFIG_TINYUSB_MSC_ENABLED
|
||||
|
||||
#ifndef USB_FW_MSC_VENDOR_ID
|
||||
#define USB_FW_MSC_VENDOR_ID "ESP32" //max 8 chars
|
||||
#endif
|
||||
#ifndef USB_FW_MSC_PRODUCT_ID
|
||||
#define USB_FW_MSC_PRODUCT_ID "Firmware MSC"//max 16 chars
|
||||
#endif
|
||||
#ifndef USB_FW_MSC_PRODUCT_REVISION
|
||||
#define USB_FW_MSC_PRODUCT_REVISION "1.0" //max 4 chars
|
||||
#endif
|
||||
#ifndef USB_FW_MSC_VOLUME_NAME
|
||||
#define USB_FW_MSC_VOLUME_NAME "ESP32-FWMSC" //max 11 chars
|
||||
#endif
|
||||
#ifndef USB_FW_MSC_SERIAL_NUMBER
|
||||
#define USB_FW_MSC_SERIAL_NUMBER 0x00000000
|
||||
#endif
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
|
||||
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
|
||||
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
|
||||
|
||||
//General Variables
|
||||
static uint8_t * msc_ram_disk = NULL;
|
||||
static fat_boot_sector_t * msc_boot = NULL;
|
||||
static uint8_t * msc_table = NULL;
|
||||
static uint16_t msc_table_sectors = 0;
|
||||
static uint16_t msc_total_sectors = 0;
|
||||
static bool mcs_is_fat16 = false;
|
||||
|
||||
//Firmware Read
|
||||
static const esp_partition_t* msc_run_partition = NULL;
|
||||
static uint16_t fw_start_sector = 0;
|
||||
static uint16_t fw_end_sector = 0;
|
||||
static size_t fw_size = 0;
|
||||
static fat_dir_entry_t * fw_entry = NULL;
|
||||
|
||||
//Firmware Write
|
||||
typedef enum {
|
||||
MSC_UPDATE_IDLE,
|
||||
MSC_UPDATE_STARTING,
|
||||
MSC_UPDATE_RUNNING,
|
||||
MSC_UPDATE_END
|
||||
} msc_update_state_t;
|
||||
|
||||
static const esp_partition_t* msc_ota_partition = NULL;
|
||||
static msc_update_state_t msc_update_state = MSC_UPDATE_IDLE;
|
||||
static uint16_t msc_update_start_sector = 0;
|
||||
static uint32_t msc_update_bytes_written = 0;
|
||||
static fat_dir_entry_t * msc_update_entry = NULL;
|
||||
|
||||
static uint32_t get_firmware_size(const esp_partition_t* partition){
|
||||
esp_image_metadata_t data;
|
||||
const esp_partition_pos_t running_pos = {
|
||||
.offset = partition->address,
|
||||
.size = partition->size,
|
||||
};
|
||||
data.start_addr = running_pos.offset;
|
||||
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
|
||||
return data.image_len;
|
||||
}
|
||||
|
||||
//Get number of sectors required based on the size of the firmware and OTA partition
|
||||
static size_t msc_update_get_required_disk_sectors(){
|
||||
size_t data_sectors = 16;
|
||||
size_t total_sectors = 0;
|
||||
msc_run_partition = esp_ota_get_running_partition();
|
||||
msc_ota_partition = esp_ota_get_next_update_partition(NULL);
|
||||
if(msc_run_partition){
|
||||
fw_size = get_firmware_size(msc_run_partition);
|
||||
data_sectors += FAT_SIZE_TO_SECTORS(fw_size);
|
||||
log_d("APP size: %u (%u sectors)", fw_size, FAT_SIZE_TO_SECTORS(fw_size));
|
||||
} else {
|
||||
log_w("APP partition not found. Reading disabled");
|
||||
}
|
||||
if(msc_ota_partition){
|
||||
data_sectors += FAT_SIZE_TO_SECTORS(msc_ota_partition->size);
|
||||
log_d("OTA size: %u (%u sectors)", msc_ota_partition->size, FAT_SIZE_TO_SECTORS(msc_ota_partition->size));
|
||||
} else {
|
||||
log_w("OTA partition not found. Writing disabled");
|
||||
}
|
||||
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, false);
|
||||
total_sectors = data_sectors + msc_table_sectors + 2;
|
||||
if(total_sectors > 0xFF4){
|
||||
log_d("USING FAT16");
|
||||
mcs_is_fat16 = true;
|
||||
total_sectors -= msc_table_sectors;
|
||||
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, true);
|
||||
total_sectors += msc_table_sectors;
|
||||
} else {
|
||||
log_d("USING FAT12");
|
||||
mcs_is_fat16 = false;
|
||||
}
|
||||
log_d("FAT data sectors: %u", data_sectors);
|
||||
log_d("FAT table sectors: %u", msc_table_sectors);
|
||||
log_d("FAT total sectors: %u (%uKB)", total_sectors, (total_sectors * DISK_SECTOR_SIZE) / 1024);
|
||||
return total_sectors;
|
||||
}
|
||||
|
||||
//setup the ramdisk and add the firmware download file
|
||||
static bool msc_update_setup_disk(const char * volume_label, uint32_t serial_number){
|
||||
msc_total_sectors = msc_update_get_required_disk_sectors();
|
||||
uint8_t ram_sectors = msc_table_sectors + 2;
|
||||
msc_ram_disk = (uint8_t*)calloc(ram_sectors, DISK_SECTOR_SIZE);
|
||||
if(!msc_ram_disk){
|
||||
log_e("Failed to allocate RAM Disk: %u bytes", ram_sectors * DISK_SECTOR_SIZE);
|
||||
return false;
|
||||
}
|
||||
fw_start_sector = ram_sectors;
|
||||
fw_end_sector = fw_start_sector;
|
||||
msc_boot = fat_add_boot_sector(msc_ram_disk, msc_total_sectors, msc_table_sectors, fat_file_system_type(mcs_is_fat16), volume_label, serial_number);
|
||||
msc_table = fat_add_table(msc_ram_disk, msc_boot, mcs_is_fat16);
|
||||
//fat_dir_entry_t * label = fat_add_label(msc_ram_disk, volume_label);
|
||||
if(msc_run_partition){
|
||||
fw_entry = fat_add_root_file(msc_ram_disk, 0, "FIRMWARE", "BIN", fw_size, 2, mcs_is_fat16);
|
||||
fw_end_sector = FAT_SIZE_TO_SECTORS(fw_size) + fw_start_sector;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void msc_update_delete_disk(){
|
||||
fw_entry = NULL;
|
||||
fw_size = 0;
|
||||
fw_end_sector = 0;
|
||||
fw_start_sector = 0;
|
||||
msc_table = NULL;
|
||||
msc_boot = NULL;
|
||||
msc_table_sectors = 0;
|
||||
msc_total_sectors = 0;
|
||||
msc_run_partition = NULL;
|
||||
msc_ota_partition = NULL;
|
||||
msc_update_state = MSC_UPDATE_IDLE;
|
||||
msc_update_start_sector = 0;
|
||||
msc_update_bytes_written = 0;
|
||||
msc_update_entry = NULL;
|
||||
free(msc_ram_disk);
|
||||
msc_ram_disk = NULL;
|
||||
}
|
||||
|
||||
//filter out entries to only include BINs in the root folder
|
||||
static fat_dir_entry_t * msc_update_get_root_bin_entry(uint8_t index){
|
||||
fat_dir_entry_t * entry = (fat_dir_entry_t *)(msc_ram_disk + ((msc_boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t)));
|
||||
fat_lfn_entry_t * lfn = (fat_lfn_entry_t*)entry;
|
||||
|
||||
//empty entry
|
||||
if(entry->file_magic == 0){
|
||||
return NULL;
|
||||
}
|
||||
//long file name
|
||||
if(lfn->attr == 0x0F && lfn->type == 0x00 && lfn->first_cluster == 0x0000){
|
||||
return NULL;
|
||||
}
|
||||
//only files marked as archives
|
||||
if(entry->file_attr != FAT_FILE_ATTR_ARCHIVE){
|
||||
return NULL;
|
||||
}
|
||||
//deleted
|
||||
if(entry->file_magic == 0xE5 || entry->file_magic == 0x05){
|
||||
return NULL;
|
||||
}
|
||||
//not bins
|
||||
if(memcmp("BIN", entry->file_extension, 3)){
|
||||
return NULL;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
//get an empty bin (the host will add an entry for file about to be written with size of zero)
|
||||
static fat_dir_entry_t * msc_update_find_new_bin(){
|
||||
for(uint8_t i=16; i;){
|
||||
i--;
|
||||
fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i);
|
||||
if(entry && entry->file_size == 0){
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//get a bin starting from particular sector
|
||||
static fat_dir_entry_t * msc_update_find_bin(uint16_t sector){
|
||||
for(uint8_t i=16; i; ){
|
||||
i--;
|
||||
fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i);
|
||||
if(entry && entry->data_start_sector == (sector - msc_boot->sectors_per_alloc_table)){
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//write the new data and erase the flash blocks when necessary
|
||||
static esp_err_t msc_update_write(const esp_partition_t *partition, uint32_t offset, void *data, size_t size){
|
||||
esp_err_t err = ESP_OK;
|
||||
if((offset & (SPI_FLASH_SEC_SIZE-1)) == 0){
|
||||
err = esp_partition_erase_range(partition, offset, SPI_FLASH_SEC_SIZE);
|
||||
log_v("ERASE[0x%08X]: %s", offset, (err != ESP_OK)?"FAIL":"OK");
|
||||
if(err != ESP_OK){
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return esp_partition_write(partition, offset, data, size);
|
||||
}
|
||||
|
||||
//called when error was encountered while updating
|
||||
static void msc_update_error(){
|
||||
log_e("UPDATE_ERROR: %u", msc_update_bytes_written);
|
||||
arduino_firmware_msc_event_data_t p = {0};
|
||||
p.error.size = msc_update_bytes_written;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_ERROR_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
msc_update_state = MSC_UPDATE_IDLE;
|
||||
msc_update_entry = NULL;
|
||||
msc_update_bytes_written = 0;
|
||||
msc_update_start_sector = 0;
|
||||
}
|
||||
|
||||
//called when all firmware bytes have been received
|
||||
static void msc_update_end(){
|
||||
log_d("UPDATE_END: %u", msc_update_entry->file_size);
|
||||
msc_update_state = MSC_UPDATE_END;
|
||||
size_t ota_size = get_firmware_size(msc_ota_partition);
|
||||
if(ota_size != msc_update_entry->file_size){
|
||||
log_e("OTA SIZE MISMATCH %u != %u", ota_size, msc_update_entry->file_size);
|
||||
msc_update_error();
|
||||
return;
|
||||
}
|
||||
if(!ota_size || esp_ota_set_boot_partition(msc_ota_partition) != ESP_OK){
|
||||
log_e("ENABLING OTA PARTITION FAILED");
|
||||
msc_update_error();
|
||||
return;
|
||||
}
|
||||
arduino_firmware_msc_event_data_t p = {0};
|
||||
p.end.size = msc_update_entry->file_size;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_END_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
|
||||
static int32_t msc_write(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){
|
||||
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
|
||||
if(lba < fw_start_sector){
|
||||
//write to sectors that are in RAM
|
||||
memcpy(msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, buffer, bufsize);
|
||||
if(msc_ota_partition && lba == (fw_start_sector - 1)){
|
||||
//monitor the root folder table
|
||||
if(msc_update_state <= MSC_UPDATE_RUNNING){
|
||||
fat_dir_entry_t * update_entry = msc_update_find_new_bin();
|
||||
if(update_entry) {
|
||||
if(msc_update_entry) {
|
||||
log_v("REPLACING ENTRY");
|
||||
} else {
|
||||
log_v("ASSIGNING ENTRY");
|
||||
}
|
||||
if(msc_update_state <= MSC_UPDATE_STARTING){
|
||||
msc_update_state = MSC_UPDATE_STARTING;
|
||||
msc_update_bytes_written = 0;
|
||||
msc_update_start_sector = 0;
|
||||
}
|
||||
msc_update_entry = update_entry;
|
||||
} else if(msc_update_state == MSC_UPDATE_RUNNING){
|
||||
if(!msc_update_entry && msc_update_start_sector){
|
||||
msc_update_entry = msc_update_find_bin(msc_update_start_sector);
|
||||
}
|
||||
if(msc_update_entry && msc_update_bytes_written >= msc_update_entry->file_size){
|
||||
msc_update_end();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(msc_ota_partition && lba >= msc_update_start_sector){
|
||||
//handle writes to the region where the new firmware will be uploaded
|
||||
arduino_firmware_msc_event_data_t p = {0};
|
||||
if(msc_update_state <= MSC_UPDATE_STARTING && buffer[0] == 0xE9){
|
||||
msc_update_state = MSC_UPDATE_RUNNING;
|
||||
msc_update_start_sector = lba;
|
||||
msc_update_bytes_written = 0;
|
||||
log_d("UPDATE_START: %u (0x%02X)", lba, lba - msc_boot->sectors_per_alloc_table);
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_START_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){
|
||||
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
|
||||
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
|
||||
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
|
||||
p.write.size = bufsize;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
} else {
|
||||
msc_update_error();
|
||||
return 0;
|
||||
}
|
||||
} else if(msc_update_state == MSC_UPDATE_RUNNING){
|
||||
if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written < msc_update_entry->file_size && (msc_update_bytes_written + bufsize) >= msc_update_entry->file_size){
|
||||
bufsize = msc_update_entry->file_size - msc_update_bytes_written;
|
||||
}
|
||||
if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){
|
||||
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
|
||||
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
|
||||
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
|
||||
p.write.size = bufsize;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written >= msc_update_entry->file_size){
|
||||
msc_update_end();
|
||||
}
|
||||
} else {
|
||||
msc_update_error();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
static int32_t msc_read(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){
|
||||
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
|
||||
if(lba < fw_start_sector){
|
||||
memcpy(buffer, msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, bufsize);
|
||||
} else if(msc_run_partition && lba < fw_end_sector){
|
||||
//read the currently running firmware
|
||||
if(esp_partition_read(msc_run_partition, ((lba - fw_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) != ESP_OK){
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
memset(buffer, 0, bufsize);
|
||||
}
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
static bool msc_start_stop(uint8_t power_condition, bool start, bool load_eject){
|
||||
//log_d("power: %u, start: %u, eject: %u", power_condition, start, load_eject);
|
||||
arduino_firmware_msc_event_data_t p = {0};
|
||||
p.power.power_condition = power_condition;
|
||||
p.power.start = start;
|
||||
p.power.load_eject = load_eject;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_POWER_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
return true;
|
||||
}
|
||||
|
||||
static volatile TaskHandle_t msc_task_handle = NULL;
|
||||
static void msc_task(void *pvParameters){
|
||||
for (;;) {
|
||||
if(msc_update_state == MSC_UPDATE_END){
|
||||
delay(100);
|
||||
esp_restart();
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
msc_task_handle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
FirmwareMSC::FirmwareMSC():msc(){}
|
||||
|
||||
FirmwareMSC::~FirmwareMSC(){
|
||||
end();
|
||||
}
|
||||
|
||||
bool FirmwareMSC::begin(){
|
||||
if(msc_ram_disk){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!msc_update_setup_disk(USB_FW_MSC_VOLUME_NAME, USB_FW_MSC_SERIAL_NUMBER)){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!msc_task_handle){
|
||||
xTaskCreateUniversal(msc_task, "msc_disk", 1024, NULL, 2, (TaskHandle_t*)&msc_task_handle, 0);
|
||||
if(!msc_task_handle){
|
||||
msc_update_delete_disk();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
msc.vendorID(USB_FW_MSC_VENDOR_ID);
|
||||
msc.productID(USB_FW_MSC_PRODUCT_ID);
|
||||
msc.productRevision(USB_FW_MSC_PRODUCT_REVISION);
|
||||
msc.onStartStop(msc_start_stop);
|
||||
msc.onRead(msc_read);
|
||||
msc.onWrite(msc_write);
|
||||
msc.mediaPresent(true);
|
||||
msc.begin(msc_boot->fat12_sector_num, DISK_SECTOR_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FirmwareMSC::end(){
|
||||
msc.end();
|
||||
if(msc_task_handle){
|
||||
vTaskDelete(msc_task_handle);
|
||||
msc_task_handle = NULL;
|
||||
}
|
||||
msc_update_delete_disk();
|
||||
}
|
||||
|
||||
void FirmwareMSC::onEvent(esp_event_handler_t callback){
|
||||
onEvent(ARDUINO_FIRMWARE_MSC_ANY_EVENT, callback);
|
||||
}
|
||||
void FirmwareMSC::onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback){
|
||||
arduino_usb_event_handler_register_with(ARDUINO_FIRMWARE_MSC_EVENTS, event, callback, this);
|
||||
}
|
||||
|
||||
#if ARDUINO_USB_MSC_ON_BOOT
|
||||
FirmwareMSC MSC_Update;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_USB_MSC_ENABLED */
|
69
cores/esp32/FirmwareMSC.h
Normal file
69
cores/esp32/FirmwareMSC.h
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include "USBMSC.h"
|
||||
#if CONFIG_TINYUSB_MSC_ENABLED
|
||||
|
||||
#include "esp_event.h"
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
ARDUINO_FIRMWARE_MSC_ANY_EVENT = ESP_EVENT_ANY_ID,
|
||||
ARDUINO_FIRMWARE_MSC_START_EVENT = 0,
|
||||
ARDUINO_FIRMWARE_MSC_WRITE_EVENT,
|
||||
ARDUINO_FIRMWARE_MSC_END_EVENT,
|
||||
ARDUINO_FIRMWARE_MSC_ERROR_EVENT,
|
||||
ARDUINO_FIRMWARE_MSC_POWER_EVENT,
|
||||
ARDUINO_FIRMWARE_MSC_MAX_EVENT,
|
||||
} arduino_firmware_msc_event_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
size_t offset;
|
||||
size_t size;
|
||||
} write;
|
||||
struct {
|
||||
uint8_t power_condition;
|
||||
bool start;
|
||||
bool load_eject;
|
||||
} power;
|
||||
struct {
|
||||
size_t size;
|
||||
} end;
|
||||
struct {
|
||||
size_t size;
|
||||
} error;
|
||||
} arduino_firmware_msc_event_data_t;
|
||||
|
||||
class FirmwareMSC {
|
||||
private:
|
||||
USBMSC msc;
|
||||
|
||||
public:
|
||||
FirmwareMSC();
|
||||
~FirmwareMSC();
|
||||
bool begin();
|
||||
void end();
|
||||
void onEvent(esp_event_handler_t callback);
|
||||
void onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback);
|
||||
};
|
||||
|
||||
#if ARDUINO_USB_MSC_ON_BOOT
|
||||
extern FirmwareMSC MSC_Update;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_TINYUSB_MSC_ENABLED */
|
@ -37,7 +37,7 @@
|
||||
#endif
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||
HardwareSerial Serial0(0);
|
||||
#else
|
||||
HardwareSerial Serial(0);
|
||||
|
@ -113,10 +113,10 @@ protected:
|
||||
extern void serialEventRun(void) __attribute__((weak));
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
#ifndef ARDUINO_SERIAL_PORT
|
||||
#define ARDUINO_SERIAL_PORT 0
|
||||
#ifndef ARDUINO_USB_CDC_ON_BOOT
|
||||
#define ARDUINO_USB_CDC_ON_BOOT 0
|
||||
#endif
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||
#include "USB.h"
|
||||
#include "USBCDC.h"
|
||||
extern HardwareSerial Serial0;
|
||||
|
@ -31,14 +31,16 @@
|
||||
#ifndef USB_SERIAL
|
||||
#define USB_SERIAL "0"
|
||||
#endif
|
||||
#ifndef USB_WEBUSB_ENABLED
|
||||
#define USB_WEBUSB_ENABLED false
|
||||
#endif
|
||||
#ifndef USB_WEBUSB_URL
|
||||
#define USB_WEBUSB_URL "https://espressif.github.io/arduino-esp32/webusb.html"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_DFU_RUNTIME
|
||||
static uint16_t load_dfu_descriptor(uint8_t * dst, uint8_t * itf)
|
||||
{
|
||||
#define DFU_ATTR_CAN_DOWNLOAD 1
|
||||
#define DFU_ATTR_CAN_UPLOAD 2
|
||||
#define DFU_ATTR_MANIFESTATION_TOLERANT 4
|
||||
#define DFU_ATTR_WILL_DETACH 8
|
||||
#define DFU_ATTRS (DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_CAN_UPLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
|
||||
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB DFU_RT");
|
||||
@ -120,8 +122,8 @@ ESPUSB::ESPUSB(size_t task_stack_size, uint8_t event_task_priority)
|
||||
,usb_protocol(MISC_PROTOCOL_IAD)
|
||||
,usb_attributes(TUSB_DESC_CONFIG_ATT_SELF_POWERED)
|
||||
,usb_power_ma(500)
|
||||
,webusb_enabled(false)
|
||||
,webusb_url("https://espressif.github.io/arduino-esp32/webusb.html")
|
||||
,webusb_enabled(USB_WEBUSB_ENABLED)
|
||||
,webusb_url(USB_WEBUSB_URL)
|
||||
,_started(false)
|
||||
,_task_stack_size(task_stack_size)
|
||||
,_event_task_priority(event_task_priority)
|
||||
|
@ -18,9 +18,11 @@
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "USBCDC.h"
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#define ARDUINO_USB_ON_BOOT (ARDUINO_USB_CDC_ON_BOOT|ARDUINO_USB_MSC_ON_BOOT|ARDUINO_USB_DFU_ON_BOOT)
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
|
@ -28,7 +28,6 @@ USBCDC * devices[MAX_USB_CDC_DEVICES] = {NULL, NULL};
|
||||
static uint16_t load_cdc_descriptor(uint8_t * dst, uint8_t * itf)
|
||||
{
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC");
|
||||
// Interface number, string index, attributes, detach timeout, transfer size */
|
||||
uint8_t descriptor[TUD_CDC_DESC_LEN] = {
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(*itf, str_index, 0x85, 64, 0x03, 0x84, 64)
|
||||
@ -41,7 +40,6 @@ static uint16_t load_cdc_descriptor(uint8_t * dst, uint8_t * itf)
|
||||
// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
|
||||
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
|
||||
{
|
||||
//isr_log_v("itf: %u, dtr: %u, rts: %u", itf, dtr, rts);
|
||||
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
|
||||
devices[itf]->_onLineState(dtr, rts);
|
||||
}
|
||||
@ -50,7 +48,6 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
|
||||
// Invoked when line coding is change via SET_LINE_CODING
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
|
||||
{
|
||||
//isr_log_v("itf: %u, bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u", itf, p_line_coding->bit_rate, p_line_coding->data_bits, p_line_coding->stop_bits, p_line_coding->parity);
|
||||
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
|
||||
devices[itf]->_onLineCoding(p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits);
|
||||
}
|
||||
@ -59,7 +56,6 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
|
||||
// Invoked when received new data
|
||||
void tud_cdc_rx_cb(uint8_t itf)
|
||||
{
|
||||
//isr_log_v("itf: %u", itf);
|
||||
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
|
||||
devices[itf]->_onRX();
|
||||
}
|
||||
@ -72,15 +68,14 @@ void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms){
|
||||
|
||||
// Invoked when space becomes available in TX buffer
|
||||
void tud_cdc_tx_complete_cb(uint8_t itf){
|
||||
//isr_log_v("itf: %u", itf);
|
||||
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
|
||||
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL && devices[itf]->tx_sem != NULL){
|
||||
xSemaphoreGive(devices[itf]->tx_sem);
|
||||
devices[itf]->_onTX();
|
||||
}
|
||||
}
|
||||
|
||||
static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size){
|
||||
if(itf >= MAX_USB_CDC_DEVICES){
|
||||
if(itf >= MAX_USB_CDC_DEVICES || devices[itf] == NULL || devices[itf]->tx_sem == NULL){
|
||||
return 0;
|
||||
}
|
||||
if(!tud_cdc_n_connected(itf)){
|
||||
@ -90,8 +85,15 @@ static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size)
|
||||
while(tosend){
|
||||
uint32_t space = tud_cdc_n_write_available(itf);
|
||||
if(!space){
|
||||
delay(1);
|
||||
continue;
|
||||
//make sure that we do not get previous semaphore
|
||||
xSemaphoreTake(devices[itf]->tx_sem, 0);
|
||||
//wait for tx_complete
|
||||
if(xSemaphoreTake(devices[itf]->tx_sem, 200 / portTICK_PERIOD_MS) == pdTRUE){
|
||||
space = tud_cdc_n_write_available(itf);
|
||||
}
|
||||
if(!space){
|
||||
return sofar;
|
||||
}
|
||||
}
|
||||
if(tosend < space){
|
||||
space = tosend;
|
||||
@ -103,7 +105,7 @@ static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size)
|
||||
sofar += sent;
|
||||
tosend -= sent;
|
||||
tud_cdc_n_write_flush(itf);
|
||||
xSemaphoreTake(devices[itf]->tx_sem, portMAX_DELAY);
|
||||
//xSemaphoreTake(devices[itf]->tx_sem, portMAX_DELAY);
|
||||
}
|
||||
return sofar;
|
||||
}
|
||||
@ -113,21 +115,21 @@ static void ARDUINO_ISR_ATTR cdc0_write_char(char c)
|
||||
tinyusb_cdc_write(0, (const uint8_t *)&c, 1);
|
||||
}
|
||||
|
||||
//void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
|
||||
|
||||
static void usb_unplugged_cb(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
|
||||
((USBCDC*)arg)->_onUnplugged();
|
||||
}
|
||||
|
||||
USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL) {
|
||||
USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_sem(NULL) {
|
||||
tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
|
||||
if(itf < MAX_USB_CDC_DEVICES){
|
||||
devices[itf] = this;
|
||||
tx_sem = NULL;
|
||||
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this);
|
||||
}
|
||||
}
|
||||
|
||||
USBCDC::~USBCDC(){
|
||||
end();
|
||||
}
|
||||
|
||||
void USBCDC::onEvent(esp_event_handler_t callback){
|
||||
onEvent(ARDUINO_USB_CDC_ANY_EVENT, callback);
|
||||
}
|
||||
@ -137,6 +139,10 @@ void USBCDC::onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback
|
||||
|
||||
size_t USBCDC::setRxBufferSize(size_t rx_queue_len){
|
||||
if(rx_queue){
|
||||
if(!rx_queue_len){
|
||||
vQueueDelete(rx_queue);
|
||||
rx_queue = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
|
||||
@ -148,15 +154,19 @@ size_t USBCDC::setRxBufferSize(size_t rx_queue_len){
|
||||
|
||||
void USBCDC::begin(unsigned long baud)
|
||||
{
|
||||
setRxBufferSize(256);//default if not preset
|
||||
if(tx_sem == NULL){
|
||||
tx_sem = xSemaphoreCreateBinary();
|
||||
xSemaphoreTake(tx_sem, 0);
|
||||
}
|
||||
setRxBufferSize(256);//default if not preset
|
||||
devices[itf] = this;
|
||||
}
|
||||
|
||||
void USBCDC::end()
|
||||
{
|
||||
connected = false;
|
||||
devices[itf] = NULL;
|
||||
setRxBufferSize(0);
|
||||
if (tx_sem != NULL) {
|
||||
vSemaphoreDelete(tx_sem);
|
||||
tx_sem = NULL;
|
||||
@ -176,6 +186,11 @@ void USBCDC::_onUnplugged(void){
|
||||
enum { CDC_LINE_IDLE, CDC_LINE_1, CDC_LINE_2, CDC_LINE_3 };
|
||||
void USBCDC::_onLineState(bool _dtr, bool _rts){
|
||||
static uint8_t lineState = CDC_LINE_IDLE;
|
||||
|
||||
if(dtr == _dtr && rts == _rts){
|
||||
return; // Skip duplicate events
|
||||
}
|
||||
|
||||
dtr = _dtr;
|
||||
rts = _rts;
|
||||
|
||||
@ -183,6 +198,11 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
|
||||
if(!dtr && rts){
|
||||
if(lineState == CDC_LINE_IDLE){
|
||||
lineState++;
|
||||
if(connected){
|
||||
connected = false;
|
||||
arduino_usb_cdc_event_data_t p = {0};
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
} else {
|
||||
lineState = CDC_LINE_IDLE;
|
||||
}
|
||||
@ -212,7 +232,7 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
|
||||
connected = true;
|
||||
arduino_usb_cdc_event_data_t p = {0};
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_CONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
} else if(!dtr && !rts && connected){
|
||||
} else if(!dtr && connected){
|
||||
connected = false;
|
||||
arduino_usb_cdc_event_data_t p = {0};
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
@ -228,7 +248,7 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
|
||||
void USBCDC::_onLineCoding(uint32_t _bit_rate, uint8_t _stop_bits, uint8_t _parity, uint8_t _data_bits){
|
||||
if(bit_rate != _bit_rate || data_bits != _data_bits || stop_bits != _stop_bits || parity != _parity){
|
||||
// ArduinoIDE sends LineCoding with 1200bps baud to reset the device
|
||||
if(_bit_rate == 1200){
|
||||
if(reboot_enable && _bit_rate == 1200){
|
||||
usb_persist_restart(RESTART_BOOTLOADER);
|
||||
} else {
|
||||
bit_rate = _bit_rate;
|
||||
@ -317,7 +337,7 @@ size_t USBCDC::read(uint8_t *buffer, size_t size)
|
||||
|
||||
void USBCDC::flush(void)
|
||||
{
|
||||
if(itf >= MAX_USB_CDC_DEVICES){
|
||||
if(itf >= MAX_USB_CDC_DEVICES || tx_sem == NULL){
|
||||
return;
|
||||
}
|
||||
tud_cdc_n_write_flush(itf);
|
||||
@ -325,7 +345,7 @@ void USBCDC::flush(void)
|
||||
|
||||
int USBCDC::availableForWrite(void)
|
||||
{
|
||||
if(itf >= MAX_USB_CDC_DEVICES){
|
||||
if(itf >= MAX_USB_CDC_DEVICES || tx_sem == NULL){
|
||||
return -1;
|
||||
}
|
||||
return tud_cdc_n_write_available(itf);
|
||||
@ -364,7 +384,7 @@ USBCDC::operator bool() const
|
||||
return connected;
|
||||
}
|
||||
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||
USBCDC Serial(0);
|
||||
#endif
|
||||
|
||||
|
@ -54,6 +54,7 @@ class USBCDC: public Stream
|
||||
{
|
||||
public:
|
||||
USBCDC(uint8_t itf=0);
|
||||
~USBCDC();
|
||||
|
||||
void onEvent(esp_event_handler_t callback);
|
||||
void onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback);
|
||||
@ -129,7 +130,7 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||
extern USBCDC Serial;
|
||||
#endif
|
||||
|
||||
|
260
cores/esp32/USBMSC.cpp
Normal file
260
cores/esp32/USBMSC.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-tinyusb.h"
|
||||
#include "USBMSC.h"
|
||||
|
||||
#if CFG_TUD_MSC
|
||||
extern "C" uint16_t tusb_msc_load_descriptor(uint8_t * dst, uint8_t * itf)
|
||||
{
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MSC");
|
||||
uint8_t ep_num = tinyusb_get_free_duplex_endpoint();
|
||||
TU_VERIFY (ep_num != 0);
|
||||
uint8_t descriptor[TUD_MSC_DESC_LEN] = {
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), 64)
|
||||
};
|
||||
*itf+=1;
|
||||
memcpy(dst, descriptor, TUD_MSC_DESC_LEN);
|
||||
return TUD_MSC_DESC_LEN;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool media_present;
|
||||
uint8_t vendor_id[8];
|
||||
uint8_t product_id[16];
|
||||
uint8_t product_rev[4];
|
||||
uint16_t block_size;
|
||||
uint32_t block_count;
|
||||
bool (*start_stop)(uint8_t power_condition, bool start, bool load_eject);
|
||||
int32_t (*read)(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
|
||||
int32_t (*write)(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
|
||||
} msc_lun_t;
|
||||
|
||||
static const uint8_t MSC_MAX_LUN = 3;
|
||||
static uint8_t MSC_ACTIVE_LUN = 0;
|
||||
static msc_lun_t msc_luns[MSC_MAX_LUN];
|
||||
|
||||
static void cplstr(void *dst, const void * src, size_t max_len){
|
||||
if(!src || !dst || !max_len){
|
||||
return;
|
||||
}
|
||||
size_t l = strlen((const char *)src);
|
||||
if(l > max_len){
|
||||
l = max_len;
|
||||
}
|
||||
memcpy(dst, src, l);
|
||||
}
|
||||
|
||||
// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
|
||||
uint8_t tud_msc_get_maxlun_cb(void)
|
||||
{
|
||||
log_v("%u", MSC_ACTIVE_LUN);
|
||||
return MSC_ACTIVE_LUN;
|
||||
}
|
||||
|
||||
// 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])
|
||||
{
|
||||
log_v("[%u]", lun);
|
||||
cplstr(vendor_id , msc_luns[lun].vendor_id, 8);
|
||||
cplstr(product_id , msc_luns[lun].product_id, 16);
|
||||
cplstr(product_rev, msc_luns[lun].product_rev, 4);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
log_v("[%u]: %u", lun, msc_luns[lun].media_present);
|
||||
return msc_luns[lun].media_present; // RAM disk is always ready
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
log_v("[%u]", lun);
|
||||
if(!msc_luns[lun].media_present){
|
||||
*block_count = 0;
|
||||
*block_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
*block_count = msc_luns[lun].block_count;
|
||||
*block_size = msc_luns[lun].block_size;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
log_v("[%u] power: %u, start: %u, eject: %u", lun, power_condition, start, load_eject);
|
||||
if(msc_luns[lun].start_stop){
|
||||
return msc_luns[lun].start_stop(power_condition, start, load_eject);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback invoked when received READ10 command.
|
||||
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
|
||||
{
|
||||
log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
|
||||
if(!msc_luns[lun].media_present){
|
||||
return 0;
|
||||
}
|
||||
if(msc_luns[lun].read){
|
||||
return msc_luns[lun].read(lba, offset, buffer, bufsize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Callback invoked when received WRITE10 command.
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
|
||||
{
|
||||
log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
|
||||
if(!msc_luns[lun].media_present){
|
||||
return 0;
|
||||
}
|
||||
if(msc_luns[lun].write){
|
||||
return msc_luns[lun].write(lba, offset, buffer, bufsize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// read10 & write10 has their own callback and MUST not be handled here
|
||||
log_v("[%u] cmd: %u, bufsize: %u", lun, scsi_cmd[0], bufsize);
|
||||
|
||||
void const* response = NULL;
|
||||
uint16_t resplen = 0;
|
||||
|
||||
// most scsi handled is input
|
||||
bool in_xfer = true;
|
||||
|
||||
if(!msc_luns[lun].media_present){
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (scsi_cmd[0]) {
|
||||
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
|
||||
// Host is about to read/write etc ... better not to disconnect disk
|
||||
resplen = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Set Sense = Invalid Command Operation
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||
|
||||
// negative means error -> tinyusb could stall and/or response with failed status
|
||||
resplen = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// return resplen must not larger than bufsize
|
||||
if (resplen > bufsize) resplen = bufsize;
|
||||
|
||||
if (response && (resplen > 0)) {
|
||||
if (in_xfer) {
|
||||
memcpy(buffer, response, resplen);
|
||||
} else {
|
||||
// SCSI output
|
||||
}
|
||||
}
|
||||
|
||||
return resplen;
|
||||
}
|
||||
|
||||
USBMSC::USBMSC(){
|
||||
if(MSC_ACTIVE_LUN < MSC_MAX_LUN){
|
||||
_lun = MSC_ACTIVE_LUN;
|
||||
MSC_ACTIVE_LUN++;
|
||||
msc_luns[_lun].media_present = false;
|
||||
msc_luns[_lun].vendor_id[0] = 0;
|
||||
msc_luns[_lun].product_id[0] = 0;
|
||||
msc_luns[_lun].product_rev[0] = 0;
|
||||
msc_luns[_lun].block_size = 0;
|
||||
msc_luns[_lun].block_count = 0;
|
||||
msc_luns[_lun].start_stop = NULL;
|
||||
msc_luns[_lun].read = NULL;
|
||||
msc_luns[_lun].write = NULL;
|
||||
}
|
||||
if(_lun == 0){
|
||||
tinyusb_enable_interface(USB_INTERFACE_MSC, TUD_MSC_DESC_LEN, tusb_msc_load_descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
USBMSC::~USBMSC(){
|
||||
end();
|
||||
}
|
||||
|
||||
bool USBMSC::begin(uint32_t block_count, uint16_t block_size){
|
||||
msc_luns[_lun].block_size = block_size;
|
||||
msc_luns[_lun].block_count = block_count;
|
||||
if(!msc_luns[_lun].block_size || !msc_luns[_lun].block_count || !msc_luns[_lun].read || !msc_luns[_lun].write){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void USBMSC::end(){
|
||||
msc_luns[_lun].media_present = false;
|
||||
msc_luns[_lun].vendor_id[0] = 0;
|
||||
msc_luns[_lun].product_id[0] = 0;
|
||||
msc_luns[_lun].product_rev[0] = 0;
|
||||
msc_luns[_lun].block_size = 0;
|
||||
msc_luns[_lun].block_count = 0;
|
||||
msc_luns[_lun].start_stop = NULL;
|
||||
msc_luns[_lun].read = NULL;
|
||||
msc_luns[_lun].write = NULL;
|
||||
}
|
||||
|
||||
void USBMSC::vendorID(const char * vid){
|
||||
cplstr(msc_luns[_lun].vendor_id, vid, 8);
|
||||
}
|
||||
|
||||
void USBMSC::productID(const char * pid){
|
||||
cplstr(msc_luns[_lun].product_id, pid, 16);
|
||||
}
|
||||
|
||||
void USBMSC::productRevision(const char * rev){
|
||||
cplstr(msc_luns[_lun].product_rev, rev, 4);
|
||||
}
|
||||
|
||||
void USBMSC::onStartStop(msc_start_stop_cb cb){
|
||||
msc_luns[_lun].start_stop = cb;
|
||||
}
|
||||
|
||||
void USBMSC::onRead(msc_read_cb cb){
|
||||
msc_luns[_lun].read = cb;
|
||||
}
|
||||
|
||||
void USBMSC::onWrite(msc_write_cb cb){
|
||||
msc_luns[_lun].write = cb;
|
||||
}
|
||||
|
||||
void USBMSC::mediaPresent(bool media_present){
|
||||
msc_luns[_lun].media_present = media_present;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_MSC_ENABLED */
|
49
cores/esp32/USBMSC.h
Normal file
49
cores/esp32/USBMSC.h
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp32-hal.h"
|
||||
#if CONFIG_TINYUSB_MSC_ENABLED
|
||||
|
||||
// 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
|
||||
typedef bool (*msc_start_stop_cb)(uint8_t power_condition, bool start, bool load_eject);
|
||||
|
||||
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
|
||||
typedef int32_t (*msc_read_cb)(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
|
||||
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
typedef int32_t (*msc_write_cb)(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
|
||||
|
||||
class USBMSC
|
||||
{
|
||||
public:
|
||||
USBMSC();
|
||||
~USBMSC();
|
||||
bool begin(uint32_t block_count, uint16_t block_size);
|
||||
void end();
|
||||
void vendorID(const char * vid);//max 8 chars
|
||||
void productID(const char * pid);//max 16 chars
|
||||
void productRevision(const char * ver);//max 4 chars
|
||||
void mediaPresent(bool media_present);
|
||||
void onStartStop(msc_start_stop_cb cb);
|
||||
void onRead(msc_read_cb cb);
|
||||
void onWrite(msc_write_cb cb);
|
||||
private:
|
||||
uint8_t _lun;
|
||||
};
|
||||
#endif
|
@ -72,20 +72,15 @@ static void configure_pins(usb_hal_context_t *usb)
|
||||
|
||||
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
|
||||
{
|
||||
log_i("Driver installation...");
|
||||
|
||||
// Hal init
|
||||
usb_hal_context_t hal = {
|
||||
.use_external_phy = config->external_phy
|
||||
};
|
||||
usb_hal_init(&hal);
|
||||
configure_pins(&hal);
|
||||
|
||||
if (!tusb_init()) {
|
||||
log_e("Can't initialize the TinyUSB stack.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
log_i("Driver installed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@ -106,6 +101,7 @@ static tusb_str_t WEBUSB_URL = "";
|
||||
static tusb_str_t USB_DEVICE_PRODUCT = "";
|
||||
static tusb_str_t USB_DEVICE_MANUFACTURER = "";
|
||||
static tusb_str_t USB_DEVICE_SERIAL = "";
|
||||
static tusb_str_t USB_DEVICE_LANGUAGE = "\x09\x04";//English (0x0409)
|
||||
|
||||
static uint8_t USB_DEVICE_ATTRIBUTES = 0;
|
||||
static uint16_t USB_DEVICE_POWER = 0;
|
||||
@ -140,7 +136,7 @@ static tusb_desc_device_t tinyusb_device_descriptor = {
|
||||
static uint32_t tinyusb_string_descriptor_len = 4;
|
||||
static char * tinyusb_string_descriptor[MAX_STRING_DESCRIPTORS] = {
|
||||
// array of pointer to string descriptors
|
||||
"\x09\x04", // 0: is supported language is English (0x0409)
|
||||
USB_DEVICE_LANGUAGE, // 0: is supported language
|
||||
USB_DEVICE_MANUFACTURER,// 1: Manufacturer
|
||||
USB_DEVICE_PRODUCT, // 2: Product
|
||||
USB_DEVICE_SERIAL, // 3: Serials, should use chip ID
|
||||
@ -563,31 +559,37 @@ static void usb_device_task(void *param) {
|
||||
/*
|
||||
* PUBLIC API
|
||||
* */
|
||||
static const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "MIDI", "CUSTOM"};
|
||||
|
||||
static bool tinyusb_is_initialized = false;
|
||||
|
||||
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb)
|
||||
{
|
||||
if(tinyusb_is_initialized){
|
||||
log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if((interface >= USB_INTERFACE_MAX) || (tinyusb_loaded_interfaces_mask & (1U << interface))){
|
||||
log_e("Interface %u not enabled", interface);
|
||||
log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
tinyusb_loaded_interfaces_mask |= (1U << interface);
|
||||
tinyusb_config_descriptor_len += descriptor_len;
|
||||
tinyusb_loaded_interfaces_callbacks[interface] = cb;
|
||||
log_d("Interface %u enabled", interface);
|
||||
log_d("Interface %s enabled", tinyusb_interface_names[interface]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
|
||||
static bool initialized = false;
|
||||
if(initialized){
|
||||
if(tinyusb_is_initialized){
|
||||
return ESP_OK;
|
||||
}
|
||||
initialized = true;
|
||||
tinyusb_is_initialized = true;
|
||||
|
||||
tinyusb_endpoints.val = 0;
|
||||
tinyusb_apply_device_config(config);
|
||||
if (!tinyusb_load_enabled_interfaces()) {
|
||||
initialized = false;
|
||||
tinyusb_is_initialized = false;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@ -605,7 +607,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
|
||||
}
|
||||
|
||||
if (esp_register_shutdown_handler(usb_persist_shutdown_handler) != ESP_OK) {
|
||||
initialized = false;
|
||||
tinyusb_is_initialized = false;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@ -614,7 +616,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
|
||||
};
|
||||
esp_err_t err = tinyusb_driver_install(&tusb_cfg);
|
||||
if (err != ESP_OK) {
|
||||
initialized = false;
|
||||
tinyusb_is_initialized = false;
|
||||
return err;
|
||||
}
|
||||
xTaskCreate(usb_device_task, "usbd", 4096, NULL, configMAX_PRIORITIES - 1, NULL);
|
||||
@ -690,84 +692,4 @@ uint8_t tinyusb_get_free_out_endpoint(void){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
void usb_dw_reg_dump(void)
|
||||
{
|
||||
#define USB_PRINT_REG(r) printf("USB0." #r " = 0x%x;\n", USB0.r)
|
||||
#define USB_PRINT_IREG(i, r) printf("USB0.in_ep_reg[%u]." #r " = 0x%x;\n", i, USB0.in_ep_reg[i].r)
|
||||
#define USB_PRINT_OREG(i, r) printf("USB0.out_ep_reg[%u]." #r " = 0x%x;\n", i, USB0.out_ep_reg[i].r)
|
||||
uint8_t i;
|
||||
USB_PRINT_REG(gotgctl);
|
||||
USB_PRINT_REG(gotgint);
|
||||
USB_PRINT_REG(gahbcfg);
|
||||
USB_PRINT_REG(gusbcfg);
|
||||
USB_PRINT_REG(grstctl);
|
||||
USB_PRINT_REG(gintsts);
|
||||
USB_PRINT_REG(gintmsk);
|
||||
USB_PRINT_REG(grxstsr);
|
||||
USB_PRINT_REG(grxstsp);
|
||||
USB_PRINT_REG(grxfsiz);
|
||||
USB_PRINT_REG(gnptxsts);
|
||||
USB_PRINT_REG(gpvndctl);
|
||||
USB_PRINT_REG(ggpio);
|
||||
USB_PRINT_REG(guid);
|
||||
USB_PRINT_REG(gsnpsid);
|
||||
USB_PRINT_REG(ghwcfg1);
|
||||
USB_PRINT_REG(ghwcfg2);
|
||||
USB_PRINT_REG(ghwcfg3);
|
||||
USB_PRINT_REG(ghwcfg4);
|
||||
USB_PRINT_REG(glpmcfg);
|
||||
USB_PRINT_REG(gpwrdn);
|
||||
USB_PRINT_REG(gdfifocfg);
|
||||
USB_PRINT_REG(gadpctl);
|
||||
USB_PRINT_REG(hptxfsiz);
|
||||
USB_PRINT_REG(hcfg);
|
||||
USB_PRINT_REG(hfir);
|
||||
USB_PRINT_REG(hfnum);
|
||||
USB_PRINT_REG(hptxsts);
|
||||
USB_PRINT_REG(haint);
|
||||
USB_PRINT_REG(haintmsk);
|
||||
USB_PRINT_REG(hflbaddr);
|
||||
USB_PRINT_REG(hprt);
|
||||
USB_PRINT_REG(dcfg);
|
||||
USB_PRINT_REG(dctl);
|
||||
USB_PRINT_REG(dsts);
|
||||
USB_PRINT_REG(diepmsk);
|
||||
USB_PRINT_REG(doepmsk);
|
||||
USB_PRINT_REG(daint);
|
||||
USB_PRINT_REG(daintmsk);
|
||||
USB_PRINT_REG(dtknqr1);
|
||||
USB_PRINT_REG(dtknqr2);
|
||||
USB_PRINT_REG(dvbusdis);
|
||||
USB_PRINT_REG(dvbuspulse);
|
||||
USB_PRINT_REG(dtknqr3_dthrctl);
|
||||
USB_PRINT_REG(dtknqr4_fifoemptymsk);
|
||||
USB_PRINT_REG(deachint);
|
||||
USB_PRINT_REG(deachintmsk);
|
||||
USB_PRINT_REG(pcgctrl);
|
||||
USB_PRINT_REG(pcgctrl1);
|
||||
USB_PRINT_REG(gnptxfsiz);
|
||||
for (i = 0; i < 4; i++) {
|
||||
printf("USB0.dieptxf[%u] = 0x%x;\n", i, USB0.dieptxf[i]);
|
||||
}
|
||||
// for (i = 0; i < 16; i++) {
|
||||
// printf("USB0.diepeachintmsk[%u] = 0x%x;\n", i, USB0.diepeachintmsk[i]);
|
||||
// }
|
||||
// for (i = 0; i < 16; i++) {
|
||||
// printf("USB0.doepeachintmsk[%u] = 0x%x;\n", i, USB0.doepeachintmsk[i]);
|
||||
// }
|
||||
for (i = 0; i < 7; i++) {
|
||||
printf("// EP %u:\n", i);
|
||||
USB_PRINT_IREG(i, diepctl);
|
||||
USB_PRINT_IREG(i, diepint);
|
||||
USB_PRINT_IREG(i, dieptsiz);
|
||||
USB_PRINT_IREG(i, diepdma);
|
||||
USB_PRINT_IREG(i, dtxfsts);
|
||||
USB_PRINT_OREG(i, doepctl);
|
||||
USB_PRINT_OREG(i, doepint);
|
||||
USB_PRINT_OREG(i, doeptsiz);
|
||||
USB_PRINT_OREG(i, doepdma);
|
||||
}
|
||||
}
|
||||
*/
|
||||
#endif /* CONFIG_TINYUSB_ENABLED */
|
||||
|
@ -82,11 +82,11 @@ void usb_persist_restart(restart_type_t mode);
|
||||
|
||||
// The following definitions and functions are to be used only by the drivers
|
||||
typedef enum {
|
||||
USB_INTERFACE_CDC,
|
||||
USB_INTERFACE_MSC,
|
||||
USB_INTERFACE_DFU,
|
||||
USB_INTERFACE_HID,
|
||||
USB_INTERFACE_VENDOR,
|
||||
USB_INTERFACE_MSC,
|
||||
USB_INTERFACE_CDC,
|
||||
USB_INTERFACE_MIDI,
|
||||
USB_INTERFACE_CUSTOM,
|
||||
USB_INTERFACE_MAX
|
||||
|
204
cores/esp32/firmware_msc_fat.c
Normal file
204
cores/esp32/firmware_msc_fat.c
Normal file
@ -0,0 +1,204 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "firmware_msc_fat.h"
|
||||
//copy up to max_len chars from src to dst and do not terminate
|
||||
static size_t cplstr(void *dst, const void * src, size_t max_len){
|
||||
if(!src || !dst || !max_len){
|
||||
return 0;
|
||||
}
|
||||
size_t l = strlen((const char *)src);
|
||||
if(l > max_len){
|
||||
l = max_len;
|
||||
}
|
||||
memcpy(dst, src, l);
|
||||
return l;
|
||||
}
|
||||
|
||||
//copy up to max_len chars from src to dst, adding spaces up to max_len. do not terminate
|
||||
static void cplstrsp(void *dst, const void * src, size_t max_len){
|
||||
size_t l = cplstr(dst, src, max_len);
|
||||
for(l; l < max_len; l++){
|
||||
((uint8_t*)dst)[l] = 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
// FAT12
|
||||
static const char * FAT12_FILE_SYSTEM_TYPE = "FAT12";
|
||||
|
||||
static uint16_t fat12_sectors_per_alloc_table(uint32_t sector_num){
|
||||
uint32_t required_bytes = (((sector_num * 3)+1)/2);
|
||||
return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & DISK_SECTOR_SIZE)?1:0);
|
||||
}
|
||||
|
||||
static uint8_t * fat12_add_table(uint8_t * dst, fat_boot_sector_t * boot){
|
||||
memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE);
|
||||
uint8_t * d = dst + DISK_SECTOR_SIZE;
|
||||
d[0] = 0xF8;
|
||||
d[1] = 0xFF;
|
||||
d[2] = 0xFF;
|
||||
return d;
|
||||
}
|
||||
|
||||
static void fat12_set_table_index(uint8_t * table, uint16_t index, uint16_t value){
|
||||
uint16_t offset = (index >> 1) * 3;
|
||||
uint8_t * data = table + offset;
|
||||
if(index & 1){
|
||||
data[2] = (value >> 4) & 0xFF;
|
||||
data[1] = (data[1] & 0xF) | ((value & 0xF) << 4);
|
||||
} else {
|
||||
data[0] = value & 0xFF;
|
||||
data[1] = (data[1] & 0xF0) | ((value >> 8) & 0xF);
|
||||
}
|
||||
}
|
||||
|
||||
//FAT16
|
||||
static const char * FAT16_FILE_SYSTEM_TYPE = "FAT16";
|
||||
|
||||
static uint16_t fat16_sectors_per_alloc_table(uint32_t sector_num){
|
||||
uint32_t required_bytes = sector_num * 2;
|
||||
return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & DISK_SECTOR_SIZE)?1:0);
|
||||
}
|
||||
|
||||
static uint8_t * fat16_add_table(uint8_t * dst, fat_boot_sector_t * boot){
|
||||
memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE);
|
||||
uint16_t * d = (uint16_t *)(dst + DISK_SECTOR_SIZE);
|
||||
d[0] = 0xFFF8;
|
||||
d[1] = 0xFFFF;
|
||||
return (uint8_t *)d;
|
||||
}
|
||||
|
||||
static void fat16_set_table_index(uint8_t * table, uint16_t index, uint16_t value){
|
||||
uint16_t offset = index * 2;
|
||||
*(uint16_t *)(table + offset) = value;
|
||||
}
|
||||
|
||||
//Interface
|
||||
const char * fat_file_system_type(bool fat16) {
|
||||
return ((fat16)?FAT16_FILE_SYSTEM_TYPE:FAT12_FILE_SYSTEM_TYPE);
|
||||
}
|
||||
|
||||
uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16){
|
||||
if(fat16){
|
||||
return fat16_sectors_per_alloc_table(sector_num);
|
||||
}
|
||||
return fat12_sectors_per_alloc_table(sector_num);
|
||||
}
|
||||
|
||||
uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16){
|
||||
if(fat16){
|
||||
return fat16_add_table(dst, boot);
|
||||
}
|
||||
return fat12_add_table(dst, boot);
|
||||
}
|
||||
|
||||
void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16){
|
||||
if(fat16){
|
||||
fat16_set_table_index(table, index, value);
|
||||
} else {
|
||||
fat12_set_table_index(table, index, value);
|
||||
}
|
||||
}
|
||||
|
||||
fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number){
|
||||
fat_boot_sector_t *boot = (fat_boot_sector_t*)dst;
|
||||
boot->jump_instruction[0] = 0xEB;
|
||||
boot->jump_instruction[1] = 0x3C;
|
||||
boot->jump_instruction[2] = 0x90;
|
||||
cplstr(boot->oem_name, "MSDOS5.0", 8);
|
||||
boot->bytes_per_sector = DISK_SECTOR_SIZE;
|
||||
boot->sectors_per_cluster = 1;
|
||||
boot->reserved_sectors_count = 1;
|
||||
boot->file_alloc_tables_num = 1;
|
||||
boot->max_root_dir_entries = 16;
|
||||
boot->fat12_sector_num = sector_num;
|
||||
boot->media_descriptor = 0xF8;
|
||||
boot->sectors_per_alloc_table = table_sectors;
|
||||
boot->sectors_per_track = 1;
|
||||
boot->num_heads = 1;
|
||||
boot->hidden_sectors_count = 0;
|
||||
boot->total_sectors_32 = 0;
|
||||
boot->physical_drive_number = 0x00;
|
||||
boot->reserved0 = 0x00;
|
||||
boot->extended_boot_signature = 0x29;
|
||||
boot->serial_number = serial_number;
|
||||
cplstrsp(boot->volume_label, volume_label, 11);
|
||||
memset(boot->reserved, 0, 448);
|
||||
cplstrsp(boot->file_system_type, file_system_type, 8);
|
||||
boot->signature = 0xAA55;
|
||||
return boot;
|
||||
}
|
||||
|
||||
fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label){
|
||||
fat_boot_sector_t * boot = (fat_boot_sector_t *)dst;
|
||||
fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE));
|
||||
memset(entry, 0, sizeof(fat_dir_entry_t));
|
||||
cplstrsp(entry->volume_label, volume_label, 11);
|
||||
entry->file_attr = FAT_FILE_ATTR_VOLUME_LABEL;
|
||||
return entry;
|
||||
}
|
||||
|
||||
fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16){
|
||||
fat_boot_sector_t * boot = (fat_boot_sector_t *)dst;
|
||||
uint8_t * table = dst + DISK_SECTOR_SIZE;
|
||||
fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t)));
|
||||
memset(entry, 0, sizeof(fat_dir_entry_t));
|
||||
cplstrsp(entry->file_name, file_name, 8);
|
||||
cplstrsp(entry->file_extension, file_extension, 3);
|
||||
entry->file_attr = FAT_FILE_ATTR_ARCHIVE;
|
||||
entry->file_size = file_size;
|
||||
entry->data_start_sector = data_start_sector;
|
||||
entry->extended_attr = 0;
|
||||
|
||||
uint16_t file_sectors = file_size / DISK_SECTOR_SIZE;
|
||||
if(file_size % DISK_SECTOR_SIZE){
|
||||
file_sectors++;
|
||||
}
|
||||
|
||||
uint16_t data_end_sector = data_start_sector + file_sectors;
|
||||
for(uint16_t i=data_start_sector; i<(data_end_sector-1); i++){
|
||||
fat_set_table_index(table, i, i+1, is_fat16);
|
||||
}
|
||||
fat_set_table_index(table, data_end_sector-1, 0xFFFF, is_fat16);
|
||||
|
||||
//Set Firmware Date based on the build time
|
||||
static const char * month_names_short[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
char mstr[8] = {'\0',};
|
||||
const char *str = __DATE__ " " __TIME__;
|
||||
int ms=0, seconds=0, minutes=0, hours=0, year=0, date=0, month=0;
|
||||
int r = sscanf(str,"%s %d %d %d:%d:%d", mstr, &date, &year, &hours, &minutes, &seconds);
|
||||
if(r >= 0){
|
||||
for(int i=0; i<12; i++){
|
||||
if(!strcmp(mstr, month_names_short[i])){
|
||||
month = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
entry->creation_time_ms = FAT_MS2V(seconds, ms);
|
||||
entry->creation_time_hms = FAT_HMS2V(hours, minutes, seconds);
|
||||
entry->creation_time_ymd = FAT_YMD2V(year, month, date);
|
||||
entry->last_access_ymd = entry->creation_time_ymd;
|
||||
entry->last_modified_hms = entry->creation_time_hms;
|
||||
entry->last_modified_ymd = entry->creation_time_ymd;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
uint8_t fat_lfn_checksum(const uint8_t *short_filename){
|
||||
uint8_t sum = 0;
|
||||
for (uint8_t i = 11; i; i--) {
|
||||
sum = ((sum & 1) << 7) + (sum >> 1) + *short_filename++;
|
||||
}
|
||||
return sum;
|
||||
}
|
141
cores/esp32/firmware_msc_fat.h
Normal file
141
cores/esp32/firmware_msc_fat.h
Normal file
@ -0,0 +1,141 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define FAT_U8(v) ((v) & 0xFF)
|
||||
#define FAT_U16(v) FAT_U8(v), FAT_U8((v) >> 8)
|
||||
#define FAT_U32(v) FAT_U8(v), FAT_U8((v) >> 8), FAT_U8((v) >> 16), FAT_U8((v) >> 24)
|
||||
|
||||
#define FAT12_TBL2B(l,h) FAT_U8(l), FAT_U8(((l >> 8) & 0xF) | ((h << 4) & 0xF0)), FAT_U8(h >> 4)
|
||||
|
||||
#define FAT_MS2B(s,ms) FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
|
||||
#define FAT_HMS2B(h,m,s) FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x7)|((h) << 3))
|
||||
#define FAT_YMD2B(y,m,d) FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1))
|
||||
|
||||
#define FAT_MS2V(s,ms) FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
|
||||
#define FAT_HMS2V(h,m,s) (FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x7)|((h) << 3)) << 8))
|
||||
#define FAT_YMD2V(y,m,d) (FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1)) << 8))
|
||||
|
||||
#define FAT_B2HMS(hms) ((hms >> 11) & 0x1F), ((hms >> 5) & 0x3F), ((hms & 0x1F) << 1)
|
||||
#define FAT_B2YMD(ymd) (((ymd >> 9) & 0x7F) + 1980), ((ymd >> 5) & 0x0F), (ymd & 0x1F)
|
||||
|
||||
#define FAT_FILE_ATTR_READ_ONLY 0x01
|
||||
#define FAT_FILE_ATTR_HIDDEN 0x02
|
||||
#define FAT_FILE_ATTR_SYSTEM 0x04
|
||||
#define FAT_FILE_ATTR_VOLUME_LABEL 0x08
|
||||
#define FAT_FILE_ATTR_SUBDIRECTORY 0x10
|
||||
#define FAT_FILE_ATTR_ARCHIVE 0x20
|
||||
#define FAT_FILE_ATTR_DEVICE 0x40
|
||||
|
||||
static const uint16_t DISK_SECTOR_SIZE = 512;
|
||||
|
||||
#define FAT_SIZE_TO_SECTORS(bytes) ((bytes) / DISK_SECTOR_SIZE) + (((bytes) % DISK_SECTOR_SIZE)?1:0)
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
uint8_t jump_instruction[3];
|
||||
char oem_name[8];//padded with spaces (0x20)
|
||||
uint16_t bytes_per_sector;//DISK_SECTOR_SIZE usually 512
|
||||
uint8_t sectors_per_cluster;//Allowed values are 1, 2, 4, 8, 16, 32, 64, and 128
|
||||
uint16_t reserved_sectors_count;//At least 1 for this sector, usually 32 for FAT32
|
||||
uint8_t file_alloc_tables_num;//Almost always 2; RAM disks might use 1
|
||||
uint16_t max_root_dir_entries;//FAT12 and FAT16
|
||||
uint16_t fat12_sector_num;//DISK_SECTOR_NUM FAT12 and FAT16
|
||||
uint8_t media_descriptor;
|
||||
uint16_t sectors_per_alloc_table;//FAT12 and FAT16
|
||||
uint16_t sectors_per_track;//A value of 0 may indicate LBA-only access
|
||||
uint16_t num_heads;
|
||||
uint32_t hidden_sectors_count;
|
||||
uint32_t total_sectors_32;
|
||||
uint8_t physical_drive_number;//0x00 for (first) removable media, 0x80 for (first) fixed disk
|
||||
uint8_t reserved0;
|
||||
uint8_t extended_boot_signature;//should be 0x29
|
||||
uint32_t serial_number;//0x1234 => 1234
|
||||
char volume_label[11];//padded with spaces (0x20)
|
||||
char file_system_type[8];//padded with spaces (0x20)
|
||||
uint8_t reserved[448];
|
||||
uint16_t signature;//should be 0xAA55
|
||||
} fat_boot_sector_t;
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
union {
|
||||
struct {
|
||||
char file_name[8];//padded with spaces (0x20)
|
||||
char file_extension[3];//padded with spaces (0x20)
|
||||
};
|
||||
struct {
|
||||
uint8_t file_magic;// 0xE5:deleted, 0x05:will_be_deleted, 0x00:end_marker, 0x2E:dot_marker(. or ..)
|
||||
char file_magic_data[10];
|
||||
};
|
||||
char volume_label[11];//padded with spaces (0x20)
|
||||
};
|
||||
uint8_t file_attr;//mask of FAT_FILE_ATTR_*
|
||||
uint8_t reserved;//always 0
|
||||
uint8_t creation_time_ms;//ms * 10; max 1990 (1s 990ms)
|
||||
uint16_t creation_time_hms; // [5:6:5] => h:m:(s/2)
|
||||
uint16_t creation_time_ymd; // [7:4:5] => (y+1980):m:d
|
||||
uint16_t last_access_ymd;
|
||||
uint16_t extended_attr;
|
||||
uint16_t last_modified_hms;
|
||||
uint16_t last_modified_ymd;
|
||||
uint16_t data_start_sector;
|
||||
uint32_t file_size;
|
||||
} fat_dir_entry_t;
|
||||
|
||||
typedef struct __attribute__ ((packed)) {
|
||||
union {
|
||||
struct {
|
||||
uint8_t number:5;
|
||||
uint8_t reserved0:1;
|
||||
uint8_t llfp:1;
|
||||
uint8_t reserved1:1;
|
||||
} seq;
|
||||
uint8_t seq_num; //0xE5: Deleted Entry
|
||||
};
|
||||
uint16_t name0[5];
|
||||
uint8_t attr; //ALWAYS 0x0F
|
||||
uint8_t type; //ALWAYS 0x00
|
||||
uint8_t dos_checksum;
|
||||
uint16_t name1[6];
|
||||
uint16_t first_cluster; //ALWAYS 0x0000
|
||||
uint16_t name2[2];
|
||||
} fat_lfn_entry_t;
|
||||
|
||||
typedef union {
|
||||
fat_dir_entry_t dir;
|
||||
fat_lfn_entry_t lfn;
|
||||
} fat_entry_t;
|
||||
|
||||
const char * fat_file_system_type(bool fat16);
|
||||
uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16);
|
||||
uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16);
|
||||
void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16);
|
||||
fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number);
|
||||
fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label);
|
||||
fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16);
|
||||
uint8_t fat_lfn_checksum(const uint8_t *short_filename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -2,8 +2,11 @@
|
||||
#include "freertos/task.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "Arduino.h"
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
#if (ARDUINO_USB_CDC_ON_BOOT|ARDUINO_USB_MSC_ON_BOOT|ARDUINO_USB_DFU_ON_BOOT)
|
||||
#include "USB.h"
|
||||
#if ARDUINO_USB_MSC_ON_BOOT
|
||||
#include "FirmwareMSC.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINO_LOOP_STACK_SIZE
|
||||
@ -47,9 +50,17 @@ void loopTask(void *pvParameters)
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
USB.begin();
|
||||
#if ARDUINO_USB_CDC_ON_BOOT
|
||||
Serial.begin();
|
||||
#endif
|
||||
#if ARDUINO_USB_MSC_ON_BOOT
|
||||
MSC_Update.begin();
|
||||
#endif
|
||||
#if ARDUINO_USB_DFU_ON_BOOT
|
||||
USB.enableDFU();
|
||||
#endif
|
||||
#if ARDUINO_USB_ON_BOOT
|
||||
USB.begin();
|
||||
#endif
|
||||
loopTaskWDTEnabled = false;
|
||||
initArduino();
|
||||
|
Reference in New Issue
Block a user