![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
this is the Peter Barrett USBHostShell program, a bit modified for being a bit more talkactive, just to let u know how the USB protocol layer is going on, and all the data trasnfers. Also there is a small implementation of HID descriptors, but not functional... yet :S the aim is to at least implement the gamepad HID, and make an array of function pointer to each HID function
Revision 0:e1e03118b8fe, committed 2010-09-13
- Comitter:
- Sergio
- Date:
- Mon Sep 13 12:40:05 2010 +0000
- Commit message:
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AutoEvents.cpp Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,154 @@ + +/* +Copyright (c) 2010 Peter Barrett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "mbed.h" +#include "USBHost.h" +#include "Utils.h" + +#define AUTOEVT(_class,_subclass,_protocol) (((_class) << 16) | ((_subclass) << 8) | _protocol) +#define AUTO_KEYBOARD AUTOEVT(CLASS_HID,1,1) +#define AUTO_MOUSE AUTOEVT(CLASS_HID,1,2) + +u8 auto_mouse[4]; // buttons,dx,dy,scroll +u8 auto_keyboard[8]; // modifiers,reserved,keycode1..keycode6 +u8 auto_joystick[4]; // x,y,buttons,throttle + +void AutoEventCallback(int device, int endpoint, int status, u8* data, int len, void* userData) +{ + int evt = (int)userData; + switch (evt) + { + case AUTO_KEYBOARD: + printf("AUTO_KEYBOARD "); + break; + case AUTO_MOUSE: + printf("AUTO_MOUSE "); + break; + default: + printf("HUH "); + } + printfBytes("data",data,len); + USBInterruptTransfer(device,endpoint,data,len,AutoEventCallback,userData); +} + +// Establish transfers for interrupt events +void AddAutoEvent(int device, InterfaceDescriptor* id, EndpointDescriptor* ed) +{ + if ((ed->bmAttributes & 3) != ENDPOINT_INTERRUPT || !(ed->bEndpointAddress & 0x80)) + return; + + // Make automatic interrupt enpoints for known devices + u32 evt = AUTOEVT(id->bInterfaceClass,id->bInterfaceSubClass,id->bInterfaceProtocol); + u8* dst = 0; + int len; + switch (evt) + { + case AUTO_MOUSE: + dst = auto_mouse; + len = sizeof(auto_mouse); + break; + case AUTO_KEYBOARD: + dst = auto_keyboard; + len = sizeof(auto_keyboard); + break; + default: + printf("Interrupt endpoint %02X %08X\n",ed->bEndpointAddress,evt); + break; + } + if (dst) + { + printf("Auto Event for %02X %08X\n",ed->bEndpointAddress,evt); + USBInterruptTransfer(device,ed->bEndpointAddress,dst,len,AutoEventCallback,(void*)evt); + } +} + +void PrintString(int device, int i) +{ + u8 buffer[256]; + int le = GetDescriptor(device,DESCRIPTOR_TYPE_STRING,i,buffer,255); + if (le < 0) + return; + char* dst = (char*)buffer; + for (int j = 2; j < le; j += 2) + *dst++ = buffer[j]; + *dst = 0; + printf("%d:%s\n",i,(const char*)buffer); + } + +// Walk descriptors and create endpoints for a given device +int StartAutoEvent(int device, int configuration, int interfaceNumber) +{ + u8 buffer[255]; + int err = GetDescriptor(device,DESCRIPTOR_TYPE_CONFIGURATION,0,buffer,255); + if (err < 0) + return err; + + int len = buffer[2] | (buffer[3] << 8); + u8* d = buffer; + u8* end = d + len; + while (d < end) + { + if (d[1] == DESCRIPTOR_TYPE_INTERFACE) + { + InterfaceDescriptor* id = (InterfaceDescriptor*)d; + if (id->bInterfaceNumber == interfaceNumber) + { + d += d[0]; + while (d < end && d[1] != DESCRIPTOR_TYPE_INTERFACE) + { + if (d[1] == DESCRIPTOR_TYPE_ENDPOINT) + AddAutoEvent(device,id,(EndpointDescriptor*)d); + d += d[0]; + } + } + } + d += d[0]; + } + return 0; +} + +// Implemented in main.cpp +int OnDiskInsert(int device); + +void OnLoadDevice(int device, DeviceDescriptor* deviceDesc, InterfaceDescriptor* interfaceDesc) +{ + printf("LoadDevice %d %02X:%02X:%02X\n",device,interfaceDesc->bInterfaceClass,interfaceDesc->bInterfaceSubClass,interfaceDesc->bInterfaceProtocol); + char s[128]; + for (int i = 1; i < 3; i++) + { + if (GetString(device,i,s,sizeof(s)) < 0) + break; + printf("%d: %s\n",i,s); + } + + switch (interfaceDesc->bInterfaceClass) + { + case CLASS_MASS_STORAGE: + if (interfaceDesc->bInterfaceSubClass == 0x06 && interfaceDesc->bInterfaceProtocol == 0x50) + OnDiskInsert(device); // it's SCSI! + break; + default: + StartAutoEvent(device,1,0); + break; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem.lib Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_unsupported/code/fatfilesystem/ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HID.cpp Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,345 @@ + + +#include "mbed.h" +#include "HID.h" + + + +const char ItemSize[4]={0,1,2,4}; + +void ResetParser(HIDParser* pParser) +{ + pParser->Pos=0; + pParser->Count=0; + pParser->nObject=0; + pParser->nReport=0; + pParser->UsageSize=0; + memset(pParser->UsageTab,0,sizeof(pParser->UsageTab)); + memset(pParser->OffsetTab,0,sizeof(pParser->OffsetTab)); + memset(&pParser->Data,0,sizeof(pParser->Data)); +} + +static void ResetLocalState(HIDParser* pParser) +{ + pParser->UsageSize=0; + memset(pParser->UsageTab,0,sizeof(pParser->UsageTab)); +} + +u8* GetReportOffset(HIDParser* pParser, + const u8 ReportID, + const u8 ReportType) + { + u8 Pos=0; + while(Pos<MAX_REPORT && pParser->OffsetTab[Pos][0]!=0) + { + if(pParser->OffsetTab[Pos][0]==ReportID + && pParser->OffsetTab[Pos][1]==ReportType) + return &pParser->OffsetTab[Pos][2]; + Pos++; + } + if(Pos<MAX_REPORT) + { + /* Increment Report count */ + pParser->nReport++; + pParser->OffsetTab[Pos][0]=ReportID; + pParser->OffsetTab[Pos][1]=ReportType; + pParser->OffsetTab[Pos][2]=0; + return &pParser->OffsetTab[Pos][2]; + } + return NULL; + } + + +long FormatValue(long Value, char Size) + { + if(Size==1) + Value=(long)(char)Value; + else if(Size==2) + Value=(long)(short)Value; + return Value; + } + + int HIDParse(HIDParser* pParser, HIDData* pData) + { + int Found=0; + + while(!Found && pParser->Pos<pParser->ReportDescSize) + { + /* Get new pParser->Item if current pParser->Count is empty */ + if(pParser->Count==0) + { + pParser->Item=pParser->ReportDesc[pParser->Pos++]; + pParser->Value=0; + + memcpy(&pParser->Value, &pParser->ReportDesc[pParser->Pos], ItemSize[pParser->Item & SIZE_MASK]); + + /* Pos on next item */ + pParser->Pos+=ItemSize[pParser->Item & SIZE_MASK]; + } + + switch(pParser->Item & ITEM_MASK) + { + case ITEM_UPAGE : + { + /* Copy UPage in Usage stack */ + pParser->UPage=(u8)pParser->Value; + break; + } + case ITEM_USAGE : + { + /* Copy global or local UPage if any, in Usage stack */ + if((pParser->Item & SIZE_MASK)>2) + pParser->UsageTab[pParser->UsageSize].UPage=(u8)(pParser->Value>>16); + else + pParser->UsageTab[pParser->UsageSize].UPage=pParser->UPage; + + /* Copy Usage in Usage stack */ + pParser->UsageTab[pParser->UsageSize].Usage=(u8)(pParser->Value & 0xFFFF); + + /* Increment Usage stack size */ + pParser->UsageSize++; + + break; + } + case ITEM_COLLECTION : + { + /* Get UPage/Usage from UsageTab and store them in pParser->Data.Path */ + pParser->Data.Path.Node[pParser->Data.Path.Size].UPage=pParser->UsageTab[0].UPage; + pParser->Data.Path.Node[pParser->Data.Path.Size].Usage=pParser->UsageTab[0].Usage; + pParser->Data.Path.Size++; + + /* Unstack UPage/Usage from UsageTab (never remove the last) */ + if(pParser->UsageSize>0) + { + u8 ii=0; + while(ii<pParser->UsageSize) + { + pParser->UsageTab[ii].Usage=pParser->UsageTab[ii+1].Usage; + pParser->UsageTab[ii].UPage=pParser->UsageTab[ii+1].UPage; + ii++; + } + /* Remove Usage */ + pParser->UsageSize--; + } + + /* Get Index if any */ + if(pParser->Value>=0x80) + { + pParser->Data.Path.Node[pParser->Data.Path.Size].UPage=0xFF; + pParser->Data.Path.Node[pParser->Data.Path.Size].Usage=pParser->Value & 0x7F; + pParser->Data.Path.Size++; + } + ResetLocalState(pParser); + break; + } + case ITEM_END_COLLECTION : + { + pParser->Data.Path.Size--; + /* Remove Index if any */ + if(pParser->Data.Path.Node[pParser->Data.Path.Size].UPage==0xFF) + pParser->Data.Path.Size--; + ResetLocalState(pParser); + break; + } + case ITEM_FEATURE : + case ITEM_INPUT : + case ITEM_OUTPUT : + { + /* An object was found */ + Found=1; + + /* Increment object count */ + pParser->nObject++; + + /* Get new pParser->Count from global value */ + if(pParser->Count==0) + { + pParser->Count=pParser->ReportCount; + } + + /* Get UPage/Usage from UsageTab and store them in pParser->Data.Path */ + pParser->Data.Path.Node[pParser->Data.Path.Size].UPage=pParser->UsageTab[0].UPage; + pParser->Data.Path.Node[pParser->Data.Path.Size].Usage=pParser->UsageTab[0].Usage; + pParser->Data.Path.Size++; + + /* Unstack UPage/Usage from UsageTab (never remove the last) */ + if(pParser->UsageSize>0) + { + u8 ii=0; + while(ii<pParser->UsageSize) + { + pParser->UsageTab[ii].UPage=pParser->UsageTab[ii+1].UPage; + pParser->UsageTab[ii].Usage=pParser->UsageTab[ii+1].Usage; + ii++; + } + /* Remove Usage */ + pParser->UsageSize--; + } + + /* Copy data type */ + pParser->Data.Type=(u8)(pParser->Item & ITEM_MASK); + + /* Copy data attribute */ + pParser->Data.Attribute=(u8)pParser->Value; + + /* Store offset */ + pParser->Data.Offset=*GetReportOffset(pParser, pParser->Data.ReportID, (u8)(pParser->Item & ITEM_MASK)); + + /* Get Object in pData */ + /* -------------------------------------------------------------------------- */ + memcpy(pData, &pParser->Data, sizeof(HIDData)); + /* -------------------------------------------------------------------------- */ + + /* Increment Report Offset */ + *GetReportOffset(pParser, pParser->Data.ReportID, (u8)(pParser->Item & ITEM_MASK)) += pParser->Data.Size; + + /* Remove path last node */ + pParser->Data.Path.Size--; + + /* Decrement count */ + pParser->Count--; + if (pParser->Count == 0) { + ResetLocalState(pParser); + } + break; + } + case ITEM_REP_ID : + { + pParser->Data.ReportID=(u8)pParser->Value; + break; + } + case ITEM_REP_SIZE : + { + pParser->Data.Size=(u8)pParser->Value; + break; + } + case ITEM_REP_COUNT : + { + pParser->ReportCount=(u8)pParser->Value; + break; + } + case ITEM_UNIT_EXP : + { + pParser->Data.UnitExp=(char)pParser->Value; + // Convert 4 bits signed value to 8 bits signed value + if (pParser->Data.UnitExp > 7) + pParser->Data.UnitExp|=0xF0; + break; + } + case ITEM_UNIT : + { + pParser->Data.Unit=pParser->Value; + break; + } + case ITEM_LOG_MIN : + { + pParser->Data.LogMin=FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); + break; + } + case ITEM_LOG_MAX : + { + pParser->Data.LogMax=FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); + break; + } + case ITEM_PHY_MIN : + { + pParser->Data.PhyMin=FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); + break; + } + case ITEM_PHY_MAX : + { + pParser->Data.PhyMax=FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); + break; + } + case ITEM_LONG : + { + /* can't handle long items, but should at least skip them */ + pParser->Pos+=(u8)(pParser->Value & 0xff); + } + } + } /* while(!Found && pParser->Pos<pParser->ReportDescSize) */ + +/* ERROR(pParser->Data.Path.Size>=PATH_SIZE); + ERROR(pParser->ReportDescSize>=REPORT_DSC_SIZE); + ERROR(pParser->UsageSize>=USAGE_TAB_SIZE); + ERROR(pParser->Data.ReportID>=MAX_REPORT); + */ + return Found; + } + + int FindObject(HIDParser* pParser, HIDData* pData) + { + HIDData FoundData; + ResetParser(pParser); + while(HIDParse(pParser, &FoundData)) + { + if(pData->Path.Size>0 && + FoundData.Type==pData->Type && + memcmp(FoundData.Path.Node, pData->Path.Node, (pData->Path.Size)*sizeof(HIDNode))==0) + { + memcpy(pData, &FoundData, sizeof(HIDData)); + return 1; + } + /* Found by ReportID/Offset */ + else if(FoundData.ReportID==pData->ReportID && + FoundData.Type==pData->Type && + FoundData.Offset==pData->Offset) + { + memcpy(pData, &FoundData, sizeof(HIDData)); + return 1; + } + } + return 0; + } + + void GetValue(const u8* Buf, HIDData* pData) + { + int Bit=pData->Offset+8; /* First byte of report indicate report ID */ + int Weight=0; + pData->Value=0; + + while(Weight<pData->Size) + { + int State=Buf[Bit>>3]&(1<<(Bit%8)); + if(State) + { + pData->Value+=(1<<Weight); + } + Weight++; + Bit++; + } + /* if(pData->Value > pData->LogMax) + pData->Value=FormatValue(pData->Value, (uchar)((pData->Size-1)/8+1)); + */ + if (pData->Value > pData->LogMax) + pData->Value |= ~pData->LogMax; + } + + void SetValue(const HIDData* pData, u8* Buf) + { + int Bit=pData->Offset+8; /* First byte of report indicate report ID */ + int Weight=0; + + while(Weight<pData->Size) + { + int State=pData->Value & (1<<Weight); + + if(Bit%8==0) + Buf[Bit/8]=0; + + if(State) + { + Buf[Bit/8]+=(1<<(Weight%8)); + } + Weight++; + Bit++; + } + } + + + + + + + + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HID.h Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,164 @@ + +#ifndef HID_H +#define HID_H + +#include "USBHost.h" + +#ifndef u8 +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; + +typedef char s8; +typedef short s16; +typedef char s32; +#endif + + + + +enum mode{ INPUT=0x81, OUTPUT=0x91, FEATURE=0xB1}; + +typedef struct +{ + u8 Usage; + u8 LocalMinimun; + u16 LocalMaximun; + u8 ReportCount; + u8 ReportSize; + mode coll_mode; //INPUT/OUTPUT//FEATURE. + u8 mode_value; +}Report; + +typedef struct +{ +u8 numberofreports; +Report* pFirstReport; +//All the reports must be allocated in an array +//so they can be accessed easily. +}Collection; + +/////////////////////////////////////////////////////////////////////// + + + +/* + * Constants + */ + #define PATH_SIZE 10 + #define USAGE_TAB_SIZE 50 + #define MAX_REPORT 300 + #define REPORT_DSC_SIZE 6144 + + #define SIZE_0 0x00 + #define SIZE_1 0x01 + #define SIZE_2 0x02 + #define SIZE_4 0x03 + #define SIZE_MASK 0x03 + + + #define TYPE_MAIN 0x00 + #define TYPE_GLOBAL 0x04 + #define TYPE_LOCAL 0x08 + #define TYPE_MASK 0x0C + +/* Main items */ + #define ITEM_COLLECTION 0xA0 + #define ITEM_END_COLLECTION 0xC0 + #define ITEM_FEATURE 0xB0 + #define ITEM_INPUT 0x80 + #define ITEM_OUTPUT 0x90 + + + /* Global items */ + #define ITEM_UPAGE 0x04 + #define ITEM_LOG_MIN 0x14 + #define ITEM_LOG_MAX 0x24 + #define ITEM_PHY_MIN 0x34 + #define ITEM_PHY_MAX 0x44 + #define ITEM_UNIT_EXP 0x54 + #define ITEM_UNIT 0x64 + #define ITEM_REP_SIZE 0x74 + #define ITEM_REP_ID 0x84 + #define ITEM_REP_COUNT 0x94 + + /* Local items */ + #define ITEM_USAGE 0x08 + #define ITEM_STRING 0x78 + + /* Long item */ + #define ITEM_LONG 0xFC + + #define ITEM_MASK 0xFC + + /* Attribute Flags */ + #define ATTR_DATA_CST 0x01 + #define ATTR_NVOL_VOL 0x80 + + + +typedef struct +{ +u8 UPage; +u8 Usage; +}HIDNode; + +typedef struct +{ + u8 Size; + HIDNode Node[PATH_SIZE]; + }HIDPath; + + +typedef struct +{ + u32 Value; + HIDPath Path; + u8 ReportID; + u8 Offset; + u8 Size; + u8 Type; + u8 Attribute; + u32 Unit; + u8 UnitExp; + u32 LogMin; + u32 LogMax; + u32 PhyMin; + u32 PhyMax; + } HIDData; + +typedef struct +{ + u8 ReportDesc[REPORT_DSC_SIZE]; + u8 ReportDescSize; + u8 Pos; + u8 Item; + u32 Value; + HIDData Data; + u8 OffsetTab[MAX_REPORT][3]; + u8 ReportCount; + u8 Count; + u8 UPage; + HIDNode UsageTab[USAGE_TAB_SIZE]; + u8 UsageSize; + u8 nObject; + u8 nReport; + } HIDParser; + + + +int HIDParse(HIDParser* pParser,HIDData* pData); +void ResetParser(HIDParser* pParser); +int FindObject(HIDParser* pParser, HIDData* pData); +void GetValue(const u8* Buff, HIDData* pData); +void SetValue(const HIDData* pData, u8* Buf); +u8* GetReportOffset(HIDParser* pParser, const u8 ReportID, const u8 ReportType); + + + + + + + + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MassStorage.cpp Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,180 @@ + +/* +Copyright (c) 2010 Peter Barrett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "stdlib.h" +#include "stdio.h" +#include "string.h" + +#include "Utils.h" +#include "USBHost.h" + + +int MassStorage_ReadCapacity(int device, u32* blockCount, u32* blockSize); +int MassStorage_ReadBlock(int device, u32 block, u8* dst); +int MassStorage_WriteBlock(int device, u32 block, const u8* dst); + + +#define ERR_BAD_CSW_SIGNATURE -200 + +#define CBW_SIGNATURE 0x43425355 +#define CSW_SIGNATURE 0x53425355 + +// Command Block +typedef struct +{ + u32 Signature; + u32 Tag; + u32 TransferLength; + u8 Flags; + u8 LUN; + u8 CBLength; + u8 CB[16]; // only 6 really +} CBW; + +// Status block +typedef struct +{ + u32 Signature; + u32 Tag; + u32 DataResidue; + u8 Status; +} CSW; + +int SCSIRequestSense(int device); + +int DoSCSI(int device, const u8* cmd, int cmdLen, int flags, u8* data, u32 transferLen) +{ + CBW cbw; + cbw.Signature = CBW_SIGNATURE; + cbw.Tag = 0; + cbw.TransferLength = transferLen; + cbw.Flags = flags; + cbw.LUN = 0; + cbw.CBLength = cmdLen; + memset(cbw.CB,0,sizeof(cbw.CB)); + memcpy(cbw.CB,cmd,cmdLen); + + int r; + r = USBBulkTransfer(device,0x01,(u8*)&cbw,31); // Send the command + if (r < 0) + return r; + + if (data) + { + r = USBBulkTransfer(device,flags | 1,data,transferLen); + if (r < 0) + return r; + } + + CSW csw; + csw.Signature = 0; + r = USBBulkTransfer(device,0x81,(u8*)&csw,13); + if (r < 0) + return r; + + if (csw.Signature != CSW_SIGNATURE) + return ERR_BAD_CSW_SIGNATURE; + + // ModeSense? + if (csw.Status == 1 && cmd[0] != 3) + return SCSIRequestSense(device); + + return csw.Status; +} + +int SCSITestUnitReady(int device) +{ + u8 cmd[6]; + memset(cmd,0,6); + return DoSCSI(device,cmd,6,DEVICE_TO_HOST,0,0); +} + +int SCSIRequestSense(int device) +{ + u8 cmd[6] = {0x03,0,0,0,18,0}; + u8 result[18]; + int r = DoSCSI(device,cmd,6,DEVICE_TO_HOST,result,18); + return r; +} + +int SCSIInquiry(int device) +{ + u8 cmd[6] = {0x12,0,0,0,36,0}; + u8 result[36+2]; + result[36] = '\n'; + result[37] = 0; + int r = DoSCSI(device,cmd,6,DEVICE_TO_HOST,result,36); + if (r == 0) + printf((const char*)result + 8); + return r; +} + +int SCSIReadCapacity(int device, u32* blockCount, u32* blockSize) +{ + u8 cmd[10] = {0x25,0,0,0,8,0,0,0,0,0}; + u8 result[8]; + *blockSize = 0; + *blockCount = 0; + int r = DoSCSI(device,cmd,10,DEVICE_TO_HOST,result,8); + if (r == 0) + { + *blockCount = BE32(result); + *blockSize = BE32(result+4); + } + return r; +} + +int SCSITransfer(int device, u32 blockAddr, u32 blockCount, u8* dst, u32 blockSize, int direction) +{ + // USB hardware will only do 4k per transfer + while (blockCount*blockSize > 4096) + { + int count = 4096/blockSize; + int r = SCSITransfer(device,blockAddr,count,dst,blockSize,direction); + dst += count*blockSize; + blockAddr += count; + blockCount -= count; + } + + u8 cmd[10]; + memset(cmd,0,10); + cmd[0] = (direction == DEVICE_TO_HOST) ? 0x28 : 0x2A; + BE32(blockAddr,cmd+2); + BE16(blockCount,cmd+7); + return DoSCSI(device,cmd,10,direction,dst,blockSize*blockCount); +} + +int MassStorage_ReadCapacity(int device, u32* blockCount, u32* blockSize) +{ + return SCSIReadCapacity(device,blockCount,blockSize); +} + +int MassStorage_Read(int device, u32 blockAddr, u32 blockCount, u8* dst, u32 blockSize = 512) +{ + return SCSITransfer(device,blockAddr,blockCount,dst,blockSize,DEVICE_TO_HOST); +} + +int MassStorage_Write(int device, u32 blockAddr, u32 blockCount, u8* dst, u32 blockSize = 512) +{ + return SCSITransfer(device,blockAddr,blockCount,dst,blockSize,HOST_TO_DEVICE); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost.cpp Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,1262 @@ + +/* +I changed a bit the code but all credit goes to the amazing work of : + +Copyright (c) 2010 Peter Barrett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "mbed.h" +#include "USBHost.h" + +// Config (default uses x bytes) +#define MAX_DEVICES 8 // Max number of devices +#define MAX_ENDPOINTS_TOTAL 16 // Max number of endpoints total +#define MAX_ENDPOINTS_PER_DEVICE 8 // Max number of endpoints for any one device + +#define USBLOG 1 +#if USBLOG +#define LOG(...) printf(__VA_ARGS__) +#else +#define LOG(...) do {} while(0) +#endif + +// USB host structures + +#define USB_RAM_SIZE 16*1024 // AHB SRAM block 1 TODO MACHINE DEPENDENT +#define USB_RAM_BASE 0x2007C000 + +#define TOKEN_SETUP 0 +#define TOKEN_IN 1 +#define TOKEN_OUT 2 + +// Status flags from hub +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOW_SPEED 9 + +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 + +typedef struct { + u8 bm_request_type; + u8 b_request; + u16 w_value; + u16 w_index; + u16 w_length; +} Setup; + + +// Hub stuff is kept private just to keep api simple +int SetPortFeature(int device, int feature, int index); +int ClearPortFeature(int device, int feature, int index); +int SetPortPower(int device, int port); +int SetPortReset(int device, int port); +int GetPortStatus(int device, int port, u32* status); + +//=================================================================== +//=================================================================== +// Hardware defines + +// HcControl +#define PeriodicListEnable 0x00000004 +#define IsochronousEnable 0x00000008 +#define ControlListEnable 0x00000010 +#define BulkListEnable 0x00000020 +#define OperationalMask 0x00000080 +#define HostControllerFunctionalState 0x000000C0 + +// HcCommandStatus +#define HostControllerReset 0x00000001 +#define ControlListFilled 0x00000002 +#define BulkListFilled 0x00000004 + +// HcInterruptStatus Register +#define WritebackDoneHead 0x00000002 +#define StartofFrame 0x00000004 +#define ResumeDetected 0x00000008 +#define UnrecoverableError 0x00000010 +#define FrameNumberOverflow 0x00000020 +#define RootHubStatusChange 0x00000040 +#define OwnershipChange 0x00000080 +#define MasterInterruptEnable 0x80000000 + +// HcRhStatus +#define SetGlobalPower 0x00010000 +#define DeviceRemoteWakeupEnable 0x00008000 + +// HcRhPortStatus (hub 0, port 1) +#define CurrentConnectStatus 0x00000001 +#define PortEnableStatus 0x00000002 +#define PortSuspendStatus 0x00000004 +#define PortOverCurrentIndicator 0x00000008 +#define PortResetStatus 0x00000010 + +#define PortPowerStatus 0x00000100 +#define LowspeedDevice 0x00000200 +#define HighspeedDevice 0x00000400 + +#define ConnectStatusChange (CurrentConnectStatus << 16) +#define PortResetStatusChange (PortResetStatus << 16) + + +#define TD_ROUNDING (u32)0x00040000 +#define TD_SETUP (u32)0x00000000 +#define TD_IN (u32)0x00100000 +#define TD_OUT (u32)0x00080000 +#define TD_DELAY_INT(x) (u32)((x) << 21) +#define TD_TOGGLE_0 (u32)0x02000000 +#define TD_TOGGLE_1 (u32)0x03000000 +#define TD_CC (u32)0xF0000000 + +// HostController EndPoint Descriptor +typedef struct { + volatile u32 Control; + volatile u32 TailTd; + volatile u32 HeadTd; + volatile u32 Next; +} HCED; + +// HostController Transfer Descriptor +typedef struct { + volatile u32 Control; + volatile u32 CurrBufPtr; + volatile u32 Next; + volatile u32 BufEnd; +} HCTD; + +// Host Controller Communication Area +typedef struct { + volatile u32 InterruptTable[32]; + volatile u16 FrameNumber; + volatile u16 FrameNumberPad; + volatile u32 DoneHead; + volatile u8 Reserved[120]; +} HCCA; + +//==================================================================================== +//==================================================================================== + +class HostController; +class Endpoint; +class Device; + +// must be 3*16 bytes long +class Endpoint +{ +public: + HCED EndpointDescriptor; // Pointer to EndpointDescriptor == Pointer to Endpoint + HCTD TDHead; + + enum State + { + Free, + NotQueued, + Idle, + SetupQueued, + DataQueued, + StatusQueued, + CallbackPending + }; + + volatile u8 CurrentState; + u8 Flags; // 0x80 In, 0x03 mask endpoint type + + u16 Length; + u8* Data; + USBCallback Callback; // Must be a multiple of 16 bytes long + void* UserData; + + int Address() + { + int ep = (EndpointDescriptor.Control >> 7) & 0xF; + if (ep) + ep |= Flags & 0x80; + return ep; + } + + int Device() + { + return EndpointDescriptor.Control & 0x7F; + } + + int Status() + { + return (TDHead.Control >> 28) & 0xF; + } + + u32 Enqueue(u32 head) + { + if (CurrentState == NotQueued) + { + EndpointDescriptor.Next = head; + head = (u32)&EndpointDescriptor; + CurrentState = Idle; + } + return head; + } +}; + +class Device +{ +public: + u8 _endpointMap[MAX_ENDPOINTS_PER_DEVICE*2]; + u8 Hub; + u8 Port; + u8 Addr; + u8 Pad; + + // Only if this device is a hub + u8 HubPortCount; // nonzero if this is a hub + u8 HubInterruptData; + u8 HubMap; + u8 HubMask; + + int Flags; // 1 = Disconnected + + Setup SetupBuffer; + + // Allocate endpoint zero + int Init(DeviceDescriptor* d, int hub, int port, int addr, int lowSpeed) + { + Hub = hub; + Port = port; + Addr = addr; + Flags = lowSpeed; + memset(_endpointMap,0xFF,sizeof(_endpointMap)); + return 0; + } + + int SetEndpointIndex(int ep, int endpointIndex) + { + for (int i = 0; i < MAX_ENDPOINTS_PER_DEVICE*2; i += 2) + { + if (_endpointMap[i] == 0xFF) // Add endpoint to map + { + _endpointMap[i] = ep; + _endpointMap[i+1] = endpointIndex; + return 0; + } + } + return ERR_ENDPOINT_NONE_LEFT; + } + + int GetEndpointIndex(int ep) + { + for (int i = 0; i < MAX_ENDPOINTS_PER_DEVICE*2; i += 2) + { + if (_endpointMap[i] == ep) + return _endpointMap[i+1]; + if (_endpointMap[i] == 0xFF) + break; + } + return -1; + } +}; + +class HostController +{ +public: + HCCA CommunicationArea; + Endpoint Endpoints[MAX_ENDPOINTS_TOTAL]; // Multiple of 16 + + Endpoint EndpointZero; // For device enumeration + HCTD _commonTail; + Setup _setupZero; + + Device Devices[MAX_DEVICES]; + u32 _frameNumber; // 32 bit ms counter + + u8 _callbacksPending; // Endpoints with callbacks are pending, set from ISR via ProcessDoneQueue + u8 _rootHubStatusChange; // Root hub status has changed, set from ISR + u8 _unused0; + u8 _unused1; + + u8 _connectPending; // Reset has initiated a connect + u8 _connectCountdown; // Number of ms left after reset before we can connect + u8 _connectHub; // Will connect on this hub + u8 _connectPort; // ... and this port + + u8 SRAM[0]; // Start of free SRAM + + void Loop() + { + u16 elapsed = CommunicationArea.FrameNumber - (u16)_frameNumber; // extend to 32 bits + _frameNumber += elapsed; + + // Do callbacks, if any + while (_callbacksPending) + { + for (int i = 0; i < MAX_ENDPOINTS_TOTAL; i++) + { + Endpoint* endpoint = Endpoints + i; + if (endpoint->CurrentState == Endpoint::CallbackPending) + { + _callbacksPending--; + endpoint->CurrentState = Endpoint::Idle; + //remember: + //typedef void (*USBCallback)(int device, int endpoint, int status, u8* data, int len, void* userData); + endpoint->Callback(endpoint->Device(),endpoint->Address(),endpoint->Status(),endpoint->Data,endpoint->Length,endpoint->UserData); + + } + } + } + + // Deal with changes on the root hub + if (_rootHubStatusChange) + { + u32 status = LPC_USB->HcRhPortStatus1; + _rootHubStatusChange = 0; + if (status >> 16) + { + HubStatusChange(0,1,status); + LPC_USB->HcRhPortStatus1 = status & 0xFFFF0000; // clear status changes + } + + } + + // Connect after reset timeout + if (_connectCountdown) + { + if (elapsed >= _connectCountdown) + { + _connectCountdown = 0; + Connect(_connectHub,_connectPort & 0x7F,_connectPort & 0x80); + } else + _connectCountdown -= elapsed; + } + }// END OF VOID LOOP() + + + // HubInterrupt - bitmap in dev->HubInterruptData + void HubInterrupt(int device) + { + Device* dev = &Devices[device-1]; + for (int i = 0; i < dev->HubPortCount; i++) + { + int port = i+1; + if (dev->HubInterruptData & (1 << port)) + { + u32 status = 0; + GetPortStatus(device,port,&status); + if (status >> 16) + { + if (_connectPending && (status & ConnectStatusChange)) + continue; // Don't connect again until previous device has been added and addressed + + HubStatusChange(device,port,status); + if (status & ConnectStatusChange) + ClearPortFeature(device,C_PORT_CONNECTION,port); + if (status & PortResetStatusChange) + ClearPortFeature(device,C_PORT_RESET,port); + } + } + } + } + + static void HubInterruptCallback(int device, int endpoint, int status, u8* data, int len, void* userData) + { + HostController* controller = (HostController*)userData; + if (status == 0) + controller->HubInterrupt(device); + USBInterruptTransfer(device,endpoint,data,1,HubInterruptCallback,userData); + } + + int InitHub(int device) + { + u8 buf[16]; + int r= USBControlTransfer(device,DEVICE_TO_HOST | REQUEST_TYPE_CLASS | RECIPIENT_DEVICE,GET_DESCRIPTOR,(DESCRIPTOR_TYPE_HUB << 8),0,buf,sizeof(buf)); + if (r < 0) + return ERR_HUB_INIT_FAILED; + + // turn on power on the hubs ports + Device* dev = &Devices[device-1]; + int ports = buf[2]; + dev->HubPortCount = ports; + for (int i = 0; i < ports; i++) + SetPortPower(device,i+1); + + // Enable hub change interrupts + return USBInterruptTransfer(device,0x81,&dev->HubInterruptData,1,HubInterruptCallback,this); + } + + int AddEndpoint(int device, int ep, int attributes, int maxPacketSize, int interval) + { + LOG("AddEndpoint D:%02X A:%02X T:%02X P:%04X I:%02X\n",device,ep,attributes,maxPacketSize,interval); + Device* dev = &Devices[device-1]; + Endpoint* endpoint = AllocateEndpoint(device,ep,attributes,maxPacketSize); + if (!endpoint) + return ERR_ENDPOINT_NONE_LEFT; + dev->SetEndpointIndex(ep,endpoint - Endpoints); + endpoint->EndpointDescriptor.Control |= dev->Flags; // Map in slow speed + return 0; // TODO ed->bInterval + } + + int AddEndpoint(int device, EndpointDescriptor* ed) + { + return AddEndpoint(device,ed->bEndpointAddress,ed->bmAttributes,ed->wMaxPacketSize,ed->bInterval); + } + + // allocate a endpoint + Endpoint* AllocateEndpoint(int device, int endpointAddress, int type, int maxPacketSize) + { + for (int i = 0; i < MAX_ENDPOINTS_TOTAL; i++) + { + Endpoint* ep = &Endpoints[i]; + if (ep->CurrentState == 0) + { + //LOG("Allocated endpoint %d to %02X:%02X\n",i,device,endpointAddress); + ep->Flags = (endpointAddress & 0x80) | (type & 3); + ep->CurrentState = Endpoint::NotQueued; + ep->EndpointDescriptor.Control = (maxPacketSize << 16) | ((endpointAddress & 0x7F) << 7) | device; + return ep; + } + } + return 0; + } + + Endpoint* GetEndpoint(int device, int ep) + { + if (device == 0) + { + //printf("WARNING: USING DEVICE 0\n"); + return &EndpointZero; + } + if (device > MAX_DEVICES) + return 0; + int i = Devices[device-1].GetEndpointIndex(ep); + if (i == -1) + return 0; + return Endpoints + i; + } + + int Transfer(Endpoint* endpoint, int token, u8* data, int len, int state) + { + #ifdef USBLOG + LOG("Transfer EDaddres:%02X Token:%d Len:%d State:%d \n",endpoint->Address(),token,len,state); + LOG("Data %s: ",(token==TOKEN_IN)? "in": "out"); + + //print the data: + //@BUG, little endian- bigendian ?? + for (int i=0; i<len; i++) + { + if ( ((i%(int)8)==0) ) LOG("\n"); + LOG("hx%02X ",data[i]); + } + LOG("\n"); + + if ( token==TOKEN_IN && len<30 && len>8) + { + + LOG("bLength: %d d \n", data[0]); + LOG("bDescriptorType: %d -",data[1]); + /* + bDescriptorType Descriptor Type Required? +01h device Yes. +02h configuration Yes. +03h string No. Optional descriptive text. +04h interface Yes. +05h endpoint No, if the device uses only Endpoint 0. +06h device_qualifier Yes, for devices that support both full and high +speeds. Not allowed for other devices. +07h other_speed_configuration Yes, for devices that support both full and high +speeds. Not allowed for other devices. +08h interface_power No. Supports interface-level power +management. +09h OTG For On-The-Go devices only. +0Ah debug No. +0Bh interface_association For composite devices. + */ + switch (data[1]) + { + case(1): { LOG("DEVICE"); break;} + case(2): { LOG("CONFIGURATION"); break;} + case(3): { LOG("STRING"); break;} + case(4): { LOG("INTERFACE"); break;} + case(5): { LOG("ENDPOINT"); break;} + case(6): { LOG("DEVICE_QUALIFIER"); break;} + case(7): { LOG("OTHER_SPEED_CONFIGURATION"); break;} + case(8): { LOG("ON_the_GO"); break;} + case(9): { LOG("DEBUG"); break;} + case(10): { LOG("INTERFACE_ASSOCIATION"); break;} + default: {LOG("?");} ; + }//end switch data1; + LOG("\n"); + } + + //the descriptor for device: + if(data[1]==1) + { + u16 *aux; + aux=(u16*) &data[2]; + LOG("bcdUSB\t %04x\t",*aux); + switch( *aux) + { + case(0x0200): {LOG("v2.0"); break;} + case(0x0110): {LOG("v1.1"); break;} + case(0x0100): {LOG("v1.0"); break;} + default: {LOG("?");} + } + LOG("\n"); + } + + + + + #endif + + int toggle = 0; + if (endpoint->Address() == 0) + toggle = (token == TOKEN_SETUP) ? TD_TOGGLE_0 : TD_TOGGLE_1; + + if (token != TOKEN_SETUP) + token = (token == TOKEN_IN ? TD_IN : TD_OUT); + + HCTD* head = &endpoint->TDHead; + HCTD* tail = &_commonTail; + + head->Control = TD_ROUNDING | token | TD_DELAY_INT(0) | toggle | TD_CC; + head->CurrBufPtr = (u32)data; //..data is a pointer. + head->BufEnd = (u32)(data + len - 1); //pointers arithmetic :S + head->Next = (u32)tail; + + HCED* ed = &endpoint->EndpointDescriptor; + ed->HeadTd = (u32)head | (ed->HeadTd & 0x00000002); // carry toggle + ed->TailTd = (u32)tail; + + //HCTD* td = head; + //LOG("%04X TD %08X %08X %08X Next:%08X\n",CommunicationArea.FrameNumber,td->Control,td->CurrBufPtr,td->BufEnd,td->Next); + //LOG("%04X ED %08X %08X %08X\n",CommunicationArea.FrameNumber,ed->Control,ed->HeadTd,ed->TailTd); + + switch (endpoint->Flags & 3) + { + case ENDPOINT_CONTROL: + LPC_USB->HcControlHeadED = endpoint->Enqueue(LPC_USB->HcControlHeadED); // May change state NotQueued->Idle + endpoint->CurrentState = state; // Get in before an int + LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | ControlListFilled; + LPC_USB->HcControl = LPC_USB->HcControl | ControlListEnable; + break; + + case ENDPOINT_BULK: + LPC_USB->HcBulkHeadED = endpoint->Enqueue(LPC_USB->HcBulkHeadED); + endpoint->CurrentState = state; + LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | BulkListFilled; + LPC_USB->HcControl = LPC_USB->HcControl | BulkListEnable; + break; + + case ENDPOINT_INTERRUPT: + CommunicationArea.InterruptTable[0] = endpoint->Enqueue(CommunicationArea.InterruptTable[0]); + endpoint->CurrentState = state; + LPC_USB->HcControl |= PeriodicListEnable; + break; + } + return 0; + }//end of transfer + + // Remove an endpoint from an active queue + bool Remove(HCED* ed, volatile HCED** queue) + { + if (*queue == 0) + return false; + if (*queue == (volatile HCED*)ed) + { + *queue = (volatile HCED*)ed->Next; // At head of queue + return true; + } + + volatile HCED* head = *queue; + while (head) + { + if (head->Next == (u32)ed) + { + head->Next = ed->Next; + return true; + } + head = (volatile HCED*)head->Next; + } + return false; + } + + void Release(Endpoint* endpoint) + { + if (endpoint->CurrentState == Endpoint::NotQueued) + { + // Never event used it, nothing to do + } + else + { + HCED* ed = (HCED*)endpoint; + ed->Control |= 0x4000; // SKIP + switch (endpoint->Flags & 0x03) + { + case ENDPOINT_CONTROL: + Remove(ed,(volatile HCED**)&LPC_USB->HcControlHeadED); + break; + case ENDPOINT_BULK: + Remove(ed,(volatile HCED**)&LPC_USB->HcBulkHeadED); + break; + case ENDPOINT_INTERRUPT: + for (int i = 0; i < 32; i++) + Remove(ed,(volatile HCED**)&CommunicationArea.InterruptTable[i]); + break; + } + + u16 fn = CommunicationArea.FrameNumber; + while (fn == CommunicationArea.FrameNumber) + ; // Wait for next frame + + } + + // In theory, the endpoint is now dead. + // TODO: Will Callbacks ever be pending? BUGBUG + memset(endpoint,0,sizeof(Endpoint)); + } + + // Pop the last TD from the list + HCTD* Reverse(HCTD* current) + { + HCTD *result = NULL,*temp; + while (current) + { + temp = (HCTD*)current->Next; + current->Next = (u32)result; + result = current; + current = temp; + } + return result; + } + + // Called from interrupt... + // Control endpoints use a state machine to progress through the transfers + void ProcessDoneQueue(u32 tdList) + { + HCTD* list = Reverse((HCTD*)tdList); + while (list) + { + Endpoint* endpoint = (Endpoint*)(list-1); + list = (HCTD*)list->Next; + int ep = endpoint->Address(); + bool in = endpoint->Flags & 0x80; + int status = (endpoint->TDHead.Control >> 28) & 0xF; + + LOG("ProcessDoneQueue %02X %08X\n\n",ep,endpoint->TDHead.Control); + + if (status != 0) + { + LOG("ProcessDoneQueue status %02X %d\n\n",ep,status); + endpoint->CurrentState = Endpoint::Idle; + } else { + switch (endpoint->CurrentState) + { + case Endpoint::SetupQueued: + if (endpoint->Length == 0) + Transfer(endpoint,in ? TOKEN_OUT : TOKEN_IN,0,0,Endpoint::StatusQueued); // Skip Data Phase + else + Transfer(endpoint,in ? TOKEN_IN : TOKEN_OUT,endpoint->Data,endpoint->Length, Endpoint::DataQueued); // Setup is done, now Data + break; + + case Endpoint::DataQueued: + if (endpoint->TDHead.CurrBufPtr) + endpoint->Length = endpoint->TDHead.CurrBufPtr - (u32)endpoint->Data; + + if (ep == 0) + Transfer(endpoint,in ? TOKEN_OUT : TOKEN_IN,0,0,Endpoint::StatusQueued); // Data is done, now Status, Control only + else + endpoint->CurrentState = Endpoint::Idle; + break; + + case Endpoint::StatusQueued: // Transaction is done + endpoint->CurrentState = Endpoint::Idle; + break; + } + } + + // Complete, flag if we need a callback + if (endpoint->Callback && endpoint->CurrentState == Endpoint::Idle) + { + endpoint->CurrentState = Endpoint::CallbackPending; + _callbacksPending++; + } + } + } + + // Hack to reset devices that don't want to connect + int AddDevice(int hub, int port, bool isLowSpeed) + { + int device = AddDeviceCore(hub,port,isLowSpeed); + if (device < 0) + { + LOG("========RETRY ADD DEVICE========\n"); // This will go for ever.. TODO power cycle root? + Disconnect(hub,port); // Could not read descriptor at assigned address, reset this port and try again + ResetPort(hub,port); // Cheap bluetooth dongles often need this on a hotplug + return -1; + } + return device; + } + + int AddDeviceCore(int hub, int port, bool isLowSpeed) + { + int lowSpeed = isLowSpeed ? 0x2000 : 0; + DeviceDescriptor desc; + EndpointZero.EndpointDescriptor.Control = (8 << 16) | lowSpeed; // MaxPacketSize == 8 + int r = GetDescriptor(0,DESCRIPTOR_TYPE_DEVICE,0,(u8*)&desc,8); + if (r < 0) + { + LOG("FAILED TO LOAD DESCRIPTOR FOR DEVICE 0\n"); + return r; + } + + EndpointZero.EndpointDescriptor.Control = (desc.bMaxPacketSize << 16) | lowSpeed; // Actual MaxPacketSize + r = GetDescriptor(0,DESCRIPTOR_TYPE_DEVICE,0,(u8*)&desc,sizeof(desc)); + if (r < 0) + return r; + + LOG("\nClass %02X found %04X:%04X\n\n",desc.bDeviceClass,desc.idVendor,desc.idProduct); + + + // Now assign the device an address, move off EndpointZero + int device = 0; + for (int i = 0; i < MAX_DEVICES; i++) + { + if (Devices[i].Port == 0) + { + device = i+1; + break; + } + } + if (!device) + return ERR_DEVICE_NONE_LEFT; + + r = SetAddress(0,device); + if (r) + return r; + DelayMS(2); + + // Now at a nonzero address, create control endpoint + Device* dev = &Devices[device-1]; + dev->Init(&desc,hub,port,device,lowSpeed); + AddEndpoint(device,0,ENDPOINT_CONTROL,desc.bMaxPacketSize,0); + _connectPending = 0; + + // Verify this all works + r = GetDescriptor(device,DESCRIPTOR_TYPE_DEVICE,0,(u8*)&desc,sizeof(desc)); + if (r < 0) + return r; + + // Set to interface 0 by default + // Calls LoadDevice if interface is found + r = SetConfigurationAndInterface(device,1,0,&desc); + + if (desc.bDeviceClass == CLASS_HUB) + InitHub(device); // Handle hubs in this code + + return device; + } + + // Walk descriptors and create endpoints for a given device + // TODO configuration !=1, alternate settings etc. + int SetConfigurationAndInterface(int device, int configuration, int interfaceNumber, DeviceDescriptor* desc) + { + LOG("SetConfiguration and Interface\n"); + u8 buffer[255]; + int err = GetDescriptor(device,DESCRIPTOR_TYPE_CONFIGURATION,0,buffer,sizeof(buffer)); + if (err < 0) + return err; + + err = SetConfiguration(device,configuration); + if (err < 0) + return err; + + // Add the endpoints for this interface + int len = buffer[2] | (buffer[3] << 8); + u8* d = buffer; + u8* end = d + len; + InterfaceDescriptor* found = 0; + while (d < end) + { + if (d[1] == DESCRIPTOR_TYPE_INTERFACE) + { + InterfaceDescriptor* id = (InterfaceDescriptor*)d; + if (id->bInterfaceNumber == interfaceNumber) + { + found = id; + d += d[0]; + while (d < end && d[1] != DESCRIPTOR_TYPE_INTERFACE) + { + switch (d[1]) + { + case DESCRIPTOR_TYPE_ENDPOINT: + AddEndpoint(device,(EndpointDescriptor*)d); + break; + default: + LOG("Skipping descriptor %02X (%d bytes)\n",d[1],d[0]); + } + d += d[0]; + } + } + } + d += d[0]; + } + + if (!found) + return ERR_INTERFACE_NOT_FOUND; + OnLoadDevice(device,desc,found); + return 0; + } + + void Init() + { + LOG("USB INIT (Controller is %d bytes)\n",sizeof(*this)); + memset(this,0,sizeof(HostController)); + EndpointZero.CurrentState = Endpoint::NotQueued; + HWInit(&CommunicationArea); + DelayMS(10); + } + + void ResetPort(int hub, int port) + { + LOG("ResetPort Hub:%d Port:%d\n",hub,port); + _connectPending++; // Only reset/add 1 device at a time + if (hub == 0) + LPC_USB->HcRhPortStatus1 = PortResetStatus; // Reset Root Hub, port 1 + else + + SetPortReset(hub,port); // or reset other hub + } + + void Disconnect(int hub, int port) + { + LOG("Disconnect Hub:%d Port:%d\n",hub,port); // Mark a device for destruction + for (int i = 0; i < MAX_DEVICES; i++) + { + Device* dev = Devices + i; + if (dev->Port == port && dev->Hub == hub) + { + // Disconnect everything that is attached to this device if it is a hub + for (int p = 0; p < dev->HubPortCount; p++) + Disconnect(i+1,p+1); + + // Now release endpoints + for (int j = 1; j < MAX_ENDPOINTS_PER_DEVICE*2; j += 2) + { + u8 endpointIndex = dev->_endpointMap[j]; + if (endpointIndex != 0xFF) + Release(Endpoints + endpointIndex); + } + dev->Port = 0; // Device is now free + dev->Flags = 0; + return; + } + } + } + + // called after reset + void Connect(int hub, int port, bool lowspeed) + { + LOG("Connect Hub:%d Port:%d %s\n\n",hub,port,lowspeed ? "slow-speed" : "full-speed"); + AddDevice(hub,port,lowspeed); + } + + // Called from interrupt + void HubStatusChange(int hub, int port, u32 status) + { + LOG("HubStatusChange Hub:%d Port:%d %08X\n",hub,port,status); + if (status & ConnectStatusChange) + { + if (status & CurrentConnectStatus) // Connecting + ResetPort(hub,port); // Reset to initiate connect (state machine?) + else + Disconnect(hub,port); + } + + if (status & PortResetStatusChange) + { + if (!(status & PortResetStatus)) + { + _connectCountdown = 200; // Schedule a connection in 200ms + if (status & LowspeedDevice) + port |= 0x80; + _connectHub = hub; + _connectPort = port; + } + } + } + + #define HOST_CLK_EN (1<<0) + #define PORTSEL_CLK_EN (1<<3) + #define AHB_CLK_EN (1<<4) + #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN) + + #define FRAMEINTERVAL (12000-1) // 1ms + #define DEFAULT_FMINTERVAL ((((6 * (FRAMEINTERVAL - 210)) / 7) << 16) | FRAMEINTERVAL) + + void DelayMS(int ms) + { + u16 f = ms + CommunicationArea.FrameNumber; + while (f != CommunicationArea.FrameNumber) + ; + } + + static void HWInit(HCCA* cca) + { + NVIC_DisableIRQ(USB_IRQn); //no interruptions + + // turn on power for USB + LPC_SC->PCONP |= (1UL<<31); + // Enable USB host clock, port selection and AHB clock + LPC_USB->USBClkCtrl |= CLOCK_MASK; + // Wait for clocks to become available + while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK) + ; + + // We are a Host + LPC_USB->OTGStCtrl |= 1; + LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN; // we don't need port selection clock until we do OTG + + // configure USB pins + LPC_PINCON->PINSEL1 &= ~((3<<26)|(3<<28)); + LPC_PINCON->PINSEL1 |= ((1<<26)|(1<<28)); // USB D+/D- + + LPC_PINCON->PINSEL3 &= ~((3 << 6) | (3 << 22)); // USB_PPWR, USB_OVRCR + LPC_PINCON->PINSEL3 |= ((2 << 6) | (2 << 22)); + + LPC_PINCON->PINSEL4 &= ~(3 << 18); // USB_CONNECT + LPC_PINCON->PINSEL4 |= (1 << 18); + + // Reset OHCI block + LPC_USB->HcControl = 0; //Defines the operating modes of the HC. + + LPC_USB->HcControlHeadED = 0; //Contains the physical address of the first endpoint + //descriptor of the control list. + + LPC_USB->HcBulkHeadED = 0; //Contains the physical address of the first endpoint + //descriptor of the bulk list. + + LPC_USB->HcCommandStatus = HostControllerReset; //This register is used to receive the commands from the + //Host Controller Driver (HCD). It also indicates the status + //of the HC. + + LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL; + LPC_USB->HcPeriodicStart = FRAMEINTERVAL*90/100; + + LPC_USB->HcControl = (LPC_USB->HcControl & (~HostControllerFunctionalState)) | OperationalMask; + LPC_USB->HcRhStatus = SetGlobalPower; + + LPC_USB->HcHCCA = (u32)cca; //Contains the physical address of the host controller + //communication area. + + LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; //Indicates the status on various events that cause + //hardware interrupts by setting the appropriate bits. + LPC_USB->HcInterruptEnable = MasterInterruptEnable | WritebackDoneHead | RootHubStatusChange | FrameNumberOverflow; + + NVIC_SetPriority(USB_IRQn, 0); //top-priority + NVIC_EnableIRQ(USB_IRQn); //enable USB interruptions. + LOG("\nHWini\n"); + while (cca->FrameNumber < 10) + ; // 10ms delay before diving in + } +}; + +//==================================================================================== +//==================================================================================== +// Host controller instance and Interrupt handler + +static HostController _controller __attribute__((at(USB_RAM_BASE))); + +extern "C" void USB_IRQHandler(void) __irq; +void USB_IRQHandler (void) __irq +{ + u32 int_status = LPC_USB->HcInterruptStatus; //Indicates the status on various events that cause + //hardware interrupts by setting the appropriate bits. + + if (int_status & RootHubStatusChange) // Root hub status change + _controller._rootHubStatusChange++; // Just flag the controller, will be processed in USBLoop + + u32 head = 0; + if (int_status & WritebackDoneHead) + { + head = _controller.CommunicationArea.DoneHead; // Writeback Done + _controller.CommunicationArea.DoneHead = 0; + } + LPC_USB->HcInterruptStatus = int_status; + + if (head) + _controller.ProcessDoneQueue(head); // TODO - low bit can be set BUGBUG +} + +//==================================================================================== +//==================================================================================== +// API Methods + +void USBInit() +{ + return _controller.Init(); +} + +void USBLoop() +{ + return _controller.Loop(); +} + +u8* USBGetBuffer(u32* len) +{ + *len = USB_RAM_SIZE - sizeof(HostController); + return _controller.SRAM; +} + + +static Setup* GetSetup(int device) +{ + if (device == 0) + return &_controller._setupZero; + + if (device < 1 || device > MAX_DEVICES) + return 0; + return &_controller.Devices[device-1].SetupBuffer; +} + +// Loop until IO on endpoint is complete +static int WaitIODone(Endpoint* endpoint) +{ + if (endpoint->CurrentState == Endpoint::NotQueued) + return 0; + while (endpoint->CurrentState != Endpoint::Idle) + USBLoop(); // May generate callbacks, mount or unmount devices etc + int status = endpoint->Status(); + if (status == 0) + return endpoint->Length; + return -status; +} + +int USBTransfer(int device, int ep, u8 flags, u8* data, int length, USBCallback callback, void* userData) +{ + Endpoint* endpoint = _controller.GetEndpoint(device,ep); + if (!endpoint) + return ERR_ENDPOINT_NOT_FOUND; + + WaitIODone(endpoint); + endpoint->Flags = flags; + endpoint->Data = data; + endpoint->Length = length; + endpoint->Callback = callback; + endpoint->UserData = userData; + if (ep == 0) + _controller.Transfer(endpoint,TOKEN_SETUP,(u8*)GetSetup(device),8,Endpoint::SetupQueued); + else + _controller.Transfer(endpoint,flags & 0x80 ? TOKEN_IN : TOKEN_OUT,data,length,Endpoint::DataQueued); + if (callback) + return IO_PENDING; + return WaitIODone(endpoint); +} + +int USBControlTransfer(int device, int request_type, int request, int value, int index, u8* data, int length, USBCallback callback, void * userData) +{ + + + Setup* setup = GetSetup(device); + if (!setup) + return ERR_DEVICE_NOT_FOUND; + + // Async control calls may overwrite setup buffer of previous call, so we need to wait before setting up next call + WaitIODone(_controller.GetEndpoint(device,0)); + + setup->bm_request_type = request_type; + setup->b_request = request; + setup->w_value = value; + setup->w_index = index; + setup->w_length = length; + + + #ifdef USBLOG + LOG("USBControlTransfer\n"); + LOG("bm_request_type\t %02x h", request_type); + LOG( "\t %d%d%d%d%d%d%d%d b \t", (((request_type)&(128))>>7), (((request_type)&(64))>>6), ((request_type)&(32))>>5, ((request_type)&(16))>>4,((request_type)&(8))>>2,((request_type)&(4))>>1,((request_type)&(2) )>>1, (request_type)&(1) ); + if( request_type &1) LOG(" -> device-to-host"); + if(!( request_type & 1)) LOG(" -> host-to-device"); + LOG("\n"); + LOG("b_request\t %02x h ",request); + /* USB2.0 specs: table 9.4: +GET_STATUS 0 +CLEAR_FEATURE 1 +Reserved for future use 2 +SET_FEATURE 3 +Reserved for future use 4 +SET_ADDRESS 5 +GET_DESCRIPTOR 6 +SET_DESCRIPTOR 7 +GET_CONFIGURATION 8 +SET_CONFIGURATION 9 +GET_INTERFACE 10 +SET_INTERFACE 11 +SYNCH_FRAME 12 + */ + LOG("\t"); + switch (request) + { + case (0) :{ LOG("GET_STATUS"); break; } + case (1) :{ LOG("CLEAR_FEATURE"); break; } + case (2) :{ LOG("reseved"); break; } + case (3) :{ LOG("SET_FEATURE"); break; } + case (6): { LOG("GET_DESCRIPTOR"); break; } + case (5) :{ LOG("SET_ADDRESS"); break; } + case (7): { LOG("SET_DESCRIPTOR"); break; } + case (8) :{ LOG("GET_CONFIGURATION"); break; } + case (9): { LOG("SET_CONFIGURATION"); break; } + case (10): { LOG("GET_INTERFACE"); break; } + case (11) :{ LOG("SET_INTERFACE"); break; } + case (12): { LOG("SYNCH_FRAEM"); break; } + + }//end switch (reques) + LOG("\n"); + LOG("w_value \t %04x h\t ",value); + /* + Descriptor Types Value +DEVICE 1 +CONFIGURATION 2 +STRING 3 +INTERFACE 4 +ENDPOINT 5 +DEVICE_QUALIFIER 6 +OTHER_SPEED_CONFIGURATION 7 +INTERFACE_POWER1 8 + */ + u8* pv=(u8*)&value; + pv=pv+1; + switch (request) + { + case (GET_DESCRIPTOR): + { + switch (*pv) + { + case(1): { LOG(" -> DEVICE"); break; } + case(2): { LOG(" -> CONFIGURATION"); break;} + case(3): { LOG(" -> STRING"); break;} + case( 4): { LOG(" -> INTERFACE"); break;} + case(5): { LOG(" -> ENDPOINT"); break;} + case( 6): { LOG(" -> DEVICE_QUALIFIER"); break;} + case( 7): { LOG(" -> OTHER SPEED_CONFIGURATION"); break;} + case (8) :{ LOG("SET_INTERFACE"); break; } + default: {LOG("->%x",*pv);} ; + }//end switch(value) + break; + }//end case 6 + + case(SET_ADDRESS): + { + LOG("<-this value is the new adress"); break; + }//end case set address. + + default : + { LOG("."); break; + } + } + LOG("\n"); + LOG("w_index = %04x h = %2d d \n",index, index); + LOG("w_length = %04x h = %2d d ",length, length); + if (request==6) LOG(" -> this should be the descriptor/answer length"); + LOG("\n"); + #endif + + return USBTransfer(device,0,request_type & DEVICE_TO_HOST,data,length,callback,userData); +} + +int USBInterruptTransfer(int device, int ep, u8* data, int length, USBCallback callback, void* userData) +{ + LOG("USBinterruptTransfer\n"); + return USBTransfer(device,ep,(ep & 0x80) | ENDPOINT_INTERRUPT,data,length,callback,userData); +} + +int USBBulkTransfer(int device, int ep, u8* data, int length, USBCallback callback, void* userData) +{ + LOG("USBBulkTransfer\n"); + return USBTransfer(device,ep,(ep & 0x80) | ENDPOINT_BULK,data,length,callback,userData); +} + +int GetDescriptor(int device, int descType,int descIndex, u8* data, int length) +{ + return USBControlTransfer(device,DEVICE_TO_HOST | RECIPIENT_DEVICE, GET_DESCRIPTOR,(descType << 8)|(descIndex), 0, data, length, 0); +} + +int GetString(int device, int index, char* dst, int length) +{ + u8 buffer[255]; + int le = GetDescriptor(device,DESCRIPTOR_TYPE_STRING,index,buffer,sizeof(buffer)); + if (le < 0) + return le; + if (length < 1) + return -1; + length <<= 1; + if (le > length) + le = length; + for (int j = 2; j < le; j += 2) + *dst++ = buffer[j]; + *dst = 0; + return (le>>1)-1; +} + +int SetAddress(int device, int new_addr) +{ + return USBControlTransfer(device,HOST_TO_DEVICE | RECIPIENT_DEVICE, SET_ADDRESS, new_addr, 0, 0, 0, 0); +} + +int SetConfiguration(int device, int configNum) +{ + return USBControlTransfer(device,HOST_TO_DEVICE | RECIPIENT_DEVICE, SET_CONFIGURATION, configNum, 0, 0, 0, 0); +} + +int SetInterface(int device, int ifNum, int altNum) +{ + return USBControlTransfer(device,HOST_TO_DEVICE | RECIPIENT_INTERFACE, SET_INTERFACE, altNum, ifNum, 0, 0, 0); +} + +// HUB stuff +int SetPortFeature(int device, int feature, int index) +{ + return USBControlTransfer(device,HOST_TO_DEVICE | REQUEST_TYPE_CLASS | RECIPIENT_OTHER,SET_FEATURE,feature,index,0,0); +} + +int ClearPortFeature(int device, int feature, int index) +{ + return USBControlTransfer(device,HOST_TO_DEVICE | REQUEST_TYPE_CLASS | RECIPIENT_OTHER,CLEAR_FEATURE,feature,index,0,0); +} + +int SetPortPower(int device, int port) +{ + int r = SetPortFeature(device,PORT_POWER,port); + _controller.DelayMS(20); // 80ms to turn on a hubs power... DESCRIPTOR? todo + return r; +} + +int SetPortReset(int device, int port) +{ + return SetPortFeature(device,PORT_RESET,port); +} + +int GetPortStatus(int device, int port, u32* status) +{ + return USBControlTransfer(device,DEVICE_TO_HOST | REQUEST_TYPE_CLASS | RECIPIENT_OTHER,GET_STATUS,0,port,(u8*)status,4); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost.h Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,282 @@ + +/* +I changed a bit the code but all credit goes to the amazing work of : + +Copyright (c) 2010 Peter Barrett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef USBHOST_H +#define USBHOST_H + +#ifndef u8 +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; + +typedef char s8; +typedef short s16; +typedef char s32; +#endif + +#define ENDPOINT_CONTROL 0 +#define ENDPOINT_ISOCRONOUS 1 +#define ENDPOINT_BULK 2 +#define ENDPOINT_INTERRUPT 3 + +#define DESCRIPTOR_TYPE_DEVICE 1 +#define DESCRIPTOR_TYPE_CONFIGURATION 2 +#define DESCRIPTOR_TYPE_STRING 3 +#define DESCRIPTOR_TYPE_INTERFACE 4 +#define DESCRIPTOR_TYPE_ENDPOINT 5 + +#define DESCRIPTOR_TYPE_HID 0x21 +#define DESCRIPTOR_TYPE_REPORT 0x22 +#define DESCRIPTOR_TYPE_PHYSICAL 0x23 +#define DESCRIPTOR_TYPE_HUB 0x29 + +enum USB_CLASS_CODE +{ + CLASS_DEVICE, + CLASS_AUDIO, + CLASS_COMM_AND_CDC_CONTROL, + CLASS_HID, + CLASS_PHYSICAL = 0x05, + CLASS_STILL_IMAGING, + CLASS_PRINTER, + CLASS_MASS_STORAGE, + CLASS_HUB, + CLASS_CDC_DATA, + CLASS_SMART_CARD, + CLASS_CONTENT_SECURITY = 0x0D, + CLASS_VIDEO = 0x0E, + CLASS_DIAGNOSTIC_DEVICE = 0xDC, + CLASS_WIRELESS_CONTROLLER = 0xE0, + CLASS_MISCELLANEOUS = 0xEF, + CLASS_APP_SPECIFIC = 0xFE, + CLASS_VENDOR_SPECIFIC = 0xFF +}; +//CLASS=0x00=DEVICE // This means that the use of the device comes in the Interface Descriptors + +#define DEVICE_TO_HOST 0x80 +#define HOST_TO_DEVICE 0x00 +#define REQUEST_TYPE_CLASS 0x20 +#define RECIPIENT_DEVICE 0x00 +#define RECIPIENT_INTERFACE 0x01 +#define RECIPIENT_ENDPOINT 0x02 +#define RECIPIENT_OTHER 0x03 + +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 +#define SYNCH_FRAME 11 + +// -5 is nak +/* +0010 ACK Handshake +1010 NAK Handshake +1110 STALL Handshake +0110 NYET (No Response Yet) +*/ + +#define IO_PENDING -100 +#define ERR_ENDPOINT_NONE_LEFT -101 +#define ERR_ENDPOINT_NOT_FOUND -102 +#define ERR_DEVICE_NOT_FOUND -103 +#define ERR_DEVICE_NONE_LEFT -104 +#define ERR_HUB_INIT_FAILED -105 +#define ERR_INTERFACE_NOT_FOUND -106 + + +//prefixeds indicators: +// b = byte(8bits) +// w = word(16bits) +// bm= bit map +// bcd = binary-coded-decimal +// i = index +// id = identifier. + + // bDescriptorType values: + // 01h device + // 02h configuration + // 03h string -just for optional descriptive test + // 04h interface + // 05h endpoint + // 06h device_qualifier -for devices theat support both full and high speeds. + // 07h other_speed_configuration + // 08h interface_powr + // 09h OTG -for On-The-Go devices only + // 0Ah debug + // 0Bh interface association -for composite devices + + +// This is the device decriptor: +typedef struct +{ + u8 bLength; // the length in bytes of the descriptor + u8 bDescriptorType; //descriptor! (01h) + u16 bcdUSB; // usb specs version + // usb 2.0 -> 0x0200 + // usb 1.1 -> 0x0110 + // usb 1.0 -> 0x0100 + u8 bDeviceClass; //class code + u8 bDeviceSubClass; //subclass code + u8 bDeviceProtocol; //protocol code + u8 bMaxPacketSize; //maximun packet size for endpoint 0 + u16 idVendor; //vendor ID + u16 idProduct; //product ID + u16 bcdDevice; // device release number + u8 iManufacturer; //index that points a string describing the manufacturer. zero if no string description. + u8 iProduct; //index that points a string describing the product. zero if no string decription + u8 iSerialNumber; //index to a string with the serial number. + u8 bNumConfigurations; //number of possible configurations. +} DeviceDescriptor; // 16 bytes + + +//after retrieving the device decriptor the host can +//ask for device configuration. +//When the configuration descriptor is read, it returns +//the entire configuration hierarchy which includes all +//related interface and endpoint descriptors. +// The wTotalLength field reflects the number of bytes in the hierarchy. +typedef struct +{ + u8 bLength; //length in bytes of the descriptor + u8 bDescriptorType; // the constant CONFIGURATION (02h) + u16 wTotalLength; + u8 bNumInterfaces; //specifies the number of interfaces present for this configuration. + u8 bConfigurationValue; //is used by the SetConfiguration request to select this configuration. + u8 iConfiguration; // Index of String Descriptor describing this configuration + u8 bmAttributes; // Bitmap D7 Reserved, set to 1. (USB 1.0 Bus Powered),D6 Self Powered,D5 Remote Wakeup,D4..0 = 0 + u8 bMaxPower; // Maximum Power Consumption in 2mA units +} ConfigurationDescriptor; + + +// the InterfaceDescriptor provides information +//or features implemented. +//The interface descriptor could be seen as a header +// or grouping of the endpoints into a functional group +// performing a single feature of the device. +typedef struct +{ + u8 bLength; //descriptor size in bytes. + u8 bDescriptorType; // the constant INTERFACE (04h) + u8 bInterfaceNumber; //number identifing this interface + u8 bAlternateSetting; //Can be used to specify alternative interfaces. + //These alternative interfaces can be selected with the Set Interface request. + u8 bNumEndpoints; //number of endpoint supported, not counting the endpoint 0 + u8 bInterfaceClass; //class code + //bInterfaceClass values: + //01 Audio + //02 Comunication interface + //03 HID-> Human Interface Device + //05 Physical + //06 Image + //07 Printer + //08 Mass storage + //09 Hub + //0A Data interface + //0B Smart Card + //0D Content security + //0E Video + //DC diagnostic divice + //E0 wirelless controler + //FE Application specific + //FF vendor specific + u8 bInterfaceSubClass; //subclass code + u8 bInterfaceProtocol; //protocol code + u8 iInterface; // Index of String Descriptor Describing this interface +} InterfaceDescriptor; + + +//Each endpoint in an interface descriptor must have +//an endpoint descriptor. +//Endpoint descriptors are used to describe endpoints +//other than endpoint zero. Endpoint zero is always +//assumed to be a control endpoint and is configured +// before any descriptors are even requested. The host +// will use the information returned from these descriptors +// to determine the bandwidth requirements of the bus. +typedef struct +{ + u8 bLength; //descriptor size in bytes. + u8 bDescriptorType; //the constant ENDPOINT(05h) + u8 bEndpointAddress; //Endpoint number and direction + // Bits 0:3 endpoint, Bits 7 Direction 0 = Out, 1 = In (Ignored for Control Endpoints) + u8 bmAttributes; //specifies the transfer type. + // Bits 0:1 00 = Control, 01 = Isochronous, 10 = Bulk, 11 = Interrupt + u16 wMaxPacketSize; //maximun packet size supported. + u8 bInterval; // Interval for polling endpoint data transfers. +} EndpointDescriptor; + +typedef struct { + u8 bLength; + u8 bDescriptorType; //(0x21h) + u16 bcdHID; // HID specs release number + u8 bCountryCode; //0X00 for no country-code + u8 bNumDescriptors;//the number of class descriptors that + //are subordinated to this descriptor. + u8 bDescriptorType2; //2? the type of descriptor that is subordinated + //to the class descriptor. Minimun 1, due it + //has to have at least 1 report descriptor and/Or + //one or more phsical descriptors. + //A report descriptionr (required) is type 22h. + u16 wDescriptorLength; + + //u8 bDescriptorTyepe; //<- Optional; The type of descriptor that follows + //A physical descriptior is 0x23h + //u16 wDescriptorLength2; + +} HIDDescriptor; + +//============================================================================ +//============================================================================ + + +void USBInit(); +void USBLoop(); +u8* USBGetBuffer(u32* len); + +// Optional callback for transfers, called at interrupt time +typedef void (*USBCallback)(int device, int endpoint, int status, u8* data, int len, void* userData); + +// Transfers +int USBControlTransfer(int device, int request_type, int request, int value, int index, u8* data, int length, USBCallback callback = 0, void* userData = 0); +int USBInterruptTransfer(int device, int ep, u8* data, int length, USBCallback callback = 0, void* userData = 0); +int USBBulkTransfer(int device, int ep, u8* data, int length, USBCallback callback = 0, void* userData = 0); + +// Standard Device methods +int GetDescriptor(int device, int descType, int descIndex, u8* data, int length); +int GetString(int device, int index, char* dst, int length); +int SetAddress(int device, int new_addr); +int SetConfiguration(int device, int configNum); +int SetInterface(int device, int ifNum, int altNum); + +// Implemented to notify app of the arrival of a device +void OnLoadDevice(int device, DeviceDescriptor* deviceDesc, InterfaceDescriptor* interfaceDesc); + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils.cpp Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,48 @@ + + +#include "mbed.h" +#include "Utils.h" + +void printfBytes(const char* s, const u8* data, int len) +{ + printf("%s %d:",s,len); + if (len > 256) + len = 256; + while (len-- > 0) + printf(" %02X",*data++); + printf("\n"); +} + +void printHexLine(const u8* d, int addr, int len) +{ + printf("%04X ",addr); + int i; + for (i = 0; i < len; i++) + printf("%02X ",d[i]); + for (;i < 16; i++) + printf(" "); + char s[16+1]; + memset(s,0,sizeof(s)); + for (i = 0; i < len; i++) + { + int c = d[i]; + if (c < 0x20 || c > 0x7E) + c = '.'; + s[i] = c; + } + printf("%s\n",s); +} + +void printHex(const u8* d, int len) +{ + int addr = 0; + while (len) + { + int count = len; + if (count > 16) + count = 16; + printHexLine(d+addr,addr,count); + addr += 16; + len -= count; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils.h Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,37 @@ + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; + +void DelayMS(int ms); + +void printfBytes(const char* label,const u8* data, int len); +void printHex(const u8* d, int len); + +#ifndef min +#define min(_a,_b) ((_a) < (_b) ? (_a) : (_b)) +#endif + +inline int LE16(const u8* d) +{ + return d[0] | (d[1] << 8); +} + +inline u32 BE32(const u8* d) +{ + return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; +} + +inline void BE32(u32 n, u8* d) +{ + d[0] = (u8)(n >> 24); + d[1] = (u8)(n >> 16); + d[2] = (u8)(n >> 8); + d[3] = (u8)n; +} + +inline void BE16(u32 n, u8* d) +{ + d[0] = (u8)(n >> 8); + d[1] = (u8)n; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,116 @@ +/* +Copyright (c) 2010 Peter Barrett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "mbed.h" +#include "USBHost.h" +#include "Utils.h" +#include "FATFileSystem.h" + +int MassStorage_ReadCapacity(int device, u32* blockCount, u32* blockSize); +int MassStorage_Read(int device, u32 blockAddr, u32 blockCount, u8* dst, u32 blockSize); +int MassStorage_Write(int device, u32 blockAddr, u32 blockCount, u8* dst, u32 blockSize); + +class USBFileSystem : public FATFileSystem +{ + int _device; + u32 _blockSize; + u32 _blockCount; + +public: + USBFileSystem() : FATFileSystem("usb"),_device(0),_blockSize(0),_blockCount(0) + { + } + + void SetDevice(int device) + { + _device = device; + } + + virtual int disk_initialize() + { + return MassStorage_ReadCapacity(_device,&_blockCount,&_blockSize); + } + + virtual int disk_write(const char *buffer, int block_number) + { + return MassStorage_Write(_device,block_number,1,(u8*)buffer,_blockSize); + } + + virtual int disk_read(char *buffer, int block_number) + { + return MassStorage_Read(_device,block_number,1,(u8*)buffer,_blockSize); + } + + virtual int disk_sectors() + { + return _blockCount; + } +}; + + +void DumpFS(int depth, int count) +{ + DIR *d = opendir("/usb"); + if (!d) + { + printf("USB file system borked\n"); + return; + } + + printf("\nDumping root dir\n"); + struct dirent *p; + while (( p = readdir(d))) + { + int len = sizeof( dirent); + printf("%s %d\n", p->d_name, len); + } + closedir(d); +} + +int OnDiskInsert(int device) +{ + USBFileSystem fs; + fs.SetDevice(device); + DumpFS(0,0); + return 0; +} + +/* + Simple test shell to exercise mouse,keyboard,mass storage and hubs. + Add 2 15k pulldown resistors between D+/D- and ground, attach a usb socket and have at it. +*/ + + + +int main() +{ + printf("\n\nhello \n"); + + printf("USBShell\nNow get a bunch of usb things and plug them in. \n"); + printf("remeber de bus goes in little-endian order :S"); + // Serial pc(USBTX, USBRX); + // pc.baud(460800); + + USBInit(); + for (;;) + USBLoop(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Mon Sep 13 12:40:05 2010 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/49a220cc26e0