| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #define ADK_INTERNAL |
| #include "fwk.h" |
| #include "btSDP.h" |
| #include <string.h> |
| #include "sgBuf.h" |
| #include "btL2CAP.h" |
| #include "dbg.h" |
| |
| |
| #define SDP_PDU_Error_Response 1 |
| #define SDP_PDU_Service_Search_Request 2 |
| #define SDP_PDU_Service_Search_Response 3 |
| #define SDP_PDU_Service_Attribute_Request 4 |
| #define SDP_PDU_Service_Attribute_Response 5 |
| #define SDP_PDU_Service_Search_Attribute_Request 6 |
| #define SDP_PDU_Service_Search_Attribute_Response 7 |
| |
| #define SDP_ERR_Invalid_SDP_Version 0x0001 |
| #define SDP_ERR_Invalid_Service_Record_Handle 0x0002 |
| #define SDP_ERR_Invalid_Request_Syntax 0x0003 |
| #define SDP_ERR_Invalid_PDU_Size 0x0004 |
| #define SDP_ERR_Invalid_Continuation_State 0x0005 |
| #define SDP_ERR_Insufficient_Resources 0x0006 |
| |
| |
| typedef struct{ |
| |
| uint64_t hi, lo; |
| |
| }uuid; |
| |
| #define MAX_UUIDS_IN_SEARCH 12 //as per spec |
| #define MAX_ATTRS_IN_SEARCH_STRING 8 //as per my opinion |
| #define MAX_SEARCH_RESULTS 16 //no more than this will ever be returned |
| |
| typedef struct SdpService{ |
| |
| struct SdpService* next; |
| |
| uint32_t handle; |
| const uint8_t* descriptor; |
| uint16_t descrLen; |
| |
| }SdpService; |
| |
| typedef struct{ |
| |
| uint16_t aclConn; |
| uint16_t remChan; |
| |
| uint32_t contDescr; //which continuation descriptor we expect |
| uint8_t* result; |
| uint32_t resultSz; |
| uint16_t numMatches; //for servicesearch |
| |
| }SdpInstance; |
| |
| |
| static SdpService* knownServices = NULL; |
| static uint32_t sdpContVal = 0x12345678; |
| static uint32_t sdpNextHandle = 0; |
| |
| static const uint8_t sdpDescrSdp[] = |
| { |
| //define the SDP service itself |
| //service class ID list |
| SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0x00, 0x01, SDP_ITEM_DESC(SDP_TYPE_ARRAY, SDP_SZ_u8), 3, |
| SDP_ITEM_DESC(SDP_TYPE_UUID, SDP_SZ_2), 0x10, 0x00, // ServiceDiscoveryServerServiceClassID |
| //ProtocolDescriptorList |
| SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0x00, 0x04, SDP_ITEM_DESC(SDP_TYPE_ARRAY, SDP_SZ_u8), 8, |
| SDP_ITEM_DESC(SDP_TYPE_ARRAY, SDP_SZ_u8), 6, |
| SDP_ITEM_DESC(SDP_TYPE_UUID, SDP_SZ_2), 0x01, 0x00, // L2CAP |
| SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), L2CAP_PSM_SDP >> 8, L2CAP_PSM_SDP & 0xFF, // L2CAP PSM |
| |
| //browse group list |
| SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0x00, 0x05, SDP_ITEM_DESC(SDP_TYPE_ARRAY, SDP_SZ_u8), 3, |
| SDP_ITEM_DESC(SDP_TYPE_UUID, SDP_SZ_2), 0x10, 0x02, // Public Browse Group |
| |
| //magic data #1 |
| SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0xDD, 0xDD, SDP_ITEM_DESC(SDP_TYPE_TEXT, SDP_SZ_u8), 19, 0x53, 0x57, 0x3A, 0x20, 0x44, 0x6D, 0x69, 0x74, 0x72, 0x79, 0x20, 0x47, 0x72, 0x69, 0x6e, 0x62, 0x65, 0x72, 0x67, |
| //magic data #2 |
| SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0xDD, 0xDE, SDP_ITEM_DESC(SDP_TYPE_TEXT, SDP_SZ_u8), 19, 0x48, 0x57, 0x3A, 0x20, 0x45, 0x72, 0x69, 0x63, 0x20, 0x53, 0x63, 0x68, 0x6c, 0x61, 0x65, 0x70, 0x66, 0x65, 0x72 |
| }; |
| |
| static const uuid bt_base_uuid = {0x0000000000001000ULL, 0x800000805F9B34FBULL}; |
| |
| static void sdpIntToUUID(uuid* dst, uint32_t val){ |
| |
| *dst = bt_base_uuid; |
| dst->hi += ((uint64_t)val) << 32; |
| } |
| |
| static char sdpUuidEqual(const uuid* a, const uuid* b){ |
| |
| return a->lo == b->lo && a->hi == b->hi; |
| } |
| |
| static uint32_t btSdpGetElemSz(const uint8_t** descr){ |
| |
| const uint8_t* ptr = *descr; |
| uint8_t item = *ptr++; |
| uint32_t sz = 0; |
| |
| if((item >> 3) != SDP_TYPE_NIL){ |
| |
| switch(item & 7){ |
| |
| case SDP_SZ_1: |
| case SDP_SZ_2: |
| case SDP_SZ_4: |
| case SDP_SZ_8: |
| case SDP_SZ_16: |
| |
| sz = 1 << (item & 7); |
| break; |
| |
| case SDP_SZ_u8: |
| |
| sz = *ptr++; |
| break; |
| |
| case SDP_SZ_u16: |
| |
| sz = ptr[0]; |
| sz = (sz << 8) | ptr[1]; |
| ptr += 2; |
| break; |
| |
| case SDP_SZ_u32: |
| |
| sz = ptr[0]; |
| sz = (sz << 8) | ptr[1]; |
| sz = (sz << 8) | ptr[2]; |
| sz = (sz << 8) | ptr[3]; |
| ptr += 4; |
| break; |
| } |
| } |
| *descr = ptr; |
| return sz; |
| } |
| |
| static uint8_t btSdpGetUUID(const uint8_t** descr, uuid* dst){ //return num bytes consumed |
| |
| uint32_t sz, i; |
| const uint8_t* orig = *descr; |
| |
| if(((**descr) >> 3) != SDP_TYPE_UUID) return 0; //not valid UUID type |
| sz = btSdpGetElemSz(descr); |
| |
| switch(sz){ |
| |
| case 2: |
| |
| sdpIntToUUID(dst, (((uint32_t)((*descr)[0])) << 8) | ((*descr)[1])); |
| break; |
| |
| case 4: |
| |
| sdpIntToUUID(dst, (((uint32_t)((*descr)[0])) << 24) | (((uint32_t)((*descr)[1])) << 16) | (((uint32_t)((*descr)[2])) << 8) | ((*descr)[3])); |
| break; |
| |
| case 16: |
| |
| dst->lo = 0; |
| dst->hi = 0; |
| |
| for(i = 0; i < 8; i++){ |
| |
| dst->lo = (dst->lo << 8) | (*descr)[i]; |
| dst->hi = (dst->lo << 8) | (*descr)[i + 8]; |
| } |
| break; |
| |
| default: |
| |
| return 0; |
| } |
| *descr += sz; |
| |
| return (*descr) - orig; |
| } |
| |
| static void btStdRecursiveWalk(void* itemList, uint8_t* listSzP, sg_buf** walkResultP, const uint8_t** ptr, uint32_t len){ |
| |
| uint32_t sz; |
| uint8_t typ, numWantedIDs; |
| const uint8_t* end = (*ptr) + len; |
| uuid id; |
| sg_buf* result = NULL; |
| char isID = 1, skipNext = 0; |
| uuid* wantedIDs; |
| uint8_t* numWantedIDsP; |
| uint32_t* wantedRanges; |
| uint8_t wantedRangesListSz; |
| |
| |
| if(walkResultP){ //copy-traversal |
| |
| result = sg_alloc(); |
| if(!result) return; |
| |
| wantedIDs = NULL; |
| numWantedIDsP = NULL; |
| wantedRanges = itemList; |
| wantedRangesListSz = *listSzP; |
| } |
| else{ //search for UUIDs |
| |
| wantedIDs = itemList; |
| numWantedIDsP = listSzP; |
| numWantedIDs = *numWantedIDsP; |
| wantedRanges = NULL; |
| wantedRangesListSz = 0; |
| } |
| |
| while((*ptr) < end){ |
| |
| typ = (**ptr) >> 3; |
| |
| if(wantedIDs && typ == SDP_TYPE_UUID){ |
| |
| sz = btSdpGetUUID(ptr, &id); |
| if(end < (*ptr)){ |
| |
| dbgPrintf("SDP: UUID size > allowed size (%d, %d)\n", sz, end - (*ptr)); |
| goto out; |
| } |
| |
| for(sz = 0; sz < numWantedIDs; sz++){ |
| |
| if(sdpUuidEqual(wantedIDs + sz, &id)){ |
| |
| wantedIDs[sz] = wantedIDs[numWantedIDs - 1]; |
| numWantedIDs--; |
| sz--; |
| } |
| } |
| } |
| else{ |
| |
| const uint8_t* itemStart = *ptr; |
| |
| sz = btSdpGetElemSz(ptr); |
| |
| if(sz > (unsigned)(end - (*ptr))){ |
| |
| dbgPrintf("SDP: element size > allowed size (%d, %d)\n", sz, end - (*ptr)); |
| goto out; |
| } |
| |
| if(typ == SDP_TYPE_ARRAY || typ == SDP_TYPE_OR_LIST){ |
| |
| btStdRecursiveWalk(wantedIDs, &numWantedIDs, NULL, ptr, sz); |
| } |
| else{ |
| |
| (*ptr) += sz; |
| } |
| if(walkResultP){ |
| |
| if(isID){ |
| |
| uint16_t attrID; |
| uint8_t i; |
| |
| if(sz != 2) dbgPrintf("SDP: attrib ID not 16 bits!\n"); |
| |
| attrID = (*ptr)[-2]; |
| attrID = (attrID << 8) | (*ptr)[-1]; |
| |
| skipNext = 2; |
| for(i = 0; i < wantedRangesListSz && skipNext; i++){ |
| |
| if(attrID >= (wantedRanges[i] >> 16) && attrID <= (wantedRanges[i] & 0xFFFF)) skipNext = 0; //in range |
| } |
| } |
| isID ^= 1; |
| |
| if(skipNext){ |
| |
| skipNext--; |
| } |
| else{ |
| |
| if(!sg_add_back(result, itemStart, (*ptr) - itemStart, SG_FLAG_MAKE_A_COPY)){ |
| |
| sg_free(result); |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| out: |
| if(walkResultP) *walkResultP = result; |
| if(numWantedIDsP) *numWantedIDsP = numWantedIDs; |
| } |
| |
| static char btSdpPutIntoGroup(sg_buf* buf){ |
| |
| uint8_t i, sizeFieldSz, sizeFieldName; |
| uint32_t sz = sg_length(buf); |
| uint8_t res[5]; |
| |
| //figure out needed header size field |
| if(sz < 0x100){ |
| |
| sizeFieldSz = 1; |
| sizeFieldName = SDP_SZ_u8; |
| sz <<= 24; |
| } |
| else if(sz < 0x10000){ |
| |
| sizeFieldSz = 2; |
| sizeFieldName = SDP_SZ_u16; |
| sz <<= 16; |
| } |
| else{ |
| |
| sizeFieldSz = 4; |
| sizeFieldName = SDP_SZ_u32; |
| } |
| |
| //add the header |
| res[0] = SDP_ITEM_DESC(SDP_TYPE_ARRAY, sizeFieldName); |
| for(i = 0; i < sizeFieldSz; i++, sz <<= 8) res[1 + i] = sz >> 24; |
| return sg_add_front(buf, res, 1 + sizeFieldSz, SG_FLAG_MAKE_A_COPY); |
| } |
| |
| static sg_buf* btSdpError(const uint8_t* trans, uint16_t errNum){ |
| |
| sg_buf* buf; |
| uint8_t data[] = {SDP_PDU_Error_Response, trans[0], trans[1], errNum >> 8, errNum, 0, 0}; |
| |
| buf = sg_alloc(); |
| if(buf){ |
| |
| if(!sg_add_front(buf, data, sizeof(data), SG_FLAG_MAKE_A_COPY)){ |
| |
| sg_free(buf); |
| buf = NULL; |
| } |
| } |
| return NULL; |
| } |
| |
| static sg_buf* btSdpProcessRequest(SdpInstance* inst, const uint8_t* req, uint16_t reqSz){ |
| uint8_t trans[2] ,cmd, contStateSz, numIDs = 0, numAttrs = 0; |
| uint32_t maxReplSz = 0, wantedHandle = 0, sz; |
| uint32_t attrs[MAX_ATTRS_IN_SEARCH_STRING]; |
| SdpService* results[MAX_SEARCH_RESULTS]; |
| uuid ids[MAX_UUIDS_IN_SEARCH]; |
| const uint8_t* end; |
| sg_buf* result; |
| unsigned i, j; |
| |
| cmd = *req++; |
| trans[0] = *req++; |
| trans[1] = *req++; |
| |
| reqSz -= 5; |
| if(reqSz != (((uint16_t)req[0]) << 8) + req[1]) return btSdpError(trans, SDP_ERR_Invalid_PDU_Size); |
| req += 2; |
| |
| //dbgPrintf("SDP request cmd %d (session %02X%02X) with %d bytes of data\n", cmd, trans[0], trans[1], reqSz); |
| |
| if(cmd == SDP_PDU_Service_Search_Request || cmd == SDP_PDU_Service_Search_Attribute_Request){ |
| |
| if((*req) >> 3 != SDP_TYPE_ARRAY) return btSdpError(trans, SDP_ERR_Invalid_Request_Syntax); |
| sz = btSdpGetElemSz(&req); |
| end = req + sz; |
| |
| while(req < end){ |
| |
| if(numIDs == MAX_UUIDS_IN_SEARCH) return btSdpError(trans, SDP_ERR_Invalid_Request_Syntax); //too many requests |
| if(!btSdpGetUUID(&req, &ids[numIDs++])) return btSdpError(trans, SDP_ERR_Invalid_Request_Syntax); //malformed UUID |
| } |
| } |
| else if(cmd == SDP_PDU_Service_Attribute_Request){ |
| |
| for(i = 0; i < 4; i++) wantedHandle = (wantedHandle << 8) | *req++; |
| } |
| else{ |
| |
| dbgPrintf("SDP: invalid request: %d\n", cmd); |
| return btSdpError(trans, SDP_ERR_Invalid_Request_Syntax); |
| } |
| |
| for(i = 0; i < 2; i++) maxReplSz = (maxReplSz << 8) | *req++; |
| |
| if(cmd == SDP_PDU_Service_Attribute_Request || cmd == SDP_PDU_Service_Search_Attribute_Request){ |
| |
| if((*req) >> 3 != SDP_TYPE_ARRAY) return btSdpError(trans, SDP_ERR_Invalid_Request_Syntax); |
| sz = btSdpGetElemSz(&req); |
| end = req + sz; |
| |
| while(req < end){ |
| |
| if(numAttrs == MAX_UUIDS_IN_SEARCH) return btSdpError(trans, SDP_ERR_Insufficient_Resources); //too many -> unsupported request -> fail |
| sz = btSdpGetElemSz(&req); |
| if(sz == 2){ |
| |
| sz = 0; |
| for(i =0; i < 2; i++) sz = (sz << 8) | *req++; |
| sz |= sz << 16; |
| } |
| else if(sz == 4){ |
| |
| sz = 0; |
| for(i =0; i < 4; i++) sz = (sz << 8) | *req++; |
| } |
| else return btSdpError(trans, SDP_ERR_Invalid_Request_Syntax); //fail -> invalid number format |
| attrs[numAttrs++] = sz; |
| } |
| } |
| |
| contStateSz = *req++; |
| |
| if(contStateSz){ // verify continuation is valid or fail |
| uint32_t contState = 0; |
| |
| if(contStateSz != sizeof(uint32_t)) return btSdpError(trans, SDP_ERR_Invalid_Continuation_State); |
| for(i = 0; i < 4; i++) contState = (contState << 8) | *req++; |
| |
| if(contState != inst->contDescr || !inst->result){ |
| |
| dbgPrintf("SDP: invalid continuation state. Wanted %08X, got %08X\n", inst->contDescr, contState); |
| if(inst->result){ |
| |
| free(inst->result); |
| inst->result = NULL; |
| } |
| return btSdpError(trans, SDP_ERR_Invalid_Continuation_State); |
| } |
| } |
| else{ //perform the actual search |
| |
| SdpService* curSvc = knownServices; |
| uint8_t numFound = 0; |
| |
| //cleanup first |
| if(inst->result){ |
| |
| free(inst->result); |
| inst->result = NULL; |
| } |
| |
| //perform the search |
| if(cmd == SDP_PDU_Service_Search_Request || cmd == SDP_PDU_Service_Search_Attribute_Request){ |
| |
| for(curSvc = knownServices; curSvc && numFound < MAX_SEARCH_RESULTS; curSvc = curSvc->next){ |
| |
| const uint8_t* ptr = curSvc->descriptor; |
| uuid uuids_copy[MAX_UUIDS_IN_SEARCH]; |
| uint8_t num; |
| |
| for(num = 0; num < numIDs; num++) uuids_copy[num] = ids[num]; |
| |
| btStdRecursiveWalk(uuids_copy, &num, NULL, &ptr, curSvc->descrLen); |
| if(!num) results[numFound++] = curSvc; |
| } |
| } |
| else if(cmd == SDP_PDU_Service_Attribute_Request){ |
| |
| for(curSvc = knownServices; curSvc && !numFound; curSvc = curSvc->next){ |
| |
| if(curSvc->handle == wantedHandle) results[numFound++] = curSvc; |
| } |
| if(!numFound) return btSdpError(trans, SDP_ERR_Invalid_Service_Record_Handle); |
| } |
| |
| //gather & prepare results |
| if(cmd == SDP_PDU_Service_Attribute_Request || cmd == SDP_PDU_Service_Search_Attribute_Request){ |
| |
| //we'll assemble the whole result in this buffer |
| sg_buf* resultSoFar = sg_alloc(); |
| if(!resultSoFar) return btSdpError(trans, SDP_ERR_Insufficient_Resources); |
| |
| //process each match |
| for(i = 0; i < numFound; i++){ |
| |
| const uint8_t* ptr = results[i]->descriptor; |
| sg_buf* res; |
| |
| //collect wanted attributes |
| btStdRecursiveWalk(attrs, &numAttrs, &res, &ptr, results[i]->descrLen); |
| if(!res) continue; |
| |
| //if requested, add the handle attribute |
| for(j = 0; j < numAttrs; j++) if(SDP_ATTR_HANDLE >= (attrs[j] >> 16) && SDP_ATTR_HANDLE <= (attrs[j] & 0xFFFF)) break; |
| if(j != numAttrs){ |
| uint8_t buf[8] = {SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0x00, 0x00, SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_4)}; |
| |
| buf[4] = results[i]->handle >> 24; |
| buf[5] = results[i]->handle >> 16; |
| buf[6] = results[i]->handle >> 8; |
| buf[7] = results[i]->handle; |
| |
| if(!sg_add_back(res, buf, sizeof(buf), SG_FLAG_MAKE_A_COPY)){ |
| |
| sg_free(res); |
| free(res); |
| continue; |
| } |
| } |
| |
| //wrap and append to the full results list |
| if(btSdpPutIntoGroup(res)) sg_concat_back(resultSoFar, res); |
| sg_free(res); |
| free(res); |
| } |
| |
| //wrap the whole thing if required |
| if((cmd == SDP_PDU_Service_Search_Attribute_Request) && !btSdpPutIntoGroup(resultSoFar)){ |
| dbgPrintf("SDP: Failed to put results into a group\n"); |
| sg_free(resultSoFar); |
| free(resultSoFar); |
| return btSdpError(trans, SDP_ERR_Insufficient_Resources); |
| } |
| |
| //flatten to a buffer |
| uint8_t* buf = malloc(sg_length(resultSoFar)); |
| if(!buf){ |
| |
| dbgPrintf("SDP: Failed to allocate flattened result array (%ub)\n", sg_length(resultSoFar)); |
| sg_free(resultSoFar); |
| free(resultSoFar); |
| return btSdpError(trans, SDP_ERR_Insufficient_Resources); |
| } |
| inst->resultSz = sg_length(resultSoFar); |
| inst->result = buf; |
| inst->contDescr = sdpContVal; |
| sg_copyto(resultSoFar, buf); |
| sg_free(resultSoFar); |
| free(resultSoFar); |
| } |
| else if(cmd == SDP_PDU_Service_Search_Request){ |
| |
| //allocate the array |
| inst->resultSz = sizeof(uint32_t[numFound]); |
| uint8_t* buf = malloc(inst->resultSz); |
| |
| if(!buf){ |
| |
| dbgPrintf("SDP: Failed to allocate flattened result array (%ub)\n", sizeof(uint32_t[numFound])); |
| return btSdpError(trans, SDP_ERR_Insufficient_Resources); |
| } |
| |
| //process each match |
| for(i = 0; i < numFound; i++){ |
| |
| buf[i * 4 + 0] = results[i]->handle >> 24; |
| buf[i * 4 + 1] = results[i]->handle >> 16; |
| buf[i * 4 + 2] = results[i]->handle >> 8; |
| buf[i * 4 + 3] = results[i]->handle; |
| } |
| |
| //put everything in the right place |
| inst->result = buf; |
| inst->contDescr = sdpContVal; |
| inst->numMatches = numFound; |
| } |
| } |
| if(++sdpContVal == 0) sdpContVal = 0x01234567; //update continuation state to the next value |
| |
| //produce the packet to send |
| uint8_t bufPrepend[9], bufPostpend[5] = {0, }, preSz = 5, postSz = 1; |
| uint32_t sendSz = 0; |
| result = sg_alloc(); |
| if(!result) return btSdpError(trans, SDP_ERR_Insufficient_Resources); |
| |
| if(cmd == SDP_PDU_Service_Attribute_Request || cmd == SDP_PDU_Service_Search_Attribute_Request){ |
| |
| if(maxReplSz > 256) maxReplSz = 256; //no harm in fragmenting - keep the packets small |
| |
| sendSz = inst->resultSz; |
| if(sendSz > maxReplSz) sendSz = maxReplSz; |
| |
| bufPrepend[preSz++] = sendSz >> 8; |
| bufPrepend[preSz++] = sendSz & 0xFF; |
| } |
| else if(cmd == SDP_PDU_Service_Search_Request){ |
| |
| if(maxReplSz > 64) maxReplSz = 64; //no harm in fragmenting - keep the packets small |
| |
| sendSz = inst->resultSz; |
| if(sendSz > maxReplSz * sizeof(uint32_t)) sendSz = maxReplSz * sizeof(uint32_t); |
| |
| bufPrepend[preSz++] = inst->numMatches >> 8; |
| bufPrepend[preSz++] = inst->numMatches & 0xFF; |
| bufPrepend[preSz++] = (sendSz / sizeof(uint32_t)) >> 8; |
| bufPrepend[preSz++] = (sendSz / sizeof(uint32_t)) & 0xFF; |
| } |
| |
| if(!sg_add_back(result, inst->result, sendSz, SG_FLAG_MAKE_A_COPY)){ |
| |
| dbgPrintf("SDP: Failed to attach reply. Droping"); |
| free(inst->result); |
| inst->result = NULL; |
| sg_free(result); |
| free(result); |
| return btSdpError(trans, SDP_ERR_Insufficient_Resources); |
| } |
| else{ |
| |
| inst->resultSz -= sendSz; |
| if(inst->resultSz){ |
| |
| memcpy(inst->result, inst->result + sendSz, inst->resultSz); |
| inst->result = realloc(inst->result, inst->resultSz); |
| } |
| else{ |
| free(inst->result); |
| inst->result = NULL; |
| } |
| } |
| |
| if(inst->result){ //have more |
| |
| bufPostpend[0] = 4; |
| for(i = 0; i < 4; i++) bufPostpend[i + 1] = inst->contDescr >> ((3 - i) << 3); |
| postSz = 5; |
| } |
| |
| bufPrepend[0] = cmd + 1; //response to this request |
| bufPrepend[1] = trans[0]; |
| bufPrepend[2] = trans[1]; |
| bufPrepend[3] = (sendSz + preSz + postSz - 5) >> 8; |
| bufPrepend[4] = (sendSz + preSz + postSz - 5) & 0xFF; |
| |
| if(sg_add_front(result, bufPrepend, preSz, SG_FLAG_MAKE_A_COPY) && sg_add_back(result, bufPostpend, postSz, SG_FLAG_MAKE_A_COPY)){ |
| |
| return result; |
| } |
| sg_free(result); |
| free(result); |
| return btSdpError(trans, SDP_ERR_Insufficient_Resources); |
| } |
| |
| static void* sdpServiceAlloc(uint16_t conn, uint16_t chan, uint16_t remChan){ |
| |
| SdpInstance* inst = malloc(sizeof(SdpInstance)); |
| if(inst){ |
| |
| inst->result = NULL; |
| inst->aclConn = conn; |
| inst->remChan = remChan; |
| } |
| return inst; |
| } |
| |
| static void sdpServiceFree(void* service){ |
| |
| SdpInstance* inst = (SdpInstance*)service; |
| |
| if(inst->result) free(inst->result); |
| free(inst); |
| } |
| |
| static void sdpServiceDataRx(void* service, const uint8_t* data, uint16_t size){ |
| |
| SdpInstance* inst = (SdpInstance*)service; |
| uint16_t conn = inst->aclConn; |
| uint16_t remChan = inst->remChan; |
| |
| sg_buf* reply = btSdpProcessRequest(inst, data, size); |
| if(reply){ |
| |
| /*// -- ugly debugging code -- |
| unsigned i; |
| uint8_t buf[256]; |
| sg_copyto(reply, buf); |
| |
| dbgPrintf("SDP req got (0x%x): ", size); |
| for(i = 0; i < size; i++) dbgPrintf(" %02X", data[i]); |
| dbgPrintf("\n"); |
| |
| dbgPrintf("SDP reply sent (0x%x): ", sg_length(reply)); |
| for(i = 0; i < sg_length(reply); i++) dbgPrintf(" %02X", buf[i]); |
| dbgPrintf("\n"); |
| */ |
| l2capServiceTx(conn, remChan, reply); |
| } |
| } |
| |
| void btSdpRegisterL2capService(){ |
| |
| const L2capService sdp = {L2CAP_FLAG_SUPPORT_CONNECTIONS, sdpServiceAlloc, sdpServiceFree, sdpServiceDataRx}; |
| if(!l2capServiceRegister(L2CAP_PSM_SDP, &sdp)) dbgPrintf("SDP L2CAP registration failed\n"); |
| |
| btSdpServiceDescriptorAdd(sdpDescrSdp, sizeof(sdpDescrSdp)); |
| } |
| |
| void btSdpServiceDescriptorAdd(const uint8_t* descriptor, uint16_t descrLen){ |
| |
| SdpService *t, *s = malloc(sizeof(SdpService)); |
| if(s){ |
| |
| s->handle = sdpNextHandle; |
| |
| if(sdpNextHandle) sdpNextHandle++; |
| else sdpNextHandle = SDP_FIRST_USER_HANDLE; //first add is special - it adds the SDP service itself |
| |
| s->descriptor = descriptor; |
| s->descrLen = descrLen; |
| s->next = NULL; |
| |
| t = knownServices; //add at end |
| while(t && t->next) t = t->next; |
| if(t) t->next = s; |
| else knownServices = s; |
| } |
| } |
| |
| void btSdpServiceDescriptorDel(const uint8_t* descriptor){ |
| |
| SdpService *s = knownServices, *p = NULL; |
| |
| while(s && s->descriptor != descriptor){ |
| |
| p = s; |
| s = s->next; |
| } |
| if(p) p->next = s->next; |
| else knownServices = s->next; |
| |
| free(s); |
| } |
| |
| |