blob: 95160506d0c4479101c34e5731b982e3a805b1cb [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2011-2012 Intel Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
* Copyright 2023-2024 NXP
*
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <endian.h>
#include <stdbool.h>
#include "lib/bluetooth.h"
#include "src/shared/util.h"
#include "src/shared/tester.h"
#include "src/shared/queue.h"
#include "src/shared/ad.h"
#include "monitor/bt.h"
#include "monitor/rfcomm.h"
#include "bthost.h"
#define lmp_bredr_capable(bthost) (!((bthost)->features[4] & 0x20))
/* ACL handle and flags pack/unpack */
#define acl_handle_pack(h, f) (uint16_t)((h & 0x0fff)|(f << 12))
#define acl_handle(h) (h & 0x0fff)
#define acl_flags(h) (h >> 12)
#define iso_flags_pb(f) (f & 0x0003)
#define iso_flags_ts(f) ((f >> 2) & 0x0001)
#define iso_flags_pack(pb, ts) (((pb) & 0x03) | (((ts) & 0x01) << 2))
#define iso_data_len_pack(h, f) ((uint16_t) ((h) | ((f) << 14)))
#define L2CAP_FEAT_FIXED_CHAN 0x00000080
#define L2CAP_FC_SIG_BREDR 0x02
#define L2CAP_FC_SMP_BREDR 0x80
#define L2CAP_IT_FEAT_MASK 0x0002
#define L2CAP_IT_FIXED_CHAN 0x0003
/* RFCOMM setters */
#define RFCOMM_ADDR(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
#define RFCOMM_CTRL(type, pf) (((type & 0xef) | (pf << 4)))
#define RFCOMM_LEN8(len) (((len) << 1) | 1)
#define RFCOMM_LEN16(len) ((len) << 1)
#define RFCOMM_MCC_TYPE(cr, type) (((type << 2) | (cr << 1) | 0x01))
/* RFCOMM FCS calculation */
#define CRC(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
static const unsigned char rfcomm_crc_table[256] = {
0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
};
static uint8_t rfcomm_fcs2(uint8_t *data)
{
return 0xff - rfcomm_crc_table[CRC(data) ^ data[2]];
}
static uint8_t rfcomm_fcs(uint8_t *data)
{
return 0xff - CRC(data);
}
struct cmd {
struct cmd *next;
struct cmd *prev;
uint8_t data[256 + sizeof(struct bt_hci_cmd_hdr)];
uint16_t len;
};
struct cmd_queue {
struct cmd *head;
struct cmd *tail;
};
struct cid_hook {
uint16_t cid;
bthost_cid_hook_func_t func;
void *user_data;
struct cid_hook *next;
};
struct rfcomm_chan_hook {
uint8_t channel;
bthost_rfcomm_chan_hook_func_t func;
void *user_data;
struct rfcomm_chan_hook *next;
};
struct iso_hook {
bthost_cid_hook_func_t func;
void *user_data;
bthost_destroy_func_t destroy;
};
struct btconn {
uint16_t handle;
uint8_t bdaddr[6];
uint8_t addr_type;
uint8_t encr_mode;
uint16_t next_cid;
uint64_t fixed_chan;
struct l2conn *l2conns;
struct rcconn *rcconns;
struct cid_hook *cid_hooks;
struct rfcomm_chan_hook *rfcomm_chan_hooks;
struct iso_hook *iso_hook;
struct btconn *next;
void *smp_data;
uint16_t recv_len;
uint16_t data_len;
void *recv_data;
};
enum l2cap_mode {
L2CAP_MODE_OTHER,
L2CAP_MODE_LE_CRED,
L2CAP_MODE_LE_ENH_CRED,
};
struct l2conn {
struct l2conn *next;
uint16_t scid;
uint16_t dcid;
uint16_t psm;
enum l2cap_mode mode;
uint16_t data_len;
uint16_t recv_len;
void *recv_data;
};
struct rcconn {
uint8_t channel;
uint16_t scid;
struct rcconn *next;
};
struct l2cap_pending_req {
uint8_t ident;
bthost_l2cap_rsp_cb cb;
void *user_data;
struct l2cap_pending_req *next;
};
struct l2cap_conn_cb_data {
uint16_t psm;
uint16_t mtu;
uint16_t mps;
uint16_t credits;
bthost_l2cap_connect_cb func;
bthost_l2cap_disconnect_cb disconn_func;
void *user_data;
struct l2cap_conn_cb_data *next;
};
struct rfcomm_conn_cb_data {
uint8_t channel;
bthost_rfcomm_connect_cb func;
void *user_data;
struct rfcomm_conn_cb_data *next;
};
struct rfcomm_connection_data {
uint8_t channel;
struct btconn *conn;
bthost_rfcomm_connect_cb cb;
void *user_data;
};
struct le_ext_adv {
struct bthost *bthost;
uint16_t event_type;
uint8_t addr_type;
uint8_t addr[6];
uint8_t direct_addr_type;
uint8_t direct_addr[6];
};
struct bthost {
bool ready;
bthost_ready_cb ready_cb;
uint8_t bdaddr[6];
uint8_t features[8];
bthost_send_func send_handler;
void *send_data;
struct cmd_queue cmd_q;
uint8_t ncmd;
struct btconn *conns;
bthost_cmd_complete_cb cmd_complete_cb;
void *cmd_complete_data;
bthost_new_conn_cb new_conn_cb;
void *new_conn_data;
bthost_accept_conn_cb accept_iso_cb;
bthost_new_conn_cb new_iso_cb;
void *new_iso_data;
struct rfcomm_connection_data *rfcomm_conn_data;
struct l2cap_conn_cb_data *new_l2cap_conn_data;
struct rfcomm_conn_cb_data *new_rfcomm_conn_data;
struct l2cap_pending_req *l2reqs;
uint8_t pin[16];
uint8_t pin_len;
uint8_t io_capability;
uint8_t auth_req;
bool reject_user_confirm;
void *smp_data;
bool conn_init;
bool le;
bool sc;
struct queue *le_ext_adv;
bthost_debug_func_t debug_callback;
bthost_destroy_func_t debug_destroy;
void *debug_data;
};
struct bthost *bthost_create(void)
{
struct bthost *bthost;
bthost = new0(struct bthost, 1);
if (!bthost)
return NULL;
bthost->smp_data = smp_start(bthost);
if (!bthost->smp_data) {
free(bthost);
return NULL;
}
bthost->le_ext_adv = queue_new();
/* Set defaults */
bthost->io_capability = 0x03;
return bthost;
}
static void l2conn_free(struct l2conn *conn)
{
free(conn->recv_data);
free(conn);
}
static void btconn_free(struct btconn *conn)
{
if (conn->smp_data)
smp_conn_del(conn->smp_data);
while (conn->l2conns) {
struct l2conn *l2conn = conn->l2conns;
conn->l2conns = l2conn->next;
l2conn_free(l2conn);
}
while (conn->cid_hooks) {
struct cid_hook *hook = conn->cid_hooks;
conn->cid_hooks = hook->next;
free(hook);
}
while (conn->rcconns) {
struct rcconn *rcconn = conn->rcconns;
conn->rcconns = rcconn->next;
free(rcconn);
}
while (conn->rfcomm_chan_hooks) {
struct rfcomm_chan_hook *hook = conn->rfcomm_chan_hooks;
conn->rfcomm_chan_hooks = hook->next;
free(hook);
}
if (conn->iso_hook && conn->iso_hook->destroy)
conn->iso_hook->destroy(conn->iso_hook->user_data);
free(conn->iso_hook);
free(conn->recv_data);
free(conn);
}
static struct btconn *bthost_find_conn(struct bthost *bthost, uint16_t handle)
{
struct btconn *conn;
for (conn = bthost->conns; conn != NULL; conn = conn->next) {
if (conn->handle == handle)
return conn;
}
return NULL;
}
static struct btconn *bthost_find_conn_by_bdaddr(struct bthost *bthost,
const uint8_t *bdaddr)
{
struct btconn *conn;
for (conn = bthost->conns; conn != NULL; conn = conn->next) {
if (!memcmp(conn->bdaddr, bdaddr, 6))
return conn;
}
return NULL;
}
static struct l2conn *bthost_add_l2cap_conn(struct bthost *bthost,
struct btconn *conn,
uint16_t scid, uint16_t dcid,
uint16_t psm)
{
struct l2conn *l2conn;
l2conn = malloc(sizeof(*l2conn));
if (!l2conn)
return NULL;
memset(l2conn, 0, sizeof(*l2conn));
l2conn->psm = psm;
l2conn->scid = scid;
l2conn->dcid = dcid;
l2conn->mode = L2CAP_MODE_OTHER;
l2conn->next = conn->l2conns;
conn->l2conns = l2conn;
return l2conn;
}
static struct rcconn *bthost_add_rfcomm_conn(struct bthost *bthost,
struct btconn *conn,
struct l2conn *l2conn,
uint8_t channel)
{
struct rcconn *rcconn;
rcconn = malloc(sizeof(*rcconn));
if (!rcconn)
return NULL;
memset(rcconn, 0, sizeof(*rcconn));
rcconn->channel = channel;
rcconn->scid = l2conn->scid;
rcconn->next = conn->rcconns;
conn->rcconns = rcconn;
return rcconn;
}
static struct rcconn *btconn_find_rfcomm_conn_by_channel(struct btconn *conn,
uint8_t chan)
{
struct rcconn *rcconn;
for (rcconn = conn->rcconns; rcconn != NULL; rcconn = rcconn->next) {
if (rcconn->channel == chan)
return rcconn;
}
return NULL;
}
static struct l2conn *btconn_find_l2cap_conn_by_scid(struct btconn *conn,
uint16_t scid)
{
struct l2conn *l2conn;
for (l2conn = conn->l2conns; l2conn != NULL; l2conn = l2conn->next) {
if (l2conn->scid == scid)
return l2conn;
}
return NULL;
}
static struct l2conn *btconn_find_l2cap_conn_by_dcid(struct btconn *conn,
uint16_t dcid)
{
struct l2conn *l2conn;
for (l2conn = conn->l2conns; l2conn != NULL; l2conn = l2conn->next) {
if (l2conn->dcid == dcid)
return l2conn;
}
return NULL;
}
static struct l2cap_conn_cb_data *bthost_find_l2cap_cb_by_psm(
struct bthost *bthost, uint16_t psm)
{
struct l2cap_conn_cb_data *cb;
for (cb = bthost->new_l2cap_conn_data; cb != NULL; cb = cb->next) {
if (cb->psm == psm)
return cb;
}
return NULL;
}
static struct rfcomm_conn_cb_data *bthost_find_rfcomm_cb_by_channel(
struct bthost *bthost, uint8_t channel)
{
struct rfcomm_conn_cb_data *cb;
for (cb = bthost->new_rfcomm_conn_data; cb != NULL; cb = cb->next) {
if (cb->channel == channel)
return cb;
}
return NULL;
}
static struct le_ext_adv *le_ext_adv_new(struct bthost *bthost)
{
struct le_ext_adv *ext_adv;
ext_adv = new0(struct le_ext_adv, 1);
ext_adv->bthost = bthost;
/* Add to queue */
if (!queue_push_tail(bthost->le_ext_adv, ext_adv)) {
free(ext_adv);
return NULL;
}
return ext_adv;
}
static void le_ext_adv_free(void *data)
{
struct le_ext_adv *ext_adv = data;
/* Remove from queue */
queue_remove(ext_adv->bthost->le_ext_adv, ext_adv);
free(ext_adv);
}
void bthost_destroy(struct bthost *bthost)
{
if (!bthost)
return;
while (bthost->cmd_q.tail) {
struct cmd *cmd = bthost->cmd_q.tail;
bthost->cmd_q.tail = cmd->prev;
free(cmd);
}
while (bthost->conns) {
struct btconn *conn = bthost->conns;
bthost->conns = conn->next;
btconn_free(conn);
}
while (bthost->l2reqs) {
struct l2cap_pending_req *req = bthost->l2reqs;
bthost->l2reqs = req->next;
req->cb(0, NULL, 0, req->user_data);
free(req);
}
while (bthost->new_l2cap_conn_data) {
struct l2cap_conn_cb_data *cb = bthost->new_l2cap_conn_data;
bthost->new_l2cap_conn_data = cb->next;
free(cb);
}
while (bthost->new_rfcomm_conn_data) {
struct rfcomm_conn_cb_data *cb = bthost->new_rfcomm_conn_data;
bthost->new_rfcomm_conn_data = cb->next;
free(cb);
}
if (bthost->rfcomm_conn_data)
free(bthost->rfcomm_conn_data);
smp_stop(bthost->smp_data);
queue_destroy(bthost->le_ext_adv, le_ext_adv_free);
free(bthost);
}
void bthost_set_send_handler(struct bthost *bthost, bthost_send_func handler,
void *user_data)
{
if (!bthost)
return;
bthost->send_handler = handler;
bthost->send_data = user_data;
}
static void queue_command(struct bthost *bthost, const struct iovec *iov,
int iovlen)
{
struct cmd_queue *cmd_q = &bthost->cmd_q;
struct cmd *cmd;
int i;
cmd = malloc(sizeof(*cmd));
if (!cmd)
return;
memset(cmd, 0, sizeof(*cmd));
for (i = 0; i < iovlen; i++) {
memcpy(cmd->data + cmd->len, iov[i].iov_base, iov[i].iov_len);
cmd->len += iov[i].iov_len;
}
if (cmd_q->tail)
cmd_q->tail->next = cmd;
else
cmd_q->head = cmd;
cmd->prev = cmd_q->tail;
cmd_q->tail = cmd;
}
static void send_packet(struct bthost *bthost, const struct iovec *iov,
int iovlen)
{
int i;
if (!bthost->send_handler)
return;
for (i = 0; i < iovlen; i++) {
if (!i)
util_hexdump('<', iov[i].iov_base, iov[i].iov_len,
bthost->debug_callback, bthost->debug_data);
else
util_hexdump(' ', iov[i].iov_base, iov[i].iov_len,
bthost->debug_callback, bthost->debug_data);
}
bthost->send_handler(iov, iovlen, bthost->send_data);
}
static void send_iov(struct bthost *bthost, uint16_t handle, uint16_t cid,
const struct iovec *iov, int iovcnt)
{
struct bt_hci_acl_hdr acl_hdr;
struct bt_l2cap_hdr l2_hdr;
uint8_t pkt = BT_H4_ACL_PKT;
struct iovec pdu[3 + iovcnt];
int i, len = 0;
for (i = 0; i < iovcnt; i++) {
pdu[3 + i].iov_base = iov[i].iov_base;
pdu[3 + i].iov_len = iov[i].iov_len;
len += iov[i].iov_len;
}
pdu[0].iov_base = &pkt;
pdu[0].iov_len = sizeof(pkt);
acl_hdr.handle = acl_handle_pack(handle, 0);
acl_hdr.dlen = cpu_to_le16(len + sizeof(l2_hdr));
pdu[1].iov_base = &acl_hdr;
pdu[1].iov_len = sizeof(acl_hdr);
l2_hdr.cid = cpu_to_le16(cid);
l2_hdr.len = cpu_to_le16(len);
pdu[2].iov_base = &l2_hdr;
pdu[2].iov_len = sizeof(l2_hdr);
send_packet(bthost, pdu, 3 + iovcnt);
}
static void send_acl(struct bthost *bthost, uint16_t handle, uint16_t cid,
bool sdu_hdr, const void *data, uint16_t len)
{
struct iovec iov[2];
uint16_t sdu;
int num = 0;
if (sdu_hdr) {
sdu = cpu_to_le16(len);
iov[num].iov_base = &sdu;
iov[num].iov_len = sizeof(sdu);
num++;
}
iov[num].iov_base = (void *) data;
iov[num].iov_len = len;
num++;
send_iov(bthost, handle, cid, iov, num);
}
static uint8_t l2cap_sig_send(struct bthost *bthost, struct btconn *conn,
uint8_t code, uint8_t ident,
const void *data, uint16_t len)
{
static uint8_t next_ident = 1;
struct bt_l2cap_hdr_sig hdr;
uint16_t cid;
struct iovec iov[2];
if (!ident) {
ident = next_ident++;
if (!ident)
ident = next_ident++;
}
hdr.code = code;
hdr.ident = ident;
hdr.len = cpu_to_le16(len);
iov[0].iov_base = &hdr;
iov[0].iov_len = sizeof(hdr);
if (conn->addr_type == BDADDR_BREDR)
cid = 0x0001;
else
cid = 0x0005;
if (len == 0) {
send_iov(bthost, conn->handle, cid, iov, 1);
return ident;
}
iov[1].iov_base = (void *) data;
iov[1].iov_len = len;
send_iov(bthost, conn->handle, cid, iov, 2);
return ident;
}
void bthost_add_cid_hook(struct bthost *bthost, uint16_t handle, uint16_t cid,
bthost_cid_hook_func_t func, void *user_data)
{
struct cid_hook *hook;
struct btconn *conn;
conn = bthost_find_conn(bthost, handle);
if (!conn)
return;
hook = malloc(sizeof(*hook));
if (!hook)
return;
memset(hook, 0, sizeof(*hook));
hook->cid = cid;
hook->func = func;
hook->user_data = user_data;
hook->next = conn->cid_hooks;
conn->cid_hooks = hook;
}
void bthost_add_iso_hook(struct bthost *bthost, uint16_t handle,
bthost_iso_hook_func_t func, void *user_data,
bthost_destroy_func_t destroy)
{
struct iso_hook *hook;
struct btconn *conn;
conn = bthost_find_conn(bthost, handle);
if (!conn || conn->iso_hook)
return;
hook = malloc(sizeof(*hook));
if (!hook)
return;
memset(hook, 0, sizeof(*hook));
hook->func = func;
hook->user_data = user_data;
hook->destroy = destroy;
conn->iso_hook = hook;
}
void bthost_send_cid(struct bthost *bthost, uint16_t handle, uint16_t cid,
const void *data, uint16_t len)
{
struct btconn *conn;
struct l2conn *l2conn;
bool sdu_hdr = false;
conn = bthost_find_conn(bthost, handle);
if (!conn)
return;
l2conn = btconn_find_l2cap_conn_by_dcid(conn, cid);
if (l2conn && (l2conn->mode == L2CAP_MODE_LE_CRED ||
l2conn->mode == L2CAP_MODE_LE_ENH_CRED))
sdu_hdr = true;
send_acl(bthost, handle, cid, sdu_hdr, data, len);
}
void bthost_send_cid_v(struct bthost *bthost, uint16_t handle, uint16_t cid,
const struct iovec *iov, int iovcnt)
{
struct btconn *conn;
conn = bthost_find_conn(bthost, handle);
if (!conn)
return;
send_iov(bthost, handle, cid, iov, iovcnt);
}
static void send_iso(struct bthost *bthost, uint16_t handle, bool ts,
uint16_t sn, uint32_t timestamp, uint8_t pkt_status,
const struct iovec *iov, int iovcnt)
{
struct bt_hci_iso_hdr iso_hdr;
struct bt_hci_iso_data_start data_hdr;
uint8_t pkt = BT_H4_ISO_PKT;
struct iovec pdu[4 + iovcnt];
uint16_t flags, dlen;
int i, len = 0;
for (i = 0; i < iovcnt; i++) {
pdu[4 + i].iov_base = iov[i].iov_base;
pdu[4 + i].iov_len = iov[i].iov_len;
len += iov[i].iov_len;
}
pdu[0].iov_base = &pkt;
pdu[0].iov_len = sizeof(pkt);
flags = iso_flags_pack(0x02, ts);
dlen = len + sizeof(data_hdr);
if (ts)
dlen += sizeof(timestamp);
iso_hdr.handle = acl_handle_pack(handle, flags);
iso_hdr.dlen = cpu_to_le16(dlen);
pdu[1].iov_base = &iso_hdr;
pdu[1].iov_len = sizeof(iso_hdr);
if (ts) {
timestamp = cpu_to_le32(timestamp);
pdu[2].iov_base = &timestamp;
pdu[2].iov_len = sizeof(timestamp);
} else {
pdu[2].iov_base = NULL;
pdu[2].iov_len = 0;
}
data_hdr.sn = cpu_to_le16(sn);
data_hdr.slen = cpu_to_le16(iso_data_len_pack(len, pkt_status));
pdu[3].iov_base = &data_hdr;
pdu[3].iov_len = sizeof(data_hdr);
send_packet(bthost, pdu, 4 + iovcnt);
}
void bthost_send_iso(struct bthost *bthost, uint16_t handle, bool ts,
uint16_t sn, uint32_t timestamp, uint8_t pkt_status,
const struct iovec *iov, int iovcnt)
{
struct btconn *conn;
conn = bthost_find_conn(bthost, handle);
if (!conn)
return;
send_iso(bthost, handle, ts, sn, timestamp, pkt_status, iov, iovcnt);
}
bool bthost_l2cap_req(struct bthost *bthost, uint16_t handle, uint8_t code,
const void *data, uint16_t len,
bthost_l2cap_rsp_cb cb, void *user_data)
{
struct l2cap_pending_req *req;
struct btconn *conn;
uint8_t ident;
conn = bthost_find_conn(bthost, handle);
if (!conn)
return false;
if (code == BT_L2CAP_PDU_CONN_REQ &&
len == sizeof(struct bt_l2cap_pdu_conn_req)) {
const struct bt_l2cap_pdu_conn_req *req = data;
bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(req->scid),
le16_to_cpu(req->scid),
le16_to_cpu(req->psm));
}
ident = l2cap_sig_send(bthost, conn, code, 0, data, len);
if (!ident)
return false;
if (!cb)
return true;
req = malloc(sizeof(*req));
if (!req)
return false;
memset(req, 0, sizeof(*req));
req->ident = ident;
req->cb = cb;
req->user_data = user_data;
req->next = bthost->l2reqs;
bthost->l2reqs = req;
return true;
}
static void send_command(struct bthost *bthost, uint16_t opcode,
const void *data, uint8_t len)
{
struct bt_hci_cmd_hdr hdr;
uint8_t pkt = BT_H4_CMD_PKT;
struct iovec iov[3];
bthost_debug(bthost, "command 0x%02x", opcode);
iov[0].iov_base = &pkt;
iov[0].iov_len = sizeof(pkt);
hdr.opcode = cpu_to_le16(opcode);
hdr.plen = len;
iov[1].iov_base = &hdr;
iov[1].iov_len = sizeof(hdr);
if (len > 0) {
iov[2].iov_base = (void *) data;
iov[2].iov_len = len;
}
if (bthost->ncmd) {
send_packet(bthost, iov, len > 0 ? 3 : 2);
bthost->ncmd--;
} else {
queue_command(bthost, iov, len > 0 ? 3 : 2);
}
}
static void next_cmd(struct bthost *bthost)
{
struct cmd_queue *cmd_q = &bthost->cmd_q;
struct cmd *cmd = cmd_q->head;
struct cmd *next;
struct iovec iov;
if (!cmd)
return;
next = cmd->next;
if (!bthost->ncmd)
return;
iov.iov_base = cmd->data;
iov.iov_len = cmd->len;
send_packet(bthost, &iov, 1);
bthost->ncmd--;
if (next)
next->prev = NULL;
else
cmd_q->tail = NULL;
cmd_q->head = next;
free(cmd);
}
static void read_bd_addr_complete(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_rsp_read_bd_addr *ev = data;
if (len < sizeof(*ev))
return;
if (ev->status)
return;
memcpy(bthost->bdaddr, ev->bdaddr, 6);
bthost->ready = true;
if (bthost->ready_cb) {
bthost->ready_cb();
bthost->ready_cb = NULL;
}
}
void bthost_notify_ready(struct bthost *bthost, bthost_ready_cb cb)
{
if (bthost->ready) {
cb();
return;
}
bthost->ready_cb = cb;
}
bool bthost_set_debug(struct bthost *bthost, bthost_debug_func_t callback,
void *user_data, bthost_destroy_func_t destroy)
{
if (!bthost)
return false;
if (bthost->debug_destroy)
bthost->debug_destroy(bthost->debug_data);
bthost->debug_callback = callback;
bthost->debug_destroy = destroy;
bthost->debug_data = user_data;
return true;
}
void bthost_debug(struct bthost *host, const char *format, ...)
{
va_list ap;
if (!host || !format || !host->debug_callback)
return;
va_start(ap, format);
util_debug_va(host->debug_callback, host->debug_data, format, ap);
va_end(ap);
}
static void read_local_features_complete(struct bthost *bthost,
const void *data, uint8_t len)
{
const struct bt_hci_rsp_read_local_features *ev = data;
if (len < sizeof(*ev))
return;
if (ev->status)
return;
memcpy(bthost->features, ev->features, 8);
}
static void evt_cmd_complete(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_cmd_complete *ev = data;
const void *param;
uint16_t opcode;
if (len < sizeof(*ev))
return;
param = data + sizeof(*ev);
bthost->ncmd = ev->ncmd;
opcode = le16toh(ev->opcode);
switch (opcode) {
case BT_HCI_CMD_RESET:
break;
case BT_HCI_CMD_READ_LOCAL_FEATURES:
read_local_features_complete(bthost, param, len - sizeof(*ev));
break;
case BT_HCI_CMD_READ_BD_ADDR:
read_bd_addr_complete(bthost, param, len - sizeof(*ev));
break;
case BT_HCI_CMD_WRITE_SCAN_ENABLE:
break;
case BT_HCI_CMD_LE_SET_ADV_ENABLE:
break;
case BT_HCI_CMD_LE_SET_ADV_PARAMETERS:
break;
case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY:
break;
case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY:
break;
case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY:
break;
case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
break;
case BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED:
break;
case BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT:
break;
case BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY:
break;
case BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY:
break;
case BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY:
break;
case BT_HCI_CMD_LE_LTK_REQ_REPLY:
break;
case BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY:
break;
case BT_HCI_CMD_LE_SET_ADV_DATA:
break;
case BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS:
break;
case BT_HCI_CMD_LE_SET_EXT_ADV_DATA:
break;
case BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE:
break;
case BT_HCI_CMD_LE_SET_PA_PARAMS:
break;
case BT_HCI_CMD_LE_SET_PA_ENABLE:
break;
default:
bthost_debug(bthost, "Unhandled cmd_complete opcode 0x%04x",
opcode);
break;
}
if (bthost->cmd_complete_cb)
bthost->cmd_complete_cb(opcode, 0, param, len - sizeof(*ev),
bthost->cmd_complete_data);
next_cmd(bthost);
}
static void evt_cmd_status(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_cmd_status *ev = data;
uint16_t opcode;
if (len < sizeof(*ev))
return;
bthost->ncmd = ev->ncmd;
opcode = le16toh(ev->opcode);
if (ev->status && bthost->cmd_complete_cb)
bthost->cmd_complete_cb(opcode, ev->status, NULL, 0,
bthost->cmd_complete_data);
next_cmd(bthost);
}
static void evt_conn_request(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_conn_request *ev = data;
struct bt_hci_cmd_accept_conn_request cmd;
if (len < sizeof(*ev))
return;
memset(&cmd, 0, sizeof(cmd));
memcpy(cmd.bdaddr, ev->bdaddr, sizeof(ev->bdaddr));
send_command(bthost, BT_HCI_CMD_ACCEPT_CONN_REQUEST, &cmd,
sizeof(cmd));
}
static void init_conn(struct bthost *bthost, uint16_t handle,
const uint8_t *bdaddr, uint8_t addr_type)
{
struct btconn *conn;
const uint8_t *ia, *ra;
uint8_t ia_type, ra_type;
conn = malloc(sizeof(*conn));
if (!conn)
return;
memset(conn, 0, sizeof(*conn));
conn->handle = handle;
memcpy(conn->bdaddr, bdaddr, 6);
conn->addr_type = addr_type;
conn->next_cid = 0x0040;
conn->next = bthost->conns;
bthost->conns = conn;
if (bthost->conn_init) {
ia = bthost->bdaddr;
if (addr_type == BDADDR_BREDR)
ia_type = addr_type;
else
ia_type = BDADDR_LE_PUBLIC;
ra = bdaddr;
ra_type = addr_type;
} else {
ia = bdaddr;
ia_type = addr_type;
ra = bthost->bdaddr;
if (addr_type == BDADDR_BREDR)
ra_type = addr_type;
else
ra_type = BDADDR_LE_PUBLIC;
}
conn->smp_data = smp_conn_add(bthost->smp_data, handle, ia, ia_type,
ra, ra_type, bthost->conn_init);
if (bthost->new_conn_cb)
bthost->new_conn_cb(conn->handle, bthost->new_conn_data);
if (addr_type == BDADDR_BREDR) {
struct bt_l2cap_pdu_info_req req;
req.type = L2CAP_IT_FIXED_CHAN;
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_REQ, 1,
&req, sizeof(req));
}
}
static void evt_conn_complete(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_conn_complete *ev = data;
if (len < sizeof(*ev))
return;
if (ev->status)
return;
init_conn(bthost, le16_to_cpu(ev->handle), ev->bdaddr, BDADDR_BREDR);
}
static void evt_disconn_complete(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_disconnect_complete *ev = data;
struct btconn **curr;
uint16_t handle;
if (len < sizeof(*ev))
return;
if (ev->status)
return;
handle = le16_to_cpu(ev->handle);
for (curr = &bthost->conns; *curr;) {
struct btconn *conn = *curr;
if (conn->handle == handle) {
*curr = conn->next;
btconn_free(conn);
} else {
curr = &conn->next;
}
}
}
static void evt_num_completed_packets(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_num_completed_packets *ev = data;
if (len < sizeof(*ev))
return;
}
static void evt_auth_complete(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_auth_complete *ev = data;
struct bt_hci_cmd_set_conn_encrypt cp;
if (len < sizeof(*ev))
return;
if (ev->status)
return;
cp.handle = ev->handle;
cp.encr_mode = 0x01;
send_command(bthost, BT_HCI_CMD_SET_CONN_ENCRYPT, &cp, sizeof(cp));
}
static void evt_pin_code_request(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_pin_code_request *ev = data;
if (len < sizeof(*ev))
return;
if (bthost->pin_len > 0) {
struct bt_hci_cmd_pin_code_request_reply cp;
memset(&cp, 0, sizeof(cp));
memcpy(cp.bdaddr, ev->bdaddr, 6);
cp.pin_len = bthost->pin_len;
memcpy(cp.pin_code, bthost->pin, bthost->pin_len);
send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_REPLY,
&cp, sizeof(cp));
} else {
struct bt_hci_cmd_pin_code_request_neg_reply cp;
memcpy(cp.bdaddr, ev->bdaddr, 6);
send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY,
&cp, sizeof(cp));
}
}
static void evt_link_key_request(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_link_key_request *ev = data;
struct bt_hci_cmd_link_key_request_neg_reply cp;
if (len < sizeof(*ev))
return;
memset(&cp, 0, sizeof(cp));
memcpy(cp.bdaddr, ev->bdaddr, 6);
send_command(bthost, BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY,
&cp, sizeof(cp));
}
static void evt_link_key_notify(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_link_key_notify *ev = data;
if (len < sizeof(*ev))
return;
}
static void evt_encrypt_change(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_encrypt_change *ev = data;
struct btconn *conn;
uint16_t handle;
if (len < sizeof(*ev))
return;
handle = acl_handle(ev->handle);
conn = bthost_find_conn(bthost, handle);
if (!conn)
return;
if (ev->status)
return;
conn->encr_mode = ev->encr_mode;
if (conn->smp_data)
smp_conn_encrypted(conn->smp_data, conn->encr_mode);
}
static void evt_io_cap_response(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_io_capability_response *ev = data;
struct btconn *conn;
if (len < sizeof(*ev))
return;
conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr);
if (!conn)
return;
}
static void evt_io_cap_request(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_io_capability_request *ev = data;
struct bt_hci_cmd_io_capability_request_reply cp;
struct btconn *conn;
if (len < sizeof(*ev))
return;
conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr);
if (!conn)
return;
memcpy(cp.bdaddr, ev->bdaddr, 6);
cp.capability = bthost->io_capability;
cp.oob_data = 0x00;
cp.authentication = bthost->auth_req;
send_command(bthost, BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY,
&cp, sizeof(cp));
}
static void evt_user_confirm_request(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_user_confirm_request *ev = data;
struct btconn *conn;
if (len < sizeof(*ev))
return;
conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr);
if (!conn)
return;
if (bthost->reject_user_confirm) {
send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY,
ev->bdaddr, 6);
return;
}
send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
ev->bdaddr, 6);
}
static void evt_simple_pairing_complete(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_simple_pairing_complete *ev = data;
if (len < sizeof(*ev))
return;
}
static void evt_le_conn_complete(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_le_conn_complete *ev = data;
uint8_t addr_type;
if (len < sizeof(*ev))
return;
if (ev->status)
return;
if (ev->peer_addr_type == 0x00)
addr_type = BDADDR_LE_PUBLIC;
else
addr_type = BDADDR_LE_RANDOM;
init_conn(bthost, le16_to_cpu(ev->handle), ev->peer_addr, addr_type);
}
static void evt_le_ext_conn_complete(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_le_enhanced_conn_complete *ev = data;
uint8_t addr_type;
if (len < sizeof(*ev))
return;
if (ev->status)
return;
if (ev->peer_addr_type == 0x00)
addr_type = BDADDR_LE_PUBLIC;
else
addr_type = BDADDR_LE_RANDOM;
init_conn(bthost, le16_to_cpu(ev->handle), ev->peer_addr, addr_type);
}
static void evt_le_conn_update_complete(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_le_conn_update_complete *ev = data;
if (len < sizeof(*ev))
return;
if (ev->status)
return;
}
static void evt_le_remote_features_complete(struct bthost *bthost,
const void *data, uint8_t len)
{
const struct bt_hci_evt_le_remote_features_complete *ev = data;
if (len < sizeof(*ev))
return;
if (ev->status)
return;
}
static void evt_le_ltk_request(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_le_long_term_key_request *ev = data;
struct bt_hci_cmd_le_ltk_req_reply cp;
struct bt_hci_cmd_le_ltk_req_neg_reply *neg_cp = (void *) &cp;
uint16_t handle, ediv;
uint64_t rand;
struct btconn *conn;
int err;
if (len < sizeof(*ev))
return;
handle = acl_handle(ev->handle);
conn = bthost_find_conn(bthost, handle);
if (!conn)
return;
rand = le64_to_cpu(ev->rand);
ediv = le16_to_cpu(ev->ediv);
cp.handle = ev->handle;
err = smp_get_ltk(conn->smp_data, rand, ediv, cp.ltk);
if (err < 0)
send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY,
neg_cp, sizeof(*neg_cp));
else
send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_REPLY, &cp,
sizeof(cp));
}
static void init_iso(struct bthost *bthost, uint16_t handle,
const uint8_t *bdaddr, uint8_t addr_type)
{
struct btconn *conn;
bthost_debug(bthost, "ISO handle 0x%4.4x", handle);
conn = malloc(sizeof(*conn));
if (!conn)
return;
memset(conn, 0, sizeof(*conn));
conn->handle = handle;
memcpy(conn->bdaddr, bdaddr, 6);
conn->addr_type = addr_type;
conn->next = bthost->conns;
bthost->conns = conn;
if (bthost->new_iso_cb)
bthost->new_iso_cb(handle, bthost->new_iso_data);
}
static void evt_le_cis_established(struct bthost *bthost, const void *data,
uint8_t size)
{
const struct bt_hci_evt_le_cis_established *ev = data;
if (ev->status)
return;
init_iso(bthost, ev->conn_handle, BDADDR_ANY->b, BDADDR_LE_PUBLIC);
}
static void evt_le_cis_req(struct bthost *bthost, const void *data, uint8_t len)
{
const struct bt_hci_evt_le_cis_req *ev = data;
struct bt_hci_cmd_le_accept_cis cmd;
struct bt_hci_cmd_le_reject_cis rej;
if (len < sizeof(*ev))
return;
if (bthost->accept_iso_cb) {
memset(&rej, 0, sizeof(rej));
rej.reason = bthost->accept_iso_cb(le16_to_cpu(ev->cis_handle),
bthost->new_iso_data);
if (rej.reason) {
rej.handle = ev->cis_handle;
send_command(bthost, BT_HCI_CMD_LE_REJECT_CIS,
&rej, sizeof(rej));
return;
}
}
memset(&cmd, 0, sizeof(cmd));
cmd.handle = ev->cis_handle;
send_command(bthost, BT_HCI_CMD_LE_ACCEPT_CIS, &cmd, sizeof(cmd));
}
static void evt_le_ext_adv_report(struct bthost *bthost, const void *data,
uint8_t len)
{
const struct bt_hci_evt_le_ext_adv_report *ev = data;
const struct bt_hci_le_ext_adv_report *report;
struct le_ext_adv *le_ext_adv;
int i;
data += sizeof(ev->num_reports);
for (i = 0; i < ev->num_reports; i++) {
char addr_str[18];
report = data;
ba2str((bdaddr_t *) report->addr, addr_str);
bthost_debug(bthost, "le ext adv report: %s (0x%02x)",
addr_str, report->addr_type);
/* Add ext event to the queue */
le_ext_adv = le_ext_adv_new(bthost);
if (le_ext_adv) {
le_ext_adv->addr_type = report->addr_type;
memcpy(le_ext_adv->addr, report->addr, 6);
le_ext_adv->direct_addr_type = report->direct_addr_type;
memcpy(le_ext_adv->direct_addr, report->direct_addr, 6);
}
data += (sizeof(*report) + report->data_len);
}
}
static void evt_le_big_complete(struct bthost *bthost, const void *data,
uint8_t size)
{
const struct bt_hci_evt_le_big_complete *ev = data;
int i;
if (ev->status)
return;
for (i = 0; i < ev->num_bis; i++) {
uint16_t handle = le16_to_cpu(ev->bis_handle[i]);
init_iso(bthost, handle, BDADDR_ANY->b, BDADDR_LE_PUBLIC);
}
}
static void evt_le_big_sync_established(struct bthost *bthost,
const void *data, uint8_t size)
{
const struct bt_hci_evt_le_big_sync_estabilished *ev = data;
int i;
if (ev->status)
return;
for (i = 0; i < ev->num_bis; i++) {
uint16_t handle = le16_to_cpu(ev->bis[i]);
init_iso(bthost, handle, BDADDR_ANY->b, BDADDR_LE_PUBLIC);
}
}
static void evt_le_meta_event(struct bthost *bthost, const void *data,
uint8_t len)
{
const uint8_t *event = data;
const void *evt_data = data + 1;
if (len < 1)
return;
bthost_debug(bthost, "meta event 0x%02x", *event);
switch (*event) {
case BT_HCI_EVT_LE_CONN_COMPLETE:
evt_le_conn_complete(bthost, evt_data, len - 1);
break;
case BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE:
evt_le_conn_update_complete(bthost, evt_data, len - 1);
break;
case BT_HCI_EVT_LE_REMOTE_FEATURES_COMPLETE:
evt_le_remote_features_complete(bthost, evt_data, len - 1);
break;
case BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST:
evt_le_ltk_request(bthost, evt_data, len - 1);
break;
case BT_HCI_EVT_LE_ENHANCED_CONN_COMPLETE:
evt_le_ext_conn_complete(bthost, evt_data, len - 1);
break;
case BT_HCI_EVT_LE_EXT_ADV_REPORT:
evt_le_ext_adv_report(bthost, evt_data, len - 1);
break;
case BT_HCI_EVT_LE_CIS_ESTABLISHED:
evt_le_cis_established(bthost, evt_data, len - 1);
break;
case BT_HCI_EVT_LE_CIS_REQ:
evt_le_cis_req(bthost, evt_data, len - 1);
break;
case BT_HCI_EVT_LE_BIG_COMPLETE:
evt_le_big_complete(bthost, evt_data, len - 1);
break;
case BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED:
evt_le_big_sync_established(bthost, evt_data, len - 1);
break;
default:
bthost_debug(bthost, "Unsupported LE Meta event 0x%2.2x",
*event);
break;
}
}
static void process_evt(struct bthost *bthost, const void *data, uint16_t len)
{
const struct bt_hci_evt_hdr *hdr = data;
const void *param;
if (len < sizeof(*hdr))
return;
if (sizeof(*hdr) + hdr->plen != len)
return;
param = data + sizeof(*hdr);
bthost_debug(bthost, "event 0x%02x", hdr->evt);
switch (hdr->evt) {
case BT_HCI_EVT_CMD_COMPLETE:
evt_cmd_complete(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_CMD_STATUS:
evt_cmd_status(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_CONN_REQUEST:
evt_conn_request(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_CONN_COMPLETE:
evt_conn_complete(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_DISCONNECT_COMPLETE:
evt_disconn_complete(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_NUM_COMPLETED_PACKETS:
evt_num_completed_packets(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_AUTH_COMPLETE:
evt_auth_complete(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_PIN_CODE_REQUEST:
evt_pin_code_request(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_LINK_KEY_REQUEST:
evt_link_key_request(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_LINK_KEY_NOTIFY:
evt_link_key_notify(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_ENCRYPT_CHANGE:
evt_encrypt_change(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_IO_CAPABILITY_RESPONSE:
evt_io_cap_response(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_IO_CAPABILITY_REQUEST:
evt_io_cap_request(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_USER_CONFIRM_REQUEST:
evt_user_confirm_request(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE:
evt_simple_pairing_complete(bthost, param, hdr->plen);
break;
case BT_HCI_EVT_LE_META_EVENT:
evt_le_meta_event(bthost, param, hdr->plen);
break;
default:
bthost_debug(bthost, "Unsupported event 0x%2.2x", hdr->evt);
break;
}
}
static bool l2cap_cmd_rej(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_cmd_reject *rsp = data;
if (len < sizeof(*rsp))
return false;
return true;
}
static bool l2cap_conn_req(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_conn_req *req = data;
struct l2cap_conn_cb_data *cb_data;
struct bt_l2cap_pdu_conn_rsp rsp;
uint16_t psm;
if (len < sizeof(*req))
return false;
psm = le16_to_cpu(req->psm);
memset(&rsp, 0, sizeof(rsp));
rsp.scid = req->scid;
cb_data = bthost_find_l2cap_cb_by_psm(bthost, psm);
if (cb_data)
rsp.dcid = rsp.scid;
else
rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONN_RSP, ident, &rsp,
sizeof(rsp));
if (!rsp.result) {
struct bt_l2cap_pdu_config_req conf_req;
struct l2conn *l2conn;
l2conn = bthost_add_l2cap_conn(bthost, conn,
le16_to_cpu(rsp.dcid),
le16_to_cpu(rsp.scid),
le16_to_cpu(psm));
memset(&conf_req, 0, sizeof(conf_req));
conf_req.dcid = rsp.scid;
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0,
&conf_req, sizeof(conf_req));
if (cb_data && l2conn->psm == cb_data->psm && cb_data->func)
cb_data->func(conn->handle, l2conn->dcid,
cb_data->user_data);
}
return true;
}
static void rfcomm_sabm_send(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, uint8_t cr, uint8_t dlci)
{
struct rfcomm_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.address = RFCOMM_ADDR(cr, dlci);
cmd.control = RFCOMM_CTRL(RFCOMM_SABM, 1);
cmd.length = RFCOMM_LEN8(0);
cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd);
send_acl(bthost, conn->handle, l2conn->dcid, false, &cmd, sizeof(cmd));
}
static bool l2cap_conn_rsp(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_conn_rsp *rsp = data;
struct bt_l2cap_pdu_config_req req;
struct l2conn *l2conn;
if (len < sizeof(*rsp))
return false;
l2conn = btconn_find_l2cap_conn_by_scid(conn, le16_to_cpu(rsp->scid));
if (l2conn)
l2conn->dcid = le16_to_cpu(rsp->dcid);
else
return false;
if (rsp->result)
return true;
memset(&req, 0, sizeof(req));
req.dcid = rsp->dcid;
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0,
&req, sizeof(req));
return true;
}
static bool l2cap_config_req(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_config_req *req = data;
struct bt_l2cap_pdu_config_rsp rsp;
struct l2conn *l2conn;
uint16_t dcid;
if (len < sizeof(*req))
return false;
dcid = le16_to_cpu(req->dcid);
l2conn = btconn_find_l2cap_conn_by_scid(conn, dcid);
if (!l2conn)
return false;
memset(&rsp, 0, sizeof(rsp));
rsp.scid = cpu_to_le16(l2conn->dcid);
rsp.flags = req->flags;
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_RSP, ident, &rsp,
sizeof(rsp));
return true;
}
static bool l2cap_config_rsp(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_config_rsp *rsp = data;
struct l2conn *l2conn;
if (len < sizeof(*rsp))
return false;
l2conn = btconn_find_l2cap_conn_by_scid(conn, rsp->scid);
if (!l2conn)
return false;
if (l2conn->psm == 0x0003 && !rsp->result && bthost->rfcomm_conn_data)
rfcomm_sabm_send(bthost, conn, l2conn, 1, 0);
return true;
}
static bool l2cap_disconn_req(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_disconn_req *req = data;
struct l2cap_conn_cb_data *cb_data;
struct bt_l2cap_pdu_disconn_rsp rsp;
struct l2conn *l2conn;
if (len < sizeof(*req))
return false;
memset(&rsp, 0, sizeof(rsp));
rsp.dcid = req->dcid;
rsp.scid = req->scid;
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_DISCONN_RSP, ident, &rsp,
sizeof(rsp));
l2conn = btconn_find_l2cap_conn_by_scid(conn, rsp.scid);
if (!l2conn)
return true;
cb_data = bthost_find_l2cap_cb_by_psm(bthost, l2conn->psm);
if (cb_data && cb_data->disconn_func)
cb_data->disconn_func(cb_data->user_data);
return true;
}
static bool l2cap_info_req(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_info_req *req = data;
uint64_t fixed_chan;
uint16_t type;
uint8_t buf[12];
struct bt_l2cap_pdu_info_rsp *rsp = (void *) buf;
if (len < sizeof(*req))
return false;
memset(buf, 0, sizeof(buf));
rsp->type = req->type;
type = le16_to_cpu(req->type);
switch (type) {
case L2CAP_IT_FEAT_MASK:
rsp->result = 0x0000;
put_le32(L2CAP_FEAT_FIXED_CHAN, rsp->data);
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident,
rsp, sizeof(*rsp) + 4);
break;
case L2CAP_IT_FIXED_CHAN:
rsp->result = 0x0000;
fixed_chan = L2CAP_FC_SIG_BREDR;
if (bthost->sc && bthost->le)
fixed_chan |= L2CAP_FC_SMP_BREDR;
put_le64(fixed_chan, rsp->data);
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident,
rsp, sizeof(*rsp) + sizeof(fixed_chan));
break;
default:
rsp->result = cpu_to_le16(0x0001); /* Not Supported */
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident,
rsp, sizeof(*rsp));
break;
}
return true;
}
static bool l2cap_info_rsp(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_info_rsp *rsp = data;
uint16_t type;
if (len < sizeof(*rsp))
return false;
if (rsp->result)
return true;
type = le16_to_cpu(rsp->type);
switch (type) {
case L2CAP_IT_FIXED_CHAN:
if (len < sizeof(*rsp) + 8)
return false;
conn->fixed_chan = get_le64(rsp->data);
if (conn->smp_data && conn->encr_mode)
smp_conn_encrypted(conn->smp_data, conn->encr_mode);
break;
default:
break;
}
return true;
}
static void handle_pending_l2reqs(struct bthost *bthost, struct btconn *conn,
uint8_t ident, uint8_t code,
const void *data, uint16_t len)
{
struct l2cap_pending_req **curr;
for (curr = &bthost->l2reqs; *curr != NULL;) {
struct l2cap_pending_req *req = *curr;
if (req->ident != ident) {
curr = &req->next;
continue;
}
*curr = req->next;
req->cb(code, data, len, req->user_data);
free(req);
}
}
static bool l2cap_rsp(uint8_t code)
{
switch (code) {
case BT_L2CAP_PDU_CMD_REJECT:
case BT_L2CAP_PDU_CONN_RSP:
case BT_L2CAP_PDU_CONFIG_RSP:
case BT_L2CAP_PDU_INFO_RSP:
return true;
}
return false;
}
static void l2cap_sig(struct bthost *bthost, struct btconn *conn,
const void *data, uint16_t len)
{
const struct bt_l2cap_hdr_sig *hdr = data;
struct bt_l2cap_pdu_cmd_reject rej;
uint16_t hdr_len;
bool ret;
if (len < sizeof(*hdr))
goto reject;
hdr_len = le16_to_cpu(hdr->len);
if (sizeof(*hdr) + hdr_len != len)
goto reject;
switch (hdr->code) {
case BT_L2CAP_PDU_CMD_REJECT:
ret = l2cap_cmd_rej(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_CONN_REQ:
ret = l2cap_conn_req(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_CONN_RSP:
ret = l2cap_conn_rsp(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_CONFIG_REQ:
ret = l2cap_config_req(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_CONFIG_RSP:
ret = l2cap_config_rsp(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_DISCONN_REQ:
ret = l2cap_disconn_req(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_INFO_REQ:
ret = l2cap_info_req(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_INFO_RSP:
ret = l2cap_info_rsp(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
default:
bthost_debug(bthost, "Unknown L2CAP code 0x%02x", hdr->code);
ret = false;
}
if (l2cap_rsp(hdr->code))
handle_pending_l2reqs(bthost, conn, hdr->ident, hdr->code,
data + sizeof(*hdr), hdr_len);
if (ret)
return;
reject:
memset(&rej, 0, sizeof(rej));
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CMD_REJECT, 0,
&rej, sizeof(rej));
}
static bool l2cap_conn_param_req(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_conn_param_req *req = data;
struct bt_l2cap_pdu_conn_param_rsp rsp;
struct bt_hci_cmd_le_conn_update hci_cmd;
if (len < sizeof(*req))
return false;
memset(&hci_cmd, 0, sizeof(hci_cmd));
hci_cmd.handle = cpu_to_le16(conn->handle);
hci_cmd.min_interval = req->min_interval;
hci_cmd.max_interval = req->max_interval;
hci_cmd.latency = req->latency;
hci_cmd.supv_timeout = req->timeout;
hci_cmd.min_length = cpu_to_le16(0x0001);
hci_cmd.max_length = cpu_to_le16(0x0001);
send_command(bthost, BT_HCI_CMD_LE_CONN_UPDATE,
&hci_cmd, sizeof(hci_cmd));
memset(&rsp, 0, sizeof(rsp));
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONN_PARAM_RSP, ident,
&rsp, sizeof(rsp));
return true;
}
static bool l2cap_conn_param_rsp(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_conn_param_req *rsp = data;
if (len < sizeof(*rsp))
return false;
return true;
}
static bool l2cap_le_conn_req(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_le_conn_req *req = data;
struct l2cap_conn_cb_data *cb_data;
struct bt_l2cap_pdu_le_conn_rsp rsp;
uint16_t psm;
if (len < sizeof(*req))
return false;
psm = le16_to_cpu(req->psm);
memset(&rsp, 0, sizeof(rsp));
cb_data = bthost_find_l2cap_cb_by_psm(bthost, psm);
if (cb_data) {
rsp.dcid = cpu_to_le16(conn->next_cid++);
rsp.mtu = cpu_to_le16(cb_data->mtu) ? : cpu_to_le16(23);
rsp.mps = cpu_to_le16(cb_data->mps) ? : cpu_to_le16(23);
rsp.credits = cpu_to_le16(cb_data->credits) ? : cpu_to_le16(1);
} else
rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_LE_CONN_RSP, ident, &rsp,
sizeof(rsp));
if (!rsp.result) {
struct l2conn *l2conn;
l2conn = bthost_add_l2cap_conn(bthost, conn,
le16_to_cpu(rsp.dcid),
le16_to_cpu(req->scid),
le16_to_cpu(psm));
l2conn->mode = L2CAP_MODE_LE_CRED;
if (cb_data && l2conn->psm == cb_data->psm && cb_data->func)
cb_data->func(conn->handle, l2conn->dcid,
cb_data->user_data);
}
return true;
}
static bool l2cap_le_conn_rsp(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_le_conn_rsp *rsp = data;
struct l2conn *l2conn;
if (len < sizeof(*rsp))
return false;
/* TODO add L2CAP connection before with proper PSM */
l2conn = bthost_add_l2cap_conn(bthost, conn, 0,
le16_to_cpu(rsp->dcid), 0);
l2conn->mode = L2CAP_MODE_LE_CRED;
return true;
}
static bool l2cap_ecred_conn_req(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_ecred_conn_req *req = data;
struct {
struct bt_l2cap_pdu_ecred_conn_rsp pdu;
uint16_t dcid[5];
} __attribute__ ((packed)) rsp;
uint16_t psm;
int num_scid, i = 0;
if (len < sizeof(*req))
return false;
psm = le16_to_cpu(req->psm);
memset(&rsp, 0, sizeof(rsp));
rsp.pdu.mtu = 64;
rsp.pdu.mps = 64;
rsp.pdu.credits = 1;
if (!bthost_find_l2cap_cb_by_psm(bthost, psm)) {
rsp.pdu.result = cpu_to_le16(0x0002); /* PSM Not Supported */
goto respond;
}
len -= sizeof(rsp.pdu);
num_scid = len / sizeof(*req->scid);
for (; i < num_scid; i++)
rsp.dcid[i] = cpu_to_le16(conn->next_cid++);
respond:
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_ECRED_CONN_RSP, ident, &rsp,
sizeof(rsp.pdu) + i * sizeof(*rsp.dcid));
return true;
}
static bool l2cap_ecred_conn_rsp(struct bthost *bthost, struct btconn *conn,
uint8_t ident, const void *data, uint16_t len)
{
const struct {
const struct bt_l2cap_pdu_ecred_conn_rsp *pdu;
uint16_t scid[5];
} __attribute__ ((packed)) *rsp = data;
int num_scid, i;
struct l2conn *l2conn;
if (len < sizeof(*rsp))
return false;
num_scid = len / sizeof(*rsp->scid);
for (i = 0; i < num_scid; i++) {
/* TODO add L2CAP connection before with proper PSM */
l2conn = bthost_add_l2cap_conn(bthost, conn, 0,
le16_to_cpu(rsp->scid[i]), 0);
l2conn->mode = L2CAP_MODE_LE_ENH_CRED;
}
return true;
}
static bool l2cap_le_rsp(uint8_t code)
{
switch (code) {
case BT_L2CAP_PDU_CMD_REJECT:
case BT_L2CAP_PDU_CONN_PARAM_RSP:
case BT_L2CAP_PDU_LE_CONN_RSP:
case BT_L2CAP_PDU_ECRED_CONN_RSP:
return true;
}
return false;
}
static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn,
const void *data, uint16_t len)
{
const struct bt_l2cap_hdr_sig *hdr = data;
struct bt_l2cap_pdu_cmd_reject rej;
uint16_t hdr_len;
bool ret;
if (len < sizeof(*hdr))
goto reject;
hdr_len = le16_to_cpu(hdr->len);
if (sizeof(*hdr) + hdr_len != len)
goto reject;
switch (hdr->code) {
case BT_L2CAP_PDU_CMD_REJECT:
ret = l2cap_cmd_rej(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_DISCONN_REQ:
ret = l2cap_disconn_req(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_CONN_PARAM_REQ:
ret = l2cap_conn_param_req(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_CONN_PARAM_RSP:
ret = l2cap_conn_param_rsp(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_LE_CONN_REQ:
ret = l2cap_le_conn_req(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_LE_CONN_RSP:
ret = l2cap_le_conn_rsp(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_ECRED_CONN_REQ:
ret = l2cap_ecred_conn_req(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
case BT_L2CAP_PDU_ECRED_CONN_RSP:
ret = l2cap_ecred_conn_rsp(bthost, conn, hdr->ident,
data + sizeof(*hdr), hdr_len);
break;
default:
bthost_debug(bthost, "Unknown L2CAP code 0x%02x", hdr->code);
ret = false;
}
if (l2cap_le_rsp(hdr->code))
handle_pending_l2reqs(bthost, conn, hdr->ident, hdr->code,
data + sizeof(*hdr), hdr_len);
if (ret)
return;
reject:
memset(&rej, 0, sizeof(rej));
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CMD_REJECT, 0,
&rej, sizeof(rej));
}
static struct cid_hook *find_cid_hook(struct btconn *conn, uint16_t cid)
{
struct cid_hook *hook;
for (hook = conn->cid_hooks; hook != NULL; hook = hook->next) {
if (hook->cid == cid)
return hook;
}
return NULL;
}
static struct rfcomm_chan_hook *find_rfcomm_chan_hook(struct btconn *conn,
uint8_t channel)
{
struct rfcomm_chan_hook *hook;
for (hook = conn->rfcomm_chan_hooks; hook != NULL; hook = hook->next)
if (hook->channel == channel)
return hook;
return NULL;
}
static void rfcomm_ua_send(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, uint8_t cr, uint8_t dlci)
{
struct rfcomm_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.address = RFCOMM_ADDR(cr, dlci);
cmd.control = RFCOMM_CTRL(RFCOMM_UA, 1);
cmd.length = RFCOMM_LEN8(0);
cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd);
send_acl(bthost, conn->handle, l2conn->dcid, false, &cmd, sizeof(cmd));
}
static void rfcomm_dm_send(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, uint8_t cr, uint8_t dlci)
{
struct rfcomm_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.address = RFCOMM_ADDR(cr, dlci);
cmd.control = RFCOMM_CTRL(RFCOMM_DM, 1);
cmd.length = RFCOMM_LEN8(0);
cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd);
send_acl(bthost, conn->handle, l2conn->dcid, false, &cmd, sizeof(cmd));
}
static void rfcomm_sabm_recv(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, const void *data,
uint16_t len)
{
const struct rfcomm_cmd *hdr = data;
uint8_t dlci;
struct rfcomm_conn_cb_data *cb;
uint8_t chan;
if (len < sizeof(*hdr))
return;
chan = RFCOMM_GET_CHANNEL(hdr->address);
dlci = RFCOMM_GET_DLCI(hdr->address);
cb = bthost_find_rfcomm_cb_by_channel(bthost, chan);
if (!dlci || cb) {
bthost_add_rfcomm_conn(bthost, conn, l2conn, chan);
rfcomm_ua_send(bthost, conn, l2conn, 1, dlci);
if (cb && cb->func)
cb->func(conn->handle, l2conn->scid, cb->user_data,
true);
} else {
rfcomm_dm_send(bthost, conn, l2conn, 1, dlci);
}
}
static void rfcomm_disc_recv(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, const void *data,
uint16_t len)
{
const struct rfcomm_cmd *hdr = data;
uint8_t dlci;
if (len < sizeof(*hdr))
return;
dlci = RFCOMM_GET_DLCI(hdr->address);
rfcomm_ua_send(bthost, conn, l2conn, 0, dlci);
}
static void rfcomm_uih_send(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, uint8_t address,
uint8_t type, const void *data, uint16_t len)
{
struct rfcomm_hdr hdr;
struct rfcomm_mcc mcc;
uint8_t fcs;
struct iovec iov[4];
hdr.address = address;
hdr.control = RFCOMM_CTRL(RFCOMM_UIH, 0);
hdr.length = RFCOMM_LEN8(sizeof(mcc) + len);
iov[0].iov_base = &hdr;
iov[0].iov_len = sizeof(hdr);
mcc.type = type;
mcc.length = RFCOMM_LEN8(len);
iov[1].iov_base = &mcc;
iov[1].iov_len = sizeof(mcc);
iov[2].iov_base = (void *) data;
iov[2].iov_len = len;
fcs = rfcomm_fcs((uint8_t *) &hdr);
iov[3].iov_base = &fcs;
iov[3].iov_len = sizeof(fcs);
send_iov(bthost, conn->handle, l2conn->dcid, iov, 4);
}
static void rfcomm_ua_recv(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, const void *data,
uint16_t len)
{
const struct rfcomm_cmd *ua_hdr = data;
uint8_t channel;
struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data;
uint8_t type;
struct rfcomm_pn pn_cmd;
if (len < sizeof(*ua_hdr))
return;
channel = RFCOMM_GET_CHANNEL(ua_hdr->address);
type = RFCOMM_GET_TYPE(ua_hdr->control);
if (channel && conn_data && conn_data->channel == channel) {
bthost_add_rfcomm_conn(bthost, conn, l2conn, channel);
if (conn_data->cb)
conn_data->cb(conn->handle, l2conn->scid,
conn_data->user_data, true);
free(bthost->rfcomm_conn_data);
bthost->rfcomm_conn_data = NULL;
return;
}
if (!conn_data || !RFCOMM_TEST_CR(type))
return;
bthost_add_rfcomm_conn(bthost, conn, l2conn, channel);
pn_cmd.dlci = conn_data->channel * 2;
pn_cmd.priority = 7;
pn_cmd.ack_timer = 0;
pn_cmd.max_retrans = 0;
pn_cmd.mtu = 667;
pn_cmd.credits = 7;
rfcomm_uih_send(bthost, conn, l2conn, RFCOMM_ADDR(1, 0),
RFCOMM_MCC_TYPE(1, RFCOMM_PN), &pn_cmd, sizeof(pn_cmd));
}
static void rfcomm_dm_recv(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, const void *data,
uint16_t len)
{
const struct rfcomm_cmd *hdr = data;
uint8_t channel;
struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data;
if (len < sizeof(*hdr))
return;
channel = RFCOMM_GET_CHANNEL(hdr->address);
if (conn_data && conn_data->channel == channel) {
if (conn_data->cb)
conn_data->cb(conn->handle, l2conn->scid,
conn_data->user_data, false);
free(bthost->rfcomm_conn_data);
bthost->rfcomm_conn_data = NULL;
}
}
static void rfcomm_msc_recv(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, uint8_t cr,
const struct rfcomm_msc *msc)
{
struct rfcomm_msc msc_cmd;
msc_cmd.dlci = msc->dlci;
msc_cmd.v24_sig = msc->v24_sig;
rfcomm_uih_send(bthost, conn, l2conn, RFCOMM_ADDR(0, 0),
RFCOMM_MCC_TYPE(cr, RFCOMM_MSC), &msc_cmd,
sizeof(msc_cmd));
}
static void rfcomm_pn_recv(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, uint8_t cr,
const struct rfcomm_pn *pn)
{
struct rfcomm_pn pn_cmd;
if (!cr) {
rfcomm_sabm_send(bthost, conn, l2conn, 1,
bthost->rfcomm_conn_data->channel * 2);
return;
}
pn_cmd.dlci = pn->dlci;
pn_cmd.flow_ctrl = pn->flow_ctrl;
pn_cmd.priority = pn->priority;
pn_cmd.ack_timer = pn->ack_timer;
pn_cmd.max_retrans = pn->max_retrans;
pn_cmd.mtu = pn->mtu;
pn_cmd.credits = 255;
rfcomm_uih_send(bthost, conn, l2conn, RFCOMM_ADDR(1, 0),
RFCOMM_MCC_TYPE(0, RFCOMM_PN), &pn_cmd, sizeof(pn_cmd));
}
static void rfcomm_mcc_recv(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, const void *data, uint16_t len)
{
const struct rfcomm_mcc *mcc = data;
const struct rfcomm_msc *msc;
const struct rfcomm_pn *pn;
if (len < sizeof(*mcc))
return;
switch (RFCOMM_GET_MCC_TYPE(mcc->type)) {
case RFCOMM_MSC:
if (len - sizeof(*mcc) < sizeof(*msc))
break;
msc = data + sizeof(*mcc);
rfcomm_msc_recv(bthost, conn, l2conn,
RFCOMM_TEST_CR(mcc->type) / 2, msc);
break;
case RFCOMM_PN:
if (len - sizeof(*mcc) < sizeof(*pn))
break;
pn = data + sizeof(*mcc);
rfcomm_pn_recv(bthost, conn, l2conn,
RFCOMM_TEST_CR(mcc->type) / 2, pn);
break;
default:
break;
}
}
#define GET_LEN8(length) ((length & 0xfe) >> 1)
#define GET_LEN16(length) ((length & 0xfffe) >> 1)
static void rfcomm_uih_recv(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, const void *data,
uint16_t len)
{
const struct rfcomm_hdr *hdr = data;
uint16_t hdr_len, data_len;
const void *p;
if (len < sizeof(*hdr)) {
bthost_debug(bthost, "RFCOMM UIH: too short");
return;
}
if (RFCOMM_TEST_EA(hdr->length)) {
data_len = (uint16_t) GET_LEN8(hdr->length);
hdr_len = sizeof(*hdr);
} else {
uint8_t ex_len = *((uint8_t *)(data + sizeof(*hdr)));
data_len = GET_LEN16((((uint16_t) ex_len << 8) | hdr->length));
hdr_len = sizeof(*hdr) + sizeof(uint8_t);
}
if (len < hdr_len + data_len) {
bthost_debug(bthost, "RFCOMM UIH: %u != %u", len,
hdr_len + data_len);
return;
}
p = data + hdr_len;
if (RFCOMM_GET_DLCI(hdr->address)) {
struct rfcomm_chan_hook *hook;
hook = find_rfcomm_chan_hook(conn,
RFCOMM_GET_CHANNEL(hdr->address));
if (hook && data_len)
hook->func(p, data_len, hook->user_data);
} else {
rfcomm_mcc_recv(bthost, conn, l2conn, p, data_len);
}
}
static void process_rfcomm(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, const void *data,
uint16_t len)
{
const struct rfcomm_hdr *hdr = data;
bthost_debug(bthost, "RFCOMM data: %u bytes", len);
switch (RFCOMM_GET_TYPE(hdr->control)) {
case RFCOMM_SABM:
rfcomm_sabm_recv(bthost, conn, l2conn, data, len);
break;
case RFCOMM_DISC:
rfcomm_disc_recv(bthost, conn, l2conn, data, len);
break;
case RFCOMM_UA:
rfcomm_ua_recv(bthost, conn, l2conn, data, len);
break;
case RFCOMM_DM:
rfcomm_dm_recv(bthost, conn, l2conn, data, len);
break;
case RFCOMM_UIH:
rfcomm_uih_recv(bthost, conn, l2conn, data, len);
break;
default:
bthost_debug(bthost, "Unknown frame type");
break;
}
}
static void append_l2conn_data(struct bthost *bthost, struct l2conn *conn,
const void *data, uint16_t len)
{
if (!conn->recv_data) {
bthost_debug(bthost, "Unexpected L2CAP SDU data: sCID 0x%4.4x ",
conn->scid);
return;
}
if (conn->recv_len + len > conn->data_len) {
bthost_debug(bthost, "Unexpected L2CAP SDU data: sCID 0x%4.4x ",
conn->scid);
return;
}
memcpy(conn->recv_data + conn->recv_len, data, len);
conn->recv_len += len;
bthost_debug(bthost, "L2CAP SDU data: %u/%u bytes", conn->recv_len,
conn->data_len);
}
static void free_l2conn_data(struct l2conn *conn)
{
free(conn->recv_data);
conn->recv_data = NULL;
conn->recv_len = 0;
conn->data_len = 0;
}
static void new_l2conn_data(struct bthost *bthost, struct l2conn *conn,
uint16_t len)
{
free(conn->recv_data);
conn->recv_data = malloc(len);
conn->recv_len = 0;
conn->data_len = len;
}
static bool process_l2cap_conn(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, struct iovec *data)
{
struct bt_l2cap_pdu_le_flowctl_creds creds;
uint16_t sdu;
if (!l2conn)
return true;
switch (l2conn->mode) {
case L2CAP_MODE_LE_CRED:
case L2CAP_MODE_LE_ENH_CRED:
break;
case L2CAP_MODE_OTHER:
return true;
}
/* Credit-based flow control */
creds.cid = cpu_to_le16(l2conn->scid);
creds.credits = cpu_to_le16(1);
l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_LE_FLOWCTL_CREDS, 0,
&creds, sizeof(creds));
if (!l2conn->data_len) {
if (!util_iov_pull_le16(data, &sdu)) {
free_l2conn_data(l2conn);
bthost_debug(bthost, "L2CAP invalid SDU");
return false;
}
new_l2conn_data(bthost, l2conn, sdu);
}
append_l2conn_data(bthost, l2conn, data->iov_base, data->iov_len);
if (l2conn->recv_len < l2conn->data_len)
return false; /* SDU incomplete */
l2conn->data_len = 0;
data->iov_base = l2conn->recv_data;
data->iov_len = l2conn->recv_len;
return true;
}
static void process_l2cap(struct bthost *bthost, struct btconn *conn,
const void *buf, uint16_t len)
{
const struct bt_l2cap_hdr *l2_hdr = buf;
struct cid_hook *hook;
struct l2conn *l2conn;
struct iovec data;
uint16_t cid, l2_len;
l2_len = le16_to_cpu(l2_hdr->len);
if (len != sizeof(*l2_hdr) + l2_len) {
bthost_debug(bthost, "L2CAP invalid length: %u != %zu",
len, sizeof(*l2_hdr) + l2_len);
return;
}
bthost_debug(bthost, "L2CAP data: %u bytes", l2_len);
cid = le16_to_cpu(l2_hdr->cid);
l2conn = btconn_find_l2cap_conn_by_scid(conn, cid);
data.iov_base = (void *)l2_hdr->data;
data.iov_len = l2_len;
if (!process_l2cap_conn(bthost, conn, l2conn, &data))
return;
hook = find_cid_hook(conn, cid);
if (hook) {
hook->func(data.iov_base, data.iov_len, hook->user_data);
return;
}
switch (cid) {
case 0x0001:
l2cap_sig(bthost, conn, data.iov_base, data.iov_len);
break;
case 0x0005:
l2cap_le_sig(bthost, conn, data.iov_base, data.iov_len);
break;
case 0x0006:
smp_data(conn->smp_data, data.iov_base, data.iov_len);
break;
case 0x0007:
smp_bredr_data(conn->smp_data, data.iov_base, data.iov_len);
break;
default:
if (l2conn && l2conn->psm == 0x0003)
process_rfcomm(bthost, conn, l2conn, data.iov_base,
data.iov_len);
else
bthost_debug(bthost,
"Packet for unknown CID 0x%04x (%u)",
cid, cid);
break;
}
}
static void append_recv_data(struct bthost *bthost, struct btconn *conn,
const char *type, uint8_t flags,
const void *data, uint16_t len)
{
if (!conn->recv_data) {
bthost_debug(bthost, "Unexpected %s frame: handle 0x%4.4x "
"flags 0x%2.2x", type, conn->handle, flags);
return;
}
if (conn->recv_len + len > conn->data_len) {
bthost_debug(bthost, "Unexpected %s frame: handle 0x%4.4x "
"flags 0x%2.2x", type, conn->handle, flags);
return;
}
memcpy(conn->recv_data + conn->recv_len, data, len);
conn->recv_len += len;
bthost_debug(bthost, "%s data: %u/%u bytes", type, conn->recv_len,
conn->data_len);
}
static void free_recv_data(struct btconn *conn)
{
free(conn->recv_data);
conn->recv_data = NULL;
conn->recv_len = 0;
conn->data_len = 0;
}
static void append_acl_data(struct bthost *bthost, struct btconn *conn,
uint8_t flags, const void *data, uint16_t len)
{
append_recv_data(bthost, conn, "ACL", flags, data, len);
if (conn->recv_len < conn->data_len)
return;
process_l2cap(bthost, conn, conn->recv_data, conn->recv_len);
free_recv_data(conn);
}
static void new_recv_data(struct btconn *conn, uint16_t len)
{
conn->recv_data = malloc(len);
conn->recv_len = 0;
conn->data_len = len;
}
static void process_acl(struct bthost *bthost, const void *data, uint16_t len)
{
const struct bt_hci_acl_hdr *acl_hdr = data;
const struct bt_l2cap_hdr *l2_hdr = (void *) acl_hdr->data;
uint16_t handle, acl_len, l2_len;
uint8_t flags;
struct btconn *conn;
acl_len = le16_to_cpu(acl_hdr->dlen);
if (len != sizeof(*acl_hdr) + acl_len)
return;
handle = acl_handle(acl_hdr->handle);
flags = acl_flags(acl_hdr->handle);
conn = bthost_find_conn(bthost, handle);
if (!conn) {
bthost_debug(bthost, "Unknown handle: 0x%4.4x", handle);
return;
}
switch (flags) {
case 0x00: /* start of a non-automatically-flushable PDU */
case 0x02: /* start of an automatically-flushable PDU */
if (conn->recv_data) {
bthost_debug(bthost, "Unexpected ACL start frame");
free_recv_data(conn);
}
l2_len = le16_to_cpu(l2_hdr->len) + sizeof(*l2_hdr);
bthost_debug(bthost, "acl_len %u l2_len %u", acl_len, l2_len);
if (acl_len == l2_len) {
process_l2cap(bthost, conn, acl_hdr->data, acl_len);
break;
}
new_recv_data(conn, l2_len);
/* fall through */
case 0x01: /* continuing fragment */
append_acl_data(bthost, conn, flags, acl_hdr->data, acl_len);
break;
case 0x03: /* complete automatically-flushable PDU */
process_l2cap(bthost, conn, acl_hdr->data, acl_len);
break;
default:
bthost_debug(bthost, "Invalid ACL frame flags 0x%2.2x", flags);
}
}
static void process_iso_data(struct bthost *bthost, struct btconn *conn,
const void *data, uint16_t len)
{