1
0
mirror of https://github.com/arduino/Arduino.git synced 2025-03-15 12:29:26 +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 "variant.h"
#include <stdio.h> #include <stdio.h>
#include <hidboot.h> #include <adk.h>
class MouseRptParser : public MouseReportParser // Accessory descriptor. It's how Arduino identifies itself to Android.
{ char applicationName[] = "Arduino_Terminal"; // the app on your phone
protected: char accessoryName[] = "Arduino Due X"; // your Arduino board
virtual void OnMouseMove (MOUSEINFO *mi); char companyName[] = "Arduino SA";
virtual void OnLeftButtonUp (MOUSEINFO *mi);
virtual void OnLeftButtonDown (MOUSEINFO *mi); // Make up anything you want for these
virtual void OnRightButtonUp (MOUSEINFO *mi); char versionNumber[] = "1.0";
virtual void OnRightButtonDown (MOUSEINFO *mi); char serialNumber[] = "1";
virtual void OnMiddleButtonUp (MOUSEINFO *mi); char url[] = "http://labs.arduino.cc/uploads/ADK/ArduinoTerminal/ThibaultTerminal_ICS_0001.apk";
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");
};
USBHost Usb; USBHost Usb;
HIDBoot<HID_PROTOCOL_MOUSE> HostMouse(&Usb); ADK adk(&Usb, companyName, applicationName, accessoryName,versionNumber,url,serialNumber);
MouseRptParser Prs;
void setup() void setup()
{ {
cpu_irq_enable(); cpu_irq_enable();
printf("\r\nProgram started:\r\n"); printf("\r\nADK demo start\r\n");
delay(200); delay(200);
HostMouse.SetReportParser(0,(HIDReportParser*)&Prs);
} }
#define RCVSIZE 128
void loop() void loop()
{ {
uint8_t buf[RCVSIZE];
uint32_t nbread = 0;
char helloworld[] = "Hello World!\r\n";
Usb.Task(); 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 Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com e-mail : support@circuitsathome.com
*/ */
/* USB functions */ /* USB functions */
#include "Arduino.h" #include "Arduino.h"
@ -24,9 +25,9 @@ static uint32_t usb_error = 0;
static uint32_t usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; 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 // Set up state machine
usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; 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() void USBHost::init()
{ {
@ -44,8 +45,11 @@ void USBHost::init()
bmHubPre = 0; bmHubPre = 0;
} }
/** /**
* Get USB state. * \brief Get USBHost state.
*
* \return USB enumeration status (see USBHost::task).
*/ */
uint32_t USBHost::getUsbTaskState(void) 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) 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) 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++) for (uint32_t i = 0; i < p->epcount; i++)
{ {
if (pep->epAddr == ep) if (pep->deviceEpNum == ep)
return pep; return pep;
pep++; pep++;
} }
return NULL; return NULL;
} }
/** /**
* Set device end point entry. * \brief Set device endpoint entry.
* Each device is different and has different number of endpoints. *
* This function plugs endpoint record structure, defined in application to devtable. * \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) 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; 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); 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) if (!*ppep)
return USB_ERROR_EP_NOT_FOUND_IN_TBL; 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--; nak_limit--;
// Set peripheral address // Set peripheral address
//regWr( rPERADDR, addr ); TRACE_USBHOST(printf(" => SetAddress deviceEP=%lu configued as hostPIPE=%lu sending to address=%lu\r\n", ep, (*ppep)->hostPipeNum, addr);)
uhd_configure_address(ep, addr); uhd_configure_address((*ppep)->hostPipeNum, 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));
return 0; return 0;
} }
/** /**
* Send a control request. * \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. * Sets address, endpoint, fills control packet with necessary data, dispatches
* Actual requests are defined as inlines. * control packet, and initiates bulk IN transfer depending on request.
* *
* return codes: * \param addr USB device address.
* 00 = success * \param ep USB device endpoint number.
* 01-0f = non-zero HRSLT * \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) 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");) TRACE_USBHOST(printf(" => ctrlReq\r\n");)
// Set peripheral address // Set peripheral address
rcode = SetAddress(addr, ep, &pep, nak_limit); rcode = setPipeAddress(addr, ep, &pep, nak_limit);
if (rcode) if (rcode)
return rcode; return rcode;
// Allocate EP0 with default 8 bytes size if not already initialized // Allocate Pipe0 with default 64 bytes size if not already initialized
rcode = UHD_EP0_Alloc(0, 8); // 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) if (rcode)
{ {
TRACE_USBHOST(printf("/!\\ USBHost::ctrlReq : EP0 allocation error: %lu\r\n", 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 // Determine request direction
direction = (( bmReqType & 0x80 ) > 0); direction = ((bmReqType & 0x80 ) > 0);
// Fill in setup packet // Fill in setup packet
setup_pkt.ReqType_u.bmRequestType = bmReqType; 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; setup_pkt.wLength = total;
// Configure and write the setup packet into the FIFO // Configure and write the setup packet into the FIFO
//bytesWr(rSUDFIFO, 8, (uint8_t *)&setup_pkt);
uhd_configure_pipe_token(0, tokSETUP); 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 // Dispatch packet
rcode = dispatchPkt(tokSETUP, ep, nak_limit); rcode = dispatchPkt(tokSETUP, pep->hostPipeNum, nak_limit);
if (rcode) if (rcode)
{ {
// Return HRSLT if not zero // 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 else
{ {
// OUT transfer // OUT transfer
//devtable[addr].epinfo[ep].sndToggle = bmSNDTOG1;
TRACE_USBHOST(printf(" => ctrlData OUT\r\n");) TRACE_USBHOST(printf(" => ctrlData OUT\r\n");)
rcode = OutTransfer(pep, nak_limit, nbytes, dataptr); 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 // Status stage
return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit ); return dispatchPkt((direction) ? tokOUTHS : tokINHS, pep->hostPipeNum, nak_limit);
} }
/** /**
* IN transfer to arbitrary endpoint. * \brief Perform IN request to the specified USB device.
* Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. *
* Keep sending INs and writes data to memory area pointed by 'data'. * \note This function handles multiple packets (if necessary) and can
* rcode 0 if no errors * receive a maximum of 'nbytesptr' bytes. It keep sending INs and writes data
* 01-0f is relayed from dispatchPkt() * to memory area pointed by 'data'. The actual amount of received bytes is
* f0 means RCVDAVIRQ error * stored in 'nbytesptr'.
* fe USB xfer timeout *
* \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) uint32_t USBHost::inTransfer(uint32_t addr, uint32_t ep, uint32_t *nbytesptr, uint8_t* data)
{ {
EpInfo *pep = NULL; EpInfo *pep = NULL;
uint32_t nak_limit = 0; 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) if (rcode)
{
return rcode; return rcode;
}
return InTransfer(pep, nak_limit, nbytesptr, data); 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 rcode = 0;
uint32_t pktsize = 0; uint32_t pktsize = 0;
uint16_t nbytes = *nbytesptr; uint32_t nbytes = *nbytesptr;
uint8_t maxpktsize = pep->maxPktSize; uint32_t maxpktsize = pep->maxPktSize;
*nbytesptr = 0; *nbytesptr = 0;
// Set toggle value
//regWr(rHCTL, devtable[addr].epinfo[ep].rcvToggle);
while (1) while (1)
{ {
// Use a 'return' to exit this loop // Use a 'return' to exit this loop
// IN packet to EP-'endpoint'. Function takes care of NAKS. // 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) if (rcode)
{ {
// Should be 0, indicating ACK. Else return error code. if (rcode == 1)
return (rcode); {
// 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 // Number of received bytes
//pktsize = regRd(rRCVBC); pktsize = uhd_byte_count(pep->hostPipeNum);
//data = bytesRd(rRCVFIFO, pktsize, data);
pktsize = uhd_byte_count(pep->epAddr);
if (nbytes < pktsize) if (nbytes < pktsize)
printf("ce test n'a pas ete fait...\r\n"); {
data += UHD_EP_Read(pep->epAddr, pktsize, data); TRACE_USBHOST(printf("/!\\ USBHost::InTransfer : receive buffer is too small, size=%lu, expected=%lu\r\n", nbytes, pktsize);)
}
// Clear the IRQ & free the buffer data += UHD_Pipe_Read(pep->hostPipeNum, pktsize, data);
//regWr(rHIRQ, bmRCVDAVIRQ);
// Add this packet's byte count to total transfer length // Add this packet's byte count to total transfer length
*nbytesptr += pktsize; *nbytesptr += pktsize;
@ -312,155 +353,116 @@ uint32_t USBHost::InTransfer(EpInfo *pep, uint32_t nak_limit, uint32_t *nbytespt
// 2. 'nbytes' have been transferred. // 2. 'nbytes' have been transferred.
if ((pktsize < maxpktsize) || (*nbytesptr >= nbytes)) if ((pktsize < maxpktsize) || (*nbytesptr >= nbytes))
{ {
/*// Have we transferred 'nbytes' bytes? return 0;
if (regRd(rHRSL) & bmRCVTOGRD)
{
// Save toggle value
devtable[addr].epinfo[ep].rcvToggle = bmRCVTOG1;
}
else
{
devtable[addr].epinfo[ep].rcvToggle = bmRCVTOG0;
}*/
return (0);
} }
} }
} }
/** /**
* OUT transfer to arbitrary endpoint. * \brief Perform OUT request to the specified USB device.
* 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
* *
* 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) uint32_t USBHost::outTransfer(uint32_t addr, uint32_t ep, uint32_t nbytes, uint8_t* data)
{ {
/* EpInfo *pep = NULL;
EpInfo *pep = NULL; uint32_t nak_limit = 0;
uint16_t nak_limit;
uint8_t rcode = SetAddress(addr, ep, &pep, nak_limit); uint32_t rcode = setPipeAddress(addr, ep, &pep, nak_limit);
if (rcode) if (rcode)
{
return rcode; return rcode;
}
return OutTransfer(pep, nak_limit, nbytes, data); 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) uint32_t USBHost::OutTransfer(EpInfo *pep, uint32_t nak_limit, uint32_t nbytes, uint8_t *data)
{ {
/* uint32_t rcode = 0;
uint8_t rcode, retry_count; uint32_t bytes_tosend = 0;
uint8_t *data_p = data; //local copy of the data pointer uint32_t bytes_left = nbytes;
uint16_t bytes_tosend, nak_count; uint32_t maxpktsize = pep->maxPktSize;
uint16_t bytes_left = nbytes;
uint8_t maxpktsize = pep->maxPktSize; if (maxpktsize < 1)
if (maxpktsize < 1 || maxpktsize > 64)
return USB_ERROR_INVALID_MAX_PKT_SIZE; return USB_ERROR_INVALID_MAX_PKT_SIZE;
unsigned long timeout = millis() + USB_XFER_TIMEOUT; while (bytes_left)
regWr( rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0 ); //set toggle value
while( bytes_left )
{ {
retry_count = 0; bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left;
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 );
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 ) // Should be 0, indicating ACK. Else return error code.
{ return 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
// 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; bytes_left -= bytes_tosend;
data_p += bytes_tosend; data += bytes_tosend;
}//while( bytes_left... }
pep->bmSndToggle = ( regRd( rHRSL ) & bmSNDTOGRD ) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0; //update toggle
return( rcode ); //should be 0 in all cases // Should be 0 in all cases
*/ return rcode;
printf("j'ai beau etre matinal.. j'ai mal!!!! arggghh\r\n");
return 1;
} }
/** /**
* Dispatch USB packet. * \brief Dispatch USB packet.
* Assumes peripheral address is set and relevant buffer is loaded/empty. *
* \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, tries to re-send up to nak_limit times.
* If nak_limit == 0, do not count NAKs, exit after timeout. * 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, * \param token Token type (Setup, In or Out).
* else packet may be corrupted. * \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 timeout = millis() + USB_XFER_TIMEOUT;
uint32_t nak_count = 0; uint32_t nak_count = 0;
uint32_t rcode = USB_ERROR_TRANSFER_TIMEOUT; 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 // Launch the transfer
//regWr(rHXFR, (token | ep)); UHD_Pipe_Send(hostPipeNum, token);
UHD_EP_Send(ep, 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 // Wait for transfer completion
if (UHD_EP_Is_Transfer_Complete(ep, token)) if (UHD_Pipe_Is_Transfer_Complete(hostPipeNum, token))
{ {
return 0; return 0;
} }
// // Is NAK received?
if (Is_uhd_nak_received(ep)) if (Is_uhd_nak_received(hostPipeNum))
{ {
uhd_ack_nak_received(ep); uhd_ack_nak_received(hostPipeNum);
nak_count++; nak_count++;
if (nak_limit && (nak_count == nak_limit)) 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. * 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) 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; 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 case of an error devConfigIndex should be reset to 0
// in order to start from the very beginning the // in order to start from the very beginning the next time
// next time the program gets here // the program gets here
if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE)
devConfigIndex = 0; 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; devConfigIndex = 0;
rcode = DefaultAddressing(parent, port, lowspeed); rcode = DefaultAddressing(parent, port, lowspeed);
@ -510,6 +527,15 @@ uint32_t USBHost::Configuring(uint32_t parent, uint32_t port, uint32_t lowspeed)
return rcode; 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 USBHost::DefaultAddressing(uint32_t parent, uint32_t port, uint32_t lowspeed)
{ {
uint32_t rcode = 0; uint32_t rcode = 0;
@ -553,30 +579,70 @@ uint32_t USBHost::DefaultAddressing(uint32_t parent, uint32_t port, uint32_t low
return 0; 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) uint32_t USBHost::ReleaseDevice(uint32_t addr)
{ {
if (!addr) if (!addr)
return 0; return 0;
for (uint32_t i = 0; i < USB_NUMDEVICES; ++i) for (uint32_t i = 0; i < USB_NUMDEVICES; ++i)
{
if (devConfig[i]->GetAddress() == addr) if (devConfig[i]->GetAddress() == addr)
{
return devConfig[i]->Release(); return devConfig[i]->Release();
}
}
return 0; 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) 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)); 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) 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)); 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) uint32_t USBHost::getConfDescr(uint32_t addr, uint32_t ep, uint32_t conf, USBReadParser *p)
{ {
const uint32_t bufSize = 64; 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)); 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) 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); 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) 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)); return (ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, 0x0000, 0, 0));
} }
/** /**
* USB main task. * \brief USB main task, responsible for enumeration and clean up stage.
* Performs enumeration/cleanup. *
* \note Must be periodically called from loop().
*/ */
void USBHost::Task(void) void USBHost::Task(void)
{ {
@ -648,16 +742,17 @@ void USBHost::Task(void)
{ {
delay = millis() + USB_SETTLE_DELAY; delay = millis() + USB_SETTLE_DELAY;
usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; 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; break;
} }
// Poll connected devices (if required)
for (uint32_t i = 0; i < USB_NUMDEVICES; ++i) for (uint32_t i = 0; i < USB_NUMDEVICES; ++i)
if (devConfig[i]) if (devConfig[i])
rcode = devConfig[i]->Poll(); rcode = devConfig[i]->Poll();
// USB state machine // Perform USB enumeration stage and clean up
switch (usb_task_state) switch (usb_task_state)
{ {
case USB_DETACHED_SUBSTATE_INITIALIZE: case USB_DETACHED_SUBSTATE_INITIALIZE:
@ -667,6 +762,7 @@ void USBHost::Task(void)
UHD_Init(); UHD_Init();
init(); init();
// Free all USB resources
for (uint32_t i = 0; i < USB_NUMDEVICES; ++i) for (uint32_t i = 0; i < USB_NUMDEVICES; ++i)
if (devConfig[i]) if (devConfig[i])
rcode = devConfig[i]->Release(); rcode = devConfig[i]->Release();

View File

@ -19,8 +19,8 @@ e-mail : support@circuitsathome.com
#ifndef USB_H_INCLUDED #ifndef USB_H_INCLUDED
#define USB_H_INCLUDED #define USB_H_INCLUDED
#define TRACE_USBHOST(x) x //#define TRACE_USBHOST(x) x
//#define TRACE_USBHOST(x) #define TRACE_USBHOST(x)
#include <stdint.h> #include <stdint.h>
#include "usb_ch9.h" #include "usb_ch9.h"
@ -120,7 +120,11 @@ typedef struct
uint16_t wLength; // 6 Depends on bRequest uint16_t wLength; // 6 Depends on bRequest
} SETUP_PKT, *PSETUP_PKT; } SETUP_PKT, *PSETUP_PKT;
// Base class for incoming data parser /**
* \class USBReadParser
*
* \brief Base class used for USB descriptor parsing.
*/
class USBReadParser class USBReadParser
{ {
public: public:
@ -128,21 +132,30 @@ public:
}; };
/** /**
* USBDeviceConfig class. * \class USBDeviceConfig
*
* \brief Device configuration class used for managing device life cycle.
*/ */
class USBDeviceConfig class USBDeviceConfig
{ {
public: public:
//! @brief Perform final enumeration stage.
virtual uint32_t Init(uint32_t parent, uint32_t port, uint32_t lowspeed) = 0; 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; 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; virtual uint32_t Poll() = 0;
//! @brief Retrieve USB device address.
virtual uint32_t GetAddress() = 0; virtual uint32_t GetAddress() = 0;
}; };
/** /**
* USBHost Class. * \class USBHost
* 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. * \brief Main USB host class.
*/ */
class USBHost 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 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 nbytes, uint32_t conf, uint8_t* dataptr);
uint32_t getConfDescr(uint32_t addr, uint32_t ep, uint32_t conf, USBReadParser *p); 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 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 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, 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: private:
void init(); 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 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); 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_NOWAIT 1 //Single NAK stops transfer
#define USB_NAK_NONAK 0 //Do not count NAKs, stop retrying after USB Timeout #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 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 uint32_t maxPktSize; // Maximum packet size
union union
@ -45,15 +58,20 @@ struct EpInfo
}; };
}; };
// 7 6 5 4 3 2 1 0 /**
// --------------------------------- * \brief USB device address definition.
// | | H | P | P | P | A | A | A | *
// --------------------------------- * \note The 8 bits USB address is defined like this:
// *
// H - if 1 the address is a hub address * 7 6 5 4 3 2 1 0
// P - parent hub address * ---------------------------------
// A - device address / port number in case of hub * | | 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 struct UsbDeviceAddress
{ {
union union
@ -73,6 +91,14 @@ struct UsbDeviceAddress
#define bmUSB_DEV_ADDR_PARENT 0x38 #define bmUSB_DEV_ADDR_PARENT 0x38
#define bmUSB_DEV_ADDR_HUB 0x40 #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 struct UsbDevice
{ {
EpInfo *epinfo; // endpoint info pointer EpInfo *epinfo; // endpoint info pointer
@ -81,6 +107,9 @@ struct UsbDevice
uint32_t lowspeed; // indicates if a device is the low speed one uint32_t lowspeed; // indicates if a device is the low speed one
}; };
/**
* \class Abstract AddressPool definition.
*/
class AddressPool class AddressPool
{ {
public: public:
@ -94,28 +123,46 @@ typedef void (*UsbDeviceHandleFunc)(UsbDevice *pdev);
#define ADDR_ERROR_INVALID_INDEX 0xFF #define ADDR_ERROR_INVALID_INDEX 0xFF
#define ADDR_ERROR_INVALID_ADDRESS 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> template <const uint32_t MAX_DEVICES_ALLOWED>
class AddressPoolImpl : public AddressPool class AddressPoolImpl : public AddressPool
{ {
private: 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 uint32_t hubCounter; // hub counter is kept
// in order to avoid hub address duplication // in order to avoid hub address duplication
UsbDevice thePool[MAX_DEVICES_ALLOWED]; 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) void InitEntry(uint32_t index)
{ {
thePool[index].address = 0; thePool[index].address = 0;
thePool[index].epcount = 1; thePool[index].epcount = 1;
thePool[index].lowspeed = 0; 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) uint32_t FindAddressIndex(uint32_t address = 0)
{ {
for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
@ -126,21 +173,45 @@ private:
return 0; 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) 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) if (((UsbDeviceAddress*)&thePool[i].address)->bmParent == addr.bmAddress)
return i; return i;
} }
return 0; 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) 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) if (index == 0)
return; return;
@ -154,10 +225,13 @@ private:
if (hubCounter == ((UsbDeviceAddress*)&thePool[index].address)->bmAddress) if (hubCounter == ((UsbDeviceAddress*)&thePool[index].address)->bmAddress)
hubCounter --; hubCounter --;
} }
InitEntry(index); InitEntry(index);
} }
// Initializes the whole address pool at once /**
* \brief Initialize all address poll entries at once.
*/
void InitAllAddresses() void InitAllAddresses()
{ {
for (uint32_t i = 1; i < MAX_DEVICES_ALLOWED; i++) for (uint32_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
@ -167,6 +241,9 @@ private:
}; };
public: public:
/**
* \brief AddressPoolImpl class constructor.
*/
AddressPoolImpl() : hubCounter(0) AddressPoolImpl() : hubCounter(0)
{ {
// Init address zero (reserved) // Init address zero (reserved)
@ -176,11 +253,18 @@ public:
InitAllAddresses(); InitAllAddresses();
// Configure ep0 used for enumeration // Configure ep0 used for enumeration
dev0ep.epAddr = 0; dev0ep.deviceEpNum = 0;
dev0ep.maxPktSize = 8; 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) virtual UsbDevice* GetUsbDevicePtr(uint32_t addr)
{ {
if (!addr) if (!addr)
@ -191,7 +275,12 @@ public:
return (!index) ? 0 : (thePool + index); 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) void ForEachUsbDevice(UsbDeviceHandleFunc pfunc)
{ {
if (!pfunc) if (!pfunc)
@ -202,7 +291,18 @@ public:
pfunc(thePool + i); 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) virtual uint32_t AllocAddress(uint32_t parent, uint32_t is_hub = 0, uint32_t port = 0)
{ {
if (parent > 127 || port > 7) if (parent > 127 || port > 7)
@ -223,7 +323,7 @@ public:
if (is_hub) if (is_hub)
{ {
thePool[index].address = 0x41; thePool[index].address = 0x41;
hubCounter ++; hubCounter++;
} }
else else
thePool[index].address = 1; thePool[index].address = 1;
@ -238,52 +338,51 @@ public:
if (is_hub) if (is_hub)
{ {
addr.bmHub = 1; addr.bmHub = 1;
addr.bmAddress = ++hubCounter; addr.bmAddress = hubCounter++;
} }
else else
{ {
addr.bmHub = 0; addr.bmHub = 0;
addr.bmAddress = port; addr.bmAddress = port;
} }
thePool[index].address = *((uint8_t*)&addr); 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; 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) 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) if (addr == 0x41)
{ {
InitAllAddresses(); InitAllAddresses();
return; return;
} }
uint32_t index = FindAddressIndex(addr); uint32_t index = FindAddressIndex(addr);
FreeAddressByIndex(index); FreeAddressByIndex(index);
}; };
// Returns number of hubs attached // 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. // 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; 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); if (thePool[i].address != 0);
counter ++; counter++;
return 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 <stdint.h>
#include "parsetools.h" #include "parsetools.h"
/**
* \class Abstract UsbConfigXtracter definition.
*
* \note This class is used for extracting USB endpoint descriptors.
*/
class UsbConfigXtracter class UsbConfigXtracter
{ {
public: public:
@ -32,7 +37,11 @@ public:
#define CP_MASK_COMPARE_PROTOCOL 4 #define CP_MASK_COMPARE_PROTOCOL 4
#define CP_MASK_COMPARE_ALL 7 #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> template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
class ConfigDescParser : public USBReadParser class ConfigDescParser : public USBReadParser
{ {
@ -62,6 +71,18 @@ public:
virtual void Parse(const uint32_t len, const uint8_t *pbuf, const uint32_t &offset); 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> 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) : ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ConfigDescParser(UsbConfigXtracter *xtractor) :
theXtractor(xtractor), theXtractor(xtractor),
@ -74,18 +95,37 @@ ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ConfigDescParser(Usb
theSkipper.Initialize(&theBuffer); 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> 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) 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; uint32_t cntdn = len;
uint8_t *p = (uint8_t*)pbuf; uint8_t *p = (uint8_t*)pbuf;
while(cntdn) while (cntdn)
if (!ParseDescriptor(&p, &cntdn)) if (!ParseDescriptor(&p, &cntdn))
return; 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> 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) 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: case 3:
switch (dscrType) switch (dscrType)
{ {
case USB_DESCRIPTOR_INTERFACE: case USB_DESCRIPTOR_INTERFACE:
isGoodInterface = false; isGoodInterface = false;
case USB_DESCRIPTOR_CONFIGURATION: case USB_DESCRIPTOR_CONFIGURATION:
theBuffer.valueSize = sizeof(USB_CONFIGURATION_DESCRIPTOR) - 2; theBuffer.valueSize = sizeof(USB_CONFIGURATION_DESCRIPTOR) - 2;
break; break;
case USB_DESCRIPTOR_ENDPOINT: case USB_DESCRIPTOR_ENDPOINT:
theBuffer.valueSize = sizeof(USB_ENDPOINT_DESCRIPTOR) - 2; theBuffer.valueSize = sizeof(USB_ENDPOINT_DESCRIPTOR) - 2;
break; break;
case HID_DESCRIPTOR_HID: case HID_DESCRIPTOR_HID:
theBuffer.valueSize = dscrLen - 2; theBuffer.valueSize = dscrLen - 2;
break; break;
} }
valParser.Initialize(&theBuffer); valParser.Initialize(&theBuffer);
stateParseDescr = 4; stateParseDescr = 4;
case 4: case 4:
switch (dscrType) switch (dscrType)
{ {
case USB_DESCRIPTOR_CONFIGURATION: case USB_DESCRIPTOR_CONFIGURATION:
if (!valParser.Parse(pp, pcntdn)) if (!valParser.Parse(pp, pcntdn))
return false; return false;
confValue = ((USB_CONFIGURATION_DESCRIPTOR*)varBuffer)->bConfigurationValue; 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; 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; isGoodInterface = true;
ifaceNumber = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceNumber; ifaceNumber = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceNumber;
ifaceAltSet = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bAlternateSetting; ifaceAltSet = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bAlternateSetting;
protoValue = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceProtocol; protoValue = ((USB_INTERFACE_DESCRIPTOR*)varBuffer)->bInterfaceProtocol;
break; break;
case USB_DESCRIPTOR_ENDPOINT: case USB_DESCRIPTOR_ENDPOINT:
if (!valParser.Parse(pp, pcntdn)) if (!valParser.Parse(pp, pcntdn))
return false; return false;
if (isGoodInterface) if (isGoodInterface)
if (theXtractor) if (theXtractor)
theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer); theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer);
break; break;
//case HID_DESCRIPTOR_HID: //case HID_DESCRIPTOR_HID:
// if (!valParser.Parse(pp, pcntdn)) // if (!valParser.Parse(pp, pcntdn))
// return false; // return false;
// PrintHidDescriptor((const USB_HID_DESCRIPTOR*)varBuffer); // PrintHidDescriptor((const USB_HID_DESCRIPTOR*)varBuffer);
// break; // break;
default: default:
if (!theSkipper.Skip(pp, pcntdn, dscrLen-2)) if (!theSkipper.Skip(pp, pcntdn, dscrLen - 2))
return false; return false;
} }
theBuffer.pValue = varBuffer; theBuffer.pValue = varBuffer;
stateParseDescr = 0; stateParseDescr = 0;
} }
return true; 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> 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) void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc)
{ {
/*Notify(PSTR("\r\n\r\nHID Descriptor:\r\n")); TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bDescLength: %d\r\n", pDesc->bLength);)
Notify(PSTR("bDescLength:\t\t")); TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bDescriptorType: %d\r\n", pDesc->bDescriptorType);)
PrintHex<uint8_t>(pDesc->bLength); 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")); for (uint32_t i = 0; i < pDesc->bNumDescriptors; ++i)
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++)
{ {
HID_CLASS_DESCRIPTOR_LEN_AND_TYPE *pLT = (HID_CLASS_DESCRIPTOR_LEN_AND_TYPE*)&(pDesc->bDescrType); HID_CLASS_DESCRIPTOR_LEN_AND_TYPE *pLT = (HID_CLASS_DESCRIPTOR_LEN_AND_TYPE*)&(pDesc->bDescrType);
Notify(PSTR("\r\nbDescrType:\t\t")); TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : bDescrType: %d\r\n", pLT[i].bDescrType);)
PrintHex<uint8_t>(pLT[i].bDescrType); TRACE_USBHOST(printf("ConfigDescParser::PrintHidDescriptor : wDescriptorLength: %d\r\n", pLT[i].wDescriptorLength);)
Notify(PSTR("\r\nwDescriptorLength:\t"));
PrintHex<uint16_t>(pLT[i].wDescriptorLength);
} }
Notify(PSTR("\r\n"));*/
printf("somebody save me!!!! orgazmo?!!!\r\n");
} }
#endif /* CONFDESCPARSER_H_INCLUDED */ #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_KEYBOARD 0x01
#define HID_PROTOCOL_MOUSE 0x02 #define HID_PROTOCOL_MOUSE 0x02
struct HidItemPrefix /**
* \brief HidItemPrefix definition.
*/
struct HidItemPrefix // Not used
{ {
uint8_t bSize : 2; uint8_t bSize : 2;
uint8_t bType : 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_SWITCH 5
#define HID_MAIN_ITEM_COLLECTION_USAGE_MODIFIER 6 #define HID_MAIN_ITEM_COLLECTION_USAGE_MODIFIER 6
struct MainItemIOFeature /**
* \brief MainItemIOFeature definition.
*/
struct MainItemIOFeature // Not used
{ {
uint8_t bmIsConstantOrData : 1; uint8_t bmIsConstantOrData : 1;
uint8_t bmIsArrayOrVariable : 1; uint8_t bmIsArrayOrVariable : 1;
@ -139,6 +145,11 @@ struct MainItemIOFeature
class HID; class HID;
/**
* \class Abstract HIDReportParser definition.
*
* \note This class is used to implement HID report parsing.
*/
class HIDReportParser class HIDReportParser
{ {
public: public:
@ -148,6 +159,9 @@ public:
#define MAX_REPORT_PARSERS 2 #define MAX_REPORT_PARSERS 2
#define HID_MAX_HID_CLASS_DESCRIPTORS 5 #define HID_MAX_HID_CLASS_DESCRIPTORS 5
/**
* \class HID definition.
*/
class HID : public USBDeviceConfig, public UsbConfigXtracter class HID : public USBDeviceConfig, public UsbConfigXtracter
{ {
protected: protected:

View File

@ -17,7 +17,14 @@ e-mail : support@circuitsathome.com
#include "hid.h" #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) uint32_t HID::GetReportDescr(uint32_t ep, USBReadParser *parser)
{ {
const uint32_t constBufLen = 64; 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)); 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) 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)); 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) 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)); 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) 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)); 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) 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)); 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) /**
{ * \brief Print HID descriptor.
/*Notify(PSTR("Endpoint descriptor:")); *
Notify(PSTR("\r\nLength:\t\t")); * \note TRACE_USBHOST macro must be enabled. See Usb.h for reference.
PrintHex<uint8_t>(ep_ptr->bLength); *
Notify(PSTR("\r\nType:\t\t")); * \param pDesc Pointer to HID descriptor.
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);*/
}
void HID::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) void HID::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc)
{ {
/* Notify(PSTR("\r\n\r\nHID Descriptor:\r\n")); TRACE_USBHOST(printf("HID::PrintHidDescriptor : bDescLength: %d\r\n", pDesc->bLength);)
Notify(PSTR("bDescLength:\t\t")); TRACE_USBHOST(printf("HID::PrintHidDescriptor : bDescriptorType: %d\r\n", pDesc->bDescriptorType);)
PrintHex<uint8_t>(pDesc->bLength); TRACE_USBHOST(printf("HID::PrintHidDescriptor : bcdHID: %d\r\n", pDesc->bcdHID);)
TRACE_USBHOST(printf("HID::PrintHidDescriptor : bCountryCode: %d\r\n", pDesc->bCountryCode);)
Notify(PSTR("\r\nbDescriptorType:\t")); TRACE_USBHOST(printf("HID::PrintHidDescriptor : bNumDescriptors: %d\r\n", pDesc->bNumDescriptors);)
PrintHex<uint8_t>(pDesc->bDescriptorType); TRACE_USBHOST(printf("HID::PrintHidDescriptor : bDescrType: %d\r\n", pDesc->bDescrType);)
TRACE_USBHOST(printf("HID::PrintHidDescriptor : wDescriptorLength: %d\r\n", pDesc->wDescriptorLength);)
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);*/
} }

View File

@ -17,6 +17,14 @@ e-mail : support@circuitsathome.com
#include "hidboot.h" #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) void MouseReportParser::Parse(HID *hid, bool is_rpt_id, uint32_t len, uint8_t *buf)
{ {
MOUSEINFO *pmi = (MOUSEINFO*)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]; 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) void KeyboardReportParser::Parse(HID *hid, bool is_rpt_id, uint32_t len, uint8_t *buf)
{ {
// On error - return // 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]; 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 KeyboardReportParser::HandleLockingKeys(HID *hid, uint8_t key)
{ {
uint8_t old_keys = kbdLockingKeys.bLeds; uint8_t old_keys = kbdLockingKeys.bLeds;
@ -109,6 +134,14 @@ const uint8_t KeyboardReportParser::symKeysUp[] = { '_', '+', '{', '}', '|', '~'
const uint8_t KeyboardReportParser::symKeysLo[] = { '-', '=', '[', ']', '\\', ' ', ';', '\'', '`', ',', '.', '/' }; const uint8_t KeyboardReportParser::symKeysLo[] = { '-', '=', '[', ']', '\\', ' ', ';', '\'', '`', ',', '.', '/' };
const uint8_t KeyboardReportParser::padKeys[] = { '/', '*', '-', '+', 0x13 }; 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 KeyboardReportParser::OemToAscii(uint8_t mod, uint8_t key)
{ {
uint8_t shift = (mod & 0x22); 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); case KEY_PERIOD: return ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '.' : 0);
} }
} }
return 0; return 0;
} }

View File

@ -31,6 +31,9 @@ e-mail : support@circuitsathome.com
#define KEY_ENTER 0x28 #define KEY_ENTER 0x28
#define KEY_PERIOD 0x63 #define KEY_PERIOD 0x63
/**
* \brief MOUSEINFO definition.
*/
struct MOUSEINFO struct MOUSEINFO
{ {
struct struct
@ -44,6 +47,9 @@ struct MOUSEINFO
int8_t dY; int8_t dY;
}; };
/**
* \class MouseReportParser definition.
*/
class MouseReportParser : public HIDReportParser class MouseReportParser : public HIDReportParser
{ {
union union
@ -65,6 +71,9 @@ protected:
virtual void OnMiddleButtonDown (MOUSEINFO *mi) {}; virtual void OnMiddleButtonDown (MOUSEINFO *mi) {};
}; };
/**
* \brief MODIFIERKEYS definition.
*/
struct MODIFIERKEYS struct MODIFIERKEYS
{ {
uint8_t bmLeftCtrl : 1; uint8_t bmLeftCtrl : 1;
@ -77,6 +86,9 @@ struct MODIFIERKEYS
uint8_t bmRightGUI : 1; uint8_t bmRightGUI : 1;
}; };
/**
* \brief KBDINFO definition.
*/
struct KBDINFO struct KBDINFO
{ {
struct struct
@ -94,6 +106,9 @@ struct KBDINFO
uint8_t Keys[6]; uint8_t Keys[6];
}; };
/**
* \brief KBDLEDS definition.
*/
struct KBDLEDS struct KBDLEDS
{ {
uint8_t bmNumLock : 1; uint8_t bmNumLock : 1;
@ -105,9 +120,18 @@ struct KBDLEDS
}; };
#define KEY_NUM_LOCK 0x53 #define KEY_NUM_LOCK 0x53
// Clear compiler warning
#ifdef KEY_CAPS_LOCK
#undef KEY_CAPS_LOCK
#endif
#define KEY_CAPS_LOCK 0x39 #define KEY_CAPS_LOCK 0x39
#define KEY_SCROLL_LOCK 0x47 #define KEY_SCROLL_LOCK 0x47
/**
* \class KeyboardReportParser definition.
*/
class KeyboardReportParser : public HIDReportParser class KeyboardReportParser : public HIDReportParser
{ {
static const uint8_t numKeys[]; static const uint8_t numKeys[];
@ -146,6 +170,9 @@ protected:
#define HID_MAX_HID_CLASS_DESCRIPTORS 5 #define HID_MAX_HID_CLASS_DESCRIPTORS 5
/**
* \class HIDBoot definition.
*/
template <const uint8_t BOOT_PROTOCOL> template <const uint8_t BOOT_PROTOCOL>
class HIDBoot : public HID class HIDBoot : public HID
{ {
@ -167,7 +194,7 @@ class HIDBoot : public HID
public: public:
HIDBoot(USBHost *p); 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 // USBDeviceConfig implementation
virtual uint32_t Init(uint32_t parent, uint32_t port, uint32_t lowspeed); 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); 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> template <const uint8_t BOOT_PROTOCOL>
HIDBoot<BOOT_PROTOCOL>::HIDBoot(USBHost *p) : HIDBoot<BOOT_PROTOCOL>::HIDBoot(USBHost *p) :
HID(p), HID(p),
@ -192,21 +222,35 @@ HIDBoot<BOOT_PROTOCOL>::HIDBoot(USBHost *p) :
pUsb->RegisterDeviceClass(this); pUsb->RegisterDeviceClass(this);
} }
/**
* \brief Initialize HIDBoot class.
*/
template <const uint8_t BOOT_PROTOCOL> template <const uint8_t BOOT_PROTOCOL>
void HIDBoot<BOOT_PROTOCOL>::Initialize() 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].maxPktSize = (i) ? 0 : 8;
epInfo[i].epAttribs = 0; epInfo[i].epAttribs = 0;
epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
} }
bNumEP = 1; bNumEP = 1;
bNumIface = 0; bNumIface = 0;
bConfNum = 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> template <const uint8_t BOOT_PROTOCOL>
uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t lowspeed) 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]; uint8_t buf[constBufSize];
uint32_t rcode = 0; uint32_t rcode = 0;
UsbDevice *p = NULL; UsbDevice *p = 0;
EpInfo *oldep_ptr = NULL; EpInfo *oldep_ptr = 0;
uint32_t len = 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(); AddressPool &addrPool = pUsb->GetAddressPool();
TRACE_USBHOST(printf("HIDBoot::Init\r\n");)
if (bAddress) if (bAddress)
return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; 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; p->lowspeed = lowspeed;
// Get device descriptor // 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]; len = (buf[0] > constBufSize) ? constBufSize : buf[0];
if (rcode) 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) for (uint32_t i = 0; i < num_of_conf; ++i)
{ {
//HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;
ConfigDescParser< ConfigDescParser<
USB_CLASS_HID, USB_CLASS_HID,
HID_BOOT_INTF_SUBCLASS, HID_BOOT_INTF_SUBCLASS,
BOOT_PROTOCOL, 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); rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
if (bNumEP > 1) if (bNumEP > 1)
@ -364,31 +408,41 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t l
return 0; return 0;
FailGetDevDescr: FailGetDevDescr:
//USBTRACE("getDevDescr:"); TRACE_USBHOST(printf("HIDBoot::Init getDevDescr : ");)
goto Fail; goto Fail;
FailSetDevTblEntry: FailSetDevTblEntry:
//USBTRACE("setDevTblEn:"); TRACE_USBHOST(printf("HIDBoot::Init setDevTblEn : ");)
goto Fail; goto Fail;
FailSetProtocol: FailSetProtocol:
//USBTRACE("SetProto:"); TRACE_USBHOST(printf("HIDBoot::Init SetProto : ");)
goto Fail; goto Fail;
FailSetIdle: FailSetIdle:
//USBTRACE("SetIdle:"); TRACE_USBHOST(printf("HIDBoot::Init SetIdle : ");)
goto Fail; goto Fail;
FailSetConfDescr: FailSetConfDescr:
//USBTRACE("setConf:"); TRACE_USBHOST(printf("HIDBoot::Init setConf : ");)
goto Fail; goto Fail;
Fail: Fail:
//Serial.println(rcode, HEX); TRACE_USBHOST(printf("error code: %lu\r\n", rcode);)
Release(); Release();
return rcode; 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> 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) 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) if (bNumEP > 1 && conf != bConfNum)
return; return;
//ErrorMessage<uint8_t>(PSTR("\r\nConf.Val"), conf); bConfNum = conf;
//ErrorMessage<uint8_t>(PSTR("Iface Num"), iface); bIfaceNum = iface;
//ErrorMessage<uint8_t>(PSTR("Alt.Set"), alt);
bConfNum = conf; uint32_t index = 0;
bIfaceNum = iface; uint32_t pipe = 0;
uint32_t index;
if ((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) if ((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80)
{ {
index = epInterruptInIndex; index = epInterruptInIndex;
// Fill in the endpoint info structure // 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].maxPktSize = (uint8_t)pep->wMaxPacketSize;
epInfo[index].epAttribs = 0; epInfo[index].epAttribs = 0;
bNumEP++;
TRACE_USBHOST(printf("HIDBoot::EndpointXtract : Found new endpoint\r\n");) 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 : maxPktSize: %lu\r\n", epInfo[index].maxPktSize);)
TRACE_USBHOST(printf("HIDBoot::EndpointXtract : index: %lu\r\n", index);) 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> template <const uint8_t BOOT_PROTOCOL>
uint32_t HIDBoot<BOOT_PROTOCOL>::Release() uint32_t HIDBoot<BOOT_PROTOCOL>::Release()
{ {
// Free allocated host pipes
UHD_Pipe_Free(epInfo[epInterruptInIndex].hostPipeNum);
// Free allocated USB address
pUsb->GetAddressPool().FreeAddress(bAddress); pUsb->GetAddressPool().FreeAddress(bAddress);
bConfNum = 0; bConfNum = 0;
@ -439,9 +508,17 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Release()
bAddress = 0; bAddress = 0;
qNextPollTime = 0; qNextPollTime = 0;
bPollEnable = false; bPollEnable = false;
return 0; 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> template <const uint8_t BOOT_PROTOCOL>
uint32_t HIDBoot<BOOT_PROTOCOL>::Poll() uint32_t HIDBoot<BOOT_PROTOCOL>::Poll()
{ {
@ -459,12 +536,10 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Poll()
uint32_t read = epInfo[epInterruptInIndex].maxPktSize; 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)
{ {
//if (rcode != hrNAK)
//USBTRACE2("Poll:", rcode);
return 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_ENDPOINT_STALL 0 // Endpoint recipient
#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // Device recipient #define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // Device recipient
#define USB_FEATURE_TEST_MODE 2 // Device recipient #define USB_FEATURE_TEST_MODE 2 // Device recipient
_Pragma("pack(1)") _Pragma("pack(1)")
/* descriptor data structures */ /* descriptor data structures */
@ -147,7 +148,7 @@ typedef struct
uint8_t bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT). uint8_t bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT).
uint8_t bEndpointAddress; // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN). uint8_t bEndpointAddress; // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN).
uint8_t bmAttributes; // Endpoint transfer type. 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. uint8_t bInterval; // Polling interval in frames.
} USB_ENDPOINT_DESCRIPTOR; } USB_ENDPOINT_DESCRIPTOR;

View File

@ -55,17 +55,16 @@ typedef enum {
//extern uhd_speed_t uhd_get_speed(void); //extern uhd_speed_t uhd_get_speed(void);
extern void UHD_SetStack(void (*pf_isr)(void)); extern void UHD_SetStack(void (*pf_isr)(void));
extern void UHD_Init(void); extern void UHD_Init(void);
extern void UHD_BusReset(void); extern void UHD_BusReset(void);
extern uhd_vbus_state_t UHD_GetVBUSState(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_Pipe0_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 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_EP_Free(uint32_t add, uint32_t endp); extern void UHD_Pipe_Free(uint32_t ul_pipe);
extern uint32_t UHD_EP_Read(uint32_t ul_ep, uint32_t ul_size, uint8_t* data); extern uint32_t UHD_Pipe_Read(uint32_t ul_pipe, 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_Pipe_Write(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data);
extern void UHD_EP_Send(uint32_t ul_ep, uint32_t ul_token_type); extern void UHD_Pipe_Send(uint32_t ul_pipe, 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_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type);
#endif /* USB_HOST_H_INCLUDED */ #endif /* USB_HOST_H_INCLUDED */

View File

@ -32,13 +32,17 @@
#if SAM3XA_SERIES #if SAM3XA_SERIES
#define TRACE_UOTGHS_HOST(x) x //#define TRACE_UOTGHS_HOST(x) x
//#define TRACE_UOTGHS_HOST(x) #define TRACE_UOTGHS_HOST(x)
extern void (*gpf_isr)(void); extern void (*gpf_isr)(void);
// Handle UOTGHS Host driver state
static uhd_vbus_state_t uhd_state = UHD_STATE_NO_VBUS; 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) static void UHD_ISR(void)
{ {
// Manage dis/connection event // Manage dis/connection event
@ -74,7 +78,7 @@ static void UHD_ISR(void)
{ {
TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS error INT\r\n");) TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS error INT\r\n");)
uhd_ack_vbus_error_interrupt(); uhd_ack_vbus_error_interrupt();
uhd_state = UHD_STATE_ERROR; uhd_state = UHD_STATE_DISCONNECTED; //UHD_STATE_ERROR;
return; 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)) void UHD_SetStack(void (*pf_isr)(void))
{ {
gpf_isr = pf_isr; gpf_isr = pf_isr;
} }
/**
* \brief Initialize the UOTGHS host driver.
*/
void UHD_Init(void) void UHD_Init(void)
{ {
irqflags_t flags; irqflags_t flags;
@ -178,7 +190,7 @@ void UHD_Init(void)
// Enable main control interrupt // Enable main control interrupt
// Connection, SOF and reset // 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(); otg_freeze_clock();
@ -187,11 +199,19 @@ void UHD_Init(void)
cpu_irq_restore(flags); cpu_irq_restore(flags);
} }
/**
* \brief Trigger a USB bus reset.
*/
void UHD_BusReset(void) void UHD_BusReset(void)
{ {
uhd_start_reset(); uhd_start_reset();
} }
/**
* \brief Get VBUS state.
*
* \return VBUS status.
*/
uhd_vbus_state_t UHD_GetVBUSState(void) uhd_vbus_state_t UHD_GetVBUSState(void)
{ {
return uhd_state; return uhd_state;
@ -224,7 +244,7 @@ uhd_vbus_state_t UHD_GetVBUSState(void)
* \retval 0 success. * \retval 0 success.
* \retval 1 error. * \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) 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. * \note UOTGHS maximum pipe number is limited to 10, meaning that only a limited
* \param ul_ep_size Actual size of the FIFO in bytes. * 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. * \param ul_dev_addr Address of remote device.
* \retval 1 error. * \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;
/* for (ul_pipe = 1; ul_pipe < UOTGHS_EPT_NUM; ++ul_pipe)
* 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))
{ {
// Pipe is already allocated if (Is_uhd_pipe_enabled(ul_pipe))
return 0; {
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; 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 // Unalloc pipe
for (uint8_t pipe = 0; pipe < UOTGHS_EPT_NUM; pipe++) uhd_disable_pipe(ul_pipe);
{ uhd_unallocate_memory(ul_pipe);
if (!Is_uhd_pipe_enabled(pipe)) uhd_reset_pipe(ul_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);
}
} }
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 *ptr_ep_data = 0;
uint8_t nb_byte_received = 0; uint8_t nb_byte_received = 0;
uint32_t ul_nb_trans = 0; uint32_t ul_nb_trans = 0;
// Get information to read data // 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 // Copy data from pipe to payload buffer
while (ul_size && nb_byte_received) { 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; 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; volatile uint8_t *ptr_ep_data = 0;
uint32_t i = 0; uint32_t i = 0;
// Check pipe // Check pipe
if (!Is_uhd_pipe_enabled(ul_ep)) if (!Is_uhd_pipe_enabled(ul_pipe))
{ {
// Endpoint not valid // Endpoint not valid
TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP_Send : pipe is not enabled!\r\n");) TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP_Send : pipe is not enabled!\r\n");)
return; 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) for (i = 0; i < ul_size; ++i)
*ptr_ep_data++ = *data++; *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 // Check pipe
if (!Is_uhd_pipe_enabled(ul_ep)) if (!Is_uhd_pipe_enabled(ul_pipe))
{ {
// Endpoint not valid // 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; return;
} }
// Set token type for zero length packet // Set token type for zero length packet
// When actually using the FIFO, pipe token MUST be configured first // 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 // Clear interrupt flags
uhd_ack_setup_ready(ul_ep); uhd_ack_setup_ready(ul_pipe);
uhd_ack_in_received(ul_ep); uhd_ack_in_received(ul_pipe);
uhd_ack_out_ready(ul_ep); uhd_ack_out_ready(ul_pipe);
uhd_ack_short_packet(ul_ep); uhd_ack_short_packet(ul_pipe);
uhd_ack_nak_received(ul_pipe);
// Send actual packet // Send actual packet
uhd_ack_fifocon(ul_ep); uhd_ack_fifocon(ul_pipe);
uhd_unfreeze_pipe(ul_ep); 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_pipe Pipe number.
* \param ul_ep_size Actual size of the FIFO in bytes. * \param ul_token_type Token type.
* *
* \retval 0 transfer is not complete. * \retval 0 transfer is not complete.
* \retval 1 transfer is 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 // Check for transfer completion depending on token type
switch (ul_token_type) switch (ul_token_type)
{ {
case UOTGHS_HSTPIPCFG_PTOKEN_SETUP: 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_freeze_pipe(ul_pipe);
uhd_ack_setup_ready(ul_ep); uhd_ack_setup_ready(ul_pipe);
return 1; return 1;
} }
case UOTGHS_HSTPIPCFG_PTOKEN_IN: 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, // In case of low USB speed and with a high CPU frequency,
// a ACK from host can be always running on USB line // a ACK from host can be always running on USB line
// then wait end of ACK on IN pipe. // 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 // IN packet received
uhd_ack_in_received(ul_ep); uhd_ack_in_received(ul_pipe);
return 1; return 1;
} }
case UOTGHS_HSTPIPCFG_PTOKEN_OUT: case UOTGHS_HSTPIPCFG_PTOKEN_OUT:
if (Is_uhd_out_ready(ul_ep)) if (Is_uhd_out_ready(ul_pipe))
{ {
// OUT packet sent // OUT packet sent
uhd_freeze_pipe(ul_ep); uhd_freeze_pipe(ul_pipe);
uhd_ack_out_ready(ul_ep); uhd_ack_out_ready(ul_pipe);
return 1; return 1;
} }
} }
// Nothing to report
return 0; return 0;
} }

View File

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