diff options
author | Andre Eisenbach <andre@broadcom.com> | 2012-02-22 13:18:21 -0800 |
---|---|---|
committer | Matthew Xie <mattx@google.com> | 2012-07-14 11:19:11 -0700 |
commit | e448862a47c08eb23185aaed574b39264f5005fc (patch) | |
tree | 2bc6246e3091315e77224fd798ea2fe8074ef972 /stack/rfcomm | |
parent | a2ca4b83ab8bbbfd8d5f6693e927ed4b82094624 (diff) | |
download | external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.zip external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.gz external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.bz2 |
Initial Bluedroid stack commit
Diffstat (limited to 'stack/rfcomm')
-rw-r--r-- | stack/rfcomm/port_api.c | 1525 | ||||
-rw-r--r-- | stack/rfcomm/port_int.h | 237 | ||||
-rw-r--r-- | stack/rfcomm/port_rfc.c | 1088 | ||||
-rw-r--r-- | stack/rfcomm/port_utils.c | 562 | ||||
-rw-r--r-- | stack/rfcomm/rfc_int.h | 373 | ||||
-rw-r--r-- | stack/rfcomm/rfc_l2cap_if.c | 430 | ||||
-rw-r--r-- | stack/rfcomm/rfc_mx_fsm.c | 650 | ||||
-rw-r--r-- | stack/rfcomm/rfc_port_fsm.c | 902 | ||||
-rw-r--r-- | stack/rfcomm/rfc_port_if.c | 325 | ||||
-rw-r--r-- | stack/rfcomm/rfc_ts_frames.c | 895 | ||||
-rw-r--r-- | stack/rfcomm/rfc_utils.c | 453 |
11 files changed, 7440 insertions, 0 deletions
diff --git a/stack/rfcomm/port_api.c b/stack/rfcomm/port_api.c new file mode 100644 index 0000000..fe023a4 --- /dev/null +++ b/stack/rfcomm/port_api.c @@ -0,0 +1,1525 @@ +/***************************************************************************** +** +** Name: port_api.c +** +** Description: this file contains the Serial Port API code +** +** +** Copyright (c) 1999-2009, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include <string.h> +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "port_int.h" +#include "btm_int.h" +#include "btm_api.h" +#include "rfc_int.h" +#include "l2c_api.h" +#include "sdp_api.h" + +/* duration of break in 200ms units */ +#define PORT_BREAK_DURATION 1 + +/******************************************************************************* +** +** Function RFCOMM_CreateConnection +** +** Description RFCOMM_CreateConnection function is used from the application +** to establish serial port connection to the peer device, +** or allow RFCOMM to accept a connection from the peer +** application. +** +** Parameters: scn - Service Channel Number as registered with +** the SDP (server) or obtained using SDP from +** the peer device (client). +** is_server - TRUE if requesting application is a server +** mtu - Maximum frame size the application can accept +** bd_addr - BD_ADDR of the peer (client) +** mask - specifies events to be enabled. A value +** of zero disables all events. +** p_handle - OUT pointer to the handle. +** p_mgmt_cb - pointer to callback function to receive +** connection up/down events. +** Notes: +** +** Server can call this function with the same scn parameter multiple times if +** it is ready to accept multiple simulteneous connections. +** +** DLCI for the connection is (scn * 2 + 1) if client originates connection on +** existing none initiator multiplexer channel. Otherwise it is (scn * 2). +** For the server DLCI can be changed later if client will be calling it using +** (scn * 2 + 1) dlci. +** +*******************************************************************************/ +int RFCOMM_CreateConnection (UINT16 uuid, UINT8 scn, BOOLEAN is_server, + UINT16 mtu, BD_ADDR bd_addr, UINT16 *p_handle, + tPORT_CALLBACK *p_mgmt_cb) +{ + tPORT *p_port; + int i; + UINT8 dlci; + tRFC_MCB *p_mcb = port_find_mcb (bd_addr); + UINT16 rfcomm_mtu; + + RFCOMM_TRACE_API3 ("RFCOMM_CreateConnection() called SCN: %d is_server:%d mtu:%d", + scn, is_server, mtu); + RFCOMM_TRACE_API6 ("RFCOMM_CreateConnection() BDA: %02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + + *p_handle = 0; + + if (( scn == 0 )||(scn >= PORT_MAX_RFC_PORTS )) + { + /* Server Channel Number(SCN) should be in range 1...30 */ + RFCOMM_TRACE_ERROR0 ("RFCOMM_CreateConnection - invalid SCN"); + return (PORT_INVALID_SCN); + } + + /* For client that originate connection on the existing none initiator */ + /* multiplexer channel DLCI should be odd */ + if (p_mcb && !p_mcb->is_initiator && !is_server) + dlci = (scn << 1) + 1; + else + dlci = (scn << 1); + + /* For the server side always allocate a new port. On the client side */ + /* do not allow the same (dlci, bd_addr) to be opened twice by application */ + if (!is_server && ((p_port = port_find_port (dlci, bd_addr)) != NULL)) + { + /* if existing port is also a client port */ + if (p_port->is_server == FALSE) + { + RFCOMM_TRACE_ERROR3 ("RFCOMM_CreateConnection - already opened state:%d, RFC state:%d, MCB state:%d", + p_port->state, p_port->rfc.state, p_port->rfc.p_mcb ? p_port->rfc.p_mcb->state : 0); + return (PORT_ALREADY_OPENED); + } + } + + if ((p_port = port_allocate_port (dlci, bd_addr)) == NULL) + { + RFCOMM_TRACE_WARNING0 ("RFCOMM_CreateConnection - no resources"); + return (PORT_NO_RESOURCES); + } + + p_port->default_signal_state = (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON); + + switch (uuid) + { + case UUID_PROTOCOL_OBEX: + p_port->default_signal_state = PORT_OBEX_DEFAULT_SIGNAL_STATE; + break; + case UUID_SERVCLASS_SERIAL_PORT: + p_port->default_signal_state = PORT_SPP_DEFAULT_SIGNAL_STATE; + break; + case UUID_SERVCLASS_LAN_ACCESS_USING_PPP: + p_port->default_signal_state = PORT_PPP_DEFAULT_SIGNAL_STATE; + break; + case UUID_SERVCLASS_DIALUP_NETWORKING: + case UUID_SERVCLASS_FAX: + p_port->default_signal_state = PORT_DUN_DEFAULT_SIGNAL_STATE; + break; + } + + RFCOMM_TRACE_EVENT2 ("RFCOMM_CreateConnection dlci:%d signal state:0x%x", dlci, p_port->default_signal_state); + + *p_handle = p_port->inx; + + p_port->state = PORT_STATE_OPENING; + p_port->uuid = uuid; + p_port->is_server = is_server; + p_port->scn = scn; + p_port->ev_mask = 0; + + /* If the MTU is not specified (0), keep MTU decision until the + * PN frame has to be send + * at that time connection should be established and we + * will know for sure our prefered MTU + */ + + rfcomm_mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD; + + if (mtu) + p_port->mtu = (mtu < rfcomm_mtu) ? mtu : rfcomm_mtu; + else + p_port->mtu = rfcomm_mtu; + + /* server doesn't need to release port when closing */ + if( is_server ) + { + p_port->keep_port_handle = TRUE; + + /* keep mtu that user asked, p_port->mtu could be updated during param negotiation */ + p_port->keep_mtu = p_port->mtu; + } + + p_port->local_ctrl.modem_signal = p_port->default_signal_state; + p_port->local_ctrl.fc = FALSE; + + p_port->p_mgmt_callback = p_mgmt_cb; + + for (i = 0; i < BD_ADDR_LEN; i++) + p_port->bd_addr[i] = bd_addr[i]; + + /* If this is not initiator of the connection need to just wait */ + if (p_port->is_server) + { + return (PORT_SUCCESS); + } + + /* Open will be continued after security checks are passed */ + return port_open_continue (p_port); +} + + +/******************************************************************************* +** +** Function RFCOMM_RemoveConnection +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +int RFCOMM_RemoveConnection (UINT16 handle) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("RFCOMM_RemoveConnection() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_RemoveConnection() BAD handle:%d", handle); + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + RFCOMM_TRACE_EVENT1 ("RFCOMM_RemoveConnection() Not opened:%d", handle); + return (PORT_SUCCESS); + } + + p_port->state = PORT_STATE_CLOSING; + + port_start_close (p_port); + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function RFCOMM_RemoveServer +** +** Description This function is called to close the server port. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +int RFCOMM_RemoveServer (UINT16 handle) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("RFCOMM_RemoveServer() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_RemoveServer() BAD handle:%d", handle); + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + /* Do not report any events to the client any more. */ + p_port->p_mgmt_callback = NULL; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + RFCOMM_TRACE_EVENT1 ("RFCOMM_RemoveServer() Not opened:%d", handle); + return (PORT_SUCCESS); + } + + /* this port will be deallocated after closing */ + p_port->keep_port_handle = FALSE; + p_port->state = PORT_STATE_CLOSING; + + port_start_close (p_port); + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_SetEventCallback +** +** Description This function is called to provide an address of the +** function which will be called when one of the events +** specified in the mask occures. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_callback - address of the callback function which should +** be called from the RFCOMM when an event +** specified in the mask occures. +** +** +*******************************************************************************/ +int PORT_SetEventCallback (UINT16 port_handle, tPORT_CALLBACK *p_port_cb) +{ + tPORT *p_port; + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + RFCOMM_TRACE_API1 ("PORT_SetEventCallback() handle:%d", port_handle); + + p_port->p_callback = p_port_cb; + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_SetDataCallback +** +** Description This function is when a data packet is received +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_callback - address of the callback function which should +** be called from the RFCOMM when data packet +** is received. +** +** +*******************************************************************************/ +int PORT_SetDataCallback (UINT16 port_handle, tPORT_DATA_CALLBACK *p_port_cb) +{ + tPORT *p_port; + + RFCOMM_TRACE_API2 ("PORT_SetDataCallback() handle:%d cb 0x%x", port_handle, p_port_cb); + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + p_port->p_data_callback = p_port_cb; + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_SetEventMask +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** mask - Bitmask of the events the host is interested in +** +*******************************************************************************/ +int PORT_SetEventMask (UINT16 port_handle, UINT32 mask) +{ + tPORT *p_port; + + RFCOMM_TRACE_API2 ("PORT_SetEventMask() handle:%d mask:0x%x", port_handle, mask); + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + p_port->ev_mask = mask; + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_CheckConnection +** +** Description This function returns PORT_SUCCESS if connection referenced +** by handle is up and running +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** bd_addr - OUT bd_addr of the peer +** p_lcid - OUT L2CAP's LCID +** +*******************************************************************************/ +int PORT_CheckConnection (UINT16 handle, BD_ADDR bd_addr, UINT16 *p_lcid) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_CheckConnection() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb + || !p_port->rfc.p_mcb->peer_ready + || (p_port->rfc.state != RFC_STATE_OPENED)) + { + return (PORT_LINE_ERR); + } + + memcpy (bd_addr, p_port->rfc.p_mcb->bd_addr, BD_ADDR_LEN); + if (p_lcid) + *p_lcid = p_port->rfc.p_mcb->lcid; + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_IsOpening +** +** Description This function returns TRUE if there is any RFCOMM connection +** opening in process. +** +** Parameters: TRUE if any connection opening is found +** bd_addr - bd_addr of the peer +** +*******************************************************************************/ +BOOLEAN PORT_IsOpening (BD_ADDR bd_addr) +{ + UINT8 xx, yy; + tRFC_MCB *p_mcb = NULL; + tPORT *p_port; + BOOLEAN found_port; + + /* Check for any rfc_mcb which is in the middle of opening. */ + for (xx = 0; xx < MAX_BD_CONNECTIONS; xx++) + { + if ((rfc_cb.port.rfc_mcb[xx].state > RFC_MX_STATE_IDLE) && + (rfc_cb.port.rfc_mcb[xx].state < RFC_MX_STATE_CONNECTED)) + { + memcpy (bd_addr, rfc_cb.port.rfc_mcb[xx].bd_addr, BD_ADDR_LEN); + return TRUE; + } + + if (rfc_cb.port.rfc_mcb[xx].state == RFC_MX_STATE_CONNECTED) + { + found_port = FALSE; + p_mcb = &rfc_cb.port.rfc_mcb[xx]; + p_port = &rfc_cb.port.port[0]; + + for (yy = 0; yy < MAX_RFC_PORTS; yy++, p_port++) + { + if (p_port->rfc.p_mcb == p_mcb) + { + found_port = TRUE; + break; + } + } + + if ((!found_port) || + (found_port && (p_port->rfc.state < RFC_STATE_OPENED))) + { + /* Port is not established yet. */ + memcpy (bd_addr, rfc_cb.port.rfc_mcb[xx].bd_addr, BD_ADDR_LEN); + return TRUE; + } + } + } + + return FALSE; +} + +/******************************************************************************* +** +** Function PORT_SetState +** +** Description This function configures connection according to the +** specifications in the tPORT_STATE structure. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure containing +** configuration information for the connection. +** +** +*******************************************************************************/ +int PORT_SetState (UINT16 handle, tPORT_STATE *p_settings) +{ + tPORT *p_port; + UINT8 baud_rate; + + RFCOMM_TRACE_API1 ("PORT_SetState() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + RFCOMM_TRACE_API2 ("PORT_SetState() handle:%d FC_TYPE:0x%x", handle, + p_settings->fc_type); + + baud_rate = p_port->user_port_pars.baud_rate; + p_port->user_port_pars = *p_settings; + + /* for now we've been asked to pass only baud rate */ + if (baud_rate != p_settings->baud_rate) + { + port_start_par_neg (p_port); + } + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_GetRxQueueCnt +** +** Description This function return number of buffers on the rx queue. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_rx_queue_count - Pointer to return queue count in. +** +*******************************************************************************/ +int PORT_GetRxQueueCnt (UINT16 handle, UINT16 *p_rx_queue_count) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_GetRxQueueCnt() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + *p_rx_queue_count = p_port->rx.queue_size; + + RFCOMM_TRACE_API2 ("PORT_GetRxQueueCnt() p_rx_queue_count:%d, p_port->rx.queue.count = %d", + *p_rx_queue_count, p_port->rx.queue_size); + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_GetState +** +** Description This function is called to fill tPORT_STATE structure +** with the curremt control settings for the port +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure in which +** configuration information is returned. +** +*******************************************************************************/ +int PORT_GetState (UINT16 handle, tPORT_STATE *p_settings) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_GetState() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + *p_settings = p_port->user_port_pars; + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Control +** +** Description This function directs a specified connection to pass control +** control information to the peer device. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** signal = specify the function to be passed +** +*******************************************************************************/ +int PORT_Control (UINT16 handle, UINT8 signal) +{ + tPORT *p_port; + UINT8 old_modem_signal; + + RFCOMM_TRACE_API2 ("PORT_Control() handle:%d signal:0x%x", handle, signal); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + old_modem_signal = p_port->local_ctrl.modem_signal; + p_port->local_ctrl.break_signal = 0; + + switch (signal) + { + case PORT_SET_CTSRTS: + p_port->local_ctrl.modem_signal |= PORT_CTSRTS_ON; + break; + + case PORT_CLR_CTSRTS: + p_port->local_ctrl.modem_signal &= ~PORT_CTSRTS_ON; + break; + + case PORT_SET_DTRDSR: + p_port->local_ctrl.modem_signal |= PORT_DTRDSR_ON; + break; + + case PORT_CLR_DTRDSR: + p_port->local_ctrl.modem_signal &= ~PORT_DTRDSR_ON; + break; + + case PORT_SET_RI: + p_port->local_ctrl.modem_signal |= PORT_RING_ON; + break; + + case PORT_CLR_RI: + p_port->local_ctrl.modem_signal &= ~PORT_RING_ON; + break; + + case PORT_SET_DCD: + p_port->local_ctrl.modem_signal |= PORT_DCD_ON; + break; + + case PORT_CLR_DCD: + p_port->local_ctrl.modem_signal &= ~PORT_DCD_ON; + break; + } + + if (signal == PORT_BREAK) + p_port->local_ctrl.break_signal = PORT_BREAK_DURATION; + else if (p_port->local_ctrl.modem_signal == old_modem_signal) + return (PORT_SUCCESS); + + port_start_control (p_port); + + RFCOMM_TRACE_EVENT4 ("PORT_Control DTR_DSR : %d, RTS_CTS : %d, RI : %d, DCD : %d", + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DTRDSR) ? 1 : 0), + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RTSCTS) ? 1 : 0), + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RI) ? 1 : 0), + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DCD) ? 1 : 0)); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_FlowControl +** +** Description This function directs a specified connection to pass +** flow control message to the peer device. Enable flag passed +** shows if port can accept more data. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** enable - enables data flow +** +*******************************************************************************/ +int PORT_FlowControl (UINT16 handle, BOOLEAN enable) +{ + tPORT *p_port; + BOOLEAN old_fc; + UINT32 events; + + RFCOMM_TRACE_API2 ("PORT_FlowControl() handle:%d enable: %d", handle, enable); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb) + { + return (PORT_NOT_OPENED); + } + + p_port->rx.user_fc = !enable; + + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + { + if (!p_port->rx.user_fc) + { + port_flow_control_peer(p_port, TRUE, 0); + } + } + else + { + old_fc = p_port->local_ctrl.fc; + + /* FC is set if user is set or peer is set */ + p_port->local_ctrl.fc = (p_port->rx.user_fc | p_port->rx.peer_fc); + + if (p_port->local_ctrl.fc != old_fc) + port_start_control (p_port); + } + + /* Need to take care of the case when we could not deliver events */ + /* to the application because we were flow controlled */ + if (enable && (p_port->rx.queue_size != 0)) + { + events = PORT_EV_RXCHAR; + if (p_port->rx_flag_ev_pending) + { + p_port->rx_flag_ev_pending = FALSE; + events |= PORT_EV_RXFLAG; + } + + events &= p_port->ev_mask; + if (p_port->p_callback && events) + { + p_port->p_callback (events, p_port->inx); + } + } + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_GetModemStatus +** +** Description This function retrieves modem control signals. Normally +** application will call this function after a callback +** function is called with notification that one of signals +** has been changed. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_signal - specify the pointer to control signals info +** +*******************************************************************************/ +int PORT_GetModemStatus (UINT16 handle, UINT8 *p_signal) +{ + tPORT *p_port; + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + *p_signal = p_port->peer_ctrl.modem_signal; + + RFCOMM_TRACE_API2 ("PORT_GetModemStatus() handle:%d signal:%x", handle, *p_signal); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_ClearError +** +** Description This function retreives information about a communications +** error and reports current status of a connection. The +** function should be called when an error occures to clear +** the connection error flag and to enable additional read +** and write operations. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_errors - pointer of the variable to receive error codes +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ +int PORT_ClearError (UINT16 handle, UINT16 *p_errors, tPORT_STATUS *p_status) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_ClearError() handle:%d", handle); + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + *p_errors = p_port->line_status; + + /* This is the only call to clear error status. We can not clear */ + /* connection failed status. To clean it port should be closed and reopened */ + p_port->line_status = (p_port->line_status & LINE_STATUS_FAILED); + + PORT_GetQueueStatus (handle, p_status); + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_SendError +** +** Description This function send a communications error to the peer device +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** errors - receive error codes +** +*******************************************************************************/ +int PORT_SendError (UINT16 handle, UINT8 errors) +{ + tPORT *p_port; + + RFCOMM_TRACE_API2 ("PORT_SendError() handle:%d errors:0x%x", handle, errors); + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb) + { + return (PORT_NOT_OPENED); + } + + RFCOMM_LineStatusReq (p_port->rfc.p_mcb, p_port->dlci, errors); + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_GetQueueStatus +** +** Description This function reports current status of a connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ +int PORT_GetQueueStatus (UINT16 handle, tPORT_STATUS *p_status) +{ + tPORT *p_port; + + /* RFCOMM_TRACE_API1 ("PORT_GetQueueStatus() handle:%d", handle); */ + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + p_status->in_queue_size = (UINT16) p_port->rx.queue_size; + p_status->out_queue_size = (UINT16) p_port->tx.queue_size; + + p_status->mtu_size = (UINT16) p_port->peer_mtu; + + p_status->flags = 0; + + if (!(p_port->peer_ctrl.modem_signal & PORT_CTSRTS_ON)) + p_status->flags |= PORT_FLAG_CTS_HOLD; + + if (!(p_port->peer_ctrl.modem_signal & PORT_DTRDSR_ON)) + p_status->flags |= PORT_FLAG_DSR_HOLD; + + if (!(p_port->peer_ctrl.modem_signal & PORT_DCD_ON)) + p_status->flags |= PORT_FLAG_RLSD_HOLD; + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Purge +** +** Description This function discards all the data from the output or +** input queues of the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** purge_flags - specify the action to take. +** +*******************************************************************************/ +int PORT_Purge (UINT16 handle, UINT8 purge_flags) +{ + tPORT *p_port; + BT_HDR *p_buf; + UINT16 count; + UINT32 events; + + RFCOMM_TRACE_API2 ("PORT_Purge() handle:%d flags:0x%x", handle, purge_flags); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (purge_flags & PORT_PURGE_RXCLEAR) + { + PORT_SCHEDULE_LOCK; /* to prevent missing credit */ + + count = p_port->rx.queue.count; + + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->rx.queue)) != NULL) + GKI_freebuf (p_buf); + + p_port->rx.queue_size = 0; + + PORT_SCHEDULE_UNLOCK; + + /* If we flowed controlled peer based on rx_queue size enable data again */ + if (count) + port_flow_control_peer (p_port, TRUE, count); + } + + if (purge_flags & PORT_PURGE_TXCLEAR) + { + PORT_SCHEDULE_LOCK; /* to prevent tx.queue_size from being negative */ + + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->tx.queue)) != NULL) + GKI_freebuf (p_buf); + + p_port->tx.queue_size = 0; + + PORT_SCHEDULE_UNLOCK; + + events = PORT_EV_TXEMPTY; + + events |= port_flow_control_user (p_port); + + events &= p_port->ev_mask; + + if ((p_port->p_callback != NULL) && events) + (p_port->p_callback)(events, p_port->inx); + } + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_ReadData +** +** Description Normally not GKI aware application will call this function +** after receiving PORT_EV_RXCHAR event. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +*******************************************************************************/ +int PORT_ReadData (UINT16 handle, char *p_data, UINT16 max_len, UINT16 *p_len) +{ + tPORT *p_port; + BT_HDR *p_buf; + UINT16 count; + + RFCOMM_TRACE_API2 ("PORT_ReadData() handle:%d max_len:%d", handle, max_len); + + /* Initialize this in case of an error */ + *p_len = 0; + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + p_buf = (BT_HDR *)GKI_getfirst (&p_port->rx.queue); + if (!p_buf) + return (PORT_SUCCESS); + + count = 0; + + while (max_len && p_buf) + { + if (p_buf->len > max_len) + { + memcpy (p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, max_len); + p_buf->offset += max_len; + p_buf->len -= max_len; + + *p_len += max_len; + + PORT_SCHEDULE_LOCK; + + p_port->rx.queue_size -= max_len; + + PORT_SCHEDULE_UNLOCK; + + break; + } + else + { + memcpy (p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + + *p_len += p_buf->len; + max_len -= p_buf->len; + + PORT_SCHEDULE_LOCK; + + p_port->rx.queue_size -= p_buf->len; + + if (max_len) + { + p_data += p_buf->len; + p_buf = (BT_HDR *)GKI_getnext (p_buf); + } + + GKI_freebuf (GKI_dequeue (&p_port->rx.queue)); + + PORT_SCHEDULE_UNLOCK; + + count++; + } + } + + if (*p_len == 1) + { + RFCOMM_TRACE_EVENT3 ("PORT_ReadData queue:%d returned:%d %x", p_port->rx.queue_size, *p_len, (p_data[0])); + } + else + { + RFCOMM_TRACE_EVENT2 ("PORT_ReadData queue:%d returned:%d", p_port->rx.queue_size, *p_len); + } + + /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ + /* check if it can be resumed now */ + port_flow_control_peer (p_port, TRUE, count); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Read +** +** Description Normally application will call this function after receiving +** PORT_EV_RXCHAR event. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** pp_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +int PORT_Read (UINT16 handle, BT_HDR **pp_buf) +{ + tPORT *p_port; + BT_HDR *p_buf; + + RFCOMM_TRACE_API1 ("PORT_Read() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + PORT_SCHEDULE_LOCK; + + p_buf = (BT_HDR *)GKI_dequeue (&p_port->rx.queue); + if (p_buf) + { + p_port->rx.queue_size -= p_buf->len; + + PORT_SCHEDULE_UNLOCK; + + /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ + /* check if it can be resumed now */ + port_flow_control_peer (p_port, TRUE, 1); + } + else + { + PORT_SCHEDULE_UNLOCK; + } + + *pp_buf = p_buf; + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function port_write +** +** Description This function when a data packet is received from the apper +** layer task. +** +** Parameters: p_port - pointer to address of port control block +** p_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +static int port_write (tPORT *p_port, BT_HDR *p_buf) +{ + /* We should not allow to write data in to server port when connection is not opened */ + if (p_port->is_server && (p_port->rfc.state != RFC_STATE_OPENED)) + { + GKI_freebuf (p_buf); + return (PORT_CLOSED); + } + + /* Keep the data in pending queue if peer does not allow data, or */ + /* Peer is not ready or Port is not yet opened or initial port control */ + /* command has not been sent */ + if (p_port->tx.peer_fc + || !p_port->rfc.p_mcb + || !p_port->rfc.p_mcb->peer_ready + || (p_port->rfc.state != RFC_STATE_OPENED) + || ((p_port->port_ctrl & (PORT_CTRL_REQ_SENT | PORT_CTRL_IND_RECEIVED)) != + (PORT_CTRL_REQ_SENT | PORT_CTRL_IND_RECEIVED))) + { + if ((p_port->tx.queue_size > PORT_TX_CRITICAL_WM) + || (p_port->tx.queue.count > PORT_TX_BUF_CRITICAL_WM)) + { + RFCOMM_TRACE_WARNING1 ("PORT_Write: Queue size: %d", + p_port->tx.queue_size); + + GKI_freebuf (p_buf); + + if ((p_port->p_callback != NULL) && (p_port->ev_mask & PORT_EV_ERR)) + p_port->p_callback (PORT_EV_ERR, p_port->inx); + + return (PORT_TX_FULL); + } + + RFCOMM_TRACE_EVENT4 ("PORT_Write : Data is enqued. flow disabled %d peer_ready %d state %d ctrl_state %x", + p_port->tx.peer_fc, + (p_port->rfc.p_mcb && p_port->rfc.p_mcb->peer_ready), + p_port->rfc.state, + p_port->port_ctrl); + + GKI_enqueue (&p_port->tx.queue, p_buf); + p_port->tx.queue_size += p_buf->len; + + return (PORT_CMD_PENDING); + } + else + { + RFCOMM_TRACE_EVENT0 ("PORT_Write : Data is being sent"); + + RFCOMM_DataReq (p_port->rfc.p_mcb, p_port->dlci, p_buf); + return (PORT_SUCCESS); + } +} + +/******************************************************************************* +** +** Function PORT_Write +** +** Description This function when a data packet is received from the apper +** layer task. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** pp_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +int PORT_Write (UINT16 handle, BT_HDR *p_buf) +{ + tPORT *p_port; + UINT32 event = 0; + int rc; + + RFCOMM_TRACE_API1 ("PORT_Write() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + GKI_freebuf (p_buf); + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + GKI_freebuf (p_buf); + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + RFCOMM_TRACE_WARNING1 ("PORT_Write: Data dropped line_status:0x%x", + p_port->line_status); + GKI_freebuf (p_buf); + return (PORT_LINE_ERR); + } + + rc = port_write (p_port, p_buf); + event |= port_flow_control_user (p_port); + + switch (rc) + { + case PORT_TX_FULL: + event |= PORT_EV_ERR; + break; + + case PORT_SUCCESS: + event |= (PORT_EV_TXCHAR | PORT_EV_TXEMPTY); + break; + } + /* Mask out all events that are not of interest to user */ + event &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && event) + (p_port->p_callback)(event, p_port->inx); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_WriteData +** +** Description Normally not GKI aware application will call this function +** to send data to the port. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +*******************************************************************************/ +int PORT_WriteData (UINT16 handle, char *p_data, UINT16 max_len, UINT16 *p_len) +{ + tPORT *p_port; + BT_HDR *p_buf; + UINT32 event = 0; + int rc = 0; + UINT16 length; + + RFCOMM_TRACE_API1 ("PORT_WriteData() max_len:%d", max_len); + + *p_len = 0; + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + RFCOMM_TRACE_WARNING1 ("PORT_WriteData() no port state:%d", p_port->state); + return (PORT_NOT_OPENED); + } + + if (!max_len || !p_port->peer_mtu) + { + RFCOMM_TRACE_ERROR1 ("PORT_WriteData() peer_mtu:%d", p_port->peer_mtu); + return (PORT_UNKNOWN_ERROR); + } + + /* Length for each buffer is the smaller of GKI buffer, peer MTU, or max_len */ + length = RFCOMM_DATA_POOL_BUF_SIZE - + (UINT16)(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + RFCOMM_DATA_OVERHEAD); + + /* If there are buffers scheduled for transmission check if requested */ + /* data fits into the end of the queue */ + PORT_SCHEDULE_LOCK; + + if (((p_buf = (BT_HDR *)p_port->tx.queue.p_last) != NULL) + && ((p_buf->len + max_len) <= p_port->peer_mtu) + && ((p_buf->len + max_len) <= length)) + { + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len, p_data, max_len); + p_port->tx.queue_size += max_len; + + *p_len = max_len; + p_buf->len += max_len; + + PORT_SCHEDULE_UNLOCK; + + return (PORT_SUCCESS); + } + + PORT_SCHEDULE_UNLOCK; + + while (max_len) + { + /* if we're over buffer high water mark, we're done */ + if ((p_port->tx.queue_size > PORT_TX_HIGH_WM) + || (p_port->tx.queue.count > PORT_TX_BUF_HIGH_WM)) + break; + + /* continue with rfcomm data write */ + p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_DATA_POOL_ID); + if (!p_buf) + break; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET; + p_buf->layer_specific = handle; + + if (p_port->peer_mtu < length) + length = p_port->peer_mtu; + if (max_len < length) + length = max_len; + p_buf->len = length; + p_buf->event = BT_EVT_TO_BTU_SP_DATA; + + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, length); + + RFCOMM_TRACE_EVENT1 ("PORT_WriteData %d bytes", length); + + rc = port_write (p_port, p_buf); + + /* If queue went below the threashold need to send flow control */ + event |= port_flow_control_user (p_port); + + if (rc == PORT_SUCCESS) + event |= PORT_EV_TXCHAR; + + if ((rc != PORT_SUCCESS) && (rc != PORT_CMD_PENDING)) + break; + + *p_len += length; + max_len -= length; + p_data += length; + + } + if (!max_len && (rc != PORT_CMD_PENDING) && (rc != PORT_TX_QUEUE_DISABLED)) + event |= PORT_EV_TXEMPTY; + + /* Mask out all events that are not of interest to user */ + event &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && event) + (p_port->p_callback)(event, p_port->inx); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Test +** +** Description Application can call this function to send RFCOMM Test frame +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** +*******************************************************************************/ +int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len) +{ + BT_HDR *p_buf; + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_Test() len:%d", len); + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (len > ((p_port->mtu == 0) ? RFCOMM_DEFAULT_MTU : p_port->mtu)) + { + return (PORT_UNKNOWN_ERROR); + } + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) != NULL) + { + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2; + p_buf->len = len; + + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, p_buf->len); + + rfc_send_test (p_port->rfc.p_mcb, TRUE, p_buf); + return (PORT_SUCCESS); + } + else + { + return (PORT_NO_MEM); + } +} + +/******************************************************************************* +** +** Function RFCOMM_Init +** +** Description This function is called to initialize RFCOMM layer +** +*******************************************************************************/ +void RFCOMM_Init (void) +{ + memset (&rfc_cb, 0, sizeof (tRFC_CB)); /* Init RFCOMM control block */ + + rfc_cb.rfc.last_mux = MAX_BD_CONNECTIONS; + +#if defined(RFCOMM_INITIAL_TRACE_LEVEL) + rfc_cb.trace_level = RFCOMM_INITIAL_TRACE_LEVEL; +#else + rfc_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + + rfcomm_l2cap_if_init (); +} + +/******************************************************************************* +** +** Function PORT_SetTraceLevel +** +** Description This function sets the trace level for RFCOMM. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 PORT_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + rfc_cb.trace_level = new_level; + + return (rfc_cb.trace_level); +} + diff --git a/stack/rfcomm/port_int.h b/stack/rfcomm/port_int.h new file mode 100644 index 0000000..acd56b7 --- /dev/null +++ b/stack/rfcomm/port_int.h @@ -0,0 +1,237 @@ +/*****************************************************************************/ +/* */ +/* Name: port_int.h */ +/* */ +/* Description: This file contains definitions internal to the PORT unit */ +/* */ +/* */ +/* Copyright (c) 1999-2009, Broadcom Corp., All Rights Reserved. */ +/* Broadcom Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ +#ifndef PORT_INT_H +#define PORT_INT_H + +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" + +/* Local events passed when application event is sent from the api to PORT */ +/* ???*/ +#define PORT_EVENT_OPEN (1 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_CONTROL (2 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_SET_STATE (3 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_SET_CALLBACK (5 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_WRITE (6 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_PURGE (7 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_SEND_ERROR (8 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_FLOW_CONTROL (9 | BT_EVT_TO_BTU_SP_EVT) + +/* +** Flow control configuration values for the mux +*/ +#define PORT_FC_UNDEFINED 0 /* mux flow control mechanism not defined yet */ +#define PORT_FC_TS710 1 /* use TS 07.10 flow control */ +#define PORT_FC_CREDIT 2 /* use RFCOMM credit based flow control */ + +/* +** Define Port Data Transfere control block +*/ +typedef struct +{ + BUFFER_Q queue; /* Queue of buffers waiting to be sent */ + BOOLEAN peer_fc; /* TRUE if flow control is set based on peer's request */ + BOOLEAN user_fc; /* TRUE if flow control is set based on user's request */ + UINT32 queue_size; /* Number of data bytes in the queue */ + tPORT_CALLBACK *p_callback; /* Address of the callback function */ +} tPORT_DATA; + +/* +** Port control structure used to pass modem info +*/ +typedef struct +{ +#define MODEM_SIGNAL_DTRDSR 0x01 +#define MODEM_SIGNAL_RTSCTS 0x02 +#define MODEM_SIGNAL_RI 0x04 +#define MODEM_SIGNAL_DCD 0x08 + + UINT8 modem_signal; /* [DTR/DSR | RTS/CTS | RI | DCD ] */ + + UINT8 break_signal; /* 0-3 s in steps of 200 ms */ + + UINT8 discard_buffers; /* 0 - do not discard, 1 - discard */ + +#define RFCOMM_CTRL_BREAK_ASAP 0 +#define RFCOMM_CTRL_BREAK_IN_SEQ 1 + + UINT8 break_signal_seq; /* as soon as possible | in sequence (default) */ + + BOOLEAN fc; /* TRUE when the device is unable to accept frames */ +} tPORT_CTRL; + + +/* +** RFCOMM multiplexer Control Block +*/ +typedef struct +{ + TIMER_LIST_ENT tle; /* Timer list entry */ + BUFFER_Q cmd_q; /* Queue for command messages on this mux */ + UINT8 port_inx[RFCOMM_MAX_DLCI + 1]; /* Array for quick access to */ + /* tPORT based on dlci */ + BD_ADDR bd_addr; /* BD ADDR of the peer if initiator */ + UINT16 lcid; /* Local cid used for this channel */ + UINT16 peer_l2cap_mtu; /* Max frame that can be sent to peer L2CAP */ + UINT8 state; /* Current multiplexer channel state */ + UINT8 is_initiator; /* TRUE if this side sends SABME (dlci=0) */ + BOOLEAN local_cfg_sent; + BOOLEAN peer_cfg_rcvd; + BOOLEAN restart_required; /* TRUE if has to restart channel after disc */ + BOOLEAN peer_ready; /* True if other side can accept frames */ + UINT8 flow; /* flow control mechanism for this mux */ + BOOLEAN l2cap_congested; /* TRUE if L2CAP is congested */ + BOOLEAN is_disc_initiator; /* TRUE if initiated disc of port */ + UINT16 pending_lcid; /* store LCID for incoming connection while connecting */ + UINT8 pending_id; /* store l2cap ID for incoming connection while connecting */ +} tRFC_MCB; + + +/* +** RFCOMM Port Connection Control Block +*/ +struct t_rfc_port +{ +#define RFC_PORT_STATE_IDLE 0 +#define RFC_PORT_STATE_WAIT_START 1 +#define RFC_PORT_STATE_OPENING 2 +#define RFC_PORT_STATE_OPENED 3 +#define RFC_PORT_STATE_CLOSING 4 + + UINT8 state; /* Current state of the connection */ + +#define RFC_RSP_PN 0x01 +#define RFC_RSP_RPN_REPLY 0x02 +#define RFC_RSP_RPN 0x04 +#define RFC_RSP_MSC 0x08 +#define RFC_RSP_RLS 0x10 + + UINT8 expected_rsp; + + tRFC_MCB *p_mcb; + + TIMER_LIST_ENT tle; /* Timer list entry */ +}; +typedef struct t_rfc_port tRFC_PORT; + + +/* +** Define control block containing information about PORT connection +*/ +struct t_port_info +{ + UINT8 inx; /* Index of this control block in the port_info array */ + BOOLEAN in_use; /* True when structure is allocated */ + +#define PORT_STATE_CLOSED 0 +#define PORT_STATE_OPENING 1 +#define PORT_STATE_OPENED 2 +#define PORT_STATE_CLOSING 3 + + UINT8 state; /* State of the application */ + + UINT8 scn; /* Service channel number */ + UINT16 uuid; /* Service UUID */ + + BD_ADDR bd_addr; /* BD ADDR of the device for the multiplexer channel */ + BOOLEAN is_server; /* TRUE if the server application */ + UINT8 dlci; /* DLCI of the connection */ + + UINT8 error; /* Last error detected */ + + UINT8 line_status; /* Line status as reported by peer */ + + UINT8 default_signal_state; /* Initial signal state depending on uuid */ + + UINT16 mtu; /* Max MTU that port can receive */ + UINT16 peer_mtu; /* Max MTU that port can send */ + + tPORT_DATA tx; /* Control block for data from app to peer */ + tPORT_DATA rx; /* Control block for data from peer to app */ + + tPORT_STATE user_port_pars; /* Port parameters for user connection */ + tPORT_STATE peer_port_pars; /* Port parameters for user connection */ + + tPORT_CTRL local_ctrl; + tPORT_CTRL peer_ctrl; + +#define PORT_CTRL_REQ_SENT 0x01 +#define PORT_CTRL_REQ_CONFIRMED 0x02 +#define PORT_CTRL_IND_RECEIVED 0x04 +#define PORT_CTRL_IND_RESPONDED 0x08 + + UINT8 port_ctrl; /* Modem Status Command */ + + BOOLEAN rx_flag_ev_pending; /* RXFLAG Character is received */ + + tRFC_PORT rfc; /* RFCOMM port control block */ + + UINT32 ev_mask; /* Event mask for the callback */ + tPORT_CALLBACK *p_callback; /* Pointer to users callback function */ + tPORT_CALLBACK *p_mgmt_callback; /* Callback function to receive connection up/down */ + tPORT_DATA_CALLBACK *p_data_callback; /* Callback function to receive data indications */ + UINT16 credit_tx; /* Flow control credits for tx path */ + UINT16 credit_rx; /* Flow control credits for rx path, this is */ + /* number of buffers peer is allowed to sent */ + UINT16 credit_rx_max; /* Max number of credits we will allow this guy to sent */ + UINT16 credit_rx_low; /* Number of credits when we send credit update */ + UINT16 rx_buf_critical; /* port receive queue critical watermark level */ + BOOLEAN keep_port_handle; /* TRUE if port is not deallocated when closing */ + /* it is set to TRUE for server when allocating port */ + UINT16 keep_mtu; /* Max MTU that port can receive by server */ +}; +typedef struct t_port_info tPORT; + + +/* Define the PORT/RFCOMM control structure +*/ +typedef struct +{ + tPORT port[MAX_RFC_PORTS]; /* Port info pool */ + tRFC_MCB rfc_mcb[MAX_BD_CONNECTIONS]; /* RFCOMM bd_connections pool */ +} tPORT_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Functions provided by the port_utils.c +*/ +extern tPORT *port_allocate_port (UINT8 dlci, BD_ADDR bd_addr); +extern void port_set_defaults (tPORT *p_port); +extern void port_select_mtu (tPORT *p_port); +extern void port_release_port (tPORT *p_port); +extern tPORT *port_find_mcb_dlci_port (tRFC_MCB *p_mcb, UINT8 dlci); +extern tRFC_MCB *port_find_mcb (BD_ADDR bd_addr); +extern tPORT *port_find_dlci_port (UINT8 dlci); +extern tPORT *port_find_port (UINT8 dlci, BD_ADDR bd_addr); +extern UINT32 port_get_signal_changes (tPORT *p_port, UINT8 old_signals, UINT8 signal); +extern UINT32 port_flow_control_user (tPORT *p_port); +extern void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count); + +/* +** Functions provided by the port_rfc.c +*/ +extern int port_open_continue (tPORT *p_port); +extern void port_start_port_open (tPORT *p_port); +extern void port_start_par_neg (tPORT *p_port); +extern void port_start_control (tPORT *p_port); +extern void port_start_close (tPORT *p_port); +extern void port_rfc_closed (tPORT *p_port, UINT8 res); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stack/rfcomm/port_rfc.c b/stack/rfcomm/port_rfc.c new file mode 100644 index 0000000..c4ac929 --- /dev/null +++ b/stack/rfcomm/port_rfc.c @@ -0,0 +1,1088 @@ +/***************************************************************************** +** +** Name: port_rfc.c +** +** Description: This module contains functions for port emulation entity +** and RFCOMM communications +** +** +** Copyright (c) 1999-2009, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ +#include <string.h> + +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "btm_int.h" +#include "btm_api.h" +#include "port_int.h" +#include "rfc_int.h" + +/* +** Local function definitions +*/ +UINT32 port_rfc_send_tx_data (tPORT *p_port); +void port_rfc_closed (tPORT *p_port, UINT8 res); +void port_get_credits (tPORT *p_port, UINT8 k); + + +/******************************************************************************* +** +** Function port_open_continue +** +** Description This function is called after security manager completes +** required security checks. +** +** Returns void +** +*******************************************************************************/ +int port_open_continue (tPORT *p_port) +{ + tRFC_MCB *p_mcb; + + RFCOMM_TRACE_EVENT0 ("port_open_continue"); + + /* Check if multiplexer channel has already been established */ + if ((p_mcb = rfc_alloc_multiplexer_channel (p_port->bd_addr, TRUE)) == NULL) + { + RFCOMM_TRACE_WARNING0 ("port_open_continue no mx channel"); + port_release_port (p_port); + return (PORT_NO_RESOURCES); + } + + p_port->rfc.p_mcb = p_mcb; + + p_mcb->port_inx[p_port->dlci] = p_port->inx; + + /* Connection is up and we know local and remote features, select MTU */ + port_select_mtu (p_port); + + if (p_mcb->state == RFC_MX_STATE_CONNECTED) + { + RFCOMM_ParNegReq (p_mcb, p_port->dlci, p_port->mtu); + } + else if ((p_mcb->state == RFC_MX_STATE_IDLE) + ||(p_mcb->state == RFC_MX_STATE_DISC_WAIT_UA)) + { + /* In RFC_MX_STATE_IDLE state, MX state machine will create connection */ + /* In RFC_MX_STATE_DISC_WAIT_UA state, MX state machine will recreate connection */ + /* after disconnecting is completed */ + RFCOMM_StartReq (p_mcb); + } + else + { + /* MX state machine ignores RFC_MX_EVENT_START_REQ in these states */ + /* When it enters RFC_MX_STATE_CONNECTED, it will check any openning ports */ + RFCOMM_TRACE_DEBUG1 ("port_open_continue: mx state(%d) mx channel is openning", p_mcb->state); + } + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function port_start_control +** +** Description This function is called in the BTU_TASK context to +** send control information +** +** Returns void +** +*******************************************************************************/ +void port_start_control (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + if (p_mcb == NULL) + return; + + RFCOMM_ControlReq (p_mcb, p_port->dlci, &p_port->local_ctrl); +} + + +/******************************************************************************* +** +** Function port_start_par_neg +** +** Description This function is called in the BTU_TASK context to +** send configuration information +** +** Returns void +** +*******************************************************************************/ +void port_start_par_neg (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + if (p_mcb == NULL) + return; + + RFCOMM_PortNegReq (p_mcb, p_port->dlci, &p_port->user_port_pars); +} + + +/******************************************************************************* +** +** Function port_start_close +** +** Description This function is called in the BTU_TASK context to +** release DLC +** +** Returns void +** +*******************************************************************************/ +void port_start_close (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + UINT8 old_signals; + UINT32 events = 0; + + /* At first indicate to the user that signals on the connection were dropped */ + p_port->line_status |= LINE_STATUS_FAILED; + old_signals = p_port->peer_ctrl.modem_signal; + + p_port->peer_ctrl.modem_signal &= ~(PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON); + + events |= port_get_signal_changes (p_port, old_signals, p_port->peer_ctrl.modem_signal); + + if(p_port->ev_mask & PORT_EV_CONNECT_ERR) + events |= PORT_EV_CONNECT_ERR; + + if(p_port->ev_mask & PORT_EV_ERR) + events |= PORT_EV_ERR; + + if ((p_port->p_callback != NULL) && events) + p_port->p_callback (events, p_port->inx); + + + /* Check if RFCOMM side has been closed while the message was queued */ + if ((p_mcb == NULL) || (p_port->rfc.state == RFC_STATE_CLOSED)) + { + /* Call management callback function before calling port_release_port() to clear tPort */ + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (PORT_CLOSED, p_port->inx); + + port_release_port (p_port); + } + else + { + RFCOMM_DlcReleaseReq (p_mcb, p_port->dlci); + } +} + + +/******************************************************************************* +** +** Function PORT_StartCnf +** +** Description This function is called from the RFCOMM layer when +** establishing of the multiplexer channel is completed. +** Continue establishing of the connection for all ports that +** are in the OPENING state +** +*******************************************************************************/ +void PORT_StartCnf (tRFC_MCB *p_mcb, UINT16 result) +{ + tPORT *p_port; + int i; + BOOLEAN no_ports_up = TRUE; + + RFCOMM_TRACE_EVENT1 ("PORT_StartCnf result:%d", result); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) + { + if (p_port->rfc.p_mcb == p_mcb) + { + no_ports_up = FALSE; + + if (result == RFCOMM_SUCCESS) + RFCOMM_ParNegReq (p_mcb, p_port->dlci, p_port->mtu); + else + { + RFCOMM_TRACE_WARNING1 ("PORT_StartCnf failed result:%d", result); + + /* Warning: result is also set to 4 when l2cap connection + fails due to l2cap connect cnf (no_resources) */ + if( result == HCI_ERR_PAGE_TIMEOUT ) + p_port->error = PORT_PAGE_TIMEOUT; + else + p_port->error = PORT_START_FAILED; + + rfc_release_multiplexer_channel (p_mcb); + p_port->rfc.p_mcb = NULL; + + /* Send event to the application */ + if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECT_ERR)) + (p_port->p_callback)(PORT_EV_CONNECT_ERR, p_port->inx); + + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (PORT_START_FAILED, p_port->inx); + + port_release_port (p_port); + } + } + } + + /* There can be a situation when after starting connection, user closes the */ + /* port, we can catch it here to close multiplexor channel */ + if (no_ports_up) + { + rfc_check_mcb_active (p_mcb); + } +} + + +/******************************************************************************* +** +** Function PORT_StartInd +** +** Description This function is called from the RFCOMM layer when +** some peer device wants to establish a multiplexer +** connection. Check if there are any ports open with this +** or not assigned multiplexer. +** +*******************************************************************************/ +void PORT_StartInd (tRFC_MCB *p_mcb) +{ + tPORT *p_port; + int i; + + RFCOMM_TRACE_EVENT0 ("PORT_StartInd"); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) + { + if ((p_port->rfc.p_mcb == NULL) + || (p_port->rfc.p_mcb == p_mcb)) + { + RFCOMM_StartRsp (p_mcb, RFCOMM_SUCCESS); + return; + } + } + RFCOMM_StartRsp (p_mcb, RFCOMM_ERROR); +} + + +/******************************************************************************* +** +** Function PORT_ParNegInd +** +** Description This function is called from the RFCOMM layer to change +** DLCI parameters (currently only MTU is negotiated). +** If can not find the port do not accept the request. +** Otherwise save the MTU size supported by the peer. +** +*******************************************************************************/ +void PORT_ParNegInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT8 our_cl; + UINT8 our_k; + + RFCOMM_TRACE_EVENT2 ("PORT_ParNegInd dlci:%d mtu:%d", dlci, mtu); + + if (!p_port) + { + /* This can be a first request for this port */ + p_port = port_find_dlci_port (dlci); + if (!p_port) + { + /* If the port cannot be opened, send a DM. Per Errata 1205 */ + rfc_send_dm(p_mcb, dlci, FALSE); + /* check if this is the last port open, some headsets have + problem, they don't disconnect if we send DM */ + rfc_check_mcb_active( p_mcb ); + RFCOMM_TRACE_EVENT0( "PORT_ParNegInd: port not found" ); + return; + } + p_mcb->port_inx[dlci] = p_port->inx; + } + + memcpy (p_port->bd_addr, p_mcb->bd_addr, BD_ADDR_LEN); + + /* Connection is up and we know local and remote features, select MTU */ + port_select_mtu (p_port); + + p_port->rfc.p_mcb = p_mcb; + p_port->mtu = (p_port->mtu < mtu) ? p_port->mtu : mtu; + p_port->peer_mtu = p_port->mtu; + + /* Negotiate the flow control mechanism. If flow control mechanism for */ + /* mux has not been set yet, set it now. If either we or peer wants TS 07.10, */ + /* use that. Otherwise both must want credit based, so use that. If flow is */ + /* already defined for this mux, we respond with that value. */ + if (p_mcb->flow == PORT_FC_UNDEFINED) + { + if ((PORT_FC_DEFAULT == PORT_FC_TS710) || (cl == RFCOMM_PN_CONV_LAYER_TYPE_1)) + { + p_mcb->flow = PORT_FC_TS710; + } + else + { + p_mcb->flow = PORT_FC_CREDIT; + } + } + + /* Regardless of our flow control mechanism, if the PN cl is zero, we must */ + /* respond with zero. "A responding implementation must set this field to 14 */ + /* if (and only if) the PN request was 15." This could happen if a PN is sent */ + /* after the DLCI is already established-- the PN in that case must have cl = 0. */ + /* See RFCOMM spec 5.5.3 */ + if (cl == RFCOMM_PN_CONV_LAYER_TYPE_1) + { + our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1; + our_k = 0; + } + else if (p_mcb->flow == PORT_FC_CREDIT) + { + /* get credits */ + port_get_credits (p_port, k); + + /* Set convergence layer and number of credits (k) */ + our_cl = RFCOMM_PN_CONV_LAYER_CBFC_R; + our_k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + p_port->credit_rx = our_k; + } + else + { + /* must not be using credit based flow control; use TS 7.10 */ + our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1; + our_k = 0; + } + RFCOMM_ParNegRsp (p_mcb, dlci, p_port->mtu, our_cl, our_k); +} + + +/******************************************************************************* +** +** Function PORT_ParNegCnf +** +** Description This function is called from the RFCOMM layer to change +** DLCI parameters (currently only MTU is negotiated). +** Save the MTU size supported by the peer. +** If the confirmation is received during the port opening +** procedure send EstablishRequest to continue. +** +*******************************************************************************/ +void PORT_ParNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT4 ("PORT_ParNegCnf dlci:%d mtu:%d cl: %d k: %d", dlci, mtu, cl, k); + + if (!p_port) + return; + + /* Flow control mechanism not set yet. Negotiate flow control mechanism. */ + if (p_mcb->flow == PORT_FC_UNDEFINED) + { + /* Our stack is configured for TS07.10 and they responded with credit-based. */ + /* This is illegal-- negotiation fails. */ + if ((PORT_FC_DEFAULT == PORT_FC_TS710) && (cl == RFCOMM_PN_CONV_LAYER_CBFC_R)) + { + rfc_send_disc (p_mcb, p_port->dlci); + rfc_port_closed (p_port); + return; + } + /* Our stack is configured for credit-based and they responded with credit-based. */ + else if (cl == RFCOMM_PN_CONV_LAYER_CBFC_R) + { + p_mcb->flow = PORT_FC_CREDIT; + } + /* They responded with any other value. Treat this as negotiation to TS07.10. */ + else + { + p_mcb->flow = PORT_FC_TS710; + } + } + /* If mux flow control mechanism set, we honor that setting regardless of */ + /* the CL value in their response. This allows us to gracefully accept any */ + /* illegal PN negotiation scenarios. */ + + p_port->mtu = (p_port->mtu < mtu) ? p_port->mtu : mtu; + p_port->peer_mtu = p_port->mtu; + + if (p_mcb->flow == PORT_FC_CREDIT) + { + port_get_credits (p_port, k); + } + + if (p_port->state == PORT_STATE_OPENING) + RFCOMM_DlcEstablishReq (p_mcb, p_port->dlci, p_port->mtu); +} + + +/******************************************************************************* +** +** Function PORT_DlcEstablishInd +** +** Description This function is called from the RFCOMM layer when peer +** device wants to establish a new DLC. If this is not the +** first message in the establishment procedure port_handle +** has a handle to the port control block otherwise the control +** block should be found based on the muliplexer channel and +** dlci. The block should be allocated allocated before +** meaning that application already made open. +** +*******************************************************************************/ +void PORT_DlcEstablishInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT2 ("PORT_DlcEstablishInd dlci:%d mtu:%d", dlci, mtu); + + if (!p_port) + { + /* This can be a first request for this port */ + p_port = port_find_dlci_port (dlci); + if (!p_port) + { + RFCOMM_DlcEstablishRsp (p_mcb, dlci, 0, RFCOMM_ERROR); + return; + } + p_mcb->port_inx[dlci] = p_port->inx; + } + + /* If L2CAP's mtu less then RFCOMM's take it */ + if (mtu && (mtu < p_port->peer_mtu)) + p_port->peer_mtu = mtu; + + /* If there was an inactivity timer running for MCB stop it */ + rfc_timer_stop (p_mcb); + + RFCOMM_DlcEstablishRsp (p_mcb, dlci, p_port->mtu, RFCOMM_SUCCESS); + + /* This is the server side. If application wants to know when connection */ + /* is established, thats the place */ + if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECTED)) + (p_port->p_callback)(PORT_EV_CONNECTED, p_port->inx); + + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (PORT_SUCCESS, p_port->inx); + + p_port->state = PORT_STATE_OPENED; +} + + +/******************************************************************************* +** +** Function PORT_DlcEstablishCnf +** +** Description This function is called from the RFCOMM layer when peer +** acknowledges establish procedure (SABME/UA). Send reply +** to the user and set state to OPENED if result was +** successfull. +** +*******************************************************************************/ +void PORT_DlcEstablishCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT3 ("PORT_DlcEstablishCnf dlci:%d mtu:%d result:%d", dlci, mtu, result); + + if (!p_port) + return; + + if (result != RFCOMM_SUCCESS) + { + p_port->error = PORT_START_FAILED; + port_rfc_closed (p_port, PORT_START_FAILED); + return; + } + + /* If L2CAP's mtu less then RFCOMM's take it */ + if (mtu && (mtu < p_port->peer_mtu)) + p_port->peer_mtu = mtu; + + /* If there was an inactivity timer running for MCB stop it */ + rfc_timer_stop (p_mcb); + + if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECTED)) + (p_port->p_callback)(PORT_EV_CONNECTED, p_port->inx); + + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (PORT_SUCCESS, p_port->inx); + + p_port->state = PORT_STATE_OPENED; + + /* RPN is required only if we want to tell DTE how the port should be opened */ + if ((p_port->uuid == UUID_SERVCLASS_DIALUP_NETWORKING) + || (p_port->uuid == UUID_SERVCLASS_FAX)) + RFCOMM_PortNegReq (p_port->rfc.p_mcb, p_port->dlci, NULL); + else + RFCOMM_ControlReq (p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl); +} + + +/******************************************************************************* +** +** Function PORT_PortNegInd +** +** Description This function is called from the RFCOMM layer when peer +** device wants to set parameters of the port. As per the spec +** this message has to be sent before the first data packet +** and can be sent before establish. The block should be +** allocated before meaning that application already made open. +** +*******************************************************************************/ +void PORT_PortNegInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, + UINT16 param_mask) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT0 ("PORT_PortNegInd"); + + if (!p_port) + { + /* This can be a first request for this port */ + p_port = port_find_dlci_port (dlci); + if (!p_port) + { + RFCOMM_PortNegRsp (p_mcb, dlci, p_pars, 0); + return; + } + p_mcb->port_inx[dlci] = p_port->inx; + } + + /* Check if the flow control is acceptable on local side */ + p_port->peer_port_pars = *p_pars; + RFCOMM_PortNegRsp (p_mcb, dlci, p_pars, param_mask); +} + + +/******************************************************************************* +** +** Function PORT_PortNegCnf +** +** Description This function is called from the RFCOMM layer to change +** state for the port. Propagate change to the user. +** +*******************************************************************************/ +void PORT_PortNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 result) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT0 ("PORT_PortNegCnf"); + + if (!p_port) + { + RFCOMM_TRACE_WARNING0 ("PORT_PortNegCnf no port"); + return; + } + /* Port negotiation failed. Drop the connection */ + if (result != RFCOMM_SUCCESS) + { + p_port->error = PORT_PORT_NEG_FAILED; + + RFCOMM_DlcReleaseReq (p_mcb, p_port->dlci); + + port_rfc_closed (p_port, PORT_PORT_NEG_FAILED); + return; + } + + if (!(p_port->port_ctrl & PORT_CTRL_REQ_SENT)) + { + RFCOMM_ControlReq (p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl); + } + else + { + RFCOMM_TRACE_WARNING0 ("PORT_PortNegCnf Control Already sent"); + } +} + + +/******************************************************************************* +** +** Function PORT_ControlInd +** +** Description This function is called from the RFCOMM layer on the modem +** signal change. Propagate change to the user. +** +*******************************************************************************/ +void PORT_ControlInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT32 event; + UINT8 old_signals; + + RFCOMM_TRACE_EVENT0 ("PORT_ControlInd"); + + if (!p_port) + return; + + old_signals = p_port->peer_ctrl.modem_signal; + + event = port_get_signal_changes (p_port, old_signals, p_pars->modem_signal); + + p_port->peer_ctrl = *p_pars; + + if (!(p_port->port_ctrl & PORT_CTRL_REQ_SENT)) + RFCOMM_ControlReq (p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl); + else + { + /* If this is the first time we received control RFCOMM is connected */ + if (!(p_port->port_ctrl & PORT_CTRL_IND_RECEIVED)) + { + event |= (PORT_EV_CONNECTED & p_port->ev_mask); + } + + if (p_port->port_ctrl & PORT_CTRL_REQ_CONFIRMED) + { + event |= port_rfc_send_tx_data(p_port); + } + } + + p_port->port_ctrl |= (PORT_CTRL_IND_RECEIVED | PORT_CTRL_IND_RESPONDED); + + if (p_pars->break_signal) + event |= (PORT_EV_BREAK & p_port->ev_mask); + + /* execute call back function only if the application is registered for events */ + if (event && p_port->p_callback) + (p_port->p_callback)(event, p_port->inx); + + RFCOMM_TRACE_EVENT4 ("PORT_ControlInd DTR_DSR : %d, RTS_CTS : %d, RI : %d, DCD : %d", + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_DTRDSR) ? 1 : 0), + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_RTSCTS) ? 1 : 0), + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_RI) ? 1 : 0), + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_DCD) ? 1 : 0)); + +} + + +/******************************************************************************* +** +** Function PORT_ControlCnf +** +** Description This function is called from the RFCOMM layer when +** peer acknowleges change of the modem signals. +** +*******************************************************************************/ +void PORT_ControlCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT32 event = 0; + + RFCOMM_TRACE_EVENT0 ("PORT_ControlCnf"); + + if (!p_port) + return; + + if (!(p_port->port_ctrl & PORT_CTRL_REQ_CONFIRMED)) + { + p_port->port_ctrl |= PORT_CTRL_REQ_CONFIRMED; + + if (p_port->port_ctrl & PORT_CTRL_IND_RECEIVED) + event = (p_port->ev_mask & PORT_EV_CONNECTED); + } + + if (p_port->port_ctrl & PORT_CTRL_IND_RECEIVED) + { + event |= port_rfc_send_tx_data(p_port); + } + + /* execute call back function only if the application is registered for events */ + if (event && p_port->p_callback) + (p_port->p_callback)(event, p_port->inx); +} + + +/******************************************************************************* +** +** Function PORT_LineStatusInd +** +** Description This function is called from the RFCOMM layer when +** peer indicates change in the line status +** +*******************************************************************************/ +void PORT_LineStatusInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT32 event = 0; + + RFCOMM_TRACE_EVENT0 ("PORT_LineStatusInd"); + + if (!p_port) + return; + + p_port->line_status |= line_status; + + if (line_status & PORT_ERR_OVERRUN) + event |= PORT_EV_OVERRUN; + + if (line_status & PORT_ERR_BREAK) + event |= PORT_EV_BREAK; + + if (line_status & ~(PORT_ERR_OVERRUN | PORT_ERR_BREAK)) + event |= PORT_EV_ERR; + + if ((p_port->p_callback != NULL) && (p_port->ev_mask & event)) + p_port->p_callback ((p_port->ev_mask & event), p_port->inx); +} + + +/******************************************************************************* +** +** Function PORT_DlcReleaseInd +** +** Description This function is called from the RFCOMM layer when +** DLC connection is released. +** +*******************************************************************************/ +void PORT_DlcReleaseInd (tRFC_MCB *p_mcb, UINT8 dlci) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT0 ("PORT_DlcReleaseInd"); + + if (!p_port) + return; + + port_rfc_closed (p_port, PORT_CLOSED); +} + + +/******************************************************************************* +** +** Function PORT_CloseInd +** +** Description This function is called from the RFCOMM layer when +** multiplexer connection is released. +** +*******************************************************************************/ +void PORT_CloseInd (tRFC_MCB *p_mcb) +{ + tPORT *p_port; + int i; + + RFCOMM_TRACE_EVENT0 ("PORT_CloseInd"); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) + { + if (p_port->rfc.p_mcb == p_mcb) + { + port_rfc_closed (p_port, PORT_PEER_CONNECTION_FAILED); + } + } + rfc_release_multiplexer_channel (p_mcb); +} + +/******************************************************************************* +** +** Function Port_TimeOutCloseMux +** +** Description This function is called when RFCOMM timesout on a command +** as a result multiplexer connection is closed. +** +*******************************************************************************/ +void Port_TimeOutCloseMux (tRFC_MCB *p_mcb) +{ + tPORT *p_port; + int i; + + RFCOMM_TRACE_EVENT0 ("Port_TimeOutCloseMux"); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) + { + if (p_port->rfc.p_mcb == p_mcb) + { + port_rfc_closed (p_port, PORT_PEER_TIMEOUT); + } + } +} + + +/******************************************************************************* +** +** Function PORT_DataInd +** +** Description This function is called from the RFCOMM layer when data +** buffer is received from the peer. +** +*******************************************************************************/ +void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT8 rx_char1; + UINT32 events = 0; + UINT8 *p; + int i; + + RFCOMM_TRACE_EVENT1 ("PORT_DataInd with data length %d", p_buf->len); + if (!p_port) + { + GKI_freebuf (p_buf); + return; + } + + /* If client registered callback we can just deliver receive data */ + if (p_port->p_data_callback) + { + /* Another packet is delivered to user. Send credits to peer if required */ + port_flow_control_peer(p_port, TRUE, 1); + + p_port->p_data_callback (p_port->inx, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + GKI_freebuf (p_buf); + return; + } + + /* Check if rx queue exceeds the limit */ + if ((p_port->rx.queue_size + p_buf->len > PORT_RX_CRITICAL_WM) + || (p_port->rx.queue.count + 1 > p_port->rx_buf_critical)) + { + RFCOMM_TRACE_EVENT0 ("PORT_DataInd. Buffer over run. Dropping the buffer"); + GKI_freebuf (p_buf); + + RFCOMM_LineStatusReq (p_mcb, dlci, LINE_STATUS_OVERRUN); + return; + } + + /* If user registered to receive notification when a particular byte is */ + /* received we mast check all received bytes */ + if (((rx_char1 = p_port->user_port_pars.rx_char1) != 0) + && (p_port->ev_mask & PORT_EV_RXFLAG)) + { + for (i = 0, p = (UINT8 *)(p_buf + 1) + p_buf->offset; i < p_buf->len; i++) + { + if (*p++ == rx_char1) + { + events |= PORT_EV_RXFLAG; + break; + } + } + } + + PORT_SCHEDULE_LOCK; + + GKI_enqueue (&p_port->rx.queue, p_buf); + p_port->rx.queue_size += p_buf->len; + + PORT_SCHEDULE_UNLOCK; + + /* perform flow control procedures if necessary */ + port_flow_control_peer(p_port, FALSE, 0); + + /* If user indicated flow control can not deliver any notifications to him */ + if (p_port->rx.user_fc) + { + if (events & PORT_EV_RXFLAG) + p_port->rx_flag_ev_pending = TRUE; + + return; + } + + events |= PORT_EV_RXCHAR; + + /* Mask out all events that are not of interest to user */ + events &= p_port->ev_mask; + + if (p_port->p_callback && events) + p_port->p_callback (events, p_port->inx); +} + + +/******************************************************************************* +** +** Function PORT_FlowInd +** +** Description This function is called from the RFCOMM layer on the flow +** control signal change. Propagate change to the user. +** +*******************************************************************************/ +void PORT_FlowInd (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN enable_data) +{ + tPORT *p_port = (tPORT *)NULL; + UINT32 events = 0; + int i; + + RFCOMM_TRACE_EVENT1 ("PORT_FlowInd fc:%d", enable_data); + + if (dlci == 0) + { + p_mcb->peer_ready = enable_data; + } + else + { + if ((p_port = port_find_mcb_dlci_port (p_mcb, dlci)) == NULL) + return; + + p_port->tx.peer_fc = !enable_data; + } + + for (i = 0; i < MAX_RFC_PORTS; i++) + { + /* If DLCI is 0 event applies to all ports */ + if (dlci == 0) + { + p_port = &rfc_cb.port.port[i]; + if (!p_port->in_use + || (p_port->rfc.p_mcb != p_mcb) + || (p_port->rfc.state != RFC_STATE_OPENED)) + continue; + } + events = 0; + + /* Check if flow of data is still enabled */ + events |= port_flow_control_user (p_port); + + /* Check if data can be sent and send it */ + events |= port_rfc_send_tx_data (p_port); + + /* Mask out all events that are not of interest to user */ + events &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && events) + (p_port->p_callback)(events, p_port->inx); + + /* If DLCI is not 0 event applies to one port only */ + if (dlci != 0) + break; + } +} + + +/******************************************************************************* +** +** Function port_rfc_send_tx_data +** +** Description This function is when forward data can be sent to the peer +** +*******************************************************************************/ +UINT32 port_rfc_send_tx_data (tPORT *p_port) +{ + UINT32 events = 0; + BT_HDR *p_buf; + + /* if there is data to be sent */ + if (p_port->tx.queue_size > 0) + { + /* while the rfcomm peer is not flow controlling us, and peer is ready */ + while (!p_port->tx.peer_fc && p_port->rfc.p_mcb && p_port->rfc.p_mcb->peer_ready) + { + /* get data from tx queue and send it */ + PORT_SCHEDULE_LOCK; + + if ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->tx.queue)) != NULL) + { + p_port->tx.queue_size -= p_buf->len; + + PORT_SCHEDULE_UNLOCK; + + RFCOMM_TRACE_DEBUG1 ("Sending RFCOMM_DataReq tx.queue_size=%d", p_port->tx.queue_size); + + RFCOMM_DataReq (p_port->rfc.p_mcb, p_port->dlci, p_buf); + + events |= PORT_EV_TXCHAR; + + if (p_port->tx.queue_size == 0) + { + events |= PORT_EV_TXEMPTY; + break; + } + } + /* queue is empty-- all data sent */ + else + { + PORT_SCHEDULE_UNLOCK; + + events |= PORT_EV_TXEMPTY; + break; + } + } + /* If we flow controlled user based on the queue size enable data again */ + events |= port_flow_control_user (p_port); + } + return (events & p_port->ev_mask); +} + + +/******************************************************************************* +** +** Function port_rfc_closed +** +** Description This function when RFCOMM side of port is closed +** +*******************************************************************************/ +void port_rfc_closed (tPORT *p_port, UINT8 res) +{ + UINT8 old_signals; + UINT32 events = 0; + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + if ((p_port->state == PORT_STATE_OPENING) && (p_port->is_server)) + { + /* The servr side has not been informed that connection is up, ignore */ + RFCOMM_TRACE_EVENT0 ("port_rfc_closed in OPENING state ignored"); + + rfc_port_timer_stop (p_port); + p_port->rfc.state = RFC_STATE_CLOSED; + + if (p_mcb) + { + p_mcb->port_inx[p_port->dlci] = 0; + + /* If there are no more ports opened on this MCB release it */ + rfc_check_mcb_active (p_mcb); + p_port->rfc.p_mcb = NULL; + } + + /* Need to restore DLCI to listening state + * if the server was on the initiating RFC + */ + p_port->dlci &= 0xfe; + + return; + } + + if ((p_port->state != PORT_STATE_CLOSING) && (p_port->state != PORT_STATE_CLOSED)) + { + p_port->line_status |= LINE_STATUS_FAILED; + + old_signals = p_port->peer_ctrl.modem_signal; + + p_port->peer_ctrl.modem_signal &= ~(PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON); + + events |= port_get_signal_changes (p_port, old_signals, p_port->peer_ctrl.modem_signal); + + if(p_port->ev_mask & PORT_EV_CONNECT_ERR) + events |= PORT_EV_CONNECT_ERR; + } + RFCOMM_TRACE_EVENT2 ("port_rfc_closed state:%d sending events:%x", p_port->state, events); + + if ((p_port->p_callback != NULL) && events) + p_port->p_callback (events, p_port->inx); + + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (res, p_port->inx); + + p_port->rfc.state = RFC_STATE_CLOSED; + + port_release_port (p_port); +} + + +/******************************************************************************* +** +** Function port_get_credits +** +** Description Set initial values for credits. +** Adjust max number of rx credits based on negotiated MTU. +** Check max allowed num of bytes, max allowed num buffers, +** should be less then 255 +** +*******************************************************************************/ +void port_get_credits (tPORT *p_port, UINT8 k) +{ + p_port->credit_tx = k; + if (p_port->credit_tx == 0) + p_port->tx.peer_fc = TRUE; +} + + + diff --git a/stack/rfcomm/port_utils.c b/stack/rfcomm/port_utils.c new file mode 100644 index 0000000..5a52774 --- /dev/null +++ b/stack/rfcomm/port_utils.c @@ -0,0 +1,562 @@ +/***************************************************************************** +** +** Name: port_utils.c +** +** Description: Port Emulation entity utilities +** +** +** Copyright (c) 1999-2009, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ +#include <string.h> + +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "port_int.h" +#include "rfc_int.h" +#include "l2cdefs.h" +#include "btm_int.h" +#include "btu.h" + + +static const tPORT_STATE default_port_pars = +{ + PORT_BAUD_RATE_9600, + PORT_8_BITS, + PORT_ONESTOPBIT, + PORT_PARITY_NO, + PORT_ODD_PARITY, + PORT_FC_OFF, + 0, /* No rx_char */ + PORT_XON_DC1, + PORT_XOFF_DC3, +}; + + + +/******************************************************************************* +** +** Function port_allocate_port +** +** Description Look through the Port Control Blocks for a free one. Note +** that one server can open several ports with the same SCN +** if it can support simulteneous requests from different +** clients. +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_allocate_port (UINT8 dlci, BD_ADDR bd_addr) +{ + tPORT *p_port = &rfc_cb.port.port[0]; + UINT8 xx, yy; + + for (xx = 0, yy = rfc_cb.rfc.last_port + 1; xx < MAX_RFC_PORTS; xx++, yy++) + { + if (yy >= MAX_RFC_PORTS) + yy = 0; + + p_port = &rfc_cb.port.port[yy]; + if (!p_port->in_use) + { + memset (p_port, 0, sizeof (tPORT)); + + p_port->in_use = TRUE; + p_port->inx = yy + 1; + + p_port->dlci = dlci; + memcpy (p_port->bd_addr, bd_addr, BD_ADDR_LEN); + + /* During the open set default state for the port connection */ + port_set_defaults (p_port); + + rfc_cb.rfc.last_port = yy; + return (p_port); + } + } + + /* If here, no free PORT found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function port_set_defaults +** +** Description Set defualt port parameters +** +** +*******************************************************************************/ +void port_set_defaults (tPORT *p_port) +{ + p_port->ev_mask = 0; + p_port->p_callback = NULL; + p_port->port_ctrl = 0; + p_port->error = 0; + p_port->line_status = 0; + p_port->rx_flag_ev_pending = FALSE; + p_port->peer_mtu = RFCOMM_DEFAULT_MTU; + + p_port->user_port_pars = default_port_pars; + p_port->peer_port_pars = default_port_pars; + + p_port->credit_tx = 0; + p_port->credit_rx = 0; +/* p_port->credit_rx_max = PORT_CREDIT_RX_MAX; Determined later */ +/* p_port->credit_rx_low = PORT_CREDIT_RX_LOW; Determined later */ + + memset (&p_port->local_ctrl, 0, sizeof (p_port->local_ctrl)); + memset (&p_port->peer_ctrl, 0, sizeof (p_port->peer_ctrl)); + memset (&p_port->rx, 0, sizeof (p_port->rx)); + memset (&p_port->tx, 0, sizeof (p_port->tx)); +} + +/******************************************************************************* +** +** Function port_select_mtu +** +** Description Select MTU which will best serve connection from our +** point of view. +** If our device is 1.2 or lower we calculate how many DH5s +** fit into 1 RFCOMM buffer. +** +** +*******************************************************************************/ +void port_select_mtu (tPORT *p_port) +{ + UINT16 packet_size; + + /* Will select MTU only if application did not setup something */ + if (p_port->mtu == 0) + { + /* find packet size which connection supports */ + packet_size = btm_get_max_packet_size (p_port->bd_addr); + if (packet_size == 0) + { + /* something is very wrong */ + RFCOMM_TRACE_WARNING0 ("port_select_mtu bad packet size"); + p_port->mtu = RFCOMM_DEFAULT_MTU; + } + else + { + /* We try to negotiate MTU that each packet can be split into whole + number of max packets. For example if link is 1.2 max packet size is 339 bytes. + At first calculate how many whole packets it is. MAX L2CAP is 1691 + 4 overhead. + 1695, that will be 5 Dh5 packets. Now maximum RFCOMM packet is + 5 * 339 = 1695. Minus 4 bytes L2CAP header 1691. Minus RFCOMM 6 bytes header overhead 1685 + + For EDR 2.0 packet size is 1027. So we better send RFCOMM packet as 1 3DH5 packet + 1 * 1027 = 1027. Minus 4 bytes L2CAP header 1023. Minus RFCOMM 6 bytes header overhead 1017 */ + if ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) >= packet_size) + { + p_port->mtu = ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) / packet_size * packet_size) - RFCOMM_DATA_OVERHEAD - L2CAP_PKT_OVERHEAD; + RFCOMM_TRACE_DEBUG1 ("port_select_mtu selected %d based on connection speed", p_port->mtu); + } + else + { + p_port->mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD; + RFCOMM_TRACE_DEBUG1 ("port_select_mtu selected %d based on l2cap PDU size", p_port->mtu); + } + } + } + else + { + RFCOMM_TRACE_DEBUG1 ("port_select_mtu application selected %d", p_port->mtu); + } + p_port->credit_rx_max = (PORT_RX_HIGH_WM / p_port->mtu); + if( p_port->credit_rx_max > PORT_RX_BUF_HIGH_WM ) + p_port->credit_rx_max = PORT_RX_BUF_HIGH_WM; + p_port->credit_rx_low = (PORT_RX_LOW_WM / p_port->mtu); + if( p_port->credit_rx_low > PORT_RX_BUF_LOW_WM ) + p_port->credit_rx_low = PORT_RX_BUF_LOW_WM; + p_port->rx_buf_critical = (PORT_RX_CRITICAL_WM / p_port->mtu); + if( p_port->rx_buf_critical > PORT_RX_BUF_CRITICAL_WM ) + p_port->rx_buf_critical = PORT_RX_BUF_CRITICAL_WM; + RFCOMM_TRACE_DEBUG3 ("port_select_mtu credit_rx_max %d, credit_rx_low %d, rx_buf_critical %d", + p_port->credit_rx_max, p_port->credit_rx_low, p_port->rx_buf_critical); +} + + +/******************************************************************************* +** +** Function port_release_port +** +** Description Release port infor control block. +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +void port_release_port (tPORT *p_port) +{ + BT_HDR *p_buf; + UINT32 mask; + tPORT_CALLBACK *p_port_cb; + tPORT_STATE user_port_pars; + + PORT_SCHEDULE_LOCK; + + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->rx.queue)) != NULL) + GKI_freebuf (p_buf); + + p_port->rx.queue_size = 0; + + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->tx.queue)) != NULL) + GKI_freebuf (p_buf); + + p_port->tx.queue_size = 0; + + PORT_SCHEDULE_UNLOCK; + + p_port->state = PORT_STATE_CLOSED; + + if (p_port->rfc.state == RFC_STATE_CLOSED) + { + RFCOMM_TRACE_DEBUG0 ("rfc_port_closed DONE"); + if (p_port->rfc.p_mcb) + { + p_port->rfc.p_mcb->port_inx[p_port->dlci] = 0; + + /* If there are no more ports opened on this MCB release it */ + rfc_check_mcb_active (p_port->rfc.p_mcb); + } + rfc_port_timer_stop (p_port); + + if( p_port->keep_port_handle ) + { + RFCOMM_TRACE_DEBUG1 ("port_release_port:Initialize handle:%d", p_port->inx); + /* save event mask and callback */ + mask = p_port->ev_mask; + p_port_cb = p_port->p_callback; + user_port_pars = p_port->user_port_pars; + + port_set_defaults(p_port); + /* restore */ + p_port->ev_mask = mask; + p_port->p_callback = p_port_cb; + p_port->user_port_pars = user_port_pars; + p_port->mtu = p_port->keep_mtu; + + p_port->state = PORT_STATE_OPENING; + p_port->rfc.p_mcb = NULL; + if(p_port->is_server) + p_port->dlci &= 0xfe; + + p_port->local_ctrl.modem_signal = p_port->default_signal_state; + memcpy (p_port->bd_addr, BT_BD_ANY, BD_ADDR_LEN); + } + else + { + RFCOMM_TRACE_DEBUG1 ("port_release_port:Clean-up handle:%d", p_port->inx); + memset (p_port, 0, sizeof (tPORT)); + } + } +} + + +/******************************************************************************* +** +** Function port_find_mcb +** +** Description This function checks if connection exists to device with +** the BD_ADDR. +** +*******************************************************************************/ +tRFC_MCB *port_find_mcb (BD_ADDR bd_addr) +{ + int i; + + for (i = 0; i < MAX_BD_CONNECTIONS; i++) + { + if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE) + && !memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN)) + { + /* Multiplexer channel found do not change anything */ + return (&rfc_cb.port.rfc_mcb[i]); + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function port_find_mcb_dlci_port +** +** Description Find port on the multiplexer channel based on DLCI. If +** this port with DLCI not found try to use even DLCI. This +** is for the case when client is establishing connection on +** none-initiator MCB. +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_find_mcb_dlci_port (tRFC_MCB *p_mcb, UINT8 dlci) +{ + UINT8 inx; + + if (!p_mcb) + return (NULL); + + if (dlci > RFCOMM_MAX_DLCI) + return (NULL); + + inx = p_mcb->port_inx[dlci]; + if (inx == 0) + return (NULL); + else + return (&rfc_cb.port.port[inx - 1]); +} + + +/******************************************************************************* +** +** Function port_find_dlci_port +** +** Description Find port with DLCI not assigned to multiplexer channel +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_find_dlci_port (UINT8 dlci) +{ + UINT16 i; + tPORT *p_port; + + for (i = 0; i < MAX_RFC_PORTS; i++) + { + p_port = &rfc_cb.port.port[i]; + if (p_port->in_use && (p_port->rfc.p_mcb == NULL)) + { + if (p_port->dlci == dlci) + { + return (p_port); + } + else if ((dlci & 0x01) && (p_port->dlci == (dlci - 1))) + { + p_port->dlci++; + return (p_port); + } + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function port_find_port +** +** Description Find port with DLCI, BD_ADDR +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_find_port (UINT8 dlci, BD_ADDR bd_addr) +{ + UINT16 i; + tPORT *p_port; + + for (i = 0; i < MAX_RFC_PORTS; i++) + { + p_port = &rfc_cb.port.port[i]; + if (p_port->in_use + && (p_port->dlci == dlci) + && !memcmp (p_port->bd_addr, bd_addr, BD_ADDR_LEN)) + { + return (p_port); + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function port_flow_control_user +** +** Description Check the current user flow control and if necessary return +** events to be send to the user based on the user's specified +** flow control type. +** +** Returns event mask to be returned to the application +** +*******************************************************************************/ +UINT32 port_flow_control_user (tPORT *p_port) +{ + UINT32 event = 0; + + /* Flow control to the user can be caused by flow controlling by the peer */ + /* (FlowInd, or flow control by the peer RFCOMM (Fcon) or internally if */ + /* tx_queue is full */ + BOOLEAN fc = p_port->tx.peer_fc + || !p_port->rfc.p_mcb + || !p_port->rfc.p_mcb->peer_ready + || (p_port->tx.queue_size > PORT_TX_HIGH_WM) + || (p_port->tx.queue.count > PORT_TX_BUF_HIGH_WM); + + if (p_port->tx.user_fc == fc) + return (0); + + p_port->tx.user_fc = fc; + + if (fc) + event = PORT_EV_FC; + else + event = PORT_EV_FC | PORT_EV_FCS; + + return (event); +} + + +/******************************************************************************* +** +** Function port_get_signal_changes +** +** Description Check modem signals that has been changed +** +** Returns event mask to be returned to the application +** +*******************************************************************************/ +UINT32 port_get_signal_changes (tPORT *p_port, UINT8 old_signals, UINT8 signal) +{ + UINT8 changed_signals = (signal ^ old_signals); + UINT32 events = 0; + + if (changed_signals & PORT_DTRDSR_ON) + { + events |= PORT_EV_DSR; + + if (signal & PORT_DTRDSR_ON) + events |= PORT_EV_DSRS; + } + + if (changed_signals & PORT_CTSRTS_ON) + { + events |= PORT_EV_CTS; + + if (signal & PORT_CTSRTS_ON) + events |= PORT_EV_CTSS; + } + + if (changed_signals & PORT_RING_ON) + events |= PORT_EV_RING; + + if (changed_signals & PORT_DCD_ON) + { + events |= PORT_EV_RLSD; + + if (signal & PORT_DCD_ON) + events |= PORT_EV_RLSDS; + } + + return (p_port->ev_mask & events); +} + +/******************************************************************************* +** +** Function port_flow_control_peer +** +** Description Send flow control messages to the peer for both enabling +** and disabling flow control, for both credit-based and +** TS 07.10 flow control mechanisms. +** +** Returns nothing +** +*******************************************************************************/ +void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count) +{ + if (!p_port->rfc.p_mcb) + return; + + /* If using credit based flow control */ + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + { + /* if want to enable flow from peer */ + if (enable) + { + /* update rx credits */ + if (count > p_port->credit_rx) + { + p_port->credit_rx = 0; + } + else + { + p_port->credit_rx -= count; + } + + /* If credit count is less than low credit watermark, and user */ + /* did not force flow control, send a credit update */ + /* There might be a special case when we just adjusted rx_max */ + if ((p_port->credit_rx <= p_port->credit_rx_low) + && !p_port->rx.user_fc + && (p_port->credit_rx_max > p_port->credit_rx)) + { + rfc_send_credit(p_port->rfc.p_mcb, p_port->dlci, + (UINT8) (p_port->credit_rx_max - p_port->credit_rx)); + + p_port->credit_rx = p_port->credit_rx_max; + + p_port->rx.peer_fc = FALSE; + } + } + /* else want to disable flow from peer */ + else + { + /* if client registered data callback, just do what they want */ + if (p_port->p_data_callback) + { + p_port->rx.peer_fc = TRUE; + } + /* if queue count reached credit rx max, set peer fc */ + else if (p_port->rx.queue.count >= p_port->credit_rx_max) + { + p_port->rx.peer_fc = TRUE; + } + } + } + /* else using TS 07.10 flow control */ + else + { + /* if want to enable flow from peer */ + if (enable) + { + /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ + /* check if it can be resumed now */ + if (p_port->rx.peer_fc + && (p_port->rx.queue_size < PORT_RX_LOW_WM) + && (p_port->rx.queue.count < PORT_RX_BUF_LOW_WM)) + { + p_port->rx.peer_fc = FALSE; + + /* If user did not force flow control allow traffic now */ + if (!p_port->rx.user_fc) + RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, TRUE); + } + } + /* else want to disable flow from peer */ + else + { + /* if client registered data callback, just do what they want */ + if (p_port->p_data_callback) + { + p_port->rx.peer_fc = TRUE; + RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); + } + /* Check the size of the rx queue. If it exceeds certain */ + /* level and flow control has not been sent to the peer do it now */ + else if ( ((p_port->rx.queue_size > PORT_RX_HIGH_WM) + || (p_port->rx.queue.count > PORT_RX_BUF_HIGH_WM)) + && !p_port->rx.peer_fc) + { + RFCOMM_TRACE_EVENT0 ("PORT_DataInd Data reached HW. Sending FC set."); + + p_port->rx.peer_fc = TRUE; + RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); + } + } + } +} + diff --git a/stack/rfcomm/rfc_int.h b/stack/rfcomm/rfc_int.h new file mode 100644 index 0000000..fcafdf0 --- /dev/null +++ b/stack/rfcomm/rfc_int.h @@ -0,0 +1,373 @@ +/*****************************************************************************/ +/* */ +/* Name: rfc_int.h */ +/* */ +/* Description: This file contains definitions internal to the RFC unit */ +/* */ +/* */ +/* Copyright (c) 1999-2008, Broadcom Corp., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ +#ifndef RFC_INT_H +#define RFC_INT_H + +#include "l2c_api.h" +#include "port_int.h" + +/* +** Define RFCOMM result codes +*/ +#define RFCOMM_SUCCESS 0 +#define RFCOMM_ERROR 1 +#define RFCOMM_LOW_RESOURCES 2 +#define RFCOMM_TRY_LATER 3 + +#define RFCOMM_USER_ERR 111 +#define RFCOMM_SECURITY_ERR 112 + +/* +** Define max and min RFCOMM MTU (N1) +*/ +#define RFCOMM_MIN_MTU 23 +#define RFCOMM_MAX_MTU 32767 + +extern void RFCOMM_StartReq (tRFC_MCB *p_mcb); +extern void RFCOMM_StartRsp (tRFC_MCB *p_mcb, UINT16 result); + +extern void RFCOMM_DlcEstablishReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu); +extern void RFCOMM_DlcEstablishRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result); + +extern void RFCOMM_DataReq (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf); + +extern void RFCOMM_DlcReleaseReq (tRFC_MCB *p_mcb, UINT8 dlci); + +extern void RFCOMM_ParNegReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu); +extern void RFCOMM_ParNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k); + +extern void RFCOMM_TestReq (UINT8 *p_data, UINT16 len); + +#define RFCOMM_FLOW_STATE_DISABLE 0 +#define RFCOMM_FLOW_STATE_ENABLE 1 + +extern void RFCOMM_FlowReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 state); + +extern void RFCOMM_PortNegReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars); +extern void RFCOMM_PortNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 param_mask); + +extern void RFCOMM_ControlReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); +extern void RFCOMM_ControlRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); + +extern void RFCOMM_LineStatusReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status); +/* +** Define logical struct used for sending and decoding MX frames +*/ +typedef struct +{ + UINT8 dlci; + UINT8 type; + UINT8 cr; + UINT8 ea; + UINT8 pf; + UINT8 credit; + + union + { + struct + { + UINT8 dlci; + UINT8 frame_type; + UINT8 conv_layer; + UINT8 priority; + UINT8 t1; + UINT16 mtu; + UINT8 n2; + UINT8 k; + } pn; + struct + { + UINT8 *p_data; + UINT16 data_len; + } test; + struct + { + UINT8 dlci; + UINT8 signals; + UINT8 break_present; + UINT8 break_duration; + } msc; + struct + { + UINT8 ea; + UINT8 cr; + UINT8 type; + } nsc; + struct + { + UINT8 dlci; + UINT8 is_request; + UINT8 baud_rate; + UINT8 byte_size; + UINT8 stop_bits; + UINT8 parity; + UINT8 parity_type; + UINT8 fc_type; + UINT8 xon_char; + UINT8 xoff_char; + UINT16 param_mask; + } rpn; + struct + { + UINT8 dlci; + UINT8 line_status; + } rls; + } u; +} MX_FRAME; + +#define LINE_STATUS_NO_ERROR 0x00 +#define LINE_STATUS_OVERRUN 0x02 /* Receive Overrun Error */ +#define LINE_STATUS_RXPARITY 0x04 /* Receive Parity Error */ +#define LINE_STATUS_FRAME 0x08 /* Receive Framing error */ +#define LINE_STATUS_FAILED 0x10 /* Connection Failed */ + +/* +** Define states and events for the RFC multiplexer state machine +*/ +#define RFC_MX_STATE_IDLE 0 +#define RFC_MX_STATE_WAIT_CONN_CNF 1 +#define RFC_MX_STATE_CONFIGURE 2 +#define RFC_MX_STATE_SABME_WAIT_UA 3 +#define RFC_MX_STATE_WAIT_SABME 4 +#define RFC_MX_STATE_CONNECTED 5 +#define RFC_MX_STATE_DISC_WAIT_UA 6 + +/* +** Define port states +*/ +#define RFC_STATE_CLOSED 0 +#define RFC_STATE_SABME_WAIT_UA 1 +#define RFC_STATE_ORIG_WAIT_SEC_CHECK 2 +#define RFC_STATE_TERM_WAIT_SEC_CHECK 3 +#define RFC_STATE_OPENED 4 +#define RFC_STATE_DISC_WAIT_UA 5 + +/* +** Events that can be received by multiplexer as well as port state machines +*/ +#define RFC_EVENT_SABME 0 +#define RFC_EVENT_UA 1 +#define RFC_EVENT_DM 2 +#define RFC_EVENT_DISC 3 +#define RFC_EVENT_UIH 4 +#define RFC_EVENT_TIMEOUT 5 +#define RFC_EVENT_BAD_FRAME 50 +/* +** Multiplexer events +*/ +#define RFC_MX_EVENT_START_REQ 6 +#define RFC_MX_EVENT_START_RSP 7 +#define RFC_MX_EVENT_CLOSE_REQ 8 +#define RFC_MX_EVENT_CONN_CNF 9 +#define RFC_MX_EVENT_CONN_IND 10 +#define RFC_MX_EVENT_CONF_CNF 11 +#define RFC_MX_EVENT_CONF_IND 12 +#define RFC_MX_EVENT_QOS_VIOLATION_IND 13 +#define RFC_MX_EVENT_DISC_IND 14 +#define RFC_MX_EVENT_TEST_CMD 15 +#define RFC_MX_EVENT_TEST_RSP 16 +#define RFC_MX_EVENT_FCON_CMD 17 +#define RFC_MX_EVENT_FCOFF_CMD 18 +#define RFC_MX_EVENT_NSC 19 +#define RFC_MX_EVENT_NSC_RSP 20 + +/* +** Port events +*/ +#define RFC_EVENT_OPEN 9 +#define RFC_EVENT_ESTABLISH_RSP 11 +#define RFC_EVENT_CLOSE 12 +#define RFC_EVENT_CLEAR 13 +#define RFC_EVENT_DATA 14 +#define RFC_EVENT_SEC_COMPLETE 15 + +// btla-specific ++ +#define RFC_T1_TIMEOUT 20 /* seconds to wait for reply with Poll bit */ +#define RFC_PORT_T1_TIMEOUT 60 /* seconds to wait for reply with Poll bit other than MX */ +#define RFC_T2_TIMEOUT 20 /* timeout to wait for Mx UIH */ +// btla-specific -- +#define RFC_DISC_TIMEOUT 3 /* If something goes wrong and we send DISC we should not wait for min */ +#define RFC_CLOSE_TIMEOUT 10 +#define RFCOMM_CONN_TIMEOUT 120 /* first connection to be established on Mx */ + + +/* Define RFComm control block +*/ +typedef struct +{ + MX_FRAME rx_frame; + tL2CAP_APPL_INFO reg_info; /* L2CAP Registration info */ + tRFC_MCB *p_rfc_lcid_mcb[MAX_L2CAP_CHANNELS]; /* MCB based on the L2CAP's lcid */ + BOOLEAN peer_rx_disabled; /* If TRUE peer sent FCOFF */ + UINT8 last_mux; /* Last mux allocated */ + UINT8 last_port; /* Last port allocated */ +} tRFCOMM_CB; + +/* Main Control Block for the RFCOMM Layer (PORT and RFC) */ +typedef struct +{ + tRFCOMM_CB rfc; + tPORT_CB port; + UINT8 trace_level; +} tRFC_CB; + + +#if RFC_DYNAMIC_MEMORY == FALSE +RFC_API extern tRFC_CB rfc_cb; +#else +RFC_API extern tRFC_CB *rfc_cb_ptr; +#define rfc_cb (*rfc_cb_ptr) +#endif + +/* Timer running on the multiplexor channel while no DLCI connection is opened */ +#define RFC_MCB_INIT_INACT_TIMER 60 /* in seconds */ + +/* Timer running on the multiplexor channel after last DLCI is released */ +#define RFC_MCB_RELEASE_INACT_TIMER 2 /* in seconds */ + +/* +** Define RFCOMM frame processing errors +*/ +#define RFCOMM_ERR_BAD_SABME 1 +#define RFCOMM_ERR_BAD_UA 2 +#define RFCOMM_ERR_BAD_DM 3 +#define RFCOMM_ERR_BAD_DISC 4 +#define RFCOMM_ERR_BAD_UIH 5 + +#ifdef RFCOMM_PRECALC_FCS + +#define RFCOMM_SABME_FCS(p_data, cr, dlci) rfc_sabme_fcs[cr][dlci] +#define RFCOMM_UA_FCS(p_data, cr, dlci) rfc_ua_fcs[cr][dlci] +#define RFCOMM_DM_FCS(p_data, cr, dlci) rfc_dm_fcs[cr][dlci] +#define RFCOMM_DISC_FCS(p_data, cr, dlci) rfc_disc_fcs[cr][dlci] +#define RFCOMM_UIH_FCS(p_data, dlci) rfc_uih_fcs[dlci] + +#else + +extern UINT8 rfc_calc_fcs (UINT16 len, UINT8 *p); + +#define RFCOMM_SABME_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_UA_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_DM_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_DISC_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_UIH_FCS(p_data, dlci) rfc_calc_fcs(2, p_data) + +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rfc_mx_sm_execute (tRFC_MCB *p_mcb, UINT16 event, void *p_data); + +/* +** Functions provided by the rfc_port_fsm.c +*/ +extern void rfc_port_sm_execute (tPORT *p_port, UINT16 event, void *p_data); + + +extern void rfc_process_pn (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame); +extern void rfc_process_msc (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame); +extern void rfc_process_rpn (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, BOOLEAN is_request, MX_FRAME *p_frame); +extern void rfc_process_rls (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame); +extern void rfc_process_nsc (tRFC_MCB *p_rfc_mcb, MX_FRAME *p_frame); +extern void rfc_process_test_rsp (tRFC_MCB *p_rfc_mcb, BT_HDR *p_buf); +extern void rfc_process_fcon (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command); +extern void rfc_process_fcoff (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command); +extern void rfc_process_l2cap_congestion (tRFC_MCB *p_mcb, BOOLEAN is_congested); + +/* +** Functions provided by the rfc_utils.c +*/ +tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator); +extern void rfc_release_multiplexer_channel (tRFC_MCB *p_rfc_mcb); +extern void rfc_timer_start (tRFC_MCB *p_rfc_mcb, UINT16 timeout); +extern void rfc_timer_stop (tRFC_MCB *p_rfc_mcb); +extern void rfc_port_timer_start (tPORT *p_port, UINT16 tout); +extern void rfc_port_timer_stop (tPORT *p_port); + +BOOLEAN rfc_check_uih_fcs (UINT8 dlci, UINT8 received_fcs); +BOOLEAN rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs); +tRFC_MCB *rfc_find_lcid_mcb (UINT16 lcid); +extern void rfc_save_lcid_mcb (tRFC_MCB *p_rfc_mcb, UINT16 lcid); +extern void rfc_check_mcb_active (tRFC_MCB *p_mcb); +extern void rfc_port_closed (tPORT *p_port); +extern void rfc_sec_check_complete (BD_ADDR bd_addr, void *p_ref_data, UINT8 res); +extern void rfc_inc_credit (tPORT *p_port, UINT8 credit); +extern void rfc_dec_credit (tPORT *p_port); +extern void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf); + +/* +** Functions provided by the rfc_ts_frames.c +*/ +extern void rfc_send_sabme (tRFC_MCB *p_rfc_mcb, UINT8 dlci); +extern void rfc_send_ua (tRFC_MCB *p_rfc_mcb, UINT8 dlci); +extern void rfc_send_dm (tRFC_MCB *p_rfc_mcb, UINT8 dlci, BOOLEAN pf); +extern void rfc_send_disc (tRFC_MCB *p_rfc_mcb, UINT8 dlci); +extern void rfc_send_pn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT16 mtu, + UINT8 cl, UINT8 k); +extern void rfc_send_test (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, BT_HDR *p_buf); +extern void rfc_send_msc (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_CTRL *p_pars); +extern void rfc_send_rls (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT8 status); +extern void rfc_send_rpn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_STATE *p_pars, UINT16 mask); +extern void rfc_send_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command); +extern void rfc_send_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command); +extern void rfc_send_buf_uih (tRFC_MCB *p_rfc_mcb, UINT8 dlci, BT_HDR *p_buf); +extern void rfc_send_credit(tRFC_MCB *p_mcb, UINT8 dlci, UINT8 credit); +extern void rfc_process_mx_message (tRFC_MCB *p_rfc_mcb, BT_HDR *p_buf); +extern UINT8 rfc_parse_data (tRFC_MCB *p_rfc_mcb, MX_FRAME *p_frame, BT_HDR *p_buf); + +/* +** Functions provided by the rfc_disp.c +*/ + + + +/* Call back functions from RFCOMM */ +extern void rfcomm_l2cap_if_init (void); + +extern void PORT_StartInd (tRFC_MCB *p_mcb); +extern void PORT_StartCnf (tRFC_MCB *p_mcb, UINT16 result); + +extern void PORT_CloseInd (tRFC_MCB *p_mcb); +extern void Port_TimeOutCloseMux (tRFC_MCB *p_mcb); + +extern void PORT_DlcEstablishInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu); +extern void PORT_DlcEstablishCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result); + +extern void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf); + +extern void PORT_DlcReleaseInd (tRFC_MCB *p_mcb, UINT8 dlci); + +extern void PORT_ParNegInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k); +extern void PORT_ParNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k); + +extern void PORT_TestCnf (tRFC_MCB *p_mcb, UINT8 *p_data, UINT16 len); + +extern void PORT_FlowInd (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN fc); + +extern void PORT_PortNegInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 param_mask); +extern void PORT_PortNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 result); + +extern void PORT_ControlInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); +extern void PORT_ControlCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); + +extern void PORT_LineStatusInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/stack/rfcomm/rfc_l2cap_if.c b/stack/rfcomm/rfc_l2cap_if.c new file mode 100644 index 0000000..fb10d07 --- /dev/null +++ b/stack/rfcomm/rfc_l2cap_if.c @@ -0,0 +1,430 @@ +/***************************************************************************** +** +** Name: rfc_l2cap_if.c +** +** Description: This file contains L2CAP interface functions +** +** +** Copyright (c) 1999-2009, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include "bt_target.h" +#include "gki.h" + +#include "rfcdefs.h" +#include "port_api.h" +#include "port_int.h" +#include "l2c_api.h" +#include "l2cdefs.h" +#include "rfc_int.h" + + +/* +** Define Callback functions to be called by L2CAP +*/ +static void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +static void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 err); +static void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +static void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +static void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_clear); +static void RFCOMM_QoSViolationInd (BD_ADDR bd_addr); +static void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf); +static void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested); + + +/******************************************************************************* +** +** Function rfcomm_l2cap_if_init +** +** Description This function is called during the RFCOMM task startup +** to register interface functions with L2CAP. +** +*******************************************************************************/ +void rfcomm_l2cap_if_init (void) +{ + tL2CAP_APPL_INFO *p_l2c = &rfc_cb.rfc.reg_info; + + p_l2c->pL2CA_ConnectInd_Cb = RFCOMM_ConnectInd; + p_l2c->pL2CA_ConnectCfm_Cb = RFCOMM_ConnectCnf; + p_l2c->pL2CA_ConnectPnd_Cb = NULL; + p_l2c->pL2CA_ConfigInd_Cb = RFCOMM_ConfigInd; + p_l2c->pL2CA_ConfigCfm_Cb = RFCOMM_ConfigCnf; + p_l2c->pL2CA_DisconnectInd_Cb = RFCOMM_DisconnectInd; + p_l2c->pL2CA_DisconnectCfm_Cb = NULL; + p_l2c->pL2CA_QoSViolationInd_Cb = RFCOMM_QoSViolationInd; + p_l2c->pL2CA_DataInd_Cb = RFCOMM_BufDataInd; + p_l2c->pL2CA_CongestionStatus_Cb = RFCOMM_CongestionStatusInd; + p_l2c->pL2CA_TxComplete_Cb = NULL; + + + L2CA_Register (BT_PSM_RFCOMM, p_l2c); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConnectInd +** +** Description This is a callback function called by L2CAP when +** L2CA_ConnectInd received. Allocate multiplexer control block +** and dispatch the event to it. +** +*******************************************************************************/ +void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tRFC_MCB *p_mcb = rfc_alloc_multiplexer_channel(bd_addr, FALSE); + + if ((p_mcb)&&(p_mcb->state != RFC_MX_STATE_IDLE)) + { + /* if this is collision case */ + if ((p_mcb->is_initiator)&&(p_mcb->state == RFC_MX_STATE_WAIT_CONN_CNF)) + { + p_mcb->pending_lcid = lcid; + p_mcb->pending_id = id; + + /* wait random timeout (2 - 12) to resolve collision */ + /* if peer gives up then local device rejects incoming connection and continues as initiator */ + /* if timeout, local device disconnects outgoing connection and continues as acceptor */ + RFCOMM_TRACE_DEBUG2 ("RFCOMM_ConnectInd start timer for collision, initiator's LCID(0x%x), acceptor's LCID(0x%x)", + p_mcb->lcid, p_mcb->pending_lcid); + + rfc_timer_start(p_mcb, (UINT16)(GKI_get_tick_count()%10 + 2)); + return; + } + else + { + /* we cannot accept connection request from peer at this state */ + /* don't update lcid */ + p_mcb = NULL; + } + } + else + { + /* store mcb even if null */ + rfc_save_lcid_mcb (p_mcb, lcid); + } + + if (p_mcb == NULL) + { + L2CA_ConnectRsp (bd_addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + p_mcb->lcid = lcid; + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &id); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConnectCnf +** +** Description This is a callback function called by L2CAP when +** L2CA_ConnectCnf received. Save L2CAP handle and dispatch +** event to the FSM. +** +*******************************************************************************/ +void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 result) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_ConnectCnf LCID:0x%x", lcid); + return; + } + + if (p_mcb->pending_lcid) + { + /* if peer rejects our connect request but peer's connect request is pending */ + if (result != L2CAP_CONN_OK ) + { + UINT16 i; + UINT8 idx; + + RFCOMM_TRACE_DEBUG1 ("RFCOMM_ConnectCnf retry as acceptor on pending LCID(0x%x)", p_mcb->pending_lcid); + + /* remove mcb from mapping table */ + rfc_save_lcid_mcb (NULL, p_mcb->lcid); + + p_mcb->lcid = p_mcb->pending_lcid; + p_mcb->is_initiator = FALSE; + p_mcb->state = RFC_MX_STATE_IDLE; + + /* store mcb into mapping table */ + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + /* update direction bit */ + for (i = 0; i < RFCOMM_MAX_DLCI; i += 2) + { + if ((idx = p_mcb->port_inx[i]) != 0) + { + p_mcb->port_inx[i] = 0; + p_mcb->port_inx[i+1] = idx; + rfc_cb.port.port[idx - 1].dlci += 1; + RFCOMM_TRACE_DEBUG2 ("RFCOMM MX - DLCI:%d -> %d", i, rfc_cb.port.port[idx - 1].dlci); + } + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &(p_mcb->pending_id)); + return; + } + else + { + RFCOMM_TRACE_DEBUG1 ("RFCOMM_ConnectCnf peer gave up pending LCID(0x%x)", p_mcb->pending_lcid); + + /* Peer gave up his connection request, make sure cleaning up L2CAP channel */ + L2CA_ConnectRsp (p_mcb->bd_addr, p_mcb->pending_id, p_mcb->pending_lcid, L2CAP_CONN_NO_RESOURCES, 0); + + p_mcb->pending_lcid = 0; + } + } + + /* Save LCID to be used in all consecutive calls to L2CAP */ + p_mcb->lcid = lcid; + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_CNF, &result); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConfigInd +** +** Description This is a callback function called by L2CAP when +** L2CA_ConfigInd received. Save parameters in the control +** block and dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_ConfigInd LCID:0x%x", lcid); + return; + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_IND, (void *)p_cfg); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConfigCnf +** +** Description This is a callback function called by L2CAP when +** L2CA_ConfigCnf received. Save L2CAP handle and dispatch +** event to the FSM. +** +*******************************************************************************/ +void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_ConfigCnf no MCB LCID:0x%x", lcid); + return; + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_CNF, (void *)p_cfg); +} + + +/******************************************************************************* +** +** Function RFCOMM_QoSViolationInd +** +** Description This is a callback function called by L2CAP when +** L2CA_QoSViolationIndInd received. Dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_QoSViolationInd (BD_ADDR bd_addr) +{ +} + + +/******************************************************************************* +** +** Function RFCOMM_DisconnectInd +** +** Description This is a callback function called by L2CAP when +** L2CA_DisconnectInd received. Dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_conf_needed) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (is_conf_needed) + { + L2CA_DisconnectRsp (lcid); + } + + if (!p_mcb) + { + RFCOMM_TRACE_WARNING1 ("RFCOMM_DisconnectInd LCID:0x%x", lcid); + return; + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_DISC_IND, NULL); +} + + +/******************************************************************************* +** +** Function RFCOMM_BufDataInd +** +** Description This is a callback function called by L2CAP when +** data RFCOMM frame is received. Parse the frames, check +** the checksum and dispatch event to multiplexer or port +** state machine depending on the frame destination. +** +*******************************************************************************/ +void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + tPORT *p_port; + UINT8 event; + + + if (!p_mcb) + { + RFCOMM_TRACE_WARNING1 ("RFCOMM_BufDataInd LCID:0x%x", lcid); + GKI_freebuf (p_buf); + return; + } + + event = rfc_parse_data (p_mcb, &rfc_cb.rfc.rx_frame, p_buf); + + /* If the frame did not pass validation just ignore it */ + if (event == RFC_EVENT_BAD_FRAME) + { + GKI_freebuf (p_buf); + return; + } + + if (rfc_cb.rfc.rx_frame.dlci == RFCOMM_MX_DLCI) + { + /* Take special care of the Multiplexer Control Messages */ + if (event == RFC_EVENT_UIH) + { + rfc_process_mx_message (p_mcb, p_buf); + return; + } + + /* Other multiplexer events go to state machine */ + rfc_mx_sm_execute (p_mcb, event, NULL); + GKI_freebuf (p_buf); + return; + } + + /* The frame was received on the data channel DLCI, verify that DLC exists */ + if (((p_port = port_find_mcb_dlci_port (p_mcb, rfc_cb.rfc.rx_frame.dlci)) == NULL) + || (!p_port->rfc.p_mcb)) + { + /* If this is a SABME on the new port, check if any appl is waiting for it */ + if (event != RFC_EVENT_SABME) + { + if (( p_mcb->is_initiator && !rfc_cb.rfc.rx_frame.cr) + || (!p_mcb->is_initiator && rfc_cb.rfc.rx_frame.cr)) + rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, rfc_cb.rfc.rx_frame.pf); + GKI_freebuf (p_buf); + return; + } + + if ((p_port = port_find_dlci_port (rfc_cb.rfc.rx_frame.dlci)) == NULL) + { + rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, TRUE); + GKI_freebuf (p_buf); + return; + } + p_mcb->port_inx[rfc_cb.rfc.rx_frame.dlci] = p_port->inx; + p_port->rfc.p_mcb = p_mcb; + } + + if (event == RFC_EVENT_UIH) + { + if (p_buf->len > 0) + rfc_port_sm_execute (p_port, event, p_buf); + else + GKI_freebuf (p_buf); + + if (rfc_cb.rfc.rx_frame.credit != 0) + rfc_inc_credit (p_port, rfc_cb.rfc.rx_frame.credit); + + return; + } + rfc_port_sm_execute (p_port, event, NULL); + GKI_freebuf (p_buf); +} + +/******************************************************************************* +** +** Function RFCOMM_CongestionStatusInd +** +** Description This is a callback function called by L2CAP when +** data RFCOMM L2CAP congestion status changes +** +*******************************************************************************/ +void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_CongestionStatusInd dropped LCID:0x%x", lcid); + return; + } + else + { + RFCOMM_TRACE_EVENT1 ("RFCOMM_CongestionStatusInd LCID:0x%x", lcid); + } + rfc_process_l2cap_congestion (p_mcb, is_congested); +} + +/******************************************************************************* +** +** Function rfc_find_lcid_mcb +** +** Description This function returns MCB block supporting local cid +** +*******************************************************************************/ +tRFC_MCB *rfc_find_lcid_mcb (UINT16 lcid) +{ + tRFC_MCB *p_mcb; + + if (lcid - L2CAP_BASE_APPL_CID >= MAX_L2CAP_CHANNELS) + { + RFCOMM_TRACE_ERROR1 ("rfc_find_lcid_mcb LCID:0x%x", lcid); + return (NULL); + } + else + { + if ((p_mcb = rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID]) != NULL) + { + if (p_mcb->lcid != lcid) + { + RFCOMM_TRACE_WARNING2 ("rfc_find_lcid_mcb LCID reused LCID:0x%x current:0x%x", lcid, p_mcb->lcid); + return (NULL); + } + } + } + return (p_mcb); +} + + +/******************************************************************************* +** +** Function rfc_save_lcid_mcb +** +** Description This function returns MCB block supporting local cid +** +*******************************************************************************/ +void rfc_save_lcid_mcb (tRFC_MCB *p_mcb, UINT16 lcid) +{ + rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID] = p_mcb; +} diff --git a/stack/rfcomm/rfc_mx_fsm.c b/stack/rfcomm/rfc_mx_fsm.c new file mode 100644 index 0000000..2f267a7 --- /dev/null +++ b/stack/rfcomm/rfc_mx_fsm.c @@ -0,0 +1,650 @@ +/***************************************************************************** +** +** Name: rfc_mx_fsm.c +** +** Description: This file contains state machine and action routines for +** multiplexer channel of the RFCOMM unit +** +** +** Copyright (c) 1999-2010, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ +#include <string.h> +#include "gki.h" +#include "bt_types.h" +#include "rfcdefs.h" +#include "l2cdefs.h" +#include "port_api.h" +#include "port_int.h" +#include "l2c_api.h" +#include "rfc_int.h" + +#define L2CAP_SUCCESS 0 +#define L2CAP_ERROR 1 + + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_wait_conn_cnf (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_configure (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_sabme_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_wait_sabme (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_connected (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data); + +static void rfc_mx_send_config_req (tRFC_MCB *p_mcb); +static void rfc_mx_conf_ind (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg); +static void rfc_mx_conf_cnf (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg); + + + +/******************************************************************************* +** +** Function rfc_mx_sm_execute +** +** Description This function sends multiplexor events through the state +** machine. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_execute (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + switch (p_mcb->state) + { + case RFC_MX_STATE_IDLE: + rfc_mx_sm_state_idle (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_WAIT_CONN_CNF: + rfc_mx_sm_state_wait_conn_cnf (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_CONFIGURE: + rfc_mx_sm_state_configure (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_SABME_WAIT_UA: + rfc_mx_sm_sabme_wait_ua (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_WAIT_SABME: + rfc_mx_sm_state_wait_sabme (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_CONNECTED: + rfc_mx_sm_state_connected (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_DISC_WAIT_UA: + rfc_mx_sm_state_disc_wait_ua (p_mcb, event, p_data); + break; + + } +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_idle +** +** Description This function handles events when the multiplexer is in +** IDLE state. This state exists when connection is being +** initially established. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_idle - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_START_REQ: + + /* Initialize L2CAP MTU */ + p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1; + + if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) + { + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + /* Save entry for quicker access to mcb based on the LCID */ + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + p_mcb->state = RFC_MX_STATE_WAIT_CONN_CNF; + return; + + case RFC_MX_EVENT_START_RSP: + case RFC_MX_EVENT_CONN_CNF: + case RFC_MX_EVENT_CONF_IND: + case RFC_MX_EVENT_CONF_CNF: + RFCOMM_TRACE_ERROR2 ("Mx error state %d event %d", p_mcb->state, event); + return; + + case RFC_MX_EVENT_CONN_IND: + + rfc_timer_start (p_mcb, RFCOMM_CONN_TIMEOUT); + L2CA_ConnectRsp (p_mcb->bd_addr, *((UINT8 *)p_data), p_mcb->lcid, L2CAP_CONN_OK, 0); + + rfc_mx_send_config_req (p_mcb); + + p_mcb->state = RFC_MX_STATE_CONFIGURE; + return; + + case RFC_EVENT_SABME: + break; + + case RFC_EVENT_UA: + case RFC_EVENT_DM: + return; + + case RFC_EVENT_DISC: + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, TRUE); + return; + + case RFC_EVENT_UIH: + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, FALSE); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_wait_conn_cnf +** +** Description This function handles events when the multiplexer is +** waiting for Connection Confirm from L2CAP. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_wait_conn_cnf (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_wait_conn_cnf - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_START_REQ: + RFCOMM_TRACE_ERROR2 ("Mx error state %d event %d", p_mcb->state, event); + return; + + /* There is some new timing so that Config Ind comes before security is completed + so we are still waiting fo the confirmation. */ + case RFC_MX_EVENT_CONF_IND: + rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_CONN_CNF: + if (*((UINT16 *)p_data) != L2CAP_SUCCESS) + { + p_mcb->state = RFC_MX_STATE_IDLE; + + PORT_StartCnf (p_mcb, *((UINT16 *)p_data)); + return; + } + p_mcb->state = RFC_MX_STATE_CONFIGURE; + rfc_mx_send_config_req (p_mcb); + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + /* we gave up outgoing connection request then try peer's request */ + if (p_mcb->pending_lcid) + { + UINT16 i; + UINT8 idx; + + RFCOMM_TRACE_DEBUG2 ("RFCOMM MX retry as acceptor in collision case - evt:%d in state:%d", event, p_mcb->state); + + rfc_save_lcid_mcb (NULL, p_mcb->lcid); + p_mcb->lcid = p_mcb->pending_lcid; + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + p_mcb->is_initiator = FALSE; + + /* update direction bit */ + for (i = 0; i < RFCOMM_MAX_DLCI; i += 2) + { + if ((idx = p_mcb->port_inx[i]) != 0) + { + p_mcb->port_inx[i] = 0; + p_mcb->port_inx[i+1] = idx; + rfc_cb.port.port[idx - 1].dlci += 1; + RFCOMM_TRACE_DEBUG2 ("RFCOMM MX - DLCI:%d -> %d", i, rfc_cb.port.port[idx - 1].dlci); + } + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &(p_mcb->pending_id)); + } + else + { + PORT_CloseInd (p_mcb); + } + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_configure +** +** Description This function handles events when the multiplexer in the +** configuration state. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_configure (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_configure - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_START_REQ: + case RFC_MX_EVENT_CONN_CNF: + + RFCOMM_TRACE_ERROR2 ("Mx error state %d event %d", p_mcb->state, event); + return; + + case RFC_MX_EVENT_CONF_IND: + rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_CONF_CNF: + rfc_mx_conf_cnf (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_sabme_wait_ua +** +** Description This function handles events when the multiplexer sent +** SABME and is waiting for UA reply. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_sabme_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_sabme_wait_ua - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_START_REQ: + case RFC_MX_EVENT_CONN_CNF: + RFCOMM_TRACE_ERROR2 ("Mx error state %d event %d", p_mcb->state, event); + return; + + /* workaround: we don't support reconfig */ + /* commented out until we support reconfig + case RFC_MX_EVENT_CONF_IND: + rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_CONF_CNF: + rfc_mx_conf_cnf (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + */ + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_UA: + rfc_timer_stop (p_mcb); + + p_mcb->state = RFC_MX_STATE_CONNECTED; + p_mcb->peer_ready = TRUE; + + PORT_StartCnf (p_mcb, RFCOMM_SUCCESS); + return; + + case RFC_EVENT_DM: + rfc_timer_stop (p_mcb); + /* Case falls through */ + + case RFC_MX_EVENT_CONF_IND: /* workaround: we don't support reconfig */ + case RFC_MX_EVENT_CONF_CNF: /* workaround: we don't support reconfig */ + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + +/******************************************************************************* +** +** Function rfc_mx_sm_state_wait_sabme +** +** Description This function handles events when the multiplexer is +** waiting for SABME on the acceptor side after configuration +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_wait_sabme (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_wait_sabme - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_SABME: + /* if we gave up outgoing connection request */ + if (p_mcb->pending_lcid) + { + p_mcb->pending_lcid = 0; + + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + + rfc_timer_stop (p_mcb); + p_mcb->state = RFC_MX_STATE_CONNECTED; + p_mcb->peer_ready = TRUE; + + /* MX channel collision has been resolved, continue to open ports */ + PORT_StartCnf (p_mcb, RFCOMM_SUCCESS); + } + else + { + rfc_timer_stop (p_mcb); + PORT_StartInd (p_mcb); + } + return; + + case RFC_MX_EVENT_START_RSP: + if (*((UINT16 *)p_data) != RFCOMM_SUCCESS) + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, TRUE); + else + { + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + + p_mcb->state = RFC_MX_STATE_CONNECTED; + p_mcb->peer_ready = TRUE; + } + return; + + case RFC_MX_EVENT_CONF_IND: /* workaround: we don't support reconfig */ + case RFC_MX_EVENT_CONF_CNF: /* workaround: we don't support reconfig */ + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + PORT_CloseInd (p_mcb); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_connected +** +** Description This function handles events when the multiplexer is +** in the CONNECTED state +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_connected (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_connected - evt:%d", event); + + switch (event) + { + case RFC_EVENT_TIMEOUT: + case RFC_MX_EVENT_CLOSE_REQ: + rfc_timer_start (p_mcb, RFC_DISC_TIMEOUT); + p_mcb->state = RFC_MX_STATE_DISC_WAIT_UA; + rfc_send_disc (p_mcb, RFCOMM_MX_DLCI); + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_DISC: + /* Reply with UA. If initiator bring down L2CAP connection */ + /* If server wait for some time if client decide to reinitiate channel */ + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + if (p_mcb->is_initiator) + { + L2CA_DisconnectReq (p_mcb->lcid); + } + /* notify all ports that connection is gone */ + PORT_CloseInd (p_mcb); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_disc_wait_ua +** +** Description This function handles events when the multiplexer sent +** DISC and is waiting for UA reply. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + BT_HDR *p_buf; + + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_disc_wait_ua - evt:%d", event); + switch (event) + { + case RFC_EVENT_UA: + case RFC_EVENT_DM: + case RFC_EVENT_TIMEOUT: + L2CA_DisconnectReq (p_mcb->lcid); + + if (p_mcb->restart_required) + { + /* Start Request was received while disconnecting. Execute it again */ + if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) + { + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + /* Save entry for quicker access to mcb based on the LCID */ + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + /* clean up before reuse it */ + while ((p_buf = (BT_HDR *)GKI_dequeue(&p_mcb->cmd_q)) != NULL) + GKI_freebuf(p_buf); + + rfc_timer_start (p_mcb, RFC_MCB_INIT_INACT_TIMER); + + p_mcb->is_initiator = TRUE; + p_mcb->restart_required = FALSE; + p_mcb->local_cfg_sent = FALSE; + p_mcb->peer_cfg_rcvd = FALSE; + + p_mcb->state = RFC_MX_STATE_WAIT_CONN_CNF; + return; + } + rfc_release_multiplexer_channel (p_mcb); + return; + + case RFC_EVENT_DISC: + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, FALSE); + return; + + case RFC_MX_EVENT_START_REQ: + p_mcb->restart_required = TRUE; + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_MX_EVENT_CLOSE_REQ: + return; + + case RFC_MX_EVENT_QOS_VIOLATION_IND: + break; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_send_config_req +** +** Description This function handles L2CA_ConnectInd message from the +** L2CAP. Accept connection. +** +*******************************************************************************/ +static void rfc_mx_send_config_req (tRFC_MCB *p_mcb) +{ + tL2CAP_CFG_INFO cfg; + + RFCOMM_TRACE_EVENT0 ("rfc_mx_send_config_req"); + + memset (&cfg, 0, sizeof (tL2CAP_CFG_INFO)); + + cfg.mtu_present = TRUE; + cfg.mtu = L2CAP_MTU_SIZE; + +/* Defaults set by memset + cfg.flush_to_present = FALSE; + cfg.qos_present = FALSE; + cfg.fcr_present = FALSE; + cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + cfg.fcs_present = FALSE; + cfg.fcs = N/A when fcs_present is FALSE; +*/ + L2CA_ConfigReq (p_mcb->lcid, &cfg); +} + + +/******************************************************************************* +** +** Function rfc_mx_conf_cnf +** +** Description This function handles L2CA_ConfigCnf message from the +** L2CAP. If result is not success tell upper layer that +** start has not been accepted. If initiator send SABME +** on DLCI 0. T1 is still running. +** +*******************************************************************************/ +static void rfc_mx_conf_cnf (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg) +{ + RFCOMM_TRACE_EVENT2 ("rfc_mx_conf_cnf p_cfg:%08x res:%d ", p_cfg, (p_cfg) ? p_cfg->result : 0); + + if (p_cfg->result != L2CAP_CFG_OK) + { + if (p_mcb->is_initiator) + { + PORT_StartCnf (p_mcb, p_cfg->result); + L2CA_DisconnectReq (p_mcb->lcid); + } + rfc_release_multiplexer_channel (p_mcb); + return; + } + + p_mcb->local_cfg_sent = TRUE; + if ((p_mcb->state == RFC_MX_STATE_CONFIGURE) && p_mcb->peer_cfg_rcvd) + { + if (p_mcb->is_initiator) + { + p_mcb->state = RFC_MX_STATE_SABME_WAIT_UA; + rfc_send_sabme (p_mcb, RFCOMM_MX_DLCI); + rfc_timer_start (p_mcb, RFC_T1_TIMEOUT); + } + else + { + p_mcb->state = RFC_MX_STATE_WAIT_SABME; + rfc_timer_start (p_mcb, RFC_T2_TIMEOUT); + } + } +} + + +/******************************************************************************* +** +** Function rfc_mx_conf_ind +** +** Description This function handles L2CA_ConfigInd message from the +** L2CAP. Send the L2CA_ConfigRsp message. +** +*******************************************************************************/ +static void rfc_mx_conf_ind (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg) +{ + /* Save peer L2CAP MTU if present */ + /* RFCOMM adds 3-4 bytes in the beginning and 1 bytes FCS */ + if (p_cfg->mtu_present) + p_mcb->peer_l2cap_mtu = p_cfg->mtu - RFCOMM_MIN_OFFSET - 1; + else + p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1; + + p_cfg->mtu_present = FALSE; + p_cfg->flush_to_present = FALSE; + p_cfg->qos_present = FALSE; + + p_cfg->result = L2CAP_CFG_OK; + + L2CA_ConfigRsp (p_mcb->lcid, p_cfg); + + p_mcb->peer_cfg_rcvd = TRUE; + if ((p_mcb->state == RFC_MX_STATE_CONFIGURE) && p_mcb->local_cfg_sent) + { + if (p_mcb->is_initiator) + { + p_mcb->state = RFC_MX_STATE_SABME_WAIT_UA; + rfc_send_sabme (p_mcb, RFCOMM_MX_DLCI); + rfc_timer_start (p_mcb, RFC_T1_TIMEOUT); + } + else + { + p_mcb->state = RFC_MX_STATE_WAIT_SABME; + rfc_timer_start (p_mcb, RFC_T2_TIMEOUT); + } + } +} diff --git a/stack/rfcomm/rfc_port_fsm.c b/stack/rfcomm/rfc_port_fsm.c new file mode 100644 index 0000000..2d34edb --- /dev/null +++ b/stack/rfcomm/rfc_port_fsm.c @@ -0,0 +1,902 @@ +/***************************************************************************** +** +** Name: rfc_port_fsm.c +** +** Description: This file contains state machine and action routines for +** a port of the RFCOMM unit +** +** +** Copyright (c) 1999-2010, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ +#include <string.h> +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "btm_api.h" +#include "btm_int.h" +#include "port_api.h" +#include "port_int.h" +#include "rfc_int.h" + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void rfc_port_sm_state_closed (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_sabme_wait_ua (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_opened (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_orig_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_term_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_disc_wait_ua (tPORT *p_port, UINT16 event, void *p_data); + +static void rfc_port_uplink_data (tPORT *p_port, BT_HDR *p_buf); + +static void rfc_set_port_state(tPORT_STATE *port_pars, MX_FRAME *p_frame); + + +/******************************************************************************* +** +** Function rfc_port_sm_execute +** +** Description This function sends port events through the state +** machine. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_execute (tPORT *p_port, UINT16 event, void *p_data) +{ + if (!p_port) + { + RFCOMM_TRACE_WARNING1 ("NULL port event %d", event); + return; + } + + switch (p_port->rfc.state) + { + case RFC_STATE_CLOSED: + rfc_port_sm_state_closed (p_port, event, p_data); + break; + + case RFC_STATE_SABME_WAIT_UA: + rfc_port_sm_sabme_wait_ua (p_port, event, p_data); + break; + + case RFC_STATE_ORIG_WAIT_SEC_CHECK: + rfc_port_sm_orig_wait_sec_check (p_port, event, p_data); + break; + + case RFC_STATE_TERM_WAIT_SEC_CHECK: + rfc_port_sm_term_wait_sec_check (p_port, event, p_data); + break; + + case RFC_STATE_OPENED: + rfc_port_sm_opened (p_port, event, p_data); + break; + + case RFC_STATE_DISC_WAIT_UA: + rfc_port_sm_disc_wait_ua (p_port, event, p_data); + break; + } +} + + +/******************************************************************************* +** +** Function rfc_port_sm_state_closed +** +** Description This function handles events when the port is in +** CLOSED state. This state exists when port is +** being initially established. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_state_closed (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_OPEN: + p_port->rfc.state = RFC_STATE_ORIG_WAIT_SEC_CHECK; + btm_sec_mx_access_request (p_port->rfc.p_mcb->bd_addr, BT_PSM_RFCOMM, TRUE, + BTM_SEC_PROTO_RFCOMM, (UINT32)(p_port->dlci / 2), + &rfc_sec_check_complete, p_port); + return; + + case RFC_EVENT_CLOSE: + break; + + case RFC_EVENT_CLEAR: + return; + + case RFC_EVENT_DATA: + GKI_freebuf (p_data); + break; + + case RFC_EVENT_SABME: + /* make sure the multiplexer disconnect timer is not running (reconnect case) */ + rfc_timer_stop(p_port->rfc.p_mcb ); + + /* Open will be continued after security checks are passed */ + p_port->rfc.state = RFC_STATE_TERM_WAIT_SEC_CHECK; + btm_sec_mx_access_request (p_port->rfc.p_mcb->bd_addr, BT_PSM_RFCOMM, FALSE, + BTM_SEC_PROTO_RFCOMM, (UINT32)(p_port->dlci / 2), + &rfc_sec_check_complete, p_port); + return; + + case RFC_EVENT_UA: + return; + + case RFC_EVENT_DM: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); + return; + + case RFC_EVENT_DISC: + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); + return; + + case RFC_EVENT_TIMEOUT: + Port_TimeOutCloseMux( p_port->rfc.p_mcb ) ; + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + } + + RFCOMM_TRACE_WARNING1 ("Port state closed Event ignored %d", event); + return; +} + +/******************************************************************************* +** +** Function rfc_port_sm_sabme_wait_ua +** +** Description This function handles events when SABME on the DLC was +** sent and SM is waiting for UA or DM. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_sabme_wait_ua (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_OPEN: + case RFC_EVENT_ESTABLISH_RSP: + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLOSE: + rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT); + rfc_send_disc (p_port->rfc.p_mcb, p_port->dlci); + p_port->rfc.expected_rsp = 0; + p_port->rfc.state = RFC_STATE_DISC_WAIT_UA; + return; + + case RFC_EVENT_CLEAR: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + GKI_freebuf (p_data); + break; + + case RFC_EVENT_UA: + rfc_port_timer_stop (p_port); + p_port->rfc.state = RFC_STATE_OPENED; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_SUCCESS); + return; + + case RFC_EVENT_DM: + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DISC: + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_SABME: + /* Continue to wait for the UA the SABME this side sent */ + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + return; + + case RFC_EVENT_TIMEOUT: + p_port->rfc.state = RFC_STATE_CLOSED; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); + return; + } + RFCOMM_TRACE_WARNING1 ("Port state sabme_wait_ua Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_term_wait_sec_check +** +** Description This function handles events for the port in the +** WAIT_SEC_CHECK state. SABME has been received from the +** peer and Security Manager verifes BD_ADDR, before we can +** send ESTABLISH_IND to the Port entity +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_term_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_SEC_COMPLETE: + if (*((UINT8 *)p_data) != BTM_SUCCESS) + { + /* Authentication/authorization failed. If link is still */ + /* up send DM and check if we need to start inactive timer */ + if (p_port->rfc.p_mcb) + { + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + port_rfc_closed (p_port, PORT_SEC_FAILED); + } + } + else + { + PORT_DlcEstablishInd (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu); + } + return; + + case RFC_EVENT_OPEN: + case RFC_EVENT_CLOSE: + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLEAR: + btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + RFCOMM_TRACE_ERROR0 ("Port error state Term Wait Sec event Data"); + GKI_freebuf (p_data); + return; + + case RFC_EVENT_SABME: + /* Ignore SABME retransmission if client dares to do so */ + return; + + case RFC_EVENT_DISC: + btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); + p_port->rfc.state = RFC_STATE_CLOSED; + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + + PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + return; + + case RFC_EVENT_ESTABLISH_RSP: + if (*((UINT8 *)p_data) != RFCOMM_SUCCESS) + { + if (p_port->rfc.p_mcb) + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + } + else + { + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + p_port->rfc.state = RFC_STATE_OPENED; + } + return; + } + RFCOMM_TRACE_WARNING1 ("Port state term_wait_sec_check Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_orig_wait_sec_check +** +** Description This function handles events for the port in the +** ORIG_WAIT_SEC_CHECK state. RFCOMM is waiting for Security +** manager to finish before sending SABME to the peer +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_orig_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_SEC_COMPLETE: + if (*((UINT8 *)p_data) != BTM_SUCCESS) + { + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, 0, RFCOMM_SECURITY_ERR); + rfc_port_closed (p_port); + return; + } + rfc_send_sabme (p_port->rfc.p_mcb, p_port->dlci); + rfc_port_timer_start (p_port, RFC_PORT_T1_TIMEOUT); + p_port->rfc.state = RFC_STATE_SABME_WAIT_UA; + return; + + case RFC_EVENT_OPEN: + case RFC_EVENT_SABME: /* Peer should not use the same dlci */ + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLOSE: + btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + RFCOMM_TRACE_ERROR0 ("Port error state Orig Wait Sec event Data"); + GKI_freebuf (p_data); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + return; + } + RFCOMM_TRACE_WARNING1 ("Port state orig_wait_sec_check Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_opened +** +** Description This function handles events for the port in the OPENED +** state +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_opened (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_OPEN: + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLOSE: + rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT); + rfc_send_disc (p_port->rfc.p_mcb, p_port->dlci); + p_port->rfc.expected_rsp = 0; + p_port->rfc.state = RFC_STATE_DISC_WAIT_UA; + return; + + case RFC_EVENT_CLEAR: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + /* Send credits in the frame. Pass them in the layer specific member of the hdr. */ + /* There might be an initial case when we reduced rx_max and credit_rx is still */ + /* bigger. Make sure that we do not send 255 */ + if ((p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + && (((BT_HDR *)p_data)->len < p_port->peer_mtu) + && (!p_port->rx.user_fc) + && (p_port->credit_rx_max > p_port->credit_rx)) + { + ((BT_HDR *)p_data)->layer_specific = (UINT8) (p_port->credit_rx_max - p_port->credit_rx); + p_port->credit_rx = p_port->credit_rx_max; + } + else + { + ((BT_HDR *)p_data)->layer_specific = 0; + } + rfc_send_buf_uih (p_port->rfc.p_mcb, p_port->dlci, (BT_HDR *)p_data); + rfc_dec_credit (p_port); + return; + + case RFC_EVENT_UA: + return; + + case RFC_EVENT_SABME: + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_DM: + PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DISC: + p_port->rfc.state = RFC_STATE_CLOSED; + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + if(p_port->rx.queue.count) + { + /* give a chance to upper stack to close port properly */ + RFCOMM_TRACE_DEBUG0("port queue is not empty"); + rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT); + } + else + PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_UIH: + rfc_port_uplink_data (p_port, (BT_HDR *)p_data); + return; + + case RFC_EVENT_TIMEOUT: + Port_TimeOutCloseMux( p_port->rfc.p_mcb ) ; + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + } + RFCOMM_TRACE_WARNING1 ("Port state opened Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_disc_wait_ua +** +** Description This function handles events when DISC on the DLC was +** sent and SM is waiting for UA or DM. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_disc_wait_ua (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_OPEN: + case RFC_EVENT_ESTABLISH_RSP: + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLEAR: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + GKI_freebuf (p_data); + return; + + case RFC_EVENT_UA: + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + /* Case falls through */ + + case RFC_EVENT_DM: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_SABME: + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + return; + + case RFC_EVENT_DISC: + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); + return; + + case RFC_EVENT_TIMEOUT: + rfc_port_closed (p_port); + return; + } + + RFCOMM_TRACE_WARNING1 ("Port state disc_wait_ua Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_uplink_data +** +** Description This function handles uplink information data frame. +** +*******************************************************************************/ +void rfc_port_uplink_data (tPORT *p_port, BT_HDR *p_buf) +{ + PORT_DataInd (p_port->rfc.p_mcb, p_port->dlci, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_process_pn +** +** Description This function handles DLC parameter negotiation frame. +** Record MTU and pass indication to the upper layer. +** +*******************************************************************************/ +void rfc_process_pn (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) +{ + tPORT *p_port; + UINT8 dlci = p_frame->dlci; + + if (is_command) + { + /* Ignore if Multiplexer is being shut down */ + if (p_mcb->state != RFC_MX_STATE_DISC_WAIT_UA) + { + PORT_ParNegInd (p_mcb, dlci, p_frame->u.pn.mtu, + p_frame->u.pn.conv_layer, p_frame->u.pn.k); + } + else + { + rfc_send_dm(p_mcb, dlci, FALSE); + RFCOMM_TRACE_WARNING0("***** MX PN while disconnecting *****"); + } + + return; + } + /* If we are not awaiting response just ignore it */ + p_port = port_find_mcb_dlci_port (p_mcb, dlci); + if ((p_port == NULL) || !(p_port->rfc.expected_rsp & RFC_RSP_PN)) + return; + + p_port->rfc.expected_rsp &= ~RFC_RSP_PN; + + rfc_port_timer_stop (p_port); + + PORT_ParNegCnf (p_mcb, dlci, p_frame->u.pn.mtu, + p_frame->u.pn.conv_layer, p_frame->u.pn.k); +} + + +/******************************************************************************* +** +** Function rfc_process_rpn +** +** Description This function handles Remote DLC parameter negotiation +** command/response. Pass command to the user. +** +*******************************************************************************/ +void rfc_process_rpn (tRFC_MCB *p_mcb, BOOLEAN is_command, + BOOLEAN is_request, MX_FRAME *p_frame) +{ + tPORT_STATE port_pars; + tPORT *p_port; + + if ((p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci)) == NULL) + { + /* This is the first command on the port */ + if (is_command) + { + + memset(&port_pars, 0, sizeof(tPORT_STATE)); + rfc_set_port_state(&port_pars, p_frame); + + PORT_PortNegInd(p_mcb, p_frame->dlci, &port_pars, p_frame->u.rpn.param_mask); + } + return; + } + + if (is_command && is_request) + { + /* This is the special situation when peer just request local pars */ + port_pars = p_port->peer_port_pars; + rfc_send_rpn (p_mcb, p_frame->dlci, FALSE, &p_port->peer_port_pars, 0); + return; + } + + port_pars = p_port->peer_port_pars; + + rfc_set_port_state(&port_pars, p_frame); + + if (is_command) + { + PORT_PortNegInd (p_mcb, p_frame->dlci, &port_pars, p_frame->u.rpn.param_mask); + return; + } + + /* If we are not awaiting response just ignore it */ + p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci); + if ((p_port == NULL) || !(p_port->rfc.expected_rsp & (RFC_RSP_RPN | RFC_RSP_RPN_REPLY))) + return; + + /* If we sent a request for port parameters to the peer he is replying with */ + /* mask 0. */ + rfc_port_timer_stop (p_port); + + if (p_port->rfc.expected_rsp & RFC_RSP_RPN_REPLY) + { + p_port->rfc.expected_rsp &= ~RFC_RSP_RPN_REPLY; + + p_port->peer_port_pars = port_pars; + + if ((port_pars.fc_type == (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT)) + || (port_pars.fc_type == (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT))) + { + /* This is satisfactory port parameters. Set mask as it was Ok */ + p_frame->u.rpn.param_mask = RFCOMM_RPN_PM_MASK; + } + else + { + /* Current peer parameters are not good, try to fix them */ + p_port->peer_port_pars.fc_type = (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT); + + p_port->rfc.expected_rsp |= RFC_RSP_RPN; + rfc_send_rpn (p_mcb, p_frame->dlci, TRUE, &p_port->peer_port_pars, + RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + return; + } + } + else + p_port->rfc.expected_rsp &= ~RFC_RSP_RPN; + + /* Check if all suggested parameters were accepted */ + if (((p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT)) == + (RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT)) + || ((p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT)) == + (RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT))) + { + PORT_PortNegCnf (p_mcb, p_port->dlci, &port_pars, RFCOMM_SUCCESS); + return; + } + + /* If we were proposing RTR flow control try RTC flow control */ + /* If we were proposing RTC flow control try no flow control */ + /* otherwise drop the connection */ + if (p_port->peer_port_pars.fc_type == (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT)) + { + /* Current peer parameters are not good, try to fix them */ + p_port->peer_port_pars.fc_type = (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT); + + p_port->rfc.expected_rsp |= RFC_RSP_RPN; + + rfc_send_rpn (p_mcb, p_frame->dlci, TRUE, &p_port->peer_port_pars, + RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + return; + } + + /* Other side does not support flow control */ + if (p_port->peer_port_pars.fc_type == (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT)) + { + p_port->peer_port_pars.fc_type = RFCOMM_FC_OFF; + PORT_PortNegCnf (p_mcb, p_port->dlci, &port_pars, RFCOMM_SUCCESS); + } +} + + +/******************************************************************************* +** +** Function rfc_process_msc +** +** Description This function handles Modem Status Command. +** Pass command to the user. +** +*******************************************************************************/ +void rfc_process_msc (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) +{ + tPORT_CTRL pars; + tPORT *p_port; + UINT8 modem_signals = p_frame->u.msc.signals; + BOOLEAN new_peer_fc = FALSE; + + p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci); + if (p_port == NULL) + return; + + pars.modem_signal = 0; + + if (modem_signals & RFCOMM_MSC_RTC) + pars.modem_signal |= MODEM_SIGNAL_DTRDSR; + + if (modem_signals & RFCOMM_MSC_RTR) + pars.modem_signal |= MODEM_SIGNAL_RTSCTS; + + if (modem_signals & RFCOMM_MSC_IC) + pars.modem_signal |= MODEM_SIGNAL_RI; + + if (modem_signals & RFCOMM_MSC_DV) + pars.modem_signal |= MODEM_SIGNAL_DCD; + + pars.fc = ((modem_signals & RFCOMM_MSC_FC) == RFCOMM_MSC_FC); + + pars.break_signal = (p_frame->u.msc.break_present) ? + p_frame->u.msc.break_duration : 0; + pars.discard_buffers = 0; + pars.break_signal_seq = RFCOMM_CTRL_BREAK_IN_SEQ; /* this is default */ + + /* Check if this command is passed only to indicate flow control */ + if (is_command) + { + rfc_send_msc (p_mcb, p_frame->dlci, FALSE, &pars); + + if (p_port->rfc.p_mcb->flow != PORT_FC_CREDIT) + { + /* Spec 1.1 indicates that only FC bit is used for flow control */ + p_port->peer_ctrl.fc = new_peer_fc = pars.fc; + + if (new_peer_fc != p_port->tx.peer_fc) + PORT_FlowInd (p_mcb, p_frame->dlci, (BOOLEAN)!new_peer_fc); + } + + PORT_ControlInd (p_mcb, p_frame->dlci, &pars); + + return; + } + + /* If we are not awaiting response just ignore it */ + if (!(p_port->rfc.expected_rsp & RFC_RSP_MSC)) + return; + + p_port->rfc.expected_rsp &= ~RFC_RSP_MSC; + + rfc_port_timer_stop (p_port); + + PORT_ControlCnf (p_port->rfc.p_mcb, p_port->dlci, &pars); +} + + +/******************************************************************************* +** +** Function rfc_process_rls +** +** Description This function handles Remote Line Status command. +** Pass command to the user. +** +*******************************************************************************/ +void rfc_process_rls (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) +{ + tPORT *p_port; + + if (is_command) + { + PORT_LineStatusInd (p_mcb, p_frame->dlci, p_frame->u.rls.line_status); + rfc_send_rls (p_mcb, p_frame->dlci, FALSE, p_frame->u.rls.line_status); + } + else + { + p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci); + + /* If we are not awaiting response just ignore it */ + if (!p_port || !(p_port->rfc.expected_rsp & RFC_RSP_RLS)) + return; + + p_port->rfc.expected_rsp &= ~RFC_RSP_RLS; + + rfc_port_timer_stop (p_port); + } +} + + +/******************************************************************************* +** +** Function rfc_process_nsc +** +** Description This function handles None Supported Command frame. +** +*******************************************************************************/ +void rfc_process_nsc (tRFC_MCB *p_mcb, MX_FRAME *p_frame) +{ +} + + +/******************************************************************************* +** +** Function rfc_process_test +** +** Description This function handles Test frame. If this is a command +** reply to it. Otherwise pass response to the user. +** +*******************************************************************************/ +void rfc_process_test_rsp (tRFC_MCB *p_mcb, BT_HDR *p_buf) +{ + GKI_freebuf (p_buf); +} + + +/******************************************************************************* +** +** Function rfc_process_fcon +** +** Description This function handles FCON frame. The peer entity is able +** to receive new information +** +*******************************************************************************/ +void rfc_process_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + if (is_command) + { + rfc_cb.rfc.peer_rx_disabled = FALSE; + + rfc_send_fcon (p_mcb, FALSE); + + if (!p_mcb->l2cap_congested) + PORT_FlowInd (p_mcb, 0, TRUE); + } +} + +/******************************************************************************* +** +** Function rfc_process_fcoff +** +** Description This function handles FCOFF frame. The peer entity is unable +** to receive new information +** +*******************************************************************************/ +void rfc_process_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + if (is_command) + { + rfc_cb.rfc.peer_rx_disabled = TRUE; + + if (!p_mcb->l2cap_congested) + PORT_FlowInd (p_mcb, 0, FALSE); + + rfc_send_fcoff (p_mcb, FALSE); + } +} + + +/******************************************************************************* +** +** Function rfc_process_l2cap_congestion +** +** Description This function handles L2CAP congestion messages +** +*******************************************************************************/ +void rfc_process_l2cap_congestion (tRFC_MCB *p_mcb, BOOLEAN is_congested) +{ + p_mcb->l2cap_congested = is_congested; + + if (!is_congested) + { + rfc_check_send_cmd(p_mcb, NULL); + } + + if (!rfc_cb.rfc.peer_rx_disabled) + { + if (!is_congested) + PORT_FlowInd (p_mcb, 0, TRUE); + else + PORT_FlowInd (p_mcb, 0, FALSE); + } +} + +/******************************************************************************* +** +** Function rfc_set_port_pars +** +** Description This function sets the tPORT_STATE structure given a p_frame. +** +*******************************************************************************/ + +void rfc_set_port_state(tPORT_STATE *port_pars, MX_FRAME *p_frame) +{ + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_BIT_RATE) + port_pars->baud_rate = p_frame->u.rpn.baud_rate; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_DATA_BITS) + port_pars->byte_size = p_frame->u.rpn.byte_size; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_STOP_BITS) + port_pars->stop_bits = p_frame->u.rpn.stop_bits; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_PARITY) + port_pars->parity = p_frame->u.rpn.parity; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_PARITY_TYPE) + port_pars->parity_type = p_frame->u.rpn.parity_type; + if (p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_XONXOFF_ON_INPUT | + RFCOMM_RPN_PM_XONXOFF_ON_OUTPUT | + RFCOMM_RPN_PM_RTR_ON_INPUT | + RFCOMM_RPN_PM_RTR_ON_OUTPUT | + RFCOMM_RPN_PM_RTC_ON_INPUT | + RFCOMM_RPN_PM_RTC_ON_OUTPUT)) + port_pars->fc_type = p_frame->u.rpn.fc_type; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_XON_CHAR) + port_pars->xon_char = p_frame->u.rpn.xon_char; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_XOFF_CHAR) + port_pars->xoff_char = p_frame->u.rpn.xoff_char; +} + diff --git a/stack/rfcomm/rfc_port_if.c b/stack/rfcomm/rfc_port_if.c new file mode 100644 index 0000000..03fb339 --- /dev/null +++ b/stack/rfcomm/rfc_port_if.c @@ -0,0 +1,325 @@ +/*****************************************************************************/ +/* */ +/* Name: rfc_port_if.c */ +/* */ +/* Description: This file contains functions callable by an application */ +/* running on top of RFCOMM */ +/* */ +/* */ +/* Copyright (c) 1999-2004, WIDCOMM Inc., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ +#include <string.h> +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "l2c_api.h" +#include "port_int.h" +#include "rfc_int.h" + + +#if RFC_DYNAMIC_MEMORY == FALSE +tRFC_CB rfc_cb; +#endif + +/******************************************************************************* +** +** Function RFCOMM_StartReq +** +** Description This function handles Start Request from the upper layer. +** If RFCOMM multiplexer channel can not be allocated +** send start not accepted confirmation. Otherwise dispatch +** start event to the state machine. +** +*******************************************************************************/ +void RFCOMM_StartReq (tRFC_MCB *p_mcb) +{ + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_START_REQ, NULL); +} + + +/******************************************************************************* +** +** Function RFCOMM_StartRsp +** +** Description This function handles Start Response from the upper layer. +** Save upper layer handle and result of the Start Indication +** in the control block and dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_StartRsp (tRFC_MCB *p_mcb, UINT16 result) +{ + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_START_RSP, &result); +} + + +/******************************************************************************* +** +** Function RFCOMM_DlcEstablishReq +** +** Description This function is called by the user app to establish +** connection with the specific dlci on a specific bd device. +** It will allocate RFCOMM connection control block if not +** allocated before and dispatch open event to the state +** machine. +** +*******************************************************************************/ +void RFCOMM_DlcEstablishReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + { + PORT_DlcEstablishCnf (p_mcb, dlci, 0, RFCOMM_ERROR); + return; + } + + rfc_port_sm_execute(p_port, RFC_EVENT_OPEN, NULL); +} + + +/******************************************************************************* +** +** Function RFCOMM_DlcEstablishRsp +** +** Description This function is called by the port emulation entity +** acks Establish Indication. +** +*******************************************************************************/ +void RFCOMM_DlcEstablishRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if ((p_mcb->state != RFC_MX_STATE_CONNECTED) && (result == RFCOMM_SUCCESS)) + { + PORT_DlcReleaseInd (p_mcb, dlci); + return; + } + + rfc_port_sm_execute(p_port, RFC_EVENT_ESTABLISH_RSP, &result); +} + + +/******************************************************************************* +** +** Function RFCOMM_ParNegReq +** +** Description This function is called by the user app to start +** DLC parameter negotiation. Port emulation can send this +** request before actually establishing the DLC. In this +** case the function will allocate RFCOMM connection control +** block. +** +*******************************************************************************/ +void RFCOMM_ParNegReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT8 flow; + UINT8 cl; + UINT8 k; + + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + { + p_port->error = PORT_PAR_NEG_FAILED; + return; + } + + /* Negotiate the flow control mechanism. If flow control mechanism for */ + /* mux has not been set yet, use our default value. If it has been set, */ + /* use that value. */ + flow = (p_mcb->flow == PORT_FC_UNDEFINED) ? PORT_FC_DEFAULT : p_mcb->flow; + + /* Set convergence layer and number of credits (k) */ + if (flow == PORT_FC_CREDIT) + { + cl = RFCOMM_PN_CONV_LAYER_CBFC_I; + k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + p_port->credit_rx = k; + } + else + { + cl = RFCOMM_PN_CONV_LAYER_TYPE_1; + k = 0; + } + + /* Send Parameter Negotiation Command UIH frame */ + p_port->rfc.expected_rsp |= RFC_RSP_PN; + + rfc_send_pn (p_mcb, dlci, TRUE, mtu, cl, k); + + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; +} + + +/******************************************************************************* +** +** Function RFCOMM_ParNegRsp +** +** Description This function is called by the user app to acknowledge +** DLC parameter negotiation. +** +*******************************************************************************/ +void RFCOMM_ParNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) +{ + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + return; + + /* Send Parameter Negotiation Response UIH frame */ + rfc_send_pn (p_mcb, dlci, FALSE, mtu, cl, k); +} + + +/******************************************************************************* +** +** Function RFCOMM_PortNegReq +** +** Description This function is called by the user app to start +** Remote Port parameter negotiation. Port emulation can +** send this request before actually establishing the DLC. +** In this case the function will allocate RFCOMM connection +** control block. +** +*******************************************************************************/ +void RFCOMM_PortNegReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + { + PORT_PortNegCnf (p_mcb, dlci, NULL, RFCOMM_ERROR); + return; + } + + /* Send Parameter Negotiation Command UIH frame */ + if (!p_pars) + p_port->rfc.expected_rsp |= RFC_RSP_RPN_REPLY; + else + p_port->rfc.expected_rsp |= RFC_RSP_RPN; + + rfc_send_rpn (p_mcb, dlci, TRUE, p_pars, RFCOMM_RPN_PM_MASK); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + +} + + +/******************************************************************************* +** +** Function RFCOMM_PortNegRsp +** +** Description This function is called by the user app to acknowledge +** Port parameters negotiation. +** +*******************************************************************************/ +void RFCOMM_PortNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, + UINT16 param_mask) +{ + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + return; + + rfc_send_rpn (p_mcb, dlci, FALSE, p_pars, param_mask); +} + + +/******************************************************************************* +** +** Function RFCOMM_ControlReq +** +** Description This function is called by the port entity to send control +** parameters to remote port emulation entity. +** +*******************************************************************************/ +void RFCOMM_ControlReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if ((p_port->state != PORT_STATE_OPENED) + || (p_port->rfc.state != RFC_STATE_OPENED)) + return; + + p_port->port_ctrl |= PORT_CTRL_REQ_SENT; + + p_port->rfc.expected_rsp |= RFC_RSP_MSC; + + rfc_send_msc (p_mcb, dlci, TRUE, p_pars); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + +} + + +/******************************************************************************* +** +** Function RFCOMM_FlowReq +** +** Description This function is called by the port entity when flow +** control state has changed. Enable flag passed shows if +** port can accept more data. +** +*******************************************************************************/ +void RFCOMM_FlowReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 enable) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if ((p_port->state != PORT_STATE_OPENED) + || (p_port->rfc.state != RFC_STATE_OPENED)) + return; + + p_port->local_ctrl.fc = !enable; + + p_port->rfc.expected_rsp |= RFC_RSP_MSC; + + rfc_send_msc (p_mcb, dlci, TRUE, &p_port->local_ctrl); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + +} + + +/******************************************************************************* +** +** Function RFCOMM_LineStatusReq +** +** Description This function is called by the port entity when line +** status should be delivered to the peer. +** +*******************************************************************************/ +void RFCOMM_LineStatusReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 status) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if ((p_port->state != PORT_STATE_OPENED) + || (p_port->rfc.state != RFC_STATE_OPENED)) + return; + + p_port->rfc.expected_rsp |= RFC_RSP_RLS; + + rfc_send_rls (p_mcb, dlci, TRUE, status); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT); +} + + +/******************************************************************************* +** +** Function RFCOMM_DlcReleaseReq +** +** Description This function is called by the PORT unit to close DLC +** +*******************************************************************************/ +void RFCOMM_DlcReleaseReq (tRFC_MCB *p_mcb, UINT8 dlci) +{ + rfc_port_sm_execute(port_find_mcb_dlci_port (p_mcb, dlci), RFC_EVENT_CLOSE, 0); +} + + +/******************************************************************************* +** +** Function RFCOMM_DataReq +** +** Description This function is called by the user app to send data buffer +** +*******************************************************************************/ +void RFCOMM_DataReq (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) +{ + rfc_port_sm_execute(port_find_mcb_dlci_port (p_mcb, dlci), RFC_EVENT_DATA, p_buf); +} + + diff --git a/stack/rfcomm/rfc_ts_frames.c b/stack/rfcomm/rfc_ts_frames.c new file mode 100644 index 0000000..ad9aaef --- /dev/null +++ b/stack/rfcomm/rfc_ts_frames.c @@ -0,0 +1,895 @@ +/***************************************************************************** +** +** Name: rfc_ts_frames.c +** +** Description: This file contains functions to send TS 07.10 frames +** +** +** Copyright (c) 1999-2008, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +******************************************************************************/ +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "l2c_api.h" +#include "port_int.h" +#include "rfc_int.h" + +/******************************************************************************* +** +** Function rfc_send_sabme +** +** Description This function sends SABME frame. +** +*******************************************************************************/ +void rfc_send_sabme (tRFC_MCB *p_mcb, UINT8 dlci) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* SABME frame, command, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_SABME | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_SABME_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_ua +** +** Description This function sends UA frame. +** +*******************************************************************************/ +void rfc_send_ua (tRFC_MCB *p_mcb, UINT8 dlci) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, FALSE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* ua frame, response, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_UA | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_UA_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_dm +** +** Description This function sends DM frame. +** +*******************************************************************************/ +void rfc_send_dm (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN pf) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, FALSE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* DM frame, response, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_DM | ((pf) ? RFCOMM_PF : 0); + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_DM_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_disc +** +** Description This function sends DISC frame. +** +*******************************************************************************/ +void rfc_send_disc (tRFC_MCB *p_mcb, UINT8 dlci) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* DISC frame, command, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_DISC | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_DISC_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_buf_uih +** +** Description This function sends UIH frame. +** +*******************************************************************************/ +void rfc_send_buf_uih (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) +{ + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + UINT8 credits; + + p_buf->offset -= RFCOMM_CTRL_FRAME_LEN; + if (p_buf->len > 127) + p_buf->offset--; + + if (dlci) + credits = (UINT8)p_buf->layer_specific; + else + credits = 0; + + if (credits) + p_buf->offset--; + + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* UIH frame, command, PF = 0, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_UIH | ((credits) ? RFCOMM_PF : 0); + if (p_buf->len <= 127) + { + *p_data++ = RFCOMM_EA | (p_buf->len << 1); + p_buf->len += 3; + } + else + { + *p_data++ = (p_buf->len & 0x7f) << 1; + *p_data++ = p_buf->len >> RFCOMM_SHIFT_LENGTH2; + p_buf->len += 4; + } + + if (credits) + { + *p_data++ = credits; + p_buf->len++; + } + + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len++; + + *p_data = RFCOMM_UIH_FCS ((UINT8 *)(p_buf + 1) + p_buf->offset, dlci); + + if (dlci == RFCOMM_MX_DLCI) + { + rfc_check_send_cmd(p_mcb, p_buf); + } + else + { + + L2CA_DataWrite (p_mcb->lcid, p_buf); + } +} + + +/******************************************************************************* +** +** Function rfc_send_pn +** +** Description This function sends DLC Parameters Negotiation Frame. +** +*******************************************************************************/ +void rfc_send_pn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT16 mtu, UINT8 cl, UINT8 k) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_PN; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_PN_LEN << 1); + + *p_data++ = dlci; + *p_data++ = RFCOMM_PN_FRAM_TYPE_UIH | cl; + + /* It appeared that we need to reply with the same priority bits as we received. + ** We will use the fact that we reply in the same context so rx_frame can still be used. + */ + if (is_command) + *p_data++ = RFCOMM_PN_PRIORITY_0; + else + *p_data++ = rfc_cb.rfc.rx_frame.u.pn.priority; + + *p_data++ = RFCOMM_T1_DSEC; + *p_data++ = mtu & 0xFF; + *p_data++ = mtu >> 8; + *p_data++ = RFCOMM_N2; + *p_data = k; + + /* Total length is sizeof PN data + mx header 2 */ + p_buf->len = RFCOMM_MX_PN_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_fcon +** +** Description This function sends Flow Control On Command. +** +*******************************************************************************/ +void rfc_send_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_FCON; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_FCON_LEN << 1); + + /* Total length is sizeof FCON data + mx header 2 */ + p_buf->len = RFCOMM_MX_FCON_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_fcoff +** +** Description This function sends Flow Control Off Command. +** +*******************************************************************************/ +void rfc_send_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_FCOFF; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_FCOFF_LEN << 1); + + /* Total length is sizeof FCOFF data + mx header 2 */ + p_buf->len = RFCOMM_MX_FCOFF_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_msc +** +** Description This function sends Modem Status Command Frame. +** +*******************************************************************************/ +void rfc_send_msc (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_CTRL *p_pars) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 signals; + UINT8 break_duration; + UINT8 len; + + signals = p_pars->modem_signal; + break_duration = p_pars->break_signal; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + if (break_duration) + len = RFCOMM_MX_MSC_LEN_WITH_BREAK; + else + len = RFCOMM_MX_MSC_LEN_NO_BREAK; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_MSC; + *p_data++ = RFCOMM_EA | (len << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_EA | + ((p_pars->fc) ? RFCOMM_MSC_FC : 0) | + ((signals & MODEM_SIGNAL_DTRDSR) ? RFCOMM_MSC_RTC : 0) | + ((signals & MODEM_SIGNAL_RTSCTS) ? RFCOMM_MSC_RTR : 0) | + ((signals & MODEM_SIGNAL_RI) ? RFCOMM_MSC_IC : 0) | + ((signals & MODEM_SIGNAL_DCD) ? RFCOMM_MSC_DV : 0); + + if (break_duration) + { + *p_data++ = RFCOMM_EA | RFCOMM_MSC_BREAK_PRESENT_MASK | + (break_duration << RFCOMM_MSC_SHIFT_BREAK); + } + + /* Total length is sizeof MSC data + mx header 2 */ + p_buf->len = len + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_rls +** +** Description This function sends Remote Line Status Command Frame. +** +*******************************************************************************/ +void rfc_send_rls (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT8 status) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_RLS; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_RLS_LEN << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_RLS_ERROR | status; + + /* Total length is sizeof RLS data + mx header 2 */ + p_buf->len = RFCOMM_MX_RLS_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_nsc +** +** Description This function sends Non Supported Command Response. +** +*******************************************************************************/ +void rfc_send_nsc (tRFC_MCB *p_mcb) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(FALSE) | RFCOMM_MX_NSC; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_NSC_LEN << 1); + + *p_data++ = rfc_cb.rfc.rx_frame.ea | + (rfc_cb.rfc.rx_frame.cr << RFCOMM_SHIFT_CR) | + rfc_cb.rfc.rx_frame.type; + + /* Total length is sizeof NSC data + mx header 2 */ + p_buf->len = RFCOMM_MX_NSC_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_rpn +** +** Description This function sends Remote Port Negotiation Command +** +*******************************************************************************/ +void rfc_send_rpn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_STATE *p_pars, UINT16 mask) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_RPN; + + if (!p_pars) + { + *p_data++ = RFCOMM_EA | (RFCOMM_MX_RPN_REQ_LEN << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + + p_buf->len = RFCOMM_MX_RPN_REQ_LEN + 2; + } + else + { + *p_data++ = RFCOMM_EA | (RFCOMM_MX_RPN_LEN << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = p_pars->baud_rate; + *p_data++ = (p_pars->byte_size << RFCOMM_RPN_BITS_SHIFT) + | (p_pars->stop_bits << RFCOMM_RPN_STOP_BITS_SHIFT) + | (p_pars->parity << RFCOMM_RPN_PARITY_SHIFT) + | (p_pars->parity_type << RFCOMM_RPN_PARITY_TYPE_SHIFT); + *p_data++ = p_pars->fc_type; + *p_data++ = p_pars->xon_char; + *p_data++ = p_pars->xoff_char; + *p_data++ = (mask & 0xFF); + *p_data++ = (mask >> 8); + + /* Total length is sizeof RPN data + mx header 2 */ + p_buf->len = RFCOMM_MX_RPN_LEN + 2; + } + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_test +** +** Description This function sends Test frame. +** +*******************************************************************************/ +void rfc_send_test (tRFC_MCB *p_mcb, BOOLEAN is_command, BT_HDR *p_buf) +{ + UINT8 *p_data; + UINT16 xx; + UINT8 *p_src, *p_dest; + + /* Shift buffer to give space for header */ + if (p_buf->offset < (L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2)) + { + p_src = (UINT8 *) (p_buf + 1) + p_buf->offset + p_buf->len - 1; + p_dest = (UINT8 *) (p_buf + 1) + L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2 + p_buf->len - 1; + + for (xx = 0; xx < p_buf->len; xx++) + *p_dest-- = *p_src--; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2; + } + + /* Adjust offset by number of bytes we are going to fill */ + p_buf->offset -= 2; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_TEST; + *p_data++ = RFCOMM_EA | (p_buf->len << 1); + + p_buf->len += 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + +/******************************************************************************* +** +** Function rfc_send_credit +** +** Description This function sends a flow control credit in UIH frame. +** +*******************************************************************************/ +void rfc_send_credit(tRFC_MCB *p_mcb, UINT8 dlci, UINT8 credit) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_UIH | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + *p_data++ = credit; + *p_data = RFCOMM_UIH_FCS ((UINT8 *)(p_buf + 1) + p_buf->offset, dlci); + + p_buf->len = 5; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_parse_data +** +** Description This function processes data packet received from L2CAP +** +*******************************************************************************/ +UINT8 rfc_parse_data (tRFC_MCB *p_mcb, MX_FRAME *p_frame, BT_HDR *p_buf) +{ + UINT8 ead, eal, fcs; + UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 *p_start = p_data; + UINT16 len; + + if (p_buf->len < RFCOMM_CTRL_FRAME_LEN) + { + RFCOMM_TRACE_ERROR1 ("Bad Length1: %d", p_buf->len); + return (RFC_EVENT_BAD_FRAME); + } + + RFCOMM_PARSE_CTRL_FIELD (ead, p_frame->cr, p_frame->dlci, p_data); + if( !ead ) + { + RFCOMM_TRACE_ERROR0 ("Bad Address(EA must be 1)"); + return (RFC_EVENT_BAD_FRAME); + } + RFCOMM_PARSE_TYPE_FIELD (p_frame->type, p_frame->pf, p_data); + RFCOMM_PARSE_LEN_FIELD (eal, len, p_data); + + p_buf->len -= (3 + !ead + !eal + 1); /* Additional 1 for FCS */ + p_buf->offset += (3 + !ead + !eal); + + /* handle credit if credit based flow control */ + if ((p_mcb->flow == PORT_FC_CREDIT) && (p_frame->type == RFCOMM_UIH) && + (p_frame->dlci != RFCOMM_MX_DLCI) && (p_frame->pf == 1)) + { + p_frame->credit = *p_data++; + p_buf->len--; + p_buf->offset++; + } + else + p_frame->credit = 0; + + if (p_buf->len != len) + { + RFCOMM_TRACE_ERROR2 ("Bad Length2 %d %d", p_buf->len, len); + return (RFC_EVENT_BAD_FRAME); + } + + fcs = *(p_data + len); + + /* All control frames that we are sending are sent with P=1, expect */ + /* reply with F=1 */ + /* According to TS 07.10 spec ivalid frames are discarded without */ + /* notification to the sender */ + switch (p_frame->type) + { + case RFCOMM_SABME: + if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr) + || !p_frame->pf || len || !RFCOMM_VALID_DLCI (p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad SABME"); + return (RFC_EVENT_BAD_FRAME); + } + else + return (RFC_EVENT_SABME); + + case RFCOMM_UA: + if (RFCOMM_FRAME_IS_CMD(p_mcb->is_initiator, p_frame->cr) + || !p_frame->pf || len || !RFCOMM_VALID_DLCI (p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad UA"); + return (RFC_EVENT_BAD_FRAME); + } + else + return (RFC_EVENT_UA); + + case RFCOMM_DM: + if (RFCOMM_FRAME_IS_CMD(p_mcb->is_initiator, p_frame->cr) + || len || !RFCOMM_VALID_DLCI(p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad DM"); + return (RFC_EVENT_BAD_FRAME); + } + else + return (RFC_EVENT_DM); + + case RFCOMM_DISC: + if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr) + || !p_frame->pf || len || !RFCOMM_VALID_DLCI(p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad DISC"); + return (RFC_EVENT_BAD_FRAME); + } + else + return (RFC_EVENT_DISC); + + case RFCOMM_UIH: + if (!RFCOMM_VALID_DLCI(p_frame->dlci)) + { + RFCOMM_TRACE_ERROR0 ("Bad UIH - invalid DLCI"); + return (RFC_EVENT_BAD_FRAME); + } + else if (!rfc_check_fcs (2, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad UIH - FCS"); + return (RFC_EVENT_BAD_FRAME); + } + else if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr)) + { + /* we assume that this is ok to allow bad implementations to work */ + RFCOMM_TRACE_ERROR0 ("Bad UIH - response"); + return (RFC_EVENT_UIH); + } + else + return (RFC_EVENT_UIH); + } + + return (RFC_EVENT_BAD_FRAME); +} + + +/******************************************************************************* +** +** Function rfc_process_mx_message +** +** Description This function processes UIH frames received on the +** multiplexer control channel. +** +*******************************************************************************/ +void rfc_process_mx_message (tRFC_MCB *p_mcb, BT_HDR *p_buf) +{ + UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + MX_FRAME *p_rx_frame = &rfc_cb.rfc.rx_frame; + UINT16 length = p_buf->len; + UINT8 ea, cr, mx_len; + BOOLEAN is_command; + + p_rx_frame->ea = *p_data & RFCOMM_EA; + p_rx_frame->cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->type = *p_data++ & ~(RFCOMM_CR_MASK | RFCOMM_EA_MASK); + + if (!p_rx_frame->ea || !length) + { + RFCOMM_TRACE_ERROR2 ("Illegal MX Frame ea:%d len:%d", p_rx_frame->ea, length); + GKI_freebuf (p_buf); + return; + } + + length--; + + is_command = p_rx_frame->cr; + + ea = *p_data & RFCOMM_EA; + + mx_len = *p_data++ >> RFCOMM_SHIFT_LENGTH1; + length--; + + if (!ea) + { + mx_len += *p_data++ << RFCOMM_SHIFT_LENGTH2; + length --; + } + + if (mx_len != length) + { + RFCOMM_TRACE_ERROR0 ("Bad MX frame"); + GKI_freebuf (p_buf); + return; + } + + switch (p_rx_frame->type) + { + case RFCOMM_MX_PN: + if (length != RFCOMM_MX_PN_LEN) + break; + + p_rx_frame->dlci = *p_data++ & RFCOMM_PN_DLCI_MASK; + p_rx_frame->u.pn.frame_type = *p_data & RFCOMM_PN_FRAME_TYPE_MASK; + p_rx_frame->u.pn.conv_layer = *p_data++ & RFCOMM_PN_CONV_LAYER_MASK; + p_rx_frame->u.pn.priority = *p_data++ & RFCOMM_PN_PRIORITY_MASK; + p_rx_frame->u.pn.t1 = *p_data++; + p_rx_frame->u.pn.mtu = *p_data + (*(p_data + 1) << 8); + p_data += 2; + p_rx_frame->u.pn.n2 = *p_data++; + p_rx_frame->u.pn.k = *p_data++ & RFCOMM_PN_K_MASK; + + if (!p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci) + || (p_rx_frame->u.pn.mtu < RFCOMM_MIN_MTU) + || (p_rx_frame->u.pn.mtu > RFCOMM_MAX_MTU)) + { + RFCOMM_TRACE_ERROR0 ("Bad PN frame"); + break; + } + + GKI_freebuf (p_buf); + + rfc_process_pn (p_mcb, is_command, p_rx_frame); + return; + + case RFCOMM_MX_TEST: + if (!length) + break; + + p_rx_frame->u.test.p_data = p_data; + p_rx_frame->u.test.data_len = length; + + p_buf->offset += 2; + p_buf->len -= 2; + + if (is_command) + rfc_send_test (p_mcb, FALSE, p_buf); + else + rfc_process_test_rsp (p_mcb, p_buf); + return; + + case RFCOMM_MX_FCON: + if (length != RFCOMM_MX_FCON_LEN) + break; + + GKI_freebuf (p_buf); + + rfc_process_fcon (p_mcb, is_command); + return; + + case RFCOMM_MX_FCOFF: + if (length != RFCOMM_MX_FCOFF_LEN) + break; + + GKI_freebuf (p_buf); + + rfc_process_fcoff (p_mcb, is_command); + return; + + case RFCOMM_MX_MSC: + + ea = *p_data & RFCOMM_EA; + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; + + if (!ea || !cr || !p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) + { + RFCOMM_TRACE_ERROR0 ("Bad MSC frame"); + break; + } + + p_rx_frame->u.msc.signals = *p_data++; + + if (mx_len == RFCOMM_MX_MSC_LEN_WITH_BREAK) + { + p_rx_frame->u.msc.break_present = *p_data & RFCOMM_MSC_BREAK_PRESENT_MASK; + p_rx_frame->u.msc.break_duration = (*p_data & RFCOMM_MSC_BREAK_MASK) >> RFCOMM_MSC_SHIFT_BREAK; + } + else + { + p_rx_frame->u.msc.break_present = FALSE; + p_rx_frame->u.msc.break_duration = 0; + } + GKI_freebuf (p_buf); + + rfc_process_msc (p_mcb, is_command, p_rx_frame); + return; + + case RFCOMM_MX_NSC: + if ((length != RFCOMM_MX_NSC_LEN) || !is_command) + break; + + p_rx_frame->u.nsc.ea = *p_data & RFCOMM_EA; + p_rx_frame->u.nsc.cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->u.nsc.type = *p_data++ >> RFCOMM_SHIFT_DLCI; + + GKI_freebuf (p_buf); + + rfc_process_nsc (p_mcb, p_rx_frame); + return; + + case RFCOMM_MX_RPN: + if ((length != RFCOMM_MX_RPN_REQ_LEN) && (length != RFCOMM_MX_RPN_LEN)) + break; + + ea = *p_data & RFCOMM_EA; + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; + + if (!ea || !cr || !p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) + { + RFCOMM_TRACE_ERROR0 ("Bad RPN frame"); + break; + } + + p_rx_frame->u.rpn.is_request = (length == RFCOMM_MX_RPN_REQ_LEN); + + if (!p_rx_frame->u.rpn.is_request) + { + p_rx_frame->u.rpn.baud_rate = *p_data++; + p_rx_frame->u.rpn.byte_size = (*p_data >> RFCOMM_RPN_BITS_SHIFT) & RFCOMM_RPN_BITS_MASK; + p_rx_frame->u.rpn.stop_bits = (*p_data >> RFCOMM_RPN_STOP_BITS_SHIFT) & RFCOMM_RPN_STOP_BITS_MASK; + p_rx_frame->u.rpn.parity = (*p_data >> RFCOMM_RPN_PARITY_SHIFT) & RFCOMM_RPN_PARITY_MASK; + p_rx_frame->u.rpn.parity_type = (*p_data++ >> RFCOMM_RPN_PARITY_TYPE_SHIFT) & RFCOMM_RPN_PARITY_TYPE_MASK; + + p_rx_frame->u.rpn.fc_type = *p_data++ & RFCOMM_FC_MASK; + p_rx_frame->u.rpn.xon_char = *p_data++; + p_rx_frame->u.rpn.xoff_char = *p_data++; + p_rx_frame->u.rpn.param_mask = (*p_data + (*(p_data + 1) << 8)) & RFCOMM_RPN_PM_MASK; + } + GKI_freebuf (p_buf); + + rfc_process_rpn (p_mcb, is_command, p_rx_frame->u.rpn.is_request, p_rx_frame); + return; + + case RFCOMM_MX_RLS: + if (length != RFCOMM_MX_RLS_LEN) + break; + + ea = *p_data & RFCOMM_EA; + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + + p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; + p_rx_frame->u.rls.line_status = (*p_data & ~0x01); + + if (!ea || !cr || !p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) + { + RFCOMM_TRACE_ERROR0 ("Bad RPN frame"); + break; + } + + GKI_freebuf (p_buf); + + rfc_process_rls (p_mcb, is_command, p_rx_frame); + return; + } + + GKI_freebuf (p_buf); + + if (is_command) + rfc_send_nsc (p_mcb); +} + diff --git a/stack/rfcomm/rfc_utils.c b/stack/rfcomm/rfc_utils.c new file mode 100644 index 0000000..9395891 --- /dev/null +++ b/stack/rfcomm/rfc_utils.c @@ -0,0 +1,453 @@ +/*****************************************************************************/ +/* */ +/* Name: rfc_utils.c */ +/* */ +/* Description: This file contains collection of utility functions used */ +/* the RFCOMM unit */ +/* */ +/* Copyright (c) 1999-2008, Broadcom Corp., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ +#include "bt_target.h" +#include "gki.h" + +#include "btm_api.h" +#include "btm_int.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "port_ext.h" +#include "port_int.h" +#include "rfc_int.h" +#include "btu.h" + +#include <string.h> + +/******************************************************************************* +** +** Function rfc_calc_fcs +** +** Description Reversed CRC Table , 8-bit, poly=0x07 +** (GSM 07.10 TS 101 369 V6.3.0) +*******************************************************************************/ +static const UINT8 rfc_crctable[] = +{ + 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 +}; + + +/******************************************************************************* +** +** Function rfc_calc_fcs +** +** Description This function calculate FCS for the RFCOMM frame +** (GSM 07.10 TS 101 369 V6.3.0) +** +** Input len - number of bytes in the message +** p - points to message +** +*******************************************************************************/ +UINT8 rfc_calc_fcs (UINT16 len, UINT8 *p) +{ + UINT8 fcs = 0xFF; + + while (len--) + { + fcs = rfc_crctable[fcs ^ *p++]; + } + + /* Ones compliment */ + return (0xFF - fcs); +} + + +/******************************************************************************* +** +** Function rfc_check_fcs +** +** Description This function checks FCS for the RFCOMM frame +** (GSM 07.10 TS 101 369 V6.3.0) +** +** Input len - number of bytes in the message +** p - points to message +** received_fcs - received FCS +** +*******************************************************************************/ +BOOLEAN rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs) +{ + UINT8 fcs = 0xFF; + + while (len--) + { + fcs = rfc_crctable[fcs ^ *p++]; + } + + /* Ones compliment */ + fcs = rfc_crctable[fcs ^ received_fcs]; + + /*0xCF is the reversed order of 11110011.*/ + return (fcs == 0xCF); +} + + +/******************************************************************************* +** +** Function rfc_alloc_multiplexer_channel +** +** Description This function returns existing or new control block for +** the BD_ADDR. +** +*******************************************************************************/ +tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator) +{ + int i, j; + tRFC_MCB *p_mcb = NULL; + + for (i = 0; i < MAX_BD_CONNECTIONS; i++) + { + if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE) + && (!memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN))) + { + /* Multiplexer channel found do not change anything */ + /* If there was an inactivity timer running stop it now */ + if (rfc_cb.port.rfc_mcb[i].state == RFC_MX_STATE_CONNECTED) + rfc_timer_stop (&rfc_cb.port.rfc_mcb[i]); + return (&rfc_cb.port.rfc_mcb[i]); + } + } + + /* connection with bd_addr does not exist */ + for (i = 0, j = rfc_cb.rfc.last_mux + 1; i < MAX_BD_CONNECTIONS; i++, j++) + { + if (j >= MAX_BD_CONNECTIONS) + j = 0; + + p_mcb = &rfc_cb.port.rfc_mcb[j]; + if (rfc_cb.port.rfc_mcb[j].state == RFC_MX_STATE_IDLE) + { + /* New multiplexer control block */ + memset (p_mcb, 0, sizeof (tRFC_MCB)); + memcpy (p_mcb->bd_addr, bd_addr, BD_ADDR_LEN); + + GKI_init_q(&p_mcb->cmd_q); + + p_mcb->is_initiator = is_initiator; + + rfc_timer_start (p_mcb, RFC_MCB_INIT_INACT_TIMER); + + rfc_cb.rfc.last_mux = (UINT8) j; + return (p_mcb); + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function rfc_release_multiplexer_channel +** +** Description This function returns existing or new control block for +** the BD_ADDR. +** +*******************************************************************************/ +void rfc_release_multiplexer_channel (tRFC_MCB *p_mcb) +{ + void *p_buf; + + rfc_timer_stop (p_mcb); + + while ((p_buf = GKI_dequeue(&p_mcb->cmd_q)) != NULL) + GKI_freebuf(p_buf); + + memset (p_mcb, 0, sizeof (tRFC_MCB)); + p_mcb->state = RFC_MX_STATE_IDLE; +} + + +/******************************************************************************* +** +** Function rfc_timer_start +** +** Description Start RFC Timer +** +*******************************************************************************/ +void rfc_timer_start (tRFC_MCB *p_mcb, UINT16 timeout) +{ + TIMER_LIST_ENT *p_tle = &p_mcb->tle; + + RFCOMM_TRACE_EVENT1 ("rfc_timer_start - timeout:%d", timeout); + + p_tle->param = (UINT32)p_mcb; + + btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_MFC, timeout); +} + + +/******************************************************************************* +** +** Function rfc_timer_stop +** +** Description Stop RFC Timer +** +*******************************************************************************/ +void rfc_timer_stop (tRFC_MCB *p_mcb) +{ + RFCOMM_TRACE_EVENT0 ("rfc_timer_stop"); + + btu_stop_timer (&p_mcb->tle); +} + + +/******************************************************************************* +** +** Function rfc_port_timer_start +** +** Description Start RFC Timer +** +*******************************************************************************/ +void rfc_port_timer_start (tPORT *p_port, UINT16 timeout) +{ + TIMER_LIST_ENT *p_tle = &p_port->rfc.tle; + + RFCOMM_TRACE_EVENT1 ("rfc_port_timer_start - timeout:%d", timeout); + + p_tle->param = (UINT32)p_port; + + btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_PORT, timeout); +} + + +/******************************************************************************* +** +** Function rfc_port_timer_stop +** +** Description Stop RFC Timer +** +*******************************************************************************/ +void rfc_port_timer_stop (tPORT *p_port) +{ + RFCOMM_TRACE_EVENT0 ("rfc_port_timer_stop"); + + btu_stop_timer (&p_port->rfc.tle); +} + + +/******************************************************************************* +** +** Function rfc_check_mcb_active +** +** Description Check if there are any opened ports on the MCB if not +** start MCB Inact timer. +** +** Returns void +** +*******************************************************************************/ +void rfc_check_mcb_active (tRFC_MCB *p_mcb) +{ + UINT16 i; + + for (i = 0; i < RFCOMM_MAX_DLCI; i++) + { + if (p_mcb->port_inx[i] != 0) + { + p_mcb->is_disc_initiator = FALSE; + return; + } + } + /* The last port was DISCed. On the client side start disconnecting Mx */ + /* On the server side start inactivity timer */ + if (p_mcb->is_disc_initiator) + { + p_mcb->is_disc_initiator = FALSE; + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CLOSE_REQ, NULL); + } + else + rfc_timer_start (p_mcb, RFC_MCB_RELEASE_INACT_TIMER); +} + + +/******************************************************************************* +** +** Function rfcomm_process_timeout +** +** Description The function called every second to check RFCOMM timers +** +** Returns void +** +*******************************************************************************/ +void rfcomm_process_timeout (TIMER_LIST_ENT *p_tle) +{ + switch (p_tle->event) + { + case BTU_TTYPE_RFCOMM_MFC: + rfc_mx_sm_execute ((tRFC_MCB *)p_tle->param, RFC_EVENT_TIMEOUT, NULL); + break; + + case BTU_TTYPE_RFCOMM_PORT: + rfc_port_sm_execute ((tPORT *)p_tle->param, RFC_EVENT_TIMEOUT, NULL); + break; + + default: + break; + } +} + + +/******************************************************************************* +** +** Function rfc_sec_check_complete +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +void rfc_sec_check_complete (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tPORT *p_port = (tPORT *)p_ref_data; + + /* Verify that PORT is still waiting for Security to complete */ + if (!p_port->in_use + || ((p_port->rfc.state != RFC_STATE_ORIG_WAIT_SEC_CHECK) + && (p_port->rfc.state != RFC_STATE_TERM_WAIT_SEC_CHECK))) + return; + + rfc_port_sm_execute ((tPORT *)p_ref_data, RFC_EVENT_SEC_COMPLETE, &res); +} + + +/******************************************************************************* +** +** Function rfc_port_closed +** +** Description The function is called when port is released based on the +** event received from the lower layer, typically L2CAP +** connection down, DISC, or DM frame. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_closed (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + RFCOMM_TRACE_DEBUG0 ("rfc_port_closed"); + + rfc_port_timer_stop (p_port); + + p_port->rfc.state = RFC_STATE_CLOSED; + + /* If multiplexer channel was up mark it as down */ + if (p_mcb) + { + p_mcb->port_inx[p_port->dlci] = 0; + + /* If there are no more ports opened on this MCB release it */ + rfc_check_mcb_active (p_mcb); + } + + /* Notify port that RFC connection is gone */ + port_rfc_closed (p_port, PORT_CLOSED); +} + +/******************************************************************************* +** +** Function rfc_inc_credit +** +** Description The function is called when a credit is received in a UIH +** frame. It increments the TX credit count, and if data +** flow had halted, it restarts it. +** +** Returns void +** +*******************************************************************************/ +void rfc_inc_credit (tPORT *p_port, UINT8 credit) +{ + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + { + p_port->credit_tx += credit; + + RFCOMM_TRACE_EVENT1 ("rfc_inc_credit:%d", p_port->credit_tx); + + if (p_port->tx.peer_fc == TRUE) + PORT_FlowInd(p_port->rfc.p_mcb, p_port->dlci, TRUE); + } +} + +/******************************************************************************* +** +** Function rfc_dec_credit +** +** Description The function is called when a UIH frame of user data is +** sent. It decrements the credit count. If credit count +** Reaches zero, peer_fc is set. +** +** Returns void +** +*******************************************************************************/ +void rfc_dec_credit (tPORT *p_port) +{ + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + { + if (p_port->credit_tx > 0) + p_port->credit_tx--; + + if (p_port->credit_tx == 0) + p_port->tx.peer_fc = TRUE; + } +} + + +/******************************************************************************* +** +** Function rfc_check_send_cmd +** +** Description This function is called to send an RFCOMM command message +** or to handle the RFCOMM command message queue. +** +** Returns void +** +*******************************************************************************/ +void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf) +{ + BT_HDR *p; + + /* if passed a buffer queue it */ + if (p_buf != NULL) + { + GKI_enqueue(&p_mcb->cmd_q, p_buf); + } + + /* handle queue if L2CAP not congested */ + while (p_mcb->l2cap_congested == FALSE) + { + if ((p = (BT_HDR *) GKI_dequeue(&p_mcb->cmd_q)) == NULL) + { + break; + } + + + L2CA_DataWrite (p_mcb->lcid, p); + } +} + + |