193 lines
8.6 KiB
Arduino
193 lines
8.6 KiB
Arduino
|
#include "USB.h"
|
||
|
#include "USBMSC.h"
|
||
|
|
||
|
#if ARDUINO_USB_CDC_ON_BOOT
|
||
|
#define HWSerial Serial0
|
||
|
#define USBSerial Serial
|
||
|
#else
|
||
|
#define HWSerial Serial
|
||
|
USBCDC USBSerial;
|
||
|
#endif
|
||
|
|
||
|
USBMSC MSC;
|
||
|
|
||
|
#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 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_TBL2B(l,h) FAT_U8(l), FAT_U8(((l >> 8) & 0xF) | ((h << 4) & 0xF0)), FAT_U8(h >> 4)
|
||
|
|
||
|
#define README_CONTENTS "This is tinyusb's MassStorage Class demo.\r\n\r\nIf you find any bugs or get any questions, feel free to file an\r\nissue at github.com/hathach/tinyusb"
|
||
|
|
||
|
static const uint32_t DISK_SECTOR_COUNT = 2 * 8; // 8KB is the smallest size that windows allow to mount
|
||
|
static const uint16_t DISK_SECTOR_SIZE = 512; // Should be 512
|
||
|
static const uint16_t DISC_SECTORS_PER_TABLE = 1; //each table sector can fit 170KB (340 sectors)
|
||
|
|
||
|
static uint8_t msc_disk[DISK_SECTOR_COUNT][DISK_SECTOR_SIZE] =
|
||
|
{
|
||
|
//------------- Block0: Boot Sector -------------//
|
||
|
{
|
||
|
// Header (62 bytes)
|
||
|
0xEB, 0x3C, 0x90, //jump_instruction
|
||
|
'M' , 'S' , 'D' , 'O' , 'S' , '5' , '.' , '0' , //oem_name
|
||
|
FAT_U16(DISK_SECTOR_SIZE), //bytes_per_sector
|
||
|
FAT_U8(1), //sectors_per_cluster
|
||
|
FAT_U16(1), //reserved_sectors_count
|
||
|
FAT_U8(1), //file_alloc_tables_num
|
||
|
FAT_U16(16), //max_root_dir_entries
|
||
|
FAT_U16(DISK_SECTOR_COUNT), //fat12_sector_num
|
||
|
0xF8, //media_descriptor
|
||
|
FAT_U16(DISC_SECTORS_PER_TABLE), //sectors_per_alloc_table;//FAT12 and FAT16
|
||
|
FAT_U16(1), //sectors_per_track;//A value of 0 may indicate LBA-only access
|
||
|
FAT_U16(1), //num_heads
|
||
|
FAT_U32(0), //hidden_sectors_count
|
||
|
FAT_U32(0), //total_sectors_32
|
||
|
0x00, //physical_drive_number;0x00 for (first) removable media, 0x80 for (first) fixed disk
|
||
|
0x00, //reserved
|
||
|
0x29, //extended_boot_signature;//should be 0x29
|
||
|
FAT_U32(0x1234), //serial_number: 0x1234 => 1234
|
||
|
'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , //volume_label padded with spaces (0x20)
|
||
|
'F' , 'A' , 'T' , '1' , '2' , ' ' , ' ' , ' ' , //file_system_type padded with spaces (0x20)
|
||
|
|
||
|
// Zero up to 2 last bytes of FAT magic code (448 bytes)
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
|
||
|
//boot signature (2 bytes)
|
||
|
0x55, 0xAA
|
||
|
},
|
||
|
|
||
|
//------------- Block1: FAT12 Table -------------//
|
||
|
{
|
||
|
FAT_TBL2B(0xFF8, 0xFFF), FAT_TBL2B(0xFFF, 0x000) // first 2 entries must be 0xFF8 0xFFF, third entry is cluster end of readme file
|
||
|
},
|
||
|
|
||
|
//------------- Block2: Root Directory -------------//
|
||
|
{
|
||
|
// first entry is volume label
|
||
|
'E' , 'S' , 'P' , '3' , '2' , 'S' , '2' , ' ' ,
|
||
|
'M' , 'S' , 'C' ,
|
||
|
0x08, //FILE_ATTR_VOLUME_LABEL
|
||
|
0x00,
|
||
|
FAT_MS2B(0,0),
|
||
|
FAT_HMS2B(0,0,0),
|
||
|
FAT_YMD2B(0,0,0),
|
||
|
FAT_YMD2B(0,0,0),
|
||
|
FAT_U16(0),
|
||
|
FAT_HMS2B(13,42,30), //last_modified_hms
|
||
|
FAT_YMD2B(2018,11,5), //last_modified_ymd
|
||
|
FAT_U16(0),
|
||
|
FAT_U32(0),
|
||
|
|
||
|
// second entry is readme file
|
||
|
'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' ,//file_name[8]; padded with spaces (0x20)
|
||
|
'T' , 'X' , 'T' , //file_extension[3]; padded with spaces (0x20)
|
||
|
0x20, //file attributes: FILE_ATTR_ARCHIVE
|
||
|
0x00, //ignore
|
||
|
FAT_MS2B(1,980), //creation_time_10_ms (max 199x10 = 1s 990ms)
|
||
|
FAT_HMS2B(13,42,36), //create_time_hms [5:6:5] => h:m:(s/2)
|
||
|
FAT_YMD2B(2018,11,5), //create_time_ymd [7:4:5] => (y+1980):m:d
|
||
|
FAT_YMD2B(2020,11,5), //last_access_ymd
|
||
|
FAT_U16(0), //extended_attributes
|
||
|
FAT_HMS2B(13,44,16), //last_modified_hms
|
||
|
FAT_YMD2B(2019,11,5), //last_modified_ymd
|
||
|
FAT_U16(2), //start of file in cluster
|
||
|
FAT_U32(sizeof(README_CONTENTS) - 1) //file size
|
||
|
},
|
||
|
|
||
|
//------------- Block3: Readme Content -------------//
|
||
|
README_CONTENTS
|
||
|
};
|
||
|
|
||
|
static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){
|
||
|
HWSerial.printf("MSC WRITE: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize);
|
||
|
memcpy(msc_disk[lba] + offset, buffer, bufsize);
|
||
|
return bufsize;
|
||
|
}
|
||
|
|
||
|
static int32_t onRead(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){
|
||
|
HWSerial.printf("MSC READ: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize);
|
||
|
memcpy(buffer, msc_disk[lba] + offset, bufsize);
|
||
|
return bufsize;
|
||
|
}
|
||
|
|
||
|
static bool onStartStop(uint8_t power_condition, bool start, bool load_eject){
|
||
|
HWSerial.printf("MSC START/STOP: power: %u, start: %u, eject: %u\n", power_condition, start, load_eject);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
|
||
|
if(event_base == ARDUINO_USB_EVENTS){
|
||
|
arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
|
||
|
switch (event_id){
|
||
|
case ARDUINO_USB_STARTED_EVENT:
|
||
|
HWSerial.println("USB PLUGGED");
|
||
|
break;
|
||
|
case ARDUINO_USB_STOPPED_EVENT:
|
||
|
HWSerial.println("USB UNPLUGGED");
|
||
|
break;
|
||
|
case ARDUINO_USB_SUSPEND_EVENT:
|
||
|
HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
|
||
|
break;
|
||
|
case ARDUINO_USB_RESUME_EVENT:
|
||
|
HWSerial.println("USB RESUMED");
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void setup() {
|
||
|
HWSerial.begin(115200);
|
||
|
HWSerial.setDebugOutput(true);
|
||
|
|
||
|
USB.onEvent(usbEventCallback);
|
||
|
MSC.vendorID("ESP32");//max 8 chars
|
||
|
MSC.productID("USB_MSC");//max 16 chars
|
||
|
MSC.productRevision("1.0");//max 4 chars
|
||
|
MSC.onStartStop(onStartStop);
|
||
|
MSC.onRead(onRead);
|
||
|
MSC.onWrite(onWrite);
|
||
|
MSC.mediaPresent(true);
|
||
|
MSC.begin(DISK_SECTOR_COUNT, DISK_SECTOR_SIZE);
|
||
|
USBSerial.begin();
|
||
|
USB.begin();
|
||
|
}
|
||
|
|
||
|
void loop() {
|
||
|
// put your main code here, to run repeatedly:
|
||
|
}
|