/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.

This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").

Contact information
-------------------

Circuits At Home, LTD
Web      :  http://www.circuitsathome.com
e-mail   :  support@circuitsathome.com
 */
/* USB functions */

#include "Usb.h"

//#define EXTRADEBUG // Uncomment to get even more debugging data

/* constructor */
USB::USB() : bmHubPre(0) {
        init();
}

/* Initialize data structures */
void USB::init() {
	ucdaddr = 9999;
        ucdep = 9999;
        bmHubPre = 0;
}

/* Not using the original state machine, using the Microchip lib's state machine instead */
uint8_t USB::getUsbTaskState(void) {
        return (usbHostState);
}

/* epInfo array in drivers is now an array of pointers to structures allocated by uC lib.
 * So is a pointer (to epInfo array) to pointers (USB_ENDPOINT_INFO) */
USB_ENDPOINT_INFO* USB::getEpInfoEntry(uint8_t addr, uint8_t ep) {
        UsbDevice *p = addrPool.GetUsbDevicePtr(addr);

        if(!p || !p->epinfo)
                return NULL;

        USB_ENDPOINT_INFO** pep = p->epinfo;

        for(uint8_t i = 0; i < p->epcount; i++) {
		////printf ("getEpInfoEntry EP %d, EpAddr= 0x%X\n", i, (*pep)->bEndpointAddress);
                if((*pep)->bEndpointAddress == ep)
		{
			/* Print EP info */
        			////printf("Endpoint descriptor:\n");
			        ////Notify(PSTR("\r\nLength:\t\t"), 0x80);
			        ////D_PrintHex<uint8_t > ((*pep)->bLength, 0x80);
			        ////Notify(PSTR("\r\nType:\t\t"), 0x80);
			        ////D_PrintHex<uint8_t > ((*pep)->bDescriptorType, 0x80);
        			////printf("bEndpointAddress: %d\n",(*pep)->bEndpointAddress);
			        ////printf("bmAttributes: %X\n",(*pep)->bmAttributes);
        			////printf("wMaxPacketSize: %d\n",(*pep)->wMaxPacketSize);
        			////printf("wInterval: %d\n",(*pep)->wInterval);
                        return *pep;
		}
                pep++;
        }
        return NULL;
}

/* set device table entry */

/* each device is different and has different number of endpoints. This function plugs endpoint record structure, defined in application, to devtable */
/* epInfo array in drivers is now an array of pointers to structures allocated by uC lib.
 * So is a pointer (to epInfo array) to pointers (USB_ENDPOINT_INFO) */
uint8_t USB::setEpInfoEntry(uint8_t addr, uint8_t epcount, USB_ENDPOINT_INFO** eprecord_ptr) {
        if(!eprecord_ptr)
                return USB_ERROR_INVALID_ARGUMENT;

        UsbDevice *p = addrPool.GetUsbDevicePtr(addr);

        if(!p)
                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;

        p->address.devAddress = addr;
        p->epinfo = eprecord_ptr;
        p->epcount = epcount;

        return 0;
}


/* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer,   */
/* depending on request. Actual requests are defined as inlines                                                                                     */
/* return codes:                */
/* 00       =   success         */
/* 0x01-0x28    =   Microchip lib. error code (USBHostIssueDeviceRequest(), USBHostTransferIsComplete()) */
// NOTE: This implementation requires buffer size to equal total read.
uint8_t USB::ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,
        uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *parser) {
	USB_ENDPOINT_INFO *pep = NULL;
	UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
        BYTE direction = 0; //request direction, IN or OUT
	BYTE rcode = 0;
	DWORD bytesIn = 0;
	DWORD bytesParsed = 0;

        if(!p)
                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;

        if(!p->epinfo)
                return USB_ERROR_EPINFO_IS_NULL;


        pep = getEpInfoEntry(addr, ep);
        if(!pep)
                return USB_ERROR_EP_NOT_FOUND_IN_TBL;

	WORD nak_limit = ( (0x0001UL << ((pep->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : pep->bmNakPower)) -1);
        //USBHostSetNAKTimeout(addr, ep, 1, nak_limit);
	pep->status.bfNAKTimeoutEnabled = 1;
        pep->timeoutNAKs = (pep->bmNakPower > 0) ? nak_limit : USB_NAK_DEFAULT;

        direction = ((bmReqType & 0x80) > 0);	/* bmReqType bit corresponds to either USB_SETUP_HOST_TO_DEVICE or 
                                                 * USB_SETUP_DEVICE_TO_HOST, which match correctly with 
                                                 * USB_DEVICE_REQUEST_SET and USB_DEVICE_REQUEST_GET in uChip lib.
                                                 */
#ifdef DEBUG_USB_HOST
        printf ("ctrlReq request %dB, read=%d, addr=0x%X, ep=%d\n", (int)total, direction, addr, ep);
#endif
        rcode = USBHostIssueDeviceRequest( addr, bmReqType, bRequest, ((wValHi << 8) + wValLo), wInd, total, dataptr,
          direction, 0 );
        if(rcode)
                return rcode;
	while (!USBHostTransferIsComplete( addr, ep, &rcode, (DWORD*)&bytesIn)) {
          // Invoke callback function if inTransfer completed successfully and callback function pointer is specified
	  if ( bytesIn > bytesParsed && direction && parser != NULL && dataptr != NULL ) {
	    ((USBReadParser*)parser)->Parse(nbytes, dataptr + bytesParsed, bytesParsed);
	    bytesParsed += nbytes;
	  }
	}
#ifdef EXTRADEBUG
	if (!usbDeviceInfo.pEndpoint0->status.bfTransferSuccessful)
	  printf ("\nctrl transfer failed, ctrlReq received %dB, ret: 0x%X\n", (int)bytesIn, rcode);
#endif
         // Any last bytes to parse?
         if(direction && parser != NULL && dataptr != NULL) {
	   while (bytesIn > bytesParsed) {
	     ((USBReadParser*)parser)->Parse(nbytes, dataptr + bytesParsed, bytesParsed);
	     bytesParsed += nbytes;
	   }
	 }
	 return rcode;
}

/* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */
/* Keep sending INs and writes data to memory area pointed by 'data'                                                           */

/* rcode 0 if no errors. Errors are passed from Microchip USB lib. */
uint8_t USB::inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data, uint8_t bInterval /*= 0*/) {
	USB_ENDPOINT_INFO *pep = NULL;
        UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
	BYTE rcode = 0;
	DWORD bytesIn = *nbytesptr;

        if(!p)
                rcode = USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;

        if(!p->epinfo)
                rcode = USB_ERROR_EPINFO_IS_NULL;

        if(rcode) {
#ifdef EXTRADEBUG
                printf("(USB::InTransfer) Address Not Found, Error: %d\n", rcode);
                printf("(USB::InTransfer) addr requested: 0x%X\n", addr);
                printf("(USB::InTransfer) ep requested: %d\n", ep);
#endif
                return rcode;
        }

        pep = getEpInfoEntry(addr, ep);
        if(!pep)
                return USB_ERROR_EP_NOT_FOUND_IN_TBL;
        
        WORD nak_limit = ( (uint32_t)0x0001 << (pep->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : pep->bmNakPower ) - 1;
        //USBHostSetNAKTimeout(addr, ep, 1, nak_limit);
	pep->status.bfNAKTimeoutEnabled  = (pep->bmNakPower > 0) ? 1 : 0;
        pep->timeoutNAKs = nak_limit;

	usbDeviceInfo.deviceAddress = addr;
////	printf ("IN Xfer %dB, ep 0x%X, NAKs=%d\n", *nbytesptr, ep, nak_limit);
	_USB_InitRead(pep, data, *nbytesptr);
	while (!USBHostTransferIsComplete(addr, ep, &rcode, (DWORD*)&bytesIn)) {
	  if(bInterval > 0)
            delay(bInterval); // Delay according to polling interval
	}
	if (bytesIn < *nbytesptr) {
#ifdef EXTRADEBUG
           printf("(USB::InTransfer) Short read: %dB of %dB\n", (int)bytesIn, *nbytesptr);
#endif
           *nbytesptr = (uint16_t)bytesIn;
        }
        return rcode;
}

/* OUT transfer to arbitrary endpoint. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */

/* rcode 0 if no errors. rcode 0x0-0x28 is relayed from Microchip USB lib.                     */
uint8_t USB::outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data) {
	USB_ENDPOINT_INFO *pep = NULL;
	UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
	BYTE rcode = 0;

        if(!p)
                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;

        if(!p->epinfo)
                return USB_ERROR_EPINFO_IS_NULL;

        pep = getEpInfoEntry(addr, ep);
        if(!pep)
                return USB_ERROR_EP_NOT_FOUND_IN_TBL;
        
        WORD nak_limit = ( (uint32_t)0x0001 << (pep->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : pep->bmNakPower ) - 1;
        //USBHostSetNAKTimeout(addr, ep, 1, nak_limit);
	pep->status.bfNAKTimeoutEnabled  = (pep->bmNakPower > 0) ? 1 : 0;
        pep->timeoutNAKs = nak_limit;

        // Set the busy flag and start a new OUT transfer.
	usbDeviceInfo.deviceAddress = addr;
	////printf ("OUT Xfer %dB, ep 0x%X, NAKs=%d\n", nbytes, ep, nak_limit);
	_USB_InitWrite(pep, data, nbytes);
	while (!USBHostTransferIsComplete(addr, ep, &rcode, (DWORD*)&nbytes)) ;;
	////printf ("  %dB done\n", nbytes);
        return rcode;
}

/* USB main task. Performs enumeration/cleanup */
void USB::Task(void) //USB state machine
{
	uint8_t rcode;

	USBHostTasks(); // Can call USB_ApplicationEventHandler
	// Poll configured USB devices
	for(uint8_t i = 0; i < USB_NUMDEVICES; i++)
	   if(devConfig[i])
	     rcode = devConfig[i]->Poll();
}

uint8_t USB::DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed) {
        uint8_t rcode;
        UsbDevice *p0 = NULL, *p = NULL;

        // Get pointer to pseudo device with address 0 assigned
        p0 = addrPool.GetUsbDevicePtr(0);

        if(!p0)
                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;

        if(!p0->epinfo)
                return USB_ERROR_EPINFO_IS_NULL;

        p0->lowspeed = (lowspeed) ? true : false;

        // Allocate new address according to device class
        uint8_t bAddress = addrPool.AllocAddress(parent, false, port);

        if(!bAddress)
                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;

        p = addrPool.GetUsbDevicePtr(bAddress);

        if(!p)
                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;

        p->lowspeed = lowspeed;

        // Assign new address to the device
        rcode = setAddr(0, 0, bAddress);

        if(rcode) {
                addrPool.FreeAddress(bAddress);
                bAddress = 0;
                return rcode;
        }
        return 0;
};

uint8_t USB::AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed) {
        ////printf("AttemptConfig: parent = %i, port = %i\n", parent, port);
        uint8_t rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed);
        if(rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET) {
		U1CONbits.USBRST = 1;
                delay(102); // delay 102ms, compensate for clock inaccuracy.
		U1CONbits.USBRST = 0;
		delay(12);
                usbDeviceInfo.deviceAddress = 0;
        }
	else if(rcode) {
#ifdef EXTRADEBUG
	   printf ("AttemptConfig - ConfigureDevice Error 0x%X\n", rcode);
#endif
           return rcode;
	 }
 
	////printf("AttemptConfig - Init\n");
        rcode = devConfig[driver]->Init(parent, port, lowspeed);
        if(rcode && rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED) {
#ifdef EXTRADEBUG
		printf ("AttemptConfig - Init Error 0x%X\n", rcode);
#endif
                // Issue a bus reset, because the device may be in a limbo state
		U1CONbits.USBRST = 1;
                delay(102); // delay 102ms, compensate for clock inaccuracy.
		U1CONbits.USBRST = 0;
		delay(12);
                usbDeviceInfo.deviceAddress = 0;
        }
        return rcode;
}

/*
 * This is broken. We need to enumerate differently.
 * It causes major problems with several devices if detected in an unexpected order.
 *
 *
 * Oleg - I wouldn't do anything before the newly connected device is considered sane.
 * i.e.(delays are not indicated for brevity):
 * 1. reset
 * 2. GetDevDescr();
 * 3a. If ACK, continue with allocating address, addressing, etc.
 * 3b. Else reset again, count resets, stop at some number (5?).
 * 4. When max.number of resets is reached, toggle power/fail
 * If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD()
 * it doesn't need to be reset again
 * New steps proposal:
 * 1: get address pool instance. exit on fail
 * 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail.
 * 3: bus reset, 100ms delay
 * 4: set address
 * 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail
 * 6: while (configurations) {
 *              for(each configuration) {
 *                      for (each driver) {
 *                              6a: Ask device if it likes configuration. Returns 0 on OK.
 *                                      If successful, the driver configured device.
 *                                      The driver now owns the endpoints, and takes over managing them.
 *                                      The following will need codes:
 *                                          Everything went well, instance consumed, exit with success.
 *                                          Instance already in use, ignore it, try next driver.
 *                                          Not a supported device, ignore it, try next driver.
 *                                          Not a supported configuration for this device, ignore it, try next driver.
 *                                          Could not configure device, fatal, exit with fail.
 *                      }
 *              }
 *    }
 * 7: for(each driver) {
 *      7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID
 * 8: if we get here, no driver likes the device plugged in, so exit failure.
 *
 */

/* Device Driver will call newEndpointInfo via ConfigDescParser (etc.) in confdescparser.h to set up endpoints for uC USB lib. */
uint8_t USB::Configuring(uint8_t parent, uint8_t port) {
        ////printf("Configuring: parent = %i, port = %i\r\n", parent, port);
        uint8_t devConfigIndex;
        uint8_t rcode = 0;
        USB_DEVICE_DESCRIPTOR *udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR *>(uddbuf);
        UsbDevice *p = NULL;
	static USB_ENDPOINT_INFO** ep_ptr = NULL;
/*      uC USB lib. maintains the real Eendpoint0 stucture 
        USB_ENDPOINT_INFO epInfo;

        epInfo.bEndpointAddress = 0;
        epInfo.wMaxPacketSize = 8;
        epInfo.bmSndToggle = 0;
        epInfo.bmRcvToggle = 0;
        epInfo.bmNakPower = USB_NAK_MAX_POWER; //Set via uC lib function call in it's endpoint0 structure?
*/
        AddressPool &addrPool = GetAddressPool();
        // Get pointer to pseudo device with address 0 assigned
        p = addrPool.GetUsbDevicePtr(0);
        if(!p) {
#ifdef EXTRADEBUG
                printf("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\n");
#endif
                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
        }
	ep_ptr = &usbDeviceInfo.pEndpoint0;
	p->epinfo = ep_ptr; /* Endpoint 0 default info already set by uC USB lib. at SUBSTATE_INITIALIZE */

        if (usbDeviceInfo.flags.bfIsLowSpeed)
          p->lowspeed = true;
	else
	  p->lowspeed = false;

	// Confused attmpt to disable this client driver ID thing in the uC USB lib.
	usbDeviceInfo.deviceClientDriver=0;

        // Get start of device descriptor
        rcode = getDevDescr(0, 0, 8, (uint8_t*)uddbuf);
	_USB_InitErrorCounters();

        // Set the EP0 packet size.
        usbDeviceInfo.pEndpoint0->wMaxPacketSize = udd->bMaxPacketSize0;

	// Get full device descriptor
	rcode = getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)uddbuf);
	_USB_InitErrorCounters();

        if(rcode) {
#ifdef EXTRADEBUG
                printf("Configuring error: Can't get USB_DEVICE_DESCRIPTOR, rcode = 0x%2.2X\n", rcode);
#endif
                return rcode;
        }

        uint16_t vid = udd->idVendor;
        uint16_t pid = udd->idProduct;
        uint8_t klass = udd->bDeviceClass;
        uint8_t subklass = udd->bDeviceSubClass;
#ifdef EXTRADEBUG
	printf("bLength: 0x%X\n"
		"bDescriptorType: 0x%X\n"
		"bcdUSB: 0x%X\n"
		"bDeviceClass: 0x%X\n"
		"bDeviceSubClass: 0x%X\n"
		"bDeviceProtocol: 0x%X\n"
		"bMaxPacketSize0 0x%X\n"
		"idVendor: 0x%X\n"
		"idProduct: 0x%X\n"
		"bcdDevice: 0x%X\n"
		"bNumConfigurations: 0x%X\n",
		udd->bLength, udd->bDescriptorType, udd->bcdUSB, klass, subklass, udd->bDeviceProtocol,
		udd->bMaxPacketSize0, vid, pid, udd->bcdDevice, udd->bNumConfigurations);
#endif
        // Attempt to configure if VID/PID or device class matches with a driver
        // Qualify with subclass too.
        //
        // VID/PID & class tests default to false for drivers not yet ported
        // subclass defaults to true, so you don't have to define it if you don't have to.
        //
        for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) {
                if(!devConfig[devConfigIndex]) continue; // no driver
                if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed
                if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) {
                        rcode = AttemptConfig(devConfigIndex, parent, port, p->lowspeed);
                        if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED)
                                break;
                }
        }

        if(devConfigIndex < USB_NUMDEVICES) {
                return rcode;
        }
#ifdef EXTRADEBUG
        puts("blind configure");
#endif
        // blindly attempt to configure
        for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) {
                if(!devConfig[devConfigIndex]) continue;
                if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed
                if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) continue; // If this is true it means it must have returned USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED above
                rcode = AttemptConfig(devConfigIndex, parent, port, p->lowspeed);

                //printf("ERROR ENUMERATING, rcode = 0x%2.2X\n", rcode);
                if(!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)) {
                        // in case of an error dev_index should be reset to 0
                        //                in order to start from the very beginning the
                        //                next time the program gets here
                        //if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE)
                        //        devConfigIndex = 0;
                        return rcode;
                }
        }
        // if we get here that means that the device class is not supported by any of registered classes
#ifdef EXTRADEBUG
        puts("default addressing");
#endif
        rcode = DefaultAddressing(parent, port, p->lowspeed);

        return rcode;
}

uint8_t USB::ReleaseDevice(uint8_t addr) {
        if(!addr)
                return 0;

        for(uint8_t i = 0; i < USB_NUMDEVICES; i++) {
                if(!devConfig[i]) continue;
                if(devConfig[i]->GetAddress() == addr)
                        return devConfig[i]->Release();
        }
        return 0;
}

#if 1 //!defined(USB_METHODS_INLINE)
//get device descriptor
// Alt. USBHostGetDeviceDescriptor from uC lib (USB/usb_host.h)
uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) {
        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, nbytes, dataptr, NULL));
}
//get configuration descriptor
// Alt. USBHostGetCurrentConfigurationDescriptor from uC lib (USB/usb_host.h)
uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {
        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, nbytes, dataptr, NULL));
}

/* Requests Configuration Descriptor. Sends two Get Conf Descr requests. The first one gets the total length of all descriptors, then the second one requests this
 total length. The length of the first request can be shorter ( 4 bytes ), however, there are devices which won't work unless this length is set to 9 */
uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p) {
        uint8_t buf[9];
	BYTE rcode;
        USB_CONFIGURATION_DESCRIPTOR *ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR *>(buf);
        
        if (conf > USB_NUMCONFIGURATIONS) {
#ifdef EXTRADEBUG
	    puts("(USB::getConfDescr) Max. conf. descriptors.");
#endif          
            return 1;
        }
        /* Configuration descriptor is buffered so that it needn't be re-read by device drivers that need it to work out whether they support the device */
	if (ucdbuf[conf] == NULL || ucdep != ep || ucdaddr != addr) {
          ucdep = ep;
          ucdaddr = addr;
          ucd->wTotalLength = 9999;
#ifdef EXTRADEBUG
          printf("(USB::getConfDescr) Fetching for endpoint %d, Address 0x%X\n", ep, addr);
#endif
          uint8_t ret = getConfDescr(addr, ep, 9, conf, buf);

          if(ret)
                  return ret;
          _USB_InitErrorCounters();

          ucdtotal[conf] = ucd->wTotalLength;
          ucdbuf[conf] = (uint8_t*)malloc(ucdtotal[conf]);
          if (ucdbuf[conf] == NULL) {
#ifdef EXTRADEBUG
	    printf("(USB::getConfDescr) Cannot allocate %dB for USB config. descriptor\n", ucdtotal[conf]);
#endif
	    return 1;
	  }

          ////printf("\ntotal conf. size: %dB\n", total);
	  rcode = ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, ucdtotal[conf],
                           ucdtotal[conf], ucdbuf[conf], p);
          return rcode;
        }
        else {
          // Avoid fetching descriptor multiple times by different drivers checking class/subclass
          ((USBReadParser*)p)->Parse(ucdtotal[conf], ucdbuf[conf], 0);
          return 0;
        }
}

//get string descriptor
// Alt. USBHostGetStringDescriptor from uC lib (USB/usb_host.h)
uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr) {
        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, ns, ns, dataptr, NULL));
}
//set address

uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) {
        uint8_t rcode = ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL);
        //delay(2); //per USB 2.0 sect.9.2.6.3
        delay(300); // Older spec says you should wait at least 200ms
	
        // Set the device's address here.
        if (!rcode)
          usbDeviceInfo.deviceAddressAndSpeed = (usbDeviceInfo.flags.bfIsLowSpeed << 7) | newaddr;

        return rcode;
}
//set configuration

uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) {
        return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL));
}

#endif // defined(USB_METHODS_INLINE)
