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.
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
{
58 typedef struct Connection
{
60 struct Connection
* next
;
62 void* serviceInstance
;
63 uint16_t conn
, chan
, remChan
;
67 Service
* services
= NULL
;
68 Connection
* connections
= NULL
;
70 static uint8_t gL2capID
= 1;
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
){
94 void l2capServiceTx(uint16_t conn
, uint16_t remChan
, sg_buf
* data
){
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
107 sg_copyto(data
, buf
);
108 dbgPrintf("L2CAP TX: ");
109 for(i
= 0; i
< sg_length(data
); i
++) dbgPrintf(" %02X", buf
[i
]);
113 btAclDataTx(conn
, 1, BT_BCAST_NONE
, data
);
122 static void l2capSendControlRawBuf(uint16_t conn
, const uint8_t* data
, uint16_t len
){
129 if(sg_add_back(buf
, data
, len
, SG_FLAG_MAKE_A_COPY
)){
131 l2capServiceTx(conn
, 1, buf
);
141 static void l2capConnectionCloseEx(Connection
* c
, Connection
* prev
, char sendDiscPacket
){
145 if(c
->service
->descr
.serviceInstanceFree
) c
->service
->descr
.serviceInstanceFree(c
->serviceInstance
);
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);
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
)){
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
184 if(c
->chan
>= chan
&& c
->conn
== conn
) chan
= c
->chan
+ 1;
187 if(chan
<= 0xFFFF) return chan
;
189 //else do something slower
190 for(chan
= L2CAP_FIRST_USABLE_CHANNEL
; chan
<= 0xFFFF; chan
++){
193 while(c
&& (c
->chan
!= chan
|| c
->conn
!= conn
)) c
= c
->next
;
197 if(chan
<= 0xFFFF) return chan
;
203 void l2capServiceCloseConn(uint16_t conn
, uint16_t chan
){
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
;
219 uint16_t chan
, remChan
, len
;
221 if(!gL2capID
) gL2capID
++;
226 dbgPrintf("L2CAP: control packet too small\n");
238 dbgPrintf("L2CAP (control packet internal sizes mismatch (%u != %u)\n", len
, size
);
243 case L2CAP_CMD_CONN_REQ
:{
247 //get some request data
250 dbgPrintf("L2CAP: ConnectionRequest packet size is wrong(%u)\n", size
);
255 PSM
= getLE16(data
+ 0);
256 remChan
= getLE16(data
+ 2);
259 buf
[0] = L2CAP_CMD_CONN_RESP
;
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
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
);
275 void* instance
= NULL
;
278 c
= malloc(sizeof(Connection
));
279 if(c
) chan
= l2capFindFreeLocalChannel(conn
);
280 if(chan
) instance
= s
->descr
.serviceInstanceAllocate(conn
, chan
, remChan
);
284 putLE16(buf
+ 4, chan
);
285 putLE16(buf
+ 8, L2CAP_CONN_SUCCESS
);
288 c
->serviceInstance
= instance
;
291 c
->remChan
= remChan
;
292 c
->next
= connections
;
297 putLE16(buf
+ 8, L2CAP_CONN_FAIL_RESOURCES
);
306 case L2CAP_CMD_CONFIG_REQ
:{
310 //get some request data
312 dbgPrintf("L2CAP: ConfigurationRequest packet size is wrong(%u)\n", size
);
317 chan
= getLE16(data
+ 0);
318 flags
= getLE16(data
+ 2);
319 if(flags
& 1){ //flags continue - we do not support that
327 //locate the connection at hand
328 c
= l2capFindConnection(conn
, chan
, NULL
);
330 dbgPrintf("L2CAP: ConfigurationRequest for an unknown channel %u.%u\n", conn
, chan
);
336 //reply with just our rx-MTU
337 buf
[0] = L2CAP_CMD_CONFIG_RESP
;
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
351 putLE16(buf
+ 2, 4); //length
352 putLE16(buf
+ 4, c
->remChan
); //SCID
353 putLE16(buf
+ 6, 0); //flags
358 case L2CAP_CMD_CONFIG_RESP
:{
360 //we do nothing here - perhaps we should?
365 case L2CAP_CMD_DISC_REQ
:{
369 //get some request data
372 dbgPrintf("L2CAP: DisconnectionRequest packet size is wrong(%u)\n", size
);
376 chan
= getLE16(data
+ 0);
377 remChan
= getLE16(data
+ 2);
378 c
= l2capFindConnection(conn
, chan
, &p
);
380 //handle some likely error cases
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
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
393 //perform needed cleanup
394 l2capConnectionCloseEx(c
, p
, 0);
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
406 case L2CAP_CMD_DISC_RESP
:{
408 //nothing to do - we did cleanup when we requested the connection closure...
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
422 case L2CAP_CMD_INFO_REQ
:{
426 //get some request data
429 dbgPrintf("L2CAP: InformationRequest packet size is wrong(%u)\n", size
);
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
445 else if(info
== 2){ //extended features
447 putLE16(buf
+ 6, 0); //success
449 putLE16(buf
+ 10, 0);
450 putLE16(buf
+ 2, 8); //length
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
464 dbgPrintf("L2CAP: unexpected command 0x%02X recieved\n", cmd
);
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
479 liutest_l2capSendControlRawBuf
= 1;
480 if(size
) l2capSendControlRawBuf(conn
, buf
, size
);
484 char l2capServiceRegister(uint16_t PSM
, const L2capService
* svcData
){
488 //first, see if this PSM is taken
492 if(s
->PSM
== PSM
) return 0;
496 //next, try to allocate the memory
497 s
= malloc(sizeof(Service
));
500 //then, init it and link it in
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
){
522 //then, see if any connections are using it, and if so, kill them
529 l2capConnectionCloseEx(c
, pc
, sendDiscPacket
);
538 //now delete the service record
539 if(ps
) ps
->next
= s
->next
;
540 else services
= s
->next
;
545 void l2capAclLinkDataRx(uint16_t conn
, char first
, const uint8_t* data
, uint16_t size
){
551 #if UGLY_SCARY_DEBUGGING_CODE
552 dbgPrintf("L2CAP data:");
553 for(chan
= 0; chan
< size
; chan
++) dbgPrintf(" %02X", data
[chan
]);
559 len
= getLE16(data
+ 0);
560 chan
= getLE16(data
+ 2);
565 if(size
> len
) dbgPrintf("L2CAP: ACL provided likely invalid L2CAP packet (ACL.len=%u L2CAP.len=%u)\n", size
, len
);
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;
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");
585 uint8_t* ptr
= malloc(size
);
588 dbgPrintf("L2CAP: cannot allocate partial frame buffer. Dropping\n");
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
;
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
);
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
);
617 ptr
= realloc(gIncomingPieces
[i
].buf
, gIncomingPieces
[i
].lenGot
+ size
);
621 dbgPrintf("L2CAP: failed to resize buffer for partial frame receive. Droping\n");
622 free(gIncomingPieces
[i
].buf
);
623 gIncomingPieces
[i
].conn
= 0;
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
;
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
645 uint16_t PSM
= getLE16(data
+ 0);
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
){
680 while(c
&& c
->conn
!= conn
){
686 if(c
) l2capConnectionCloseEx(c
, p
, 0);
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()
708 if(c
) l2capConnectionCloseEx(c
, p
, 0);
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;