Files
2020-07-11 22:41:23 +03:00

178 lines
5.3 KiB
C

#include "drivers/usb/const.h"
#include "drivers/usb/device.h"
#include "drivers/usb/request.h"
#include "drivers/usb/driver.h"
#include "sys/assert.h"
#include "sys/debug.h"
#include "sys/heap.h"
#include "sys/attr.h"
#define USB_DESCRIPTOR_HUB 0x29
#define USB_FEATURE_PORT_RESET 4
#define USB_FEATURE_PORT_POWER 8
#define HUB_STATUS_PORT_CONNECTED (1 << 0)
#define HUB_STATUS_PORT_ENABLED (1 << 1)
#define HUB_STATUS_PORT_SUSPENDED (1 << 2)
#define HUB_STATUS_PORT_SPEED_SHIFT 9
#define HUB_STATUS_PORT_SPEED_MASK (0x3 << HUB_STATUS_PORT_SPEED_SHIFT)
#define HUB_POWER_MASK 0x03
#define HUB_POWER_INDIVIDUAL 0x01
struct usb_desc_hub {
uint8_t len;
uint8_t type;
uint8_t port_count;
uint16_t chars;
uint8_t power_time;
uint8_t current;
} __attribute__((packed));
struct usb_hub {
struct usb_desc_hub desc_hub;
};
static void usb_hub_poll(struct usb_device *dev);
static int usb_hub_init(struct usb_device *dev);
static struct usb_driver g_usb_hub = {
.usb_init = usb_hub_init,
.usb_poll = usb_hub_poll
};
static void usb_hub_poll(struct usb_device *dev) {
}
static uint16_t usb_hub_reset_port(struct usb_device *dev, uint8_t port) {
int res;
if ((res = usb_device_request(dev,
USB_REQUEST_TYPE_CLASS | USB_REQUEST_TYPE_OTHER,
USB_REQUEST_SET_FEATURE,
port + 1,
USB_FEATURE_PORT_RESET,
0, 0)) != 0) {
kwarn("Hub port reset failed\n");
return 0;
}
uint32_t status = 0;
for (int attempt = 0; attempt < 10; ++attempt) {
// Wait for port to reset
for (int i = 0; i < 1000000; ++i) {
asm volatile ("pause");
}
// Get status
if ((res = usb_device_request(dev,
USB_REQUEST_TYPE_CLASS | USB_REQUEST_TYPE_D2H | USB_REQUEST_TYPE_OTHER,
USB_REQUEST_GET_STATUS,
port + 1,
0,
sizeof(status), &status)) != 0) {
kwarn("Failed to get hub status after port reset\n");
return 0;
}
if (!(status & HUB_STATUS_PORT_CONNECTED)) {
break;
}
if (status & HUB_STATUS_PORT_ENABLED) {
break;
}
}
return status;
}
static int usb_hub_probe(struct usb_device *dev) {
struct usb_hub *hub = dev->driver_data;
uint8_t port_count = hub->desc_hub.port_count;
int res;
// Check if each port has separate power controls
if ((hub->desc_hub.chars & HUB_POWER_MASK) == HUB_POWER_INDIVIDUAL) {
// Enable all the ports when probing
for (uint8_t i = 0; i < port_count; ++i) {
if ((res = usb_device_request(dev,
USB_REQUEST_TYPE_CLASS | USB_REQUEST_TYPE_OTHER,
USB_REQUEST_SET_FEATURE,
i + 1,
USB_FEATURE_PORT_POWER,
0, 0)) != 0) {
kwarn("Port power on failed for hub\n");
return res;
}
// Wait for port to power on
for (int i = 0; i < 1000000; ++i) {
asm volatile ("pause");
}
}
}
for (uint8_t i = 0; i < port_count; ++i) {
// Reset port
uint16_t status = usb_hub_reset_port(dev, i);
if (status & HUB_STATUS_PORT_ENABLED) {
struct usb_device *ndev = usb_device_create();
ndev->hc = dev->hc;
ndev->max_packet = 8;
ndev->hc_control = dev->hc_control;
ndev->hc_interrupt = dev->hc_interrupt;
ndev->address = 0;
ndev->endpoint_toggle = 0;
ndev->speed = (status & HUB_STATUS_PORT_SPEED_MASK) >> HUB_STATUS_PORT_SPEED_SHIFT;
usb_device_init(ndev);
}
}
return 0;
}
static int usb_hub_init(struct usb_device *dev) {
int res;
if (dev->desc_interface.interface_class == USB_CLASS_HUB) {
struct usb_hub *hub = kmalloc(sizeof(struct usb_hub));
_assert(hub);
dev->driver = &g_usb_hub;
dev->driver_data = hub;
// Get hub descriptor
if ((res = usb_device_request(dev,
USB_REQUEST_TYPE_D2H | USB_REQUEST_TYPE_CLASS,
USB_REQUEST_GET_DESCRIPTOR,
0,
USB_DESCRIPTOR_HUB << 8,
sizeof(struct usb_desc_hub),
&hub->desc_hub)) != 0) {
kwarn("Failed to get hub descriptor\n");
return res;
}
kinfo("Hub descriptor:\n");
kinfo("Port count: %d\n", hub->desc_hub.port_count);
kinfo("Characteristics: %02x\n", hub->desc_hub.chars);
kinfo("Power time: %d\n", hub->desc_hub.power_time);
kinfo("Current: %d\n", hub->desc_hub.current);
usb_hub_probe(dev);
return 0;
}
return -1;
}
__init(usb_hub_register_driver) {
usb_add_driver(&g_usb_hub);
}