diff options
Diffstat (limited to 'stack/obx/obx_l2c.c')
-rw-r--r-- | stack/obx/obx_l2c.c | 1031 |
1 files changed, 1031 insertions, 0 deletions
diff --git a/stack/obx/obx_l2c.c b/stack/obx/obx_l2c.c new file mode 100644 index 0000000..51226d9 --- /dev/null +++ b/stack/obx/obx_l2c.c @@ -0,0 +1,1031 @@ +/***************************************************************************** +** +** Name: obx_l2c.c +** +** Description: This OBX module interfaces to L2CAP +** +** Copyright (c) 2008-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include <string.h> +#include "wcassert.h" +#include "data_types.h" +#include "bt_target.h" +#include "obx_api.h" +#include "obx_int.h" +#include "l2c_api.h" +#include "l2cdefs.h" +#include "port_api.h" +#include "btu.h" +#include "btm_int.h" + +/* Configuration flags. */ +#define OBX_L2C_CFG_IND_DONE 0x01 +#define OBX_L2C_CFG_CFM_DONE 0x02 +#define OBX_L2C_SECURITY_DONE 0x04 +#define OBX_L2C_CONN_RQS_DONE 0x07 + +/* "states" used for L2CAP channel */ +#define OBX_CH_IDLE 0 /* No connection */ +#define OBX_CH_CONN 1 /* Waiting for connection confirm */ +#define OBX_CH_CFG 2 /* Waiting for configuration complete */ +#define OBX_CH_OPEN 3 /* Channel opened */ + +/* callback function declarations */ +void obx_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void obx_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); +void obx_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void obx_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void obx_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void obx_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void obx_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); +void obx_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +tOBX_SR_SESS_CB * obx_lcb_2_sr_sess_cb(tOBX_L2C_CB *p_lcb); + +#if (OBX_SERVER_INCLUDED == TRUE) +/* L2CAP callback function structure for server */ +const tL2CAP_APPL_INFO obx_l2c_sr_appl = { + obx_l2c_connect_ind_cback, /* tL2CA_CONNECT_IND_CB */ + NULL, /* tL2CA_CONNECT_CFM_CB */ + NULL, /* tL2CA_CONNECT_PND_CB */ + obx_l2c_config_ind_cback, /* tL2CA_CONFIG_IND_CB */ + obx_l2c_config_cfm_cback, /* tL2CA_CONFIG_CFM_CB */ + obx_l2c_disconnect_ind_cback, /* tL2CA_DISCONNECT_IND_CB */ + obx_l2c_disconnect_cfm_cback, /* tL2CA_DISCONNECT_CFM_CB */ + NULL, /* tL2CA_QOS_VIOLATION_IND_CB */ + obx_l2c_data_ind_cback, /* tL2CA_DATA_IND_CB */ + obx_l2c_congestion_ind_cback, /* tL2CA_CONGESTION_STATUS_CB */ + NULL /* tL2CA_TX_COMPLETE_CB */ +}; +#endif + +#if (OBX_CLIENT_INCLUDED == TRUE) +/* L2CAP callback function structure for client */ +const tL2CAP_APPL_INFO obx_l2c_cl_appl = { + NULL, /* tL2CA_CONNECT_IND_CB */ + obx_l2c_connect_cfm_cback, /* tL2CA_CONNECT_CFM_CB */ + NULL, /* tL2CA_CONNECT_PND_CB */ + obx_l2c_config_ind_cback, /* tL2CA_CONFIG_IND_CB */ + obx_l2c_config_cfm_cback, /* tL2CA_CONFIG_CFM_CB */ + obx_l2c_disconnect_ind_cback, /* tL2CA_DISCONNECT_IND_CB */ + obx_l2c_disconnect_cfm_cback, /* tL2CA_DISCONNECT_CFM_CB */ + NULL, /* tL2CA_QOS_VIOLATION_IND_CB */ + obx_l2c_data_ind_cback, /* tL2CA_DATA_IND_CB */ + obx_l2c_congestion_ind_cback, /* tL2CA_CONGESTION_STATUS_CB */ + NULL /* tL2CA_TX_COMPLETE_CB */ +}; +#endif + +/* OBX eL2CAP default options */ +const tL2CAP_FCR_OPTS obx_l2c_fcr_opts_def = { + L2CAP_FCR_ERTM_MODE, /* Mandatory for Obex over L2CAP */ + OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR, /* Tx window size over Bluetooth */ + OBX_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */ + OBX_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */ + OBX_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */ + L2CAP_DEFAULT_ERM_MPS /* MPS segment size */ +}; + +/******************************************************************************* +** Function obx_l2c_snd_evt +** Description Sends an L2CAP event to OBX through the BTU task. +*******************************************************************************/ +void obx_l2c_snd_evt (tOBX_L2C_CB *p_l2cb, tOBX_L2C_EVT_PARAM param, tOBX_L2C_EVT l2c_evt) +{ + BT_HDR *p_msg; + tOBX_L2C_EVT_MSG *p_evt; + UINT16 event; + + if (!p_l2cb) + return; + + p_msg = (BT_HDR*)GKI_getbuf(BT_HDR_SIZE + sizeof(tOBX_PORT_EVT)); + WC_ASSERT(p_msg); + + if (p_l2cb->handle & OBX_CL_HANDLE_MASK) + event = BT_EVT_TO_OBX_CL_L2C_MSG; + else + event = BT_EVT_TO_OBX_SR_L2C_MSG; + + p_msg->event = event; + p_msg->len = sizeof(tOBX_L2C_EVT_MSG); + p_msg->offset = 0; + p_evt = (tOBX_L2C_EVT_MSG *)(p_msg + 1); + p_evt->l2c_evt = l2c_evt; + p_evt->p_l2cb = p_l2cb; + p_evt->param = param; + + GKI_send_msg(BTU_TASK, BTU_HCI_RCV_MBOX, p_msg); +} + +#if (OBX_SERVER_INCLUDED == TRUE) +/******************************************************************************* +** +** Function obx_sr_scb_by_psm +** +** Description Find the server session control block for L2CAP. +** +** +** Returns void +** +*******************************************************************************/ +tOBX_SR_SESS_CB * obx_sr_scb_by_psm (UINT16 psm) +{ + UINT32 xx, yy; + tOBX_SR_CB *p_cb; + tOBX_SR_SESS_CB *p_scb = NULL, *p_scbt; + UINT16 port_handle; + + for (xx=0; xx<obx_cb.num_server; xx++) + { + if (obx_cb.server[xx].psm == psm) + { + p_cb = &obx_cb.server[xx]; + /* find one that has not allocated a RFCOMM port */ + for (yy=0; yy<p_cb->num_sess; yy++) + { + p_scbt = &obx_cb.sr_sess[p_cb->sess[yy]-1]; + if (p_scbt->ll_cb.comm.id == 0) + { + p_scb = p_scbt; + p_scb->ll_cb.l2c.p_close_fn = obx_close_l2c; + p_scb->ll_cb.l2c.p_send_fn = (tOBX_SEND_FN *)obx_l2c_snd_msg; + break; + } + } + + if (p_scb == NULL) + { + /* check if an RFCOMM port can be freed */ + for (yy=0; yy<p_cb->num_sess; yy++) + { + p_scbt = &obx_cb.sr_sess[p_cb->sess[yy]-1]; + if (p_scbt->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg && p_scbt->state == OBX_SS_NOT_CONNECTED) + { + port_handle = p_scbt->ll_cb.port.port_handle; + p_scbt->ll_cb.port.port_handle = 0; + p_scb = p_scbt; + p_scb->ll_cb.l2c.p_close_fn = obx_close_l2c; + p_scb->ll_cb.l2c.p_send_fn = (tOBX_SEND_FN *)obx_l2c_snd_msg; + obx_close_port(port_handle); + RFCOMM_RemoveServer(port_handle); + obx_sr_free_scb(p_scbt); + break; + } + } + } + break; + } + } + + return p_scb; +} + +/******************************************************************************* +** +** Function obx_sr_proc_l2c_evt +** +** Description This is called to process BT_EVT_TO_OBX_SR_L2C_MSG +** Process server events from L2CAP. Get the associated server control +** block. If this is a request packet, stop timer. Find the +** associated API event and save it in server control block +** (api_evt). Fill the event parameter (param). +** Call obx_ssm_event() with the associated events.If the associated +** control block is not found (maybe the target header does not +** match) or busy, compose a service unavailable response and call +** obx_l2c_snd_msg(). +** Returns void +** +*******************************************************************************/ +void obx_sr_proc_l2c_evt (tOBX_L2C_EVT_MSG *p_msg) +{ + tOBX_SR_SESS_CB *p_scb = NULL; + tOBX_L2C_CB *p_lcb; + BT_HDR *p_pkt=NULL; + tOBX_RX_HDR *p_rxh; + UINT8 opcode; + tOBX_L2C_IND *p_ind; + tL2CAP_CFG_INFO cfg; + UINT16 result = L2CAP_CONN_NO_RESOURCES; +#if (BT_USE_TRACES == TRUE) + UINT16 len; +#endif + tL2CAP_ERTM_INFO ertm_info; + tBT_UUID bt_uuid = {2, {UUID_PROTOCOL_OBEX}}; + tOBX_EVT_PARAM param; + tOBX_SR_CB *p_cb; + + if (p_msg == NULL || p_msg->p_l2cb == NULL) + return; + + if (p_msg->l2c_evt == OBX_L2C_EVT_CONN_IND) + { + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + + p_ind = &p_msg->param.conn_ind; + if ((p_scb = obx_sr_scb_by_psm(p_ind->psm)) != NULL) + { + memcpy(p_scb->param.conn.peer_addr, p_ind->bd_addr, BD_ADDR_LEN); + memcpy(p_scb->peer_addr, p_ind->bd_addr, BD_ADDR_LEN); + result = L2CAP_CONN_OK; + p_lcb = &p_scb->ll_cb.l2c; + cfg.mtu = p_lcb->rx_mtu; + + cfg.fcr_present = TRUE; + cfg.fcr = obx_l2c_fcr_opts_def; + } + /* else no control channel yet, reject */ + + /* Set the FCR options: */ + ertm_info.preferred_mode = obx_l2c_fcr_opts_def.mode; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = OBX_USER_RX_POOL_ID; + ertm_info.user_tx_pool_id = OBX_USER_TX_POOL_ID; + ertm_info.fcr_rx_pool_id = OBX_FCR_RX_POOL_ID; + ertm_info.fcr_tx_pool_id = OBX_FCR_TX_POOL_ID; + + /* Send L2CAP connect rsp */ + L2CA_CONNECT_RSP(p_ind->bd_addr, p_ind->id, p_ind->lcid, result, 0, &ertm_info, &bt_uuid); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) + { + /* store LCID */ + p_lcb->lcid = p_ind->lcid; + obx_cb.l2c_map[p_ind->lcid - L2CAP_BASE_APPL_CID] = p_lcb->handle; + OBX_TRACE_DEBUG2("l2c_map[%d]=0x%x",p_ind->lcid - L2CAP_BASE_APPL_CID, p_lcb->handle ); + + /* transition to configuration state */ + p_lcb->ch_state = OBX_CH_CFG; + p_lcb->ch_flags = OBX_L2C_SECURITY_DONE; + + /* Send L2CAP config req */ + L2CA_CONFIG_REQ(p_ind->lcid, &cfg); + } + return; + } + + p_lcb = p_msg->p_l2cb; + p_scb = obx_lcb_2_sr_sess_cb(p_lcb); + if (p_scb == NULL) + return; + + switch (p_msg->l2c_evt) + { + case OBX_L2C_EVT_RESUME: + p_cb = &obx_cb.server[p_scb->handle - 1]; + param.ssn = p_scb->ssn; + (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, param, p_pkt); + break; + + case OBX_L2C_EVT_CONG: + p_lcb->cong = p_msg->param.is_cong; + obx_ssm_event (p_scb, OBX_FCS_SET_SEVT, NULL); + break; + + case OBX_L2C_EVT_CLOSE: + obx_ssm_event (p_scb, OBX_PORT_CLOSE_SEVT, NULL); + break; + + case OBX_L2C_EVT_DATA_IND: + p_pkt = p_msg->param.p_pkt; + OBX_TRACE_DEBUG2("obx_sr_proc_l2c_evt len:%d, offset:%d", p_pkt->len, p_pkt->offset ); +#if (BT_USE_TRACES == TRUE) + len = p_pkt->len; + if (len > 0x20) + len = 0x20; + obxu_dump_hex ((UINT8 *)(p_pkt + 1) + p_pkt->offset, "rsp evt", len); +#endif + p_rxh = (tOBX_RX_HDR *)(p_pkt + 1); + opcode = *((UINT8 *)(p_pkt + 1) + p_pkt->offset); + memset(p_rxh, 0, sizeof(tOBX_RX_HDR)); + if (obx_verify_request (opcode, p_rxh) == OBX_BAD_SM_EVT) + { + OBX_TRACE_ERROR1("bad opcode:0x%x disconnect now", opcode ); + GKI_freebuf(p_pkt); + /* coverity [overrun-call] */ + obx_ssm_event(p_scb, OBX_TX_EMPTY_SEVT, NULL); + return; + } + p_pkt->event = obx_sm_evt_to_api_evt[p_rxh->sm_evt]; + p_pkt->layer_specific = GKI_get_buf_size(p_pkt) - BT_HDR_SIZE - p_pkt->offset - p_pkt->len; + OBX_TRACE_DEBUG3("opcode:0x%x event:%d sm_evt:%d", opcode, p_pkt->event, p_rxh->sm_evt ); + if (p_pkt->event != OBX_BAD_SM_EVT) + { + if (GKI_queue_is_empty(&p_lcb->rx_q) && (p_scb->srm & OBX_SRM_WAIT_UL) == 0) + { + obx_sr_proc_pkt (p_scb, p_pkt); + } + else + { + GKI_enqueue (&p_lcb->rx_q, p_pkt); + if (p_lcb->rx_q.count > obx_cb.max_rx_qcount) + { + p_lcb->stopped = TRUE; + L2CA_FlowControl(p_lcb->lcid, FALSE); + } + OBX_TRACE_DEBUG4 ("obx_sr_proc_l2c_evt stopped:%d state:%d rx_q.count:%d, srm:0x%x", + p_lcb->stopped, p_scb->state, p_lcb->rx_q.count, p_scb->srm ); + } + } + else + { + OBX_TRACE_ERROR0("bad SM event" ); + } + break; + } +} + +/******************************************************************************* +** +** Function obx_l2c_sr_register +** +** Description register the PSM to L2CAP. +** +** +** Returns void +** +*******************************************************************************/ +tOBX_STATUS obx_l2c_sr_register (tOBX_SR_CB *p_cb) +{ + tOBX_STATUS status = OBX_NO_RESOURCES; + + if (L2CA_REGISTER (p_cb->psm, &obx_l2c_sr_appl, AMP_AUTOSWITCH_ALLOWED|AMP_USE_AMP_IF_POSSIBLE)) + { + status = OBX_SUCCESS; + } + return status; +} +#endif + +#if (OBX_SERVER_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function obx_lcb_2_sr_sess_cb +** +** Description Find the client control block for the given l2cap session. +** +** +** Returns void +** +*******************************************************************************/ +tOBX_SR_SESS_CB * obx_lcb_2_sr_sess_cb(tOBX_L2C_CB *p_lcb) +{ + UINT32 xx, yy; + tOBX_SR_CB *p_cb; + tOBX_SR_SESS_CB *p_scb = NULL; + + for (xx=0; xx<obx_cb.num_server; xx++) + { + if (obx_cb.server[xx].num_sess) + { + p_cb = &obx_cb.server[xx]; + for (yy=0; yy<p_cb->num_sess; yy++) + { + if (&(obx_cb.sr_sess[p_cb->sess[yy]-1].ll_cb.l2c) == p_lcb) + { + p_scb = &(obx_cb.sr_sess[p_cb->sess[yy]-1]); + break; + } + } + + if (p_scb) + break; + } + } + return p_scb; +} +#endif + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function obx_lcb_2_clcb +** +** Description Find the client control block for the given l2cap session. +** +** +** Returns void +** +*******************************************************************************/ +tOBX_CL_CB * obx_lcb_2_clcb(tOBX_L2C_CB *p_lcb) +{ + UINT32 xx; + tOBX_CL_CB *p_cl_cb = NULL; + + for (xx=0; xx<obx_cb.num_client; xx++) + { + if (&obx_cb.client[xx].ll_cb.l2c == p_lcb) + { + p_cl_cb = &obx_cb.client[xx]; + break; + } + } + return p_cl_cb; +} +#endif + +/******************************************************************************* +** +** Function obx_lcid_2lcb +** +** Description Given a lcid, return the associated client or server +** control block. +** +** Returns +** +*******************************************************************************/ +tOBX_L2C_CB * obx_lcid_2lcb(UINT16 lcid) +{ + tOBX_L2C_CB *p_lcb = NULL; + tOBX_HANDLE obx_handle = 0; +#if (OBX_SERVER_INCLUDED == TRUE) + tOBX_SR_CB *p_cb; +#endif + tOBX_HANDLE obx_mskd_handle; + + /* this function is called by obx_rfc_cback() only. + * assume that port_handle is within range */ + obx_handle = obx_cb.l2c_map[lcid-L2CAP_BASE_APPL_CID]; + obx_mskd_handle = obx_handle&OBX_HANDLE_MASK; + OBX_TRACE_DEBUG3("obx_lcid_2lcb lcid:0x%x obx_handle:0x%x obx_mskd_handle:0x%x", + lcid, obx_handle, obx_mskd_handle); + + if (obx_handle > 0) + { + if (obx_mskd_handle & OBX_CL_HANDLE_MASK) + { +#if (OBX_CLIENT_INCLUDED == TRUE) + obx_mskd_handle &= ~OBX_CL_HANDLE_MASK; + p_lcb = &obx_cb.client[obx_mskd_handle - 1].ll_cb.l2c; +#endif /* OBX_CLIENT_INCLUDED */ + } + else if (obx_mskd_handle < OBX_NUM_SERVERS) + { +#if (OBX_SERVER_INCLUDED == TRUE) + p_cb = &obx_cb.server[obx_mskd_handle - 1]; + p_lcb = &obx_cb.sr_sess[p_cb->sess[OBX_DEC_SESS_IND(obx_handle)]-1].ll_cb.l2c; + OBX_TRACE_DEBUG3("p_lcb lcid:0x%x sess_ind:%d, sr_sess[%d]", + p_lcb->lcid, OBX_DEC_SESS_IND(obx_handle), p_cb->sess[OBX_DEC_SESS_IND(obx_handle)]-1); +#endif /* OBX_SERVER_INCLUDED */ + } + } + + return p_lcb; +} + +/******************************************************************************* +** +** Function obx_l2c_checks_ch_flags +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void obx_l2c_checks_ch_flags (tOBX_L2C_CB *p_lcb) +{ + tOBX_L2C_EVT_PARAM evt_param; + + OBX_TRACE_DEBUG1 ("obx_l2c_checks_ch_flags ch_flags:0x%x ", p_lcb->ch_flags); + /* if all the required ch_flags are set, report the OPEN event now */ + if ((p_lcb->ch_flags & OBX_L2C_CONN_RQS_DONE) == OBX_L2C_CONN_RQS_DONE) + { + p_lcb->ch_state = OBX_CH_OPEN; + obx_start_timer((tOBX_COMM_CB *)p_lcb); + evt_param.is_cong = FALSE; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CONG); + } +} + +/******************************************************************************* +** +** Function obx_l2c_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +#if (OBX_SERVER_INCLUDED == TRUE) +void obx_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tOBX_L2C_EVT_PARAM evt_param; + + obx_cb.sr_l2cb.handle = 0; /* to mark as server event */ + memcpy( evt_param.conn_ind.bd_addr, bd_addr, BD_ADDR_LEN); + evt_param.conn_ind.lcid = lcid; + evt_param.conn_ind.psm = psm; + evt_param.conn_ind.id = id; + obx_l2c_snd_evt (&obx_cb.sr_l2cb, evt_param, OBX_L2C_EVT_CONN_IND); +} +#endif /* OBX_SERVER_INCLUDED */ + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function obx_cl_proc_l2c_evt +** +** Description This is called to process BT_EVT_TO_OBX_CL_L2C_MSG +** Process client events from L2CAP. Get the associated client control +** block. If this is a response packet, stop timer. Call +** obx_csm_event() with event OK_CFM, FAIL_CFM or CONT_CFM. +** Returns void +** +*******************************************************************************/ +void obx_cl_proc_l2c_evt (tOBX_L2C_EVT_MSG *p_msg) +{ + tOBX_CL_CB *p_cl_cb = NULL; + tOBX_L2C_CB *p_l2cb; + BT_HDR *p_pkt; + tOBX_RX_HDR *p_rxh; + UINT8 opcode; + + if (p_msg == NULL || p_msg->p_l2cb == NULL) + return; + + p_l2cb = p_msg->p_l2cb; + p_cl_cb = obx_lcb_2_clcb(p_l2cb); + if (p_cl_cb == NULL) + return; + + switch (p_msg->l2c_evt) + { + case OBX_L2C_EVT_CONG: + p_msg->p_l2cb->cong = p_msg->param.is_cong; + obx_csm_event (p_cl_cb, OBX_FCS_SET_CEVT, NULL); + break; + + case OBX_L2C_EVT_CLOSE: + obx_csm_event (p_cl_cb, OBX_PORT_CLOSE_CEVT, NULL); + break; + + case OBX_L2C_EVT_DATA_IND: + p_pkt = p_msg->param.p_pkt; + p_rxh = (tOBX_RX_HDR *)(p_pkt + 1); + opcode = *((UINT8 *)(p_pkt + 1) + p_pkt->offset); + memset(p_rxh, 0, sizeof(tOBX_RX_HDR)); + obx_verify_response (opcode, p_rxh); + + OBX_TRACE_DEBUG4 ("obx_cl_proc_l2c_evt event:0x%x/0x%x state:%d srm:0x%x", p_pkt->event, p_rxh->sm_evt, p_cl_cb->state, p_cl_cb->srm ); + if (p_rxh->sm_evt != OBX_BAD_SM_EVT) + { + if (GKI_queue_is_empty(&p_l2cb->rx_q) && (p_cl_cb->srm & OBX_SRM_WAIT_UL) == 0) + { + obx_cl_proc_pkt (p_cl_cb, p_pkt); + } + else + { + GKI_enqueue (&p_l2cb->rx_q, p_pkt); + if (p_l2cb->rx_q.count > obx_cb.max_rx_qcount) + { + p_l2cb->stopped = TRUE; + L2CA_FlowControl(p_l2cb->lcid, FALSE); + } + OBX_TRACE_DEBUG3 ("obx_cl_proc_l2c_evt rx_q.count:%d, stopped:%d state:%d", p_l2cb->rx_q.count, p_l2cb->stopped, p_cl_cb->state ); + } + } + else + { + OBX_TRACE_ERROR0("bad SM event" ); + } + break; + + } +} + +/******************************************************************************* +** +** Function obx_l2c_sec_check_complete +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void obx_l2c_sec_check_complete (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tOBX_L2C_CB *p_lcb = (tOBX_L2C_CB *)p_ref_data; + + OBX_TRACE_DEBUG3 ("obx_l2c_sec_check_complete ch_state:%d, ch_flags:0x%x, status:%d", + p_lcb->ch_state, p_lcb->ch_flags, res); + if (p_lcb->ch_state == OBX_CH_IDLE) + return; + + if (res == BTM_SUCCESS) + { + p_lcb->ch_flags |= OBX_L2C_SECURITY_DONE; + obx_l2c_checks_ch_flags (p_lcb); + } + else + { + /* security failed - disconnect the channel */ + L2CA_DISCONNECT_REQ (p_lcb->lcid); + } +} + +/******************************************************************************* +** +** Function obx_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tOBX_L2C_CB *p_lcb; + tL2CAP_CFG_INFO cfg; + tOBX_L2C_EVT_PARAM evt_param; + tOBX_CL_CB *p_cl_cb = NULL; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + OBX_TRACE_DEBUG1("obx_l2c_connect_cfm_cback ch_state:%d", p_lcb->ch_state); + /* if in correct state */ + if (p_lcb->ch_state == OBX_CH_CONN) + { + /* if result successful */ + if (result == L2CAP_CONN_OK) + { + /* set channel state */ + p_lcb->ch_state = OBX_CH_CFG; + + p_cl_cb = obx_lcb_2_clcb(p_lcb); + btm_sec_mx_access_request (p_cl_cb->peer_addr, p_cl_cb->psm, TRUE, + 0, 0, &obx_l2c_sec_check_complete, p_lcb); + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + cfg.mtu_present = TRUE; + cfg.mtu = p_lcb->rx_mtu; + + cfg.fcr_present = TRUE; + cfg.fcr = obx_l2c_fcr_opts_def; + + L2CA_CONFIG_REQ(lcid, &cfg); + } + /* else failure */ + else + { + evt_param.any = 0; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CLOSE); + } + } + } +} +#endif /* OBX_CLIENT_INCLUDED */ + + +/******************************************************************************* +** +** Function obx_l2c_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tOBX_L2C_CB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + /* if in correct state */ + if (p_lcb->ch_state == OBX_CH_CFG) + { + /* if result successful */ + if (p_cfg->result == L2CAP_CFG_OK) + { + /* update flags */ + p_lcb->ch_flags |= OBX_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + obx_l2c_checks_ch_flags(p_lcb); + } + /* else failure */ + else + { + /* store result value + p_lcb->ch_result = p_cfg->result; */ + + /* Send L2CAP disconnect req */ + L2CA_DISCONNECT_REQ(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function obx_l2c_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tOBX_L2C_CB *p_lcb; + UINT16 max_mtu = OBX_MAX_MTU; + + /* Don't include QoS nor flush timeout in the response since we + currently always accept these values. Note: fcr_present is left + untouched since l2cap negotiates this internally + */ + p_cfg->flush_to_present = FALSE; + p_cfg->qos_present = FALSE; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + /* store the mtu in tbl */ + if (p_cfg->mtu_present) + { + p_lcb->tx_mtu = p_cfg->mtu; + } + else + { + p_lcb->tx_mtu = L2CAP_DEFAULT_MTU; + } + + if (p_lcb->tx_mtu > max_mtu) + { + p_lcb->tx_mtu = p_cfg->mtu = max_mtu; + + /* Must tell the peer what the adjusted value is */ + p_cfg->mtu_present = TRUE; + } + else /* Don't include in the response */ + p_cfg->mtu_present = FALSE; + OBX_TRACE_DEBUG2 ("obx_l2c_config_ind_cback tx_mtu:%d use:%d", p_lcb->tx_mtu, max_mtu); + + p_cfg->result = L2CAP_CFG_OK; + + /* send L2CAP configure response */ + L2CA_CONFIG_RSP(lcid, p_cfg); + + if (p_cfg->result != L2CAP_CFG_OK) + { + return; + } + + /* if first config ind */ + if ((p_lcb->ch_flags & OBX_L2C_CFG_IND_DONE) == 0) + { + /* update flags */ + p_lcb->ch_flags |= OBX_L2C_CFG_IND_DONE; + + /* if configuration complete */ + obx_l2c_checks_ch_flags(p_lcb); + } + } +} + +/******************************************************************************* +** +** Function obx_l2c_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tOBX_L2C_CB *p_lcb; + tOBX_L2C_EVT_PARAM evt_param; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DISCONNECT_RSP(lcid); + } + + evt_param.any = 0; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CLOSE); + } +} + +/******************************************************************************* +** +** Function obx_l2c_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tOBX_L2C_CB *p_lcb; + tOBX_L2C_EVT_PARAM evt_param; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + evt_param.any = 0; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CLOSE); + } +} + +/******************************************************************************* +** +** Function obx_l2c_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tOBX_L2C_CB *p_lcb; + tOBX_L2C_EVT_PARAM evt_param; +#if (BT_USE_TRACES == TRUE) + UINT16 len; +#endif + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + evt_param.p_pkt = p_buf; + OBX_TRACE_DEBUG3("obx_l2c_data_ind_cback 0x%x, len:%d, offset:%d", p_buf, p_buf->len, p_buf->offset ); +#if (BT_USE_TRACES == TRUE) + len = p_buf->len; + if (len > 0x20) + len = 0x20; + obxu_dump_hex ((UINT8 *)(p_buf + 1) + p_buf->offset, "rsp cback", len); +#endif + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_DATA_IND); + } + else /* prevent buffer leak */ + GKI_freebuf(p_buf); +} + + +/******************************************************************************* +** +** Function obx_l2c_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tOBX_L2C_CB *p_lcb; + tOBX_L2C_EVT_PARAM evt_param; + + OBX_TRACE_DEBUG2("obx_l2c_congestion_ind_cback lcid:%d, is_congested:%d",lcid, is_congested ); + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + evt_param.is_cong = is_congested; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CONG); + } +} + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_register_l2c +** Description Call L2CA_Register() to get virtual psm. +** Returns +*******************************************************************************/ +void obx_register_l2c(tOBX_CL_CB *p_cl_cb, UINT16 psm) +{ + p_cl_cb->psm = L2CA_REGISTER (psm, &obx_l2c_cl_appl, AMP_AUTOSWITCH_ALLOWED|AMP_USE_AMP_IF_POSSIBLE); +} + +/******************************************************************************* +** Function obx_open_l2c +** Description Call L2CA_Register() & L2CA_ConnectReq() to get lcid. +** Returns port handle +*******************************************************************************/ +tOBX_STATUS obx_open_l2c(tOBX_CL_CB *p_cl_cb, const BD_ADDR bd_addr) +{ + tOBX_L2C_CB *p_l2cb = &p_cl_cb->ll_cb.l2c; + tOBX_STATUS status = OBX_NO_RESOURCES; /* successful */ + UINT16 max_mtu = OBX_MAX_MTU; + tL2CAP_CFG_INFO cfg; + tL2CAP_ERTM_INFO ertm_info; + tBT_UUID bt_uuid = {2, {UUID_PROTOCOL_OBEX}}; + + OBX_TRACE_DEBUG2("obx_open_l2c rxmtu:%d, cbmtu:%d", p_l2cb->rx_mtu, max_mtu ); + + /* clear buffers from previous connection */ + obx_free_buf(&p_cl_cb->ll_cb); + + /* make sure the MTU is in registered range */ + if (p_l2cb->rx_mtu > max_mtu) + p_l2cb->rx_mtu = max_mtu; + if (p_l2cb->rx_mtu < OBX_MIN_MTU) + p_l2cb->rx_mtu = OBX_MIN_MTU; + + if (p_cl_cb->psm) + { + memcpy (p_cl_cb->peer_addr, bd_addr, BD_ADDR_LEN); + + /* Set the FCR options: */ + ertm_info.preferred_mode = obx_l2c_fcr_opts_def.mode; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = OBX_USER_RX_POOL_ID; + ertm_info.user_tx_pool_id = OBX_USER_TX_POOL_ID; + ertm_info.fcr_rx_pool_id = OBX_FCR_RX_POOL_ID; + ertm_info.fcr_tx_pool_id = OBX_FCR_TX_POOL_ID; + + p_l2cb->lcid = L2CA_CONNECT_REQ (p_cl_cb->psm, (BD_ADDR_PTR)bd_addr, &ertm_info, &bt_uuid); + if (p_l2cb->lcid) + { + p_l2cb->ch_state = OBX_CH_CONN; + p_l2cb->ch_flags = 0; + p_l2cb->cong = TRUE; + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.fcr_present = TRUE; + cfg.fcr = obx_l2c_fcr_opts_def; + status = OBX_SUCCESS; + } + } + + OBX_TRACE_DEBUG3("obx_open_l2c rxmtu:%d, lcid:%d, l2c.handle:0x%x", + p_l2cb->rx_mtu, p_l2cb->lcid, p_l2cb->handle ); + + if (status == OBX_SUCCESS) + { + obx_cb.l2c_map[p_l2cb->lcid - L2CAP_BASE_APPL_CID] = p_l2cb->handle; + p_l2cb->p_send_fn = (tOBX_SEND_FN *)obx_l2c_snd_msg; + p_l2cb->p_close_fn = obx_close_l2c; + } + else + { + status = OBX_NO_RESOURCES; + } + + return status; +} +#endif + +/******************************************************************************* +** +** Function obx_close_l2c +** Description Clear the port event mask and callback. Close the port. +** Returns void +*******************************************************************************/ +void obx_close_l2c(UINT16 lcid) +{ + L2CA_DISCONNECT_REQ (lcid); +} + +/******************************************************************************* +** Function obx_l2c_snd_msg +** Description Call PORT_WriteData() to send an OBEX message to peer. If +** all data is sent, free the GKI buffer that holds +** the OBEX message. If only portion of data is +** sent, adjust the BT_HDR for PART state. +** Returns TRUE if all data is sent +*******************************************************************************/ +BOOLEAN obx_l2c_snd_msg(tOBX_L2C_CB *p_l2cb) +{ + BOOLEAN sent = FALSE; + + if (!p_l2cb->cong) + { + OBX_TRACE_DEBUG2("obx_l2c_snd_msg len:%d, offset:0x%x", p_l2cb->p_txmsg->len, p_l2cb->p_txmsg->offset); + + obx_stop_timer(&p_l2cb->tle); + if (L2CA_DATA_WRITE (p_l2cb->lcid, p_l2cb->p_txmsg) == L2CAP_DW_CONGESTED) + { + OBX_TRACE_DEBUG0("obx_l2c_snd_msg congested"); + p_l2cb->cong = TRUE; + } + obx_start_timer ((tOBX_COMM_CB *)p_l2cb); + p_l2cb->p_txmsg = NULL; + sent = TRUE; + } + + return sent; +} |