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:
commit
7acc6fe474
29 changed files with 5593 additions and 0 deletions
186
src/demo.c
Normal file
186
src/demo.c
Normal 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
287
src/usart_helpers.c
Normal 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
56
src/usart_helpers.h
Normal 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
420
src/usbh_driver_gp_xbox.c
Normal 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
294
src/usbh_driver_hid_mouse.c
Normal 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
865
src/usbh_driver_hub.c
Normal 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;
|
||||
|
||||
}
|
||||
}
|
||||
108
src/usbh_driver_hub_private.h
Normal file
108
src/usbh_driver_hub_private.h
Normal 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
634
src/usbh_hubbed.c
Normal 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
1048
src/usbh_lld_stm32f4.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue