1
0
mirror of https://github.com/mist-devel/mist-firmware.git synced 2026-01-11 23:43:04 +00:00
Eugene Lozovoy ac42d360eb usb: fix "usb_hid_poll() error: 6" after reconnecting two or more devices
This error was caused by missing bmSndToggle/bmRcvToggle fields initialization. I decided to zeroing allocated dev struct completely instead of setting these fields in usb_hid_init(), as zeroing approach potentialy is more safe, since there may be  other unitialized fields in dark corners of code.
2024-09-05 20:22:51 +03:00

233 lines
6.9 KiB
C

#include <stdio.h>
#include <string.h>
#include "timer.h"
#include "usb.h"
#include "debug.h"
static usb_device_t dev[USB_NUMDEVICES];
usb_device_t *usb_get_devices() {
return dev;
}
void usb_init() {
puts(__FUNCTION__);
uint8_t i;
for(i=0;i<USB_NUMDEVICES;i++)
dev[i].bAddress = 0;
usb_hw_init();
}
// list of supported device classes
static const usb_device_class_config_t *class_list[] = {
&usb_hub_class,
&usb_hid_class,
&usb_xbox_class,
&usb_asix_class,
#ifdef USB_STORAGE
&usb_storage_class,
#endif
&usb_usbrtc_class,
&usb_pl2303_class,
NULL
};
uint8_t usb_configure(uint8_t parent, uint8_t port, bool lowspeed) {
uint8_t rcode = 0;
iprintf("%s(parent=%x port=%d lowspeed=%d)\n", __FUNCTION__, parent, port, lowspeed);
// find an empty device entry
uint8_t i;
usb_device_descriptor_t dev_desc;
union {
usb_string0_descriptor_t str0_desc;
usb_string_descriptor_t str_desc;
uint8_t buf[255];
} str;
for(i=0; i<USB_NUMDEVICES && dev[i].bAddress; i++);
if(i < USB_NUMDEVICES) {
iprintf("using free entry at %d\n", i);
usb_device_t *d = &dev[i];
memset(d, 0, sizeof(*d));
// setup generic info
d->parent = parent;
d->lowspeed = lowspeed;
d->port = port;
// setup endpoint 0
d->ep0.maxPktSize = 8;
d->ep0.bmNakPower = USB_NAK_MAX_POWER;
if(rcode = usb_get_dev_descr( d, 8, &dev_desc ))
return rcode;
d->ep0.maxPktSize = dev_desc.bMaxPacketSize0;
usb_debugf("EP0 max packet size: %d", d->ep0.maxPktSize);
// Assign new address to the device
// (address is simply the number of the free slot + 1)
iprintf("Setting addr %x\n", i+1);
rcode = usb_set_addr(d, i+1);
if(rcode) {
iprintf("failed to assign address (rcode=%d)", rcode);
return rcode;
}
uint32_t timer = timer_get_msec();
do {
rcode = usb_get_dev_descr( d, 8, &dev_desc );
} while (rcode && !timer_check(timer, 5)); // Some recovery interval (2 ms as USB 2.0 9.2.6.3)
if(rcode) return rcode;
// --- enumerate device ---
if(rcode = usb_get_dev_descr( d, sizeof(usb_device_descriptor_t), &dev_desc ))
return rcode;
usb_dump_device_descriptor(&dev_desc);
iprintf("USB vendor ID: %04X, product ID: %04X\n", dev_desc.idVendor, dev_desc.idProduct);
// save vid/pid
d->vid = dev_desc.idVendor;
d->pid = dev_desc.idProduct;
// The Retroflag Classic USB Gamepad doesn't report movement until the string descriptors are read,
// so read all of them here (and show them on the console)
if (!usb_get_string_descr(d, sizeof(str), 0, 0, &str.str_desc)) { // supported languages descriptor
uint16_t wLangId = str.str0_desc.wLANGID[0];
iprintf("wLangId: %04X\n", wLangId);
// Some gamepads (Retrobit) breaks if its strings are queried like below, so don't do it until it can be done safely.
#if 0
if (dev_desc.iManufacturer &&
!usb_get_string_descr(d, sizeof(str), dev_desc.iManufacturer, wLangId, &str.str_desc)) {
for (i=0; i<((str.str_desc.bLength-2)/2); i++) {
s[i] = ff_uni2oem(str.str_desc.bString[i], FF_CODE_PAGE);
}
s[i] = 0;
iprintf("Manufacturer: %s\n", s);
}
if (dev_desc.iProduct &&
!usb_get_string_descr(d, sizeof(str), dev_desc.iProduct, wLangId, &str.str_desc)) {
for (i=0; i<((str.str_desc.bLength-2)/2); i++) {
s[i] = ff_uni2oem(str.str_desc.bString[i], FF_CODE_PAGE);
}
s[i] = 0;
iprintf("Product: %s\n", s);
}
if (dev_desc.iSerialNumber &&
!usb_get_string_descr(d, sizeof(str), dev_desc.iSerialNumber, wLangId, &str.str_desc)) {
for (i=0; i<((str.str_desc.bLength-2)/2); i++) {
s[i] = ff_uni2oem(str.str_desc.bString[i], FF_CODE_PAGE);
}
s[i] = 0;
iprintf("Serial no.: %s\n", s);
}
#endif
}
// try to connect device to one of the supported classes
uint8_t c;
for(c=0;class_list[c];c++) {
iprintf("trying to init class %d\n", c);
rcode = class_list[c]->init(d, &dev_desc);
if (!rcode) {
d->class = class_list[c];
puts(" -> accepted :-)");
// ok, device accepted by class
return 0;
}
puts(" -> not accepted :-(");
}
} else
iprintf("no more free entries\n");
iprintf("unsupported device\n");
return 0;
}
uint8_t usb_release_device(uint8_t parent, uint8_t port) {
iprintf("%s(parent=%x, port=%d\n", __FUNCTION__, parent, port);
uint8_t i;
for(i=0; i<USB_NUMDEVICES; i++) {
if(dev[i].bAddress && dev[i].parent == parent && dev[i].port == port) {
iprintf(" -> device with address %x\n", dev[i].bAddress);
// check if this is a hub (parent of some other device)
// and release its kids first
uint8_t j;
for(j=0; j<USB_NUMDEVICES; j++) {
if(dev[j].parent == dev[i].bAddress)
usb_release_device(dev[i].bAddress, dev[j].port);
}
uint8_t rcode = 0;
if(dev[i].class)
rcode = dev[i].class->release(dev+i);
dev[i].bAddress = 0;
return rcode;
}
}
// this should never happen ...
return 0;
}
uint8_t usb_get_dev_descr( usb_device_t *dev, uint16_t nbytes, usb_device_descriptor_t* p ) {
return( usb_ctrl_req( dev, USB_REQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR,
0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, (uint8_t*)p));
}
uint8_t usb_get_dev_qualifier_descr( usb_device_t *dev, uint16_t nbytes, usb_device_qualifier_descriptor_t* p ) {
return( usb_ctrl_req( dev, USB_REQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR,
0x00, USB_DESCRIPTOR_DEVICE_QUALIFIER, 0x0000, nbytes, (uint8_t*)p));
}
//get configuration descriptor
uint8_t usb_get_conf_descr( usb_device_t *dev, uint16_t nbytes,
uint8_t conf, usb_configuration_descriptor_t* p ) {
return( usb_ctrl_req( dev, USB_REQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR,
conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, (uint8_t*)p));
}
uint8_t usb_get_other_speed_descr( usb_device_t *dev, uint16_t nbytes,
uint8_t conf, usb_configuration_descriptor_t* p ) {
return( usb_ctrl_req( dev, USB_REQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR,
conf, USB_DESCRIPTOR_OTHER_SPEED, 0x0000, nbytes, (uint8_t*)p));
}
uint8_t usb_set_addr( usb_device_t *dev, uint8_t newaddr ) {
iprintf("%s(new=%x)\n", __FUNCTION__, newaddr);
uint8_t rcode = usb_ctrl_req( dev, USB_REQ_SET, USB_REQUEST_SET_ADDRESS, newaddr,
0x00, 0x0000, 0x0000, NULL);
if(!rcode) dev->bAddress = newaddr;
return rcode;
}
//get configuration
uint8_t usb_get_conf( usb_device_t *dev, uint8_t *conf_value ) {
return( usb_ctrl_req( dev, USB_REQ_GET, USB_REQUEST_GET_CONFIGURATION,
0x00, 0x00, 0x0000, 1, conf_value));
}
//set configuration
uint8_t usb_set_conf( usb_device_t *dev, uint8_t conf_value ) {
return( usb_ctrl_req( dev, USB_REQ_SET, USB_REQUEST_SET_CONFIGURATION,
conf_value, 0x00, 0x0000, 0x0000, NULL));
}
uint8_t usb_get_string_descr( usb_device_t *dev, uint16_t nbytes, uint8_t index, uint16_t lang_id, usb_string_descriptor_t* dataptr ) {
return( usb_ctrl_req( dev, USB_REQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR,
index, USB_DESCRIPTOR_STRING, lang_id, nbytes, (uint8_t*)dataptr));
}