Ubuntu系统(bluez)蓝牙调试
来源:互联网 发布:淘宝怎么出售禁售商品 编辑:程序博客网 时间:2024/06/03 18:14
前言
现在调试的Ubuntu、debian系统,蓝牙上层的协议使用bluez,蓝牙的移植与bluedroid略有不同。本文主要介绍Ubuntu(蓝牙移植上debian与Ubuntu是一样的)系统下蓝牙移植的相关知识,并给出移植指导。涉及的知识点有bluez下蓝牙的驱动、hciattach的作用、蓝牙电源的控制、蓝牙移植修改点。
1 Bluez下内核蓝牙框架简介
使用Bluez时,需要内核提供一系列的socket接口来操作蓝牙,内核中蓝牙的框架如图1所示。蓝牙框架分成两部分:蓝牙socket部分及蓝牙驱动部分。蓝牙socket部分负责管理提供给bluez的socket,并包含L2cap层的功能;蓝牙驱动包含hci层协议及蓝牙硬件接口的管理。蓝牙socket部分与蓝牙驱动通过hci_core来连接。从Bluez下移植蓝牙方面看,只关心两个地方,一个是蓝牙驱动的移植,另一个是bluez的工具集中的hciattach工具(使用uart接口的蓝牙才需要这部分),
图1 内核中蓝牙框图
对比bluedroid与bluez在蓝牙移植方面的差异,最大的不同就是hci和L2cap层所处的位置,在bluedroid中,hci和L2cap层放在bluedroid中,是在内核之上。而bluez中,hci和L2cap层不属于bluez中的代码,而是放到内核里。
2 内核中的蓝牙
在Ubuntu系统下,Bluez的蓝牙驱动负责hci协议的处理、与蓝牙硬件交互数据、注册hci接口供蓝牙socket部分使用。
2.1 注册hci_core接口
不管是uart接口还是usb接口的蓝牙,都是通过hci_register_dev函数向hci_core层注册接口,下面为uart接口及usb接口蓝牙向hci_core层注册例子:
kernel\drivers\bluetooth\hci_ldisc.c hdev->bus = HCI_UART; hci_set_drvdata(hdev, hu); hdev->open = hci_uart_open; hdev->close = hci_uart_close; hdev->flush = hci_uart_flush; hdev->send = hci_uart_send_frame; SET_HCIDEV_DEV(hdev, hu->tty->dev); if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags)) hdev->dev_type = HCI_AMP; else hdev->dev_type = HCI_BREDR; if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) return 0; if (hci_register_dev(hdev) < 0) //uart接口蓝牙注册
kernel\drivers\bluetooth\rtk_btusb_8723bu.c HDEV_BUS = HCI_USB; data->hdev = hdev; SET_HCIDEV_DEV(hdev, &intf->dev); hdev->open = btusb_open; hdev->close = btusb_close; hdev->flush = btusb_flush; hdev->send = btusb_send_frame; hdev->notify = btusb_notify;#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 4, 0) hci_set_drvdata(hdev, data);#else hdev->driver_data = data; hdev->destruct = btusb_destruct; hdev->owner = THIS_MODULE;#endif…… err = hci_register_dev(hdev); // usb接口蓝牙注册
Hci_core向蓝牙驱动传递数据通过hdev->send接口,而蓝牙驱动接收到数据后直接调用hci_core Export的hci_recv_frame、hci_recv_fragment接口提交数据。
在使用hci_register_dev注册接口的时候,hci_core会在rfkill下注册一个RFKILL_TYPE_BLUETOOTH类型的设备,名称为hciX。在Ubuntu系统中,只要发现rfkill下有注册RFKILL_TYPE_BLUETOOTH类型设备,就认为存在蓝牙设备,桌面上就会显示蓝牙图标,后面对蓝牙的开关操作就是通过rfkill下的接口。
2.2 蓝牙驱动的移植
现在调试过的蓝牙有uart接口和usb接口的,这两种接口的驱动是完全不同的,下面分别介绍。
2.2.1 USB接口蓝牙
对于usb接口的蓝牙,只要给蓝牙上电,usb枚举到蓝牙设备后,就会调用蓝牙驱动的probe函数,在该函数中就会向hci_core注册接口。
kernel\drivers\bluetooth\rtk_btusb_8723bu.c static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id){…… hdev = hci_alloc_dev(); if (!hdev) { rtk_free(data); data = NULL; return -ENOMEM; } HDEV_BUS = HCI_USB; data->hdev = hdev; SET_HCIDEV_DEV(hdev, &intf->dev); hdev->open = btusb_open; hdev->close = btusb_close; hdev->flush = btusb_flush; hdev->send = btusb_send_frame; hdev->notify = btusb_notify;…… err = hci_register_dev(hdev);
然后在hdev->open 的时候,在btusb_open中进行fw下载和参数配置的工作,这时蓝牙就可以正常工作了。下载的fw和配置文件通过内核的int request_firmware(const struct firmware **fw, const char *name, struct device *device)函数获取,对于需要获取的文件,只需要提供文件名,该函数会自动搜索系统部分路径,其中就包含“/lib/firmware/”,所以只要把fw及配置文件放到“/lib/firmware/”目录下即可。同时usb保证了传输的可靠性,所以也不需要什么h4、h5协议了。
从上面可以看出,对于usb接口蓝牙的移植,只需要保证两步工作就可以了:
1、 蓝牙usb功能驱动的移植;
如rtl8723bu的蓝牙,bluez与bluedroid下使用的驱动是一样的,但有一个定义是区分用于bluez还是bluedroid的。在rtk_btusb_8723bu.h文件如下代码中:
#ifndef CONFIG_PLATFORM_UBUNTU#define CONFIG_BLUEDROID 1 /* bleuz 0, bluedroid 1 */#else#define CONFIG_BLUEDROID 0#endif
只要定义了CONFIG_PLATFORM_UBUNTU即可。
该定义在kernel\arch\arm\configs\下config文件中配置,
CONFIG_PLATFORM_UBUNTU=y
2、 把fw及配置文件打包到“/lib/firmware/”目录下;
如gb5_wxga板子的rtl8723bu模组,只需要把rtl8723b_fw、rtl8723bu_config文件放到“\ rootfs\lib\firmware\”目录下即可。
2.2.2 UART接口蓝牙
不像usb接口蓝牙,可以直接向usb驱动注册蓝牙的功能驱动,后面就等着probe被调用就可以了。Uart接口蓝牙,使用那个uart口依赖硬件,同时也没办法像usb一样向串口驱动注册一个功能驱动等待probe,uart是没有枚举的过程的。所以uart接口蓝牙就需要应用层把使用的串口通知hci_core层,同时uart接口蓝牙的fw download及config配置工作也放到了应用层,这些工作都是由hciattach来实现。Hciattach的流程下一节介绍,这里介绍uart接口蓝牙驱动移植需要做的工作。相比图1,图2描述的uart驱动更接近代码结构。
图2 uart接口蓝牙驱动框图
从图2可以看到,串口的使用有一个切换的过程,在初始化的时候,由hciattach使用串口,初始化完成后,把串口切换给hci使用,hci负责与串口交互蓝牙数据,中间还经过了h4/h5协议层,驱动层跟移植相关只有h4/h5协议,若h4/h5使用的是内核自带的协议,那驱动层就不需要做任何的工作。
以rtl8723bs为例,需要使用rtk修改过的h5协议,就需要在kernel\drivers\bluetooth\目录下增加hci_rtk_h5.c文件,hci_ldisc.c增加对hci_rtk_h5.c的init及deinit,由于内核中注册hci协议会使用一个id号,相同id的协议不能再注册,内核中已经有的hci_h5.c与 hci_rtk_h5.c使用的是相同的id号,所以内核中需要屏蔽hci_h5.c的注册。
kernel\drivers\bluetooth\hci_ldisc.cstatic int __init hci_uart_init(void)……#ifdef CONFIG_BT_HCIUART_H4 h4_init();#endif#ifdef CONFIG_BT_HCIUART_BCSP bcsp_init();#endif#ifdef CONFIG_BT_HCIUART_LL ll_init();#endif#ifdef CONFIG_BT_HCIUART_ATH3K ath_init();#endif#ifdef CONFIG_BT_HCIUART_3WIRE h5_init();#endif//Realtek_add_start //add realtek h5 support #ifdef CONFIG_BT_HCIUART_RTKH5 rtk_h5_init();#endif//Realtek_add_end ……static void __exit hci_uart_exit(void)…… #ifdef CONFIG_BT_HCIUART_RTKH5 rtk_h5_deinit();#endifkernel\drivers\bluetooth\ hci_uart.h//Realtek_add_start#ifdef CONFIG_BT_HCIUART_RTKH5int rtk_h5_init(void);int rtk_h5_deinit(void);#endifkernel\drivers\bluetooth\ hci_rtk_h5.cstatic struct hci_uart_proto h5 = { .id = HCI_UART_3WIRE, // 与h5_init注册是相同的id .open = h5_open, .close = h5_close, .enqueue = h5_enqueue, .dequeue = h5_dequeue, .recv = h5_recv, .flush = h5_flush};int rtk_h5_init(void){ int err = hci_uart_register_proto(&h5);
屏蔽内核中现有的hci_h5.c修改方式为:kernel\arch\arm\configs\目录下config文件修改下面两行。# CONFIG_BT_HCIUART_ATH3K is not set //屏蔽BT_HCIUART_ATH3K
CONFIG_BT_HCIUART_RTKH5=y // 打开BT_HCIUART_RTKH5
Hci_ldisc通过tty_register_ldisc(N_HCI, &hci_uart_ldisc)向串口注册HCI line discipline,当hciattach通过ioctl把串口切换到HCI line discipline时,hci_ldisc就可以与串口通信了。
kernel\drivers\bluetooth\ hci_ldisc.cstatic int __init hci_uart_init(void){ static struct tty_ldisc_ops hci_uart_ldisc; int err; BT_INFO("HCI UART driver ver %s", VERSION); /* Register the tty discipline */ memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc)); hci_uart_ldisc.magic = TTY_LDISC_MAGIC; hci_uart_ldisc.name = "n_hci"; hci_uart_ldisc.open = hci_uart_tty_open; hci_uart_ldisc.close = hci_uart_tty_close; hci_uart_ldisc.read = hci_uart_tty_read; hci_uart_ldisc.write = hci_uart_tty_write; hci_uart_ldisc.ioctl = hci_uart_tty_ioctl; hci_uart_ldisc.poll = hci_uart_tty_poll; hci_uart_ldisc.receive_buf = hci_uart_tty_receive; hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup; hci_uart_ldisc.owner = THIS_MODULE; if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) { BT_ERR("HCI line discipline registration failed. (%d)", err); return err; }
3 Hciattach的处理流程
只有uart接口的蓝牙才需要hciattach工具,hciattach的作用为配置串口,下载fw及config文件,把串口切换给hci_ldisc使用。
Bluez带有hciattach的源码,框架也比较清晰,对很多厂家都有支持,但实际调试realtek及boardcom的模组时,Bluez自带的hciattach都是不能使用的,realtek及boardcom对hciattach有特殊的修改,主要是针对fw和config的下载部分。但hciattach的作用及流程与Bluez自带的hciattach是一样的。Hciattach的流程如图3所示。
图3 hciattach初始化流程
Hciattach的流程比较简单,从现在Ubuntu及debian系统的设计看,hciattach都是开机时就运行,一直到关机时才结束。
Hciattach的移植涉及下面几个地方:
1、 把hciattach可执行文件放到bin目录下;
以lemaker板子为例:
把hciattach_rtk放到\rootfs\usr\sbin\目录下;
2、 把fw及config文件打包进系统,放置的路径由hciattach open fw确定
以使用rtl8723bs模组:
把rtl8723b_fw、rtk8723_bt_config放到
\rootfs\lib\firmware\rtl8723bs\目录下;
3、 加入hciattach的启动与退出控制:
把 bluetooth.conf文件放到\rootfs\etc\init\目录下。
bluetooth.conf文件内容description "bluetooth daemon"start on started dbusstop on stopping dbusenv UART_CONF=/etc/bluetooth/uartenv RFCOMM_CONF=/etc/bluetooth/rfcomm.confexpect forkrespawnexec /usr/sbin/bluetoothdpost-start script #[ "$VERBOSE" = no ] && redirect='>/dev/null 2>&1' || redirect= # start_uarts() #if [ -x /usr/sbin/hciattach ] && [ -f $UART_CONF ]; #then # grep -v '^#' $UART_CONF | while read i; do # eval "/usr/sbin/hciattach $i $redirect" || : # done #fi exec hciattach_rtk -n -s 115200 /dev/ttyS2 rtk_h5 & # start_rfcomm() if [ -x /usr/bin/rfcomm ] && [ -f $RFCOMM_CONF ] ; then # rfcomm must always succeed for now: users # may not yet have an rfcomm-enabled kernel eval "/usr/bin/rfcomm -f $RFCOMM_CONF bind all $redirect" || : fiend scriptpost-stop script # stop_uarts() logger -t bluez "Stopping uarts" kill
bluetooth.conf中有脚本,在开机、关机时运行,这里:
开机运行:exec hciattach_rtk -n -s 115200 /dev/ttyS2 rtk_h5 &
关机运行:killall hciattach_rtk >/dev/null 2>&1 || :
4 蓝牙电源的管理
前面的文档中一直没有提到蓝牙的电源是怎么控制的。在蓝牙的电源控制方面,Ubuntu及debian系统都没有做很好的处理,从现在的蓝牙图形界面应用看,这两个系统中,默认蓝牙是一直有电的并且开机时就打开,并没有考虑关闭蓝牙的时候把蓝牙断电。
现在我们使用usb接口蓝牙,对于插拨的usb蓝牙,不需要考虑电源,只要插上就有电了,对于焊在板子上的蓝牙,由于没有增加对蓝牙上电的操作,所以要在wifi打开的情况下(wifi上电了,蓝牙也就上电了)才能使用蓝牙。
对于sdio接口的蓝牙,以rtl8723bs为例,修改了kernel\net\rfkill\目录下的rfkill-actions_8723bs.c文件,在这个文件里不再注册rfkill接口,而是修改为在加载驱动是给蓝牙上电,卸载驱动时断开蓝牙电源。
至于为什么不保留rfkill-actions_8723bs.c在rfkill中的接口,通过rfkill来控制电源,是由于rfkill-actions_8723bs.c注册进rfkill的类型也是RFKILL_TYPE_BLUETOOTH,与hci注册的类型是一样的,这并没有冲突,但由于Ubuntu的蓝牙图形界面操作蓝牙打开、关闭时,同时都会把RFKILL_TYPE_BLUETOOTH类型的节点打开、关闭,这里上电的延时就没法保证,而且并没有调用hciattach进行蓝牙的初始化,对于串口蓝牙就没法使用了。
若重写蓝牙图形操作界面时,可以采用下面的方案进行电源的管理。
1、 usb接口蓝牙:
在rfkill中增加一个节点用于控制蓝牙上、掉电,类型为
RFKILL_TYPE_BLUETOOTH,但名称修改为bt_power;
蓝牙打开的操作:
A) 找到rfkill下类型为RFKILL_TYPE_BLUETOOTH名称为bt_power的节点,给蓝牙上电;
B) 延时(根据实际调整);
C) 找到rfkill下类型为RFKILL_TYPE_BLUETOOTH名称为hciX的节点,打开蓝牙;
蓝牙关闭的操作:
A) 找到rfkill下类型为RFKILL_TYPE_BLUETOOTH名称为hciX的节点,关闭蓝牙;
B) 延时(根据实际调整);
C) 找到rfkill下类型为RFKILL_TYPE_BLUETOOTH名称为bt_power的节点,给蓝牙断电;
2、 Uart接口蓝牙
在rfkill中增加一个节点用于控制蓝牙上、掉电,类型为
RFKILL_TYPE_BLUETOOTH,但名称修改为bt_power;
蓝牙打开的操作:
A) 找到rfkill下类型为RFKILL_TYPE_BLUETOOTH名称为bt_power的节点,给蓝牙上电;
B) 延时(根据实际调整);
C) 启动hciattach完成蓝牙的初始化并切换串口给hci使用;
D) 找到rfkill下类型为RFKILL_TYPE_BLUETOOTH名称为hciX的节点,打开蓝牙;
蓝牙关闭的操作:
A) 找到rfkill下类型为RFKILL_TYPE_BLUETOOTH名称为hciX的节点,关闭蓝牙;
B) 关闭hciattach;
C) 延时(根据实际调整);
D) 找到rfkill下类型为RFKILL_TYPE_BLUETOOTH名称为bt_power的节点,给蓝牙断电;
上面方案要求图形界面对不同接口蓝牙进行不同的操作,统一性不是很好,可以把操作部分放到脚本中实现,脚本根据实际硬件修改,而图形界面只需要调用脚本,不需要关心脚本的操作内容,这样实现会更好一些。
- Ubuntu系统(bluez)蓝牙调试
- Ubuntu系统(bluez)蓝牙调试
- ubuntu蓝牙bluez
- 如何查看Ubuntu 蓝牙bluez 软件版本
- 蓝牙 Bluez
- linux蓝牙编程 bluez
- bluez蓝牙协议
- 蓝牙BlueZ的移植
- Android蓝牙栈bluez使用方法
- ARM USB蓝牙,Bluez 移植。
- 基于BlueZ的C语言蓝牙编程
- 基于BlueZ的C语言蓝牙编程
- 蓝牙驱动及Bluez使用流程分析
- 基于BlueZ的C语言蓝牙编程
- 使用BlueZ与Ofono实现蓝牙应用
- bluez/obex蓝牙协议栈编程
- arm上交叉编译蓝牙bluez
- 基于BlueZ的C语言蓝牙编程
- 分布式memcache实现web集群session同步
- HDU 1102 Constructing Roads【简单最小生成树,Prime算法+Kruskal算法】
- eclipse中的.project 和 .classpath文件的具体作用
- C++: 运算符重载(一)、友元函数、链式编程支持
- KMP字符串模式匹配详解
- Ubuntu系统(bluez)蓝牙调试
- vector和iterator的使用,排序,打印,挑选偶数
- MySQL之游标
- Java心得体会之多态
- 115.leetcode Distinct Subsequences(hard)[动态规划]
- 初识virtual memory
- 编写一个存储过程,根据学号、课程号 按成绩的20%进行加分,如果增加后的分数大于100,则取消加分。同时在存储过程中返回增加后的成绩。
- 玩无限消乐的设计示例(基础篇)填充动画元素操作
- Java----Object