Prechádzať zdrojové kódy

Some pipe, port and cdc rework, add HID mouse with example
mouse class and hid parser still WIP

chegewara 3 rokov pred
rodič
commit
5b76abf54c
16 zmenil súbory, kde vykonal 992 pridanie a 145 odobranie
  1. 14 10
      examples/cdc/cdc.ino
  2. 22 0
      examples/cdc/test.ino
  3. 80 0
      examples/mouse/mouse.ino
  4. 28 49
      src/cdchost.cpp
  5. 12 5
      src/cdchost.h
  6. 29 38
      src/configparse.cpp
  7. 13 5
      src/configparse.h
  8. 1 1
      src/hcd.c
  9. 185 0
      src/mousepipe.cpp
  10. 54 0
      src/mousepipe.h
  11. 347 0
      src/parse_hid.cpp
  12. 1 0
      src/parse_hid.h
  13. 125 15
      src/pipe.cpp
  14. 25 9
      src/pipe.h
  15. 37 7
      src/port.cpp
  16. 19 6
      src/port.h

+ 14 - 10
examples/cdc/cdc.ino

@@ -2,31 +2,34 @@
 #define DEVICE_ADDRESS 2
 USBHostCDC port(DEVICE_ADDRESS);
 
-void ctrl_pipe_cb(usb_host_even_t event, void *data)
+void usbh_cdc_device_ready()
+{
+  port.setControlLine(1, 1);
+  port.setLineCoding(115200, 0, 0, 5);
+  port.inpipe->inData();
+}
+
+void ctrl_pipe_cb(ext_pipe_event_msg_t event, usb_irp_t *irp)
 {
   Serial.printf("CTRL EVENT: 0x%x\n", event);
 }
 
-void port_cb(port_event_msg_t msg, USBHostPort* port)
+void port_cb(port_event_msg_t msg, USBHostPort *port)
 {
   Serial.printf("PORT EVENT: 0x%x\n", msg.port_event);
 }
 
-void cdc_datain_cb(usb_host_even_t event, void *data)
+void cdc_datain_cb(ext_pipe_event_msg_t event, usb_irp_t *irp)
 {
-  usb_irp_t *irp = (usb_irp_t *)data;
-  Serial.printf("IN EVENT: 0x%x, buffer_len: %d, received: %d\n", event, irp->num_bytes, irp->actual_num_bytes);
-  Serial.print("DATA: ");
   for (size_t i = 0; i < irp->actual_num_bytes; i++)
   {
     Serial.printf("%c", irp->data_buffer[i]);
   }
-  Serial.println();
 }
 
-void cdc_dataout_cb(usb_host_even_t event, void *data)
+// this is optional callback, it is just a status check and/or echo from low level stack
+void cdc_dataout_cb(ext_pipe_event_msg_t event, usb_irp_t *irp)
 {
-  usb_irp_t *irp = (usb_irp_t *)data;
   Serial.printf("OUT EVENT: 0x%x, buffer_len: %d, sent: %d\n", event, irp->num_bytes, irp->actual_num_bytes);
   Serial.print("DATA: ");
   for (size_t i = 0; i < irp->actual_num_bytes; i++)
@@ -39,11 +42,11 @@ void cdc_dataout_cb(usb_host_even_t event, void *data)
 void setup()
 {
   Serial.begin(115200);
-  port.init();
   port.onPortEvent(port_cb);
   port.onControlEvent(ctrl_pipe_cb);
   port.onDataIn(cdc_datain_cb);
   port.onDataOut(cdc_dataout_cb);
+  port.init();
 }
 
 void loop()
@@ -51,6 +54,7 @@ void loop()
   delay(10);
   while (Serial.available())
   {
+    test_strings();
     size_t l = Serial.available();
     uint8_t b[l];
     l = Serial.read(b, l);

+ 22 - 0
examples/cdc/test.ino

@@ -0,0 +1,22 @@
+
+void test_strings()
+{
+    port.getSerialString();
+    delay(10);
+    port.getProductString();
+    delay(10);
+    port.getManufacturerString();
+}
+
+void onSerialString(char *str)
+{
+  Serial.println(str);
+}
+void onProductString(char *str)
+{
+  Serial.println(str);
+}
+void onManufacturerString(char *str)
+{
+  Serial.println(str);
+}

+ 80 - 0
examples/mouse/mouse.ino

@@ -0,0 +1,80 @@
+#include "port.h"
+// #include "pipe.h"
+#include "mousepipe.h"
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+#endif
+#include "parse_hid.h"
+
+
+#define DEVICE_ADDRESS 2
+
+USBHostMouse port(2);
+
+extern void parse_hid_report_map(uint8_t *map, size_t size);
+
+void usbh_mouse_device_ready()
+{
+  port.setIdle();
+  port.getHidReportMap();
+}
+
+void ctrl_pipe_cb(ext_pipe_event_msg_t event, usb_irp_t *irp)
+{
+  Serial.printf("CTRL EVENT: 0x%x\n", event);
+  if (event == 0xA206)
+  {
+    port.inpipe->inData();
+  }
+}
+
+/**
+ * hid report with buttons, wheel and move
+ */
+static void hid_datain_cb(ext_pipe_event_msg_t event, usb_irp_t *irp)
+{
+    switch (event)
+    {
+    case HCD_PIPE_EVENT_IRP_DONE:
+        ESP_LOGD("Pipe cdc: ", "XFER status: %d, num bytes: %d, actual bytes: %d", irp->status, irp->num_bytes, irp->actual_num_bytes);
+        // ESP_LOG_BUFFER_HEX("", irp->data_buffer, irp->num_bytes);
+        ESP_LOGI("", "HID REPORT ID: %d", irp->data_buffer[0]);
+        ESP_LOGI("", "Mouse buttons: %d", irp->data_buffer[1]);
+        ESP_LOGI("", "X axis(raw bytes): %i", (int8_t)irp->data_buffer[2]);
+        ESP_LOGI("", "Y axis(raw bytes): %i", (int8_t)irp->data_buffer[4]);
+        ESP_LOGI("", "Mouse wheel: %i\n", (int8_t)irp->data_buffer[5]);
+
+        // ESP_LOGI("", "Mouse buttons: %d", get_buttons(irp->data_buffer));
+        // ESP_LOGI("", "X axis(raw bytes): %i", get_x_axis(irp->data_buffer));
+        // ESP_LOGI("", "Y axis(raw bytes): %i", get_y_axis(irp->data_buffer));
+        // ESP_LOGI("", "Mouse wheel: %i\n", get_wheel(irp->data_buffer));
+        break;
+
+    }
+}
+
+void port_cb(port_event_msg_t msg, USBHostPort *port)
+{
+  Serial.printf("PORT EVENT: 0x%x\n", msg.port_event);
+}
+
+void setup()
+{
+  Serial.begin(115200);
+  if(port.init()){
+    port.onPortEvent(port_cb);
+    port.onControlEvent(ctrl_pipe_cb);
+    port.onDataIn(hid_datain_cb);
+  }
+}
+
+void loop()
+{
+    delay(1000);
+    // hid_mouse_t * mouse = get_mouse_struct();
+    // Serial.printf("buttons first bits: %d, bits count: %d\n", mouse->buttons.bits_start + 8, (mouse->buttons.count));
+    // Serial.printf("axes first bits: %d, bits count: %d\n", mouse->axes.bits_start + 8, (mouse->axes.count) * mouse->axes.size);
+    // Serial.printf("wheel first bits: %d, bits count: %d\n", mouse->wheel.bits_start + 8, (mouse->wheel.count) * mouse->wheel.size);
+}

+ 28 - 49
src/cdchost.cpp

@@ -1,23 +1,22 @@
 #include <cstring>
 #include "cdchost.h"
 #include "configparse.h"
-#include "Arduino.h"
 
 /**
- * ADD set of custom USB host events
+ * //IDEA ADD set of custom USB host events
  * add callbacks with custom events in ctrl pipe
  * 
  * 
  * 
  * 
- * 
- * dont change order of callbacks
+ */ 
+ /** dont change order of callbacks
  * port and EPs/pipes local callbacks
  */
 IRAM_ATTR static void ctrl_pipe_cb(pipe_event_msg_t msg, usb_irp_t *irp, USBHostPipe *pipe)
 {
     usb_ctrl_req_t *ctrl = (usb_ctrl_req_t *)irp->data_buffer;
-    usb_host_even_t event = msg.event + BASE_PIPE_EVENT;
+    ext_pipe_event_msg_t event = msg.event + BASE_PIPE_EVENT;
     switch (msg.event)
     {
     case HCD_PIPE_EVENT_NONE:
@@ -35,68 +34,45 @@ IRAM_ATTR static void ctrl_pipe_cb(pipe_event_msg_t msg, usb_irp_t *irp, USBHost
                 if (irp->actual_num_bytes)
                 {
                     USBHostCDC *p = (USBHostCDC *)pipe->port;
-                    p->begin(irp->data_buffer);
+                    if (p->begin(irp->data_buffer))
+                    {
+                        usbh_cdc_device_ready();
+                    }
                 }
             }
         }
         else if (ctrl->bRequest == USB_B_REQUEST_GET_CONFIGURATION)
         {
-            event = BASE_PIPE_EVENT + ctrl->bRequest;
-            ESP_LOGD("", "get current configuration: %d", irp->data_buffer[8]);
         }
         else if (ctrl->bRequest == USB_B_REQUEST_SET_CONFIGURATION)
         {
-            event = BASE_PIPE_EVENT + ctrl->bRequest;
-            ESP_LOGD("", "set current configuration: %d", ctrl->wValue);
-            pipe->getConfigDescriptor();
         }
         else if (ctrl->bRequest == USB_B_REQUEST_SET_ADDRESS)
         {
-            event = BASE_PIPE_EVENT + ctrl->bRequest;
-            ESP_LOGD("", "address set: %d", ctrl->wValue);
-            pipe->updateAddress(ctrl->wValue);
-            pipe->setConfiguration(1);
         }
-        else if (ctrl->bRequestType == SET_VALUE && ctrl->bRequest == SET_LINE_CODING) // set line coding
+        // CDC CLASS specific events
+        else if (ctrl->bRequestType == SET_VALUE && ctrl->bRequest == SET_LINE_CODING)
         {
-            event = BASE_PIPE_EVENT + (ctrl->bRequestType << 4) + ctrl->bRequest;
+            event = BASE_PIPE_EVENT + CDC_BASE_PIPE_EVENT + (ctrl->bRequestType << 4) + ctrl->bRequest;
             line_coding_t *data = (line_coding_t *)(irp->data_buffer + sizeof(usb_ctrl_req_t));
             ESP_LOGD("Set line coding", "Bitrate: %d, stop bits: %d, parity: %d, bits: %d",
                      data->dwDTERate, data->bCharFormat, data->bParityType, data->bDataBits);
         }
-        else if (ctrl->bRequestType == GET_VALUE && ctrl->bRequest == GET_LINE_CODING) // get line coding
+        else if (ctrl->bRequestType == GET_VALUE && ctrl->bRequest == GET_LINE_CODING)
         {
-            event = BASE_PIPE_EVENT + (ctrl->bRequestType << 4) + ctrl->bRequest;
+            event = BASE_PIPE_EVENT + CDC_BASE_PIPE_EVENT + (ctrl->bRequestType << 4) + ctrl->bRequest;
             line_coding_t *data = (line_coding_t *)(irp->data_buffer + sizeof(usb_ctrl_req_t));
             ESP_LOGD("Get line coding", "Bitrate: %d, stop bits: %d, parity: %d, bits: %d",
                      data->dwDTERate, data->bCharFormat, data->bParityType, data->bDataBits);
         }
-        else if (ctrl->bRequestType == SET_VALUE && ctrl->bRequest == SET_CONTROL_LINE_STATE) // set line coding
+        else if (ctrl->bRequestType == SET_VALUE && ctrl->bRequest == SET_CONTROL_LINE_STATE)
         {
-            event = BASE_PIPE_EVENT + (ctrl->bRequestType << 4) + ctrl->bRequest;
+            event = BASE_PIPE_EVENT + CDC_BASE_PIPE_EVENT + (ctrl->bRequestType << 4) + ctrl->bRequest;
             ESP_LOGD("Set control line state", "");
         }
-        else
-        {
-            ESP_LOG_BUFFER_HEX_LEVEL("Ctrl data", ctrl, sizeof(usb_ctrl_req_t), ESP_LOG_WARN);
-            ESP_LOGD("", "unknown request handled");
-        }
 
         break;
     }
-    case HCD_PIPE_EVENT_ERROR_XFER:
-        ESP_LOGW("", "XFER error: %d", irp->status);
-        pipe->reset();
-        break;
-
-    case HCD_PIPE_EVENT_ERROR_STALL:
-        ESP_LOG_BUFFER_HEX_LEVEL("Ctrl data", ctrl, sizeof(usb_ctrl_req_t), ESP_LOG_INFO);
-        pipe->reset();
-        break;
-
-    default:
-        ESP_LOGW("", "not handled pipe event: %d", msg.event);
-        break;
     }
 
     if (pipe->port->ctrl_callback != NULL)
@@ -128,7 +104,7 @@ IRAM_ATTR static void port_cb(port_event_msg_t msg, USBHostPort *p)
         break;
     }
     case HCD_PORT_EVENT_SUDDEN_DISCONN:
-        // this is called before event in port.cpp to delete all pipes here to properly recover port later in port.cpp
+        // OPTIMIZE this is called before event in port.cpp to delete all pipes here to properly recover port later in port.cpp
         if (port->inpipe)
             delete (port->inpipe);
         if (port->outpipe)
@@ -205,12 +181,12 @@ void USBHostCDC::init()
 }
 
 // initialize endpoints after getting config descriptor
-void USBHostCDC::begin(uint8_t *irp)
+bool USBHostCDC::begin(uint8_t *irp)
 {
     USBHostConfigParser parser;
-    uint8_t *itf0 = parser.getInterfaceByClass(irp, 0x02);
-    uint8_t *itf1 = parser.getInterfaceByClass(irp, 0x0A);
-    if (itf1)
+    // uint8_t *itf0 = parser.getInterfaceByClass(irp, USB_CLASS_COMM); // TODO add INTR endpoint
+    uint8_t *itf1 = parser.getInterfaceByClass(irp, USB_CLASS_CDC_DATA);
+    if (itf1 != nullptr)
     {
         uint8_t *ep = parser.getEndpointByDirection(itf1, 1);
         if (ep)
@@ -230,10 +206,10 @@ void USBHostCDC::begin(uint8_t *irp)
             outpipe->port = this;
         }
 
-        setControlLine(1, 1);
-        setLineCoding(115200, 0, 0, 5);
-        inpipe->inData();
+        if (inpipe != nullptr && outpipe != nullptr)
+            return true;
     }
+    return false;
 }
 
 // CDC class control pipe requests
@@ -301,12 +277,12 @@ void USBHostCDC::sendData(uint8_t *data, size_t len)
 }
 
 // IN/OUT callbacks
-void USBHostCDC::onDataIn(usb_host_event_cb_t cb)
+void USBHostCDC::onDataIn(ext_usb_pipe_cb_t cb)
 {
     data_in = cb;
 }
 
-void USBHostCDC::onDataOut(usb_host_event_cb_t cb)
+void USBHostCDC::onDataOut(ext_usb_pipe_cb_t cb)
 {
     data_out = cb;
 }
@@ -324,3 +300,6 @@ void USBHostCDC::intrData()
     //     ESP_LOGW("", "INTR enqueue err: 0x%x", err);
     // }
 }
+
+USBH_WEAK_CB void usbh_cdc_device_ready(){}
+

+ 12 - 5
src/cdchost.h

@@ -55,6 +55,7 @@ typedef union
         (ctrl_req_ptr)->data.bParityType = (parity);                                       \
         (ctrl_req_ptr)->data.bDataBits = (bits);                                           \
     })
+
 /**
  * @brief Initializer for a GET_LINE_CODING request
  *
@@ -83,12 +84,15 @@ typedef union
         (ctrl_req_ptr)->wLength = (0);                                           \
     })
 
+void usbh_cdc_device_ready();
+
 class USBHostCDC : public USBHostPort
 {
 private:
 public:
+    USBHostCDC() : USBHostPort() {}
     USBHostCDC(uint8_t addr) : USBHostPort(addr) {}
-    void begin(uint8_t *);
+    bool begin(uint8_t *);
     void init();
 
     // ----------- CDC related requests ----------- //
@@ -109,6 +113,7 @@ public:
     void getLineCoding();
 
     // ---------------- IN/OUT data related functions -------------------- //
+    // IDEA add handling INTR
     /**
      * @brief Enqueue INTR in request
      */
@@ -119,19 +124,21 @@ public:
      */
     void sendData(uint8_t *, size_t);
 
+    // OPTIMIZE move to parent class??
     /**
      * @brief Register on data IN callback
      */
-    void onDataIn(usb_host_event_cb_t);
+    void onDataIn(ext_usb_pipe_cb_t);
 
     /**
      * @brief Register on data OUT callback
      */
-    void onDataOut(usb_host_event_cb_t);
+    void onDataOut(ext_usb_pipe_cb_t);
 
+    // OPTIMIZE make them private or protected
     // class related callbacks and pipes
-    usb_host_event_cb_t data_in;
-    usb_host_event_cb_t data_out;
+    ext_usb_pipe_cb_t data_in;
+    ext_usb_pipe_cb_t data_out;
     USBHostPipe *inpipe;
     USBHostPipe *outpipe;
 };

+ 29 - 38
src/configparse.cpp

@@ -13,56 +13,54 @@
 #include "esp_log.h"
 #include "configparse.h"
 
-#define USB_W_VALUE_DT_HID 0x22
-#define USB_W_VALUE_DT_CS_INTERFACE 0x24
 
-static char *class_to_str(uint8_t cls)
+char *USBHostConfigParser::class_to_str(uint8_t cls)
 {
     switch (cls)
     {
-    case 0x00:
+    case USB_CLASS_PER_INTERFACE:
         return ">ifc";
-    case 0x01:
+    case USB_CLASS_AUDIO:
         return "Audio";
-    case 0x02:
+    case USB_CLASS_COMM:
         return "CDC";
-    case 0x03:
+    case USB_CLASS_HID:
         return "HID";
-    case 0x05:
+    case USB_CLASS_PHYSICAL:
         return "Physical";
-    case 0x06:
+    case USB_CLASS_STILL_IMAGE:
         return "Image";
-    case 0x07:
+    case USB_CLASS_PRINTER:
         return "Printer";
-    case 0x08:
+    case USB_CLASS_MASS_STORAGE:
         return "Mass Storage";
-    case 0x09:
+    case USB_CLASS_HUB:
         return "Hub";
-    case 0x0a:
+    case USB_CLASS_CDC_DATA:
         return "CDC-data";
-    case 0x0b:
+    case USB_CLASS_CSCID:
         return "Smart card";
-    case 0x0d:
+    case USB_CLASS_CONTENT_SEC:
         return "Content security";
-    case 0x0e:
+    case USB_CLASS_VIDEO:
         return "Video";
-    case 0x0f:
+    case USB_CLASS_PERSONAL_HEALTHCARE:
         return "Personal heathcare";
-    case 0x10:
+    case USB_CLASS_AUDIO_VIDEO:
         return "Audio/Vdeo devices";
-    case 0x11:
+    case USB_CLASS_BILLBOARD:
         return "Bilboard";
-    case 0x12:
+    case USB_CLASS_USB_TYPE_C_BRIDGE:
         return "USB-C bridge";
     case 0xdc:
         return "Diagnostic device";
-    case 0xe0:
+    case USB_CLASS_WIRELESS_CONTROLLER:
         return "Wireless controller";
-    case 0xef:
+    case USB_CLASS_MISC:
         return "Miscellaneous";
-    case 0xfe:
+    case USB_CLASS_APP_SPEC:
         return "Application specific";
-    case 0xff:
+    case USB_CLASS_VENDOR_SPEC:
         return "Vendor specific";
 
     default:
@@ -70,16 +68,16 @@ static char *class_to_str(uint8_t cls)
     }
 }
 
-static void utf16_to_utf8(char *in, char *out, uint8_t len)
+void USBHostConfigParser::utf16_to_utf8(uint8_t *in, char *out, uint8_t len)
 {
     for (size_t i = 0; i < len; i++)
     {
-        out[i / 2] = in[i];
-        i++;
+        // FIXME
+        out[i / 2] = in[i++];
     }
 }
 
-static uint8_t *parse_cfg_descriptor(uint8_t *data_buffer, uint8_t **out, uint8_t *_type)
+uint8_t* USBHostConfigParser::parse_cfg_descriptor(uint8_t *data_buffer, uint8_t **out, uint8_t *_type)
 {
     uint8_t offset = 0;
     uint8_t type = *(&data_buffer[0] + offset + 1);
@@ -113,7 +111,7 @@ static uint8_t *parse_cfg_descriptor(uint8_t *data_buffer, uint8_t **out, uint8_
             len = data->bLength;
             offset += len;
             char *str = (char *)calloc(1, len);
-            utf16_to_utf8((char *)&data->val[2], str, len);
+            utf16_to_utf8(&data->val[2], str, len);
             ESP_LOGV("", "strings: %s", str);
             free(str);
             break;
@@ -173,14 +171,6 @@ static uint8_t *parse_cfg_descriptor(uint8_t *data_buffer, uint8_t **out, uint8_
     return (uint8_t *)data_buffer + offset;
 }
 
-USBHostConfigParser::USBHostConfigParser()
-{
-}
-
-USBHostConfigParser::~USBHostConfigParser()
-{
-}
-
 uint8_t* USBHostConfigParser::getInterfaceByClass(uint8_t *data, uint8_t cls)
 {
     uint8_t *ep;
@@ -228,8 +218,9 @@ uint8_t* USBHostConfigParser::getEndpointByDirection(uint8_t *interface, uint8_t
     return NULL;
 }
 
-void USBHostConfigParser::getString()
+void USBHostConfigParser::getString(uint8_t *in, char *out, uint8_t len)
 {
+    return utf16_to_utf8(in, out, len);
 }
 
 

+ 13 - 5
src/configparse.h

@@ -4,16 +4,24 @@
 #else
 #include "esp_log.h"
 #endif
+#define USB_W_VALUE_DT_HID 0x22
+#define USB_W_VALUE_DT_CS_INTERFACE 0x24
+
 class USBHostConfigParser
 {
 private:
     
 public:
-    USBHostConfigParser();
-    ~USBHostConfigParser();
+    USBHostConfigParser() {}
+    ~USBHostConfigParser() {}
+
+    // IDEA add more functions, like get all interfaces, all endpoints as array etc, get endpoint by address
+    static uint8_t* getInterfaceByClass(uint8_t *data, uint8_t cls = 0);
+    static uint8_t* getEndpointByDirection(uint8_t* interface, uint8_t dir = 0);
+    static void getString(uint8_t *in, char *out, uint8_t len);
 
-    uint8_t* getInterfaceByClass(uint8_t *data, uint8_t cls = 0);
-    uint8_t* getEndpointByDirection(uint8_t* interface, uint8_t dir = 0);
-    void getString();
+    static char* class_to_str(uint8_t cls);
+    static void utf16_to_utf8(uint8_t *in, char *out, uint8_t len);
+    static uint8_t* parse_cfg_descriptor(uint8_t *data_buffer, uint8_t **out, uint8_t *_type);
 };
 

+ 1 - 1
src/hcd.c

@@ -2104,7 +2104,7 @@ static inline void _buffer_fill_intr(dma_buffer_block_t *buffer, usb_irp_t *irp,
         num_qtds = irp->num_bytes / mps;
     } else {
         num_qtds = irp->num_bytes / mps;    //Floor division for number of MPS packets
-        if (irp->num_bytes % irp->num_bytes > 0) {
+        if (irp->num_bytes % mps > 0) {
             num_qtds++; //For the last shot packet
         }
     }

+ 185 - 0
src/mousepipe.cpp

@@ -0,0 +1,185 @@
+#include <cstring>
+#include "configparse.h"
+#include "mousepipe.h"
+
+extern void parse_hid_report_map(uint8_t *map, size_t size);
+
+ /** dont change order of callbacks
+ * port and EPs/pipes local callbacks
+ */
+IRAM_ATTR static void ctrl_pipe_cb(pipe_event_msg_t msg, usb_irp_t *irp, USBHostPipe *pipe)
+{
+    usb_ctrl_req_t *ctrl = (usb_ctrl_req_t *)irp->data_buffer;
+    ext_pipe_event_msg_t event = msg.event + BASE_PIPE_EVENT;
+    switch (msg.event)
+    {
+    case HCD_PIPE_EVENT_NONE:
+        break;
+    case HCD_PIPE_EVENT_IRP_DONE:
+    {
+        ESP_LOGD("Pipe: ", "XFER status: %d, num bytes: %d, actual bytes: %d", irp->status, irp->num_bytes, irp->actual_num_bytes);
+        usb_ctrl_req_t *ctrl = (usb_ctrl_req_t *)irp->data_buffer;
+
+        if (ctrl->bRequest == USB_B_REQUEST_GET_DESCRIPTOR)
+        {
+            event = BASE_PIPE_EVENT + ctrl->bRequest + (ctrl->wValue);
+            if (ctrl->wValue == USB_W_VALUE_DT_CONFIG << 8)
+            {
+                if (irp->actual_num_bytes)
+                {
+                    USBHostMouse *p = (USBHostMouse *)pipe->port;
+                    if (p->begin(irp->data_buffer))
+                    {
+                        usbh_mouse_device_ready();
+                    }
+                } else if ((ctrl->wValue >> 8) == 0x22) // HID report map descriptor
+                {
+                    ESP_LOGI("", "HID report map");
+                    for (int i = 0; i < (irp->actual_num_bytes + 8); i++)
+                    {
+                        ets_printf("%02x ", irp->data_buffer[i]);
+                    }
+                    parse_hid_report_map(irp->data_buffer + 8, irp->actual_num_bytes);
+                }
+                
+            }
+        }
+        else if (ctrl->bRequest == USB_B_REQUEST_GET_CONFIGURATION)
+        {
+        }
+        else if (ctrl->bRequest == USB_B_REQUEST_SET_CONFIGURATION)
+        {
+        }
+        else if (ctrl->bRequest == USB_B_REQUEST_SET_ADDRESS)
+        {
+        }
+        // HID CLASS specific events
+
+        break;
+    }
+    }
+
+    if (pipe->port->ctrl_callback != NULL)
+    {
+        pipe->port->ctrl_callback(event, irp);
+    }
+}
+
+IRAM_ATTR static void port_cb(port_event_msg_t msg, USBHostPort *p)
+{
+    static USBHostMouse *port = (USBHostMouse *)p;
+    hcd_port_state_t state;
+    switch (msg.port_event)
+    {
+    case HCD_PORT_EVENT_CONNECTION:
+    {
+        ESP_LOGD("", "physical connection detected");
+        // we are ready to establish connection with device
+        if (port->connect())
+        {
+            // create ctrl callback
+            USBHostPipe *pipe = new USBHostPipe();
+            pipe->port = port;
+            port->addCTRLpipe(pipe);
+            pipe->onPipeEvent(ctrl_pipe_cb);
+            pipe->getDeviceDescriptor();
+            pipe->setDeviceAddress(port->address);
+        }
+        break;
+    }
+    case HCD_PORT_EVENT_SUDDEN_DISCONN:
+        // OPTIMIZE this is called before event in port.cpp to delete all pipes here to properly recover port later in port.cpp
+        if (port->inpipe)
+            delete (port->inpipe);
+        break;
+
+    default:
+        ESP_LOGW("", "port event: %d", msg.port_event);
+        break;
+    }
+}
+
+static void in_pipe_cb(pipe_event_msg_t msg, usb_irp_t *irp, USBHostPipe *pipe)
+{
+    if (((USBHostMouse *)pipe->port)->data_in != NULL)
+    {
+        ESP_LOGV("", "callback IN: %d", irp->status);
+        ((USBHostMouse *)pipe->port)->data_in(msg.event, irp);
+    }
+    switch (msg.event)
+    {
+    case HCD_PIPE_EVENT_IRP_DONE:
+        ESP_LOGD("Pipe cdc: ", "XFER status: %d, num bytes: %d, actual bytes: %d", irp->status, irp->num_bytes, irp->actual_num_bytes);
+        break;
+
+    case HCD_PIPE_EVENT_ERROR_XFER:
+        ESP_LOGW("", "XFER error: %d", irp->status);
+        pipe->reset();
+        break;
+
+    case HCD_PIPE_EVENT_ERROR_STALL:
+        ESP_LOGW("", "Device stalled CDC pipe, state: %d", pipe->getState());
+        pipe->reset();
+        break;
+
+    default:
+        break;
+    }
+    // previous IRP dequeued, time to send another request
+    pipe->inData();
+}
+
+bool USBHostMouse::init()
+{
+    return USBHostPort::init(port_cb);
+}
+
+// initialize endpoints after getting config descriptor
+bool USBHostMouse::begin(uint8_t *irp)
+{
+    USBHostConfigParser parser;
+    uint8_t *itf1 = parser.getInterfaceByClass(irp, USB_CLASS_HID);
+    if (itf1 != nullptr)
+    {
+        uint8_t *ep = parser.getEndpointByDirection(itf1, 1);
+        if (ep)
+        {
+            inpipe = new USBHostPipe(handle);
+            inpipe->onPipeEvent(in_pipe_cb);
+            inpipe->init((usb_desc_ep_t *)ep, address);
+            inpipe->port = this;
+        }
+
+        if (inpipe != nullptr)
+            return true;
+    }
+    return false;
+}
+
+void USBHostMouse::setIdle()
+{
+    usb_irp_t *irp = ctrlPipe->allocateIRP(0);
+    USB_CTRL_SET_IDLE((usb_ctrl_req_t *)irp->data_buffer);
+
+    esp_err_t err;
+    if(ESP_OK != (err = hcd_irp_enqueue(ctrlPipe->getHandle(), irp))) {
+        ESP_LOGW("", "SET_IDLE enqueue err: 0x%x", err);
+    }
+}
+
+void USBHostMouse::getHidReportMap()
+{
+    usb_irp_t *irp = ctrlPipe->allocateIRP(70);
+    USB_CTRL_GET_HID_REPORT_DESC((usb_ctrl_req_t *)irp->data_buffer, 0, 70);
+
+    esp_err_t err;
+    if(ESP_OK != (err = hcd_irp_enqueue(ctrlPipe->getHandle(), irp))) {
+        ESP_LOGW("", "SET_IDLE enqueue err: 0x%x", err);
+    }
+}
+
+
+void USBHostMouse::onDataIn(ext_usb_pipe_cb_t cb)
+{
+    data_in = cb;
+}

+ 54 - 0
src/mousepipe.h

@@ -0,0 +1,54 @@
+#pragma once
+#include "hcd.h"
+#include "usb.h"
+#include "port.h"
+#include "pipe.h"
+
+#define SET_VALUE       0x21
+#define SET_IDLE        0x0A
+
+#define USB_CTRL_SET_IDLE(ctrl_req_ptr) ({  \
+    (ctrl_req_ptr)->bRequestType = SET_VALUE;   \
+    (ctrl_req_ptr)->bRequest = SET_IDLE;  \
+    (ctrl_req_ptr)->wValue = (0x0);   \
+    (ctrl_req_ptr)->wIndex = (0);    \
+    (ctrl_req_ptr)->wLength = (0);   \
+})
+
+#define USB_CTRL_GET_HID_REPORT_DESC(ctrl_req_ptr, desc_index, desc_len) ({  \
+    (ctrl_req_ptr)->bRequestType = USB_B_REQUEST_TYPE_DIR_IN | USB_B_REQUEST_TYPE_TYPE_STANDARD | USB_B_REQUEST_TYPE_RECIP_INTERFACE;   \
+    (ctrl_req_ptr)->bRequest = USB_B_REQUEST_GET_DESCRIPTOR;   \
+    (ctrl_req_ptr)->wValue = (0x22<<8) | ((desc_index) & 0xFF); \
+    (ctrl_req_ptr)->wIndex = 0;    \
+    (ctrl_req_ptr)->wLength = (desc_len);  \
+})
+
+void usbh_mouse_device_ready();
+
+// TODO
+// FIXME refactor class to inherit from port class
+class USBHostMouse : public USBHostPort
+{
+private:
+
+public:
+    USBHostMouse() : USBHostPort() {}
+    USBHostMouse(uint8_t addr) : USBHostPort(addr) {}
+    bool begin(uint8_t *);
+    bool init();
+    void setIdle();
+    void getHidReportMap();
+
+    // OPTIMIZE move to parent class??
+    /**
+     * @brief Register on data IN callback
+     */
+    void onDataIn(ext_usb_pipe_cb_t);
+
+    // OPTIMIZE make them private or protected
+    // class related callbacks and pipes
+    ext_usb_pipe_cb_t data_in;
+    USBHostPipe *inpipe;
+};
+
+// USBH_WEAK_CB void usbh_mouse_device_ready(){}

+ 347 - 0
src/parse_hid.cpp

@@ -0,0 +1,347 @@
+#include "parse_hid.h"
+#include <stdio.h>
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+#endif
+
+static uint8_t global_item = 0;
+static uint8_t local_item = 0;
+static uint8_t usage_page_item = 0;
+static uint8_t usage_item = 0;
+static uint8_t report_count = 0;
+static uint8_t report_size = 0;
+static uint8_t reportID = 0;
+static uint16_t report_bits = 0;
+
+static hid_mouse_t mouse;
+static bool set_x, set_y, set_wheel, set_buttons;
+
+static void setup_buttons()
+{
+    mouse.buttons.bits_start = report_bits;
+    mouse.buttons.count += report_count * report_size;
+    mouse.buttons.size = report_size;
+    ets_printf("buttons => start bits: %d, count: %d, size: %d\n", report_bits, report_count, report_size);
+    report_bits += report_count * report_size;
+    set_buttons = false;
+}
+
+static void setup_x_axis()
+{
+    mouse.axes.bits_start = report_bits;
+    mouse.axes.count = report_count;
+    mouse.axes.size = report_size;
+    ets_printf("X => start bits: %d, count: %d, size: %d\n", report_bits, report_count, report_size);
+    report_bits += report_size * report_count;
+    set_x = false;
+}
+
+static void setup_y_axis()
+{
+    mouse.axes.bits_start = report_bits;
+    mouse.axes.count = report_count;
+    mouse.axes.size = report_size;
+    ets_printf("Y => start bits: %d, count: %d, size: %d\n", report_bits, report_count, report_size);
+    report_bits += report_size;
+    set_y = false;
+}
+
+static void setup_wheel()
+{
+    mouse.wheel.bits_start = report_bits;
+    mouse.wheel.count = report_count;
+    mouse.wheel.size = report_size;
+    ets_printf("wheel => start bits: %d, count: %d, size: %d\n", report_bits, report_count, report_size);
+    report_bits += report_size;
+    set_wheel = false;
+}
+
+static void setup_input_device()
+{
+    switch (usage_page_item)
+    {
+    case GENERIC_DESKTOP_POINTER:
+        ets_printf("generic desktop pointer\n");
+        if(set_x) setup_x_axis();
+        // if(set_y) setup_y_axis();
+        if(set_buttons) setup_buttons();
+        if(set_wheel) setup_wheel();
+
+        break;
+
+    case GENERIC_DESKTOP_MOUSE:
+        ets_printf("generic desktop mouse\n");
+        // setup_mouse();
+        break;
+
+    case GENERIC_DESKTOP_JOYSTICK:
+        ets_printf("generic desktop joystick\n");
+        break;
+
+    case GENERIC_DESKTOP_GAMEPAD:
+        ets_printf("generic desktop gamepad\n");
+        break;
+
+    case GENERIC_DESKTOP_KEYBAORD:
+        ets_printf("generic desktop keyboard\n");
+        break;
+
+    case GENERIC_DESKTOP_KEYPAD:
+        ets_printf("generic desktop keypad\n");
+        break;
+
+    case GENERIC_DESKTOP_MULTIAXIS:
+        ets_printf("generic desktop multi-axis\n");
+        break;
+
+    case GENERIC_DESKTOP_BUTTON:
+        ets_printf("generic desktop button\n");
+        set_buttons = true;
+        setup_buttons();
+        break;
+
+    default:
+        break;
+    }
+}
+
+inline void parse_usage_page(uint32_t item)
+{
+    usage_page_item = item;
+}
+
+inline void parse_usage(uint32_t item)
+{
+    usage_item = item;
+    switch (usage_item)
+    {
+    case GENERIC_DESKTOP_X:
+        ets_printf("generic desktop X axis\n");
+        set_x = true;
+        break;
+
+    case GENERIC_DESKTOP_Y:
+        ets_printf("generic desktop Y axis\n");
+        set_y = true;
+        break;
+
+    case GENERIC_DESKTOP_WHEEL:
+        ets_printf("generic desktop wheel\n");
+        set_wheel = true;
+        break;
+
+    default:
+        break;
+    }
+}
+
+static void parse_global_item(uint8_t type, uint8_t len, uint8_t *val)
+{
+    uint32_t value = 0;
+    for (size_t i = 0; i < len; i++)
+    {
+        value += val[i] << (i * 8);
+    }
+
+    // ets_printf("global type: %02x, len: %d, value: %d\n", type, len, value);
+    switch (type)
+    {
+    case USAGE_PAGE(0):
+        parse_usage_page(value);
+        break;
+    case LOGICAL_MINIMUM(0):
+        break;
+    case LOGICAL_MAXIMUM(0):
+        break;
+    case 0x34:
+        break;
+    case 0x44:
+        break;
+    case 0x54:
+        break;
+    case 0x64:
+        break;
+    case 0x74:
+        report_size = value;
+        break;
+    case 0x84:
+        reportID = *val;
+        break;
+    case 0x94:
+        report_count = value;
+        break;
+    case 0xa4:
+        break;
+    case 0xb4:
+        break;
+
+    default:
+        break;
+    }
+}
+
+static void parse_local_item(uint8_t type, uint8_t len, uint8_t *val)
+{
+    uint32_t value = 0;
+    for (size_t i = 0; i < len; i++)
+    {
+        value += val[i] << (i * 8);
+    }
+
+    // ets_printf("local type: %02x, len: %d, value: %d\n", type, len, value);
+    switch (type)
+    {
+    case 0x08:
+        parse_usage(value);
+        break;
+    case 0x18:
+        break;
+    case 0x28:
+        break;
+    case 0x38:
+        break;
+    case 0x48:
+        break;
+    case 0x58:
+        break;
+    case 0x68:
+        break;
+    case 0x78:
+        break;
+    case 0x88:
+        break;
+    case 0x98:
+        break;
+    case 0xA8:
+        break;
+
+    default:
+        break;
+    }
+}
+
+inline void parse_feature_item(uint8_t type, uint8_t len, uint8_t *val)
+{
+    uint32_t value = 0;
+    for (size_t i = 0; i < len; i++)
+    {
+        value += val[i] << (i * 8);
+    }
+
+    // ets_printf("feature type: %02x, len: %d, value: %d\n", type, len, value);
+    switch (type)
+    {
+    case 0x80:
+        setup_input_device();
+        break;
+    case 0x90:
+        break;
+    case 0xA0:
+        break;
+    case 0xB0:
+        break;
+    case 0xC0:
+        break;
+
+    default:
+        break;
+    }
+}
+
+void parse_hid_report_map(uint8_t *map, size_t size)
+{
+    ESP_LOGI("", "size: %d", size);
+    for (size_t i = 0; i < size; i++)
+    {
+        uint8_t type = map[i] & 0xfc;
+        uint8_t len = map[i] & 0x03;
+        uint8_t *value = &map[i + 1];
+
+        if (type & (1 << 2))
+        {
+            parse_global_item(type, len, value);
+        }
+        else if (type & (1 << 3))
+        {
+            parse_local_item(type, len, value);
+        }
+        else
+        {
+            parse_feature_item(type, len, value);
+        }
+
+        i += len;
+    }
+}
+
+hid_mouse_t *get_mouse_struct()
+{
+    return &mouse;
+}
+
+uint16_t bitmask(uint8_t i, uint8_t count)
+{
+    uint16_t val = 0;
+    for (i = 0; i < count; i++)
+    {
+        val |= 1<<i;
+    }
+    return val;
+}
+
+// always add 1 bytes, is byte is always reportID
+uint8_t get_buttons(uint8_t *data)
+{
+    uint8_t start = 1 + mouse.buttons.bits_start / 8;    
+    return data[start];
+}
+
+int16_t get_x_axis(uint8_t *data)
+{
+    uint16_t value = 0;
+    uint8_t start = 1 + mouse.axes.bits_start / 8;
+    uint8_t bits = mouse.axes.count * mouse.axes.size; // 2 * 12 = 24
+    uint8_t n = bits / 8;
+    for (size_t i = 0; i < n; i++)
+    {
+        value += data[start + i] << ((n-i-1) * 8);
+    }
+
+    ets_printf("X => start: %d, bits: %d, n: %d, value: %d\n", start, bits, n, value);
+    return ((value >> (bits - mouse.axes.size)) | (value & 1<<(mouse.axes.size-1)));
+}
+
+int16_t get_y_axis(uint8_t *data)
+{
+    uint16_t value = 0;
+    uint8_t start = 1 + mouse.axes.bits_start / 8;
+    uint8_t bits = mouse.axes.count * mouse.axes.size; // 3 * 8 = 24
+    uint8_t n = bits / 8;
+    for (size_t i = 0; i < n; i++)
+    {
+        value += data[start + i] << ((n-i-1) * 8);
+    }
+
+    ets_printf("Y => start: %d, bits: %d, n: %d, value: %d\n", start, bits, n, value);
+    return (((value & (1<< mouse.axes.size)) >> (bits - mouse.axes.size * 2)) | (value & 1<<(mouse.axes.size-1)));
+}
+
+int8_t get_wheel(uint8_t *data)
+{
+    uint16_t value = 0;
+    uint8_t start = 1 + mouse.wheel.bits_start / 8;
+    uint8_t bits = mouse.wheel.count * mouse.wheel.size; // 3 * 8 = 24
+    uint8_t n = bits / 8;
+    for (size_t i = 0; i < n; i++)
+    {
+        value += data[start + i] << ((n-i-1) * 8);
+    }
+
+    ets_printf("wheel => start: %d, bits: %d, n: %d, value: %d\n", start, bits, n, value);
+    return ((value >> (bits - mouse.wheel.size)) | (value & 1<<(mouse.wheel.size-1)));
+}
+
+

+ 1 - 0
src/parse_hid.h

@@ -1,4 +1,5 @@
 #pragma once
+#include <stdio.h>
 
 #define HIDINPUT(size) (0x80 | size)
 #define HIDOUTPUT(size) (0x90 | size)

+ 125 - 15
src/pipe.cpp

@@ -1,25 +1,104 @@
 #include <cstring>
 #include "pipe.h"
 #include "usb.h"
+#include "configparse.h"
 
 static void pipe_event_task(void *p)
 {
-    USBHostPipe *ctx = (USBHostPipe *)p;
+    // IDEA pointer passed to task is pipe object the task belongs to
+    USBHostPipe *ctrl_pipe = (USBHostPipe *)p;
 
     pipe_event_msg_t msg;
     while (1)
     {
-        xQueueReceive(ctx->pipeEvtQueue, &msg, portMAX_DELAY);
+        xQueueReceive(ctrl_pipe->pipeEvtQueue, &msg, portMAX_DELAY);
         usb_irp_t *irp = hcd_irp_dequeue(msg.handle);
         if (irp == NULL)
             continue;
 
-        if (ctx && ctx->callback != NULL)
+        usb_ctrl_req_t *ctrl = (usb_ctrl_req_t *)irp->data_buffer;
+        uint8_t* data = irp->data_buffer + sizeof(usb_ctrl_req_t);
+
+        switch (msg.event)
+        {
+        case HCD_PIPE_EVENT_NONE:
+            break;
+        case HCD_PIPE_EVENT_IRP_DONE:
         {
-            ctx->callback(msg, irp, ctx);
+            ESP_LOGD("Pipe: ", "XFER status: %d, num bytes: %d, actual bytes: %d", irp->status, irp->num_bytes, irp->actual_num_bytes);
+            usb_ctrl_req_t *ctrl = (usb_ctrl_req_t *)irp->data_buffer;
+
+            if (ctrl->bRequest == USB_B_REQUEST_GET_DESCRIPTOR)
+            {
+                if (ctrl->wValue == (USB_W_VALUE_DT_DEVICE << 8))
+                {
+                    ctrl_pipe->setDeviceDesc(data);
+                } else if ((ctrl->wValue >> 8) & 0xff)
+                {
+                    char out[irp->actual_num_bytes] = {};
+                    USBHostConfigParser parser;
+                    parser.getString(data, out, irp->actual_num_bytes);
+                    uint8_t str_id = ctrl->wValue & 0x0F;
+                    if (str_id == ctrl_pipe->device_desc.iSerialNumber)
+                    {
+                        ESP_LOGV("", "string iSerialNumber: %d", str_id);
+                        onSerialString(out);
+                    }
+                    else if (str_id == ctrl_pipe->device_desc.iProduct)
+                    {
+                        ESP_LOGV("", "string iProduct: %d", str_id);
+                        onProductString(out);
+                    } else if (str_id == ctrl_pipe->device_desc.iManufacturer)
+                    {
+                        ESP_LOGV("", "string iManufacturer: %d", str_id);
+                        onManufacturerString(out);
+                    }
+                }
+            }
+            else if (ctrl->bRequest == USB_B_REQUEST_GET_CONFIGURATION)
+            {
+                ESP_LOGD("", "get current configuration: %d", irp->data_buffer[8]);
+            }
+            else if (ctrl->bRequest == USB_B_REQUEST_SET_CONFIGURATION)
+            {
+                ESP_LOGD("", "set current configuration: %d", ctrl->wValue);
+                ctrl_pipe->getConfigDescriptor(255);
+            }
+            else if (ctrl->bRequest == USB_B_REQUEST_SET_ADDRESS)
+            {
+                ESP_LOGD("", "address set: %d", ctrl->wValue);
+                ctrl_pipe->updateAddress(ctrl->wValue);
+                ctrl_pipe->setConfiguration(1);
+            }
+            else
+            {
+                ESP_LOG_BUFFER_HEX_LEVEL("Ctrl data", ctrl, sizeof(usb_ctrl_req_t), ESP_LOG_WARN);
+                ESP_LOGD("", "unknown request handled: %d", ctrl->bRequest);
+            }
+
+            break;
         }
+        case HCD_PIPE_EVENT_ERROR_XFER:
+            ESP_LOGW("", "XFER error: %d", irp->status);
+            ctrl_pipe->reset();
+            break;
+
+        case HCD_PIPE_EVENT_ERROR_STALL:
+            ESP_LOG_BUFFER_HEX_LEVEL("Ctrl data", ctrl, sizeof(usb_ctrl_req_t), ESP_LOG_INFO);
+            ctrl_pipe->reset();
+            break;
 
-        ctx->freeIRP(irp);
+        default:
+            ESP_LOGW("", "not handled pipe event: %d", msg.event);
+            break;
+        }
+
+        if (ctrl_pipe && ctrl_pipe->callback != NULL)
+        {
+            ctrl_pipe->callback(msg, irp, ctrl_pipe);
+        }
+
+        ctrl_pipe->freeIRP(irp);
     }
 }
 
@@ -65,7 +144,8 @@ void USBHostPipe::init(usb_desc_ep_t *ep_desc, uint8_t addr)
 {
     // TODO add config parse object to check if it is actually endpoint data
     usb_speed_t port_speed;
-    if(port_hdl == NULL)ESP_LOGE("", "FAILED");
+    if (port_hdl == NULL)
+        ESP_LOGE("", "FAILED");
     if (ESP_OK != hcd_port_get_speed(port_hdl, &port_speed))
     {
         return;
@@ -81,7 +161,7 @@ void USBHostPipe::init(usb_desc_ep_t *ep_desc, uint8_t addr)
     hcd_pipe_config_t config = {
         .callback = pipeCallback,
         .callback_arg = (void *)pipeEvtQueue,
-        .context = (void *)this,
+        .context = this,
         .ep_desc = ep_desc,
         .dev_speed = port_speed,
         .dev_addr = addr,
@@ -201,12 +281,12 @@ void USBHostPipe::setConfiguration(uint8_t cfg)
     }
 }
 
-void USBHostPipe::getConfigDescriptor()
+void USBHostPipe::getConfigDescriptor(size_t len)
 {
-    usb_irp_t *irp = allocateIRP(TRANSFER_DATA_MAX_BYTES);
+    usb_irp_t *irp = allocateIRP(len);
     if (irp == NULL)
         return;
-    USB_CTRL_REQ_INIT_GET_CFG_DESC((usb_ctrl_req_t *)irp->data_buffer, 0, TRANSFER_DATA_MAX_BYTES);
+    USB_CTRL_REQ_INIT_GET_CFG_DESC((usb_ctrl_req_t *)irp->data_buffer, 0, len);
 
     //Enqueue those transfer requests
     esp_err_t err;
@@ -264,20 +344,23 @@ void USBHostPipe::freeIRP(usb_irp_t *irp)
 
 void USBHostPipe::inData(size_t size)
 {
-    ESP_LOGV("", "EP: 0x%02x", USB_DESC_EP_GET_ADDRESS(endpoint));
+    ESP_LOGV("", "EP: 0x%02x", USB_DESC_EP_GET_EP_NUM(&endpoint));
+    if(!USB_DESC_EP_GET_EP_DIR(&endpoint)) return;
     size_t len = endpoint.wMaxPacketSize;
-    usb_irp_t *irp = allocateIRP(size? size:len);
+    usb_irp_t *irp = allocateIRP(size ? size : len);
 
     esp_err_t err;
-    if(ESP_OK != (err = hcd_irp_enqueue(handle, irp))) {
-        ESP_LOGW("", "IN endpoint 0x%02x enqueue err: 0x%x", USB_DESC_EP_GET_ADDRESS(endpoint), err);
+    if (ESP_OK != (err = hcd_irp_enqueue(handle, irp)))
+    {
+        ESP_LOGW("", "IN endpoint 0x%02x enqueue err: 0x%x", USB_DESC_EP_GET_EP_NUM(&endpoint), err);
     }
 }
 
 void USBHostPipe::outData(uint8_t *data, size_t len)
 {
+    ESP_LOGV("", "EP: 0x%02x", USB_DESC_EP_GET_EP_NUM(&endpoint));
+    if(USB_DESC_EP_GET_EP_DIR(&endpoint)) return;
     usb_irp_t *irp = allocateIRP(len);
-    ESP_LOGV("", "EP: 0x%02x", USB_DESC_EP_GET_ADDRESS(endpoint));
     memcpy(irp->data_buffer, data, len);
 
     esp_err_t err;
@@ -291,3 +374,30 @@ hcd_pipe_handle_t USBHostPipe::getHandle()
 {
     return handle;
 }
+
+void USBHostPipe::setDeviceDesc(uint8_t* data)
+{
+    memcpy(&device_desc, data, 18);
+}
+
+void USBHostPipe::getSerialString()
+{
+    getString(device_desc.iSerialNumber);
+}
+
+void USBHostPipe::getProductString()
+{
+    getString(device_desc.iProduct);
+}
+
+void USBHostPipe::getManufacturerString()
+{
+    getString(device_desc.iManufacturer);
+}
+
+
+
+//----------------- WEAK callbacks ---------------//
+USBH_WEAK_CB void onSerialString(char* str){}
+USBH_WEAK_CB void onProductString(char* str){}
+USBH_WEAK_CB void onManufacturerString(char* str){}

+ 25 - 9
src/pipe.h

@@ -13,10 +13,12 @@
 #else
 #include "esp_log.h"
 #endif
-#define TRANSFER_DATA_MAX_BYTES 256 //Just assume that will only IN/OUT 64 bytes for now
-#define USB_DESC_EP_GET_ADDRESS(desc_ptr) ((desc_ptr).bEndpointAddress & 0x7F)
-#define BASE_PIPE_EVENT 0x1000
-#define CDC_BASE_PIPE_EVENT 0x2000
+#define TRANSFER_DATA_MAX_BYTES 255 //Just assume that will only IN/OUT 64 bytes for now
+
+#define BASE_PIPE_EVENT 0x8000
+#define CDC_BASE_PIPE_EVENT 0x1000
+#define HID_BASE_PIPE_EVENT 0x2000
+#define USBH_WEAK_CB __attribute__((weak))
 
 /**
  * @brief Build get string request
@@ -44,6 +46,10 @@ class USBHostPort;
  */
 typedef void (*pipe_cb_t)(pipe_event_msg_t msg, usb_irp_t *irp, USBHostPipe *context);
 
+void onSerialString(char* str);
+void onProductString(char* str);
+void onManufacturerString(char* str);
+
 class USBHostPipe
 {
 protected:
@@ -53,8 +59,8 @@ protected:
     hcd_port_handle_t port_hdl;
     // 
     xTaskHandle taskHandle;
-
 public:
+    // friend void pipe_event_task(void *p);
     // 
     usb_desc_ep_t endpoint;
     // 
@@ -62,6 +68,9 @@ public:
     // 
     QueueHandle_t pipeEvtQueue;
 
+    usb_desc_devc_t device_desc;
+
+
     USBHostPipe(hcd_port_handle_t handle = nullptr);
     ~USBHostPipe();
     // master port which this pipe belongs to
@@ -117,9 +126,6 @@ public:
      */
     hcd_pipe_handle_t getHandle();
 
-    // pipes are IN or OUT, but it is better to have functions in base class
-    // laterr need to add assert if pipe is IN/OUT
-
     /**
      * @brief Send data IN request
      */
@@ -154,10 +160,20 @@ public:
     /**
      * @brief Prepare and enqueue get configuration descriptor request
      */
-    void getConfigDescriptor();
+    void getConfigDescriptor(size_t n = 9);
 
     /**
      * @brief Prepare and enqueue get string by id request
      */
     void getString(uint8_t);
+
+    void setDeviceDesc(uint8_t* data);
+
+    void getSerialString();
+    void getProductString();
+    void getManufacturerString();
 };
+
+// TODO add abort IRP
+
+

+ 37 - 7
src/port.cpp

@@ -78,10 +78,12 @@ static void port_event_task(void *p)
 }
 
 // ------------------------------------------------ CLASS ---------------------------------------------------
+bool USBHostPort::port_ready = false;
 
 USBHostPort::USBHostPort()
 {
     address = 1;
+    port_evt_queue = xQueueCreate(5, sizeof(port_event_msg_t));
 }
 
 USBHostPort::USBHostPort(uint8_t addr) : address(addr)
@@ -91,8 +93,13 @@ USBHostPort::USBHostPort(uint8_t addr) : address(addr)
 
 USBHostPort::~USBHostPort() {}
 
-void USBHostPort::init(port_evt_cb_t cb)
+bool USBHostPort::init(port_evt_cb_t cb)
 {
+    if (port_ready == true || port_evt_queue == NULL)
+    {
+        return false;
+    }
+    
     hcd_config_t config = {
         .intr_flags = ESP_INTR_FLAG_LEVEL1,
     };
@@ -112,7 +119,7 @@ void USBHostPort::init(port_evt_cb_t cb)
 
             phy_force_conn_state(false, 0); //Force disconnected state on PHY
             if (ESP_OK != powerON())
-                return;
+                return false;
             ESP_LOGI("", "Port is power ON now");
             phy_force_conn_state(true, pdMS_TO_TICKS(10)); //Allow for connected state on PHY
             // return true;
@@ -120,17 +127,19 @@ void USBHostPort::init(port_evt_cb_t cb)
         else
         {
             ESP_LOGE("", "Error to init port: %d!!!", err);
-            return;
+            return false;
         }
     }
     else
     {
         ESP_LOGE("", "Error to install HCD!!!");
-        return;
+        return false;
     }
 
+    port_ready = true;
     xTaskCreate(port_event_task, "port_task", 3 * 1024, this, 16, NULL);
     callback = cb;
+    return true;
 }
 
 bool USBHostPort::connect()
@@ -155,12 +164,11 @@ void USBHostPort::onPortEvent(port_evt_cb_t cb)
     _port_cb = cb;
 }
 
-void USBHostPort::onControlEvent(usb_host_event_cb_t cb)
+void USBHostPort::onControlEvent(ext_usb_pipe_cb_t cb)
 {
     ctrl_callback = cb;
 }
 
-
 hcd_port_handle_t USBHostPort::getHandle()
 {
     return handle;
@@ -201,7 +209,9 @@ esp_err_t USBHostPort::powerON()
 
 void USBHostPort::powerOFF()
 {
-    ESP_LOGD(__func__, "%d", hcd_port_command(handle, HCD_PORT_CMD_POWER_OFF));
+    // FIXME
+    hcd_port_command(handle, HCD_PORT_CMD_POWER_OFF);
+    ESP_LOGD(__func__, "%d", 0);
 }
 
 void USBHostPort::suddenDisconnect()
@@ -237,3 +247,23 @@ void USBHostPort::addCTRLpipe(USBHostPipe *pipe)
     ctrlPipe->updatePort(handle);
     ctrlPipe->init();
 }
+
+/**
+ * Standard control requests
+ */
+void USBHostPort::getSerialString()
+{
+    getCTRLpipe()->getSerialString();
+}
+
+void USBHostPort::getProductString()
+{
+    getCTRLpipe()->getProductString();
+}
+
+void USBHostPort::getManufacturerString()
+{
+    getCTRLpipe()->getManufacturerString();
+}
+
+

+ 19 - 6
src/port.h

@@ -18,10 +18,9 @@
 #include "esp_log.h"
 #endif
 
-#define USBH_WEAK_CB __attribute__((weak))
 #define PORT_NUM 1 // we have only 1 port
 
-typedef uint16_t usb_host_even_t;
+typedef uint16_t ext_pipe_event_msg_t;
 
 typedef struct
 {
@@ -38,7 +37,7 @@ typedef void (*port_evt_cb_t)(port_event_msg_t msg, USBHostPort *port);
 /**
  * @brief Pipe events callback for user space
  */
-typedef void (*usb_host_event_cb_t)(usb_host_even_t event, void *data);
+typedef void (*ext_usb_pipe_cb_t)(ext_pipe_event_msg_t event, usb_irp_t *data);
 
 class USBHostPort
 {
@@ -47,6 +46,7 @@ protected:
     hcd_port_handle_t handle;
     // control pipe
     USBHostPipe *ctrlPipe;
+    static bool port_ready;
 
 public:
     uint8_t address = 0;
@@ -58,7 +58,7 @@ public:
     /**
      * @brief Initialize USB host port and create low level port callback task
      */
-    void init(port_evt_cb_t cb);
+    bool init(port_evt_cb_t cb);
 
     /**
      * @brief Call this function when physical connection is detected to properly reset port
@@ -123,7 +123,7 @@ public:
     /**
      * @brief Add control pipe callback for user space
      */
-    void onControlEvent(usb_host_event_cb_t cb);
+    void onControlEvent(ext_usb_pipe_cb_t cb);
 
     /**
      * @brief Get port handle
@@ -134,6 +134,19 @@ public:
     /**
      * @brief cllbacks
      */
-    usb_host_event_cb_t ctrl_callback;
+    ext_usb_pipe_cb_t ctrl_callback;
     port_evt_cb_t _port_cb;
+
+
+    // CTRL pipe calls
+    void getSerialString();
+    void getProductString();
+    void getManufacturerString();
 };
+// TODO add suspend, resume and disable port
+// TODO add port fifo bias function
+// TODO add HCD uninstall and deinit
+// 
+
+
+