【BlueZ5】 如何用MGMT接口实现ble slave

如何用BlueZ MGMT接口实现Ble Slave

一,关于MGMT接口

1,为什么有MGMT接口

In short, it’s a new interface for user space Bluetooth components (like bluetoothd) to talk to the kernel and it aims to replace the existing raw HCI sockets.

关于这部分信息可以访问:http://www.bluez.org/the-management-interface/
同样,bluez的获取方式如下:

git clone https://git.buildroot.net/buildroot   
git clone git://git.buildroot.net/buildroot
2,为什么要用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 requests.

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 Bluetooth 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 reused.

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 unlike 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 userspace process to catch all traffic to and from an adapter from the first moment that it is plugged in.

二,如何用MGMT实现ble slave。

1,大致思路以及需要熟悉的接口文件。(本来不想跟网上一样多BB的,上来写代码的,但是一想,这个“渔”对于写代码还是很重要的。)
思路如下:
[1]  如何实现开关蓝牙.
[2]  如何实现广播,包含广播包和响应包的设定,以及广播参数设定等等。
[3]  如何实现GATT连接.
[4]  如何实现服务注册,即收发数据.
[5]  如何实现状态通知,包含连接成功,断开成功,timeout,terminate等等.
[6]  如何实现更新连接参数,连接间隔设定等等。

需要指出,以上几点可归纳为如何两个文件以及kernel中node的设定等等。
[1][2]      -->bluez-5.5x\tools\btmgmt.c

[3][4][5]   -->bluez-5.5x\tools\btgatt-server.c

[6]         -->部分可从btmgmt.c中获取,剩下部分可参考如下:
// 仅供参考:
cat /sys/kernel/debug/bluetooth/hci0/
adv_channel_map                conn_latency                   dut_mode                       identity_resolving_keys        quirk_strict_duplicate_filter  ssp_debug_mode                 white_list_size
adv_max_interval               conn_max_interval              features                       idle_timeout                   random_address                 static_address                 
adv_min_interval               conn_min_interval              force_static_address           inquiry_cache                  remote_oob                     supervision_timeout            
auto_accept_delay              dev_class                      hardware_error                 link_keys                      rpa_timeout                    use_debug_keys                 
blacklist                      device_id                      hci_revision                   long_term_keys                 sc_only_mode                   uuids                          
conn_info_max_age              device_list                    hci_version                    manufacturer                   sniff_max_interval             voice_setting                  
conn_info_min_age              discov_interleaved_timeout     identity                       quirk_simultaneous_discovery   sniff_min_interval             white_list     
2,接口文件解释
[1] 如何实现开关蓝牙.

如何实现开关bluetooth

[2] 如何实现广播,包含广播包和响应包的设定,以及广播参数设定等等。

在这里插入图片描述
// node可以修改部分参数
在这里插入图片描述

[3] 如何实现GATT连接.
[4] 如何实现服务注册,即收发数据.

BlueZ提供了两份实例关于ble master以及ble slave。
其中ble slave对应上btgatt-server.c; ble master对应上btgatt-client.c

A.设定设备类型

在这里插入图片描述

B.设定GATT参数以及characteristic,同时注册回调

在这里插入图片描述

[5] 如何实现状态通知,包含连接成功,断开成功,timeout,terminate等等.
A,在设定状态等等之前,我们需要监听所有信息。

在这里插入图片描述

B,状态上报

在这里插入图片描述

[6] 如何实现更新连接参数,连接间隔设定等等。

关于更新连接参数以及连接间隔等操作,需要操作node,对应裸板实现时,需要把bluetooth的node引出,对其写操作
这部分不详述

三,如何用代码和MGMT接口实现ble slave。

1,在实现之前,我们需要理解使用socketpair实现双向通讯。实现线程间通信。

在这里插入图片描述
示例代码如下(以Btmgmt为例):

// 初始化如下:
void* mgmt_ble_init(MGMT_CBS *cbs)
{
	int sockets[2];
	int result;
	int bufferSize = SOCKET_BUFFER_SIZE;

	result = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets);
	if (-1 == result) {
		printf("socketpair error!\n");
		return NULL;
	}

    struct timeval mgmt_timeout;
    memset(&mgmt_timeout, 0x0, sizeof(mgmt_timeout));

    mgmt_timeout.tv_sec  = 10;
    mgmt_timeout.tv_usec = 0;

    if(setsockopt(sockets[0], SOL_SOCKET, SO_SNDTIMEO, (void *)&mgmt_timeout, sizeof(mgmt_timeout)) == -1) {
        printf("setsockopt: unable to set SO_RCVTIMEO");
        goto failed2;
    }

    if(setsockopt(sockets[1], SOL_SOCKET, SO_SNDTIMEO, (void *)&mgmt_timeout, sizeof(mgmt_timeout)) == -1) {
        printf("setsockopt: unable to set SO_RCVTIMEO");
        goto failed2;
    }

	setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

    struct mgmt_ble_com *hdl = NULL;
    hdl = (struct mgmt_ble_com*)malloc(sizeof(struct mgmt_ble_com));
    if (hdl == NULL) {
        printf("malloc failed\n");
        goto failed2;
    }
    hdl->msock[0] = sockets[0];
    hdl->msock[1] = sockets[1];
    memcpy(&hdl->cbs, cbs, sizeof(MGMT_CBS));

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    pthread_attr_setstacksize(&attr, 1024 * 1024);

    pthread_t mgmt_ble_thread = 0;
    if (pthread_create(&mgmt_ble_thread, &attr, mgmt_ble_process, hdl) != 0) {
        printf("cannot create mgmt server thread!\n");
        goto failed1;
    }

    pthread_t mgmt_rsp_thread = 0;
    if (pthread_create(&mgmt_rsp_thread, &attr, mgmt_ble_rsp, hdl) != 0) {
        printf("cannot create mgmt client thread!\n");
        goto failed1;
    }

    pthread_attr_destroy(&attr);

    return hdl;

failed1:
    pthread_attr_destroy(&attr);
    free(hdl);

failed2:
	printf("closing socket(%d), socket(%d)\n", sockets[0], sockets[1]);
    close(sockets[0]);
    close(sockets[1]);

    return NULL;
}

// 双向缓冲buffer如下:
void *mgmt_ble_rsp(void *arg)
{
    unsigned char buf[BUFFER_SIZE] = {0};
    struct mgmt_ble_com *phdl = arg;
    int ret = -1;
    fd_set rdfds;
    struct timeval timeout;

    prctl(PR_SET_NAME, "mgmg-handle rsp");

    while(1) {
        FD_ZERO(&rdfds);
        FD_SET(phdl->msock[0], &rdfds);

        timeout.tv_sec  = 3;
        timeout.tv_usec = 0;

        if (phdl->m_num == 0) {
            (phdl->m_num)++;
        }

        ret = select(phdl->msock[0] + 1, &rdfds, NULL, NULL, &timeout);
        if (ret == 0) {
            //printf("select timeout\n");
            continue;
        } else if (ret < 0) {
            printf("select error, need to do something...\n");
            break;
        }

        if (FD_ISSET(phdl->msock[0], &rdfds)) {
            ret = read(phdl->msock[0], buf, BUFFER_SIZE);
            if (ret < 0) {
                printf("recv error...\n");
                break;
            }  else if(ret == 0) {
                printf("read zero\n");
                break;
            }

            buf[ret] = '\0';

            if (mgmt_ble_resp_data(phdl, buf, ret)) {
                printf("exit mgmt thread\n");
                break;
            }
        }
    }

	close(phdl->msock[0]);
	close(phdl->msock[1]);
	free(phdl);
    return NULL;
}
2,操作蓝牙开关以及广播。
// 通过写操作,对端接收信息并处理事件
int ble_mgmt_power(int index,void *bt_hdl, int onoff)
{
    struct mgmt_ble_com *hdl = bt_hdl;
    if (hdl == NULL) {
        printf("bt handle is null\n");
        return -1;
    }

    char *cmd = onoff?"power on":"power off";
    if (write(hdl->msock[0], cmd, strlen(cmd)) <= 0) {
        printf("(%d) send failed: %d %s\n",
                    hdl->msock[0], errno, strerror(errno));
        return -1;
    }

    return 0;
}

// 发送广播信息
int ble_mgmt_adv(int index,void *bt_hdl, tuya_ble_mgmt_adv_data_t *adv, tuya_ble_mgmt_rsp_data_t *rsp)
{
    struct mgmt_ble_com *hdl = bt_hdl;
    
    char cmd[256] = {0};
    
    if (hdl == NULL) {
        printf("bt handle is null\n");
        return -1;
    }
    if((adv->data == NULL)||(adv->len == 0)||(rsp->data == NULL)||(rsp->len == 0)) {
		return -2;
	}else {
        snprintf(cmd, sizeof(cmd), "add-adv -c -d %.*s -s %.*s %.*d", adv->len * 2,adv->str,rsp->len * 2,rsp->str,1,instance_id);
    }

    if (write(hdl->msock[0], cmd, strlen(cmd)) <= 0) {
        printf("(%d) send failed: %d %s\n",
                    hdl->msock[0], errno, strerror(errno));
        return -3;
    }

    return 0;
}

3,如何写操作。
A, 对于btgatt-server初始化部分如上述相同方式。我们直接贴上如何操作写数据
// 写数据
int ble_gatt_set_value(void *bt_hdl, unsigned char *data, int len)
{ 
    struct mgmt_ble_com *hdl = bt_hdl;
    char cmd[512] = {0};
    int i, size;
    int j = 0;
    
    if (hdl == NULL) {
        printf("bt handle is null\n");
        return -1;
    }

    if(hdl->common_handle == 0) {
        printf("error handle id for this notify");
        return -1;
    }
    
    snprintf(cmd, sizeof(cmd), "notify 0x%04x ", hdl->common_handle);

    size = strlen(cmd);


    for (i = 0; i < len; i++) {
        snprintf(cmd+size+j, sizeof(cmd), "%02x ", data[i]);
        j=j+3;
    }

	if (ble_gatt_send_cmd(hdl, cmd) < 0)
		return -1;
    
    return 0;
}
4,实现效果.
A,先跑起来

在这里插入图片描述

B,实现透传

在这里插入图片描述

C, 对应上手机端

在这里插入图片描述

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tim-Cheng

你的鼓励是我最大的动力,奥利给

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值