1
0
mirror of https://github.com/arduino/Arduino.git synced 2025-03-13 10:29:35 +01:00

[sam] USBHost ADK+HID supported okay. Code is more documented.

This commit is contained in:
Thibault RICHARD 2012-06-26 16:20:26 +02:00
parent 96e8db0299
commit 3ba2e37651
17 changed files with 1609 additions and 576 deletions

View File

@ -1,61 +1,54 @@
#include "variant.h"
#include <stdio.h>
#include <hidboot.h>
#include <adk.h>
class MouseRptParser : public MouseReportParser
{
protected:
virtual void OnMouseMove (MOUSEINFO *mi);
virtual void OnLeftButtonUp (MOUSEINFO *mi);
virtual void OnLeftButtonDown (MOUSEINFO *mi);
virtual void OnRightButtonUp (MOUSEINFO *mi);
virtual void OnRightButtonDown (MOUSEINFO *mi);
virtual void OnMiddleButtonUp (MOUSEINFO *mi);
virtual void OnMiddleButtonDown (MOUSEINFO *mi);
};
void MouseRptParser::OnMouseMove(MOUSEINFO *mi)
{
printf("Pos={%d,%d}\r\n", mi->dX, mi->dY);
};
void MouseRptParser::OnLeftButtonUp (MOUSEINFO *mi)
{
printf("L Butt Up\r\n");
};
void MouseRptParser::OnLeftButtonDown (MOUSEINFO *mi)
{
printf("L Butt Dn\r\n");
};
void MouseRptParser::OnRightButtonUp (MOUSEINFO *mi)
{
printf("R Butt Up\r\n");
};
void MouseRptParser::OnRightButtonDown (MOUSEINFO *mi)
{
printf("R Butt Dn\r\n");
};
void MouseRptParser::OnMiddleButtonUp (MOUSEINFO *mi)
{
printf("M Butt Up\r\n");
};
void MouseRptParser::OnMiddleButtonDown (MOUSEINFO *mi)
{
printf("M Butt Dn\r\n");
};
// Accessory descriptor. It's how Arduino identifies itself to Android.
char applicationName[] = "Arduino_Terminal"; // the app on your phone
char accessoryName[] = "Arduino Due X"; // your Arduino board
char companyName[] = "Arduino SA";
// Make up anything you want for these
char versionNumber[] = "1.0";
char serialNumber[] = "1";
char url[] = "http://labs.arduino.cc/uploads/ADK/ArduinoTerminal/ThibaultTerminal_ICS_0001.apk";
USBHost Usb;
HIDBoot<HID_PROTOCOL_MOUSE> HostMouse(&Usb);
MouseRptParser Prs;
ADK adk(&Usb, companyName, applicationName, accessoryName,versionNumber,url,serialNumber);
void setup()
{
cpu_irq_enable();
printf("\r\nProgram started:\r\n");
printf("\r\nADK demo start\r\n");
delay(200);
HostMouse.SetReportParser(0,(HIDReportParser*)&Prs);
}
#define RCVSIZE 128
void loop()
{
uint8_t buf[RCVSIZE];
uint32_t nbread = 0;
char helloworld[] = "Hello World!\r\n";
Usb.Task();
if (adk.isReady())
{
/* Write hello string to ADK */
adk.write(strlen(helloworld), (uint8_t *)helloworld);
delay(1000);
/* Read data from ADK and print to UART */
adk.read(&nbread, RCVSIZE, buf);
if (nbread > 0)
{
printf("RCV: ");
for (uint32_t i = 0; i < nbread; ++i)
{
printf("%c", (char)buf[i]);
}
printf("\r\n");
}
}
}

View File

@ -14,6 +14,7 @@ Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
/* USB functions */
#include "Arduino.h"
@ -24,9 +25,9 @@ static uint32_t usb_error = 0;
static uint32_t usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE;
/**
* Class Constructor.
* \brief USBHost class constructor.
*/
USBHost::USBHost () : bmHubPre(0)
USBHost::USBHost() : bmHubPre(0)
{
// Set up state machine
usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE;
@ -36,7 +37,7 @@ USBHost::USBHost () : bmHubPre(0)
}
/**
* Initialize data structures.
* \brief Initialize USBHost class.
*/
void USBHost::init()
{
@ -44,8 +45,11 @@ void USBHost::init()
bmHubPre = 0;
}
/**
* Get USB state.
* \brief Get USBHost state.
*
* \return USB enumeration status (see USBHost::task).
*/
uint32_t USBHost::getUsbTaskState(void)
{
@ -53,7 +57,9 @@ uint32_t USBHost::getUsbTaskState(void)
}
/**
* Set USB state.
* \brief Set USB state.
*
* \param state New USBHost status to be set.
*/
void USBHost::setUsbTaskState(uint32_t state)
{
@ -61,7 +67,15 @@ void USBHost::setUsbTaskState(uint32_t state)
}
/**
* Get end point info from address.
* \brief Get endpoint info from USB device address and device endpoint.
*
* \note This function should be used to know which host pipe is being used for
* the corresponding device endpoint.
*
* \param addr USB device address.
* \param ep USB device endpoint number.
*
* \return Pointer to an EpInfo structure.
*/
EpInfo* USBHost::getEpInfoEntry(uint32_t addr, uint32_t ep)
{
@ -74,18 +88,27 @@ EpInfo* USBHost::getEpInfoEntry(uint32_t addr, uint32_t ep)
for (uint32_t i = 0; i < p->epcount; i++)
{
if (pep->epAddr == ep)
if (pep->deviceEpNum == ep)
return pep;
pep++;
}
return NULL;
}
/**
* Set device end point entry.
* Each device is different and has different number of endpoints.
* This function plugs endpoint record structure, defined in application to devtable.
* \brief Set device endpoint entry.
*
* \note Each device is different and has a different number of endpoints.
* This function sets endpoint record structure to the device using address
* addr in the address pool.
*
* \param ul_pipe Pipe address.
* \param ul_token_type Token type.
*
* \retval 0 on success.
* \retval USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL device not found.
*/
uint32_t USBHost::setEpInfoEntry(uint32_t addr, uint32_t epcount, EpInfo* eprecord_ptr)
{
@ -104,7 +127,21 @@ uint32_t USBHost::setEpInfoEntry(uint32_t addr, uint32_t epcount, EpInfo* epreco
return 0;
}
uint32_t USBHost::SetAddress(uint32_t addr, uint32_t ep, EpInfo **ppep, uint32_t &nak_limit)
/**
* \brief Set host pipe target address and set ppep pointer to the endpoint
* structure matching the specified USB device address and endpoint.
*
* \param addr USB device address.
* \param ep USB device endpoint number.
* \param ppep Endpoint info structure pointer set by setPipeAddress.
* \param nak_limit Maximum number of NAK permitted.
*
* \retval 0 on success.
* \retval USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL device not found.
* \retval USB_ERROR_EPINFO_IS_NULL no endpoint structure found for this device.
* \retval USB_ERROR_EP_NOT_FOUND_IN_TBL the specified device endpoint cannot be found.
*/
uint32_t USBHost::setPipeAddress(uint32_t addr, uint32_t ep, EpInfo **ppep, uint32_t &nak_limit)
{
UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
@ -119,29 +156,34 @@ uint32_t USBHost::SetAddress(uint32_t addr, uint32_t ep, EpInfo **ppep, uint32_t
if (!*ppep)
return USB_ERROR_EP_NOT_FOUND_IN_TBL;
nak_limit = (0x0001UL << ( ( (*ppep)->bmNakPower > USB_NAK_MAX_POWER ) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower) );
nak_limit = (0x0001UL << (((*ppep)->bmNakPower > USB_NAK_MAX_POWER ) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower));
nak_limit--;
// Set peripheral address
//regWr( rPERADDR, addr );
uhd_configure_address(ep, addr);
///////////////////////////////////////////////////////////////////////////////////////////// Y A TIL QQCHOSE A FAIRE???
//uint8_t mode = regRd( rMODE );
// Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise
//regWr( rMODE, (p->lowspeed) ? mode | bmLOWSPEED | bmHubPre : mode & ~(bmHUBPRE | bmLOWSPEED));
TRACE_USBHOST(printf(" => SetAddress deviceEP=%lu configued as hostPIPE=%lu sending to address=%lu\r\n", ep, (*ppep)->hostPipeNum, addr);)
uhd_configure_address((*ppep)->hostPipeNum, addr);
return 0;
}
/**
* Send a control request.
* 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.
* \brief Send a control request.
* Sets address, endpoint, fills control packet with necessary data, dispatches
* control packet, and initiates bulk IN transfer depending on request.
*
* return codes:
* 00 = success
* 01-0f = non-zero HRSLT
* \param addr USB device address.
* \param ep USB device endpoint number.
* \param bmReqType Request direction.
* \param bRequest Request type.
* \param wValLo Value low.
* \param wValHi Value high.
* \param wInd Index field.
* \param total Request length.
* \param nbytes Number of bytes to read.
* \param dataptr Data pointer.
* \param p USB class reader.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::ctrlReq(uint32_t addr, uint32_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi, uint16_t wInd, uint16_t total, uint32_t nbytes, uint8_t* dataptr, USBReadParser *p)
{
@ -156,12 +198,13 @@ uint32_t USBHost::ctrlReq(uint32_t addr, uint32_t ep, uint8_t bmReqType, uint8_t
TRACE_USBHOST(printf(" => ctrlReq\r\n");)
// Set peripheral address
rcode = SetAddress(addr, ep, &pep, nak_limit);
rcode = setPipeAddress(addr, ep, &pep, nak_limit);
if (rcode)
return rcode;
// Allocate EP0 with default 8 bytes size if not already initialized
rcode = UHD_EP0_Alloc(0, 8);
// Allocate Pipe0 with default 64 bytes size if not already initialized
// TODO : perform a get device descriptor first to get device endpoint size (else data can be missed if device ep0 > host pipe0)
rcode = UHD_Pipe0_Alloc(0, 64);
if (rcode)
{
TRACE_USBHOST(printf("/!\\ USBHost::ctrlReq : EP0 allocation error: %lu\r\n", rcode);)
@ -169,7 +212,7 @@ uint32_t USBHost::ctrlReq(uint32_t addr, uint32_t ep, uint8_t bmReqType, uint8_t
}
// Determine request direction
direction = (( bmReqType & 0x80 ) > 0);
direction = ((bmReqType & 0x80 ) > 0);
// Fill in setup packet
setup_pkt.ReqType_u.bmRequestType = bmReqType;
@ -180,12 +223,11 @@ uint32_t USBHost::ctrlReq(uint32_t addr, uint32_t ep, uint8_t bmReqType, uint8_t
setup_pkt.wLength = total;
// Configure and write the setup packet into the FIFO
//bytesWr(rSUDFIFO, 8, (uint8_t *)&setup_pkt);
uhd_configure_pipe_token(0, tokSETUP);
UHD_EP_Write(ep, 8, (uint8_t *)&setup_pkt);
UHD_Pipe_Write(pep->hostPipeNum, 8, (uint8_t *)&setup_pkt);
// Dispatch packet
rcode = dispatchPkt(tokSETUP, ep, nak_limit);
rcode = dispatchPkt(tokSETUP, pep->hostPipeNum, nak_limit);
if (rcode)
{
// Return HRSLT if not zero
@ -224,7 +266,6 @@ uint32_t USBHost::ctrlReq(uint32_t addr, uint32_t ep, uint8_t bmReqType, uint8_t
else
{
// OUT transfer
//devtable[addr].epinfo[ep].sndToggle = bmSNDTOG1;
TRACE_USBHOST(printf(" => ctrlData OUT\r\n");)
rcode = OutTransfer(pep, nak_limit, nbytes, dataptr);
}
@ -237,27 +278,36 @@ uint32_t USBHost::ctrlReq(uint32_t addr, uint32_t ep, uint8_t bmReqType, uint8_t
}
// Status stage
return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit );
return dispatchPkt((direction) ? tokOUTHS : tokINHS, pep->hostPipeNum, nak_limit);
}
/**
* 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
* 01-0f is relayed from dispatchPkt()
* f0 means RCVDAVIRQ error
* fe USB xfer timeout
* \brief Perform IN request to the specified USB device.
*
* \note This function handles multiple packets (if necessary) and can
* receive a maximum of 'nbytesptr' bytes. It keep sending INs and writes data
* to memory area pointed by 'data'. The actual amount of received bytes is
* stored in 'nbytesptr'.
*
* \param addr USB device address.
* \param ep USB device endpoint number.
* \param nbytesptr Receive buffer size. It is set to the amount of received
* bytes when the function returns.
* \param data Buffer to store received data.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::inTransfer(uint32_t addr, uint32_t ep, uint32_t *nbytesptr, uint8_t* data)
{
EpInfo *pep = NULL;
uint32_t nak_limit = 0;
uint32_t rcode = SetAddress(addr, ep, &pep, nak_limit);
uint32_t rcode = setPipeAddress(addr, ep, &pep, nak_limit);
if (rcode)
{
return rcode;
}
return InTransfer(pep, nak_limit, nbytesptr, data);
}
@ -266,43 +316,34 @@ uint32_t USBHost::InTransfer(EpInfo *pep, uint32_t nak_limit, uint32_t *nbytespt
{
uint32_t rcode = 0;
uint32_t pktsize = 0;
uint16_t nbytes = *nbytesptr;
uint8_t maxpktsize = pep->maxPktSize;
uint32_t nbytes = *nbytesptr;
uint32_t maxpktsize = pep->maxPktSize;
*nbytesptr = 0;
// Set toggle value
//regWr(rHCTL, devtable[addr].epinfo[ep].rcvToggle);
while (1)
{
// Use a 'return' to exit this loop
// IN packet to EP-'endpoint'. Function takes care of NAKS.
rcode = dispatchPkt(tokIN, pep->epAddr, nak_limit);
rcode = dispatchPkt(tokIN, pep->hostPipeNum, nak_limit);
if (rcode)
{
// Should be 0, indicating ACK. Else return error code.
return (rcode);
if (rcode == 1)
{
// Pipe freeze is mandatory to avoid sending IN endlessly (else reception becomes messy then)
uhd_freeze_pipe(pep->hostPipeNum);
}
// Should be 1, indicating NAK. Else return error code.
return rcode;
}
// check for RCVDAVIRQ and generate error if not present
// the only case when absense of RCVDAVIRQ makes sense is when toggle error occured. Need to add handling for that
/*if ((regRd(rHIRQ) & bmRCVDAVIRQ) == 0)
{
// Receive error
return (0xf0);
}*/
// Number of received bytes
//pktsize = regRd(rRCVBC);
//data = bytesRd(rRCVFIFO, pktsize, data);
pktsize = uhd_byte_count(pep->epAddr);
pktsize = uhd_byte_count(pep->hostPipeNum);
if (nbytes < pktsize)
printf("ce test n'a pas ete fait...\r\n");
data += UHD_EP_Read(pep->epAddr, pktsize, data);
// Clear the IRQ & free the buffer
//regWr(rHIRQ, bmRCVDAVIRQ);
{
TRACE_USBHOST(printf("/!\\ USBHost::InTransfer : receive buffer is too small, size=%lu, expected=%lu\r\n", nbytes, pktsize);)
}
data += UHD_Pipe_Read(pep->hostPipeNum, pktsize, data);
// Add this packet's byte count to total transfer length
*nbytesptr += pktsize;
@ -312,155 +353,116 @@ uint32_t USBHost::InTransfer(EpInfo *pep, uint32_t nak_limit, uint32_t *nbytespt
// 2. 'nbytes' have been transferred.
if ((pktsize < maxpktsize) || (*nbytesptr >= nbytes))
{
/*// Have we transferred 'nbytes' bytes?
if (regRd(rHRSL) & bmRCVTOGRD)
{
// Save toggle value
devtable[addr].epinfo[ep].rcvToggle = bmRCVTOG1;
}
else
{
devtable[addr].epinfo[ep].rcvToggle = bmRCVTOG0;
}*/
return (0);
return 0;
}
}
}
/**
* OUT transfer to arbitrary endpoint.
* Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes.
* Handles NAK bug per Maxim Application Note 4000 for single buffer transfer
* rcode 0 if no errors
* 01-0f is relayed from HRSL
* \brief Perform OUT request to the specified USB device.
*
* Major part of this function borrowed from code shared by Richard Ibbotson
* \note This function handles multiple packets (if necessary) and sends
* 'nbytes' bytes.
*
* \param addr USB device address.
* \param ep USB device endpoint number.
* \param nbytes Buffer size to be sent.
* \param data Buffer to send.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::outTransfer(uint32_t addr, uint32_t ep, uint32_t nbytes, uint8_t* data)
{
/*
EpInfo *pep = NULL;
uint16_t nak_limit;
EpInfo *pep = NULL;
uint32_t nak_limit = 0;
uint8_t rcode = SetAddress(addr, ep, &pep, nak_limit);
uint32_t rcode = setPipeAddress(addr, ep, &pep, nak_limit);
if (rcode)
{
return rcode;
}
return OutTransfer(pep, nak_limit, nbytes, data);
*/
printf("j'ai beau etre matinal.. j'ai mal!!!!\r\n");
return 1;
}
uint32_t USBHost::OutTransfer(EpInfo *pep, uint32_t nak_limit, uint32_t nbytes, uint8_t *data)
{
/*
uint8_t rcode, retry_count;
uint8_t *data_p = data; //local copy of the data pointer
uint16_t bytes_tosend, nak_count;
uint16_t bytes_left = nbytes;
uint32_t rcode = 0;
uint32_t bytes_tosend = 0;
uint32_t bytes_left = nbytes;
uint32_t maxpktsize = pep->maxPktSize;
uint8_t maxpktsize = pep->maxPktSize;
if (maxpktsize < 1 || maxpktsize > 64)
if (maxpktsize < 1)
return USB_ERROR_INVALID_MAX_PKT_SIZE;
unsigned long timeout = millis() + USB_XFER_TIMEOUT;
regWr( rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0 ); //set toggle value
while( bytes_left )
while (bytes_left)
{
retry_count = 0;
nak_count = 0;
bytes_tosend = ( bytes_left >= maxpktsize ) ? maxpktsize : bytes_left;
bytesWr( rSNDFIFO, bytes_tosend, data_p ); //filling output FIFO
regWr( rSNDBC, bytes_tosend ); //set number of bytes
regWr( rHXFR, ( tokOUT | pep->epAddr )); //dispatch packet
while(!(regRd( rHIRQ ) & bmHXFRDNIRQ )); //wait for the completion IRQ
regWr( rHIRQ, bmHXFRDNIRQ ); //clear IRQ
rcode = ( regRd( rHRSL ) & 0x0f );
bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left;
while( rcode && ( timeout > millis()))
// Write FIFO
UHD_Pipe_Write(pep->hostPipeNum, bytes_tosend, data);
// Use a 'return' to exit this loop
// OUT packet to EP-'endpoint'. Function takes care of NAKS.
rcode = dispatchPkt(tokOUT, pep->hostPipeNum, nak_limit);
if (rcode)
{
switch( rcode )
{
case hrNAK:
nak_count ++;
if( nak_limit && ( nak_count == nak_limit ))
return( rcode );
break;
case hrTIMEOUT:
retry_count ++;
if( retry_count == USB_RETRY_LIMIT )
return( rcode );
break;
default:
return( rcode );
}//switch( rcode
// Should be 0, indicating ACK. Else return error code.
return rcode;
}
// process NAK according to Host out NAK bug
regWr( rSNDBC, 0 );
regWr( rSNDFIFO, *data_p );
regWr( rSNDBC, bytes_tosend );
regWr( rHXFR, ( tokOUT | pep->epAddr )); //dispatch packet
while(!(regRd( rHIRQ ) & bmHXFRDNIRQ )); //wait for the completion IRQ
regWr( rHIRQ, bmHXFRDNIRQ ); //clear IRQ
rcode = ( regRd( rHRSL ) & 0x0f );
}//while( rcode && ....
bytes_left -= bytes_tosend;
data_p += bytes_tosend;
}//while( bytes_left...
pep->bmSndToggle = ( regRd( rHRSL ) & bmSNDTOGRD ) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0; //update toggle
return( rcode ); //should be 0 in all cases
*/
printf("j'ai beau etre matinal.. j'ai mal!!!! arggghh\r\n");
return 1;
data += bytes_tosend;
}
// Should be 0 in all cases
return rcode;
}
/**
* Dispatch USB packet.
* Assumes peripheral address is set and relevant buffer is loaded/empty.
* \brief Dispatch USB packet.
*
* \note Ensure peripheral address is set and relevant buffer is loaded/empty.
* If NAK, tries to re-send up to nak_limit times.
* If nak_limit == 0, do not count NAKs, exit after timeout.
* If bus timeout, re-sends up to USB_RETRY_LIMIT times.
* rcode 0 for success
* 1 for naked
* 2 for timeout
*
* Note: pipe token MUST be configured first when the corresponding FIFO is used,
* else packet may be corrupted.
* \param token Token type (Setup, In or Out).
* \param hostPipeNum Host pipe number to use for sending USB packet.
* \param nak_limit Maximum number of NAK permitted.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::dispatchPkt(uint32_t token, uint32_t ep, uint32_t nak_limit)
uint32_t USBHost::dispatchPkt(uint32_t token, uint32_t hostPipeNum, uint32_t nak_limit)
{
uint32_t timeout = millis() + USB_XFER_TIMEOUT;
uint32_t nak_count = 0;
uint32_t rcode = USB_ERROR_TRANSFER_TIMEOUT;
TRACE_USBHOST(printf(" => dispatchPkt token=%lu\r\n", token);)
TRACE_USBHOST(printf(" => dispatchPkt token=%lu pipe=%lu nak_limit=%lu\r\n", token, hostPipeNum, nak_limit);)
// Launch the transfer
//regWr(rHXFR, (token | ep));
UHD_EP_Send(ep, token);
UHD_Pipe_Send(hostPipeNum, token);
while (timeout > millis())
// Check timeout but don't hold timeout if VBUS is lost
while ((timeout > millis()) && (UHD_GetVBUSState() == UHD_STATE_CONNECTED))
{
// Wait for transfer completion
if (UHD_EP_Is_Transfer_Complete(ep, token))
if (UHD_Pipe_Is_Transfer_Complete(hostPipeNum, token))
{
return 0;
}
//
if (Is_uhd_nak_received(ep))
// Is NAK received?
if (Is_uhd_nak_received(hostPipeNum))
{
uhd_ack_nak_received(ep);
uhd_ack_nak_received(hostPipeNum);
nak_count++;
if (nak_limit && (nak_count == nak_limit))
{
return 1; //////////////////////////// cree un code pour ca
// Return NAK
return 1;
}
}
}
@ -469,8 +471,14 @@ uint32_t USBHost::dispatchPkt(uint32_t token, uint32_t ep, uint32_t nak_limit)
}
/**
* Configure a device using known device classes.
* \brief Configure device using known device classes.
* The device get a new address even if its class remain unknown.
*
* \param parent USB device address of the device's parent (0 if root).
* \param port USB device base address (see AddressPoolImpl).
* \param lowspeed Device speed.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::Configuring(uint32_t parent, uint32_t port, uint32_t lowspeed)
{
@ -490,11 +498,20 @@ uint32_t USBHost::Configuring(uint32_t parent, uint32_t port, uint32_t lowspeed)
return 0;
}
if (!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE))
if (rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED)
{
TRACE_USBHOST(printf("USBHost::Configuring : ERROR : device not supported!\r\n");)
}
else if (rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)
{
TRACE_USBHOST(printf("USBHost::Configuring : ERROR : class instance already in use!\r\n");)
}
else
{
// in case of an error devConfigIndex should be reset to 0
// in order to start from the very beginning the
// next time the program gets here
// 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;
@ -502,7 +519,7 @@ uint32_t USBHost::Configuring(uint32_t parent, uint32_t port, uint32_t lowspeed)
}
}
// if we get here that means that the device class is not supported by any of registered classes
// Device class is not supported by any of the registered classes
devConfigIndex = 0;
rcode = DefaultAddressing(parent, port, lowspeed);
@ -510,6 +527,15 @@ uint32_t USBHost::Configuring(uint32_t parent, uint32_t port, uint32_t lowspeed)
return rcode;
}
/**
* \brief Configure device with unknown USB class.
*
* \param parent USB device address of the device's parent (0 if root).
* \param port USB device base address (see AddressPoolImpl).
* \param lowspeed Device speed.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::DefaultAddressing(uint32_t parent, uint32_t port, uint32_t lowspeed)
{
uint32_t rcode = 0;
@ -553,30 +579,70 @@ uint32_t USBHost::DefaultAddressing(uint32_t parent, uint32_t port, uint32_t low
return 0;
}
/**
* \brief Release device and free associated resources.
*
* \param addr USB device address.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::ReleaseDevice(uint32_t addr)
{
if (!addr)
return 0;
for (uint32_t i = 0; i < USB_NUMDEVICES; ++i)
{
if (devConfig[i]->GetAddress() == addr)
{
return devConfig[i]->Release();
}
}
return 0;
}
// Get device descriptor
/**
* \brief Get device descriptor.
*
* \param addr USB device address.
* \param ep USB device endpoint number.
* \param nbytes Buffer size.
* \param dataptr Buffer to store received descriptor.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::getDevDescr(uint32_t addr, uint32_t ep, uint32_t nbytes, uint8_t* dataptr)
{
return (ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, nbytes, dataptr, 0));
}
// Get configuration descriptor
/**
* \brief Get configuration descriptor.
*
* \param addr USB device address.
* \param ep USB device endpoint number.
* \param nbytes Buffer size.
* \param conf Configuration number.
* \param dataptr Buffer to store received descriptor.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::getConfDescr(uint32_t addr, uint32_t ep, uint32_t nbytes, uint32_t conf, uint8_t* dataptr)
{
return (ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, nbytes, dataptr, 0));
}
/**
* \brief Get configuration descriptor and extract endpoints using USBReadParser object.
*
* \param addr USB device address.
* \param ep USB device endpoint number.
* \param conf Configuration number.
* \param p USBReadParser object pointer used to extract endpoints.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::getConfDescr(uint32_t addr, uint32_t ep, uint32_t conf, USBReadParser *p)
{
const uint32_t bufSize = 64;
@ -593,28 +659,56 @@ uint32_t USBHost::getConfDescr(uint32_t addr, uint32_t ep, uint32_t conf, USBRea
return (ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, total, bufSize, buf, p));
}
// Get string descriptor
uint32_t USBHost::getStrDescr(uint32_t addr, uint32_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr)
/**
* \brief Get string descriptor.
*
* \param addr USB device address.
* \param ep USB device endpoint number.
* \param nbytes Buffer size.
* \param index String index.
* \param langid Language ID.
* \param dataptr Buffer to store received descriptor.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::getStrDescr(uint32_t addr, uint32_t ep, uint32_t nbytes, 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, 0));
return (ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, nbytes, nbytes, dataptr, 0));
}
// Set address
/**
* \brief Set USB device address.
*
* \param oldaddr Current USB device address.
* \param ep USB device endpoint number.
* \param addr New USB device address to be set.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::setAddr(uint32_t oldaddr, uint32_t ep, uint32_t newaddr)
{
TRACE_USBHOST(printf(" => setAddr\r\n");)
TRACE_USBHOST(printf(" => USBHost::setAddr\r\n");)
return ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, 0, 0);
}
// Set configuration
/**
* \brief Set configuration.
*
* \param addr USB device address.
* \param ep USB device endpoint number.
* \param conf_value New configuration value to be set.
*
* \return 0 on success, error code otherwise.
*/
uint32_t USBHost::setConf(uint32_t addr, uint32_t ep, uint32_t conf_value)
{
return (ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, 0x0000, 0, 0));
}
/**
* USB main task.
* Performs enumeration/cleanup.
* \brief USB main task, responsible for enumeration and clean up stage.
*
* \note Must be periodically called from loop().
*/
void USBHost::Task(void)
{
@ -648,16 +742,17 @@ void USBHost::Task(void)
{
delay = millis() + USB_SETTLE_DELAY;
usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE;
///////////////////////////////////////////////////////////lowspeed = 0 ou 1; faire un get speed
//FIXME TODO: lowspeed = 0 ou 1; already done by hardware?
}
break;
}
// Poll connected devices (if required)
for (uint32_t i = 0; i < USB_NUMDEVICES; ++i)
if (devConfig[i])
rcode = devConfig[i]->Poll();
// USB state machine
// Perform USB enumeration stage and clean up
switch (usb_task_state)
{
case USB_DETACHED_SUBSTATE_INITIALIZE:
@ -667,6 +762,7 @@ void USBHost::Task(void)
UHD_Init();
init();
// Free all USB resources
for (uint32_t i = 0; i < USB_NUMDEVICES; ++i)
if (devConfig[i])
rcode = devConfig[i]->Release();

View File

@ -19,8 +19,8 @@ e-mail : support@circuitsathome.com
#ifndef USB_H_INCLUDED
#define USB_H_INCLUDED
#define TRACE_USBHOST(x) x
//#define TRACE_USBHOST(x)
//#define TRACE_USBHOST(x) x
#define TRACE_USBHOST(x)
#include <stdint.h>
#include "usb_ch9.h"
@ -120,7 +120,11 @@ typedef struct
uint16_t wLength; // 6 Depends on bRequest
} SETUP_PKT, *PSETUP_PKT;
// Base class for incoming data parser
/**
* \class USBReadParser
*
* \brief Base class used for USB descriptor parsing.
*/
class USBReadParser
{
public:
@ -128,21 +132,30 @@ public:
};
/**
* USBDeviceConfig class.
* \class USBDeviceConfig
*
* \brief Device configuration class used for managing device life cycle.
*/
class USBDeviceConfig
{
public:
//! @brief Perform final enumeration stage.
virtual uint32_t Init(uint32_t parent, uint32_t port, uint32_t lowspeed) = 0;
//! @brief Free USB allocated resources (pipes and address).
virtual uint32_t Release() = 0;
//! @brief Poll USB device. Call is made for each connected USB device on USBHost.task() call.
virtual uint32_t Poll() = 0;
//! @brief Retrieve USB device address.
virtual uint32_t GetAddress() = 0;
};
/**
* USBHost Class.
* The device table is filled during enumeration.
* Index corresponds to device address and each entry contains pointer to endpoint structure and device class to use.
* \class USBHost
*
* \brief Main USB host class.
*/
class USBHost
{
@ -190,7 +203,7 @@ class USBHost
uint32_t getDevDescr(uint32_t addr, uint32_t ep, uint32_t nbytes, uint8_t* dataptr);
uint32_t getConfDescr(uint32_t addr, uint32_t ep, uint32_t nbytes, uint32_t conf, uint8_t* dataptr);
uint32_t getConfDescr(uint32_t addr, uint32_t ep, uint32_t conf, USBReadParser *p);
uint32_t getStrDescr(uint32_t addr, uint32_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr);
uint32_t getStrDescr(uint32_t addr, uint32_t ep, uint32_t nbytes, uint8_t index, uint16_t langid, uint8_t* dataptr);
uint32_t setAddr(uint32_t oldaddr, uint32_t ep, uint32_t newaddr);
uint32_t setConf(uint32_t addr, uint32_t ep, uint32_t conf_value);
uint32_t ctrlReq(uint32_t addr, uint32_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,
@ -209,7 +222,7 @@ class USBHost
private:
void init();
uint32_t SetAddress(uint32_t addr, uint32_t ep, EpInfo **ppep, uint32_t &nak_limit);
uint32_t setPipeAddress(uint32_t addr, uint32_t ep, EpInfo **ppep, uint32_t &nak_limit);
uint32_t OutTransfer(EpInfo *pep, uint32_t nak_limit, uint32_t nbytes, uint8_t *data);
uint32_t InTransfer(EpInfo *pep, uint32_t nak_limit, uint32_t *nbytesptr, uint8_t* data);
};

View File

@ -27,9 +27,22 @@ e-mail : support@circuitsathome.com
#define USB_NAK_NOWAIT 1 //Single NAK stops transfer
#define USB_NAK_NONAK 0 //Do not count NAKs, stop retrying after USB Timeout
/**
* \brief Device endpoint definition.
*
* \note hostPipeNum is the allocated pipe used for the communication with
* deviceEpNum remote device endpoint.
* There is exactly one hostPipeNum corresponding to a deviceEpNum.
*
* \note The number of host pipe is limited by the hardware (10 on SAM3X).
* Moreover hostPipeNum allocation is static, meaning that only a limited
* amount of device endpoints can be opened at the same time, thus limitating
* the maximum number of connected devices at the same time.
*/
struct EpInfo
{
uint32_t epAddr; // Endpoint address
uint32_t deviceEpNum; // Device endpoint number
uint32_t hostPipeNum; // Host corresponding pipe number
uint32_t maxPktSize; // Maximum packet size
union
@ -45,15 +58,20 @@ struct EpInfo
};
};
// 7 6 5 4 3 2 1 0
// ---------------------------------
// | | H | P | P | P | A | A | A |
// ---------------------------------
//
// H - if 1 the address is a hub address
// P - parent hub address
// A - device address / port number in case of hub
//
/**
* \brief USB device address definition.
*
* \note The 8 bits USB address is defined like this:
*
* 7 6 5 4 3 2 1 0
* ---------------------------------
* | | H | P | P | P | A | A | A |
* ---------------------------------
*
* H - if 1 the address is a hub address
* P - parent hub address
* A - device address / port number in case of hub
*/
struct UsbDeviceAddress
{
union
@ -73,6 +91,14 @@ struct UsbDeviceAddress
#define bmUSB_DEV_ADDR_PARENT 0x38
#define bmUSB_DEV_ADDR_HUB 0x40
/**
* \brief USB device definition.
*
* \note epinfo is used to store the list of device endpoints currently used
* by the USBHost stack. This field is set during enumeration process when
* a supported USB class is found. See any USB classes implementing
* USBDeviceConfig and init() method for reference.
*/
struct UsbDevice
{
EpInfo *epinfo; // endpoint info pointer
@ -81,6 +107,9 @@ struct UsbDevice
uint32_t lowspeed; // indicates if a device is the low speed one
};
/**
* \class Abstract AddressPool definition.
*/
class AddressPool
{
public:
@ -94,28 +123,46 @@ typedef void (*UsbDeviceHandleFunc)(UsbDevice *pdev);
#define ADDR_ERROR_INVALID_INDEX 0xFF
#define ADDR_ERROR_INVALID_ADDRESS 0xFF
/**
* \class AddressPoolImpl definition.
* Used to store the list of connected devices and to keep track of free USB
* addresses.
*/
template <const uint32_t MAX_DEVICES_ALLOWED>
class AddressPoolImpl : public AddressPool
{
private:
EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device
EpInfo dev0ep; // Endpoint data structure used during enumeration for uninitialized device
uint32_t hubCounter; // hub counter is kept
// in order to avoid hub address duplication
UsbDevice thePool[MAX_DEVICES_ALLOWED];
// Initializes address pool entry
/**
* \brief Initialize the specified address pool entry.
*
* \param index Index pointing to a UsbDevice instance in the address pool.
*/
void InitEntry(uint32_t index)
{
thePool[index].address = 0;
thePool[index].epcount = 1;
thePool[index].address = 0;
thePool[index].epcount = 1;
thePool[index].lowspeed = 0;
thePool[index].epinfo = &dev0ep;
thePool[index].epinfo = &dev0ep;
};
// Returns thePool index for a given address
/**
* \brief Return an address pool index for a given address. This index can
* further be used to retrieve the corresponding USB device instance
* UsbDevice.
*
* \param index Index pointing to a UsbDevice instance in the address pool.
*
* \return Index number if found, 0 otherwise.
* \note Index 0 is reserved for address 0 and shall never be used.
*/
uint32_t FindAddressIndex(uint32_t address = 0)
{
for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
@ -126,21 +173,45 @@ private:
return 0;
};
// Returns thePool child index for a given parent
/**
* \brief Return address pool child index for a given parent. This index can
* further be used to retrieve the corresponding USB device instance
* UsbDevice.
*
* \param addr Parent USB address.
* \param start Search in the pool from this index. Calling multiple time
* this function with the returned index + 1 can be used to walk through
* all children.
*
* \return Child index number if found, 0 otherwise.
* \note Index 0 is reserved for address 0 and shall never be used.
*/
uint32_t FindChildIndex(UsbDeviceAddress addr, uint32_t start = 1)
{
for (uint32_t i = (start < 1 || start >= MAX_DEVICES_ALLOWED) ? 1 : start; i < MAX_DEVICES_ALLOWED; i++)
for (uint32_t i = (start < 1 || start >= MAX_DEVICES_ALLOWED) ? 1 : start; i < MAX_DEVICES_ALLOWED; ++i)
{
if (((UsbDeviceAddress*)&thePool[i].address)->bmParent == addr.bmAddress)
return i;
}
return 0;
};
// Frees address entry specified by index parameter
/**
* \brief Free address entry specified by index parameter.
*
* \note Calling FreeAddressByIndex only frees the USB address for possible
* further assignement. However, it does not free the actual USB resources
* used by the device. This can be made by calling the release() method
* from any UsbDevice class implementing USBDeviceConfig.
*
* \param index Index pointing to a UsbDevice instance in the address pool.
*
* \note Calling FreeAddressByIndex with a 0 index param has no effect.
*/
void FreeAddressByIndex(uint32_t index)
{
// Zerro field is reserved and should not be affected
// Zero field is reserved and should not be affected
if (index == 0)
return;
@ -154,10 +225,13 @@ private:
if (hubCounter == ((UsbDeviceAddress*)&thePool[index].address)->bmAddress)
hubCounter --;
}
InitEntry(index);
}
// Initializes the whole address pool at once
/**
* \brief Initialize all address poll entries at once.
*/
void InitAllAddresses()
{
for (uint32_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
@ -167,6 +241,9 @@ private:
};
public:
/**
* \brief AddressPoolImpl class constructor.
*/
AddressPoolImpl() : hubCounter(0)
{
// Init address zero (reserved)
@ -176,11 +253,18 @@ public:
InitAllAddresses();
// Configure ep0 used for enumeration
dev0ep.epAddr = 0;
dev0ep.maxPktSize = 8;
dev0ep.deviceEpNum = 0;
dev0ep.hostPipeNum = 0;
dev0ep.maxPktSize = 8;
};
// Returns a pointer to a specified address entry
/**
* \brief Get a UsbDevice pointer from a USB device address.
*
* \param addr USB device address.
*
* \return UsbDevice pointer on success, 0 otherwise.
*/
virtual UsbDevice* GetUsbDevicePtr(uint32_t addr)
{
if (!addr)
@ -191,7 +275,12 @@ public:
return (!index) ? 0 : (thePool + index);
};
// Performs an operation specified by pfunc for each addressed device
/**
* \brief Perform an operation specified by pfunc for each addressed
* USB device.
*
* \param pfunc Any function pointer with type UsbDeviceHandleFunc.
*/
void ForEachUsbDevice(UsbDeviceHandleFunc pfunc)
{
if (!pfunc)
@ -202,7 +291,18 @@ public:
pfunc(thePool + i);
};
// Allocates new address
/**
* \brief Allocate a new USB device address.
*
* \note See UsbDeviceAddress definition for better understanding.
*
* \param parent USB device address of the Parent device.
* \param is_hub Set to true if the corresponding device is a Hub, false
* otherwise.
* \param port USB device base address.
*
* \return UsbDevice pointer on success, 0 otherwise.
*/
virtual uint32_t AllocAddress(uint32_t parent, uint32_t is_hub = 0, uint32_t port = 0)
{
if (parent > 127 || port > 7)
@ -223,7 +323,7 @@ public:
if (is_hub)
{
thePool[index].address = 0x41;
hubCounter ++;
hubCounter++;
}
else
thePool[index].address = 1;
@ -238,52 +338,51 @@ public:
if (is_hub)
{
addr.bmHub = 1;
addr.bmAddress = ++hubCounter;
addr.bmAddress = hubCounter++;
}
else
{
addr.bmHub = 0;
addr.bmAddress = port;
}
thePool[index].address = *((uint8_t*)&addr);
/*
Serial.print("Addr:");
Serial.print(addr.bmHub, HEX);
Serial.print(".");
Serial.print(addr.bmParent, HEX);
Serial.print(".");
Serial.println(addr.bmAddress, HEX);
*/
return thePool[index].address;
};
// Empties pool entry
/**
* \brief Free the specified USB device address.
*
* \param addr USB device address to free.
*/
virtual void FreeAddress(uint32_t addr)
{
// if the root hub is disconnected all the addresses should be initialized
// If the root hub is disconnected all the addresses should be initialized
if (addr == 0x41)
{
InitAllAddresses();
return;
}
uint32_t index = FindAddressIndex(addr);
FreeAddressByIndex(index);
};
// Returns number of hubs attached
// It can be rather helpfull to find out if there are hubs attached than getting the exact number of hubs.
/*uint8_t GetNumHubs()
/*uint32_t GetNumHubs()
{
return hubCounter;
};
uint8_t GetNumDevices()
uint32_t GetNumDevices()
{
uint8_t counter = 0;
uint32_t counter = 0;
for (uint8_t i=1; i<MAX_DEVICES_ALLOWED; i++)
for (uint32_t i = 1; i < MAX_DEVICES_ALLOWED; ++i)
if (thePool[i].address != 0);
counter ++;
counter++;
return counter;
};*/

View File

@ -0,0 +1,405 @@
/* 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
*/
/* Google ADK interface */
#include "adk.h"
const uint32_t ADK::epDataInIndex = 1;
const uint32_t ADK::epDataOutIndex = 2;
/**
* \brief ADK class constructor.
*/
ADK::ADK(USBHost *p, const char* pmanufacturer,
const char* pmodel,
const char* pdescription,
const char* pversion,
const char* puri,
const char* pserial) :
manufacturer(pmanufacturer),
model(pmodel),
description(pdescription),
version(pversion),
uri(puri),
serial(pserial),
pUsb(p),
bAddress(0),
bNumEP(1),
ready(false)
{
// Initialize endpoint data structures
for (uint32_t i = 0; i < ADK_MAX_ENDPOINTS; ++i)
{
epInfo[i].deviceEpNum = 0;
epInfo[i].hostPipeNum = 0;
epInfo[i].maxPktSize = (i) ? 0 : 8;
epInfo[i].epAttribs = 0;
epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
}
// Register in USB subsystem
if (pUsb)
{
pUsb->RegisterDeviceClass(this);
}
}
/**
* \brief Initialize connection to an Android Phone.
*
* \param parent USB device address of the Parent device.
* \param port USB device base address.
* \param lowspeed USB device speed.
*
* \return 0 on success, error code otherwise.
*/
uint32_t ADK::Init(uint32_t parent, uint32_t port, uint32_t lowspeed)
{
uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)];
uint32_t rcode = 0;
UsbDevice *p = NULL;
EpInfo *oldep_ptr = NULL;
uint32_t adkproto = -1;
uint32_t num_of_conf = 0;
// Get memory address of USB device address pool
AddressPool &addrPool = pUsb->GetAddressPool();
TRACE_USBHOST(printf("ADK::Init\r\n");)
// Check if address has already been assigned to an instance
if (bAddress)
{
return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
}
// Get pointer to pseudo device with address 0 assigned
p = addrPool.GetUsbDevicePtr(0);
if (!p)
{
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
}
if (!p->epinfo)
{
return USB_ERROR_EPINFO_IS_NULL;
}
// Save old pointer to EP_RECORD of address 0
oldep_ptr = p->epinfo;
// Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
p->epinfo = epInfo;
p->lowspeed = lowspeed;
// Get device descriptor
rcode = pUsb->getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);
// Restore p->epinfo
p->epinfo = oldep_ptr;
if (rcode)
{
goto FailGetDevDescr;
}
// Allocate new address according to device class
bAddress = addrPool.AllocAddress(parent, false, port);
// Extract Max Packet Size from device descriptor
epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0;
// Assign new address to the device
rcode = pUsb->setAddr(0, 0, bAddress);
if (rcode)
{
p->lowspeed = false;
addrPool.FreeAddress(bAddress);
bAddress = 0;
TRACE_USBHOST(printf("ADK::Init : setAddr failed with rcode %lu\r\n", rcode);)
return rcode;
}
TRACE_USBHOST(printf("ADK::Init : device address is now %lu\r\n", bAddress);)
p->lowspeed = false;
//get pointer to assigned address record
p = addrPool.GetUsbDevicePtr(bAddress);
if (!p)
{
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
}
p->lowspeed = lowspeed;
// Assign epInfo to epinfo pointer - only EP0 is known
rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
if (rcode)
{
goto FailSetDevTblEntry;
}
// Check if ADK device is already in accessory mode; if yes, configure and exit
if (((USB_DEVICE_DESCRIPTOR*)buf)->idVendor == ADK_VID &&
(((USB_DEVICE_DESCRIPTOR*)buf)->idProduct == ADK_PID || ((USB_DEVICE_DESCRIPTOR*)buf)->idProduct == ADB_PID))
{
TRACE_USBHOST(printf("ADK::Init : Accessory mode device detected\r\n");)
/* Go through configurations, find first bulk-IN, bulk-OUT EP, fill epInfo and quit */
num_of_conf = ((USB_DEVICE_DESCRIPTOR*)buf)->bNumConfigurations;
TRACE_USBHOST(printf("ADK::Init : number of configuration is %lu\r\n", num_of_conf);)
for (uint32_t i = 0; i < num_of_conf; ++i)
{
ConfigDescParser<0, 0, 0, 0> confDescrParser(this);
delay(1);
rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
#if defined(XOOM)
//Added by Jaylen Scott Vanorden
if (rcode)
{
TRACE_USBHOST(printf("ADK::Init : Got 1st bad code for config: %lu\r\n", rcode);)
// Try once more
rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
}
#endif
if (rcode)
{
goto FailGetConfDescr;
}
if (bNumEP > 2)
{
break;
}
}
if (bNumEP == 3)
{
// Assign epInfo to epinfo pointer - this time all 3 endpoins
rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
if (rcode)
{
goto FailSetDevTblEntry;
}
}
// Set Configuration Value
rcode = pUsb->setConf(bAddress, 0, bConfNum);
if (rcode)
{
goto FailSetConf;
}
TRACE_USBHOST(printf("ADK::Init : ADK device configured successfully\r\n");)
ready = true;
return 0;
}
// Probe device - get accessory protocol revision
delay(1);
rcode = getProto((uint8_t*)&adkproto);
#if defined(XOOM)
// Added by Jaylen Scott Vanorden
if (rcode)
{
TRACE_USBHOST(printf("ADK::Init : Got 1st bad code for config: %lu\r\n", rcode);)
// Try once more
rcode = getProto((uint8_t*)&adkproto );
}
#endif
if (rcode)
{
goto FailGetProto;
}
TRACE_USBHOST(printf("ADK::Init : DK protocol rev. %lu", adkproto);)
// Send ID strings
sendStr(ACCESSORY_STRING_MANUFACTURER, manufacturer);
sendStr(ACCESSORY_STRING_MODEL, model);
sendStr(ACCESSORY_STRING_DESCRIPTION, description);
sendStr(ACCESSORY_STRING_VERSION, version);
sendStr(ACCESSORY_STRING_URI, uri);
sendStr(ACCESSORY_STRING_SERIAL, serial);
// Switch to accessory mode
// The Android phone will reset
rcode = switchAcc();
if (rcode)
{
goto FailSwAcc;
}
rcode = -1;
goto SwAttempt; // Switch to accessory mode
// Diagnostic messages
FailGetDevDescr:
TRACE_USBHOST(printf("ADK::Init getDevDescr : ");)
goto Fail;
FailSetDevTblEntry:
TRACE_USBHOST(printf("ADK::Init setDevTblEn : ");)
goto Fail;
FailGetProto:
TRACE_USBHOST(printf("ADK::Init getProto : ");)
goto Fail;
FailSwAcc:
TRACE_USBHOST(printf("ADK::Init swAcc : ");)
goto Fail;
SwAttempt:
TRACE_USBHOST(printf("ADK::Init Accessory mode switch attempt : ");)
goto Fail;
FailGetConfDescr:
TRACE_USBHOST(printf("ADK::Init getConf : ");)
goto Fail;
FailSetConf:
TRACE_USBHOST(printf("ADK::Init setConf : ");)
goto Fail;
Fail:
TRACE_USBHOST(printf("error code: %lu\r\n", rcode);)
Release();
return rcode;
}
/**
* \brief Extract bulk-IN and bulk-OUT endpoint information from configuration
* descriptor.
*
* \param conf Configuration number.
* \param iface Interface number.
* \param alt Alternate setting.
* \param proto Protocol version used.
* \param pep Pointer to endpoint descriptor.
*/
void ADK::EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *pep)
{
if (bNumEP == 3)
{
return;
}
bConfNum = conf;
uint32_t index = 0;
uint32_t pipe = 0;
if ((pep->bmAttributes & 0x02) == 2)
{
index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
}
// Fill in the endpoint info structure
epInfo[index].deviceEpNum = pep->bEndpointAddress & 0x0F;
epInfo[index].maxPktSize = pep->wMaxPacketSize;
TRACE_USBHOST(printf("ADK::EndpointXtract : Found new endpoint\r\n");)
TRACE_USBHOST(printf("ADK::EndpointXtract : deviceEpNum: %lu\r\n", epInfo[index].deviceEpNum);)
TRACE_USBHOST(printf("ADK::EndpointXtract : maxPktSize: %lu\r\n", epInfo[index].maxPktSize);)
TRACE_USBHOST(printf("ADK::EndpointXtract : index: %lu\r\n", index);)
if (index == epDataInIndex)
pipe = UHD_Pipe_Alloc(bAddress, epInfo[index].deviceEpNum, UOTGHS_HSTPIPCFG_PTYPE_BLK, UOTGHS_HSTPIPCFG_PTOKEN_IN, epInfo[index].maxPktSize, 0, UOTGHS_HSTPIPCFG_PBK_1_BANK);
else if (index == epDataOutIndex)
pipe = UHD_Pipe_Alloc(bAddress, epInfo[index].deviceEpNum, UOTGHS_HSTPIPCFG_PTYPE_BLK, UOTGHS_HSTPIPCFG_PTOKEN_OUT, epInfo[index].maxPktSize, 0, UOTGHS_HSTPIPCFG_PBK_1_BANK);
// Ensure pipe allocation is okay
if (pipe == 0)
{
TRACE_USBHOST(printf("ADK::EndpointXtract : Pipe allocation failure\r\n");)
// Enumeration failed, so user should not perform write/read since isConnected will return false
return;
}
epInfo[index].hostPipeNum = pipe;
bNumEP++;
}
/**
* \brief Release USB allocated resources (pipes and address).
*
* \note Release call is made from USBHost.task() on disconnection events.
* \note Release call is made from Init() on enumeration failure.
*
* \return Always 0.
*/
uint32_t ADK::Release()
{
// Free allocated host pipes
UHD_Pipe_Free(epInfo[epDataInIndex].hostPipeNum);
UHD_Pipe_Free(epInfo[epDataOutIndex].hostPipeNum);
// Free allocated USB address
pUsb->GetAddressPool().FreeAddress(bAddress);
// Must have to be reset to 1
bNumEP = 1;
bAddress = 0;
ready = false;
return 0;
}
/**
* \brief Read from ADK.
*
* \param nreadbytes Return value containing the number of read bytes.
* \param datalen Buffer length.
* \param dataptr Buffer to store the incoming data.
*
* \return 0 on success, error code otherwise.
*/
uint32_t ADK::read(uint32_t *nreadbytes, uint32_t datalen, uint8_t *dataptr)
{
*nreadbytes = datalen;
return pUsb->inTransfer(bAddress, epInfo[epDataInIndex].deviceEpNum, nreadbytes, dataptr);
}
/**
* \brief Write to ADK.
*
* \param datalen Amount of data to send. This parameter shall not exceed
* dataptr length.
* \param dataptr Buffer containing the data to be sent.
*
* \return 0 on success, error code otherwise.
*/
uint32_t ADK::write(uint32_t datalen, uint8_t *dataptr)
{
return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].deviceEpNum, datalen, dataptr);
}

View File

@ -0,0 +1,150 @@
/* 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
*/
/* Google ADK interface support header */
#ifndef ADK_H_INCLUDED
#define ADK_H_INCLUDED
#include <stdint.h>
#include "usb_ch9.h"
#include "Usb.h"
#include "hid.h"
#include "Arduino.h"
#include "confdescparser.h"
#define ADK_VID 0x18D1
#define ADK_PID 0x2D00
#define ADB_PID 0x2D01
#define XOOM //enables repeating getProto() and getConf() attempts
//necessary for slow devices such as Motorola XOOM
//defined by default, can be commented out to save memory
/* Requests */
#define ADK_GETPROTO 51 //check USB accessory protocol version
#define ADK_SENDSTR 52 //send identifying string
#define ADK_ACCSTART 53 //start device in accessory mode
#define bmREQ_ADK_GET USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE
#define bmREQ_ADK_SEND USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE
#define ACCESSORY_STRING_MANUFACTURER 0
#define ACCESSORY_STRING_MODEL 1
#define ACCESSORY_STRING_DESCRIPTION 2
#define ACCESSORY_STRING_VERSION 3
#define ACCESSORY_STRING_URI 4
#define ACCESSORY_STRING_SERIAL 5
#define ADK_MAX_ENDPOINTS 3 //endpoint 0, bulk_IN, bulk_OUT
/**
* \class ADK definition.
*/
class ADK : public USBDeviceConfig, public UsbConfigXtracter
{
private:
/* ID strings */
const char* manufacturer;
const char* model;
const char* description;
const char* version;
const char* uri;
const char* serial;
/* ADK proprietary requests */
uint32_t getProto(uint8_t* adkproto);
uint32_t sendStr(uint32_t index, const char* str);
uint32_t switchAcc(void);
protected:
static const uint32_t epDataInIndex; // DataIn endpoint index
static const uint32_t epDataOutIndex; // DataOUT endpoint index
/* Mandatory members */
USBHost *pUsb;
uint32_t bAddress; // Device USB address
uint32_t bConfNum; // configuration number
uint32_t bNumEP; // total number of EP in the configuration
bool ready;
/* Endpoint data structure describing the device EP */
EpInfo epInfo[ADK_MAX_ENDPOINTS];
public:
ADK(USBHost *pUsb, const char* pmanufacturer,
const char* pmodel,
const char* pdescription,
const char* pversion,
const char* puri,
const char* pserial);
// Methods for receiving and sending data
uint32_t read(uint32_t *nreadbytes, uint32_t datalen, uint8_t *dataptr);
uint32_t write(uint32_t datalen, uint8_t *dataptr);
// USBDeviceConfig implementation
virtual uint32_t Init(uint32_t parent, uint32_t port, uint32_t lowspeed);
virtual uint32_t Release();
virtual uint32_t Poll() { return 0; }; // not implemented
virtual uint32_t GetAddress() { return bAddress; };
virtual bool isReady() { return ready; };
// UsbConfigXtracter implementation
virtual void EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
};
/**
* \brief Get ADK protocol version.
*
* \param adkproto Empty buffer to be filled by getProto (2 bytes) with the ADK
* protocol version value.
*
* \return 0 on success, error code otherwise.
*/
inline uint32_t ADK::getProto(uint8_t* adkproto)
{
return (pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_GET, ADK_GETPROTO, 0, 0, 0, 2, 2, adkproto, NULL));
}
/**
* \brief Send ADK string.
*
* \param index String index.
* \param str String to send.
*
* \return 0 on success, error code otherwise.
*/
inline uint32_t ADK::sendStr(uint32_t index, const char* str)
{
return (pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_SEND, ADK_SENDSTR, 0, 0, index, strlen(str) + 1, strlen(str) + 1, (uint8_t*)str, NULL));
}
/**
* \brief Send a switch to accessory mode request.
*
* \return 0 on success, error code otherwise.
*/
inline uint32_t ADK::switchAcc(void)
{
return (pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_SEND, ADK_ACCSTART, 0, 0, 0, 0, 0, NULL, NULL));
}
#endif /* ADK_H_INCLUDED */

View File

@ -21,6 +21,11 @@ e-mail : support@circuitsathome.com
#include <stdint.h>
#include "parsetools.h"
/**
* \class Abstract UsbConfigXtracter definition.
*
* \note This class is used for extracting USB endpoint descriptors.
*/
class UsbConfigXtracter
{
public:
@ -32,7 +37,11 @@ public:
#define CP_MASK_COMPARE_PROTOCOL 4
#define CP_MASK_COMPARE_ALL 7
// Configuration Descriptor Parser Class Template
/**
* \class ConfigDescParser definition.
*
* \note This class is used for parsing configuration descriptors.
*/
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
class ConfigDescParser : public USBReadParser
{
@ -62,6 +71,18 @@ public:
virtual void Parse(const uint32_t len, const uint8_t *pbuf, const uint32_t &offset);
};
/**
* \brief ConfigDescParser class constructor.
*
* \param xtractor Is saved as ConfigDescParser attribute and later used as a
* callback for parsing the endpoint descriptors.
*
* \note During enumeration stage, all supported USB classes invoke
* ConfigDescParser with "this" as parameter, meaning that one class is also
* responsible for parsing its endpoint descriptors. This makes sense since
* each USB class handles different number of endpoints and configurations.
* For instance see ADK::Init from ADK class.
*/
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ConfigDescParser(UsbConfigXtracter *xtractor) :
theXtractor(xtractor),
@ -74,18 +95,37 @@ ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ConfigDescParser(Usb
theSkipper.Initialize(&theBuffer);
};
/**
* \brief Parse a complete USB configuration descriptor.
*
* \param len Buffer length.
* \param pbuf Buffer containing the configuration descriptor.
* \param offset Current offset position.
*/
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::Parse(const uint32_t len, const uint8_t *pbuf, const uint32_t &offset)
{
uint32_t cntdn = len;
uint8_t *p = (uint8_t*)pbuf;
while(cntdn)
while (cntdn)
if (!ParseDescriptor(&p, &cntdn))
return;
}
/* Parser for the configuration descriptor. Takes values for class, subclass, protocol fields in interface descriptor and
compare masks for them. When the match is found, calls EndpointXtract passing buffer containing endpoint descriptor */
/**
* \brief Parse a USB configuration descriptor.
* Takes values for class, subclass, protocol fields in interface descriptor
* and compare masks for them. When the match is found, calls EndpointXtract
* passing buffer containing endpoint descriptor.
*
* \note This method should not be called directly, use Parse() instead.
*
* \param pcntdn Buffer length.
* \param pp Buffer containing the configuration descriptor.
*
* \return true if data remains in the buffer for parsing, false otherwise.
*/
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor(uint8_t **pp, uint32_t *pcntdn)
{
@ -112,102 +152,90 @@ bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor
case 3:
switch (dscrType)
{
case USB_DESCRIPTOR_INTERFACE:
isGoodInterface = false;
case USB_DESCRIPTOR_CONFIGURATION:
theBuffer.valueSize = sizeof(USB_CONFIGURATION_DESCRIPTOR) - 2;
break;
case USB_DESCRIPTOR_ENDPOINT:
theBuffer.valueSize = sizeof(USB_ENDPOINT_DESCRIPTOR) - 2;
break;
case HID_DESCRIPTOR_HID:
theBuffer.valueSize = dscrLen - 2;
break;
case USB_DESCRIPTOR_INTERFACE:
isGoodInterface = false;
case USB_DESCRIPTOR_CONFIGURATION:
theBuffer.valueSize = sizeof(USB_CONFIGURATION_DESCRIPTOR) - 2;
break;
case USB_DESCRIPTOR_ENDPOINT:
theBuffer.valueSize = sizeof(USB_ENDPOINT_DESCRIPTOR) - 2;
break;
case HID_DESCRIPTOR_HID:
theBuffer.valueSize = dscrLen - 2;
break;
}
valParser.Initialize(&theBuffer);
stateParseDescr = 4;
case 4:
switch (dscrType)
{
case USB_DESCRIPTOR_CONFIGURATION:
if (!valParser.Parse(pp, pcntdn))
return false;
confValue = ((USB_CONFIGURATION_DESCRIPTOR*)varBuffer)->bConfigurationValue;
break;
case USB_DESCRIPTOR_INTERFACE:
if (!valParser.Parse(pp, pcntdn))
return false;
if ((MASK & CP_MASK_COMPARE_CLASS) && ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceClass != CLASS_ID)
break;
if ((MASK & CP_MASK_COMPARE_SUBCLASS) && ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceSubClass != SUBCLASS_ID)
break;
if ((MASK & CP_MASK_COMPARE_PROTOCOL) && ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceProtocol != PROTOCOL_ID)
case USB_DESCRIPTOR_CONFIGURATION:
if (!valParser.Parse(pp, pcntdn))
return false;
confValue = ((USB_CONFIGURATION_DESCRIPTOR*)varBuffer)->bConfigurationValue;
break;
case USB_DESCRIPTOR_INTERFACE:
if (!valParser.Parse(pp, pcntdn))
return false;
if ((MASK & CP_MASK_COMPARE_CLASS) && ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceClass != CLASS_ID)
break;
if ((MASK & CP_MASK_COMPARE_SUBCLASS) && ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceSubClass != SUBCLASS_ID)
break;
if ((MASK & CP_MASK_COMPARE_PROTOCOL) && ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceProtocol != PROTOCOL_ID)
break;
isGoodInterface = true;
ifaceNumber = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceNumber;
ifaceAltSet = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bAlternateSetting;
protoValue = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceProtocol;
break;
case USB_DESCRIPTOR_ENDPOINT:
if (!valParser.Parse(pp, pcntdn))
return false;
if (isGoodInterface)
if (theXtractor)
theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer);
break;
//case HID_DESCRIPTOR_HID:
// if (!valParser.Parse(pp, pcntdn))
// return false;
// PrintHidDescriptor((const USB_HID_DESCRIPTOR*)varBuffer);
// break;
default:
if (!theSkipper.Skip(pp, pcntdn, dscrLen-2))
return false;
}
theBuffer.pValue = varBuffer;
stateParseDescr = 0;
isGoodInterface = true;
ifaceNumber = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceNumber;
ifaceAltSet = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bAlternateSetting;
protoValue = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceProtocol;
break;
case USB_DESCRIPTOR_ENDPOINT:
if (!valParser.Parse(pp, pcntdn))
return false;
if (isGoodInterface)
if (theXtractor)
theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer);
break;
//case HID_DESCRIPTOR_HID:
// if (!valParser.Parse(pp, pcntdn))
// return false;
// PrintHidDescriptor((const USB_HID_DESCRIPTOR*)varBuffer);
// break;
default:
if (!theSkipper.Skip(pp, pcntdn, dscrLen - 2))
return false;
}
theBuffer.pValue = varBuffer;
stateParseDescr = 0;
}
return true;
}
/**
* \brief Print HID descriptor.
*
* \note TRACE_USBHOST macro must be enabled. See Usb.h for reference.
*
* \param pDesc Pointer to HID descriptor.
*/
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc)
{
/*Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"));
Notify(PSTR("bDescLength:\t\t"));
PrintHex<uint8_t>(pDesc->bLength);
TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bDescLength: %d\r\n", pDesc->bLength);)
TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bDescriptorType: %d\r\n", pDesc->bDescriptorType);)
TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bcdHID: %d\r\n", pDesc->bcdHID);)
TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bCountryCode: %d\r\n", pDesc->bCountryCode);)
TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bNumDescriptors: %d\r\n", pDesc->bNumDescriptors);)
TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bDescrType: %d\r\n", pDesc->bDescrType);)
TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : wDescriptorLength: %d\r\n", pDesc->wDescriptorLength);)
Notify(PSTR("\r\nbDescriptorType:\t"));
PrintHex<uint8_t>(pDesc->bDescriptorType);
Notify(PSTR("\r\nbcdHID:\t\t\t"));
PrintHex<uint16_t>(pDesc->bcdHID);
Notify(PSTR("\r\nbCountryCode:\t\t"));
PrintHex<uint8_t>(pDesc->bCountryCode);
Notify(PSTR("\r\nbNumDescriptors:\t"));
PrintHex<uint8_t>(pDesc->bNumDescriptors);
//Notify(PSTR("\r\nbDescrType:\t\t"));
//PrintHex<uint8_t>(pDesc->bDescrType);
//
//Notify(PSTR("\r\nwDescriptorLength:\t"));
//PrintHex<uint16_t>(pDesc->wDescriptorLength);
for (uint8_t i=0; i<pDesc->bNumDescriptors; i++)
for (uint32_t i = 0; i < pDesc->bNumDescriptors; ++i)
{
HID_CLASS_DESCRIPTOR_LEN_AND_TYPE *pLT = (HID_CLASS_DESCRIPTOR_LEN_AND_TYPE*)&(pDesc->bDescrType);
Notify(PSTR("\r\nbDescrType:\t\t"));
PrintHex<uint8_t>(pLT[i].bDescrType);
Notify(PSTR("\r\nwDescriptorLength:\t"));
PrintHex<uint16_t>(pLT[i].wDescriptorLength);
TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bDescrType: %d\r\n", pLT[i].bDescrType);)
TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : wDescriptorLength: %d\r\n", pLT[i].wDescriptorLength);)
}
Notify(PSTR("\r\n"));*/
printf("somebody save me!!!! orgazmo?!!!\r\n");
}
#endif /* CONFDESCPARSER_H_INCLUDED */

View File

@ -0,0 +1,54 @@
#include "variant.h"
#include <stdio.h>
#include <adk.h>
// Accessory descriptor. It's how Arduino identifies itself to Android.
char applicationName[] = "Arduino_Terminal"; // the app on your phone
char accessoryName[] = "Arduino Due X"; // your Arduino board
char companyName[] = "Arduino SA";
// Make up anything you want for these
char versionNumber[] = "1.0";
char serialNumber[] = "1";
char url[] = "http://labs.arduino.cc/uploads/ADK/ArduinoTerminal/ThibaultTerminal_ICS_0001.apk";
USBHost Usb;
ADK adk(&Usb, companyName, applicationName, accessoryName,versionNumber,url,serialNumber);
void setup()
{
cpu_irq_enable();
printf("\r\nADK demo start\r\n");
delay(200);
}
#define RCVSIZE 128
void loop()
{
uint8_t buf[RCVSIZE];
uint32_t nbread = 0;
char helloworld[] = "Hello World!\r\n";
Usb.Task();
if (adk.isReady())
{
/* Write hello string to ADK */
adk.write(strlen(helloworld), (uint8_t *)helloworld);
delay(1000);
/* Read data from ADK and print to UART */
adk.read(&nbread, RCVSIZE, buf);
if (nbread > 0)
{
printf("RCV: ");
for (uint32_t i = 0; i < nbread; ++i)
{
printf("%c", (char)buf[i]);
}
printf("\r\n");
}
}
}

View File

@ -95,7 +95,10 @@ e-mail : support@circuitsathome.com
#define HID_PROTOCOL_KEYBOARD 0x01
#define HID_PROTOCOL_MOUSE 0x02
struct HidItemPrefix
/**
* \brief HidItemPrefix definition.
*/
struct HidItemPrefix // Not used
{
uint8_t bSize : 2;
uint8_t bType : 2;
@ -125,7 +128,10 @@ struct HidItemPrefix
#define HID_MAIN_ITEM_COLLECTION_USAGE_SWITCH 5
#define HID_MAIN_ITEM_COLLECTION_USAGE_MODIFIER 6
struct MainItemIOFeature
/**
* \brief MainItemIOFeature definition.
*/
struct MainItemIOFeature // Not used
{
uint8_t bmIsConstantOrData : 1;
uint8_t bmIsArrayOrVariable : 1;
@ -139,6 +145,11 @@ struct MainItemIOFeature
class HID;
/**
* \class Abstract HIDReportParser definition.
*
* \note This class is used to implement HID report parsing.
*/
class HIDReportParser
{
public:
@ -148,6 +159,9 @@ public:
#define MAX_REPORT_PARSERS 2
#define HID_MAX_HID_CLASS_DESCRIPTORS 5
/**
* \class HID definition.
*/
class HID : public USBDeviceConfig, public UsbConfigXtracter
{
protected:

View File

@ -17,7 +17,14 @@ e-mail : support@circuitsathome.com
#include "hid.h"
// Get HID report descriptor
/**
* \brief Get HID report descriptor and parse it.
*
* \param ep USB device endpoint.
* \param parser Parser used to decode report.
*
* \return 0 on success, error code otherwise.
*/
uint32_t HID::GetReportDescr(uint32_t ep, USBReadParser *parser)
{
const uint32_t constBufLen = 64;
@ -27,75 +34,108 @@ uint32_t HID::GetReportDescr(uint32_t ep, USBReadParser *parser)
HID_DESCRIPTOR_REPORT, 0x0000, 128, constBufLen, buf, (USBReadParser*)parser));
}
/**
* \brief Set HID report descriptor.
*
* \param ep USB device endpoint.
* \param iface Interface number.
* \param report_type HID report type.
* \param report_id HID report ID.
* \param nbytes Buffer length.
* \param dataptr Buffer containing the HID report to send.
*
* \return 0 on success, error code otherwise.
*/
uint32_t HID::SetReport(uint32_t ep, uint32_t iface, uint32_t report_type, uint32_t report_id, uint32_t nbytes, uint8_t* dataptr)
{
return (pUsb->ctrlReq(bAddress, ep, bmREQ_HIDOUT, HID_REQUEST_SET_REPORT, report_id, report_type, iface, nbytes, nbytes, dataptr, NULL));
}
/**
* \brief Get HID report descriptor.
*
* \param ep USB device endpoint.
* \param iface Interface number.
* \param report_type HID report type.
* \param report_id HID report ID.
* \param nbytes Buffer length.
* \param dataptr Buffer containing the HID report to send.
*
* \return 0 on success, error code otherwise.
*/
uint32_t HID::GetReport(uint32_t ep, uint32_t iface, uint32_t report_type, uint32_t report_id, uint32_t nbytes, uint8_t* dataptr)
{
return (pUsb->ctrlReq(bAddress, ep, bmREQ_HIDIN, HID_REQUEST_GET_REPORT, report_id, report_type, iface, nbytes, nbytes, dataptr, NULL));
}
uint32_t HID::GetIdle(uint32_t iface, uint32_t reportID, uint8_t* dataptr)
/**
* \brief Get HID idle status.
*
* \param iface Interface number.
* \param report_id HID report ID.
* \param dataptr Buffer to receive data. Size must be >= 1.
*
* \return 0 on success, error code otherwise.
*/
uint32_t HID::GetIdle(uint32_t iface, uint32_t report_id, uint8_t* dataptr)
{
return (pUsb->ctrlReq(bAddress, 0, bmREQ_HIDIN, HID_REQUEST_GET_IDLE, reportID, 0, iface, 0x0001, 0x0001, dataptr, NULL));
return (pUsb->ctrlReq(bAddress, 0, bmREQ_HIDIN, HID_REQUEST_GET_IDLE, report_id, 0, iface, 0x0001, 0x0001, dataptr, NULL));
}
uint32_t HID::SetIdle(uint32_t iface, uint32_t reportID, uint32_t duration)
/**
* \brief Set HID idle status.
*
* \param iface Interface number.
* \param report_id HID report ID.
* \param duration Status duration.
*
* \return 0 on success, error code otherwise.
*/
uint32_t HID::SetIdle(uint32_t iface, uint32_t report_id, uint32_t duration)
{
return (pUsb->ctrlReq(bAddress, 0, bmREQ_HIDOUT, HID_REQUEST_SET_IDLE, reportID, duration, iface, 0x0000, 0x0000, NULL, NULL));
return (pUsb->ctrlReq(bAddress, 0, bmREQ_HIDOUT, HID_REQUEST_SET_IDLE, report_id, duration, iface, 0x0000, 0x0000, NULL, NULL));
}
/**
* \brief Set HID protocol.
*
* \param iface Interface number.
* \param protocol Protocol value.
*
* \return 0 on success, error code otherwise.
*/
uint32_t HID::SetProtocol(uint32_t iface, uint32_t protocol)
{
return (pUsb->ctrlReq(bAddress, 0, bmREQ_HIDOUT, HID_REQUEST_SET_PROTOCOL, protocol, 0x00, iface, 0x0000, 0x0000, NULL, NULL));
}
/**
* \brief Get HID protocol.
*
* \param iface Interface number.
* \param dataptr Buffer used to store protocol value. Size must be >= 1.
*
* \return 0 on success, error code otherwise.
*/
uint32_t HID::GetProtocol(uint32_t iface, uint8_t* dataptr)
{
return (pUsb->ctrlReq(bAddress, 0, bmREQ_HIDIN, HID_REQUEST_GET_PROTOCOL, 0x00, 0x00, iface, 0x0001, 0x0001, dataptr, NULL));
}
void HID::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr)
{
/*Notify(PSTR("Endpoint descriptor:"));
Notify(PSTR("\r\nLength:\t\t"));
PrintHex<uint8_t>(ep_ptr->bLength);
Notify(PSTR("\r\nType:\t\t"));
PrintHex<uint8_t>(ep_ptr->bDescriptorType);
Notify(PSTR("\r\nAddress:\t"));
PrintHex<uint8_t>(ep_ptr->bEndpointAddress);
Notify(PSTR("\r\nAttributes:\t"));
PrintHex<uint8_t>(ep_ptr->bmAttributes);
Notify(PSTR("\r\nMaxPktSize:\t"));
PrintHex<uint16_t>(ep_ptr->wMaxPacketSize);
Notify(PSTR("\r\nPoll Intrv:\t"));
PrintHex<uint8_t>(ep_ptr->bInterval);*/
}
/**
* \brief Print HID descriptor.
*
* \note TRACE_USBHOST macro must be enabled. See Usb.h for reference.
*
* \param pDesc Pointer to HID descriptor.
*/
void HID::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc)
{
/* Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"));
Notify(PSTR("bDescLength:\t\t"));
PrintHex<uint8_t>(pDesc->bLength);
Notify(PSTR("\r\nbDescriptorType:\t"));
PrintHex<uint8_t>(pDesc->bDescriptorType);
Notify(PSTR("\r\nbcdHID:\t\t\t"));
PrintHex<uint16_t>(pDesc->bcdHID);
Notify(PSTR("\r\nbCountryCode:\t\t"));
PrintHex<uint8_t>(pDesc->bCountryCode);
Notify(PSTR("\r\nbNumDescriptors:\t"));
PrintHex<uint8_t>(pDesc->bNumDescriptors);
Notify(PSTR("\r\nbDescrType:\t\t"));
PrintHex<uint8_t>(pDesc->bDescrType);
Notify(PSTR("\r\nwDescriptorLength:\t"));
PrintHex<uint16_t>(pDesc->wDescriptorLength);*/
TRACE_USBHOST(printf("HID::PrintHidDescriptor : bDescLength: %d\r\n", pDesc->bLength);)
TRACE_USBHOST(printf("HID::PrintHidDescriptor : bDescriptorType: %d\r\n", pDesc->bDescriptorType);)
TRACE_USBHOST(printf("HID::PrintHidDescriptor : bcdHID: %d\r\n", pDesc->bcdHID);)
TRACE_USBHOST(printf("HID::PrintHidDescriptor : bCountryCode: %d\r\n", pDesc->bCountryCode);)
TRACE_USBHOST(printf("HID::PrintHidDescriptor : bNumDescriptors: %d\r\n", pDesc->bNumDescriptors);)
TRACE_USBHOST(printf("HID::PrintHidDescriptor : bDescrType: %d\r\n", pDesc->bDescrType);)
TRACE_USBHOST(printf("HID::PrintHidDescriptor : wDescriptorLength: %d\r\n", pDesc->wDescriptorLength);)
}

View File

@ -17,6 +17,14 @@ e-mail : support@circuitsathome.com
#include "hidboot.h"
/**
* \brief Parse HID mouse report.
*
* \param hid HID device pointer.
* \param is_rpt_id True if this is a report ID.
* \param len Buffer length.
* \param buf Buffer containing report data.
*/
void MouseReportParser::Parse(HID *hid, bool is_rpt_id, uint32_t len, uint8_t *buf)
{
MOUSEINFO *pmi = (MOUSEINFO*)buf;
@ -46,6 +54,14 @@ void MouseReportParser::Parse(HID *hid, bool is_rpt_id, uint32_t len, uint8_t *b
prevState.bInfo[i] = buf[i];
};
/**
* \brief Parse HID keyboard report.
*
* \param hid HID device pointer.
* \param is_rpt_id True if this is a report ID.
* \param len Buffer length.
* \param buf Buffer containing report data.
*/
void KeyboardReportParser::Parse(HID *hid, bool is_rpt_id, uint32_t len, uint8_t *buf)
{
// On error - return
@ -81,6 +97,15 @@ void KeyboardReportParser::Parse(HID *hid, bool is_rpt_id, uint32_t len, uint8_t
prevState.bInfo[i] = buf[i];
};
/**
* \brief Handle keyboard locking keys and manage keyboard leds using USB
* report.
*
* \param hid HID device pointer.
* \param key Locking key.
*
* \return 0 on success, error code otherwise.
*/
uint8_t KeyboardReportParser::HandleLockingKeys(HID *hid, uint8_t key)
{
uint8_t old_keys = kbdLockingKeys.bLeds;
@ -109,6 +134,14 @@ const uint8_t KeyboardReportParser::symKeysUp[] = { '_', '+', '{', '}', '|', '~'
const uint8_t KeyboardReportParser::symKeysLo[] = { '-', '=', '[', ']', '\\', ' ', ';', '\'', '`', ',', '.', '/' };
const uint8_t KeyboardReportParser::padKeys[] = { '/', '*', '-', '+', 0x13 };
/**
* \brief Manage keyboard OEM to ASCII conversion.
*
* \param mod Keyboard modifier.
* \param key Key value to convert.
*
* \return Keyboard corresponding ASCII value on success, 0 otherwise.
*/
uint8_t KeyboardReportParser::OemToAscii(uint8_t mod, uint8_t key)
{
uint8_t shift = (mod & 0x22);
@ -154,5 +187,6 @@ uint8_t KeyboardReportParser::OemToAscii(uint8_t mod, uint8_t key)
case KEY_PERIOD: return ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '.' : 0);
}
}
return 0;
}

View File

@ -31,6 +31,9 @@ e-mail : support@circuitsathome.com
#define KEY_ENTER 0x28
#define KEY_PERIOD 0x63
/**
* \brief MOUSEINFO definition.
*/
struct MOUSEINFO
{
struct
@ -44,6 +47,9 @@ struct MOUSEINFO
int8_t dY;
};
/**
* \class MouseReportParser definition.
*/
class MouseReportParser : public HIDReportParser
{
union
@ -65,6 +71,9 @@ protected:
virtual void OnMiddleButtonDown (MOUSEINFO *mi) {};
};
/**
* \brief MODIFIERKEYS definition.
*/
struct MODIFIERKEYS
{
uint8_t bmLeftCtrl : 1;
@ -77,6 +86,9 @@ struct MODIFIERKEYS
uint8_t bmRightGUI : 1;
};
/**
* \brief KBDINFO definition.
*/
struct KBDINFO
{
struct
@ -94,6 +106,9 @@ struct KBDINFO
uint8_t Keys[6];
};
/**
* \brief KBDLEDS definition.
*/
struct KBDLEDS
{
uint8_t bmNumLock : 1;
@ -105,9 +120,18 @@ struct KBDLEDS
};
#define KEY_NUM_LOCK 0x53
// Clear compiler warning
#ifdef KEY_CAPS_LOCK
#undef KEY_CAPS_LOCK
#endif
#define KEY_CAPS_LOCK 0x39
#define KEY_SCROLL_LOCK 0x47
/**
* \class KeyboardReportParser definition.
*/
class KeyboardReportParser : public HIDReportParser
{
static const uint8_t numKeys[];
@ -146,6 +170,9 @@ protected:
#define HID_MAX_HID_CLASS_DESCRIPTORS 5
/**
* \class HIDBoot definition.
*/
template <const uint8_t BOOT_PROTOCOL>
class HIDBoot : public HID
{
@ -167,7 +194,7 @@ class HIDBoot : public HID
public:
HIDBoot(USBHost *p);
virtual bool SetReportParser(uint32_t id, HIDReportParser *prs) { pRptParser = prs; return true;}; /////////////////////// le return ne sert a rien ?!!!
virtual bool SetReportParser(uint32_t id, HIDReportParser *prs) { pRptParser = prs; return true; };
// USBDeviceConfig implementation
virtual uint32_t Init(uint32_t parent, uint32_t port, uint32_t lowspeed);
@ -179,6 +206,9 @@ public:
virtual void EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
};
/**
* \brief HIDBoot class constructor.
*/
template <const uint8_t BOOT_PROTOCOL>
HIDBoot<BOOT_PROTOCOL>::HIDBoot(USBHost *p) :
HID(p),
@ -192,21 +222,35 @@ HIDBoot<BOOT_PROTOCOL>::HIDBoot(USBHost *p) :
pUsb->RegisterDeviceClass(this);
}
/**
* \brief Initialize HIDBoot class.
*/
template <const uint8_t BOOT_PROTOCOL>
void HIDBoot<BOOT_PROTOCOL>::Initialize()
{
for(uint32_t i = 0; i < totalEndpoints; ++i)
for (uint32_t i = 0; i < totalEndpoints; ++i)
{
epInfo[i].epAddr = 0;
epInfo[i].deviceEpNum = 0;
epInfo[i].hostPipeNum = 0;
epInfo[i].maxPktSize = (i) ? 0 : 8;
epInfo[i].epAttribs = 0;
epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
}
bNumEP = 1;
bNumIface = 0;
bConfNum = 0;
}
/**
* \brief Initialize connection to an HID device.
*
* \param parent USB device address of the Parent device.
* \param port USB device base address.
* \param lowspeed USB device speed.
*
* \return 0 on success, error code otherwise.
*/
template <const uint8_t BOOT_PROTOCOL>
uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t lowspeed)
{
@ -214,14 +258,16 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t l
uint8_t buf[constBufSize];
uint32_t rcode = 0;
UsbDevice *p = NULL;
EpInfo *oldep_ptr = NULL;
UsbDevice *p = 0;
EpInfo *oldep_ptr = 0;
uint32_t len = 0;
uint32_t num_of_conf; // number of configurations
uint32_t num_of_conf = 0; // number of configurations
AddressPool &addrPool = pUsb->GetAddressPool();
TRACE_USBHOST(printf("HIDBoot::Init\r\n");)
if (bAddress)
return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
@ -246,9 +292,9 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t l
p->lowspeed = lowspeed;
// Get device descriptor
rcode = pUsb->getDevDescr( 0, 0, 8, (uint8_t*)buf );
rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf);
if (!rcode)
if (!rcode)
len = (buf[0] > constBufSize) ? constBufSize : buf[0];
if (rcode)
@ -312,14 +358,12 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t l
for (uint32_t i = 0; i < num_of_conf; ++i)
{
//HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;
ConfigDescParser<
USB_CLASS_HID,
HID_BOOT_INTF_SUBCLASS,
BOOT_PROTOCOL,
CP_MASK_COMPARE_ALL> confDescrParser(this);
CP_MASK_COMPARE_ALL> confDescrParser(this);
//rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump);
rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
if (bNumEP > 1)
@ -364,31 +408,41 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t l
return 0;
FailGetDevDescr:
//USBTRACE("getDevDescr:");
TRACE_USBHOST(printf("HIDBoot::Init getDevDescr : ");)
goto Fail;
FailSetDevTblEntry:
//USBTRACE("setDevTblEn:");
TRACE_USBHOST(printf("HIDBoot::Init setDevTblEn : ");)
goto Fail;
FailSetProtocol:
//USBTRACE("SetProto:");
TRACE_USBHOST(printf("HIDBoot::Init SetProto : ");)
goto Fail;
FailSetIdle:
//USBTRACE("SetIdle:");
TRACE_USBHOST(printf("HIDBoot::Init SetIdle : ");)
goto Fail;
FailSetConfDescr:
//USBTRACE("setConf:");
TRACE_USBHOST(printf("HIDBoot::Init setConf : ");)
goto Fail;
Fail:
//Serial.println(rcode, HEX);
TRACE_USBHOST(printf("error code: %lu\r\n", rcode);)
Release();
return rcode;
}
/**
* \brief Extract interrupt-IN endpoint information from configuration
* descriptor.
*
* \param conf Configuration number.
* \param iface Interface number.
* \param alt Alternate setting.
* \param proto Protocol version used.
* \param pep Pointer to endpoint descriptor.
*/
template <const uint8_t BOOT_PROTOCOL>
void HIDBoot<BOOT_PROTOCOL>::EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *pep)
{
@ -396,41 +450,56 @@ void HIDBoot<BOOT_PROTOCOL>::EndpointXtract(uint32_t conf, uint32_t iface, uint3
if (bNumEP > 1 && conf != bConfNum)
return;
//ErrorMessage<uint8_t>(PSTR("\r\nConf.Val"), conf);
//ErrorMessage<uint8_t>(PSTR("Iface Num"), iface);
//ErrorMessage<uint8_t>(PSTR("Alt.Set"), alt);
bConfNum = conf;
bIfaceNum = iface;
bConfNum = conf;
bIfaceNum = iface;
uint32_t index;
uint32_t index = 0;
uint32_t pipe = 0;
if ((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80)
{
index = epInterruptInIndex;
// Fill in the endpoint info structure
epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
epInfo[index].deviceEpNum = (pep->bEndpointAddress & 0x0F);
epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
epInfo[index].epAttribs = 0;
bNumEP++;
TRACE_USBHOST(printf("HIDBoot::EndpointXtract : Found new endpoint\r\n");)
TRACE_USBHOST(printf("HIDBoot::EndpointXtract : epAddr: %lu\r\n", epInfo[index].epAddr);)
TRACE_USBHOST(printf("HIDBoot::EndpointXtract : deviceEpNum: %lu\r\n", epInfo[index].deviceEpNum);)
TRACE_USBHOST(printf("HIDBoot::EndpointXtract : maxPktSize: %lu\r\n", epInfo[index].maxPktSize);)
TRACE_USBHOST(printf("HIDBoot::EndpointXtract : index: %lu\r\n", index);)
UHD_EP_Alloc(epInfo[index].epAddr, bAddress, 10, UOTGHS_HSTPIPCFG_PTYPE_INTRPT, UOTGHS_HSTPIPCFG_PTOKEN_IN, epInfo[index].maxPktSize, UOTGHS_HSTPIPCFG_PBK_1_BANK);
// Ensure pipe allocation is okay
pipe = UHD_Pipe_Alloc(bAddress, epInfo[index].deviceEpNum, UOTGHS_HSTPIPCFG_PTYPE_INTRPT, UOTGHS_HSTPIPCFG_PTOKEN_IN, epInfo[index].maxPktSize, 10, UOTGHS_HSTPIPCFG_PBK_1_BANK);
if (pipe == 0)
{
TRACE_USBHOST(printf("HIDBoot::EndpointXtract : Pipe allocation failure\r\n");)
// Enumeration failed
return;
}
//PrintEndpointDescriptor(pep);
epInfo[index].hostPipeNum = pipe;
bNumEP++;
}
}
/**
* \brief Release USB allocated resources (pipes and address).
*
* \note Release call is made from USBHost.task() on disconnection events.
* \note Release call is made from Init() on enumeration failure.
*
* \return Always 0.
*/
template <const uint8_t BOOT_PROTOCOL>
uint32_t HIDBoot<BOOT_PROTOCOL>::Release()
{
// Free allocated host pipes
UHD_Pipe_Free(epInfo[epInterruptInIndex].hostPipeNum);
// Free allocated USB address
pUsb->GetAddressPool().FreeAddress(bAddress);
bConfNum = 0;
@ -439,9 +508,17 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Release()
bAddress = 0;
qNextPollTime = 0;
bPollEnable = false;
return 0;
}
/**
* \brief Poll USB device activity.
*
* \note Poll call is periodically made from USBHost.task().
*
* \return 0 on success, error code otherwise.
*/
template <const uint8_t BOOT_PROTOCOL>
uint32_t HIDBoot<BOOT_PROTOCOL>::Poll()
{
@ -459,12 +536,10 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Poll()
uint32_t read = epInfo[epInterruptInIndex].maxPktSize;
rcode = pUsb->inTransfer(bAddress, epInfo[epInterruptInIndex].epAddr, &read, buf);
rcode = pUsb->inTransfer(bAddress, epInfo[epInterruptInIndex].deviceEpNum, &read, buf);
if (rcode)
{
//if (rcode != hrNAK)
//USBTRACE2("Poll:", rcode);
return rcode;
}

View File

@ -90,6 +90,7 @@ e-mail : support@circuitsathome.com
#define USB_FEATURE_ENDPOINT_STALL 0 // Endpoint recipient
#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // Device recipient
#define USB_FEATURE_TEST_MODE 2 // Device recipient
_Pragma("pack(1)")
/* descriptor data structures */
@ -147,7 +148,7 @@ typedef struct
uint8_t bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT).
uint8_t bEndpointAddress; // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN).
uint8_t bmAttributes; // Endpoint transfer type.
uint16_t wMaxPacketSize; // Maximum packet size.
uint16_t wMaxPacketSize; // Maximum packet size.
uint8_t bInterval; // Polling interval in frames.
} USB_ENDPOINT_DESCRIPTOR;

View File

@ -55,17 +55,16 @@ typedef enum {
//extern uhd_speed_t uhd_get_speed(void);
extern void UHD_SetStack(void (*pf_isr)(void));
extern void UHD_Init(void);
extern void UHD_BusReset(void);
extern uhd_vbus_state_t UHD_GetVBUSState(void);
extern uint32_t UHD_EP0_Alloc(uint32_t ul_add, uint32_t ul_ep_size);
extern uint32_t UHD_EP_Alloc(uint32_t ul_pipe, uint32_t ul_addr, uint32_t ul_interval, uint32_t ul_type, uint32_t ul_dir, uint32_t ul_maxsize, uint32_t ul_bank);
extern void UHD_EP_Free(uint32_t add, uint32_t endp);
extern uint32_t UHD_EP_Read(uint32_t ul_ep, uint32_t ul_size, uint8_t* data);
extern void UHD_EP_Write(uint32_t ul_ep, uint32_t ul_size, uint8_t* data);
extern void UHD_EP_Send(uint32_t ul_ep, uint32_t ul_token_type);
extern uint32_t UHD_EP_Is_Transfer_Complete(uint32_t ul_ep, uint32_t ul_token_type);
extern uint32_t UHD_Pipe0_Alloc(uint32_t ul_add, uint32_t ul_ep_size);
extern uint32_t UHD_Pipe_Alloc(uint32_t ul_dev_addr, uint32_t ul_dev_ep, uint32_t ul_type, uint32_t ul_dir, uint32_t ul_maxsize, uint32_t ul_interval, uint32_t ul_nb_bank);
extern void UHD_Pipe_Free(uint32_t ul_pipe);
extern uint32_t UHD_Pipe_Read(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data);
extern void UHD_Pipe_Write(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data);
extern void UHD_Pipe_Send(uint32_t ul_pipe, uint32_t ul_token_type);
extern uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type);
#endif /* USB_HOST_H_INCLUDED */

View File

@ -32,13 +32,17 @@
#if SAM3XA_SERIES
#define TRACE_UOTGHS_HOST(x) x
//#define TRACE_UOTGHS_HOST(x)
//#define TRACE_UOTGHS_HOST(x) x
#define TRACE_UOTGHS_HOST(x)
extern void (*gpf_isr)(void);
// Handle UOTGHS Host driver state
static uhd_vbus_state_t uhd_state = UHD_STATE_NO_VBUS;
/**
* \brief Interrupt sub routine for USB Host state machine management.
*/
static void UHD_ISR(void)
{
// Manage dis/connection event
@ -74,7 +78,7 @@ static void UHD_ISR(void)
{
TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS error INT\r\n");)
uhd_ack_vbus_error_interrupt();
uhd_state = UHD_STATE_ERROR;
uhd_state = UHD_STATE_DISCONNECTED; //UHD_STATE_ERROR;
return;
}
@ -111,11 +115,19 @@ static void UHD_ISR(void)
}
}
/**
* \brief Set the interrupt sub routines callback for USB interrupts.
*
* \param pf_isr the ISR address.
*/
void UHD_SetStack(void (*pf_isr)(void))
{
gpf_isr = pf_isr;
}
/**
* \brief Initialize the UOTGHS host driver.
*/
void UHD_Init(void)
{
irqflags_t flags;
@ -178,7 +190,7 @@ void UHD_Init(void)
// Enable main control interrupt
// Connection, SOF and reset
UOTGHS->UOTGHS_HSTIER = UOTGHS_HSTICR_DCONNIC;// | UOTGHS_HSTICR_RSTIC;// | UOTGHS_HSTICR_HSOFIC;
UOTGHS->UOTGHS_HSTIER = UOTGHS_HSTICR_DCONNIC;
otg_freeze_clock();
@ -187,11 +199,19 @@ void UHD_Init(void)
cpu_irq_restore(flags);
}
/**
* \brief Trigger a USB bus reset.
*/
void UHD_BusReset(void)
{
uhd_start_reset();
}
/**
* \brief Get VBUS state.
*
* \return VBUS status.
*/
uhd_vbus_state_t UHD_GetVBUSState(void)
{
return uhd_state;
@ -224,7 +244,7 @@ uhd_vbus_state_t UHD_GetVBUSState(void)
* \retval 0 success.
* \retval 1 error.
*/
uint32_t UHD_EP0_Alloc(uint32_t ul_add, uint32_t ul_ep_size)
uint32_t UHD_Pipe0_Alloc(uint32_t ul_add, uint32_t ul_ep_size)
{
if (ul_ep_size < 8)
{
@ -262,88 +282,88 @@ uint32_t UHD_EP0_Alloc(uint32_t ul_add, uint32_t ul_ep_size)
}
/**
* \brief Allocate FIFO for the specified pipe.
* \brief Allocate a new pipe.
*
* \param ul_add Address of remote device for pipe 0.
* \param ul_ep_size Actual size of the FIFO in bytes.
* \note UOTGHS maximum pipe number is limited to 10, meaning that only a limited
* amount of devices can be connected. Unfortunately, using only one pipe shared accross
* various endpoints and devices is not possible because the UOTGHS IP does not allow to
* change the data toggle value through register interface.
*
* \retval 0 success.
* \retval 1 error.
* \param ul_dev_addr Address of remote device.
* \param ul_dev_ep Targeted endpoint of remote device.
* \param ul_type Pipe type.
* \param ul_dir Pipe direction.
* \param ul_maxsize Pipe size.
* \param ul_interval Polling interval (if applicable to pipe type).
* \param ul_nb_bank Number of banks associated with this pipe.
*
* \return the newly allocated pipe number on success, 0 otherwise.
*/
uint32_t UHD_EP_Alloc(uint32_t ul_pipe, uint32_t ul_addr, uint32_t ul_interval, uint32_t ul_type, uint32_t ul_dir, uint32_t ul_maxsize, uint32_t ul_bank)
uint32_t UHD_Pipe_Alloc(uint32_t ul_dev_addr, uint32_t ul_dev_ep, uint32_t ul_type, uint32_t ul_dir, uint32_t ul_maxsize, uint32_t ul_interval, uint32_t ul_nb_bank)
{
uint32_t ul_pipe = 1;
/*
* Warning: this is a first implementation, pipe allocation is very limited.
* We should probably check maxsize and if maxsize > current size, then realloc with maxsize.
* If pipe type is changed, a pipe reset and re-configuration is required.
*/
if (Is_uhd_pipe_enabled(ul_pipe))
for (ul_pipe = 1; ul_pipe < UOTGHS_EPT_NUM; ++ul_pipe)
{
// Pipe is already allocated
return 0;
if (Is_uhd_pipe_enabled(ul_pipe))
{
continue;
}
uhd_enable_pipe(ul_pipe);
uhd_configure_pipe(ul_pipe, ul_interval, ul_dev_ep, ul_type, ul_dir,
ul_maxsize, ul_nb_bank, UOTGHS_HSTPIPCFG_AUTOSW);
uhd_allocate_memory(ul_pipe);
if (!Is_uhd_pipe_configured(ul_pipe))
{
uhd_disable_pipe(ul_pipe);
return 0;
}
uhd_configure_address(ul_pipe, ul_dev_addr);
// Pipe is configured and allocated successfully
return ul_pipe;
}
uhd_enable_pipe(ul_pipe);
uhd_configure_pipe(ul_pipe, ul_interval, ul_addr, ul_type, ul_dir,
ul_maxsize, ul_bank, UOTGHS_HSTPIPCFG_AUTOSW);
uhd_allocate_memory(ul_pipe);
if (!Is_uhd_pipe_configured(ul_pipe))
{
uhd_disable_pipe(ul_pipe);
return 1;
}
uhd_configure_address(ul_pipe, ul_addr);
return 0;
}
void UHD_EP_Free(uint32_t add, uint32_t endp)
/**
* \brief Free a pipe.
*
* \param ul_pipe Pipe number to free.
*/
void UHD_Pipe_Free(uint32_t ul_pipe)
{
// Search endpoint(s) in all pipes
for (uint8_t pipe = 0; pipe < UOTGHS_EPT_NUM; pipe++)
{
if (!Is_uhd_pipe_enabled(pipe))
{
continue;
}
if (add != uhd_get_configured_address(pipe))
{
continue;
}
/* if (endp != 0xFF)
{
// Disable specific endpoint number
if (endp != uhd_get_pipe_endpoint_address(pipe))
{
continue; // Mismatch
}
}
*/
// Unalloc pipe
uhd_disable_pipe(pipe);
uhd_unallocate_memory(pipe);
uhd_reset_pipe(pipe);
}
// Unalloc pipe
uhd_disable_pipe(ul_pipe);
uhd_unallocate_memory(ul_pipe);
uhd_reset_pipe(ul_pipe);
}
uint32_t UHD_EP_Read(uint32_t ul_ep, uint32_t ul_size, uint8_t* data)
/**
* \brief Read from a pipe.
*
* \param ul_pipe Pipe number.
* \param ul_size Maximum number of data to read.
* \param data Buffer to store the data.
*
* \return number of data read.
*/
uint32_t UHD_Pipe_Read(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data)
{
uint8_t *ptr_ep_data = 0;
uint8_t nb_byte_received = 0;
uint32_t ul_nb_trans = 0;
// Get information to read data
nb_byte_received = uhd_byte_count(ul_ep);
nb_byte_received = uhd_byte_count(ul_pipe);
ptr_ep_data = (uint8_t *) & uhd_get_pipe_fifo_access(ul_ep, 8);
ptr_ep_data = (uint8_t *) & uhd_get_pipe_fifo_access(ul_pipe, 8);
// Copy data from pipe to payload buffer
while (ul_size && nb_byte_received) {
@ -356,98 +376,111 @@ uint32_t UHD_EP_Read(uint32_t ul_ep, uint32_t ul_size, uint8_t* data)
return ul_nb_trans;
}
void UHD_EP_Write(uint32_t ul_ep, uint32_t ul_size, uint8_t* data)
/**
* \brief Write into a pipe.
*
* \param ul_pipe Pipe number.
* \param ul_size Maximum number of data to read.
* \param data Buffer containing data to write.
*/
void UHD_Pipe_Write(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data)
{
volatile uint8_t *ptr_ep_data = 0;
uint32_t i = 0;
// Check pipe
if (!Is_uhd_pipe_enabled(ul_ep))
if (!Is_uhd_pipe_enabled(ul_pipe))
{
// Endpoint not valid
TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP_Send : pipe is not enabled!\r\n");)
return;
}
ptr_ep_data = (volatile uint8_t *)&uhd_get_pipe_fifo_access(ul_ep, 8);
ptr_ep_data = (volatile uint8_t *)&uhd_get_pipe_fifo_access(ul_pipe, 8);
for (i = 0; i < ul_size; ++i)
*ptr_ep_data++ = *data++;
}
void UHD_EP_Send(uint32_t ul_ep, uint32_t ul_token_type)
/**
* \brief Send a pipe content.
*
* \param ul_pipe Pipe number.
* \param ul_token_type Token type.
*/
void UHD_Pipe_Send(uint32_t ul_pipe, uint32_t ul_token_type)
{
// Check pipe
if (!Is_uhd_pipe_enabled(ul_ep))
if (!Is_uhd_pipe_enabled(ul_pipe))
{
// Endpoint not valid
TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP_Send : pipe %lu is not enabled!\r\n", ul_ep);)
TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP_Send : pipe %lu is not enabled!\r\n", ul_pipe);)
return;
}
// Set token type for zero length packet
// When actually using the FIFO, pipe token MUST be configured first
uhd_configure_pipe_token(ul_ep, ul_token_type);
uhd_configure_pipe_token(ul_pipe, ul_token_type);
// Clear interrupt flags
uhd_ack_setup_ready(ul_ep);
uhd_ack_in_received(ul_ep);
uhd_ack_out_ready(ul_ep);
uhd_ack_short_packet(ul_ep);
uhd_ack_setup_ready(ul_pipe);
uhd_ack_in_received(ul_pipe);
uhd_ack_out_ready(ul_pipe);
uhd_ack_short_packet(ul_pipe);
uhd_ack_nak_received(ul_pipe);
// Send actual packet
uhd_ack_fifocon(ul_ep);
uhd_unfreeze_pipe(ul_ep);
uhd_ack_fifocon(ul_pipe);
uhd_unfreeze_pipe(ul_pipe);
}
/**
* \brief Check is transfer is complete.
* \brief Check for pipe transfer completion.
*
* \param ul_add Address of remote device for pipe 0.
* \param ul_ep_size Actual size of the FIFO in bytes.
* \param ul_pipe Pipe number.
* \param ul_token_type Token type.
*
* \retval 0 transfer is not complete.
* \retval 1 transfer is complete.
*/
uint32_t UHD_EP_Is_Transfer_Complete(uint32_t ul_ep, uint32_t ul_token_type)
uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type)
{
// Check for transfer completion depending on token type
switch (ul_token_type)
{
case UOTGHS_HSTPIPCFG_PTOKEN_SETUP:
if (Is_uhd_setup_ready(ul_ep))
if (Is_uhd_setup_ready(ul_pipe))
{
uhd_freeze_pipe(ul_ep);
uhd_ack_setup_ready(ul_ep);
uhd_freeze_pipe(ul_pipe);
uhd_ack_setup_ready(ul_pipe);
return 1;
}
case UOTGHS_HSTPIPCFG_PTOKEN_IN:
if (Is_uhd_in_received(ul_ep))
if (Is_uhd_in_received(ul_pipe))
{
// In case of low USB speed and with a high CPU frequency,
// a ACK from host can be always running on USB line
// then wait end of ACK on IN pipe.
while(!Is_uhd_pipe_frozen(ul_ep))
while(!Is_uhd_pipe_frozen(ul_pipe))
;
// IN packet received
uhd_ack_in_received(ul_ep);
uhd_ack_in_received(ul_pipe);
return 1;
}
case UOTGHS_HSTPIPCFG_PTOKEN_OUT:
if (Is_uhd_out_ready(ul_ep))
if (Is_uhd_out_ready(ul_pipe))
{
// OUT packet sent
uhd_freeze_pipe(ul_ep);
uhd_ack_out_ready(ul_ep);
uhd_freeze_pipe(ul_pipe);
uhd_ack_out_ready(ul_pipe);
return 1;
}
}
// Nothing to report
return 0;
}

View File

@ -99,14 +99,14 @@ pwmc.o:
00000000 T PWMC_SetSyncChannelUpdateUnlock
00000000 T PWMC_WriteBuffer
U __assert_func
00000000 r __func__.3296
00000000 r __func__.3307
00000000 r __func__.3322
00000000 r __func__.3333
00000000 r __func__.3344
00000000 r __func__.3351
00000000 r __func__.3435
00000000 r __func__.3441
00000000 r __func__.3295
00000000 r __func__.3306
00000000 r __func__.3321
00000000 r __func__.3332
00000000 r __func__.3343
00000000 r __func__.3350
00000000 r __func__.3434
00000000 r __func__.3440
rtc.o:
00000000 T RTC_ClearSCCR
@ -122,9 +122,9 @@ rtc.o:
00000000 T RTC_SetTime
00000000 T RTC_SetTimeAlarm
U __assert_func
00000000 r __func__.3293
00000000 r __func__.3302
00000000 r __func__.3307
00000000 r __func__.3292
00000000 r __func__.3301
00000000 r __func__.3306
rtt.o:
00000000 T RTT_EnableIT
@ -133,8 +133,8 @@ rtt.o:
00000000 T RTT_SetAlarm
00000000 T RTT_SetPrescaler
U __assert_func
00000000 r __func__.3300
00000000 r __func__.3308
00000000 r __func__.3299
00000000 r __func__.3307
spi.o:
00000000 T SPI_Configure
@ -155,9 +155,9 @@ tc.o:
00000000 T TC_Start
00000000 T TC_Stop
U __assert_func
00000000 r __func__.3295
00000000 r __func__.3301
00000000 r __func__.3307
00000000 r __func__.3294
00000000 r __func__.3300
00000000 r __func__.3306
timetick.o:
00000000 T GetTickCount
@ -184,18 +184,18 @@ twi.o:
00000000 T TWI_TransferComplete
00000000 T TWI_WriteByte
U __assert_func
00000000 r __func__.3660
00000000 r __func__.3675
00000000 r __func__.3679
00000000 r __func__.3686
00000000 r __func__.3690
00000000 r __func__.3695
00000000 r __func__.3703
00000000 r __func__.3717
00000000 r __func__.3722
00000000 r __func__.3726
00000000 r __func__.3731
00000000 r __func__.3735
00000000 r __func__.3659
00000000 r __func__.3674
00000000 r __func__.3678
00000000 r __func__.3685
00000000 r __func__.3689
00000000 r __func__.3694
00000000 r __func__.3702
00000000 r __func__.3716
00000000 r __func__.3721
00000000 r __func__.3725
00000000 r __func__.3730
00000000 r __func__.3734
usart.o:
00000000 T USART_Configure
@ -214,7 +214,7 @@ usart.o:
00000000 T USART_Write
00000000 T USART_WriteBuffer
U __assert_func
00000000 r __func__.3581
00000000 r __func__.3580
wdt.o:
00000000 T WDT_Disable
@ -385,20 +385,19 @@ uotghs_device.o:
uotghs_host.o:
00000000 T UHD_BusReset
00000000 T UHD_EP0_Alloc
00000000 T UHD_EP_Alloc
00000000 T UHD_EP_Free
00000000 T UHD_EP_Is_Transfer_Complete
00000000 T UHD_EP_Read
00000000 T UHD_EP_Send
00000000 T UHD_EP_Write
00000000 T UHD_GetVBUSState
00000000 t UHD_ISR
00000000 T UHD_Init
00000000 T UHD_Pipe0_Alloc
00000000 T UHD_Pipe_Alloc
00000000 T UHD_Pipe_Free
00000000 T UHD_Pipe_Is_Transfer_Complete
00000000 T UHD_Pipe_Read
00000000 T UHD_Pipe_Send
00000000 T UHD_Pipe_Write
00000000 T UHD_SetStack
U g_interrupt_enabled
U gpf_isr
U iprintf
U pmc_enable_periph_clk
U pmc_enable_udpck
U pmc_enable_upll_clock