usb: hub class driver

This commit is contained in:
Matheus
2026-05-13 12:04:17 -03:00
parent 53f444eb2f
commit 9c45a41abe
8 changed files with 598 additions and 20 deletions
+31 -2
View File
@@ -23,6 +23,8 @@ typedef struct usb_class_driver usb_class_driver_t;
#define USB_DESCRIPTOR_TYPE_INTERFACE 4
#define USB_DESCRIPTOR_TYPE_ENDPOINT 5
#define USB_DESCRIPTOR_TYPE_SS_EP_COMPANION 48
#define USB_DESCRIPTOR_TYPE_HUB 0x29
#define USB_DESCRIPTOR_TYPE_SS_HUB 0x2A
typedef struct {
uint8_t bLength;
@@ -30,6 +32,7 @@ typedef struct {
} usb_desc_hdr_t;
#define USB_MAX_CONFIG_DESC_SIZE 4096
#define USB_CLASS_HUB 0x09
typedef struct {
uint8_t bLength;
@@ -103,6 +106,28 @@ typedef struct {
uint16_t wBytesPerInterval;
} __attribute__((packed)) usb_ss_ep_companion_desc_t;
#define USB_HUB_PROTOCOL_MULTI_TT 2
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bNbrPorts;
uint16_t wHubCharacteristics;
uint8_t bPwrOn2PwrGood;
uint8_t bHubContrCurrent;
} __attribute__((packed)) usb_hub_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bNbrPorts;
uint16_t wHubCharacteristics;
uint8_t bPwrOn2PwrGood;
uint8_t bHubContrCurrent;
uint8_t bHubHdrDecLat;
uint16_t wHubDelay;
} __attribute__((packed)) usb_ss_hub_desc_t;
#define USB_REQUEST_RECIP_DEVICE 0x00
#define USB_REQUEST_RECIP_INTERFACE 0x01
#define USB_REQUEST_RECIP_ENDPOINT 0x02
@@ -246,6 +271,7 @@ struct usb_device {
// Descriptors for device
usb_device_desc_t desc;
usb_config_desc_t *config_desc;
usb_desc_hdr_t *hub_desc;
// Port number on the parent hub.
uint8_t port_number;
// Hub-port generation captured when enumeration started.
@@ -313,9 +339,11 @@ typedef void (*usb_address_device_callback_t)(usb_hub_t *hub, uint8_t port, usb_
typedef struct {
// Allocate, enable and address a USB device.
int (*address_device)(usb_ctrl_t *, usb_hub_t *, uint8_t port, usb_address_device_callback_t callback);
int (*address_device)(usb_ctrl_t *, usb_hub_t *, uint8_t port, usb_speed_t speed, usb_address_device_callback_t callback);
// Deaddress, disable and free a USB device
void (*deaddress_device)(usb_ctrl_t *, usb_device_t *);
// Mark a device as a hub in the controller
int (*mark_as_hub)(usb_ctrl_t *, usb_device_t *, usb_completion_callback_t callback, void *callback_ctx);
// Configure an endpoint.
int (*configure_ep)(usb_ctrl_t *, usb_device_t *, usb_endpoint_t *, usb_completion_callback_t callback, void *callback_ctx);
// Execute a control or data transfer.
@@ -330,13 +358,14 @@ int usb_submit_xfer(usb_device_t *dev, usb_xfer_t *xfer);
int usb_control_xfer(usb_device_t *dev, usb_setup_t *setup, void *buffer, usb_completion_callback_t callback, void *ctx);
int usb_get_device_descriptor(usb_device_t *dev, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx);
int usb_get_class_device_descriptor(usb_device_t *dev, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx);
int usb_get_interface_descriptor(usb_device_t *dev, uint16_t interface_number, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx);
int usb_get_endpoint_descriptor(usb_device_t *dev, uint16_t endpoint_address, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx);
int usb_set_configuration(usb_device_t *dev, uint8_t config_value, usb_completion_callback_t callback);
void usb_hub_event_connect(usb_hub_t *hub, int port);
void usb_hub_event_disconnect(usb_hub_t *hub, int port);
void usb_hub_event_reset(usb_hub_t *hub, int port);
void usb_hub_event_reset(usb_hub_t *hub, int port, usb_speed_t speed);
#define USB_DRIVER_SCORE_NONE 0
#define USB_DRIVER_SCORE_GENERIC 10
-1
View File
@@ -294,7 +294,6 @@ static int hid_attach(usb_device_t *dev, usb_interface_t *interface) {
}
static int hid_probe(usb_device_t *, usb_interface_t *interface) {
if (interface->desc->bInterfaceClass == USB_INTERFACE_CLASS_HID)
return USB_DRIVER_SCORE_CLASS_MATCH;
+411
View File
@@ -0,0 +1,411 @@
#include <kernel/usb.h>
#include <errno.h>
#include <logging.h>
#include <kernel/alloc.h>
#include <kernel/scheduler.h>
#include <string.h>
#define USB_HUB_REQUEST_GET_STATUS 0
#define USB_HUB_REQUEST_CLEAR_FEATURE 1
#define USB_HUB_REQUEST_SET_FEATURE 3
#define USB_HUB_POWER_SWITCHING_MODE_MASK 0x0003
#define USB_HUB_POWER_SWITCHING_MODE_NONE (1 << 1)
typedef struct {
uint64_t in_buffer;
size_t in_bitmap_size;
usb_hub_t *usb_hub;
uint32_t *status_buffer;
usb_endpoint_t *interrupt_in_endpoint;
size_t ports_powered;
size_t ports_initialized;
} hub_driver_data_t;
static int hub_port_control_xfer(usb_device_t *dev, uint8_t request, uint16_t value, int port, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx) {
usb_setup_t setup = {0};
setup.bmRequestType = USB_REQUEST_RECIP_OTHER | USB_REQUEST_CLASS;
setup.bmRequestType |= buffer ? USB_REQUEST_DIR_TO_HOST : USB_REQUEST_DIR_TO_DEVICE;
setup.bRequest = request;
setup.wValue = value;
setup.wIndex = port + 1;
setup.wLength = length;
return usb_control_xfer(dev, &setup, buffer, callback, callback_ctx);
}
static int get_status_cmd(usb_device_t *dev, hub_driver_data_t *data, int port, usb_completion_callback_t callback, void *callback_ctx) {
if (port < 0 || port >= data->usb_hub->port_count)
return EINVAL;
return hub_port_control_xfer(dev, USB_HUB_REQUEST_GET_STATUS, 0, port, &data->status_buffer[port], sizeof(data->status_buffer[port]), callback, callback_ctx);
}
static hub_driver_data_t *hub_get_driver_data(usb_hub_t *hub) {
usb_device_t *dev = hub->device;
if (dev == NULL || dev->config_desc == NULL || dev->interfaces == NULL)
return NULL;
for (int i = 0; i < dev->config_desc->bNumInterfaces; ++i) {
hub_driver_data_t *data = dev->interfaces[i].driver_data;
if (data != NULL && data->usb_hub == hub)
return data;
}
return NULL;
}
static int hub_get_port_status(usb_hub_t *hub, uint8_t port, uint16_t *status, uint16_t *change) {
if (port >= hub->port_count)
return EINVAL;
hub_driver_data_t *data = hub_get_driver_data(hub);
if (data == NULL)
return ENODEV;
uint32_t port_status = data->status_buffer[port];
if (status)
*status = port_status & 0xffff;
if (change)
*change = port_status >> 16;
return 0;
}
static void reset_port_callback(usb_device_t *dev, void *ctx, usb_status_t status, size_t transferred) {
(void)dev;
(void)transferred;
if (status != USB_STATUS_SUCCESS)
printf("hub: failed to reset port %lu\n", (uintptr_t)ctx);
}
static int hub_set_port_feature(usb_hub_t *hub, uint8_t port, uint16_t feature) {
if (port >= hub->port_count)
return EINVAL;
usb_completion_callback_t callback = NULL;
void *callback_ctx = NULL;
if (feature == USB_HUB_FEATURE_PORT_RESET) {
callback = reset_port_callback;
callback_ctx = (void *)(uintptr_t)(port + 1);
}
return hub_port_control_xfer(hub->device, USB_HUB_REQUEST_SET_FEATURE, feature, port, NULL, 0, callback, callback_ctx);
}
static int hub_clear_port_feature(usb_hub_t *hub, uint8_t port, uint16_t feature) {
if (port >= hub->port_count)
return EINVAL;
return hub_port_control_xfer(hub->device, USB_HUB_REQUEST_CLEAR_FEATURE, feature, port, NULL, 0, NULL, NULL);
}
typedef struct {
int port;
hub_driver_data_t *data;
} callback_ctx_t;
static void clear_port_changes(hub_driver_data_t *data, int port, uint16_t port_change) {
if (port_change & USB_HUB_PORT_CHANGE_PORT_CONNECTION) {
if (data->usb_hub->ops->clear_port_feature(data->usb_hub, port, USB_HUB_FEATURE_C_PORT_CONNECTION))
printf("hub: failed to clear connection change on port %d\n", port);
}
if (port_change & USB_HUB_PORT_CHANGE_PORT_ENABLE) {
if (data->usb_hub->ops->clear_port_feature(data->usb_hub, port, USB_HUB_FEATURE_C_PORT_ENABLE))
printf("hub: failed to clear enable change on port %d\n", port);
}
if (port_change & USB_HUB_PORT_CHANGE_PORT_OVER_CURRENT) {
if (data->usb_hub->ops->clear_port_feature(data->usb_hub, port, USB_HUB_FEATURE_C_PORT_OVER_CURRENT))
printf("hub: failed to clear over-current change on port %d\n", port);
}
if (port_change & USB_HUB_PORT_CHANGE_PORT_RESET) {
if (data->usb_hub->ops->clear_port_feature(data->usb_hub, port, USB_HUB_FEATURE_C_PORT_RESET))
printf("hub: failed to clear reset change on port %d\n", port);
}
}
static usb_speed_t speed_from_port_status(usb_hub_t *hub, uint16_t port_status) {
if (hub->device->speed == USB_SPEED_SUPER || hub->device->speed == USB_SPEED_SUPER_PLUS)
return USB_SPEED_SUPER;
if (port_status & USB_HUB_PORT_STATUS_LOW_SPEED)
return USB_SPEED_LOW;
if (port_status & USB_HUB_PORT_STATUS_HIGH_SPEED)
return USB_SPEED_HIGH;
return USB_SPEED_FULL;
}
static void handle_port_change(hub_driver_data_t *data, int port, uint16_t port_status, uint16_t port_change) {
if (port_change & USB_HUB_PORT_CHANGE_PORT_CONNECTION) {
if (port_status & USB_HUB_PORT_STATUS_PORT_CONNECTION) {
if (data->usb_hub->ports[port].device != NULL)
usb_hub_event_disconnect(data->usb_hub, port);
usb_hub_event_connect(data->usb_hub, port);
} else {
usb_hub_event_disconnect(data->usb_hub, port);
}
} else if (port_change & USB_HUB_PORT_CHANGE_PORT_RESET) {
usb_hub_event_reset(data->usb_hub, port, speed_from_port_status(data->usb_hub, port_status));
}
}
static void get_status_callback(usb_device_t *dev, void *ctx, usb_status_t status, size_t transferred) {
callback_ctx_t *callback_ctx = ctx;
hub_driver_data_t *data = callback_ctx->data;
int port = callback_ctx->port;
if (status != USB_STATUS_SUCCESS) {
printf("hub: failed to get status of port %d\n", port);
goto out;
}
uint16_t port_status = data->status_buffer[port] & 0xffff;
uint16_t port_change = data->status_buffer[port] >> 16;
handle_port_change(data, port, port_status, port_change);
clear_port_changes(data, port, port_change);
out:
free(ctx);
}
static int insert_in_transfer(usb_device_t *dev, hub_driver_data_t *data);
static bool hub_change_bitmap_has_port(hub_driver_data_t *data, size_t transferred, int port) {
size_t bit = port + 1;
size_t byte = bit / 8;
if (byte >= transferred || byte >= data->in_bitmap_size || byte >= sizeof(data->in_buffer))
return false;
uint8_t *bitmap = (uint8_t *)&data->in_buffer;
return (bitmap[byte] & (1 << (bit % 8))) != 0;
}
static void interrupt_in_callback(usb_device_t *dev, void *ctx, usb_status_t status, size_t transferred) {
hub_driver_data_t *data = ctx;
if (status != USB_STATUS_SUCCESS) {
printf("hub: IN interrupt failed; retrying\n");
} else {
for (int port = 0; port < data->usb_hub->port_count; ++port) {
if (!hub_change_bitmap_has_port(data, transferred, port))
continue;
callback_ctx_t *callback_ctx = alloc(sizeof(callback_ctx_t));
if (callback_ctx == NULL) {
printf("hub: out of memory to do get_status\n");
continue;
}
callback_ctx->port = port;
callback_ctx->data = data;
if (get_status_cmd(dev, data, port, get_status_callback, callback_ctx)) {
printf("hub: get_status_cmd() failed\n");
free(callback_ctx);
}
}
}
data->in_buffer = 0;
if (insert_in_transfer(dev, data)) {
printf("hub: failed to insert in trasnfer during callback. hub will not be functional.\n");
}
}
static int insert_in_transfer(usb_device_t *dev, hub_driver_data_t *data) {
usb_xfer_t xfer = {
.ep = data->interrupt_in_endpoint,
.flags = USB_XFER_FLAG_TO_HOST,
.type = USB_XFER_TYPE_INTERRUPT,
.data = &data->in_buffer,
.data_length = data->in_bitmap_size,
.completion = interrupt_in_callback,
.completion_ctx = data
};
int error = usb_submit_xfer(dev, &xfer);
if (error) {
printf("hub: failed to submit IN xfer: %s\n", strerror(error));
}
return error;
}
static usb_hub_ops_t hub_ops = {
.get_port_status = hub_get_port_status,
.set_port_feature = hub_set_port_feature,
.clear_port_feature = hub_clear_port_feature,
};
static void init_status_get_callback(usb_device_t *dev, void *ctx, usb_status_t status, size_t transferred) {
hub_driver_data_t *data = ctx;
size_t port = data->ports_initialized;
if (status != USB_STATUS_SUCCESS) {
printf("hub: failed to get first status of port %lu\n", port);
return;
}
uint16_t port_status = data->status_buffer[port] & 0xffff;
uint16_t port_change = data->status_buffer[port] >> 16;
if (port_status & USB_HUB_PORT_STATUS_PORT_CONNECTION)
usb_hub_event_connect(data->usb_hub, port);
clear_port_changes(data, port, port_change);
if (++data->ports_initialized != data->usb_hub->port_count) {
if (get_status_cmd(dev, data, data->ports_initialized, init_status_get_callback, data)) {
printf("hub: get_status_cmd() failed\n");
}
}
}
static int power_port(usb_device_t *dev, int port, hub_driver_data_t *data);
static void initial_status_scan(usb_device_t *dev, hub_driver_data_t *data) {
if (get_status_cmd(dev, data, 0, init_status_get_callback, data))
printf("hub: failed to get status of first port\n");
}
static void power_on_callback(usb_device_t *dev, void *ctx, usb_status_t status, size_t transferred) {
hub_driver_data_t *data = ctx;
if (status != USB_STATUS_SUCCESS) {
printf("hub: failed to power on port %lu\n", data->ports_powered);
return;
}
if (++data->ports_powered == data->usb_hub->port_count) {
usb_hub_desc_t *desc = (usb_hub_desc_t *)dev->hub_desc;
size_t power_good_us = (size_t)desc->bPwrOn2PwrGood * 2000;
if (power_good_us)
sched_sleep_us(power_good_us);
initial_status_scan(dev, data);
} else if (power_port(dev, data->ports_powered, data)) {
printf("hub: power_port() failed\n");
}
}
static int power_port(usb_device_t *dev, int port, hub_driver_data_t *data) {
return hub_port_control_xfer(data->usb_hub->device, USB_HUB_REQUEST_SET_FEATURE, USB_HUB_FEATURE_PORT_POWER, port, NULL, 0, power_on_callback, data);
}
static bool hub_advertises_port_power_switching(usb_device_t *dev) {
usb_hub_desc_t *desc = (usb_hub_desc_t *)dev->hub_desc;
uint16_t power_mode = desc->wHubCharacteristics & USB_HUB_POWER_SWITCHING_MODE_MASK;
return (power_mode & USB_HUB_POWER_SWITCHING_MODE_NONE) == 0;
}
static void get_hub_name(usb_device_t *dev, char *buffer, size_t buffer_size) {
uint8_t major = (dev->desc.bcdUSB >> 8) & 0x0f;
uint8_t minor_tens = (dev->desc.bcdUSB >> 4) & 0x0f;
uint8_t minor_ones = dev->desc.bcdUSB & 0x0f;
if (minor_ones)
snprintf(buffer, buffer_size, "usb-hub-%u.%u%u", major, minor_tens, minor_ones);
else
snprintf(buffer, buffer_size, "usb-hub-%u.%u", major, minor_tens);
}
static int hub_attach(usb_device_t *dev, usb_interface_t *interface) {
__assert(dev->hub_desc);
hub_driver_data_t *hub_driver_data = alloc(sizeof(hub_driver_data_t));
if (hub_driver_data == NULL)
return ENOMEM;
hub_driver_data->usb_hub = alloc(sizeof(usb_hub_t));
if (hub_driver_data->usb_hub == NULL) {
free(hub_driver_data);
return ENOMEM;
}
get_hub_name(dev, hub_driver_data->usb_hub->name, sizeof(hub_driver_data->usb_hub->name));
hub_driver_data->usb_hub->ctrl = dev->hub->ctrl;
hub_driver_data->usb_hub->ops = &hub_ops;
hub_driver_data->usb_hub->device = dev;
hub_driver_data->usb_hub->port_count = ((usb_hub_desc_t *)dev->hub_desc)->bNbrPorts;
if (hub_driver_data->usb_hub->port_count == 0) {
free(hub_driver_data->usb_hub);
free(hub_driver_data);
return EINVAL;
}
hub_driver_data->usb_hub->ports = alloc(sizeof(usb_hub_port_t) * hub_driver_data->usb_hub->port_count);
if (hub_driver_data->usb_hub->ports == NULL) {
free(hub_driver_data->usb_hub);
free(hub_driver_data);
return ENOMEM;
}
hub_driver_data->status_buffer = alloc(sizeof(uint32_t) * hub_driver_data->usb_hub->port_count);
if (hub_driver_data->status_buffer == NULL) {
free(hub_driver_data->usb_hub->ports);
free(hub_driver_data->usb_hub);
free(hub_driver_data);
return ENOMEM;
}
hub_driver_data->in_bitmap_size = ROUND_UP(hub_driver_data->usb_hub->port_count + 1, 8) / 8;
if (hub_driver_data->in_bitmap_size > sizeof(hub_driver_data->in_buffer)) {
printf("hub: too many ports for interrupt bitmap\n");
free(hub_driver_data->status_buffer);
free(hub_driver_data->usb_hub->ports);
free(hub_driver_data->usb_hub);
free(hub_driver_data);
return EINVAL;
}
for (int i = 0; i < interface->desc->bNumEndpoints; ++i) {
if (interface->endpoints[i].desc && (interface->endpoints[i].desc->bEndpointAddress & USB_ENDPOINT_ADDRESS_DIR_IN) && (interface->endpoints[i].desc->bmAttributes & USB_ENDPOINT_ATTRIB_TYPE_MASK) == USB_ENDPOINT_ATTRIB_TYPE_INTR)
hub_driver_data->interrupt_in_endpoint = &interface->endpoints[i];
}
if (hub_driver_data->interrupt_in_endpoint == NULL) {
printf("hub: no IN interrupt endpoint found\n");
free(hub_driver_data->status_buffer);
free(hub_driver_data->usb_hub->ports);
free(hub_driver_data->usb_hub);
free(hub_driver_data);
return EINVAL;
}
interface->driver_data = hub_driver_data;
int error = insert_in_transfer(dev, hub_driver_data);
if (error) {
printf("hub: failed to insert first IN transfer\n");
interface->driver_data = NULL;
free(hub_driver_data->status_buffer);
free(hub_driver_data->usb_hub->ports);
free(hub_driver_data->usb_hub);
free(hub_driver_data);
return error;
}
if (hub_advertises_port_power_switching(dev)) {
if (power_port(dev, 0, hub_driver_data))
printf("hub: failed to power first port\n");
} else {
initial_status_scan(dev, hub_driver_data);
}
return 0;
}
static int hub_probe(usb_device_t *dev, usb_interface_t *interface) {
if (interface->desc->bInterfaceClass == USB_CLASS_HUB)
return USB_DRIVER_SCORE_CLASS_MATCH;
return USB_DRIVER_SCORE_NONE;
}
DEFINE_USB_CLASS_DRIVER(hub_class,
.name = "USB Hub",
.attach = hub_attach,
.probe = hub_probe
// TODO detach
)
+101 -4
View File
@@ -50,6 +50,13 @@ static bool endpoint_requires_ss_companion(usb_device_t *dev, usb_endpoint_t *en
return (endpoint->desc->bmAttributes & USB_ENDPOINT_ATTRIB_TYPE_MASK) != USB_ENDPOINT_ATTRIB_TYPE_CONTROL;
}
static uint8_t hub_descriptor_type(usb_device_t *dev) {
if (dev->speed == USB_SPEED_SUPER || dev->speed == USB_SPEED_SUPER_PLUS)
return USB_DESCRIPTOR_TYPE_SS_HUB;
return USB_DESCRIPTOR_TYPE_HUB;
}
static bool device_port_stale(usb_device_t *dev) {
return dev->hub->ports[dev->port_number].generation != dev->port_generation;
}
@@ -144,6 +151,8 @@ static void post_config_cleanup(usb_device_t *dev) {
free(dev->interfaces[i].endpoints);
}
if (dev->hub_desc)
free(dev->hub_desc);
free(dev->interfaces);
free(dev->config_desc);
dev->hub->ctrl->ops->deaddress_device(dev->hub->ctrl, dev);
@@ -169,6 +178,76 @@ static void endpoint_configured(usb_device_t *dev, void *ctx, usb_status_t statu
post_config_cleanup(dev);
}
static void marked_as_hub(usb_device_t *dev, void *, usb_status_t status, size_t) {
if (status != USB_STATUS_SUCCESS) {
printf("usb: failed to mark device as hub\n");
post_config_cleanup(dev);
return;
}
if (device_port_stale(dev)) {
post_config_cleanup(dev);
return;
}
if (configure_next_endpoint(dev, 0, 0)) {
printf("usb: failed to configure first endpoint\n");
post_config_cleanup(dev);
return;
}
}
static void get_hub_desc_done(usb_device_t *dev, void *, usb_status_t status, size_t) {
if (status != USB_STATUS_SUCCESS) {
printf("usb: failed to get full hub descriptor\n");
post_config_cleanup(dev);
return;
}
if (device_port_stale(dev)) {
post_config_cleanup(dev);
return;
}
if (dev->hub->ctrl->ops->mark_as_hub) {
if (dev->hub->ctrl->ops->mark_as_hub(dev->hub->ctrl, dev, marked_as_hub, NULL)) {
printf("usb: mark_as_hub failed\n");
post_config_cleanup(dev);
}
} else {
if (configure_next_endpoint(dev, 0, 0)) {
printf("usb: failed to configure first endpoint of device\n");
post_config_cleanup(dev);
}
}
}
static void get_hub_desc_header_done(usb_device_t *dev, void *, usb_status_t status, size_t transferred) {
if (status != USB_STATUS_SUCCESS) {
printf("usb: failed to get hub descriptor header\n");
post_config_cleanup(dev);
return;
}
if (device_port_stale(dev)) {
post_config_cleanup(dev);
return;
}
void *p = realloc(dev->hub_desc, dev->hub_desc->bLength);
if (p == NULL) {
printf("usb: out of memory to get full hub descriptor\n");
post_config_cleanup(dev);
return;
}
dev->hub_desc = p;
if (usb_get_class_device_descriptor(dev, hub_descriptor_type(dev), 0, dev->hub_desc, dev->hub_desc->bLength, get_hub_desc_done, NULL)) {
printf("usb: usb_get_class_device_descriptor failed\n");
post_config_cleanup(dev);
}
}
// allocate and populate interface and endpoint datastructures, then start configuring endpoints
static void set_configuration_done(usb_device_t *dev, void *, usb_status_t status, size_t transferred) {
if (status != USB_STATUS_SUCCESS) {
@@ -192,6 +271,7 @@ static void set_configuration_done(usb_device_t *dev, void *, usb_status_t statu
return;
}
bool has_hub = dev->desc.bDeviceClass == USB_CLASS_HUB;
// set up interface data
usb_for_each_descriptor(dev->config_desc, desc) {
// since this is the first pass over the configure descriptor,
@@ -224,6 +304,9 @@ static void set_configuration_done(usb_device_t *dev, void *, usb_status_t statu
goto interface_setup_error;
}
if (interface_desc->bInterfaceClass == USB_CLASS_HUB)
has_hub = true;
interface->desc = interface_desc;
if (interface_desc->bNumEndpoints > 0) {
interface->endpoints = alloc(interface_desc->bNumEndpoints * sizeof(usb_endpoint_t));
@@ -322,8 +405,22 @@ static void set_configuration_done(usb_device_t *dev, void *, usb_status_t statu
}
}
if (configure_next_endpoint(dev, 0, 0) == 0)
return; // success, wait for endpoint to be configured.
if (has_hub) {
// to set up the context in xhci
dev->hub_desc = alloc(sizeof(usb_desc_hdr_t));
if (dev->hub_desc == NULL) {
printf("usb: out of memory to get hub descriptor header\n");
post_config_cleanup(dev);
return;
}
if (usb_get_class_device_descriptor(dev, hub_descriptor_type(dev), 0, dev->hub_desc, sizeof(usb_desc_hdr_t), get_hub_desc_header_done, NULL) == 0)
return;
printf("usb: usb_get_class_device_descriptor failed\n");
} else if (configure_next_endpoint(dev, 0, 0) == 0) {
return; // success, wait for endpoint to be configured.
}
interface_setup_error:
post_config_cleanup(dev);
@@ -435,8 +532,8 @@ static void address_done(usb_hub_t *hub, uint8_t port, usb_device_t *dev) {
}
// address device
void usb_hub_event_reset(usb_hub_t *hub, int port) {
if (hub->ctrl->ops->address_device(hub->ctrl, hub, port, address_done)) {
void usb_hub_event_reset(usb_hub_t *hub, int port, usb_speed_t speed) {
if (hub->ctrl->ops->address_device(hub->ctrl, hub, port, speed, address_done)) {
printf("usb_hub %s: failed to address device\n", hub->name);
}
}
+9 -5
View File
@@ -25,9 +25,9 @@ int usb_control_xfer(usb_device_t *dev, usb_setup_t *setup, void *buffer, usb_co
return usb_submit_xfer(dev, &xfer);
}
static int usb_get_descriptor_for_recipient(usb_device_t *dev, uint8_t recipient, uint16_t index, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx) {
static int usb_get_descriptor_for_recipient(usb_device_t *dev, uint8_t request_type, uint8_t recipient, uint16_t index, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx) {
usb_setup_t setup = {0};
setup.bmRequestType = recipient | USB_REQUEST_STANDARD | USB_REQUEST_DIR_TO_HOST;
setup.bmRequestType = recipient | request_type | USB_REQUEST_DIR_TO_HOST;
setup.bRequest = USB_REQUEST_GET_DESCRIPTOR;
setup.wValue = ((uint16_t)desc_type << 8) | desc_index;
setup.wIndex = index;
@@ -37,15 +37,19 @@ static int usb_get_descriptor_for_recipient(usb_device_t *dev, uint8_t recipient
}
int usb_get_device_descriptor(usb_device_t *dev, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx) {
return usb_get_descriptor_for_recipient(dev, USB_REQUEST_RECIP_DEVICE, 0, desc_type, desc_index, buffer, length, callback, callback_ctx);
return usb_get_descriptor_for_recipient(dev, USB_REQUEST_STANDARD, USB_REQUEST_RECIP_DEVICE, 0, desc_type, desc_index, buffer, length, callback, callback_ctx);
}
int usb_get_class_device_descriptor(usb_device_t *dev, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx) {
return usb_get_descriptor_for_recipient(dev, USB_REQUEST_CLASS, USB_REQUEST_RECIP_DEVICE, 0, desc_type, desc_index, buffer, length, callback, callback_ctx);
}
int usb_get_interface_descriptor(usb_device_t *dev, uint16_t interface_number, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx) {
return usb_get_descriptor_for_recipient(dev, USB_REQUEST_RECIP_INTERFACE, interface_number, desc_type, desc_index, buffer, length, callback, callback_ctx);
return usb_get_descriptor_for_recipient(dev, USB_REQUEST_STANDARD, USB_REQUEST_RECIP_INTERFACE, interface_number, desc_type, desc_index, buffer, length, callback, callback_ctx);
}
int usb_get_endpoint_descriptor(usb_device_t *dev, uint16_t endpoint_address, uint8_t desc_type, uint8_t desc_index, void *buffer, uint16_t length, usb_completion_callback_t callback, void *callback_ctx) {
return usb_get_descriptor_for_recipient(dev, USB_REQUEST_RECIP_ENDPOINT, endpoint_address, desc_type, desc_index, buffer, length, callback, callback_ctx);
return usb_get_descriptor_for_recipient(dev, USB_REQUEST_STANDARD, USB_REQUEST_RECIP_ENDPOINT, endpoint_address, desc_type, desc_index, buffer, length, callback, callback_ctx);
}
int usb_set_configuration(usb_device_t *dev, uint8_t config_value, usb_completion_callback_t callback) {
+44 -6
View File
@@ -90,6 +90,35 @@ static void xhci_update_input_context(xhci_ctrl_t *ctrl, xhci_device_t *dev) {
memset(input_ctx, 0, sizeof(uint32_t) * 2);
}
static int xhci_update_hub_slot_context(usb_device_t *dev, volatile xhci_slot_ctx_t *slot_ctx) {
if (dev->hub_desc == NULL)
return 0;
slot_ctx->dw0.hub = 1;
if (dev->hub_desc->bDescriptorType == USB_DESCRIPTOR_TYPE_SS_HUB) {
if (dev->hub_desc->bLength < sizeof(usb_ss_hub_desc_t))
return EINVAL;
usb_ss_hub_desc_t *hub_desc = (usb_ss_hub_desc_t *)dev->hub_desc;
slot_ctx->dw0.mtt = 0;
slot_ctx->dw1.number_of_ports = hub_desc->bNbrPorts;
return 0;
}
if (dev->hub_desc->bDescriptorType == USB_DESCRIPTOR_TYPE_HUB) {
if (dev->hub_desc->bLength < sizeof(usb_hub_desc_t))
return EINVAL;
usb_hub_desc_t *hub_desc = (usb_hub_desc_t *)dev->hub_desc;
slot_ctx->dw0.mtt = dev->desc.bDeviceProtocol == USB_HUB_PROTOCOL_MULTI_TT;
slot_ctx->dw1.number_of_ports = hub_desc->bNbrPorts;
return 0;
}
return EINVAL;
}
static void xhci_release_device_resources(xhci_ctrl_t *xhci, xhci_device_t *dev) {
for (int i = 0; i < 31; ++i) {
if (dev->ep_rings[i].ring != NULL)
@@ -324,7 +353,7 @@ static void address_device_create_slot_callback(usb_device_t *device, void *ctx,
xhci_ring_command_doorbell(xhci);
}
static int xhci_ctrl_address_device(usb_ctrl_t *ctrl, usb_hub_t *hub, uint8_t port, usb_address_device_callback_t callback) {
static int xhci_ctrl_address_device(usb_ctrl_t *ctrl, usb_hub_t *hub, uint8_t port, usb_speed_t speed, usb_address_device_callback_t callback) {
++port; // convert to the format xhci expects
xhci_ctrl_t *xhci = container_of(ctrl, xhci_ctrl_t, ctrl);
@@ -378,7 +407,6 @@ static int xhci_ctrl_address_device(usb_ctrl_t *ctrl, usb_hub_t *hub, uint8_t po
xhci_dev->root_port_number = port;
}
// TODO the device speed is gonna come from elsewhere and not from the (just now allocated) usb device
if (hub->device == NULL) {
// this is a root hub! we can just take the speed directly from the portsc register
xhci_root_hub_t *rh = container_of(hub, xhci_root_hub_t, hub);
@@ -386,10 +414,13 @@ static int xhci_ctrl_address_device(usb_ctrl_t *ctrl, usb_hub_t *hub, uint8_t po
uint32_t port_speed = (portsc >> 10) & 0xf;
xhci_dev->ctrl_speed = port_speed;
xhci_dev->device.speed = xhci_speed_to_usb_speed(port_speed);
} else if (!usb_speed_to_xhci_speed(xhci_dev->device.speed, &xhci_dev->ctrl_speed)) {
// this is a super speed plus device behind a hub.
// we can't know its speed in advance, so set it as super speed and figure it out later.
xhci_dev->ctrl_speed = XHCI_PORT_SPEED_SUPER;
} else {
xhci_dev->device.speed = speed;
if (!usb_speed_to_xhci_speed(xhci_dev->device.speed, &xhci_dev->ctrl_speed)) {
// This is a super speed plus device behind a hub.
// We can't know its speed in advance, so set it as super speed and figure it out later.
xhci_dev->ctrl_speed = XHCI_PORT_SPEED_SUPER;
}
}
xhci_submission_t xhci_submission = {
@@ -463,6 +494,13 @@ static int xhci_ctrl_configure_ep(usb_ctrl_t *ctrl, usb_device_t *dev, usb_endpo
if (slot_ctx->dw0.ctx_entries < ep_index)
slot_ctx->dw0.ctx_entries = ep_index;
int error = xhci_update_hub_slot_context(dev, slot_ctx);
if (error) {
free(configure_ctx);
xhci_release_ep_ring(xhci_dev, ep_index);
return error;
}
// Set up the endpoint context.
uint8_t ep_type = ep->desc->bmAttributes & USB_ENDPOINT_ATTRIB_TYPE_MASK;
uint16_t max_packet_size = usb_endpoint_max_packet_size(ep->desc);
+1 -1
View File
@@ -37,7 +37,7 @@ void xhci_handle_port_change_event(xhci_ctrl_t *xhci, int global_port) {
usb_hub_event_disconnect(&hub->hub, local_port);
usb_hub_event_connect(&hub->hub, local_port);
} else if (reset_event) {
usb_hub_event_reset(&hub->hub, local_port);
usb_hub_event_reset(&hub->hub, local_port, USB_SPEED_UNKNOWN);
}
xhci->portregs[global_port - 1].portsc = (portsc & XHCI_PORTSC_PRESERVE_BITS) |