// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "USBVendor.h" #if SOC_USB_OTG_SUPPORTED #if CONFIG_TINYUSB_VENDOR_ENABLED #include "esp32-hal-tinyusb.h" ESP_EVENT_DEFINE_BASE(ARDUINO_USB_VENDOR_EVENTS); esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait); esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg); static USBVendor *_Vendor = NULL; static QueueHandle_t rx_queue = NULL; static uint16_t USB_VENDOR_ENDPOINT_SIZE = CFG_TUD_ENDOINT_SIZE; uint16_t tusb_vendor_load_descriptor(uint8_t *dst, uint8_t *itf) { uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB Vendor"); uint8_t ep_num = tinyusb_get_free_duplex_endpoint(); TU_VERIFY(ep_num != 0); uint8_t descriptor[TUD_VENDOR_DESC_LEN] = {// Interface number, string index, EP Out & IN address, EP size TUD_VENDOR_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), USB_VENDOR_ENDPOINT_SIZE) }; *itf += 1; memcpy(dst, descriptor, TUD_VENDOR_DESC_LEN); return TUD_VENDOR_DESC_LEN; } void tud_vendor_rx_cb(uint8_t itf) { size_t len = tud_vendor_n_available(itf); log_v("%u", len); if (len) { uint8_t buffer[len]; len = tud_vendor_n_read(itf, buffer, len); log_buf_v(buffer, len); if (_Vendor) { _Vendor->_onRX(buffer, len); } } else { if (_Vendor) { _Vendor->_onRX(NULL, len); } } } extern "C" bool tinyusb_vendor_control_request_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) { log_v( "Port: %u, Stage: %u, Direction: %u, Type: %u, Recipient: %u, bRequest: 0x%x, wValue: %u, wIndex: %u, wLength: %u", rhport, stage, request->bmRequestType_bit.direction, request->bmRequestType_bit.type, request->bmRequestType_bit.recipient, request->bRequest, request->wValue, request->wIndex, request->wLength ); if (_Vendor) { return _Vendor->_onRequest(rhport, stage, (arduino_usb_control_request_t const *)request); } return false; } USBVendor::USBVendor(uint16_t endpoint_size) : itf(0), cb(NULL) { if (!_Vendor) { _Vendor = this; if (endpoint_size == 0) { endpoint_size = CFG_TUD_ENDOINT_SIZE; } if (endpoint_size <= CFG_TUD_ENDOINT_SIZE) { USB_VENDOR_ENDPOINT_SIZE = endpoint_size; } tinyusb_enable_interface(USB_INTERFACE_VENDOR, TUD_VENDOR_DESC_LEN, tusb_vendor_load_descriptor); } else { itf = _Vendor->itf; cb = _Vendor->cb; } } size_t USBVendor::setRxBufferSize(size_t rx_queue_len) { if (rx_queue) { if (!rx_queue_len) { vQueueDelete(rx_queue); rx_queue = NULL; } return 0; } rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t)); if (!rx_queue) { return 0; } return rx_queue_len; } void USBVendor::begin() { setRxBufferSize(512); //default if not preset } void USBVendor::end() { setRxBufferSize(0); } void USBVendor::onEvent(esp_event_handler_t callback) { onEvent(ARDUINO_USB_VENDOR_ANY_EVENT, callback); } void USBVendor::onEvent(arduino_usb_vendor_event_t event, esp_event_handler_t callback) { arduino_usb_event_handler_register_with(ARDUINO_USB_VENDOR_EVENTS, event, callback, this); } bool USBVendor::mounted() { return tud_vendor_n_mounted(itf); } bool USBVendor::sendResponse(uint8_t rhport, arduino_usb_control_request_t const *request, void *data, size_t len) { if (!request) { return false; } if (!data || !len) { return tud_control_status(rhport, (tusb_control_request_t const *)request); } else { return tud_control_xfer(rhport, (tusb_control_request_t const *)request, data, len); } } void USBVendor::onRequest(arduino_usb_vendor_control_request_handler_t handler) { cb = handler; } bool USBVendor::_onRequest(uint8_t rhport, uint8_t stage, arduino_usb_control_request_t const *request) { if (cb) { return cb(rhport, stage, request); } return false; } void USBVendor::_onRX(const uint8_t *buffer, size_t len) { for (uint32_t i = 0; i < len; i++) { if (rx_queue == NULL || !xQueueSend(rx_queue, buffer + i, 0)) { len = i + 1; log_e("RX Queue Overflow"); break; } } arduino_usb_vendor_event_data_t p; p.data.len = len; arduino_usb_event_post(ARDUINO_USB_VENDOR_EVENTS, ARDUINO_USB_VENDOR_DATA_EVENT, &p, sizeof(arduino_usb_vendor_event_data_t), portMAX_DELAY); } size_t USBVendor::write(const uint8_t *buffer, size_t len) { if (!mounted()) { log_e("not mounted"); return 0; } size_t max_len = tud_vendor_n_write_available(itf); if (len > max_len) { len = max_len; } if (len) { return tud_vendor_n_write(itf, buffer, len); } return len; } size_t USBVendor::write(uint8_t c) { return write(&c, 1); } int USBVendor::available(void) { if (rx_queue == NULL) { return -1; } return uxQueueMessagesWaiting(rx_queue); } int USBVendor::peek(void) { if (rx_queue == NULL) { return -1; } uint8_t c; if (xQueuePeek(rx_queue, &c, 0)) { return c; } return -1; } int USBVendor::read(void) { if (rx_queue == NULL) { return -1; } uint8_t c = 0; if (xQueueReceive(rx_queue, &c, 0)) { return c; } return -1; } size_t USBVendor::read(uint8_t *buffer, size_t size) { if (rx_queue == NULL) { return -1; } uint8_t c = 0; size_t count = 0; while (count < size && xQueueReceive(rx_queue, &c, 0)) { buffer[count++] = c; } return count; } void USBVendor::flush(void) { tud_vendor_n_write_flush(itf); } #endif /* CONFIG_TINYUSB_VENDOR_ENABLED */ #endif /* SOC_USB_OTG_SUPPORTED */