赞
踩
关键词:蓝牙blueZ UART HCI_UART H4 HCI L2CAP RFCOMM
版本:基于android4.2之前版本 bluez
内核:linux/linux3.08
系统:android/android4.1.3.4
作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)
欢迎指正错误,共同学习、共同进步!!
Android bluetooth介绍(一):基本概念及硬件接口
Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
Android bluetooth介绍(四): a2dp connect流程分析
一、Android Bluetooth Architecture蓝牙代码架构部分(google 官方蓝牙框架)
Android的蓝牙系统,自下而上包括以下一些内容如上图所示:
1、串口驱动
Linux的内核的蓝牙驱动程、Linux的内核的蓝牙协议的层
2、BlueZ的适配器
BlueZ的(蓝牙在用户空间的函式库)
bluez代码结构
Bluetooth协议栈BlueZ分为两部分:内核代码和用户态程序及工具集。
(1)、内核代码:由BlueZ核心协议和驱动程序组成
Bluetooth协议实现在内核源代码 kernel/net/bluetooth中。包括hci,l2cap,hid,rfcomm,sco,SDP,BNEP等协议的实现。
(2)、驱动程序:kernel/driver/bluetooth中,包含Linuxkernel对各种接口的
Bluetooth device的驱动,如:USB接口,串口等。
(3)、用户态程序及工具集:
包括应用程序接口和BlueZ工具集。BlueZ提供函数库以及应用程序接口,便于程序员开发bluetooth应用程序。BlueZ utils是主要工具集,实现对bluetooth设备的初始化和控制。
3、蓝牙相关的应用程序接口
Android.buletooth包中的各个Class(蓝牙在框架层的内容-----java)
类名 | 作用 |
BluetoothAdapter | 本地蓝牙设备的适配类,所有的蓝牙操作都要通过该类完成 |
BluetoothClass | 用于描述远端设备的类型,特点等信息 |
BluetoothDevice | 蓝牙设备类,代表了蓝牙通讯过程中的远端设备 |
BluetoothServerSocket | 蓝牙设备服务端,类似ServerSocket |
BluetoothSocket | 蓝牙设备客户端,类似Socket |
BluetoothClass.Device | 蓝牙关于设备信息 |
BluetoothClass.Device.Major | 蓝牙设备管理 |
BluetoothClass.Service | 蓝牙相关服务 |
同样下图也是一张比较经典的蓝牙代码架构图(google官方提供)
二、蓝牙通过Hciattach启动串口流程:
1、hciattach总体流程
2、展讯hciattach代码实现流程:
三、具体代码分析
1、initrc中定义
idh.code\device\sprd\sp8830ec_nwcn\init.sc8830.rc
adb 下/dev/ttybt0(不同平台有所不同)
PS 进程中:hicattch
2、/system/bin/hciattach 执行的Main函数
idh.code\external\bluetooth\bluez\tools\hciattach.c
service hciattach /system/bin/hciattach -n /dev/sttybt0 sprd_shark
传进两个参数,/dev/sttybt0 和 sprd_shark
(1)、解析驱动的位置;
(2)、解析串口的配置相关参数;获取参数对应的结构体;
这个函数比较简单,通过循环对比,如传进了的参数sprd_shark和uart结构体中的对比,找到对应的数组。如果是其他蓝牙芯片,如博通、RDA、BEKN等着到其相对应的初始化配置函数。
注意:init_sprd_config这个函数在uart_init中用到,这个函数其实对我们具体芯片的初始化配置。
注释:HCI_UART_H4和HCI_UART_BCSP的区别如下图。
(3)、通过对前面参数的解析,把uart[i]中的数值初始化;
(4)、初始化串口;
这里一个重要的部分是:u->init指向init_sprd_config
4、uart具体到芯片的初始化init_sprd_config(这部分根据不同的芯片,对应进入其相应初始化部分)
idh.code\external\bluetooth\bluez\tools\hciattach_sprd.c
(1)、这部分检查bt_mac,如果存在,从文件中读取,如果不存在,随机生成,并写入相应文件/productinfo/btmac.txt;
(2)、PSKey参数、射频参数的设定;
get_pskey_from_file(&bt_para_tmp);这个函数后面分析;
(3)、读取失败,把bt_para_setting中defaut参数写入;频率、主从设备设定等……
5、get_pskey_from_file 解析相关射频参数
idh.code\external\bluetooth\bluez\tools\pskey_get.c
(4)、下面函数就不做具体分析,大致意识是,根据/dev/board_type中,读取的PCB类型,设置不同的ini文件。
(1)、判断PCB的版本;
char *BOARD_TYPE_PATH = "/dev/board_type";
(2)、最终生成ini文件存储的位置,就是系统运行时读取ini文件的地方;
char *CFG_2351_PATH_2 ="/productinfo/2351_connectivity_configure.ini";
(3)、针对不同PCB版本,不同的ini配置文件;
(4)、下面函数就不做具体分析,大致意识是,根据/dev/board_type中,读取的PCB类型,设置不同的ini文件。 覆盖到(2)中的文件。
四、HCI_UART_H4和H4层的加入
uart->hci_uart->Uart-H4->hci:从uart开始分析,介绍整个驱动层数据流(涉及tty_uart中断, 线路层ldisc_bcsp、tasklet、work queue、skb_buffer的等)
这是数据的流动过程,最底层的也就是和硬件打交道的是uart层了,它的存在和起作用是通过串口驱动来保证的,这个请参阅附录,但是其它的层我们都不知道什么时候work的,下面来看。
1、idh.code\kernel\drivers\bluetooth\hci_ldisc.c
(1)、这部分完成ldisc的注册;
tty_register_ldisc(N_HCI,&hci_uart_ldisc)
注册了一个ldisc,这是通过把新的ldisc放在一个ldisc的数组里面实现的,tty_ldiscs是一个全局的ldisc数组里面会根据序号对应一个ldisc,这个序号就是上层通过ioctl来指定的,比如我们在前面已经看到的:
i = N_HCI;
ioctl(fd, TIOCSETD, &i) < 0
可以看到这里指定的N_HCI刚好就是这里注册的这个号码15;
(2)、蓝牙芯片用的是H4,这部分完成H4的注册;
h4_init();
hci_uart_proto结构体的初始化:
idh.code\kernel\drivers\bluetooth\hci_h4.c
H4的注册:
idh.code\kernel\drivers\bluetooth\hci_h4.c
这是通过hci_uart_register_proto(&bcsp)来完成的,这个函数非常简单,本质如下:
hup[p->id]= p;其中static struct hci_uart_proto*hup[HCI_UART_MAX_PROTO];也就是说把对应于协议p的id和协议p连接起来,这样设计的好处是hci uart层本身可以支持不同的协议,包括h4、bcsp等,通过这个数组连接这些协议,等以后有数据的时候调用对应的协议来处理,这里比较关键的是h4里面的这些函数。
五、HCI层的加入
hci的加入是通过hci_register_dev函数来做的,这时候用户通过hciconfig就可以看到有一个接口了,通过这个接口用户可以访问底层的信息了,hci0已经生成;至于它在何时被加入的,我们再看看hciattach在内核里面的处理过程;
1、TIOCSEATD的处理流程
Ioctl的作用是设置一个新的ldisc;
2、HCIUARTSETPROTO的处理流程:
这部分比较重要,注册生成hci0, 初始化3个工作队列,hci_rx_work、hci_tx_work、hci_cmd_work;完成hci部分数据、命令的接收、发送。
六、数据在驱动的传递流程
1、uart数据接收
这部分流程比较简单,其实就是注册一个tty驱动程序和相对应的函数,注册相应的open\close\ioctl等方法,通过应用open /dev/ttyS*操作,注册中断接收函数,接收处理蓝牙模块触发中断的数据。
在这个中断函数里面会接受到来自于蓝牙模块的数据;在中断函数里面会先读取串口的状态寄存器判断是否是data准备好,如果准备好就调用serial_sprd_rx_chars函数来接收数据,下面看看这个函数是如何处理的:
那就是把数据一个个的加入到uart层的缓冲区,直到底层不处于dataready状态,或者读了maxcount个数,当读完后就调用tty层的接口把数据传递给tty层,tty层则把数据交给了ldisc,于是控制权也就交给了hci_uart层;
七、Hci_uart的数据接收
它基本上就是要个二传手,通过:
如图:
九、HCI以上的处理
这里的hci_rx_work前面已经看到它了,它是一个工作队列用来处理hci层的数据接收的;先看是否有进程打开hci的socket用来监听数据,如果有的话,就把数据的一个copy发送给它,然后根据包的类型调用不同的处理函数,分别对应于event、acl、sco处理;
hci_event_packet是对于事件的处理,里面包含有包括扫描,信号,授权,pin码,总之基本上上层所能收到的事件,基本都是在这里处理的,它的很多信息都是先存起来,等待上层的查询然后才告诉上层;
hci_acldata_packet是一个经常的情况,也就是说上层通常都是使用的是l2cap层的接口,而l2cap就是基于这个的,如下图所示:
到这里如果有基于BTPROTO_L2CAP的socket,那么这个socket就可以收到数据了;再看看BTPROTO_RFCOMM的流程:
十、 数据流程的总结
简单总结一下,数据的流程,
|基本上是:
1, uart口取得蓝牙模块的数据;
2, uart口通过ldisc传给hci_uart;
3, hci_uart传给在其上的h4;
4, h4传给hci层;
5, hci层传给l2cap层
6, l2cap层再传给rfcomm;
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。