summaryrefslogtreecommitdiff
path: root/src/usbh_driver_hub.c
diff options
context:
space:
mode:
authorAmir Hammad <amir.hammad@hotmail.com>2015-04-01 16:22:05 +0200
committerAmir Hammad <amir.hammad@hotmail.com>2015-04-01 16:22:05 +0200
commit7acc6fe474766788687a5257be21ac549bed77f3 (patch)
tree859e621840f9362e60fec116b7528ad16202871f /src/usbh_driver_hub.c
downloadsecure-hid-7acc6fe474766788687a5257be21ac549bed77f3.tar.gz
secure-hid-7acc6fe474766788687a5257be21ac549bed77f3.tar.bz2
secure-hid-7acc6fe474766788687a5257be21ac549bed77f3.zip
libusbhost: Open source USB host stack for embedded devices
First public version, date: 1.4.2015 Signed-off-by: Amir Hammad <amir.hammad@hotmail.com>
Diffstat (limited to 'src/usbh_driver_hub.c')
-rw-r--r--src/usbh_driver_hub.c865
1 files changed, 865 insertions, 0 deletions
diff --git a/src/usbh_driver_hub.c b/src/usbh_driver_hub.c
new file mode 100644
index 0000000..c82eacd
--- /dev/null
+++ b/src/usbh_driver_hub.c
@@ -0,0 +1,865 @@
+/*
+ * This file is part of the libusbhost library
+ * hosted at http://github.com/libusbhost/libusbhost
+ *
+ * Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
+ *
+ *
+ * libusbhost is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "usbh_driver_hub_private.h"
+#include "driver/usbh_device_driver.h"
+#include "usart_helpers.h"
+#include "usbh_config.h"
+
+#include <stdint.h>
+
+
+static hub_device_t hub_device[USBH_MAX_HUBS];
+
+static void *hub_init(void *usbh_dev);
+static bool hub_analyze_descriptor(void *drvdata, void *descriptor);
+static void hub_poll(void *drvdata, uint32_t tflp);
+static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data);
+static void hub_remove(void *drvdata);
+
+static const usbh_dev_driver_info_t usbh_hub_driver_info = {
+ .deviceClass = 0x09,
+ .deviceSubClass = -1,
+ .deviceProtocol = -1,
+ .idVendor = -1,
+ .idProduct = -1,
+ .ifaceClass = 0x09,
+ .ifaceSubClass = -1,
+ .ifaceProtocol = -1
+};
+
+const usbh_dev_driver_t usbh_hub_driver = {
+ .init = hub_init,
+ .analyze_descriptor = hub_analyze_descriptor,
+ .poll = hub_poll,
+ .remove = hub_remove,
+ .info = &usbh_hub_driver_info
+};
+
+
+
+void hub_driver_init(void)
+{
+ uint32_t i;
+
+ for (i = 0; i < USBH_MAX_HUBS; i++) {
+ hub_device[i].device[0] = 0;
+ hub_device[i].ports_num = 0;
+ hub_device[i].current_port = -1;
+ }
+}
+
+
+/**
+ *
+ *
+ */
+static void *hub_init(void *usbh_dev)
+{
+ uint32_t i;
+ hub_device_t *drvdata = 0;
+ // find free data space for hub device
+ for (i = 0; i < USBH_MAX_HUBS; i++) {
+ if (hub_device[i].device[0] == 0) {
+ break;
+ }
+ }
+ LOG_PRINTF("%{%d}",i);
+ LOG_FLUSH();
+ if (i == USBH_MAX_HUBS) {
+ LOG_PRINTF("ERRRRRRR");
+ return 0;
+ }
+
+ drvdata = &hub_device[i];
+ drvdata->state = 0;
+ drvdata->ports_num = 0;
+ drvdata->device[0] = usbh_dev;
+ drvdata->busy = 0;
+ drvdata->endpoint_in_address = 0;
+ drvdata->endpoint_in_maxpacketsize = 0;
+
+// for (i = 1; i < USBH_MAX_HUBS + 1; i++) {
+// drvdata->device[i]->address = 0;
+// drvdata->device[i]->state = 0;
+// }
+ return drvdata;
+}
+
+/**
+ * Returns true if all needed data are parsed
+ */
+static bool hub_analyze_descriptor(void *drvdata, void *descriptor)
+{
+ hub_device_t *hub = drvdata;
+ uint8_t desc_type = ((uint8_t *)descriptor)[1];
+ switch (desc_type) {
+ case USB_DT_CONFIGURATION:
+ {
+ struct usb_config_descriptor *cfg = (struct usb_config_descriptor*)descriptor;
+ hub->buffer[0] = cfg->bConfigurationValue;
+ }
+ break;
+
+ case USB_DT_ENDPOINT:
+ {
+ struct usb_endpoint_descriptor *ep = (struct usb_endpoint_descriptor *)descriptor;
+ if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) {
+ uint8_t epaddr = ep->bEndpointAddress;
+ if (epaddr & (1<<7)) {
+ hub->endpoint_in_address = epaddr&0x7f;
+ hub->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
+ }
+ }
+ LOG_PRINTF("ENDPOINT DESCRIPTOR FOUND\r\n");
+ }
+ break;
+
+ case USB_DT_HUB:
+ {
+ struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)descriptor;
+ //~ hub->ports_num = desc->head.bNbrPorts;
+ if ( desc->head.bNbrPorts <= USBH_HUB_MAX_DEVICES) {
+ hub->ports_num = desc->head.bNbrPorts;
+ } else {
+ LOG_PRINTF("INCREASE NUMBER OF ENABLED PORTS\r\n");
+ hub->ports_num = USBH_HUB_MAX_DEVICES;
+ }
+ LOG_PRINTF("HUB DESCRIPTOR FOUND \r\n");
+ }
+ break;
+
+ default:
+ LOG_PRINTF("TYPE: %02X \r\n",desc_type);
+ break;
+ }
+
+ if (hub->endpoint_in_address) {
+ hub->state = 1;
+ LOG_PRINTF("end enum");
+ return true;
+ }
+ return false;
+}
+
+// Enumerate
+static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
+{
+ //~ usbh_device_t *dev = arg;
+ hub_device_t *hub = dev->drvdata;
+
+ LOG_PRINTF("\r\nHUB->STATE = %d\r\n", hub->state);
+ switch (hub->state) {
+ case 26:
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ {
+ uint8_t i;
+ uint8_t *buf = hub->buffer;
+ uint32_t psc = 0; // Limit: up to 4 bytes...
+ for (i = 0; i < cb_data.transferred_length; i++) {
+ psc += buf[i] << (i*8);
+ }
+ int8_t port = 0;
+
+ LOG_PRINTF("psc:%d\r\n",psc);
+ // Driver error... port not found
+ if (!psc) {
+ // Continue reading status change endpoint
+ hub->state = 25;
+ break;
+ }
+
+ for (i = 0; i <= hub->ports_num; i++) {
+ if (psc & (1<<i)) {
+ port = i;
+ psc = 0;
+ break;
+ }
+ }
+
+ if (hub->current_port >= 1) {
+ if (hub->current_port != port) {
+ LOG_PRINTF("X");
+ hub->state = 25;
+ break;
+ }
+ }
+ struct usb_setup_data setup_data;
+ // If regular port event, else hub event
+ if (port) {
+ setup_data.bmRequestType = 0b10100011;
+ } else {
+ setup_data.bmRequestType = 0b10100000;
+ }
+
+
+ //~ LOG_PRINTF("port:%d", port);
+ setup_data.bRequest = USB_REQ_GET_STATUS;
+ setup_data.wValue = 0;
+ setup_data.wIndex = port;
+ setup_data.wLength = 4;
+ hub->state = 31;
+
+ hub->current_port = port;
+ LOG_PRINTF("\r\n\r\nPORT FOUND: %d\r\n", port);
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ hub->state = 0;
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+
+ // In case of EAGAIN error, retry read on status endpoint
+ hub->state = 25;
+ LOG_PRINTF("HUB: Retrying...\r\n");
+ break;
+ }
+ break;
+
+ case 2:
+ {
+ LOG_PRINTF("|empty packet read|");
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ device_xfer_control_read(0, 0, event, dev);
+ hub->state++;
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ break;
+ }
+ }
+ break;
+
+ case 3: // Get HUB Descriptor write
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ if (hub->ports_num) {
+ hub->index = 0;
+ hub->state = 6;
+ LOG_PRINTF("No need to get HUB DESC\r\n");
+ event(dev, cb_data);
+ } else {
+ hub->endpoint_in_toggle = 0;
+
+ struct usb_setup_data setup_data;
+ hub->desc_len = hub->device[0]->packet_size_max0;
+
+ setup_data.bmRequestType = 0b10100000;
+ setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
+ setup_data.wValue = 0x29<<8;
+ setup_data.wIndex = 0;
+ setup_data.wLength = hub->desc_len;
+
+ hub->state++;
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ LOG_PRINTF("DO Need to get HUB DESC\r\n");
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ break;
+ }
+ }
+ break;
+
+ case 4: // Get HUB Descriptor read
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ hub->state++;
+ device_xfer_control_read(hub->buffer, hub->desc_len, event, dev); // "error dynamic size" - bad comment, investigate
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ break;
+ }
+ }
+ break;
+
+ case 5:// Hub descriptor found
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ {
+ struct usb_hub_descriptor *hub_descriptor =
+ (struct usb_hub_descriptor *)hub->buffer;
+
+ // Check size
+ if (hub_descriptor->head.bDescLength > hub->desc_len) {
+ struct usb_setup_data setup_data;
+ hub->desc_len = hub_descriptor->head.bDescLength;
+
+ setup_data.bmRequestType = 0b10100000;
+ setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
+ setup_data.wValue = 0x29<<8;
+ setup_data.wIndex = 0;
+ setup_data.wLength = hub->desc_len;
+
+ hub->state = 4;
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ break;
+ } else if (hub_descriptor->head.bDescLength == hub->desc_len) {
+ hub->ports_num = hub_descriptor->head.bNbrPorts;
+
+ hub->state++;
+ hub->index = 0;
+ cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
+ event(dev, cb_data);
+ } else {
+ //try again
+ }
+ }
+ break;
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ {
+ LOG_PRINTF("->\t\t\t\t\t ERRSIZ: deschub\r\n");
+ struct usb_hub_descriptor*hub_descriptor =
+ (struct usb_hub_descriptor *)hub->buffer;
+
+ if (cb_data.transferred_length >= sizeof(struct usb_hub_descriptor_head)) {
+ if (cb_data.transferred_length == hub_descriptor->head.bDescLength) {
+ // Process HUB descriptor
+ if ( hub_descriptor->head.bNbrPorts <= USBH_HUB_MAX_DEVICES) {
+ hub->ports_num = hub_descriptor->head.bNbrPorts;
+ } else {
+ LOG_PRINTF("INCREASE NUMBER OF ENABLED PORTS\r\n");
+ hub->ports_num = USBH_HUB_MAX_DEVICES;
+ }
+ hub->state++;
+ hub->index = 0;
+
+ cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
+ event(dev, cb_data);
+ }
+ }
+ }
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ ERROR(cb_data.status);
+ break;
+ }
+ }
+ break;
+
+ case 6:// enable ports
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ if (hub->index < hub->ports_num) {
+ hub->index++;
+ struct usb_setup_data setup_data;
+
+ LOG_PRINTF("[!%d!]",hub->index);
+ setup_data.bmRequestType = 0b00100011;
+ setup_data.bRequest = HUB_REQ_SET_FEATURE;
+ setup_data.wValue = HUB_FEATURE_PORT_POWER;
+ setup_data.wIndex = hub->index;
+ setup_data.wLength = 0;
+
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ } else {
+ hub->state++;
+ // TODO:
+ // Delay Based on hub descriptor field bPwr2PwrGood
+ // delay_ms_busy_loop(200);
+
+ LOG_PRINTF("\r\nHUB CONFIGURED & PORTS POWERED\r\n");
+
+ cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
+ event(dev, cb_data);
+ }
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ break;
+ }
+ }
+ break;
+
+ case 7:
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ {
+ struct usb_setup_data setup_data;
+
+ setup_data.bmRequestType = 0b10100000;
+ setup_data.bRequest = USB_REQ_GET_STATUS;
+ setup_data.wValue = 0;
+ setup_data.wIndex = 0;
+ setup_data.wLength = 4;
+
+ hub->state++;
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ break;
+ }
+
+ }
+ break;
+ case 8:
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ device_xfer_control_read(hub->buffer, 4, event, dev);
+ hub->index = 0;
+ hub->state++;
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ break;
+ }
+ }
+ break;
+ case 9:
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ {
+ struct usb_setup_data setup_data;
+
+ setup_data.bmRequestType = 0b10100011;
+ setup_data.bRequest = USB_REQ_GET_STATUS;
+ setup_data.wValue = 0;
+ setup_data.wIndex = ++hub->index;
+ setup_data.wLength = 4;
+
+ hub->state++;
+
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ break;
+ }
+ }
+ break;
+ case 10:
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ device_xfer_control_read(hub->buffer, 4, event, dev);
+ hub->state++;
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ break;
+ }
+ }
+ break;
+ case 11:
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ if (hub->index < hub->ports_num) {
+ hub->state = 9;
+ // process data contained in hub->buffer
+ // TODO:
+ cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
+ event(dev, cb_data);
+ } else {
+ hub->busy = 0;
+ hub->state = 25;
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ break;
+ }
+ }
+ break;
+
+ case 31: // Read port status
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ {
+ int8_t port = hub->current_port;
+ hub->state++;
+
+ // TODO: rework to endianess aware,
+ // (maybe whole library is affected by this)
+ // Detail:
+ // Isn't universal. Here is endianess ok,
+ // but on another architecture must not be
+ device_xfer_control_read(&hub->hub_and_port_status[port], 4, event, dev);
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ // continue
+ hub->state = 25;
+ break;
+ }
+
+ }
+ break;
+ case 32:
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ {
+ int8_t port = hub->current_port;
+ LOG_PRINTF("|%d",port);
+
+
+ // Get Port status, else Get Hub status
+ if (port) {
+ uint16_t stc = hub->hub_and_port_status[port].stc;
+
+ // Connection status changed
+ if (stc & (1<<HUB_FEATURE_PORT_CONNECTION)) {
+
+ // Check, whether device is in connected state
+ if (!hub->device[port]) {
+ if (!usbh_enum_available() || hub->busy) {
+ LOG_PRINTF("\r\n\t\t\tCannot enumerate %d %d\r\n", !usbh_enum_available(), hub->busy);
+ hub->state = 25;
+ break;
+ }
+ }
+
+ // clear feature C_PORT_CONNECTION
+ struct usb_setup_data setup_data;
+
+ setup_data.bmRequestType = 0b00100011;
+ setup_data.bRequest = HUB_REQ_CLEAR_FEATURE;
+ setup_data.wValue = HUB_FEATURE_C_PORT_CONNECTION;
+ setup_data.wIndex = port;
+ setup_data.wLength = 0;
+
+ hub->state = 33;
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+
+ } else if(stc & (1<<HUB_FEATURE_PORT_RESET)) {
+ // clear feature C_PORT_RESET
+ // Reset processing is complete, enumerate device
+ struct usb_setup_data setup_data;
+
+ setup_data.bmRequestType = 0b00100011;
+ setup_data.bRequest = HUB_REQ_CLEAR_FEATURE;
+ setup_data.wValue = HUB_FEATURE_C_PORT_RESET;
+ setup_data.wIndex = port;
+ setup_data.wLength = 0;
+
+ hub->state = 35;
+
+ LOG_PRINTF("RESET");
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ } else {
+ LOG_PRINTF("another STC %d\r\n", stc);
+ }
+ } else {
+ hub->state = 25;
+ LOG_PRINTF("HUB status change\r\n");
+ }
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ // continue
+ hub->state = 25;
+ break;
+ }
+ }
+ break;
+ case 33:
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ {
+ int8_t port = hub->current_port;
+ uint16_t stc = hub->hub_and_port_status[port].stc;
+ if (!hub->device[port]) {
+ if ((stc) & (1<<HUB_FEATURE_PORT_CONNECTION)) {
+ struct usb_setup_data setup_data;
+
+ setup_data.bmRequestType = 0b00100011;
+ setup_data.bRequest = HUB_REQ_SET_FEATURE;
+ setup_data.wValue = HUB_FEATURE_PORT_RESET;
+ setup_data.wIndex = port;
+ setup_data.wLength = 0;
+
+ hub->state = 11;
+ LOG_PRINTF("CONN");
+
+ hub->busy = 1;
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ }
+ } else {
+ LOG_PRINTF("\t\t\t\tDISCONNECT EVENT\r\n");
+ if (hub->device[port]->drv && hub->device[port]->drvdata) {
+ hub->device[port]->drv->remove(hub->device[port]->drvdata);
+ }
+ hub->device[port]->address = -1;
+
+ hub->device[port]->drv = 0;
+ hub->device[port]->drvdata = 0;
+ hub->device[port] = 0;
+ hub->current_port = CURRENT_PORT_NONE;
+ hub->state = 25;
+ hub->busy = 0;
+ }
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ // continue
+ hub->state = 25;
+ break;
+ }
+ }
+ break;
+ case 35: // RESET COMPLETE, start enumeration
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ {
+ LOG_PRINTF("\r\nPOLL\r\n");
+ int8_t port = hub->current_port;
+ uint16_t sts = hub->hub_and_port_status[port].sts;
+
+
+ if (sts & (1<<HUB_FEATURE_PORT_ENABLE)) {
+ hub->device[port] = usbh_get_free_device(dev);
+
+ if (!hub->device[port]) {
+ LOG_PRINTF("\r\nFATAL ERROR\r\n");
+ return;// DEAD END
+ }
+ if ((sts & (1<<(HUB_FEATURE_PORT_LOWSPEED))) &&
+ !(sts & (1<<(HUB_FEATURE_PORT_HIGHSPEED)))) {
+ LOG_PRINTF("Low speed device");
+
+ // Disable Low speed device immediately
+ struct usb_setup_data setup_data;
+
+ setup_data.bmRequestType = 0b00100011;
+ setup_data.bRequest = HUB_REQ_CLEAR_FEATURE;
+ setup_data.wValue = HUB_FEATURE_PORT_ENABLE;
+ setup_data.wIndex = port;
+ setup_data.wLength = 0;
+
+ // After write process another devices, poll for events
+ hub->state = 11;//Expecting all ports are powered (constant/non-changeable after init)
+ hub->current_port = CURRENT_PORT_NONE;
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ } else if (!(sts & (1<<(HUB_FEATURE_PORT_LOWSPEED))) &&
+ !(sts & (1<<(HUB_FEATURE_PORT_HIGHSPEED)))) {
+ hub->device[port]->speed = USBH_SPEED_FULL;
+ LOG_PRINTF("Full speed device");
+ hub->timestamp_us = hub->time_curr_us;
+ hub->state = 100; // schedule wait for 500ms
+ }
+
+
+ } else {
+ LOG_PRINTF("%s:%d Do not know what to do, when device is disabled after reset\r\n", __FILE__, __LINE__);
+ hub->state = 25;
+ return;
+ }
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ // continue
+ hub->state = 25;
+ break;
+ }
+ }
+ break;
+ default:
+ LOG_PRINTF("UNHANDLED EVENT %d\r\n",hub->state);
+ break;
+ }
+}
+
+static void read_ep1(void *drvdata)
+{
+ hub_device_t *hub = drvdata;
+ usbh_packet_t packet;
+
+ packet.address = hub->device[0]->address;
+ packet.data = hub->buffer;
+ packet.datalen = hub->endpoint_in_maxpacketsize;
+ packet.endpoint_address = hub->endpoint_in_address;
+ packet.endpoint_size_max = hub->endpoint_in_maxpacketsize;
+ packet.endpoint_type = USBH_EPTYP_INTERRUPT;
+ packet.speed = hub->device[0]->speed;
+ packet.callback = event;
+ packet.callback_arg = hub->device[0];
+ packet.toggle = &hub->endpoint_in_toggle;
+
+ hub->state = 26;
+ usbh_read(hub->device[0], &packet);
+ LOG_PRINTF("@hub %d/EP1 | \r\n", hub->device[0]->address);
+
+}
+
+/**
+ *
+ * tflp time from last poll [ms]
+ */
+static void hub_poll(void *drvdata, uint32_t time_curr_us)
+{
+ hub_device_t *hub = drvdata;
+ usbh_device_t *dev = hub->device[0];
+
+ hub->time_curr_us = time_curr_us;
+
+ switch (hub->state) {
+ case 25:
+ {
+ if (usbh_enum_available()) {
+ read_ep1(hub);
+ } else {
+ LOG_PRINTF("enum not available\r\n");
+ }
+ }
+ break;
+
+ case 1:
+ {
+ LOG_PRINTF("CFGVAL: %d\r\n", hub->buffer[0]);
+ struct usb_setup_data setup_data;
+
+ setup_data.bmRequestType = 0b00000000;
+ setup_data.bRequest = USB_REQ_SET_CONFIGURATION;
+ setup_data.wValue = hub->buffer[0];
+ setup_data.wIndex = 0;
+ setup_data.wLength = 0;
+
+ hub->state += 2;
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+
+ }
+ break;
+ case 100:
+ if (hub->time_curr_us - hub->timestamp_us > 500000) {
+ int8_t port = hub->current_port;
+ LOG_PRINTF("PORT: %d", port);
+ LOG_PRINTF("\r\nNEW device at address: %d\r\n", hub->device[port]->address);
+ hub->device[port]->lld = hub->device[0]->lld;
+
+ device_enumeration_start(hub->device[port]);
+ hub->current_port = CURRENT_PORT_NONE;
+
+ // Maybe error, when assigning address is taking too long
+ //
+ // Detail:
+ // USB hub cannot enable another port while the device
+ // the current one is also in address state (has address==0)
+ // Only one device on bus can have address==0
+ hub->busy = 0;
+
+ hub->state = 25;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (usbh_enum_available()) {
+ uint32_t i;
+ for (i = 1; i < USBH_HUB_MAX_DEVICES + 1; i++) {
+ if (hub->device[i]) {
+ if (hub->device[i]->drv && hub->device[i]->drvdata) {
+ hub->device[i]->drv->poll(hub->device[i]->drvdata, time_curr_us);
+ }
+ }
+ }
+ }
+}
+static void hub_remove(void *drvdata)
+{
+ hub_device_t *hub = drvdata;
+ uint8_t i;
+
+ // Call fast... to avoid polling
+ hub->state = 0;
+ hub->endpoint_in_address = 0;
+ hub->busy = 0;
+ for (i = 1; i < USBH_HUB_MAX_DEVICES + 1; i++) {
+ if (hub->device[i]) {
+ if (hub->device[i]->drv && hub->device[i]->drvdata) {
+ if (hub->device[i]->drv->remove != hub_remove) {
+ LOG_PRINTF("\t\t\t\tHUB REMOVE %d\r\n",hub->device[i]->address);
+ hub->device[i]->drv->remove(hub->device[i]->drvdata);
+ }
+ }
+ hub->device[i] = 0;
+ }
+ hub->device[0]->drv = 0;
+ hub->device[0]->drvdata = 0;
+ hub->device[0] = 0;
+
+ }
+}