Radio Junk Box
/
KAMUI_USBHOST_MIDI-CV_Example
KAMUI USB HOST MIDI-CV Example based on Peter Barrett's BlueUSB
Revision 0:3b4e3e2ec6a5, committed 2012-05-11
- Comitter:
- radiojunkbox
- Date:
- Fri May 11 15:31:59 2012 +0000
- Commit message:
- Rev. 0.1 alfa
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AutoEvents.cpp Fri May 11 15:31:59 2012 +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. +*/ + +/* +May 11 2012 RadioJunkBox : added MIDI USB support +*/ + +#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); + +// Implemented in TestShell.cpp +int OnBluetoothInsert(int device); + +// Added by RadioJunkBox ------------------------------------ +// Implemented in MidiUSB.cpp +int OnMidiInsert(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; + case CLASS_WIRELESS_CONTROLLER: + if (interfaceDesc->bInterfaceSubClass == 0x01 && interfaceDesc->bInterfaceProtocol == 0x01) + OnBluetoothInsert(device); // it's bluetooth! + break; +// Added by RadioJunkBox ------------------------------------ + case CLASS_AUDIO: + if (interfaceDesc->bInterfaceSubClass == 0x03 && interfaceDesc->bInterfaceProtocol == 0x00) + OnMidiInsert(device); // it's MIDI! + break; + case CLASS_VENDOR_SPECIFIC: + if (interfaceDesc->bInterfaceSubClass == 0x03 && interfaceDesc->bInterfaceProtocol == 0x00) + OnMidiInsert(device); // it's MIDI! (EDIROL PC-50) + 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 Fri May 11 15:31:59 2012 +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/L2CAP.cpp Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,274 @@ +/* +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 <stdio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "Utils.h" +#include "hci.h" + +#define L2CAP_COMMAND_REJ 0x01 +#define L2CAP_CONN_REQ 0x02 +#define L2CAP_CONN_RSP 0x03 +#define L2CAP_CONF_REQ 0x04 +#define L2CAP_CONF_RSP 0x05 +#define L2CAP_DISCONN_REQ 0x06 +#define L2CAP_DISCONN_RSP 0x07 +#define L2CAP_ECHO_REQ 0x08 +#define L2CAP_ECHO_RSP 0x09 +#define L2CAP_INFO_REQ 0x0a +#define L2CAP_INFO_RSP 0x0b + + + /* L2CAP command codes */ + const char* L2CAP_ComandCodeStr(int c) + { + switch (c) + { + case L2CAP_COMMAND_REJ: return "L2CAP_COMMAND_REJ"; + case L2CAP_CONN_REQ: return "L2CAP_CONN_REQ"; + case L2CAP_CONN_RSP: return "L2CAP_CONN_RSP"; + case L2CAP_CONF_REQ: return "L2CAP_CONF_REQ"; + case L2CAP_CONF_RSP: return "L2CAP_CONF_RSP"; + case L2CAP_DISCONN_REQ: return "L2CAP_DISCONN_REQ"; + case L2CAP_DISCONN_RSP: return "L2CAP_DISCONN_RSP"; + case L2CAP_ECHO_REQ: return "L2CAP_ECHO_REQ"; + case L2CAP_ECHO_RSP: return "L2CAP_ECHO_RSP"; + case L2CAP_INFO_REQ: return "L2CAP_INFO_REQ"; + case L2CAP_INFO_RSP: return "L2CAP_INFO_RSP"; + } + return "unknown"; + } + +typedef struct +{ + u16 handle; + u16 length; // total + u16 l2capLength; // length -4 + u16 cid; // Signaling packet CID = 1 + u8 data[64]; // Largest thing to send!!! todo +} L2CAPData; + +typedef struct +{ + u16 handle; + u16 length; // total + u16 l2capLength; // length -4 + u16 cid; // Signaling packet CID = 1 + + // Payload + u8 cmd; // + u8 id; + u16 cmdLength; // total-8 + u16 params[4]; // Params +} L2CAPCmd; + +// +void BTDevice::Init() +{ + memset(&_info,0,sizeof(inquiry_info)); + _handle = 0; + _name[0] = 0; + _state = 0; +} + +// virtual SocketHandler +int BTDevice::Open(SocketInternal* sock, SocketAddrHdr* addr) +{ + L2CAPSocket* s = (L2CAPSocket*)sock; + L2CAPAddr* a = (L2CAPAddr*)addr; + s->scid = 0x40 + sock->ID-1; // are these reserved? + s->dcid = 0; + Connect(s->scid,a->psm); + return sock->ID; +} + +// virtual SocketHandler +int BTDevice::Send(SocketInternal* sock, const u8* data, int len) +{ + L2CAPData d; + L2CAPSocket* s = (L2CAPSocket*)sock; + + d.handle = _handle | 0x2000; + d.length = 4 + len; + d.l2capLength = len; + d.cid = s->dcid; + + if (len > 64) + return -1; + memcpy(d.data,data,len); + return Send((u8*)&d,len+8); +} + +// virtual SocketHandler +int BTDevice::Close(SocketInternal* sock) +{ + printf("L2CAP close %d\n",sock->ID); + L2CAPSocket* s = (L2CAPSocket*)sock; + return Disconnect(s->scid,s->dcid); +} + +L2CAPSocket* BTDevice::SCIDToSocket(int scid) +{ + return (L2CAPSocket*)GetSocketInternal(scid-0x40+1); +} + +int BTDevice::Send(const u8* data, int len) +{ + _transport->ACLSend(data,len); + return 0; +} + +int BTDevice::Send(u8 c, u8 id, u16* params, int count) +{ + L2CAPCmd cmd; + cmd.handle = _handle | 0x2000; + cmd.length = 8 + count*2; + + cmd.l2capLength = cmd.length-4; + cmd.cid = 1; // Signaling packet + + cmd.cmd = c; + cmd.id = id; + cmd.cmdLength = count*2; + for (int i = 0; i < count; i++) + cmd.params[i] = params[i]; + return Send((u8*)&cmd,cmd.length+4); +} + +int BTDevice::Connect(int scid, int psm) +{ + u16 p[2]; + p[0] = psm; + p[1] = scid; + return Send(L2CAP_CONN_REQ,_txid++,p,2); +} + +int BTDevice::Disconnect(int scid, int dcid) +{ + u16 p[2]; + p[0] = dcid; + p[1] = scid; + return Send(L2CAP_DISCONN_REQ,_txid++,p,2); +} + +int BTDevice::ConfigureRequest(int dcid) +{ + u16 p[4]; + p[0] = dcid; + p[1] = 0; + p[2] = 0x0201; // Options + p[3] = 0x02A0; // 672 + return Send(L2CAP_CONF_REQ,_txid++,p,4); +} + +int BTDevice::ConfigureResponse(u8 rxid, int dcid) +{ + u16 p[3]; + p[0] = dcid; + p[1] = 0; + p[2] = 0; + return Send(L2CAP_CONF_RSP,rxid,p,3); +} + +int BTDevice::DisconnectResponse(u8 rxid, int scid, int dcid) +{ + u16 p[2]; + p[0] = dcid; + p[1] = scid; + return Send(L2CAP_DISCONN_RSP,rxid,p,2); +} + +void BTDevice::Control(const u8* data, int len) +{ + int cc = data[8]; + printf(L2CAP_ComandCodeStr(cc)); + int result = LE16(data+16); + printf(" Result %d\n",result); + switch (cc) + { + case L2CAP_COMMAND_REJ: + break; + case L2CAP_CONN_REQ: + break; + + // Response to our initial connect from Remote + case L2CAP_CONN_RSP: + { + if (result == 0) + { + int dcid = LE16(data+12); + int scid = LE16(data+14); + L2CAPSocket* s = SCIDToSocket(scid); + if (s) + { + s->dcid = dcid; + ConfigureRequest(dcid); + } + } else + printf("Connect failed?\n"); + } + break; + + case L2CAP_CONF_RSP: + { + int scid = LE16(data+12); + SocketInternal* s = (SocketInternal*)SCIDToSocket(scid); + if (s) + s->SetState(SocketState_Open); + } + break; + + case L2CAP_CONF_REQ: + { + int scid = LE16(data+12); + L2CAPSocket* s = SCIDToSocket(scid); + if (s) + ConfigureResponse(data[9],s->dcid); + } + break; + } +} + +void BTDevice::ACLRecv(const u8* data, int len) +{ + // printfBytes("L2CP",data,16); + int handle = LE16(data); + if (handle != (0x2000 | _handle)) + return; + + int cid = LE16(data+6); + if (cid == 1) + { + Control(data,len); + return; + } + + SocketInternal* s = (SocketInternal*)SCIDToSocket(cid); + if (s) + s->Recv(data+8,LE16(data+2)-4); + else + printf("Bad event cid %d\n",cid); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MassStorage.cpp Fri May 11 15:31:59 2012 +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/MidiUSB.cpp Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,107 @@ + +/* +Copyright (c) 2012 RadioJunkBox + +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 MIDIEVT(_class,_subclass,_protocol) (((_class) << 16) | ((_subclass) << 8) | _protocol) +u8 ms_buf[255]; // MIDI Streaming Buffer + +// Ring Buffer +#define BUFSIZE 32 // size of ring buffer (ex 4,8,16,32...) +extern int gPtr_buf_in, gPtr_buf_out; +extern unsigned char gRxBuf[]; + +// Received MIDI Steram +void MidiEventCallback(int device, int endpoint, int status, u8* data, int len, void* userData) +{ + int i; + int ptr = 0; + + while((data[ptr] != 0) && (ptr < len)) + { + // printf("MIDI %02X %02X %02X",device,status,len); + // printfBytes("",data,4); + for( i = 1; i<4; i++) + { + gPtr_buf_in++; + gPtr_buf_in &= (BUFSIZE - 1); + gRxBuf[gPtr_buf_in] = data[ptr+i]; + } + ptr+=4; + } + USBBulkTransfer(device,endpoint,data,len,MidiEventCallback,userData); +} + +// Add MIDI Interface +void AddMidiInterface(int device, InterfaceDescriptor* id, EndpointDescriptor* ed,int len) +{ + if ((ed->bmAttributes & 3) != ENDPOINT_BULK || !(ed->bEndpointAddress & 0x80)) + return; + + // Make bulk enpoints for MIDI devices + u32 evt = MIDIEVT(id->bInterfaceClass,id->bInterfaceSubClass,id->bInterfaceProtocol); + u8* dst = ms_buf; + + if(dst) + { + // printf("Add Midi Interface %02X %08X\r\n",ed->bEndpointAddress,evt); + USBBulkTransfer(device,ed->bEndpointAddress,dst,len,MidiEventCallback,(void*)evt); + } +} + +// Detected Midi Device +int OnMidiInsert(int device) +{ + 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; + + // printf("OnMidiInsert %02X %02X\r\n",device,len); + + while (d < end) + { + if (d[1] == DESCRIPTOR_TYPE_INTERFACE) + { + InterfaceDescriptor* id = (InterfaceDescriptor*)d; + d += d[0]; + while (d < end) + { + if (d[1] == DESCRIPTOR_TYPE_ENDPOINT) + { + // printfBytes("MIDI Endpoint",d,d[0]); + AddMidiInterface(device,id,(EndpointDescriptor*)d,d[4]); + } + d += d[0]; + } + } + d += d[0]; + } + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Socket.cpp Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,140 @@ +/* +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 <stdio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "Utils.h" +#include "Socket.h" + +#define MAX_SOCKET_HANDLERS 3 +#define MAX_SOCKETS 16 + +class SocketInternalPad +{ + public: + SocketInternal si; + u8 pad[8]; +}; + +class SocketManager +{ + SocketHandler* _handlers[MAX_SOCKET_HANDLERS]; + SocketInternalPad _sockets[MAX_SOCKETS]; + + public: + SocketManager() + { + memset(_handlers,0,sizeof(_handlers)); + memset(_sockets,0,sizeof(_sockets)); + } + + SocketHandler* GetHandler(int type) + { + if (type < 1 || type > MAX_SOCKET_HANDLERS) + return 0; + return _handlers[type - 1]; + } + + SocketInternal* GetInternal(int s) + { + if (s < 1 || s > MAX_SOCKETS) + return 0; + return &_sockets[s - 1].si; + } + + int RegisterSocketHandler(int type, SocketHandler* handler) + { + if (type < 1 || type > MAX_SOCKET_HANDLERS) + return ERR_SOCKET_TYPE_NOT_FOUND; + _handlers[type - 1] = handler; + return 0; + } + + int Open(int type, SocketAddrHdr* addr, SocketCallback callback, void* userData) + { + SocketHandler* h = GetHandler(type); + if (!h) + return ERR_SOCKET_TYPE_NOT_FOUND; + + for (int i = 0; i < MAX_SOCKETS; i++) + { + SocketInternal* si = (SocketInternal*)(_sockets+i); + if (si->ID == 0) + { + si->ID = i+1; + si->Type = type; + si->Callback = callback; + si->userData = userData; + return h->Open(si,addr); + } + } + return ERR_SOCKET_NONE_LEFT; + } + + int Send(int socket, const u8* data, int len) + { + SocketInternal* si = GetInternal(socket); + if (!si || si->ID != socket) + return ERR_SOCKET_NOT_FOUND; + return GetHandler(si->Type)->Send(si,data,len); + } + + int Close(int socket) + { + SocketInternal* si = GetInternal(socket); + if (!si || si->ID != socket) + return ERR_SOCKET_NOT_FOUND; + si->ID = 0; + return GetHandler(si->Type)->Close(si); + } +}; + +SocketManager gSocketManager; + +int Socket_Open(int type, SocketAddrHdr* addr, SocketCallback callback, void* userData) +{ + return gSocketManager.Open(type,addr,callback,userData); +} + +int Socket_Send(int socket, const u8* data, int len) +{ + return gSocketManager.Send(socket,data,len); +} + +int Socket_Close(int socket) +{ + return gSocketManager.Close(socket); +} + +int RegisterSocketHandler(int type, SocketHandler* handler) +{ + return gSocketManager.RegisterSocketHandler(type,handler); +} + +SocketInternal* GetSocketInternal(int socket) +{ + return gSocketManager.GetInternal(socket); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Socket.h Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,96 @@ +/* +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 SOCKET_H_INCLUDED +#define SOCKET_H_INCLUDED + +#define SOCKET_HCI 1 +#define SOCKET_L2CAP 2 +#define SOCKET_RFCOM 3 + +typedef struct +{ + u8 AddressSpecific[0]; // BDADDR,psm etc +} SocketAddrHdr; + +enum SocketState +{ + SocketState_Unknown, + SocketState_Opening, + SocketState_Open, + SocketState_Closing, + SocketState_Closed +}; + +typedef void (*SocketCallback)(int socket, SocketState state, const u8* data, int len, void* userData); + +int Socket_Open(int type, SocketAddrHdr* addr, SocketCallback callback, void* userData); // Open a socket +int Socket_Send(int socket, const u8* data, int len); +int Socket_State(int socket); +int Socket_Close(int socket); + +//=========================================================================== +//=========================================================================== +// Don't need to look at or use anything below this line: +// Internal representation of socket + +class SocketHandler; +class SocketInternal +{ + public: + + u8 ID; + u8 State; + u8 Type; + u8 B0; + SocketCallback Callback; + void* userData; + u8 Data[0]; // Extra socket data starts here + + void Recv(const u8* data, int len) + { + Callback(ID,(SocketState)State,data,len,userData); + } + + void SetState(SocketState state) + { + State = state; + Callback(ID,(SocketState)State,0,0,userData); + } +}; + +class SocketHandler +{ + public: + virtual int Open(SocketInternal* sock, SocketAddrHdr* addr) = 0; + virtual int Send(SocketInternal* sock, const u8* data, int len) = 0; + virtual int Close(SocketInternal* sock) = 0; +}; + +int RegisterSocketHandler(int type, SocketHandler* handler); +SocketInternal* GetSocketInternal(int socket); + +#define ERR_SOCKET_TYPE_NOT_FOUND -200 +#define ERR_SOCKET_NOT_FOUND -201 +#define ERR_SOCKET_NONE_LEFT -202 + +#endif // SOCKET_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TestShell.cpp Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,400 @@ + +/* +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. +*/ + +/* +May 11 2012 Modified by RadioJunkBox +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "Utils.h" +#include "USBHost.h" +#include "hci.h" + +void printf(const BD_ADDR* addr) +{ + const u8* a = addr->addr; + printf("%02X:%02X:%02X:%02X:%02X:%02X",a[5],a[4],a[3],a[2],a[1],a[0]); +} + +#define MAX_HCL_SIZE 260 +#define MAX_ACL_SIZE 400 + +class HCITransportUSB : public HCITransport +{ + int _device; + u8* _hciBuffer; + u8* _aclBuffer; + + public: + void Open(int device, u8* hciBuffer, u8* aclBuffer) + { + _device = device; + _hciBuffer = hciBuffer; + _aclBuffer = aclBuffer; + USBInterruptTransfer(_device,0x81,_hciBuffer,MAX_HCL_SIZE,HciCallback,this); + USBBulkTransfer(_device,0x82,_aclBuffer,MAX_ACL_SIZE,AclCallback,this); + } + + static void HciCallback(int device, int endpoint, int status, u8* data, int len, void* userData) + { + HCI* t = ((HCITransportUSB*)userData)->_target; + if (t) + t->HCIRecv(data,len); + USBInterruptTransfer(device,0x81,data,MAX_HCL_SIZE,HciCallback,userData); + } + + static void AclCallback(int device, int endpoint, int status, u8* data, int len, void* userData) + { + HCI* t = ((HCITransportUSB*)userData)->_target; + if (t) + t->ACLRecv(data,len); + USBBulkTransfer(device,0x82,data,MAX_ACL_SIZE,AclCallback,userData); + } + + virtual void HCISend(const u8* data, int len) + { + USBControlTransfer(_device,REQUEST_TYPE_CLASS, 0, 0, 0,(u8*)data,len); + } + + virtual void ACLSend(const u8* data, int len) + { + USBBulkTransfer(_device,0x02,(u8*)data,len); + } +}; + + +#define WII_REMOTE 0x042500 + +class HIDBluetooth +{ + int _control; // Sockets for control (out) and interrupt (in) + int _interrupt; + int _devClass; + BD_ADDR _addr; + u8 _pad[2]; // Struct align + +public: + HIDBluetooth() : _control(0),_interrupt(0),_devClass(0) {}; + + bool InUse() + { + return _control != 0; + } + + static void OnHidInterrupt(int socket, SocketState state, const u8* data, int len, void* userData) + { + HIDBluetooth* t = (HIDBluetooth*)userData; + if (data) + { + if (t->_devClass == WII_REMOTE && data[1] == 0x30) + { + printf("================wii====================\n"); + t->Led(); + t->Hid(); // ask for accelerometer + t->_devClass = 0; + } + + const u8* d = data; + switch (d[1]) + { + case 0x02: + { + int x = (signed char)d[3]; + int y = (signed char)d[4]; + printf("Mouse %2X dx:%d dy:%d\n",d[2],x,y); + } + break; + + case 0x37: // Accelerometer http://wiki.wiimoteproject.com/Reports + { + int pad = (d[2] & 0x9F) | ((d[3] & 0x9F) << 8); + int x = (d[2] & 0x60) >> 5 | d[4] << 2; + int y = (d[3] & 0x20) >> 4 | d[5] << 2; + int z = (d[3] & 0x40) >> 5 | d[6] << 2; + printf("WII %04X %d %d %d\n",pad,x,y,z); + } + break; + default: + printHex(data,len); + } + } + } + + static void OnHidControl(int socket, SocketState state, const u8* data, int len, void* userData) + { + printf("OnHidControl\n"); + if (data) + printHex(data,len); + } + + void Open(BD_ADDR* bdAddr, inquiry_info* info) + { + printf("L2CAPAddr size %d\n",sizeof(L2CAPAddr)); + _addr = *bdAddr; + L2CAPAddr sockAddr; + sockAddr.bdaddr = _addr; + sockAddr.psm = L2CAP_PSM_HID_INTR; + printf("Socket_Open size %d\n",sizeof(L2CAPAddr)); + _interrupt = Socket_Open(SOCKET_L2CAP,&sockAddr.hdr,OnHidInterrupt,this); + sockAddr.psm = L2CAP_PSM_HID_CNTL; + _control = Socket_Open(SOCKET_L2CAP,&sockAddr.hdr,OnHidControl,this); + + printfBytes("OPEN DEVICE CLASS",info->dev_class,3); + _devClass = (info->dev_class[0] << 16) | (info->dev_class[1] << 8) | info->dev_class[2]; + } + + void Close() + { + if (_control) + Socket_Close(_control); + if (_interrupt) + Socket_Close(_interrupt); + _control = _interrupt = 0; + } + + void Led(int id = 0x10) + { + u8 led[3] = {0x52, 0x11, id}; + if (_control) + Socket_Send(_control,led,3); + } + + void Hid(int report = 0x37) + { + u8 hid[4] = { 0x52, 0x12, 0x00, report }; + if (_control != -1) + Socket_Send(_control,hid,4); + } +}; + + +HCI* gHCI = 0; + +#define MAX_HID_DEVICES 8 + +int GetConsoleChar(); +class ShellApp +{ + char _line[64]; + HIDBluetooth _hids[MAX_HID_DEVICES]; + +public: + void Ready() + { + printf("HIDBluetooth %d\n",sizeof(HIDBluetooth)); + memset(_hids,0,sizeof(_hids)); + Inquiry(); + + } + + // We have connected to a device + void ConnectionComplete(HCI* hci, connection_info* info) + { + printf("ConnectionComplete "); + BD_ADDR* a = &info->bdaddr; + printf(a); + BTDevice* bt = hci->Find(a); + HIDBluetooth* hid = NewHIDBluetooth(); + printf("%08x %08x\n",bt,hid); + if (hid) + hid->Open(a,&bt->_info); + } + + HIDBluetooth* NewHIDBluetooth() + { + for (int i = 0; i < MAX_HID_DEVICES; i++) + if (!_hids[i].InUse()) + return _hids+i; + return 0; + } + + void ConnectDevices() + { + BTDevice* devs[8]; + int count = gHCI->GetDevices(devs,8); + for (int i = 0; i < count; i++) + { + printfBytes("DEVICE CLASS",devs[i]->_info.dev_class,3); + if (devs[i]->_handle == 0) + { + BD_ADDR* bd = &devs[i]->_info.bdaddr; + printf("Connecting to "); + printf(bd); + printf("\n"); + gHCI->CreateConnection(bd); + } + } + } + + const char* ReadLine() + { + int i; + for (i = 0; i < 255; ) + { + USBLoop(); + int c = GetConsoleChar(); + if (c == -1) + continue; + if (c == '\n' || c == 13) + break; + _line[i++] = c; + } + _line[i] = 0; + return _line; + } + + void Inquiry() + { + printf("Inquiry..\n"); + gHCI->Inquiry(); + } + + void List() + { + #if 0 + printf("%d devices\n",_deviceCount); + for (int i = 0; i < _deviceCount; i++) + { + printf(&_devices[i].info.bdaddr); + printf("\n"); + } + #endif + } + + void Connect() + { + ConnectDevices(); + } + + void Disconnect() + { + gHCI->DisconnectAll(); + } + + void CloseMouse() + { + } + + void Quit() + { + CloseMouse(); + } + + void Run() + { + for(;;) + { + const char* cmd = ReadLine(); + if (strcmp(cmd,"scan") == 0 || strcmp(cmd,"inquiry") == 0) + Inquiry(); + else if (strcmp(cmd,"ls") == 0) + List(); + else if (strcmp(cmd,"connect") == 0) + Connect(); + else if (strcmp(cmd,"disconnect") == 0) + Disconnect(); + else if (strcmp(cmd,"q")== 0) + { + Quit(); + break; + } else { + printf("eh? %s\n",cmd); + } + } + } +}; + +// Instance +ShellApp gApp; + +static int HciCallback(HCI* hci, HCI_CALLBACK_EVENT evt, const u8* data, int len) +{ + switch (evt) + { + case CALLBACK_READY: + printf("CALLBACK_READY\n"); + gApp.Ready(); + break; + + case CALLBACK_INQUIRY_RESULT: + printf("CALLBACK_INQUIRY_RESULT "); + printf((BD_ADDR*)data); + printf("\n"); + break; + + case CALLBACK_INQUIRY_DONE: + printf("CALLBACK_INQUIRY_DONE\n"); + gApp.ConnectDevices(); + break; + + case CALLBACK_REMOTE_NAME: + { + BD_ADDR* addr = (BD_ADDR*)data; + const char* name = (const char*)(data + 6); + printf(addr); + printf(" % s\n",name); + } + break; + + case CALLBACK_CONNECTION_COMPLETE: + gApp.ConnectionComplete(hci,(connection_info*)data); + break; + }; + return 0; +} + +// these should be placed in the DMA SRAM +typedef struct +{ + u8 _hciBuffer[MAX_HCL_SIZE]; + u8 _aclBuffer[MAX_ACL_SIZE]; +} SRAMPlacement; + +HCITransportUSB _HCITransportUSB; +HCI _HCI; + +u8* USBGetBuffer(u32* len); +int OnBluetoothInsert(int device) +{ + printf("Bluetooth inserted of %d\n",device); + u32 sramLen; + u8* sram = USBGetBuffer(&sramLen); + sram = (u8*)(((u32)sram + 1023) & ~1023); + SRAMPlacement* s = (SRAMPlacement*)sram; + _HCITransportUSB.Open(device,s->_hciBuffer,s->_aclBuffer); + _HCI.Open(&_HCITransportUSB,HciCallback); + RegisterSocketHandler(SOCKET_L2CAP,&_HCI); + gHCI = &_HCI; + gApp.Inquiry(); + return 0; +} + +void TestShell() +{ +// USBInit(); // Modified by RadioJunkBox + gApp.Run(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TextLCD.lib Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/simon/code/TextLCD/#44f34c09bd37
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost.cpp Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,1079 @@ + +/* +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. +*/ + +/* +May 11 2012 RadioJunkBox : added MIDI USB support +*/ + +#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; + 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; + } + } + + // 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) + { + //LOG("Transfer %02X T:%d Len:%d S:%d\n",endpoint->Address(),token,len,state); + + 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; + head->BufEnd = (u32)(data + len - 1); + 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; + } + + // 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",ep,endpoint->TDHead.Control); + + if (status != 0) + { + LOG("ProcessDoneQueue status %02X %d\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) + { + 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; +// Modified by RadioJunkBox --------------------------------- + if (id->bNumEndpoints != 0) +// 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",hub,port,lowspeed ? "slow" : "full"); + 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); + + // 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; + LPC_USB->HcControlHeadED = 0; + LPC_USB->HcBulkHeadED = 0; + + LPC_USB->HcCommandStatus = HostControllerReset; + 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; + LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; + LPC_USB->HcInterruptEnable = MasterInterruptEnable | WritebackDoneHead | RootHubStatusChange | FrameNumberOverflow; + + NVIC_SetPriority(USB_IRQn, 0); + NVIC_EnableIRQ(USB_IRQn); + 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; + + 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; + 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) +{ + 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) +{ + 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 Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,200 @@ + +/* +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 +}; + +#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 + +typedef struct +{ + u8 bLength; + u8 bDescriptorType; + u16 bcdUSB; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize; + u16 idVendor; + u16 idProduct; + u16 bcdDevice; // version + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; +} DeviceDescriptor; // 16 bytes + +typedef struct +{ + u8 bLength; + u8 bDescriptorType; + u16 wTotalLength; + u8 bNumInterfaces; + u8 bConfigurationValue; // Value to use as an argument 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; + +typedef struct +{ + u8 bLength; + u8 bDescriptorType; + u8 bInterfaceNumber; + u8 bAlternateSetting; + u8 bNumEndpoints; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + u8 iInterface; // Index of String Descriptor Describing this interface +} InterfaceDescriptor; + +typedef struct +{ + u8 bLength; + u8 bDescriptorType; + u8 bEndpointAddress; // Bits 0:3 endpoint, Bits 7 Direction 0 = Out, 1 = In (Ignored for Control Endpoints) + u8 bmAttributes; // Bits 0:1 00 = Control, 01 = Isochronous, 10 = Bulk, 11 = Interrupt + u16 wMaxPacketSize; + u8 bInterval; // Interval for polling endpoint data transfers. +} EndpointDescriptor; + +typedef struct { + u8 bLength; + u8 bDescriptorType; + u16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; + u8 bDescriptorType2; + u16 wDescriptorLength; +} 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 Fri May 11 15:31:59 2012 +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 Fri May 11 15:31:59 2012 +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/hci.cpp Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,426 @@ + +/* +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 <stdio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "Utils.h" +#include "hci.h" +#include "hci_private.h" + +enum hci_callback_evt +{ + NONE, + CONNECT, + DISCONECT, + INQUIRYRESULT +}; + +#define MAX_BLUETOOTH_ADAPTERS 1 + +enum StateMask { + MASK_RESET = 1, + MASK_READ_BUFFER_SIZE = 2, + MASK_READ_BD_ADDR = 4, + MASK_INITED = 8, + MASK_INQUIRY = 16, + MASK_REMOTE_NAME = 32, + MASK_CREATE_CONNECTION = 64 +}; + +int HCI::Open(HCITransport* transport, HCICallback callback) +{ + _transport = transport; + _transport->Set(this); + _callback = callback; + _state = 0; + for (int i = 0; i < MAX_BTDEVICES; i++) + { + _devices[i].Init(); + _devices[i]._transport = transport; + } + return SendCmd(HCI_OP_RESET); +} + +void printf(const BD_ADDR* addr); + +BTDevice* HCI::Find(const BD_ADDR* addr) +{ + for (int i = 0; i < MAX_BTDEVICES; i++) + if (_devices[i]._state != 0 && memcmp(addr,&_devices[i]._info.bdaddr,6) == 0) + return &_devices[i]; + return 0; +} + +BTDevice* HCI::Find(int handle) +{ + for (int i = 0; i < MAX_BTDEVICES; i++) + if (_devices[i]._state != 0 && handle == _devices[i]._handle) + return &_devices[i]; + return 0; +} +// +bool HCI::Busy() +{ + return (_state & (MASK_INQUIRY | MASK_REMOTE_NAME | MASK_CREATE_CONNECTION)) != 0; +} + +int HCI::Inquiry(int duration) +{ + _state |= MASK_INQUIRY; + u8 buf[5]; + buf[0] = 0x33; + buf[1] = 0x8B; + buf[2] = 0x9E; + buf[3] = duration; + buf[4] = 5; // 5 results + SendCmd(HCI_OP_INQUIRY,buf,sizeof(buf)); + return 0; +} + +int HCI::SendCmd(int cmd, const u8* params, int len) +{ + u8 b[32]; + b[0] = cmd; + b[1] = (cmd >> 8); + b[2] = len; + if (params) + memcpy(b+3,params,len); + _transport->HCISend(b,len+3); + return 0; +} + +void HCI::OnCommandComplete(int cmd, const u8* data, int len) +{ + // printf("%04X %s",cmd,CmdStr(cmd)); + if (len < 0) + return; + //printfBytes(" complete",data,min(16,len)); + + switch (cmd) + { + // Init phase 0 + case HCI_OP_RESET: // Reset done, init chain to HCI_OP_READ_LOCAL_NAME + SendCmd(HCI_OP_READ_BUFFER_SIZE); + _state |= MASK_RESET; + break; + + // Init phase 1 + case HCI_OP_READ_BUFFER_SIZE: + _acl_mtu = LE16(data); + _sco_mtu = data[2]; + _acl_max_pkt = LE16(data+3); + _sco_max_pkt = LE16(data+5); + SendCmd(HCI_OP_READ_BD_ADDR); + _state |= MASK_READ_BUFFER_SIZE; + break; + + // Init phase 2 + case HCI_OP_READ_BD_ADDR: + _localAddr = *((BD_ADDR*)data); // Local Address + _state |= MASK_READ_BD_ADDR; + _state |= MASK_INITED; + Callback(CALLBACK_READY,data,6); + break; + + // 0CXX + case HCI_OP_READ_LOCAL_NAME: + break; + + case HCI_OP_READ_LOCAL_VERSION: + // params + //SendCmd(HCI_OP_READ_LOCAL_NAME); + break; + + case HCI_OP_READ_LOCAL_COMMANDS: + break; + + case HCI_OP_READ_LOCAL_FEATURES: + //SendCmd(HCI_OP_READ_LOCAL_VERSION); + break; + + case HCI_OP_READ_LOCAL_EXT_FEATURES: + break; + + case HCI_OP_PIN_CODE_REPLY: + printf("Got pin reply\n"); + break; + + default: + printf("Unrecognized Command %04X\n",cmd); + break; + } +} + +void HCI::Callback(HCI_CALLBACK_EVENT c, const u8* data, int len) +{ + _callback(this,c,data,len); +} + +int HCI::RemoteNameRequest(const BD_ADDR* addr) +{ + _state |= MASK_REMOTE_NAME; + u8 buf[6+4]; + memset(buf,0,sizeof(buf)); + memcpy(buf,addr,6); + buf[7] = 1; + return SendCmd(HCI_OP_REMOTE_NAME_REQ,buf,sizeof(buf)); +} + +int HCI::CreateConnection(const BD_ADDR* remoteAddr) +{ + _state |= MASK_CREATE_CONNECTION; + u8 buf[6+7]; + memset(buf,0,sizeof(buf)); + memcpy(buf,remoteAddr,6); + buf[6] = 0x18; // DM1,DH1 + buf[7] = 0xCC; // DM3, DH3, DM5, DH5 + buf[8] = 1; // Page Repetition R1 + return SendCmd(HCI_OP_CREATE_CONN,buf,sizeof(buf)); +} + +int HCI::Disconnect(const BD_ADDR* bdaddr) +{ + BTDevice* d = Find(bdaddr); + if (!d) + return ERR_HCI_DEVICE_NOT_FOUND; + int handle = d->_handle; + printf("Disconnect from %d\n",handle); + _state |= MASK_CREATE_CONNECTION; + u8 buf[3]; + buf[0] = handle; + buf[1] = (handle >> 8); + buf[2] = 0x13; + return SendCmd(HCI_OP_DISCONNECT,buf,sizeof(buf)); +} + +void HCI::DisconnectComplete(int handle) +{ + BTDevice* d = Find(handle); + if (!d) + return; + d->_handle = 0; +} + +int HCI::DisconnectAll() +{ + BTDevice* devs[8]; + int count = GetDevices(devs,8); + for (int i = 0; i < count; i++) + Disconnect(&devs[i]->_info.bdaddr); + return 0; +} + +int HCI::PinCodeReply(const u8* data) +{ + u8 b[6+1+16]; + memset(b,0,sizeof(b)); + memcpy(b,data,6); + b[6] = 4; + b[7] = '0'; + b[8] = '0'; + b[9] = '0'; + b[10] = '0'; + return SendCmd(HCI_OP_PIN_CODE_REPLY,b,sizeof(b)); +} + +void HCI::InquiryResult(const inquiry_info* info) +{ + BTDevice* bt = Find(&info->bdaddr); + if (!bt) // new device + { + for (int i = 0; i < MAX_BTDEVICES; i++) + { + if (_devices[i]._state == 0) + { + bt = _devices + i; + bt->_state = 1; + break; + } + } + if (!bt) + { + printf("HCI::InquiryResult too many devices\n"); + return; // Too many devices! + } + } + + bt->_info = *info; +} + +int HCI::GetDevices(BTDevice** devices, int maxDevices) +{ + int j = 0; + for (int i = 0; i < MAX_BTDEVICES; i++) + { + if (_devices[i]._state != 0) + { + devices[j++] = _devices + i; + if (j == maxDevices) + break; + } + } + return j; +} + +void HCI::RemoteName(const BD_ADDR* addr, const char* name) +{ + BTDevice* d = Find(addr); + if (d) + { + strncpy(d->_name,name,sizeof(d->_name)-1); + d->_name[sizeof(d->_name)-1] = 0; + } +} + +void HCI::ConnectComplete(const connection_info* info) +{ + BTDevice* d = Find(&info->bdaddr); + if (!d) + return; + if (info->status == 0) + { + d->_handle = info->handle; + printf("Connected on %04X\n",info->handle); + } else + printf("Connection failed with %d\n",info->status); +} + +void HCI::HCIRecv(const u8* data, int len) +{ + // printfBytes(EvtStr(data[0]),data,min(len,16)); + switch (data[0]) + { + case HCI_EV_INQUIRY_COMPLETE: + printfBytes("Inquiry Complete",data,data[1]); + _state &= ~MASK_INQUIRY; + Callback(CALLBACK_INQUIRY_DONE,0,0); + break; + + case HCI_EV_INQUIRY_RESULT: + { + const u8* end = data[1] + data + 2; + data += 3; + while (data < end) + { + inquiry_info align; + memcpy(&align,data,sizeof(inquiry_info)); + InquiryResult(&align); + Callback(CALLBACK_INQUIRY_RESULT,(u8*)&align,sizeof(inquiry_info)); + data += 14; + } + } + break; + + case HCI_EV_CONN_COMPLETE: + _state &= ~MASK_CREATE_CONNECTION; + { + connection_info align; + memcpy(&align,data+2,sizeof(connection_info)); + ConnectComplete(&align); + Callback(CALLBACK_CONNECTION_COMPLETE,(u8*)&align,sizeof(connection_info)); + } + break; + + case HCI_EV_CONN_REQUEST: + break; + + case HCI_EV_DISCONN_COMPLETE: + DisconnectComplete(LE16(data+3)); + break; + + case HCI_EV_REMOTE_NAME: + { + BD_ADDR* addr = (BD_ADDR*)(data+3); + const char* name = (const char*)(data + 9); + RemoteName(addr,name); + } + Callback(CALLBACK_REMOTE_NAME,data+3,LE16(data+1)); // addr is in here too + _state &= ~MASK_REMOTE_NAME; + break; + + case HCI_EV_CMD_STATUS: + { + const char* errs = HCIErrStr(data[2]); + printf("Status %s %s\n",CmdStr(LE16(data+4)),errs); + } + break; + + case HCI_EV_CMD_COMPLETE: + OnCommandComplete(data[3] | (data[4] << 8),data+6,data[1]-4); + break; + + case HCI_EV_PIN_CODE_REQ: + PinCodeReply(data+2); + break; + + case HCI_EV_LINK_KEY_REQ: + SendCmd(HCI_OP_LINK_KEY_NEG_REPLY,data+2,6); + break; + + default: + ; + // printfBytes(":",data,data[1]+2); + } +} + +int HCI::Open(SocketInternal* sock, SocketAddrHdr* addr) +{ + L2CAPSocket* l2capsock = (L2CAPSocket*)sock; + L2CAPAddr* l2capaddr = (L2CAPAddr*)addr; + BTDevice* bt = Find(&l2capaddr->bdaddr); + if (!bt) + { + printf("Can't open l2cap %d on ",l2capaddr->psm); + printf(&l2capaddr->bdaddr); + printf("\n"); + return ERR_HCI_DEVICE_NOT_FOUND; + } + l2capsock->btdevice = bt; + return bt->Open(sock,addr); +} + +int HCI::Send(SocketInternal* sock, const u8* data, int len) +{ + L2CAPSocket* l2capsock = (L2CAPSocket*)sock; + return l2capsock->btdevice->Send(sock,data,len); // Pointless double dispatch +} + +int HCI::Close(SocketInternal* sock) +{ + L2CAPSocket* l2capsock = (L2CAPSocket*)sock; + return l2capsock->btdevice->Close(sock); // Pointless double dispatch +} + +void HCI::ACLRecv(const u8* data, int len) +{ + int handle = LE16(data); + BTDevice* d = Find(handle & 0x0FFF); + if (d) + d->ACLRecv(data,len); +} + +//=================================================================== +//===================================================================
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hci.h Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,224 @@ +/* +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 HCI_H_INCLUDED +#define HCI_H_INCLUDED + +#include "Socket.h" + +#pragma pack(1) + +#define ERR_HCI_DEVICE_NOT_FOUND -300 + +class HCI; +class HCITransport; +class BTDevice; + +typedef struct +{ + u8 addr[6]; +} BD_ADDR; + +typedef struct +{ + BD_ADDR bdaddr; + u8 pscan_rep_mode; + u8 pscan_period_mode; + u8 pscan_mode; + u8 dev_class[3]; + u16 clock_offset; +} inquiry_info; + +typedef struct +{ + u8 status; + u16 handle; + BD_ADDR bdaddr; + u8 link_type; + u8 encr_mode; +} connection_info; + +// Address struct for creating L2CAP sockets +typedef struct { + SocketAddrHdr hdr; + BD_ADDR bdaddr; + u16 psm; +} L2CAPAddr; + +#pragma pack(4) + +class BTDevice; +typedef struct +{ + public: + SocketInternal si; + BTDevice* btdevice; + u16 scid; + u16 dcid; +} L2CAPSocket; + +#define MAX_HCL_NAME_LENGTH 20 // TODO - BTDevice wants to be a multiple of 4 + +// BTDevice encapsulates individual device state +// It provides L2CAP layer sockets + +class BTDevice : public SocketHandler +{ + public: + HCITransport* _transport; + inquiry_info _info; + u16 _handle; // acl connection handle + u8 _state; // connection state + u8 _txid; + char _name[MAX_HCL_NAME_LENGTH]; + + void Init(); + + BD_ADDR* GetAddress() { return &_info.bdaddr; } + + // Called from HCI + void ACLRecv(const u8* data, int len); + + // SocketHandler + virtual int Open(SocketInternal* sock, SocketAddrHdr* addr); + virtual int Send(SocketInternal* sock, const u8* data, int len); + virtual int Close(SocketInternal* sock); + +private: + L2CAPSocket* SCIDToSocket(int scid); + int Send(const u8* data, int len); + int Send(u8 c, u8 id, u16* params, int count); + int Connect(int scid, int psm); + int Disconnect(int scid, int dcid); + int ConfigureRequest(int dcid); + int ConfigureResponse(u8 rxid, int dcid); + int DisconnectResponse(u8 rxid, int scid, int dcid); + void Control(const u8* data, int len); +}; + +enum HCI_CALLBACK_EVENT +{ + CALLBACK_NONE, + CALLBACK_READY, + CALLBACK_INQUIRY_RESULT, + CALLBACK_INQUIRY_DONE, + CALLBACK_REMOTE_NAME, + CALLBACK_CONNECTION_COMPLETE, + CALLBACK_CONNECTION_FAILED +}; + +// L2CAP Protocol/Service Multiplexor (PSM) values + +#define L2CAP_PSM_ANY 0x0000 /* Any/Invalid PSM */ +#define L2CAP_PSM_SDP 0x0001 /* Service Discovery Protocol */ +#define L2CAP_PSM_RFCOMM 0x0003 /* RFCOMM protocol */ +#define L2CAP_PSM_TCP 0x0005 /* Telephony Control Protocol */ +#define L2CAP_PSM_TCS 0x0007 /* TCS cordless */ +#define L2CAP_PSM_BNEP 0x000f /* Bluetooth Network Encapsulation Protocol*/ +#define L2CAP_PSM_HID_CNTL 0x0011 /* HID Control */ +#define L2CAP_PSM_HID_INTR 0x0013 /* HID Interrupt */ +#define L2CAP_PSM_ESDP 0x0015 /* Extended Service Discovery Profile */ +#define L2CAP_PSM_AVCTP 0x0017 /* Audio/Visual Control Transport Protocol */ +#define L2CAP_PSM_AVDTP 0x0019 /* Audio/Visual Distribution */ + +// Callback from inquiry +typedef int (*HCICallback)(HCI* hci, HCI_CALLBACK_EVENT evt, const u8* data, int len); + +#define MAX_BTDEVICES 8 + +class HCITransport; +class HCI : public SocketHandler +{ + HCITransport* _transport; + HCICallback _callback; + BD_ADDR _localAddr; + + BTDevice _devices[MAX_BTDEVICES]; + int _deviceCount; + + int _acl_mtu; + int _acl_max_pkt; + int _sco_mtu; + int _sco_max_pkt; + + int _state; + + public: + + // Open a local adapter + int Open(HCITransport* transport, HCICallback callback); + + // Return list of discovered addreses + int GetDevices(BTDevice** devices, int maxDevices); + + // Lookup a device by address or handle + BTDevice* Find(const BD_ADDR* addr); + BTDevice* Find(int handle); + + // Disconnect from a remote device + int Disconnect(const BD_ADDR* addr); + int DisconnectAll(); + + // see what devies are in the system + int Inquiry(int duration = 10); + + // get a name, delivered in callback + int RemoteNameRequest(const BD_ADDR* addr); + + // Connect to a remote device + int CreateConnection(const BD_ADDR* remoteAddr); + + bool Busy(); + + // called from transport + void HCIRecv(const u8* data, int len); + + // called from transport + void ACLRecv(const u8* data, int len); + + // SocketHandler methods for maintaining L2CAP sockets + virtual int Open(SocketInternal* sock, SocketAddrHdr* addr); + virtual int Send(SocketInternal* sock, const u8* data, int len); + virtual int Close(SocketInternal* sock); + + private: + void InquiryResult(const inquiry_info* info); + void RemoteName(const BD_ADDR* addr, const char* name); + void ConnectComplete(const connection_info* info); + void DisconnectComplete(int handle); + int SendCmd(int cmd, const u8* params = 0, int len = 0); + void OnCommandComplete(int cmd, const u8* data, int len); + void Callback(HCI_CALLBACK_EVENT c, const u8* data, int len); + int PinCodeReply(const u8* data); +}; + +class HCITransport +{ +protected: + HCI* _target; +public: + void Set(HCI* target) { _target = target; }; + virtual void HCISend(const u8* data, int len) = 0; + virtual void ACLSend(const u8* data, int len) = 0; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hci_private.h Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,323 @@ +/* +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 HCI_PRIVATE_H_INCLUDED +#define HCI_PRIVATE_H_INCLUDED + +#define HCI_OP_INQUIRY 0x0401 +#define HCI_OP_INQUIRY_CANCEL 0x0402 +#define HCI_OP_EXIT_PERIODIC_INQ 0x0404 +#define HCI_OP_CREATE_CONN 0x0405 +#define HCI_OP_DISCONNECT 0x0406 +#define HCI_OP_ADD_SCO 0x0407 +#define HCI_OP_CREATE_CONN_CANCEL 0x0408 +#define HCI_OP_ACCEPT_CONN_REQ 0x0409 +#define HCI_OP_REJECT_CONN_REQ 0x040a +#define HCI_OP_LINK_KEY_REPLY 0x040b +#define HCI_OP_LINK_KEY_NEG_REPLY 0x040c +#define HCI_OP_PIN_CODE_REPLY 0x040d +#define HCI_OP_PIN_CODE_NEG_REPLY 0x040e +#define HCI_OP_CHANGE_CONN_PTYPE 0x040f +#define HCI_OP_AUTH_REQUESTED 0x0411 +#define HCI_OP_SET_CONN_ENCRYPT 0x0413 +#define HCI_OP_CHANGE_CONN_LINK_KEY 0x0415 +#define HCI_OP_REMOTE_NAME_REQ 0x0419 +#define HCI_OP_REMOTE_NAME_REQ_CANCEL 0x041a +#define HCI_OP_READ_REMOTE_FEATURES 0x041b +#define HCI_OP_READ_REMOTE_EXT_FEATURES 0x041c +#define HCI_OP_READ_REMOTE_VERSION 0x041d +#define HCI_OP_SETUP_SYNC_CONN 0x0428 +#define HCI_OP_ACCEPT_SYNC_CONN_REQ 0x0429 +#define HCI_OP_REJECT_SYNC_CONN_REQ 0x042a + +#define HCI_OP_SNIFF_MODE 0x0803 +#define HCI_OP_EXIT_SNIFF_MODE 0x0804 +#define HCI_OP_ROLE_DISCOVERY 0x0809 +#define HCI_OP_SWITCH_ROLE 0x080b +#define HCI_OP_READ_LINK_POLICY 0x080c +#define HCI_OP_WRITE_LINK_POLICY 0x080d +#define HCI_OP_READ_DEF_LINK_POLICY 0x080e +#define HCI_OP_WRITE_DEF_LINK_POLICY 0x080f +#define HCI_OP_SNIFF_SUBRATE 0x0811 + + +#define HCI_OP_SET_EVENT_MASK 0x0c01 +#define HCI_OP_RESET 0x0c03 +#define HCI_OP_SET_EVENT_FLT 0x0c05 +#define HCI_OP_WRITE_LOCAL_NAME 0x0c13 +#define HCI_OP_READ_LOCAL_NAME 0x0c14 +#define HCI_OP_WRITE_CA_TIMEOUT 0x0c16 +#define HCI_OP_WRITE_PG_TIMEOUT 0x0c18 +#define HCI_OP_WRITE_SCAN_ENABLE 0x0c1a +#define HCI_OP_READ_AUTH_ENABLE 0x0c1f +#define HCI_OP_WRITE_AUTH_ENABLE 0x0c20 +#define HCI_OP_READ_ENCRYPT_MODE 0x0c21 +#define HCI_OP_WRITE_ENCRYPT_MODE 0x0c22 + #define ENCRYPT_DISABLED 0x00 + #define ENCRYPT_P2P 0x01 + #define ENCRYPT_BOTH 0x02 +#define HCI_OP_READ_CLASS_OF_DEV 0x0c23 +#define HCI_OP_WRITE_CLASS_OF_DEV 0x0c24 +#define HCI_OP_READ_VOICE_SETTING 0x0c25 +#define HCI_OP_WRITE_VOICE_SETTING 0x0c26 +#define HCI_OP_HOST_BUFFER_SIZE 0x0c33 +#define HCI_OP_READ_SSP_MODE 0x0c55 +#define HCI_OP_WRITE_SSP_MODE 0x0c56 + +#define HCI_OP_READ_LOCAL_VERSION 0x1001 +#define HCI_OP_READ_LOCAL_COMMANDS 0x1002 +#define HCI_OP_READ_LOCAL_FEATURES 0x1003 +#define HCI_OP_READ_LOCAL_EXT_FEATURES 0x1004 +#define HCI_OP_READ_BUFFER_SIZE 0x1005 +#define HCI_OP_READ_BD_ADDR 0x1009 + +// events +#define HCI_EV_INQUIRY_COMPLETE 0x01 +#define HCI_EV_INQUIRY_RESULT 0x02 +#define HCI_EV_CONN_COMPLETE 0x03 +#define HCI_EV_CONN_REQUEST 0x04 +#define HCI_EV_DISCONN_COMPLETE 0x05 +#define HCI_EV_AUTH_COMPLETE 0x06 +#define HCI_EV_REMOTE_NAME 0x07 +#define HCI_EV_ENCRYPT_CHANGE 0x08 +#define HCI_EV_CHANGE_LINK_KEY_COMPLETE 0x09 +#define HCI_EV_REMOTE_FEATURES 0x0b +#define HCI_EV_REMOTE_VERSION 0x0c +#define HCI_EV_QOS_SETUP_COMPLETE 0x0d +#define HCI_EV_CMD_COMPLETE 0x0e +#define HCI_EV_CMD_STATUS 0x0f +#define HCI_EV_ROLE_CHANGE 0x12 +#define HCI_EV_NUM_COMP_PKTS 0x13 +#define HCI_EV_MODE_CHANGE 0x14 +#define HCI_EV_PIN_CODE_REQ 0x16 +#define HCI_EV_LINK_KEY_REQ 0x17 +#define HCI_EV_LINK_KEY_NOTIFY 0x18 +#define HCI_EV_CLOCK_OFFSET 0x1c +#define HCI_EV_PKT_TYPE_CHANGE 0x1d +#define HCI_EV_PSCAN_REP_MODE 0x20 +#define HCI_EV_INQUIRY_RESULT_WITH_RSSI 0x22 +#define HCI_EV_REMOTE_EXT_FEATURES 0x23 +#define HCI_EV_SYNC_CONN_COMPLETE 0x2c +#define HCI_EV_SYNC_CONN_CHANGED 0x2d +#define HCI_EV_SNIFF_SUBRATE 0x2e +#define HCI_EV_EXTENDED_INQUIRY_RESULT 0x2f +#define HCI_EV_IO_CAPA_REQUEST 0x31 +#define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36 +#define HCI_EV_REMOTE_HOST_FEATURES 0x3d + +/* Possible error codes */ +#define HCI_UNKNOWN_HCI_COMMAND 0x01 +#define HCI_NO_CONNECTION 0x02 +#define HCI_HW_FAILURE 0x03 +#define HCI_PAGE_TIMEOUT 0x04 +#define HCI_AUTHENTICATION_FAILURE 0x05 +#define HCI_KEY_MISSING 0x06 +#define HCI_MEMORY_FULL 0x07 +#define HCI_CONN_TIMEOUT 0x08 +#define HCI_MAX_NUMBER_OF_CONNECTIONS 0x09 +#define HCI_MAX_NUMBER_OF_SCO_CONNECTIONS_TO_DEVICE 0x0A +#define HCI_ACL_CONNECTION_EXISTS 0x0B +#define HCI_COMMAND_DISSALLOWED 0x0C +#define HCI_HOST_REJECTED_DUE_TO_LIMITED_RESOURCES 0x0D +#define HCI_HOST_REJECTED_DUE_TO_SECURITY_REASONS 0x0E +#define HCI_HOST_REJECTED_DUE_TO_REMOTE_DEVICE_ONLY_PERSONAL_SERVICE 0x0F +#define HCI_HOST_TIMEOUT 0x10 +#define HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE 0x11 +#define HCI_INVALID_HCI_COMMAND_PARAMETERS 0x12 +#define HCI_OTHER_END_TERMINATED_CONN_USER_ENDED 0x13 +#define HCI_OTHER_END_TERMINATED_CONN_LOW_RESOURCES 0x14 +#define HCI_OTHER_END_TERMINATED_CONN_ABOUT_TO_POWER_OFF 0x15 +#define HCI_CONN_TERMINATED_BY_LOCAL_HOST 0x16 +#define HCI_REPETED_ATTEMPTS 0x17 +#define HCI_PAIRING_NOT_ALLOWED 0x18 +#define HCI_UNKNOWN_LMP_PDU 0x19 +#define HCI_UNSUPPORTED_REMOTE_FEATURE 0x1A +#define HCI_SCO_OFFSET_REJECTED 0x1B +#define HCI_SCO_INTERVAL_REJECTED 0x1C +#define HCI_SCO_AIR_MODE_REJECTED 0x1D +#define HCI_INVALID_LMP_PARAMETERS 0x1E +#define HCI_UNSPECIFIED_ERROR 0x1F +#define HCI_UNSUPPORTED_LMP_PARAMETER_VALUE 0x20 +#define HCI_ROLE_CHANGE_NOT_ALLOWED 0x21 +#define HCI_LMP_RESPONSE_TIMEOUT 0x22 +#define HCI_LMP_ERROR_TRANSACTION_COLLISION 0x23 +#define HCI_LMP_PDU_NOT_ALLOWED 0x24 +#define HCI_ENCRYPTION_MODE_NOT_ACCEPTABLE 0x25 +#define HCI_UNIT_KEY_USED 0x26 +#define HCI_QOS_NOT_SUPPORTED 0x27 +#define HCI_INSTANT_PASSED 0x28 +#define HCI_PAIRING_UNIT_KEY_NOT_SUPPORTED 0x29 + +const char* EvtStr(int evt) +{ + switch (evt) + { + case HCI_EV_INQUIRY_COMPLETE: return "HCI_EV_INQUIRY_COMPLETE"; + case HCI_EV_INQUIRY_RESULT: return "HCI_EV_INQUIRY_RESULT"; + case HCI_EV_CONN_COMPLETE: return "HCI_EV_CONN_COMPLETE"; + case HCI_EV_CONN_REQUEST: return "HCI_EV_CONN_REQUEST"; + case HCI_EV_DISCONN_COMPLETE: return "HCI_EV_DISCONN_COMPLETE"; + case HCI_EV_AUTH_COMPLETE: return "HCI_EV_AUTH_COMPLETE"; + case HCI_EV_REMOTE_NAME: return "HCI_EV_REMOTE_NAME"; + case HCI_EV_ENCRYPT_CHANGE: return "HCI_EV_ENCRYPT_CHANGE"; + case HCI_EV_CHANGE_LINK_KEY_COMPLETE : return "HCI_EV_CHANGE_LINK_KEY_COMPLETE"; + case HCI_EV_REMOTE_FEATURES: return "HCI_EV_REMOTE_FEATURES"; + case HCI_EV_REMOTE_VERSION: return "HCI_EV_REMOTE_VERSION"; + case HCI_EV_QOS_SETUP_COMPLETE : return "HCI_EV_QOS_SETUP_COMPLETE"; + case HCI_EV_CMD_COMPLETE: return "HCI_EV_CMD_COMPLETE"; + case HCI_EV_CMD_STATUS: return "HCI_EV_CMD_STATUS"; + case HCI_EV_ROLE_CHANGE: return "HCI_EV_ROLE_CHANGE"; + case HCI_EV_NUM_COMP_PKTS: return "HCI_EV_NUM_COMP_PKTS"; + case HCI_EV_MODE_CHANGE: return "HCI_EV_MODE_CHANGE"; + case HCI_EV_PIN_CODE_REQ: return "HCI_EV_PIN_CODE_REQ"; + case HCI_EV_LINK_KEY_REQ: return "HCI_EV_LINK_KEY_REQ"; + case HCI_EV_LINK_KEY_NOTIFY: return "HCI_EV_LINK_KEY_NOTIFY"; + case HCI_EV_CLOCK_OFFSET: return "HCI_EV_CLOCK_OFFSET"; + case HCI_EV_PKT_TYPE_CHANGE: return "HCI_EV_PKT_TYPE_CHANGE"; + case HCI_EV_PSCAN_REP_MODE: return "HCI_EV_PSCAN_REP_MODE"; + case HCI_EV_INQUIRY_RESULT_WITH_RSSI : return "HCI_EV_INQUIRY_RESULT_WITH_RSSI"; + case HCI_EV_REMOTE_EXT_FEATURES: return "HCI_EV_REMOTE_EXT_FEATURES"; + case HCI_EV_SYNC_CONN_COMPLETE: return "HCI_EV_SYNC_CONN_COMPLETE"; + case HCI_EV_SYNC_CONN_CHANGED: return "HCI_EV_SYNC_CONN_CHANGED"; + case HCI_EV_SNIFF_SUBRATE: return "HCI_EV_SNIFF_SUBRATE"; + case HCI_EV_EXTENDED_INQUIRY_RESULT: return "HCI_EV_EXTENDED_INQUIRY_RESULT"; + case HCI_EV_IO_CAPA_REQUEST: return "HCI_EV_IO_CAPA_REQUEST"; + case HCI_EV_SIMPLE_PAIR_COMPLETE: return "HCI_EV_SIMPLE_PAIR_COMPLETE"; + case HCI_EV_REMOTE_HOST_FEATURES: return "HCI_EV_REMOTE_HOST_FEATURES"; + } + return "Unknown Event"; +} + +const char* CmdStr(int cmd) +{ + switch (cmd) + { + // 0x04XX + case HCI_OP_INQUIRY: return "HCI_OP_INQUIRY"; + case HCI_OP_INQUIRY_CANCEL: return "HCI_OP_INQUIRY_CANCEL"; + case HCI_OP_EXIT_PERIODIC_INQ: return "HCI_OP_EXIT_PERIODIC_INQ"; + case HCI_OP_CREATE_CONN: return "HCI_OP_CREATE_CONN"; + case HCI_OP_DISCONNECT: return "HCI_OP_DISCONNECT"; + case HCI_OP_ADD_SCO: return "HCI_OP_ADD_SCO"; + case HCI_OP_CREATE_CONN_CANCEL: return "HCI_OP_CREATE_CONN_CANCEL"; + case HCI_OP_ACCEPT_CONN_REQ: return "HCI_OP_ACCEPT_CONN_REQ"; + case HCI_OP_REJECT_CONN_REQ: return "HCI_OP_REJECT_CONN_REQ"; + case HCI_OP_LINK_KEY_REPLY: return "HCI_OP_LINK_KEY_REPLY"; + case HCI_OP_LINK_KEY_NEG_REPLY: return "HCI_OP_LINK_KEY_NEG_REPLY"; + case HCI_OP_PIN_CODE_REPLY: return "HCI_OP_PIN_CODE_REPLY"; + case HCI_OP_PIN_CODE_NEG_REPLY: return "HCI_OP_PIN_CODE_NEG_REPLY"; + case HCI_OP_CHANGE_CONN_PTYPE: return "HCI_OP_CHANGE_CONN_PTYPE"; + case HCI_OP_AUTH_REQUESTED: return "HCI_OP_AUTH_REQUESTED"; + case HCI_OP_SET_CONN_ENCRYPT: return "HCI_OP_SET_CONN_ENCRYPT"; + case HCI_OP_CHANGE_CONN_LINK_KEY: return "HCI_OP_CHANGE_CONN_LINK_KEY"; + case HCI_OP_REMOTE_NAME_REQ: return "HCI_OP_REMOTE_NAME_REQ"; + case HCI_OP_REMOTE_NAME_REQ_CANCEL: return "HCI_OP_REMOTE_NAME_REQ_CANCEL"; + case HCI_OP_READ_REMOTE_FEATURES: return "HCI_OP_READ_REMOTE_FEATURES"; + case HCI_OP_READ_REMOTE_EXT_FEATURES: return "HCI_OP_READ_REMOTE_EXT_FEATURES"; + case HCI_OP_READ_REMOTE_VERSION: return "HCI_OP_READ_REMOTE_VERSION"; + case HCI_OP_SETUP_SYNC_CONN: return "HCI_OP_SETUP_SYNC_CONN"; + case HCI_OP_ACCEPT_SYNC_CONN_REQ: return "HCI_OP_ACCEPT_SYNC_CONN_REQ"; + case HCI_OP_REJECT_SYNC_CONN_REQ: return "HCI_OP_REJECT_SYNC_CONN_REQ"; + // 0x0CXX + case HCI_OP_SET_EVENT_MASK: return "HCI_OP_SET_EVENT_MASK"; + case HCI_OP_RESET: return "HCI_OP_RESET"; + case HCI_OP_SET_EVENT_FLT: return "HCI_OP_SET_EVENT_FLT"; + case HCI_OP_WRITE_LOCAL_NAME: return "HCI_OP_WRITE_LOCAL_NAME"; + case HCI_OP_READ_LOCAL_NAME: return "HCI_OP_READ_LOCAL_NAME"; + case HCI_OP_WRITE_CA_TIMEOUT: return "HCI_OP_WRITE_CA_TIMEOUT"; + case HCI_OP_WRITE_PG_TIMEOUT: return "HCI_OP_WRITE_PG_TIMEOUT"; + case HCI_OP_WRITE_SCAN_ENABLE: return "HCI_OP_WRITE_SCAN_ENABLE"; + case HCI_OP_READ_AUTH_ENABLE: return "HCI_OP_READ_AUTH_ENABLE"; + case HCI_OP_WRITE_AUTH_ENABLE: return "HCI_OP_WRITE_AUTH_ENABLE"; + case HCI_OP_READ_ENCRYPT_MODE: return "HCI_OP_READ_ENCRYPT_MODE"; + case HCI_OP_WRITE_ENCRYPT_MODE: return "HCI_OP_WRITE_ENCRYPT_MODE"; + case HCI_OP_READ_CLASS_OF_DEV: return "HCI_OP_READ_CLASS_OF_DEV"; + case HCI_OP_WRITE_CLASS_OF_DEV: return "HCI_OP_WRITE_CLASS_OF_DEV"; + case HCI_OP_READ_VOICE_SETTING: return "HCI_OP_READ_VOICE_SETTING"; + case HCI_OP_WRITE_VOICE_SETTING: return "HCI_OP_WRITE_VOICE_SETTING"; + case HCI_OP_HOST_BUFFER_SIZE: return "HCI_OP_HOST_BUFFER_SIZE"; + case HCI_OP_READ_SSP_MODE: return "HCI_OP_READ_SSP_MODE"; + case HCI_OP_WRITE_SSP_MODE: return "HCI_OP_WRITE_SSP_MODE"; + + // 10xx + case HCI_OP_READ_LOCAL_VERSION: return "HCI_OP_READ_LOCAL_VERSION"; + case HCI_OP_READ_LOCAL_COMMANDS: return "HCI_OP_READ_LOCAL_COMMANDS"; + case HCI_OP_READ_LOCAL_FEATURES: return "HCI_OP_READ_LOCAL_FEATURES"; + case HCI_OP_READ_LOCAL_EXT_FEATURES: return "HCI_OP_READ_LOCAL_EXT_FEATURES"; + case HCI_OP_READ_BUFFER_SIZE: return "HCI_OP_READ_BUFFER_SIZE"; + case HCI_OP_READ_BD_ADDR: return "HCI_OP_READ_BD_ADDR"; + } + return "Unknown Cmd"; +} + +const char* HCIErrStr(int err) +{ + switch (err) + { + case 0: return "OK"; + case HCI_UNKNOWN_HCI_COMMAND: return "HCI_UNKNOWN_HCI_COMMAND"; + case HCI_NO_CONNECTION: return "HCI_NO_CONNECTION"; + case HCI_HW_FAILURE: return "HCI_HW_FAILURE"; + case HCI_PAGE_TIMEOUT: return "HCI_PAGE_TIMEOUT"; + case HCI_AUTHENTICATION_FAILURE: return "HCI_AUTHENTICATION_FAILURE"; + case HCI_KEY_MISSING: return "HCI_KEY_MISSING"; + case HCI_MEMORY_FULL: return "HCI_MEMORY_FULL"; + case HCI_CONN_TIMEOUT: return "HCI_CONN_TIMEOUT"; + case HCI_MAX_NUMBER_OF_CONNECTIONS: return "HCI_CONN_TIMEOUT"; + case HCI_MAX_NUMBER_OF_SCO_CONNECTIONS_TO_DEVICE: return "HCI_MAX_NUMBER_OF_SCO_CONNECTIONS_TO_DEVICE"; + case HCI_ACL_CONNECTION_EXISTS: return "HCI_ACL_CONNECTION_EXISTS"; + case HCI_COMMAND_DISSALLOWED: return "HCI_COMMAND_DISSALLOWED"; + case HCI_HOST_REJECTED_DUE_TO_LIMITED_RESOURCES: return "HCI_HOST_REJECTED_DUE_TO_LIMITED_RESOURCES"; + case HCI_HOST_REJECTED_DUE_TO_SECURITY_REASONS: return "HCI_HOST_REJECTED_DUE_TO_SECURITY_REASONS"; + case HCI_HOST_REJECTED_DUE_TO_REMOTE_DEVICE_ONLY_PERSONAL_SERVICE: return "HCI_HOST_REJECTED_DUE_TO_REMOTE_DEVICE_ONLY_PERSONAL_SERVICE"; + case HCI_HOST_TIMEOUT: return "HCI_HOST_TIMEOUT"; + case HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE: return "HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE"; + case HCI_INVALID_HCI_COMMAND_PARAMETERS: return "HCI_INVALID_HCI_COMMAND_PARAMETERS"; + case HCI_OTHER_END_TERMINATED_CONN_USER_ENDED: return "HCI_OTHER_END_TERMINATED_CONN_USER_ENDED"; + case HCI_OTHER_END_TERMINATED_CONN_LOW_RESOURCES: return "HCI_OTHER_END_TERMINATED_CONN_LOW_RESOURCES"; + case HCI_OTHER_END_TERMINATED_CONN_ABOUT_TO_POWER_OFF: return "HCI_OTHER_END_TERMINATED_CONN_ABOUT_TO_POWER_OFF"; + case HCI_CONN_TERMINATED_BY_LOCAL_HOST: return "HCI_CONN_TERMINATED_BY_LOCAL_HOST"; + case HCI_REPETED_ATTEMPTS: return "HCI_REPETED_ATTEMPTS"; + case HCI_PAIRING_NOT_ALLOWED: return "HCI_PAIRING_NOT_ALLOWED"; + case HCI_UNKNOWN_LMP_PDU: return "HCI_UNKNOWN_LMP_PDU"; + case HCI_UNSUPPORTED_REMOTE_FEATURE: return "HCI_UNSUPPORTED_REMOTE_FEATURE"; + case HCI_SCO_OFFSET_REJECTED: return "HCI_SCO_OFFSET_REJECTED"; + case HCI_SCO_INTERVAL_REJECTED: return "HCI_SCO_INTERVAL_REJECTED"; + case HCI_SCO_AIR_MODE_REJECTED: return "HCI_SCO_AIR_MODE_REJECTED"; + case HCI_INVALID_LMP_PARAMETERS: return "HCI_INVALID_LMP_PARAMETERS"; + case HCI_UNSPECIFIED_ERROR: return "HCI_UNSPECIFIED_ERROR"; + case HCI_UNSUPPORTED_LMP_PARAMETER_VALUE: return "HCI_UNSUPPORTED_LMP_PARAMETER_VALUE"; + case HCI_ROLE_CHANGE_NOT_ALLOWED: return "HCI_ROLE_CHANGE_NOT_ALLOWED"; + case HCI_LMP_RESPONSE_TIMEOUT: return "HCI_LMP_RESPONSE_TIMEOUT"; + case HCI_LMP_ERROR_TRANSACTION_COLLISION: return "HCI_LMP_ERROR_TRANSACTION_COLLISION"; + case HCI_LMP_PDU_NOT_ALLOWED: return "HCI_LMP_PDU_NOT_ALLOWED"; + case HCI_ENCRYPTION_MODE_NOT_ACCEPTABLE: return "HCI_ENCRYPTION_MODE_NOT_ACCEPTABLE"; + case HCI_UNIT_KEY_USED: return "HCI_UNIT_KEY_USED"; + case HCI_QOS_NOT_SUPPORTED: return "HCI_QOS_NOT_SUPPORTED"; + case HCI_INSTANT_PASSED: return "HCI_INSTANT_PASSED"; + case HCI_PAIRING_UNIT_KEY_NOT_SUPPORTED: return "HCI_PAIRING_UNIT_KEY_NOT_SUPPORTED"; + }; + return "Unknow HCI err"; +}; + + +#endif // HCI_PRIVATE_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,646 @@ +//------------------------------------------------------------- +// KAMUI MIDI-CV Exapmple +// Copyright (C) 2012 RJB RadioJunkBox +// Released under the MIT License: http://mbed.org/license/mit +//------------------------------------------------------------- + +/* +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 "TextLCD.h" +#include "USBHost.h" +#include "Utils.h" +#include "FATFileSystem.h" +#include <stdlib.h> +#include <math.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; + for(;;) + { + p = readdir(d); + if (!p) + break; + 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. +*/ + +Serial pc(USBTX, USBRX); +int GetConsoleChar() +{ + if (!pc.readable()) + return -1; + char c = pc.getc(); + pc.putc(c); // echo + return c; +} + +//------------------------------------------------------------- +// Define + +#define AD5551 // 14bitDAC + +#define SPI_RATE 1000000 // 1Mbps +#define MIDI_RATE 31250 // 31.25kbps +#define BEEP_FREQ 1760.0 // 1760Hz +#define UPDATE_INTERVAL 100 // 100us +#define SW_WATCH_INTERVAL (25000/UPDATE_INTERVAL) // 25ms +#define PARAM_GLIDE 6554.0 +#define PARAM_DP 6912.0 // UPDATE_INTERVAL = 100us +//#define PARAM_DP 8192.0 // UPDATE_INTERVAL = 50us + +#define UPDATE_MODE0 0 // Update Interval CV ch1-6 1200us, ch7,8 400us +#define UPDATE_MODE1 1 // Update Interval CV ch1-6 N/A, ch7,8 200us + +#define GATE1 0x01 +#define GATE2 0x02 +#define GATE3 0x04 +#define GATE4 0x08 + +#define SYNC1CLK 0x01 +#define SYNC1RUN 0x02 +#define SYNC2CLK 0x04 +#define SYNC2RUN 0x08 + +#define MODE_CV 0x00 +#define MODE_GATE 0x40 +#define MODE_SYNC 0x80 +#define MODE_SET_SYNC 0xC0 + +#define SW1 0x01 +#define SW2 0x02 +#define SW3 0x04 +#define SW4 0x08 +#define SYNC1CLK_IN 0x10 +#define SYNC1RUN_IN 0x20 +#define SYNC2CLK_IN 0x40 +#define GATE_IN 0x80 + +#define _ENABLE 0 +#define _DISABLE 1 + +#define BUFSIZE 32 // size of ring buffer (ex 4,8,16,32...) +#define LFO_WF_TRI 0 +#define LFO_WF_SQR 1 +#define LFO_WF_SAW 2 +#define LFO_WF_NONE 3 +#define MINIMUMNOTE 12 +#define SYNC_TURN_TIME (5000/UPDATE_INTERVAL) // 5ms + +#define MENULOOP_INTERVAL 25000 // 25ms + +//------------------------------------------------------------- +// Functions + +void MenuLoop(void); +void InitKamui(void); +void UpdateCV(void); +unsigned char CheckSW(unsigned char); + +void RcvMIDI(void); +void GetMIDI(void); +void MidiCV(void); +void CalcHzVTbl(void); +unsigned short OctVtoHzV(unsigned short); +void DinSync(void); +extern void MIDI_Parser(unsigned char); + +//------------------------------------------------------------- +// Global Variables + +int gUpdateMode; +unsigned short gCV[8]; +unsigned char gGATE; +unsigned char gSYNC; +unsigned char gSW; + +union { + unsigned short WORD; + struct { + unsigned char L; + unsigned char H; + } BYTE; +} gDAC; + +int gPtr_buf_in, gPtr_buf_out; +unsigned char gRxBuf[BUFSIZE]; + +float gGLIDE[4]; +unsigned short gLFO_DP[4]; +unsigned char gLFO_FORM[4]; +unsigned char gMIDI_CH[4]; +short gTblHzV[3072]; + +extern unsigned char gPlayNoteBuf[]; +extern unsigned char gGateBuf[]; +extern unsigned char gPitchBendBuf[]; +extern unsigned char gModWheelBuf[]; +extern unsigned char gMIDISYNC_CLK; +extern unsigned char gMIDISYNC_RUN; + +int pot[4],_pot[4]; +unsigned char mode = 7; // for Intialize +unsigned char edit[4]; +int val[2][4] = { 0, 0, 0, 0, 50, 50, 50, 50 }; +char *wave[4] = { "TR","SQ","SW","--" }; + +//------------------------------------------------------------- +// mbed Functions + +// TextLCD +TextLCD gLCD(p23, p24, p25, p26, p29, p30); // rs, e, d4-d7 + +// SPI +SPI gSPI(p11,p12,p13); +DigitalOut gCSA(p14); +DigitalOut gCSB(p22); + +// Sirial MIDI +Serial gMIDI(p9,p10); + +// AnalogIn +AnalogIn gAIN1(p15); // VR1 +AnalogIn gAIN2(p16); // VR2 +AnalogIn gAIN3(p17); // VR3 +AnalogIn gAIN4(p18); // VR4 +AnalogIn gAIN5(p19); // IN1 +AnalogIn gAIN6(p20); // IN2 + +// BEEP +PwmOut gBEEP(p21); + +// LED +DigitalOut gLED1(LED1); +DigitalOut gLED2(LED2); +DigitalOut gLED3(LED3); +DigitalOut gLED4(LED4); +BusOut gLEDS(LED1,LED2,LED3,LED4); + +// Ticker +Ticker gTICKER; +Ticker gTICKER2; + +// Implemented in TestShell.cpp +void TestShell(); +void USBInit(); + +//------------------------------------------------------------- +// main + +int main() { + + int i; + + USBInit(); + + // Initialize + gPtr_buf_in = gPtr_buf_out = 0; + for( i=0; i<4; i++) { + pot[i] = _pot[i] = 0; + edit[i] = 0; + gGLIDE[i] = 1.0 / expf(val[0][i]*656.0/PARAM_GLIDE); + gLFO_DP[i] = expf(val[1][i]*656.0/PARAM_DP); + gLFO_FORM[i] = LFO_WF_TRI; + gMIDI_CH[i] = i; + } + + for( i=0; i<16; i++) { // MIDI Data Buffers + gPlayNoteBuf[i] =24; + gGateBuf[i] = 0; + gPitchBendBuf[i] = 0x40; + gModWheelBuf[i] = 0; + } + + gSW = 1; // for Intialize + + CalcHzVTbl(); + InitKamui(); + gTICKER2.attach_us(&MenuLoop, MENULOOP_INTERVAL); + TestShell(); +} + +//------------------------------------------------------------- +// Menu LOOP +void MenuLoop() +{ + int i; + static unsigned char ch; + + // Read pot + pot[0] = gAIN1.read_u16(); + pot[1] = gAIN2.read_u16(); + pot[2] = gAIN3.read_u16(); + pot[3] = gAIN4.read_u16(); + + // change pot amount? + if(abs(pot[ch] - _pot[ch]) > 0x2000) edit[ch] = 1; + + if(edit[ch]) { + switch(mode) { + case 0: + gGLIDE[ch] = 1.0 / expf(pot[ch]/PARAM_GLIDE); + val[0][ch] = pot[ch] / 656; + break; + case 1: + gLFO_DP[ch] = expf(pot[ch]/PARAM_DP); + val[1][ch] = pot[ch] / 656; + break; + case 2: + gLFO_FORM[ch] = pot[ch] / 0x4000; + break; + case 3: + gMIDI_CH[ch] = pot[ch] / 0x1000; + break; + default: + break; + } + } + + // Push Mode SW + if(gSW & SW1) { + mode++; + mode &= 0x03; + for( i=0; i<4; i++) { + _pot[i] = pot[i]; + edit[i] = 0; + } + } + gSW = 0; + + // LCD Display + gLCD.locate( 0, 1 ); + switch(mode) { + case 0: + gLCD.printf("GLID %02d %02d %02d %02d", + val[0][0], val[0][1], val[0][2], val[0][3]); + break; + case 1: + gLCD.printf("FREQ %02d %02d %02d %02d", + val[1][0], val[1][1], val[1][2], val[1][3]); + break; + case 2: + gLCD.printf("FORM %s %s %s %s", + wave[gLFO_FORM[0]], wave[gLFO_FORM[1]], + wave[gLFO_FORM[2]], wave[gLFO_FORM[3]]); + break; + case 3: + gLCD.printf("MIDI %02d %02d %02d %02d", + gMIDI_CH[0]+1, gMIDI_CH[1]+1, + gMIDI_CH[2]+1, gMIDI_CH[3]+1); + break; + } + + ch++; + ch &= 0x03; +} + +//------------------------------------------------------------- +// Initialize KAMUI + +void InitKamui() +{ + // Init. Variables + for( int i=0; i<8; i++) { + gCV[i] = 0x8000; + } + gGATE = 0; + gSYNC = 0; + + gUpdateMode = UPDATE_MODE0; + + // Init. SPI + gCSA = _DISABLE; + gCSB = _DISABLE; + gSPI.format(8,0); + gSPI.frequency(SPI_RATE); + + // Init. Serial MIDI + gMIDI.baud(MIDI_RATE); + + // Ticker + gTICKER.attach_us(&UpdateCV, UPDATE_INTERVAL); + + // Beep + gBEEP.period(1.0/BEEP_FREQ); + gBEEP.write(0.5); + wait(0.2); + gBEEP.write(0.0); + + // Init Display + gLCD.locate( 0, 0 ); + // 123456789ABCDEF + gLCD.printf("USBHOST MIDI-CV"); +} + +//------------------------------------------------------------- +// Update CV, GATE, SYNC + +void UpdateCV() +{ + unsigned char rcv,ch; + unsigned char ptn[] = { 0,1,6,7,2,3,6,7,4,5,6,7 }; + const int numptn = (sizeof ptn / sizeof ptn[0]) - 1; + static unsigned char cnt; + + // SET DAC + ch = ptn[cnt]; + if(gUpdateMode) ch |= 0x06; + +#ifdef AD5551 // 14bitDAC + gDAC.WORD = gCV[ch] >> 2; +#else + gDAC.WORD = gCV[ch]; +#endif + + gCSA = _ENABLE; + gSPI.write(gDAC.BYTE.H); + gSPI.write(gDAC.BYTE.L); + gCSA = _DISABLE; + + // GATE or SYNC OUT + if(cnt & 0x01) { + // GATE OUT + gCSB = _ENABLE; + rcv = gSPI.write(gGATE | MODE_GATE) & 0x0F; + gCSB = _DISABLE; + } + else { + // SYNC OUT + gCSB = _ENABLE; + rcv = gSPI.write(gSYNC | MODE_SYNC); + gCSB = _DISABLE; + } + + // SEL CV CHANNEL + gCSB = _ENABLE; + gSPI.write(ch); + gCSB = _DISABLE; + + cnt < numptn ? cnt++ : cnt = 0; + + gSW |= CheckSW(rcv); + RcvMIDI(); + GetMIDI(); + DinSync(); + MidiCV(); +} + +//------------------------------------------------------------- +// Check SW + +unsigned char CheckSW(unsigned char c) { + + static unsigned char swbuf[2]; + static unsigned int cntsw; + unsigned char ret = 0; + + if(cntsw > SW_WATCH_INTERVAL) { + if(c &= 0x0F) { + if(!swbuf[1]) { + if( swbuf[0] == c) { + swbuf[1] = c; + ret = c; + } + else { + swbuf[0] = c; + } + } + } + else { + swbuf[1] = 0; + swbuf[0] = 0; + } + cntsw = 0; + } + cntsw++; + return ret; +} + +//------------------------------------------------------------- +// Receive MIDI Data & Store Ring Buffer + +void RcvMIDI() { + + if(!gMIDI.readable()) return; + + gPtr_buf_in++; + gPtr_buf_in &= (BUFSIZE - 1); + gRxBuf[gPtr_buf_in] = gMIDI.getc(); +} + +//------------------------------------------------------------- +// Get MIDI Data from Ring Buffer + +void GetMIDI() { + unsigned char rb; + + // ring buffer empty? + if(gPtr_buf_in != gPtr_buf_out) { + // get 1byte from ring buffer + gPtr_buf_out++; + gPtr_buf_out &= (BUFSIZE - 1); + rb = gRxBuf[gPtr_buf_out]; + MIDI_Parser(rb); + gMIDI.putc(rb); + } +} + +//------------------------------------------------------------- +// MIDI Data to CV, GATE + +void MidiCV() +{ + static unsigned char ch; + static unsigned short phase[4]; + static float cvf[4]; + int lfo,mod; + unsigned char midi_ch; + unsigned int cv; + unsigned int note; + + midi_ch = gMIDI_CH[ch]; + + note = gPlayNoteBuf[midi_ch]; + if( note < MINIMUMNOTE) note = MINIMUMNOTE; + note -= MINIMUMNOTE; + + // DDS Phase + phase[ch] += gLFO_DP[ch]; + + // LFO DDS Genelator + switch(gLFO_FORM[ch]) { + case LFO_WF_TRI: + if(phase[ch] < 32738) lfo = phase[ch] - 16384; + else lfo = (16383 + 32768) - phase[ch]; + break; + case LFO_WF_SQR: + if(phase[ch] < 32738) lfo = 32767; + else lfo = 0; + break; + case LFO_WF_SAW: + lfo = phase[ch] / 2 - 16384; + break; + default : + lfo = 0; + break; + } + + // Modulation amount + mod = lfo * gModWheelBuf[midi_ch] >> 8; + + // Calculate CV + cvf[ch] = ((float)(note << 8) - cvf[ch]) * gGLIDE[ch] + cvf[ch]; + cv = (unsigned int)cvf[ch] + (0x8000 - (0x0040 << 3)) + + (gPitchBendBuf[midi_ch] << 2) + mod; + if(cv > 0xFFFF) cv = 0xFFFF; + gCV[ch] = (unsigned short)cv; + gCV[ch+4] = OctVtoHzV(gCV[ch]); + + // GATE + gGateBuf[midi_ch] ? gGATE |= (1<<ch) : gGATE &= ~(1<<ch); + + ch++; + ch &= 0x03; +} + +//------------------------------------------------------------- +// Oct/V to Hz/V Converter + +void CalcHzVTbl() // Calc Conv. Table +{ + int i; + float v; + + for( i=0; i<3072; i++) { + v = 24576.0 * pow(2.0,(i/3072.0)); + gTblHzV[i] = (unsigned short)v; + } +} + +unsigned short OctVtoHzV( unsigned short vin) +{ + int oct,res; + unsigned short vout; + + if(vin > 0xE400) vin = 0xE400; // Maximum Note E8 Vin = 10.794V + if(vin < 0x6800) vin = 0x6800; // Minimum Note C-2 Vin = -2.000V + vin -= 0x6800; + + oct = vin / 0xC00; // 0xC00 : 3072 + res = vin % 0xC00; + + vout = ((unsigned short)gTblHzV[res] >> (10 - oct)) + 0x8000; + return vout; +} + +//------------------------------------------------------------- +// DIN SYNC Control + +void DinSync() +{ + static unsigned int cnt; + static unsigned int cnt24 = 10; + + if(gMIDISYNC_RUN) gSYNC |= (SYNC1RUN | SYNC2RUN); + else gSYNC &= ~(SYNC1RUN | SYNC2RUN); + + if(cnt >= SYNC_TURN_TIME) gSYNC &= ~(SYNC1CLK | SYNC2CLK); + + if(gMIDISYNC_CLK) { + gSYNC |= (SYNC1CLK | SYNC2CLK); + gMIDISYNC_CLK = 0; + cnt = 0; + cnt24++; + } + if(cnt24 >= 24) cnt24 = 0; + + gLED3 = gSYNC & SYNC1RUN ? 1 : 0; + gLED4 = cnt24 < 4 ? 1 : 0; + + cnt++; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/49a220cc26e0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/midi_parser.c Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,258 @@ +//------------------------------------------------------------- +// file : midi_parser.c +// Copyright (C) 2012 RJB RadioJunkBox +// Released under the MIT License: http://mbed.org/license/mit +//------------------------------------------------------------- + +#include "mbed.h" +#include "midi_parser.h" + +//------------------------------------------------------------- +// MIDI Parser + +void MIDI_Parser(unsigned char mididata) +{ + RxByte = mididata; + + if(MIDI_SystemMessage()) { + MIDI_ChannelMessage(); + } +} + +//------------------------------------------------------------- +// MIDI System Meassage + +int MIDI_SystemMessage(void) +{ + if(SysEx){ + if(RxByte == MIDI_EndSysEx){ + SysEx = FALSE; + } + } + else{ + if(RxByte < 0xF8){ + if(RxByte > 0x7F){ + if(RxByte == MIDI_StartSysEx){ + SysEx = TRUE; + } + else{ + MidiCh = RxByte & 0x0F; + } + PC = 0; + } + else{ + MByte[PC & 0x01] = RxByte; + } + return TRUE; + } + else { + MIDI_SystemRealtimeMessage(); + } + } + return FALSE; +} + +//------------------------------------------------------------- +// MIDI System Realtime Message + +void MIDI_SystemRealtimeMessage(void) +{ + switch(RxByte) { + case MIDI_TimingClock: + gMIDISYNC_CLK |= 0x01; + break; + case MIDI_Start: + gMIDISYNC_RUN = 0x01; + break; + case MIDI_Continue: + gMIDISYNC_RUN = 0x01; + break; + case MIDI_Stop: + gMIDISYNC_RUN = 0x00; + break; + case MIDI_ActiveSensing: + break; + case MIDI_SystemReset: + break; + } +} + +//------------------------------------------------------------- +// MIDI Channel Message + +void MIDI_ChannelMessage(void) +{ + switch(PC){ + case 0: + switch(RxByte & 0xF0){ + case MIDI_NoteOff: + PC = 2; + break; + case MIDI_NoteOn: + PC = 4; + break; + case MIDI_PolykeyPressure: + PC = 6; + break; + case MIDI_ProgramChange: + PC = 8; + break; + case MIDI_ControlChange: + PC = 10; + break; + case MIDI_ChannelPressure: + PC = 12; + break; + case MIDI_PitchBend: + PC = 14; + break; + } break; + + // Note OFF + case 2: + PC = 3; + break; + case 3: + PC = 2; + NoteOFF(); + break; + + // Note ON + case 4: + PC = 5; + break; + case 5: + PC = 4; + if( MByte[1] == 0){ + NoteOFF(); + } + else{ + NoteON(); + } + break; + + // Polyphonic Key Pressure + case 6: + PC = 7; + break; + case 7: + PC = 6; + break; + + // Program Change + case 8: + break; + + // Control Change + case 10: PC = 11; break; + case 11: + switch(MByte[0]) { + case MIDI_CC_Moduration: + gModWheelBuf[MidiCh] = MByte[1] >> 2; + break; + case MIDI_CC_DataEntry: + break; + case MIDI_CC_RPN_LSB: + break; + case MIDI_CC_RPN_MSB: + break; + case MIDI_MM_AllSoundOff: + break; + case MIDI_MM_ResetAllControl: + break; + case MIDI_MM_AllNoteOff: + break; + } + break; + + // Channel Pressure + case 12: + break; + + // Pitch Bend + case 14: + PC = 15; + break; + + case 15: + PC = 14; + gPitchBendBuf[MidiCh] = (MByte[1] << 1) | (MByte[0] >> 6); + break; + + default: + break; + } +} + +//------------------------------------------------------------- +// Note ON Message Processing + +void NoteON(void) +{ + unsigned char i; + unsigned char h = 0; // higheest note + + // ignore note if registed buffer + for(i = 0; i < NoteCnt[MidiCh]; i++) { + if(NoteBuf[MidiCh][i] == MByte[0]) { + return; + } + } + + // full note buffer? + if(NoteCnt[MidiCh] == MAX_NOTE_CNT) { + for(i = 0; i < (MAX_NOTE_CNT - 1); i++) { + NoteBuf[MidiCh][i] = NoteBuf[MidiCh][i+1]; + } + NoteBuf[MidiCh][MAX_NOTE_CNT - 1] = MByte[0]; + } + else { + NoteBuf[MidiCh][NoteCnt[MidiCh]] = MByte[0]; + NoteCnt[MidiCh]++; + } + + // set highest note + for(i = 0; i < NoteCnt[MidiCh]; i++) { + if(h < NoteBuf[MidiCh][i]) { + h = NoteBuf[MidiCh][i]; + } + } + gPlayNoteBuf[MidiCh] = h; + gGateBuf[MidiCh] = ON; +} + +//------------------------------------------------------------- +// Note OFF Message Processing + +void NoteOFF(void) +{ + unsigned char i; + unsigned char h = 0; // highest note + int flg = FALSE; + + // Delete Note If Registed Buffer + for(i = 0; i < NoteCnt[MidiCh]; i++) { + if(flg) { + NoteBuf[MidiCh][i-1] = NoteBuf[MidiCh][i]; + } + if(NoteBuf[MidiCh][i] == MByte[0]) { + flg = TRUE; + } + } + if(flg) NoteCnt[MidiCh]--; + + if(NoteCnt[MidiCh] == 0) { + // Empty Buffer then Gate OFF + gGateBuf[MidiCh] = OFF; + } + else { + // Highest Note + for(i = 0; i < NoteCnt[MidiCh]; i++) { + if( h < NoteBuf[MidiCh][i]) { + h = NoteBuf[MidiCh][i]; + } + } + gPlayNoteBuf[MidiCh] = h; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/midi_parser.h Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,79 @@ +//------------------------------------------------------------- +// file : midi_parser.h +// Copyright (C) 2012 RJB RadioJunkBox +// Released under the MIT License: http://mbed.org/license/mit +//------------------------------------------------------------- + +#ifndef MBED_MIDI_PARSER_H +#define MBED_MIDI_PARSER_H + +//------------------------------------------------------------- +// Define + +#define TRUE 1 +#define FALSE 0 +#define ON 1 +#define OFF 0 + +#define MAX_CH 16 +#define MAX_NOTE_CNT 16 + +// MIDI Massage +#define MIDI_NoteOff 0x80 +#define MIDI_NoteOn 0x90 +#define MIDI_PolykeyPressure 0xA0 +#define MIDI_ControlChange 0xB0 +#define MIDI_ProgramChange 0xC0 +#define MIDI_ChannelPressure 0xD0 +#define MIDI_PitchBend 0xE0 + +#define MIDI_StartSysEx 0xF0 +#define MIDI_TuneRequest 0xF6 +#define MIDI_EndSysEx 0xF7 + +#define MIDI_TimingClock 0xF8 +#define MIDI_Start 0xFA +#define MIDI_Continue 0xFB +#define MIDI_Stop 0xFC +#define MIDI_ActiveSensing 0xFE +#define MIDI_SystemReset 0xFF + +#define MIDI_CC_Moduration 0x01 +#define MIDI_CC_DataEntry 0x06 +#define MIDI_CC_RPN_LSB 0x64 +#define MIDI_CC_RPN_MSB 0x65 + +#define MIDI_MM_AllSoundOff 0x78 +#define MIDI_MM_ResetAllControl 0x79 +#define MIDI_MM_AllNoteOff 0x7B + +//------------------------------------------------------------- +// Global variables + +unsigned char gPlayNoteBuf[MAX_CH]; +unsigned char gGateBuf[MAX_CH]; +unsigned char gPitchBendBuf[MAX_CH]; +unsigned char gModWheelBuf[MAX_CH]; +unsigned char gMIDISYNC_CLK; +unsigned char gMIDISYNC_RUN; + +unsigned char NoteCnt[MAX_CH]; +unsigned char NoteBuf[MAX_CH][MAX_NOTE_CNT]; + +unsigned char RxByte; +unsigned char SysEx; +unsigned char MidiCh; +unsigned char PC; +unsigned char MByte[2]; + +//------------------------------------------------------------- +// Functions + +void MIDI_Parser(unsigned char); +int MIDI_SystemMessage(void); +void MIDI_SystemRealtimeMessage(void); +void MIDI_ChannelMessage(void); +void NoteON(void); +void NoteOFF(void); + +#endif