summaryrefslogtreecommitdiffstats
path: root/stack/obx/obx_rfc.c
diff options
context:
space:
mode:
Diffstat (limited to 'stack/obx/obx_rfc.c')
-rw-r--r--stack/obx/obx_rfc.c613
1 files changed, 613 insertions, 0 deletions
diff --git a/stack/obx/obx_rfc.c b/stack/obx/obx_rfc.c
new file mode 100644
index 0000000..b02fbef
--- /dev/null
+++ b/stack/obx/obx_rfc.c
@@ -0,0 +1,613 @@
+/*****************************************************************************
+**
+** Name: obx_rfc.c
+**
+** File: OBEX interface to the RFCOMM module
+**
+** Copyright (c) 2003-2008, Broadcom Corp., All Rights Reserved.
+** WIDCOMM Bluetooth Core. Proprietary and confidential.
+**
+*****************************************************************************/
+#include <string.h>
+#include "wcassert.h"
+
+#include "bt_target.h"
+#include "obx_int.h"
+
+#include "port_api.h"
+#include "sdpdefs.h"
+#include "btu.h"
+
+/*******************************************************************************
+** Function obx_rfc_snd_evt
+** Description Sends an rfcomm event to OBX through the BTU task.
+*******************************************************************************/
+static void obx_rfc_snd_evt (tOBX_PORT_CB *p_pcb, UINT32 code)
+{
+ BT_HDR *p_msg;
+ tOBX_PORT_EVT *p_evt;
+ UINT16 event;
+
+ if (!p_pcb)
+ return;
+
+ p_msg = (BT_HDR*)GKI_getbuf(BT_HDR_SIZE + sizeof(tOBX_PORT_EVT));
+ WC_ASSERT(p_msg);
+
+ if (p_pcb->handle & OBX_CL_HANDLE_MASK)
+ event = BT_EVT_TO_OBX_CL_MSG;
+ else
+ event = BT_EVT_TO_OBX_SR_MSG;
+
+ p_msg->event = event;
+ p_msg->len = sizeof(tOBX_PORT_EVT);
+ p_msg->offset = 0;
+ p_evt = (tOBX_PORT_EVT *)(p_msg + 1);
+ p_evt->code = code;
+ p_evt->p_pcb = p_pcb;
+
+ GKI_send_msg(BTU_TASK, BTU_HCI_RCV_MBOX, p_msg);
+}
+
+/*******************************************************************************
+** Function obx_rfc_cback
+** Description find the port control block and post an event to BTU task.
+** NOTE: This callback does not handle connect up/down events.
+** obx_rfc_mgmt_cback is used for these events.
+*******************************************************************************/
+static void obx_rfc_cback (UINT32 code, UINT16 port_handle)
+{
+ tOBX_PORT_CB *p_pcb = obx_port_handle_2cb(port_handle);
+
+ if (p_pcb)
+ {
+ obx_rfc_snd_evt (p_pcb, code);
+ }
+ else
+ {
+ OBX_TRACE_WARNING0("Can not find control block");
+ }
+}
+
+/*******************************************************************************
+** Function obx_rfc_mgmt_cback
+** Callback registered with the PORT entity's Management Callback so that OBX
+** can be notified when the connection has come up or gone down.
+********************************************************************************/
+void obx_rfc_mgmt_cback(UINT32 port_status, UINT16 port_handle)
+{
+ tOBX_PORT_CB *p_pcb = obx_port_handle_2cb(port_handle);
+ UINT32 code;
+
+#if (OBX_CLIENT_INCLUDED == TRUE)
+ if (!p_pcb && port_status != PORT_SUCCESS)
+ {
+ /* See if error called within RFCOMM_CreateConnection */
+ if (obx_cb.p_temp_pcb)
+ {
+ p_pcb = obx_cb.p_temp_pcb;
+ obx_cb.p_temp_pcb = NULL;
+ }
+ }
+#endif
+
+ if (p_pcb)
+ {
+ code = (port_status == PORT_SUCCESS) ? PORT_EV_CONNECTED : PORT_EV_CONNECT_ERR;
+ obx_rfc_snd_evt (p_pcb, code);
+ }
+ else
+ {
+ OBX_TRACE_WARNING0("mgmt cback: Can not find control block");
+ }
+}
+
+
+/*******************************************************************************
+** Function obx_read_data
+** Description This functions reads data from FRCOMM. Return a message if the
+** whole packet is read.
+** The following defines how BT_HDR is used in this function
+** event: response or request event code
+** len: the length read so far.
+** offset: offset to the beginning of the actual data.
+** layer_specific: left
+*******************************************************************************/
+static BT_HDR * obx_read_data (tOBX_PORT_CB *p_pcb, tOBX_VERIFY_OPCODE p_verify_opcode)
+{
+ BT_HDR *p_ret = NULL;
+ UINT8 *p;
+ UINT16 ask_len;
+ UINT16 got_len;
+ int rc;
+ tOBX_RX_HDR *p_rxh;
+ tOBX_SR_SESS_CB *p_scb;
+ UINT8 opcode;
+ UINT16 pkt_len;
+ BOOLEAN failed = FALSE;
+
+ OBX_TRACE_DEBUG1("obx_read_data port_handle:%d", p_pcb->port_handle );
+ for (;;)
+ {
+ if (p_pcb->p_rxmsg == NULL)
+ {
+ p_pcb->p_rxmsg = OBX_HdrInit((tOBX_HANDLE)(p_pcb->handle|OBX_HANDLE_RX_MTU_MASK),
+ OBX_LRG_DATA_POOL_SIZE);
+ memset((p_pcb->p_rxmsg + 1), 0, sizeof(tOBX_RX_HDR));
+ }
+ /* we use this header to keep the status of this packet (instead of in control block) */
+ p_rxh = (tOBX_RX_HDR *)(p_pcb->p_rxmsg + 1);
+
+ ask_len = 0;
+ if (p_rxh->code == 0)
+ {
+ if (p_pcb->p_rxmsg->len == 0) /* we need this if statement in case of "throw away" */
+ ask_len = 1;
+ }
+ else if (p_pcb->p_rxmsg->len < (OBX_PKT_LEN_SIZE + 1) )
+ {
+ /* if we do not know the packet len yet, read from port */
+ ask_len = OBX_PKT_LEN_SIZE + 1 - p_pcb->p_rxmsg->len;
+ }
+ else
+ {
+ /* we already know the packet len.
+ * determine how many more bytes we need for this packet */
+ ask_len = p_rxh->pkt_len - p_pcb->p_rxmsg->len;
+ }
+
+ /* the position of next byte to read */
+ p = (UINT8 *)(p_pcb->p_rxmsg + 1) + p_pcb->p_rxmsg->offset + p_pcb->p_rxmsg->len;
+
+ if (ask_len)
+ {
+ rc = PORT_ReadData( p_pcb->port_handle, (char*)p, ask_len, &got_len);
+ if (rc != PORT_SUCCESS)
+ {
+ OBX_TRACE_WARNING2("Error %d returned from PORT_Read_Data, len:%d", rc, got_len);
+ }
+
+ OBX_TRACE_DEBUG2("ask_len: %d, got_len:%d", ask_len, got_len );
+ if (got_len == 0)
+ {
+ /* If we tried to read but did not get anything, */
+ /* there is nothing more to read at this time */
+ break;
+ }
+ p_pcb->p_rxmsg->len += got_len;
+ p_pcb->p_rxmsg->layer_specific -= got_len;
+ }
+
+ /* process the response/opcode, if not yet */
+ if (p_rxh->code == 0 && p_pcb->p_rxmsg->len)
+ {
+ opcode = *((UINT8 *)(p_pcb->p_rxmsg + 1) + p_pcb->p_rxmsg->offset);
+ if ( (p_verify_opcode)(opcode, p_rxh) == OBX_BAD_SM_EVT)
+ {
+ OBX_TRACE_WARNING1("bad opcode:0x%x - Disconnecting", opcode );
+ /* received data with bad length. */
+
+ /*bad length disconnect */
+ failed = TRUE;
+ break;
+ }
+ continue;
+ }
+
+ /* process the packet len */
+ if (p_rxh->pkt_len == 0 && p_pcb->p_rxmsg->len >= (OBX_PKT_LEN_SIZE + 1) )
+ {
+ p = (UINT8 *)(p_pcb->p_rxmsg + 1) + p_pcb->p_rxmsg->offset + 1;
+ BE_STREAM_TO_UINT16(pkt_len, p);
+
+ if ( (pkt_len > p_pcb->rx_mtu) ||
+ (pkt_len < 3) ||
+ (pkt_len == 4) )
+ {
+ /* received data with bad length. */
+ OBX_TRACE_WARNING2("Received bad packet len -Disconnecting: %d RX MTU: %x",
+ pkt_len, p_pcb->rx_mtu);
+ /*bad length disconnect */
+ failed = TRUE;
+ break;
+ }
+ else
+ {
+ /* keep the packet len in the header */
+ p_rxh->pkt_len = pkt_len;
+ }
+ continue;
+ }
+
+ if (p_pcb->p_rxmsg->len == p_rxh->pkt_len)
+ {
+ /* received a whole packet */
+ OBX_TRACE_DEBUG1("got a packet. opcode:0x%x", p_rxh->code );
+ p_ret = p_pcb->p_rxmsg;
+ p_pcb->p_rxmsg = NULL;
+ break;
+ }
+
+ }
+
+ if (failed)
+ {
+ if (p_pcb->handle & OBX_CL_HANDLE_MASK)
+ {
+ obx_close_port(p_pcb->port_handle);
+ }
+ else
+ {
+ if ((p_scb = obx_sr_get_scb(p_pcb->handle)) != NULL)
+ obx_ssm_event(p_scb, OBX_PORT_CLOSE_SEVT, NULL);
+
+ }
+ p_ret = NULL;
+ }
+
+ return p_ret;
+}
+
+
+#if (OBX_CLIENT_INCLUDED == TRUE)
+/*******************************************************************************
+** Function obx_cl_proc_evt
+** Description This is called to process BT_EVT_TO_OBX_CL_MSG
+** Process events from RFCOMM. 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.
+*******************************************************************************/
+void obx_cl_proc_evt(tOBX_PORT_EVT *p_evt)
+{
+ tOBX_PORT_CB *p_pcb = p_evt->p_pcb;
+ tOBX_CL_CB *p_cb = obx_cl_get_cb(p_pcb->handle);
+ BT_HDR *p_pkt;
+
+ if (p_cb == NULL)
+ {
+ /* probably already close the port and deregistered from OBX */
+ OBX_TRACE_ERROR1("Could not find control block for handle: 0x%x", p_pcb->handle);
+ return;
+ }
+
+ if (p_evt->code & PORT_EV_CONNECT_ERR)
+ {
+ obx_csm_event(p_cb, OBX_PORT_CLOSE_CEVT, NULL);
+ return;
+ } /* PORT_EV_CONNECT_ERR */
+
+ if (p_evt->code & PORT_EV_TXEMPTY)
+ {
+ obx_csm_event(p_cb, OBX_TX_EMPTY_CEVT, NULL);
+ } /* PORT_EV_TXEMPTY */
+
+ if (p_evt->code & PORT_EV_RXCHAR)
+ {
+ while( (p_pkt = obx_read_data(p_pcb, obx_verify_response)) != NULL )
+ {
+ if (GKI_queue_is_empty(&p_pcb->rx_q))
+ {
+ if (p_pkt->event != OBX_BAD_SM_EVT)
+ {
+ obx_cl_proc_pkt (p_cb, p_pkt);
+ }
+ else
+ {
+ OBX_TRACE_ERROR0("bad SM event" );
+ }
+ }
+ else if (p_pkt->event != OBX_BAD_SM_EVT)
+ {
+ GKI_enqueue (&p_pcb->rx_q, p_pkt);
+ if (p_pcb->rx_q.count > obx_cb.max_rx_qcount)
+ {
+ p_pcb->stopped = TRUE;
+ PORT_FlowControl(p_pcb->port_handle, FALSE);
+ }
+ }
+ } /* while received a packet */
+ } /* PORT_EV_RXCHAR */
+
+ if (p_evt->code & PORT_EV_FC)
+ {
+ if (p_evt->code & PORT_EV_FCS)
+ {
+ OBX_TRACE_EVENT0("cl flow control event - FCS SET ----" );
+ obx_csm_event(p_cb, OBX_FCS_SET_CEVT, NULL);
+ }
+ } /* PORT_EV_FC */
+}
+#endif
+
+#if (OBX_SERVER_INCLUDED == TRUE)
+/*******************************************************************************
+** Function obx_build_dummy_rsp
+** Description make up a dummy response if the app does not call response API
+** yet and AbortRsp is called
+*******************************************************************************/
+BT_HDR * obx_build_dummy_rsp(tOBX_SR_SESS_CB *p_scb, UINT8 rsp_code)
+{
+ BT_HDR *p_pkt;
+ UINT8 *p;
+ UINT16 size = 3;
+
+ p_pkt = OBX_HdrInit(p_scb->ll_cb.comm.handle, OBX_CMD_POOL_SIZE);
+ p = (UINT8 *)(p_pkt+1)+p_pkt->offset+p_pkt->len;
+ *p++ = (rsp_code|OBX_FINAL);
+ if (p_scb->conn_id)
+ {
+ size += 5;
+ UINT16_TO_BE_STREAM(p, size);
+ *p++ = OBX_HI_CONN_ID;
+ UINT32_TO_BE_STREAM(p, p_scb->conn_id);
+ }
+ else
+ {
+ UINT16_TO_BE_STREAM(p, size);
+ }
+ p_pkt->len = size;
+ p_pkt->event = OBX_PUT_RSP_EVT; /* or OBX_GET_RSP_EVT: for tracing purposes */
+ return p_pkt;
+}
+
+/*******************************************************************************
+** Function obx_add_port
+** Description check if this server has aother un-used port to open
+**
+**
+** Returns void
+*******************************************************************************/
+void obx_add_port(tOBX_HANDLE obx_handle)
+{
+ tOBX_SR_CB * p_cb = obx_sr_get_cb(obx_handle);
+ tOBX_SR_SESS_CB *p_scb, *p_scb0;
+ int xx;
+ tOBX_STATUS status = OBX_NO_RESOURCES;
+ BOOLEAN found;
+
+ OBX_TRACE_DEBUG1("obx_add_port handle:0x%x", obx_handle );
+ if (p_cb && p_cb->scn)
+ {
+ OBX_TRACE_DEBUG2("num_sess:%d scn:%d", p_cb->num_sess, p_cb->scn );
+ p_scb0 = &obx_cb.sr_sess[p_cb->sess[0]-1];
+ found = FALSE;
+ /* find an RFCOMM port that is not connected yet */
+ for (xx=0; xx < p_cb->num_sess && p_cb->sess[xx]; xx++)
+ {
+ p_scb = &obx_cb.sr_sess[p_cb->sess[xx]-1];
+ OBX_TRACE_DEBUG3("[%d] id:0x%x, state:%d", xx, p_scb->ll_cb.comm.id, p_scb->state );
+
+ if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg
+ && p_scb->state == OBX_SS_NOT_CONNECTED)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ for (xx=0; xx < p_cb->num_sess && p_cb->sess[xx]; xx++)
+ {
+ p_scb = &obx_cb.sr_sess[p_cb->sess[xx]-1];
+ OBX_TRACE_DEBUG2("[%d] port_handle:%d", xx, p_scb->ll_cb.port.port_handle );
+ if (!p_scb->ll_cb.comm.id)
+ {
+ status = obx_open_port(&p_scb->ll_cb.port, BT_BD_ANY, p_cb->scn);
+ if (status == OBX_SUCCESS)
+ {
+ p_scb->ll_cb.port.rx_mtu= p_scb0->ll_cb.port.rx_mtu;
+ p_scb->state = OBX_SS_NOT_CONNECTED;
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+** Function obx_sr_proc_evt
+** Description This is called to process BT_EVT_TO_OBX_SR_MSG
+** Process events from RFCOMM. 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_rfc_snd_msg().
+*******************************************************************************/
+void obx_sr_proc_evt(tOBX_PORT_EVT *p_evt)
+{
+ tOBX_SR_SESS_CB *p_scb;
+ BT_HDR *p_pkt;
+ tOBX_RX_HDR *p_rxh;
+ tOBX_PORT_CB *p_pcb = p_evt->p_pcb;
+
+
+ OBX_TRACE_DEBUG2("obx_sr_proc_evt handle: 0x%x, port_handle:%d", p_evt->p_pcb->handle, p_evt->p_pcb->port_handle);
+ if (p_pcb->handle == 0 || p_pcb->p_send_fn != (tOBX_SEND_FN*)obx_rfc_snd_msg)
+ return;
+
+ if ((p_scb = obx_sr_get_scb(p_pcb->handle)) == NULL)
+ {
+ /* probably already close the port and deregistered from OBX */
+ OBX_TRACE_ERROR1("Could not find control block for handle: 0x%x", p_pcb->handle);
+ return;
+ }
+
+ if (p_evt->code & PORT_EV_CONNECTED)
+ {
+ p_scb->ll_cb.port.tx_mtu = OBX_MIN_MTU;
+ obx_start_timer(&p_scb->ll_cb.comm);
+ /* Get the Bd_Addr */
+ PORT_CheckConnection (p_scb->ll_cb.port.port_handle,
+ p_scb->param.conn.peer_addr,
+ NULL);
+ memcpy(p_scb->peer_addr, p_scb->param.conn.peer_addr, BD_ADDR_LEN);
+ }
+
+ if (p_evt->code & PORT_EV_CONNECT_ERR)
+ {
+ obx_ssm_event(p_scb, OBX_PORT_CLOSE_SEVT, NULL);
+ return;
+ } /* PORT_EV_CONNECT_ERR */
+
+ if (p_evt->code & PORT_EV_RXCHAR)
+ {
+ while( (p_pkt = obx_read_data(p_pcb, obx_verify_request)) != NULL)
+ {
+ p_rxh = (tOBX_RX_HDR *)(p_pkt + 1);
+ p_pkt->event = obx_sm_evt_to_api_evt[p_rxh->sm_evt];
+ if (GKI_queue_is_empty(&p_pcb->rx_q))
+ {
+ if (p_pkt->event != OBX_BAD_SM_EVT)
+ {
+ obx_sr_proc_pkt (p_scb, p_pkt);
+ }
+ else
+ {
+ OBX_TRACE_ERROR0("bad SM event" );
+ }
+ }
+ else
+ {
+ GKI_enqueue (&p_pcb->rx_q, p_pkt);
+ if (p_pcb->rx_q.count > obx_cb.max_rx_qcount)
+ {
+ p_pcb->stopped = TRUE;
+ PORT_FlowControl(p_pcb->port_handle, FALSE);
+ }
+ }
+ } /* while a packet */
+ } /* PORT_EV_RXCHAR */
+
+ /* The server does not need to handle this event
+ */
+ if (p_evt->code & PORT_EV_TXEMPTY)
+ {
+ obx_ssm_event(p_scb, OBX_TX_EMPTY_SEVT, NULL);
+ }
+
+ if (p_evt->code & PORT_EV_FC)
+ {
+ if (p_evt->code & PORT_EV_FCS)
+ {
+ OBX_TRACE_EVENT0("sr flow control event - FCS SET ----" );
+ obx_ssm_event(p_scb, OBX_FCS_SET_SEVT, NULL);
+ }
+ } /* PORT_EV_FC */
+}
+#endif /* OBX_SERVER_INCLUDED */
+
+/*******************************************************************************
+** Function obx_open_port
+** Description Call RFCOMM_CreateConnection() to get port_handle.
+** Call PORT_SetEventCallback() with given callback.
+** Call PORT_SetEventMask() with given event mask. Return port handle.
+** Returns port handle
+*******************************************************************************/
+tOBX_STATUS obx_open_port(tOBX_PORT_CB *p_pcb, const BD_ADDR bd_addr, UINT8 scn)
+{
+ tOBX_STATUS status = OBX_SUCCESS; /* successful */
+ UINT16 port_rc;
+ BOOLEAN is_server = (p_pcb->handle & OBX_CL_HANDLE_MASK)?FALSE:TRUE;
+ UINT16 max_mtu = OBX_MAX_MTU;
+
+ OBX_TRACE_DEBUG2("obx_open_port rxmtu:%d, cbmtu:%d", p_pcb->rx_mtu, max_mtu );
+
+ /* clear buffers from previous connection */
+ obx_free_buf ((tOBX_LL_CB*)p_pcb);
+
+ /* make sure the MTU is in registered range */
+ if (p_pcb->rx_mtu > max_mtu)
+ p_pcb->rx_mtu = max_mtu;
+ if (p_pcb->rx_mtu < OBX_MIN_MTU)
+ p_pcb->rx_mtu = OBX_MIN_MTU;
+
+#if (OBX_CLIENT_INCLUDED == TRUE)
+ /* There's a remote chance that an error can occur in L2CAP before the handle
+ * before the handle can be assigned (server side only). We will save the
+ * client control block while the handle is not known */
+ if (!is_server)
+ {
+ obx_cb.p_temp_pcb = p_pcb;
+ }
+#endif
+
+ port_rc = RFCOMM_CreateConnection ( UUID_PROTOCOL_OBEX, scn,
+ is_server, (UINT16)(p_pcb->rx_mtu+1), (BD_ADDR_PTR)bd_addr,
+ &p_pcb->port_handle, obx_rfc_mgmt_cback);
+
+ OBX_TRACE_DEBUG3("obx_open_port rxmtu:%d, port_handle:%d, port.handle:0x%x",
+ p_pcb->rx_mtu, p_pcb->port_handle, p_pcb->handle );
+
+#if (OBX_CLIENT_INCLUDED == TRUE)
+ if (!is_server)
+ {
+ obx_cb.p_temp_pcb = NULL;
+ }
+#endif
+
+ if (port_rc == PORT_SUCCESS)
+ {
+ obx_cb.hdl_map[p_pcb->port_handle - 1] = p_pcb->handle;
+ PORT_SetEventCallback (p_pcb->port_handle, obx_rfc_cback);
+ PORT_SetEventMask (p_pcb->port_handle, OBX_PORT_EVENT_MASK);
+ p_pcb->p_send_fn = (tOBX_SEND_FN *)obx_rfc_snd_msg;
+ p_pcb->p_close_fn = obx_close_port;
+ }
+ else
+ {
+ status = OBX_NO_RESOURCES;
+ }
+
+ return status;
+}
+
+
+/*******************************************************************************
+** Function obx_close_port
+** Description Clear the port event mask and callback. Close the port.
+** Returns void
+*******************************************************************************/
+void obx_close_port(UINT16 port_handle)
+{
+ RFCOMM_RemoveConnection(port_handle);
+}
+
+/*******************************************************************************
+** Function obx_rfc_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_rfc_snd_msg(tOBX_PORT_CB *p_pcb)
+{
+ BOOLEAN status = FALSE;
+ UINT16 bytes_written = 0;
+
+ obx_stop_timer(&p_pcb->tle);
+ PORT_WriteData(p_pcb->port_handle, ((char*)(p_pcb->p_txmsg + 1)) + p_pcb->p_txmsg->offset,
+ p_pcb->p_txmsg->len, &bytes_written);
+
+ obx_start_timer ((tOBX_COMM_CB *)p_pcb);
+
+ if (bytes_written == p_pcb->p_txmsg->len)
+ {
+ GKI_freebuf(p_pcb->p_txmsg);
+ p_pcb->p_txmsg = NULL;
+ status = TRUE;
+ }
+ else
+ {
+ /* packet not completely written to RFCOMM */
+ p_pcb->p_txmsg->offset += bytes_written;
+ p_pcb->p_txmsg->len -= bytes_written;
+ }
+
+ return status;
+}