diff --git a/README.md b/README.md index 342311e..955696f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# Extrnded USB host support +Fork of chegewara's EspTinyUSB library, adding extra functionality to the USB Host wrapper. + + # New version New version of library, compatible with esp-idf (after cleanup) can be find here: diff --git a/examples/host/control_transfer_example/README.md b/examples/host/control_transfer_example/README.md new file mode 100644 index 0000000..c431a5c --- /dev/null +++ b/examples/host/control_transfer_example/README.md @@ -0,0 +1,238 @@ +# USB Host Control Transfer Example + +This example demonstrates how to use the enhanced USB host library with control transfer support. The library now provides comprehensive control transfer functionality for communicating with USB devices. + +## Features Added + +The USB host library has been expanded with the following control transfer capabilities: + +### Basic Control Transfer Management +- `sendControlTransfer()` - Send custom control transfers with setup packets and data +- `allocateControlTransfer()` / `freeControlTransfer()` - **Deprecated**: Transfers are now allocated per request automatically + +### Standard USB Control Requests +- `getDescriptor()` - Get any USB descriptor +- `setAddress()` - Set device address +- `setConfiguration()` - Set device configuration +- `getConfiguration()` - Get current configuration +- `setInterface()` - Set interface alternate setting +- `getInterface()` - Get interface alternate setting +- `clearFeature()` - Clear device/interface/endpoint features +- `setFeature()` - Set device/interface/endpoint features +- `getStatus()` - Get device/interface/endpoint status + +### Helper Methods +- `getDeviceDescriptorAsync()` - Get device descriptor (async) +- `getConfigurationDescriptorAsync()` - Get configuration descriptor (async) +- `getStringDescriptorAsync()` - Get string descriptor (async) + +## Usage + +### 1. Initialize USB Host + +```cpp +#include "USBhost.h" + +USBhost host; + +void setup() { + // Initialize USB host + if (!host.init()) { + Serial.println("Failed to initialize USB host"); + return; + } + + // Control transfers are automatically allocated per request + // No need to pre-allocate buffers +} +``` + +### 2. Send Control Transfers + +#### Using Standard Methods +```cpp +// Get device descriptor +esp_err_t err = host.getDeviceDescriptorAsync(control_transfer_callback); + +// Get device status +err = host.getStatus(USB_BM_REQUEST_TYPE_RECIP_DEVICE, 0, control_transfer_callback); + +// Set configuration +err = host.setConfiguration(1, control_transfer_callback); +``` + +#### Using Custom Setup Packets +```cpp +usb_setup_packet_t setup = { + .bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE, + .bRequest = USB_B_REQUEST_GET_STATUS, + .wValue = 0, + .wIndex = 0, + .wLength = 2 +}; + +esp_err_t err = host.sendControlTransfer(&setup, nullptr, 0, control_transfer_callback); +``` + +#### With Data +```cpp +uint8_t data[] = {0x01, 0x02, 0x03}; +esp_err_t err = host.sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_RECIP_DEVICE, + 0x01, // Custom request + 0x1234, // wValue + 0x5678, // wIndex + sizeof(data), // wLength + data, // Data to send + control_transfer_callback +); +``` + +#### HID SET_REPORT Example +```cpp +// Create 256-byte payload +uint8_t hid_payload[256]; +memset(hid_payload, 0x00, sizeof(hid_payload)); + +// Fill with your data (first 48 bytes) +hid_payload[0] = 0x1c; hid_payload[1] = 0x00; // ... fill your data +hid_payload[43] = preset_num; hid_payload[44] = preset_hex; + +// Send HID SET_REPORT control transfer +esp_err_t err = host.sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE, + 0x09, // SET_REPORT + (2 << 8) | 0, // Report type Output=2, Report ID=0 + 4, // Interface number + sizeof(hid_payload), + hid_payload, + control_transfer_callback +); +``` + +### 3. Handle Control Transfer Callbacks + +```cpp +void control_transfer_callback(usb_transfer_t *transfer) +{ + if (transfer->status == USB_TRANSFER_STATUS_COMPLETED) { + Serial.println("Control transfer completed successfully!"); + + // Access received data + if (transfer->actual_num_bytes > 0) { + Serial.print("Received data: "); + for (int i = 0; i < transfer->actual_num_bytes; i++) { + Serial.printf("%02X ", transfer->data_buffer[i]); + } + Serial.println(); + } + } else { + Serial.printf("Control transfer failed with status: %d\n", transfer->status); + } +} +``` + +## API Reference + +### Control Transfer Management + +#### `bool allocateControlTransfer(size_t data_buffer_size = 64)` (Deprecated) +**Deprecated**: This method is kept for backward compatibility but is no longer needed. +- Control transfers are now allocated automatically per request +- **Returns:** Always `true` + +#### `void freeControlTransfer()` (Deprecated) +**Deprecated**: This method is kept for backward compatibility but is no longer needed. +- Control transfers are now freed automatically after completion + +#### `esp_err_t sendControlTransfer(const usb_setup_packet_t *setup_pkt, const void *data = nullptr, size_t data_len = 0, usb_transfer_cb_t callback = nullptr, void *context = nullptr)` +Sends a control transfer using a setup packet. +- **Parameters:** + - `setup_pkt`: USB setup packet + - `data`: Optional data to send + - `data_len`: Length of data + - `callback`: Optional callback function + - `context`: Optional context pointer +- **Returns:** ESP error code + +#### `esp_err_t sendControlTransfer(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, const void *data = nullptr, usb_transfer_cb_t callback = nullptr, void *context = nullptr)` +Sends a control transfer using individual parameters. +- **Parameters:** + - `bmRequestType`: Request type (direction, type, recipient) + - `bRequest`: Request code + - `wValue`: Value field + - `wIndex`: Index field + - `wLength`: Length field + - `data`: Optional data to send + - `callback`: Optional callback function + - `context`: Optional context pointer +- **Returns:** ESP error code + +### Standard USB Control Requests + +All standard USB control request methods follow the same pattern: +- Take optional callback and context parameters +- Return ESP error code +- Use the allocated control transfer buffer + +#### Descriptor Operations +- `getDescriptor(uint8_t desc_type, uint8_t desc_index, uint16_t lang_id, uint16_t length, ...)` +- `getDeviceDescriptorAsync(...)` +- `getConfigurationDescriptorAsync(uint8_t config_index, uint16_t length, ...)` +- `getStringDescriptorAsync(uint8_t string_index, uint16_t lang_id, uint16_t length, ...)` + +#### Device Configuration +- `setAddress(uint8_t address, ...)` +- `setConfiguration(uint8_t config_value, ...)` +- `getConfiguration(...)` + +#### Interface Management +- `setInterface(uint8_t interface, uint8_t alt_setting, ...)` +- `getInterface(uint8_t interface, ...)` + +#### Feature Management +- `clearFeature(uint8_t recipient, uint8_t feature, uint16_t index, ...)` +- `setFeature(uint8_t recipient, uint8_t feature, uint16_t index, ...)` +- `getStatus(uint8_t recipient, uint16_t index, ...)` + +## Error Handling + +The library provides comprehensive error handling: +- All methods return ESP error codes +- Callbacks receive transfer status information +- Automatic cleanup on device disconnection +- Buffer size validation + +## Notes + +- Control transfers are asynchronous by default +- Each control transfer is allocated and freed automatically +- Multiple control transfers can be queued (ESP-IDF handles the queuing) +- All standard USB control requests are supported as per the USB 2.0 specification +- The library follows ESP-IDF USB host API conventions +- **Naming Convention**: Control transfer methods that return `esp_err_t` are suffixed with "Async" to distinguish them from synchronous methods that return descriptor pointers +- **Important**: Callbacks must free the transfer using `usb_host_transfer_free(transfer)` after processing + +## Example Output + +When running this example with a USB device connected, you should see output similar to: + +``` +USB Host Control Transfer Example +USB host initialized. Waiting for device... +New device connected at address: 1 +Device detected! Allocating control transfer... +Control transfer allocated successfully +Getting device descriptor... +Control transfer completed successfully! +Received data: 12 01 00 02 00 00 00 40 83 04 23 05 00 01 01 02 00 01 + +--- Running Control Transfer Tests --- +Test 1: Getting device status... +Control transfer completed successfully! +Received data: 00 00 +Test 2: Getting current configuration... +Control transfer completed successfully! +Received data: 01 +... +``` diff --git a/examples/host/control_transfer_example/control_transfer_example.ino b/examples/host/control_transfer_example/control_transfer_example.ino new file mode 100644 index 0000000..4c57bf7 --- /dev/null +++ b/examples/host/control_transfer_example/control_transfer_example.ino @@ -0,0 +1,195 @@ +#include "USBhost.h" + +USBhost host; + +// Control transfer callback +void control_transfer_callback(usb_transfer_t *transfer) +{ + if (transfer->status == USB_TRANSFER_STATUS_COMPLETED) { + Serial.println("Control transfer completed successfully!"); + + // Print the received data (if any) + if (transfer->actual_num_bytes > 0) { + Serial.print("Received data: "); + for (int i = 0; i < transfer->actual_num_bytes; i++) { + Serial.printf("%02X ", transfer->data_buffer[i]); + } + Serial.println(); + } + } else { + Serial.printf("Control transfer failed with status: %d\n", transfer->status); + } +} + +// Device event callback +void device_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg) +{ + switch (event_msg->event) { + case USB_HOST_CLIENT_EVENT_NEW_DEV: + Serial.printf("New device connected at address: %d\n", event_msg->new_dev.address); + break; + + case USB_HOST_CLIENT_EVENT_DEV_GONE: + Serial.println("Device disconnected"); + break; + + default: + Serial.printf("Unhandled event: %d\n", event_msg->event); + break; + } +} + +void setup() +{ + Serial.begin(115200); + Serial.println("USB Host Control Transfer Example"); + + // Initialize USB host + if (!host.init()) { + Serial.println("Failed to initialize USB host"); + return; + } + + // Register device event callback + host.registerClientCb(device_event_callback); + + Serial.println("USB host initialized. Waiting for device..."); +} + +void loop() +{ + static bool device_ready = false; + static uint32_t last_test = 0; + + // Check if device is connected and ready + if (host.deviceHandle() && !device_ready) { + Serial.println("Device detected! Allocating control transfer..."); + + // Allocate control transfer buffer (needs to be large enough for 256-byte payloads) + if (host.allocateControlTransfer(256)) { + device_ready = true; + Serial.println("Control transfer allocated successfully"); + + // Get device descriptor + Serial.println("Getting device descriptor..."); + esp_err_t err = host.getDeviceDescriptorAsync(control_transfer_callback); + if (err != ESP_OK) { + Serial.printf("Failed to get device descriptor: %d\n", err); + } + } else { + Serial.println("Failed to allocate control transfer"); + } + } + + // Run periodic control transfer tests + if (device_ready && (millis() - last_test) > 5000) { + last_test = millis(); + + Serial.println("\n--- Running Control Transfer Tests ---"); + + // Test 1: Get device status + Serial.println("Test 1: Getting device status..."); + esp_err_t err = host.getStatus(USB_BM_REQUEST_TYPE_RECIP_DEVICE, 0, control_transfer_callback); + if (err != ESP_OK) { + Serial.printf("Failed to get device status: %d\n", err); + } + + delay(1000); + + // Test 2: Get current configuration + Serial.println("Test 2: Getting current configuration..."); + err = host.getConfiguration(control_transfer_callback); + if (err != ESP_OK) { + Serial.printf("Failed to get configuration: %d\n", err); + } + + delay(1000); + + // Test 3: Custom control transfer using helper macro + Serial.println("Test 3: Custom control transfer using helper macro..."); + usb_setup_packet_t custom_setup; + USB_SETUP_PACKET_INIT_GET_STATUS(&custom_setup); + err = host.sendControlTransfer(&custom_setup, nullptr, 0, control_transfer_callback); + if (err != ESP_OK) { + Serial.printf("Failed to send custom control transfer: %d\n", err); + } + + delay(1000); + + // Test 4: Get string descriptor (if available) + Serial.println("Test 4: Getting string descriptor..."); + err = host.getStringDescriptorAsync(1, 0x0409, 64, control_transfer_callback); // English language + if (err != ESP_OK) { + Serial.printf("Failed to get string descriptor: %d\n", err); + } + + delay(1000); + + // Test 5: Vendor-specific control transfer + Serial.println("Test 5: Vendor-specific control transfer..."); + uint8_t vendor_data[] = {0x01, 0x02, 0x03, 0x04}; + err = host.sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_RECIP_DEVICE, + 0x01, // Custom vendor request + 0x1234, // wValue + 0x5678, // wIndex + sizeof(vendor_data), // wLength + vendor_data, // Data to send + control_transfer_callback + ); + if (err != ESP_OK) { + Serial.printf("Failed to send vendor control transfer: %d\n", err); + } + + delay(1000); + + // Test 6: HID SET_REPORT control transfer with large payload + Serial.println("Test 6: HID SET_REPORT control transfer..."); + + // Create 256-byte payload (similar to your Python example) + uint8_t hid_payload[256]; + memset(hid_payload, 0x00, sizeof(hid_payload)); // Initialize with zeros + + // Fill the first 48 bytes with your data + uint8_t preset_num = 0x01; // Example preset number + uint8_t preset_hex = 0x02; // Example preset hex value + + hid_payload[0] = 0x1c; hid_payload[1] = 0x00; hid_payload[2] = 0xb0; hid_payload[3] = 0xa2; + hid_payload[4] = 0xc7; hid_payload[5] = 0x15; hid_payload[6] = 0x86; hid_payload[7] = 0x9d; + hid_payload[8] = 0xff; hid_payload[9] = 0xff; hid_payload[10] = 0x00; hid_payload[11] = 0x00; + hid_payload[12] = 0x00; hid_payload[13] = 0x00; hid_payload[14] = 0x1b; hid_payload[15] = 0x00; + hid_payload[16] = 0x00; hid_payload[17] = 0x02; hid_payload[18] = 0x00; hid_payload[19] = 0x07; + hid_payload[20] = 0x00; hid_payload[21] = 0x00; hid_payload[22] = 0x02; hid_payload[23] = 0x08; + hid_payload[24] = 0x01; hid_payload[25] = 0x00; hid_payload[26] = 0x00; hid_payload[27] = 0x00; + hid_payload[28] = 0x21; hid_payload[29] = 0x09; hid_payload[30] = 0x00; hid_payload[31] = 0x02; + hid_payload[32] = 0x04; hid_payload[33] = 0x00; hid_payload[34] = 0x00; hid_payload[35] = 0x01; + hid_payload[36] = 0xe0; hid_payload[37] = 0xa2; hid_payload[38] = 0x05; hid_payload[39] = 0x00; + hid_payload[40] = 0xb7; hid_payload[41] = 0x00; hid_payload[42] = 0x06; hid_payload[43] = preset_num; + hid_payload[44] = preset_hex; hid_payload[45] = 0x00; hid_payload[46] = 0x00; hid_payload[47] = 0x00; + // Remaining bytes are already zero from memset + + // Send HID SET_REPORT control transfer (equivalent to your Python code) + // bmRequestType = 0x21 (Host to device | Class | Interface) + // bRequest = 0x09 (SET_REPORT) + // wValue = (2 << 8) | 0 (Report type Output=2, Report ID=0) + // wIndex = 4 (Interface number) + err = host.sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE, + 0x09, // SET_REPORT + (2 << 8) | 0, // Report type Output=2, Report ID=0 + 4, // Interface number + sizeof(hid_payload), // 256 bytes + hid_payload, + control_transfer_callback + ); + if (err != ESP_OK) { + Serial.printf("Failed to send HID SET_REPORT: %d\n", err); + } else { + Serial.println("HID SET_REPORT sent successfully!"); + } + + Serial.println("--- Tests completed ---\n"); + } + + delay(100); +} diff --git a/library.properties b/library.properties index 7bf2781..5b96369 100644 --- a/library.properties +++ b/library.properties @@ -1,11 +1,11 @@ -name=ESP32TinyUSB -version=2.0.2 -author=Dariusz Krempa -maintainer=Dariusz Krempa -sentence=USB functions for ESP32-S2 -paragraph=This library provides an implementation of tinyusb for the ESP32-S2 for Arduino. +name=ESP32TinyUSB-extended-usb-host +version=1.0.0 +author=Gershy13 +maintainer=Gershy13 +sentence=Extended USB host functions for ESP32-S2 +paragraph=This library provides an implementation of tinyusb for the ESP32-S2 for Arduino. Added extra usb host functioanlity category=Communication architectures=esp32 includes= depends= -url=https://github.com/chegewara/EspTinyUSB +url=https://github.com/Gershy13/ESP32TinyUSB-extended-usb-host diff --git a/src/device/cdc/cdcusb.cpp b/src/device/cdc/cdcusb.cpp index 97bba26..3058064 100644 --- a/src/device/cdc/cdcusb.cpp +++ b/src/device/cdc/cdcusb.cpp @@ -102,10 +102,10 @@ size_t CDCusb::write(const uint8_t *buffer, size_t size) { if (tud_cdc_n_connected(_itf)) { - size_t d = 0; + uint32_t d = 0; do{ - d += tud_cdc_n_write(_itf, buffer + d, size - d <= 64 ? size - d : 64); - }while(size > d); + d += tud_cdc_n_write(_itf, buffer + d, size <= 64 ? size: size - d); + }while(size - d > 0 || d == 0); tud_cdc_n_write_flush(_itf); return d; } diff --git a/src/device/msc/flashdisk.cpp b/src/device/msc/flashdisk.cpp index 39f6180..edc1787 100644 --- a/src/device/msc/flashdisk.cpp +++ b/src/device/msc/flashdisk.cpp @@ -147,7 +147,7 @@ bool FlashUSB::begin(char* str) return MSCusb::begin(str); } -bool FlashUSB::init(const char* path, char* label) +bool FlashUSB::init(char* path, char* label) { esp_vfs_fat_mount_config_t mount_config = { diff --git a/src/device/msc/sdcard.cpp b/src/device/msc/sdcard.cpp index a3cd23f..f658a62 100644 --- a/src/device/msc/sdcard.cpp +++ b/src/device/msc/sdcard.cpp @@ -62,19 +62,17 @@ class SDCallbacks : public MSCCallbacks { } int32_t onRead(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { - log_v("default onread lba (%u) bufsize (%u)", lba, bufsize); + log_v("default onread"); (void) lun; - for (int i = 0; m_parent->block_size * i < bufsize; i++) - SD.readRAW((uint8_t *)buffer + m_parent->block_size * i, lba + i); + SD.readRAW((uint8_t*)buffer, lba); return bufsize; } int32_t onWrite(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { - log_v("default onwrite lba (%u) bufsize (%u)", lba, bufsize); + log_v("default onwrite"); (void) lun; - for (int i = 0; m_parent->block_size * i < bufsize; i++) - SD.writeRAW((uint8_t *)buffer + m_parent->block_size * i, lba + i); + SD.writeRAW((uint8_t*)buffer, lba); return bufsize; } @@ -99,16 +97,7 @@ bool SDCard2USB::initSD(uint8_t ssPin, SPIClass &spi, uint32_t frequency, const Serial.println("Card Mount Failed"); return false; } - - uint8_t cardType = SD.cardType(); - if(cardType == CARD_NONE){ - Serial.println("No SD card attached"); - return false; - } - - block_count = SD.cardSize() / block_size; - sdcardReady = true; return true; } diff --git a/src/device/web/webusb.cpp b/src/device/web/webusb.cpp index 7bbc5da..ff7eb0c 100644 --- a/src/device/web/webusb.cpp +++ b/src/device/web/webusb.cpp @@ -127,7 +127,7 @@ void WebUSB::setCallbacks(WebUSBCallbacks* cb) void tud_vendor_rx_cb(uint8_t itf) { - if(_webUSB->m_callbacks) + if(itf && _webUSB->m_callbacks) _webUSB->m_callbacks->onData(); } diff --git a/src/flashdisk.h b/src/flashdisk.h index 16f441c..1b80101 100644 --- a/src/flashdisk.h +++ b/src/flashdisk.h @@ -12,7 +12,7 @@ class FlashUSB : public MSCusb { public: FlashUSB(bool aes = false); bool begin(char* str = nullptr); - bool init(const char* path = "/fatfs",char* label = NULL); + bool init(char* path = "/fatfs",char* label = NULL); void setCallbacks(MSCCallbacks*); void setCapacity(uint32_t count, uint32_t size); bool isReady(); diff --git a/src/host/common/usb_host.cpp b/src/host/common/usb_host.cpp index 822153c..8bf9066 100644 --- a/src/host/common/usb_host.cpp +++ b/src/host/common/usb_host.cpp @@ -4,54 +4,116 @@ #include "usb/usb_host.h" #include "usb_host.hpp" +#include +static const char *TAG = "USBH"; + +// Forward-declared in header as a friend void _client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg) { - USBhost *host = (USBhost *)arg; - if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV) - { - ESP_LOGI("", "client event: %d, address: %d", event_msg->event, event_msg->new_dev.address); - if (host->_client_event_cb) - { - host->_client_event_cb(event_msg, arg); - } else { + USBhost *host = static_cast(arg); + if (!host) { + ESP_LOGE(TAG, "Callback with null host"); + return; + } + + ESP_LOGI(TAG, "client event: %d", event_msg ? event_msg->event : -1); + + // If user registered a callback, ALWAYS forward all events. + if (host->_client_event_cb) { + host->_client_event_cb(event_msg, arg); + return; + } + + // Default behavior only when no user callback is registered + if (!event_msg) return; + + switch (event_msg->event) { + case USB_HOST_CLIENT_EVENT_NEW_DEV: + ESP_LOGI(TAG, "NEW_DEV addr=%d", event_msg->new_dev.address); host->open(event_msg); - } + break; + + case USB_HOST_CLIENT_EVENT_DEV_GONE: + ESP_LOGI(TAG, "DEV_GONE"); + host->close(); // Release resources as per ESP-IDF expectations + break; + + default: + // Other events can be safely ignored or logged + ESP_LOGI(TAG, "Unhandled event: %d", event_msg->event); + break; } } static void client_async_seq_task(void *param) { usb_host_client_handle_t client_hdl = *(usb_host_client_handle_t *)param; - uint32_t event_flags; - while (1) - { - usb_host_client_handle_events(client_hdl, 1); - - if (ESP_OK == usb_host_lib_handle_events(1, &event_flags)) - { - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) - { - ESP_LOGI("", "No more clients"); + uint32_t event_flags = 0; + + // Keep servicing events indefinitely so reconnects work. + // The app can later add a stop flag if it needs to terminate this task. + for (;;) { + // Service client event queue (dispatches to _client_event_callback) + esp_err_t cerr = usb_host_client_handle_events(client_hdl, 1); + if (cerr != ESP_OK && cerr != ESP_ERR_TIMEOUT) { + ESP_LOGW(TAG, "usb_host_client_handle_events: %d", cerr); + } + + // Service library-level events (port/power/housekeeping) + esp_err_t lerr = usb_host_lib_handle_events(1, &event_flags); + if (lerr == ESP_OK) { + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_LOGI(TAG, "NO_CLIENTS -> freeing all devices"); usb_host_device_free_all(); } - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) - { - break; + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + // In the original file this broke out of the loop. + // Do NOT break — keep the task alive for future device connections. + ESP_LOGI(TAG, "ALL_FREE (devices freed). Staying alive for future connections."); } + } else if (lerr != ESP_ERR_TIMEOUT) { + ESP_LOGW(TAG, "usb_host_lib_handle_events: %d", lerr); } + + // Be nice to the scheduler + vTaskDelay(pdMS_TO_TICKS(1)); } + + // Unreachable with the current design; kept for completeness. usb_host_client_deregister(client_hdl); vTaskDelete(NULL); } +// -------- USBhost methods ---------- + USBhost::USBhost() + : client_hdl(nullptr), + dev_hdl(nullptr), + _client_event_cb(nullptr), + _dev_addr(0), + _configs(0), + _itfs(0) { + memset(&dev_info, 0, sizeof(dev_info)); } USBhost::~USBhost() { - usb_host_device_close(client_hdl, dev_hdl); + // Best-effort cleanup + if (dev_hdl && client_hdl) { + usb_host_device_close(client_hdl, dev_hdl); + dev_hdl = nullptr; + } + if (client_hdl) { + usb_host_client_deregister(client_hdl); + client_hdl = nullptr; + } + + // Free control transfer if allocated + freeControlTransfer(); + + // Note: usb_host_uninstall() is not called here since the app may have other clients. } bool USBhost::init(bool create_tasks) @@ -60,7 +122,11 @@ bool USBhost::init(bool create_tasks) .intr_flags = ESP_INTR_FLAG_LEVEL1, }; esp_err_t err = usb_host_install(&config); - ESP_LOGI("", "install status: %d", err); + if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { // INVALID_STATE if already installed + ESP_LOGE(TAG, "usb_host_install failed: %d", err); + return false; + } + ESP_LOGI(TAG, "usb_host_install: %d", err); const usb_host_client_config_t client_config = { .is_synchronous = false, @@ -72,11 +138,20 @@ bool USBhost::init(bool create_tasks) }; err = usb_host_client_register(&client_config, &client_hdl); - ESP_LOGI("", "client register status: %d", err); + if (err != ESP_OK) { + ESP_LOGE(TAG, "usb_host_client_register failed: %d", err); + return false; + } + ESP_LOGI(TAG, "client registered"); - if (create_tasks) - { - xTaskCreate(client_async_seq_task, "async", 4 * 512, &client_hdl, 20, NULL); + if (create_tasks) { + BaseType_t ok = xTaskCreate(client_async_seq_task, "usb_async", + 4 * 512, &client_hdl, 20, nullptr); + if (ok != pdPASS) { + ESP_LOGE(TAG, "Failed to create client_async_seq_task"); + // We can still function in synchronous mode, but this class expects async. + return false; + } } return true; @@ -84,40 +159,122 @@ bool USBhost::init(bool create_tasks) bool USBhost::open(const usb_host_client_event_msg_t *event_msg) { + if (!client_hdl || !event_msg) { + ESP_LOGE(TAG, "open: invalid state (client_hdl=%p, event_msg=%p)", client_hdl, event_msg); + return false; + } + + // If we somehow still have a device handle, close it first + if (dev_hdl) { + ESP_LOGW(TAG, "open: closing previous dev handle before opening new device"); + usb_host_device_close(client_hdl, dev_hdl); + dev_hdl = nullptr; + } + esp_err_t err = usb_host_device_open(client_hdl, event_msg->new_dev.address, &dev_hdl); + if (err != ESP_OK) { + ESP_LOGE(TAG, "usb_host_device_open failed: %d (addr=%d)", err, event_msg->new_dev.address); + return false; + } + + _dev_addr = event_msg->new_dev.address; + ESP_LOGI(TAG, "Device opened (addr=%u)", _dev_addr); + + // Optional: parse descriptors immediately for convenience parseConfig(); return true; } +void USBhost::close() +{ + if (!client_hdl) { + ESP_LOGW(TAG, "close: no client"); + return; + } + if (!dev_hdl) { + ESP_LOGI(TAG, "close: no device to close"); + return; + } + + // No need to free control transfer - they are allocated per request + + esp_err_t err = usb_host_device_close(client_hdl, dev_hdl); + if (err != ESP_OK) { + ESP_LOGW(TAG, "usb_host_device_close: %d", err); + } else { + ESP_LOGI(TAG, "Device closed"); + } + dev_hdl = nullptr; + _dev_addr = 0; +} + void USBhost::parseConfig() { - const usb_device_desc_t *device_desc; - usb_host_get_device_descriptor(dev_hdl, &device_desc); - // ESP_LOG_BUFFER_HEX("", device_desc->val, USB_DEVICE_DESC_SIZE); - const usb_config_desc_t *config_desc; - usb_host_get_active_config_descriptor(dev_hdl, &config_desc); + if (!dev_hdl) { + ESP_LOGW(TAG, "parseConfig: no device handle"); + return; + } + + const usb_device_desc_t *device_desc = nullptr; + esp_err_t err = usb_host_get_device_descriptor(dev_hdl, &device_desc); + if (err == ESP_OK && device_desc) { + // ESP_LOG_BUFFER_HEX(TAG, device_desc->val, USB_DEVICE_DESC_SIZE); + ESP_LOGI(TAG, "Device VID:PID = %04x:%04x", + device_desc->idVendor, device_desc->idProduct); + } else { + ESP_LOGW(TAG, "get_device_descriptor failed: %d", err); + } + + const usb_config_desc_t *config_desc = nullptr; + err = usb_host_get_active_config_descriptor(dev_hdl, &config_desc); + if (err == ESP_OK && config_desc) { + // Could parse bNumInterfaces, etc., if useful + _configs = config_desc->bNumInterfaces ? 1 : 0; // active config present + _itfs = config_desc->bNumInterfaces; + ESP_LOGI(TAG, "Active config: itfs=%u", _itfs); + } else { + ESP_LOGW(TAG, "get_active_config_descriptor failed: %d", err); + } } usb_device_info_t USBhost::getDeviceInfo() { - usb_host_device_info(dev_hdl, &dev_info); + if (!dev_hdl) { + ESP_LOGW(TAG, "getDeviceInfo: no device handle"); + memset(&dev_info, 0, sizeof(dev_info)); + return dev_info; + } + esp_err_t err = usb_host_device_info(dev_hdl, &dev_info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "usb_host_device_info: %d", err); + memset(&dev_info, 0, sizeof(dev_info)); + } return dev_info; } const usb_device_desc_t* USBhost::getDeviceDescriptor() { - const usb_device_desc_t *device_desc; - usb_host_get_device_descriptor(dev_hdl, &device_desc); - + if (!dev_hdl) return nullptr; + const usb_device_desc_t *device_desc = nullptr; + esp_err_t err = usb_host_get_device_descriptor(dev_hdl, &device_desc); + if (err != ESP_OK) { + ESP_LOGW(TAG, "getDeviceDescriptor failed: %d", err); + return nullptr; + } return device_desc; } const usb_config_desc_t* USBhost::getConfigurationDescriptor() { - const usb_config_desc_t *config_desc; - usb_host_get_active_config_descriptor(dev_hdl, &config_desc); + if (!dev_hdl) return nullptr; + const usb_config_desc_t *config_desc = nullptr; + esp_err_t err = usb_host_get_active_config_descriptor(dev_hdl, &config_desc); + if (err != ESP_OK) { + ESP_LOGW(TAG, "getConfigurationDescriptor failed: %d", err); + return nullptr; + } return config_desc; } @@ -136,4 +293,294 @@ usb_device_handle_t USBhost::deviceHandle() return dev_hdl; } -// bool USBhost::setConfiguration(uint8_t); +// bool USBhost::setConfiguration(uint8_t); // still not implemented here + +// -------- Control Transfer Methods ---------- + +bool USBhost::allocateControlTransfer(size_t data_buffer_size) +{ + // Deprecated method - transfers are now allocated per request + ESP_LOGW(TAG, "allocateControlTransfer is deprecated - transfers are allocated per request"); + return true; // Always return true since allocation happens in sendControlTransfer +} + +void USBhost::freeControlTransfer() +{ + // Deprecated method - transfers are now freed automatically after completion + ESP_LOGW(TAG, "freeControlTransfer is deprecated - transfers are freed automatically"); +} + +esp_err_t USBhost::sendControlTransfer(const usb_setup_packet_t *setup_pkt, + const void *data, + size_t data_len, + usb_transfer_cb_t callback, + void *context) +{ + if (!dev_hdl) { + ESP_LOGE(TAG, "sendControlTransfer: no device handle"); + return ESP_ERR_INVALID_STATE; + } + + if (!setup_pkt) { + ESP_LOGE(TAG, "sendControlTransfer: null setup packet"); + return ESP_ERR_INVALID_ARG; + } + + // Allocate transfer for this specific control transfer + size_t total_buffer_size = sizeof(usb_setup_packet_t) + data_len; + usb_transfer_t *transfer = nullptr; + esp_err_t err = usb_host_transfer_alloc(total_buffer_size, 0, &transfer); + if (err != ESP_OK) { + ESP_LOGE(TAG, "usb_host_transfer_alloc failed: %d", err); + return err; + } + + // Configure transfer + transfer->device_handle = dev_hdl; + transfer->bEndpointAddress = 0; // Control endpoint + + // Copy setup packet to transfer buffer + memcpy(transfer->data_buffer, setup_pkt, sizeof(usb_setup_packet_t)); + + // Copy data if provided + if (data && data_len > 0) { + memcpy(transfer->data_buffer + sizeof(usb_setup_packet_t), data, data_len); + } + + // Set transfer parameters + transfer->num_bytes = sizeof(usb_setup_packet_t) + data_len; + transfer->callback = callback; + transfer->context = context; + + // Submit the control transfer + err = usb_host_transfer_submit_control(client_hdl, transfer); + if (err != ESP_OK) { + ESP_LOGE(TAG, "usb_host_transfer_submit_control failed: %d", err); + usb_host_transfer_free(transfer); + } else { + ESP_LOGI(TAG, "Control transfer submitted (request: 0x%02x, length: %zu)", + setup_pkt->bRequest, data_len); + } + + return err; +} + +esp_err_t USBhost::sendControlTransfer(uint8_t bmRequestType, uint8_t bRequest, + uint16_t wValue, uint16_t wIndex, uint16_t wLength, + const void *data, + usb_transfer_cb_t callback, + void *context) +{ + usb_setup_packet_t setup_pkt; + setup_pkt.bmRequestType = bmRequestType; + setup_pkt.bRequest = bRequest; + setup_pkt.wValue = wValue; + setup_pkt.wIndex = wIndex; + setup_pkt.wLength = wLength; + + return sendControlTransfer(&setup_pkt, data, wLength, callback, context); +} + +esp_err_t USBhost::getDescriptor(uint8_t desc_type, uint8_t desc_index, + uint16_t lang_id, uint16_t length, + usb_transfer_cb_t callback, void *context) +{ + uint16_t wValue = (desc_type << 8) | desc_index; + return sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE, + USB_B_REQUEST_GET_DESCRIPTOR, + wValue, + lang_id, + length, + nullptr, + callback, + context + ); +} + +esp_err_t USBhost::setAddress(uint8_t address, + usb_transfer_cb_t callback, void *context) +{ + return sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE, + USB_B_REQUEST_SET_ADDRESS, + address, + 0, + 0, + nullptr, + callback, + context + ); +} + +esp_err_t USBhost::setConfiguration(uint8_t config_value, + usb_transfer_cb_t callback, void *context) +{ + return sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE, + USB_B_REQUEST_SET_CONFIGURATION, + config_value, + 0, + 0, + nullptr, + callback, + context + ); +} + +esp_err_t USBhost::getConfiguration(usb_transfer_cb_t callback, void *context) +{ + return sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE, + USB_B_REQUEST_GET_CONFIGURATION, + 0, + 0, + 1, + nullptr, + callback, + context + ); +} + +esp_err_t USBhost::setInterface(uint8_t interface, uint8_t alt_setting, + usb_transfer_cb_t callback, void *context) +{ + return sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_INTERFACE, + USB_B_REQUEST_SET_INTERFACE, + alt_setting, + interface, + 0, + nullptr, + callback, + context + ); +} + +esp_err_t USBhost::getInterface(uint8_t interface, + usb_transfer_cb_t callback, void *context) +{ + return sendControlTransfer( + USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_INTERFACE, + USB_B_REQUEST_GET_INTERFACE, + 0, + interface, + 1, + nullptr, + callback, + context + ); +} + +esp_err_t USBhost::clearFeature(uint8_t recipient, uint8_t feature, uint16_t index, + usb_transfer_cb_t callback, void *context) +{ + uint8_t bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD; + + switch (recipient) { + case USB_BM_REQUEST_TYPE_RECIP_DEVICE: + bmRequestType |= USB_BM_REQUEST_TYPE_RECIP_DEVICE; + break; + case USB_BM_REQUEST_TYPE_RECIP_INTERFACE: + bmRequestType |= USB_BM_REQUEST_TYPE_RECIP_INTERFACE; + break; + case USB_BM_REQUEST_TYPE_RECIP_ENDPOINT: + bmRequestType |= USB_BM_REQUEST_TYPE_RECIP_ENDPOINT; + break; + default: + ESP_LOGE(TAG, "clearFeature: invalid recipient"); + return ESP_ERR_INVALID_ARG; + } + + return sendControlTransfer( + bmRequestType, + USB_B_REQUEST_CLEAR_FEATURE, + feature, + index, + 0, + nullptr, + callback, + context + ); +} + +esp_err_t USBhost::setFeature(uint8_t recipient, uint8_t feature, uint16_t index, + usb_transfer_cb_t callback, void *context) +{ + uint8_t bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD; + + switch (recipient) { + case USB_BM_REQUEST_TYPE_RECIP_DEVICE: + bmRequestType |= USB_BM_REQUEST_TYPE_RECIP_DEVICE; + break; + case USB_BM_REQUEST_TYPE_RECIP_INTERFACE: + bmRequestType |= USB_BM_REQUEST_TYPE_RECIP_INTERFACE; + break; + case USB_BM_REQUEST_TYPE_RECIP_ENDPOINT: + bmRequestType |= USB_BM_REQUEST_TYPE_RECIP_ENDPOINT; + break; + default: + ESP_LOGE(TAG, "setFeature: invalid recipient"); + return ESP_ERR_INVALID_ARG; + } + + return sendControlTransfer( + bmRequestType, + USB_B_REQUEST_SET_FEATURE, + feature, + index, + 0, + nullptr, + callback, + context + ); +} + +esp_err_t USBhost::getStatus(uint8_t recipient, uint16_t index, + usb_transfer_cb_t callback, void *context) +{ + uint8_t bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD; + + switch (recipient) { + case USB_BM_REQUEST_TYPE_RECIP_DEVICE: + bmRequestType |= USB_BM_REQUEST_TYPE_RECIP_DEVICE; + break; + case USB_BM_REQUEST_TYPE_RECIP_INTERFACE: + bmRequestType |= USB_BM_REQUEST_TYPE_RECIP_INTERFACE; + break; + case USB_BM_REQUEST_TYPE_RECIP_ENDPOINT: + bmRequestType |= USB_BM_REQUEST_TYPE_RECIP_ENDPOINT; + break; + default: + ESP_LOGE(TAG, "getStatus: invalid recipient"); + return ESP_ERR_INVALID_ARG; + } + + return sendControlTransfer( + bmRequestType, + USB_B_REQUEST_GET_STATUS, + 0, + index, + 2, // Status is 2 bytes + nullptr, + callback, + context + ); +} + +esp_err_t USBhost::getDeviceDescriptorAsync(usb_transfer_cb_t callback, void *context) +{ + return getDescriptor(USB_W_VALUE_DT_DEVICE, 0, 0, USB_DEVICE_DESC_SIZE, callback, context); +} + +esp_err_t USBhost::getConfigurationDescriptorAsync(uint8_t config_index, uint16_t length, + usb_transfer_cb_t callback, void *context) +{ + return getDescriptor(USB_W_VALUE_DT_CONFIG, config_index, 0, length, callback, context); +} + +esp_err_t USBhost::getStringDescriptorAsync(uint8_t string_index, uint16_t lang_id, uint16_t length, + usb_transfer_cb_t callback, void *context) +{ + return getDescriptor(USB_W_VALUE_DT_STRING, string_index, lang_id, length, callback, context); +} diff --git a/src/sdusb.h b/src/sdusb.h index 452e48d..7013d94 100644 --- a/src/sdusb.h +++ b/src/sdusb.h @@ -1,6 +1,8 @@ #pragma once #include "mscusb.h" +#include "FS.h" #include "SD.h" +#include "SPI.h" #if CFG_TUD_MSC diff --git a/src/usb_host.hpp b/src/usb_host.hpp index 871c1db..8a01a89 100644 --- a/src/usb_host.hpp +++ b/src/usb_host.hpp @@ -15,6 +15,8 @@ class USBhost uint8_t _configs; uint8_t _itfs; + // Control transfer management (no longer using persistent allocation) + public: USBhost(); ~USBhost(); @@ -22,6 +24,7 @@ class USBhost bool init(bool create_tasks = true); bool open(const usb_host_client_event_msg_t *event_msg); + void close(); usb_device_info_t getDeviceInfo(); const usb_device_desc_t* getDeviceDescriptor(); const usb_config_desc_t* getConfigurationDescriptor(); @@ -35,4 +38,57 @@ class USBhost void registerClientCb(usb_host_client_event_cb_t cb) { _client_event_cb = cb; } + // Control transfer methods (deprecated - transfers are now allocated per request) + // These methods are kept for backward compatibility but are no longer needed + bool allocateControlTransfer(size_t data_buffer_size = 64); + void freeControlTransfer(); + + // Basic control transfer methods + esp_err_t sendControlTransfer(const usb_setup_packet_t *setup_pkt, + const void *data = nullptr, + size_t data_len = 0, + usb_transfer_cb_t callback = nullptr, + void *context = nullptr); + + esp_err_t sendControlTransfer(uint8_t bmRequestType, uint8_t bRequest, + uint16_t wValue, uint16_t wIndex, uint16_t wLength, + const void *data = nullptr, + usb_transfer_cb_t callback = nullptr, + void *context = nullptr); + + // Standard USB control requests + esp_err_t getDescriptor(uint8_t desc_type, uint8_t desc_index, + uint16_t lang_id, uint16_t length, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + + esp_err_t setAddress(uint8_t address, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + + esp_err_t setConfiguration(uint8_t config_value, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + + esp_err_t getConfiguration(usb_transfer_cb_t callback = nullptr, void *context = nullptr); + + esp_err_t setInterface(uint8_t interface, uint8_t alt_setting, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + + esp_err_t getInterface(uint8_t interface, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + + esp_err_t clearFeature(uint8_t recipient, uint8_t feature, uint16_t index, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + + esp_err_t setFeature(uint8_t recipient, uint8_t feature, uint16_t index, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + + esp_err_t getStatus(uint8_t recipient, uint16_t index, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + + // Helper methods for common control transfers + esp_err_t getDeviceDescriptorAsync(usb_transfer_cb_t callback = nullptr, void *context = nullptr); + esp_err_t getConfigurationDescriptorAsync(uint8_t config_index, uint16_t length, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + esp_err_t getStringDescriptorAsync(uint8_t string_index, uint16_t lang_id, uint16_t length, + usb_transfer_cb_t callback = nullptr, void *context = nullptr); + }; diff --git a/src/usb_requests.hpp b/src/usb_requests.hpp index 6fa50ec..3fbf3bf 100644 --- a/src/usb_requests.hpp +++ b/src/usb_requests.hpp @@ -12,6 +12,71 @@ #define SET_CONTROL_LINE_STATE 0x22 // #define SERIAL_STATE 0x20 +// Control Transfer Setup Packet Helpers +#define USB_SETUP_PACKET_INIT_GET_STATUS(setup_pkt_ptr) ({ \ + (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \ + (setup_pkt_ptr)->bRequest = USB_B_REQUEST_GET_STATUS; \ + (setup_pkt_ptr)->wValue = 0; \ + (setup_pkt_ptr)->wIndex = 0; \ + (setup_pkt_ptr)->wLength = 2; \ +}) + +#define USB_SETUP_PACKET_INIT_SET_ADDR(setup_pkt_ptr, addr) ({ \ + (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \ + (setup_pkt_ptr)->bRequest = USB_B_REQUEST_SET_ADDRESS; \ + (setup_pkt_ptr)->wValue = (addr); \ + (setup_pkt_ptr)->wIndex = 0; \ + (setup_pkt_ptr)->wLength = 0; \ +}) + +#define USB_SETUP_PACKET_INIT_GET_DEVICE_DESC(setup_pkt_ptr) ({ \ + (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \ + (setup_pkt_ptr)->bRequest = USB_B_REQUEST_GET_DESCRIPTOR; \ + (setup_pkt_ptr)->wValue = (USB_W_VALUE_DT_DEVICE << 8) | 0; \ + (setup_pkt_ptr)->wIndex = 0; \ + (setup_pkt_ptr)->wLength = USB_DEVICE_DESC_SIZE; \ +}) + +#define USB_SETUP_PACKET_INIT_GET_CONFIG(setup_pkt_ptr) ({ \ + (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \ + (setup_pkt_ptr)->bRequest = USB_B_REQUEST_GET_CONFIGURATION; \ + (setup_pkt_ptr)->wValue = 0; \ + (setup_pkt_ptr)->wIndex = 0; \ + (setup_pkt_ptr)->wLength = 1; \ +}) + +#define USB_SETUP_PACKET_INIT_GET_CONFIG_DESC(setup_pkt_ptr, desc_index, desc_len) ({ \ + (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \ + (setup_pkt_ptr)->bRequest = USB_B_REQUEST_GET_DESCRIPTOR; \ + (setup_pkt_ptr)->wValue = (USB_W_VALUE_DT_CONFIG << 8) | (desc_index); \ + (setup_pkt_ptr)->wIndex = 0; \ + (setup_pkt_ptr)->wLength = (desc_len); \ +}) + +#define USB_SETUP_PACKET_INIT_SET_CONFIG(setup_pkt_ptr, config_num) ({ \ + (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \ + (setup_pkt_ptr)->bRequest = USB_B_REQUEST_SET_CONFIGURATION; \ + (setup_pkt_ptr)->wValue = (config_num); \ + (setup_pkt_ptr)->wIndex = 0; \ + (setup_pkt_ptr)->wLength = 0; \ +}) + +#define USB_SETUP_PACKET_INIT_SET_INTERFACE(setup_pkt_ptr, intf_num, alt_setting_num) ({ \ + (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ + (setup_pkt_ptr)->bRequest = USB_B_REQUEST_SET_INTERFACE; \ + (setup_pkt_ptr)->wValue = (alt_setting_num); \ + (setup_pkt_ptr)->wIndex = (intf_num); \ + (setup_pkt_ptr)->wLength = 0; \ +}) + +#define USB_SETUP_PACKET_INIT_GET_STR_DESC(setup_pkt_ptr, string_index, lang_id, desc_len) ({ \ + (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \ + (setup_pkt_ptr)->bRequest = USB_B_REQUEST_GET_DESCRIPTOR; \ + (setup_pkt_ptr)->wValue = (USB_W_VALUE_DT_STRING << 8) | (string_index); \ + (setup_pkt_ptr)->wIndex = (lang_id); \ + (setup_pkt_ptr)->wLength = (desc_len); \ +}) + #define MSC_SCSI_REQ_INIT_RESET(setup_pkt_ptr, intf_num) ({ \ (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ (setup_pkt_ptr)->bRequest = 0xFF; \