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>
This commit is contained in:
Amir Hammad 2015-04-01 16:22:05 +02:00
commit 7acc6fe474
29 changed files with 5593 additions and 0 deletions

186
src/demo.c Normal file
View file

@ -0,0 +1,186 @@
/*
* 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 "usart_helpers.h" /// provides LOG_PRINTF macros used for debugging
#include "usbh_hubbed.h" /// provides usbh_init() and usbh_poll()
#include "usbh_lld_stm32f4.h" /// provides low level usb host driver for stm32f4 platform
#include "usbh_driver_hid_mouse.h" /// provides usb device driver Human Interface Device - type mouse
#include "usbh_driver_hub.h" /// provides usb full speed hub driver (Low speed devices on hub are not supported)
#include "usbh_driver_gp_xbox.h" /// provides usb device driver for Gamepad: Microsoft XBOX compatible Controller
// STM32f407 compatible
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/otg_hs.h>
#include <libopencm3/stm32/otg_fs.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
static inline void delay_ms_busy_loop(uint32_t ms)
{
volatile uint32_t i;
for (i = 0; i < 14903*ms; i++);
}
/* Set STM32 to 168 MHz. */
static void clock_setup(void)
{
rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]);
// GPIO
rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPAEN); // OTG_FS + button
rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPBEN); // OTG_HS
rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPCEN); // USART + OTG_FS charge pump
rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN); // LEDS
// periphery
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_USART6EN);// USART
rcc_peripheral_enable_clock(&RCC_AHB2ENR, RCC_AHB2ENR_OTGFSEN);
rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_OTGHSEN);
}
static void gpio_setup(void)
{
/* Set GPIO12-15 (in GPIO port D) to 'output push-pull'. */
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT,
GPIO_PUPD_NONE, GPIO12 | GPIO13 | GPIO14 | GPIO15);
/* Set */
gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO0);
gpio_clear(GPIOC, GPIO0);
// OTG_FS
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12);
gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12);
// OTG_HS
gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO15 | GPIO14);
gpio_set_af(GPIOB, GPIO_AF12, GPIO14 | GPIO15);
// USART TX
gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6 | GPIO7);
gpio_set_af(GPIOC, GPIO_AF8, GPIO6 | GPIO7);
// button
gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO0);
}
static const usbh_dev_driver_t *device_drivers[] = {
&usbh_hub_driver,
&usbh_hid_mouse_driver,
&usbh_gp_xbox_driver,
0
};
static void gp_xbox_update(uint8_t device_id, gp_xbox_packet_t packet)
{
(void)device_id;
(void)packet;
LOG_PRINTF("update %d: %d %d \r\n", device_id, packet.axis_left_x, packet.buttons & GP_XBOX_BUTTON_A);
}
static void gp_xbox_connected(uint8_t device_id)
{
(void)device_id;
LOG_PRINTF("connected %d", device_id);
}
static void gp_xbox_disconnected(uint8_t device_id)
{
(void)device_id;
LOG_PRINTF("disconnected %d", device_id);
}
static const gp_xbox_config_t gp_xbox_config = {
.update = &gp_xbox_update,
.notify_connected = &gp_xbox_connected,
.notify_disconnected = &gp_xbox_disconnected
};
static void mouse_in_message_handler(uint8_t device_id, const uint8_t *data)
{
(void)device_id;
(void)data;
// print only first 4 bytes, since every mouse should have at least these four set.
// Report descriptors are not read by driver for now, so we do not know what each byte means
LOG_PRINTF("MOUSE EVENT %02X %02X %02X %02X \r\n", data[0], data[1], data[2], data[3]);
}
static const hid_mouse_config_t mouse_config = {
.mouse_in_message_handler = &mouse_in_message_handler
};
int main(void)
{
clock_setup();
gpio_setup();
#ifdef USART_DEBUG
usart_init(USART6, 921600);
#endif
LOG_PRINTF("\r\n\r\n\r\n\r\n\r\n###################\r\nInit\r\n");
/**
* device driver initialization
*
* Pass configuration struct where the callbacks are defined
*/
hid_mouse_driver_init(&mouse_config);
hub_driver_init();
gp_xbox_driver_init(&gp_xbox_config);
gpio_set(GPIOD, GPIO13);
/**
* Pass array of supported low level drivers
* In case of stm32f407, there are up to two supported OTG hosts on one chip.
* Each one can be enabled or disabled in config.mk - optimization for speed
*
* Pass array of supported device drivers
*/
usbh_init(usbh_lld_stm32f4_drivers, device_drivers);
gpio_clear(GPIOD, GPIO13);
LOG_PRINTF("USB init complete\r\n");
uint32_t i = 0;
while (1) {
LOG_FLUSH();
// Toggle some led
gpio_set(GPIOD, GPIO14);
usbh_poll(i);
gpio_clear(GPIOD, GPIO14);
delay_ms_busy_loop(1);
i += 1000;
}
return 0;
}

287
src/usart_helpers.c Normal file
View file

@ -0,0 +1,287 @@
/*
* 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 "usart_helpers.h"
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/gpio.h>
#ifndef USART_DEBUG
void usart_init(uint32_t usart, uint32_t baudrate)
{
(void)usart;
(void)baudrate;
}
void usart_printf(const char *str, ...)
{
(void)str;
}
void usart_vprintf(const char *str, va_list va)
{
(void)va;
(void)str;
}
void usart_fifo_send(void){}
void usart_call_cmd(struct usart_commands * commands)
{
(void)commands;
}
void usart_interrupt(void){}
#else
#warning compiling with debug functions
#define USART_FIFO_OUT_SIZE (4096)
uint8_t usart_fifo_out_data[USART_FIFO_OUT_SIZE];
uint32_t usart_fifo_out_len = 0;
uint32_t usart_fifo_out_index = 0;
#define USART_FIFO_IN_SIZE (1024)
uint8_t usart_fifo_in_data[USART_FIFO_IN_SIZE];
uint32_t usart_fifo_in_len = 0;
uint32_t usart_fifo_in_index = 0;
static uint32_t usart = 0;
static uint8_t usart_fifo_pop(void)
{
uint8_t ret;
usart_fifo_out_len--;
ret = usart_fifo_out_data[usart_fifo_out_index];
usart_fifo_out_index++;
if (usart_fifo_out_index == USART_FIFO_OUT_SIZE ) {
usart_fifo_out_index = 0;
}
return ret;
}
static void usart_fifo_push(uint8_t aData)
{
uint32_t i;
if( (usart_fifo_out_len + 1) == USART_FIFO_OUT_SIZE)//overflow
{
usart_fifo_out_len = 0;
LOG_PRINTF("OVERFLOW!");
return;
}
i = usart_fifo_out_index + usart_fifo_out_len;
if (i >= USART_FIFO_OUT_SIZE) {
i -= USART_FIFO_OUT_SIZE;
}
usart_fifo_out_data[i] = aData;
usart_fifo_out_len++;
}
static uint8_t usart_fifo_in_pop(void)
{
uint8_t ret;
usart_fifo_in_len--;
ret = usart_fifo_in_data[usart_fifo_in_index];
usart_fifo_in_index++;
if (usart_fifo_in_index == USART_FIFO_IN_SIZE ) {
usart_fifo_in_index = 0;
}
return ret;
}
static void usart_fifo_in_push(uint8_t aData)
{
uint32_t i;
if( (usart_fifo_in_len + 1) == USART_FIFO_IN_SIZE)//overflow
{
usart_fifo_in_len = 0;
return;
}
i = usart_fifo_in_index + usart_fifo_in_len;
if (i >= USART_FIFO_IN_SIZE) {
i -= USART_FIFO_IN_SIZE;
}
usart_fifo_in_data[i] = aData;
usart_fifo_in_len++;
}
static void usart_write(const char * data, uint32_t len)
{
uint32_t i;
for(i = 0; i < len; i++)
{
usart_fifo_push(data[i]);
}
}
void usart_printf(const char *str, ...)
{
va_list va;
va_start(va, str);
usart_vprintf(str, va);
va_end(va);
}
void usart_vprintf(const char *str, va_list va)
{
char databuffer[128];
int i = vsnprintf(databuffer, 128, str, va);
if (i > 0) {
usart_write(databuffer, i);
}
}
void usart_init(uint32_t arg_usart, uint32_t baudrate)
{
usart_set_baudrate(arg_usart, baudrate);
usart_set_databits(arg_usart, 8);
usart_set_flow_control(arg_usart, USART_FLOWCONTROL_NONE);
usart_set_mode(arg_usart, USART_MODE_TX | USART_MODE_RX);
usart_set_parity(arg_usart, USART_PARITY_NONE);
usart_set_stopbits(arg_usart, USART_STOPBITS_1);
usart_enable_rx_interrupt(arg_usart);
usart_enable(arg_usart);
usart = arg_usart;
}
void usart_interrupt(void)
{
if (usart_get_interrupt_source(usart, USART_SR_RXNE)) {
uint8_t data = usart_recv(usart);
usart_fifo_in_push(data);
if ( data != 3 && data != '\r' && data != '\n') {
usart_fifo_push(data);
} else {
LOG_PRINTF("\r\n>>");
}
}
}
void usart_fifo_send(void)
{
while(usart_fifo_out_len) {
uint8_t data = usart_fifo_pop();
usart_wait_send_ready(usart);
usart_send(usart, data);
}
}
static char command[128];
static uint8_t command_len = 0;
static uint8_t command_argindex = 0;
static uint8_t usart_read_command(void)
{
uint32_t fifo_len = usart_fifo_in_len;
while (fifo_len) {
uint8_t data = usart_fifo_in_pop();
if ((data >= 'A') && (data <= 'Z')) {
data += 'a'-'A';
}
if (((data >= 'a') && (data <= 'z')) || ((data >='0') && (data<='9'))) {
command[command_len++] = data;
} else if (data == ' ') {
if (command_len) {
if (command_argindex == 0) {
command[command_len++] = 0;
command_argindex = command_len;
} else {
command[command_len++] = ' ';
}
}
} else if (data == '\r' || data == '\n') {
if (command_len) {
command[command_len++] = 0;
if (!command_argindex) {
command_argindex = command_len;
}
return 1;
}
} else if (data == 127) {
if (command_len) {
if (command_argindex) {
if (command_len == command_argindex) {
command_argindex = 0;
}
}
command[command_len] = '\0';
command_len--;
}
} else if (data == 3) {
command_len = 0;
command_argindex = 0;
} else {
LOG_PRINTF("%d ",data);
}
fifo_len--;
}
return 0;
}
void usart_call_cmd(struct usart_commands * commands)
{
uint32_t i = 0;
if(!usart_read_command()) {
return;
}
if (!command_len) {
LOG_PRINTF("#2");
return;
}
//~ for (i = 0; i < command_len; i++) {
//~ LOG_PRINTF("%c", command[i]);
//~ }
i=0;
while(commands[i].cmd != NULL) {
if (!strcmp((char*)command, (char*)commands[i].cmd)) {
if (commands[i].callback) {
if(command_argindex == command_len) {
commands[i].callback(NULL);
} else {
commands[i].callback(&command[command_argindex]);
}
}
usart_write("\r\n>>",4);
command_len = 0;
command_argindex = 0;
return;
} else {
}
i++;
}
command_len = 0;
command_argindex = 0;
LOG_PRINTF("INVALID COMMAND\r\n>>");
}
#endif

56
src/usart_helpers.h Normal file
View file

@ -0,0 +1,56 @@
/*
* 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/>.
*
*/
#ifndef USBH_USART_HELPERS_H
#define USBH_USART_HELPERS_H
#include "usbh_hubbed.h"
#include <stdint.h>
#include <stdarg.h>
BEGIN_DECLS
struct usart_commands{
const char * cmd;
void (*callback)(const char * arg);
};
void usart_init(uint32_t usart, uint32_t baudrate);
void usart_printf(const char *str, ...);
void usart_vprintf(const char *str, va_list va);
void usart_fifo_send(void);
void usart_call_cmd(struct usart_commands * commands);
void usart_interrupt(void);
#ifdef USART_DEBUG
#define LOG_PRINTF(format, ...) usart_printf(format, ##__VA_ARGS__);
#define LOG_FLUSH() usart_fifo_send()
#else
#define LOG_PRINTF(dummy, ...) ((void)dummy)
#define LOG_FLUSH()
#endif
END_DECLS
#endif

420
src/usbh_driver_gp_xbox.c Normal file
View file

@ -0,0 +1,420 @@
/*
* 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 "usart_helpers.h"
#include "usbh_driver_gp_xbox.h"
#include "driver/usbh_device_driver.h"
#include <stdint.h>
#include <libopencm3/usb/usbstd.h>
static void *gp_xbox_init(void *usbh_dev);
static bool gp_xbox_analyze_descriptor(void *drvdata, void *descriptor);
static void gp_xbox_poll(void *drvdata, uint32_t tflp);
static void gp_xbox_remove(void *drvdata);
static const usbh_dev_driver_info_t usbh_gp_xbox_driver_info = {
.deviceClass = 0xff,
.deviceSubClass = 0xff,
.deviceProtocol = 0xff,
.idVendor = 0x045e,
.idProduct = 0x028e,
.ifaceClass = 0xff,
.ifaceSubClass = 93,
.ifaceProtocol = 0x01
};
const usbh_dev_driver_t usbh_gp_xbox_driver = {
.init = gp_xbox_init,
.analyze_descriptor = gp_xbox_analyze_descriptor,
.poll = gp_xbox_poll,
.remove = gp_xbox_remove,
.info = &usbh_gp_xbox_driver_info
};
enum STATES {
STATE_INACTIVE,
STATE_READING_COMPLETE,
STATE_READING_REQUEST,
STATE_SET_CONFIGURATION_REQUEST,
STATE_SET_CONFIGURATION_EMPTY_READ,
STATE_SET_CONFIGURATION_COMPLETE
};
#define GP_XBOX_CORRECT_TRANSFERRED_LENGTH 20
struct _gp_xbox_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_GP_XBOX_BUFFER];
uint16_t endpoint_in_maxpacketsize;
uint8_t endpoint_in_address;
enum STATES state_next;
uint8_t endpoint_in_toggle;
uint8_t device_id;
uint8_t configuration_value;
};
typedef struct _gp_xbox_device gp_xbox_device_t;
static gp_xbox_device_t gp_xbox_device[USBH_GP_XBOX_MAX_DEVICES];
static const gp_xbox_config_t *gp_xbox_config;
static bool initialized = false;
static void read_gp_xbox_in(gp_xbox_device_t *gp_xbox);
void gp_xbox_driver_init(const gp_xbox_config_t *config)
{
if (!config) {
return;
}
initialized = true;
uint32_t i;
gp_xbox_config = config;
for (i = 0; i < USBH_GP_XBOX_MAX_DEVICES; i++) {
gp_xbox_device[i].state_next = STATE_INACTIVE;
}
}
/**
*
*
*/
static void *gp_xbox_init(void *usbh_dev)
{
if (!initialized) {
LOG_PRINTF("driver not initialized");
return false;
}
uint32_t i;
gp_xbox_device_t *drvdata = 0;
// find free data space for gp_xbox device
for (i = 0; i < USBH_GP_XBOX_MAX_DEVICES; i++) {
if (gp_xbox_device[i].state_next == STATE_INACTIVE) {
drvdata = &gp_xbox_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->usbh_device = usbh_dev;
break;
}
}
return drvdata;
}
/**
* Returns true if all needed data are parsed
*/
static bool gp_xbox_analyze_descriptor(void *drvdata, void *descriptor)
{
gp_xbox_device_t *gp_xbox = 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;
gp_xbox->configuration_value = cfg->bConfigurationValue;
}
break;
case USB_DT_DEVICE:
break;
case USB_DT_INTERFACE:
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)) {
gp_xbox->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_GP_XBOX_BUFFER) {
gp_xbox->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
gp_xbox->endpoint_in_maxpacketsize = USBH_GP_XBOX_BUFFER;
}
}
if (gp_xbox->endpoint_in_address) {
gp_xbox->state_next = STATE_SET_CONFIGURATION_REQUEST;
return true;
}
}
}
break;
// TODO Class Specific descriptors
default:
break;
}
return false;
}
static void parse_data(usbh_device_t *dev)
{
gp_xbox_device_t *gp_xbox = dev->drvdata;
uint8_t *packet = gp_xbox->buffer;
gp_xbox_packet_t gp_xbox_packet;
gp_xbox_packet.buttons = 0;
// DPAD
const uint8_t data1 = packet[2];
const uint8_t data2 = packet[3];
if (data1 & (1 << 0)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_TOP;
}
if (data1 & (1 << 1)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_BOTTOM;
}
if (data1 & (1 << 2)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_LEFT;
}
if (data1 & (1 << 3)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_RIGHT;
}
// Start + select
if (data1 & (1 << 4)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_START;
}
if (data1 & (1 << 5)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_SELECT;
}
// axis buttons
if (data1 & (1 << 6)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_AXIS_LEFT;
}
if (data1 & (1 << 7)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_AXIS_RIGHT;
}
// buttons ABXY
if (data2 & (1 << 4)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_A;
}
if (data2 & (1 << 5)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_B;
}
if (data2 & (1 << 6)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_X;
}
if (data2 & (1 << 7)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_Y;
}
// buttons rear
if (data2 & (1 << 0)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_LT;
}
if (data2 & (1 << 1)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_RT;
}
if (data2 & (1 << 2)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_XBOX;
}
// rear levers
gp_xbox_packet.axis_rear_left = packet[4];
gp_xbox_packet.axis_rear_right = packet[5];
gp_xbox_packet.axis_left_x = packet[7]*256 + packet[6];
gp_xbox_packet.axis_left_y = packet[9]*256 + packet[8];
gp_xbox_packet.axis_right_x = packet[11]*256 + packet[10];
gp_xbox_packet.axis_right_y = packet[13]*256 + packet[12];
// call update callback
if (gp_xbox_config->update) {
gp_xbox_config->update(gp_xbox->device_id, gp_xbox_packet);
}
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
gp_xbox_device_t *gp_xbox = dev->drvdata;
switch (gp_xbox->state_next) {
case STATE_READING_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
parse_data(dev);
gp_xbox->state_next = STATE_READING_REQUEST;
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (cb_data.transferred_length == GP_XBOX_CORRECT_TRANSFERRED_LENGTH) {
parse_data(dev);
}
gp_xbox->state_next = STATE_READING_REQUEST;
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
gp_xbox->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_SET_CONFIGURATION_EMPTY_READ:
{
LOG_PRINTF("|empty packet read|");
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
gp_xbox->state_next = STATE_SET_CONFIGURATION_COMPLETE;
device_xfer_control_read(0, 0, 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);
gp_xbox->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_SET_CONFIGURATION_COMPLETE: // Configured
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
gp_xbox->state_next = STATE_READING_REQUEST;
gp_xbox->endpoint_in_toggle = 0;
LOG_PRINTF("\r\ngp_xbox CONFIGURED\r\n");
if (gp_xbox_config->notify_connected) {
gp_xbox_config->notify_connected(gp_xbox->device_id);
}
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
ERROR(cb_data.status);
gp_xbox->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_INACTIVE:
{
LOG_PRINTF("XBOX inactive");
}
break;
default:
{
LOG_PRINTF("Unknown state\r\n");
}
break;
}
}
static void read_gp_xbox_in(gp_xbox_device_t *gp_xbox)
{
usbh_packet_t packet;
packet.address = gp_xbox->usbh_device->address;
packet.data = &gp_xbox->buffer[0];
packet.datalen = gp_xbox->endpoint_in_maxpacketsize;
packet.endpoint_address = gp_xbox->endpoint_in_address;
packet.endpoint_size_max = gp_xbox->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_EPTYP_BULK;
packet.speed = gp_xbox->usbh_device->speed;
packet.callback = event;
packet.callback_arg = gp_xbox->usbh_device;
packet.toggle = &gp_xbox->endpoint_in_toggle;
gp_xbox->state_next = STATE_READING_COMPLETE;
usbh_read(gp_xbox->usbh_device, &packet);
// LOG_PRINTF("@gp_xbox EP1 | \r\n");
}
/**
*
* tflp time from last poll [us]
*/
static void gp_xbox_poll(void *drvdata, uint32_t tflp)
{
(void)tflp;
gp_xbox_device_t *gp_xbox = drvdata;
usbh_device_t *dev = gp_xbox->usbh_device;
switch (gp_xbox->state_next) {
case STATE_READING_REQUEST:
{
read_gp_xbox_in(gp_xbox);
}
break;
case STATE_SET_CONFIGURATION_REQUEST:
{
struct usb_setup_data setup_data;
setup_data.bmRequestType = 0b00000000;
setup_data.bRequest = USB_REQ_SET_CONFIGURATION;
setup_data.wValue = gp_xbox->configuration_value;
setup_data.wIndex = 0;
setup_data.wLength = 0;
gp_xbox->state_next = STATE_SET_CONFIGURATION_EMPTY_READ;
device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
}
break;
default:
{
// do nothing - probably transfer is in progress
}
break;
}
}
static void gp_xbox_remove(void *drvdata)
{
LOG_PRINTF("Removing xbox\r\n");
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)drvdata;
if (gp_xbox_config->notify_disconnected) {
gp_xbox_config->notify_disconnected(gp_xbox->device_id);
}
gp_xbox->state_next = STATE_INACTIVE;
gp_xbox->endpoint_in_address = 0;
}

294
src/usbh_driver_hid_mouse.c Normal file
View file

@ -0,0 +1,294 @@
/*
* 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_hubbed.h"
#include "driver/usbh_device_driver.h"
#include "usbh_driver_hid_mouse.h"
#include "usart_helpers.h"
#include <libopencm3/usb/usbstd.h>
static void *mouse_init(void *usbh_dev);
static bool mouse_analyze_descriptor(void *drvdata, void *descriptor);
static void mouse_poll(void *drvdata, uint32_t tflp);
static void mouse_remove(void *drvdata);
static const usbh_dev_driver_info_t usbh_hid_mouse_driver_info = {
.deviceClass = -1,
.deviceSubClass = -1,
.deviceProtocol = -1,
.idVendor = -1,
.idProduct = -1,
.ifaceClass = 0x03,
.ifaceSubClass = -1,
.ifaceProtocol = 0x02
};
const usbh_dev_driver_t usbh_hid_mouse_driver = {
.init = mouse_init,
.analyze_descriptor = mouse_analyze_descriptor,
.poll = mouse_poll,
.remove = mouse_remove,
.info = &usbh_hid_mouse_driver_info
};
enum STATES {
STATE_INACTIVE,
STATE_READING_COMPLETE,
STATE_READING_REQUEST,
STATE_SET_CONFIGURATION_REQUEST,
STATE_SET_CONFIGURATION_EMPTY_READ,
STATE_SET_CONFIGURATION_COMPLETE
};
struct _hid_mouse_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_HID_MOUSE_BUFFER];
uint16_t endpoint_in_maxpacketsize;
uint8_t endpoint_in_address;
enum STATES state_next;
uint8_t endpoint_in_toggle;
uint8_t device_id;
uint8_t configuration_value;
};
typedef struct _hid_mouse_device hid_mouse_device_t;
static hid_mouse_device_t mouse_device[USBH_HID_MOUSE_MAX_DEVICES];
static const hid_mouse_config_t *mouse_config;
#include <stdint.h>
void hid_mouse_driver_init(const hid_mouse_config_t *config)
{
uint32_t i;
mouse_config = config;
for (i = 0; i < USBH_HID_MOUSE_MAX_DEVICES; i++) {
mouse_device[i].state_next = STATE_INACTIVE;
}
}
/**
*
*
*/
static void *mouse_init(void *usbh_dev)
{
uint32_t i;
hid_mouse_device_t *drvdata = 0;
// find free data space for mouse device
for (i = 0; i < USBH_HID_MOUSE_MAX_DEVICES; i++) {
if (mouse_device[i].state_next == STATE_INACTIVE) {
drvdata = &mouse_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->usbh_device = usbh_dev;
break;
}
}
return drvdata;
}
/**
* Returns true if all needed data are parsed
*/
static bool mouse_analyze_descriptor(void *drvdata, void *descriptor)
{
hid_mouse_device_t *mouse = 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;
mouse->configuration_value = cfg->bConfigurationValue;
}
break;
case USB_DT_DEVICE:
break;
case USB_DT_INTERFACE:
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)) {
mouse->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_HID_MOUSE_BUFFER) {
mouse->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
mouse->endpoint_in_maxpacketsize = USBH_HID_MOUSE_BUFFER;
}
}
if (mouse->endpoint_in_address) {
mouse->state_next = STATE_SET_CONFIGURATION_REQUEST;
return true;
}
}
}
break;
// TODO Class Specific descriptors
default:
break;
}
return false;
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
hid_mouse_device_t *mouse = dev->drvdata;
switch (mouse->state_next) {
case STATE_READING_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
mouse->state_next = STATE_READING_REQUEST;
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
mouse->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_SET_CONFIGURATION_EMPTY_READ:
{
LOG_PRINTF("|empty packet read|");
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
mouse->state_next++;
device_xfer_control_read(0, 0, event, dev);
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
mouse->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_SET_CONFIGURATION_COMPLETE: // Configured
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
mouse->state_next = STATE_READING_REQUEST;
mouse->endpoint_in_toggle = 0;
LOG_PRINTF("\r\nMOUSE CONFIGURED\r\n");
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
mouse->state_next = STATE_INACTIVE;
break;
}
}
break;
default:
break;
}
}
static void read_mouse_in(void *drvdata)
{
hid_mouse_device_t *mouse = drvdata;
usbh_packet_t packet;
packet.address = mouse->usbh_device->address;
packet.data = &mouse->buffer[0];
packet.datalen = mouse->endpoint_in_maxpacketsize;
packet.endpoint_address = mouse->endpoint_in_address;
packet.endpoint_size_max = mouse->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_EPTYP_BULK;
packet.speed = mouse->usbh_device->speed;
packet.callback = event;
packet.callback_arg = mouse->usbh_device;
packet.toggle = &mouse->endpoint_in_toggle;
mouse->state_next = STATE_READING_COMPLETE;
usbh_read(mouse->usbh_device, &packet);
// LOG_PRINTF("@MOUSE EP1 | \r\n");
}
/**
*
* tflp time from last poll [us]
*/
static void mouse_poll(void *drvdata, uint32_t tflp)
{
(void)drvdata;
(void)tflp;
hid_mouse_device_t *mouse = drvdata;
usbh_device_t *dev = mouse->usbh_device;
switch (mouse->state_next) {
case STATE_READING_REQUEST:
{
read_mouse_in(drvdata);
}
break;
case STATE_SET_CONFIGURATION_REQUEST:
{
struct usb_setup_data setup_data;
setup_data.bmRequestType = 0b00000000;
setup_data.bRequest = USB_REQ_SET_CONFIGURATION;
setup_data.wValue = mouse->configuration_value;
setup_data.wIndex = 0;
setup_data.wLength = 0;
mouse->state_next++;
device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
}
break;
default:
// do nothing - probably transfer is in progress
break;
}
}
static void mouse_remove(void *drvdata)
{
hid_mouse_device_t *mouse = (hid_mouse_device_t *)drvdata;
mouse->state_next = STATE_INACTIVE;
mouse->endpoint_in_address = 0;
}

865
src/usbh_driver_hub.c Normal file
View file

@ -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;
}
}

View file

@ -0,0 +1,108 @@
/*
* 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/>.
*
*/
#ifndef USBH_DRIVER_HUB_PRIVATE_
#define USBH_DRIVER_HUB_PRIVATE_
#include "usbh_config.h"
#include "driver/usbh_device_driver.h"
#include "usbh_driver_hub.h"
#include <stdint.h>
#include <stdbool.h>
#include <libopencm3/usb/usbstd.h>
// # HUB DEFINITIONS
#define HUB_FEATURE_PORT_CONNECTION 0
#define HUB_FEATURE_PORT_ENABLE 1
#define HUB_FEATURE_PORT_SUSPEND 2
#define HUB_FEATURE_PORT_OVERCURRENT 3
#define HUB_FEATURE_PORT_RESET 4
#define HUB_FEATURE_PORT_POWER 8
#define HUB_FEATURE_PORT_LOWSPEED 9
#define HUB_FEATURE_PORT_HIGHSPEED 10
#define HUB_FEATURE_C_PORT_CONNECTION 16
#define HUB_FEATURE_C_PORT_ENABLE 17
#define HUB_FEATURE_C_PORT_SUSPEND 18
#define HUB_FEATURE_C_PORT_OVERCURRENT 19
#define HUB_FEATURE_C_PORT_RESET 20
#define HUB_REQ_GET_STATUS 0
#define HUB_REQ_CLEAR_FEATURE 1
#define HUB_REQ_SET_FEATURE 3
#define HUB_REQ_GET_DESCRIPTOR 6
#define USB_DT_HUB (41)
#define USB_DT_HUB_SIZE (9)
// Hub buffer: must be larger than hub descriptor
#define USBH_HUB_BUFFER_SIZE (USB_DT_HUB_SIZE)
#define CURRENT_PORT_NONE -1
struct _hub_device {
usbh_device_t *device[USBH_HUB_MAX_DEVICES + 1];
uint8_t buffer[USBH_HUB_BUFFER_SIZE];
uint16_t endpoint_in_maxpacketsize;
uint8_t endpoint_in_address;
uint8_t endpoint_in_toggle;
uint8_t state;
uint8_t desc_len;
uint16_t ports_num;
int8_t index;
int8_t current_port;
struct {
uint16_t sts;
uint16_t stc;
} hub_and_port_status[USBH_HUB_MAX_DEVICES + 1];
bool busy;
uint32_t time_curr_us;
uint32_t timestamp_us;
};
typedef struct _hub_device hub_device_t;
struct usb_hub_descriptor_head {
uint8_t bDescLength;
uint8_t bDescriptorType;
uint8_t bNbrPorts;
uint16_t wHubCharacteristics;
uint8_t bPwrOn2PwrGood;
uint8_t bHubContrCurrent;
} __attribute__((packed));
struct usb_hub_descriptor_body {
uint8_t bDeviceRemovable;
uint8_t PortPwrCtrlMask;
} __attribute__((packed));
// for hubs with up to 7 ports on hub
struct usb_hub_descriptor {
struct usb_hub_descriptor_head head;
struct usb_hub_descriptor_body body[1];
} __attribute__((packed));
#endif

634
src/usbh_hubbed.c Normal file
View file

@ -0,0 +1,634 @@
/*
* 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_config.h"
#include "usbh_lld_stm32f4.h"
#include "driver/usbh_device_driver.h"
#include "usart_helpers.h"
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/usb/usbstd.h>
static struct {
bool enumeration_run;
const usbh_driver_t * const *lld_drivers;
const usbh_dev_driver_t * const *dev_drivers;
int8_t address_temporary;
} usbh_data = {0};
static void set_enumeration(void)
{
usbh_data.enumeration_run = true;
}
static void reset_enumeration(void)
{
usbh_data.enumeration_run = false;
}
static bool enumeration(void)
{
return usbh_data.enumeration_run;
}
/**
*
*/
static const usbh_dev_driver_t *find_driver(const usbh_dev_driver_info_t * device_info)
{
#define CHECK_PARTIAL_COMPATIBILITY(what) \
if (usbh_data.dev_drivers[i]->info->what != -1\
&& device_info->what != usbh_data.dev_drivers[i]->info->what) {\
i++;\
continue;\
}
int i = 0;
while (usbh_data.dev_drivers[i]) {
CHECK_PARTIAL_COMPATIBILITY(ifaceClass);
CHECK_PARTIAL_COMPATIBILITY(ifaceSubClass);
CHECK_PARTIAL_COMPATIBILITY(ifaceProtocol);
CHECK_PARTIAL_COMPATIBILITY(deviceClass);
CHECK_PARTIAL_COMPATIBILITY(deviceSubClass);
CHECK_PARTIAL_COMPATIBILITY(deviceProtocol);
CHECK_PARTIAL_COMPATIBILITY(idVendor);
CHECK_PARTIAL_COMPATIBILITY(idProduct);
return usbh_data.dev_drivers[i];
}
return 0;
#undef CHECK_PARTIAL_COMPATIBILITY
}
static void device_register(void *descriptors, uint16_t descriptors_len, usbh_device_t *dev)
{
uint32_t i = 0;
dev->drv = 0;
uint8_t *buf = (uint8_t *)descriptors;
dev->drv = 0;
dev->drvdata = 0;
uint8_t desc_len = buf[i];
uint8_t desc_type = buf[i + 1];
usbh_dev_driver_info_t device_info;
if (desc_type == USB_DT_DEVICE) {
struct usb_device_descriptor *device_desc = (void*)&buf[i];
LOG_PRINTF("DEVICE DESCRIPTOR");
device_info.deviceClass = device_desc->bDeviceClass;
device_info.deviceSubClass = device_desc->bDeviceSubClass;
device_info.deviceProtocol = device_desc->bDeviceProtocol;
device_info.idVendor = device_desc->idVendor;
device_info.idProduct = device_desc->idProduct;
} else {
LOG_PRINTF("INVALID descriptors pointer - fatal error");
return;
}
while (i < descriptors_len) {
desc_len = buf[i];
desc_type = buf[i + 1];
switch (desc_type) {
case USB_DT_DEVICE:
{
struct usb_device_descriptor *device_desc = (void*)&buf[i];
LOG_PRINTF("DEVICE DESCRIPTOR");
device_info.deviceClass = device_desc->bDeviceClass;
device_info.deviceSubClass = device_desc->bDeviceSubClass;
}
break;
case USB_DT_INTERFACE:
{
LOG_PRINTF("INTERFACE_DESCRIPTOR\r\n");
struct usb_interface_descriptor *iface = (void*)&buf[i];
device_info.ifaceClass = iface->bInterfaceClass;
device_info.ifaceSubClass = iface->bInterfaceSubClass;
device_info.ifaceProtocol = iface->bInterfaceProtocol;
const usbh_dev_driver_t *driver = find_driver(&device_info);
if (driver) {
dev->drv = driver;
dev->drvdata = dev->drv->init(dev);
if (!dev->drvdata) {
LOG_PRINTF("CANT TOUCH THIS");
}
break;
}
}
break;
default:
break;
}
if (desc_len == 0) {
LOG_PRINTF("PROBLEM WITH PARSE %d\r\n",i);
return;
}
i += desc_len;
}
if (dev->drv && dev->drvdata) {
// analyze descriptors
LOG_PRINTF("ANALYZE");
i = 0;
while (i < descriptors_len) {
desc_len = buf[i];
void *drvdata = dev->drvdata;
LOG_PRINTF("[%d]",buf[i+1]);
if (dev->drv->analyze_descriptor(drvdata, &buf[i])) {
LOG_PRINTF("Device Initialized\r\n");
return;
}
i += desc_len;
}
}
LOG_PRINTF("Device NOT Initialized\r\n");
}
void usbh_init(const void *drivers_lld[], const usbh_dev_driver_t * const device_drivers[])
{
if (!drivers_lld) {
return;
}
usbh_data.lld_drivers = (const usbh_driver_t **)drivers_lld;
usbh_data.dev_drivers = device_drivers;
// TODO: init structures
uint32_t k = 0;
while (usbh_data.lld_drivers[k]) {
LOG_PRINTF("DRIVER %d\r\n", k);
usbh_device_t * usbh_device =
((usbh_generic_data_t *)(usbh_data.lld_drivers[k])->driver_data)->usbh_device;
uint32_t i;
for (i = 0; i < USBH_MAX_DEVICES; i++) {
//~ LOG_PRINTF("%p ", &usbh_device[i]);
usbh_device[i].address = -1;
usbh_device[i].drv = 0;
usbh_device[i].drvdata = 0;
}
LOG_PRINTF("DRIVER %d", k);
usbh_data.lld_drivers[k]->init(usbh_data.lld_drivers[k]->driver_data);
k++;
}
}
/*
* NEW ENUMERATE
*
*/
void device_xfer_control_write(void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev)
{
usbh_packet_t packet;
packet.data = data;
packet.datalen = datalen;
packet.address = dev->address;
packet.endpoint_address = 0;
packet.endpoint_size_max = dev->packet_size_max0;
packet.endpoint_type = USBH_EPTYP_CONTROL;
packet.speed = dev->speed;
packet.callback = callback;
packet.callback_arg = dev;
packet.toggle = &dev->toggle0;
usbh_write(dev, &packet);
LOG_PRINTF("WR@device...%d | \r\n", dev->address);
}
void device_xfer_control_read(void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev)
{
usbh_packet_t packet;
packet.data = data;
packet.datalen = datalen;
packet.address = dev->address;
packet.endpoint_address = 0;
packet.endpoint_size_max = dev->packet_size_max0;
packet.endpoint_type = USBH_EPTYP_CONTROL;
packet.speed = dev->speed;
packet.callback = callback;
packet.callback_arg = dev;
packet.toggle = &dev->toggle0;
usbh_read(dev, &packet);
LOG_PRINTF("RD@device...%d | \r\n", dev->address);
}
bool usbh_enum_available(void)
{
return !enumeration();
}
/**
* Returns 0 on error
* device otherwise
*/
usbh_device_t *usbh_get_free_device(const usbh_device_t *dev)
{
const usbh_driver_t *lld = dev->lld;
usbh_generic_data_t *lld_data = lld->driver_data;
usbh_device_t *usbh_device = lld_data->usbh_device;
uint8_t i;
LOG_PRINTF("DEV ADDRESS%d\r\n", dev->address);
for (i = 0; i < USBH_MAX_DEVICES; i++) {
if (usbh_device[i].address < 0) {
LOG_PRINTF("\t\t\t\t\tFOUND: %d", i);
usbh_device[i].address = i+1;
return &usbh_device[i];
} else {
LOG_PRINTF("address: %d\r\n\r\n\r\n", usbh_device[i].address);
}
}
return 0;
}
static void device_enumeration_terminate(usbh_device_t *dev)
{
reset_enumeration();
dev->state = 0;
dev->address = -1;
}
/* Do not call this function directly,
* only via callback passing into low-level function
* If you must, call it carefully ;)
*/
static void device_enumerate(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
const usbh_driver_t *lld = dev->lld;
usbh_generic_data_t *lld_data = lld->driver_data;
uint8_t *usbh_buffer = lld_data->usbh_buffer;
uint8_t state_start = dev->state; // Detection of hang
// LOG_PRINTF("\r\nSTATE: %d\r\n", state);
switch (dev->state) {
case 1:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
dev->state++;
LOG_PRINTF("::%d::", dev->address);
device_xfer_control_read(0, 0, device_enumerate, dev);
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case 2:
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
if (dev->address == 0) {
dev->address = usbh_data.address_temporary;
LOG_PRINTF("ADDR: %d\r\n", dev->address);
}
struct usb_setup_data setup_data;
setup_data.bmRequestType = 0b10000000;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_DEVICE << 8;
setup_data.wIndex = 0;
setup_data.wLength = USB_DT_DEVICE_SIZE;
dev->state++;
device_xfer_control_write(&setup_data, sizeof(setup_data),
device_enumerate, dev);
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
break;
case 3:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
dev->state++;
device_xfer_control_read(&usbh_buffer[0], USB_DT_DEVICE_SIZE,
device_enumerate, dev);
break;
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
dev->state = 2;
// WARNING: Recursion
// .. but should work
cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
device_enumerate(dev, cb_data);
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case 4:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_device_descriptor *ddt =
(struct usb_device_descriptor *)&usbh_buffer[0];
struct usb_setup_data setup_data;
setup_data.bmRequestType = 0b10000000;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_CONFIGURATION << 8;
setup_data.wIndex = 0;
setup_data.wLength = ddt->bMaxPacketSize0;//USB_DT_CONFIGURATION_SIZE;
dev->state++;
device_xfer_control_write(&setup_data, sizeof(setup_data),
device_enumerate, dev);
}
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (cb_data.transferred_length >= 8) {
struct usb_device_descriptor *ddt =
(struct usb_device_descriptor *)&usbh_buffer[0];
dev->packet_size_max0 = ddt->bMaxPacketSize0;
dev->state = 2;
// WARNING: Recursion
// .. but should work
cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
device_enumerate(dev, cb_data);
}
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case 5:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
dev->state++;
device_xfer_control_read(&usbh_buffer[USB_DT_DEVICE_SIZE],
dev->packet_size_max0, device_enumerate, dev);
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case 6:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
struct usb_setup_data setup_data;
LOG_PRINTF("WRITE: LEN: %d", cdt->wTotalLength);
setup_data.bmRequestType = 0b10000000;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_CONFIGURATION << 8;
setup_data.wIndex = 0;
setup_data.wLength = cdt->wTotalLength;
dev->state++;
device_xfer_control_write(&setup_data, sizeof(setup_data),
device_enumerate, dev);
}
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (cb_data.transferred_length >= USB_DT_CONFIGURATION_SIZE) {
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
if (cb_data.transferred_length <= cdt->wTotalLength) {
dev->state = 8;
// WARNING: Recursion
// .. but should work
cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
device_enumerate(dev, cb_data);
}
}
break;
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case 7:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
dev->state++;
device_xfer_control_read(&usbh_buffer[USB_DT_DEVICE_SIZE],
cdt->wTotalLength, device_enumerate, dev);
}
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case 8:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
LOG_PRINTF("TOTAL_LENGTH: %d\r\n", cdt->wTotalLength);
device_register(usbh_buffer, cdt->wTotalLength + USB_DT_DEVICE_SIZE, dev);
dev->state++;
reset_enumeration();
}
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
}
if (state_start == dev->state) {
LOG_PRINTF("\r\n !HANG %d\r\n", state_start);
}
}
void device_enumeration_start(usbh_device_t *dev)
{
set_enumeration();
dev->state = 1;
// save address
uint8_t address = dev->address;
dev->address = 0;
if (dev->speed == USBH_SPEED_LOW) {
dev->packet_size_max0 = 8;
} else {
dev->packet_size_max0 = 64;
}
usbh_data.address_temporary = address;
LOG_PRINTF("\r\n\r\n\r\n ENUMERATION OF DEVICE@%d STARTED \r\n\r\n", address);
struct usb_setup_data setup_data;
setup_data.bmRequestType = 0b00000000;
setup_data.bRequest = USB_REQ_SET_ADDRESS;
setup_data.wValue = address;
setup_data.wIndex = 0;
setup_data.wLength = 0;
device_xfer_control_write(&setup_data, sizeof(setup_data),
device_enumerate, dev);
}
/**
* Should be called with at least 1kHz frequency
*
*/
void usbh_poll(uint32_t t_us)
{
uint32_t k = 0;
while (usbh_data.lld_drivers[k]) {
usbh_device_t * usbh_device =
((usbh_generic_data_t *)(usbh_data.lld_drivers[k]->driver_data))->usbh_device;
usbh_generic_data_t *lld_data = usbh_data.lld_drivers[k]->driver_data;
enum USBH_POLL_STATUS poll_status =
usbh_data.lld_drivers[k]->poll(lld_data, t_us);
switch (poll_status) {
case USBH_POLL_STATUS_DEVICE_CONNECTED:
// New device found
LOG_PRINTF("\r\nDEVICE FOUND\r\n");
usbh_device[0].lld = usbh_data.lld_drivers[k];
usbh_device[0].speed = usbh_data.lld_drivers[k]->root_speed(lld_data);
usbh_device[0].address = 1;
device_enumeration_start(&usbh_device[0]);
break;
case USBH_POLL_STATUS_DEVICE_DISCONNECTED:
{
// Device disconnected
if (usbh_device[0].drv && usbh_device[0].drvdata) {
usbh_device[0].drv->remove(usbh_device[0].drvdata);
}
usbh_device[0].drv = 0;
usbh_device[0].drvdata = 0;
uint32_t i;
for (i = 1; i < USBH_MAX_DEVICES; i++) {
usbh_device[i].address = -1;
usbh_device[i].drv = 0;
usbh_device[i].drvdata = 0;
}
}
break;
default:
break;
}
if (lld_data->usbh_device[0].drv && usbh_device[0].drvdata) {
usbh_device[0].drv->poll(usbh_device[0].drvdata, t_us);
}
k++;
}
}
void usbh_read(usbh_device_t *dev, usbh_packet_t *packet)
{
const usbh_driver_t *lld = dev->lld;
lld->read(lld->driver_data, packet);
}
void usbh_write(usbh_device_t *dev, const usbh_packet_t *packet)
{
const usbh_driver_t *lld = dev->lld;
lld->write(lld->driver_data, packet);
}

1048
src/usbh_lld_stm32f4.c Normal file

File diff suppressed because it is too large Load diff