SDP client for myBlueUSB
Dependents: mbed_TANK_Kinect ftusbClass
Revision 0:7493bf6bb1b9, committed 2011-04-04
- Comitter:
- networker
- Date:
- Mon Apr 04 16:45:20 2011 +0000
- Child:
- 1:70ee392bcfd4
- Commit message:
- initial revision
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdp.cpp Mon Apr 04 16:45:20 2011 +0000 @@ -0,0 +1,329 @@ +#include "mbed.h" +#include "Utils.h" +#include "hci.h" +#include "sdp_data.h" +#include "sdp.h" + +SDPManager SDP; //instance +const unsigned char base_uuid[16] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0, 0x07, 0x70, 0, 0x10, 0, 0}; + +void attribHandler(serv_rec *r) { + printf("Service 0x%08X\n", (*r)[0x0000]->asUnsigned()); + map<unsigned short, sdp_data*>::iterator it = r->begin(); + for (;it != r->end();it++) { + printf(" 0x%04X: %s\n", (*it).first, (*it).second->asString()); + } +} + +//this function is called when the L2CAP layer receives SDP packets (see SDPManager::Open), userdata is the sdpmanager instance +void SDPManager::OnSdpRsp(int socket, SocketState state, const u8* data, int len, void* userData) { +printf("\x1B[%dm", 34); + printf("OnSdpRsp(socket=%d, state=%d, len=%d)\n", socket, state, len); + //printfBytes("Got SDP Response from L2CAP: ", data, len); + SDPManager *self = (SDPManager*)userData; + if (state != SocketState_Open) + return; + sdp_data list(sdp_data::SEQUENCE); + if (data) + self->parseRsp(data, len); + if (len==0) { + sdp_data root(sdp_data::UUID, 0x1101); + sdp_data req(sdp_data::SEQUENCE); + req.add_element(&root); + self->ServiceSearchRequest(&req, 5); + } else if (data[0]==3) { + self->index = self->services.begin(); + if (self->index != self->services.end()) { + unsigned handle = (*self->index).first; + //printf("req.: handle %#X\n", handle); + sdp_data all(0xffffU,4); + list.add_element(&all); + self->ServiceAttributeRequest(handle, 100, &list);//0x1001D + self->index++; + } else printf(" - empty list - \n");//should not happen + } else if (data[0]==5) { + if (self->index != self->services.end()) { + //printf("req.: handle %#X\n", (*self->index).first); + self->ServiceAttributeRequest((*self->index).first, 100, &list); + self->index++; + } else { + printf(" - end of list - \n"); + Socket_Close(self->sdp_socket); //Note: socket=L2CAP, sdp_socket=SDP !!! + } + } +printf("\x1B[%dm", 0); +} + +//this function is called when the SDP sockets receives data (see HCICallback in TestShell), +//currently does not happen because not forwarded from OnSdpRsp, can be used to handle multiple connections +void SDPManager::OnSockCallback(int socket, SocketState state, const u8* data, int len, void* userData) { + printf("OnSockCallback(socket=%d, state=%d, len=%d)\n", socket, state, len); + printfBytes("Got SDP Response from socket: ", data, len); +} + +void SDPManager::errorhandler(unsigned err) {//default error handler + switch (err) { + case 1: + printf("Unsupported version of SDP\n"); + break; + case 2: + printf("Invalid SDP ServiceRecordHandle\n"); + break; + case 3: + printf("SDP syntax error\n"); + break; + case 4: + printf("PDU size was invalid\n"); + break; + case 5: + printf("Continuation state was invalid\n"); + break; + case 6: + printf("SDP server has insufficient resources\n"); + break; + default: + printf("Unknown SDP error code\n"); + break; + } +} + +int SDPManager::ServiceSearchRequest(sdp_data *sp, unsigned count, unsigned cs) { + int parlen = sp->Size() + 3; //no continuation + buf[0] = 2; //pdu + buf[1] = txid>>8; + buf[2] = txid++; + buf[4] = parlen; + buf[3] = parlen>>8; + int p = sp->build(buf+5, 100-10); + buf[p+6] = count; + buf[p+5] = count>>8; + if (cs==0) + buf[p+7] = 0; + else + printf("Continuation not supported\n"); + //printfBytes("SDP Send: ", buf, parlen+5); + return Socket_Send(_l2cap, l2cap_buf, parlen + 5 + OFFSET); +} + +int SDPManager::ServiceAttributeRequest(unsigned handle, unsigned count, sdp_data* al, unsigned cs) { + int parlen = al->Size() + 7; //no continuation + buf[0] = 4; //pdu + buf[1] = txid>>8; + buf[2] = txid++; + buf[4] = parlen; + buf[3] = parlen>>8; + for (int i = 0; i < 4; i++) + buf[i+5] = ((char*)&handle)[3-i]; + buf[9] = count>>8; + buf[10] = count; + int p = al->build(buf+11, 100-26); + if (cs==0) + buf[p+11] = 0; + else + printf("Continuation not supported\n"); + //printfBytes("SDP Send: ", buf, parlen+5); + return Socket_Send(_l2cap, l2cap_buf, parlen + 5 + OFFSET); +} + +int SDPManager::ServiceSearchAttributeRequest(sdp_data *sp, unsigned count, sdp_data* al, unsigned cs) { + int parlen = sp->Size() + al->Size() + 3; //no continuation (1 byte), count (2 bytes) + buf[0] = 6; //pdu + buf[1] = txid>>8; + buf[2] = txid++; + buf[4] = parlen; + buf[3] = parlen>>8; + int p = sp->build(buf+5, 30); + buf[p+6] = count; + buf[p+5] = count>>8; + p += al->build(buf+11, 100-38); + if (cs==0) + buf[p+7] = 0; + else + printf("Continuation not supported\n"); + //printfBytes("SDP Send: ", buf, parlen+5); + return Socket_Send(_l2cap, l2cap_buf, parlen + 5 + OFFSET); +} + +unsigned SDPManager::getval(const unsigned char *p, int n) { + unsigned ret = 0; + for (int i = 0; i < n; i++) + ret = (ret<<8) + (unsigned)p[i]; + return ret; +} + +unsigned SDPManager::length(const unsigned char *el, unsigned &p) { + unsigned len = 0; + switch (el[p++] & 7) {//length + case 0: + len = 1; + break; + case 1: + len = 2; + break; + case 2: + len = 4; + break; + case 3: + len = 8; + break; + case 4: + len = 16; + break; + case 7://4bytes + len= el[p++]<<24; + len += el[p++]<<16; + case 6://2bytes + len += el[p++]<<8; + case 5://1byte + len += el[p++]; + break; + } + return len; +} + +extern "C" void HardFault_Handler() { printf("Hard Fault!\n"); while(1); } + + +unsigned SDPManager::parse (const unsigned char *el, unsigned count, sdp_data* &result, serv_rec* &record) { + unsigned p = 0; + unsigned len = length(el, p); + int end = p+len;//end is the index of the item just after the sequence + sdp_data *item = 0; + switch (el[0]>>3) {//type + case sdp_data::NULL_: + item = new sdp_data(); + break; + case sdp_data::UNSIGNED: + item = new sdp_data((unsigned)getval(el+p, len), len); + break; + case sdp_data::SIGNED: + item = new sdp_data((int)getval(el+p, len), len); + break; + case sdp_data::UUID: + if (len==16) { + char rev[16]; + for (int i = 0; i < 16; i++) + rev[i] = el[p+15-i]; + item = new sdp_data(sdp_data::UUID, rev, len); + } else + item = new sdp_data(sdp_data::UUID, getval(el+p, len), len); + break; + case sdp_data::STRING: + item = new sdp_data((char*)el+p, len); + break; + case sdp_data::BOOL: + item = new sdp_data((bool)getval(el+p, len), len); + break; + case sdp_data::SEQUENCE: + item = new sdp_data(sdp_data::SEQUENCE); + goto skip; + case sdp_data::ALTERNATIVE: + item = new sdp_data(sdp_data::ALTERNATIVE); +skip: {//p points just after the length indicator, hence at the first item IN the sequence +// printf("SEQ%d{%p ", len, item); + int n = 0; + unsigned short key; + serv_rec *dummy = 0; + while (p < end) { + sdp_data *elem = 0; + p += parse(el + p, len-p, elem, dummy);//parse each element in the sequence, the second arg is as yet unused + item->add_element(elem); + if (record) { + if (n & 1) { //value + record->insert(pair<unsigned short, sdp_data*>(key, elem)); + } else //key + key = elem->asUnsigned(); + n++; + } + } + } +// printf("}\n"); + break; + case 8: + item = new sdp_data(sdp_data::URL, (char*)el+p, len); + break; + default: + printf("Parse: Unknown type %d, len=%d (code=%#02X)\n", el[0]>>3, len, el[0]); + } + result = item; + return end; +} + +int SDPManager::parseRsp(const unsigned char*rsp, int len) { + //unsigned tid = rsp[2] + ((unsigned)rsp[1]<<8); + unsigned parlen = rsp[4] + ((unsigned)rsp[3]<<8); +// printf("ParseRsp: tid=%04X, parlen=%d ", tid, parlen); + unsigned cont = 0; + switch (rsp[0]) { + case 1: {//errorRsp + unsigned errorcode = rsp[6] + ((unsigned)rsp[5]<<8); + if (parlen > 2) { + printf("ErrorInfo (%d bytes) for error %d is available\n", parlen-2, errorcode); + } + if (ErrorResponse) + ErrorResponse(errorcode); + return errorcode; + } + //break; + case 3: { //servicesearchRsp + //unsigned total = rsp[6] + ((unsigned)rsp[5]<<8); + unsigned current = rsp[8] + ((unsigned)rsp[7]<<8); + cont = rsp[9+4*current]; +// printf("total=%d, current=%d, cont=%d\n", total, current, cont); + if (cont) break; + //linear list of 32bit service-handles + for (int i = 0; i < current; i++) { + unsigned result = 0; + for (int j = 0; j< 4; j++) + result = (result<<8) + rsp[9 + 4*i + j]; +// printf("SDP Search handle %08X\n", result); + services.insert(pair<unsigned, serv_rec*>(result, 0)); + } + if (ServiceSearchResponse) + ServiceSearchResponse(); + } + break; + case 5: { //serviceattributeRsp + if (tree) delete tree; + unsigned count = rsp[6] + ((unsigned)rsp[5]<<8); + cont = rsp[7+count]; + if (cont) + break; +// printf("pos=%d, count=%d parsing...\n", 7, count); + serv_rec *serv = new serv_rec; + unsigned p = parse(rsp+7, count, tree, serv); + unsigned key = (*serv)[0]->asUnsigned(); + services[key] = serv; +// printf("...parsing done, pos=%d, cont=%d\n", p, cont); + if (ServiceAttributeResponse) + ServiceAttributeResponse(serv); + } + break; + case 7: { //servicesearchattributeRsp + if (tree) delete tree; + unsigned count = rsp[6] + ((unsigned)rsp[5]<<8); + cont = rsp[7+count]; + if (cont) + break; + unsigned pos = 7; + while (rsp[pos]>>3 == sdp_data::SEQUENCE) { + unsigned len = length(rsp, pos); + printf("pos=%d, count=%d, parsing...\n", pos, len); + serv_rec *serv = new serv_rec; + pos = parse(rsp+pos, len, tree, serv); + unsigned key = (*serv)[0]->asUnsigned(); + services[key] = serv; + } + printf("...parsing done, pos=%d\n", pos); + if (ServiceSearchAttributeResponse) + ServiceSearchAttributeResponse(); + } + break; + default: + printf("Unknown SDP response type %02X\n", rsp[0]); + break; + } + if (cont) + printf("Continuation not supported (yet)\n"); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdp.h Mon Apr 04 16:45:20 2011 +0000 @@ -0,0 +1,89 @@ +#ifndef SDP_H +#define SDP_H + +#include <map> +#define OFFSET 8 + +class SDPManager; +extern SDPManager SDP; +typedef map<unsigned short, sdp_data*> serv_rec; + +void attribHandler(serv_rec *r); + +//at the moment, SDP can handle only one connection at a time +class SDPManager: public SocketHandler { + int _l2cap; + int sdp_socket; //at the moment the only socket + unsigned char l2cap_buf[100+OFFSET]; + unsigned char* buf; + unsigned txid; + sdp_data *tree;//root of the entire service tree + map<unsigned, serv_rec*> services;//the set of supported services <handle, service> + map<unsigned, serv_rec*>::iterator index; +public: + SDPManager(): _l2cap(0), txid(1), tree(0) { + ErrorResponse=errorhandler; + ServiceSearchResponse=0; + ServiceAttributeResponse=attribHandler; + ServiceSearchAttributeResponse=0; + buf = l2cap_buf+OFFSET; + } + + //Called as: Socket_Open(SOCKET_SDP, addr, callback, userdata(this)) + virtual int Open(SocketInternal* sock, SocketAddrHdr* addr) { + L2CAPAddr* ad = (L2CAPAddr*)addr; + if (_l2cap) { + printf("This SDP supports only one connection at a time\n"); + return 0; + } + //BD_ADDR* a = &ad->bdaddr; + sdp_socket = sock->ID; + for (index = services.begin(); index != services.end(); index++) + delete (*index).second; + services.clear(); + ad->psm = L2CAP_PSM_SDP;//open the l2cap channel + _l2cap = Socket_Open(SOCKET_L2CAP, addr, OnSdpRsp, this);//this is the socket between SDP and the L2CAP layer + if (_l2cap <= 0) { + printf("Opening L2CAP channel failed\n"); + return _l2cap; + } + printf("Successfully opened L2CAP channel for SDP on socket %d\n", _l2cap); + return sock->ID; + } + + virtual int Send(SocketInternal* sock, const u8* data, int len) { + printf("SDPManager::Send should not be called directly\n"); + return Socket_Send(_l2cap, data, len); + } + + virtual int Close(SocketInternal* sock) { + printf("SDP socket %d and L2CAP socket %d closed\n", sock->ID, _l2cap); + return Socket_Close(_l2cap); + } + + virtual char* Name() { + return "SDPManager SocketHandler"; + } + + //this function is called when the L2CAP layer receives SDP packets (see SDPManager::Open), userdata is the sdpmanager instance + static void OnSdpRsp(int socket, SocketState state, const u8* data, int len, void* userData) ; + //this function is called when the SDP sockets receives data (see HCICallback in TestShell), + //currently does not happen because not forwarded from OnSdpRsp, can be used to handle multiple connections + static void OnSockCallback(int socket, SocketState state, const u8* data, int len, void* userData) ; + static void errorhandler(unsigned err); + + void (*ErrorResponse)(unsigned) ; + void (*ServiceSearchResponse)() ; + void (*ServiceAttributeResponse)(serv_rec*) ; + void (*ServiceSearchAttributeResponse)() ; + int ServiceSearchRequest(sdp_data *sp, unsigned count, unsigned cs=0); + int ServiceAttributeRequest(unsigned handle, unsigned count, sdp_data* al, unsigned cs=0) ; + int ServiceSearchAttributeRequest(sdp_data *sp, unsigned count, sdp_data* al, unsigned cs=0); +private: + unsigned length(const unsigned char *el, unsigned &p); + unsigned getval(const unsigned char *p, int n) ; + unsigned parse (const unsigned char *el, unsigned count, sdp_data* &result, serv_rec* &record) ; + int parseRsp(const unsigned char*rsp, int len) ; +}; + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdp_data.cpp Mon Apr 04 16:45:20 2011 +0000 @@ -0,0 +1,203 @@ +#include "mbed.h" +#include "sdp_data.h" + +char sdp_data::ret[12]; + +unsigned sdp_data::asUnsigned() { + switch (type) { + case NULL_: + return 0; + case UNSIGNED: + case SIGNED: + case BOOL: + return data; + case UUID: +#ifdef LONGUUID + return uuid[6] + uuid[7]<<16; +#else + return data; +#endif + default: + return 0; + } +} + +const char* sdp_data::asString(bool alt) { + char sep = ','; + switch (type) { + case NULL_: + return "NULL"; + case UNSIGNED: + if (alt) sprintf(ret, "0x%0*X", size*2, data); + else sprintf(ret, "%u", data); + return ret; + case SIGNED: + sprintf(ret, "%d", data); + return ret; + case BOOL: + return data ? "TRUE" : "FALSE"; + case STRING: + case URL: + return str; + case ALTERNATIVE: + sep = '|'; + case SEQUENCE: { + if (longstr) delete[] longstr; + int n = sprintf(ret, "SEQ %d { ", size) + 1; + longstr = new char[n]; + strcpy(longstr, ret); + for (int i = 0; i < sequence.size(); i++) { + const char *s = sequence[i]->asString(alt); + n = strlen(longstr) + strlen(s) + 2; + char *t = new char[n]; + strcpy(t, longstr); + strcat(t, s); + t[n-2] = sep; + t[n-1]='\0'; + //printf("[%s]+[%s]+%c=[%s]\n", longstr, s, sep, t); + delete[] longstr; + longstr = t; + } + longstr[n-2] = '}'; + } + return longstr; + case UUID: +#ifdef LONGUUID + switch (size) { + case 2: + sprintf(ret, "0x%04X", uuid[6]); + return ret; + case 4: + sprintf(ret, "0x%04X%04X", uuid[7],uuid[6]); + return ret; + case 16: + longstr = new char[35]; + sprintf(longstr, "%04X%04X-%04X-%04X-%04X-%04X%04X%04X", uuid[7],uuid[6],uuid[5],uuid[4],uuid[3],uuid[2],uuid[1],uuid[0]); + return longstr; + } +#else + switch (size) { + case 2: + sprintf(ret, "0x%04X", data & 0xffff); + return ret; + case 4: + sprintf(ret, "0x%08X", data); + return ret; + case 16: + longstr = new char[35]; + sprintf(longstr, "%08X-%04X-%04X-%04X-%04X%04X%04X", data,base_uuid[5],base_uuid[4],base_uuid[3],base_uuid[2],base_uuid[1],base_uuid[0]); + return longstr; + } +#endif + } + return "Unsupported"; +} + +unsigned sdp_data::Size() { + if (size<3 || size==4 || size==8 || size==16) + return size+1;//include descriptor + if (size < 256) return size+2; //1 extra byte + if (size < 65536) return size+3; //2 extra bytes + return size+5; //4 extra bytes +} + +unsigned sdp_data::sizedesc(unsigned char *buf) { + int desc, extra=0; + switch (size) { + case 0: + case 1: + desc = 0; + break; + case 2: + desc = 1; + break; + case 4: + desc = 2; + break; + case 8: + desc = 3; + break; + case 16: + desc = 4; + break; + default: + if (size < 256) { + desc = 5; + extra = 1; + buf[1] = size; + } else if (size < 65536) { + desc = 6; + extra = 2; + *(unsigned short*)&buf[1] = size; + } else { + desc = 7; + extra = 4; + *(unsigned*)&buf[1] = size; + } + } + buf[0] |= desc; + return extra+1; +} + +void sdp_data::revcpy(unsigned char*d, const unsigned char*s, int n) { + for (int i = 0; i < n; i++) + d[i] = s[n-i-1]; +} + +unsigned sdp_data::build(unsigned char *buf, unsigned max) { + int p = 0; + if (size+5 < max) { + buf[p] = type<<3; + switch (type) { + case NULL_: + p++; + break; + case UNSIGNED: + case SIGNED: + case BOOL: + p += sizedesc(buf+p); + revcpy(buf+p, (unsigned char*)&data, size); + break; + case UUID: + p += sizedesc(buf+p); +#ifdef LONGUUID + switch (size) { + case 2: + case 4: + revcpy(buf+p, (unsigned char*)&uuid[6], size); + break; + case 16: + revcpy(buf+p, (unsigned char*)uuid, size); + break; + } +#else + switch (size) { + case 2: + case 4: + revcpy(buf+p, (unsigned char*)&data, size); + break; + case 16: + revcpy(buf+p, (unsigned char*)&data, 4); + revcpy(buf+p+4, base_uuid, 12); + break; + } +#endif + break; + case STRING: + case URL: + p += sizedesc(buf+p); + memcpy(buf+p, str, size); + break; + case SEQUENCE: + case ALTERNATIVE: + p += sizedesc(buf+p); + for (int i = 0; i < sequence.size(); i++) + sequence.at(i)->build(buf+p, max-p); + break; + } + p += size; + } else { //too big, make continuation + printf ("Data too large to fit\n"); + } + return p; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdp_data.h Mon Apr 04 16:45:20 2011 +0000 @@ -0,0 +1,106 @@ +#ifndef SDP_DATA_H +#define SDP_DATA_H + +#include <vector> + +extern const unsigned char base_uuid[16];// = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0, 0x07, 0x70, 0, 0x10, 0, 0}; + +class sdp_data { +public: + enum elements { NULL_, UNSIGNED, SIGNED, UUID, STRING, BOOL, SEQUENCE, ALTERNATIVE, URL}; +private: + enum elements type; + char size; + union { + unsigned data; + char *str; +#ifdef LONGUUID + unsigned short uuid[8]; +#endif + }; + static char ret[12]; + char *longstr; + vector<sdp_data*> sequence; +public: + sdp_data(): type(NULL_), size(0), longstr(0) { + //printf("NULL%d ", size); + } + sdp_data(unsigned d, unsigned sz=4): type(UNSIGNED), size(sz), longstr(0) { + data=d; + //printf("UINT%d=%u ", size, data); + } + sdp_data(signed d, unsigned sz=4): type(SIGNED), size(sz), longstr(0) { + data=d; + // printf("INT%d=%d ", size, data); + } + sdp_data(bool d, unsigned sz=1): type(BOOL), size(sz), longstr(0) { + data=d; + // printf("BOOL%d=%u ", size, data); + } + sdp_data(char*s, unsigned sz=0): type(STRING), longstr(0) { + if (sz) size = sz; + else size = strlen(s)+1; + str = new char[size]; + strncpy(str, s, size); + // printf("STR%d='%s' ", size, str); + } + sdp_data(enum elements t, unsigned d, unsigned sz=2): type(t), size(sz), longstr(0) { + if (t==UUID) { +#ifdef LONGUUID + memcpy(uuid, base_uuid, 16); + uuid[6] = d; + uuid[7] = d>>16; + // printf("UUID%d=%04X%04X ", size, uuid[7], uuid[6]); +#else + data = d; +#endif + } else printf("Please use other constructor for type %d\n", t); + } + sdp_data(enum elements t, char *d=0, unsigned sz=0): type(t), size(sz), longstr(0) { + switch (t) { +#ifdef LONGUUID + case UUID: + memcpy(uuid, d, size); + // printf("UUID%d=%08X ", size, uuid[6]); + break; +#endif + case URL: + //size = strlen(d)+1; + str = new char[size+1]; + strcpy(str, d); + // printf("URL%d='%u' ", size, str); + break; + case SEQUENCE: + case ALTERNATIVE: + break; + default: + printf("Please use other constructor for type %d\n", t); + } + } + ~sdp_data() { + switch (type) { + case STRING: + case URL: + delete[] str; + break; + case SEQUENCE: + case ALTERNATIVE: + for (int i = 0; i < sequence.size(); i++) + delete sequence.at(i); + break; + } + delete[] longstr; + } + void add_element(sdp_data *el) { + sequence.push_back(el); + size += el->Size(); + } + unsigned asUnsigned() ; + const char* asString(bool alt=false) ; + unsigned Size() ; + unsigned sizedesc(unsigned char *buf) ; + void revcpy(unsigned char*d, const unsigned char*s, int n) ; + unsigned build(unsigned char *buf, unsigned max) ; +}; + +#endif \ No newline at end of file