精华内容
下载资源
问答
  • 我们继续理解bluez设置Adapter中的属性。作为DBus一个最基本的应用就是:可以被任意个客户端同时访问(bluez包含一个DBUS服务端),并且被修改的属性可以通知给所有的客户端。在DBUS中可以通过信号完成这个操作。 /*...

    我们继续理解bluez设置Adapter中的属性。作为DBus一个最基本的应用就是:可以被任意个客户端同时访问(bluez包含一个DBUS服务端),并且被修改的属性可以通知给所有的客户端。在DBUS中可以通过信号完成这个操作。

    /*
     * bluez_adapter_set_properties.c - Set the Powered property of Adapter
     * 	- The example uses GDBUS to set the Adapter Powered state to "on" and "off"
     * 	  in sequence.
     *	- It also registers signal handling for the PropertiesChanged event and prints
     *	  the current powere state of the Adapter
     * gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/bluez_adapter_set_properties ./bluez_adapter_set_properties.c `pkg-config --libs glib-2.0 gio-2.0`
     */
    #include <glib.h>
    #include <gio/gio.h>
    
    GDBusConnection *con;
    static void bluez_signal_adapter_changed(GDBusConnection *conn,
    					const gchar *sender,
    					const gchar *path,
    					const gchar *interface,
    					const gchar *signal,
    					GVariant *params,
    					void *userdata)
    {
    	(void)conn;
    	(void)sender;
    	(void)path;
    	(void)interface;
    
    	static int exit = 0;
    	GVariantIter *properties = NULL;
    	GVariantIter *unknown = NULL;
    	const char *iface;
    	const char *key;
    	GVariant *value = NULL;
    	const gchar *signature = g_variant_get_type_string(params);
    
    	if(strcmp(signature, "(sa{sv}as)") != 0) {
    		g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
    		goto done;
    	}
    
    	g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
    	while(g_variant_iter_next(properties, "{&sv}", &key, &value)) {
    		if(!g_strcmp0(key, "Powered")) {
    			if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
    				g_print("Invalid argument type for %s: %s != %s", key,
    						g_variant_get_type_string(value), "b");
    				goto done;
    			}
    			g_print("Adapter is Powered \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
    			exit++;
    		}
    	}
    done:
    	if(properties != NULL)
    		g_variant_iter_free(properties);
    	if(value != NULL)
    		g_variant_unref(value);
    
    	if(exit == 2)
    		g_main_loop_quit((GMainLoop *)userdata);
    }
    
    static int bluez_adapter_set_property(const char *prop, GVariant *value)
    {
    	GVariant *result;
    	GError *error = NULL;
    
    	result = g_dbus_connection_call_sync(con,
    					     "org.bluez",
    					     "/org/bluez/hci0",
    					     "org.freedesktop.DBus.Properties",
    					     "Set",
    					     g_variant_new("(ssv)", "org.bluez.Adapter1", prop, value),
    					     NULL,
    					     G_DBUS_CALL_FLAGS_NONE,
    					     -1,
    					     NULL,
    					     &error);
    	if(error != NULL)
    		return 1;
    
    	g_variant_unref(result);
    	return 0;
    }
    
    int main(void)
    {
    	GMainLoop *loop;
    	int rc;
    	guint sub;
    
    	con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
    	if(con == NULL) {
    		g_print("Not able to get connection to system bus\n");
    		return 1;
    	}
    
    	loop = g_main_loop_new(NULL, FALSE);
    
    	sub = g_dbus_connection_signal_subscribe(con,
    						"org.bluez",
    						"org.freedesktop.DBus.Properties",
    						"PropertiesChanged",
    						NULL,
    						"org.bluez.Adapter1",
    						G_DBUS_SIGNAL_FLAGS_NONE,
    						bluez_signal_adapter_changed,
    						loop,
    						NULL);
    
    	rc = bluez_adapter_set_property("Powered", g_variant_new("b", TRUE));
    	if(rc) {
    		g_print("Not able to enable the adapter\n");
    		g_main_loop_quit(loop);
    	}
    
    	rc = bluez_adapter_set_property("Powered", g_variant_new("b", FALSE));
    	if(rc) {
    		g_print("Not able to disable the adapter\n");
    		g_main_loop_quit(loop);
    	}
    	g_main_loop_run(loop);
    
    	g_dbus_connection_signal_unsubscribe(con, sub);
    	g_object_unref(con);
    	return 0;
    }

    信号的描述:

    为了在属性改变的时候获取到信号通知,我们需要订阅信号。订阅信号就是将一个回调函数和这个信号相关联。当检测到这个信号的时候,回调函数被调用。具体的相关内容可以参考DBUS规范和bluez的API文档。

     

    展开全文
  • BlueZ

    千次阅读 2019-09-20 23:42:07
    BlueZ 首先要在树莓派上安装必要的工具。 BlueZ是Linux官方的蓝牙协议栈。可以通过BlueZ提供的接口,进行丰富的蓝牙操作。 Raspbian中已经安装了BlueZ。使用的版本是5.43。可以检查自己的BlueZ版本: bluetoothd...
    用树莓派玩转蓝牙

    BlueZ

    首先要在树莓派上安装必要的工具。

    BlueZ是Linux官方的蓝牙协议栈。可以通过BlueZ提供的接口,进行丰富的蓝牙操作。

    Raspbian中已经安装了BlueZ。使用的版本是5.43。可以检查自己的BlueZ版本:

     bluetoothd -v 

    低版本的BlueZ对低功耗蓝牙的支持有限。如果使用版本低于5.43,那么建议升级BlueZ。

    可以用下面的命令检查BlueZ的运行状态:

     systemctl status bluetooth 

    返回结果是:

    pi@raspberrypi:~ $ systemctl status bluetooth
    ● bluetooth.service - Bluetooth service
       Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled)
       Active: active (running) since Sun 2019-05-05 18:16:39 CST; 10min ago
         Docs: man:bluetoothd(8)
     Main PID: 531 (bluetoothd)
       Status: "Running"
       CGroup: /system.slice/bluetooth.service
               └─531 /usr/lib/bluetooth/bluetoothd
    
    May 05 18:16:38 raspberrypi systemd[1]: Starting Bluetooth service...
    May 05 18:16:39 raspberrypi bluetoothd[531]: Bluetooth daemon 5.43
    May 05 18:16:39 raspberrypi systemd[1]: Started Bluetooth service.
    May 05 18:16:39 raspberrypi bluetoothd[531]: Starting SDP server
    May 05 18:16:39 raspberrypi bluetoothd[531]: Bluetooth management interface 1.14 initialized
    May 05 18:16:39 raspberrypi bluetoothd[531]: Failed to obtain handles for "Service Changed" characteristic
    May 05 18:16:39 raspberrypi bluetoothd[531]: Sap driver initialization failed.
    May 05 18:16:39 raspberrypi bluetoothd[531]: sap-server: Operation not permitted (1)
    May 05 18:16:39 raspberrypi bluetoothd[531]: Endpoint registered: sender=:1.10 path=/A2DP/SBC/Source/1
    May 05 18:16:39 raspberrypi bluetoothd[531]: Endpoint registered: sender=:1.10 path=/A2DP/SBC/Sink/1

    可以看到,蓝牙服务已经打开,并在正常运行。

    可以用下面命令手动启动或关闭蓝牙服务:

    sudo systemctl start bluetooth
    sudo systemctl stop bluetooth

    此外,还可以让蓝牙服务随系统启动:

     sudo systemctl enable bluetooth 

    了解树莓派上的蓝牙

    在Raspbian中,基本的蓝牙操作可以通过bluez中的 bluetoothctl 命令进行。该命令运行后,将进入到一个新的Shell。

    在这个shell中输入:

     list 

    将显示树莓派上可用的蓝牙模块,例如:

     Controller B8:27:EB:72:47:5E raspberrypi [default] 

    运行scan命令,开启扫描:

     scan on 

    扫描启动后,用devices命令,可以打印扫描到蓝牙设备的MAC地址和名称,例如:

     Device 00:9E:C8:62:AF:55 MiBOX3 Device 4D:CE:7A:1D:B8:6A vamei 

    如果设备未在清单中列出,输入 scan on 命令设置设备发现模式。 

    输入 agent on 命令打开代理。

    输入  pair MAC Address  开始配对(支持 tab 键补全)。 

    如果使用无 PIN 码设备,再次连接可能需要手工认证。

    输入  trust MAC Address  命令。 

    最后,用 connect MAC Address 命令建立连接。

    此外,还可以用 help 命令获得帮助。使用结束后,可以用 exit 命令退出bluetoothctl。

    除了bluetoothctl,在Raspbian是shell中可以通过hciconfig来控制蓝牙模块。比如开关蓝牙模块:

     sudo hciconfig hci0 up #启动hci设备 sudo hciconfig hci0 down #关闭hci设备

    命令中的hci0指的是0号HCI设备,即树莓派的蓝牙适配器。

    与此同时,可以用下面命令来查看蓝牙设备的工作日志:

     hcidump 

    bluez本身还提供了连接和读写工具。但不同版本的bluez相关功能的差异比较大,而且使用起来不太方便,所以下面使用Node.js的工具来实现相关功能。

    转载于:https://www.cnblogs.com/Java-Script/p/11095648.html

    展开全文
  • BlueZ5

    万次阅读 多人点赞 2017-03-15 17:58:19
    BlueZ5 Bluez介绍 BlueZ 是官方 Linux Bluetooth 栈,由主机控制接口(Host Control Interface ,HCI)层、Bluetooth 协议核心、逻辑链路控制和适配协议(Logical Link Control and Adaptation Protocol,L2CAP)、...

    BlueZ5

    1. Bluez介绍

    BlueZ 是官方 Linux Bluetooth 栈,由主机控制接口(Host Control Interface ,HCI)层、Bluetooth 协议核心、逻辑链路控制和适配协议(Logical Link Control and Adaptation Protocol,L2CAP)、SCO 音频层、其他 Bluetooth 服务、用户空间后台进程以及配置工具组成。
    BlueZ提供对核心蓝牙层和协议的支持。它灵活,高效,并使用模块化实现。Bluez有很多有趣的特性:
    (1)完全的模块化实现
    (2)均衡的多处理安全
    (3)支持多线程数据处理
    (4)支持多个蓝牙设备
    (5)真正的硬件抽象
    (5)向所有层提供标准socket接口
    (6)支持设备和服务级别的安全

    目前BlueZ由许多单独的模块组成:
    (1)蓝牙内核核心子系统
    (2)L2CAP和SCO音频内核层
    (3)RFCOMM,BNEP,CMTP和HIDP的内核实现
    (4)HCI UART,USB,PCMCIA以及虚拟设备的驱动程序
    (5)通用的蓝牙和SDP库以及守护进程
    (6)配置和测试程序
    (7)协议解码和分析工具

    BlueZ目前支持很多蓝牙协议:
    (1)底层主机协议栈(Lower level host stack)
       Core Specification 4.2 (GAP, L2CAP, RFCOMM, SDP, GATT)
         1)Classic Bluetooth (BR/EDR)
         2)Bluetooth Smart (Low Energy)
    (2)上层协议
       由BlueZ提供:
       A2DP 1.3
       AVRCP 1.5
       DI 1.3
       HDP 1.0
       HID 1.0
       PAN 1.0
       SPP 1.1
       基于GATT(LE)协议:
       PXP 1.0
       HTP 1.0
       HoG 1.0
       TIP 1.0
       CSCP 1.0
       基于OBEX的协议(通过obexd支持):
       FTP 1.1
       OPP 1.1
       PBAP 1.1
       MAP 1.0

       由oFono项目提供的协议:
       HFP 1.6(AG&HF)

    BlueZ的内核模块,库和以及实用程序已支持在Linux支持的许多架构上完美工作。这还包括很多单处理器和多处理器平台以及超线程系统:
    (1)英特尔和AMD x86
    (2)AMD64和EM64T(x86-64)
    (3)SUN SPARC 32 / 64bit
    (4)PowerPC 32 / 64bit
    (5)英特尔StrongARM和XScale
    (6)日立/瑞萨SH处理器
    (7)摩托罗拉 DragonBall

    BlueZ可以在许多Linux发行版中找到,一般来说它与市场上的任何Linux系统兼容:
    (1)Debian GNU / Linux
    (2)Ubuntu Linux
    (3)Fedora Core / Red Hat Linux
    (4)OpenSuSE / SuSE Linux
    (5)Mandrake Linux
    (6)Gentoo Linux
    (7)Chrome操作系统

    2.BlueZ架构

    目前最新的BlueZ版本是BlueZ5。下面将以BlueZ5为例,详细说明BlueZ架构:
    Bluez5架构
                 图1. Bluez5架构.
    Bluez5的分为kernel和user space 两部分。
    BlueZ的kernel模块从Linux 2.6内核开始就已经包含在linux内核中。
    kernel 模块包括:
    (1)底层协议(L2CAP,RFCOMM,BNEP,HIDP等)
    (2)安全性(SSP,SMP)
    (3)硬件驱动程序
    (4)提供给user space的基于socket的接口
       1)数据接口(L2CAP,RFCOMM,SCO,HCI)
       2)控制接口(MGMT,HCl,BNEP,HIDP)
    虽然kernel模块早已存在于linux内核中,但是没有user space模块的支持,kernel模块无法发挥作用。最新版本的user space模块的下载地址为:
    http://www.kernel.org/pub/linux/bluetooth/bluez-5.44.tar.xz
    user space模块包括:
    (1)bluetoothd
       1)是BlueZ的中央守护进程
       2)提供用于UI和其他子系统的D-Bus接口
       3)减少暴露底层细节
       4)可以通过插件扩展(例如通过neard支持NFC,通过sixaxis支持DS3)
    (2)obexd
       1)是OBEX协议的守护进程
       2)提供用于UI的D-Bus接口
       3)具有与bluetoothd类似的架构
    (3)工具(tools)
       1)bluetoothctl - bluetooth命令行测试工具
       2)obexctl - obex命令行测试工具
       3)btmon - HCl信息跟踪
       4)其他用于测试,开发和跟踪的命令行工具集
    (4)上层协议
       1)Audio and media (A2DP, AVRCP)
       2)Telephony (HFP, HSP)
       3)Networking (PAN, 6LoWPAN)
       4)Input device (HID, HoG)
       5)OBEX (FTP, OPP, MAP, PBAP)
       6)Others

    3.BlueZ源代码

      下载BlueZ5(bluez-5.44.tar.xz)源代码压缩包,经解压后得到BlueZ5源代码,BlueZ5源代码中有很多目录和文件:
       android/ - 用于替代android中bluedroid的android版本bluez源码。
       attrib/ - 包含gatttool 源码以及与gatt attribute相关的代码,gatttool程序入口为gatttool.c。   
       btio/ - 通过的标准socket接口与BlueZ5 kernel模块通信?。
       client/ - bluetoothctl源码,程序入口为main.c。
       doc/ - BlueZ5 API文档。
       emulator/ - 与bluetooth虚拟controller工具相关的代码?。
       gdbus/ - BlueZ5自带的内部gdbus库源码。
       gobex/ - Blue5自带的内部gobex库源码。
       lib/ - libbluetooth.so 源码,提供BlueZ4 API,用来支持某些第三方应用。
       monitor/ - btmon源码, 程序入口为main.c。
       obexd/ - obexd源码,程序入口为src/main.c。
       peripheral/ - 与蓝牙ble的GATT相关的代码?。
       plugins/ - BlueZ5插件源码(neard,autopair等插件)。
       profiles/ - BlueZ5蓝牙上层协议(a2dp,hid等)源码。
       src/ - bluetoothd源码,程序入口为main.c。
       test/ - Bluez5测试脚本。
       tools/ - Bluez5测试工具集源码。
       unit/ - PTS测试相关的一些代码?。
       README / INSTALL - 配置,编译,安装Bluez5的说明。
       Makefile.obexd - 定义obexd编译规则,此文件被include于Makefile.am中。
       Makefile.plugins - 定义BlueZ5的plugins(neard,autopair等)的编译规则, 此文件被include于Makefile.am中。
       Makefile.tools - 定义BlueZ5测试工具集的编译规则,此文件被include于 Makefile.am中。
       Makefile.am - 定义了Bluez5的编译规则。用于automake工具,生成 Makefile.in文件。
       Makefile.in - 用于configure脚本,生成最终的Makefile文件。
       configure.ac - 用于autoconf工具,生成configure脚本。
       configure - 配置编译选项,生成最终的Makefile文件,以及config.h文件。

    4.BlueZ测试工具

      BlueZ5提供了很多测试工具:
      bccmd - 用于向CSR(Cambridge Silicon Radio)设备发出BlueCore命令。
      bluemoon - 是一个Bluemoon配置工具。
      bluetoothctl - bluetoothd测试工具。
      bluetooth-player - 蓝牙音频测试(AVRCT)工具。
      btmgmt - 使用Bluetooth Management API的蓝牙测试工具,通过Bluetooth Management sockets 与 kernel模块通信。
      btmon - 用于监控HCI,获取HCI log,解析HCI log,可以替代hcidump。
      ciptool - 用于设置,维护和检查Linux内核中蓝牙子系统的CIP配置。
      hciattach - 用于将串口连接到蓝牙协议栈作为HCI的传输接口。
      hciconfig - 用于配置蓝牙设备。
      hcidump - 用于监控HCI,获取HCI log,解析HCI log。
      hcitool - 用于配置蓝牙连接并向蓝牙设备发送一些特殊命令。
      hex2hcd - 用于将Broadcom设备所需的文件转换为hcd(Broadcom蓝牙固件)格式。
      hid2hci - 蓝牙USB Dongle 的HID和HCI模式切换工具。
      l2ping - ping扫描到的远端设备。
      l2test - l2cap测试工具。
      mpris-proxy - 媒体播放器远程接口规范(mpris)是标准的D-Bus接口,其旨在提供用于控制媒体播放器的公共API。mpris-proxy 是一种实现mpris的媒体播放器测试工具(AVRTG?)。更多关于mpris的信息见:
      http://specifications.freedesktop.org/mpris-spec/latest/
      obexctl - obexd测试工具。
      obex-client-tool - obex server端测试工具。
      obex-server-tool - obex client端测试工具。
      rctest - 用于测试蓝牙协议栈上的RFCOMM通信。
      rfcomm - 用于设置,维护和检查Linux内核中蓝牙子系统的RFCOMM配置。
      sdptool - 用于在蓝牙设备上执行SDP查询。

    5.BlueZ5 API介绍和使用

      所有BlueZ5的API都可以在BlueZ5源码目录下的 doc/ 目录下查询到,同时API 相关代码示例可以在BlueZ5源码目录下的 test/ 目录中查询到。与BlueZ 4相比,BlueZ 5的 D-Bus API有重大更改。大部分更改是由于BlueZ 5中的以下新特性导致的:
      1)采用标准D-Bus Properties和ObjectManager接口(在D-Bus specification文档中定义)。
      2)引入特定版本的接口(例如org.bluez.Adapter1)。当引入新版本接口时,会尽量同时支持至少两个最新版本。
      3)简化或删除单独的profile接口,添加通用的org.bluez.Device1.Connect方法来连接profile。
      4)移除org.bluez.Service接口(用于注册SDP records and authorization),引入新的org.bluez.Profile1接口。
      5)在设备扫描期间动态创建设备对象。
      6)引入新的AgentManager1接口。
      7)基本D-Bus路径已移至“/org/bluez”。

    (1)D-Bus Properties
      D-Bus的Properties接口提供了对object/interface的属性(Properties)的访问方法。BlueZ4在需要提供访问属性方法的每个接口上自定义了GetProperties和SetProperty方法以及PropertyChanged信号。在BlueZ 4发布以后,一个新的的标准的属性接口已经完成了,现在不是在特定的接口上实现这些方法和信号,而是在每个对象(object)上都有一个包含如Set,Get和GetAll方法和PropertiesChanged信号的通用的org.freedesktop.DBus.Properties接口。这些方法和信号包含一部分属性属于消息参数的特定接口。
      属性无效化(the invalidation of a property)的特性仅由标准属性接口提供,不存在与BlueZ4中。这个特性对于传递一个不再存在的属性的信息特别方便。

    (2)D-Bus ObjectManager
      D-Bus的ObjectManager接口是访问D-Bus服务的整个对象层次结构的通用方法。它包括用于监听接口添加和删除的信号(signal)以及一个返回服务中所有可用对象、对象的接口以及接口上的所有属性的GetManagedObjects方法。实际的ObjectManager接口可以在BlueZ服务的根(“/”)路径上找到。
      因为大多数管理和发现对象的任务都可以通过ObjectManager接口来完成,BlueZ 4中的许多方法,信号和属性都因为变得多余,而被删除了。其中最著名的一个是旧的org.bluez.Manager接口。这个接口在BlueZ 5中没有任何对应的东西。相反,应用程序将通过ObjectManager.GetManagedObjects方法查找具有“org.bluez.Adapter1”接口的任何返回对象来发现可用的adapter。默认adapter的概念总是有点不清晰,并且adapter值不能改变,所以如果应用需要设置默认的adapter,应选择查找到的第一个adapter。
      BlueZ5源代码test/目录下的get-managed-objects测试脚本,可以通过ObjectManager接口来查看bluetoothd上的对象。

    (3)Device discovery
      Ble的蓝牙地址基本上只是在经典蓝牙地址的基础上扩展了额外的一个位,这个地址还有“随机(ramdom)”还是“公共(public)”之分。这导致调用BlueZ 4 的CreateDevice和CreatePairedDevice API时,由于传入的蓝牙地址参数没有包含任何这个额外的随机/公共信息,bluetoothd不得不维护一个内部缓存来查找必要的信息。另一个问题是,由于BlueZ D-Bus API不区分传统的BR / EDR设备和LE设备,因此基本上有三种可能的地址类型需要缓存:BR / EDR,LE公共和LE随机。
      为了解决这个问题,BlueZ5去除了显式CreateDevice / CreatePairedDevice的方法,同时由于远端设备是在设备扫描过程中发现的而采用了动态创建设备对象的方法。由于引入了新的ObjectManager,单独的DeviceFound信号也不再需要了。应用程序可以在设备发现过程中通过通用的ObjectManager信号简单地跟踪新创建的设备对象。BlueZ5引入一个新的Device1.Pair方法(与旧的CreatePairedDevice方法类似)去配对远端设备,同样引入一个新的Device1.Connect方法(与CreateDevice方法类似,但不完全相同)去跳过配对过程,直接连接远端设备。一旦停止了设备发现,未连接或配对的设备将在三分钟内被bluetoothd自动删除。
      BlueZ5源代码的test/目录下有一个可以用于测试设备发现功能的test-discovery脚本同时提供了为应用程序实现设备发现功能(涉及哪些方法和信号等)的示例。

    (4)General device connection procedure
      BlueZ 4有一个通用的Device.Disconnect方法,但没有一个通用的Device.Connect方法。BlueZ4的Audio接口上的Connect方法与BluezZ5 的Device.Connect方法有点类似,该方法将所有音频profile组合到一个协调的连接过程中。
      通过内部重构和创建一个更干净的内部profile接口,BlueZ 5已经能够为所有profile文件引入一个新的Device1.Connect方法。任何已经实现的profile都可以选择参与到这个通用连接过程中来,并在用户调用该方法时连接。在此方法内部,此方法包含有关profile的建议连接顺序的一些特殊知识,并将基于此对profile进行排序。以音频配置文件为例,其中首先连接HFP,然后是A2DP,最后是AVRCP。
      这个接口对应用程序来说,不需要首先尝试发现设备的支持那些profile,然后确定需要调用哪个接口的Connect方法(例如BlueZ 4中的org.bluez.Input,org.bluez.Audio等),应用程序可以直接调用Device1.Connect方法连接profile,而并不需要关注细节。
      要测试Device1.Connect()功能,您可以使用BlueZ5源代码目录下test/目录中的 test-device脚本:test-device connect < remote addr >。

    (5)The new Profile1 interface (and removal of org.bluez.Service)
      BlueZ5引入了一个新的通用D-Bus接口来实现外部profile。profile(驻留在单独的进程中)实现org.bluez.Profile1接口,并通过BlueZ端的新ProfileManager1接口注册实现它的对象。在RegisterProfile方法中(在ProfileManager1接口上),需要至少提供要注册的profile的UUID。 BlueZ内部有一个默认的公用profile信息表,所以除了UUID外其他信息并不是必要的。当然,如果需要也可以提供profile的完整SDP记录(XML编码),期望的安全级别,启用/禁用授权,版本,特征,角色,名称等的信息。
      注册profile后,bluetoothd将接管所有需要完成的任务,直到profile完成连接。这些任务包括注册SDP记录(对于服务器端profile),启动服务器socket或连接客户端socket,以及执行授权(对于服务器端profile)等。一旦bluetoothd完成这些任务,它将通过一个Profile1.NewConnection方法将新的连接传给外部的profile对象。这个方法传递了连接的描述符(socket)以及连接的属性字典。连接的属性包括profile的版本和特性(从远程SDP record提取)或者由bluetoothd内部提供了注册绑定的机制的特定于profile的条目。
      由于BlueZ4中的org.bluez.Service接口与此接口与具有相同的功能,旧的org.bluez.Service接口已被删除。在BlueZ 5发布时,包括obexd(所有OBEX profile)和oFono(适用于HFP)在内的项目已转换为使用新的Profile接口。
      在BlueZ5源代码的 test/ 目录中有一些测试脚本,如test-profile和test-hfp等,可以作为与ProfileManager1接口交互的示例。

    (6)The new AgentManager1 interface
      BlueZ需要注册代理(agent)对象来处理配对和连接授权。BlueZ 5不使用旧的Adapter.RegisterAgent方法和CreatePairedDevice方法(BlueZ5中使用Device1.Pair方法),而是使用更通用的AgentManager1接口。所有代理需要通过此接口注册,BlueZ将自动选择调用Device1.Pair的相同应用程序的代理(假设应用程序还拥有该代理)。通过AgentManager1.RequestDefault方法,代理可以请求成为默认代理,在这种情况下,默认代理将处理所有传入(远端发起的)请求。通过Adapter.RegisterAgent注册自身的代理,应该将自己注册为BlueZ 5中的默认代理。
      对于Agent1接口还有一些更改(除了将接口名从Agent改为为Agent1)。首先,接口中的ConfirmModeChange方法已被删除。其次,引入了用于可能不与用户交互的配对请求的新的请求授权方法。这种情况的主要发生在传入SSP配对请求将触发just-works模型(?)时。
      BlueZ5 源码目录下 test/ 目录中的 simple-agent 脚本,用于测试AgentManager1接口,并提供了如何完成代理注册的示例代码。

    6.Bluez5移植

      下面主要说明如何将BlueZ5移植到yocto平台上。
    (1) 在yocto上编译BlueZ5
      目前开发使用的yocto2.1.1平台已经默认支持BlueZ5(在poky/meta/conf/bitbake.conf 中定义),yocto平台使用bitbake工具构建,编译整个系统。Bluez5的.bb文件位于poky/meta/recipe-connectivity/bluez5目录下。Bitbake依据Bluez5的.bb文件编译,打包特定版本的BlueZ5。
      单独编译BlueZ5模块的方法:
      1)重置 BlueZ5编译状态
        bitbake -c cleansstate bluez5
      2)编译BlueZ
        bitbake bluez5
        或强制编译模块:
        bitbake bluez5 -C compile
      BlueZ5编译成功后会在 build/tmp/work/ 目录下生成BlueZ5工作目录,Bluez5工作目录中的 build/ 目录用于存放编译生成的目标文件,image/目录中存放了将打包的目标文件及其路径(do_install任务指定那些目标文件将会被打包),deploy-debs/ 目录下存放了目标文件经打包后生成的deb软件包(pakckages变量定义了打包规则,如果packages未定义,则按照默认规则打包)。
      
    (2)安装Bluez5到yocto
      BlueZ5编译成功后,会生成很多软件包,但是这些软件包不一定会安装到yocto系统中,将会安装到yocto系统的deb软件包放在 build/tmp/deploy/deb/aarch64/下面(具体目录不固定,依据不同软件包类型和目标系统架构而定)。如果发现编译好的软件包并没有安装到yocto系统中,此时我们可以将软件包手动添加到用于编译系统镜像的.bb文件的IMAGE_INSTALL变量上。
      例如,现在有一个软件包 bluez5_5.37-r0_arm64.deb 未安装进系统,假设编译系统镜像的.bb文件名为xxxx-image-xx.bb,我们可以通过如下方法添加软件包到系统镜像中:

      IMAGE_INSTALL += “bluez5”

    我们在xxxx-image-xx.bb的IMAGE_INSTALL变量上添加的软件包名,并不需要完整的包名,只需要完整软件包名中的第一个“_”号前面的部分即可。

    (3) 初始化蓝牙芯片(brcm)
      由于目前yocto平台上使用的Brcom公司蓝牙芯片,蓝牙芯片初始化采用的是brcm_patchram_plus工具,该工具的作用是初始化蓝牙芯片,进行基本参数的配置。
      brcm_patchram_plus参数:
      –d: 显示调试信息
      –enable_hci: 启动hci协议
      –enable_lpm: 启动lpm模式
      –baudrate: 指定工作时的波特率
      –patchram: 指定hcd文件(firmware)
      –bd_addr: 加载蓝牙地址
      –/dev/ttyxxx: 指定串口
      
    brcm_patchram_plus初始化蓝牙并设置基本参数示例:

    1)给蓝牙模块上电
       echo 1 > /sys/class/rfkill/rfkill0/state
    2)初始化蓝牙芯片
       brcm_patchram_plus --d --bd_addr xx:xx:xx:xx:xx:xx --enable_hci --enable_lpm  --baudrate 3000000 --patchram /etc/bcm4330.hcd  /dev/ttyHS0 &

      执行上面的操作后brcm_patchram_plus会开始初始化蓝牙芯片并配置基本参数,加载完成后,我们可以通过hciconfig工具判断加载是否成功,通过hciconfig -a命令,我们可以查看hci信息:

    ts@ts:~$ hciconfig -a
    hci0:   Type: BR/EDR  Bus: UART
        BD Address: 00:1A:7D:DA:71:13  ACL MTU: 310:10  SCO MTU: 64:8
        DOWN 
        RX bytes:16457693 acl:48845 sco:0 events:268201 errors:0
        TX bytes:1438036 acl:408 sco:0 commands:267572 errors:0
        Features: 0xff 0xff 0x8f 0xfe 0xdb 0xff 0x5b 0x87
        Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 
        Link policy: RSWITCH HOLD SNIFF PARK 
        Link mode: SLAVE ACCEPT

      从上面我们可以看出hci0上的蓝牙设备目前还未打开,通过hciconfig hci0 up命令我们可以尝试打开蓝牙,执行打开蓝牙命令后,再次输入hciconfig -a:

    ts@ts:~$ hciconfig hci0 up
    ts@ts:~$ hciconfig -a
    hci0:   Type: BR/EDR  Bus: UART
        BD Address: 00:1A:7D:DA:71:13  ACL MTU: 310:10  SCO MTU: 64:8
        UP RUNNING PSCAN 
        RX bytes:16459230 acl:48845 sco:0 events:268283 errors:0
        TX bytes:1439507 acl:408 sco:0 commands:267652 errors:0
        Features: 0xff 0xff 0x8f 0xfe 0xdb 0xff 0x5b 0x87
        Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 
       ......

      如果输入hciconfig -a 或者hciconfig hci0 up后没有任何反应,那么说明加载失败了,如果加载失败的话,我们如何判断问题出在哪里呢?我们可以在执行brcm_patchram_plus 时加上 -d 参数可以打开调试信息,下面是设置蓝牙串口波特率时打印的调试信息:

      > writing
      > 01 18 fc 06 00 00 c0 c6 2d 00
      > received 7
      > 04 0e 04 01 18 fc 00
      > Done setting baudrate

      通过在brcm_patchram_plus 源码中查询“01 18 fc”可以确定“01 18 fc 06 00 00 c0 c6 2d 00“是设置波特率的命令,而”04 0e 04 01 18 fc 00”自然就是命令的执行结果。我们可以看到返回值中也包含了”01 18 fc“字符,这一般表示命令执行成功了,失败的情况下一般没有返回值。
      brcm_patchram_plus工具如果设置了“–enable_hci”,那么初始化蓝牙的最后一步是“enable_hci”,这个“enable_hci”到底做了些什么事呢?为了知道“enable_hci”到底做了什么,我们在执行brcm_patchram_plus之前,先开启btmon来监控hci(hci0):btmon -i hci0 &,然后再执行brcm_patchram_plus,这样在“enable_hci”时,我们得到如下log:

      > = New Index: 00:00:00:00:00:00 (BR/EDR,UART,hci0) 0.778738 < HCI 
      > Command: Read Local Supported Features (0x04|0x0003) plen 0
      > 0.783448
      > > HCI Event: Command Complete (0x0e) plen 12 0.791996
      >       Read Local Supported Features (0x04|0x0003) ncmd 1
      >         Status: Success (0x00)
      >         Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87
      >           3 slot packets
      >           5 slot packets
      >           Encryption
      >           Slot offset
      >           Timing accuracy
      >           Role switch
      >           Sniff mode
      >           Power control requests
      >           Channel quality driven data rate (CQDDR)
      >           SCO link
      >           HV2 packets
      >           HV3 packets
      >           u-law log synchronous data
      >           A-law log synchronous data
      >           CVSD synchronous data
      >           Paging parameter negotiation
      >           Power control
      >           Transparent synchronous data
      >           Flow control lag (most significant bit)
      >           Broadcast Encryption
      >           Enhanced Data Rate ACL 2 Mbps mode
      >           Enhanced Data Rate ACL 3 Mbps mode
      >           Enhanced inquiry scan
      >           Interlaced inquiry scan
      >           Interlaced page scan
      >           RSSI with inquiry results
      >           Extended SCO link (EV3 packets)
      >           EV4 packets
      >           EV5 packets
      >           AFH capable slave
      >           AFH classification slave
      >           LE Supported (Controller)
      >           3-slot Enhanced Data Rate ACL packets
      >           5-slot Enhanced Data Rate ACL packets
      >           Sniff subrating
      >           Pause encryption
      >           AFH capable master
      >           AFH classification master
      >           Enhanced Data Rate eSCO 2 Mbps mode
      >           Enhanced Data Rate eSCO 3 Mbps mode
      >           3-slot Enhanced Data Rate eSCO packets
      >           Extended Inquiry Response
      >           Simultaneous LE and BR/EDR (Controller)
      >           Secure Simple Pairing
      >           Encapsulated PDU
      >           Erroneous Data Reporting
      >           Non-flushable Packet Boundary Flag
      >           Link Supervision Timeout Changed Event
      >           Inquiry TX Power Level
      >           Enhanced Power Control
      >           Extended features
      > < HCI Command: Read Local Version Information (0x04|0x0001) plen 0
      > 0.792528
      ...
      ...

      从上面的log可以看出,“enable_hci”操作其实就是通过HCI接口对芯片(controller)做一些读写操作,获取芯片的相关信息(name,ferature等),同时对芯片一些参数做初始化设置,这与正常打开蓝牙后所做的操作类似。

    (4)启动bluetoothd 和 obexd
      在蓝牙加载成功后,我们就可以启动bluetoothd和obexd(用于obex文件传输),执行:
      /usr/libexec/bluetooth/bluetoothd –compat &
    启动bluetoothd。bluetoothd成功启动后会在D-Bus的systembus上注册“org.bluez”,并通过其向外提供BlueZ5 API。我们可以通过D-Bus命令去查询BlueZ提供的API:

    dbus-send --system --print-reply --dest=org.bluez <dest object path> org.freedesktop.DBus.Introspectable.Introspect

      例如,我们可以通过以下命令查询/org/bluez路径下的bluez5 API。

    dbus-send --system --print-reply --dest=org.bluez /org/bluez org.freedesktop.DBus.Introspectable.Introspect 

      如果查询不到API,则启动bluetoothd加上 -n -d参数,这样就能打开debug,打印出log信息:
      /usr/libexec/bluetooth/bluetoothd -d -n –compat & 
      查询bluetoothd在D-Bus上提供的API时,有时会出现/org/bluez/hci0消失不见了,这种问题一般是由于使用brcm_patchram_plus加载fireware出现问题导致的。
      在Bluetoothd成功启动后,如果需要实现obex功能,那么执行:
      /usr/libexec/bluetooth/obexd -a -r ‘/’ &
    启动obexd,obexd依赖于bluetoothd,obexd启动后将会自动接收文件(-a 参数),同时接收到的文件将会放在 / 目录下(由-r参数指定)。
      同样obexd成功启动后,会在D-Bus的session bus上注册“org.bluez.obex”,并向外提供obex相关的API。我们依然可以通过D-Bus命令去查询这些API:

    dbus-send --session --print-reply --dest=org.bluez.obex <destination object path> org.freedesktop.DBus.Introspectable.Introspect

      例如,我们可以通过以下命令查询/org/bluez/obex路径下的API:

    dbus-send --session --print-reply --dest=org.bluez.obex /org/bluez/obex org.freedesktop.DBus.Introspectable.Introspect

      如果查询不到API,那么在启动obexd时加上“-n -d”参数就可以打开obexd的debug开关并打印出log:
      /usr/libexec/bluetooth/obexd -n -d -a -r ‘/’ &

    (5)通过D-Bus API 控制蓝牙
      BlueZ5通过D-Bus提供API给第三方应用,所有的BlueZ API可以在BlueZ5源码的doc/目录下查到,这些API可以通过glib2.0中的GDBus来访问,下面的网址提供了所有可用的GDBus API:
    https://developer.gnome.org/gio/stable/index.html
    常用的Bluez5 API有adapter,agent,device以及profile,下面将具体介绍如何通过GDBus来访问这些常用BlueZ API:
    1)监控目标D-Bus
      GDBus中的g_bus_watch_name API可以很容易的实现对目标D-Bus的监控并获得GDBusConnection 对象:

    #include <gtk/gtk.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <dirent.h>
    #include <sys/stat.h>
    #include <syslog.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <pthread.h>
    #include <linux/input.h>
    #include <gio/gio.h>
    
    const gchar *service_name= "org.bluez";
    gboolean opt_system_bus = TRUE;
    guint watcher_id=0;
    GBusNameWatcherFlags flags;
    
    void on_name_appeared (GDBusConnection *connection,
                      const gchar     *name,
                      const gchar     *name_owner,
                      gpointer         user_data)
    {
       g_print("Name %s on %s is owned by %s\n",
               name,
               opt_system_bus ? "the system bus" : "the session bus",
               name_owner);
    }
    void on_name_vanished (GDBusConnection *connection,
                      const gchar     *name,
                      gpointer         user_data)
    {
       g_print("Name %s does not exist on %s\n",
               name,
               opt_system_bus ? "the system bus" : "the session bus");
    }
    
    /**
    *connect and watch "org.bluez" on dbus.
    */
    void watch_dbus(){
        flags = G_BUS_NAME_WATCHER_FLAGS_NONE;
        g_print("######watch_dbus######\n");
        watcher_id = g_bus_watch_name (opt_system_bus ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION,
                                     service_name,
                                     flags,
                                     on_name_appeared,
                                     on_name_vanished,
                                     NULL,
                                     NULL);
    }

    2)创建GDBusProxy
      在获得了GDBusConnection对象后,我们可以通过该对象创建GDBusProxy代理。GDBUS通过GDBusProxy来访问dbus 上的接口(interface)。下面以创建adapter接口代理为例说明如何如何创建目标接口代理并监控目标接口属性(Porperties)以及如何访问目标接口提供的API和属性:
      1.创建adapter 接口代理并通过 “g-properties-changed” signal监控adapter接口属性改变。

    const gchar *service_name= "org.bluez";
    const gchar *object_path_bluez_hci0="/org/bluez/hci0";
    const gchar *interface_adapter1="org.bluez.Adapter1";
    
    GDBusProxy *default_adapter=NULL;
    /**
    *property change signal handler.
    */
    void on_properties_changed (GDBusProxy          *proxy,
                           GVariant            *changed_properties,
                           const gchar* const  *invalidated_properties,
                           gpointer             user_data)
    {
      /* Note that we are guaranteed that changed_properties and
       * invalidated_properties are never NULL
       */
    
      if (g_variant_n_children (changed_properties) > 0)
        {
          GVariantIter *iter;
          const gchar *key;
          GVariant *value;
    
          g_print (" *** Properties Changed:\n");
          g_variant_get (changed_properties,
                         "a{sv}",
                         &iter);
          while (g_variant_iter_loop (iter, "{&sv}", &key, &value))
            {
              gchar *value_str;
              value_str = g_variant_print (value, TRUE);
              g_print ("      %s -> %s\n", key, value_str);
              g_free (value_str);
            }
          g_variant_iter_free (iter);
        }
    
      if (g_strv_length ((GStrv) invalidated_properties) > 0)
        {
          guint n;
          g_print (" *** Properties Invalidated:\n");
          for (n = 0; invalidated_properties[n] != NULL; n++)
            {
              const gchar *key = invalidated_properties[n];
              g_print ("      %s\n", key);
            }
        }
    }
    /**
    *Async callback of create_GDBusProxy.
    */
    void adapter_proxy_ready(GObject *source_object,GAsyncResult *res,
                            gpointer user_data){
                GError *error;
    
                default_adapter=g_dbus_proxy_new_finish (res,&error);
                if(default_adapter == NULL){
                    g_print("create adapter proxy failed.\n");
                    return;
                }else{
                    g_print("create adapter proxy success.\n");
                }
    
               g_signal_connect (default_adapter,
                        "g-properties-changed",
                        G_CALLBACK (on_properties_changed),
                        NULL);
    }
    
    /**
    *create adapter proxy.
    */
    void create_adapter_proxy(GDBusConnection *connection){
                if(connection == NULL){
                return; 
                }
                g_print("create_adapter_proxy.\n");
                gpointer user_data;
                g_dbus_proxy_new (connection,
                      G_DBUS_PROXY_FLAGS_NONE,
                      NULL,
                      service_name,
                      object_path_bluez_hci0,
                      interface_adapter1,
                      NULL,
                      adapter_proxy_ready,
                      user_data);
    }

      2.通过GDbusProxy 代理访问adapter接口的API adapter接口中提供了蓝牙扫描功能的相关API(void StartDiscovery() 和void StopDiscovery()),下面详细说明访问这两个API的方法:

    /**
    *check default adapter.
    */
    gboolean check_default_adapter(){
         if(default_adapter == NULL){
              g_print("default_adapter is not available.\n");
              return FALSE;
           }
         return TRUE;
    }
    
    void adapter_callback(GObject *source_object,
                            GAsyncResult *res,
                            gpointer user_data){
                   GError *error = NULL;
                   g_print("adapter_callback\n");               
                   g_dbus_proxy_call_finish (default_adapter,
                                             res,
                                             &error);
                   if(error != NULL){ 
                   g_print("error: %s\n", g_dbus_error_get_remote_error(error));
                   }
    }
    /**
    *set scan remote device start/stop.
    */
    void cmd_scan(gboolean scan){
           if(check_default_adapter() == FALSE){
              return;
           }
           g_print("cmd_scan: %s\n",scan ? "on" : "off");
           gchar *method_name;
           if(scan){
           method_name = "StartDiscovery";
           }else{
           method_name = "StopDiscovery";
           }
           gpointer user_data;
           g_dbus_proxy_call (default_adapter,
                       method_name,
                       NULL,
                       G_DBUS_CALL_FLAGS_NONE,
                       -1,
                       NULL,
                       adapter_callback,
                       user_data);        
    }

      3.创建D-Bus Properties接口代理

    #define INTERFACE_PROPERTY "org.freedesktop.DBus.Properties"
    const gchar *interface_adapter1_property=INTERFACE_PROPERTY;
    
    GDBusProxy *adapter_property_proxy=NULL;
    
    /**
    *Async callback of create_GDBusProxy.
    */
    void adapter_property_proxy_ready(GObject *source_object,GAsyncResult *res,
                            gpointer user_data){
                GError *error;
                adapter_property_proxy=g_dbus_proxy_new_finish (res,&error);
                if(adapter_property_proxy == NULL){
                    g_print("create adapter property proxy failed.\n");
                    return;
                }else{
                    g_print("create adapter property proxy success.\n");
                }
    }
    /**
    *create adapter property proxy.
    */
    void create_adapter_property_proxy(GDBusConnection *connection){
                if(connection == NULL){
                return; 
                }
                g_print("create_adapter_property_proxy.\n");
                gpointer user_data;
                g_dbus_proxy_new (connection,
                      G_DBUS_PROXY_FLAGS_NONE,
                      NULL,
                      service_name,
                      object_path_bluez_hci0,
                      interface_adapter1_property,
                      NULL,
                      adapter_property_proxy_ready,
                      user_data);
    }

      4.通过通用的D-Bus的Properties接口访问adapter接口属性adapter接口中提供了“Alias”属性,通过Properties接口的“Set”方法设置该属性来改变蓝牙设备名。

    /**
    *check default adapter.
    */
    gboolean check_adapter_property_proxy(){
         if(adapter_property_proxy == NULL){
              g_print("adapter_property_proxy is not available.\n");
              return FALSE;
           }
         return TRUE;
    }
    
    void cmd_set_bluetooth_name(const gchar *name){
           if(check_adapter_property_proxy() == FALSE){
              return;
           }
           if(name == NULL){
              g_print("error: cmd_set_bluetooth_name: name is null\n");
              return;
           }
           g_print("cmd_set_bluetooth_name: name: %s\n",name);
           const gchar *property_name = "Alias";
           const gchar *method_name = "Set";
           GVariant *alias=g_variant_new_string (name);
    
           GError *error=NULL;
           GVariant * result = g_dbus_proxy_call_sync (adapter_property_proxy,
                                       method_name,g_variant_new("      (ssv)",interface_adapter1,property_name,alias),         
                                       G_DBUS_CALL_FLAGS_NONE,
                                       -1,
                                       NULL,
                                       &error);
           if(error != NULL){
           g_print("error: %s\n", g_dbus_error_get_remote_error(error));
           }else{
           //
           }
    }

    3)创建GDBusObjectManager 代理
    在获得了GDBusConnection对象后,我们可通过该对象创建GDBusObjectManager 代理。GDBUS通过GDBusObjectManager 来监控D-Bus上接口的添加删除,下面详细介绍如何创建GDBusObjectManager 代理并通过该代理监控device接口的动态添加删除。
      1.创建GDBusObjectManager代理并通过GDBusObjectManager代理监控device接口的动态添加和删除

    const gchar *service_name= "org.bluez";
    const gchar *interface_device1="org.bluez.Device1";
    
    GDBusObjectManager *objectmanager=NULL;
    /**
    *callback of "interface-added" signal.
    */
    void on_interface_added(GDBusObjectManager *manager,
                                   GDBusObject *object,
                                   GDBusInterface *interface_){
                g_print("######on_interface_added######.\n");
    }
    
    /**
    *callback of "interface-removed" signal.
    */
    void on_interface_removed(GDBusObjectManager *manager,
                                     GDBusObject *object,
                                     GDBusInterface *interface_){
                g_print("######on_interface_removed######.\n");                          
    }
    void on_object_added(GDBusObjectManager *manager,
                                GDBusObject *object){
                g_print("######on_object_added######.\n");
                const gchar *object_path=g_dbus_object_get_object_path(object);
                if(object_path != NULL){
                   g_print("object added : %s\n",object_path);
                }else{
                   g_print("object_path is NULL");
                   return;
                }
                GDBusInterface *ifc=g_dbus_object_get_interface (object,interface_device1);
                if(ifc != NULL){
                //device added.
                g_print("device added.\n");
                }
    }
    void on_object_removed(GDBusObjectManager *manager,
                                  GDBusObject *object){
    
                g_print("######on_object_removed######.\n");
                const gchar *object_path=g_dbus_object_get_object_path(object);
                if(object_path != NULL){
                   g_print("object removed : %s\n",object_path);
                }else{
                   g_print("object_path is NULL");
                }
                GDBusInterface *ifc=g_dbus_object_get_interface (object,interface_device1);
                if(ifc != NULL){
                //device removed.
                 g_print("device removed.\n");
                }
    }
    /**
    *async callback.
    */
    void GDBusClientManager_Ready(GObject *source_object,GAsyncResult *res,
                            gpointer user_data){
                GError *error=NULL;
                objectmanager=g_dbus_object_manager_client_new_finish
                                                            (res,&error);
                if(objectmanager== NULL){
                g_print("create objectmanager failed. Error: %s.\n",
                        g_dbus_error_get_remote_error(error));
                return;
                }
                g_print("create objectmanager success.\n");
                //connect signal.
                g_signal_connect (objectmanager,
                        "interface-added",
                        G_CALLBACK (on_interface_added),
                        NULL);
                g_signal_connect (objectmanager,
                        "interface-removed",
                        G_CALLBACK (on_interface_removed),
                        NULL);
                g_signal_connect (objectmanager,
                         "object-added",
                        G_CALLBACK (on_object_added),
                        NULL);
                g_signal_connect (objectmanager,
                        "object-removed",
                        G_CALLBACK (on_object_removed),
                        NULL);
    }
    
    void destroyed(gpointer data) {
        printf("Got a GDestroyNotify callback\n");
    }
    
    /**
    *create GDBusClientManager of “org.bluez”
    */
    void create_GDbusClientManager(GDBusConnection *connection){
    
           g_print("######create_GDbusClientManager######\n");
           if(connection == NULL){
           g_print("Error: GDBusConnection is not available!");
           return;
           }
           gpointer get_proxy_type_user_data;
           gpointer user_data;
           g_dbus_object_manager_client_new(connection,G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
                                            service_name,"/",
                                            NULL,user_data,destroyed,
                                            NULL,GDBusClientManager_Ready,
                                            user_data);
    
    }

      当然BlueZ5 也提供了bluetoothctl 和 obexctl两个命令行工具来访问Bluez5的D-Bus API,启动bluetoothctl 和 obexctl后输入 help可以查看到所有支持的操作,如果不需要图形界面的话,这两个命令行工具已经实现了大部分蓝牙功能。如果系统支持Gnome,也可以直接移植Gnome-bluetooth。

    展开全文
  • 蓝牙 Bluez

    千次阅读 2013-09-24 14:56:00
    Bluez作为当前最成熟的开源蓝牙协议栈,在Linux的各大发行版中已经得到了广泛的应用。在桌面环境下,使用Bluez应该已经没有太大的问题,本文的主要目的是介绍在嵌入式平台上,搭建和配置Bluez的各个Profile运行所需...
    Bluez作为当前最成熟的开源蓝牙协议栈,在Linux的各大发行版中已经得到了广泛的应用。在桌面环境下,使用Bluez应该已经没有太大的问题,本文的主要目的是介绍在嵌入式平台上,搭建和配置Bluez的各个Profile运行所需做的工作,讨论可能遇到的问题,介绍一些工具的使用和工作原理。因为本人的能力和测试时间有限,可能下文中有些理解、分析不一定准确,欢迎联系指正。 


    1 相关说明 
    1.1 网站资源 
    Bluez的官方网址:http://www.bluez.org/ 这里提供最新的源码下载,最近服务器崩溃了一次,有些东西没了。。。。 
    Bluez的Wiki:http://wiki.bluez.org/wiki/ 这里提供Bluez相关的Howto等文档资源 
    相关邮件列表: 
    https://lists.sourceforge.net/lists/listinfo/bluez-users 关于如何使用和配置Bluez,多数是在讨论PC环境下的问题。。。。 
    https://lists.sourceforge.net/lists/listinfo/bluez-devel Bluez开发者活动的地方,有什么Bug之类的怀疑,还有编程接口之类的问题,就发到这里吧。 

    1.2 工作环境 
    个人感觉,使用Bluez最大的问题就是文档的欠缺,除了Wiki上的有限资料以外,很难找到其它有用的文档。 
    由于Bluez的代码实现更新变化得很快,网上的许多文档介绍的都是早期版本的使用,再有的文章多数是基于成熟的Linux发行版,来讨论蓝牙设备的配置和使用,对于嵌入系统开发,自己编译,搭建和配置相关环境的文章很少。此外和具体蓝牙芯片相关的资料也很难找到。 

    这里我不打算也没有能力写一个完整的指南,只能基于前段时间在自己的板子上所做的工作,总结一下相关的步骤和所遇到的各类问题以及这期间所掌握的各种相关知识。希望能给有类似开发需求的朋友一些有益的帮助,下面是这篇文章所基于的工作环境: 

    &#61656; 硬件平台:基于ARM的嵌入式板子 
    &#61656; 蓝牙芯片:CSR BC4 ROM 版本芯片,不带eeprom 
    &#61656; 软件环境:Linux 2.6.21 ,自制文件系统 
    &#61656; Bluez版本:bluez-libs 3.22 bluez-utils 3.22 

    2 编译 
    2.1 内核 
    相信多数人使用的都是2.6的内核了,在2.6的内核中要支持Bluez,只要你的内核版本不是太旧,无需打Patch,直接配置好就OK了,内核里面的代码相对比较稳定了。当然,Bluez对一些Bluetooth协议栈新特性的支持,还是需要更新kernel代码的。你应该确认你使用的kernel版本是否以及包含了对应的支持。 

    内核的配置,基本上把 networking下 --- Bluetooth subsystem support 里的以下几项全部选上即可: 

    L2CAP protocol support 
    SCO links support 
    RFCOMM protocol support 
    RFCOMM TTY support 
    BNEP protocol support 
    HIDP protocol support 

    此外,在Bluetooth device drivers里选上你所需要支持的Bluetooth设备。我使用的CSR的chip是我们直接build在板子上,通过串口和cpu通讯的,芯片默认使用BCSP作为通讯协议,所以我选择了: 

    HCI UART driver 
    BCSP protocol support 

    如果你是通过usb接口使用蓝牙适配器,需要选择 
    HCI USB driver 

    2.2 Bluez Lib / Utils 

    Bluez Lib的编译比较简单,而Bluez-Utils所依赖的库就比较多了,大体包括 dbus alsa hal gstreamer openobex xml等等,仔细观察./configure 的输出,将所需要的包先安装或者build好。 
    值得注意的一点是: 
    如果你需要打开所有的功能模块的支持,需要在 ./configure 参数中添加 --enable-all --enable-audio --enable-input --enable-network –enable-serial 等,在3.22版本中 --enable-all 居然不包括 audio等相关模块的service的编译,不知道是否是因为还保留了daemon和service等不同方案的缘故。不过,这至少与他的configure --help 对于 --enable-all 的描述是不符合的。 

    3 蓝牙硬件初始化及基础服务启动 

    如果在PC环境下,使用Ubuntu,调用 /etc/init.d/bluetooth start 应该就能完成这一步的工作了。下面叙述一下在我的嵌入式环境下,如何手动完成这一步骤。 

    3.1 何谓硬件初始化 
    硬件初始化,指的是配置蓝牙芯片,将其置于一个能够正常通讯的状态。 对于CSR的芯片来说,就是通过设置PSKEY,设置其晶振频率,UART波特率等等一些关键参数。 如果使用的是USB形式的适配器,因为其EEPROM存储了相关的默认参数,这一步很可能不需要做,而我使用的是不带EEPROM的ROM版本芯片,如何正确完成初始化工作着实让我折腾了一阵。 

    对于其它芯片,没有太多研究,不过,据我有限的了解,TI的芯片在hciattach时也需要完成一些额外的初始化工作,其它如ST的芯片则可能需要下载firmware。 

    3.2 硬件初始化步骤 
    通常蓝牙芯片的初始化和协议绑定可以通过 hciattach 来完成(通过配置bluez的启动脚本,可以不需要使用hciattach,标准发行版应该都是不用hciattach,如何配置,还没有研究 。。。 8 ) 

    Hciattach 需要的参数主要包括 TTY节点,设备类型,波特率等。多数类型的设备的初始化工作,在选择了正确的设备类型参数后,都由hciattach在init_uart函数中调用具体的初始化函数所完成。 

    很遗憾的是,因为要重新设置晶振频率和波特率,并同步BCSP协议,这种方式好像处理不了我所使用的芯片(不排除我没有找到正确的解决方案的可能性),我最终的解决办法是在hciattach之前,使用Bluez-utils里的BCCMD工具先完成这些PSKEY的设置工作。 

    具体命令是: 
    bccmd -t bcsp -d /dev/ttyS1 psload -r csr.psr 

    在这时,由于HCI接口还没有启动,所以只能使用BCSP协议来进行通讯,我的设备是暴露在ttyS1下,你的可能不一样,-r参数指明在psload完成 PSKEY的批量加载操作之后,对芯片进行Warmreset,否则这些参数的修改不会起作用。 

    Csr.psr的内容取决与你的芯片,我的大致如下: 

    // PSKEY_ANA_FREQ 
    &01fe = 9C40 // 相当于40M的晶振 
    // PSKEY_UART_BAUD_RATE 
    &01be = 0EBF // 921600的波特率 
    // PSKEY_UART_SEQ_WINSIZE 
    &0407 = 0006 
    // BDADDR 
    &0001 = 1122 3344 5566 7788 
    。。。 

    这里有个问题,你会发现,通过bccmd -t bcsp psset 命令理论上应该是可以单步设置每一个PSKEY的,但是从我实践看来,单步的操作在两次对bccmd的调用过程中,上一次对PSKEY的修改,都会在下一次调用之前被复位,从代码上看估计和BCSP协议的同步过程有关。 
    3.2.1 关于PSKEY的获取 
    如何获得正确的完整的PSKEY参数,大概会有几个途径: 
    &#61656; 通过CSR的网站下载boot_strap包,这是CSR自己的BCHS协议栈所使用的初始化代码,在里面找到你所需要的pskey值。 
    &#61656; 下载CSR的bluesuite工具,里面包含了一个叫pstool的工具,可以用它来读写CSR的Casira开发板或其它BT设备的PSKEY设置,试验并找出你能用的参数。 
    &#61656; 找CSR或模组厂商支持 8 ) 

    不过,基本上来说,如果只是要让芯片通过串口能够和Bluez协议栈正常通讯上,只需要设置PSKEY_ANA_FREQ 和 PSKEY_UART_BAUD_RATE 这两个PSKEY就可以了。 

    3.3 Daemon进程的启动 
    早先的版本里,Bluez的Daemon很多,但是最近的版本,很多daemon都转为service的形式来做了,3.22 里面包括了以下这几个Service,其它profile貌似还保留着daemon的形式。 
    bluetoothd-service-serial 
    bluetoothd-service-network 
    bluetoothd-service-audio 
    bluetoothd-service-input 
    这几个Service的启动依赖于hcid的启动以及相关的配置文件 
    主要配置文件位于:/etc/bluetooth/ 
    此外,通常还需要启动SDP来提供服务查询,另外,Bluez本身还依赖于Dbus daemon的运行。

    所以,整体上来说,我的手动启动Bluez的全过程如下:(其中内核代码是以模块形式编译的) 

    insmod bluetooth.ko 
    insmod hci_uart.ko 
    insmod l2cap.ko 
    insmod rfcomm.ko 
    insmod sco.ko 
    insmod hidp.ko 

    /etc/rc2.d/S20dbus start 
    bccmd -t bcsp -d /dev/ttyS1 psload -r csr.psr 
    hciattach -s 921600 /dev/ttyS1 bcsp 921600 
    hciconfig hci0 up 
    sdpd 
    hcid –d 

    4 Paring配对 
    4.1 Passkey_agent 
    在正常使用一个蓝牙设备前,通常都需要对该设备进行配对绑定的操作。 
    Bluez的配对机制貌似也修改了几次,2.x版本中通过pin_helper来处理pin code的应答,3.22版本里使用的配对机制,其API是基于Dbus来实现的,需要向dbus注册一个agent,PC的发行版通常都会有一些基于各种图形库的passkey_agent,对于嵌入式系统,这部分代码可以想象,应该是要按照相应的API自己实现一个,为了测试,我直接使用了bluez-utils/daemon 目录下的passkey-agent 
        这是一个命令行下的可以使用预先设定的pin code进行配对的程序 

    为了使用它,我的文件系统里 /etc/Bluetooth/hcid.conf 中 option一节类似如下 : 

    # HCId options 
    options { 
    # Automatically initialize new devices 
    autoinit yes; 

    # Security Manager mode 
    # none - Security manager disabled 
    # auto - Use local PIN for incoming connections 
    # user - Always ask user for a PIN 

    security user; 

    # Pairing mode 
    pairing multi; 

    # Do the same as "hciconfig hci0 down" when SetMode("off") 
    # is called. 
    offmode devdown; 

    # Default PIN code for incoming connections 
    passkey "1234"; 


    4.2 关于自动配对和请求的发起 

    配对的发起,这里主要是从请求的发起者是谁的角度来说。 

    通常可能不需要关心配对请求是由本地还是由远端发起的,使用passkey_agent都能够正确处理。 

    不过如果在hcid.conf中将 Security Manager mode 设置为 auto,则Bluez会将passkey后面的字符串作为默认的Pin code,自动答复远端发起的配对请求。这是在没有使用passkey_agent的情况下的一种配对方式。 

    在这种情况下,Bluez可以处理远端的配对请求,但是对于本地发起的配对请求,将无法正确处理,我没有仔细的分析原因,或许是代码特意设计成这种工作方式。所以在无法明确知道谁将会主动先发起配对请求的情况下,使用Atuo模式,可能就会出现有些时候设备能绑定有些时候不能绑定的现象。 

    通常如果是由本地设备搜索发现的新设备,配对绑定的操作应该也是由本地发起。 

    另外可以观察到,对远端一个非PC类的蓝牙设备,如蓝牙耳机,如果上次绑定过,在耳机启动时会主动发起连接请求,如果本地的link key丢失了,也就会再走一次绑定的流程,这种情况下配对请求就是由远端设备发起的。 


    5 A2DP 
    A2DP蓝牙立体声应该是蓝牙最常见的Profile之一。 
    2.x版本的Bluez,对A2DP的支持是通过BTSCO来实现的,3.22的版本通过bluetoothd-service-audio来支持。 
    对Bluez A2DP profile的支持,还依赖于Alsa或Gstreamer。 
    5.1 配置 
    测试A2DP的时候,我使用的是aplay,同时在相关的配置文件里面写死了蓝牙耳机的地址 
    主要的配置文件包括: 

    /etc/asound.conf : 

    pcm.bluetooth{ 
    type bluetooth 
    device 00:02:5B:00:C1:A0 
    profile "hifi" 


    /etc/bluetooth/audio.conf : 

    [General] 
    # disable=Sink 
    SCORouting=PCM 

    [Headset] 
    DisableHFP=true 

    [A2DP] 
    SourceCount=2 

    配置好这些以后,使用 aplay -D bluetooth sample.wav 进行测试。 

    值得注意的是,使用Aplay打开蓝牙设备进行播放,需要有如下两个Alsa的plugin: 

    /usr/lib/alsa-lib/libasound_module_pcm_bluetooth.so 
    /usr/lib/alsa-lib/libasound_module_ctl_bluetooth.so 

    这两个so文件可以在bluez-utils 里面找到。需要注意他们和libasound 的版本匹配。 

    5.2 问题 
    在测试中发现,如果连接的耳机是由PC上的蓝牙适配器提供的AV耳机服务,那么配对可以完成,但是连接会失败,真正的耳机则没有这个问题,不知道是否是因为以上的方法还存在缺陷?尝试使用由DBUS发起命令的形式来连接PC的AV耳机服务,也是一样的问题。是否连接PC模拟的AV耳机服务,首先要切换设备的role? 

    没有测试Ctl接口 

    如何动态选择不同的耳机,而不是写死在脚本里?(这个想来估计是要自己基于Alsa的API编程处理,aplay无法直接完成测试) 

    播放大文件会出现under run错误,需要测试是由与波特率设置不够高造成,还是SBC编码效率不够,还是这个版本里存在的bug。 

    6 DUN的使用 
    Dun profile运行于rfcomm之上,主要是通过蓝牙接口暴露一个Modem的接口,用于提供拨号上网服务。 在这里所讨论的不是提供拨号上网服务本身,而是使用外部设备所提供的这个服务,进行网络连接。 
    6.1 系统配置 
    通常为了使用DUN,或者任何一个其它类型的Modem,我们会通过PPP协议来拨号和建立网络连接。 
    首先需要内核的支持,可以简单的把 device drivers -> Network device support 下面的PPP相关的内容全部选上。 
    其次,要编译应用层的PPP包,我的测试是基于ppp-2.4.4 

    主要的两个ppp配置文件: 

    /etc/ppp/peers/gprs: 

    /dev/rfcomm0 
    115200 
    defaultroute 
    usepeerdns 
    nodetach 
    noauth 
    local 
    debug 
    connect "/usr/sbin/chat -v -f /etc/ppp/chat-gprs" 

    /etc/ppp/chat-gprs: 

    TIMEOUT 10 
    ABORT 'BUSY' 
    ABORT 'NO ANSWER' 
    ABORT 'ERROR' 
    "" 'ATZ' 
    SAY 'Init....\n' 
    OK 'AT+CGDCONT=1,"IP","CMWAP"' 
    ABORT 'NO CARRIER' 
    SAY 'Dialing....\n' 
    OK 'ATD*99***1#' 
    CONNECT '' 

    前面一个配置文件基本就是那样了,后面一个,这两句要根据你的SIM卡的实际情况来处理:

    OK 'AT+CGDCONT=1,"IP","CMWAP"' 
    OK 'ATD*99***1#' 

    这里我设置的是使用中国移动的CMWAP。 

    此外,使用CMWAP还需要设置你的浏览器的代理服务器:10.0.0.172 端口9201 

    6.2 连接步骤 
    首先是要查找提供dun服务的设备,将服务提供在哪个channel通道上。这可以通过sdptool来查看,在我的设备上查询的结果是在channel 1上: 

    ~ # ./sdptool browse 00:08:C6:77:A0:6C 

    Browsing 00:08:C6:77:A0:6C ... 
    Service Name: Dial-upnetworking 
    Service RecHandle: 0x10000 
    Service Class ID List: 
    "Dialup Networking" (0x1103) 
    "Generic Networking" (0x1201) 
    Protocol Descriptor List: 
    "L2CAP" (0x0100) 
    "RFCOMM" (0x0003) 
    Channel: 1 
    Profile Descriptor List: 
    "Dialup Networking" (0x1103) 
    Version: 0x0100 

    其次,如果rfcomm所需设备节点不存在,将其创建: 

    mknod -m 666 /dev/rfcomm0 c 216 0 

    然后就是拨号了,如果该设备之前没有绑定,这过程中会自动发起绑定操作: 

    pppd debug dump call gprs & 

    完成以后就可以看到 ppp0这样一个网络接口了。 

    ~ # ifconfig 
    ppp0 Link encap:Point-to-Point Protocol 
    inet addr:10.154.76.82 P-t-P:192.200.1.21 Mask:255.255.255.255 
    UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1 
    RX packets:4 errors:0 dropped:0 overruns:0 frame:0 
    TX packets:6 errors:0 dropped:0 overruns:0 carrier:0 
    collisions:0 txqueuelen:3 
    RX bytes:64 (64.0 B) TX bytes:101 (101.0 B) 

    7 Bluez相关的各种tools的使用 
    在这一段折腾Bluez的时间里,越来越发现Bluez相关的许多工具做得还是挺好用的,主要在Bluez-utils/tools 目录下。只是有一点让我很遗憾,除了man以外很难找到更多的帮助文档,而man文档本身对一些功能的描述也不是很详细。 
    其中有些选项,如果你不了解蓝牙协议栈,或者没有查阅过相关蓝牙芯片的一些文档,很难搞明白是什么意思,甚至有些选项的具体参数值的设定,如果不读源码你都无从得知有哪些备选值。。。。 

    能力有限,下面所写的只是我所用过的有限的几个工具的一些使用经验,希望能有所帮助。 
    7.1 Bccmd 
    Bccmd是用来和CSR的芯片进行BCCMD(Bluecore command protocol)通讯的一个工具。BCCMD并非蓝牙协议栈的标准,而是CSR芯片的专属协议 
    Bccmd的调用格式为:bccmd [-t <transport>] [-d <device>] <command> [<args>] 
    Tansport类型包括 HCI USB BCSP H4等,常用的估计就是HCI和BCSP两种。需要注意一下他们的使用场合: 
    HCI是一个抽象的标准的蓝牙通讯接口,在基于HCI协议调用BCCMD时,需要在Bluez已经建立好hci接口的基础上使用。 
    BCSP(Bluecore Serial Protocol)是CSR自己制定的传输层协议,主要目的是用来加强在没有使用CTS、RTS进行流量控制的情况下进行可靠的数据传输的能力。其概念是相对H3 , H4而言,( 具体分析,请参考下面杂项一章中相应的小节 ) 

    BCCMD的主要用途就是用来读写pskey,这里以 psset 这个command来介绍一下格式: 
    Psset 格式如下: psset [-r] [-s <stores>] <key> <value> 
    其它都好理解,关键是-s参数之后跟的store具体的含义。这个参数可以是数值也可以是字符串 
    查询CSR的BCCMD相关的文档,可以找到具体的含义如下: 

    0x0000 Default 
    0x0008 psram 
    0x0001 psi 
    0x0002 psf 
    0x0004 psrom 
    0x0003 psi then psf 
    0x0007 psi, psf then psrom 
    0x0009 psram then psi 
    0x000b psram, psi then psf 
    0x000f psram, psi, psf then psrom 

    CSR的蓝牙芯片中,PSKEY可能存储在 rom flash eeprom ram等介质里,这里的数值指明了psset/get命令操作PSKEY时所针对的存储介质及其优先顺序,通常我们会用 –s 0x0 或 –s “default” 来使用该命令,0x0的含义与0xf一样。 

    值得注意的是,哪个参数是有效的,还取决于哪一类的存储介质实际存在于蓝牙芯片中,此外,只读类的介质对写操作类的命令也是无效的。 

    基本上来说,所修改的都是位于psram中的pskey,此外,pskey修改以后要起作用,还要一并使用 –r参数,或直接用warmreset命令将蓝牙芯片进行warm reset。 


    7.2 Hciattach 
    Hciattach主要用来初始化蓝牙设备,它的命令格式如下: 

    hciattach [-n] [-p] [-b] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr] 

    其中最重要的参数就是 type和speed,type决定了要初始化的设备的型号,可以使用 hciattach –l 来列出所支持的设备型号。 
    并不是所有的参数对所有的设备都是适用的,有些设备会忽略一些参数设置,例如:查看hciattach的代码就可以看到,多数设备都忽略bdaddr参数。 

    Hciattach命令内部的工作步骤是:首先打开制定的tty设备,然后做一些通用的设置,如flow等,然后设置波特率为initial_speed,然后根据type调用各自的初始化代码,最后将波特率重新设置为speed。所以调用hciattach时,要根据你的实际情况,设置好initial_speed和speed。 

    对于type BCSP来说,它的初始化代码只做了一件事,就是完成BCSP协议的同步操作,它并不对蓝牙芯片做任何的pskey的设置。同步操作的具体流程和规范可以参考CSR的相关文档: BCSP Link Establishment Protocol 

    7.3 其它 
    下面几个,使用了,但是没有太多研究 
    7.3.1 Hcidump 
    Hcidump不在bluez-utils包里,而是在单独的hcidump包里。主要用来分析捕获和分析HCI数据包,如果使用bluez过程中出了什么问题,用hcidump往往可以发现一些出错的线索,原因。 参数很多,基本上hcidump –X –V 就可以帮你获得详细的经过格式解析的数据包。 
    7.3.2 Hcitool 
    主要用hcitool来scan远端的设备,显示设备地址,名称等。 
    例如:Hcitool scan, hcitool inq 

    7.3.3 Sdptool 
    主要用来浏览远端设备SDP服务,或者管理本地的SDPD维护的数据库。 
    常用的应该就是查找远端设备的服务了 
    例如: 
    sdptool browse 00:02:72:B0:00:26 浏览地址为00:02:72:B0:00:26的设备所提供的服务 
    sdptool search 0x1112 00:02:72:B0:00:26 查找地址为00:02:72:B0:00:26的设备上的Headset Audio Gateway服务。 

    ./sdptool search 0x1112 00:02:72:B0:00:26 
    Class 0x1112 
    Inquiring ... 
    Searching for 0x1112 on 00:02:72:B0:00:26 ... 
    Service Name: Headset Audio Gateway 
    Service RecHandle: 0x1001d 
    Service Class ID List: 
    "Headset Audio Gateway" (0x1112) 
       "Generic Audio" (0x1203) 
    。。。 

    7.3.4 Hciconfig 
    这个就不用多说了,格式上很类似于ifconfig,用来设置HCI设备的参数 
    例如 
    hciconfig hci0 up 启动hci0接口 
    hciconfig hci0 iscan 使能位于hci0接口的蓝牙芯片的inquery scan模式(使得设备能被其它蓝牙设备发现) 


    8 杂项 
    8.1 使用Dbus-send进行测试 
    由于Bluez使用dbus进行进程间通讯,所以我们可以使用dbus-send命令直接发送命令进行一些查询,试验的工作。 
    Bluez每个Daemon或service所支持的Dbus接口API描述文本,可以在各自的目录下找到,例如Audio的API写在 audio/audio-api.txt中。 
    以Audio为例,可以参考 http://wiki.bluez.org/wiki/HOWTO/AudioDevices 中的描述 


    8.2 HCI、H4、USB、BCSP 之间的关系 
    个人理解,严格的说HCI和其它几种protocol并不是可以对比的同一层次的东西。 
    HCI protocol 并不考虑在实际传输载体以及其中的纠错等问题,只是一个抽象的传输层或叫做接口。USB,H3,H4等才是具体的transport layer(此外还有SD Transport layer)。HCI数据包需要附着在这些具体的Transport Layer的协议包中。 
    以BCSP为例,4种类型的HCI数据包各自使用了一个BCSP通道,做为这些通道的payload封装在BCSP的协议包里,需要通过TTY的lldsic层走一次,并由hci_uart模块做相应的封装工作。而BCSP还通过其它通道支持其它的一些自定的Protocol。BCSP作为一个具体的传输层协议,还支持了校验,同步等功能。 
    H4机制类似,SD和USB transport好像区别比较大一点。具体可以参考 Bluetooth Specification Volume 4. 

    8.3 BCSP数据包结构 
    HCI数据包的结构,在bluetooth的spec里面有详细定义,不过,CSR自己的BCSP,BCCMD等一系列协议,又添加了一堆的东西,其中,HCI数据包是作为BCSP的payload,而BCCMD又是作为HCI的payload,所以测试过程中,发觉要分析清楚bluez通过kernel最后到底往蓝牙芯片的串口发送了什么数据,特别是想要自己手工构建一串数据,着实要看上一堆spec,拼凑起来才能完成。 
    要具体学习分析一串命令,最好的办法,我能想到的就是修改bccmd的代码,将它传给串口的每一个字符串都打印出来,这样对照这spec看,事半功倍。 

    例如下面这条,是使用我修改后的bccmd指令。所做的操作是读取串口波特率的pskey: 

    ./bccmd.dbg -t bcsp -d /dev/ttyS1 psget -s 0x0 0x01be 

    cmd : 00 fc 13 c2 00 00 09 00 01 00 03 70 00 00 be 01 01 00 00 00 00 00 
    c0 d1 65 01 c8 00 fc 13 c2 00 00 09 00 01 00 03 70 00 00 be 01 01 00 00 00 00 69 a6 c0 

    在这里 HCI的数据包是第一行,具体解释一下: 

    头4个字节是HCI Head,其中 
    00 fc :整体代表这是一个制造商自定义的命令。 
    13 :HCI命令长度为0x13。 
    C2 :包的内容是唯一的一个BCCMD数据包。 

    后面是BCCMD的Head 
    00 00 :这是一个GetReq命令 
    09 00 :BCCMD的命令9个word长度,及18字节 
    01 00 :seqno, 包的顺序标记 包1 
    03 70 :varid 7003, 表示这是对PSKEY的操作 
    00 00 :状态标志 
    再下来是BCCMD的payload 
    be 01 :0x01be 波特率PSKEY的index 
    01 00 :该PSKEY的长度为1 
    00 00 :strore 为 00 
    00 00 :该PSKEY的值,这里是发送读命令,所以填0 

    第二行的数据是将HCI包封装在了BCSP数据包里: 
    前面部分:c0 d1 65 01 c8 : 
    C0 :是BCSP数据包的分割符 
    D1 :类型为可靠链接数据流,有CRC校验 
    65 01 :channel 5 ( HCI CMD ), 长度为0x16 
    C8 :包头的校验 
    后面部分:69 a6 c0: 
    69 a6 :整个BCSP包的CRC校验 
    C0 :分隔符 

    其它命令类似的分析可得。 

    如果只是希望看到HCI命令本身,也可以用hcidump来看。这是上面的读pskey操作通过HCI接口操作的dump: 

    < HCI Command: Vendor (0x3f|0x0000) plen 19 
    BCCMD: Get req: len 9 seqno 1 varid 0x7003 status 0 
    PSKEY: key 0x01be len 1 stores 0x0000 
    8.4 Hid / Serial / HF / OBEX 
    这几个比较常用的profile,还没测试哪。。。。。。谁给我买个蓝牙鼠标玩玩?! 

    8.5 总的遗留问题 
    整体上,PC上实现的自动识别,自动启动服务的一整套脚本,还没有仔细研究。

    原文url:http://blog.chinaunix.net/uid-20723653-id-1886857.html
    展开全文
  • Bluez HID分析

    2019-10-01 13:35:40
    本文分析了蓝牙bluez协议栈中HID协议的实现。 1. 基本概念 HID协议用于人机输入设备。Bluez中关于HID的实现代码在其根目录下的input目录。蓝牙规范中包含关于HID的profile,里面重用了USB中关于HID的一些协议...
  • 基于c语言的bluez广播

    2021-04-17 11:00:18
    1 摘要 运行平台:君正x2000 蓝牙:bluez5.54 编译器:mips-linux-gnu-gcc (gcc version 7.2.0 (Ingenic r4.0.0-gcc720 2018.02-28)) 编译主机:ubuntu 18.04 1.2 完整代码请参考 ... /*...
  • android bluez

    千次阅读 2010-07-06 17:15:00
    蓝牙的学习一共要跨hardware, linux kernel, framework, library, application layer.  ...Kernel: bluez协议栈, uart驱动, h4协议, hci,l2cap, sco, rfcomm  Library: libbluedroid.so 等  
  • blueZ应用编程

    千次阅读 2015-10-19 18:14:46
    主要摘取对于HCI、l2cap、sdp和rfcomm的一些应用编程。 关于hci   一、HCI层协议概述   ...详见bluez源码:lib/hci.h [html] view plaincopy /* Link Control */  #define
  • ubuntu蓝牙bluez

    千次阅读 2014-05-14 14:35:15
    Ubuntu和BlueZ BLE 1. Ubuntu12.04查看BlueZ版本: #dpkg --status bluez | grep '^Version:' Version: 4.98-2ubuntu7 ...
  • Bluez交叉编译-bluez4.101

    千次阅读 2014-08-18 10:38:53
    先进行环境变量的设置,告诉之后系统,我的库安装的目录 export PKG_CONFIG_PATH=/path/usr/lib/pkgconfig Expat ./configure --prefix=/path/usr --host=arm-linux-gnueabihf && make sudo make install...
  • bluez5.50源码分析

    千次阅读 2020-05-20 17:58:12
    网上查找资料,没有找到详细的bluez协议栈源码分析的文档,只能自己硬着头皮看bluez的源码,这里记下我自己对源码的理解,供后来者参考。
  • Kodi插件,以便设置具有bluez和组合接收器支持的Pulse-Audio接收器 想象一下,您的公寓有一个以上的房间(可能您不住在英国伦敦)。 位于起居室的计算机已连接至立体声和电视机。 它运行KODI,代表基础结构的媒体...
  • 交叉编译bluez-4.95

    千次阅读 2013-01-23 13:20:38
    编译环境: fedora 11 编译工具 arm-linux-gcc 4.1.1   编译bluez——第一次 从 http://www.kernel.org/pub/linux/bluetooth/ 下载bluez-4.95 ...[root@localhost bluez-4.95]# cd bluez-4.95
  • blueZ 笔记一

    2021-04-04 16:16:53
    //注册一个设备控制接口 //Registered interface org.bluez.MediaControl1 on path /org/bluez/hci0/dev_E0_62_67_38_2A_B7 g_dbus_register_interface(btd_get_dbus_connection(), device_get_path(dev), ///...
  • bluez5.50交叉编译

    千次阅读 2018-12-20 13:40:17
    编译环境:ubuntu14.04 ...这个文档说的很详细,但是编译的是低版本的bluez,下面是编译bluez5.50中遇到的问题 总体思路:交叉编译过程中遇到提示缺少的库就去下载编译,有些库不是必须的,可以...
  • BlueZ的交叉编译

    千次阅读 2012-06-17 10:06:20
    Sam几年前在写Linux下Bluetooth程序时,就基于BlueZ库。3年多过去了,没有再研究过Bluetooth。最近有个需求需要重新研究一下BlueZ中的实现。看看BlueZ,竟然也使用git管理了。再看看结构,变化也非常大。看来重新实现...
  • bluez蓝牙协议

    千次阅读 2013-03-12 14:51:41
    昨天看了一下介绍蓝牙协议文档,今天索性对照看了看kernel里的代码(bluez),这里记点笔记,还是继承了老毛病,只关注整体流程而忽略细节,先了解个大概,等真正需要时再仔细分析。 net/hci_core.c HCI在主机端...
  • BlueZ移植与配置

    2015-01-12 00:50:55
    在桌面环境下,使用Bluez应该已经没有太大的问题,本文的主要目的是介绍在嵌入式平台上,搭建和配置Bluez的各个Profile运行所需做的工作,讨论可能遇到的问题,介绍一些工具的使用和工作原理。因为本人的能力和测试...
  • 蓝牙bluez进行HCI编程

    2021-08-13 17:37:03
    我是一名嵌入式蓝牙工程师,平时大部分时间都在RTOS系统上进行蓝牙开发,最近因为工作需求要在Unix环境下搭建蓝牙开发环境,而我最熟悉的Unix系统莫过于Linux/Ubuntu,于是开始下载bluez的源代码,搭建蓝牙开发环境...
  • 在buildroot上交叉编译bluez-5.46

    千次阅读 2019-08-23 10:10:40
    环境说明: 编译平台:在Ubuntu16.04.1上使用buildroot-2017.08 ...整体思路:直接编译bluez包,根据buildroot的提示,一步一步添加编译bluez所需要的各个依赖 1、查看buildroot-2017.08/package/bluez5_utils/Con...
  • 交叉编译bluez-5.50

    千次阅读 2019-04-13 16:04:27
    环境:ubuntu 14.04 目标:arm板 交叉编译器:arm-poky-linux-gnueabi-gcc ..../configure --prefix=/home/用户/arm/bluez/usr --mandir=/home/用户/arm/bluez/usr/share/man --sysconfdir=/home/用户/arm/blu...
  • bluez蓝牙协议栈交叉编译移植教程
  • BLUEZ测试报告

    2013-12-18 01:22:00
    通过设置不同的敌军攻击目标(CCPoint),测试敌军的位置的变化,以及到达攻击目标是否有相应的攻击动作 敌军的攻击2 通过 通过设置不同的敌军攻击目标的x和y值,测试敌军的位置的变化,以及到达...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,732
精华内容 1,092
关键字:

bluez设置