1.简介

前面写过两篇文章gdbus开发bluez的文章,仅仅只是简单的扫描或连接。但实际进行蓝牙连接时会发现还有些请求授权的流程,这篇文章主要也是来讲解下这部分。讲真拿纯c写dbus挺折磨人的,如果能用其他高级语言的话还是建议用高级语言,用起来方便些。
如果对dbus和bluez的一些基础知识不太了解的话可以先参考下我前面写过的文章。
如何通过dbus开发bluez(c语言版)
如何通过dbus开发bluez(python版)

2.源码

源码放在 https://github.com/904221150/c-test-bluez ,建议根据文章看里面的部分代码,主要是还写多了个自动连接某个名称的蓝牙的功能,导致代码比较乱。

此外还可以参考这个仓库的代码,我的代码也参考了这个仓库
https://github.com/AmateurECE/bluez-iot-agent

编译,由于感觉虚拟机好像没法完成透传蓝牙,gatt相关的东西也没在dbus上看到,所以我全程是在交叉编译,在全志R528上验的蓝牙功能,cmake基本得改才能在qt环境编译。附README.md里的部分说明

本工程进行了交叉编译,目标是编译出全志R528上可以运行的程序,若要编译,需要重新处理下cmake。(一些环境变量设置得过饱和,按自己的工程使用工具链相关得变量以及glib变量即可)
编译流程
mkdir build
cd build
../build.sh

3.解析

这里先把目录贴出来
请添加图片描述

3.1 cmake编译解析

主要有两个CMakeLists.txt文件。
gdbus/CMakeLists.txt:负责处理xml文件
CMakeLists.txt:负责编译源码

org.bluez.agent1.xml里的内容大概长这个样

<node>
  <!-- Interface description documented in bluez.git, doc/agent-api.txt -->
  <interface name="org.bluez.Agent1">
    <method name="Release" />

    <method name="RequestPinCode">
      <arg name="device" direction="in" type="o" />
      <arg name="pincode" direction="out" type="s" />
    </method>
	
	.....

	<method name="Cancel" />
  </interface>
</node>

作用是描述dbus接口,随后再通过gdbus-codegen来生成dbus接口的.c文件。使用gdbus-codegen生成的.c文件会帮你封装好gdbus的一些接口,让人方便写代码。
gdbus-codegen命令参考,具体参数自己查吧,没什么特殊要讲的

gdbus-codegen --interface-prefix org.bluez. --c-namespace Agent --generate-c-code ${PROJECT_BINARY_DIR}/${NAME_TMP} ${PROJECT_SOURCE_DIR}/gdbus/${XML}

另外xml文件理论上得自己写,bluez没提供。bluez有哪些接口,可以看bluez源码里的doc文件甲下的txt文件,用d-feet也能很方便的看接口,但源码里的文档有功能解析

还有理论上讲xml应该不用把全部接口都写了,偷懒办法是用上哪个写哪个

3.2源码解析

连接蓝牙, 流程上来讲基本就是扫描到mac地址后,获取mac地址对应的dbus接口,再pair和connect。我这里就没用上gdbus-codegen,代码老长了。

static int pair_commond(const gchar *path, int paired)
{
    static int flag_test = 0;
    // if(flag_test == 0) {
        flag_test = 1;
        printf("test Pair\n");
        GError *error = NULL;
        GDBusProxy *device_test;
        GVariant *result;
        device_test = g_dbus_proxy_new_sync(conn,
	    			      G_DBUS_PROXY_FLAGS_NONE,
	    			      NULL,
	    			      "org.bluez",
	    			      path,
	    			      "org.bluez.Device1",
	    			      NULL,
	    			      &error);
	    if(error != NULL) {
            printf("error 1\n");
            flag_test = 0;
            return -1;
        }
	    
        if(paired == 0) {
            result = g_dbus_proxy_call_sync(device_test,
	        				"Pair",
	        				NULL,
	        				G_DBUS_CALL_FLAGS_NONE,
	        				-1,
	        				NULL,
	        				&error);
            if(error != NULL) {
                printf("error 2\n");
                flag_test = 0;
                return -1;
            }
            printf("test Pair success\n");
        }
        

        result = g_dbus_proxy_call_sync(device_test,
	    				"Connect",
	    				NULL,
	    				G_DBUS_CALL_FLAGS_NONE,
	    				-1,
	    				NULL,
	    				&error);
        if(error != NULL) {
            printf("error 3\n");
            flag_test = 0;
            return -1;
        }
        printf("test Connect success\n");
        return 0;
    // }
    // return 0;
}

另外解析pair时还可能会出现授权请求之类的,这个就涉及到bluez的agent模块,要整套dbus接口给bluez的agent模块调用。用bluetoothctl命令来进行pair时,有时候会出现”Accept pairing (yes/no):“ 这种打印。要自动化处理就要处理agent。
我这里的xml对应的接口就是给agent用的。节选下相关代码来说明这agent注册流程


void request_authorization(AgentAgent1* interface,
    GDBusMethodInvocation* invocation, const char* device)
{
    g_info("%s called", __FUNCTION__);
    agent_agent1_complete_request_authorization(interface, invocation);
}

static void bus_acquired_cb(GDBusConnection *connection,
                            const gchar     *bus_name,
                            gpointer         user_data)
{
    printf("bus_acquired_cb\n");
    GError *pError = NULL;

    /** Second step: Try to get a connection to the given bus. */
    AgentAgent1 *pInterface = agent_agent1_skeleton_new();

    /** Third step: Attach to dbus signals. */
    (void) g_signal_connect(pInterface, "handle-request-authorization", G_CALLBACK(request_authorization), NULL);

    /** Fourth step: Export interface skeleton. */
    (void) g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(pInterface),
                                            connection,
                                            "/org/bluez/agent1",
                                            &pError);
}

int main()
{
	...
	conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
	if(error != NULL)
		return 1;

    // (void)g_bus_own_name_on_connection(conn,
    //                      "org.bluez",
    //                      G_BUS_NAME_OWNER_FLAGS_NONE,
    //                      &bus_acquired_cb,
    //                      &name_lost_cb,
    //                      NULL,
    //                      NULL);
    const gchar* service_name = g_dbus_connection_get_unique_name(
            conn);
    bus_acquired_cb(conn, service_name, NULL);
	...
	GDBusProxy *agentManager = g_dbus_proxy_new_sync(conn,
				      G_DBUS_PROXY_FLAGS_NONE,
				      NULL,
				      "org.bluez",
				      "/org/bluez",
				      "org.bluez.AgentManager1",
				      NULL,
				      &error);
	if(error != NULL)
		return 1;

    printf("start RegisterAgent\n");
    error = NULL;
	result = g_dbus_proxy_call_sync(agentManager,
					"RegisterAgent",
					g_variant_new("(os)", "/org/bluez/agent1", "NoInputNoOutput"),
					G_DBUS_CALL_FLAGS_NONE,
					-1,
					NULL,
					&error);
	if(error != NULL) {
        g_error("Failed to call RegisterAgent: %s", error->message); 
        g_error_free(error); 
        return 1;
    }
    ...
}

g_bus_own_name_on_connection是用来给dbus连接重命名的,我重命名一直报错索性就不命名了,用g_dbus_connection_get_unique_name来获取已经生成好的名字。通过d-feet看
请添加图片描述
请添加图片描述
org.bluez是bluez的服务器端,而:1.86是客户端实际应用中对应我们的程序,重命名应该就是改掉:1.86。

bus_acquired_cb:这个函数具体内容就是往dbus注册xml文件里写好的接口,我这里“ agent_agent1”开头的函数就是gdbus-codegen生成出来的函数。其他就没什么要留意的地方的就是流程,就g_dbus_interface_skeleton_export里的"/org/bluez/agent1",这里定义了接口的路径,后续注册了agent候bluez服务器端就会往客户端该路径下的接口找api,从而去调用对应的函数。

agent_agent1_complete_request_authorization: 这个是RequestConfirmation的肯定应答,我这里要一直自动同一名称的蓝牙,要一直肯定应答。还有一些错误返回可以参考bluez的api文档以及gdbus的相关操作。

4.总结

这次写了个agent,如果后面还有时间的话还会再写写gatt的操作。现在回过头来再看下gdbus开发bluez,感觉就没多少人用。先gdbus用法比python里用麻烦了非常多,再又是bluez的逻辑只能参考bluetoothctl,而bluetoothctl用的dbus库还是另外一套,一些帮忙封装bluetoothctl的还被连带了GPL,感觉挺麻烦。说句实话感觉就挺奇怪的linux开发蓝牙怎么和andriod、单片机开发区别那么大,但映像中蓝牙官方确实推荐用的dbus,但实际用下来怎么却麻烦得不正常了。就这样吧。

Logo

更多推荐