blob: de152db57521583350119f3303652f68f6d97ecd [file] [log] [blame]
/*
* 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 "btRFCOMM.h"
#include "BT.h"
#include "dbg.h"
#include "btL2CAP.h"
#include "coop.h"
//NOTE: to be good citizens, we really should verify incoming CRC. We have better things to do...sorry...
//Plus, they do not provide safety for the data since data is not checksummed.
//We are, however, forced to produce good CRCs.
#define UGLY_SCARY_DEBUGGING_CODE 0
#define BT_RFCOMM_DLCI_MSK_EA 0x01
#define BT_RFCOMM_DLCI_MSK_CR 0x02
#define BT_RFCOMM_DLCI_MSK_D 0x04
#define BT_RFCOMM_DLCI_SHIFT 2
#define BT_SZ_MASK_OVER 0x01
#define BT_RFCOMM_CMD_DM 0x0F
#define BT_RFCOMM_CMD_SABM 0x3F
#define BT_RFCOMM_CMD_DISC 0x53
#define BT_RFCOMM_CMD_UA 0x73
#define BT_RFCOMM_CMD_UIH 0xEF
#define BT_RFCOMM_CMD_UIH_CRED 0xFF
//_C = command _R = resp
#define BT_RFCOMM_NSC_R 0x11
#define BT_RFCOMM_TEST_R 0x21
#define BT_RFCOMM_TEST_C 0x23
#define BT_RFCOMM_RLS_R 0x51
#define BT_RFCOMM_RLS_C 0x53
#define BT_RFCOMM_FCOFF_R 0x61
#define BT_RFCOMM_FCOFF_C 0x63
#define BT_RFCOMM_PN_R 0x81
#define BT_RFCOMM_PN_C 0x83
#define BT_RFCOMM_RPN_R 0x91
#define BT_RFCOMM_RPN_C 0x93
#define BT_RFCOMM_FCON_R 0xA1
#define BT_RFCOMM_FCON_C 0xA3
#define BT_RFCOMM_CLD_C 0xC3
#define BT_RFCOMM_MSC_R 0xE1
#define BT_RFCOMM_MSC_C 0xE3
#define BT_RFCOMM_REMOTE_CREDS 4
#define BT_RFCOMM_MAX_CRED_DEBT (BT_RFCOMM_REMOTE_CREDS - 1)
typedef struct{
uint8_t addr;
uint8_t control;
uint8_t length; //we do not support 2-byte length
uint8_t data[]; //and one byte CRC (FCS)
}RfcommPacket;
typedef struct{
BtRfcommPortOpenF oF;
BtRfcommPortCloseF cF;
BtRfcommPortRxF rF;
}RfcommPort;
static RfcommPort gPortHandlers[NUM_DLCIs] = {{0,}, };
static uint64_t reserved = 0xC000000000000003; //these are always reserved
typedef struct{
uint16_t aclConn;
uint16_t remChan;
struct{
sg_buf* queued;
uint16_t MTU;
uint8_t credits;
uint8_t creditsOwed;
char on;
}ports[NUM_DLCIs];
}RfcommInstanceState;
extern uint8_t btRfcommFcs(uint8_t *data, uint32_t len);
static void btRfcommSend(uint16_t conn, uint16_t remChan, const uint8_t* data, uint16_t sz){
int i;
#if UGLY_SCARY_DEBUGGING_CODE
dbgPrintf("Sending RFCOMM packet:");
for(i = 0; i < sz; i++) dbgPrintf(" %02X", data[i]);
dbgPrintf("\n\n");
#endif
sg_buf* buf = sg_alloc();
if(!buf) return;
if(sg_add_front(buf, data, sz, SG_FLAG_MAKE_A_COPY)){
l2capServiceTx(conn, remChan, buf);
return;
}
sg_free(buf);
free(buf);
}
static void btRfcommTxData(RfcommInstanceState* state, uint8_t dlci, sg_buf* buf){
uint8_t data[5] = {(dlci << BT_RFCOMM_DLCI_SHIFT) | BT_RFCOMM_DLCI_MSK_EA, BT_RFCOMM_CMD_UIH};
uint32_t dataLen = sg_length(buf);
uint8_t hdrLen = 3;
if(dataLen >= 128){ //need 2 length bytes
data[2] = dataLen << 1;
data[3] = dataLen >> 7;
hdrLen++;
} else data[2] = (dataLen << 1) | 1;
if(state->ports[dlci].creditsOwed){ //use the oportunity to send the credits
data[1] = BT_RFCOMM_CMD_UIH_CRED;
data[hdrLen++] = state->ports[dlci].creditsOwed;
state->ports[dlci].creditsOwed = 0;
}
if(sg_add_front(buf, data, hdrLen, SG_FLAG_MAKE_A_COPY)){
data[0] = btRfcommFcs(data, 2);
if(sg_add_back(buf, data, 1, SG_FLAG_MAKE_A_COPY)){
l2capServiceTx(state->aclConn, state->remChan, buf);
return;
}
}
sg_free(buf);
free(buf);
}
static void rfcommServiceDataRx(void* service, const uint8_t* req, uint16_t reqSz){
RfcommInstanceState* state = service;
const uint16_t conn = state->aclConn;
const uint16_t remChan = state->remChan;
const RfcommPacket* p = (RfcommPacket*)req; //note that we can do this only because packet has no non-8-bit-members
const uint8_t* data = p->data;
int i;
uint8_t DLCI = p->addr >> BT_RFCOMM_DLCI_SHIFT;
uint8_t ctrl = p->control;
uint16_t len;
uint8_t buf[32];
if(p->length & 1){
len = p->length >> 1;
}
else{
len = *data++;
len <<= 7;
len |= p->length >> 1;
}
if(ctrl == BT_RFCOMM_CMD_SABM || ctrl == BT_RFCOMM_CMD_DISC){
buf[ 0] = p->addr;
buf[ 1] = BT_RFCOMM_CMD_UA;
buf[ 2] = BT_SZ_MASK_OVER;
buf[ 3] = btRfcommFcs(buf, 3);
btRfcommSend(conn, remChan, buf, 4);
if(state->ports[DLCI].queued){
sg_free(state->ports[DLCI].queued);
free(state->ports[DLCI].queued);
}
state->ports[DLCI].creditsOwed = 0;
state->ports[DLCI].credits = 0;
state->ports[DLCI].queued = NULL;
if(ctrl == BT_RFCOMM_CMD_SABM){
if(gPortHandlers[DLCI].oF) gPortHandlers[DLCI].oF(state, DLCI);
else if(UGLY_SCARY_DEBUGGING_CODE) dbgPrintf("DLCI %2d opened\n", DLCI);
state->ports[DLCI].on = 0;
}
else{
if(gPortHandlers[DLCI].cF) gPortHandlers[DLCI].cF(state, DLCI);
else if(UGLY_SCARY_DEBUGGING_CODE) dbgPrintf("DLCI %2d closed\n", DLCI);
state->ports[DLCI].on = 1;
}
return;
}
if(ctrl == BT_RFCOMM_CMD_UIH && DLCI){ //we got a msg - send back credits if we already owe the limit, else buffer it
if(++state->ports[DLCI].creditsOwed == BT_RFCOMM_MAX_CRED_DEBT){
buf[ 0] = p->addr;
buf[ 1] = BT_RFCOMM_CMD_UIH_CRED;
buf[ 2] = BT_SZ_MASK_OVER;
buf[ 3] = state->ports[DLCI].creditsOwed + 1;
buf[ 4] = btRfcommFcs(buf, 2);
btRfcommSend(conn, remChan, buf, 5);
state->ports[DLCI].creditsOwed = 0;
}
}
if(ctrl == BT_RFCOMM_CMD_UIH_CRED){
state->ports[DLCI].credits += *data++;
ctrl = BT_RFCOMM_CMD_UIH;
if(state->ports[DLCI].queued && state->ports[DLCI].credits){
state->ports[DLCI].credits--;
btRfcommTxData(state, DLCI, state->ports[DLCI].queued);
state->ports[DLCI].queued = NULL;
}
}
if(DLCI == 0){ //control channel transaction
if(ctrl == BT_RFCOMM_CMD_UIH){ //data recieved
switch(data[0]){
case BT_RFCOMM_PN_C:{ //negotiations are for wusses!
uint8_t targetDLCI = data[2] & 0x3F;
state->ports[targetDLCI].credits = data[9];
state->ports[targetDLCI].MTU = (((uint16_t)data[7]) << 8) | data[6];
buf[ 0] = BT_RFCOMM_DLCI_MSK_EA;
buf[ 1] = BT_RFCOMM_CMD_UIH;
buf[ 2] = BT_SZ_MASK_OVER + (10 << 1);
buf[ 3] = BT_RFCOMM_PN_R;
buf[ 4] = BT_SZ_MASK_OVER + (8 << 1);
buf[ 5] = targetDLCI;
buf[ 6] = (data[3] == 0xF0) ? 0xE0 : 0x00; //as per spec
buf[ 7] = data[4]; //copy as per spec
buf[ 8] = 0; //0 as per spec
buf[ 9] = RFCOMM_MTU & 0xFF; //max_packet_size.lo
buf[ 10] = RFCOMM_MTU >> 8; //max_packet_size.hi
buf[ 11] = 0; //0 retransmissions
buf[ 12] = BT_RFCOMM_REMOTE_CREDS; //initial credits
buf[ 13] = btRfcommFcs(buf, 2);
btRfcommSend(conn, remChan, buf, 14);
break;
}
case BT_RFCOMM_RPN_C: //we need to echo back the negotiation
case BT_RFCOMM_MSC_C:{ //we need to echo back the control signals
if(len > 127) dbgPrintf("Control packet way too big\n");
else{
buf[ 0] = BT_RFCOMM_DLCI_MSK_EA;
buf[ 1] = BT_RFCOMM_CMD_UIH;
buf[ 2] = p->length;
buf[ 3] = (data[0] == BT_RFCOMM_MSC_C) ? BT_RFCOMM_MSC_R : BT_RFCOMM_RPN_C;
buf[ 4] = data[1];
for(i = 0; i < data[1] >> 1; i++) buf[ 5 + i] = data[2 + i];
buf[ 5 + i] = btRfcommFcs(buf, 2);
btRfcommSend(conn, remChan, buf, 6 + i);
if(data[0] == BT_RFCOMM_MSC_C){
//now we send our own signals (coincidentally they are identical ;-)
buf[ 3] = BT_RFCOMM_MSC_C;
btRfcommSend(conn, remChan, buf, 6 + i);
}
}
break;
}
case BT_RFCOMM_RPN_R:
case BT_RFCOMM_MSC_R:{
//nothing to do about this :)
break;
}
default:
dbgPrintf("Recieved unhandled RFCOMM packet:");
for(i = 0; i < reqSz; i++) dbgPrintf(" %02X", req[i]);
dbgPrintf("\n\n");
break;
}
}
}
else if(ctrl == BT_RFCOMM_CMD_UIH){
if(len){
if(gPortHandlers[DLCI].rF) gPortHandlers[DLCI].rF(state, DLCI, data, len);
else if(UGLY_SCARY_DEBUGGING_CODE){
dbgPrintf("RFCOMM DLCI %d data:", p->addr >> 2);
for(i = 0; i < len; i++) dbgPrintf(" %02X", data[i]);
dbgPrintf("\n\n\n");
}
}
}
else{
dbgPrintf("Recieved unhandled RFCOMM cmd (dlci=%d):", DLCI);
for(i = 0; i < reqSz; i++) dbgPrintf(" %02X", req[i]);
dbgPrintf("\n\n");
}
}
static void* rfcommServiceAlloc(uint16_t conn, uint16_t chan, uint16_t remChan){
RfcommInstanceState* state = malloc(sizeof(RfcommInstanceState));
int i;
if(!state) return NULL;
state->aclConn = conn;
state->remChan = remChan;
for(i = 0; i < NUM_DLCIs; i++){
state->ports[i].creditsOwed = 0;
state->ports[i].credits = 0;
state->ports[i].on = 0;
state->ports[i].queued = 0;
}
return state;
}
static void rfcommServiceFree(void* service){
free(service);
}
void btRfcommRegisterL2capService(){
const L2capService rfcom = {L2CAP_FLAG_SUPPORT_CONNECTIONS, rfcommServiceAlloc, rfcommServiceFree, rfcommServiceDataRx};
if(!l2capServiceRegister(L2CAP_PSM_RFCOMM, &rfcom)) dbgPrintf("SDP L2CAP registration failed\n");
}
void btRfcommRegisterPort(uint8_t dlci, BtRfcommPortOpenF oF, BtRfcommPortCloseF cF, BtRfcommPortRxF rF){
if(dlci >= NUM_DLCIs) return; //no such DLCI;
gPortHandlers[dlci].oF = oF;
gPortHandlers[dlci].cF = cF;
gPortHandlers[dlci].rF = rF;
}
void btRfcommPortTx(void* port, uint8_t dlci, const uint8_t* data, uint16_t size){
RfcommInstanceState* state = port;
if(dlci >= NUM_DLCIs || dlci == 0) return; //cannot send there, buddy...
sg_buf* buf = sg_alloc();
if(!buf) return;
if(!sg_add_front(buf, data, size, SG_FLAG_MAKE_A_COPY)){
sg_free(buf);
free(buf);
}
else{
if(state->ports[dlci].credits){ //send immediately
state->ports[dlci].credits--;
btRfcommTxData(state, dlci, buf);
}
else{ //enqueue it
if(state->ports[dlci].queued){
while(state->ports[dlci].queued && state->ports[dlci].on) coopYield();
if(!state->ports[dlci].on) return; //port closed - packet dropped
}
state->ports[dlci].queued = buf;
}
}
}
#define RFCOMM_DLCI_PREFERENCE_NONE 0x80 //use this param to the below
#define RFCOMM_DLCI_NEED_EVEN 0x81
#define RFCOMM_DLCI_NEED_ODD 0x82
uint8_t btRfcommReserveDlci(uint8_t preference){
uint8_t start = 0, end = 64, step = 2;
if(preference == RFCOMM_DLCI_PREFERENCE_NONE) step = 1;
else if(preference == RFCOMM_DLCI_NEED_EVEN);
else if(preference == RFCOMM_DLCI_NEED_ODD) start++;
else{
start = preference;
end = preference + 1;
step = 1;
}
while(start < end && (reserved & (1ULL << ((uint64_t)start)))) start += step;
if(start >= end) return 0; //we failed
reserved |= (1ULL << ((uint64_t)start));
return start;
}
void btRfcommReleaseDlci(uint8_t dlci){
reserved &=~ (1ULL << ((uint64_t)dlci));
}
static const uint8_t crctab[256] = {
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75,
0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69,
0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D,
0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51,
0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05,
0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19,
0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D,
0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21,
0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95,
0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89,
0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD,
0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1,
0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5,
0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9,
0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD,
0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1,
0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
};
uint8_t btRfcommFcs(uint8_t *data, uint32_t len){ //calculate fcs value for RFCOMM
uint32_t i;
uint8_t crcval = 0xFF;
for (i = 0; i < len; i++) crcval = crctab[crcval ^ (*data++)];
return 0xFF - crcval;
}