【BlueZ】【蓝⽛】跨平台实现BleMasterSlaveMesh之Linux篇-1
⼀,前述
1,上⼀篇讲述了如何通过Windows实现蓝⽛应⽤,该⽅式可适⽤于Windows/Linux/Macos等场景。,该⽅式⽐较特殊的⼀点,⽤了指定的蓝⽛dongle设备,也规避了不同平台产⽣的差异⽽导致的问题。
2,但是市⾯上更多的是WIFI+BT/BLE combo的芯⽚。针对该类设备,各个⼚商都遵循统⼀的HCI接⼝以及适配协议,不同的是针对各⾃设备都有各⾃的驱动程序。所以contorller部分协议以及硬件都是由原⼚提供,host部分的协议栈有我们熟知的linux官⽅蓝⽛协议栈bluez。()
⼆、设计思路
1,模型框图
当前我们利⽤通⽤设备(如市⾯上的USB蓝⽛dongle/系统蓝⽛等)实现ble master/ble slave。
2,具体设计
2.1 我们采⽤MGMT⽅式实现ble master/ble slave。为什么应⽤MGMT⽅式实现主从机。
1,很重要的⼀点,占⽤资源少。(实测Dbus环境动态库接近2M,bin接近1M,还是strip之后的。对于flash很⼩的设备⽽⾔内存占⽤压⼒很⼤,⽽⽤MGMT加起来不到1M)
2,便于纠错,直接和kernel通信的接⼝,避免过多的进程间交互,导致问题出来,不需要花⼤量时间纠错。(也是避免了内核与⽤户空间没有冲突的风险)3,剩下的优点不提了,对于我当前简单实现ble slave,单点连接设备⽽⾔,⽤的⽐较少。
// 感兴趣可以看⼀下,来⾃官⽅blog
Command queues and synchronization
Since the kernel is now responsible for all HCI traffic there’s no risk of conflicts between kernel and userspace.
Blocking operations
With the management interface there are simple asynchronous messages that are used to power on and off adapters: blocking problem solved.
Unnecessary HCI event processing
No raw HCI sockets means no promisc flag on the kernel side. So extra processing of these packets isn’t needed anymore.
Distributed security policy and logic
With the management interface only user interaction (PIN code/pass key requests, etc) and link key storage is handled on the user space side. User space will feed the kernel with all stored link keys, including the key types, upon adapter initialization. After that the kernel is responsible for handling link key requ ests.
An additional benefit with having an abstracted interface for security is that it can be used for the Security Manager Protocol (SMP) that’s part of the Blueto oth Low Energy (LE) specification. SMP has a similar user interaction model as SSP so the same messages between user space and the kernel can be reu sed.
As long as SMP is implemented on the kernel side there’d be a big problem with dealing with it from user space using the existing kernel interface since unl ike SSP, SMP uses L2CAP and not HCI for messaging.
Lack of early-tracing capability
The management interface will offer a special type of tracing socket which can be used to get the HCI traffic of all connected adapters. This will allow a use rspace process to catch all traffic to and from an adapter from the first moment that it is plugged in.
三、代码理解
1,Ble Master之扫描设备
源码信息:btmgmt.c
由于我们采⽤MGMT⽅式实现ble master,故我们应⽤btmgmt⼯具集实现扫描设备。
// 扫描设备分为两个步骤
// 第⼀步设定扫描参数:
// 对应命令⾏:btmgmt scan-params 0x30 0x30
// 对应代码如下:
static void cmd_scan_params(int argc, char **argv)
{
struct mgmt_cp_set_scan_params cp;
uint16_t index;
index = mgmt_index;
if (index == MGMT_INDEX_NONE)
index = 0;
cp.interval = strtol(argv[1], NULL, 0);
cp.window = strtol(argv[2], NULL, 0);
if (mgmt_send(mgmt, MGMT_OP_SET_SCAN_PARAMS, index, sizeof(cp), &cp,
scan_params_rsp, NULL, NULL) == 0) {
error("Unable to send set_scan_params cmd");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
}
// 第⼆步扫描设备:
// 对应命令⾏:btmgmt find-service -u 0xa201 -l
// 注:如需多个设备扫描可在后⾯追加service uuid,例如:find-service -u 0xa201 -u 0x1827 -u 0x1828 -l (这⾥包含了mesh 设备的unprov service uuid和 pro xy service uuid)
// 对应代码如下:
static void cmd_find_service(int argc, char **argv)
{
struct mgmt_cp_start_service_discovery *cp;
uint8_t buf[sizeof(*cp) + 16 * MAX_UUIDS];
uuid_t uuid;
uint128_t uint128;
uuid_t uuid128;
uint8_t type = SCAN_TYPE_DUAL;
int8_t rssi;
uint16_t count;
int opt;
uint16_t index;
index = mgmt_index;
if (index == MGMT_INDEX_NONE)
index = 0;
...//省略很多⾏
argc -= optind;
argv += optind;
optind = 0;
cp = (void *) buf;
cp->type = type;
cp->rssi = rssi;
cp->uuid_count = cpu_to_le16(count);
if (mgmt_send(mgmt, MGMT_OP_START_SERVICE_DISCOVERY, index,
if (mgmt_send(mgmt, MGMT_OP_START_SERVICE_DISCOVERY, index,
sizeof(*cp) + count * 16, cp,
find_service_rsp, NULL, NULL) == 0) {
error("Unable to send start_service_discovery cmd");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
}
对应到kernel的源码如下:
static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_start_service_discovery *cp = data;
struct mgmt_pending_cmd *cmd;
struct hci_request req;
const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
u16 uuid_count, expected_len;
u8 status;
int err;
BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
...//省略很多⾏
linux内核设计与实现 pdf
if (!trigger_discovery(&req, &status)) {
err = mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_START_SERVICE_DISCOVERY,
status, &cp->type, sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
err = hci_req_run(&req, start_discovery_complete);
if (err < 0) {
mgmt_pending_remove(cmd);
goto failed;
}
hci_discovery_set_state(hdev, DISCOVERY_STARTING);
failed:
hci_dev_unlock(hdev);
return err;
}
// 扫描开启成功后,会设定对应设定扫描Timeout
// 这⾥很重要,决定了你的设备是否需要短期多次被扫到或者扫描开启周期等。static void start_discovery_complete(struct hci_dev *hdev, u8 status,u16 opcode);
以下贴图,便于理解,如果不好理解,可省略。。。
扫描设备实际结果:
注:这⾥我们选取⼀个设备(BC:23:00:00:FF:02)。2,Ble Master之设备连接
源码信息:btgatt-client.c
// 蓝⽛⼦设备连接包含两个主要步骤:
// 第⼀步:L2CAP链路连接:
// 这⾥的connect是否成功很⼤程度取决于蓝⽛设备的性能,否则会⼀直出现conn_fail()的情况,或者c
onn_fail()之后connected的情况,设备占⽤,但是⽆法操作等等状况。
static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
int sec)
{
int sock;
struct sockaddr_l2 srcaddr, dstaddr;
struct bt_security btsec;
...//省略很多⾏
/* Set up destination address */
memset(&dstaddr, 0, sizeof(dstaddr));
dstaddr.l2_family = AF_BLUETOOTH;
dstaddr.l2_cid = htobs(ATT_CID);
dstaddr.l2_bdaddr_type = dst_type;
bacpy(&dstaddr.l2_bdaddr, dst);
printf("Connecting ");
fflush(stdout);
if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) { // 这⾥如果连接失败或者errno出现busy/not implement等情况,fd会失效
perror(" Failed to connect");
close(sock);
return -1;
}
printf(" Done\n");
return sock;
}
这⾥仅仅是ATT连接成功流程。
// 第⼆步便是注册GATT Client。
/
/ 上⼀步connect后的fd在这⼀步中进⾏数据交互。
// 包含有:断开事件注册,服务获取
static struct client *client_create(int fd, uint16_t mtu)
{
struct client *cli;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论