SDP client for myBlueUSB
Dependents: mbed_TANK_Kinect ftusbClass
sdp.cpp
- Committer:
- networker
- Date:
- 2011-04-07
- Revision:
- 2:d5a27b2d2e08
- Parent:
- 1:70ee392bcfd4
- Child:
- 3:e8d2ebb7392e
File content as of revision 2:d5a27b2d2e08:
#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()); } } #define BROWSEROOT 0x1002 #define SERIALSERV 0x1101 //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) { void SDPManager::OnSdpRsp(const u8* data, int len) { static sdp_data list(sdp_data::SEQUENCE); static sdp_data all(0x0000ffffU,4); static sdp_data serviceID(0U, 2); static sdp_data name(0x100U, 2); static sdp_data root(sdp_data::UUID, BROWSEROOT); static sdp_data req(sdp_data::SEQUENCE); static bool once = true; //printf("_state=%d first=%d ", _state, once); if (once) { list.add_element(&all); //list.add_element(&serviceID); //list.add_element(&name); req.add_element(&root); once = false; } if (data){ parseRsp(data, len); } switch (_state) { case 0: //closed if (len==0) { //socket just opened //'Open' cleared the services list ServiceSearchRequest(&req, 10); _state = 1; //wait for service handles } break; case 1: //service handles arriving if (contState[0]) {//continuation, repeat request ServiceSearchRequest(&req, 5); } else { if (data[0]==3) { index = services.begin(); if (index != services.end()) { unsigned handle = (*index).first; //printf("req.: handle %#X\n", handle); ServiceAttributeRequest(handle, 100, &list);//0x1001D } else printf(" - empty list - \n");//should not happen _state = 2; //wait for attribute response } else printf("Expected a ServiceSearchResponse 0x03, got %#x\n", data[0]); } break; case 2: if (contState[0])//repeat request ServiceAttributeRequest((*index).first, 100, &list); else { if (data[0]==5) { index++; //move to next service if (index != services.end()) { //printf("req.: handle %#X\n", (*index).first); ServiceAttributeRequest((*index).first, 100, &list); } else { printf(" - end of list - \n"); Socket_Close(sdp_socket); //Note: socket=L2CAP, sdp_socket=SDP !!! _state = 0; } } else printf("Expected a ServiceAttributeResponse 0x05, got %#x\n", data[0]); } break; } } //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() + contState[0] + 3; 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; buf[p+7] = contState[0]; for (int j = 1; j <= contState[0]; j++) buf[p+j+7] = contState[j]; //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() + contState[0] + 7; 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); buf[p+11] = contState[0]; for (int j = 1; j <= contState[0]; j++) buf[p+j+11] = contState[j]; //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() + contState[0] + 3; // count (2 bytes) + at least 1 cont 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); buf[p+7] = contState[0]; for (int j = 1; j <= contState[0]; j++) buf[p+j+7] = contState[j]; //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! %d bytes left\n", AvailableMemory(1)); while (1); } unsigned SDPManager::parseLight (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_: printf("NULL "); break; case sdp_data::UNSIGNED: printf("UINT%d=%u ", len, (unsigned)getval(el+p, len)); break; case sdp_data::SIGNED: printf("INT%d=%d ", len, (unsigned)getval(el+p, len)); break; case sdp_data::UUID: if (len==16) { char rev[16]; printf("UUID16= "); for (int i = 0; i < 16; i++) printf("%02x ", el[p+i]); } else printf("UUID%d=%u ", len, (unsigned)getval(el+p, len)); break; case sdp_data::STRING: printf("STR%d='%s' ", len, (char*)el+p); break; case sdp_data::BOOL: printf("BOOL%d=%d ", len, (unsigned)getval(el+p, len)); break; case sdp_data::SEQUENCE: goto skip; case 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 += parseLight(el + p, len-p, elem, dummy);//parse each element in the sequence, the second arg is as yet unused if (record) { if (n & 1) { //value record->insert(pair<unsigned short, sdp_data*>(key, elem)); } else //key key = n; n++; } } } printf("}\n"); break; case 8: printf("URL%d='%s' ", len, (char*)el+p); break; default: printf("Parse: Unknown type %d, len=%d (code=%#02X)\n", el[0]>>3, len, el[0]); } result = item; return end; } 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;//means: there is no service record to fill in for deeper levels while (p < end) { sdp_data *elem = 0; //this becomes the tree with attribute values p += parse(el + p, len-p, elem, dummy);//parse each element in the sequence, the second arg is as yet unused if (record) { //if at the level of attribute list, add elem to record as key/value pair if (n & 1) { //value record->insert(pair<unsigned short, sdp_data*>(key, elem)); } else //key key = elem->asUnsigned(); n++; } else //just add the elements to the value tree item->add_element(elem); } } //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; } void SDPManager::append(const unsigned char *payload, int len) { unsigned char *tmp = new unsigned char[byteCount + len];//append the payload to the previous continuation buffer if (contBuf && byteCount) { memcpy(tmp, contBuf, byteCount); //copy the existing part delete[] contBuf;//delete the old buffer } memcpy(tmp+byteCount, payload, len); //append the new part contBuf = tmp; byteCount += len; } void SDPManager::freeBuf() { if (contBuf) { delete[] contBuf; contBuf = 0; } byteCount = 0; } //TODO: test case 7, add server support (cases 2, 4, 6) //3 cases: cont==0 && contBuf==0 -> use rsp; cont!=0 -> append; cont==0 && contBuf!=0 -> append and use contBuf 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]; memcpy(contState, &rsp[9+4*current], cont+1);//copy the continuation state printf("total=%d, current=%d, cont=%d\n", total, current, cont); if (cont) { //no special handling here, just append the servicerecordhandles } //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 unsigned count = rsp[6] + ((unsigned)rsp[5]<<8);//bytes in this part of the attribute list // append(rsp+7, count); cont = rsp[7+count]; memcpy(contState, &rsp[7+count], cont+1);//copy the continuation state if (cont) { append(rsp+7, count); break; } //printf("count=%d parsing...\n", byteCount); serv_rec *serv = new serv_rec; if (contBuf){ append(rsp+7, count); parse(contBuf, byteCount, tree, serv); } else parse(rsp+7, count, tree, serv); //printf("...parsing done, "); //get the AttributeID, make sure attribId 0 is always included in the request unsigned key = (*serv)[0]->asUnsigned();//AttributeID '0' always refers to the serviceID //printf("Key=%#X\n", key); //key will be 0 when not requested services[key] = serv; //Add the attribute list to the services freeBuf(); if (ServiceAttributeResponse) ServiceAttributeResponse(serv); } break; //below is UNTESTED case 7: { //servicesearchattributeRsp unsigned count = rsp[6] + ((unsigned)rsp[5]<<8); append(rsp+7, count); cont = rsp[7+count]; memcpy(contState, &rsp[7+count], cont+1); if (cont) break; unsigned pos = 0; if (contBuf[pos]>>3 != sdp_data::SEQUENCE) { printf("Expected a sequence of attribute lists\n"); break; } unsigned len = length(contBuf, pos);//get the length of the list of lists and advance pos to the first list while (pos<len) { printf("pos=%d, count=%d, parsing...\n", pos, len); serv_rec *serv = new serv_rec; pos = parse(contBuf+pos, len, tree, serv); unsigned key = (*serv)[0]->asUnsigned(); services[key] = serv; } freeBuf(); printf("...parsing done, pos=%d\n", pos); if (ServiceSearchAttributeResponse) ServiceSearchAttributeResponse(); } break; default: printf("Unknown SDP response type %02X\n", rsp[0]); break; } return 0; }