163 lines
4.7 KiB
C++
163 lines
4.7 KiB
C++
#pragma once
|
|
|
|
// Should not be needed, but it's required to pass CI clang-tidy checks
|
|
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
|
#include "esphome/core/component.h"
|
|
#include <vector>
|
|
#include "usb/usb_host.h"
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/task.h>
|
|
#include "esphome/core/lock_free_queue.h"
|
|
#include "esphome/core/event_pool.h"
|
|
#include <list>
|
|
|
|
namespace esphome {
|
|
namespace usb_host {
|
|
|
|
static const char *const TAG = "usb_host";
|
|
|
|
// Forward declarations
|
|
struct TransferRequest;
|
|
class USBClient;
|
|
|
|
// constants for setup packet type
|
|
static const uint8_t USB_RECIP_DEVICE = 0;
|
|
static const uint8_t USB_RECIP_INTERFACE = 1;
|
|
static const uint8_t USB_RECIP_ENDPOINT = 2;
|
|
static const uint8_t USB_TYPE_STANDARD = 0 << 5;
|
|
static const uint8_t USB_TYPE_CLASS = 1 << 5;
|
|
static const uint8_t USB_TYPE_VENDOR = 2 << 5;
|
|
static const uint8_t USB_DIR_MASK = 1 << 7;
|
|
static const uint8_t USB_DIR_IN = 1 << 7;
|
|
static const uint8_t USB_DIR_OUT = 0;
|
|
static const size_t SETUP_PACKET_SIZE = 8;
|
|
|
|
static const size_t MAX_REQUESTS = 16; // maximum number of outstanding requests possible.
|
|
static constexpr size_t USB_EVENT_QUEUE_SIZE = 32; // Size of event queue between USB task and main loop
|
|
static constexpr size_t USB_TASK_STACK_SIZE = 4096; // Stack size for USB task (same as ESP-IDF USB examples)
|
|
static constexpr UBaseType_t USB_TASK_PRIORITY = 5; // Higher priority than main loop (tskIDLE_PRIORITY + 5)
|
|
|
|
// used to report a transfer status
|
|
struct TransferStatus {
|
|
bool success;
|
|
uint16_t error_code;
|
|
uint8_t *data;
|
|
size_t data_len;
|
|
uint8_t endpoint;
|
|
void *user_data;
|
|
};
|
|
|
|
using transfer_cb_t = std::function<void(const TransferStatus &)>;
|
|
|
|
class USBClient;
|
|
|
|
// struct used to capture all data needed for a transfer
|
|
struct TransferRequest {
|
|
usb_transfer_t *transfer;
|
|
transfer_cb_t callback;
|
|
TransferStatus status;
|
|
USBClient *client;
|
|
};
|
|
|
|
enum EventType : uint8_t {
|
|
EVENT_DEVICE_NEW,
|
|
EVENT_DEVICE_GONE,
|
|
EVENT_TRANSFER_COMPLETE,
|
|
EVENT_CONTROL_COMPLETE,
|
|
};
|
|
|
|
struct UsbEvent {
|
|
EventType type;
|
|
union {
|
|
struct {
|
|
uint8_t address;
|
|
} device_new;
|
|
struct {
|
|
usb_device_handle_t handle;
|
|
} device_gone;
|
|
struct {
|
|
TransferRequest *trq;
|
|
} transfer;
|
|
} data;
|
|
|
|
// Required for EventPool - no cleanup needed for POD types
|
|
void release() {}
|
|
};
|
|
|
|
// callback function type.
|
|
|
|
enum ClientState {
|
|
USB_CLIENT_INIT = 0,
|
|
USB_CLIENT_OPEN,
|
|
USB_CLIENT_CLOSE,
|
|
USB_CLIENT_GET_DESC,
|
|
USB_CLIENT_GET_INFO,
|
|
USB_CLIENT_CONNECTED,
|
|
};
|
|
class USBClient : public Component {
|
|
friend class USBHost;
|
|
|
|
public:
|
|
USBClient(uint16_t vid, uint16_t pid) : vid_(vid), pid_(pid) { init_pool(); }
|
|
|
|
void init_pool() {
|
|
this->trq_pool_.clear();
|
|
for (size_t i = 0; i != MAX_REQUESTS; i++)
|
|
this->trq_pool_.push_back(&this->requests_[i]);
|
|
}
|
|
void setup() override;
|
|
void loop() override;
|
|
// setup must happen after the host bus has been setup
|
|
float get_setup_priority() const override { return setup_priority::IO; }
|
|
void on_opened(uint8_t addr);
|
|
void on_removed(usb_device_handle_t handle);
|
|
void control_transfer_callback(const usb_transfer_t *xfer) const;
|
|
void transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length);
|
|
void transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length);
|
|
void dump_config() override;
|
|
void release_trq(TransferRequest *trq);
|
|
bool control_transfer(uint8_t type, uint8_t request, uint16_t value, uint16_t index, const transfer_cb_t &callback,
|
|
const std::vector<uint8_t> &data = {});
|
|
|
|
// Lock-free event queue and pool for USB task to main loop communication
|
|
// Must be public for access from static callbacks
|
|
LockFreeQueue<UsbEvent, USB_EVENT_QUEUE_SIZE> event_queue;
|
|
EventPool<UsbEvent, USB_EVENT_QUEUE_SIZE> event_pool;
|
|
|
|
protected:
|
|
bool register_();
|
|
TransferRequest *get_trq_();
|
|
virtual void disconnect();
|
|
virtual void on_connected() {}
|
|
virtual void on_disconnected() { this->init_pool(); }
|
|
|
|
// USB task management
|
|
static void usb_task_fn(void *arg);
|
|
void usb_task_loop();
|
|
|
|
TaskHandle_t usb_task_handle_{nullptr};
|
|
|
|
usb_host_client_handle_t handle_{};
|
|
usb_device_handle_t device_handle_{};
|
|
int device_addr_{-1};
|
|
int state_{USB_CLIENT_INIT};
|
|
uint16_t vid_{};
|
|
uint16_t pid_{};
|
|
std::list<TransferRequest *> trq_pool_{};
|
|
TransferRequest requests_[MAX_REQUESTS]{};
|
|
};
|
|
class USBHost : public Component {
|
|
public:
|
|
float get_setup_priority() const override { return setup_priority::BUS; }
|
|
void loop() override;
|
|
void setup() override;
|
|
|
|
protected:
|
|
std::vector<USBClient *> clients_{};
|
|
};
|
|
|
|
} // namespace usb_host
|
|
} // namespace esphome
|
|
|
|
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
|