You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1224 lines
53 KiB
1224 lines
53 KiB
/* Copyright (C) 2015-2016 Andrew J. Kroll
|
|
and
|
|
Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
Contact information
|
|
-------------------
|
|
|
|
Circuits At Home, LTD
|
|
Web : http://www.circuitsathome.com
|
|
e-mail : support@circuitsathome.com
|
|
*/
|
|
|
|
#if defined(LOAD_USB_HOST_SYSTEM) && !defined(USB_HOST_SYSTEM_LOADED)
|
|
#define USB_HOST_SYSTEM_LOADED
|
|
|
|
#ifndef DEBUG_PRINTF_EXTRA_HUGE_UHS_HOST
|
|
#define DEBUG_PRINTF_EXTRA_HUGE_UHS_HOST 0
|
|
#endif
|
|
|
|
#if DEBUG_PRINTF_EXTRA_HUGE
|
|
#if DEBUG_PRINTF_EXTRA_HUGE_UHS_HOST
|
|
#define HOST_DEBUG(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define HOST_DEBUG(...) VOID0
|
|
#endif
|
|
#else
|
|
#define HOST_DEBUG(...) VOID0
|
|
#endif
|
|
|
|
UHS_EpInfo* UHS_USB_HOST_BASE::getEpInfoEntry(uint8_t addr, uint8_t ep) {
|
|
UHS_Device *p = addrPool.GetUsbDevicePtr(addr);
|
|
|
|
if(!p || !p->epinfo)
|
|
return NULL;
|
|
|
|
|
|
UHS_EpInfo *pep;
|
|
for(uint8_t j = 0; j < UHS_HOST_MAX_INTERFACE_DRIVERS; j++) {
|
|
pep = (UHS_EpInfo *)(p->epinfo[j]);
|
|
|
|
for(uint8_t i = 0; i < p->epcount; i++) {
|
|
if((pep)->epAddr == ep) {
|
|
HOST_DEBUG("ep entry for interface %d ep %d max packet size = %d\r\n", pep->bIface, ep, pep->maxPktSize);
|
|
return pep;
|
|
}
|
|
|
|
pep++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Sets a device table entry for a device.
|
|
* Each device is different and has different number of endpoints.
|
|
* This function plugs endpoint record structure, defined in application, to devtable
|
|
*
|
|
* @param addr device address
|
|
* @param epcount how many endpoints
|
|
* @param eprecord pointer to the endpoint structure
|
|
* @return Zero for success, or error code
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::setEpInfoEntry(uint8_t addr, uint8_t iface, uint8_t epcount, volatile UHS_EpInfo* eprecord) {
|
|
if(!eprecord)
|
|
return UHS_HOST_ERROR_BAD_ARGUMENT;
|
|
|
|
UHS_Device *p = addrPool.GetUsbDevicePtr(addr);
|
|
|
|
if(!p)
|
|
return UHS_HOST_ERROR_NO_ADDRESS_IN_POOL;
|
|
|
|
p->address.devAddress = addr;
|
|
p->epinfo[iface] = eprecord;
|
|
p->epcount = epcount;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sets all enpoint addresses to zero.
|
|
* Sets all max packet sizes to defaults
|
|
* Clears all endpoint attributes
|
|
* Sets bmNakPower to USB_NAK_DEFAULT
|
|
* Sets binterface to zero.
|
|
* Sets bNumEP to zero.
|
|
* Sets bAddress to zero.
|
|
* Clears qNextPollTime and sets bPollEnable to false.
|
|
*
|
|
* @param maxep How many endpoints to initialize
|
|
* @param device pointer to the device driver instance (this)
|
|
*
|
|
*/
|
|
|
|
void UHS_USB_HOST_BASE::DeviceDefaults(uint8_t maxep, UHS_USBInterface *interface) {
|
|
|
|
for(uint8_t i = 0; i < maxep; i++) {
|
|
interface->epInfo[i].epAddr = 0;
|
|
interface->epInfo[i].maxPktSize = (i) ? 0 : 8;
|
|
interface->epInfo[i].epAttribs = 0;
|
|
interface->epInfo[i].bmNakPower = UHS_USB_NAK_DEFAULT;
|
|
}
|
|
interface->pUsb->GetAddressPool()->FreeAddress(interface->bAddress);
|
|
interface->bIface = 0;
|
|
interface->bNumEP = 1;
|
|
interface->bAddress = 0;
|
|
interface->qNextPollTime = 0;
|
|
interface->bPollEnable = false;
|
|
}
|
|
|
|
/**
|
|
* Perform a bus reset to the port of the connected device
|
|
*
|
|
* @param parent index to Parent
|
|
* @param port what port on the parent
|
|
* @param address address of the device
|
|
* @return Zero for success, or error code
|
|
*/
|
|
|
|
uint8_t UHS_USB_HOST_BASE::doSoftReset(uint8_t parent, uint8_t port, uint8_t address) {
|
|
uint8_t rcode = 0;
|
|
|
|
if(parent == 0) {
|
|
// Send a bus reset on the root interface.
|
|
doHostReset();
|
|
} else {
|
|
// reset parent port
|
|
devConfig[parent]->ResetHubPort(port);
|
|
}
|
|
|
|
//
|
|
// Many devices require a delay before setting the address here...
|
|
// We loop upon fails for up to 2 seconds instead.
|
|
// Most devices will be happy without a retry.
|
|
//
|
|
uint8_t retries = 0;
|
|
if(address) {
|
|
do {
|
|
rcode = setAddr(0, address);
|
|
if(!rcode) break;
|
|
retries++;
|
|
sof_delay(10);
|
|
} while(retries < 200);
|
|
HOST_DEBUG("%i retries.\r\n", retries);
|
|
} else {
|
|
#if DEBUG_PRINTF_EXTRA_HUGE
|
|
printf("\r\ndoSoftReset called with address == 0.\r\n");
|
|
#endif
|
|
}
|
|
return rcode;
|
|
}
|
|
|
|
/*
|
|
* Pseudo code so you may understand the code flow.
|
|
*
|
|
* reset; (happens at the lower level)
|
|
* GetDevDescr();
|
|
* reset;
|
|
* If there are no configuration descriptors {
|
|
* //
|
|
* // Note: I know of no device that does this.
|
|
* // I suppose there could be one though.
|
|
* //
|
|
* try to enumerate.
|
|
* } else {
|
|
* last success count = 0
|
|
* best config = 0
|
|
* for each configuration descriptor {
|
|
* for each interface descriptor {
|
|
* get the endpoint descriptors for this interface.
|
|
* Check to see if a driver can handle this interface.
|
|
* If it can, add 1 to the success count.
|
|
* }
|
|
* if success count > last success count {
|
|
* best config = current config
|
|
* last success count = success count
|
|
* }
|
|
* }
|
|
* set the device config to the best config
|
|
* for each best config interface descriptor {
|
|
* initialize driver that can handle this interface config
|
|
* }
|
|
* }
|
|
*
|
|
* NOTES:
|
|
* 1: We do not need to save toggle states anymore and have not
|
|
* needed to for some time, because the lower level driver
|
|
* actually corrects wrong toggles on-the-fly for us.
|
|
*
|
|
* 2: We always do a second reset, since this stupid bug is
|
|
* actually part of the specification documents that I
|
|
* have found all over the net. Even Linux does it, and
|
|
* many devices actually EXPECT this behavior. Some devices
|
|
* will not enumerate without it. For devices that do not
|
|
* need it, the additional reset is harmless. Here is an
|
|
* example of one of these documents, see page Five:
|
|
* http://www.ftdichip.com/Support/Documents/TechnicalNotes/TN_113_Simplified%20Description%20of%20USB%20Device%20Enumeration.pdf
|
|
*
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Enumerates interfaces on devices
|
|
*
|
|
* @param parent index to Parent
|
|
* @param port what port on the parent
|
|
* @param speed the speed of the device
|
|
* @return Zero for success, or error code
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::Configuring(uint8_t parent, uint8_t port, uint8_t speed) {
|
|
//uint8_t bAddress = 0;
|
|
HOST_DEBUG("\r\n\r\n\r\nConfiguring: parent = %i, port = %i, speed = %i\r\n", parent, port, speed);
|
|
uint8_t rcode = 0;
|
|
uint8_t retries = 0;
|
|
uint8_t numinf = 0;
|
|
uint8_t configs;
|
|
UHS_Device *p = NULL;
|
|
//EpInfo epInfo; // cap at 16, this should be fairly reasonable.
|
|
ENUMERATION_INFO ei;
|
|
uint8_t bestconf = 0;
|
|
uint8_t bestsuccess = 0;
|
|
|
|
uint8_t devConfigIndex;
|
|
|
|
#if UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE
|
|
const uint8_t biggest = 0x40;
|
|
// wrap in {} to throw away the 64 byte buffer when we are done with it
|
|
{
|
|
uint8_t buf[biggest];
|
|
USB_DEVICE_DESCRIPTOR *udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR *>(buf);
|
|
#else
|
|
const uint8_t biggest = 18;
|
|
uint8_t buf[biggest];
|
|
USB_DEVICE_DESCRIPTOR *udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR *>(buf);
|
|
USB_CONFIGURATION_DESCRIPTOR *ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR *>(buf);
|
|
#endif
|
|
|
|
//for(devConfigIndex = 0; devConfigIndex < UHS_HOST_MAX_INTERFACE_DRIVERS; devConfigIndex++) {
|
|
// if((devConfig[devConfigIndex]->bAddress) && (!devConfig[devConfigIndex]->bPollEnable)) {
|
|
// devConfig[devConfigIndex]->bAddress = 0;
|
|
// }
|
|
//}
|
|
// Serial.print("HOST USB Host @ 0x");
|
|
// Serial.println((uint32_t)this, HEX);
|
|
// Serial.print("HOST USB Host Address Pool @ 0x");
|
|
// Serial.println((uint32_t)GetAddressPool(), HEX);
|
|
|
|
sof_delay(200);
|
|
p = addrPool.GetUsbDevicePtr(0);
|
|
if(!p) {
|
|
HOST_DEBUG("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\r\n");
|
|
return UHS_HOST_ERROR_NO_ADDRESS_IN_POOL;
|
|
}
|
|
|
|
p->speed = speed;
|
|
#if UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE
|
|
|
|
p->epinfo[0][0].maxPktSize = 0x40; // Windows bug is expected.
|
|
// poison data
|
|
// udd->bMaxPacketSize0 = 0U;
|
|
#else
|
|
p->epinfo[0][0].maxPktSize = 0x08; // USB Spec, start small, work your way up.
|
|
#endif
|
|
again:
|
|
memset((void *)buf, 0, biggest);
|
|
HOST_DEBUG("\r\n\r\nConfiguring PktSize 0x%2.2x, rcode: 0x%2.2x, retries %i,\r\n", p->epinfo[0][0].maxPktSize, rcode, retries);
|
|
rcode = getDevDescr(0, biggest, (uint8_t*)buf);
|
|
#if UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE
|
|
if(rcode || udd->bMaxPacketSize0 < 8)
|
|
#else
|
|
if(rcode)
|
|
#endif
|
|
{
|
|
if(rcode == UHS_HOST_ERROR_JERR && retries < 4) {
|
|
//
|
|
// Some devices return JERR when plugged in.
|
|
// Attempts to reinitialize the device usually works.
|
|
//
|
|
// I have a hub that will refuse to work and acts like
|
|
// this unless external power is supplied.
|
|
// So this may not always work, and you may be fooled.
|
|
//
|
|
sof_delay(100);
|
|
retries++;
|
|
goto again;
|
|
#if UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE
|
|
} else if(((rcode == UHS_HOST_ERROR_DMA || rcode == UHS_HOST_ERROR_MEM_LAT) && retries < 4) || (udd->bMaxPacketSize0 < 8 && !rcode)) {
|
|
|
|
if(p->epinfo[0][0].maxPktSize > 8 && rcode == UHS_HOST_ERROR_DMA) p->epinfo[0][0].maxPktSize = p->epinfo[0][0].maxPktSize >> 1;
|
|
#else
|
|
} else if((rcode == UHS_HOST_ERROR_DMA || rcode == UHS_HOST_ERROR_MEM_LAT) && retries < 4) {
|
|
if(p->epinfo[0][0].maxPktSize < 32) p->epinfo[0][0].maxPktSize = p->epinfo[0][0].maxPktSize << 1;
|
|
#endif
|
|
HOST_DEBUG("Configuring error: 0x%2.2x UHS_HOST_ERROR_DMA. Retry with maxPktSize: %i\r\n", rcode, p->epinfo[0][0].maxPktSize);
|
|
doSoftReset(parent, port, 0);
|
|
retries++;
|
|
sof_delay(200);
|
|
goto again;
|
|
}
|
|
HOST_DEBUG("Configuring error: 0x%2.2x Can't get USB_DEVICE_DESCRIPTOR\r\n", rcode);
|
|
return rcode;
|
|
}
|
|
|
|
|
|
#if UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE
|
|
ei.address = addrPool.AllocAddress(parent, false, port);
|
|
|
|
if(!ei.address) {
|
|
return UHS_HOST_ERROR_ADDRESS_POOL_FULL;
|
|
}
|
|
|
|
p = addrPool.GetUsbDevicePtr(ei.address);
|
|
// set to 1 if you suspect address table corruption.
|
|
#if 0
|
|
if(!p) {
|
|
return UHS_HOST_ERROR_NO_ADDRESS_IN_POOL;
|
|
}
|
|
#endif
|
|
|
|
p->speed = speed;
|
|
|
|
rcode = doSoftReset(parent, port, ei.address);
|
|
|
|
if(rcode) {
|
|
addrPool.FreeAddress(ei.address);
|
|
HOST_DEBUG("Configuring error: %2.2x Can't set USB INTERFACE ADDRESS\r\n", rcode);
|
|
return rcode;
|
|
}
|
|
|
|
{ // the { } wrapper saves on stack.
|
|
HOST_DEBUG("DevDescr 2nd poll, bMaxPacketSize0:%u\r\n", udd->bMaxPacketSize0);
|
|
UHS_EpInfo dev1ep;
|
|
dev1ep.maxPktSize = udd->bMaxPacketSize0;
|
|
dev1ep.epAddr = 0;
|
|
dev1ep.epAttribs = 0;
|
|
dev1ep.bmNakPower = UHS_USB_NAK_MAX_POWER;
|
|
p->address.devAddress = ei.address;
|
|
p->epcount = 1;
|
|
p->epinfo[0] = &dev1ep;
|
|
|
|
sof_delay(10);
|
|
memset((void *)buf, 0, biggest);
|
|
rcode = getDevDescr(ei.address, 18, (uint8_t*)buf);
|
|
if(rcode) HOST_DEBUG("getDevDescr err: 0x%x \r\n", rcode);
|
|
|
|
addrPool.FreeAddress(ei.address);
|
|
if(rcode && rcode != UHS_HOST_ERROR_DMA) {
|
|
return rcode;
|
|
}
|
|
sof_delay(10);
|
|
}
|
|
#endif
|
|
|
|
ei.vid = udd->idVendor;
|
|
ei.pid = udd->idProduct;
|
|
ei.bcdDevice = udd->bcdDevice;
|
|
ei.klass = udd->bDeviceClass;
|
|
ei.subklass = udd->bDeviceSubClass;
|
|
ei.protocol = udd->bDeviceProtocol;
|
|
ei.bMaxPacketSize0 = udd->bMaxPacketSize0;
|
|
ei.currentconfig = 0;
|
|
ei.parent = parent;
|
|
ei.port = port;
|
|
configs = udd->bNumConfigurations;
|
|
#if UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE
|
|
} // unwrapped, old large buf now invalid and discarded.
|
|
|
|
uint8_t buf[18];
|
|
USB_CONFIGURATION_DESCRIPTOR *ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR *>(buf);
|
|
#endif
|
|
|
|
ei.address = addrPool.AllocAddress(parent, IsHub(ei.klass), port);
|
|
|
|
if(!ei.address) {
|
|
return UHS_HOST_ERROR_ADDRESS_POOL_FULL;
|
|
}
|
|
|
|
p = addrPool.GetUsbDevicePtr(ei.address);
|
|
// set to 1 if you suspect address table corruption.
|
|
#if 0
|
|
if(!p) {
|
|
return UHS_HOST_ERROR_NO_ADDRESS_IN_POOL;
|
|
}
|
|
#endif
|
|
|
|
p->speed = speed;
|
|
|
|
rcode = doSoftReset(parent, port, ei.address);
|
|
|
|
if(rcode) {
|
|
addrPool.FreeAddress(ei.address);
|
|
HOST_DEBUG("Configuring error: %2.2x Can't set USB INTERFACE ADDRESS\r\n", rcode);
|
|
return rcode;
|
|
}
|
|
|
|
if(configs < 1) {
|
|
HOST_DEBUG("No interfaces?!\r\n");
|
|
addrPool.FreeAddress(ei.address);
|
|
// rcode = TestInterface(&ei);
|
|
// Not implemented (yet)
|
|
rcode = UHS_HOST_ERROR_DEVICE_NOT_SUPPORTED;
|
|
} else {
|
|
HOST_DEBUG("configs: %i\r\n", configs);
|
|
for(uint8_t conf = 0; (!rcode) && (conf < configs); conf++) {
|
|
// read the config descriptor into a buffer.
|
|
rcode = getConfDescr(ei.address, sizeof (USB_CONFIGURATION_DESCRIPTOR), conf, buf);
|
|
if(rcode) {
|
|
HOST_DEBUG("Configuring error: %2.2x Can't get USB_INTERFACE_DESCRIPTOR\r\n", rcode);
|
|
rcode = UHS_HOST_ERROR_FailGetConfDescr;
|
|
continue;
|
|
}
|
|
ei.currentconfig = conf;
|
|
numinf = ucd->bNumInterfaces; // Does _not_ include alternates!
|
|
HOST_DEBUG("CONFIGURATION: %i, bNumInterfaces %i, wTotalLength %i\r\n", conf, numinf, ucd->wTotalLength);
|
|
uint8_t success = 0;
|
|
uint16_t inf = 0;
|
|
uint8_t data[ei.bMaxPacketSize0];
|
|
UHS_EpInfo *pep;
|
|
pep = ctrlReqOpen(ei.address, mkSETUP_PKT8(UHS_bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, ei.currentconfig, USB_DESCRIPTOR_CONFIGURATION, 0x0000U, ucd->wTotalLength), data);
|
|
if(!pep) {
|
|
rcode = UHS_HOST_ERROR_NULL_EPINFO;
|
|
continue;
|
|
}
|
|
uint16_t left;
|
|
uint16_t read;
|
|
uint8_t offset;
|
|
rcode = initDescrStream(&ei, ucd, pep, data, &left, &read, &offset);
|
|
if(rcode) {
|
|
HOST_DEBUG("Configuring error: %2.2x Can't get USB_INTERFACE_DESCRIPTOR stream.\r\n", rcode);
|
|
break;
|
|
}
|
|
for(; (numinf) && (!rcode); inf++) {
|
|
// iterate for each interface on this config
|
|
rcode = getNextInterface(&ei, pep, data, &left, &read, &offset);
|
|
if(rcode == UHS_HOST_ERROR_END_OF_STREAM) {
|
|
HOST_DEBUG("USB_INTERFACE END OF STREAM\r\n");
|
|
ctrlReqClose(pep, UHS_bmREQ_GET_DESCR, left, ei.bMaxPacketSize0, data);
|
|
rcode = 0;
|
|
break;
|
|
}
|
|
if(rcode) {
|
|
HOST_DEBUG("Configuring error: %2.2x Can't close USB_INTERFACE_DESCRIPTOR stream.\r\n", rcode);
|
|
continue;
|
|
}
|
|
rcode = TestInterface(&ei);
|
|
if(!rcode) success++;
|
|
rcode = 0;
|
|
}
|
|
if(!inf) {
|
|
rcode = TestInterface(&ei);
|
|
if(!rcode) success++;
|
|
rcode = 0;
|
|
}
|
|
if(success > bestsuccess) {
|
|
bestconf = conf;
|
|
bestsuccess = success;
|
|
}
|
|
}
|
|
if(!bestsuccess) rcode = UHS_HOST_ERROR_DEVICE_NOT_SUPPORTED;
|
|
}
|
|
if(!rcode) {
|
|
rcode = getConfDescr(ei.address, sizeof (USB_CONFIGURATION_DESCRIPTOR), bestconf, buf);
|
|
if(rcode) {
|
|
HOST_DEBUG("Configuring error: %2.2x Can't get USB_INTERFACE_DESCRIPTOR\r\n", rcode);
|
|
rcode = UHS_HOST_ERROR_FailGetConfDescr;
|
|
}
|
|
}
|
|
if(!rcode) {
|
|
bestconf++;
|
|
ei.currentconfig = bestconf;
|
|
numinf = ucd->bNumInterfaces; // Does _not_ include alternates!
|
|
HOST_DEBUG("CONFIGURATION: %i, bNumInterfaces %i, wTotalLength %i\r\n", bestconf, numinf, ucd->wTotalLength);
|
|
if(!rcode) {
|
|
HOST_DEBUG("Best configuration is %i, enumerating interfaces.\r\n", bestconf);
|
|
uint16_t inf = 0;
|
|
uint8_t data[ei.bMaxPacketSize0];
|
|
UHS_EpInfo *pep;
|
|
pep = ctrlReqOpen(ei.address, mkSETUP_PKT8(UHS_bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, ei.currentconfig - 1, USB_DESCRIPTOR_CONFIGURATION, 0x0000U, ucd->wTotalLength), data);
|
|
if(!pep) {
|
|
rcode = UHS_HOST_ERROR_NULL_EPINFO;
|
|
|
|
} else {
|
|
uint16_t left;
|
|
uint16_t read;
|
|
uint8_t offset;
|
|
rcode = initDescrStream(&ei, ucd, pep, data, &left, &read, &offset);
|
|
if(rcode) {
|
|
HOST_DEBUG("Configuring error: %2.2x Can't get USB_INTERFACE_DESCRIPTOR stream.\r\n", rcode);
|
|
} else {
|
|
for(; (numinf) && (!rcode); inf++) {
|
|
// iterate for each interface on this config
|
|
rcode = getNextInterface(&ei, pep, data, &left, &read, &offset);
|
|
if(rcode == UHS_HOST_ERROR_END_OF_STREAM) {
|
|
ctrlReqClose(pep, UHS_bmREQ_GET_DESCR, left, ei.bMaxPacketSize0, data);
|
|
rcode = 0;
|
|
break;
|
|
}
|
|
if(rcode) {
|
|
HOST_DEBUG("Configuring error: %2.2x Can't close USB_INTERFACE_DESCRIPTOR stream.\r\n", rcode);
|
|
continue;
|
|
}
|
|
|
|
if(enumerateInterface(&ei) == UHS_HOST_MAX_INTERFACE_DRIVERS) {
|
|
HOST_DEBUG("No interface driver for this interface.");
|
|
} else {
|
|
HOST_DEBUG("Interface Configured\r\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
HOST_DEBUG("Configuring error: %2.2x Can't set USB_INTERFACE_CONFIG stream.\r\n", rcode);
|
|
}
|
|
}
|
|
|
|
if(!rcode) {
|
|
rcode = setConf(ei.address, bestconf);
|
|
if(rcode) {
|
|
HOST_DEBUG("Configuring error: %2.2x Can't set Configuration.\r\n", rcode);
|
|
addrPool.FreeAddress(ei.address);
|
|
} else {
|
|
for(devConfigIndex = 0; devConfigIndex < UHS_HOST_MAX_INTERFACE_DRIVERS; devConfigIndex++) {
|
|
HOST_DEBUG("Driver %i ", devConfigIndex);
|
|
if(!devConfig[devConfigIndex]) {
|
|
HOST_DEBUG("no driver at this index.\r\n");
|
|
continue; // no driver
|
|
}
|
|
HOST_DEBUG("@ %2.2x ", devConfig[devConfigIndex]->bAddress);
|
|
if(devConfig[devConfigIndex]->bAddress) {
|
|
if(!devConfig[devConfigIndex]->bPollEnable) {
|
|
HOST_DEBUG("Initialize\r\n");
|
|
rcode = devConfig[devConfigIndex]->Finalize();
|
|
rcode = devConfig[devConfigIndex]->Start();
|
|
if(!rcode) {
|
|
HOST_DEBUG("Total endpoints = (%i)%i\r\n", p->epcount, devConfig[devConfigIndex]->bNumEP);
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
HOST_DEBUG("Already initialized.\r\n");
|
|
continue; // consumed
|
|
}
|
|
} else {
|
|
HOST_DEBUG("Skipped\r\n");
|
|
}
|
|
}
|
|
#if 0 // defined(UHS_HID_LOADED)
|
|
// Now do HID
|
|
#endif
|
|
}
|
|
} else {
|
|
addrPool.FreeAddress(ei.address);
|
|
}
|
|
return rcode;
|
|
}
|
|
|
|
/**
|
|
* Removes a device from the tables
|
|
*
|
|
* @param addr address of the device
|
|
* @return nothing
|
|
*/
|
|
void UHS_USB_HOST_BASE::ReleaseDevice(uint8_t addr) {
|
|
if(addr) {
|
|
#if 0 // defined(UHS_HID_LOADED)
|
|
// Release any HID children
|
|
UHS_HID_Release(this, addr);
|
|
#endif
|
|
for(uint8_t i = 0; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) {
|
|
if(!devConfig[i]) continue;
|
|
if(devConfig[i]->bAddress == addr) {
|
|
devConfig[i]->Release();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the device descriptor, or part of it from endpoint Zero.
|
|
*
|
|
* @param addr Address of the device
|
|
* @param nbytes how many bytes to return
|
|
* @param dataptr pointer to the data to return
|
|
* @return status of the request, zero is success.
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::getDevDescr(uint8_t addr, uint16_t nbytes, uint8_t* dataptr) {
|
|
return ( ctrlReq(addr, mkSETUP_PKT8(UHS_bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes), nbytes, dataptr));
|
|
}
|
|
|
|
/**
|
|
* Gets the config descriptor, or part of it from endpoint Zero.
|
|
*
|
|
* @param addr Address of the device
|
|
* @param nbytes how many bytes to return
|
|
* @param conf index to descriptor to return
|
|
* @param dataptr ointer to the data to return
|
|
* @return status of the request, zero is success.
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::getConfDescr(uint8_t addr, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {
|
|
return ( ctrlReq(addr, mkSETUP_PKT8(UHS_bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes), nbytes, dataptr));
|
|
}
|
|
|
|
/**
|
|
* Get the string descriptor from a device
|
|
*
|
|
* @param addr Address of the device
|
|
* @param ns
|
|
* @param index
|
|
* @param langid language ID
|
|
* @param dataptr pointer to the data to return
|
|
* @return status of the request, zero is success.
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::getStrDescr(uint8_t addr, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr) {
|
|
return ( ctrlReq(addr, mkSETUP_PKT8(UHS_bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, ns), ns, dataptr));
|
|
}
|
|
|
|
//
|
|
//set address
|
|
//
|
|
|
|
/**
|
|
* Set the address of a device to a new address via endpoint Zero.
|
|
*
|
|
* @param oldaddr current address
|
|
* @param newaddr new address
|
|
* @return status of the request, zero is success.
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::setAddr(uint8_t oldaddr, uint8_t newaddr) {
|
|
uint8_t rcode = ctrlReq(oldaddr, mkSETUP_PKT8(UHS_bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000), 0x0000, NULL);
|
|
sof_delay(300); // Older spec says you should wait at least 200ms
|
|
return rcode;
|
|
}
|
|
|
|
//
|
|
//set configuration
|
|
//
|
|
|
|
/**
|
|
* Set the configuration for the device to use via endpoint Zero.
|
|
*
|
|
* @param addr Address of the device
|
|
* @param conf_value configuration index value
|
|
* @return status of the request, zero is success.
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::setConf(uint8_t addr, uint8_t conf_value) {
|
|
return ( ctrlReq(addr, mkSETUP_PKT8(UHS_bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000), 0x0000, NULL));
|
|
}
|
|
|
|
/* rcode 0 if no errors. rcode 01-0f is relayed from HRSL */
|
|
|
|
/**
|
|
* Writes data to an interface pipe
|
|
*
|
|
* @param addr Address of the device
|
|
* @param ep Endpoint of the pipe
|
|
* @param nbytes number of bytes to transfer
|
|
* @param data pointer to buffer to hold transfer
|
|
* @return zero for success or error code
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data) {
|
|
UHS_EpInfo *pep = NULL;
|
|
uint16_t nak_limit = 0;
|
|
HOST_DEBUG("outTransfer: addr: 0x%2.2x ep: 0x%2.2x nbytes: 0x%4.4x data: 0x%p\r\n", addr, ep, nbytes, data);
|
|
|
|
uint8_t rcode = SetAddress(addr, ep, &pep, nak_limit);
|
|
HOST_DEBUG("outTransfer: SetAddress 0x%2.2x\r\n", rcode);
|
|
if(!rcode)
|
|
rcode = OutTransfer(pep, nak_limit, nbytes, data);
|
|
return rcode;
|
|
};
|
|
|
|
/**
|
|
* Reads data from an interface pipe
|
|
*
|
|
* @param addr Address of the device
|
|
* @param ep Endpoint of the pipe
|
|
* @param nbytesptr number of bytes to transfer
|
|
* @param data pointer to buffer to hold transfer
|
|
* @return zero for success or error code
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data) {
|
|
UHS_EpInfo *pep = NULL;
|
|
uint16_t nak_limit = 0;
|
|
|
|
uint8_t rcode = SetAddress(addr, ep, &pep, nak_limit);
|
|
|
|
// if(rcode) {
|
|
// USBTRACE3("(USB::InTransfer) SetAddress Failed ", rcode, 0x81);
|
|
// USBTRACE3("(USB::InTransfer) addr requested ", addr, 0x81);
|
|
// USBTRACE3("(USB::InTransfer) ep requested ", ep, 0x81);
|
|
// return rcode;
|
|
// }
|
|
if(!rcode)
|
|
rcode = InTransfer(pep, nak_limit, nbytesptr, data);
|
|
return rcode;
|
|
|
|
}
|
|
|
|
/**
|
|
* Initialize the descriptor stream, works much like opening a file.
|
|
*
|
|
* @param ei
|
|
* @param ucd
|
|
* @param pep
|
|
* @param data
|
|
* @param left
|
|
* @param read
|
|
* @param offset
|
|
* @return zero for success or error code
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::initDescrStream(ENUMERATION_INFO *ei, USB_CONFIGURATION_DESCRIPTOR *ucd, UHS_EpInfo *pep, uint8_t *data, uint16_t *left, uint16_t *read, uint8_t *offset) {
|
|
if(!ei || !ucd) return UHS_HOST_ERROR_BAD_ARGUMENT;
|
|
if(!pep) return UHS_HOST_ERROR_NULL_EPINFO;
|
|
*left = ucd->wTotalLength;
|
|
*read = 0;
|
|
*offset = 1;
|
|
uint8_t rcode;
|
|
pep->maxPktSize = ei->bMaxPacketSize0;
|
|
rcode = getone(pep, left, read, data, offset);
|
|
return rcode;
|
|
}
|
|
|
|
uint8_t UHS_USB_HOST_BASE::getNextInterface(ENUMERATION_INFO *ei, UHS_EpInfo *pep, uint8_t data[], uint16_t *left, uint16_t *read, uint8_t *offset) {
|
|
uint16_t remain;
|
|
uint8_t ty;
|
|
uint8_t rcode = UHS_HOST_ERROR_END_OF_STREAM;
|
|
uint8_t *ptr;
|
|
uint8_t epc = 0;
|
|
ei->interface.numep = 0;
|
|
ei->interface.klass = 0;
|
|
ei->interface.subklass = 0;
|
|
ei->interface.protocol = 0;
|
|
while(*left + *read) {
|
|
remain = data[*offset]; // bLength
|
|
while(remain < 2) {
|
|
rcode = getone(pep, left, read, data, offset);
|
|
if(rcode)
|
|
return rcode;
|
|
remain = data[*offset];
|
|
}
|
|
rcode = getone(pep, left, read, data, offset);
|
|
if(rcode)
|
|
return rcode;
|
|
ty = data[*offset]; // bDescriptorType
|
|
HOST_DEBUG("bLength: %i ", remain);
|
|
HOST_DEBUG("bDescriptorType: %2.2x\r\n", ty);
|
|
remain--;
|
|
if(ty == USB_DESCRIPTOR_INTERFACE) {
|
|
HOST_DEBUG("INTERFACE DESCRIPTOR FOUND\r\n");
|
|
ptr = (uint8_t *)(&(ei->interface.bInterfaceNumber));
|
|
for(int i = 0; i < 6; i++) {
|
|
rcode = getone(pep, left, read, data, offset);
|
|
if(rcode)
|
|
return rcode;
|
|
*ptr = data[*offset];
|
|
ptr++;
|
|
}
|
|
rcode = getone(pep, left, read, data, offset);
|
|
if(rcode)
|
|
return rcode;
|
|
// Now at iInterface
|
|
// Get endpoints.
|
|
HOST_DEBUG("Getting %i endpoints\r\n", ei->interface.numep);
|
|
while(epc < ei->interface.numep) {
|
|
rcode = getone(pep, left, read, data, offset);
|
|
if(rcode) {
|
|
HOST_DEBUG("ENDPOINT DESCRIPTOR DIED WAY EARLY\r\n");
|
|
return rcode;
|
|
}
|
|
remain = data[*offset]; // bLength
|
|
while(remain < 2) {
|
|
rcode = getone(pep, left, read, data, offset);
|
|
if(rcode)
|
|
return rcode;
|
|
remain = data[*offset];
|
|
}
|
|
rcode = getone(pep, left, read, data, offset);
|
|
if(rcode) {
|
|
HOST_DEBUG("ENDPOINT DESCRIPTOR DIED EARLY\r\n");
|
|
return rcode;
|
|
}
|
|
ty = data[*offset]; // bDescriptorType
|
|
HOST_DEBUG("bLength: %i ", remain);
|
|
HOST_DEBUG("bDescriptorType: %2.2x\r\n", ty);
|
|
remain -= 2;
|
|
if(ty == USB_DESCRIPTOR_ENDPOINT) {
|
|
HOST_DEBUG("ENDPOINT DESCRIPTOR: %i\r\n", epc);
|
|
ptr = (uint8_t *)(&(ei->interface.epInfo[epc].bEndpointAddress));
|
|
for(unsigned int i = 0; i< sizeof (ENDPOINT_INFO); i++) {
|
|
rcode = getone(pep, left, read, data, offset);
|
|
if(rcode) {
|
|
HOST_DEBUG("ENDPOINT DESCRIPTOR DIED LATE\r\n");
|
|
return rcode;
|
|
}
|
|
*ptr = data[*offset];
|
|
ptr++;
|
|
remain--;
|
|
}
|
|
epc++;
|
|
HOST_DEBUG("ENDPOINT DESCRIPTOR OK\r\n");
|
|
}
|
|
rcode = eat(pep, left, read, data, offset, &remain);
|
|
if(rcode) {
|
|
HOST_DEBUG("ENDPOINT DESCRIPTOR DIED EATING\r\n");
|
|
return rcode;
|
|
}
|
|
remain = 0;
|
|
}
|
|
remain = 1;
|
|
// queue ahead, but do not report if error.
|
|
rcode = eat(pep, left, read, data, offset, &remain);
|
|
if(!ei->interface.numep && rcode) {
|
|
return rcode;
|
|
}
|
|
HOST_DEBUG("ENDPOINT DESCRIPTORS FILLED\r\n");
|
|
return 0;
|
|
} else {
|
|
rcode = eat(pep, left, read, data, offset, &remain);
|
|
if(rcode)
|
|
return rcode;
|
|
}
|
|
rcode = UHS_HOST_ERROR_END_OF_STREAM;
|
|
}
|
|
return rcode;
|
|
}
|
|
|
|
uint8_t UHS_USB_HOST_BASE::seekInterface(ENUMERATION_INFO *ei, uint16_t inf, USB_CONFIGURATION_DESCRIPTOR *ucd) {
|
|
if(!ei || !ucd) return UHS_HOST_ERROR_BAD_ARGUMENT;
|
|
uint8_t data[ei->bMaxPacketSize0];
|
|
UHS_EpInfo *pep;
|
|
pep = ctrlReqOpen(ei->address, mkSETUP_PKT8(UHS_bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, ei->currentconfig,
|
|
USB_DESCRIPTOR_CONFIGURATION, 0x0000U, ucd->wTotalLength), data);
|
|
if(!pep) return UHS_HOST_ERROR_NULL_EPINFO;
|
|
uint16_t left = ucd->wTotalLength;
|
|
uint8_t cinf = 0;
|
|
uint8_t ty;
|
|
uint8_t epc = 0;
|
|
uint16_t remain = ucd->bLength;
|
|
uint16_t read = 0;
|
|
uint8_t offset = remain;
|
|
uint8_t *ptr;
|
|
uint8_t rcode;
|
|
ei->interface.numep = 0;
|
|
ei->interface.klass = 0;
|
|
ei->interface.subklass = 0;
|
|
ei->interface.protocol = 0;
|
|
pep->maxPktSize = ei->bMaxPacketSize0;
|
|
|
|
rcode = getone(pep, &left, &read, data, &offset);
|
|
if(rcode)
|
|
return rcode;
|
|
HOST_DEBUG("\r\nGetting interface: %i\r\n", inf);
|
|
inf++;
|
|
while(cinf != inf && (left + read)) {
|
|
//HOST_DEBUG("getInterface: cinf: %i inf: %i left: %i read: %i offset: %i remain %i\r\n", cinf, inf, left, read, offset, remain);
|
|
// Go past current descriptor
|
|
HOST_DEBUG("Skip: %i\r\n", remain);
|
|
rcode = eat(pep, &left, &read, data, &offset, &remain);
|
|
if(rcode)
|
|
return rcode;
|
|
remain = data[offset]; // bLength
|
|
while(remain < 2) {
|
|
rcode = getone(pep, &left, &read, data, &offset);
|
|
if(rcode)
|
|
return rcode;
|
|
remain = data[offset];
|
|
}
|
|
rcode = getone(pep, &left, &read, data, &offset);
|
|
if(rcode)
|
|
return rcode;
|
|
ty = data[offset]; // bDescriptorType
|
|
HOST_DEBUG("bLength: %i ", remain);
|
|
HOST_DEBUG("bDescriptorType: %2.2x\r\n", ty);
|
|
remain--;
|
|
if(ty == USB_DESCRIPTOR_INTERFACE) {
|
|
HOST_DEBUG("INTERFACE DESCRIPTOR: %i\r\n", cinf);
|
|
cinf++;
|
|
if(cinf == inf) {
|
|
// Get the interface descriptor information.
|
|
ptr = (uint8_t *)(&(ei->interface.bInterfaceNumber));
|
|
for(int i = 0; i < 6; i++) {
|
|
rcode = getone(pep, &left, &read, data, &offset);
|
|
if(rcode)
|
|
return rcode;
|
|
*ptr = data[offset];
|
|
ptr++;
|
|
}
|
|
rcode = getone(pep, &left, &read, data, &offset);
|
|
if(rcode)
|
|
return rcode;
|
|
// Now at iInterface
|
|
remain = 0;
|
|
// Get endpoints.
|
|
HOST_DEBUG("Getting %i endpoints\r\n", ei->interface.numep);
|
|
while(epc < ei->interface.numep) {
|
|
rcode = getone(pep, &left, &read, data, &offset);
|
|
if(rcode)
|
|
return rcode;
|
|
remain = data[offset]; // bLength
|
|
while(remain < 2) {
|
|
rcode = getone(pep, &left, &read, data, &offset);
|
|
if(rcode)
|
|
return rcode;
|
|
remain = data[offset];
|
|
}
|
|
rcode = getone(pep, &left, &read, data, &offset);
|
|
if(rcode)
|
|
return rcode;
|
|
ty = data[offset]; // bDescriptorType
|
|
HOST_DEBUG("bLength: %i ", remain);
|
|
HOST_DEBUG("bDescriptorType: %2.2x\r\n", ty);
|
|
remain--;
|
|
if(ty == USB_DESCRIPTOR_ENDPOINT) {
|
|
HOST_DEBUG("ENDPOINT DESCRIPTOR: %i\r\n", epc);
|
|
ptr = (uint8_t *)(&(ei->interface.epInfo[epc].bEndpointAddress));
|
|
for(unsigned int i = 0; i< sizeof (ENDPOINT_INFO); i++) {
|
|
rcode = getone(pep, &left, &read, data, &offset);
|
|
if(rcode)
|
|
return rcode;
|
|
*ptr = data[offset];
|
|
ptr++;
|
|
}
|
|
epc++;
|
|
remain = 0;
|
|
} else {
|
|
rcode = eat(pep, &left, &read, data, &offset, &remain);
|
|
if(rcode)
|
|
return rcode;
|
|
remain = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ctrlReqClose(pep, UHS_bmREQ_GET_DESCR, left, ei->bMaxPacketSize0, data);
|
|
}
|
|
|
|
uint8_t UHS_USB_HOST_BASE::getone(UHS_EpInfo *pep, uint16_t *left, uint16_t *read, uint8_t *dataptr, uint8_t *offset) {
|
|
uint8_t rcode = 0;
|
|
*offset += 1;
|
|
if(*offset < *read) {
|
|
return 0;
|
|
} else if(*left > 0) {
|
|
// uint16_t num = *left;
|
|
uint16_t num = pep->maxPktSize;
|
|
if(num > *left) num = *left;
|
|
*offset = 0;
|
|
rcode = ctrlReqRead(pep, left, read, num, dataptr);
|
|
if(rcode == 0) {
|
|
if(*read == 0) {
|
|
rcode = UHS_HOST_ERROR_END_OF_STREAM;
|
|
} else if(*read < num) *left = 0;
|
|
}
|
|
} else {
|
|
rcode = UHS_HOST_ERROR_END_OF_STREAM;
|
|
}
|
|
return rcode;
|
|
}
|
|
|
|
uint8_t UHS_USB_HOST_BASE::eat(UHS_EpInfo *pep, uint16_t *left, uint16_t *read, uint8_t *dataptr, uint8_t *offset, uint16_t *yum) {
|
|
uint8_t rcode = 0;
|
|
HOST_DEBUG("eating %i\r\n", *yum);
|
|
while(*yum) {
|
|
*yum -= 1;
|
|
rcode = getone(pep, left, read, dataptr, offset);
|
|
if(rcode) break;
|
|
}
|
|
return rcode;
|
|
}
|
|
|
|
uint8_t UHS_USB_HOST_BASE::ctrlReq(uint8_t addr, uint64_t Request, uint16_t nbytes, uint8_t* dataptr) {
|
|
//bool direction = bmReqType & 0x80; //request direction, IN or OUT
|
|
uint8_t rcode = 0;
|
|
|
|
// Serial.println("");
|
|
UHS_EpInfo *pep = ctrlReqOpen(addr, Request, dataptr);
|
|
if(!pep) {
|
|
HOST_DEBUG("ctrlReq1: ERROR_NULL_EPINFO addr: %d\r\n", addr);
|
|
return UHS_HOST_ERROR_NULL_EPINFO;
|
|
}
|
|
uint8_t rt = (uint8_t)(Request & 0xFFU);
|
|
|
|
// Serial.println("Opened");
|
|
uint16_t left = (uint16_t)(Request >> 48) /*total*/;
|
|
if(dataptr != NULL) {
|
|
//data stage
|
|
if((rt & 0x80) == 0x80) {
|
|
//IN transfer
|
|
while(left) {
|
|
// Bytes read into buffer
|
|
uint16_t read = nbytes;
|
|
HOST_DEBUG("ctrlReq2: left: %i, read:%i, nbytes %i\r\n", left, read, nbytes);
|
|
rcode = ctrlReqRead(pep, &left, &read, nbytes, dataptr);
|
|
|
|
#if UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE
|
|
HOST_DEBUG("RESULT: 0x%2.2x 0x%2.2x 0x%2.2x 0x%8.8lx%8.8lx\r\n", rcode, addr, read, (uint32_t)((Request>>32)&0xfffffffflu), (uint32_t)(Request&0xfffffffflu));
|
|
// Should only be used for GET_DESCRIPTOR USB_DESCRIPTOR_DEVICE
|
|
constexpr uint32_t req_match = ((uint32_t)USB_DESCRIPTOR_DEVICE << 24) |
|
|
((uint32_t)USB_REQUEST_GET_DESCRIPTOR << 8);
|
|
const uint32_t req_found = Request & 0xFF00FF00ul;
|
|
if(!addr && read && (req_found == req_match)) {
|
|
HOST_DEBUG("ctrlReq3: acceptBuffer sz %i nbytes %i left %i\n\r", read, nbytes, left);
|
|
left = 0;
|
|
rcode = UHS_HOST_ERROR_NONE;
|
|
break;
|
|
}
|
|
#endif
|
|
if(rcode) {
|
|
return rcode;
|
|
}
|
|
}
|
|
} else {
|
|
// OUT transfer
|
|
rcode = OutTransfer(pep, 0, nbytes, dataptr);
|
|
}
|
|
if(rcode) {
|
|
//return error
|
|
return ( rcode);
|
|
}
|
|
}
|
|
|
|
// Serial.println("Close Phase");
|
|
// Serial.flush();
|
|
// Status stage
|
|
rcode = ctrlReqClose(pep, rt, left, nbytes, dataptr);
|
|
// Serial.println("Closed");
|
|
return rcode;
|
|
}
|
|
|
|
uint8_t UHS_USB_HOST_BASE::EPClearHalt(uint8_t addr, uint8_t ep) {
|
|
return ctrlReq(addr, mkSETUP_PKT8(USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT, USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT, 0, ep, 0), 0, NULL);
|
|
}
|
|
|
|
uint8_t UHS_USB_HOST_BASE::TestInterface(ENUMERATION_INFO *ei) {
|
|
|
|
uint8_t devConfigIndex;
|
|
uint8_t rcode = 0;
|
|
HOST_DEBUG("TestInterface VID:%4.4x PID:%4.4x Class:%2.2x Subclass:%2.2x Protocol %2.2x\r\n", ei->vid, ei->pid, ei->klass, ei->subklass, ei->protocol);
|
|
HOST_DEBUG("Interface data: Class:%2.2x Subclass:%2.2x Protocol %2.2x, number of endpoints %i\r\n", ei->interface.klass, ei->interface.subklass, ei->interface.subklass, ei->interface.numep);
|
|
HOST_DEBUG("Parent: %2.2x, bAddress: %2.2x\r\n", ei->parent, ei->address);
|
|
for(devConfigIndex = 0; devConfigIndex < UHS_HOST_MAX_INTERFACE_DRIVERS; devConfigIndex++) {
|
|
if(!devConfig[devConfigIndex]) {
|
|
HOST_DEBUG("No driver at index %i\r\n", devConfigIndex);
|
|
continue; // no driver
|
|
}
|
|
if(devConfig[devConfigIndex]->bAddress) {
|
|
HOST_DEBUG("Driver %i is already consumed @ %2.2x\r\n", devConfigIndex, devConfig[devConfigIndex]->bAddress);
|
|
continue; // consumed
|
|
}
|
|
|
|
if(devConfig[devConfigIndex]->OKtoEnumerate(ei)) {
|
|
HOST_DEBUG("Driver %i supports this interface\r\n", devConfigIndex);
|
|
break;
|
|
}
|
|
}
|
|
if(devConfigIndex == UHS_HOST_MAX_INTERFACE_DRIVERS) {
|
|
rcode = UHS_HOST_ERROR_DEVICE_NOT_SUPPORTED;
|
|
#if 0 // defined(UHS_HID_LOADED)
|
|
// Check HID here, if it is, then lie
|
|
if(ei->klass == UHS_USB_CLASS_HID) {
|
|
devConfigIndex = UHS_HID_INDEX; // for debugging, otherwise this has no use.
|
|
rcode = 0;
|
|
}
|
|
#endif
|
|
}
|
|
if(!rcode) HOST_DEBUG("Driver %i can be used for this interface\r\n", devConfigIndex);
|
|
else HOST_DEBUG("No driver for this interface.\r\n");
|
|
return rcode;
|
|
};
|
|
|
|
uint8_t UHS_USB_HOST_BASE::enumerateInterface(ENUMERATION_INFO *ei) {
|
|
uint8_t devConfigIndex;
|
|
|
|
HOST_DEBUG("AttemptConfig: parent = %i, port = %i\r\n", ei->parent, ei->port);
|
|
|
|
#if 0 // defined(UHS_HID_LOADED)
|
|
// Check HID here, if it is, then lie
|
|
if(ei->klass == UHS_USB_CLASS_HID || ei->interface.klass == UHS_USB_CLASS_HID) {
|
|
UHS_HID_SetUSBInterface(this, ENUMERATION_INFO * ei);
|
|
devConfigIndex = UHS_HID_INDEX;
|
|
} else
|
|
#endif
|
|
for(devConfigIndex = 0; devConfigIndex < UHS_HOST_MAX_INTERFACE_DRIVERS; devConfigIndex++) {
|
|
if(!devConfig[devConfigIndex]) {
|
|
HOST_DEBUG("No driver at index %i\r\n", devConfigIndex);
|
|
continue; // no driver
|
|
}
|
|
if(devConfig[devConfigIndex]->bAddress) {
|
|
HOST_DEBUG("Driver %i is already consumed @ %2.2x\r\n", devConfigIndex, devConfig[devConfigIndex]->bAddress);
|
|
continue; // consumed
|
|
}
|
|
|
|
if(devConfig[devConfigIndex]->OKtoEnumerate(ei)) {
|
|
HOST_DEBUG("Driver %i supports this interface\r\n", devConfigIndex);
|
|
if(!devConfig[devConfigIndex]->SetInterface(ei)) break;
|
|
else devConfigIndex = UHS_HOST_MAX_INTERFACE_DRIVERS;
|
|
}
|
|
}
|
|
return devConfigIndex;
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Vendor Specific Interface Class
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if 0
|
|
/**
|
|
* Might go away, depends on if it is useful, or not.
|
|
*
|
|
* @param ei Enumeration information
|
|
* @return true if this interface driver can handle this interface description
|
|
*/
|
|
bool UHS_NI UHS_VSI::OKtoEnumerate(ENUMERATION_INFO *ei) {
|
|
return (
|
|
(ei->subklass == UHS_USB_CLASS_VENDOR_SPECIFIC) ||
|
|
(ei->interface.subklass == UHS_USB_CLASS_VENDOR_SPECIFIC)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Copy the entire ENUMERATION_INFO structure
|
|
* @param ei Enumeration information
|
|
* @return 0
|
|
*/
|
|
uint8_t UHS_NI UHS_VSI::SetInterface(ENUMERATION_INFO *ei) {
|
|
bNumEP = 1;
|
|
bAddress = ei->address;
|
|
|
|
eInfo.address = ei->address;
|
|
eInfo.bMaxPacketSize0 = ei->bMaxPacketSize0;
|
|
eInfo.currentconfig = ei->currentconfig;
|
|
eInfo.interface.bAlternateSetting = ei->interface.bAlternateSetting;
|
|
eInfo.interface.bInterfaceNumber = ei->interface.bInterfaceNumber;
|
|
eInfo.interface.numep = ei->interface.numep;
|
|
eInfo.interface.protocol = ei->interface.protocol;
|
|
eInfo.interface.subklass = ei->interface.subklass;
|
|
eInfo.klass = ei->klass;
|
|
eInfo.parent = ei->parent;
|
|
eInfo.pid = ei->pid;
|
|
eInfo.port = ei->port;
|
|
eInfo.protocol = ei->protocol;
|
|
eInfo.subklass = ei->subklass;
|
|
eInfo.vid = ei->vid;
|
|
for(uint8_t i = 0; i < eInfo.interface.numep; i++) {
|
|
eInfo.interface.epInfo[i].bEndpointAddress = ei->interface.epInfo[i].bEndpointAddress;
|
|
eInfo.interface.epInfo[i].bInterval = ei->interface.epInfo[i].bInterval;
|
|
eInfo.interface.epInfo[i].bmAttributes = ei->interface.epInfo[i].bmAttributes;
|
|
eInfo.interface.epInfo[i].wMaxPacketSize = ei->interface.epInfo[i].wMaxPacketSize;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#if 0
|
|
|
|
/* TO-DO: Move this silliness to a NONE driver.
|
|
* When we have a generic NONE driver we can:
|
|
* o Extract ALL device information to help users with a new device.
|
|
* o Use an unknown device from a sketch, kind of like usblib does.
|
|
* This will aid in making more drivers in a faster way.
|
|
*/
|
|
uint8_t UHS_USB_HOST_BASE::DefaultAddressing(uint8_t parent, uint8_t port, uint8_t speed) {
|
|
uint8_t rcode;
|
|
UHS_Device *p0 = NULL, *p = NULL;
|
|
|
|
// Get pointer to pseudo device with address 0 assigned
|
|
p0 = addrPool.GetUsbDevicePtr(0);
|
|
|
|
if(!p0)
|
|
return UHS_HOST_ERROR_NO_ADDRESS_IN_POOL;
|
|
|
|
if(!p0->epinfo)
|
|
return UHS_HOST_ERROR_NULL_EPINFO;
|
|
|
|
p0->speed = speed;
|
|
|
|
// Allocate new address according to device class
|
|
uint8_t bAddress = addrPool.AllocAddress(parent, false, port);
|
|
|
|
if(!bAddress)
|
|
return UHS_HOST_ERROR_ADDRESS_POOL_FULL;
|
|
|
|
p = addrPool.GetUsbDevicePtr(bAddress);
|
|
|
|
if(!p)
|
|
return UHS_HOST_ERROR_NO_ADDRESS_IN_POOL;
|
|
|
|
p->speed = speed;
|
|
|
|
// Assign new address to the device
|
|
rcode = setAddr(0, bAddress);
|
|
|
|
if(rcode) {
|
|
addrPool.FreeAddress(bAddress);
|
|
bAddress = 0;
|
|
return rcode;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
#error "Never include UHS_host_INLINE.h, include UHS_host.h instead"
|
|
#endif
|
|
|