blackmagic-esp32-s2/components/tinyusb/drivers/usb-glue.c

450 lines
13 KiB
C

#include <tusb.h>
#include "dap-link/dap-link-descriptors.h"
#include "dual-cdc/dual-cdc-descriptors.h"
#include "usb-glue.h"
#define TAG "usb-glue"
#define CONFIG_TINYUSB_TASK_STACK_SIZE 4096
#define CONFIG_TINYUSB_TASK_PRIORITY 17
typedef struct {
uint8_t const* desc_device;
uint8_t const* desc_config;
uint16_t const* (*desc_string_cb)(uint8_t, uint16_t);
} USBDevice;
const USBDevice usb_device[] = {
[USBDeviceTypeDapLink] =
{
.desc_device = (uint8_t const*)&dap_link_desc_device,
.desc_config = dap_link_desc_configuration,
.desc_string_cb = dap_link_descriptor_string_cb,
},
[USBDeviceTypeDualCDC] =
{
.desc_device = (uint8_t const*)&blackmagic_desc_device,
.desc_config = blackmagic_desc_fs_configuration,
.desc_string_cb = blackmagic_descriptor_string_cb,
},
};
static USBDeviceType usb_device_type = USBDeviceTypeDualCDC;
typedef enum {
BlackmagicCDCTypeGDB = 0,
BlackmagicCDCTypeUART = 1,
} BlackmagicCDCType;
typedef enum {
DapCDCTypeUART = 0,
} DapCDCType;
typedef struct {
void (*connected)(void* context);
void* connected_context;
void (*disconnected)(void* context);
void* disconnected_context;
void (*cdc_receive)(void* context);
void* cdc_receive_context;
void (*cdc_line_coding)(cdc_line_coding_t const* p_line_coding, void* context);
void* cdc_line_coding_context;
void (*cdc_line_state)(bool dtr, bool rts, void* context);
void* cdc_line_state_context;
void (*gdb_receive)(void* context);
void* gdb_receive_context;
void (*dap_receive)(void* context);
void* dap_receive_context;
} USBGlueCallbacks;
static USBGlueCallbacks callbacks = {
.connected = NULL,
.connected_context = NULL,
.disconnected = NULL,
.disconnected_context = NULL,
.cdc_receive = NULL,
.cdc_receive_context = NULL,
.cdc_line_coding = NULL,
.cdc_line_coding_context = NULL,
.cdc_line_state = NULL,
.cdc_line_state_context = NULL,
.gdb_receive = NULL,
.gdb_receive_context = NULL,
.dap_receive = NULL,
.dap_receive_context = NULL,
};
/***** Callbacks *****/
static void callback_connected(void) {
if(callbacks.connected) {
callbacks.connected(callbacks.connected_context);
}
}
static void callback_disconnected(void) {
if(callbacks.disconnected) {
callbacks.disconnected(callbacks.disconnected_context);
}
}
static void callback_cdc_receive() {
if(callbacks.cdc_receive) {
callbacks.cdc_receive(callbacks.cdc_receive_context);
}
}
static void callback_cdc_line_coding(cdc_line_coding_t const* p_line_coding) {
if(callbacks.cdc_line_coding) {
callbacks.cdc_line_coding(p_line_coding, callbacks.cdc_line_coding_context);
}
}
static void callback_cdc_line_state(bool dtr, bool rts) {
if(callbacks.cdc_line_state) {
callbacks.cdc_line_state(dtr, rts, callbacks.cdc_line_state_context);
}
}
static void callback_gdb_receive() {
if(callbacks.gdb_receive) {
callbacks.gdb_receive(callbacks.gdb_receive_context);
}
}
static void callback_dap_receive() {
if(callbacks.dap_receive) {
callbacks.dap_receive(callbacks.dap_receive_context);
}
}
/***** Tiny USB *****/
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const* tud_descriptor_device_cb(void) {
return usb_device[usb_device_type].desc_device;
}
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
return usb_device[usb_device_type].desc_config;
}
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
return usb_device[usb_device_type].desc_string_cb(index, langid);
}
uint8_t const* tud_descriptor_bos_cb(void) {
// return NULL;
return dap_link_desc_bos;
}
bool tud_vendor_control_xfer_cb(
uint8_t rhport,
uint8_t stage,
tusb_control_request_t const* request) {
// nothing to with DATA & ACK stage
if(stage != CONTROL_STAGE_SETUP) return true;
switch(request->bRequest) {
case VENDOR_REQUEST_MICROSOFT:
if(request->wIndex == 7) {
// Get Microsoft OS 2.0 compatible descriptor
uint16_t total_len;
memcpy(&total_len, dap_link_desc_ms_os_20 + 8, 2);
return tud_control_xfer(rhport, request, (void*)dap_link_desc_ms_os_20, total_len);
} else {
return false;
}
default:
// stall unknown request
return false;
}
return true;
}
void tud_vendor_rx_cb(uint8_t itf) {
(void)itf;
callback_dap_receive();
}
void tud_mount_cb(void) {
callback_connected();
}
void tud_umount_cb(void) {
callback_disconnected();
}
void tud_resume_cb(void) {
callback_connected();
}
void tud_suspend_cb(bool remote_wakeup_en) {
callback_disconnected();
}
void tud_cdc_rx_cb(uint8_t interface) {
do {
if(usb_device_type == USBDeviceTypeDualCDC) {
if(interface == BlackmagicCDCTypeGDB) {
callback_gdb_receive();
break;
} else if(interface == BlackmagicCDCTypeUART) {
callback_cdc_receive();
break;
}
} else if(usb_device_type == USBDeviceTypeDapLink) {
if(interface == DapCDCTypeUART) {
callback_cdc_receive();
break;
}
}
tud_cdc_n_read_flush(interface);
} while(false);
}
void tud_cdc_line_state_cb(uint8_t interface, bool dtr, bool rts) {
if(usb_device_type == USBDeviceTypeDualCDC) {
if(interface == BlackmagicCDCTypeUART) {
callback_cdc_line_state(dtr, rts);
}
} else if(usb_device_type == USBDeviceTypeDapLink) {
if(interface == DapCDCTypeUART) {
callback_cdc_line_state(dtr, rts);
}
}
}
void tud_cdc_line_coding_cb(uint8_t interface, cdc_line_coding_t const* p_line_coding) {
if(usb_device_type == USBDeviceTypeDualCDC) {
if(interface == BlackmagicCDCTypeUART) {
callback_cdc_line_coding(p_line_coding);
}
} else if(usb_device_type == USBDeviceTypeDapLink) {
if(interface == DapCDCTypeUART) {
callback_cdc_line_coding(p_line_coding);
}
}
}
/***** HAL *****/
#include <driver/gpio.h>
#include <driver/periph_ctrl.h>
#include <hal/usb_hal.h>
#include <soc/usb_periph.h>
#include <esp_rom_gpio.h>
#include <hal/gpio_ll.h>
#include <delay.h>
#include <esp_log.h>
#include <esp_check.h>
static void usb_hal_init_pins(usb_hal_context_t* usb) {
/* usb_periph_iopins currently configures USB_OTG as USB Device.
* Introduce additional parameters in usb_hal_context_t when adding support
* for USB Host.
*/
for(const usb_iopin_dsc_t* iopin = usb_periph_iopins; iopin->pin != -1; ++iopin) {
if((usb->use_external_phy) || (iopin->ext_phy_only == 0)) {
esp_rom_gpio_pad_select_gpio(iopin->pin);
if(iopin->is_output) {
esp_rom_gpio_connect_out_signal(iopin->pin, iopin->func, false, false);
} else {
esp_rom_gpio_connect_in_signal(iopin->pin, iopin->func, false);
if((iopin->pin != GPIO_FUNC_IN_LOW) && (iopin->pin != GPIO_FUNC_IN_HIGH)) {
gpio_ll_input_enable(&GPIO, iopin->pin);
}
}
esp_rom_gpio_pad_unhold(iopin->pin);
}
}
if(!usb->use_external_phy) {
gpio_set_drive_capability(USBPHY_DM_NUM, GPIO_DRIVE_CAP_3);
gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3);
}
}
static void usb_hal_bus_reset() {
gpio_config_t io_conf;
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT_OD;
io_conf.pin_bit_mask = ((1 << USBPHY_DM_NUM) | (1 << USBPHY_DP_NUM));
io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
gpio_set_level(USBPHY_DM_NUM, 0);
gpio_set_level(USBPHY_DP_NUM, 0);
delay(100);
gpio_set_level(USBPHY_DM_NUM, 1);
gpio_set_level(USBPHY_DP_NUM, 1);
}
static void usb_hal_tusb_device_task(void* arg) {
ESP_LOGD(TAG, "tinyusb task started");
while(1) { // RTOS forever loop
tud_task();
}
}
/***** Glue *****/
char* serial_desc = NULL;
char dap_serial_number[32];
static void usb_glue_set_serial_number(uint8_t* serial_number, uint8_t length) {
if(serial_desc != NULL) {
free(serial_desc);
}
serial_desc = malloc(length * 2 + 1);
for(uint8_t i = 0; i < length; i++) {
uint8_t nibble = serial_number[i] >> 4;
serial_desc[i * 2 + 0] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
nibble = serial_number[i] & 0x0F;
serial_desc[i * 2 + 1] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
}
serial_desc[length * 2] = 0;
}
const char* usb_glue_get_serial_number() {
return serial_desc;
}
esp_err_t usb_glue_init(USBDeviceType device_type) {
usb_device_type = device_type;
uint8_t mac[8] = {0, 0, 0, 0, 0, 0, 0, 0};
if(esp_efuse_mac_get_default(mac) == ESP_OK) {
usb_glue_set_serial_number(mac, 6);
dap_link_set_serial_number(usb_glue_get_serial_number());
blackmagic_set_serial_number(usb_glue_get_serial_number());
strncpy(dap_serial_number, usb_glue_get_serial_number(), sizeof(dap_serial_number) - 1);
dap_serial_number[sizeof(dap_serial_number) - 1] = '\0';
ESP_LOGI(TAG, "Serial number: %s", usb_glue_get_serial_number());
ESP_LOGI(TAG, "Dap serial number: %s", dap_serial_number);
}
usb_hal_bus_reset();
// Enable APB CLK to USB peripheral
periph_module_enable(PERIPH_USB_MODULE);
periph_module_reset(PERIPH_USB_MODULE);
// Initialize HAL layer
usb_hal_context_t hal = {.use_external_phy = false};
usb_hal_init(&hal);
usb_hal_init_pins(&hal);
ESP_RETURN_ON_FALSE(tusb_init(), ESP_FAIL, TAG, "init TinyUSB failed");
TaskHandle_t s_tusb_tskh;
xTaskCreate(
usb_hal_tusb_device_task,
"TinyUSB",
CONFIG_TINYUSB_TASK_STACK_SIZE,
NULL,
CONFIG_TINYUSB_TASK_PRIORITY,
&s_tusb_tskh);
ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_FAIL, TAG, "create TinyUSB main task failed");
ESP_LOGI(TAG, "TinyUSB Driver installed");
return ESP_OK;
}
void usb_glue_reset_bus() {
usb_hal_bus_reset();
}
void usb_glue_set_connected_callback(void (*callback)(void* context), void* context) {
callbacks.connected = callback;
callbacks.connected_context = context;
}
void usb_glue_set_disconnected_callback(void (*callback)(void* context), void* context) {
callbacks.disconnected = callback;
callbacks.disconnected_context = context;
}
void usb_glue_cdc_set_receive_callback(void (*callback)(void* context), void* context) {
callbacks.cdc_receive = callback;
callbacks.cdc_receive_context = context;
}
void usb_glue_cdc_set_line_coding_callback(
void (*callback)(cdc_line_coding_t const* p_line_coding, void* context),
void* context) {
callbacks.cdc_line_coding = callback;
callbacks.cdc_line_coding_context = context;
}
void usb_glue_cdc_set_line_state_callback(
void (*callback)(bool dtr, bool rts, void* context),
void* context) {
callbacks.cdc_line_state = callback;
callbacks.cdc_line_state_context = context;
}
void usb_glue_gdb_set_receive_callback(void (*callback)(void* context), void* context) {
callbacks.gdb_receive = callback;
callbacks.gdb_receive_context = context;
}
void usb_glue_dap_set_receive_callback(void (*callback)(void* context), void* context) {
callbacks.dap_receive = callback;
callbacks.dap_receive_context = context;
}
void usb_glue_cdc_send(const uint8_t* buf, size_t len, bool flush) {
if(usb_device_type == USBDeviceTypeDualCDC) {
tud_cdc_n_write(BlackmagicCDCTypeUART, buf, len);
if(flush) {
tud_cdc_n_write_flush(BlackmagicCDCTypeUART);
}
} else {
tud_cdc_n_write(DapCDCTypeUART, buf, len);
if(flush) {
tud_cdc_n_write_flush(DapCDCTypeUART);
}
}
}
size_t usb_glue_cdc_receive(uint8_t* buf, size_t len) {
return tud_cdc_n_read(BlackmagicCDCTypeUART, buf, len);
}
void usb_glue_gdb_send(const uint8_t* buf, size_t len, bool flush) {
if(usb_device_type == USBDeviceTypeDualCDC) {
tud_cdc_n_write(BlackmagicCDCTypeGDB, buf, len);
if(flush) {
tud_cdc_n_write_flush(BlackmagicCDCTypeGDB);
}
} else {
esp_system_abort("Wrong USB device type");
}
}
size_t usb_glue_gdb_receive(uint8_t* buf, size_t len) {
return tud_cdc_n_read(BlackmagicCDCTypeGDB, buf, len);
}
void usb_glue_dap_send(const uint8_t* buf, size_t len, bool flush) {
if(usb_device_type == USBDeviceTypeDapLink) {
tud_vendor_write(buf, len);
} else {
esp_system_abort("Wrong USB device type");
}
}
size_t usb_glue_dap_receive(uint8_t* buf, size_t len) {
return tud_vendor_read(buf, len);
}