├── .vscode └── settings.json ├── examples ├── host │ ├── remote_pendrive │ │ ├── embedded │ │ │ ├── README.md │ │ │ ├── index.html │ │ │ └── app.css │ │ ├── README.md │ │ ├── index_html.h │ │ ├── remote_pendrive.ino │ │ ├── app_css.h │ │ └── server.ino │ ├── acm │ │ └── acm.ino │ └── msc │ │ └── msc.ino └── device │ ├── all_in_one │ ├── partitions.csv │ └── all_in_one.ino │ ├── msc │ ├── flashdisk │ │ ├── partitions.csv │ │ └── flashdisk.ino │ ├── sd_msc │ │ ├── sd_msc.ino │ │ └── test.ino │ └── ramdisk │ │ └── ramdisk.ino │ ├── hid │ ├── mouse │ │ └── mouse.ino │ ├── keyboard │ │ └── keyboard.ino │ ├── generic │ │ └── generic.ino │ ├── composite │ │ └── composite.ino │ ├── keyboard2 │ │ └── keyboard2.ino │ └── gamepad │ │ └── gamepad.ino │ ├── dfu │ └── dfu.ino │ ├── webusb │ └── webusb.ino │ ├── basic_setup │ └── basic_setup.ino │ ├── cdc │ └── cdc.ino │ └── midi │ └── midi.ino ├── src ├── hidgeneric.h ├── diskio_rawmsc.hpp ├── hidkeyboard.h ├── dfuusb.h ├── flashdisk.h ├── hidmouse.h ├── sdusb.h ├── device │ ├── dfu │ │ └── dfuusb.cpp │ ├── hid │ │ ├── hidgeneric.cpp │ │ ├── hidgamepad.cpp │ │ ├── hidkeyboard.cpp │ │ ├── hidusb.cpp │ │ ├── hidmouse.cpp │ │ └── hidcomposite.cpp │ ├── msc │ │ ├── mscusb.cpp │ │ ├── ramdisk.cpp │ │ ├── sdcard.cpp │ │ └── flashdisk.cpp │ ├── cdc │ │ └── cdcusb.cpp │ ├── web │ │ └── webusb.cpp │ └── midi │ │ └── midiusb.cpp ├── hidgamepad.h ├── usb_descriptors.h ├── usb_device.hpp ├── usb_host.hpp ├── hidcomposite.h ├── usb_acm.hpp ├── hidusb.h ├── midiusb.h ├── cdcusb.h ├── host │ ├── common │ │ ├── usb_device.cpp │ │ └── usb_host.cpp │ ├── msc │ │ └── vfs │ │ │ └── diskio_rawmsc.cpp │ └── acm │ │ └── usb_acm.cpp ├── mscusb.h ├── usb_msc.hpp ├── webusb.h ├── esptinyusb.h ├── ramdisk.h ├── usb_requests.hpp ├── hidkeylayout.h ├── usb_descriptors.cpp └── esptinyusb.cpp ├── library.properties ├── LICENSE └── README.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "mscusb.h": "c" 4 | } 5 | } -------------------------------------------------------------------------------- /examples/host/remote_pendrive/embedded/README.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | Upload files to pendrive which will be inserted into esp32 S2 and use `#define SERVE_WEB_FROM_PENDRIVE 1`. -------------------------------------------------------------------------------- /src/hidgeneric.h: -------------------------------------------------------------------------------- 1 | 2 | #include "hidusb.h" 3 | 4 | #pragma once 5 | #if CFG_TUD_HID 6 | 7 | class HIDgeneric : public HIDusb 8 | { 9 | public: 10 | HIDgeneric(uint8_t id = 1); 11 | bool begin(char* str = nullptr); 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /examples/device/all_in_one/partitions.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x9000, 0x5000, 3 | otadata, data, ota, 0xe000, 0x2000, 4 | app0, app, ota_0, 0x10000, 0x60000, 5 | ffat, data, fat, ,0x100000, 6 | ffat1, data, fat, ,0x100000, -------------------------------------------------------------------------------- /examples/device/msc/flashdisk/partitions.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x9000, 0x5000, 3 | otadata, data, ota, 0xe000, 0x2000, 4 | app0, app, ota_0, 0x10000, 0x60000, 5 | ffat, data, fat, ,0x100000, 6 | ffat1, data, fat, ,0x100000, -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32TinyUSB 2 | version=2.0.2 3 | author=Dariusz Krempa 4 | maintainer=Dariusz Krempa 5 | sentence=USB functions for ESP32-S2 6 | paragraph=This library provides an implementation of tinyusb for the ESP32-S2 for Arduino. 7 | category=Communication 8 | architectures=esp32 9 | includes= 10 | depends= 11 | url=https://github.com/chegewara/EspTinyUSB 12 | -------------------------------------------------------------------------------- /src/diskio_rawmsc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "diskio_impl.h" 5 | #include "ffconf.h" 6 | #include "ff.h" 7 | #include "esp_log.h" 8 | #include "diskio_rawflash.h" 9 | #include "esp_compiler.h" 10 | 11 | esp_err_t ff_msc_register_raw_partition(BYTE pdrv, USBmscDevice* part_handle); 12 | BYTE ff_msc_get_pdrv_raw(const void* part_handle); 13 | esp_err_t vfs_fat_rawmsc_mount(const char* base_path, 14 | const esp_vfs_fat_mount_config_t* mount_config, uint8_t lun); 15 | 16 | -------------------------------------------------------------------------------- /src/hidkeyboard.h: -------------------------------------------------------------------------------- 1 | 2 | #include "hidusb.h" 3 | #include "hidkeylayout.h" 4 | 5 | #pragma once 6 | #if CFG_TUD_HID 7 | 8 | class HIDkeyboard : public HIDusb 9 | { 10 | public: 11 | HIDkeyboard(uint8_t id = 3); 12 | bool begin(char* str = nullptr); 13 | 14 | bool sendKey(uint8_t _keycode, uint8_t modifier = 0); 15 | bool sendChar(uint8_t _keycode); 16 | bool sendPress(uint8_t _keycode, uint8_t modifier = 0); 17 | bool sendRelease(); 18 | bool sendString(const char* text); 19 | bool sendString(String text); 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/dfuusb.h: -------------------------------------------------------------------------------- 1 | 2 | #include "esptinyusb.h" 3 | 4 | #pragma once 5 | 6 | #if CFG_TUD_DFU_RUNTIME 7 | 8 | class DFUusb : public EspTinyUSB 9 | { 10 | public: 11 | DFUusb(); 12 | bool begin(char* str = nullptr); 13 | int available(void) { return -1; } 14 | int peek(void) { return -1; } 15 | int read(void) { return -1; } 16 | size_t read(uint8_t *buffer, size_t size) { return 0; } 17 | void flush(void) { return; } 18 | size_t write(uint8_t) { return 0; } 19 | size_t write(const uint8_t *buffer, size_t size) { return 0; } 20 | void setBaseEP(uint8_t) { return; } 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /examples/device/hid/mouse/mouse.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple HID mouse 3 | * author: chegewara 4 | */ 5 | 6 | #include "hidmouse.h" 7 | #if CFG_TUD_HID 8 | HIDmouse mouse; 9 | 10 | void setup() 11 | { 12 | Serial.begin(115200); 13 | mouse.begin(); 14 | } 15 | 16 | void loop() 17 | { 18 | delay(1000); 19 | mouse.doublePressLeft(); 20 | delay(1000); 21 | mouse.pressRight(); 22 | delay(1000); 23 | mouse.move(-15, -15); 24 | delay(1000); 25 | mouse.pressLeft(); 26 | delay(1000); 27 | mouse.scrollUp(1); 28 | delay(1000); 29 | mouse.scrollDown(1); 30 | delay(1000); 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /examples/device/msc/sd_msc/sd_msc.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple MSC device with SD card 3 | * author: chegewara 4 | */ 5 | #include "sdusb.h" 6 | #if CFG_TUD_MSC 7 | 8 | #define SD_MISO 37 9 | #define SD_MOSI 39 10 | #define SD_SCK 38 11 | #define SD_CS 40 12 | 13 | SDCard2USB dev; 14 | 15 | void setup() 16 | { 17 | Serial.begin(115200); 18 | 19 | if(dev.initSD(SD_SCK, SD_MISO, SD_MOSI, SD_CS)) 20 | { 21 | if(dev.begin()) { 22 | Serial.println("MSC lun 1 begin"); 23 | } else log_e("LUN 1 failed"); 24 | } else Serial.println("Failed to init SD"); 25 | 26 | test(); 27 | } 28 | 29 | void loop() 30 | { 31 | delay(1000); 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/flashdisk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "mscusb.h" 3 | #include "esp_vfs_fat.h" 4 | 5 | #if CFG_TUD_MSC 6 | 7 | class FlashUSB : public MSCusb { 8 | const char *base_path = "/fatfs"; 9 | const char *partition_label = NULL; 10 | wl_handle_t wl_handle; 11 | 12 | public: 13 | FlashUSB(bool aes = false); 14 | bool begin(char* str = nullptr); 15 | bool init(const char* path = "/fatfs",char* label = NULL); 16 | void setCallbacks(MSCCallbacks*); 17 | void setCapacity(uint32_t count, uint32_t size); 18 | bool isReady(); 19 | 20 | MSCCallbacks* m_private; 21 | uint32_t block_count = 0; 22 | uint32_t block_size = 0; 23 | bool sdcardReady; 24 | bool encrypted; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /examples/device/hid/keyboard/keyboard.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple HID keyboard 3 | * author: chegewara 4 | */ 5 | 6 | 7 | #include "hidkeyboard.h" 8 | #include "Wire.h" 9 | #if CFG_TUD_HID 10 | #define KEYBOARD_I2C_ADDR 0X5f 11 | 12 | HIDkeyboard dev; 13 | 14 | void setup() 15 | { 16 | Serial.begin(115200); 17 | Wire.begin(7,8); 18 | dev.begin(); 19 | } 20 | 21 | void loop() 22 | { 23 | delay(10); 24 | Wire.requestFrom(KEYBOARD_I2C_ADDR, 1); // request 1 byte from keyboard 25 | while (Wire.available()) { 26 | uint8_t key_val = Wire.read(); // receive a byte as character 27 | if(key_val != 0) { 28 | dev.sendChar(key_val); // send ASCII char 29 | } 30 | } 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/hidmouse.h: -------------------------------------------------------------------------------- 1 | 2 | #include "hidusb.h" 3 | 4 | #pragma once 5 | #define LEFT_BTN 1 6 | #define RIGHT_BTN 2 7 | #define MIDDLE_BTN 3 8 | #define BACK_BTN 4 9 | #define FORWARD_BTN 5 10 | #if CFG_TUD_HID 11 | 12 | class HIDmouse : public HIDusb 13 | { 14 | public: 15 | HIDmouse(uint8_t id = 2); 16 | bool begin(char* str = nullptr); 17 | 18 | void buttons(uint8_t buttons); 19 | void pressLeft(); 20 | void pressMiddle(); 21 | void pressRight(); 22 | void doublePressLeft(); 23 | void backwardBtn(); 24 | void forwardBtn(); 25 | void scrollUp(uint8_t); 26 | void scrollDown(uint8_t); 27 | 28 | void move(int8_t x, int8_t y); 29 | void wheel(int8_t x, int8_t y); 30 | 31 | uint8_t button; 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/sdusb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "mscusb.h" 3 | #include "SD.h" 4 | 5 | #if CFG_TUD_MSC 6 | 7 | class SDCard2USB : public MSCusb { 8 | // fs::SDFS &_sd; 9 | 10 | public: 11 | SDCard2USB(); 12 | bool begin(char* str = nullptr); 13 | bool initSD(uint8_t ssPin=SS, SPIClass &spi=SPI, uint32_t frequency=4000000, const char * mountpoint="/sd", uint8_t max_files=5); 14 | bool initSD(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); 15 | void setCallbacks(MSCCallbacks*); 16 | void setCapacity(uint32_t count, uint32_t size); 17 | void ready(bool ready); 18 | bool isReady(); 19 | 20 | MSCCallbacks* m_private; 21 | uint32_t block_count = 0; 22 | uint32_t block_size = 512; 23 | bool sdcardReady; 24 | 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /examples/device/dfu/dfu.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple example that enables DFU class, it will restart esp32 to bootloader mode when issued idf.py dfu-flash 3 | * also CDC class is active, but does nothing 4 | * author: chegewara 5 | */ 6 | #include "cdcusb.h" 7 | #include "dfuusb.h" 8 | #if CFG_TUD_DFU_RUNTIME 9 | 10 | // CDCusb USBSerial; 11 | DFUusb dev; 12 | 13 | void conCB(bool isCon) 14 | { 15 | Serial.printf("connection state changed, new state %s\n", isCon? "connected" : "disconnected"); 16 | } 17 | 18 | void setup() { 19 | Serial.begin(115200); 20 | 21 | dev.begin(); 22 | // if(!USBSerial.begin()) 23 | // Serial.println("Failed to start CDC USB stack"); 24 | 25 | // USBSerial.onConnect(conCB); 26 | } 27 | 28 | 29 | void loop() { 30 | 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /examples/device/msc/flashdisk/flashdisk.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple MSC device, use fat partition 3 | * author: chegewara 4 | */ 5 | #include "flashdisk.h" 6 | 7 | #if CFG_TUD_MSC 8 | 9 | FlashUSB dev; 10 | FlashUSB dev1; 11 | char *l1 = "ffat"; 12 | char *l2 = "ffat1"; 13 | void setup() 14 | { 15 | Serial.begin(115200); 16 | 17 | if (dev.init("/fat1", "ffat")) 18 | { 19 | if (dev.begin()) 20 | { 21 | Serial.println("MSC lun 1 begin"); 22 | } 23 | else 24 | log_e("LUN 1 failed"); 25 | } 26 | if (dev1.init("/fat2", "ffat1")) 27 | { 28 | if (dev1.begin()) 29 | { 30 | Serial.println("MSC lun 2 begin"); 31 | } 32 | else 33 | log_e("LUN 2 failed"); 34 | } 35 | } 36 | 37 | void loop() 38 | { 39 | delay(1000); 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /examples/device/hid/generic/generic.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple vendor In/Out HID device 3 | * author: chegewara 4 | */ 5 | 6 | #include "hidgeneric.h" 7 | #if CFG_TUD_HID 8 | HIDgeneric dev; 9 | 10 | class MyHIDCallbacks: public HIDCallbacks{ 11 | void onData(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { 12 | Serial.printf("ID: %d, type: %d, size: %d\n", report_id, (int)report_type, bufsize); 13 | for (size_t i = 0; i < bufsize; i++) 14 | { 15 | Serial.printf("%c", buffer[i]); 16 | } 17 | Serial.println(); 18 | } 19 | }; 20 | 21 | void setup() 22 | { 23 | Serial.begin(115200); 24 | dev.begin(); 25 | dev.setCallbacks(new MyHIDCallbacks()); 26 | } 27 | 28 | void loop() 29 | { 30 | delay(1000); 31 | dev.write("test", 4); 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/device/dfu/dfuusb.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "soc/rtc_cntl_reg.h" 3 | 4 | #include "dfuusb.h" 5 | #if CFG_TUD_DFU_RUNTIME 6 | 7 | DFUusb* _DFU = NULL; 8 | 9 | DFUusb::DFUusb() 10 | { 11 | enableDFU = true; 12 | _DFU = this; 13 | } 14 | 15 | bool DFUusb::begin(char* str) 16 | { 17 | // Interface number, string index, attributes, detach timeout, transfer size 18 | uint8_t dfu[] = {TUD_DFU_RT_DESCRIPTOR(ifIdx++, 9, 0x0f, 1000, 1024)}; 19 | memcpy(&desc_configuration[total], dfu, sizeof(dfu)); 20 | total += sizeof(dfu); 21 | count++; 22 | if (!EspTinyUSB::begin(str, 9)) return false; 23 | return true; 24 | } 25 | 26 | /** 27 | * enter dfu bootload mode 28 | */ 29 | void tud_dfu_rt_reboot_to_dfu(void) 30 | { 31 | _DFU->persistentReset(RESTART_BOOTLOADER_DFU); 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /examples/device/msc/ramdisk/ramdisk.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple MSC device, use as ram disk 3 | * author: chegewara 4 | */ 5 | #include "ramdisk.h" 6 | //#define AUTO_ALLOCATE_DISK 7 | #define BLOCK_COUNT 2 * 100 8 | #define BLOCK_SIZE 512 9 | #if CFG_TUD_MSC 10 | 11 | USBramdisk dev; 12 | 13 | void setup() 14 | { 15 | Serial.begin(115200); 16 | 17 | #ifndef AUTO_ALLOCATE_DISK 18 | uint8_t* disk = (uint8_t*)ps_calloc(BLOCK_COUNT, BLOCK_SIZE); 19 | dev.setDiskMemory(disk, true); // pass pointer to allocated ram disk and initialize it with demo FAT12 (true) 20 | #endif 21 | dev.setCapacity(BLOCK_COUNT, BLOCK_SIZE); // if PSRAM is enableb, then ramdisk will be initialized in it 22 | 23 | if(dev.begin()) { 24 | Serial.println("MSC lun 1 begin"); 25 | } else log_e("LUN 1 failed"); 26 | 27 | } 28 | 29 | void loop() 30 | { 31 | delay(1000); 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/hidgamepad.h: -------------------------------------------------------------------------------- 1 | #include "hidusb.h" 2 | 3 | #pragma once 4 | #if CFG_TUD_HID 5 | // | X | Y | Z | Rz | Rx | Ry (1 byte each) | hat/DPAD (1 byte) | Button Map (4 bytes) | 6 | typedef struct { 7 | int8_t x; 8 | int8_t y; 9 | int8_t z; 10 | int8_t Rz; 11 | int8_t Rx; 12 | int8_t Ry; 13 | uint8_t hat; 14 | uint32_t buttons; 15 | }__attribute((packed)) hid_gamepad_t; 16 | class HIDgamepad : public HIDusb 17 | { 18 | public: 19 | HIDgamepad(uint8_t id = 4); 20 | bool begin(char* str = nullptr); 21 | 22 | void buttons(uint32_t); 23 | void joystick1(int8_t, int8_t, int8_t); 24 | void joystick2(int8_t, int8_t, int8_t); 25 | void sendAll(uint32_t bt, int8_t x, int8_t y, int8_t z, int8_t rx, int8_t ry, int8_t rz, uint8_t hat); 26 | void hat(uint8_t); 27 | 28 | private: 29 | void sendReport(); 30 | hid_gamepad_t report; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/usb_descriptors.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) 18 | 19 | #define USB_ESPRESSIF_VID 0x303A 20 | 21 | #define USB_STRING_DESCRIPTOR_ARRAY_SIZE 10 22 | typedef char *tusb_desc_strarray_device_t[USB_STRING_DESCRIPTOR_ARRAY_SIZE]; 23 | -------------------------------------------------------------------------------- /src/usb_device.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "esp_err.h" 3 | #include "usb/usb_host.h" 4 | #include "usb_host.hpp" 5 | 6 | // class USBhost; 7 | class USBhostDevice 8 | { 9 | protected: 10 | USBhost* _host; 11 | uint8_t itf_num; 12 | 13 | usb_transfer_t *xfer_ctrl = NULL; //Must be large enough to contain CBW and MSC reset control transfer 14 | usb_transfer_t *xfer_write = NULL; //Must be large enough to contain CBW and MSC reset control transfer 15 | usb_transfer_t *xfer_read = NULL; //Must be large enough to contain CBW and MSC reset control transfer 16 | usb_transfer_t *xfer_out[2] = {}; //Must be large enough to contain CBW and MSC reset control transfer 17 | usb_transfer_t *xfer_in = nullptr; //Must be large enough to contain CSW and Data 18 | 19 | public: 20 | USBhostDevice(); 21 | ~USBhostDevice(); 22 | 23 | virtual bool init() = 0; 24 | esp_err_t allocate(size_t); 25 | 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /examples/device/hid/composite/composite.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple HID mouse and keyboard composite 3 | * author: chegewara 4 | */ 5 | 6 | 7 | #include "hidcomposite.h" 8 | #if CFG_TUD_HID 9 | HIDcomposite device; 10 | 11 | void setup() 12 | { 13 | Serial.begin(115200); 14 | device.begin(); 15 | } 16 | 17 | void loop() 18 | { 19 | delay(1000); 20 | device.doublePressLeft(); 21 | delay(1000); 22 | device.pressRight(); 23 | delay(1000); 24 | device.move(-150, -150); 25 | delay(1000); 26 | device.move(150, 150); 27 | delay(1000); 28 | device.pressLeft(); 29 | delay(1000); 30 | device.scrollUp(1); 31 | delay(1000); 32 | device.scrollDown(1); 33 | delay(1000); 34 | device.sendKey(HID_KEY_A); 35 | delay(1000); 36 | Serial.println(device.sendString(String("123456789\n"))?"OK":"FAIL"); 37 | delay(1000); 38 | Serial.println(device.sendString(String("abcdefghijklmnopqrst Uvwxyz\n"))?"OK":"FAIL"); 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /examples/device/webusb/webusb.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple webUSB device to echo with tinyusb webecho terminal 3 | * VID = 0xcafe to match filter on website 4 | * author: chegewara 5 | */ 6 | #include "webusb.h" 7 | #if CFG_TUD_VENDOR 8 | WebUSB USBSerial; 9 | 10 | class MyWebUSBCallbacks : public WebUSBCallbacks{ 11 | void onConnect(bool state) { 12 | Serial.printf("webusb is %s\n", state ? "connected":"disconnected"); 13 | } 14 | }; 15 | 16 | void setup() { 17 | Serial.begin(115200); 18 | USBSerial.deviceID(0xcafe, 0x0002); 19 | 20 | USBSerial.setCallbacks(new MyWebUSBCallbacks()); 21 | 22 | if(!USBSerial.begin()) 23 | Serial.println("Failed to start webUSB stack"); 24 | 25 | } 26 | 27 | void echo_all(char c) 28 | { 29 | Serial.write(c); 30 | USBSerial.write(c); 31 | } 32 | 33 | void loop() { 34 | while (USBSerial.available()) 35 | { 36 | echo_all(USBSerial.read()); 37 | } 38 | 39 | while (Serial.available()) 40 | { 41 | echo_all(Serial.read()); 42 | } 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/usb_host.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "usb/usb_host.h" 3 | 4 | class USBhost 5 | { 6 | friend void _client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg); 7 | 8 | protected: 9 | usb_device_info_t dev_info; 10 | usb_host_client_handle_t client_hdl; 11 | usb_device_handle_t dev_hdl; 12 | 13 | usb_host_client_event_cb_t _client_event_cb = nullptr; 14 | uint8_t _dev_addr; 15 | uint8_t _configs; 16 | uint8_t _itfs; 17 | 18 | public: 19 | USBhost(); 20 | ~USBhost(); 21 | 22 | 23 | bool init(bool create_tasks = true); 24 | bool open(const usb_host_client_event_msg_t *event_msg); 25 | usb_device_info_t getDeviceInfo(); 26 | const usb_device_desc_t* getDeviceDescriptor(); 27 | const usb_config_desc_t* getConfigurationDescriptor(); 28 | 29 | uint8_t getConfiguration(); 30 | bool setConfiguration(uint8_t); 31 | void parseConfig(); 32 | 33 | usb_host_client_handle_t clientHandle(); 34 | usb_device_handle_t deviceHandle(); 35 | 36 | void registerClientCb(usb_host_client_event_cb_t cb) { _client_event_cb = cb; } 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 chegewara 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/device/hid/hidgeneric.cpp: -------------------------------------------------------------------------------- 1 | #include "hidgeneric.h" 2 | #define EPNUM_HID 0x03 3 | #define REPORT_ID 1 4 | #if CFG_TUD_HID 5 | 6 | HIDgeneric::HIDgeneric(uint8_t reportid) 7 | { 8 | report_id = reportid; 9 | enableHID = true; 10 | _EPNUM_HID = EPNUM_HID; 11 | } 12 | 13 | bool HIDgeneric::begin(char* str) 14 | { 15 | uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_BUFSIZE, HID_REPORT_ID(report_id))}; 16 | // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval 17 | uint8_t hid[] = {TUD_HID_INOUT_DESCRIPTOR(ifIdx++, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), _EPNUM_HID, (uint8_t)(0x80 | _EPNUM_HID), CFG_TUD_HID_BUFSIZE, 10)}; 18 | memcpy(&desc_configuration[total], hid, sizeof(hid)); 19 | total += sizeof(hid); 20 | count++; 21 | 22 | memcpy(&hid_report_desc[EspTinyUSB::hid_report_desc_len], (uint8_t *)desc_hid_report, sizeof(desc_hid_report)); 23 | EspTinyUSB::hid_report_desc_len += TUD_HID_INOUT_DESC_LEN; 24 | log_d("begin len: %d", EspTinyUSB::hid_report_desc_len); 25 | 26 | return EspTinyUSB::begin(str, 6); 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /examples/device/basic_setup/basic_setup.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple example to show how to set product variables 3 | * author: chegewara 4 | */ 5 | #include "Arduino.h" 6 | #include "cdcusb.h" 7 | 8 | CDCusb USBSerial; 9 | 10 | class MyUSBSallnbacks: public USBCallbacks { 11 | void onMount() 12 | { 13 | Serial.println("device mounted"); 14 | } 15 | void onUnmount() 16 | { 17 | Serial.println("device unmounted"); 18 | } 19 | void onSuspend() 20 | { 21 | Serial.println("device suspended"); 22 | } 23 | void onResume(bool resume) 24 | { 25 | Serial.println("device resumed"); 26 | } 27 | }; 28 | 29 | void setup() 30 | { 31 | Serial.begin(115200); 32 | USBSerial.manufacturer("espressif"); 33 | USBSerial.serial("1234-567890"); 34 | USBSerial.product("Test device"); 35 | USBSerial.revision(100); 36 | USBSerial.deviceID(0xdead, 0xbeef); 37 | USBSerial.registerDeviceCallbacks(new MyUSBSallnbacks()); 38 | 39 | 40 | if (!USBSerial.begin()) 41 | Serial.println("Failed to start CDC USB device"); 42 | 43 | } 44 | 45 | void loop() 46 | { 47 | delay(1000); 48 | } 49 | -------------------------------------------------------------------------------- /src/hidcomposite.h: -------------------------------------------------------------------------------- 1 | 2 | #include "hidusb.h" 3 | #include "hidkeylayout.h" 4 | #pragma once 5 | #define LEFT_BTN 1 6 | #define RIGHT_BTN 2 7 | #define MIDDLE_BTN 3 8 | #define BACK_BTN 4 9 | #define FORWARD_BTN 5 10 | #if CFG_TUD_HID 11 | 12 | class HIDcomposite : public HIDusb 13 | { 14 | public: 15 | HIDcomposite(uint8_t id = 2); 16 | bool begin(char* str = nullptr); 17 | 18 | void buttons(uint8_t buttons); 19 | void pressLeft(); 20 | void pressMiddle(); 21 | void pressRight(); 22 | void doublePressLeft(); 23 | void backwardBtn(); 24 | void forwardBtn(); 25 | void scrollUp(uint8_t); 26 | void scrollDown(uint8_t); 27 | 28 | void move(int8_t x, int8_t y); 29 | void wheel(int8_t x, int8_t y); 30 | 31 | bool sendKey(uint8_t _keycode, uint8_t modifier = 0); 32 | bool sendChar(uint8_t _keycode); 33 | bool sendPress(uint8_t _keycode, uint8_t modifier = 0); 34 | bool sendRelease(); 35 | bool sendString(const char* text); 36 | bool sendString(String text); 37 | 38 | private: 39 | uint8_t report_mouse; 40 | uint8_t report_keyboard; 41 | uint8_t button; 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/usb_acm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "usb_device.hpp" 3 | #include "usb_requests.hpp" 4 | 5 | #define CDC_DATA_IN 1 6 | #define CDC_DATA_OUT 2 7 | #define CDC_CTRL_SET_CONTROL_LINE_STATE 3 8 | #define CDC_CTRL_SET_LINE_CODING 4 9 | #define CDC_CTRL_GET_LINE_CODING 5 10 | 11 | typedef void (*cdc_event_cb_t)(int, void* data, size_t len); 12 | class USBacmDevice : public USBhostDevice 13 | { 14 | private: 15 | const usb_ep_desc_t * ep_int; 16 | const usb_ep_desc_t * ep_in; 17 | const usb_ep_desc_t * ep_out; 18 | cdc_event_cb_t event_cb = nullptr; 19 | bool connected; 20 | 21 | public: 22 | USBacmDevice(const usb_config_desc_t* config_desc, USBhost*); 23 | ~USBacmDevice(); 24 | 25 | bool init(); 26 | void setControlLine(bool dtr, bool rts); 27 | void setLineCoding(uint32_t bitrate, uint8_t cf, uint8_t parity, uint8_t bits); 28 | void getLineCoding(); 29 | void INDATA(); 30 | void OUTDATA(uint8_t*, size_t); 31 | void onEvent(cdc_event_cb_t _cb); 32 | bool isConnected(); 33 | 34 | void _callback(int event, usb_transfer_t* data); 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /src/hidusb.h: -------------------------------------------------------------------------------- 1 | 2 | #include "esptinyusb.h" 3 | 4 | #pragma once 5 | #if CFG_TUD_HID 6 | 7 | class HIDCallbacks 8 | { 9 | public: 10 | virtual ~HIDCallbacks() { } 11 | virtual void onData(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { } 12 | }; 13 | 14 | class HIDusb : public EspTinyUSB 15 | { 16 | public: 17 | HIDusb(uint8_t reportid = 0); 18 | virtual bool begin(char* str = nullptr) = 0; 19 | void begin(uint8_t* desc_hid_report, size_t len1, uint8_t* hid, size_t len2); 20 | int available(void) { return -1; } 21 | int peek(void) { return -1; } 22 | int read(void) { return -1; } 23 | size_t read(uint8_t *buffer, size_t size) { return 0; } 24 | void flush(void) { return; } 25 | size_t write(uint8_t); 26 | size_t write(const uint8_t *buffer, size_t size); 27 | size_t write(char); 28 | size_t write(const char *buffer, size_t size); 29 | void setBaseEP(uint8_t); 30 | 31 | void setCallbacks(HIDCallbacks* cb); 32 | 33 | uint8_t _EPNUM_HID; 34 | uint8_t report_id; 35 | static uint8_t hid_report_desc[500]; 36 | static size_t hid_report_desc_len; 37 | HIDCallbacks* m_callbacks; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/midiusb.h: -------------------------------------------------------------------------------- 1 | 2 | #include "esptinyusb.h" 3 | 4 | #pragma once 5 | 6 | #ifdef CFG_TUD_MIDI 7 | 8 | class MIDIusb : public EspTinyUSB 9 | { 10 | public: 11 | MIDIusb(); 12 | bool begin(char* str = nullptr); 13 | int available(void); 14 | int peek(void) { return -1; } 15 | int read(void) { return -1; } 16 | size_t read(uint8_t *buffer, size_t size) { return 0; } 17 | void flush(void) { return; } 18 | size_t write(uint8_t) { return 0; } 19 | size_t write(const uint8_t *buffer, size_t size) { return 0; } 20 | 21 | bool setSong(uint8_t* song, size_t len); 22 | void playSong(); 23 | 24 | void noteON(uint8_t note, uint8_t velocity, uint8_t channel = 0); 25 | void noteOFF(uint8_t note, uint8_t velocity = 0, uint8_t channel = 0); 26 | void polyKey(uint8_t note, uint8_t pressure, uint8_t channel = 0); 27 | void controlChange(uint8_t controller, uint8_t value, uint8_t channel = 0); 28 | void programChange(uint8_t program, uint8_t channel = 0); 29 | void channelPresure(uint8_t presure, uint8_t channel = 0); 30 | void pitchChange(uint16_t value, uint8_t channel = 0); 31 | void setBaseEP(uint8_t); 32 | 33 | private: 34 | uint8_t* _song; 35 | size_t _len; 36 | uint8_t _EPNUM_MIDI; 37 | }; 38 | #endif 39 | -------------------------------------------------------------------------------- /examples/device/hid/keyboard2/keyboard2.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple HID keyboard 3 | * author: chegewara 4 | */ 5 | 6 | #include "hidkeyboard.h" 7 | #if CFG_TUD_HID 8 | HIDkeyboard dev; 9 | 10 | class MyHIDCallbacks: public HIDCallbacks{ 11 | void onData(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { 12 | Serial.printf("ID: %d, type: %d, size: %d\n", report_id, (int)report_type, bufsize); 13 | for (size_t i = 0; i < bufsize; i++) 14 | { 15 | Serial.printf("%d\n", buffer[i]); 16 | } 17 | } 18 | }; 19 | 20 | void dataCB(uint8_t report_id, uint8_t report_type, uint8_t const* buffer, uint16_t bufsize) 21 | { 22 | for (size_t i = 0; i < bufsize; i++) 23 | { 24 | Serial.printf("%d\n", buffer[i]); 25 | Serial.printf("%c\n", buffer[i]); 26 | } 27 | } 28 | 29 | 30 | void setup() 31 | { 32 | Serial.begin(115200); 33 | dev.begin(); 34 | dev.setCallbacks(new MyHIDCallbacks()); 35 | } 36 | 37 | void loop() 38 | { 39 | delay(1000); 40 | dev.sendKey(HID_KEY_A); 41 | delay(1000); 42 | Serial.println(dev.sendString(String("123456789\n"))?"OK":"FAIL"); 43 | delay(1000); 44 | Serial.println(dev.sendString(String("abcdefghijklmnopqrst Uvwxyz\n"))?"OK":"FAIL"); 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/cdcusb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "esptinyusb.h" 3 | #if CFG_TUD_CDC 4 | 5 | #include "class/cdc/cdc.h" 6 | 7 | class CDCCallbacks : public USBCallbacks { 8 | public: 9 | virtual ~CDCCallbacks() { } 10 | virtual bool onConnect(bool dtr, bool rts) { return true; } 11 | virtual void onData() { } 12 | virtual void onCodingChange(cdc_line_coding_t const* p_line_coding) { } 13 | virtual void onWantedChar(char c) { } 14 | }; 15 | 16 | class CDCusb : public EspTinyUSB 17 | { 18 | public: 19 | CDCusb(uint8_t itf = 0); 20 | bool begin(char* str = nullptr); 21 | int available(void); 22 | int peek(void); 23 | int read(void); 24 | size_t read(uint8_t *buffer, size_t size); 25 | void flush(void); 26 | size_t write(uint8_t); 27 | size_t write(const uint8_t *buffer, size_t size); 28 | void setBaseEP(uint8_t); 29 | uint32_t getBitrate(); 30 | uint8_t getParity(); 31 | uint8_t getDataBits(); 32 | uint8_t getStopBits(); 33 | void setWantedChar(char c); 34 | 35 | void setCallbacks(CDCCallbacks*); 36 | CDCCallbacks* m_callbacks = nullptr; 37 | operator bool() const; 38 | 39 | 40 | uint8_t _EPNUM_CDC; 41 | 42 | friend void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding); 43 | 44 | protected: 45 | cdc_line_coding_t coding; 46 | 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /examples/device/hid/gamepad/gamepad.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple HID gamepad, 32 buttons + 2x 3 axis + 8 position hat 3 | * author: chegewara 4 | */ 5 | 6 | 7 | #include "hidgamepad.h" 8 | #if CFG_TUD_HID 9 | HIDgamepad gamepad; 10 | 11 | void setup() 12 | { 13 | Serial.begin(115200); 14 | gamepad.begin(); 15 | } 16 | 17 | void loop() 18 | { 19 | delay(1000); 20 | if(!digitalRead(0)){ 21 | // 32 buttons 22 | for (size_t i = 0; i < 32; i++) 23 | { 24 | // buttons send map of buttons represented by bits 25 | gamepad.buttons(1<deviceHandle(); 26 | xfer_out[i]->device_handle = handle; 27 | xfer_out[i]->context = this; 28 | } 29 | } 30 | 31 | err = usb_host_transfer_alloc(out_worst_case_size, 0, &xfer_in); 32 | xfer_in->device_handle = _host->deviceHandle(); 33 | xfer_in->context = this; 34 | 35 | err = usb_host_transfer_alloc(out_worst_case_size, 0, &xfer_write); 36 | xfer_write->device_handle = _host->deviceHandle(); 37 | xfer_write->context = this; 38 | 39 | err = usb_host_transfer_alloc(out_worst_case_size, 0, &xfer_read); 40 | xfer_read->device_handle = _host->deviceHandle(); 41 | xfer_read->context = this; 42 | 43 | err = usb_host_transfer_alloc(64, 0, &xfer_ctrl); 44 | xfer_ctrl->device_handle = _host->deviceHandle(); 45 | xfer_ctrl->context = this; 46 | xfer_ctrl->bEndpointAddress = 0; 47 | 48 | return err; 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /examples/host/remote_pendrive/README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is cool example which allows to make USB host to wifi gateway with esp32 S2. It is opposite of USB device with sd card connected to it over SPI. 4 | The idea is to connect for example normal pendrive to esp32 S2 and open files, make dirs or delete files/folders. With some mode effort it will be possible to upload files to pendrive. 5 | At the same time it is possible to perform all normal operations on files from app level using POSIX functions. 6 | In addition there is possibility to setup wifi STA and AP credentials from website. 7 | 8 | Maybe it s not very impresive usage case, but with it it is possible to build app that will log everything to pendrive, maybe to add crash dump to pendrive or just device update from pendrive, which can be inserted with new firmware or just uploaded to it and triggered from website update. 9 | I can see more possibilities and i would like to see how others will use it. 10 | 11 | # Why posix 12 | 13 | It is possible to use only posix functions, because library is not prepared to work like FATFS or SPIFFS libraries included in arduino. 14 | I understand it would be nice to have such integration, but i decided to make this library arduino and esp-idf compatible. 15 | If anyone would like to add arduino-ish implementation of FS then i am open to merge PRs. 16 | 17 | 18 | # USAGE 19 | 20 | With `#define SERVE_WEB_FROM_PENDRIVE 1` (line 25) it is possible to control where web UI files are read from: 21 | 1. files embedded into app, which will increase app size 22 | 2. files are on pendrive, which not only let to decrease app size, but also allow to customize web UI without re-flashing app 23 | exmaple files are in embedded folder 24 | 25 | -------------------------------------------------------------------------------- /examples/device/cdc/cdc.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple CDC device connect with putty to use it 3 | * author: chegewara 4 | * Serial - used only for logging 5 | * Serial1 - can be used to control GPS or any other device, may be replaced with Serial 6 | */ 7 | #include "cdcusb.h" 8 | #if CFG_TUD_CDC 9 | CDCusb USBSerial; 10 | 11 | class MyUSBCallbacks : public CDCCallbacks { 12 | void onCodingChange(cdc_line_coding_t const* p_line_coding) 13 | { 14 | int bitrate = USBSerial.getBitrate(); 15 | Serial.printf("new bitrate: %d\n", bitrate); 16 | } 17 | 18 | bool onConnect(bool dtr, bool rts) 19 | { 20 | Serial.printf("connection state changed, dtr: %d, rts: %d\n", dtr, rts); 21 | return true; // allow to persist reset, when Arduino IDE is trying to enter bootloader mode 22 | } 23 | 24 | void onData() 25 | { 26 | int len = USBSerial.available(); 27 | Serial.printf("\nnew data, len %d\n", len); 28 | uint8_t buf[len] = {}; 29 | USBSerial.read(buf, len); 30 | Serial.write(buf, len); 31 | } 32 | 33 | void onWantedChar(char c) 34 | { 35 | Serial.printf("wanted char: %c\n", c); 36 | } 37 | }; 38 | 39 | 40 | void setup() 41 | { 42 | Serial.begin(115200); 43 | USBSerial.setCallbacks(new MyUSBCallbacks()); 44 | USBSerial.setWantedChar('x'); 45 | 46 | if (!USBSerial.begin()) 47 | Serial.println("Failed to start CDC USB stack"); 48 | 49 | } 50 | 51 | void loop() 52 | { 53 | while (Serial.available()) 54 | { 55 | int len = Serial.available(); 56 | char buf1[len]; 57 | Serial.read(buf1, len); 58 | int a = USBSerial.write((uint8_t*)buf1, len); 59 | } 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /examples/device/midi/midi.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple example MIDI class 3 | * author: chegewara 4 | */ 5 | #include "midiusb.h" 6 | #include "song.h" 7 | // #ifdef CFG_TUD_MIDI 8 | 9 | MIDIusb midi; 10 | 11 | void setup() { 12 | Serial.begin(115200); 13 | midi.begin(); 14 | delay(1000); 15 | if(midi.setSong(song, song_len)) { 16 | Serial.println("we can play now"); 17 | midi.playSong(); 18 | } 19 | } 20 | 21 | 22 | // Variable that holds the current position in the sequence. 23 | uint32_t note_pos = 0; 24 | 25 | // Store example melody as an array of note values 26 | uint8_t note_sequence[] = 27 | { 28 | 74,78,81,86,90,93,98,102,57,61,66,69,73,78,81,85,88,92,97,100,97,92,88,85,81,78, 29 | 74,69,66,62,57,62,66,69,74,78,81,86,90,93,97,102,97,93,90,85,81,78,73,68,64,61, 30 | 56,61,64,68,74,78,81,86,90,93,98,102 31 | }; 32 | 33 | void midi_task(void) 34 | { 35 | static uint32_t start_ms = 0; 36 | 37 | // send note every 1000 ms 38 | if (millis() - start_ms < 286) return; // not enough time 39 | start_ms += 286; 40 | 41 | // Previous positions in the note sequence. 42 | int previous = note_pos - 1; 43 | 44 | // If we currently are at position 0, set the 45 | // previous position to the last note in the sequence. 46 | if (previous < 0) previous = sizeof(note_sequence) - 1; 47 | 48 | // Send Note On for current position at full velocity (127) on channel 1. 49 | midi.noteON(note_sequence[note_pos], 127); 50 | 51 | // Send Note Off for previous note. 52 | midi.noteOFF(note_sequence[previous]); 53 | 54 | // Increment position 55 | note_pos++; 56 | 57 | // If we are at the end of the sequence, start over. 58 | if (note_pos >= sizeof(note_sequence)) note_pos = 0; 59 | } 60 | 61 | void loop() { 62 | // midi_task(); 63 | // midi.playSong(); 64 | } 65 | 66 | // #endif 67 | -------------------------------------------------------------------------------- /src/device/hid/hidgamepad.cpp: -------------------------------------------------------------------------------- 1 | #include "hidgamepad.h" 2 | #include "byteswap.h" 3 | #define EPNUM_HID 0x03 4 | #if CFG_TUD_HID 5 | 6 | HIDgamepad::HIDgamepad(uint8_t id) 7 | { 8 | report_id = id; 9 | enableHID = true; 10 | _EPNUM_HID = EPNUM_HID; 11 | } 12 | 13 | bool HIDgamepad::begin(char* str) 14 | { 15 | uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(report_id))}; 16 | // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval 17 | uint8_t hid[] = {TUD_HID_DESCRIPTOR(ifIdx++, 6, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), (uint8_t)(_EPNUM_HID | 0x80), CFG_TUD_HID_BUFSIZE, 10)}; 18 | memcpy(&desc_configuration[total], hid, sizeof(hid)); 19 | total += sizeof(hid); 20 | count++; 21 | 22 | memcpy(&hid_report_desc[EspTinyUSB::hid_report_desc_len], (uint8_t *)desc_hid_report, sizeof(desc_hid_report)); 23 | EspTinyUSB::hid_report_desc_len += TUD_HID_DESC_LEN; 24 | log_d("begin len: %d", EspTinyUSB::hid_report_desc_len); 25 | 26 | return EspTinyUSB::begin(str, 6); 27 | } 28 | 29 | void HIDgamepad::sendReport() 30 | { 31 | if(tud_hid_ready()){ 32 | int ret = write((uint8_t*)&report, sizeof(hid_gamepad_t)); 33 | if(-1 == ret) log_e("error: %i", ret); 34 | } 35 | } 36 | 37 | void HIDgamepad::buttons(uint32_t bt) 38 | { 39 | report.buttons = bt; 40 | sendReport(); 41 | } 42 | 43 | void HIDgamepad::joystick1(int8_t x, int8_t y, int8_t z) 44 | { 45 | report.x = x; 46 | report.y = y; 47 | report.z = z; 48 | sendReport(); 49 | } 50 | 51 | void HIDgamepad::joystick2(int8_t rx, int8_t ry, int8_t rz) 52 | { 53 | report.Rx = rx; 54 | report.Ry = ry; 55 | report.Rz = rz; 56 | sendReport(); 57 | } 58 | 59 | void HIDgamepad::sendAll(uint32_t bt, int8_t x, int8_t y, int8_t z, int8_t rx, int8_t ry, int8_t rz, uint8_t hat) 60 | { 61 | report.buttons = bt; 62 | report.x = x; 63 | report.y = y; 64 | report.z = z; 65 | report.Rx = rx; 66 | report.Ry = ry; 67 | report.Rz = rz; 68 | report.hat = hat; 69 | sendReport(); 70 | } 71 | 72 | void HIDgamepad::hat(uint8_t hat) 73 | { 74 | report.hat = hat; 75 | sendReport(); 76 | } 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/mscusb.h: -------------------------------------------------------------------------------- 1 | 2 | #include "esptinyusb.h" 3 | 4 | #pragma once 5 | #if CFG_TUD_MSC 6 | 7 | class MSCCallbacks { 8 | public: 9 | ~MSCCallbacks(); 10 | virtual void onInquiry(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) = 0; 11 | virtual bool onReady(uint8_t lun) = 0; 12 | virtual void onCapacity(uint8_t lun, uint32_t* block_count, uint16_t* block_size) = 0; 13 | virtual bool onStop(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) = 0; // { return false; } 14 | virtual int32_t onRead(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) = 0; // { return 0; } 15 | virtual int32_t onWrite(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) = 0 ; //{ return 0; } 16 | }; 17 | 18 | class MSCusb : public EspTinyUSB 19 | { 20 | public: 21 | MSCusb(); 22 | // virtual ~MSCusb() = 0; 23 | bool begin(char* str = nullptr); 24 | 25 | int available(void) { return -1; } 26 | int peek(void) { return -1; } 27 | int read(void) { return -1; } 28 | size_t read(uint8_t *buffer, size_t size) { return 0; } 29 | void flush(void) { return; } 30 | size_t write(uint8_t) { return 0; } 31 | size_t write(const uint8_t *buffer, size_t size) { return 0; } 32 | void setBaseEP(uint8_t); 33 | void setCallbacks(MSCCallbacks*); 34 | uint8_t _EPNUM_MSC; 35 | 36 | friend TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject); 37 | friend TU_ATTR_WEAK int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize); 38 | friend TU_ATTR_WEAK int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize); 39 | friend TU_ATTR_WEAK void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]); 40 | friend TU_ATTR_WEAK void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size); 41 | friend TU_ATTR_WEAK int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize); 42 | friend TU_ATTR_WEAK bool tud_msc_test_unit_ready_cb(uint8_t lun); 43 | 44 | protected: 45 | MSCCallbacks* m_callbacks; 46 | uint8_t m_lun; 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/device/hid/hidkeyboard.cpp: -------------------------------------------------------------------------------- 1 | #include "hidkeyboard.h" 2 | #define EPNUM_HID 0x02 3 | #if CFG_TUD_HID 4 | 5 | HIDkeyboard::HIDkeyboard(uint8_t reportid) 6 | { 7 | report_id = reportid; 8 | enableHID = true; 9 | _EPNUM_HID = EPNUM_HID; 10 | } 11 | 12 | bool HIDkeyboard::begin(char *str) 13 | { 14 | uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(report_id))}; 15 | // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval 16 | uint8_t hid[] = {TUD_HID_DESCRIPTOR(ifIdx++, 6, HID_ITF_PROTOCOL_KEYBOARD, sizeof(desc_hid_report), (uint8_t)(_EPNUM_HID | 0x80), CFG_TUD_HID_BUFSIZE, 1)}; 17 | memcpy(&desc_configuration[total], hid, sizeof(hid)); 18 | total += sizeof(hid); 19 | count++; 20 | 21 | memcpy(&hid_report_desc[EspTinyUSB::hid_report_desc_len], (uint8_t *)desc_hid_report, sizeof(desc_hid_report)); 22 | EspTinyUSB::hid_report_desc_len += TUD_HID_DESC_LEN; 23 | log_d("begin len: %d", EspTinyUSB::hid_report_desc_len); 24 | 25 | return EspTinyUSB::begin(str, 6); 26 | } 27 | 28 | bool HIDkeyboard::sendKey(uint8_t _keycode, uint8_t modifier) 29 | { 30 | /*------------- Keyboard -------------*/ 31 | if (tud_hid_ready()) 32 | { 33 | if(sendPress(_keycode, modifier)) { 34 | delay(2); 35 | return sendRelease(); 36 | } 37 | } 38 | return false; 39 | } 40 | 41 | bool HIDkeyboard::sendChar(uint8_t _keycode) 42 | { 43 | return sendKey(keymap[_keycode].usage, keymap[_keycode].modifier); 44 | } 45 | 46 | bool HIDkeyboard::sendPress(uint8_t _keycode, uint8_t modifier) 47 | { 48 | uint8_t keycode[6] = {0}; 49 | keycode[0] = _keycode; 50 | 51 | return tud_hid_keyboard_report(report_id, modifier, keycode); 52 | } 53 | 54 | bool HIDkeyboard::sendRelease() 55 | { 56 | // send empty key report if previously has key pressed 57 | return tud_hid_keyboard_report(report_id, 0, NULL); 58 | } 59 | 60 | bool HIDkeyboard::sendString(const char* _text) 61 | { 62 | size_t len = strlen(_text); 63 | uint8_t keycode; 64 | for(size_t i = 0; i < len; i++) { 65 | keycode = (uint8_t) _text[i]; 66 | if(!sendKey(keymap[keycode].usage, keymap[keycode].modifier)) return false; 67 | delay(2); 68 | } 69 | 70 | return true; 71 | } 72 | 73 | bool HIDkeyboard::sendString(String text) 74 | { 75 | return sendString(text.c_str()); 76 | } 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/device/hid/hidusb.cpp: -------------------------------------------------------------------------------- 1 | #include "hidusb.h" 2 | #define EPNUM_HID 0x03 3 | #if CFG_TUD_HID 4 | 5 | static HIDusb *_hidUSB; 6 | static uint8_t num; 7 | uint8_t HIDusb::hid_report_desc[500]; 8 | static bool firstHID = true; 9 | static size_t pos = 9; 10 | HIDusb::HIDusb(uint8_t reportid) 11 | { 12 | enableHID = true; 13 | _EPNUM_HID = EPNUM_HID; 14 | _hidUSB = this; 15 | } 16 | 17 | void HIDusb::setBaseEP(uint8_t ep) 18 | { 19 | _EPNUM_HID = ep; 20 | } 21 | 22 | void HIDusb::setCallbacks(HIDCallbacks* cb) 23 | { 24 | m_callbacks = cb; 25 | } 26 | 27 | size_t HIDusb::write(uint8_t _r) { 28 | uint8_t report = _r; 29 | if(tud_hid_report(report_id, &report, 1)) return 1; 30 | 31 | return 0; 32 | } 33 | 34 | size_t HIDusb::write(const uint8_t *buffer, size_t len) { 35 | if(tud_hid_report(report_id, buffer, len)) { 36 | log_d("write hid: %s", (char*)buffer); 37 | log_d("len: %d", len); 38 | } else return -1; 39 | return len; 40 | } 41 | 42 | size_t HIDusb::write(char c) { 43 | return write((uint8_t) c); 44 | } 45 | 46 | size_t HIDusb::write(const char *buffer, size_t len) { 47 | return write((const uint8_t*) buffer, len); 48 | } 49 | 50 | // Invoked when received GET HID REPORT DESCRIPTOR request 51 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 52 | const uint8_t * tud_hid_descriptor_report_cb(uint8_t itf) 53 | { 54 | return HIDusb::hid_report_desc; 55 | } 56 | 57 | // Invoked when received GET_REPORT control request 58 | // Application must fill buffer report's content and return its length. 59 | // Return zero will cause the stack to STALL request 60 | uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) 61 | { 62 | // TODO not Implemented 63 | (void) report_id; 64 | (void) report_type; 65 | (void) buffer; 66 | (void) reqlen; 67 | 68 | log_w("%s", __func__); 69 | return 0; 70 | } 71 | 72 | // Invoked when received SET_REPORT control request or 73 | // received data on OUT endpoint ( Report ID = 0, Type = 0 ) 74 | void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) 75 | { 76 | // This example doesn't use multiple report and report ID 77 | log_d("tud_hid_set_report_cb: %d", report_id); 78 | 79 | if(_hidUSB->m_callbacks) { 80 | _hidUSB->m_callbacks->onData(report_id, report_type, buffer, bufsize); 81 | } 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/usb_msc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "freertos/FreeRTOS.h" 3 | #include "freertos/task.h" 4 | #include "freertos/semphr.h" 5 | #include "usb_device.hpp" 6 | #include "usb_requests.hpp" 7 | 8 | typedef int host_event_t; 9 | 10 | class USBmscDevice : public USBhostDevice 11 | { 12 | friend void usb_transfer_cb(usb_transfer_t *transfer); 13 | 14 | private: 15 | const usb_ep_desc_t * ep_in; 16 | const usb_ep_desc_t * ep_out; 17 | 18 | uint8_t* temp_buffer; // pointer to buffer in read10, write10 19 | 20 | bool _in = false; 21 | TaskHandle_t xTaskToNotify = NULL; // notify 22 | uint32_t block_count[4]; 23 | uint32_t block_size[4] = {4096}; 24 | uint8_t _luns; // max lun number 25 | uint8_t _lun; // current handled lun 26 | int event = -1; // current handled event 27 | 28 | static USBmscDevice* instance; // class is treat as singleton, it is used in ff_vfs 29 | msc_transfer_cb_t callbacks = {}; // user callbacks 30 | 31 | public: 32 | USBmscDevice(const usb_config_desc_t* config_desc, USBhost*); 33 | ~USBmscDevice(); 34 | 35 | bool init(); 36 | void reset(); 37 | void format(); 38 | void mount(char*, uint8_t lun = 0); 39 | uint8_t getMaxLUN(); 40 | uint32_t getBlockCount(uint8_t lun = 0); 41 | uint16_t getBlockSize(uint8_t lun = 0); 42 | void registerCallbacks(msc_transfer_cb_t); 43 | void onEvent(); 44 | 45 | 46 | public: // POSIX 47 | 48 | // int open(char* name); 49 | // void close(int); 50 | // void read(char* name, uint8_t* buffer, size_t len); 51 | // void write(char* name, uint8_t* buffer, size_t len); 52 | // void delete(char* name); 53 | 54 | 55 | public: // public to use from ff_diskio 56 | static USBmscDevice* getInstance(); 57 | esp_err_t _read10 (uint8_t lun, int offset, int num_sectors, uint8_t* buff); 58 | esp_err_t _write10(uint8_t lun, int offset, int num_sectors, uint8_t* buff); 59 | 60 | private: 61 | void inquiry(); 62 | esp_err_t unitReady(); 63 | void _getCapacity(uint8_t); 64 | void _setCapacity(uint32_t, uint32_t); 65 | void _emitEvent(host_event_t event, usb_transfer_t* data); 66 | void _getMaxLUN(); 67 | void csw(); 68 | void csw(usb_transfer_cb_t); 69 | 70 | esp_err_t allocate(size_t); 71 | esp_err_t dealocate(uint8_t); 72 | 73 | 74 | private: // internal callbacks 75 | void onCSW(usb_transfer_t *transfer); 76 | void onCBW(usb_transfer_t *transfer); 77 | void onData(usb_transfer_t *transfer); 78 | }; 79 | 80 | -------------------------------------------------------------------------------- /src/device/hid/hidmouse.cpp: -------------------------------------------------------------------------------- 1 | #include "hidmouse.h" 2 | #define EPNUM_HID 0x03 3 | #if CFG_TUD_HID 4 | 5 | HIDmouse::HIDmouse(uint8_t id) 6 | { 7 | report_id = id; 8 | enableHID = true; 9 | _EPNUM_HID = EPNUM_HID; 10 | } 11 | 12 | bool HIDmouse::begin(char *str) 13 | { 14 | uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(report_id))}; 15 | // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval 16 | uint8_t hid[] = {TUD_HID_DESCRIPTOR(ifIdx++, 6, HID_ITF_PROTOCOL_MOUSE, sizeof(desc_hid_report), (uint8_t)(_EPNUM_HID | 0x80), CFG_TUD_HID_BUFSIZE, 10)}; 17 | memcpy(&desc_configuration[total], hid, sizeof(hid)); 18 | total += sizeof(hid); 19 | count++; 20 | 21 | memcpy(&hid_report_desc[EspTinyUSB::hid_report_desc_len], (uint8_t *)desc_hid_report, sizeof(desc_hid_report)); 22 | EspTinyUSB::hid_report_desc_len += TUD_HID_DESC_LEN; 23 | log_d("begin len: %d", EspTinyUSB::hid_report_desc_len); 24 | 25 | return EspTinyUSB::begin(str, 6); 26 | } 27 | 28 | void HIDmouse::buttons(uint8_t bt) 29 | { 30 | button = bt; 31 | if (tud_hid_ready()) 32 | { 33 | // uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal 34 | tud_hid_mouse_report(report_id, button, 0, 0, 0, 0); 35 | } 36 | } 37 | 38 | void HIDmouse::move(int8_t x, int8_t y) 39 | { 40 | if (tud_hid_ready()) 41 | { 42 | // uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal 43 | tud_hid_mouse_report(report_id, button, x, y, 0, 0); 44 | } 45 | } 46 | 47 | void HIDmouse::wheel(int8_t vertical, int8_t horizontal) 48 | { 49 | if (tud_hid_ready()) 50 | { 51 | // uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal 52 | tud_hid_mouse_report(report_id, button, 0, 0, vertical, horizontal); 53 | } 54 | } 55 | 56 | void HIDmouse::pressLeft() 57 | { 58 | buttons(LEFT_BTN); 59 | delay(10); 60 | buttons(0); 61 | } 62 | 63 | void HIDmouse::pressMiddle() 64 | { 65 | buttons(MIDDLE_BTN); 66 | delay(10); 67 | buttons(0); 68 | } 69 | 70 | void HIDmouse::pressRight() 71 | { 72 | buttons(RIGHT_BTN); 73 | delay(10); 74 | buttons(0); 75 | } 76 | 77 | void HIDmouse::doublePressLeft() 78 | { 79 | pressLeft(); 80 | delay(80); 81 | pressLeft(); 82 | } 83 | 84 | void HIDmouse::backwardBtn() 85 | { 86 | buttons(BACK_BTN); 87 | delay(10); 88 | buttons(0); 89 | } 90 | 91 | void HIDmouse::forwardBtn() 92 | { 93 | buttons(FORWARD_BTN); 94 | delay(10); 95 | buttons(0); 96 | } 97 | 98 | void HIDmouse::scrollUp(uint8_t val) 99 | { 100 | wheel(val, 0); 101 | } 102 | 103 | void HIDmouse::scrollDown(uint8_t val) 104 | { 105 | wheel(-1 * val, 0); 106 | } 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /examples/host/acm/acm.ino: -------------------------------------------------------------------------------- 1 | #include "usb/usb_host.h" 2 | 3 | #include "usb_host.hpp" 4 | #include "usb_acm.hpp" 5 | 6 | USBhost host; // host is required to detect any device, before USB class is initialized 7 | USBacmDevice *device; // when USB class is detected from descriptor 8 | 9 | void acm_events(int event, void *data, size_t len) 10 | { 11 | switch (event) 12 | { 13 | case CDC_CTRL_SET_CONTROL_LINE_STATE: 14 | log_i("CDC_CTRL_SET_CONTROL_LINE_STATE"); 15 | device->setLineCoding(115200, 0, 0, 8); 16 | break; 17 | 18 | case CDC_DATA_IN: 19 | { 20 | device->INDATA(); 21 | char *buf = (char *)data; 22 | buf[len] = 0; 23 | printf("%s", (char *)data); 24 | break; 25 | } 26 | case CDC_DATA_OUT: 27 | 28 | break; 29 | 30 | case CDC_CTRL_SET_LINE_CODING: 31 | log_i("CDC_CTRL_SET_LINE_CODING"); 32 | break; 33 | } 34 | } 35 | 36 | void client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg) 37 | { 38 | if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV) 39 | { 40 | host.open(event_msg); 41 | usb_device_info_t info = host.getDeviceInfo(); 42 | log_i("device speed: %s, device address: %d, max ep_ctrl size: %d, config: %d", info.speed ? "USB_SPEED_FULL" : "USB_SPEED_LOW", info.dev_addr, info.bMaxPacketSize0, info.bConfigurationValue); 43 | const usb_device_desc_t *dev_desc = host.getDeviceDescriptor(); 44 | int offset = 0; 45 | for (size_t i = 0; i < dev_desc->bNumConfigurations; i++) 46 | { 47 | const usb_config_desc_t *config_desc = host.getConfigurationDescriptor(); 48 | for (size_t n = 0; n < config_desc->bNumInterfaces; n++) 49 | { 50 | const usb_intf_desc_t *intf = usb_parse_interface_descriptor(config_desc, n, 0, &offset); 51 | if (intf->bInterfaceClass == 0x0a) // CDC ACM 52 | { 53 | device = new USBacmDevice(config_desc, &host); 54 | n = config_desc->bNumInterfaces; 55 | if (device) 56 | { 57 | device->init(); 58 | device->onEvent(acm_events); 59 | device->setControlLine(1, 1); 60 | device->INDATA(); 61 | } 62 | } 63 | 64 | printf("config: %d[%d], interface: %d[%d], intf class: %d\n", i, dev_desc->bNumConfigurations, n, config_desc->bNumInterfaces, intf->bInterfaceClass); 65 | } 66 | } 67 | } 68 | else 69 | { 70 | log_w("DEVICE gone event"); 71 | } 72 | } 73 | 74 | void setup() 75 | { 76 | Serial.begin(115200); 77 | host.registerClientCb(client_event_callback); 78 | host.init(); 79 | } 80 | 81 | void loop() 82 | { 83 | if (device && device->isConnected()) 84 | { 85 | device->OUTDATA((uint8_t *)"test\n", 5); 86 | } 87 | delay(1000); 88 | } 89 | -------------------------------------------------------------------------------- /examples/host/remote_pendrive/embedded/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Remote pendrive 6 | 7 | 8 | 9 | 10 | 11 | 15 |
16 |
17 | 18 | 19 | 20 |
21 | 22 | 62 | 63 | 77 | 78 |
79 | 80 | 81 | -------------------------------------------------------------------------------- /examples/host/remote_pendrive/index_html.h: -------------------------------------------------------------------------------- 1 | const char* index_html = "" 2 | "" 3 | "" 4 | "" 5 | " Remote pendrive" 6 | " " 7 | " " 8 | "" 9 | "" 10 | "" 11 | " " 15 | "
" 16 | "
" 17 | " " 18 | " " 19 | " " 20 | "
" 21 | "" 22 | " " 62 | "" 63 | " " 77 | "" 78 | "
" 79 | "" 80 | "" 81 | ""; -------------------------------------------------------------------------------- /src/webusb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "esptinyusb.h" 3 | #define MS_OS_20_DESC_LEN 0xB2 4 | #define EPNUM_VENDOR 0x04 5 | #define _vendor "Vendor class (webUSB)" 6 | #if CFG_TUD_VENDOR 7 | 8 | // https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb#microsoft_os_compatibility_descriptors 9 | const uint8_t desc_ms_os_20[] = { 10 | // Set header: length, type, windows version, total length 11 | U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), 12 | U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), 13 | 14 | // Configuration subset header: length, type, configuration index, reserved, 15 | // configuration total length 16 | U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 17 | 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A), 18 | 19 | // Function Subset header: length, type, first interface, reserved, subset 20 | // length 21 | U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), 22 | 0 /*itf num*/, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08), 23 | 24 | // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub 25 | // compatible ID 26 | U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 27 | 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 | 0x00, 0x00, // sub-compatible 29 | 30 | // MS OS 2.0 Registry property descriptor: length, type 31 | U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14), 32 | U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), U16_TO_U8S_LE(0x0007), 33 | U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and 34 | // PropertyName "DeviceInterfaceGUIDs\0" in UTF-16 35 | 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 36 | 'n', 0x00, 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 37 | 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 38 | 0x00, 39 | U16_TO_U8S_LE(0x0050), // wPropertyDataLength 40 | // bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”. 41 | '{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 42 | 'D', 0x00, '9', 0x00, '-', 0x00, '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, 43 | '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, '8', 0x00, 44 | 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 45 | 'C', 0x00, 'A', 0x00, '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, 46 | '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00}; 47 | 48 | enum 49 | { 50 | VENDOR_REQUEST_WEBUSB = 1, 51 | VENDOR_REQUEST_MICROSOFT = 2 52 | }; 53 | 54 | class WebUSBCallbacks : public USBCallbacks { 55 | public: 56 | virtual ~WebUSBCallbacks() { } 57 | virtual void onConnect(bool state) { } 58 | virtual void onData() { } 59 | }; 60 | 61 | class WebUSB : public EspTinyUSB 62 | { 63 | public: 64 | WebUSB(uint8_t itf = 0); 65 | bool begin(char* vendor = nullptr, const char* url = nullptr, bool ssl = true); 66 | int available(void); 67 | int peek(void); 68 | int read(void); 69 | size_t read(uint8_t *buffer, size_t size); 70 | void flush(void); 71 | size_t write(uint8_t); 72 | size_t write(const uint8_t *buffer, size_t size); 73 | 74 | operator bool() const; 75 | 76 | void setCallbacks(WebUSBCallbacks*); 77 | void landingPageURI(String url, bool ssl); 78 | void landingPageURI(const char* url, bool ssl); 79 | void setBaseEP(uint8_t); 80 | WebUSBCallbacks* m_callbacks; 81 | 82 | private: 83 | 84 | friend bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); 85 | 86 | uint8_t* _url; 87 | uint8_t _EPNUM_VENDOR; 88 | 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/host/common/usb_host.cpp: -------------------------------------------------------------------------------- 1 | #include "freertos/FreeRTOS.h" 2 | #include "freertos/task.h" 3 | #include "esp_log.h" 4 | #include "usb/usb_host.h" 5 | 6 | #include "usb_host.hpp" 7 | 8 | void _client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg) 9 | { 10 | USBhost *host = (USBhost *)arg; 11 | if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV) 12 | { 13 | ESP_LOGI("", "client event: %d, address: %d", event_msg->event, event_msg->new_dev.address); 14 | if (host->_client_event_cb) 15 | { 16 | host->_client_event_cb(event_msg, arg); 17 | } else { 18 | host->open(event_msg); 19 | } 20 | } 21 | } 22 | 23 | static void client_async_seq_task(void *param) 24 | { 25 | usb_host_client_handle_t client_hdl = *(usb_host_client_handle_t *)param; 26 | uint32_t event_flags; 27 | while (1) 28 | { 29 | usb_host_client_handle_events(client_hdl, 1); 30 | 31 | if (ESP_OK == usb_host_lib_handle_events(1, &event_flags)) 32 | { 33 | if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) 34 | { 35 | ESP_LOGI("", "No more clients"); 36 | usb_host_device_free_all(); 37 | } 38 | if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) 39 | { 40 | break; 41 | } 42 | } 43 | } 44 | usb_host_client_deregister(client_hdl); 45 | vTaskDelete(NULL); 46 | } 47 | 48 | USBhost::USBhost() 49 | { 50 | } 51 | 52 | USBhost::~USBhost() 53 | { 54 | usb_host_device_close(client_hdl, dev_hdl); 55 | } 56 | 57 | bool USBhost::init(bool create_tasks) 58 | { 59 | const usb_host_config_t config = { 60 | .intr_flags = ESP_INTR_FLAG_LEVEL1, 61 | }; 62 | esp_err_t err = usb_host_install(&config); 63 | ESP_LOGI("", "install status: %d", err); 64 | 65 | const usb_host_client_config_t client_config = { 66 | .is_synchronous = false, 67 | .max_num_event_msg = 5, 68 | .async = { 69 | .client_event_callback = _client_event_callback, 70 | .callback_arg = this 71 | } 72 | }; 73 | 74 | err = usb_host_client_register(&client_config, &client_hdl); 75 | ESP_LOGI("", "client register status: %d", err); 76 | 77 | if (create_tasks) 78 | { 79 | xTaskCreate(client_async_seq_task, "async", 4 * 512, &client_hdl, 20, NULL); 80 | } 81 | 82 | return true; 83 | } 84 | 85 | bool USBhost::open(const usb_host_client_event_msg_t *event_msg) 86 | { 87 | esp_err_t err = usb_host_device_open(client_hdl, event_msg->new_dev.address, &dev_hdl); 88 | parseConfig(); 89 | 90 | return true; 91 | } 92 | 93 | void USBhost::parseConfig() 94 | { 95 | const usb_device_desc_t *device_desc; 96 | usb_host_get_device_descriptor(dev_hdl, &device_desc); 97 | // ESP_LOG_BUFFER_HEX("", device_desc->val, USB_DEVICE_DESC_SIZE); 98 | const usb_config_desc_t *config_desc; 99 | usb_host_get_active_config_descriptor(dev_hdl, &config_desc); 100 | } 101 | 102 | usb_device_info_t USBhost::getDeviceInfo() 103 | { 104 | usb_host_device_info(dev_hdl, &dev_info); 105 | 106 | return dev_info; 107 | } 108 | 109 | const usb_device_desc_t* USBhost::getDeviceDescriptor() 110 | { 111 | const usb_device_desc_t *device_desc; 112 | usb_host_get_device_descriptor(dev_hdl, &device_desc); 113 | 114 | return device_desc; 115 | } 116 | 117 | const usb_config_desc_t* USBhost::getConfigurationDescriptor() 118 | { 119 | const usb_config_desc_t *config_desc; 120 | usb_host_get_active_config_descriptor(dev_hdl, &config_desc); 121 | return config_desc; 122 | } 123 | 124 | uint8_t USBhost::getConfiguration() 125 | { 126 | return getDeviceInfo().bConfigurationValue; 127 | } 128 | 129 | usb_host_client_handle_t USBhost::clientHandle() 130 | { 131 | return client_hdl; 132 | } 133 | 134 | usb_device_handle_t USBhost::deviceHandle() 135 | { 136 | return dev_hdl; 137 | } 138 | 139 | // bool USBhost::setConfiguration(uint8_t); 140 | -------------------------------------------------------------------------------- /src/esptinyusb.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | #include "Arduino.h" 4 | #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 5 | #if CONFIG_TINYUSB_ENABLED 6 | 7 | #include "common/tusb_common.h" 8 | #include "tusb.h" 9 | 10 | #include "soc/rtc_cntl_reg.h" 11 | #include "soc/usb_struct.h" 12 | #include "soc/usb_reg.h" 13 | #include "soc/usb_wrap_reg.h" 14 | #include "soc/usb_wrap_struct.h" 15 | #if CONFIG_IDF_TARGET_ESP32S2 16 | #include "esp32s2/rom/usb/usb_persist.h" 17 | #include "esp32s2/rom/usb/usb_dc.h" 18 | #include "esp32s2/rom/usb/chip_usb_dw_wrapper.h" 19 | #endif 20 | #if CONFIG_IDF_TARGET_ESP32S3 21 | #include "esp32s3/rom/usb/usb_persist.h" 22 | #include "esp32s3/rom/usb/usb_dc.h" 23 | #include "esp32s3/rom/usb/chip_usb_dw_wrapper.h" 24 | #endif 25 | 26 | 27 | #include "usb_descriptors.h" 28 | /* 29 | * USB Persistence API 30 | * */ 31 | typedef enum { 32 | RESTART_NO_PERSIST, 33 | RESTART_PERSIST, 34 | RESTART_BOOTLOADER, 35 | RESTART_BOOTLOADER_DFU, 36 | RESTART_TYPE_MAX 37 | } restart_type_t; 38 | 39 | //--------------------------------------------------------------------+ 40 | // Device callbacks 41 | //--------------------------------------------------------------------+ 42 | class USBCallbacks { 43 | public: 44 | virtual ~USBCallbacks() { } 45 | virtual void onMount() { } 46 | virtual void onUnmount() { } 47 | virtual void onSuspend(bool remote_wakeup_en) { } 48 | virtual void onResume() { } 49 | }; 50 | 51 | typedef struct 52 | { 53 | char langId[2]; 54 | const char *manufacturer; 55 | const char *product; 56 | const char *serial; 57 | 58 | const char *cdc; 59 | const char *dfu; 60 | const char *hid; 61 | const char *midi; 62 | const char *msc; 63 | const char *vendor; 64 | } descriptor_strings_t; 65 | 66 | static const char *descriptor_str_config[11]; 67 | 68 | 69 | class EspTinyUSB : public Stream 70 | { 71 | public: 72 | EspTinyUSB(bool extPhy = false); 73 | bool begin(char* str, uint8_t n); 74 | static void registerDeviceCallbacks(USBCallbacks* cb); 75 | void persistentReset(restart_type_t usb_persist_mode); 76 | 77 | static size_t hid_report_desc_len; 78 | 79 | tusb_desc_device_t getDeviceDescriptor(); 80 | void setDeviceDescriptorStrings(); 81 | uint8_t *getConfigurationDescriptor(); 82 | void deviceID(uint16_t, uint16_t); 83 | void deviceID(uint16_t *, uint16_t *); 84 | void useDFU(bool); 85 | void useMSC(bool); 86 | static void manufacturer(char*); 87 | static void product(char*); 88 | static void serial(char*); 89 | static void revision(uint16_t); 90 | virtual void setBaseEP(uint8_t) = 0; 91 | 92 | 93 | friend uint8_t const *tud_descriptor_device_cb(void); 94 | friend uint8_t const *tud_descriptor_configuration_cb(uint8_t index); 95 | friend uint8_t const *tud_descriptor_device_cb(void); 96 | friend uint8_t const *tud_descriptor_configuration_cb(uint8_t index); 97 | friend uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid); 98 | friend tusb_desc_device_t *tusb_get_active_desc(void); 99 | friend char **tusb_get_active_str_desc(void); 100 | friend void tusb_clear_descriptor(void); 101 | friend void tud_mount_cb(void); 102 | friend void tud_umount_cb(void); 103 | friend void tud_suspend_cb(bool remote_wakeup_en); 104 | friend void tud_resume_cb(void); 105 | 106 | protected: 107 | static uint8_t *descriptor_config; 108 | static uint8_t *descriptor_config_if; 109 | uint8_t _itf; 110 | static USBCallbacks* m_callbacks; 111 | 112 | xTaskHandle usbTaskHandle; 113 | 114 | static bool enableCDC; 115 | static bool enableMSC; 116 | static bool enableMIDI; 117 | static bool enableHID; 118 | static bool enableVENDOR; 119 | static bool enableDFU; 120 | 121 | static descriptor_strings_t strings; 122 | static uint8_t desc_configuration[1500]; 123 | 124 | char langId[2]; 125 | static uint16_t _VID; 126 | static uint16_t _PID; 127 | bool isEnabled = false; 128 | static uint8_t ifIdx; 129 | static int total; 130 | static uint8_t count; 131 | static uint16_t _revision; 132 | static uint16_t _bcdUSB; 133 | }; 134 | 135 | #endif 136 | #endif 137 | -------------------------------------------------------------------------------- /examples/device/all_in_one/all_in_one.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * This is (al)most complex example 3 | * Device is using all available on S2 endpoints, with 2 LUNs mounted on FAT 2 partitions 4 | * DFU for update from website for example, CDC and WebUSB serial and of course Serial from CP210x 5 | * 6 | * author: chegewara 7 | */ 8 | #include "Arduino.h" 9 | #include "webusb.h" 10 | #include "cdcusb.h" 11 | #include "mscusb.h" 12 | #include "dfuusb.h" 13 | #include "flashdisk.h" 14 | #include "hidkeyboard.h" 15 | 16 | WebUSB WebUSBSerial; 17 | CDCusb CDCUSBSerial; 18 | HIDkeyboard keyboard; 19 | FlashUSB fat1; 20 | FlashUSB fat2; 21 | DFUusb dfu; 22 | 23 | char *l1 = "ffat"; 24 | char *l2 = "ffat1"; 25 | 26 | class MyWebUSBCallbacks : public WebUSBCallbacks{ 27 | void onConnect(bool state) { 28 | Serial.printf("webusb is %s\n", state ? "connected":"disconnected"); 29 | } 30 | }; 31 | 32 | class MyCDCCallbacks : public CDCCallbacks { 33 | void onCodingChange(cdc_line_coding_t const* p_line_coding) 34 | { 35 | int bitrate = CDCUSBSerial.getBitrate(); 36 | Serial.printf("new bitrate: %d\n", bitrate); 37 | } 38 | 39 | bool onConnect(bool dtr, bool rts) 40 | { 41 | Serial.printf("connection state changed, dtr: %d, rts: %d\n", dtr, rts); 42 | return true; // allow to persist reset, when Arduino IDE is trying to enter bootloader mode 43 | } 44 | 45 | void onData() 46 | { 47 | int len = CDCUSBSerial.available(); 48 | Serial.printf("\nnew data, len %d\n", len); 49 | uint8_t buf[len] = {}; 50 | CDCUSBSerial.read(buf, len); 51 | Serial.write(buf, len); 52 | } 53 | }; 54 | 55 | class Device: public USBCallbacks { 56 | void onMount() { Serial.println("Mount"); } 57 | void onUnmount() { Serial.println("Unmount"); } 58 | void onSuspend(bool remote_wakeup_en) { Serial.println("Suspend"); } 59 | void onResume() { Serial.println("Resume"); } 60 | }; 61 | 62 | class MyHIDCallbacks : public HIDCallbacks 63 | { 64 | void onData(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) 65 | { 66 | Serial.printf("ID: %d, type: %d, size: %d\n", report_id, (int)report_type, bufsize); 67 | for (size_t i = 0; i < bufsize; i++) 68 | { 69 | Serial.printf("%d\n", buffer[i]); 70 | } 71 | } 72 | }; 73 | 74 | void setup() 75 | { 76 | Serial.begin(115200); 77 | 78 | keyboard.setBaseEP(3); 79 | keyboard.begin(); 80 | keyboard.setCallbacks(new MyHIDCallbacks()); 81 | 82 | if (fat1.init("/fat1", "ffat")) 83 | { 84 | if (fat1.begin()) 85 | { 86 | Serial.println("MSC lun 1 begin"); 87 | } 88 | else 89 | log_e("LUN 1 failed"); 90 | } 91 | if (fat2.init("/fat2", "ffat1")) 92 | { 93 | if (fat2.begin()) 94 | { 95 | Serial.println("MSC lun 2 begin"); 96 | } 97 | else 98 | log_e("LUN 2 failed"); 99 | } 100 | 101 | WebUSBSerial.setBaseEP(5); 102 | 103 | if (!WebUSBSerial.begin()) 104 | Serial.println("Failed to start webUSB stack"); 105 | if (!CDCUSBSerial.begin()) 106 | Serial.println("Failed to start CDC USB stack"); 107 | 108 | WebUSBSerial.setCallbacks(new MyWebUSBCallbacks()); 109 | CDCUSBSerial.setCallbacks(new MyCDCCallbacks()); 110 | 111 | dfu.begin(); 112 | 113 | EspTinyUSB::registerDeviceCallbacks(new Device()); 114 | 115 | 116 | xTaskCreate(keyboardTask, "kTask", 3*1024, NULL, 5, NULL); 117 | } 118 | 119 | void keyboardTask(void* p) 120 | { 121 | while(1) 122 | { 123 | delay(5000); 124 | Serial.println(keyboard.sendString(String("123456789\n")) ? "OK" : "FAIL"); 125 | delay(5000); 126 | Serial.println(keyboard.sendString(String("abcdefghijklmnopqrst Uvwxyz\n")) ? "OK" : "FAIL"); 127 | } 128 | } 129 | 130 | 131 | void echo_all(char c) 132 | { 133 | WebUSBSerial.write(c); 134 | CDCUSBSerial.write(c); 135 | Serial.write(c); 136 | } 137 | 138 | void loop() 139 | { 140 | while (WebUSBSerial.available()) 141 | { 142 | echo_all(WebUSBSerial.read()); 143 | } 144 | while (CDCUSBSerial.available()) 145 | { 146 | echo_all(CDCUSBSerial.read()); 147 | } 148 | while (Serial.available()) 149 | { 150 | echo_all(Serial.read()); 151 | } 152 | } -------------------------------------------------------------------------------- /src/device/msc/mscusb.cpp: -------------------------------------------------------------------------------- 1 | #include "mscusb.h" 2 | 3 | #if CFG_TUD_MSC 4 | 5 | #include "mscusb.h" 6 | #define EPNUM_MSC 0x04 7 | MSCusb* _MSCusb[4] = { }; 8 | static uint8_t luns = 0; 9 | 10 | MSCusb::MSCusb() 11 | { 12 | m_lun = luns++; 13 | enableMSC = true; 14 | _EPNUM_MSC = EPNUM_MSC; 15 | _MSCusb[m_lun] = this; 16 | } 17 | 18 | bool MSCusb::begin(char* str) 19 | { 20 | if(m_lun >= 4) return false; 21 | if(m_lun == 0) { // init only first time, then just add luns 22 | // Interface number, string index, EP Out & EP In address, EP size 23 | uint8_t msc[] = {TUD_MSC_DESCRIPTOR(ifIdx++, 5, _EPNUM_MSC, (uint8_t)(0x80 | _EPNUM_MSC), 64)}; // highspeed 512 24 | memcpy(&desc_configuration[total], msc, sizeof(msc)); 25 | total += sizeof(msc); 26 | count++; 27 | } 28 | if (!EspTinyUSB::begin(str, 5)) return false; 29 | return true; 30 | } 31 | 32 | void MSCusb::setBaseEP(uint8_t ep) 33 | { 34 | _EPNUM_MSC = ep; 35 | } 36 | 37 | void MSCusb::setCallbacks(MSCCallbacks* cb) 38 | { 39 | m_callbacks = cb; 40 | } 41 | 42 | TU_ATTR_WEAK void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) 43 | { 44 | for (size_t i = 0; i < 4; i++) 45 | { 46 | if(_MSCusb[i] && _MSCusb[i]->m_callbacks && _MSCusb[i]->m_lun == lun) { 47 | _MSCusb[i]->m_callbacks->onInquiry(lun, vendor_id, product_id, product_rev); 48 | } 49 | } 50 | } 51 | 52 | TU_ATTR_WEAK bool tud_msc_test_unit_ready_cb(uint8_t lun) 53 | { 54 | for (size_t i = 0; i < 4; i++) 55 | { 56 | if(_MSCusb[i] && _MSCusb[i]->m_callbacks && _MSCusb[i]->m_lun == lun) { 57 | return _MSCusb[i]->m_callbacks->onReady(lun); 58 | } 59 | } 60 | 61 | return false; 62 | } 63 | 64 | TU_ATTR_WEAK void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) 65 | { 66 | for (size_t i = 0; i < 4; i++) 67 | { 68 | if(_MSCusb[i] && _MSCusb[i]->m_callbacks && _MSCusb[i]->m_lun == lun) { 69 | _MSCusb[i]->m_callbacks->onCapacity(lun, block_count, block_size); 70 | } 71 | } 72 | } 73 | 74 | TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) 75 | { 76 | for (size_t i = 0; i < 4; i++) 77 | { 78 | if(_MSCusb[i] && _MSCusb[i]->m_callbacks && _MSCusb[i]->m_lun == lun) { 79 | return _MSCusb[i]->m_callbacks->onStop(lun, power_condition, start, load_eject); 80 | } 81 | } 82 | 83 | return true; 84 | } 85 | 86 | TU_ATTR_WEAK int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) 87 | { 88 | for (size_t i = 0; i < 4; i++) 89 | { 90 | if(_MSCusb[i] && _MSCusb[i]->m_callbacks && _MSCusb[i]->m_lun == lun) { 91 | return _MSCusb[i]->m_callbacks->onRead(lun, lba, offset, buffer, bufsize); 92 | } 93 | } 94 | 95 | return -1; 96 | } 97 | 98 | TU_ATTR_WEAK int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) 99 | { 100 | for (size_t i = 0; i < 4; i++) 101 | { 102 | if(_MSCusb[i] && _MSCusb[i]->m_callbacks && _MSCusb[i]->m_lun == lun) { 103 | return _MSCusb[i]->m_callbacks->onWrite(lun, lba, offset, buffer, bufsize); 104 | } 105 | } 106 | 107 | return -1; 108 | } 109 | 110 | TU_ATTR_WEAK int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) 111 | { 112 | 113 | void const* response = NULL; 114 | uint16_t resplen = 0; 115 | 116 | // most scsi handled is input 117 | bool in_xfer = true; 118 | 119 | switch (scsi_cmd[0]) 120 | { 121 | case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: 122 | // Host is about to read/write etc ... better not to disconnect disk 123 | resplen = 0; 124 | break; 125 | 126 | default: 127 | // Set Sense = Invalid Command Operation 128 | tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); 129 | 130 | // negative means error -> tinyusb could stall and/or response with failed status 131 | resplen = -1; 132 | break; 133 | } 134 | 135 | // return resplen must not larger than bufsize 136 | if ( resplen > bufsize ) resplen = bufsize; 137 | 138 | if ( response && (resplen > 0) ) 139 | { 140 | if(in_xfer) 141 | { 142 | memcpy(buffer, response, resplen); 143 | }else 144 | { 145 | // SCSI output 146 | } 147 | } 148 | 149 | return resplen; 150 | } 151 | 152 | // Support multi LUNs 153 | uint8_t tud_msc_get_maxlun_cb(void) 154 | { 155 | return luns; 156 | } 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /src/device/hid/hidcomposite.cpp: -------------------------------------------------------------------------------- 1 | #include "hidcomposite.h" 2 | #define EPNUM_HID 0x03 3 | #if CFG_TUD_HID 4 | 5 | HIDcomposite::HIDcomposite(uint8_t id) 6 | { 7 | report_mouse = id; 8 | report_keyboard = id + 1; 9 | enableHID = true; 10 | _EPNUM_HID = EPNUM_HID; 11 | } 12 | 13 | bool HIDcomposite::begin(char *str) 14 | { 15 | uint8_t const desc_hid_report[] = { 16 | TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(report_mouse)), 17 | TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID((uint8_t)(report_keyboard))) 18 | }; 19 | // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval 20 | uint8_t hid[] = {TUD_HID_DESCRIPTOR(ifIdx++, 6, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), (uint8_t)(_EPNUM_HID | 0x80), CFG_TUD_HID_BUFSIZE, 1)}; 21 | memcpy(&desc_configuration[total], hid, sizeof(hid)); 22 | total += sizeof(hid); 23 | count++; 24 | 25 | memcpy(&hid_report_desc[EspTinyUSB::hid_report_desc_len], (uint8_t *)desc_hid_report, sizeof(desc_hid_report)); 26 | EspTinyUSB::hid_report_desc_len += TUD_HID_DESC_LEN; 27 | log_d("begin len: %d", EspTinyUSB::hid_report_desc_len); 28 | 29 | return EspTinyUSB::begin(str, 6); 30 | } 31 | 32 | /*------------- MOUSE ----------------*/ 33 | void HIDcomposite::buttons(uint8_t bt) 34 | { 35 | button = bt; 36 | if (tud_hid_ready()) 37 | { 38 | // uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal 39 | tud_hid_mouse_report(report_mouse, button, 0, 0, 0, 0); 40 | } 41 | } 42 | 43 | void HIDcomposite::move(int8_t x, int8_t y) 44 | { 45 | if (tud_hid_ready()) 46 | { 47 | // uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal 48 | tud_hid_mouse_report(report_mouse, button, x, y, 0, 0); 49 | } 50 | } 51 | 52 | void HIDcomposite::wheel(int8_t vertical, int8_t horizontal) 53 | { 54 | if (tud_hid_ready()) 55 | { 56 | // uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal 57 | tud_hid_mouse_report(report_mouse, button, 0, 0, vertical, horizontal); 58 | } 59 | } 60 | 61 | void HIDcomposite::pressLeft() 62 | { 63 | buttons(LEFT_BTN); 64 | delay(10); 65 | buttons(0); 66 | } 67 | 68 | void HIDcomposite::pressMiddle() 69 | { 70 | buttons(MIDDLE_BTN); 71 | delay(10); 72 | buttons(0); 73 | } 74 | 75 | void HIDcomposite::pressRight() 76 | { 77 | buttons(RIGHT_BTN); 78 | delay(10); 79 | buttons(0); 80 | } 81 | 82 | void HIDcomposite::doublePressLeft() 83 | { 84 | pressLeft(); 85 | delay(80); 86 | pressLeft(); 87 | } 88 | 89 | void HIDcomposite::backwardBtn() 90 | { 91 | buttons(BACK_BTN); 92 | delay(10); 93 | buttons(0); 94 | } 95 | 96 | void HIDcomposite::forwardBtn() 97 | { 98 | buttons(FORWARD_BTN); 99 | delay(10); 100 | buttons(0); 101 | } 102 | 103 | void HIDcomposite::scrollUp(uint8_t val) 104 | { 105 | wheel(val, 0); 106 | } 107 | 108 | void HIDcomposite::scrollDown(uint8_t val) 109 | { 110 | wheel(-1 * val, 0); 111 | } 112 | /*------------- MOUSE ----------------*/ 113 | 114 | /*------------- Keyboard -------------*/ 115 | bool HIDcomposite::sendKey(uint8_t _keycode, uint8_t modifier) 116 | { 117 | if (tud_hid_ready()) 118 | { 119 | if(sendPress(_keycode, modifier)) { 120 | delay(2); 121 | return sendRelease(); 122 | } 123 | } 124 | return false; 125 | } 126 | 127 | bool HIDcomposite::sendChar(uint8_t _keycode) 128 | { 129 | return sendKey(keymap[_keycode].usage, keymap[_keycode].modifier); 130 | } 131 | 132 | bool HIDcomposite::sendPress(uint8_t _keycode, uint8_t modifier) 133 | { 134 | uint8_t keycode[6] = {0}; 135 | keycode[0] = _keycode; 136 | 137 | return tud_hid_keyboard_report(report_keyboard, modifier, keycode); 138 | } 139 | 140 | bool HIDcomposite::sendRelease() 141 | { 142 | // send empty key report if previously has key pressed 143 | return tud_hid_keyboard_report(report_keyboard, 0, NULL); 144 | } 145 | 146 | bool HIDcomposite::sendString(const char* _text) 147 | { 148 | size_t len = strlen(_text); 149 | uint8_t keycode; 150 | for(size_t i = 0; i < len; i++) { 151 | keycode = (uint8_t) _text[i]; 152 | if(!sendKey(keymap[keycode].usage, keymap[keycode].modifier)) return false; 153 | delay(2); 154 | } 155 | 156 | return true; 157 | } 158 | 159 | bool HIDcomposite::sendString(String text) 160 | { 161 | return sendString(text.c_str()); 162 | } 163 | /*------------- Keyboard -------------*/ 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /src/device/msc/ramdisk.cpp: -------------------------------------------------------------------------------- 1 | #include "ramdisk.h" 2 | 3 | #ifdef CFG_TUD_MSC 4 | 5 | class RAMCallbacks : public MSCCallbacks { 6 | USBramdisk* m_parent; 7 | public: 8 | RAMCallbacks(USBramdisk* ram) { m_parent = ram; } 9 | ~RAMCallbacks() { } 10 | void onInquiry(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) 11 | { 12 | if (m_parent->m_private) 13 | { 14 | m_parent->m_private->onInquiry(lun, vendor_id, product_id, product_rev); 15 | } else { 16 | const char vid[] = "ESP32-S2"; 17 | const char pid[] = "RAM disk"; 18 | const char rev[] = "1.0"; 19 | 20 | memcpy(vendor_id , vid, strlen(vid)); 21 | memcpy(product_id , pid, strlen(pid)); 22 | memcpy(product_rev, rev, strlen(rev)); 23 | log_v("default onInquiry"); 24 | } 25 | } 26 | bool onReady(uint8_t lun) { 27 | if (m_parent->m_private) 28 | { 29 | log_v("custom RAM disk onready"); 30 | return m_parent->m_private->onReady(lun); 31 | } else { 32 | log_v("RAM disk always ready"); 33 | return true; // RAM disk is always ready 34 | } 35 | } 36 | void onCapacity(uint8_t lun, uint32_t* block_count, uint16_t* block_size) 37 | { 38 | (void) lun; 39 | *block_count = m_parent->block_count; 40 | *block_size = m_parent->block_size; 41 | log_v("ram disk block count: %d, block size: %d", *block_count, *block_size); 42 | } 43 | bool onStop(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) 44 | { 45 | (void) lun; 46 | (void) power_condition; 47 | 48 | if ( load_eject ) 49 | { 50 | if (start) 51 | { 52 | // load disk storage 53 | log_v("default load"); 54 | }else 55 | { 56 | // unload disk storage 57 | log_v("default unload"); 58 | } 59 | } else { 60 | if (start) 61 | { 62 | // load disk storage 63 | log_v("default start"); 64 | }else 65 | { 66 | // unload disk storage 67 | log_v("default stop"); 68 | } 69 | } 70 | 71 | return true; 72 | } 73 | int32_t onRead(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) 74 | { 75 | log_v("default onread"); 76 | (void) lun; 77 | uint8_t* addr = &m_parent->ram_disk[lba * 512] + offset; 78 | memcpy(buffer, addr, bufsize); 79 | 80 | return bufsize; 81 | } 82 | int32_t onWrite(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) 83 | { 84 | log_v("default onwrite"); 85 | (void) lun; 86 | uint8_t* addr = &m_parent->ram_disk[lba * 512] + offset; 87 | memcpy(addr, buffer, bufsize); 88 | 89 | return bufsize; 90 | } 91 | }; 92 | 93 | USBramdisk::USBramdisk( ) 94 | { 95 | MSCusb::setCallbacks(new RAMCallbacks(this)); 96 | } 97 | 98 | bool USBramdisk::begin(char* str) 99 | { 100 | assert(block_count); 101 | assert(block_size); 102 | if(ram_disk == nullptr){ 103 | log_e("NO disk"); 104 | if (psramFound()){ 105 | ram_disk = (uint8_t*)heap_caps_calloc(1, block_count * block_size, MALLOC_CAP_SPIRAM); 106 | log_e("init ram disk from SPIRAM: %d", sizeof(ram_disk)); 107 | } else { 108 | ram_disk = (uint8_t*)heap_caps_calloc(1, block_count * block_size, MALLOC_CAP_INTERNAL); 109 | log_e("init ram disk from internal memory: %d", sizeof(ram_disk)); 110 | } 111 | if(ram_disk == nullptr) return false; 112 | } 113 | if(set_demo_content){ 114 | setContent(&ram_disk_demo[0][0], sizeof(ram_disk_demo)); 115 | log_e("init ram disk size: %d", sizeof(ram_disk)/sizeof(*ram_disk)); 116 | ram_disk[20] = (uint8_t)(block_count >> 8); 117 | ram_disk[19] = (uint8_t)(block_count & 0xff); 118 | } 119 | return MSCusb::begin(str); 120 | } 121 | 122 | void USBramdisk::setCapacity(uint32_t count, uint32_t size) 123 | { 124 | block_count = count; 125 | block_size = size; 126 | } 127 | 128 | void USBramdisk::setDiskMemory(uint8_t* memory, bool demo) 129 | { 130 | ram_disk = memory; 131 | set_demo_content = demo; 132 | } 133 | 134 | void USBramdisk::setContent(uint8_t* content, size_t size) 135 | { 136 | memcpy(ram_disk, content, size); 137 | } 138 | 139 | void USBramdisk::setCallbacks(MSCCallbacks* cb) 140 | { 141 | m_private = cb; 142 | } 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /src/device/msc/sdcard.cpp: -------------------------------------------------------------------------------- 1 | #include "sdusb.h" 2 | 3 | #ifdef CFG_TUD_MSC 4 | 5 | class SDCallbacks : public MSCCallbacks { 6 | SDCard2USB* m_parent; 7 | public: 8 | SDCallbacks(SDCard2USB* ram) { m_parent = ram; } 9 | ~SDCallbacks() { } 10 | void onInquiry(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) 11 | { 12 | if (m_parent->m_private) 13 | { 14 | m_parent->m_private->onInquiry(lun, vendor_id, product_id, product_rev); 15 | } else { 16 | const char vid[] = "ESP32-S2"; 17 | const char pid[] = "SD card"; 18 | const char rev[] = "1.0"; 19 | 20 | memcpy(vendor_id , vid, strlen(vid)); 21 | memcpy(product_id , pid, strlen(pid)); 22 | memcpy(product_rev, rev, strlen(rev)); 23 | log_v("default onInquiry"); 24 | } 25 | } 26 | bool onReady(uint8_t lun) { 27 | if (m_parent->m_private) 28 | { 29 | log_v("custom RAM disk onready"); 30 | return m_parent->m_private->onReady(lun); 31 | } else { 32 | log_v("RAM disk always ready"); 33 | return m_parent->sdcardReady; // RAM disk is always ready 34 | } 35 | } 36 | void onCapacity(uint8_t lun, uint32_t* block_count, uint16_t* block_size) 37 | { 38 | (void) lun; 39 | *block_count = m_parent->block_count; 40 | *block_size = m_parent->block_size; 41 | log_v("ram disk block count: %d, block size: %d", *block_count, *block_size); 42 | } 43 | bool onStop(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) 44 | { 45 | (void) lun; 46 | (void) power_condition; 47 | 48 | if ( load_eject ) 49 | { 50 | if (start) 51 | { 52 | // load disk storage 53 | log_v("default start/stop load"); 54 | }else 55 | { 56 | // unload disk storage 57 | log_v("default start/stop unload"); 58 | } 59 | } 60 | 61 | return true; 62 | } 63 | int32_t onRead(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) 64 | { 65 | log_v("default onread lba (%u) bufsize (%u)", lba, bufsize); 66 | (void) lun; 67 | for (int i = 0; m_parent->block_size * i < bufsize; i++) 68 | SD.readRAW((uint8_t *)buffer + m_parent->block_size * i, lba + i); 69 | 70 | return bufsize; 71 | } 72 | int32_t onWrite(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) 73 | { 74 | log_v("default onwrite lba (%u) bufsize (%u)", lba, bufsize); 75 | (void) lun; 76 | for (int i = 0; m_parent->block_size * i < bufsize; i++) 77 | SD.writeRAW((uint8_t *)buffer + m_parent->block_size * i, lba + i); 78 | 79 | return bufsize; 80 | } 81 | }; 82 | 83 | SDCard2USB::SDCard2USB( ) 84 | { 85 | MSCusb::setCallbacks(new SDCallbacks(this)); 86 | } 87 | 88 | bool SDCard2USB::begin(char* str) 89 | { 90 | assert(block_count); 91 | assert(block_size); 92 | 93 | return MSCusb::begin(str); 94 | } 95 | 96 | bool SDCard2USB::initSD(uint8_t ssPin, SPIClass &spi, uint32_t frequency, const char * mountpoint, uint8_t max_files) 97 | { 98 | if(!SD.begin(ssPin, spi, frequency, mountpoint, max_files)){ 99 | Serial.println("Card Mount Failed"); 100 | return false; 101 | } 102 | 103 | uint8_t cardType = SD.cardType(); 104 | 105 | if(cardType == CARD_NONE){ 106 | Serial.println("No SD card attached"); 107 | return false; 108 | } 109 | 110 | block_count = SD.cardSize() / block_size; 111 | sdcardReady = true; 112 | return true; 113 | } 114 | 115 | bool SDCard2USB::initSD(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) 116 | { 117 | 118 | static SPIClass* spi = NULL; 119 | spi = new SPIClass(FSPI); 120 | spi->begin(sck, miso, mosi, ss); 121 | if(!SD.begin(ss, *spi, 40000000)){ 122 | Serial.println("Card Mount Failed"); 123 | return false; 124 | } 125 | 126 | uint8_t cardType = SD.cardType(); 127 | 128 | if(cardType == CARD_NONE){ 129 | Serial.println("No SD card attached"); 130 | return false; 131 | } 132 | 133 | block_count = SD.cardSize() / block_size; 134 | sdcardReady = true; 135 | return true; 136 | } 137 | 138 | void SDCard2USB::setCapacity(uint32_t count, uint32_t size) 139 | { 140 | block_count = count; 141 | block_size = size; 142 | } 143 | 144 | void SDCard2USB::setCallbacks(MSCCallbacks* cb) 145 | { 146 | m_private = cb; 147 | } 148 | 149 | void SDCard2USB::ready(bool ready) 150 | { 151 | 152 | } 153 | 154 | bool SDCard2USB::isReady() 155 | { 156 | return sdcardReady; 157 | } 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /src/device/cdc/cdcusb.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "esptinyusb.h" 3 | #include "cdcusb.h" 4 | 5 | #if CFG_TUD_CDC 6 | 7 | #define EPNUM_CDC 0x02 8 | 9 | static CDCusb* _CDCusb[2] = {}; 10 | enum { CDC_LINE_IDLE, CDC_LINE_1, CDC_LINE_2, CDC_LINE_3 }; 11 | 12 | CDCusb::CDCusb(uint8_t itf) 13 | { 14 | _itf = itf; 15 | _CDCusb[_itf] = this; 16 | enableCDC = true; 17 | _EPNUM_CDC = EPNUM_CDC; 18 | } 19 | 20 | void CDCusb::setBaseEP(uint8_t ep) 21 | { 22 | _EPNUM_CDC = ep; 23 | } 24 | 25 | bool CDCusb::begin(char* str) 26 | { 27 | // Interface number, string index, EP notification address and size, EP data address (out, in) and size. 28 | uint8_t cdc[TUD_CDC_DESC_LEN] = {TUD_CDC_DESCRIPTOR(ifIdx, 4, (uint8_t)(0x80 | (_EPNUM_CDC - 1)), 8, (uint8_t)_EPNUM_CDC, (uint8_t)(0x80 | _EPNUM_CDC), 64)}; 29 | memcpy(&desc_configuration[total], cdc, sizeof(cdc)); 30 | total += sizeof(cdc); 31 | ifIdx += 2; 32 | count += 2; 33 | 34 | if(!EspTinyUSB::begin(str, 4)) return false; 35 | return true; 36 | } 37 | 38 | int CDCusb::available() 39 | { 40 | return tud_cdc_n_available(_itf); 41 | } 42 | 43 | int CDCusb::peek() 44 | { 45 | if (tud_cdc_n_connected(_itf)) 46 | { 47 | uint8_t buffer; 48 | tud_cdc_n_peek(_itf, &buffer); 49 | return buffer; 50 | } 51 | else 52 | { 53 | return -1; 54 | } 55 | } 56 | 57 | int CDCusb::read() 58 | { 59 | if (tud_cdc_n_connected(_itf)) 60 | { 61 | if (tud_cdc_n_available(_itf)) 62 | { 63 | char c; 64 | uint32_t count = tud_cdc_n_read(_itf, &c, 1); 65 | return c; 66 | } 67 | } 68 | 69 | return -1; 70 | } 71 | 72 | size_t CDCusb::read(uint8_t *buffer, size_t size) 73 | { 74 | if (tud_cdc_n_connected(_itf)) 75 | { 76 | if (tud_cdc_n_available(_itf)) 77 | { 78 | uint32_t count = tud_cdc_n_read(_itf, buffer, size); 79 | return count; 80 | } 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | size_t CDCusb::write(uint8_t buffer) 87 | { 88 | uint8_t c = buffer; 89 | if (tud_cdc_n_connected(_itf)) 90 | { 91 | uint32_t d = tud_cdc_n_write(_itf, &c, 1); 92 | tud_cdc_n_write_flush(_itf); 93 | return d; 94 | } 95 | else 96 | { 97 | return 0; 98 | } 99 | } 100 | 101 | size_t CDCusb::write(const uint8_t *buffer, size_t size) 102 | { 103 | if (tud_cdc_n_connected(_itf)) 104 | { 105 | size_t d = 0; 106 | do{ 107 | d += tud_cdc_n_write(_itf, buffer + d, size - d <= 64 ? size - d : 64); 108 | }while(size > d); 109 | tud_cdc_n_write_flush(_itf); 110 | return d; 111 | } 112 | else 113 | { 114 | return 0; 115 | } 116 | } 117 | 118 | void CDCusb::flush() 119 | { 120 | tud_cdc_n_read_flush(_itf); 121 | tud_cdc_n_write_flush(_itf); 122 | } 123 | 124 | CDCusb::operator bool() const 125 | { 126 | return tud_cdc_n_connected(_itf); 127 | } 128 | 129 | void CDCusb::setWantedChar(char c) 130 | { 131 | tud_cdc_n_set_wanted_char(0, c); 132 | } 133 | 134 | void CDCusb::setCallbacks(CDCCallbacks* cb) 135 | { 136 | m_callbacks = cb; 137 | } 138 | 139 | uint32_t CDCusb::getBitrate() 140 | { 141 | return coding.bit_rate; 142 | } 143 | 144 | uint8_t CDCusb::getParity() 145 | { 146 | return coding.parity; 147 | } 148 | 149 | uint8_t CDCusb::getDataBits() 150 | { 151 | return coding.data_bits; 152 | } 153 | 154 | uint8_t CDCusb::getStopBits() 155 | { 156 | return coding.stop_bits; 157 | } 158 | 159 | // Invoked when received new data 160 | void tud_cdc_rx_cb(uint8_t itf) 161 | { 162 | if(_CDCusb[itf]->m_callbacks) 163 | _CDCusb[itf]->m_callbacks->onData(); 164 | } 165 | 166 | // void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted); 167 | // Invoked when received `wanted_char` 168 | void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { 169 | if(_CDCusb[itf]->m_callbacks) 170 | _CDCusb[itf]->m_callbacks->onWantedChar(wanted_char); 171 | } 172 | 173 | // Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE 174 | void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) 175 | { 176 | static uint8_t lineState = CDC_LINE_IDLE; 177 | bool reset = true; 178 | 179 | if(_CDCusb[itf]->m_callbacks) 180 | reset = _CDCusb[itf]->m_callbacks->onConnect(dtr, rts); 181 | 182 | if(!dtr && rts){ 183 | if(lineState == CDC_LINE_IDLE){ 184 | lineState++; 185 | } else { 186 | lineState = CDC_LINE_IDLE; 187 | } 188 | } else if(dtr && rts){ 189 | if(lineState == CDC_LINE_1){ 190 | lineState++; 191 | } else { 192 | lineState = CDC_LINE_IDLE; 193 | } 194 | } else if(dtr && !rts){ 195 | if(lineState == CDC_LINE_2){ 196 | lineState++; 197 | } else { 198 | lineState = CDC_LINE_IDLE; 199 | } 200 | } else if(!dtr && !rts){ 201 | if(lineState == CDC_LINE_3){ 202 | if (reset) 203 | { 204 | _CDCusb[itf]->persistentReset(RESTART_BOOTLOADER); 205 | } 206 | } else { 207 | lineState = CDC_LINE_IDLE; 208 | } 209 | } 210 | } 211 | 212 | void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding) 213 | { 214 | if(p_line_coding->bit_rate == 1200){ 215 | esp_restart(); 216 | } 217 | memcpy(&_CDCusb[itf]->coding, p_line_coding, sizeof(cdc_line_coding_t)); 218 | if(_CDCusb[itf]->m_callbacks) 219 | _CDCusb[itf]->m_callbacks->onCodingChange(p_line_coding); 220 | } 221 | 222 | #endif 223 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # New version 2 | New version of library, compatible with esp-idf (after cleanup) can be find here: 3 | 4 | https://github.com/chegewara/esp32-usb-v2 5 | 6 | # Description 7 | 8 | Library allows to build USB class devices and to make it usable with minimal effort: 9 | - CDC, communication device class, 10 | - MSC, mass storage class, 11 | - HID, human interface device class: keyboard, mouse, gamepad, generic IN/OUT, 12 | - MIDI, musical instrument digital interface class, 13 | - DFU, device firmware update class, 14 | - WebUSB, its using vendor class to show webusb usage. 15 | 16 | # Hardware 17 | To use native USB we need to connect pins 19 and 20 to usb cable or with similar connectors: 18 | ![](https://ae01.alicdn.com/kf/HTB1MFvqNgHqK1RjSZJnq6zNLpXaR/10-sztuk-Mini-Micro-USB-do-DIP-2-54mm-Adapter-z-cze-modu-u-Panel-kobiet.jpg) 19 | ![](https://ae01.alicdn.com/kf/HTB1cfmCgcnI8KJjSspeq6AwIpXa6/AMS1117-3-3V-AMS1117-3-3V-Mini-USB-5V-3-3V-DC-Perfect-Power-Supply-Module.jpg) 20 | 21 | # How to 22 | Library allows to set all values in standard USB device like: 23 | - manufacturer 24 | - product name 25 | - serial number 26 | - revision 27 | - VID and PID 28 | 29 | ``` 30 | ANYusb device; // any USB class like HID, MSC, CDC 31 | device.manufacturer(char*); 32 | device.product(char*); // product name 33 | device.serial(char*); // serial number SN 34 | device.revision(uint16_t); // product revison 35 | device.deviceID(uint16_t VID, uint16_t PID); 36 | device.deviceID(uint16_t* VID, uint16_t* PID); 37 | ``` 38 | 39 | # Default EP numbers 40 | In case of using more than 1 USB class in 1 device please make sure there is no conflict between EP numbers and eventuallu use `setBaseEP`: 41 | - CDC - EP1 and EP2, 42 | - HID - keyboard - EP2, mouse - EP3, 43 | - MSC - EP4, 44 | - WebUSB - EP4, 45 | - MIDI - EP5 46 | - DFU - not required 47 | 48 | # Contributions 49 | Issues and PRs are welcome. 50 | 51 | # USB host (WIP) 52 | 53 | I have some basic implementation of USB host which i decided to add. This is still experimental version as i dont know what tpe of design to implement (events or callbacks). 54 | 55 | Here is log from MSC host reading files from pendrive: 56 | 57 | ``` 58 | [ 960][I][test.ino:86] client_event_callback(): device speed: USB_SPEED_FULL, device address: 1, max ep_ctrl size: 64, config: 1 59 | EP num: 1/2, len: 32, address: 0x81, EP max size: 64, dir: IN 60 | EP num: 2/2, len: 32, address: 0x02, EP max size: 64, dir: OUT 61 | capacity_cb: block_size: 512, block_count: 60948479 62 | inquiry_cb 63 | [ 1038][I][test.ino:221] write_test(): File written 64 | [ 1049][I][test.ino:149] read_test(): /msc/README.txt 65 | [ 1051][I][test.ino:159] read_test(): Hello World! 66 | [ 1055][I][test.ino:205] read_test(): Found file : /msc/fanet_module.pdf (735686 bytes) 67 | [ 1056][I][test.ino:205] read_test(): Found file : /msc/06639_datasheet.pdf (205240 bytes) 68 | [ 1063][I][test.ino:205] read_test(): Found file : /msc/Bluetooth_5-FINAL.pdf (2100903 bytes) 69 | [ 1072][I][test.ino:205] read_test(): Found file : /msc/USB-Basics-USB-2.0-.pdf (113669 bytes) 70 | [ 1080][I][test.ino:205] read_test(): Found file : /msc/DS_SX1280-1_V2.2.book.pdf (197260 bytes) 71 | [ 1096][I][test.ino:205] read_test(): Found file : /msc/USB-Basics-USB-2.0- (1).pdf (113669 bytes) 72 | [ 1102][I][test.ino:205] read_test(): Found file : /msc/USB---Enumeration-States.pdf (29098 bytes) 73 | [ 1110][I][test.ino:205] read_test(): Found file : /msc/USB---Protocol---bits4device.pdf (1022853 bytes) 74 | [ 1125][I][test.ino:205] read_test(): Found file : /msc/esp32-s2_technical_reference_manual_en.pdf (6769251 bytes) 75 | [ 1132][I][test.ino:205] read_test(): Found file : /msc/cd00167594-stm32-microcontroller-system-memory-boot-mode-stmicroelectronics.pdf (4613315 bytes) 76 | [ 1146][I][test.ino:205] read_test(): Found file : /msc/USB Complete The Developer's Guide 4th Ed.pdf (6936935 bytes) 77 | [ 1161][I][test.ino:205] read_test(): Found file : /msc/USB Mass Storage Designing and Programming Devices and Embedded Hosts.pdf (3360734 bytes) 78 | [ 1173][I][test.ino:205] read_test(): Found file : /msc/README.txt (13 bytes) 79 | [ 1181][I][test.ino:233] write_test(): Renaming file 80 | [ 1212][I][test.ino:240] write_test(): Reading file 81 | [ 1225][I][test.ino:254] write_test(): Read from file /msc/README1.txt: 'Hello World!' 82 | [ 1233][E][test.ino:145] read_test(): Failed to open file 83 | [ 1237][I][test.ino:205] read_test(): Found file : /msc/fanet_module.pdf (735686 bytes) 84 | [ 1238][I][test.ino:205] read_test(): Found file : /msc/06639_datasheet.pdf (205240 bytes) 85 | [ 1245][I][test.ino:205] read_test(): Found file : /msc/Bluetooth_5-FINAL.pdf (2100903 bytes) 86 | [ 1253][I][test.ino:205] read_test(): Found file : /msc/USB-Basics-USB-2.0-.pdf (113669 bytes) 87 | [ 1262][I][test.ino:205] read_test(): Found file : /msc/DS_SX1280-1_V2.2.book.pdf (197260 bytes) 88 | [ 1278][I][test.ino:205] read_test(): Found file : /msc/USB-Basics-USB-2.0- (1).pdf (113669 bytes) 89 | [ 1284][I][test.ino:205] read_test(): Found file : /msc/USB---Enumeration-States.pdf (29098 bytes) 90 | [ 1292][I][test.ino:205] read_test(): Found file : /msc/USB---Protocol---bits4device.pdf (1022853 bytes) 91 | [ 1307][I][test.ino:205] read_test(): Found file : /msc/esp32-s2_technical_reference_manual_en.pdf (6769251 bytes) 92 | [ 1314][I][test.ino:205] read_test(): Found file : /msc/cd00167594-stm32-microcontroller-system-memory-boot-mode-stmicroelectronics.pdf (4613315 bytes) 93 | [ 1327][I][test.ino:205] read_test(): Found file : /msc/USB Complete The Developer's Guide 4th Ed.pdf (6936935 bytes) 94 | [ 1342][I][test.ino:205] read_test(): Found file : /msc/USB Mass Storage Designing and Programming Devices and Embedded Hosts.pdf (3360734 bytes) 95 | [ 1352][I][test.ino:205] read_test(): Found file : /msc/README1.txt (13 bytes) 96 | [ 1352][I][test.ino:289] setup(): storage used: 26329088/31189319680 97 | ``` 98 | -------------------------------------------------------------------------------- /src/host/msc/vfs/diskio_rawmsc.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include "diskio_impl.h" 17 | #include "ff.h" 18 | extern "C" { 19 | #include "ffconf.h" 20 | } 21 | #include "esp_log.h" 22 | #include "diskio_rawflash.h" 23 | #include "esp_compiler.h" 24 | #include "esp_vfs_fat.h" 25 | 26 | #include "usb_msc.hpp" 27 | 28 | static const char* TAG = "msc_rawflash"; 29 | 30 | uint8_t ff_raw_handles[FF_VOLUMES]; // change to lun?? 31 | 32 | 33 | DSTATUS ff_raw_initialize (BYTE pdrv) 34 | { 35 | ESP_LOGD(TAG, "ff_raw_initialize => %d", pdrv); 36 | return 0; 37 | } 38 | 39 | DSTATUS ff_raw_status (BYTE pdrv) 40 | { 41 | return 0; 42 | } 43 | 44 | DRESULT ff_raw_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count) 45 | { 46 | ESP_LOGV(TAG, "ff_raw_read - pdrv=%i, sector=%i, count=%in", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count); 47 | uint8_t lun = ff_raw_handles[pdrv]; 48 | USBmscDevice* part = USBmscDevice::getInstance(); 49 | assert(part); 50 | esp_err_t err = part->_read10(lun, sector, count, buff); 51 | if (unlikely(err != ESP_OK)) { 52 | ESP_LOGE(TAG, "msc disk lun failed (0x%x)", err); 53 | return RES_ERROR; 54 | } 55 | return RES_OK; 56 | } 57 | 58 | DRESULT ff_raw_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) 59 | { 60 | ESP_LOGV(TAG, "ff_raw_write - pdrv=%i, sector=%i, count=%in", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count); 61 | uint8_t lun = ff_raw_handles[pdrv]; 62 | USBmscDevice* part = USBmscDevice::getInstance(); 63 | assert(part); 64 | 65 | esp_err_t err = part->_write10(lun, sector, count, (uint8_t*)buff); 66 | if (unlikely(err != ESP_OK)) { 67 | ESP_LOGE(TAG, "msc disk lun failed (0x%x)", err); 68 | return RES_ERROR; 69 | } 70 | return RES_OK; 71 | return RES_ERROR; 72 | } 73 | 74 | DRESULT ff_raw_ioctl (BYTE pdrv, BYTE cmd, void *buff) 75 | { 76 | uint8_t lun = ff_raw_handles[pdrv]; 77 | USBmscDevice* part = USBmscDevice::getInstance(); 78 | ESP_LOGV(TAG, "ff_raw_ioctl: cmd=%in", cmd); 79 | assert(part); 80 | switch (cmd) { 81 | case CTRL_SYNC: 82 | return RES_OK; 83 | case GET_SECTOR_COUNT: 84 | *((DWORD *) buff) = part->getBlockCount(lun); 85 | return RES_OK; 86 | case GET_SECTOR_SIZE: 87 | *((WORD *) buff) = part->getBlockSize(lun); 88 | return RES_OK; 89 | case GET_BLOCK_SIZE: 90 | return RES_ERROR; 91 | } 92 | return RES_ERROR; 93 | } 94 | 95 | 96 | esp_err_t ff_msc_register_raw_partition(BYTE pdrv, uint8_t lun) 97 | { 98 | if (pdrv >= FF_VOLUMES) { 99 | return ESP_ERR_INVALID_ARG; 100 | } 101 | static const ff_diskio_impl_t raw_impl = { 102 | .init = &ff_raw_initialize, 103 | .status = &ff_raw_status, 104 | .read = &ff_raw_read, 105 | .write = &ff_raw_write, 106 | .ioctl = &ff_raw_ioctl 107 | }; 108 | ff_diskio_register(pdrv, &raw_impl); 109 | ff_raw_handles[pdrv] = lun; 110 | return ESP_OK; 111 | } 112 | 113 | 114 | // BYTE ff_msc_get_pdrv_raw(const void* part_handle) 115 | // { 116 | // for (int i = 0; i < FF_VOLUMES; i++) { 117 | // if (part_handle == ff_raw_handles[i]) { 118 | // return i; 119 | // } 120 | // } 121 | // return 0xff; 122 | // } 123 | 124 | esp_err_t vfs_fat_rawmsc_mount(const char* base_path, 125 | const esp_vfs_fat_mount_config_t* mount_config, uint8_t lun) 126 | { 127 | esp_err_t result = ESP_OK; 128 | 129 | USBmscDevice* part = USBmscDevice::getInstance(); 130 | assert(part); 131 | if(lun > part->getMaxLUN()) 132 | { 133 | result = ESP_ERR_INVALID_STATE; 134 | return result; 135 | } 136 | 137 | BYTE pdrv = 0xFF; 138 | if (ff_diskio_get_drive(&pdrv) != ESP_OK) { 139 | ESP_LOGW(TAG, "the maximum count of volumes is already mounted"); 140 | return ESP_ERR_NO_MEM; 141 | } 142 | char drv[3] = {(char)('0' + pdrv), ':', 0}; 143 | ESP_LOGD(TAG, "using pdrv=%i, drv: %s", pdrv, drv); 144 | 145 | result = ff_msc_register_raw_partition(pdrv, lun); 146 | if (result != ESP_OK) { 147 | ESP_LOGE(TAG, "ff_msc_register_raw_partition failed pdrv=%i, error - 0x(%x)", pdrv, result); 148 | goto fail; 149 | } 150 | 151 | FRESULT fresult; 152 | FATFS *fs; 153 | result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs); 154 | if (result == ESP_ERR_INVALID_STATE) { 155 | // it's okay, already registered with VFS 156 | } else if (result != ESP_OK) { 157 | ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result); 158 | goto fail; 159 | } 160 | 161 | // Try to mount partition 162 | fresult = f_mount(fs, drv, 0); 163 | if (fresult != FR_OK) { 164 | ESP_LOGW(TAG, "f_mount failed (%d)", fresult); 165 | result = ESP_FAIL; 166 | goto fail; 167 | } 168 | return ESP_OK; 169 | 170 | fail: 171 | esp_vfs_fat_unregister_path(base_path); 172 | ff_diskio_unregister(pdrv); 173 | return result; 174 | } 175 | -------------------------------------------------------------------------------- /examples/device/msc/sd_msc/test.ino: -------------------------------------------------------------------------------- 1 | 2 | void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ 3 | Serial.printf("Listing directory: %s\n", dirname); 4 | 5 | File root = fs.open(dirname); 6 | if(!root){ 7 | Serial.println("Failed to open directory"); 8 | return; 9 | } 10 | if(!root.isDirectory()){ 11 | Serial.println("Not a directory"); 12 | return; 13 | } 14 | 15 | File file = root.openNextFile(); 16 | while(file){ 17 | if(file.isDirectory()){ 18 | Serial.print(" DIR : "); 19 | Serial.println(file.name()); 20 | if(levels){ 21 | listDir(fs, file.name(), levels -1); 22 | } 23 | } else { 24 | Serial.print(" FILE: "); 25 | Serial.print(file.name()); 26 | Serial.print(" SIZE: "); 27 | Serial.println(file.size()); 28 | } 29 | file = root.openNextFile(); 30 | } 31 | } 32 | 33 | void createDir(fs::FS &fs, const char * path){ 34 | Serial.printf("Creating Dir: %s\n", path); 35 | if(fs.mkdir(path)){ 36 | Serial.println("Dir created"); 37 | } else { 38 | Serial.println("mkdir failed"); 39 | } 40 | } 41 | 42 | void removeDir(fs::FS &fs, const char * path){ 43 | Serial.printf("Removing Dir: %s\n", path); 44 | if(fs.rmdir(path)){ 45 | Serial.println("Dir removed"); 46 | } else { 47 | Serial.println("rmdir failed"); 48 | } 49 | } 50 | 51 | void readFile(fs::FS &fs, const char * path){ 52 | Serial.printf("Reading file: %s\n", path); 53 | 54 | File file = fs.open(path); 55 | if(!file){ 56 | Serial.println("Failed to open file for reading"); 57 | return; 58 | } 59 | 60 | Serial.print("Read from file: "); 61 | while(file.available()){ 62 | Serial.write(file.read()); 63 | } 64 | file.close(); 65 | } 66 | 67 | void writeFile(fs::FS &fs, const char * path, const char * message){ 68 | Serial.printf("Writing file: %s\n", path); 69 | 70 | File file = fs.open(path, FILE_WRITE); 71 | if(!file){ 72 | Serial.println("Failed to open file for writing"); 73 | return; 74 | } 75 | if(file.print(message)){ 76 | Serial.println("File written"); 77 | } else { 78 | Serial.println("Write failed"); 79 | } 80 | file.close(); 81 | } 82 | 83 | void appendFile(fs::FS &fs, const char * path, const char * message){ 84 | Serial.printf("Appending to file: %s\n", path); 85 | 86 | File file = fs.open(path, FILE_APPEND); 87 | if(!file){ 88 | Serial.println("Failed to open file for appending"); 89 | return; 90 | } 91 | if(file.print(message)){ 92 | Serial.println("Message appended"); 93 | } else { 94 | Serial.println("Append failed"); 95 | } 96 | file.close(); 97 | } 98 | 99 | void renameFile(fs::FS &fs, const char * path1, const char * path2){ 100 | Serial.printf("Renaming file %s to %s\n", path1, path2); 101 | if (fs.rename(path1, path2)) { 102 | Serial.println("File renamed"); 103 | } else { 104 | Serial.println("Rename failed"); 105 | } 106 | } 107 | 108 | void deleteFile(fs::FS &fs, const char * path){ 109 | Serial.printf("Deleting file: %s\n", path); 110 | if(fs.remove(path)){ 111 | Serial.println("File deleted"); 112 | } else { 113 | Serial.println("Delete failed"); 114 | } 115 | } 116 | 117 | void testFileIO(fs::FS &fs, const char * path){ 118 | File file = fs.open(path); 119 | static uint8_t buf[512]; 120 | size_t len = 0; 121 | uint32_t start = millis(); 122 | uint32_t end = start; 123 | if(file){ 124 | len = file.size(); 125 | size_t flen = len; 126 | start = millis(); 127 | while(len){ 128 | size_t toRead = len; 129 | if(toRead > 512){ 130 | toRead = 512; 131 | } 132 | file.read(buf, toRead); 133 | len -= toRead; 134 | } 135 | end = millis() - start; 136 | Serial.printf("%u bytes read for %u ms\n", flen, end); 137 | file.close(); 138 | } else { 139 | Serial.println("Failed to open file for reading"); 140 | } 141 | 142 | 143 | file = fs.open(path, FILE_WRITE); 144 | if(!file){ 145 | Serial.println("Failed to open file for writing"); 146 | return; 147 | } 148 | 149 | size_t i; 150 | start = millis(); 151 | for(i=0; i<2048; i++){ 152 | file.write(buf, 512); 153 | } 154 | end = millis() - start; 155 | Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); 156 | file.close(); 157 | } 158 | 159 | void test() 160 | { 161 | 162 | uint8_t cardType = SD.cardType(); 163 | Serial.print("SD Card Type: "); 164 | if(cardType == CARD_MMC){ 165 | Serial.println("MMC"); 166 | } else if(cardType == CARD_SD){ 167 | Serial.println("SDSC"); 168 | } else if(cardType == CARD_SDHC){ 169 | Serial.println("SDHC"); 170 | } else { 171 | Serial.println("UNKNOWN"); 172 | } 173 | 174 | uint64_t cardSize = SD.cardSize() / (1024 * 1024); 175 | Serial.printf("SD Card Size: %lluMB\n", cardSize); 176 | 177 | listDir(SD, "/", 0); 178 | createDir(SD, "/mydir"); 179 | listDir(SD, "/", 0); 180 | removeDir(SD, "/mydir"); 181 | listDir(SD, "/", 2); 182 | writeFile(SD, "/hello.txt", "Hello "); 183 | appendFile(SD, "/hello.txt", "World!\n"); 184 | readFile(SD, "/hello.txt"); 185 | deleteFile(SD, "/foo.txt"); 186 | renameFile(SD, "/hello.txt", "/foo.txt"); 187 | readFile(SD, "/foo.txt"); 188 | testFileIO(SD, "/test.txt"); 189 | Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024)); 190 | Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024)); 191 | } -------------------------------------------------------------------------------- /src/device/web/webusb.cpp: -------------------------------------------------------------------------------- 1 | #include "webusb.h" 2 | 3 | #if CFG_TUD_VENDOR 4 | 5 | static bool web_serial_connected = false; 6 | static WebUSB *_webUSB = NULL; 7 | 8 | WebUSB::WebUSB(uint8_t itf) 9 | { 10 | _itf = 0; // hardcoded due to limitations in arduino tinyusb 11 | _webUSB = this; 12 | enableVENDOR = true; 13 | String url = String("www.tinyusb.org/examples/webusb-serial"); 14 | landingPageURI(url, true); 15 | _EPNUM_VENDOR = EPNUM_VENDOR; 16 | } 17 | 18 | void WebUSB::setBaseEP(uint8_t ep) 19 | { 20 | _EPNUM_VENDOR = ep; 21 | } 22 | 23 | bool WebUSB::begin(char* str, const char *url, bool ssl) 24 | { 25 | if (url != nullptr) 26 | landingPageURI(url, ssl); 27 | 28 | // Interface number, string index, EP Out & IN address, EP size 29 | uint8_t vendor[] = {TUD_VENDOR_DESCRIPTOR(ifIdx++, 7, _EPNUM_VENDOR, (uint8_t)(0x80 | _EPNUM_VENDOR), 64)}; 30 | memcpy(&desc_configuration[total], vendor, sizeof(vendor)); 31 | total += sizeof(vendor); 32 | count++; 33 | _bcdUSB = 0x210; 34 | return EspTinyUSB::begin(str, 7); 35 | } 36 | 37 | int WebUSB::available() 38 | { 39 | return tud_vendor_n_available(_itf); 40 | } 41 | 42 | int WebUSB::peek() 43 | { 44 | int pos; 45 | uint8_t buffer[1]; 46 | if (web_serial_connected) 47 | { 48 | tud_vendor_n_peek(_itf, buffer); 49 | return buffer[0]; 50 | } 51 | else 52 | { 53 | return -1; 54 | } 55 | } 56 | 57 | int WebUSB::read() 58 | { 59 | if (web_serial_connected) 60 | { 61 | char c; 62 | if (tud_vendor_n_available(_itf)) 63 | { 64 | uint32_t count = tud_vendor_n_read(_itf, &c, 1); 65 | return c; 66 | } 67 | } 68 | 69 | return -1; 70 | } 71 | 72 | size_t WebUSB::read(uint8_t *buffer, size_t size) 73 | { 74 | if (web_serial_connected) 75 | { 76 | if (tud_vendor_n_available(_itf)) 77 | { 78 | uint32_t count = tud_vendor_n_read(_itf, buffer, size); 79 | return count; 80 | } 81 | } 82 | 83 | return -1; 84 | } 85 | 86 | size_t WebUSB::write(uint8_t c) 87 | { 88 | return write(&c, 1); 89 | } 90 | 91 | size_t WebUSB::write(const uint8_t *buffer, size_t size) 92 | { 93 | uint32_t sent = tud_vendor_n_write(_itf, buffer, size); 94 | return sent; 95 | } 96 | 97 | void WebUSB::flush() {} 98 | 99 | WebUSB::operator bool() const 100 | { 101 | return web_serial_connected; 102 | } 103 | 104 | void WebUSB::landingPageURI(String url, bool ssl) 105 | { 106 | landingPageURI((const char *)url.c_str(), ssl); 107 | } 108 | 109 | void WebUSB::landingPageURI(const char *url, bool ssl) 110 | { 111 | if (_url != NULL) 112 | { 113 | free(_url); 114 | } 115 | uint8_t size = strlen(url); 116 | _url = (uint8_t *)calloc(1, size + 3); 117 | memcpy(&_url[3], url, size); 118 | _url[0] = size + 3; 119 | _url[1] = 3; 120 | _url[2] = ssl; 121 | } 122 | 123 | void WebUSB::setCallbacks(WebUSBCallbacks* cb) 124 | { 125 | m_callbacks = cb; 126 | } 127 | 128 | void tud_vendor_rx_cb(uint8_t itf) 129 | { 130 | if(_webUSB->m_callbacks) 131 | _webUSB->m_callbacks->onData(); 132 | } 133 | 134 | //--------------------------------------------------------------------+ 135 | // WebUSB use vendor class 136 | //--------------------------------------------------------------------+ 137 | extern "C" 138 | { 139 | #define BOS_TOTAL_LEN \ 140 | (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN) 141 | 142 | uint8_t const desc_bos[] = { 143 | // total length, number of device caps 144 | TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2), 145 | 146 | // Vendor Code, iLandingPage 147 | TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1), 148 | 149 | // Microsoft OS 2.0 descriptor 150 | TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT)}; 151 | 152 | uint8_t const *tud_descriptor_bos_cb(void) 153 | { 154 | if(_webUSB == NULL) return nullptr; 155 | 156 | return desc_bos; 157 | } 158 | 159 | // Invoked when received VENDOR control request 160 | bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) 161 | { 162 | if(_webUSB == NULL) return false; 163 | // nothing to with DATA & ACK stage 164 | if (stage != CONTROL_STAGE_SETUP ) return true; 165 | 166 | switch (request->bRequest) 167 | { 168 | case VENDOR_REQUEST_WEBUSB: 169 | // match vendor request in BOS descriptor 170 | // Get landing page url 171 | if (!_webUSB->_url) 172 | return false; 173 | return tud_control_xfer(rhport, request, (void *)_webUSB->_url, _webUSB->_url[0]); 174 | 175 | case VENDOR_REQUEST_MICROSOFT: 176 | if (request->wIndex == 7) 177 | { 178 | // Get Microsoft OS 2.0 compatible descriptor 179 | uint16_t total_len; 180 | memcpy(&total_len, desc_ms_os_20 + 8, 2); 181 | 182 | return tud_control_xfer(rhport, request, (void *)desc_ms_os_20, total_len); 183 | } 184 | else 185 | { 186 | return false; 187 | } 188 | break; 189 | case 0x22: 190 | // Webserial simulate the CDC_REQUEST_SET_CONTROL_LINE_STATE (0x22) to 191 | // connect and disconnect. 192 | web_serial_connected = (request->wValue != 0); 193 | 194 | if(_webUSB->m_callbacks) 195 | _webUSB->m_callbacks->onConnect(web_serial_connected); 196 | 197 | // response with status OK 198 | return tud_control_status(rhport, request); 199 | 200 | default: 201 | // stall unknown request 202 | return false; 203 | } 204 | 205 | return true; 206 | } 207 | 208 | } 209 | 210 | #endif 211 | 212 | -------------------------------------------------------------------------------- /src/ramdisk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "mscusb.h" 3 | 4 | #if CFG_TUD_MSC 5 | 6 | #define README_CONTENTS \ 7 | "This is tinyusb's MassStorage Class demo.\r\n\r\n\ 8 | If you find any bugs or get any questions, feel free to file an\r\n\ 9 | issue at github.com/hathach/tinyusb" 10 | 11 | enum 12 | { 13 | DISK_BLOCK_NUM = 2 * 8, // 8KB is the smallest size that windows allow to mount 14 | DISK_BLOCK_SIZE = 512 15 | }; 16 | 17 | static uint8_t ram_disk_demo[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = 18 | { 19 | //------------- Block0: Boot Sector -------------// 20 | // byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM; 21 | // sector_per_cluster = 1; reserved_sectors = 1; 22 | // fat_num = 1; fat12_root_entry_num = 16; 23 | // sector_per_fat = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0; 24 | // drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29; 25 | // filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC"; 26 | // FAT magic code at offset 510-511 27 | { 28 | 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00, 29 | 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 30 | 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x56, 0x78, 'E' , 'S' , 'P' , '3' , '2' , 31 | 'S' , '2' , ' ' , 'M' , 'S' , 'C' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00, 32 | 33 | // Zero up to 2 last bytes of FAT magic code 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 35 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 36 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 37 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 39 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 40 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 41 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 42 | 43 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 | 52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 56 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 57 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 58 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 59 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 60 | 61 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 62 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 63 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA 65 | }, 66 | 67 | //------------- Block1: FAT12 Table -------------// 68 | { 69 | 0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file 70 | }, 71 | 72 | //------------- Block2: Root Directory -------------// 73 | { 74 | // first entry is volume label 75 | 'E' , 'S' , 'P' , '3' , '2' , 'S' , '2' , ' ' , 'M' , 'S' , 'C' , 0x08, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 77 | // second entry is readme file 78 | 'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' , 'T' , 'X' , 'T' , 0x20, 0x00, 0xC6, 0x52, 0x6D, 79 | 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00, 80 | sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes) 81 | }, 82 | 83 | //------------- Block3: Readme Content -------------// 84 | README_CONTENTS 85 | }; 86 | 87 | class USBramdisk : public MSCusb { 88 | public: 89 | USBramdisk(); 90 | bool begin(char* str = nullptr); 91 | void setCallbacks(MSCCallbacks*); 92 | void setCapacity(uint32_t count, uint32_t size); 93 | void setDiskMemory(uint8_t* memory, bool demo = false); 94 | void setContent(uint8_t*, size_t); 95 | 96 | MSCCallbacks* m_private; 97 | uint32_t block_count = DISK_BLOCK_NUM; 98 | uint32_t block_size = DISK_BLOCK_SIZE; 99 | uint8_t WORD_ALIGNED_ATTR *ram_disk = nullptr; 100 | bool set_demo_content = true; 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /src/device/midi/midiusb.cpp: -------------------------------------------------------------------------------- 1 | #include "midiusb.h" 2 | #define EPNUM_MIDI 0x05 3 | #if CFG_TUD_MIDI 4 | 5 | MIDIusb::MIDIusb() 6 | { 7 | enableMIDI = true; 8 | _EPNUM_MIDI = EPNUM_MIDI; 9 | } 10 | 11 | void MIDIusb::setBaseEP(uint8_t ep) 12 | { 13 | _EPNUM_MIDI = ep; 14 | } 15 | 16 | bool MIDIusb::begin(char* str) 17 | { 18 | // Interface number, string index, EP Out & EP In address, EP size 19 | uint8_t midi[] = {TUD_MIDI_DESCRIPTOR(1, 8, _EPNUM_MIDI, (uint8_t)(0x80 | _EPNUM_MIDI), 64)}; 20 | memcpy(&desc_configuration[total], midi, sizeof(midi)); 21 | total += sizeof(midi); 22 | count += 2; 23 | return EspTinyUSB::begin(str, 8); 24 | } 25 | 26 | void MIDIusb::noteON(uint8_t note, uint8_t velocity, uint8_t channel) 27 | { 28 | log_v("ON: %d\n", note); 29 | uint8_t buf[] = {0x90, note, velocity}; 30 | tud_midi_stream_write(channel, buf, 3); 31 | } 32 | 33 | void MIDIusb::noteOFF(uint8_t note, uint8_t velocity, uint8_t channel) 34 | { 35 | log_v("OFF: %d\n", note); 36 | uint8_t buf[] = {0x80, note, velocity}; 37 | tud_midi_stream_write(channel, buf, 3); 38 | } 39 | 40 | void MIDIusb::polyKey(uint8_t note, uint8_t pressure, uint8_t channel) 41 | { 42 | uint8_t buf[] = {0xa0, note, pressure}; 43 | tud_midi_stream_write(channel, buf, 3); 44 | } 45 | 46 | void MIDIusb::controlChange(uint8_t controller, uint8_t value, uint8_t channel) 47 | { 48 | uint8_t buf[] = {0xb0, controller, value}; 49 | tud_midi_stream_write(channel, buf, 3); 50 | } 51 | 52 | void MIDIusb::programChange(uint8_t program, uint8_t channel) 53 | { 54 | } 55 | 56 | void MIDIusb::channelPresure(uint8_t presure, uint8_t channel) 57 | { 58 | } 59 | 60 | void MIDIusb::pitchChange(uint16_t value, uint8_t channel) 61 | { 62 | } 63 | 64 | typedef struct 65 | { 66 | uint8_t type[4]; 67 | uint32_t length; 68 | uint16_t format; 69 | uint16_t tracks; 70 | uint16_t division; 71 | } mthd_t; 72 | 73 | typedef struct 74 | { 75 | uint8_t type[4]; 76 | uint8_t *data; 77 | } mtrk_t; 78 | 79 | bool MIDIusb::setSong(uint8_t *song, size_t len) 80 | { 81 | _len = len - 36; 82 | mthd_t mthd; 83 | mtrk_t mtrk; 84 | if (strstr((char*)song, "MThd") != NULL) 85 | { 86 | _song = (uint8_t*)strstr((char*)song, "MThd"); 87 | memcpy(&mthd, _song, sizeof(mthd)); 88 | log_v("MThd format => %d\n", mthd.format); 89 | if(mthd.format > 0) return false; 90 | } 91 | 92 | if (strstr((char*)song, "MT") != NULL) 93 | { 94 | _song = (uint8_t*)strstr((char*)_song, "MTrk"); 95 | Serial.println("MTrk"); 96 | } 97 | do{ 98 | song++; 99 | }while(song[0] != 0x90); 100 | if(song == NULL) return false; 101 | song--; 102 | 103 | log_v("song => %x, i = %x\n", *(song), *song); 104 | _song = (uint8_t*)calloc(_len, 1); 105 | memcpy(_song, song, _len); 106 | return true; 107 | } 108 | 109 | uint8_t parseMeta(uint8_t* _song, size_t i) 110 | { 111 | uint8_t n = 0; 112 | if (_song[i + 1] == 0x0){ 113 | n = 3; 114 | }else if (_song[i + 1] == 0x03){ 115 | Serial.println(_song[i + 3]); 116 | n = _song[i+2] + 3; 117 | } else if (_song[i + 1] <= 0x07){ 118 | n = _song[i+2] + 3; 119 | } else if (_song[i + 1] == 0x20){ // channel prefix 120 | n = 4; 121 | } else if (_song[i + 1] == 0x2f){ // end track 122 | n = 3; 123 | } else if (_song[i + 1] == 0x51){ // 124 | n = 3 + 3; 125 | } else if (_song[i + 1] == 0x54){ 126 | n = 3 + 5; 127 | } else if (_song[i + 1] == 0x58){ 128 | n = 3 + 4; 129 | } else if (_song[i + 1] == 0x59){ 130 | n = 3 + 2; 131 | } else if (_song[i + 1] == 0x7f){ 132 | n = _song[i+2] + 3; 133 | } 134 | 135 | log_v("meta => %x, n = %x\n", _song[i+1], n - 1); 136 | 137 | return n - 1; 138 | } 139 | 140 | void MIDIusb::playSong() 141 | { 142 | uint8_t delta; 143 | uint8_t _byte; 144 | if(_song == nullptr) return; 145 | log_v("play"); 146 | 147 | for (size_t i = 0; i < _len; i++) 148 | { 149 | delta = _song[i]; 150 | _byte = _song[++i]; 151 | log_v("delta => %x, _byte = %x\n", delta, _byte); 152 | if(delta) delay(delta * 8); 153 | if ((_byte & 0xff) == 0xff){ // meta event 154 | i += parseMeta(_song, i); 155 | } 156 | else if ((_byte & 0xe0) == 0xe0){ // pitch wheel 157 | i += 2; 158 | log_v("pitch"); 159 | } 160 | else if ((_byte & 0xd0) == 0xd0){ // pressure 161 | i += 1; 162 | log_v("pressure"); 163 | } 164 | else if ((_byte & 0xc0) == 0xc0){ // prog change 165 | i += 1; 166 | log_v("prog"); 167 | } 168 | else if ((_byte & 0xb0) == 0xb0){ // ctrl change 169 | controlChange(_song[i + 1], _song[i + 2]); 170 | i += 2; 171 | log_v("ctrl"); 172 | } 173 | else if ((_byte & 0xa0) == 0xa0){ // poly key 174 | polyKey(_song[i + 1], _song[i + 2]); 175 | i += 2; 176 | log_v("poly"); 177 | } 178 | else if ((_byte & 0x90) == 0x90){ // note on 179 | noteON(_song[i + 1], _song[i + 2]); 180 | i += 2; 181 | log_v("on"); 182 | } 183 | else if ((_byte & 0x80) == 0x80){ // note off 184 | noteOFF(_song[i + 1], _song[i + 2]); 185 | i += 2; 186 | log_v("off"); 187 | } else { 188 | Serial.printf("unknown: %d\n", _song[i]); 189 | } 190 | } 191 | } 192 | 193 | void tud_midi_rx_cb(uint8_t itf) 194 | { 195 | uint8_t _mid[4] = {0}; 196 | if(tud_midi_packet_read(_mid)) { 197 | for (size_t i = 0; i < 4; i++) 198 | { 199 | Serial.printf("%02x ", _mid[i]); 200 | } 201 | Serial.println(); 202 | } else log_e("failed to receive"); 203 | } 204 | 205 | int MIDIusb::available() 206 | { 207 | return tud_midi_available(); 208 | } 209 | 210 | #endif 211 | -------------------------------------------------------------------------------- /src/device/msc/flashdisk.cpp: -------------------------------------------------------------------------------- 1 | #include "flashdisk.h" 2 | #include "ffconf.h" 3 | #include "ff.h" 4 | #include "diskio.h" 5 | #ifdef CFG_TUD_MSC 6 | static uint8_t _buf[4 * 1024] = {}; 7 | 8 | class FlashCallbacks : public MSCCallbacks { 9 | FlashUSB* m_parent; 10 | wl_handle_t wl_handle_thandle; 11 | int s_disk_block_size = 0; 12 | int s_pdrv = 0; 13 | bool eject = true; 14 | 15 | public: 16 | FlashCallbacks(FlashUSB* ram, wl_handle_t handle, int pdrv) { m_parent = ram; wl_handle_thandle = handle; s_pdrv = pdrv; } 17 | ~FlashCallbacks() { } 18 | void onInquiry(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) 19 | { 20 | if (m_parent->m_private) 21 | { 22 | m_parent->m_private->onInquiry(lun, vendor_id, product_id, product_rev); 23 | } else { 24 | const char vid[] = "ESP32-S2"; 25 | const char pid[] = "FLASH"; 26 | const char rev[] = "1.0"; 27 | 28 | memcpy(vendor_id , vid, strlen(vid)); 29 | memcpy(product_id , pid, strlen(pid)); 30 | memcpy(product_rev, rev, strlen(rev)); 31 | log_v("default onInquiry"); 32 | } 33 | } 34 | bool onReady(uint8_t lun) { 35 | if (m_parent->m_private) 36 | { 37 | return m_parent->m_private->onReady(lun); 38 | } else { 39 | return m_parent->sdcardReady; 40 | } 41 | } 42 | void onCapacity(uint8_t lun, uint32_t* block_count, uint16_t* block_size) 43 | { 44 | if (m_parent->m_private) 45 | { 46 | return m_parent->m_private->onCapacity(lun, block_count, block_size); 47 | } else { 48 | (void) lun; 49 | disk_ioctl(s_pdrv, GET_SECTOR_COUNT, block_count); 50 | disk_ioctl(s_pdrv, GET_SECTOR_SIZE, block_size); 51 | s_disk_block_size = *block_size; 52 | m_parent->block_count = *block_count; 53 | m_parent->block_size = *block_size; 54 | log_d("disk block count: %d, block size: %d", *block_count, *block_size); 55 | } 56 | } 57 | bool onStop(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) 58 | { 59 | if (m_parent->m_private) 60 | { 61 | return m_parent->m_private->onStop(lun, power_condition, start, load_eject); 62 | } else { 63 | (void) lun; 64 | (void) power_condition; 65 | 66 | if ( load_eject ) 67 | { 68 | if (!start) { 69 | // Eject but first flush. 70 | if (disk_ioctl(s_pdrv, CTRL_SYNC, NULL) != RES_OK) { 71 | eject = false; 72 | return false; 73 | } else { 74 | eject = true; 75 | } 76 | } else { 77 | // We can only load if it hasn't been ejected. 78 | return !eject; 79 | } 80 | } else { 81 | if (!start) { 82 | // Stop the unit but don't eject. 83 | if (disk_ioctl(s_pdrv, CTRL_SYNC, NULL) != RES_OK) { 84 | return false; 85 | } 86 | } 87 | } 88 | 89 | return true; 90 | } 91 | } 92 | int32_t onRead(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) 93 | { 94 | if (m_parent->m_private) 95 | { 96 | return m_parent->m_private->onRead(lun, lba, offset, buffer, bufsize); 97 | } else { 98 | log_v("default onread"); 99 | (void) lun; 100 | if(CONFIG_WL_SECTOR_SIZE > CONFIG_TINYUSB_MSC_BUFSIZE){ 101 | disk_read(s_pdrv, _buf, lba, 1); 102 | memcpy(buffer, &_buf[offset], bufsize); 103 | } else { 104 | disk_read(s_pdrv, (BYTE*)buffer, lba, 1); 105 | } 106 | 107 | return bufsize; 108 | } 109 | } 110 | 111 | int32_t onWrite(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) 112 | { 113 | if (m_parent->m_private) 114 | { 115 | return m_parent->m_private->onWrite(lun, lba, offset, buffer, bufsize); 116 | } else { 117 | log_v("default onwrite; lba: %d, off: %d, size: %d", lba, offset, bufsize); 118 | (void) lun; 119 | if(CONFIG_WL_SECTOR_SIZE > CONFIG_TINYUSB_MSC_BUFSIZE){ 120 | disk_read(s_pdrv, _buf, lba, 1); 121 | memcpy(&_buf[offset], buffer, bufsize); 122 | disk_write(s_pdrv, _buf, lba, 1); 123 | } else { 124 | disk_write(s_pdrv, (BYTE*)buffer, lba, 1); 125 | } 126 | log_v("lba: %d, offset: %d, buffsize: %d, s_disk: %d, count: %d ", lba, offset, bufsize, s_disk_block_size, 1); 127 | 128 | return bufsize; 129 | } 130 | } 131 | }; 132 | 133 | FlashUSB::FlashUSB(bool _aes) 134 | { 135 | static int pdrv = 0; 136 | 137 | MSCusb::setCallbacks(new FlashCallbacks(this, wl_handle, pdrv)); 138 | pdrv++; 139 | encrypted = _aes; 140 | } 141 | 142 | bool FlashUSB::begin(char* str) 143 | { 144 | assert(block_count); 145 | assert(block_size); 146 | 147 | return MSCusb::begin(str); 148 | } 149 | 150 | bool FlashUSB::init(const char* path, char* label) 151 | { 152 | esp_vfs_fat_mount_config_t mount_config = 153 | { 154 | .format_if_mount_failed = true, 155 | .max_files = 5, 156 | .allocation_unit_size = CONFIG_WL_SECTOR_SIZE 157 | }; 158 | 159 | esp_err_t err = esp_vfs_fat_spiflash_mount(path, label, &mount_config, &wl_handle); 160 | if(!err){ 161 | setCapacity(wl_size(wl_handle)/wl_sector_size(wl_handle), wl_sector_size(wl_handle)); 162 | sdcardReady = true; 163 | } 164 | 165 | return err == ESP_OK; 166 | } 167 | 168 | void FlashUSB::setCapacity(uint32_t count, uint32_t size) 169 | { 170 | block_count = count; 171 | block_size = size; 172 | } 173 | 174 | void FlashUSB::setCallbacks(MSCCallbacks* cb) 175 | { 176 | m_private = cb; 177 | } 178 | 179 | bool FlashUSB::isReady() 180 | { 181 | return sdcardReady; 182 | } 183 | 184 | #endif 185 | -------------------------------------------------------------------------------- /src/usb_requests.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define SET_VALUE 0x21 4 | #define GET_VALUE 0xA1 5 | 6 | // DTR/RTS control in SET_CONTROL_LINE_STATE 7 | #define ENABLE_DTR(val) (val<<0) 8 | #define ENABLE_RTS(val) (val<<1) 9 | 10 | #define SET_LINE_CODING 0x20 11 | #define GET_LINE_CODING 0x21 12 | #define SET_CONTROL_LINE_STATE 0x22 13 | // #define SERIAL_STATE 0x20 14 | 15 | #define MSC_SCSI_REQ_INIT_RESET(setup_pkt_ptr, intf_num) ({ \ 16 | (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ 17 | (setup_pkt_ptr)->bRequest = 0xFF; \ 18 | (setup_pkt_ptr)->wValue = 0; \ 19 | (setup_pkt_ptr)->wIndex = (intf_num); \ 20 | (setup_pkt_ptr)->wLength = 0; \ 21 | }) 22 | 23 | #define MSC_SCSI_REQ_MAX_LUN(setup_pkt_ptr, intf_num) ({ \ 24 | (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ 25 | (setup_pkt_ptr)->bRequest = 0xFE; \ 26 | (setup_pkt_ptr)->wValue = 0; \ 27 | (setup_pkt_ptr)->wIndex = (intf_num); \ 28 | (setup_pkt_ptr)->wLength = 1; \ 29 | }) 30 | 31 | /** 32 | * @brief 33 | * 34 | */ 35 | #define USB_CTRL_REQ_CDC_SET_LINE_CODING(ctrl_req_ptr, index, bitrate, cf, parity, bits) ({ \ 36 | (ctrl_req_ptr)->bmRequestType = SET_VALUE; \ 37 | (ctrl_req_ptr)->bRequest = SET_LINE_CODING; \ 38 | (ctrl_req_ptr)->wValue = 0; \ 39 | (ctrl_req_ptr)->wIndex = (index); \ 40 | (ctrl_req_ptr)->wLength = (7); \ 41 | }) 42 | 43 | #define USB_CTRL_REQ_CDC_GET_LINE_CODING(ctrl_req_ptr, index) ({ \ 44 | (ctrl_req_ptr)->bmRequestType = GET_VALUE; \ 45 | (ctrl_req_ptr)->bRequest = GET_LINE_CODING; \ 46 | (ctrl_req_ptr)->wValue = 0; \ 47 | (ctrl_req_ptr)->wIndex = (index); \ 48 | (ctrl_req_ptr)->wLength = (7); \ 49 | }) 50 | 51 | #define USB_CTRL_REQ_CDC_SET_CONTROL_LINE_STATE(ctrl_req_ptr, index, dtr, rts) ({ \ 52 | (ctrl_req_ptr)->bmRequestType = SET_VALUE; \ 53 | (ctrl_req_ptr)->bRequest = SET_CONTROL_LINE_STATE; \ 54 | (ctrl_req_ptr)->wValue = ENABLE_DTR(dtr) | ENABLE_RTS(rts); \ 55 | (ctrl_req_ptr)->wIndex = (index); \ 56 | (ctrl_req_ptr)->wLength = (0); \ 57 | }) 58 | 59 | 60 | /// SCSI Command Operation Code 61 | typedef enum 62 | { 63 | SCSI_CMD_TEST_UNIT_READY = 0x00, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation. 64 | SCSI_CMD_MAX_LUN = 0x01, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation. 65 | SCSI_CMD_FORMAT_UNIT = 0x04, /// 66 | SCSI_CMD_INQUIRY = 0x12, ///< The SCSI Inquiry command is used to obtain basic information from a target device. 67 | SCSI_CMD_MODE_SELECT_6 = 0x15, ///< provides a means for the application client to specify medium, logical unit, or peripheral device parameters to the device server. Device servers that implement the MODE SELECT(6) command shall also implement the MODE SENSE(6) command. Application clients should issue MODE SENSE(6) prior to each MODE SELECT(6) to determine supported mode pages, page lengths, and other parameters. 68 | SCSI_CMD_MODE_SENSE_6 = 0x1A, ///< provides a means for a device server to report parameters to an application client. It is a complementary command to the MODE SELECT(6) command. Device servers that implement the MODE SENSE(6) command shall also implement the MODE SELECT(6) command. 69 | SCSI_CMD_START_STOP_UNIT = 0x1B, 70 | SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E, 71 | SCSI_CMD_READ_CAPACITY_10 = 0x25, ///< The SCSI Read Capacity command is used to obtain data capacity information from a target device. 72 | SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device. 73 | SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed 74 | SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer. 75 | SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from the data-out buffer and write them. 76 | }scsi_cmd_type_t; 77 | 78 | typedef struct __attribute__((packed)) { 79 | uint8_t opcode; //0x28 = read(10), 0x2A=write(10) 80 | uint8_t flags; 81 | uint8_t lba_3; 82 | uint8_t lba_2; 83 | uint8_t lba_1; 84 | uint8_t lba_0; 85 | uint8_t group; 86 | uint8_t len_1; 87 | uint8_t len_0; 88 | uint8_t control; 89 | } scsi_cmd10_t; 90 | 91 | typedef struct __attribute__((packed)) { 92 | uint32_t dCBWSignature; 93 | uint32_t dCBWTag; 94 | uint32_t dCBWDataTransferLength; 95 | uint8_t bmCBWFlags; 96 | uint8_t bCBWLUN; 97 | uint8_t bCBWCBLength; 98 | scsi_cmd10_t CBWCB; 99 | uint8_t padding[6]; 100 | } msc_bulk_cbw_t; 101 | 102 | // USB Bulk Transfer Command Status Wrapper data 103 | typedef struct __attribute__((packed)) { 104 | uint32_t dCSWSignature; 105 | uint32_t dCSWTag; 106 | uint32_t dCSWDataResidue; 107 | uint8_t bCSWStatus; 108 | } msc_bulk_csw_t; 109 | 110 | typedef struct { 111 | usb_transfer_cb_t cbw_cb; 112 | usb_transfer_cb_t data_cb; 113 | usb_transfer_cb_t csw_cb; 114 | usb_transfer_cb_t capacity_cb; 115 | usb_transfer_cb_t inquiry_cb; 116 | usb_transfer_cb_t unit_ready_cb; 117 | usb_transfer_cb_t max_luns_cb; 118 | usb_transfer_cb_t sense_cb; 119 | } msc_transfer_cb_t; 120 | 121 | typedef struct{ 122 | uint32_t dwDTERate; 123 | uint8_t bCharFormat; 124 | uint8_t bParityType; 125 | uint8_t bDataBits; 126 | }line_coding_t; 127 | 128 | -------------------------------------------------------------------------------- /src/hidkeylayout.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum { 4 | UK_LAYOUT, 5 | US_LAYOUT 6 | }; 7 | 8 | #ifndef KEY_LAYOUT 9 | #define KEY_LAYOUT US_LAYOUT 10 | #endif 11 | 12 | /* Modifiers */ 13 | enum MODIFIER_KEY { 14 | KEY_CTRL = 1, 15 | KEY_SHIFT = 2, 16 | KEY_ALT = 4, 17 | }; 18 | 19 | typedef struct { 20 | unsigned char usage; 21 | unsigned char modifier; 22 | } KEYMAP; 23 | 24 | #if KEY_LAYOUT == US_LAYOUT 25 | /* US keyboard (as HID standard) */ 26 | #define KEYMAP_SIZE (152) 27 | const KEYMAP keymap[KEYMAP_SIZE] = { 28 | {0, 0}, /* NUL */ 29 | {0, 0}, /* SOH */ 30 | {0, 0}, /* STX */ 31 | {0, 0}, /* ETX */ 32 | {0, 0}, /* EOT */ 33 | {0, 0}, /* ENQ */ 34 | {0, 0}, /* ACK */ 35 | {0, 0}, /* BEL */ 36 | {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ 37 | {0x2b, 0}, /* TAB */ /* Keyboard Tab */ 38 | {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ 39 | {0, 0}, /* VT */ 40 | {0, 0}, /* FF */ 41 | {0, 0}, /* CR */ 42 | {0, 0}, /* SO */ 43 | {0, 0}, /* SI */ 44 | {0, 0}, /* DEL */ 45 | {0, 0}, /* DC1 */ 46 | {0, 0}, /* DC2 */ 47 | {0, 0}, /* DC3 */ 48 | {0, 0}, /* DC4 */ 49 | {0, 0}, /* NAK */ 50 | {0, 0}, /* SYN */ 51 | {0, 0}, /* ETB */ 52 | {0, 0}, /* CAN */ 53 | {0, 0}, /* EM */ 54 | {0, 0}, /* SUB */ 55 | {0, 0}, /* ESC */ 56 | {0, 0}, /* FS */ 57 | {0, 0}, /* GS */ 58 | {0, 0}, /* RS */ 59 | {0, 0}, /* US */ 60 | {0x2c, 0}, /* */ 61 | {0x1e, KEY_SHIFT}, /* ! */ 62 | {0x34, KEY_SHIFT}, /* " */ 63 | {0x20, KEY_SHIFT}, /* # */ 64 | {0x21, KEY_SHIFT}, /* $ */ 65 | {0x22, KEY_SHIFT}, /* % */ 66 | {0x24, KEY_SHIFT}, /* & */ 67 | {0x34, 0}, /* ' */ 68 | {0x26, KEY_SHIFT}, /* ( */ 69 | {0x27, KEY_SHIFT}, /* ) */ 70 | {0x25, KEY_SHIFT}, /* * */ 71 | {0x2e, KEY_SHIFT}, /* + */ 72 | {0x36, 0}, /* , */ 73 | {0x2d, 0}, /* - */ 74 | {0x37, 0}, /* . */ 75 | {0x38, 0}, /* / */ 76 | {0x27, 0}, /* 0 */ 77 | {0x1e, 0}, /* 1 */ 78 | {0x1f, 0}, /* 2 */ 79 | {0x20, 0}, /* 3 */ 80 | {0x21, 0}, /* 4 */ 81 | {0x22, 0}, /* 5 */ 82 | {0x23, 0}, /* 6 */ 83 | {0x24, 0}, /* 7 */ 84 | {0x25, 0}, /* 8 */ 85 | {0x26, 0}, /* 9 */ 86 | {0x33, KEY_SHIFT}, /* : */ 87 | {0x33, 0}, /* ; */ 88 | {0x36, KEY_SHIFT}, /* < */ 89 | {0x2e, 0}, /* = */ 90 | {0x37, KEY_SHIFT}, /* > */ 91 | {0x38, KEY_SHIFT}, /* ? */ 92 | {0x1f, KEY_SHIFT}, /* @ */ 93 | {0x04, KEY_SHIFT}, /* A */ 94 | {0x05, KEY_SHIFT}, /* B */ 95 | {0x06, KEY_SHIFT}, /* C */ 96 | {0x07, KEY_SHIFT}, /* D */ 97 | {0x08, KEY_SHIFT}, /* E */ 98 | {0x09, KEY_SHIFT}, /* F */ 99 | {0x0a, KEY_SHIFT}, /* G */ 100 | {0x0b, KEY_SHIFT}, /* H */ 101 | {0x0c, KEY_SHIFT}, /* I */ 102 | {0x0d, KEY_SHIFT}, /* J */ 103 | {0x0e, KEY_SHIFT}, /* K */ 104 | {0x0f, KEY_SHIFT}, /* L */ 105 | {0x10, KEY_SHIFT}, /* M */ 106 | {0x11, KEY_SHIFT}, /* N */ 107 | {0x12, KEY_SHIFT}, /* O */ 108 | {0x13, KEY_SHIFT}, /* P */ 109 | {0x14, KEY_SHIFT}, /* Q */ 110 | {0x15, KEY_SHIFT}, /* R */ 111 | {0x16, KEY_SHIFT}, /* S */ 112 | {0x17, KEY_SHIFT}, /* T */ 113 | {0x18, KEY_SHIFT}, /* U */ 114 | {0x19, KEY_SHIFT}, /* V */ 115 | {0x1a, KEY_SHIFT}, /* W */ 116 | {0x1b, KEY_SHIFT}, /* X */ 117 | {0x1c, KEY_SHIFT}, /* Y */ 118 | {0x1d, KEY_SHIFT}, /* Z */ 119 | {0x2f, 0}, /* [ */ 120 | {0x31, 0}, /* \ */ 121 | {0x30, 0}, /* ] */ 122 | {0x23, KEY_SHIFT}, /* ^ */ 123 | {0x2d, KEY_SHIFT}, /* _ */ 124 | {0x35, 0}, /* ` */ 125 | {0x04, 0}, /* a */ 126 | {0x05, 0}, /* b */ 127 | {0x06, 0}, /* c */ 128 | {0x07, 0}, /* d */ 129 | {0x08, 0}, /* e */ 130 | {0x09, 0}, /* f */ 131 | {0x0a, 0}, /* g */ 132 | {0x0b, 0}, /* h */ 133 | {0x0c, 0}, /* i */ 134 | {0x0d, 0}, /* j */ 135 | {0x0e, 0}, /* k */ 136 | {0x0f, 0}, /* l */ 137 | {0x10, 0}, /* m */ 138 | {0x11, 0}, /* n */ 139 | {0x12, 0}, /* o */ 140 | {0x13, 0}, /* p */ 141 | {0x14, 0}, /* q */ 142 | {0x15, 0}, /* r */ 143 | {0x16, 0}, /* s */ 144 | {0x17, 0}, /* t */ 145 | {0x18, 0}, /* u */ 146 | {0x19, 0}, /* v */ 147 | {0x1a, 0}, /* w */ 148 | {0x1b, 0}, /* x */ 149 | {0x1c, 0}, /* y */ 150 | {0x1d, 0}, /* z */ 151 | {0x2f, KEY_SHIFT}, /* { */ 152 | {0x31, KEY_SHIFT}, /* | */ 153 | {0x30, KEY_SHIFT}, /* } */ 154 | {0x35, KEY_SHIFT}, /* ~ */ 155 | {0,0}, /* DEL */ 156 | 157 | {0x3a, 0}, /* F1 */ 158 | {0x3b, 0}, /* F2 */ 159 | {0x3c, 0}, /* F3 */ 160 | {0x3d, 0}, /* F4 */ 161 | {0x3e, 0}, /* F5 */ 162 | {0x3f, 0}, /* F6 */ 163 | {0x40, 0}, /* F7 */ 164 | {0x41, 0}, /* F8 */ 165 | {0x42, 0}, /* F9 */ 166 | {0x43, 0}, /* F10 */ 167 | {0x44, 0}, /* F11 */ 168 | {0x45, 0}, /* F12 */ 169 | 170 | {0x46, 0}, /* PRINT_SCREEN */ 171 | {0x47, 0}, /* SCROLL_LOCK */ 172 | {0x39, 0}, /* CAPS_LOCK */ 173 | {0x53, 0}, /* NUM_LOCK */ 174 | {0x49, 0}, /* INSERT */ 175 | {0x4a, 0}, /* HOME */ 176 | {0x4b, 0}, /* PAGE_UP */ 177 | {0x4e, 0}, /* PAGE_DOWN */ 178 | 179 | {0x4f, 0}, /* RIGHT_ARROW */ 180 | {0x50, 0}, /* LEFT_ARROW */ 181 | {0x51, 0}, /* DOWN_ARROW */ 182 | {0x52, 0}, /* UP_ARROW */ 183 | }; 184 | #elif KEY_LAYOUT == UK_LAYOUT 185 | #define KEYMAP_SIZE (152) 186 | #else 187 | #warning "no layout selected" 188 | #endif 189 | -------------------------------------------------------------------------------- /src/usb_descriptors.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "esptinyusb.h" 16 | #include "usb_descriptors.h" 17 | #include "esp_log.h" 18 | 19 | #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 20 | #if CONFIG_TINYUSB_ENABLED 21 | 22 | #define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(DFU_RT, 5)) 23 | 24 | uint8_t* EspTinyUSB::descriptor_config = NULL; 25 | uint8_t* EspTinyUSB::descriptor_config_if = NULL; 26 | 27 | 28 | uint8_t* EspTinyUSB::getConfigurationDescriptor() 29 | { 30 | int CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN + 31 | (int)enableCDC * TUD_CDC_DESC_LEN + (int)enableMSC * TUD_MSC_DESC_LEN + 32 | (int)enableHID * EspTinyUSB::hid_report_desc_len + (int)enableVENDOR * TUD_VENDOR_DESC_LEN + 33 | (int)enableMIDI * TUD_MIDI_DESC_LEN + (int)enableDFU * TUD_DFU_RT_DESC_LEN; 34 | 35 | 36 | // interface count, string index, total length, attribute, power in mA 37 | uint8_t dcd[TUD_CONFIG_DESC_LEN] = {TUD_CONFIG_DESCRIPTOR(1, count, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 500)}; 38 | memcpy(&desc_configuration[0], dcd, sizeof(dcd)); 39 | 40 | if(EspTinyUSB::descriptor_config_if != NULL) { 41 | free(EspTinyUSB::descriptor_config_if); 42 | } 43 | EspTinyUSB::descriptor_config_if = (uint8_t*)calloc(1, total); 44 | memcpy(EspTinyUSB::descriptor_config_if, desc_configuration, total); 45 | log_d("descriptor length: %d\n", total); 46 | 47 | return EspTinyUSB::descriptor_config_if; 48 | } 49 | 50 | void EspTinyUSB::setDeviceDescriptorStrings() 51 | { 52 | descriptor_str_config[0] = strings.langId; // 0: supported language is English (0x0409) 53 | descriptor_str_config[1] = strings.manufacturer; // 1: Manufacturer 54 | descriptor_str_config[2] = strings.product; // 2: Product 55 | descriptor_str_config[3] = strings.serial; // 3: Serials, should use chip ID 56 | 57 | descriptor_str_config[4] = strings.cdc; // 4: CDC Interface 58 | descriptor_str_config[5] = strings.msc; // 5: MSC Interface 59 | descriptor_str_config[6] = strings.hid; // 6: HIDs 60 | descriptor_str_config[7] = strings.vendor; // 7: Vendor 61 | descriptor_str_config[8] = strings.midi; // 8: MIDI 62 | descriptor_str_config[9] = strings.dfu; // 9: DFU 63 | } 64 | 65 | /** 66 | * Device descriptor 67 | */ 68 | tusb_desc_device_t EspTinyUSB::getDeviceDescriptor() 69 | { 70 | /**** Kconfig driven Descriptor ****/ 71 | tusb_desc_device_t _descriptor_config = { 72 | .bLength = sizeof(_descriptor_config), 73 | .bDescriptorType = TUSB_DESC_DEVICE, 74 | .bcdUSB = _bcdUSB, 75 | 76 | .bDeviceClass = 0x00, 77 | .bDeviceSubClass = 0x00, 78 | .bDeviceProtocol = 0x00, 79 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 80 | .idVendor = _VID, 81 | .idProduct = _PID, 82 | 83 | .bcdDevice = _revision, 84 | 85 | .iManufacturer = 0x01, 86 | .iProduct = 0x02, 87 | .iSerialNumber = 0x03, 88 | 89 | .bNumConfigurations = 0x01, 90 | }; 91 | 92 | 93 | if (enableCDC) { 94 | // Use Interface Association Descriptor (IAD) for CDC 95 | // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) 96 | _descriptor_config.bDeviceClass = TUSB_CLASS_MISC; 97 | _descriptor_config.bDeviceSubClass = MISC_SUBCLASS_COMMON; 98 | _descriptor_config.bDeviceProtocol = MISC_PROTOCOL_IAD; 99 | } 100 | 101 | 102 | if(descriptor_config != NULL) { 103 | free(descriptor_config); 104 | } 105 | descriptor_config = (uint8_t *)calloc(1, sizeof(_descriptor_config)); 106 | 107 | memcpy(descriptor_config, &_descriptor_config, sizeof(_descriptor_config)); 108 | 109 | return _descriptor_config; 110 | } 111 | 112 | // ============================================================================= 113 | // Driver functions 114 | // ============================================================================= 115 | /** 116 | * @brief Invoked when received GET DEVICE DESCRIPTOR. 117 | * Application returns pointer to descriptor 118 | * 119 | * @return uint8_t const* 120 | */ 121 | uint8_t const *tud_descriptor_device_cb(void) 122 | { 123 | return (uint8_t const*)EspTinyUSB::descriptor_config; 124 | } 125 | 126 | /** 127 | * @brief Invoked when received GET CONFIGURATION DESCRIPTOR. 128 | * Descriptor contents must exist long enough for transfer to complete 129 | * 130 | * @param index 131 | * @return uint8_t const* Application return pointer to descriptor 132 | */ 133 | uint8_t const *tud_descriptor_configuration_cb(uint8_t index) 134 | { 135 | (void)index; // for multiple configurations 136 | return (uint8_t const*)EspTinyUSB::descriptor_config_if; 137 | } 138 | 139 | static uint16_t _desc_str[32]; 140 | 141 | /** 142 | * @brief Invoked when received GET STRING DESCRIPTOR request. 143 | * Application returns pointer to descriptor, whose contents must exist long 144 | * enough for transfer to complete 145 | * 146 | * @param index 147 | * @return uint16_t const* 148 | */ 149 | uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) 150 | { 151 | uint8_t chr_count = 0; 152 | if (index == 0) 153 | { 154 | memcpy(&_desc_str[1], descriptor_str_config[0], 2); 155 | chr_count = 1; 156 | } 157 | else 158 | { 159 | // Convert ASCII string into UTF-16 160 | if (index >= sizeof(descriptor_str_config) / 161 | sizeof(descriptor_str_config[0])) 162 | { 163 | return NULL; 164 | } 165 | const char *str = descriptor_str_config[index]; 166 | // Cap at max char 167 | chr_count = strlen(str); 168 | if (chr_count > 31) 169 | { 170 | chr_count = 31; 171 | } 172 | for (uint8_t i = 0; i < chr_count; i++) 173 | { 174 | _desc_str[1 + i] = str[i]; 175 | } 176 | } 177 | 178 | // first byte is len, second byte is string type 179 | _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); 180 | 181 | return _desc_str; 182 | } 183 | 184 | #endif 185 | #endif 186 | -------------------------------------------------------------------------------- /examples/host/remote_pendrive/remote_pendrive.ino: -------------------------------------------------------------------------------- 1 | /* 2 | esp32 S2 USB host MSC example with access from website 3 | 4 | author: chegewara 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "usb/usb_host.h" 21 | #include "usb_host.hpp" 22 | #include "usb_acm.hpp" 23 | #include "usb_msc.hpp" 24 | 25 | #define SERVE_WEB_FROM_PENDRIVE 1 26 | 27 | WebServer server(80); 28 | USBhost host; 29 | USBmscDevice *device; 30 | 31 | #define EXAMPLE_ESP_WIFI_SSID "pendrive" 32 | #define EXAMPLE_ESP_WIFI_PASS "" 33 | #define MOUNT_POINT "/files" 34 | 35 | String ssidSTA; 36 | String passSTA; 37 | String ssidAP; 38 | String passAP; 39 | 40 | bool is_mount = false; 41 | 42 | static void inquiry_cb(usb_transfer_t *transfer) 43 | { 44 | printf("inquiry_cb\n"); 45 | device->mount(MOUNT_POINT); 46 | is_mount = true; 47 | } 48 | 49 | void client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg) 50 | { 51 | if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV) 52 | { 53 | host.open(event_msg); 54 | usb_device_info_t info = host.getDeviceInfo(); 55 | ESP_LOGI("", "device speed: %s, device address: %d, max ep_ctrl size: %d, config: %d", info.speed ? "USB_SPEED_FULL" : "USB_SPEED_LOW", info.dev_addr, info.bMaxPacketSize0, info.bConfigurationValue); 56 | const usb_config_desc_t *config_desc = host.getConfigurationDescriptor(); 57 | int offset = 0; 58 | 59 | for (size_t n = 0; n < config_desc->bNumInterfaces; n++) 60 | { 61 | const usb_intf_desc_t *intf = usb_parse_interface_descriptor(config_desc, n, 0, &offset); 62 | 63 | if (intf->bInterfaceClass == 0x08) // MSC 64 | { 65 | msc_transfer_cb_t cb = { 66 | .inquiry_cb = inquiry_cb, 67 | }; 68 | device = new USBmscDevice(config_desc, &host); 69 | ((USBmscDevice *)device)->registerCallbacks(cb); 70 | n = config_desc->bNumInterfaces; 71 | } 72 | else if (intf->bInterfaceClass == 0x02 || intf->bInterfaceClass == 0x0a) // CDC ACM 73 | { 74 | n = config_desc->bNumInterfaces; 75 | } 76 | 77 | if (device) 78 | device->init(); 79 | } 80 | } 81 | else 82 | { 83 | ESP_LOGW("", "DEVICE gone event"); 84 | } 85 | } 86 | 87 | void setup(void) 88 | { 89 | Serial.begin(115200); 90 | host.registerClientCb(client_event_callback); 91 | host.init(); 92 | 93 | WiFi.mode(WIFI_AP_STA); 94 | WiFi.softAP(EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); 95 | IPAddress myIP = WiFi.softAPIP(); 96 | Serial.print("AP IP address: "); 97 | Serial.println(myIP); 98 | 99 | if (MDNS.begin("esp32")) 100 | { 101 | Serial.println("MDNS responder started"); 102 | } 103 | 104 | if (ssidSTA.length() > 1) 105 | { 106 | WiFi.begin(ssidSTA.c_str(), passSTA.c_str()); 107 | } 108 | 109 | #ifndef SERVE_WEB_FROM_PENDRIVE 110 | server.on("/", handleRoot); 111 | server.on("/app.js", handleJS); 112 | server.on("/app.css", handleCSS); 113 | #else 114 | server.on("/", handleRoot2); 115 | #endif 116 | server.on("/station", handleSTA); 117 | server.on("/wifi_ap", handleAP); 118 | server.on("/mkdir", handleMkdir); 119 | server.on("/delete", handleDelete); 120 | 121 | server.onNotFound([]() 122 | { 123 | if (!handleFileRead(server.uri())) 124 | { 125 | handleNotFound(); 126 | } 127 | }); 128 | 129 | server.begin(); 130 | Serial.println("HTTP server started"); 131 | 132 | delay(1000); // let already inserted pendrive to mount and get statistics 133 | if (is_mount) 134 | { 135 | read_test(0); 136 | 137 | uint64_t used_space; 138 | uint64_t max_space; 139 | getFreeSpace(&used_space, &max_space); 140 | 141 | log_i("storage used: %lld/%lld", used_space, max_space); 142 | } 143 | } 144 | 145 | void loop(void) 146 | { 147 | server.handleClient(); 148 | delay(2); 149 | } 150 | 151 | //--------------------- optional test functions ------------------------// 152 | void read_test(int t) 153 | { 154 | char line[500]; 155 | FILE *f; 156 | char *mount; 157 | if (t) 158 | { 159 | f = fopen(MOUNT_POINT "/README.txt", "r"); 160 | mount = MOUNT_POINT; 161 | } 162 | else 163 | { 164 | f = fopen(MOUNT_POINT "/README2.txt", "r"); 165 | mount = MOUNT_POINT; 166 | } 167 | 168 | if (f == NULL) 169 | { 170 | ESP_LOGE("TAG", "Failed to open file"); 171 | } 172 | else 173 | { 174 | ESP_LOGI("READ file", "%s", MOUNT_POINT "/README.txt"); 175 | char *pos; 176 | fgets(line, sizeof(line), f); 177 | // strip newline 178 | pos = strchr(line, '\n'); 179 | do 180 | { 181 | if (pos) 182 | { 183 | *pos = '\0'; 184 | } 185 | ESP_LOGI("", "%s", line); 186 | fgets(line, sizeof(line), f); 187 | pos = strchr(line, '\n'); 188 | } while (pos); 189 | fclose(f); 190 | } 191 | 192 | char *_dirpath = mount; 193 | char *dirpath = MOUNT_POINT "/"; 194 | DIR *dir = opendir(_dirpath); 195 | 196 | // /* Retrieve the base path of file storage to construct the full path */ 197 | // strlcpy(entrypath, dirpath, sizeof(entrypath)); 198 | char entrypath[256]; 199 | char entrysize[32]; 200 | const char *entrytype; 201 | 202 | struct dirent *entry; 203 | struct stat entry_stat; 204 | const size_t dirpath_len = strlen(dirpath); 205 | strlcpy(entrypath, dirpath, sizeof(entrypath)); 206 | 207 | if (!dir) 208 | { 209 | ESP_LOGE("TAG", "Failed to stat dir : %s", _dirpath); 210 | } 211 | else 212 | { 213 | entry = readdir(dir); 214 | while (entry != NULL) 215 | { 216 | entrytype = (entry->d_type == DT_DIR ? "directory" : "file"); 217 | 218 | strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len); 219 | 220 | int st = 0; 221 | st = stat(entrypath, &entry_stat); 222 | if (st == -1) 223 | { 224 | ESP_LOGE(TAG, "Failed to stat %s : %s", entrytype, entrypath); 225 | entry = readdir(dir); 226 | continue; 227 | } 228 | sprintf(entrysize, "%ld", entry_stat.st_size); 229 | ESP_LOGI(TAG, "Found %s : %s (%s bytes)", entrytype, entrypath, entrysize); 230 | entry = readdir(dir); 231 | } 232 | closedir(dir); 233 | } 234 | } 235 | 236 | static void getFreeSpace(uint64_t *used_space, uint64_t *max_space) 237 | { 238 | 239 | FATFS *fs; 240 | DWORD c; 241 | if (f_getfree(MOUNT_POINT, &c, &fs) == FR_OK) 242 | { 243 | *used_space = 244 | ((uint64_t)fs->csize * (fs->n_fatent - 2 - fs->free_clst)) * fs->ssize; 245 | *max_space = ((uint64_t)fs->csize * (fs->n_fatent - 2)) * fs->ssize; 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/host/acm/usb_acm.cpp: -------------------------------------------------------------------------------- 1 | #include "esp_log.h" 2 | #include "string.h" 3 | 4 | #include "usb_acm.hpp" 5 | 6 | void IRAM_ATTR usb_ctrl_cb(usb_transfer_t *transfer) 7 | { 8 | USBacmDevice *dev = (USBacmDevice *)transfer->context; 9 | 10 | if (transfer->data_buffer[0] == SET_VALUE && transfer->data_buffer[1] == SET_LINE_CODING) // set line coding 11 | { 12 | dev->_callback(CDC_CTRL_SET_LINE_CODING, transfer); 13 | } 14 | else if (transfer->data_buffer[0] == GET_VALUE && transfer->data_buffer[1] == GET_LINE_CODING) // get line coding 15 | { 16 | dev->_callback(CDC_CTRL_GET_LINE_CODING, transfer); 17 | } 18 | else if (transfer->data_buffer[0] == SET_VALUE && transfer->data_buffer[1] == SET_CONTROL_LINE_STATE) // set line coding 19 | { 20 | dev->_callback(CDC_CTRL_SET_CONTROL_LINE_STATE, transfer); 21 | } 22 | } 23 | 24 | void IRAM_ATTR usb_read_cb(usb_transfer_t *transfer) 25 | { 26 | USBacmDevice *dev = (USBacmDevice *)transfer->context; 27 | dev->_callback(CDC_DATA_IN, transfer); 28 | } 29 | 30 | void IRAM_ATTR usb_write_cb(usb_transfer_t *transfer) 31 | { 32 | USBacmDevice *dev = (USBacmDevice *)transfer->context; 33 | dev->_callback(CDC_DATA_OUT, transfer); 34 | } 35 | 36 | USBacmDevice::USBacmDevice(const usb_config_desc_t *config_desc, USBhost *host) 37 | { 38 | _host = host; 39 | int offset = 0; 40 | for (size_t n = 0; n < config_desc->bNumInterfaces; n++) 41 | { 42 | const usb_intf_desc_t *intf = usb_parse_interface_descriptor(config_desc, n, 0, &offset); 43 | const usb_ep_desc_t *ep = nullptr; 44 | 45 | if (intf->bInterfaceClass == 0x02) 46 | { 47 | if (intf->bNumEndpoints != 1) 48 | return; 49 | int _offset = 0; 50 | ep = usb_parse_endpoint_descriptor_by_index(intf, 0, config_desc->wTotalLength, &_offset); 51 | ep_int = ep; 52 | ESP_LOGI("", "EP CDC comm."); 53 | 54 | printf("EP num: %d/%d, len: %d, ", 1, intf->bNumEndpoints, config_desc->wTotalLength); 55 | if (ep) 56 | printf("address: 0x%02x, EP max size: %d, dir: %s\n", ep->bEndpointAddress, ep->wMaxPacketSize, (ep->bEndpointAddress & 0x80) ? "IN" : "OUT"); 57 | else 58 | ESP_LOGW("", "error to parse endpoint by index; EP num: %d/%d, len: %d", 1, intf->bNumEndpoints, config_desc->wTotalLength); 59 | 60 | esp_err_t err = usb_host_interface_claim(_host->clientHandle(), _host->deviceHandle(), n, 0); 61 | ESP_LOGI("", "interface claim status: %d", err); 62 | itf_num = 0; 63 | } 64 | else if (intf->bInterfaceClass == 0x0a) 65 | { 66 | if (intf->bNumEndpoints != 2) 67 | return; 68 | ESP_LOGI("", "EP CDC data."); 69 | for (size_t i = 0; i < intf->bNumEndpoints; i++) 70 | { 71 | int _offset = 0; 72 | ep = usb_parse_endpoint_descriptor_by_index(intf, i, config_desc->wTotalLength, &_offset); 73 | if (ep->bEndpointAddress & 0x80) 74 | { 75 | ep_in = ep; 76 | } 77 | else 78 | { 79 | ep_out = ep; 80 | } 81 | 82 | printf("EP num: %d/%d, len: %d, ", i + 1, intf->bNumEndpoints, config_desc->wTotalLength); 83 | if (ep) 84 | printf("address: 0x%02x, EP max size: %d, dir: %s\n", ep->bEndpointAddress, ep->wMaxPacketSize, (ep->bEndpointAddress & 0x80) ? "IN" : "OUT"); 85 | else 86 | ESP_LOGW("", "error to parse endpoint by index; EP num: %d/%d, len: %d", i + 1, intf->bNumEndpoints, config_desc->wTotalLength); 87 | } 88 | esp_err_t err = usb_host_interface_claim(_host->clientHandle(), _host->deviceHandle(), n, 0); 89 | ESP_LOGI("", "interface claim status: %d", err); 90 | } 91 | } 92 | } 93 | 94 | USBacmDevice::~USBacmDevice() 95 | { 96 | } 97 | 98 | bool USBacmDevice::init() 99 | { 100 | usb_device_info_t info = _host->getDeviceInfo(); 101 | 102 | esp_err_t err = usb_host_transfer_alloc(64, 0, &xfer_write); 103 | xfer_write->device_handle = _host->deviceHandle(); 104 | xfer_write->context = this; 105 | xfer_write->callback = usb_write_cb; 106 | xfer_write->bEndpointAddress = ep_out->bEndpointAddress; 107 | 108 | err = usb_host_transfer_alloc(64, 0, &xfer_read); 109 | xfer_read->device_handle = _host->deviceHandle(); 110 | xfer_read->context = this; 111 | xfer_read->callback = usb_read_cb; 112 | xfer_read->bEndpointAddress = ep_in->bEndpointAddress; 113 | 114 | err = usb_host_transfer_alloc(info.bMaxPacketSize0, 0, &xfer_ctrl); 115 | xfer_ctrl->device_handle = _host->deviceHandle(); 116 | xfer_ctrl->context = this; 117 | xfer_ctrl->callback = usb_ctrl_cb; 118 | xfer_ctrl->bEndpointAddress = 0; 119 | 120 | return true; 121 | } 122 | 123 | void USBacmDevice::setControlLine(bool dtr, bool rts) 124 | { 125 | USB_CTRL_REQ_CDC_SET_CONTROL_LINE_STATE((usb_setup_packet_t *)xfer_ctrl->data_buffer, 0, dtr, rts); 126 | xfer_ctrl->num_bytes = sizeof(usb_setup_packet_t) + ((usb_setup_packet_t *)xfer_ctrl->data_buffer)->wLength; 127 | esp_err_t err = usb_host_transfer_submit_control(_host->clientHandle(), xfer_ctrl); 128 | } 129 | 130 | void USBacmDevice::setLineCoding(uint32_t bitrate, uint8_t cf, uint8_t parity, uint8_t bits) 131 | { 132 | line_coding_t data; 133 | data.dwDTERate = bitrate; 134 | data.bCharFormat = cf; 135 | data.bParityType = parity; 136 | data.bDataBits = bits; 137 | USB_CTRL_REQ_CDC_SET_LINE_CODING((usb_setup_packet_t *)xfer_ctrl->data_buffer, 0, bitrate, cf, parity, bits); 138 | memcpy(xfer_ctrl->data_buffer + sizeof(usb_setup_packet_t), &data, sizeof(line_coding_t)); 139 | xfer_ctrl->num_bytes = sizeof(usb_setup_packet_t) + 7; 140 | esp_err_t err = usb_host_transfer_submit_control(_host->clientHandle(), xfer_ctrl); 141 | } 142 | 143 | void USBacmDevice::getLineCoding() 144 | { 145 | USB_CTRL_REQ_CDC_GET_LINE_CODING((usb_setup_packet_t *)xfer_ctrl->data_buffer, 0); 146 | xfer_ctrl->num_bytes = sizeof(usb_setup_packet_t) + ((usb_setup_packet_t *)xfer_ctrl->data_buffer)->wLength; 147 | esp_err_t err = usb_host_transfer_submit_control(_host->clientHandle(), xfer_ctrl); 148 | } 149 | 150 | void USBacmDevice::INDATA() 151 | { 152 | if (!connected) 153 | return; 154 | xfer_read->num_bytes = 64; 155 | 156 | esp_err_t err = usb_host_transfer_submit(xfer_read); 157 | if (err) 158 | { 159 | ESP_LOGW("", "test read data: 0x%02x", err); 160 | } 161 | } 162 | 163 | void USBacmDevice::OUTDATA(uint8_t *data, size_t len) 164 | { 165 | if (!connected) 166 | return; 167 | if (!len) 168 | return; 169 | xfer_write->num_bytes = len; 170 | memcpy(xfer_write->data_buffer, data, len); 171 | 172 | esp_err_t err = usb_host_transfer_submit(xfer_write); 173 | if (err) 174 | { 175 | ESP_LOGW("", "test write data: 0x%02x", err); 176 | } 177 | } 178 | 179 | bool USBacmDevice::isConnected() 180 | { 181 | return connected; 182 | } 183 | 184 | void USBacmDevice::onEvent(cdc_event_cb_t _cb) 185 | { 186 | event_cb = _cb; 187 | } 188 | 189 | void USBacmDevice::_callback(int event, usb_transfer_t *transfer) 190 | { 191 | switch (event) 192 | { 193 | case CDC_DATA_IN: 194 | if (event_cb) 195 | event_cb(event, transfer->data_buffer, transfer->actual_num_bytes); 196 | break; 197 | case CDC_DATA_OUT: 198 | if (event_cb) 199 | event_cb(event, transfer->data_buffer, transfer->actual_num_bytes); 200 | break; 201 | 202 | default: 203 | if (event_cb) 204 | event_cb(event, NULL, transfer->actual_num_bytes); 205 | connected = true; 206 | break; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /examples/host/remote_pendrive/embedded/app.css: -------------------------------------------------------------------------------- 1 | .main-content { 2 | width: 1440px; 3 | margin: auto; 4 | font-size: 14px; 5 | } 6 | 7 | .connect-container { 8 | margin: 20px 0; 9 | } 10 | 11 | .container { 12 | display: flex; 13 | } 14 | 15 | .sender, .receiver { 16 | flex-grow: 0.5; 17 | flex-shrink: 1; 18 | } 19 | 20 | .sender { 21 | margin-right: 8px; 22 | } 23 | 24 | .receiver { 25 | margin-right: 8px; 26 | } 27 | 28 | .lines-header { 29 | height: 30px; 30 | width: 100%; 31 | box-sizing: border-box; 32 | background-color: #444; 33 | line-height: 30px; 34 | color: white; 35 | padding-left: 10px; 36 | } 37 | 38 | .lines-body { 39 | width: 100%; 40 | background-color: #222; 41 | min-height: 300px; 42 | padding: 10px 0 20px 0; 43 | } 44 | 45 | .line, .command-line { 46 | box-sizing: border-box; 47 | width: 100%; 48 | color: #f1f1f1; 49 | background-color: #222; 50 | outline: none; 51 | border: none; 52 | padding: 5px 10px; 53 | font-size: 14px; 54 | } 55 | 56 | .line:hover { 57 | background-color: #444; 58 | } 59 | 60 | .button::before { 61 | -webkit-border-radius: 3px; 62 | -moz-border-radius: 3px; 63 | -webkit-box-shadow: #959595 0 2px 5px; 64 | -moz-box-shadow: #959595 0 2px 5px; 65 | border-radius: 3px; 66 | box-shadow: #959595 0 2px 5px; 67 | content: ''; 68 | display: block; 69 | left: 0; 70 | padding: 2px 0 0; 71 | position: absolute; 72 | top: 0; 73 | } 74 | 75 | .button:active::before { padding: 1px 0 0; } 76 | 77 | .button.black { 78 | background: #656565; 79 | background: -webkit-gradient(linear, 0 0, 0 bottom, from(#656565), to(#444)); 80 | background: -moz-linear-gradient(#656565, #444); 81 | background: linear-gradient(#656565, #444); 82 | border: solid 1px #535353; 83 | border-bottom: solid 3px #414141; 84 | box-shadow: inset 0 0 0 1px #939393; 85 | color: #fff; 86 | text-shadow: 0 1px 0 #2f2f2f; 87 | padding: 8px 16px; 88 | outline: none; 89 | } 90 | 91 | .button.black:hover { 92 | background: #4c4c4c; 93 | background: -webkit-gradient(linear, 0 0, 0 bottom, from(#4c4c4c), to(#565656)); 94 | background: -moz-linear-gradient(#4c4c4c, #565656); 95 | background: linear-gradient(#4c4c4c, #565656); 96 | border: solid 1px #464646; 97 | border-bottom: solid 3px #414141; 98 | box-shadow: inset 0 0 0 1px #818181; 99 | } 100 | 101 | .button.black:active { 102 | background: #474747; 103 | background: -webkit-gradient(linear, 0 0, 0 bottom, from(#474747), to(#444)); 104 | background: -moz-linear-gradient(#474747, #444); 105 | background: linear-gradient(#474747, #444); 106 | border: solid 1px #2f2f2f; 107 | box-shadow: inset 0 10px 15px 0 #3e3e3e; 108 | } 109 | 110 | 111 | .filestree { 112 | border: 1px solid black; 113 | width: 100%; 114 | } 115 | 116 | .filestree1 { 117 | border: 1px solid black; 118 | width: 100%; 119 | text-align: end; 120 | } 121 | 122 | th { 123 | border: 1px solid black; 124 | width: 100%; 125 | } 126 | 127 | body { 128 | background-color: #125597; 129 | /* background-image: url('https://cdn.shopify.com/s/files/1/0770/0935/files/nasa-53884_1512x.jpg'); */ 130 | text-decoration-color: white; 131 | background-size: cover; 132 | font-family: 'Helvetica Neue', Arial, sans-serif; 133 | } 134 | 135 | * { 136 | box-sizing: border-box; 137 | } 138 | 139 | .card { 140 | background: linear-gradient(-45deg, #5555FF, #9787FF); 141 | box-shadow: 0 0 6px rgba(0, 0, 0, 0.1); 142 | position: absolute; 143 | top: 50%; 144 | left: 50%; 145 | transform: translate(-50%, -50%); 146 | width: 300px; 147 | height: 375px; 148 | border-radius: 10px; 149 | overflow: hidden; 150 | } 151 | 152 | /* hide limit values on X axis */ 153 | .card #canvas { 154 | margin-left: -30px; 155 | margin-right: -30px; 156 | width: 360px!important; 157 | } 158 | 159 | .card .about { 160 | height: 185px; 161 | padding: 20px; 162 | box-sizing: border-box; 163 | } 164 | 165 | .card .about h3, 166 | .card .about .lead { 167 | margin-top: 0; 168 | margin-bottom: 0; 169 | font-weight: 400; 170 | } 171 | 172 | .card .about h3 { 173 | font-size: 24px; 174 | color: #fff; 175 | } 176 | 177 | .card .about .lead { 178 | color: #eee; 179 | } 180 | 181 | .card .info { 182 | float: left; 183 | padding: 10px 30px 10px 0; 184 | } 185 | 186 | .card .info p { 187 | font-size: 11px; 188 | color: #aaa; 189 | font-weight: 300; 190 | } 191 | 192 | .legends { 193 | padding-top: 20px; 194 | overflow: hidden; 195 | } 196 | 197 | .legend { 198 | display: block; 199 | width: 8px; 200 | height: 8px; 201 | margin-top: 15px; 202 | margin-bottom: 15px; 203 | border-radius: 50%; 204 | } 205 | 206 | .legend--this { 207 | background-color: #5555FF; 208 | } 209 | 210 | .legend--prev { 211 | background-color: #FF55B8; 212 | } 213 | 214 | .axis { 215 | position: absolute; 216 | color: #fff; 217 | z-index: 1; 218 | text-transform: uppercase; 219 | display: flex; 220 | width: 100%; 221 | bottom: 0; 222 | } 223 | 224 | .axis .tick { 225 | flex: 1; 226 | position: relative; 227 | font-size: 11px; 228 | text-align: center; 229 | padding-top: 10px; 230 | padding-bottom: 10px; 231 | line-height: 20px; 232 | } 233 | 234 | .axis .tick::after { 235 | content: ''; 236 | position: absolute; 237 | display: block; 238 | right: 0; 239 | bottom: 0; 240 | width: 1px; 241 | height: 200px; 242 | background: rgba(255, 255, 255, 0.2); 243 | } 244 | 245 | .axis .tick .value { 246 | transform: translateY(-240px); 247 | opacity: 0; 248 | transition: all 0.3s; 249 | position: absolute; 250 | top: 20px; 251 | left: 0; 252 | color: #fff; 253 | border-radius: 2px; 254 | width: 100%; 255 | line-height: 20px; 256 | } 257 | 258 | .axis .tick:hover .value.value--this { 259 | transform: translateY(-160px); 260 | display: block; 261 | opacity: 0.4; 262 | } 263 | 264 | .value.value--this { 265 | color: #fff; 266 | font-weight: bold; 267 | } 268 | 269 | .day-number { 270 | display: block; 271 | } 272 | 273 | .day-name { 274 | display: block; 275 | opacity: 0.4; 276 | } 277 | 278 | /* Animated background, credits: Manuel Pinto, https://codepen.io/P1N2O/pen/pyBNzX */ 279 | .card { 280 | background: linear-gradient(-45deg, #5555FF, #9787FF, #FF55B8, #FF8787); 281 | background-size: 400% 400%; 282 | animation: bg 20s infinite; 283 | } 284 | 285 | @keyframes bg 286 | { 287 | 0% { 288 | background-position: 0% 50% 289 | } 290 | 50% { 291 | background-position: 100% 50% 292 | } 293 | 100% { 294 | background-position: 0% 50% 295 | } 296 | } 297 | 298 | 299 | 300 | /* The Modal (background) */ 301 | .modal { 302 | display: none; /* Hidden by default */ 303 | position: fixed; /* Stay in place */ 304 | z-index: 1; /* Sit on top */ 305 | left: 0; 306 | top: 0; 307 | width: 100%; /* Full width */ 308 | height: 100%; /* Full height */ 309 | overflow: auto; /* Enable scroll if needed */ 310 | background-color: rgb(0,0,0); /* Fallback color */ 311 | background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ 312 | } 313 | 314 | /* Modal Content/Box */ 315 | .modal-content { 316 | background-color: #fefefe; 317 | margin: 10% 10% auto; /* 15% from the top and centered */ 318 | padding: 20px; 319 | border: 1px solid #888; 320 | width: 80%; /* Could be more or less, depending on screen size */ 321 | } 322 | 323 | /* The Close Button */ 324 | .close { 325 | color: #aaa; 326 | float: right; 327 | font-size: 28px; 328 | font-weight: bold; 329 | } 330 | 331 | .close:hover, 332 | .close:focus { 333 | color: black; 334 | text-decoration: none; 335 | cursor: pointer; 336 | } 337 | 338 | footer { 339 | position: absolute; 340 | bottom: 0px; 341 | } -------------------------------------------------------------------------------- /examples/host/remote_pendrive/app_css.h: -------------------------------------------------------------------------------- 1 | const char* app_css = 2 | ".main-content {" 3 | " width: 1440px;" 4 | " margin: auto;" 5 | " font-size: 14px;" 6 | " }" 7 | " " 8 | " .connect-container {" 9 | " margin: 20px 0;" 10 | " }" 11 | " " 12 | " .container {" 13 | " display: flex;" 14 | " }" 15 | " " 16 | " .sender, .receiver {" 17 | " flex-grow: 0.5;" 18 | " flex-shrink: 1;" 19 | " }" 20 | " " 21 | " .sender {" 22 | " margin-right: 8px;" 23 | " }" 24 | " " 25 | " .receiver {" 26 | " margin-right: 8px;" 27 | " }" 28 | " " 29 | " .lines-header {" 30 | " height: 30px;" 31 | " width: 100%;" 32 | " box-sizing: border-box;" 33 | " background-color: #444;" 34 | " line-height: 30px;" 35 | " color: white;" 36 | " padding-left: 10px;" 37 | " }" 38 | " " 39 | " .lines-body {" 40 | " width: 100%;" 41 | " background-color: #222;" 42 | " min-height: 300px;" 43 | " padding: 10px 0 20px 0;" 44 | " }" 45 | " " 46 | " .line, .command-line {" 47 | " box-sizing: border-box;" 48 | " width: 100%;" 49 | " color: #f1f1f1;" 50 | " background-color: #222;" 51 | " outline: none;" 52 | " border: none;" 53 | " padding: 5px 10px;" 54 | " font-size: 14px;" 55 | " }" 56 | " " 57 | " .line:hover {" 58 | " background-color: #444;" 59 | " }" 60 | " " 61 | " .button::before {" 62 | " -webkit-border-radius: 3px;" 63 | " -moz-border-radius: 3px;" 64 | " -webkit-box-shadow: #959595 0 2px 5px;" 65 | " -moz-box-shadow: #959595 0 2px 5px;" 66 | " border-radius: 3px;" 67 | " box-shadow: #959595 0 2px 5px;" 68 | " content: '';" 69 | " display: block;" 70 | " left: 0;" 71 | " padding: 2px 0 0;" 72 | " position: absolute;" 73 | " top: 0;" 74 | " }" 75 | " " 76 | " .button:active::before { padding: 1px 0 0; }" 77 | " " 78 | " .button.black {" 79 | " background: #656565;" 80 | " background: -webkit-gradient(linear, 0 0, 0 bottom, from(#656565), to(#444));" 81 | " background: -moz-linear-gradient(#656565, #444);" 82 | " background: linear-gradient(#656565, #444);" 83 | " border: solid 1px #535353;" 84 | " border-bottom: solid 3px #414141;" 85 | " box-shadow: inset 0 0 0 1px #939393;" 86 | " color: #fff;" 87 | " text-shadow: 0 1px 0 #2f2f2f;" 88 | " padding: 8px 16px;" 89 | " outline: none;" 90 | " }" 91 | " " 92 | " .button.black:hover {" 93 | " background: #4c4c4c;" 94 | " background: -webkit-gradient(linear, 0 0, 0 bottom, from(#4c4c4c), to(#565656));" 95 | " background: -moz-linear-gradient(#4c4c4c, #565656);" 96 | " background: linear-gradient(#4c4c4c, #565656);" 97 | " border: solid 1px #464646;" 98 | " border-bottom: solid 3px #414141;" 99 | " box-shadow: inset 0 0 0 1px #818181;" 100 | " }" 101 | " " 102 | " .button.black:active {" 103 | " background: #474747;" 104 | " background: -webkit-gradient(linear, 0 0, 0 bottom, from(#474747), to(#444));" 105 | " background: -moz-linear-gradient(#474747, #444);" 106 | " background: linear-gradient(#474747, #444);" 107 | " border: solid 1px #2f2f2f;" 108 | " box-shadow: inset 0 10px 15px 0 #3e3e3e;" 109 | " }" 110 | " " 111 | " " 112 | " .filestree {" 113 | " border: 1px solid black;" 114 | " width: 100%;" 115 | " }" 116 | " " 117 | " .filestree1 {" 118 | " border: 1px solid black;" 119 | " width: 100%;" 120 | " text-align: end;" 121 | " }" 122 | " " 123 | " th {" 124 | " border: 1px solid black;" 125 | " width: 100%;" 126 | " }" 127 | " " 128 | "body {" 129 | " background-color: #125597;" 130 | " /* background-image: url('https://cdn.shopify.com/s/files/1/0770/0935/files/nasa-53884_1512x.jpg'); */" 131 | " text-decoration-color: white;" 132 | " background-size: cover;" 133 | " font-family: 'Helvetica Neue', Arial, sans-serif;" 134 | "}" 135 | "" 136 | "* {" 137 | " box-sizing: border-box;" 138 | "}" 139 | "" 140 | ".card {" 141 | " background: linear-gradient(-45deg, #5555FF, #9787FF);" 142 | " box-shadow: 0 0 6px rgba(0, 0, 0, 0.1); " 143 | " position: absolute;" 144 | " top: 50%;" 145 | " left: 50%;" 146 | " transform: translate(-50%, -50%);" 147 | " width: 300px;" 148 | " height: 375px;" 149 | " border-radius: 10px;" 150 | " overflow: hidden;" 151 | "}" 152 | "" 153 | "/* hide limit values on X axis */" 154 | ".card #canvas {" 155 | " margin-left: -30px;" 156 | " margin-right: -30px;" 157 | " width: 360px!important;" 158 | "}" 159 | "" 160 | ".card .about {" 161 | " height: 185px;" 162 | " padding: 20px;" 163 | " box-sizing: border-box;" 164 | "}" 165 | "" 166 | ".card .about h3," 167 | ".card .about .lead {" 168 | " margin-top: 0;" 169 | " margin-bottom: 0;" 170 | " font-weight: 400;" 171 | "}" 172 | "" 173 | ".card .about h3 {" 174 | " font-size: 24px;" 175 | " color: #fff;" 176 | "}" 177 | "" 178 | ".card .about .lead {" 179 | " color: #eee;" 180 | "}" 181 | "" 182 | ".card .info {" 183 | " float: left;" 184 | " padding: 10px 30px 10px 0;" 185 | "}" 186 | "" 187 | ".card .info p {" 188 | " font-size: 11px;" 189 | " color: #aaa;" 190 | " font-weight: 300;" 191 | "}" 192 | "" 193 | ".legends {" 194 | " padding-top: 20px;" 195 | " overflow: hidden;" 196 | "}" 197 | "" 198 | ".legend {" 199 | " display: block;" 200 | " width: 8px;" 201 | " height: 8px;" 202 | " margin-top: 15px;" 203 | " margin-bottom: 15px;" 204 | " border-radius: 50%;" 205 | "}" 206 | "" 207 | ".legend--this {" 208 | " background-color: #5555FF;" 209 | "}" 210 | "" 211 | ".legend--prev {" 212 | " background-color: #FF55B8;" 213 | "}" 214 | "" 215 | ".axis {" 216 | " position: absolute;" 217 | " color: #fff;" 218 | " z-index: 1;" 219 | " text-transform: uppercase;" 220 | " display: flex;" 221 | " width: 100%;" 222 | " bottom: 0;" 223 | "}" 224 | "" 225 | ".axis .tick {" 226 | " flex: 1;" 227 | " position: relative;" 228 | " font-size: 11px;" 229 | " text-align: center;" 230 | " padding-top: 10px;" 231 | " padding-bottom: 10px;" 232 | " line-height: 20px;" 233 | "}" 234 | "" 235 | ".axis .tick::after {" 236 | " content: '';" 237 | " position: absolute;" 238 | " display: block;" 239 | " right: 0;" 240 | " bottom: 0;" 241 | " width: 1px;" 242 | " height: 200px;" 243 | " background: rgba(255, 255, 255, 0.2);" 244 | "}" 245 | "" 246 | ".axis .tick .value {" 247 | " transform: translateY(-240px);" 248 | " opacity: 0;" 249 | " transition: all 0.3s;" 250 | " position: absolute;" 251 | " top: 20px;" 252 | " left: 0;" 253 | " color: #fff;" 254 | " border-radius: 2px;" 255 | " width: 100%;" 256 | " line-height: 20px;" 257 | "}" 258 | "" 259 | ".axis .tick:hover .value.value--this {" 260 | " transform: translateY(-160px);" 261 | " display: block;" 262 | " opacity: 0.4;" 263 | "}" 264 | "" 265 | ".value.value--this {" 266 | " color: #fff;" 267 | " font-weight: bold;" 268 | "}" 269 | "" 270 | ".day-number {" 271 | " display: block;" 272 | "}" 273 | "" 274 | ".day-name {" 275 | " display: block;" 276 | " opacity: 0.4;" 277 | "}" 278 | "" 279 | "/* Animated background, credits: Manuel Pinto, https://codepen.io/P1N2O/pen/pyBNzX */" 280 | ".card {" 281 | " background: linear-gradient(-45deg, #5555FF, #9787FF, #FF55B8, #FF8787);" 282 | " background-size: 400% 400%;" 283 | " animation: bg 20s infinite;" 284 | "}" 285 | "" 286 | "@keyframes bg" 287 | "{" 288 | " 0% {" 289 | " background-position: 0% 50%" 290 | " }" 291 | " 50% {" 292 | " background-position: 100% 50%" 293 | " }" 294 | " 100% {" 295 | " background-position: 0% 50%" 296 | " }" 297 | "}" 298 | "" 299 | "" 300 | "" 301 | "/* The Modal (background) */" 302 | ".modal {" 303 | " display: none; /* Hidden by default */" 304 | " position: fixed; /* Stay in place */" 305 | " z-index: 1; /* Sit on top */" 306 | " left: 0;" 307 | " top: 0;" 308 | " width: 100%; /* Full width */" 309 | " height: 100%; /* Full height */" 310 | " overflow: auto; /* Enable scroll if needed */" 311 | " background-color: rgb(0,0,0); /* Fallback color */" 312 | " background-color: rgba(0,0,0,0.4); /* Black w/ opacity */" 313 | "}" 314 | "" 315 | "/* Modal Content/Box */" 316 | ".modal-content {" 317 | " background-color: #fefefe;" 318 | " margin: 10% 10% auto; /* 15% from the top and centered */" 319 | " padding: 20px;" 320 | " border: 1px solid #888;" 321 | " width: 80%; /* Could be more or less, depending on screen size */" 322 | "}" 323 | "" 324 | "/* The Close Button */" 325 | ".close {" 326 | " color: #aaa;" 327 | " float: right;" 328 | " font-size: 28px;" 329 | " font-weight: bold;" 330 | "}" 331 | "" 332 | ".close:hover," 333 | ".close:focus {" 334 | " color: black;" 335 | " text-decoration: none;" 336 | " cursor: pointer;" 337 | "}" 338 | "" 339 | "footer {" 340 | " position: absolute;" 341 | " bottom: 0px;" 342 | "}"; -------------------------------------------------------------------------------- /examples/host/msc/msc.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "string.h" 3 | #include "freertos/FreeRTOS.h" 4 | #include "freertos/task.h" 5 | #include "esp_log.h" 6 | #include "usb/usb_host.h" 7 | 8 | #include "usb_host.hpp" 9 | #include "usb_acm.hpp" 10 | #include "usb_msc.hpp" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | USBhost host; 23 | // USBhostDevice* device; 24 | USBmscDevice *device; 25 | 26 | void cbw_cb(usb_transfer_t *transfer) 27 | { 28 | printf("USER CBW CB\n"); 29 | } 30 | 31 | void data_cb(usb_transfer_t *transfer) 32 | { 33 | printf("USER data CB\n"); 34 | } 35 | 36 | static void csw_cb(usb_transfer_t *transfer) 37 | { 38 | printf("USER CSW CB\n"); 39 | } 40 | 41 | void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) 42 | { 43 | ESP_LOGE("", "stack overflow: %s\n", pcTaskName); 44 | } 45 | 46 | #define MOUNT_POINT "/msc" 47 | #define MOUNT_POINT1 "/msc1" 48 | char line[5000]; 49 | bool is_mount = false; 50 | static void capacity_cb(usb_transfer_t *transfer) 51 | { 52 | printf("capacity_cb: block_size: %d, block_count: %d\n", device->getBlockSize(is_mount), device->getBlockCount(is_mount)); 53 | } 54 | 55 | static void inquiry_cb(usb_transfer_t *transfer) 56 | { 57 | printf("inquiry_cb\n"); 58 | if(!is_mount){ 59 | device->mount(MOUNT_POINT, 0); 60 | device->mount(MOUNT_POINT1, 1); 61 | } 62 | is_mount = true; 63 | } 64 | 65 | static void unit_ready_cb(usb_transfer_t *transfer) 66 | { 67 | printf("unit_ready_cb\n"); 68 | } 69 | 70 | static void max_luns_cb(usb_transfer_t *transfer) 71 | { 72 | printf("max_luns_cb\n"); 73 | } 74 | 75 | static void sense_cb(usb_transfer_t *transfer) 76 | { 77 | printf("sense_cb\n"); 78 | } 79 | 80 | void client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg) 81 | { 82 | if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV) 83 | { 84 | host.open(event_msg); 85 | usb_device_info_t info = host.getDeviceInfo(); 86 | ESP_LOGI("", "device speed: %s, device address: %d, max ep_ctrl size: %d, config: %d", info.speed ? "USB_SPEED_FULL" : "USB_SPEED_LOW", info.dev_addr, info.bMaxPacketSize0, info.bConfigurationValue); 87 | const usb_config_desc_t *config_desc = host.getConfigurationDescriptor(); 88 | int offset = 0; 89 | 90 | for (size_t n = 0; n < config_desc->bNumInterfaces; n++) 91 | { 92 | const usb_intf_desc_t *intf = usb_parse_interface_descriptor(config_desc, n, 0, &offset); 93 | 94 | if (intf->bInterfaceClass == 0x08) // MSC 95 | { 96 | msc_transfer_cb_t cb = { 97 | // .cbw_cb = cbw_cb, 98 | // .data_cb = data_cb, 99 | // .csw_cb = csw_cb, 100 | .capacity_cb = capacity_cb, 101 | .inquiry_cb = inquiry_cb, 102 | // .unit_ready_cb = unit_ready_cb, 103 | // .max_luns_cb = max_luns_cb, 104 | // .sense_cb = sense_cb, 105 | }; 106 | device = new USBmscDevice(config_desc, &host); 107 | ((USBmscDevice *)device)->registerCallbacks(cb); 108 | n = config_desc->bNumInterfaces; 109 | } 110 | else if (intf->bInterfaceClass == 0x02 || intf->bInterfaceClass == 0x0a) // CDC ACM 111 | { 112 | // device = new USBacmDevice(config_desc, &host); 113 | n = config_desc->bNumInterfaces; 114 | } 115 | 116 | if (device) 117 | device->init(); 118 | 119 | // heap_caps_print_heap_info(MALLOC_CAP_INTERNAL); 120 | 121 | } 122 | } else { 123 | ESP_LOGW("", "DEVICE gone event"); 124 | } 125 | } 126 | 127 | #define TAG "" 128 | 129 | void read_test(int t) 130 | { 131 | FILE *f; 132 | char* mount; 133 | if (t) 134 | { 135 | f = fopen(MOUNT_POINT"/README.txt", "r"); 136 | mount = MOUNT_POINT; 137 | } else 138 | { 139 | f = fopen(MOUNT_POINT"/README2.txt", "r"); 140 | mount = MOUNT_POINT; 141 | } 142 | 143 | if (f == NULL) 144 | { 145 | ESP_LOGE("TAG", "Failed to open file"); 146 | } 147 | else 148 | { 149 | ESP_LOGI("READ file", "%s", MOUNT_POINT"/README.txt"); 150 | char *pos; 151 | fgets(line, sizeof(line), f); 152 | // strip newline 153 | pos = strchr(line, '\n'); 154 | do{ 155 | if (pos) 156 | { 157 | *pos = '\0'; 158 | } 159 | ESP_LOGI("", "%s", line); 160 | fgets(line, sizeof(line), f); 161 | pos = strchr(line, '\n'); 162 | }while(pos); 163 | fclose(f); 164 | } 165 | 166 | char* _dirpath = mount; 167 | char* dirpath = "/msc/"; 168 | DIR *dir = opendir(_dirpath); 169 | 170 | // /* Retrieve the base path of file storage to construct the full path */ 171 | // strlcpy(entrypath, dirpath, sizeof(entrypath)); 172 | char entrypath[256]; 173 | char entrysize[32]; 174 | const char *entrytype; 175 | 176 | struct dirent *entry; 177 | struct stat entry_stat; 178 | const size_t dirpath_len = strlen(dirpath); 179 | strlcpy(entrypath, dirpath, sizeof(entrypath)); 180 | 181 | if (!dir) 182 | { 183 | ESP_LOGE("TAG", "Failed to stat dir : %s", _dirpath); 184 | } 185 | else 186 | { 187 | entry = readdir(dir); 188 | while (entry != NULL) 189 | { 190 | entrytype = (entry->d_type == DT_DIR ? "directory" : "file"); 191 | 192 | strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len); 193 | 194 | int st = 0; 195 | st = stat(entrypath, &entry_stat); 196 | if (st == -1) 197 | { 198 | ESP_LOGE(TAG, "Failed to stat %s : %s", entrytype, entrypath); 199 | if(unlink(entrypath)) 200 | ESP_LOGE(TAG, "unlink failed"); 201 | entry = readdir(dir); 202 | continue; 203 | } 204 | sprintf(entrysize, "%ld", entry_stat.st_size); 205 | ESP_LOGI(TAG, "Found %s : %s (%s bytes)", entrytype, entrypath, entrysize); 206 | entry = readdir(dir); 207 | } 208 | closedir(dir); 209 | } 210 | } 211 | 212 | void write_test() 213 | { 214 | FILE* f = fopen("/msc/README.txt", "w"); 215 | if (f == NULL) { 216 | ESP_LOGE(TAG, "Failed to open file for writing"); 217 | return; 218 | } 219 | fprintf(f, "Hello World!\n"); 220 | fclose(f); 221 | ESP_LOGI(TAG, "File written"); 222 | read_test(1); 223 | 224 | // Check if destination file exists before renaming 225 | struct stat st; 226 | if (stat("/msc/README1.txt", &st) == 0) { 227 | // Delete it if it exists 228 | if(unlink("/msc/README1.txt")) 229 | ESP_LOGE(TAG, "unlink failed"); 230 | } 231 | 232 | // Rename original file 233 | ESP_LOGI(TAG, "Renaming file"); 234 | if (rename("/msc/README.txt", "/msc/README1.txt") != 0) { 235 | ESP_LOGE(TAG, "Rename failed"); 236 | // return; 237 | } 238 | 239 | // Open renamed file for reading 240 | ESP_LOGI(TAG, "Reading file"); 241 | f = fopen("/msc/README1.txt", "r"); 242 | if (f == NULL) { 243 | ESP_LOGE(TAG, "Failed to open file for reading"); 244 | return; 245 | } 246 | char line[64]; 247 | fgets(line, sizeof(line), f); 248 | fclose(f); 249 | // strip newline 250 | char* pos = strchr(line, '\n'); 251 | if (pos) { 252 | *pos = '\0'; 253 | } 254 | ESP_LOGI(TAG, "Read from file %s: '%s'", "/msc/README1.txt", line); 255 | } 256 | 257 | 258 | static void getFreeSpace(uint64_t* used_space, uint64_t* max_space) 259 | { 260 | 261 | FATFS *fs; 262 | DWORD c; 263 | if (f_getfree("/files", &c, &fs) == FR_OK) 264 | { 265 | *used_space = 266 | ((uint64_t)fs->csize * (fs->n_fatent - 2 - fs->free_clst)) * fs->ssize; 267 | *max_space = ((uint64_t)fs->csize * (fs->n_fatent - 2)) * fs->ssize; 268 | } 269 | } 270 | 271 | 272 | void setup() 273 | { 274 | host.registerClientCb(client_event_callback); 275 | host.init(); 276 | 277 | while (!is_mount) 278 | { 279 | vTaskDelay(1); 280 | } 281 | 282 | write_test(); 283 | read_test(0); 284 | 285 | uint64_t used_space; 286 | uint64_t max_space; 287 | getFreeSpace(&used_space, &max_space); 288 | 289 | ESP_LOGI("TEST", "storage used: %lld/%lld", used_space, max_space); 290 | } 291 | 292 | void loop() 293 | { 294 | } -------------------------------------------------------------------------------- /examples/host/remote_pendrive/server.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "index_html.h" 6 | #ifndef SERVE_WEB_FROM_PENDRIVE 7 | #include "app_js.h" 8 | #include "app_css.h" 9 | #endif 10 | 11 | void handleRoot() 12 | { 13 | server.send(200, "text/html", index_html); 14 | } 15 | 16 | void handleRoot2() 17 | { 18 | String path = String(MOUNT_POINT) + "/index.html"; 19 | server.sendHeader("Location", path); 20 | server.send(301, "text/html", ""); 21 | } 22 | 23 | #ifndef SERVE_WEB_FROM_PENDRIVE 24 | 25 | void handleJS() 26 | { 27 | server.send(200, "text/javascript", app_js); 28 | } 29 | 30 | void handleCSS() 31 | { 32 | server.send(200, "text/css", app_css); 33 | } 34 | 35 | #endif 36 | 37 | bool handleFileList(String path) 38 | { 39 | ESP_LOGI("", "%s", path.c_str()); 40 | 41 | const char *dirpath = path.c_str(); 42 | char entrypath[256]; 43 | char entrysize[32]; 44 | const char *entrytype; 45 | 46 | struct dirent *entry; 47 | struct stat entry_stat; 48 | char _dirpath[255] = {0}; 49 | const size_t dirpath_len = strlen(dirpath); 50 | strncpy(_dirpath, dirpath, path.length() - 1); 51 | _dirpath[dirpath_len - 1] = 0x0; 52 | 53 | DIR *dir = opendir(_dirpath); 54 | 55 | /* Retrieve the base path of file storage to construct the full path */ 56 | strlcpy(entrypath, dirpath, sizeof(entrypath)); 57 | 58 | if (!dir) 59 | { 60 | ESP_LOGE(TAG, "Failed to stat dir : %s", _dirpath); 61 | return false; 62 | } 63 | String output = "["; 64 | // /* Iterate over all files / folders and fetch their names and sizes */ 65 | char _script[500] = {0}; 66 | if (strlen(dirpath) > 7) 67 | { 68 | sprintf(_script, "{\"path\":\"%s/..\", \"dir\":1,\"name\":\"Up\", \"size\":\"0\"}", _dirpath); 69 | } 70 | else 71 | { 72 | sprintf(_script, "{\"path\":\"%s\", \"dir\":1,\"name\":\"Refresh\", \"size\":\"0\"}", _dirpath); 73 | } 74 | output += _script; 75 | entry = readdir(dir); 76 | 77 | while (entry != NULL) 78 | { 79 | entrytype = (entry->d_type == DT_DIR ? "directory" : "file"); 80 | 81 | strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len); 82 | 83 | int st = 0; 84 | st = stat(entrypath, &entry_stat); 85 | 86 | if (st == -1) 87 | { 88 | // ESP_LOGE(TAG, "Failed to stat %s : %s", entrytype, entry->d_name); 89 | entry = readdir(dir); 90 | continue; 91 | } 92 | 93 | output += ','; 94 | output += "{\"dir\":\""; 95 | output += (entry->d_type == DT_DIR) ? 1 : 0; 96 | output += "\",\"name\":\""; 97 | output += String(entry->d_name); 98 | output += "\",\"path\":\""; 99 | output += String(dirpath); 100 | output += String(entry->d_name); 101 | output += "\",\"size\":\""; 102 | output += String(entry_stat.st_size); 103 | output += "\"}"; 104 | 105 | entry = readdir(dir); 106 | } 107 | 108 | output += "]"; 109 | server.send(200, "text/json", output); 110 | return true; 111 | } 112 | 113 | void handleSTA() 114 | { 115 | for (uint8_t i = 0; i < server.args(); i++) 116 | { 117 | if (strcmp(server.argName(i).c_str(), "ssid") == 0) 118 | { 119 | ssidSTA = server.arg(i); 120 | } 121 | 122 | if (strcmp(server.argName(i).c_str(), "pass") == 0) 123 | { 124 | passSTA = server.arg(i); 125 | } 126 | } 127 | 128 | handleRoot(); 129 | WiFi.begin(ssidSTA.c_str(), passSTA.c_str()); 130 | } 131 | 132 | void handleAP() 133 | { 134 | for (uint8_t i = 0; i < server.args(); i++) 135 | { 136 | if (strcmp(server.argName(i).c_str(), "ssid") == 0) 137 | { 138 | ssidAP = server.arg(i); 139 | } 140 | 141 | if (strcmp(server.argName(i).c_str(), "pass") == 0) 142 | { 143 | passAP = server.arg(i); 144 | } 145 | } 146 | 147 | handleRoot(); 148 | WiFi.softAP(ssidAP.c_str(), passAP.c_str()); 149 | } 150 | 151 | String getContentType(String filename) 152 | { 153 | if (server.hasArg("download")) 154 | { 155 | return "application/octet-stream"; 156 | } 157 | else if (filename.endsWith(".htm")) 158 | { 159 | return "text/html"; 160 | } 161 | else if (filename.endsWith(".html")) 162 | { 163 | return "text/html"; 164 | } 165 | else if (filename.endsWith(".css")) 166 | { 167 | return "text/css"; 168 | } 169 | else if (filename.endsWith(".js")) 170 | { 171 | return "application/javascript"; 172 | } 173 | else if (filename.endsWith(".png")) 174 | { 175 | return "image/png"; 176 | } 177 | else if (filename.endsWith(".gif")) 178 | { 179 | return "image/gif"; 180 | } 181 | else if (filename.endsWith(".jpg")) 182 | { 183 | return "image/jpeg"; 184 | } 185 | else if (filename.endsWith(".ico")) 186 | { 187 | return "image/x-icon"; 188 | } 189 | else if (filename.endsWith(".xml")) 190 | { 191 | return "text/xml"; 192 | } 193 | else if (filename.endsWith(".pdf")) 194 | { 195 | return "application/pdf"; 196 | } 197 | else if (filename.endsWith(".zip")) 198 | { 199 | return "application/x-zip"; 200 | } 201 | else if (filename.endsWith(".gz")) 202 | { 203 | return "application/x-gzip"; 204 | } 205 | return "text/plain"; 206 | } 207 | 208 | bool handleFileRead(String path) 209 | { 210 | if (path.endsWith("/")) 211 | { 212 | return handleFileList(path); 213 | } 214 | Serial.println(path); 215 | 216 | FILE *fd = NULL; 217 | struct stat file_stat; 218 | const char *filepath = path.c_str(); 219 | 220 | if (stat(filepath, &file_stat) == -1) 221 | { 222 | ESP_LOGE(TAG, "Failed to stat file : %s", filepath); 223 | return false; 224 | } 225 | 226 | fd = fopen(filepath, "r"); 227 | if (!fd) 228 | { 229 | ESP_LOGE(TAG, "Failed to read existing file : %s", filepath); 230 | return false; 231 | } 232 | ESP_LOGI(TAG, "Sending file : %s (%ld bytes), type: %s...", filepath, file_stat.st_size, getContentType(String(filepath)).c_str()); 233 | // server._streamFileCore(, String(filepath), getContentType(String(filepath))); 234 | 235 | String contentType = getContentType(String(filepath)); 236 | server.setContentLength(file_stat.st_size); 237 | server.send(200, contentType, ""); 238 | 239 | char chunk[500] = {}; 240 | size_t chunksize; 241 | size_t total = 0; 242 | do 243 | { 244 | chunksize = fread(chunk, 1, 500, fd); 245 | server.client().write((uint8_t *)chunk, chunksize); 246 | total += chunksize; 247 | } while (total < file_stat.st_size); 248 | /* Close file after sending complete */ 249 | fclose(fd); 250 | return true; 251 | } 252 | 253 | void handleNotFound() 254 | { 255 | String message = "File Not Found\n\n"; 256 | message += "URI: "; 257 | message += server.uri(); 258 | message += "\nMethod: "; 259 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 260 | message += "\nArguments: "; 261 | message += server.args(); 262 | message += "\n"; 263 | for (uint8_t i = 0; i < server.args(); i++) 264 | { 265 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 266 | } 267 | server.send(404, "text/plain", message); 268 | } 269 | 270 | void handleMkdir() 271 | { 272 | const char* path; 273 | if (strcmp(server.argName(0).c_str(), "dirname") == 0) 274 | { 275 | path = server.arg(0).c_str(); 276 | } 277 | 278 | // char path[256] = {}; 279 | // sprintf(path, "%s", name + 8); 280 | if (mkdir(path, 0755)) 281 | { 282 | ESP_LOGE("", "failed to mkdir [%d]: %s", server.args(), path); 283 | server.send(409, "text/html", ""); 284 | for (uint8_t i = 0; i < server.args(); i++) 285 | { 286 | Serial.println(server.argName(i).c_str()); 287 | } 288 | return; 289 | } 290 | else 291 | { 292 | server.send(201, "text/html", ""); 293 | } 294 | } 295 | 296 | void handleDelete() 297 | { 298 | const char* path; 299 | if (strcmp(server.argName(0).c_str(), "file") == 0) 300 | { 301 | path = server.arg(0).c_str(); 302 | } 303 | 304 | // char path[256] = {}; 305 | // sprintf(path, "%s", name + 8); 306 | if (unlink(path)) 307 | { 308 | ESP_LOGE("", "failed to unlink: %s", path); 309 | server.send(409, "text/html", ""); 310 | return; 311 | } 312 | else 313 | { 314 | server.send(201, "text/html", ""); 315 | } 316 | 317 | } 318 | -------------------------------------------------------------------------------- /src/esptinyusb.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "hal/usb_hal.h" 3 | #include "soc/usb_periph.h" 4 | #include "driver/periph_ctrl.h" 5 | #include "driver/gpio.h" 6 | #if CONFIG_IDF_TARGET_ESP32S3 7 | #include "esp32s3/rom/gpio.h" 8 | #endif 9 | #if CONFIG_IDF_TARGET_ESP32S2 10 | #include "esp32s2/rom/gpio.h" 11 | #endif 12 | #include "esp_log.h" 13 | 14 | #include "esptinyusb.h" 15 | #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 16 | #if CONFIG_TINYUSB_ENABLED 17 | 18 | #define _manufacturer "Espressif" 19 | #define _product "ESP32 Arduino Device" 20 | #define _serial "1234-5678" 21 | 22 | #define _cdc "CDC class" 23 | #define _dfu "DFU class" 24 | #define _hid "HID class" 25 | #define _midi "MIDI class" 26 | #define _msc "MSC class" 27 | #define _vendor "Vendor class (webUSB)" 28 | 29 | #define TAG __func__ 30 | static EspTinyUSB* _device = NULL; 31 | bool EspTinyUSB::enableCDC; 32 | bool EspTinyUSB::enableMSC; 33 | bool EspTinyUSB::enableMIDI; 34 | bool EspTinyUSB::enableHID; 35 | bool EspTinyUSB::enableVENDOR; 36 | bool EspTinyUSB::enableDFU; 37 | uint8_t EspTinyUSB::ifIdx = 0; 38 | int EspTinyUSB::total = 9; 39 | uint8_t EspTinyUSB::count = 0; 40 | uint8_t EspTinyUSB::desc_configuration[1500] = {0}; 41 | uint16_t EspTinyUSB::_VID = 0x303a; 42 | uint16_t EspTinyUSB::_PID = 0x0002; 43 | descriptor_strings_t EspTinyUSB::strings; 44 | uint16_t EspTinyUSB::_revision; 45 | uint16_t EspTinyUSB::_bcdUSB; 46 | size_t EspTinyUSB::hid_report_desc_len = 0; 47 | USBCallbacks* EspTinyUSB::m_callbacks; 48 | static void IRAM_ATTR usb_persist_shutdown_handler(void); 49 | 50 | static void esptinyusbtask(void *p) 51 | { 52 | (void)p; 53 | ESP_LOGD(TAG, "USB tud_task created"); 54 | while (1) 55 | { 56 | // tinyusb device task 57 | tud_task(); 58 | } 59 | } 60 | 61 | EspTinyUSB::EspTinyUSB(bool extPhy) 62 | { 63 | if (!isEnabled) 64 | { 65 | 66 | bool usb_did_persist = (USB_WRAP.date.val == USBDC_PERSIST_ENA); 67 | 68 | if(usb_did_persist){ 69 | // Enable USB/IO_MUX peripheral reset, if coming from persistent reboot 70 | REG_CLR_BIT(RTC_CNTL_USB_CONF_REG, RTC_CNTL_IO_MUX_RESET_DISABLE); 71 | REG_CLR_BIT(RTC_CNTL_USB_CONF_REG, RTC_CNTL_USB_RESET_DISABLE); 72 | } 73 | 74 | if(1){ 75 | // Reset USB module 76 | periph_module_reset((periph_module_t)PERIPH_USB_MODULE); 77 | periph_module_enable((periph_module_t)PERIPH_USB_MODULE); 78 | } 79 | 80 | 81 | // Hal init 82 | usb_hal_context_t hal = { 83 | .use_external_phy = extPhy}; 84 | usb_hal_init(&hal); 85 | /* usb_periph_iopins currently configures USB_OTG as USB Device. 86 | * Introduce additional parameters in usb_hal_context_t when adding support 87 | * for USB Host. 88 | */ 89 | for (const usb_iopin_dsc_t *iopin = usb_periph_iopins; iopin->pin != -1; ++iopin) 90 | { 91 | if ((iopin->ext_phy_only == 0)) 92 | { 93 | gpio_pad_select_gpio(iopin->pin); 94 | if (iopin->is_output) 95 | { 96 | gpio_matrix_out(iopin->pin, iopin->func, false, false); 97 | } 98 | else 99 | { 100 | gpio_matrix_in(iopin->pin, iopin->func, false); 101 | gpio_pad_input_enable(iopin->pin); 102 | } 103 | gpio_pad_unhold(iopin->pin); 104 | } 105 | } 106 | gpio_set_drive_capability((gpio_num_t)USBPHY_DP_NUM, GPIO_DRIVE_CAP_3); 107 | gpio_set_drive_capability((gpio_num_t)USBPHY_DM_NUM, GPIO_DRIVE_CAP_3); 108 | isEnabled = true; 109 | _device = this; 110 | _revision = 0x100; 111 | 112 | strings.langId[0] = 0x09; 113 | strings.langId[1] = 0x04; 114 | strings.manufacturer = _manufacturer; 115 | strings.product = _product; 116 | strings.serial = _serial; 117 | strings.cdc = _cdc; 118 | strings.dfu = _dfu; 119 | strings.hid = _hid; 120 | strings.midi = _midi; 121 | strings.msc = _msc; 122 | strings.vendor = _vendor; 123 | 124 | _bcdUSB = 0x200; 125 | esp_register_shutdown_handler(usb_persist_shutdown_handler); 126 | } 127 | } 128 | 129 | void EspTinyUSB::manufacturer(char* str) 130 | { 131 | strings.manufacturer = str; 132 | } 133 | 134 | void EspTinyUSB::product(char* str) 135 | { 136 | strings.product = str; 137 | } 138 | 139 | void EspTinyUSB::serial(char* str) 140 | { 141 | strings.serial = str; 142 | } 143 | 144 | void EspTinyUSB::revision(uint16_t val) 145 | { 146 | _revision = val; 147 | } 148 | 149 | bool EspTinyUSB::begin(char* str, uint8_t n) 150 | { 151 | switch (n) 152 | { 153 | case 4: 154 | if(str != nullptr) 155 | strings.cdc = str; 156 | break; 157 | 158 | case 5: 159 | if(str != nullptr) 160 | strings.msc = str; 161 | break; 162 | 163 | case 6: 164 | if(str != nullptr) 165 | strings.hid = str; 166 | break; 167 | 168 | case 7: 169 | if(str != nullptr) 170 | strings.vendor = str; 171 | break; 172 | 173 | case 8: 174 | if(str != nullptr) 175 | strings.midi = str; 176 | break; 177 | 178 | case 9: 179 | if(str != nullptr) 180 | strings.dfu = str; 181 | break; 182 | 183 | default: 184 | break; 185 | } 186 | 187 | getDeviceDescriptor(); 188 | setDeviceDescriptorStrings(); 189 | getConfigurationDescriptor(); 190 | 191 | if (tusb_inited()) { 192 | return true; 193 | } 194 | 195 | if(!tusb_init()) { 196 | ESP_LOGE("TAG", "failed to init"); 197 | return false; 198 | } 199 | 200 | if (usbTaskHandle != nullptr) { 201 | return true; 202 | } 203 | 204 | return xTaskCreate(esptinyusbtask, "espUSB", 4 * 1024, NULL, 24, &usbTaskHandle) == pdTRUE; 205 | } 206 | 207 | void EspTinyUSB::registerDeviceCallbacks(USBCallbacks* cb) 208 | { 209 | m_callbacks = cb; 210 | } 211 | 212 | 213 | void EspTinyUSB::deviceID(uint16_t vid, uint16_t pid) 214 | { 215 | _VID = vid; 216 | _PID = pid; 217 | } 218 | 219 | void EspTinyUSB::deviceID(uint16_t* vid, uint16_t* pid) 220 | { 221 | *vid = _VID; 222 | *pid = _PID; 223 | } 224 | 225 | void EspTinyUSB::useDFU(bool en) 226 | { 227 | enableDFU = en; 228 | } 229 | 230 | void EspTinyUSB::useMSC(bool en) 231 | { 232 | enableMSC = en; 233 | } 234 | 235 | int usb_persist_mode = RESTART_BOOTLOADER; 236 | void EspTinyUSB::persistentReset(restart_type_t _usb_persist_mode) 237 | { 238 | usb_persist_mode = _usb_persist_mode; 239 | if(usb_persist_mode != RESTART_NO_PERSIST){ 240 | if (usb_persist_mode == RESTART_BOOTLOADER) { 241 | REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); 242 | } else if (usb_persist_mode == RESTART_BOOTLOADER_DFU) { 243 | //DFU Download 244 | chip_usb_set_persist_flags(USBDC_BOOT_DFU); 245 | REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); 246 | } else { 247 | //USB Persist reboot 248 | // chip_usb_set_persist_flags(USBDC_PERSIST_ENA); 249 | } 250 | } 251 | } 252 | 253 | static void IRAM_ATTR usb_persist_shutdown_handler(void) 254 | { 255 | if(usb_persist_mode != RESTART_NO_PERSIST){ 256 | int usb_persist_enabled = 0; 257 | if (usb_persist_enabled) { 258 | usb_dc_prepare_persist(); 259 | } 260 | if (usb_persist_mode == RESTART_BOOTLOADER) { 261 | //USB CDC Download 262 | if (usb_persist_enabled) { 263 | chip_usb_set_persist_flags(USBDC_PERSIST_ENA); 264 | } else { 265 | periph_module_reset(PERIPH_USB_MODULE); 266 | periph_module_enable(PERIPH_USB_MODULE); 267 | } 268 | REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); 269 | } else if (usb_persist_mode == RESTART_BOOTLOADER_DFU) { 270 | //DFU Download 271 | // Reset USB Core 272 | USB0.grstctl |= USB_CSFTRST; 273 | while ((USB0.grstctl & USB_CSFTRST) == USB_CSFTRST){} 274 | chip_usb_set_persist_flags(USBDC_BOOT_DFU); 275 | REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); 276 | } else if (usb_persist_enabled) { 277 | //USB Persist reboot 278 | chip_usb_set_persist_flags(USBDC_PERSIST_ENA); 279 | } 280 | } 281 | } 282 | 283 | //--------------------------------------------------------------------+ 284 | // Device callbacks 285 | //--------------------------------------------------------------------+ 286 | 287 | // Invoked when device is mounted 288 | void tud_mount_cb(void) 289 | { 290 | if (EspTinyUSB::m_callbacks) 291 | { 292 | EspTinyUSB::m_callbacks->onMount(); 293 | } 294 | } 295 | 296 | // Invoked when device is unmounted 297 | void tud_umount_cb(void) 298 | { 299 | if (EspTinyUSB::m_callbacks) 300 | { 301 | EspTinyUSB::m_callbacks->onUnmount(); 302 | } 303 | } 304 | 305 | // Invoked when usb bus is suspended 306 | // remote_wakeup_en : if host allow us to perform remote wakeup 307 | // Within 7ms, device must draw an average of current less than 2.5 mA from bus 308 | void tud_suspend_cb(bool remote_wakeup_en) 309 | { 310 | if (EspTinyUSB::m_callbacks) 311 | { 312 | EspTinyUSB::m_callbacks->onSuspend(remote_wakeup_en); 313 | } 314 | } 315 | 316 | // Invoked when usb bus is resumed 317 | void tud_resume_cb(void) 318 | { 319 | if (EspTinyUSB::m_callbacks) 320 | { 321 | EspTinyUSB::m_callbacks->onResume(); 322 | } 323 | } 324 | 325 | #endif 326 | #endif 327 | --------------------------------------------------------------------------------