added k60d100m project
[adk-bluetooth-test.git] / adk-stack / btL2CAP.c
blob7656d5ac474c29aeec75341026f65c20f6d63f3f
1 /*
2 * Copyright (C) 2012 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 #define ADK_INTERNAL
17 #include "fwk.h"
18 #include "sgBuf.h"
19 #include "btL2CAP.h"
20 #include "BT.h"
21 #include "dbg.h"
22 #include <string.h>
24 #define UGLY_SCARY_DEBUGGING_CODE 0 // 0
26 #define L2CAP_CMD_REJECT 0x01
27 #define L2CAP_CMD_CONN_REQ 0x02
28 #define L2CAP_CMD_CONN_RESP 0x03
29 #define L2CAP_CMD_CONFIG_REQ 0x04
30 #define L2CAP_CMD_CONFIG_RESP 0x05
31 #define L2CAP_CMD_DISC_REQ 0x06
32 #define L2CAP_CMD_DISC_RESP 0x07
33 #define L2CAP_CMD_ECHO_REQ 0x08
34 #define L2CAP_CMD_ECHO_RESP 0x09
35 #define L2CAP_CMD_INFO_REQ 0x0A
36 #define L2CAP_CMD_INFO_RESP 0x0B
38 #define L2CAP_CONN_SUCCESS 0x0000
39 #define L2CAP_CONN_FAIL_NO_SUCH_PSM 0x0002
40 #define L2CAP_CONN_FAIL_RESOURCES 0x0004
42 #define L2CAP_REJECT_REASON_WTF 0x0000 //command not understood
43 #define L2CAP_REJECT_REASON_INVALID_CID 0x0002
45 #define L2CAP_FIRST_USABLE_CHANNEL 0x0040
47 #define L2CAP_OPTION_MTU 1
48 #define L2CAP_DEFAULT_MTU 672 //as per spec
50 typedef struct Service{
52 struct Service* next;
53 uint16_t PSM;
54 L2capService descr;
56 }Service;
58 typedef struct Connection{
60 struct Connection* next;
61 Service* service;
62 void* serviceInstance;
63 uint16_t conn, chan, remChan;
65 }Connection;
67 Service* services = NULL;
68 Connection* connections = NULL;
70 static uint8_t gL2capID = 1;
72 struct{
74 uint16_t conn;
75 uint16_t chan;
76 uint8_t* buf;
77 uint16_t lenGot;
78 uint16_t lenNeed;
79 }gIncomingPieces[L2CAP_MAX_PIECED_MESSAGES] = {{0,},};
83 static inline uint16_t getLE16(const uint8_t* ptr){
85 return (((uint16_t)ptr[1]) << 8) + ptr[0];
88 static inline void putLE16(uint8_t* ptr, uint16_t val){
90 ptr[0] = val & 0xFF;
91 ptr[1] = val >> 8;
94 void l2capServiceTx(uint16_t conn, uint16_t remChan, sg_buf* data){
96 uint8_t hdr[4];
97 uint16_t len = sg_length(data);
99 putLE16(hdr + 0, len);
100 putLE16(hdr + 2, remChan);
102 if(sg_add_front(data, hdr, 4, SG_FLAG_MAKE_A_COPY)){
104 #if UGLY_SCARY_DEBUGGING_CODE
105 uint32_t i;
106 uint8_t buf[256];
107 sg_copyto(data, buf);
108 dbgPrintf("L2CAP TX: ");
109 for(i = 0; i < sg_length(data); i++) dbgPrintf(" %02X", buf[i]);
110 dbgPrintf("\n");
111 #endif
113 btAclDataTx(conn, 1, BT_BCAST_NONE, data);
115 else{
117 sg_free(data);
118 free(data);
122 static void l2capSendControlRawBuf(uint16_t conn, const uint8_t* data, uint16_t len){
124 sg_buf* buf;
126 buf = sg_alloc();
127 if(buf){
129 if(sg_add_back(buf, data, len, SG_FLAG_MAKE_A_COPY)){
131 l2capServiceTx(conn, 1, buf);
133 else{
135 sg_free(buf);
136 free(buf);
141 static void l2capConnectionCloseEx(Connection* c, Connection* prev, char sendDiscPacket){
143 uint8_t discCmd[6];
145 if(c->service->descr.serviceInstanceFree) c->service->descr.serviceInstanceFree(c->serviceInstance);
147 if(sendDiscPacket){
149 discCmd[0] = L2CAP_CMD_DISC_REQ;
150 discCmd[1] = gL2capID++;
151 putLE16(discCmd + 2, c->remChan);
152 putLE16(discCmd + 4, c->chan);
154 l2capSendControlRawBuf(c->conn, discCmd, 6);
157 if(prev) prev->next = c->next;
158 else connections = c->next;
159 //printf("l2capConnectionCloseEx %d,%d\n",c->conn,c->chan);
160 free(c);
163 static Connection* l2capFindConnection(uint16_t conn, uint16_t chan, Connection** prev){
165 Connection *c = connections, *p = NULL;
167 while(c && (c->conn != conn || c->chan != chan)){
169 p = c;
170 c = c->next;
173 if(prev) *prev = p;
174 return c;
177 static uint16_t l2capFindFreeLocalChannel(uint16_t conn){
179 Connection *c = connections;
180 uint32_t chan = L2CAP_FIRST_USABLE_CHANNEL;
182 //first try something fast
183 while(c){
184 if(c->chan >= chan && c->conn == conn) chan = c->chan + 1;
185 c = c->next;
187 if(chan <= 0xFFFF) return chan;
189 //else do something slower
190 for(chan = L2CAP_FIRST_USABLE_CHANNEL; chan <= 0xFFFF; chan++){
192 c = connections;
193 while(c && (c->chan != chan || c->conn != conn)) c = c->next;
194 if(!c) break;
197 if(chan <= 0xFFFF) return chan;
199 //now we failed
200 return 0;
203 void l2capServiceCloseConn(uint16_t conn, uint16_t chan){
205 Connection *c, *p;
207 if((c = l2capFindConnection(conn, chan, &p))) l2capConnectionCloseEx(c, p, 1);
210 int liutest_l2capSendControlRawBuf = 0;
211 static void l2capHandleControlChannel(uint16_t conn, const uint8_t* data, uint16_t size){
213 char rejectCommand = 0;
214 uint16_t rejectReason = L2CAP_REJECT_REASON_WTF;
215 Service* s = services;
216 Connection* c;
217 uint8_t cmd, id;
218 uint8_t buf[16];
219 uint16_t chan, remChan, len;
221 if(!gL2capID) gL2capID++;
223 if(size < 2){
225 rejectCommand = 1;
226 dbgPrintf("L2CAP: control packet too small\n");
228 else{
230 cmd = *data++;
231 id = *data++;
232 len = getLE16(data);
233 data += 2;
234 size -= 4;
236 if(len != size){
238 dbgPrintf("L2CAP (control packet internal sizes mismatch (%u != %u)\n", len, size);
239 rejectCommand = 1;
241 else switch(cmd){
243 case L2CAP_CMD_CONN_REQ:{
245 uint16_t PSM;
247 //get some request data
248 if(size != 4){
250 dbgPrintf("L2CAP: ConnectionRequest packet size is wrong(%u)\n", size);
251 rejectCommand = 1;
252 break;
255 PSM = getLE16(data + 0);
256 remChan = getLE16(data + 2);
258 //init the reply
259 buf[0] = L2CAP_CMD_CONN_RESP;
260 buf[1] = id;
261 putLE16(buf + 2, 8); //length
262 putLE16(buf + 4, 0); //DCID
263 putLE16(buf + 6, remChan); //SCID
264 putLE16(buf + 10, 0); //no further information
266 //find the service
267 while(s && s->PSM != PSM) s = s->next;
268 if(!s || !(s->descr.flags & L2CAP_FLAG_SUPPORT_CONNECTIONS)){
270 dbgPrintf("L2CAP: rejecting conection to unknown PSM 0x%04X\n", PSM);
271 putLE16(buf + 8, L2CAP_CONN_FAIL_NO_SUCH_PSM);
273 else{
275 void* instance = NULL;
277 chan = 0;
278 c = malloc(sizeof(Connection));
279 if(c) chan = l2capFindFreeLocalChannel(conn);
280 if(chan) instance = s->descr.serviceInstanceAllocate(conn, chan, remChan);
282 if(instance){
284 putLE16(buf + 4, chan);
285 putLE16(buf + 8, L2CAP_CONN_SUCCESS);
287 c->service = s;
288 c->serviceInstance = instance;
289 c->conn = conn;
290 c->chan = chan;
291 c->remChan = remChan;
292 c->next = connections;
293 connections = c;
295 else{
297 putLE16(buf + 8, L2CAP_CONN_FAIL_RESOURCES);
299 if(c) free(c);
302 size = 12;
303 break;
306 case L2CAP_CMD_CONFIG_REQ:{
308 uint16_t flags;
310 //get some request data
311 if(size < 4){
312 dbgPrintf("L2CAP: ConfigurationRequest packet size is wrong(%u)\n", size);
313 rejectCommand = 1;
314 break;
317 chan = getLE16(data + 0);
318 flags = getLE16(data + 2);
319 if(flags & 1){ //flags continue - we do not support that
321 size = 0;
322 break;
324 size -= 4;
325 data += 4;
327 //locate the connection at hand
328 c = l2capFindConnection(conn, chan, NULL);
329 if(!c){
330 dbgPrintf("L2CAP: ConfigurationRequest for an unknown channel %u.%u\n", conn, chan);
331 rejectCommand = 1;
332 break;
334 chan = c->remChan;
336 //reply with just our rx-MTU
337 buf[0] = L2CAP_CMD_CONFIG_RESP;
338 buf[1] = id;
339 putLE16(buf + 2, 10); //length
340 putLE16(buf + 4, c->remChan); //SCID
341 putLE16(buf + 6, 0); //flags
342 putLE16(buf + 8, 0); //success
343 buf[10] = L2CAP_OPTION_MTU; //mtu_property.type
344 buf[11] = 2; //mtu_property.len
345 putLE16(buf + 12, BT_RX_BUF_SZ - 8); //mtu value
346 l2capSendControlRawBuf(conn, buf, 14); //send it
348 //we need to send such a packet there too
349 buf[0] = L2CAP_CMD_CONFIG_REQ; //configuration request
350 buf[1] = gL2capID++;
351 putLE16(buf + 2, 4); //length
352 putLE16(buf + 4, c->remChan); //SCID
353 putLE16(buf + 6, 0); //flags
354 size = 8;
355 break;
358 case L2CAP_CMD_CONFIG_RESP:{
360 //we do nothing here - perhaps we should?
361 size = 0;
362 break;
365 case L2CAP_CMD_DISC_REQ:{
367 Connection* p;
369 //get some request data
370 if(size != 4){
372 dbgPrintf("L2CAP: DisconnectionRequest packet size is wrong(%u)\n", size);
373 rejectCommand = 1;
374 break;
376 chan = getLE16(data + 0);
377 remChan = getLE16(data + 2);
378 c = l2capFindConnection(conn, chan, &p);
380 //handle some likely error cases
381 if(!c){
382 dbgPrintf("L2CAP: DisconnectionRequest for an unknown channel %u.%u\n", conn, chan);
383 rejectReason = L2CAP_REJECT_REASON_INVALID_CID;
384 rejectCommand = 1; //reject as per spec
385 break;
387 if(c->remChan != remChan){
388 dbgPrintf("L2CAP: DisconnectionRequest for an unmatched channel on %u.%u (%u != %u)\n", conn, chan, c->remChan, remChan);
389 size = 0; //drop as per spec
390 break;
393 //perform needed cleanup
394 l2capConnectionCloseEx(c, p, 0);
396 //send the reply
397 buf[0] = L2CAP_CMD_DISC_RESP; //disconnection response
398 buf[1] = id; //copied as required
399 putLE16(buf + 2, 4); //length
400 putLE16(buf + 4, chan); //DCID
401 putLE16(buf + 6, remChan); //SCID
402 size = 8;
403 break;
406 case L2CAP_CMD_DISC_RESP:{
408 //nothing to do - we did cleanup when we requested the connection closure...
409 size = 0;
410 break;
413 case L2CAP_CMD_ECHO_REQ:{
415 buf[0] = L2CAP_CMD_ECHO_RESP; //ping response
416 buf[1] = id; //copied as required
417 putLE16(buf + 2, 0); //0-length replies are ok
418 size = 4;
419 break;
422 case L2CAP_CMD_INFO_REQ:{
424 uint16_t info;
426 //get some request data
427 if(size != 2){
429 dbgPrintf("L2CAP: InformationRequest packet size is wrong(%u)\n", size);
430 rejectCommand = 1;
431 break;
433 info = getLE16(data + 0);
435 buf[0] = L2CAP_CMD_INFO_RESP; //information response
436 buf[1] = id; //copied as required
437 putLE16(buf + 4, info); //info type
438 if(info == 1){ //connectionless mtu
440 putLE16(buf + 6, 0); //success
441 putLE16(buf + 8, BT_RX_BUF_SZ - 8);
442 putLE16(buf + 2, 6); //length
443 size = 10;
445 else if(info == 2){ //extended features
447 putLE16(buf + 6, 0); //success
448 putLE16(buf + 8, 0);
449 putLE16(buf + 10, 0);
450 putLE16(buf + 2, 8); //length
451 size = 12;
453 else{ //whatever this request is, we do not support it
455 putLE16(buf + 6, 1); //info type not supported
456 putLE16(buf + 2, 4); //length
457 size = 8;
459 break;
462 default:{
464 dbgPrintf("L2CAP: unexpected command 0x%02X recieved\n", cmd);
465 size = 0;
470 if(rejectCommand){
472 buf[0] = L2CAP_CMD_REJECT; //disconnection response
473 buf[1] = id; //copied as required
474 putLE16(buf + 2, 4); //length
475 putLE16(buf + 4, rejectReason); //rejection reason
476 size = 6;
479 liutest_l2capSendControlRawBuf = 1;
480 if(size) l2capSendControlRawBuf(conn, buf, size);
484 char l2capServiceRegister(uint16_t PSM, const L2capService* svcData){
486 Service* s;
488 //first, see if this PSM is taken
489 s = services;
490 while(s){
492 if(s->PSM == PSM) return 0;
493 s = s->next;
496 //next, try to allocate the memory
497 s = malloc(sizeof(Service));
498 if(!s) return 0;
500 //then, init it and link it in
501 s->PSM = PSM;
502 s->descr = *svcData;
503 s->next = services;
504 services = s;
506 return 1;
509 char l2capServiceUnregister(uint16_t PSM,char sendDiscPacket){
511 Service *s = services, *ps = NULL;
512 Connection *c, *pc = NULL;
514 //first, find it in the list
515 while(s && s->PSM != PSM){
517 ps = s;
518 s = s->next;
520 if(!s) return 0;
522 //then, see if any connections are using it, and if so, kill them
524 c = connections;
525 while(c){
527 if(c->service == s){
529 l2capConnectionCloseEx(c, pc, sendDiscPacket);
530 break;
533 pc = c;
534 c = c->next;
536 }while(c);
538 //now delete the service record
539 if(ps) ps->next = s->next;
540 else services = s->next;
541 free(s);
542 return 1;
545 void l2capAclLinkDataRx(uint16_t conn, char first, const uint8_t* data, uint16_t size){
547 uint16_t chan, len;
548 unsigned i;
549 char freeIt = 0;
551 #if UGLY_SCARY_DEBUGGING_CODE
552 dbgPrintf("L2CAP data:");
553 for(chan = 0; chan < size; chan++) dbgPrintf(" %02X", data[chan]);
554 dbgPrintf("\n\n");
555 #endif
557 if(first){
559 len = getLE16(data + 0);
560 chan = getLE16(data + 2);
561 data += 4;
562 size -= 4;
564 if(size >= len){
565 if(size > len) dbgPrintf("L2CAP: ACL provided likely invalid L2CAP packet (ACL.len=%u L2CAP.len=%u)\n", size, len);
567 else{
568 for(i = 0; i < L2CAP_MAX_PIECED_MESSAGES; i++){
570 if(gIncomingPieces[i].conn == conn){
572 dbgPrintf("L2CAP: conn %d: 'first' frame while another incomplete already buffered. Dropping the old one.\n", conn);
573 free(gIncomingPieces[i].buf);
574 gIncomingPieces[i].conn = 0;
575 break;
578 if(i == L2CAP_MAX_PIECED_MESSAGES) for(i = 0; i < L2CAP_MAX_PIECED_MESSAGES && gIncomingPieces[i].conn; i++);
579 if(i == L2CAP_MAX_PIECED_MESSAGES){
581 dbgPrintf("L2CAP: not enough buffer slots to buffer incomplete frame. Dropping\n");
583 else{
585 uint8_t* ptr = malloc(size);
586 if(!ptr){
588 dbgPrintf("L2CAP: cannot allocate partial frame buffer. Dropping\n");
589 return;
591 memcpy(ptr, data, size);
592 gIncomingPieces[i].buf = ptr;
593 gIncomingPieces[i].lenGot = size;
594 gIncomingPieces[i].lenNeed = len - size;
595 gIncomingPieces[i].chan = chan;
596 gIncomingPieces[i].conn = conn;
598 return;
601 else{
603 uint8_t* ptr;
605 for(i = 0; i < L2CAP_MAX_PIECED_MESSAGES && gIncomingPieces[i].conn != conn; i++);
606 if(i == L2CAP_MAX_PIECED_MESSAGES){
607 dbgPrintf("L2CAP: unexpected 'non-first' frame for conn %u. Dropping.\n", conn);
608 return;
611 if(size > gIncomingPieces[i].lenNeed){
613 dbgPrintf("L2CAP: 'non-first' frame too large. Need %u bytes, got %u. Dropping.\n", gIncomingPieces[i].lenNeed, size);
614 return;
617 ptr = realloc(gIncomingPieces[i].buf, gIncomingPieces[i].lenGot + size);
618 //printf()
619 if(!ptr){
621 dbgPrintf("L2CAP: failed to resize buffer for partial frame receive. Droping\n");
622 free(gIncomingPieces[i].buf);
623 gIncomingPieces[i].conn = 0;
624 return;
627 memcpy(ptr + gIncomingPieces[i].lenGot, data, size);
628 gIncomingPieces[i].buf = ptr;
629 gIncomingPieces[i].lenGot += size;
630 gIncomingPieces[i].lenNeed -= size;
632 if(gIncomingPieces[i].lenNeed) return; //data still not complete
634 gIncomingPieces[i].conn = 0;
635 chan = gIncomingPieces[i].chan;
636 len = gIncomingPieces[i].lenGot;
637 data = ptr;
638 freeIt = 1;
641 if(chan == 0) dbgPrintf("L2CAP: data on connection %u.0\n", conn);
642 else if(chan == 1) l2capHandleControlChannel(conn, data, len);
643 else if(chan == 2){ //connectionless
644 ASSERT(freeIt == 0);
645 uint16_t PSM = getLE16(data + 0);
646 data += 2;
647 len -= 2;
649 Service* s = services;
651 while(s && s->PSM != PSM) s = s->next;
652 if(!s || !(s->descr.flags & L2CAP_FLAG_SUPPORT_CONNECTIONLESS)) dbgPrintf("L2CAP: connectionless data on %u.2 for unknown PSM 0x%04X\n", conn, PSM);
653 else s->descr.serviceRx(NULL, data, len);
655 else{ //conection-oriented
657 Connection* c = l2capFindConnection(conn, chan, NULL);
658 if(!c) dbgPrintf("L2CAP: data for nonexistent connection %u.%u\n", conn, chan);
659 else c->service->descr.serviceRx(c->serviceInstance, data, len);
662 if(freeIt) free((void *)data);
665 void l2capAclLinkUp(uint16_t conn){
667 //nothing here [yet?]
670 void l2capAclLinkDown(uint16_t conn){
672 Connection *p, *c;
673 unsigned i;
677 p = NULL;
678 c = connections;
680 while(c && c->conn != conn){
682 p = c;
683 c = c->next;
686 if(c) l2capConnectionCloseEx(c, p, 0);
688 }while(c);
690 for(i = 0; i < L2CAP_MAX_PIECED_MESSAGES; i++) if(gIncomingPieces[i].conn == conn){
692 free(gIncomingPieces[i].buf);
693 gIncomingPieces[i].conn = 0;
698 void l2capAclLinkDownAll()
701 Connection *p, *c;
702 unsigned i;
705 p = NULL;
706 c = connections;
708 if(c) l2capConnectionCloseEx(c, p, 0);
710 }while(c);
712 for(i = 0; i < L2CAP_MAX_PIECED_MESSAGES; i++) {
713 if(gIncomingPieces[i].conn != 0) {
714 free(gIncomingPieces[i].buf);
715 gIncomingPieces[i].conn = 0;