显示标签为“Bluetooth”的博文。显示所有博文
显示标签为“Bluetooth”的博文。显示所有博文

2009年2月19日星期四

高速数据交换时的lock问题

作者:Sam(甄峰) sam_code@hotmail.com

Sam在作Bluetooth Remote Control程序时,遇到一个问题。
手柄每5ms发送一个数据包。
而Sam程序是这样设计的:
在手柄连接时,BTRC创建Read thread. 在Read thread中会读取手柄数据并解析包。解析结果放到一个buffer(结构体)中。
另外一边,应用程序会使用BTRC的read接口去读取这个buffer的内容。这个应用程序应该会用一个thread去不断的调用BTRC的read接口去读取。


方案一:
Sam在解析包部分向buffer中写东西时会加锁,写完会解锁。BTRC的read接口读取buffer时加锁,读完会解锁。
但这样有个后果:
1. 如果 双方都是全速运行,也就是说:如果双方都不添加sleep.则会出现:程序先进入BTRC的read接口读取buffer很多次(20次)左右。这个时候,BTRC的使用BTX去读取并解析然后写入buffer的程序的lock无法进入。
然后,在某一时刻,BTRC使用BTX区读取并解析然后写入buffer的程序又集中运行很多次(15-20次)。
这就造成上层程序使用BTRC的读取接口读到的解析后的数据完全不正确。

2. 如果上层程序调用BTRC的读取接口时作一个延迟,比如4ms.
这样的话,因为程序使用BTRC 的读取接口有一个延迟,所以BTRC是用BTX去读取并解析然后写入buffer的程序有更大机会去写入。但会造成写入多个才能够读取一次的情况。例如:A包过来了,BTRC使用BTX去读取,解析后写入buffer. A+1包又过来了,然后BTRC使用BTX读取时,BTRC的读接口来读buffer,但buffer已经被lock了。所以只能等下一次去读取了。这造成只能读取第二个包。所以还是会造成丢包。

3. 如果去掉lock,并全速运行:
现象与情况一相同。


方案二:
将BTRC的读接口改造,每次不是再去读当前的一份数据,而是如下去做:
用返回值表示从上次读取到此次读取这段时间内,buffer中新的数据包个数。参数为指针的指针。指向数据头。





注[1]:Sam有个感觉,在不同速度的CPU上,尤其是一些嵌入式平台中,thread的schedule速度好像比较慢,所以在不同thread之前的切换速度比较慢。像在X5平台上,BTRC的Read接口去read buffer, 竟然只能读到30%-40%的包。Canmore可以读到70%的包。而PC上则可以读到100%的包,还有重复读到的。





2009年2月18日星期三

Hi3110 EV 100平台上Bluetooth的移植

作者:Sam(甄峰) sam_code@hotmail.com


Sam想把手柄库移植到海思 Hi3100 EV 100平台上去。先进行评估如下:



1. Hi3100 EV 100为ARM平台。
2. Hi3100 EV 100使用uclibc。
这两个差异决定了需要从bluetooth 底层库开始移植。


一:
Sam从linux kernel中编译出了bluetooth.ko, hci_usb.ko, l2cap.ko, hidp.ko。

二:
下载新版bluez.
http://www.bluez.org/


三:
交叉编译bluez:
交叉编译时发现,新版bluez要基于D-BUS等库。所以Sam暂时使用bluez 1.0版本。
#cd bluez-libs-2.12
# CC=/opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc ./configure --host=arm-linux
#make clean;make
#cd bluez-libs-2.12/src/.libs
看到libbluetooth.so就在这里。


四:
交叉编译BTX:
在BTX Makefile中,
CC := /opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc
CFLAGS := -D_SHOW -D_DEBUGTIMEOUT -Wall -I../include -I/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/
LFLAGS := -L./ -L../resource -L/opt/hisilicon/toolchains/sysroot-uclibc-soft/usr/lib
编译libBTX.so和BTX_Test成功。
其中libBTX.so编译选项如下:
$(CC) $(CFLAGS) $(LFLAGS) -fPIC -shared BTX.o -o libBTX.so -lbluetooth

但奇怪的是,在Hi3110 EV100平台上,运行BTX_Test会出现如下信息:
Can't modify .//libBTX.so's text section. Use GCC option -fPIC for shared objects, please.

Sam只好把BTX并入程序中。


五:
编译Bluetooth_remote:
CC := /opt/hisilicon/toolchains/arm-uclibc-linux-soft/bin/arm-uclibc-linux-gcc CFLAGS := -D_SHOW -D_DEBUGTIMEOUT -Wall -I../include -I/opt/hisilicon/toolchains/arm-uclibc-linux-soft/include/ LFLAGS := -L./ -L../resource -L/opt/hisilicon/toolchains/sysroot-uclibc-soft/usr/lib
并将BTX.c编译进去。
编译成功。
但运行时会有问题,反应比较慢。Sam还在查。

2008年12月5日星期五

Bluetooth Sniff 详解

作者:Sam(甄峰) sam_code@hotmail.com

前面讲了Blueooth Remote Control发送Sniff request,要求每5ms发送一个数据包。这篇详细介绍一下Bluetooth Sniff.

在Sniff mode, Slave活动的占空比可能会降低。如果一个ACL逻辑传输的Slave 处于Active Mode,它将在在每个Master 发送ACL Slot中监听。除非link处理为分散连接或者在Hold Mode。
在Sniff Mode,Slave监听的次数会减少。于是, Master 只在指定的Slot中传送数据给Slave.
Sniff 定位点间隔由T(Sniff)指定。

也就是说:Sniff Mode. Slave只在固定间隔中的某个Master 传输Slot监听。

Sam在读到这里,不禁想到一个问题。Bluetooth Remote Control在于Linux 连接时,是作为Slave连接的。Sniff Mode下,只是可以约定Slave(手柄)每5ms去listen data. 但我们的手柄却每5ms去发送数据给主机。这好像讲不通。
Ray回答说:因为Per Bluetooth spec, slave can not send data to master unless master poll slave。就是说:在Bluetooth协议中,Slave不能主动发送数据给Master除非Master向它发送Poll。
So, the slave have to listen to Master first, when it receive the poll or other packet from master, then it can send thedata to master。于是,双方约定Sniff Time后,Master 每5ms发送一个Poll。则Slave就可以发送数据给主机了。这就做到了手柄每5ms发送一个数据包给主机。



2008年12月4日星期四

使用BlueZ Interface修改ACL连接Supervision timeout

作者: Sam (甄峰) sam_code@hotmail.com

背景:
Sam在做Bluetooth USB Dongle与Bluetooth Remote Control连接的程序时。注意到当手柄断电时,需要20s时间才能够接收到Disconnect Event.(Error Code: timeout). Ray觉得这个时间太久。要求修改到5-6s.

实际上,因为Bluetooth是无线连接设备,所以有时会连接不上,Supervision的作用就是,ACL连接双方约定多长时间没有联系上算断开。

在读取文档:Host Controller Interface Functional Specification文档中HCI Commands and Events-〉Controler & BaseBand Command->Read Link Supervision Timeout+Write Link Supervision Timeout中。发现可以修改这个timeout.

1. 读取Supervision Timeout.
Command: HCI_Read_Link_Supervision_Timeout
OCF:0x0036
Command Parameters:Connection_Handle
Return Parameters: Status, Connection_Handle, Link_Supervision_Timeout
这个Command用来读取Link supervision timeout . 这是个基于ACL Connect的Command.

Command Param: Connect_Handle

Return Parameters:
Status: 0x00 : Success。
other: ErrorCode
Connection_Handle:ACL Connect Handle
Link_Supervision_Timeout:
0x00: No Link_Supervision_Timeout
N : BaseBand slots.一个BaseBand Slot等于0.625个ms.


2. 设置Supervision Timeout.
Command: HCI_Write_Link_Supervision_Timeout
OCF:0x0037
Command Parameters: Connection_Handle, Link_Supervision_Timeout
Return Parameters: Status, Connection_Handle
这个命令用来设置Supervision Timeout.

2008年11月30日星期日

BTX初建

作者: Sam(甄峰) sam_code@hotmail.com

1. 因为BTX是Sam设计的整个架构的基础,需要非常稳定。所以Sam决定每个接口都经过大量测试。
1.1. 单独的接口测试,包括[in]参数的边界测试,超出范围测试,[out]参数的参数为空,buffer不足够测试等。接口多次调用测试。
1.2. 多个接口组合测试,各个接口的各种常规和非常规组合测试。有意使用错误组合和参数测试。


2. BTX的具体实现:
2.1 BTX_Rel BTX_Init(uint16_t dev_id)
这个function 将 USB Bluetooth Dongle up起来。类似#hciconfig hci0 up
需要测试:
2.1.1. Bluetooth driver, protocol没有insmod时的反应。
2.1.2. Bluetooth USB Dongle没有插入时的反应。
2.1.3. 参数dev_id=0 (第一个USB Dongle) 和 dev_id = 1(第二个USB Dongle)时的反应。
2.1.4. 各种状况下 /proc/uid/fd/下察看终端。看是否有该释放的handle而没有释放的情况发生。注[1]。
常见的问题:打开hci socket后,遇到error,没有关闭socket就推出function.这样就会遗留一个socket.

2.2 BTX_Rel BTX_GetLocalDeviceInfo(uint16_t dev_id, bdaddr_t* pBdaddr, char* pBdName)
这个function的到本地Bluetooth 信息,包括 bdaddr, device name等。如果返回值不为BTX_SUCCESS。则说明Bluetooth USB Dongle没有Ready.
需要测试:
2.2.1:参数dev_id=0和dev_id=1时。
2.2.2:参数pBdaddr,pBdName没有分配实际的内存时,会出现Segmentation fault
Sam虽然在function 实现中添加了if(pBdaddr == NULL pBdName == NULL)...
但如果使用时不讲pBdaddr初始化为NULL。则它还是有一个值的。还是会造成Segmentation fault.这个问题等待未来解决。
2.2.3: 在调用此函数之前没有调用 BTX_Init():会返回BTX_DONGLE_NO_READY。但用perror()输出protocol未insmod.

2.3 BTX_Rel BTX_Scan_Device(uint16_t dev_id, unsigned int* pNum_rsp, bdaddr_t** ppBdaddr, uint16_t time);




注[1]:
在所有涉及到打开句柄或者socket的程序中,都应该察看 /proc/uid/fd中的信息。察看是否正常建立和销毁了句柄。

2008年11月27日星期四

Linux 下Bluetooth开发包--BTX的开发的背景介绍

作者:Sam(甄峰) sam_code@hotmail.com

BTX背景介绍:
Sam所在公司需要Linux下与Bluetooth Game Control连接,Linux为操作系统的嵌入式设备使用USB Bluetooth Dongle与Bluetooth Game Control连接,并收发数据。Sam评估后决定用以下结构:

1. HID,L2cap, hci 这三层protocol使用Linux下官方Bluetooth 协议栈--BlueZ. Bluez详细介绍见:[注1]

2. Bluetooth USB Dongle的驱动使用BlueZ提供的hci_usb 驱动。

3. 使用BlueZ提供的函数库和应用程序接口,创建一个针对手柄的连接库。即为BTX。这个手柄连接库提供的接口如下:
1>.Bluetooth USB Dongle初始化
2>.得到Bluetooth USB Dongle信息。
3>.查找周边Bluetooth Device设备。
4>.连接指定手柄。
5>.断开指定手柄。
6>.读取数据。
7>.写数据。
其中连接手柄是建立2个l2cap连接。PSM=0x11. 0x13.
这一层是提供给公司与所有手柄连接的库。任何一代手柄都将使用这个库作基本连接。

4. 使用BTX库和接口,开发一套针对某一代手柄所使用的库。BTRC--Bluetooth Remote Control.
因为每一代手柄连接过程中都有自己独特的加密与验证过程。并且数据格式也各不相同。所以使用这一层将这些不同与上层程序隔绝开来。

5.使用BTRC库,开发一套包含不同动作分析 的库供更上层程序使用。



注[1]:
BlueZ介绍:
Linux OS开放的bluetooth 协议栈主要包括IBM公司的BlueDrekar, Nokia公司的Affix, Axis公司的OpenBT和官方的协议栈BlueZ.
Bluetooth协议栈BlueZ分为两部分:内核代码和用户态程序及工具集。
其中内核代码由BlueZ核心协议驱动程序组成;Bluetooth协议实现在内核源代码 /net/bluetooth中。包括hci,l2cap,hid,rfcomm,sco,SDP,BNEP等协议的实现。驱动程序放在 /driver/bluetooth中,包含Linux kernel对各种接口的Bluetooth device的驱动。例如:USB接口,串口等。
用户态程序及工具集包括应用程序接口和BlueZ工具集。BlueZ提供函数库以及应用程序接口,便于程序员开发bluetooth应用程序。BlueZ utils是主要工具集,实现对bluetooth设备的初始化和控制。

2008年10月29日星期三

Bluetooth HCI Event的处理

作者:Sam(甄峰) sam_code@hotmail.com

1.当Bluetooth USB dongle插入USB接口时,driver/bluetooth/hci_usb.c中probe程序被调用。probe会调用hci_register_dev()--〉tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
将hci_rx_task() 连上rx_task->func.
2.同样在Bluetooth USB dongle插入时,hdev->open = hci_usb_open; 当USB Dongle被UP时,这个function被调用。它调用hci_usb_intr_rx_submit(),它注册URB完成处理程序,hci_usb_rx_complete。
当有URB出现时, hci_usb_rx_complete ->__recv_frame() ->hci_recv_frame()->hci_sched_rx(hdev);->hci_sched_rx() ->tasklet_schedule(&hdev->rx_task)
tasklet_schedule():
将这个tasklet放在 tasklet_vec链表的头部,并唤醒后台线程ksoftirqd。当后台线程ksoftirqd运行调用__do_softirq时,会执行在中断向量表softirq_vec里中断号TASKLET_SOFTIRQ对应的tasklet_action函数,然后tasklet_action遍历 tasklet_vec链表,调用每个tasklet的函数完成软中断操作。
也就是调用hci_rx_task()

也就是:当有URB时,会最终调用到hci_rx_task()

当包类型为 HCI Event时,则调用hci_event_packet()
在hci_event_packet()中,会判断是何种Event.并处理之。

下面以HCI_EV_DISCONN_COMPLETE为例子看Event如何被处理。这个Event表明连接断掉了。reason则表示断掉的原因。

HCI_EV_DISCONN_COMPLETE Event使用处理程序hci_disconn_complete_evt来处理,下面详细讲解:

1.hci_dev_lock():使用自旋锁得到资源。

2.hci_conn_hash_lookup_handle():从连接队列中找出对应连接。注:当有ACL或SOC连接时,这个连接就会被加入连接链表。

3.这个连接的state改为BT_CLOSED。

4.hci_proto_disconn_ind(): 调用全局变量hci_proto中的disconn_ind()。

注:在L2cap protocol insmod时,hci_register_proto()被调用。全局变量hci_proto则被赋值。disconn_ind=l2cap_disconn_ind;

所以当HCI层得到Disconnect时,会通知l2cap层做删除channel等工作。

且因为state改为BT_CLOSED. 所以在l2cap 层调用poll(最终调用bt_sock_poll)时会发现POLLHUP被置位。

这样l2cap应用程序就能够知道连接已经断开了。

2008年10月28日星期二

Linux BlueZ 下 Inquiry的实现

作者:Sam(甄峰) sam_code@hotmail.com

前面讲了使用 ioctl发送HCI Command的方法。但HCI protocol还有一些Command,比如Inquiry等,他们则不需要使用ioctl来发送。blueZ 直接提供了支持,虽然他们的最终都是用同样办法实现的。

在应用程序中:
hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
这样需要包含blueZ 头文件如下:
#include //BTPROTO_HCI
#include //struct hci_dev_info
#include //hci_devid()
#include //l2cap
#include //hidp

hci_inquiry()的实现在 bluez-lib-xxx/lib/src/hci.c中。
哈哈,发现其实最终也是使用:
ioctl(dd, HCIINQUIRY, (unsigned long) buf);
这样,其实就和上一篇对应起来了。所有HCI Command最终都使用ioctl来发送。

2008年10月27日星期一

Linux下Bluetooth HCI Command的实现

作者:Sam(甄峰) sam_code@hotmail.com

Linux下写Bluetooth程序,首先接触到的就是使用HCI Command来设置Bluetooth Modules(USB Bluetooth dongle)。那这些HCI command在blueZ中是如何实现的呢?举例说明。


if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < dev_id =" dev_id;">open(). 这个open function也同上,在USB dongle probe时给定。在hci_usb.c hci_usb_probe()中。=hci_usb_open;
hci_usb_open()中则设置hci_dev->flag=HCI_RUNNING.
并向USB Core提交一个中断URB。当有中断URB被USB Core出来完毕后,调用hci_usb_rx_complete()。 它则调用__recv_frame()。它处理所有USB Dongle通过中断URB送来的数据。
4.利用hci_init_req()发送request.
hci_init_req()调用hci_send_cmd()发送命令。
hci_send_cmd() 则调用skb_queue_tail()将命令skb添加到 发送队列。再调用hci_sched_cmd()去调用发送命令去发送。发送命令为:hci_cmd_task()
hci_cmd_task()-〉hci_send_frame()-〉hci_send_frame()-〉hdev->send(skb);=hci_usb_send_frame();发送命令给USB bluetooth dongle.

HCI就是通过这个途径发送Command到Dongle的。所以HCI Socket其实就是PC和Dongle之间的一个通道。注意,不是PC和远端bluetooth 设备的通道,而是本地Dongle。