转自:https://blog.csdn.net/xubin341719/article/details/38519555
Android bluetooth介绍(一):基本概念及硬件接口
关键词:蓝牙硬件接口 UART PCM blueZ
版本:基于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流程分析
一、基本概念补充
1、AP:ApplicationProcessor应用处理器
采用ARM架构的CPU,通常负责运行OS和一些特定设置和载入开机预设。比如一个没有电话功能的平板电脑,只跑android或者win8的系统,只要AP就可以。如国外性能比较好的AP:三星exynos系列芯片、高通骁龙系列、NVIDIA等;国内近几年芯片做的也不错,如:全志、炬力、瑞芯微、晶晨……不过稳定性、性能都还要提高;支持国产,要不然钱都给老外赚取。
2、BP:BasebandProcessor 基带处理器
架构相对于AP简单,处理电话、信息之类的功能。
基带芯片相对来说,高通一家独大了。然后就是台湾的MTK、大陆的展讯市场占有率逐步上升。
3、CP:coprocessor Processor 协处理器
基带芯片加协处理器(CP,通常是多媒体加速器)。这类产品以MTK方案为典型代表,展讯的市场占有份额也逐年上升,推类似的产品, 用来通过处理主cpu的一些工作负荷来使操作提速的辅助处理器。
4、RF:Radio Frequency射频部分
无线信号的发射部分,如电话、蓝牙、WIFI信号发射、接收;
5、展讯Tshark芯片的集成框图:
我们看到有一个AP,和三个CP:CP0、CP1、CP2。
AP为四核:4个A7。3个CP分别为三个ARM9架构的处理器。
这里所说的CP和一般的意义上的理解不同,理论上的CP不包括BP的,展讯的CP0、CP1其实是两个基带处理芯片,算是公司内部的一种说法。2G支持的制式都一样,3G的一个支持TS-SCDMA、一个支持WCDMA。
其实我们重点说的是CP2,四合一部分的,重点是蓝牙。
二、常见组合(简单框图)
如上图所示,一个AP芯片+外设+android软件,就构成一个简单的平板电脑。
2、以三星、高通等AP设计的手机 AP芯片+BP芯片+RF芯片
如果用单AP芯片,要加基带、射频芯片构成手机硬件架构,如下图所示:
3、以MTK、展讯方案设计的手机(AP+BP+CP)芯片+RF芯片
下面是展讯Tshark芯片的手机简单架构,和传统手机相比,硬件上更集成化,这样有利于厂商的生产、节省成本……MTK现阶段做的比较成熟,由低端向中高端进阶。展讯还在艰苦奋战,希望能再接再厉,成为一流芯片公司,填补大陆在芯片技术上的空白。
BT、WIFI、GPS、FM四合一的集成
前面我们了解BP、CP集成,了解手机整体架构,其实我重点讲解四合一芯片。主要说明四合一芯片独立芯片,和AP集成到同一芯片中两种架构。类型与BP,独立芯片、集成芯片。
(1)、独立芯片:
通过串口接出,如上图的troutII.其他芯片,如BCM、RDA、CSR、Retelk等……如上图troutII 也是独立芯片。
(2)、集成芯片:(WIFI \BT\FM\GPS的集成到主控芯片内,直接输出模拟信号量,通过RF放大后发射出去)
模拟芯片输出、出入到SR2351芯片,展讯比较多的芯片集成四合一(BT/WIFI/GPS/FM),节省客户成本,不过芯片性能跟BCM比还是有点距离,各位同仁继续加油。
三、蓝牙硬件接口概述(以UART接口接出的芯片为例)
蓝牙与主控部分硬件接口比较简单,分三部分,如上图框图所示:
UART部分:串口,主要负责数据传输;有其他USB、SDIO、PC card等通信;
PCM部分:语言接口,用于通话时语音数据传输;
Power部分:也就是BT模块的电源控制部分,VDD2.8V、PDN、RST。
1、UART:通用异步收发传输器(UniversalAsynchronous Receiver/Transmitter)
硬件接口比较简单,只有RX、DX。UART0用于trout芯片的debug:
是传输数据的接口,可以传输音频数据和文件数据。
1)、当通过蓝牙和别人互传文件时,就用UART传输。数据传输如下所示:AP通过uart把数据发送到bt芯片中,通过无线发射出去,对方接收到后传给AP侧处理。
2)、当用蓝牙耳机听音乐时,音频信号也由UART传输。
音乐数据通过uart 传送出去,对方耳机接收到后解码播放。如果是录音数据,则同样传回手机端存储。
2、PCM/IIS
(1)、PCM:脉冲编码调制(pulse codemodulation)
是传输语音信号所使用的调制方式。
用途:传输通话时的语音信号。
硬件接口:
(2)、PCM基本工作原理:
脉冲调制就是把一个时间连续,取值连续的模拟信号变换成时间离散,取值离散的数字信号后在信道中传输.脉冲编码调制就是对模拟信号先抽样,再对样值幅度量化,编码的过程。
一个模拟信号经过抽样量化后,得到已量化的脉冲幅度调制信号,它仅为有限个数值。PCM话音信号先经防混叠低通滤波器,进行脉冲抽样,变成8KHz重复频率的抽样信号(即离散的脉冲调幅PAM信号),然后将幅度连续的PAM信号用"四舍五入"办法量化为有限个幅度取值的信号,再经编码后转换成二进制码.对于电话,CCITT规定抽样率为8KHz,每抽样值编8位码,即共有28=256个量化值,因而每话路PCM编码后的标准数码率是64kb/s。
如下图PCM时序:
(3)、IIS(Inter-IC Sound bus)即集成音频接口
IIS只能传输单声道或双声道立体声的数字音频,数据格式为PCM格式。该接口又派生出三类:左对齐格式、右对齐格式、I2S格式。I2S时差性能要优于SPDIF,适合短距离通讯。
IIS总线一般具有4根信号线,如图1所示,包括串行数据输入(IISDI)、串行数据输出(IISD0)、左/右声道选择(IISLRCK)和串行数据时钟(IISCLK);产生IISLRCK和IISCLK的是主设备。
如下图IIS时序:
语音通话时数据流:语音数据有3G网络接入,通过PCM传递到蓝牙芯片,蓝牙芯片通过2.4G发射出去,到蓝牙耳机接收,同样mic传回的语言数据做相应处理。
(4)、为什么蓝牙用PCM通道(网络参考)
纠正个说法,一般手机cpu到蓝牙的通话实时音频传输才都使用pcm,MP3之类的音频走的是urat/usb之类的其他接口。
真正原因我也不能确定,不过有几个情况可能是原因之一。
(1)、PCM和IIS的区别在于,PCM一般是固定8k的采样率的单声道音频,最早似乎和固定电话的编码有直接关系,之后所有的话音编码几乎都是在PCM编码基础上再次编码得到的。而IIS则多了一个专门的信号线,采样率也可以配置到较高的频率,如44k;
(2)、手机通话时候的编码一般都会被解码成8k采样率的PCM码,高了也没有用。因为本来手机通话的话音频段就是300~3400Hz而已,编码也是按8k采样的。某些手机平台并没有IIS接口,相对来说基本上都会有PCM接口;
(3)、从蓝牙角度讲,蓝牙在制定协议的时候就特意为通话需求制定了一个协议层,专门定义了一种包结构(SCO)用于通话,有很好的实时性。而通过UART传输的包一般为ALC,用分组传输的方式。两种包的编解使用的软硬件都有些区别。sco包支持的基础码率也是8k的pcm.所以一般蓝牙芯片都会有一个PCM接口;
(4)、还一个原因就是,开始大家这么做了,后来就都这么做了,形成行业规范。
3、POWER 控制部分
程序对应引脚按power on /off 时序来完成on/off操作。同其他硬件一样,通过时序控制。
Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
关键词:蓝牙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
- service hciattach /system/bin/hciattach -n /dev/sttybt0 sprd_shark
- socket bluetooth stream 660 bluetooth bluetooth
- user bluetooth
- group wifi bluetooth net_bt_admin net_bt inet net_raw net_admin system
- disabled
- oneshot
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
- nt main(int argc, char *argv[])
- {
- ………………
- for (n = 0; optind < argc; n++, optind++) {
- char *opt;
-
- opt = argv[optind];
-
- switch(n) {
- case 0://(1)、解析驱动的位置;
- dev[0] = 0;
- if (!strchr(opt, '/'))
- strcpy(dev, "/dev/");
- strcat(dev, opt);
- break;
-
- case 1://(2)、解析串口的配置相关参数;
- if (strchr(argv[optind], ',')) {
- int m_id, p_id;
- sscanf(argv[optind], "%x,%x", &m_id, &p_id);
- u = get_by_id(m_id, p_id);
- } else {
- u = get_by_type(opt);
- }
-
- if (!u) {
- fprintf(stderr, "Unknown device type or id\n");
- exit(1);
- }
-
- break;
-
- case 2://(3)、通过对前面参数的解析,把uart[i]中的数值初始化;
- u->speed = atoi(argv[optind]);
- break;
-
- case 3:
- if (!strcmp("flow", argv[optind]))
- u->flags |= FLOW_CTL;
- else
- u->flags &= ~FLOW_CTL;
- break;
-
- case 4:
- if (!strcmp("sleep", argv[optind]))
- u->pm = ENABLE_PM;
- else
- u->pm = DISABLE_PM;
- break;
-
- case 5:
- u->bdaddr = argv[optind];
- break;
- }
- }
-
- ………………
- if (init_speed)//初始化串口速率;
- u->init_speed = init_speed;
- ………………
- n = init_uart(dev, u, send_break, raw);//(4)、初始化串口;
- ………………
-
- return 0;
- }
(1)、解析驱动的位置;
- if (!strchr(opt, '/'))
- strcpy(dev, "/dev/");
- service hciattach /system/bin/hciattach -n /dev/sttybt0 sprd_shark
- dev = /dev/ttyb0
(2)、解析串口的配置相关参数;获取参数对应的结构体;
- u = get_by_id(m_id, p_id);
- static struct uart_t * get_by_id(int m_id, int p_id)
- {
- int i;
- for (i = 0; uart[i].type; i++) {
- if (uart[i].m_id == m_id && uart[i].p_id == p_id)
- return &uart[i];
- }
- return NULL;
- }
这个函数比较简单,通过循环对比,如传进了的参数sprd_shark和uart结构体中的对比,找到对应的数组。如果是其他蓝牙芯片,如博通、RDA、BEKN等着到其相对应的初始化配置函数。
- struct uart_t uart[] = {
- { "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
- FLOW_CTL, DISABLE_PM, NULL, NULL },
- { "sprd_shark", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
- FLOW_CTL, DISABLE_PM, NULL, init_sprd_config },
-
- { "ericsson", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200,
- FLOW_CTL, DISABLE_PM, NULL, ericsson },
-
- ………………
- { "bk3211", 0x0000, 0x0000, HCI_UART_BCSP, 115200, 921600, 0, DISABLE_PM, NULL, beken_init, NULL},
- { NULL, 0 }
- };
注意:init_sprd_config这个函数在uart_init中用到,这个函数其实对我们具体芯片的初始化配置。
注释:HCI_UART_H4和HCI_UART_BCSP的区别如下图。
(3)、通过对前面参数的解析,把uart[i]中的数值初始化;
- u->speed = atoi(argv[optind]);
- break;
(4)、初始化串口;
- n = init_uart(dev, u, send_break, raw);
- idh.code\external\bluetooth\bluez\tools\hciattach.c
- /* Initialize UART driver */
- int init_uart(char *dev, struct uart_t *u, int send_break)
- {
- struct termios ti;
- int fd, i;
- fd = open(dev, O_RDWR | O_NOCTTY);//打开串口设备,其中标志
- //O_RDWR,可以对此设备进行读写操作;
- //O_NOCTTY:告诉Unix这个程序不想成为“控制终端”控制的程序,不说明这个标志的话,任何输入都会影响你的程序。
- //O_NDELAY:告诉Unix这个程序不关心DCD信号线状态,即其他端口是否运行,不说明这个标志的话,该程序就会在DCD信号线为低电平时停止。
- //但是不要以控制 tty 的模式,因为我们并不希望在发送 Ctrl-C
- 后结束此进程
- if (fd < 0) {
- perror(“Can’t open serial port”);
- return -1;
- }
- //drop fd’s data;
- tcflush(fd, TCIOFLUSH);//清空数据线
- if (tcgetattr(fd, &ti) < 0) {
- perror(“Can’t get port settings”);
- return -1;
- }
- cfmakeraw(&ti);
- cfmakeraw sets the terminal attributes as follows://此函数设置串口终端的以下这些属性,
- termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
- |INLCR|IGNCR|ICRNL|IXON);
- termios_p->c_oflag &= ~OPOST;
- termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
- termios_p->c_cflag &= ~(CSIZE|PARENB) ;
- termios_p->c_cflag |=CS8;
- ti.c_cflag |= CLOCAL;//本地连接,无调制解调器控制
- if (u->flags & FLOW_CTL)
- ti.c_cflag |= CRTSCTS;//输出硬件流控
- else
- ti.c_cflag &= ~CRTSCTS;
- if (tcsetattr(fd, TCSANOW, &ti) < 0) {//启动新的串口设置
- perror(“Can’t set port settings”);
- return -1;
- }
- /* Set initial baudrate */
- if (set_speed(fd, &ti, u->init_speed) < 0) {//设置串口的传输速率bps, 也可以使
- //用 cfsetispeed 和 cfsetospeed 来设置
- perror(“Can’t set initial baud rate”);
- return -1;
- }
- tcflush(fd, TCIOFLUSH);//清空数据线
- if (send_break)
- tcsendbreak(fd, 0);
- //int tcsendbreak ( int fd, int duration );Sends a break for
- //the given time.在串口线上发送0值,至少维持0.25秒。
- //If duration is 0, it transmits zero-valued bits for at least 0.25 seconds, and
- //not more than 0.5seconds.
- //where place register u’s init function;
- if (u->init && u->init(fd, u, &ti) < 0)
- //所有bluez支持的蓝牙串口设备类型构成了一个uart结构数组,通过
- //查找对应的uart类型,这个uart的init成员显示了它的init调用方法;
- struct uart_t uart[] = {
- { "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,FLOW_CTL, DISABLE_PM, NULL, NULL },
- { "sprd_shark", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,FLOW_CTL, DISABLE_PM, NULL, init_sprd_config },
-
- { "ericsson", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200,FLOW_CTL, DISABLE_PM, NULL, ericsson },
- ………………
- { "bk3211", 0x0000, 0x0000, HCI_UART_BCSP, 115200, 921600, 0, DISABLE_PM, NULL, beken_init, NULL},
- { NULL, 0的init函数名为bcsp,定义在本文件中**;
- return -1;
- tcflush(fd, TCIOFLUSH);//清空数据线
- /* Set actual baudrate */
- if (set_speed(fd, &ti, u->speed) < 0) {
- perror(“Can’t set baud rate”);
- return -1;
- }
- /* Set TTY to N_HCI line discipline */
- i = N_HCI;
- if (ioctl(fd, TIOCSETD, &i) < 0) {//
- TIOCSETD int *ldisc//改变到 i 行规,即hci行规
- Change to the new line discipline pointed to by ldisc. The available line disciplines are listed in
- /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
- /* line disciplines */
- #define N_TTY 0
- ……
- #define N_HCI 15 /* Bluetooth HCI UART */
-
- perror(“Can’t set line discipline”);
- return -1;
- }
- if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
- //设置hci设备的proto操作函数集为hci_uart操作集;
- perror(“Can’t set device”);
- return -1;
- }
- return fd;
- }
这里一个重要的部分是:u->init指向init_sprd_config
4、uart具体到芯片的初始化init_sprd_config(这部分根据不同的芯片,对应进入其相应初始化部分)
idh.code\external\bluetooth\bluez\tools\hciattach_sprd.c
- int sprd_config_init(int fd, char *bdaddr, struct termios *ti)
- {
- int i,psk_fd,fd_btaddr,ret = 0,r,size=0,read_btmac=0;
- unsigned char resp[30];
- BT_PSKEY_CONFIG_T bt_para_tmp;
- char bt_mac[30] = {0};
- char bt_mac_tmp[20] = {0};
- uint8 bt_mac_bin[32] = {0};
-
- fprintf(stderr,"init_sprd_config in \n");
- //(1)、这部分检查bt_mac,如果存在,从文件中读取,如果不存在,随机生成,并写入相应文件;
- if(access(BT_MAC_FILE, F_OK) == 0) {//这部分检查bt_mac
- LOGD("%s: %s exists",__FUNCTION__, BT_MAC_FILE);
- fd_btaddr = open(BT_MAC_FILE, O_RDWR);// #define BT_MAC_FILE "/productinfo/btmac.txt"
- if(fd_btaddr>=0) {
- size = read(fd_btaddr, bt_mac, sizeof(bt_mac));//读取BT_MAC_FILE中的地址;
- LOGD("%s: read %s %s, size=%d",__FUNCTION__, BT_MAC_FILE, bt_mac, size);
- if(size == BT_RAND_MAC_LENGTH){
- LOGD("bt mac already exists, no need to random it");
- fprintf(stderr, "read btmac ok \n");
- read_btmac=1;
- }
- …………
- }else{//如果不存在,就随机生成一个bt_mac地址,写入/productinfo/btmac.txt
- fprintf(stderr, "btmac.txt not exsit!\n");
- read_btmac=0;
- mac_rand(bt_mac);
- LOGD("bt random mac=%s",bt_mac);
- printf("bt_mac=%s\n",bt_mac);
- write_btmac2file(bt_mac);
-
- fd_btaddr = open(BT_MAC_FILE, O_RDWR);
- if(fd_btaddr>=0) {
- size = read(fd_btaddr, bt_mac, sizeof(bt_mac));
- LOGD("%s: read %s %s, size=%d",__FUNCTION__, BT_MAC_FILE, bt_mac, size);
- if(size == BT_RAND_MAC_LENGTH){
- LOGD("bt mac already exists, no need to random it");
- fprintf(stderr, "read btmac ok \n");
- read_btmac=1;
- }
- close(fd_btaddr);
- …………
- }
-
- /* Reset the BT Chip */
-
- memset(resp, 0, sizeof(resp));
- memset(&bt_para_tmp, 0, sizeof(BT_PSKEY_CONFIG_T) );
- ret = getPskeyFromFile( (void *)(&bt_para_tmp) );//ret = get_pskey_from_file(&bt_para_tmp);//(2)、PSKey参数、射频参数的设定;
- if(ret != 0){//参数失败处理
- fprintf(stderr, "get_pskey_from_file faill \n");
- /* Send command from hciattach*/
- if(read_btmac == 1){
- memcpy(bt_para_setting.device_addr, bt_mac_bin, sizeof(bt_para_setting.device_addr));// (3)、读取失败,把bt_para_setting中defaut参数写入;
- }
- if (write(fd, (char *)&bt_para_setting, sizeof(BT_PSKEY_CONFIG_T)) != sizeof(BT_PSKEY_CONFIG_T)) {
- fprintf(stderr, "Failed to write reset command\n");
- return -1;
- }
- }else{//getpskey成功处理
- /* Send command from pskey_bt.txt*/
- if(read_btmac == 1){
- memcpy(bt_para_tmp.device_addr, bt_mac_bin, sizeof(bt_para_tmp.device_addr));
- }
- …………
- return 0;
- }
(1)、这部分检查bt_mac,如果存在,从文件中读取,如果不存在,随机生成,并写入相应文件/productinfo/btmac.txt;
(2)、PSKey参数、射频参数的设定;
get_pskey_from_file(&bt_para_tmp);这个函数后面分析;
(3)、读取失败,把bt_para_setting中defaut参数写入;频率、主从设备设定等……
- // pskey file structure default value
- BT_PSKEY_CONFIG_T bt_para_setting={
- 5,
- 0,
- 0,
- 0,
- 0,
- 0x18cba80,
- 0x001f00,
- 0x1e,
- {0x7a00,0x7600,0x7200,0x5200,0x2300,0x0300},
- …………
- };
5、get_pskey_from_file 解析相关射频参数
idh.code\external\bluetooth\bluez\tools\pskey_get.c
- int getPskeyFromFile(void *pData)
- {
- …………
- char *BOARD_TYPE_PATH = "/dev/board_type";//(1)、判断PCB的版本;
- int fd_board_type;
- char board_type_str[MAX_BOARD_TYPE_LEN] = {0};
- int board_type;
- char *CFG_2351_PATH_2 = "/productinfo/2351_connectivity_configure.ini";//(2)、最终生成ini文件存储的位置;
- char *CFG_2351_PATH[MAX_BOARD_TYPE];
- (3)、针对不同PCB版本,不同的ini配置文件;
- CFG_2351_PATH[0] = "/system/etc/wifi/2351_connectivity_configure_hw100.ini";
- CFG_2351_PATH[1] = "/system/etc/wifi/2351_connectivity_configure_hw102.ini";
- CFG_2351_PATH[2] = "/system/etc/wifi/2351_connectivity_configure_hw104.ini";
(4)、下面函数就不做具体分析,大致意识是,根据/dev/board_type中,读取的PCB类型,设置不同的ini文件。
- ………………
- ret = chmod(CFG_2351_PATH_2, 0644);
- ALOGE("chmod 0664 %s ret:%d\n", CFG_2351_PATH_2, ret);
- if(pBuf == pBuf2)
- free(pBuf1);
- ………………
- }
(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配置文件;
- CFG_2351_PATH[0] = "/system/etc/wifi/2351_connectivity_configure_hw100.ini";
- CFG_2351_PATH[1] = "/system/etc/wifi/2351_connectivity_configure_hw102.ini";
- CFG_2351_PATH[2] = "/system/etc/wifi/2351_connectivity_configure_hw104.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
- static int __init hci_uart_init(void)
- {
- static struct tty_ldisc_ops hci_uart_ldisc;
- int err;
- /* 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))) {//(1)、这部分完成ldisc的注册;
- BT_ERR("HCI line discipline registration failed. (%d)", err);
- return err;
- }
-
- #ifdef CONFIG_BT_HCIUART_H4
- h4_init();//(2)、我们蓝牙芯片用的是H4,这部分完成H4的注册;
- #endif
- #ifdef CONFIG_BT_HCIUART_BCSP
- bcsp_init();
- #endif
- ………………
- return 0;
- }
(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
- static struct hci_uart_proto h4p = {
- .id = HCI_UART_H4,
- .open = h4_open,
- .close = h4_close,
- .recv = h4_recv,
- .enqueue = h4_enqueue,
- .dequeue = h4_dequeue,
- .flush = h4_flush,
- };
H4的注册:
idh.code\kernel\drivers\bluetooth\hci_h4.c
- int __init h4_init(void)
- {
- int err = hci_uart_register_proto(&h4p);
-
- if (!err)
- BT_INFO("HCI H4 protocol initialized");
- else
- BT_ERR("HCI H4 protocol registration failed");
-
- return err;
- }
这是通过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的数据接收
它基本上就是要个二传手,通过:
- spin_lock(&hu->rx_lock);
- hu->proto->recv(hu,(void *) data, count);
- hu->hdev->stat.byte_rx+= count;
- spin_unlock(&hu->rx_lock);
把数据交给了在它之上的协议层,对于我们的设置来说实际上就交给了h4层;
八、H4层处理
这层主要是通过函数h4_recv来处理的,根据协议处理包头、CRC等,然后调用更上层的hci_recv_frame来处理已经剥去h4包头的数据;
如图:
九、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;
Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
关键词:蓝牙blueZ A2DP、SINK、sink_connect、sink_disconnect、sink_suspend、sink_resume、sink_is_connected、sink_get_properties、AUDIO、DBUS
版本:基于android4.2之前版本 bluez
内核:linux/linux3.08
系统:android/android4.1.3.4
作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)
欢迎指正错误,共同学习、共同进步!!
参考网站:
http://blog.csdn.net/u011960402/article/details/17216563
http://www.cnblogs.com/fityme/archive/2013/04/13/3019471.html socket相关
http://hi.baidu.com/wwwkljoel/item/a35e5745d14e02e6bcf45170 setsockopt
Android bluetooth介绍(一):基本概念及硬件接口
Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
Android bluetooth介绍(四): a2dp connect流程分析
一、蓝牙扫描常用的方法:
蓝牙扫描的可以通过两种途径实现:命令行hciitool扫描;Android界面触发,通过JNI、DUBS下发命令。
1、 命令行hciitool扫描(这部分通过Linux命令操作,跟android没有关系)
通过bluez的tool发送扫描命令,如:hcitoool scan
adb shell 下#hcitool scan扫描结果
Hcitool扫描逻辑如下所示:
2、Android界面触发,通过JNI、DUBS下发命令:通过android界面点击搜索设备
应用扫描触发逻辑流程:自上而下三种颜色,分别代表应用部分、JNI部分、linux blueZ部分。
二、Hcitool触发逻辑分析
1、hcitool这部分代码比较简单,实现函数
idh.code\external\bluetooth\bluez\tools\hcitool.c代码大致流程如下:
通过所带的参数,找到cmd_scan,进入hci_inquriy。这个函数中创建一个BTPROTO_HCI的socket,通过ioctlHCINQUIRY向内核读取数据,保存返回信息。
2、内核层逻辑:
当然IOCTL只是其中一项。
idh.code\kernel\net\bluetooth\ hci_sock.c
- static const struct proto_ops hci_sock_ops = {
- …………
- .ioctl = hci_sock_ioctl,
- .poll = datagram_poll,
- .listen = sock_no_listen,
- …………
- };
它的流程就是构造查询命令,放入命令队列,调度队列来发送命令,其中hci_send_frame后面会讲解,这里关键是命令的发送和数据的收集是分开的,所以它在里面会放弃2s的调度,以此来等待数据的收集,收集的数据放在hdev->inq_cache里面,我们来看看这个数据是如何取得的,如下图所示:
入口点hci_rx_work前面已经详细分析过了,这里就不说了,它里面会根据不同的事件类型做不同的处理,通常情况下,扫描都是带信号强度的扫描,所以走的hci_inquiry_result_with_rssi_evt路线,还有其它几种扫描方式,比如:HCI_EV_INQUIRY_RESULT,HCI_EV_EXTENDED_INQUIRY_RESULT等,处理逻辑都差不多的,里面会hci_inquiry_cache_update来把结果放到hdev->discovery链表里面去,供后面的查询;比如前面调用的inquiry_cache_dump函数就可以从这个链表里面把数据取出来,然后copy到用户层;
三、Android界面触发,通过JNI、DUBS下发命令
整体流程如下所示:
(一)、应用部分:
1、 idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
- Preference preference) {
- …………
- mLocalAdapter.startScanning(true);
- return true;
- }
2、 idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\LocalBluetoothAdapter.java
- private final BluetoothAdapter mAdapter;
- void startScanning(boolean force) {
- // Only start if we're not already scanning
- if (!mAdapter.isDiscovering()) {
- if (!force) {
- // Don't scan more than frequently than SCAN_EXPIRATION_MS,
- // unless forced
- if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
- return;
- }
- // If we are playing music, don't scan unless forced.
- A2dpProfile a2dp = mProfileManager.getA2dpProfile();
- if (a2dp != null && a2dp.isA2dpPlaying()) {
- return;
- }
- }
- if (mAdapter.startDiscovery()) {
- mLastScan = System.currentTimeMillis();
- }
- }
- }
3、idh.code\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java
- public boolean startDiscovery() {
- if (getState() != STATE_ON) return false;
- try {
- return mService.startDiscovery();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
4、JNI函数的调用idh.code\frameworks\base\core\java\android\server\BluetoothService.java
- private native boolean startDiscoveryNative();//Native函数声明
- public class BluetoothService extends IBluetooth.Stub {
- private static final String TAG = "BluetoothService";
- private static final boolean DBG = true;
- …………
- public synchronized boolean startDiscovery() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (!isEnabledInternal()) return false;
- return startDiscoveryNative();
- }
- ………………
- }
(二)、JNI部分:
1、android_server_BluetoothService.cpp中JNI函数的对照表
idh.code\frameworks\base\core\jni\android_server_BluetoothService.cpp
- static JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- ………………
- {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
- {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
- …………
- }
2、对应Native函数的实现
这里面有个知识点DBUS,这个后面我们单独去讲解
idh.code\frameworks\base\core\jni\android_server_BluetoothService.cpp
- #define BLUEZ_DBUS_BASE_IFC "org.bluez"
- #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"//其实DBUS_ADAPTER_IFACE 也就是 org.bluez.Adapter
- static jboolean startDiscoveryNative(JNIEnv *env, jobject object) {
-
- ………………
- /* Compose the command */
- msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
- get_adapter_path(env, object),
- DBUS_ADAPTER_IFACE, "StartDiscovery");
- …………
- }
- Native函数startDiscoveryNative和字符串StartDiscovery对应。
(三)、DBUS部分
1、DBUS对应方法的实现,这里跟JNI部分比较类似,也是用了函数结构体对应关系。
idh.code\external\bluetooth\bluez\src\adapter.c
- #define ADAPTER_INTERFACE "org.bluez.Adapter"
- static GDBusMethodTable adapter_methods[] = {
- ………………
- { "ReleaseSession", "", "", release_session },
- { "StartDiscovery", "", "", adapter_start_discovery },
- { "StopDiscovery", "", "", adapter_stop_discovery,
- G_DBUS_METHOD_FLAG_ASYNC},
- ………………
- }
字符StartDiscovery又对应C中的实现函数adapter_start_discovery。
2、adapter_start_discovery的实现
idh.code\external\bluetooth\bluez\src\adapter.c
- static DBusMessage *adapter_start_discovery(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- …………
- err = start_discovery(adapter);
- if (err < 0 && err != -EINPROGRESS)
- return btd_error_failed(msg, strerror(-err));
-
- done:
- req = create_session(adapter, conn, msg, 0,
- session_owner_exit);
-
- adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req);
-
- return dbus_message_new_method_return(msg);
- }
3、 start_discovery调用
idh.code\external\bluetooth\bluez\src\adapter.c
- const struct btd_adapter_ops *adapter_ops = NULL;
- static int start_discovery(struct btd_adapter *adapter)
- {
- …………
- pending_remote_name_cancel(adapter);
- return adapter_ops->start_discovery(adapter->dev_id);
- }
adapter_ops对应结构体btd_adapter_ops中对应函数,如下:上面部分就对应到btd_adapter_ops中的hci_ops结构体。
4、btd_adapter_ops中的hci_ops结构体
idh.code\external\bluetooth\bluez\plugins\hciops.c
- static struct btd_adapter_ops hci_ops = {
- …………
- .set_powered = hciops_set_powered,
- .set_discoverable = hciops_set_discoverable,
- .set_pairable = hciops_set_pairable,
- .set_limited_discoverable = hciops_set_limited_discoverable,
- .start_discovery = hciops_start_discovery,
- .stop_discovery = hciops_stop_discovery,
- ………………
- .create_bonding = hciops_create_bonding,
- .cancel_bonding = hciops_cancel_bonding,
- .read_local_oob_data = hciops_read_local_oob_data,
- .add_remote_oob_data = hciops_add_remote_oob_data,
- .remove_remote_oob_data = hciops_remove_remote_oob_data,
- .set_link_timeout = hciops_set_link_timeout,
- .retry_authentication = hciops_retry_authentication,
- };
5、hciops_start_discovery函数的实现
idh.code\external\bluetooth\bluez\plugins\hciops.c
- static int hciops_start_discovery(int index)
- {
- int adapter_type = get_adapter_type(index);
-
- switch (adapter_type) {
- case BR_EDR_LE:
- return hciops_start_inquiry(index, LENGTH_BR_LE_INQ);
- case BR_EDR: //蓝牙芯片为2.1+EDR的
- return hciops_start_inquiry(index, LENGTH_BR_INQ);
- case LE_ONLY:
- return hciops_start_scanning(index, TIMEOUT_LE_SCAN);
- default:
- return -EINVAL;
- }
- }
6、hciops_start_inquiry
idh.code\external\bluetooth\bluez\plugins\hciops.c
- static int hciops_start_inquiry(int index, uint8_t length)
- {
- struct dev_info *dev = &devs[index];
- uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
- inquiry_cp inq_cp;
-
- DBG("hci%d length %u", index, length);
-
- memset(&inq_cp, 0, sizeof(inq_cp));
- memcpy(&inq_cp.lap, lap, 3);
- inq_cp.length = length;
- inq_cp.num_rsp = 0x00;
-
- if (hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp) < 0)
- return -errno;
-
- return 0;
- }
7、idh.code\external\bluetooth\bluez\lib\hci.c
- /* HCI functions that require open device
- * dd - Device descriptor returned by hci_open_dev. */
- dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
- int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
- {
- ………………
- if (plen) {
- iv[2].iov_base = param;
- iv[2].iov_len = plen;
- ivn = 3;
- }
-
- while (writev(dd, iv, ivn) < 0) {//writev这里把数据写到socket里面。
- if (errno == EAGAIN || errno == EINTR)
- continue;
- return -1;
- }
- return 0;
- }
(四)、内核部分:
1、HCI FILTER的设置
HCIsocket的类型为BTPROTO_HCI。上层调用setsockopt的时候,触发了内核的hci_sock_setsockopt函数的执行,在这里面设置了socket的filter特性,包括包类型,包括事件类型;
当上层调用setsockopt(sock, SOL_HCI, HCI_FILTER,&flt, sizeof(flt))时,触发相应的内核路径。
idh.code\kernel\net\bluetooth\hci_sock.c
- static const struct proto_ops hci_sock_ops = {
- .family = PF_BLUETOOTH,
- .owner = THIS_MODULE,
- …………
- .shutdown = sock_no_shutdown,
- .setsockopt = hci_sock_setsockopt,
- .getsockopt = hci_sock_getsockopt,
- .connect = sock_no_connect,
- …………
- };
idh.code\kernel\net\bluetooth\hci_sock.c
- static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int len)
- {
- ………………
- case HCI_FILTER:
- {
- struct hci_filter *f = &hci_pi(sk)->filter;
-
- uf.type_mask = f->type_mask;
- uf.opcode = f->opcode;
- uf.event_mask[0] = *((u32 *) f->event_mask + 0);
- uf.event_mask[1] = *((u32 *) f->event_mask + 1);
- }
- ………………
- }<span style="font-weight:bold;">
- span>
内核这部分就比较统一的数据,通过hci_send_cmd把命令发出去,HCI_FILTER这个地方的处理还没理解,后面补充。
Writev函数通过socket把数据写下去,经过VFS层,调用到内核空间的sendmsg函数。
(五)、EVENT返回状态
Controller收到查询命令后,返回一个命令状态
1、cmd_status
idh.code\external\bluetooth\bluez\plugins\hciops.c
- switch (eh->evt) {
- case EVT_CMD_STATUS:
- cmd_status(index, ptr);
- break;
- static inline void cmd_status(int index, void *ptr)
- {
- evt_cmd_status *evt = ptr;
- uint16_t opcode = btohs(evt->opcode);
-
- if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))//如果是inquriy做特殊处理;
- cs_inquiry_evt(index, evt->status);
- }
2、cs_inquiry_evt的实现 idh.code\external\bluetooth\bluez\plugins\hciops.c
- static inline void cs_inquiry_evt(int index, uint8_t status)
- {
- if (status) {//错误信息
- error("Inquiry Failed with status 0x%02x", status);
- return;
- }
-
- set_state(index, DISCOV_INQ);//设置状态为INQ,向上层回复discoverying的property change
- }
3、设置不同的DISCOV 状态 idh.code\external\bluetooth\bluez\plugins\hciops.c
- static void set_state(int index, int state)
- {
- ………………
- switch (dev->discov_state) {
- case DISCOV_HALTED://停止发现;
- if (adapter_get_state(adapter) == STATE_SUSPENDED)
- return;
-
- if (is_resolvname_enabled() &&
- adapter_has_discov_sessions(adapter))
- adapter_set_state(adapter, STATE_RESOLVNAME);
- else
- adapter_set_state(adapter, STATE_IDLE);
- break;
- case DISCOV_INQ:
- case DISCOV_SCAN://扫描发现;
- adapter_set_state(adapter, STATE_DISCOV);
- break;
- }
- }
4、设置adapter的状态 idh.code\external\bluetooth\bluez\src\adapter.c
- idh.code\external\bluetooth\bluez\src\adapter.c
- #define ADAPTER_INTERFACE "org.bluez.Adapter"
- void adapter_set_state(struct btd_adapter *adapter, int state)
- {
- …………
- case STATE_DISCOV:
- discov_active = TRUE;
- //向上层回复discovering的property change
- emit_property_changed(connection, path,
- ADAPTER_INTERFACE, "Discovering",
- DBUS_TYPE_BOOLEAN, &discov_active);
- break;
- …………
- }
emit_property_changed发送PropertyChanged的消息,消息内容为Discovering。通知上层BluetoothEventLoop进行Discovering。
5、emit_property_changed发送Discovering消息的实现 idh.code\external\bluetooth\bluez\src\dbus-common.c
这部分涉及到DBUS内容
- dbus_bool_t emit_property_changed(DBusConnection *conn,
- const char *path,
- const char *interface,
- const char *name,
- int type, void *value)
- {
- DBusMessage *signal;
- DBusMessageIter iter;
- signal = dbus_message_new_signal(path, interface, "PropertyChanged"); // 创建消息对象并标识路径
- if (!signal) {
- error("Unable to allocate new %s.PropertyChanged signal",
- interface);
- return FALSE;
- }
- dbus_message_iter_init_append(signal, &iter);//把信号相对应的参数压进去
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);//申请一个首地址,把前面压入的参数传入这个首地址
- append_variant(&iter, type, value);//
- return g_dbus_send_message(conn, signal);//启动发送调用,并释放发送相关消息信息
- }
6、DBUS消息接收的实现 idh.code\frameworks\base\core\jni\android_server_BluetoothEventLoop.cpp
- // Called by dbus during WaitForAndDispatchEventNative()
- static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
- void *data) {
- …………
- else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "PropertyChanged")) {
- jobjectArray str_array = parse_adapter_property_change(env, msg);//(1)、对收到消息的解析
- if (str_array != NULL) {
- /* Check if bluetoothd has (re)started, if so update the path. */
- jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);
- const char *c_property = env->GetStringUTFChars(property, NULL);
- if (!strncmp(c_property, "Powered", strlen("Powered"))) {
- jstring value =
- (jstring) env->GetObjectArrayElement(str_array, 1);
- const char *c_value = env->GetStringUTFChars(value, NULL);
- if (!strncmp(c_value, "true", strlen("true")))
- nat->adapter = get_adapter_path(nat->conn);
- env->ReleaseStringUTFChars(value, c_value);
- }
- env->ReleaseStringUTFChars(property, c_property);
-
- env->CallVoidMethod(nat->me,
- method_onPropertyChanged,//(2)、
- method_onPropertyChanged NATVIE函数的实现
- str_array);
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- goto success;
- }
(1)、对收到消息的解析 idh.code\frameworks\base\core\jni\android_bluetooth_common.cpp
- jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) {
- return parse_property_change(env, msg, (Properties *) &adapter_properties,
- sizeof(adapter_properties) / sizeof(Properties));
- }
针对org.bluez.Adapter不同的消息类型
idh.code\frameworks\base\core\jni\android_bluetooth_common.cpp
- static Properties adapter_properties[] = {
- {"Address", DBUS_TYPE_STRING},
- {"Name", DBUS_TYPE_STRING},
- {"Class", DBUS_TYPE_UINT32},
- {"Powered", DBUS_TYPE_BOOLEAN},
- {"Discoverable", DBUS_TYPE_BOOLEAN},
- {"DiscoverableTimeout", DBUS_TYPE_UINT32},
- {"Pairable", DBUS_TYPE_BOOLEAN},
- {"PairableTimeout", DBUS_TYPE_UINT32},
- {"Discovering", DBUS_TYPE_BOOLEAN},
- {"Devices", DBUS_TYPE_ARRAY},
- {"UUIDs", DBUS_TYPE_ARRAY},
- };
(2)、method_onPropertyChanged NATVIE函数的实现 idh.code\frameworks\base\core\jni\android_server_BluetoothEventLoop.cpp
- static void classInitNative(JNIEnv* env, jclass clazz) {
- ALOGV("%s", __FUNCTION__);
- #ifdef HAVE_BLUETOOTH
- method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",
- "([Ljava/lang/String;)V");
- method_onDevicePropertyChanged = env->GetMethodID(clazz,
- "onDevicePropertyChanged","(Ljava/lang/String;[Ljava/lang/String;)V");
- …………
- }
7、JNI调用onPropertyChanged对应JAVA的实现,在BluetoothEventLoop.java
idh.code\frameworks\base\core\java\android\server\BluetoothEventLoop.java中
- private static native void classInitNative();
- *package*/ void onPropertyChanged(String[] propValues) {
- ………………
- log("Property Changed: " + propValues[0] + " : " + propValues[1]);
- String name = propValues[0];
- if (name.equals("Name")) {//获取蓝牙名字;
- …………
- } else if (name.equals("Pairable") || name.equals("Discoverable")) {//配对;
- ………………
- } else if (name.equals("Discovering")) {//扫描查询;
- Intent intent;
- adapterProperties.setProperty(name, propValues[1]);
- if (propValues[1].equals("true")) {
- intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
- } else {
- // Stop the discovery.
- mBluetoothService.cancelDiscovery();
- intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
- }
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- } else if (name.equals("Devices") || name.equals("UUIDs")) {//Devices、UUID的获取;
- ………………
- } else if (name.equals("Powered")) {//蓝牙打开、关闭;
- mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,
- propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));
- } else if (name.equals("DiscoverableTimeout")) {
- adapterProperties.setProperty(name, propValues[1]);
- }
- }
(1)、看到这份log我们也许会更明白其他功能的由来:
- D BluetoothEventLoop: Property Changed: Powered : true
- D BluetoothEventLoop: Property Changed: Pairable : true
- D BluetoothEventLoop: Property Changed: Class : 5898764
- D BluetoothEventLoop: Property Changed: Pairable : true
- D BluetoothEventLoop: Property Changed: Discoverable : false
- D BluetoothEventLoop: Property Changed: Discovering : true
- D BluetoothEventLoop: Property Changed: Discovering : false
- D BluetoothEventLoop: Property Changed: Devices : 1
- D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Connected value: true
- D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Paired value: true
- D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: UUIDs value: 4
(2)、下面我们重点分析Discovering这部分:
idh.code\frameworks\base\core\java\android\server\BluetoothEventLoop.java
- else if (name.equals("Discovering")) {
- Intent intent;
- adapterProperties.setProperty(name, propValues[1]);
- if (propValues[1].equals("true")) {//开始扫描
- intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//
- } else {
- // Stop the discovery. //停止扫描
- mBluetoothService.cancelDiscovery();
- intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
- }
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- 这样就可以通过broadcast发送ACTION_DISCOVERY_STARTED广播,注册的receiver来响应了。
8、ACTION_DISCOVERY_STARTED\ACTION_DISCOVERY_FINISHED的receiver分析
从代码中我们可以看到这个action一共有两个receiver,一个是静态注册的BluetoothDiscoveryReceiver,一个是动态注册是ScanningStateChangedHandler。
(1)、BluetoothDiscoveryReceiver:这个receiver是在settings中的Androidmanifest中静态注册的。用途:主要用于获取扫描开始和终止的时间。
idh.code\packages\apps\Settings\AndroidManifest.xml
- <receiver
- android:name=".bluetooth.BluetoothDiscoveryReceiver">
- <intent-filter>
- <action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
- <action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />
- <category android:name="android.intent.category.DEFAULT" />
- intent-filter>
- receiver>
1)、ACTION_DISCOVERY_STARTED、ACTION_DISCOVERY_FINISHED和AndroidManifest.xml文件的联系
idh.code\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java
- public final class BluetoothAdapter {
- private static final String TAG = "BluetoothAdapter";
- private static final boolean DBG = false;
- …………
- public static final String ACTION_DISCOVERY_STARTED =
- "android.bluetooth.adapter.action.DISCOVERY_STARTED";
- public static final String ACTION_DISCOVERY_FINISHED =
- "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
- …………
- }
2)、BluetoothAdapter,蓝牙适配器,直到我们建立bluetoothSocket连接之前,都要不断操作它。
BluetoothAdapter中的动作常量
ACTION_DISCOVERY_FINISHED |
已完成蓝牙搜索 |
ACTION_DISCOVERY_STARTED |
已经开始搜索蓝牙设备 |
ACTION_LOCAL_NAME_CHANGED |
更改蓝牙的名字 |
ACTION_REQUEST_DISCOVERABLE |
请求能够被搜索 |
ACTION_REQUEST_ENABLE |
请求启动蓝牙 |
ACTION_SCAN_MODE_CHANGED |
扫描模式已经改变 |
ACTION_STATE_CHANGED |
状态已改变 |
ACTION_CONNECTION_STATE_CHANGED |
|
3)、收到广播后函数实现,开始扫描
Main log中显示的log为DISCOVERY_STARTED
D BluetoothDiscoveryReceiver: Received:android.bluetooth.adapter.action.DISCOVERY_STARTED
HCI log 中:
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothDiscoveryReceiver.java这个文件中就一个函数,还是比简单
- public final class BluetoothDiscoveryReceiver extends BroadcastReceiver {
- private static final String TAG = "BluetoothDiscoveryReceiver";
- private static final boolean DEBUG = Debug.isDebug();
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (DEBUG) Log.d(TAG, "Received: " + action);
-
- if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED) ||
- action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
- //共享时间戳,扫描开始和结束的时间。
- LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
- }
- }
- }
ScanningStateChangedHandler的注册及用途,要用于开始扫描,和扫描显示界面的控制。
这个receiver是在idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothEventManager.java动态注册的,如下:
- BluetoothEventManager(LocalBluetoothAdapter adapter,
- CachedBluetoothDeviceManager deviceManager, Context context) {
- mLocalAdapter = adapter;
- …………
- // Bluetooth on/off broadcasts
- addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
-
- // Discovery broadcastsaddHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
- addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
- …………
- }
(1)、ScanningStateChangedHandler函数实现如下:idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothEventManager.java
- private class ScanningStateChangedHandler implements Handler {
- private final boolean mStarted;
-
- ScanningStateChangedHandler(boolean started) {
- mStarted = started;
- }
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
- synchronized (mCallbacks) {//1)、调用注册的callback
- 中的onScanningStateChanged函数。
- for (BluetoothCallback callback : mCallbacks) {
- callback.onScanningStateChanged(mStarted);
- }
- }
- //2)、这个函数就是把上次扫描到设备、和之前的设备做相应处理;
- mDeviceManager.onScanningStateChanged(mStarted);
- LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
- }
- }
1)、调用注册的callback中的callback.onScanningStateChanged(mStarted)函数。
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java
- public void onScanningStateChanged(boolean started) {
- if (started == false) {//《1》、如果扫描结束;
- removeOutOfRangeDevices();
- }
- updateProgressUi(started);// 《2》、UI显示小圆圈扫描;
《1》、如果扫描结束;removeOutOfRangeDevices();
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java
- private void removeOutOfRangeDevices() {
- Collection<CachedBluetoothDevice> cachedDevices =
- mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
- for (CachedBluetoothDevice cachedDevice : cachedDevices) {
- if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
- cachedDevice.isVisible() == false) {
- BluetoothDevicePreference preference = mDevicePreferenceMap.get(cachedDevice);
- if (preference != null) {
- mDeviceListGroup.removePreference(preference);
- }
- mDevicePreferenceMap.remove(cachedDevice);
- }
- }
- }
《2》、UI显示小圆圈扫描,updateProgressUi(started);如下图所示:
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java
- private void updateProgressUi(boolean start) {
- if (mDeviceListGroup instanceof ProgressCategory) {
- ((ProgressCategory) mDeviceListGroup).setProgress(start);
- }
- }
2)、这部分的作用,开始扫描,不显示列表中内容,或把之前列表中没扫描到的设备清除
mDeviceManager.onScanningStateChanged(mStarted);
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\CachedBluetoothDevice.java
- private void updateProgressUi(boolean start) {
- if (mDeviceListGroup instanceof ProgressCategory) {
- ((ProgressCategory) mDeviceListGroup).setProgress(start);
- }
- }
- 2)、这部分的作用,开始扫描,不显示列表中内容,或把之前列表中没扫描到的设备清除
- mDeviceManager.onScanningStateChanged(mStarted);
- idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\ CachedBluetoothDevice.java
- public synchronized void onScanningStateChanged(boolean started) {
- // If starting a new scan, clear old visibility
- // Iterate in reverse order since devices may be removed.
- //如果开始新的扫描,清除旧的能见设备,迭代反序因为有的设备可能被删除
- for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
- CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
- if (started) {//如果扫描开始就不显示;
- cachedDevice.setVisible(false);
- } else {//对扫描的结果作出判断,如果之前扫描过,这次没有扫描到,就移除列表。
- if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
- cachedDevice.isVisible() == false) {
- mCachedDevices.remove(cachedDevice);
- }
- }
- }
- }
Android bluetooth介绍(四): a2dp connect流程分析
关键词:蓝牙blueZ A2DP、SINK、sink_connect、sink_disconnect、sink_suspend、sink_resume、sink_is_connected、sink_get_properties、AUDIO、DBUS
版本:基于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流程分析
一、A2DP_CONNECT上层代码流程
二、从HCI log中看AVDTP 创建过程
1、AVDTP l2cap建立过程
2、AVDTP相关信令处理流程在HCI 中的流程
DISCOVER \GET_CAPABILITIES\SET_CONFIGURATION\OPEN\START\SUSPEND
三、audiosink函数注册、及命令处理流程
AVDTP_DISCOVER\AVDTP_GET_CAPABILITIES\AVDTP_SET_CONFIGURATION\AVDTP_OPEN\AVDTP_START:等一系列控制命令
(一)、sink_connect创建流程
整体流程如下所示
1、idh.code\external\bluetooth\bluez\audio\sink.c
- static DBusMessage *sink_connect(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- …………
- if (!sink->session)//(1)、如果没有AVDTP会话,获取AVDTP连接状态;
- sink->session = avdtp_get(&dev->src, &dev->dst);
-
- if (!sink->session)//相关失败操作
- return btd_error_failed(msg, "Unable to get a session");
-
- if (sink->connect || sink->disconnect)//如果正在连接、断开,发送busy消息;
- return btd_error_busy(msg);
-
- if (sink->stream_state >= AVDTP_STATE_OPEN)//如果已经打开,发送已经连接消息;
- return btd_error_already_connected(msg);
-
- if (!sink_setup_stream(sink, NULL))//(2)、创建AVDTP流;
- return btd_error_failed(msg, "Failed to create a stream");
-
- dev->auto_connect = FALSE;
-
- pending = sink->connect;
-
- pending->conn = dbus_connection_ref(conn);//(3)、保存客户端dbus信息;
- pending->msg = dbus_message_ref(msg);
-
- DBG("stream creation in progress");
-
- return NULL;
- }
(1)、如果没有AVDTP会话,获取AVDTP连接状态;
- sink->session = avdtp_get(&dev->src, &dev->dst);
- idh.code\external\bluetooth\hcidump\parser\avdtp.c
- struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst)
- {
- ………………
- session = avdtp_get_internal(src, dst);
- ………………
-
- }
- avdtp_get_internal 中设置 session->state状态,
- session->state = AVDTP_SESSION_STATE_DISCONNECTED;
(2)、创建AVDTP流;
sink_setup_stream(sink,NULL)
idh.code\external\bluetooth\hcidump\parser\avdtp.c
- gboolean sink_setup_stream(struct sink *sink, struct avdtp *session)
- {
- …………
- avdtp_set_auto_disconnect(sink->session, FALSE);//不能自动断开;
-
- if (avdtp_discover(sink->session, discovery_complete, sink) < 0)//调用avdtp_discover,
- discovery_complete为回调函数;
- return FALSE;
-
- sink->connect = g_new0(struct pending_request, 1);
-
- return TRUE;
- }
idh.code\external\bluetooth\hcidump\parser\avdtp.c
- int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
- void *user_data)
- {
- int err;
-
- if (session->discov_cb)
- return -EBUSY;
-
- if (session->seps) {
- session->discov_cb = cb;
- session->user_data = user_data;
- g_idle_add(process_discover, session);
- return 0;
- }
-
- err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);
- //发送AVDTP_DISCOVER命令出去
- if (err == 0) {
- session->discov_cb = cb;
- session->user_data = user_data;
- }
-
- return err;
- }
idh.code\external\bluetooth\hcidump\parser\avdtp.c
- static int send_request(struct avdtp *session, gboolean priority,
- struct avdtp_stream *stream, uint8_t signal_id,
- void *buffer, size_t size)
- {
- struct pending_req *req;
-
- if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
- DBG("Unable to send requests while aborting");
- return -EINVAL;
- }
-
- req = g_new0(struct pending_req, 1);
- req->signal_id = signal_id;
- req->data = g_malloc(size);
- memcpy(req->data, buffer, size);
- req->data_size = size;
- req->stream = stream;
-
- return send_req(session, priority, req);//这个函数我们后面分析;
- }
(3)、保存客户端dbus信息;
- pending->conn = dbus_connection_ref(conn);
- pending->msg = dbus_message_ref(msg);
2、send_req 创建L2CAP连接
idh.code\external\bluetooth\hcidump\parser\avdtp.c
- static int send_req(struct avdtp *session, gboolean priority,
- struct pending_req *req)
- {
- static int transaction = 0;
- int err;
-
- if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//如果AVDTP没有连接,
- session->io = l2cap_connect(session);//(1)、创建l2cap连接;
- if (!session->io) {
- err = -EIO;
- goto failed;
- }
- avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
- }
-
- if (session->state < AVDTP_SESSION_STATE_CONNECTED ||
- session->req != NULL) {//如果AVDTP没连接
- queue_request(session, req, priority);//把相关参数放入队列
- return 0;//在这里返回,后面AVDTP sock建立完成后,会再次调用这个函数;
- }
-
- req->transaction = transaction++;
- transaction %= 16;
-
- /* FIXME: Should we retry to send if the buffer
- was not totally sent or in case of EINTR? */
- if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
- req->signal_id, req->data, req->data_size)) {//(2)、发送相关命令
- err = -EIO;
- goto failed;
- }
- …………
- }
(1)、创建l2cap连接
sink connect的过程本质上是建立一个avdtp 连接的过程,avdtp是基于l2cap的,包括控制命令的发送和数据的发送都是l2cap的,所以这个图纸表示了建立一个发送控制命令的l2cap的socket,等这个socket建立起来以后,开始发送AVDPT_DISCOVER的请求;
idh.code\external\bluetooth\hcidump\parser\avdtp.c
- session->io = l2cap_connect(session);
- static GIOChannel *l2cap_connect(struct avdtp *session)
- {
- GError *err = NULL;
- GIOChannel *io;
-
- io = bt_io_connect(BT_IO_L2CAP, avdtp_connect_cb, session,
- NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, &session->server->src,
- BT_IO_OPT_DEST_BDADDR, &session->dst,
- BT_IO_OPT_PSM, AVDTP_PSM,
- BT_IO_OPT_INVALID);
- if (!io) {
- error("%s", err->message);
- g_error_free(err);
- return NULL;
- }
-
- return io;<strong>
- }
- strong>
这个函数中注意两点,1)、bt_io_connect;2)、avdtp_connect_cb回调函数;
1)、bt_io_connect
idh.code\external\bluetooth\bluez\btio\btio.c
- GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
- gpointer user_data, GDestroyNotify destroy,
- GError **gerr, BtIOOption opt1, ...)
- {
- …………
-
- io = create_io(type, FALSE, &opts, gerr);
- if (io == NULL)
- return NULL;
- sock = g_io_channel_unix_get_fd(io);
- switch (type) {
- case BT_IO_L2RAW:
- err = l2cap_connect(sock, &opts.dst, 0, opts.cid);
- break;
- //不同协议的连接,如L2CPA、RFCOMM、SCO
- case BT_IO_L2CAP:
- err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid);
- break;
- case BT_IO_RFCOMM:
- err = rfcomm_connect(sock, &opts.dst, opts.channel);
- break;
- case BT_IO_SCO:
- err = sco_connect(sock, &opts.dst);
- break;
- …………
- connect_add(io, connect, user_data, destroy);
-
- return io;
- }<strong>
- strong>
Btio中l2cap_connect的实现:
idh.code\external\bluetooth\bluez\btio\btio.c
- static int l2cap_connect(int sock, const bdaddr_t *dst,
- uint16_t psm, uint16_t cid)
- {
- int err;
- struct sockaddr_l2 addr;
-
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- bacpy(&addr.l2_bdaddr, dst);
- if (cid)
- addr.l2_cid = htobs(cid);
- else
- addr.l2_psm = htobs(psm);
-
- err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));//建立BTPROTO_L2CAP
- if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
- return err;
-
- return 0;
- }
2)、avdtp_connect_cb回调函数
idh.code\external\bluetooth\hcidump\parser\avdtp.c
- static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
- {
- ………………
- if (session->state == AVDTP_SESSION_STATE_CONNECTING) {//如果处于正在连接状态;
- DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);
-
- session->buf = g_malloc0(session->imtu);
- avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);//设置AVDTP状态为已经连接状态;
-
- if (session->io_id)
- g_source_remove(session->io_id);
-
- /* This watch should be low priority since otherwise the
- * connect callback might be dispatched before the session
- * callback if the kernel wakes us up at the same time for
- * them. This could happen if a headset is very quick in
- * sending the Start command after connecting the stream
- * transport channel.
- */
- session->io_id = g_io_add_watch_full(chan,
- G_PRIORITY_LOW,
- G_IO_IN | G_IO_ERR | G_IO_HUP
- | G_IO_NVAL,
- (GIOFunc) session_cb, session,
- NULL);
-
- ………………
- process_queue(session);//发送DISCOVER
-
- return;
- …………
- }
3、process_queue(session)发送DISCOVER命令出去
idh.code\external\bluetooth\hcidump\parser\avdtp.c
- static int process_queue(struct avdtp *session)
- {
- …………
- *queue = g_slist_remove(*queue, req);
-
- return send_req(session, FALSE, req);
- }<strong>
- strong>
这个函数调用send_req,这个函数前面已经调用过,可是现在AVDTP的状态不同,第一次调用AVDTP_SESSION_STATE_DISCONNECTED状态,第二次调用为
AVDTP_SESSION_STATE_CONNECTED状态;
idh.code\external\bluetooth\hcidump\parser\avdtp.c
- static int send_req(struct avdtp *session, gboolean priority,
- struct pending_req *req)
- {
- static int transaction = 0;
- int err;
-
- if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//第二次调用时,就不走这段函数
- session->io = l2cap_connect(session);
- if (!session->io) {
- err = -EIO;
- goto failed;
- }
- avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
- }
-
- if (session->state < AVDTP_SESSION_STATE_CONNECTED ||//第二次调用也越过这段函数
- session->req != NULL) {
- queue_request(session, req, priority);
- return 0;
- }
-
- req->transaction = transaction++;
- transaction %= 16;
-
- /* FIXME: Should we retry to send if the buffer
- was not totally sent or in case of EINTR? */
- if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
- req->signal_id, req->data, req->data_size)) {//avdtp_send就是主要的操作
- err = -EIO;
- goto failed;
- }
4、avdtp_send的实现
idh.code\external\bluetooth\hcidump\parser\avdtp.c
- static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,
- uint8_t message_type, uint8_t signal_id,
- void *data, size_t len)
- {
- …………
- /* Send the start packet */
- memset(&start, 0, sizeof(start));
- start.transaction = transaction;
- start.packet_type = AVDTP_PKT_TYPE_START;
- start.message_type = message_type;
- start.no_of_packets = cont_fragments + 1;
- start.signal_id = signal_id;
-
- memcpy(session->buf, &start, sizeof(start));
- memcpy(session->buf + sizeof(start), data,
- session->omtu - sizeof(start));
-
- if (!try_send(sock, session->buf, session->omtu))
- return FALSE;
-
- ………………
- cont.message_type = message_type;
-
- memcpy(session->buf, &cont, sizeof(cont));
- memcpy(session->buf + sizeof(cont), data + sent, to_copy);
-
- if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
- return FALSE;
-
- sent += to_copy;
- }
-
- return TRUE;
- } <strong>
- strong>
5、Try_sends函数的实现
- static gboolean try_send(int sk, void *data, size_t len)
- {
- int err;
- do {
- err = send(sk, data, len, 0);
- } while (err < 0 && errno == EINTR);
-
- if (err < 0) {
- error("send: %s (%d)", strerror(errno), errno);
- return FALSE;
- } else if ((size_t) err != len) {
- error("try_send: complete buffer not sent (%d/%zu bytes)",
- err, len);
- return FALSE;
- }
-
- return TRUE;
- }<strong>
- strong>
(二)、AVDTP_DISCOVER的命令发送流程如上图所示;
avdtp是基于l2cap的,包括控制命令的发送和数据的发送都是l2cap的,所以建立一个发送控制命令的l2cap的socket,等这个socket建立起来以后,开始发送AVDPT_DISCOVER的请求;|
`AVDTP_DISCOVER\AVDTP_GET_CAPABILITIES\AVDTP_SET_CONFIGURATION\AVDTP_OPEN\AVDTP_START:等一系列控制命令
建立了一个l2cap的连接,等有数据过来的时候,就开始触发逻辑,session_cb是一个非常重要的函数,这里控制了整个连接的流程,我们下面会讲,剩下的就是通过avdtp_send来发送一个AVDTP_DISCOVER的命令,这个命令的作用就是查看远程设备看它支持那些sep(stream end point),也就是说是否支持source,sink等;
四、AVDTP_GET_CAPABILITIES命令发送(其他代码流程比较类似)
如下图所示:
这个图在发送了avdtp discover命令以后,会被先前设立好的回调函数执行,里面会把远程设备的sep都加入到session的seps连边里面去,然后开始发送AVDTP_GET_CAPABILITIES命令了;
当收到远端设备的回复消息后触发调用下面的逻辑:
在系列初始化、状态设定之后,发送哦AVDTP_SET_CONFIGURATION
五、AVDTP_SET_CONFIGURATION命令发送
发送AVDTP_OPEN命令;
六、AVDTP_OPEN的处理流程
到这里就表示已经确立了sep和caps,开始打开AVDTP了,如下:
数stream_setup_complete里面会对先前的dbus消息进行回复;
七、AVDTP_START命令发送
这里发送AVDTP_START的命令,它的触发是由客户端引起的,比如aplay –Dbluetooth 2.wav的时候通过alsa提供的bluetooth的插件,daemonbluetoothd-service-audio通过socket(PF_LOCAL, SOCK_STREAM,0);建立起一个socket来监听客户端的接入,触发server_cb的执行,在这里accept客户端,并设置监听函数client_cb,当收到客户端的启动流播放命令的时候就开始调用avdtp_start函数来发送命令,注意这里设置了一个回调函数a2dp_resume_complete,后面会被调用;当bluetoothd-service-audio收到了这个命令AVDTP_START的响应消息时执行下面的逻辑:
进程间传递文件描述符,内核层里面的实现,通过socket发送这个文件描述符,在内核里面把struct file信息传递给socket的peer端,它再取得一个空的fd把它和struct file关联起来,于是就实现了文件描述符传递。