#include "port_api.h"
#include "rfcdefs.h"
#include "sdp_api.h"
+#include "stack/l2cap/l2c_int.h"
#include "utl.h"
#include "osi/include/osi.h"
}
break;
case BTA_JV_CONN_TYPE_L2CAP_LE:
+ psm = L2CA_AllocateLePSM();
+ if (psm == 0) {
+ LOG(ERROR) << __func__ << ": Error: No free LE PSM available";
+ }
break;
default:
break;
bta_jv_set_free_psm(scn);
break;
case BTA_JV_CONN_TYPE_L2CAP_LE:
- // TODO: Not yet implemented...
+ VLOG(2) << __func__ << ": type=BTA_JV_CONN_TYPE_L2CAP_LE. psm=" << scn;
+ L2CA_FreeLePSM(scn);
break;
default:
break;
*
******************************************************************************/
tBTA_JV_STATUS BTA_JvGetChannelId(int conn_type, uint32_t id, int32_t channel) {
- VLOG(2) << __func__;
+ VLOG(2) << __func__ << ": conn_type=" << conn_type;
if (conn_type != BTA_JV_CONN_TYPE_RFCOMM &&
conn_type != BTA_JV_CONN_TYPE_L2CAP &&
*sock_fd = INVALID_FD;
bt_status_t status = BT_STATUS_FAIL;
+ int original_channel = channel;
switch (type) {
case BTSOCK_RFCOMM:
status =
btsock_l2cap_listen(service_name, channel, sock_fd, flags, app_uid);
break;
-
+ case BTSOCK_L2CAP_LE:
+ if (flags & BTSOCK_FLAG_NO_SDP) {
+ channel = L2CAP_MASK_LE_COC_CHANNEL;
+ } else if (channel > 0) {
+ channel |= L2CAP_MASK_LE_COC_CHANNEL;
+ } else {
+ LOG_ERROR(LOG_TAG, "%s: type BTSOCK_L2CAP_LE: invalid channel=%d",
+ __func__, channel);
+ break;
+ }
+ LOG_DEBUG(LOG_TAG,
+ "%s: type=BTSOCK_L2CAP_LE, channel=0x%x, original=0x%x",
+ __func__, channel, original_channel);
+ status =
+ btsock_l2cap_listen(service_name, channel, sock_fd, flags, app_uid);
+ break;
case BTSOCK_SCO:
status = btsock_sco_listen(sock_fd, flags);
break;
status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid);
break;
+ case BTSOCK_L2CAP_LE:
+ channel |= L2CAP_MASK_LE_COC_CHANNEL;
+ LOG_DEBUG(LOG_TAG, "%s: type=BTSOCK_L2CAP_LE, channel=0x%x", __func__,
+ channel);
+ status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid);
+ break;
+
case BTSOCK_SCO:
status = btsock_sco_connect(bd_addr, sock_fd, flags);
break;
btsock_rfc_signaled(fd, flags, user_id);
break;
case BTSOCK_L2CAP:
+ case BTSOCK_L2CAP_LE:
+ /* Note: The caller may not distinguish between BTSOCK_L2CAP and
+ * BTSOCK_L2CAP_LE correctly */
btsock_l2cap_signaled(fd, flags, user_id);
break;
default:
BTA_JvL2capClose(sock->handle);
}
if ((sock->channel >= 0) && (sock->server)) {
- BTA_JvFreeChannel(sock->channel, BTA_JV_CONN_TYPE_L2CAP);
+ BTA_JvFreeChannel(sock->channel, BTA_JV_CONN_TYPE_L2CAP_LE);
}
} else {
// Only call if we are non server connections
}
bt_status_t btsock_l2cap_init(int handle, uid_set_t* set) {
- APPL_TRACE_DEBUG("%s handle = %d", __func__);
+ APPL_TRACE_DEBUG("%s: handle = %d", __func__, handle);
std::unique_lock<std::mutex> lock(state_lock);
pth = handle;
socks = NULL;
}
static inline bool send_app_psm_or_chan_l(l2cap_socket* sock) {
+ APPL_TRACE_DEBUG("%s: channel=%d", __func__, sock->channel);
return sock_send_all(sock->our_fd, (const uint8_t*)&sock->channel,
sizeof(sock->channel)) == sizeof(sock->channel);
}
cfg.fcr_present = true;
cfg.fcr = obex_l2c_fcr_opts_def;
+ APPL_TRACE_DEBUG("%s: fixed_chan=%d, channel=%d, is_le_coc=%d", __func__,
+ sock->fixed_chan, sock->channel, sock->is_le_coc);
+
if (sock->fixed_chan) {
if (BTA_JvL2capStartServerLE(sock->security, 0, NULL, sock->channel,
L2CAP_DEFAULT_MTU, NULL, btsock_l2cap_cbk,
} else {
/* If we have a channel specified in the request, just start the server,
* else we request a PSM and start the server after we receive a PSM. */
- if (sock->channel < 0) {
+ if (sock->channel <= 0) {
if (sock->is_le_coc) {
if (BTA_JvGetChannelId(BTA_JV_CONN_TYPE_L2CAP_LE, sock->id, 0) !=
BTA_JV_SUCCESS)
typedef enum {
BTSOCK_RFCOMM = 1,
BTSOCK_SCO = 2,
- BTSOCK_L2CAP = 3
+ BTSOCK_L2CAP = 3,
+ BTSOCK_L2CAP_LE = 4
} btsock_type_t;
/** Represents the standard BT SOCKET interface. */
/*******************************************************************************
*
+ * Function L2CA_AllocateLePSM
+ *
+ * Description Other layers call this function to find an unused LE PSM for
+ * L2CAP services.
+ *
+ * Returns LE_PSM to use if success. Otherwise returns 0.
+ *
+ ******************************************************************************/
+extern uint16_t L2CA_AllocateLePSM(void);
+
+/*******************************************************************************
+ *
+ * Function L2CA_FreeLePSM
+ *
+ * Description Free an assigned LE PSM.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+extern void L2CA_FreeLePSM(uint16_t psm);
+
+/*******************************************************************************
+ *
* Function L2CA_ConnectReq
*
* Description Higher layers call this function to create an L2CAP
/*******************************************************************************
*
+ * Function L2CA_AllocateLePSM
+ *
+ * Description To find an unused LE PSM for L2CAP services.
+ *
+ * Returns LE_PSM to use if success. Otherwise returns 0.
+ *
+ ******************************************************************************/
+uint16_t L2CA_AllocateLePSM(void) {
+ bool done = false;
+ uint16_t psm = l2cb.le_dyn_psm;
+ uint16_t count = 0;
+
+ L2CAP_TRACE_API("%s: last psm=%d", __func__, psm);
+ while (!done) {
+ count++;
+ if (count > LE_DYNAMIC_PSM_RANGE) {
+ L2CAP_TRACE_ERROR("%s: Out of free BLE PSM", __func__);
+ return 0;
+ }
+
+ psm++;
+ if (psm > LE_DYNAMIC_PSM_END) {
+ psm = LE_DYNAMIC_PSM_START;
+ }
+
+ if (!l2cb.le_dyn_psm_assigned[psm - LE_DYNAMIC_PSM_START]) {
+ /* make sure the newly allocated psm is not used right now */
+ if (l2cu_find_ble_rcb_by_psm(psm)) {
+ L2CAP_TRACE_WARNING("%s: supposedly-free PSM=%d have allocated rcb!",
+ __func__, psm);
+ continue;
+ }
+
+ l2cb.le_dyn_psm_assigned[psm - LE_DYNAMIC_PSM_START] = true;
+ L2CAP_TRACE_DEBUG("%s: assigned PSM=%d", __func__, psm);
+ done = true;
+ break;
+ }
+ }
+ l2cb.le_dyn_psm = psm;
+
+ return (psm);
+}
+
+/*******************************************************************************
+ *
+ * Function L2CA_FreeLePSM
+ *
+ * Description Free an assigned LE PSM.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void L2CA_FreeLePSM(uint16_t psm) {
+ L2CAP_TRACE_API("%s: to free psm=%d", __func__, psm);
+
+ if ((psm < LE_DYNAMIC_PSM_START) || (psm > LE_DYNAMIC_PSM_END)) {
+ L2CAP_TRACE_ERROR("%s: Invalid PSM=%d value!", __func__, psm);
+ return;
+ }
+
+ if (!l2cb.le_dyn_psm_assigned[psm - LE_DYNAMIC_PSM_START]) {
+ L2CAP_TRACE_WARNING("%s: PSM=%d was not allocated!", __func__, psm);
+ }
+ l2cb.le_dyn_psm_assigned[psm - LE_DYNAMIC_PSM_START] = false;
+}
+
+/*******************************************************************************
+ *
* Function L2CA_ConnectReq
*
* Description Higher layers call this function to create an L2CAP
/* Check if this is a registration for an outgoing-only connection to */
/* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */
- if ((psm >= 0x0080) && (p_cb_info->pL2CA_ConnectInd_Cb == NULL)) {
- for (vpsm = 0x0080; vpsm < 0x0100; vpsm++) {
- p_rcb = l2cu_find_ble_rcb_by_psm(vpsm);
- if (p_rcb == NULL) break;
+ if ((psm >= LE_DYNAMIC_PSM_START) &&
+ (p_cb_info->pL2CA_ConnectInd_Cb == NULL)) {
+ vpsm = L2CA_AllocateLePSM();
+ if (vpsm == 0) {
+ L2CAP_TRACE_ERROR("%s: Out of free BLE PSM", __func__);
+ return 0;
}
L2CAP_TRACE_API("%s Real PSM: 0x%04x Virtual PSM: 0x%04x", __func__, psm,
/* If registration block already there, just overwrite it */
p_rcb = l2cu_find_ble_rcb_by_psm(vpsm);
if (p_rcb == NULL) {
+ L2CAP_TRACE_API("%s Allocate rcp for Virtual PSM: 0x%04x", __func__, vpsm);
p_rcb = l2cu_allocate_ble_rcb(vpsm);
if (p_rcb == NULL) {
L2CAP_TRACE_WARNING("%s No BLE RCB available, PSM: 0x%04x vPSM: 0x%04x",
tL2C_RCB* p_rcb = l2cu_find_ble_rcb_by_psm(psm);
if (p_rcb == NULL) {
- L2CAP_TRACE_WARNING("%s PSM: 0x%04x not found for deregistration", psm);
+ L2CAP_TRACE_WARNING("%s PSM: 0x%04x not found for deregistration", __func__,
+ psm);
return;
}
l2c_csm_execute(p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
}
- l2cu_release_rcb(p_rcb);
+ l2cu_release_ble_rcb(p_rcb);
}
/*******************************************************************************
*/
#define L2CEVT_L2CAP_RECV_FLOW_CONTROL_CREDIT 36 /* Peer credit packet */
+/* Constants for LE Dynamic PSM values */
+#define LE_DYNAMIC_PSM_START 0x0080
+#define LE_DYNAMIC_PSM_END 0x00FF
+#define LE_DYNAMIC_PSM_RANGE (LE_DYNAMIC_PSM_END - LE_DYNAMIC_PSM_START + 1)
+
/* Bitmask to skip over Broadcom feature reserved (ID) to avoid sending two
successive ID values, '0' id only or both */
#define L2CAP_ADJ_BRCM_ID 0x1
#endif /* (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE) */
uint16_t dyn_psm;
+
+ uint16_t le_dyn_psm; /* Next LE dynamic PSM value to try to assign */
+ bool le_dyn_psm_assigned[LE_DYNAMIC_PSM_RANGE]; /* Table of assigned LE PSM */
+
} tL2C_CB;
/* Define a structure that contains the information about a connection.
extern tL2C_RCB* l2cu_allocate_rcb(uint16_t psm);
extern tL2C_RCB* l2cu_find_rcb_by_psm(uint16_t psm);
extern void l2cu_release_rcb(tL2C_RCB* p_rcb);
+extern void l2cu_release_ble_rcb(tL2C_RCB* p_rcb);
extern tL2C_RCB* l2cu_allocate_ble_rcb(uint16_t psm);
extern tL2C_RCB* l2cu_find_ble_rcb_by_psm(uint16_t psm);
/* the psm is increased by 2 before being used */
l2cb.dyn_psm = 0xFFF;
+ /* the LE PSM is increased by 1 before being used */
+ l2cb.le_dyn_psm = LE_DYNAMIC_PSM_START - 1;
+
/* Put all the channel control blocks on the free queue */
for (xx = 0; xx < MAX_L2CAP_CHANNELS - 1; xx++) {
l2cb.ccb_pool[xx].p_next_ccb = &l2cb.ccb_pool[xx + 1];
/*******************************************************************************
*
+ * Function l2cu_release_ble_rcb
+ *
+ * Description Mark an LE RCB as no longer in use
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void l2cu_release_ble_rcb(tL2C_RCB* p_rcb) {
+ L2CA_FreeLePSM(p_rcb->psm);
+ p_rcb->in_use = false;
+ p_rcb->psm = 0;
+}
+
+/*******************************************************************************
+ *
* Function l2cu_disconnect_chnl
*
* Description Disconnect a channel. Typically, this is due to either