Android bluetooth介绍

转自: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

[html]  view plain  copy
  1. service hciattach /system/bin/hciattach -n /dev/sttybt0 sprd_shark  
  2.     socket bluetooth stream 660 bluetooth bluetooth  
  3.     user bluetooth  
  4.     group wifi bluetooth net_bt_admin net_bt inet net_raw net_admin system  
  5.     disabled  
  6. 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

[html]  view plain  copy
  1. nt main(int argc, char *argv[])  
  2. {  
  3. ………………  
  4.     for (n = 0; optind < argc; n++, optind++) {  
  5.         char *opt;  
  6.   
  7.         opt = argv[optind];  
  8.   
  9.         switch(n) {  
  10.         case 0://(1)、解析驱动的位置;  
  11.             dev[0] = 0;  
  12.             if (!strchr(opt, '/'))  
  13.                 strcpy(dev, "/dev/");  
  14.             strcat(dev, opt);  
  15.             break;  
  16.   
  17.         case 1://(2)、解析串口的配置相关参数;  
  18.             if (strchr(argv[optind], ',')) {  
  19.                 int m_id, p_id;  
  20.                 sscanf(argv[optind], "%x,%x", &m_id, &p_id);  
  21.                 u = get_by_id(m_id, p_id);  
  22.             } else {  
  23.                 u = get_by_type(opt);  
  24.             }  
  25.   
  26.             if (!u) {  
  27.                 fprintf(stderr, "Unknown device type or id\n");  
  28.                 exit(1);  
  29.             }  
  30.   
  31.             break;  
  32.   
  33.         case 2://(3)、通过对前面参数的解析,把uart[i]中的数值初始化;  
  34.             u->speed = atoi(argv[optind]);  
  35.             break;  
  36.   
  37.         case 3:  
  38.             if (!strcmp("flow", argv[optind]))  
  39.                 u->flags |=  FLOW_CTL;  
  40.             else  
  41.                 u->flags &= ~FLOW_CTL;  
  42.             break;  
  43.   
  44.         case 4:  
  45.             if (!strcmp("sleep", argv[optind]))  
  46.                 u->pm = ENABLE_PM;  
  47.             else  
  48.                 u->pm = DISABLE_PM;  
  49.             break;  
  50.   
  51.         case 5:  
  52.             u->bdaddr = argv[optind];  
  53.             break;  
  54.         }  
  55.     }  
  56.   
  57. ………………  
  58.     if (init_speed)//初始化串口速率;  
  59.         u->init_speed = init_speed;  
  60. ………………  
  61.     n = init_uart(dev, u, send_break, raw);//(4)、初始化串口;  
  62. ………………  
  63.   
  64.     return 0;  
  65. }  

(1)、解析驱动的位置;

[html]  view plain  copy
  1.             if (!strchr(opt, '/'))  
  2.                 strcpy(dev, "/dev/");  
  3. service hciattach /system/bin/hciattach -n /dev/sttybt0 sprd_shark  
  4. dev = /dev/ttyb0  

(2)、解析串口的配置相关参数;获取参数对应的结构体;

[html]  view plain  copy
  1.     u = get_by_id(m_id, p_id);  
  2. static struct uart_t * get_by_id(int m_id, int p_id)  
  3. {  
  4.     int i;  
  5.     for (i = 0; uart[i].type; i++) {  
  6.         if (uart[i].m_id == m_id && uart[i].p_id == p_id)  
  7.             return &uart[i];  
  8.     }  
  9.     return NULL;  
  10. }  

这个函数比较简单,通过循环对比,如传进了的参数sprd_shark和uart结构体中的对比,找到对应的数组。如果是其他蓝牙芯片,如博通、RDA、BEKN等着到其相对应的初始化配置函数。

[html]  view plain  copy
  1. struct uart_t uart[] = {  
  2.     { "any",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200,  
  3.                 FLOW_CTL, DISABLE_PM, NULL, NULL     },  
  4.     { "sprd_shark",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200,  
  5.                 FLOW_CTL, DISABLE_PM, NULL, init_sprd_config     },  
  6.   
  7.     { "ericsson",   0x0000, 0x0000, HCI_UART_H4,   57600,  115200,  
  8.                 FLOW_CTL, DISABLE_PM, NULL, ericsson },  
  9.   
  10. ………………  
  11.     { "bk3211",    0x0000, 0x0000, HCI_UART_BCSP,   115200, 921600, 0, DISABLE_PM,   NULL, beken_init, NULL},  
  12.     { NULL, 0 }  
  13. };  

注意:init_sprd_config这个函数在uart_init中用到,这个函数其实对我们具体芯片的初始化配置。
注释:HCI_UART_H4和HCI_UART_BCSP的区别如下图。

(3)、通过对前面参数的解析,把uart[i]中的数值初始化;

[html]  view plain  copy
  1. u->speed = atoi(argv[optind]);  
  2. break;  

(4)、初始化串口;

[html]  view plain  copy
  1. n = init_uart(dev, u, send_break, raw);  
  2. idh.code\external\bluetooth\bluez\tools\hciattach.c  
  3. /* Initialize UART driver */  
  4. int init_uart(char *dev, struct uart_t *u, int send_break)  
  5. {  
  6.  struct termios ti;  
  7.  int  fd, i;  
  8.  fd = open(dev, O_RDWR | O_NOCTTY);//打开串口设备,其中标志  
  9. //O_RDWR,可以对此设备进行读写操作;  
  10. //O_NOCTTY:告诉Unix这个程序不想成为“控制终端”控制的程序,不说明这个标志的话,任何输入都会影响你的程序。  
  11. //O_NDELAY:告诉Unix这个程序不关心DCD信号线状态,即其他端口是否运行,不说明这个标志的话,该程序就会在DCD信号线为低电平时停止。  
  12. //但是不要以控制 tty 的模式,因为我们并不希望在发送 Ctrl-C  
  13.  后结束此进程  
  14.  if (fd < 0) {  
  15.   perror(“Can’t open serial port”);  
  16.   return -1;  
  17.  }  
  18.  //drop fd’s data;  
  19.  tcflush(fd, TCIOFLUSH);//清空数据线  
  20.  if (tcgetattr(fd, &ti) < 0) {  
  21.   perror(“Can’t get port settings”);  
  22.   return -1;  
  23.  }  
  24.  cfmakeraw(&ti);  
  25. cfmakeraw sets the terminal attributes as follows://此函数设置串口终端的以下这些属性,  
  26. termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP  
  27. |INLCR|IGNCR|ICRNL|IXON);  
  28. termios_p->c_oflag &= ~OPOST;  
  29. termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);  
  30. termios_p->c_cflag &= ~(CSIZE|PARENB) ;  
  31. termios_p->c_cflag |=CS8;  
  32.  ti.c_cflag |= CLOCAL;//本地连接,无调制解调器控制  
  33.  if (u->flags & FLOW_CTL)  
  34.   ti.c_cflag |= CRTSCTS;//输出硬件流控  
  35.  else  
  36.   ti.c_cflag &= ~CRTSCTS;  
  37.  if (tcsetattr(fd, TCSANOW, &ti) < 0) {//启动新的串口设置  
  38.   perror(“Can’t set port settings”);  
  39.   return -1;  
  40.  }  
  41.  /* Set initial baudrate */  
  42.  if (set_speed(fd, &ti, u->init_speed) < 0) {//设置串口的传输速率bps, 也可以使  
  43. //用 cfsetispeed 和 cfsetospeed 来设置  
  44.   perror(“Can’t set initial baud rate”);  
  45.   return -1;  
  46.  }  
  47.  tcflush(fd, TCIOFLUSH);//清空数据线  
  48.  if (send_break)  
  49.   tcsendbreak(fd, 0);  
  50. //int tcsendbreak ( int fd, int duration );Sends a break for  
  51. //the given time.在串口线上发送0值,至少维持0.25秒。  
  52. //If duration is 0, it transmits zero-valued bits for at least 0.25 seconds, and  
  53. //not more than 0.5seconds.  
  54.  //where place register u’s init function;  
  55.  if (u->init && u->init(fd, u, &ti) < 0)  
  56. //所有bluez支持的蓝牙串口设备类型构成了一个uart结构数组,通过  
  57. //查找对应的uart类型,这个uart的init成员显示了它的init调用方法;  
  58. struct uart_t uart[] = {  
  59. { "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,FLOW_CTL, DISABLE_PM, NULL, NULL     },  
  60. { "sprd_shark", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,FLOW_CTL, DISABLE_PM, NULL, init_sprd_config     },  
  61.   
  62. { "ericsson", 0x0000, 0x0000, HCI_UART_H4,   57600,  115200,FLOW_CTL, DISABLE_PM, NULL, ericsson },  
  63. ………………  
  64.     { "bk3211",    0x0000, 0x0000, HCI_UART_BCSP,   115200, 921600, 0, DISABLE_PM,   NULL, beken_init, NULL},  
  65.     { NULL, 0的init函数名为bcsp,定义在本文件中**;  
  66.   return -1;  
  67.  tcflush(fd, TCIOFLUSH);//清空数据线  
  68.  /* Set actual baudrate */  
  69.  if (set_speed(fd, &ti, u->speed) < 0) {  
  70.   perror(“Can’t set baud rate”);  
  71.   return -1;  
  72.  }  
  73.  /* Set TTY to N_HCI line discipline */  
  74.  i = N_HCI;  
  75.  if (ioctl(fd, TIOCSETD, &i) < 0) {//  
  76. TIOCSETD int *ldisc//改变到 i 行规,即hci行规  
  77. Change to the new line discipline pointed to by ldisc. The available line disciplines are listed in   
  78. /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */  
  79. /* line disciplines */  
  80. #define N_TTY  0  
  81. ……  
  82. #define N_HCI  15  /* Bluetooth HCI UART */  
  83.   
  84.   perror(“Can’t set line discipline”);  
  85.   return -1;  
  86.  }  
  87.  if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {  
  88. //设置hci设备的proto操作函数集为hci_uart操作集;  
  89.   perror(“Can’t set device”);  
  90.   return -1;  
  91.  }  
  92.  return fd;  
  93. }  

这里一个重要的部分是:u->init指向init_sprd_config
4、uart具体到芯片的初始化init_sprd_config(这部分根据不同的芯片,对应进入其相应初始化部分)
idh.code\external\bluetooth\bluez\tools\hciattach_sprd.c

[html]  view plain  copy
  1. int sprd_config_init(int fd, char *bdaddr, struct termios *ti)  
  2. {  
  3.     int i,psk_fd,fd_btaddr,ret = 0,r,size=0,read_btmac=0;  
  4.     unsigned char resp[30];  
  5.     BT_PSKEY_CONFIG_T bt_para_tmp;  
  6.     char bt_mac[30] = {0};  
  7.     char bt_mac_tmp[20] = {0};  
  8.     uint8 bt_mac_bin[32]     = {0};  
  9.   
  10.     fprintf(stderr,"init_sprd_config in \n");  
  11. //(1)、这部分检查bt_mac,如果存在,从文件中读取,如果不存在,随机生成,并写入相应文件;  
  12.     if(access(BT_MAC_FILE, F_OK) == 0) {//这部分检查bt_mac  
  13.         LOGD("%s: %s exists",__FUNCTION__, BT_MAC_FILE);  
  14.         fd_btaddr = open(BT_MAC_FILE, O_RDWR);// #define BT_MAC_FILE        "/productinfo/btmac.txt"  
  15.         if(fd_btaddr>=0) {  
  16.             size = read(fd_btaddr, bt_mac, sizeof(bt_mac));//读取BT_MAC_FILE中的地址;  
  17.             LOGD("%s: read %s %s, size=%d",__FUNCTION__, BT_MAC_FILE, bt_mac, size);  
  18.             if(size == BT_RAND_MAC_LENGTH){  
  19.                         LOGD("bt mac already exists, no need to random it");  
  20.                         fprintf(stderr, "read btmac ok \n");  
  21.                         read_btmac=1;  
  22.             }  
  23. …………  
  24.     }else{//如果不存在,就随机生成一个bt_mac地址,写入/productinfo/btmac.txt  
  25.         fprintf(stderr, "btmac.txt not exsit!\n");  
  26.         read_btmac=0;  
  27.         mac_rand(bt_mac);  
  28.         LOGD("bt random mac=%s",bt_mac);  
  29.         printf("bt_mac=%s\n",bt_mac);  
  30.         write_btmac2file(bt_mac);  
  31.   
  32.         fd_btaddr = open(BT_MAC_FILE, O_RDWR);  
  33.         if(fd_btaddr>=0) {  
  34.             size = read(fd_btaddr, bt_mac, sizeof(bt_mac));  
  35.             LOGD("%s: read %s %s, size=%d",__FUNCTION__, BT_MAC_FILE, bt_mac, size);  
  36.             if(size == BT_RAND_MAC_LENGTH){  
  37.                         LOGD("bt mac already exists, no need to random it");  
  38.                         fprintf(stderr, "read btmac ok \n");  
  39.                         read_btmac=1;  
  40.             }  
  41.             close(fd_btaddr);  
  42. …………  
  43.     }  
  44.   
  45.     /* Reset the BT Chip */  
  46.   
  47.     memset(resp, 0, sizeof(resp));  
  48.     memset(&bt_para_tmp, 0, sizeof(BT_PSKEY_CONFIG_T) );  
  49.     ret = getPskeyFromFile(  (void *)(&bt_para_tmp) );//ret = get_pskey_from_file(&bt_para_tmp);//(2)、PSKey参数、射频参数的设定;  
  50.        if(ret != 0){//参数失败处理  
  51.             fprintf(stderr, "get_pskey_from_file faill \n");  
  52.             /* Send command from hciattach*/  
  53.             if(read_btmac == 1){  
  54.                 memcpy(bt_para_setting.device_addr, bt_mac_bin, sizeof(bt_para_setting.device_addr));// (3)、读取失败,把bt_para_setting中defaut参数写入;  
  55.             }  
  56.             if (write(fd, (char *)&bt_para_setting, sizeof(BT_PSKEY_CONFIG_T)) != sizeof(BT_PSKEY_CONFIG_T)) {  
  57.                 fprintf(stderr, "Failed to write reset command\n");  
  58.                 return -1;  
  59.             }  
  60.         }else{//getpskey成功处理  
  61.             /* Send command from pskey_bt.txt*/  
  62.             if(read_btmac == 1){  
  63.                 memcpy(bt_para_tmp.device_addr, bt_mac_bin, sizeof(bt_para_tmp.device_addr));  
  64.             }  
  65. …………  
  66.     return 0;  
  67. }  

(1)、这部分检查bt_mac,如果存在,从文件中读取,如果不存在,随机生成,并写入相应文件/productinfo/btmac.txt
(2)、PSKey参数、射频参数的设定;
get_pskey_from_file(&bt_para_tmp);这个函数后面分析;
(3)、读取失败,把bt_para_setting中defaut参数写入;频率、主从设备设定等……

[html]  view plain  copy
  1. // pskey file structure default value  
  2. BT_PSKEY_CONFIG_T bt_para_setting={  
  3. 5,  
  4. 0,  
  5. 0,  
  6. 0,  
  7. 0,  
  8. 0x18cba80,  
  9. 0x001f00,  
  10. 0x1e,  
  11. {0x7a00,0x7600,0x7200,0x5200,0x2300,0x0300},  
  12. …………  
  13. };  

5、get_pskey_from_file 解析相关射频参数
idh.code\external\bluetooth\bluez\tools\pskey_get.c

[html]  view plain  copy
  1. int getPskeyFromFile(void *pData)  
  2. {  
  3. …………  
  4.         char *BOARD_TYPE_PATH = "/dev/board_type";//(1)、判断PCB的版本;  
  5.         int fd_board_type;  
  6.         char board_type_str[MAX_BOARD_TYPE_LEN] = {0};  
  7.         int board_type;  
  8.         char *CFG_2351_PATH_2 = "/productinfo/2351_connectivity_configure.ini";//(2)、最终生成ini文件存储的位置;  
  9.         char *CFG_2351_PATH[MAX_BOARD_TYPE];  
  10.         (3)、针对不同PCB版本,不同的ini配置文件;  
  11.         CFG_2351_PATH[0] = "/system/etc/wifi/2351_connectivity_configure_hw100.ini";  
  12.         CFG_2351_PATH[1] = "/system/etc/wifi/2351_connectivity_configure_hw102.ini";  
  13.         CFG_2351_PATH[2] = "/system/etc/wifi/2351_connectivity_configure_hw104.ini";  

(4)、下面函数就不做具体分析,大致意识是,根据/dev/board_type中,读取的PCB类型,设置不同的ini文件。   

[html]  view plain  copy
  1. ………………  
  2.     ret = chmod(CFG_2351_PATH_2, 0644);  
  3.     ALOGE("chmod 0664 %s ret:%d\n", CFG_2351_PATH_2, ret);    
  4.     if(pBuf == pBuf2)  
  5.         free(pBuf1);  
  6. ………………  
  7. }  

(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配置文件;

[html]  view plain  copy
  1. CFG_2351_PATH[0] = "/system/etc/wifi/2351_connectivity_configure_hw100.ini";  
  2. CFG_2351_PATH[1] = "/system/etc/wifi/2351_connectivity_configure_hw102.ini";  
  3. 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

[html]  view plain  copy
  1. static int __init hci_uart_init(void)  
  2. {  
  3.     static struct tty_ldisc_ops hci_uart_ldisc;  
  4.     int err;  
  5.     /* Register the tty discipline */  
  6.   
  7.     memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));  
  8.     hci_uart_ldisc.magic        = TTY_LDISC_MAGIC;  
  9.     hci_uart_ldisc.name     = "n_hci";  
  10.     hci_uart_ldisc.open     = hci_uart_tty_open;  
  11.     hci_uart_ldisc.close        = hci_uart_tty_close;  
  12.     hci_uart_ldisc.read     = hci_uart_tty_read;  
  13.     hci_uart_ldisc.write        = hci_uart_tty_write;  
  14.     hci_uart_ldisc.ioctl        = hci_uart_tty_ioctl;  
  15.     hci_uart_ldisc.poll     = hci_uart_tty_poll;  
  16.     hci_uart_ldisc.receive_buf  = hci_uart_tty_receive;  
  17.     hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup;  
  18.     hci_uart_ldisc.owner        = THIS_MODULE;  
  19.   
  20.     if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {//(1)、这部分完成ldisc的注册;  
  21.         BT_ERR("HCI line discipline registration failed. (%d)", err);  
  22.         return err;  
  23.     }  
  24.   
  25. #ifdef CONFIG_BT_HCIUART_H4  
  26.     h4_init();//(2)、我们蓝牙芯片用的是H4,这部分完成H4的注册;  
  27. #endif  
  28. #ifdef CONFIG_BT_HCIUART_BCSP  
  29.     bcsp_init();  
  30. #endif  
  31. ………………  
  32.     return 0;  
  33. }  

(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

[html]  view plain  copy
  1. static struct hci_uart_proto h4p = {  
  2.     .id     = HCI_UART_H4,  
  3.     .open       = h4_open,  
  4.     .close      = h4_close,  
  5.     .recv       = h4_recv,  
  6.     .enqueue    = h4_enqueue,  
  7.     .dequeue    = h4_dequeue,  
  8.     .flush      = h4_flush,  
  9. };  

H4的注册:
idh.code\kernel\drivers\bluetooth\hci_h4.c

[html]  view plain  copy
  1. int __init h4_init(void)  
  2. {  
  3.     int err = hci_uart_register_proto(&h4p);  
  4.   
  5.     if (!err)  
  6.         BT_INFO("HCI H4 protocol initialized");  
  7.     else  
  8.         BT_ERR("HCI H4 protocol registration failed");  
  9.   
  10.     return err;  
  11. }  

这是通过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的数据接收
它基本上就是要个二传手,通过:

[html]  view plain  copy
  1. spin_lock(&hu->rx_lock);  
  2. hu->proto->recv(hu,(void *) data, count);  
  3. hu->hdev->stat.byte_rx+= count;  
  4. 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

[html]  view plain  copy
  1. static const struct proto_ops hci_sock_ops = {  
  2. …………  
  3.     .ioctl      = hci_sock_ioctl,  
  4.     .poll       = datagram_poll,  
  5.     .listen     = sock_no_listen,  
  6. …………  
  7. };  


它的流程就是构造查询命令,放入命令队列,调度队列来发送命令,其中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

[html]  view plain  copy
  1. @Override  
  2.   public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,  
  3.       Preference preference) {  
  4.     …………  
  5.       mLocalAdapter.startScanning(true);  
  6.       return true;  
  7.     }  

2、 idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\LocalBluetoothAdapter.java

[html]  view plain  copy
  1.  private final BluetoothAdapter mAdapter;  
  2.  void startScanning(boolean force) {  
  3.         // Only start if we're not already scanning  
  4.         if (!mAdapter.isDiscovering()) {  
  5.             if (!force) {  
  6.                 // Don't scan more than frequently than SCAN_EXPIRATION_MS,  
  7.                 // unless forced  
  8.                 if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {  
  9.                     return;  
  10.                 }  
  11.                 // If we are playing music, don't scan unless forced.  
  12.                 A2dpProfile a2dp = mProfileManager.getA2dpProfile();  
  13.                 if (a2dp != null && a2dp.isA2dpPlaying()) {  
  14.                     return;  
  15.                 }  
  16.             }  
  17.             if (mAdapter.startDiscovery()) {  
  18.                 mLastScan = System.currentTimeMillis();  
  19.             }  
  20.         }  
  21. }   

3、idh.code\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java

[html]  view plain  copy
  1. public boolean startDiscovery() {  
  2.     if (getState() != STATE_ON) return false;  
  3.     try {  
  4.         return mService.startDiscovery();  
  5.     } catch (RemoteException e) {Log.e(TAG, "", e);}  
  6.     return false;  
  7. }  

4、JNI函数的调用idh.code\frameworks\base\core\java\android\server\BluetoothService.java

[html]  view plain  copy
  1. private native boolean startDiscoveryNative();//Native函数声明  
  2. public class BluetoothService extends IBluetooth.Stub {  
  3.     private static final String TAG = "BluetoothService";  
  4.     private static final boolean DBG = true;  
  5. …………  
  6. public synchronized boolean startDiscovery() {  
  7. mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,  
  8.         "Need BLUETOOTH_ADMIN permission");  
  9.         if (!isEnabledInternal()) return false;  
  10.         return startDiscoveryNative();  
  11. }  
  12. ………………  
  13. }  

(二)、JNI部分:
1、android_server_BluetoothService.cpp中JNI函数的对照表
idh.code\frameworks\base\core\jni\android_server_BluetoothService.cpp

[html]  view plain  copy
  1. static JNINativeMethod sMethods[] = {  
  2.      /* name, signature, funcPtr */  
  3.    ………………  
  4.     {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},  
  5. {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},   
  6. …………  
  7. }  

2、对应Native函数的实现
这里面有个知识点DBUS,这个后面我们单独去讲解
idh.code\frameworks\base\core\jni\android_server_BluetoothService.cpp

[html]  view plain  copy
  1. #define BLUEZ_DBUS_BASE_IFC       "org.bluez"  
  2. #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"//其实DBUS_ADAPTER_IFACE 也就是 org.bluez.Adapter  
  3. static jboolean startDiscoveryNative(JNIEnv *env, jobject object) {  
  4.   
  5. ………………  
  6.     /* Compose the command */  
  7.     msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,  
  8.                                        get_adapter_path(env, object),  
  9.                                        DBUS_ADAPTER_IFACE, "StartDiscovery");  
  10. …………  
  11. }  
  12. Native函数startDiscoveryNative和字符串StartDiscovery对应。  

(三)、DBUS部分

1、DBUS对应方法的实现,这里跟JNI部分比较类似,也是用了函数结构体对应关系。
idh.code\external\bluetooth\bluez\src\adapter.c

[html]  view plain  copy
  1. #define ADAPTER_INTERFACE   "org.bluez.Adapter"  
  2. static GDBusMethodTable adapter_methods[] = {  
  3. ………………  
  4.     { "ReleaseSession", "", "", release_session     },  
  5.     { "StartDiscovery", "", "", adapter_start_discovery },  
  6.     { "StopDiscovery",  "", "", adapter_stop_discovery,  
  7.                         G_DBUS_METHOD_FLAG_ASYNC},  
  8. ………………  
  9. }  

字符StartDiscovery又对应C中的实现函数adapter_start_discovery。
2、adapter_start_discovery的实现
idh.code\external\bluetooth\bluez\src\adapter.c

[html]  view plain  copy
  1. static DBusMessage *adapter_start_discovery(DBusConnection *conn,  
  2.                         DBusMessage *msg, void *data)  
  3. {  
  4. …………  
  5.     err = start_discovery(adapter);  
  6.     if (err < 0 && err != -EINPROGRESS)  
  7.         return btd_error_failed(msg, strerror(-err));  
  8.   
  9. done:  
  10.     req = create_session(adapter, conn, msg, 0,  
  11.                 session_owner_exit);  
  12.   
  13.     adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req);  
  14.   
  15.     return dbus_message_new_method_return(msg);  
  16. }  

3、 start_discovery调用
idh.code\external\bluetooth\bluez\src\adapter.c

[html]  view plain  copy
  1. const struct btd_adapter_ops *adapter_ops = NULL;  
  2. static int start_discovery(struct btd_adapter *adapter)  
  3. {  
  4. …………  
  5.     pending_remote_name_cancel(adapter);  
  6.     return adapter_ops->start_discovery(adapter->dev_id);  
  7. }  

adapter_ops对应结构体btd_adapter_ops中对应函数,如下:上面部分就对应到btd_adapter_ops中的hci_ops结构体。
4、btd_adapter_ops中的hci_ops结构体
idh.code\external\bluetooth\bluez\plugins\hciops.c

[html]  view plain  copy
  1. static struct btd_adapter_ops hci_ops = {  
  2. …………  
  3.     .set_powered = hciops_set_powered,  
  4.     .set_discoverable = hciops_set_discoverable,  
  5.     .set_pairable = hciops_set_pairable,  
  6.     .set_limited_discoverable = hciops_set_limited_discoverable,  
  7.     .start_discovery = hciops_start_discovery,  
  8.     .stop_discovery = hciops_stop_discovery,  
  9.     ………………  
  10.     .create_bonding = hciops_create_bonding,  
  11.     .cancel_bonding = hciops_cancel_bonding,  
  12.     .read_local_oob_data = hciops_read_local_oob_data,  
  13.     .add_remote_oob_data = hciops_add_remote_oob_data,  
  14.     .remove_remote_oob_data = hciops_remove_remote_oob_data,  
  15.     .set_link_timeout = hciops_set_link_timeout,  
  16.     .retry_authentication = hciops_retry_authentication,  
  17. };  

5、hciops_start_discovery函数的实现
idh.code\external\bluetooth\bluez\plugins\hciops.c

[html]  view plain  copy
  1. static int hciops_start_discovery(int index)  
  2. {  
  3.     int adapter_type = get_adapter_type(index);  
  4.   
  5.     switch (adapter_type) {  
  6.     case BR_EDR_LE:  
  7.         return hciops_start_inquiry(index, LENGTH_BR_LE_INQ);  
  8.     case BR_EDR: //蓝牙芯片为2.1+EDR的  
  9.         return hciops_start_inquiry(index, LENGTH_BR_INQ);  
  10.     case LE_ONLY:  
  11.         return hciops_start_scanning(index, TIMEOUT_LE_SCAN);  
  12.     default:  
  13.         return -EINVAL;  
  14.     }  
  15. }  

6、hciops_start_inquiry
idh.code\external\bluetooth\bluez\plugins\hciops.c

[html]  view plain  copy
  1. static int hciops_start_inquiry(int index, uint8_t length)  
  2. {  
  3.     struct dev_info *dev = &devs[index];  
  4.     uint8_t lap[3] = { 0x33, 0x8b, 0x9e };  
  5.     inquiry_cp inq_cp;  
  6.   
  7.     DBG("hci%d length %u", index, length);  
  8.   
  9.     memset(&inq_cp, 0, sizeof(inq_cp));  
  10.     memcpy(&inq_cp.lap, lap, 3);  
  11.     inq_cp.length = length;  
  12.     inq_cp.num_rsp = 0x00;  
  13.   
  14.     if (hci_send_cmd(dev->sk, OGF_LINK_CTL,  
  15.             OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp) < 0)  
  16.         return -errno;  
  17.   
  18.     return 0;  
  19. }  

7、idh.code\external\bluetooth\bluez\lib\hci.c

[html]  view plain  copy
  1. /* HCI functions that require open device  
  2.  * dd - Device descriptor returned by hci_open_dev. */  
  3. dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);  
  4. int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)  
  5. {  
  6. ………………  
  7.     if (plen) {  
  8.         iv[2].iov_base = param;  
  9.         iv[2].iov_len  = plen;  
  10.         ivn = 3;  
  11.     }  
  12.   
  13.     while (writev(dd, iv, ivn) < 0) {//writev这里把数据写到socket里面。  
  14.         if (errno == EAGAIN || errno == EINTR)  
  15.             continue;  
  16.         return -1;  
  17.     }  
  18.     return 0;  
  19. }  

(四)、内核部分:
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

[html]  view plain  copy
  1. static const struct proto_ops hci_sock_ops = {  
  2.     .family     = PF_BLUETOOTH,  
  3.     .owner      = THIS_MODULE,  
  4. …………  
  5.     .shutdown   = sock_no_shutdown,  
  6.     .setsockopt = hci_sock_setsockopt,  
  7.     .getsockopt = hci_sock_getsockopt,  
  8.     .connect    = sock_no_connect,  
  9. …………  
  10. };  

idh.code\kernel\net\bluetooth\hci_sock.c

[html]  view plain  copy
  1. static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int len)  
  2. {  
  3. ………………  
  4.     case HCI_FILTER:  
  5.         {  
  6.             struct hci_filter *f = &hci_pi(sk)->filter;  
  7.   
  8.             uf.type_mask = f->type_mask;  
  9.             uf.opcode    = f->opcode;  
  10.             uf.event_mask[0] = *((u32 *) f->event_mask + 0);  
  11.             uf.event_mask[1] = *((u32 *) f->event_mask + 1);  
  12.         }  
  13.     ………………  
  14. }<span style="font-weight:bold;">  
  15. span>  

内核这部分就比较统一的数据,通过hci_send_cmd把命令发出去,HCI_FILTER这个地方的处理还没理解,后面补充
Writev函数通过socket把数据写下去,经过VFS层,调用到内核空间的sendmsg函数。

(五)、EVENT返回状态

Controller收到查询命令后,返回一个命令状态
1、cmd_status
idh.code\external\bluetooth\bluez\plugins\hciops.c

[html]  view plain  copy
  1.     switch (eh->evt) {  
  2.     case EVT_CMD_STATUS:  
  3.         cmd_status(index, ptr);  
  4.         break;  
  5. static inline void cmd_status(int index, void *ptr)  
  6. {  
  7.     evt_cmd_status *evt = ptr;  
  8.     uint16_t opcode = btohs(evt->opcode);  
  9.   
  10.     if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))//如果是inquriy做特殊处理;  
  11.         cs_inquiry_evt(index, evt->status);  
  12. }  

2、cs_inquiry_evt的实现 idh.code\external\bluetooth\bluez\plugins\hciops.c

[html]  view plain  copy
  1. static inline void cs_inquiry_evt(int index, uint8_t status)  
  2. {  
  3.     if (status) {//错误信息  
  4.         error("Inquiry Failed with status 0x%02x", status);  
  5.         return;  
  6.     }  
  7.   
  8.     set_state(index, DISCOV_INQ);//设置状态为INQ,向上层回复discoverying的property change  
  9. }  

3、设置不同的DISCOV 状态 idh.code\external\bluetooth\bluez\plugins\hciops.c

[html]  view plain  copy
  1. static void set_state(int index, int state)  
  2. {  
  3.     ………………  
  4.     switch (dev->discov_state) {  
  5.     case DISCOV_HALTED://停止发现;  
  6.         if (adapter_get_state(adapter) == STATE_SUSPENDED)  
  7.             return;  
  8.   
  9.         if (is_resolvname_enabled() &&  
  10.                     adapter_has_discov_sessions(adapter))  
  11.             adapter_set_state(adapter, STATE_RESOLVNAME);  
  12.         else  
  13.             adapter_set_state(adapter, STATE_IDLE);  
  14.         break;  
  15.     case DISCOV_INQ:  
  16.     case DISCOV_SCAN://扫描发现;  
  17.         adapter_set_state(adapter, STATE_DISCOV);  
  18.         break;  
  19.     }  
  20. }  

4、设置adapter的状态 idh.code\external\bluetooth\bluez\src\adapter.c

[html]  view plain  copy
  1. idh.code\external\bluetooth\bluez\src\adapter.c  
  2. #define ADAPTER_INTERFACE   "org.bluez.Adapter"  
  3. void adapter_set_state(struct btd_adapter *adapter, int state)  
  4. {  
  5. …………  
  6.     case STATE_DISCOV:  
  7.         discov_active = TRUE;  
  8. //向上层回复discovering的property change  
  9.         emit_property_changed(connection, path,  
  10.                     ADAPTER_INTERFACE, "Discovering",  
  11.                     DBUS_TYPE_BOOLEAN, &discov_active);  
  12.         break;  
  13. …………  
  14. }  

emit_property_changed发送PropertyChanged的消息,消息内容为Discovering。通知上层BluetoothEventLoop进行Discovering。
5、emit_property_changed发送Discovering消息的实现 idh.code\external\bluetooth\bluez\src\dbus-common.c
这部分涉及到DBUS内容

[html]  view plain  copy
  1. dbus_bool_t emit_property_changed(DBusConnection *conn,  
  2.                     const char *path,  
  3.                     const char *interface,  
  4.                     const char *name,  
  5.                     int type, void *value)  
  6. {  
  7.     DBusMessage *signal;  
  8.     DBusMessageIter iter;  
  9.     signal = dbus_message_new_signal(path, interface, "PropertyChanged"); // 创建消息对象并标识路径   
  10.     if (!signal) {  
  11.         error("Unable to allocate new %s.PropertyChanged signal",  
  12.                 interface);  
  13.         return FALSE;  
  14.     }  
  15.     dbus_message_iter_init_append(signal, &iter);//把信号相对应的参数压进去  
  16.     dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);//申请一个首地址,把前面压入的参数传入这个首地址      
  17. append_variant(&iter, type, value);//  
  18.     return g_dbus_send_message(conn, signal);//启动发送调用,并释放发送相关消息信息  
  19. }  

6、DBUS消息接收的实现 idh.code\frameworks\base\core\jni\android_server_BluetoothEventLoop.cpp

[html]  view plain  copy
  1. // Called by dbus during WaitForAndDispatchEventNative()  
  2. static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,  
  3.                                       void *data) {  
  4. …………  
  5. else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "PropertyChanged")) {  
  6.         jobjectArray str_array = parse_adapter_property_change(env, msg);//(1)、对收到消息的解析  
  7.         if (str_array != NULL) {  
  8.             /* Check if bluetoothd has (re)started, if so update the path. */  
  9.             jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);  
  10.             const char *c_property = env->GetStringUTFChars(property, NULL);  
  11.             if (!strncmp(c_property, "Powered", strlen("Powered"))) {  
  12.                 jstring value =  
  13.                     (jstring) env->GetObjectArrayElement(str_array, 1);  
  14.                 const char *c_value = env->GetStringUTFChars(value, NULL);  
  15.                 if (!strncmp(c_value, "true", strlen("true")))  
  16.                     nat->adapter = get_adapter_path(nat->conn);  
  17.                 env->ReleaseStringUTFChars(value, c_value);  
  18.             }  
  19.             env->ReleaseStringUTFChars(property, c_property);  
  20.   
  21.             env->CallVoidMethod(nat->me,  
  22.                               method_onPropertyChanged,//(2)、  
  23. method_onPropertyChanged NATVIE函数的实现  
  24.                               str_array);  
  25.         } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);  
  26.         goto success;  
  27. }  

(1)、对收到消息的解析 idh.code\frameworks\base\core\jni\android_bluetooth_common.cpp 

[html]  view plain  copy
  1. jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) {  
  2.     return parse_property_change(env, msg, (Properties *) &adapter_properties,  
  3.                     sizeof(adapter_properties) / sizeof(Properties));  
  4. }  

针对org.bluez.Adapter不同的消息类型
idh.code\frameworks\base\core\jni\android_bluetooth_common.cpp

[html]  view plain  copy
  1. static Properties adapter_properties[] = {  
  2.     {"Address", DBUS_TYPE_STRING},  
  3.     {"Name", DBUS_TYPE_STRING},  
  4.     {"Class", DBUS_TYPE_UINT32},  
  5.     {"Powered", DBUS_TYPE_BOOLEAN},  
  6.     {"Discoverable", DBUS_TYPE_BOOLEAN},  
  7.     {"DiscoverableTimeout", DBUS_TYPE_UINT32},  
  8.     {"Pairable", DBUS_TYPE_BOOLEAN},  
  9.     {"PairableTimeout", DBUS_TYPE_UINT32},  
  10.     {"Discovering", DBUS_TYPE_BOOLEAN},  
  11.     {"Devices", DBUS_TYPE_ARRAY},  
  12.     {"UUIDs", DBUS_TYPE_ARRAY},  
  13. };  

(2)、method_onPropertyChanged NATVIE函数的实现 idh.code\frameworks\base\core\jni\android_server_BluetoothEventLoop.cpp

[html]  view plain  copy
  1. static void classInitNative(JNIEnv* env, jclass clazz) {  
  2.     ALOGV("%s", __FUNCTION__);  
  3. #ifdef HAVE_BLUETOOTH  
  4.     method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",  
  5.                                                 "([Ljava/lang/String;)V");  
  6. method_onDevicePropertyChanged = env->GetMethodID(clazz,  
  7.  "onDevicePropertyChanged","(Ljava/lang/String;[Ljava/lang/String;)V");  
  8. …………  
  9. }  

7、JNI调用onPropertyChanged对应JAVA的实现,在BluetoothEventLoop.java
idh.code\frameworks\base\core\java\android\server\BluetoothEventLoop.java中

[html]  view plain  copy
  1.   
[html]  view plain  copy
  1.   private static native void classInitNative();  
  2. *package*/ void onPropertyChanged(String[] propValues) {  
  3. ………………  
  4.        log("Property Changed: " + propValues[0] + " : " + propValues[1]);  
  5.        String name = propValues[0];  
  6.        if (name.equals("Name")) {//获取蓝牙名字;  
  7.             …………  
  8.        } else if (name.equals("Pairable") || name.equals("Discoverable")) {//配对;  
  9.           ………………  
  10.        } else if (name.equals("Discovering")) {//扫描查询;  
  11.            Intent intent;  
  12.            adapterProperties.setProperty(name, propValues[1]);  
  13.            if (propValues[1].equals("true")) {  
  14.                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);  
  15.            } else {  
  16.                // Stop the discovery.  
  17.                mBluetoothService.cancelDiscovery();  
  18.                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);  
  19.            }  
  20.            mContext.sendBroadcast(intent, BLUETOOTH_PERM);  
  21.        } else if (name.equals("Devices") || name.equals("UUIDs")) {//Devices、UUID的获取;  
  22.         ………………  
  23.        } else if (name.equals("Powered")) {//蓝牙打开、关闭;  
  24.            mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,  
  25.                propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));  
  26.        } else if (name.equals("DiscoverableTimeout")) {  
  27.            adapterProperties.setProperty(name, propValues[1]);  
  28.        }  
  29.    }   

(1)、看到这份log我们也许会更明白其他功能的由来:
[html]  view plain  copy
  1. D BluetoothEventLoop: Property Changed: Powered : true  
  2. D BluetoothEventLoop: Property Changed: Pairable : true  
  3. D BluetoothEventLoop: Property Changed: Class : 5898764  
  4. D BluetoothEventLoop: Property Changed: Pairable : true  
  5. D BluetoothEventLoop: Property Changed: Discoverable : false  
  6. D BluetoothEventLoop: Property Changed: Discovering : true  
  7. D BluetoothEventLoop: Property Changed: Discovering : false  
  8. D BluetoothEventLoop: Property Changed: Devices : 1  
  9. D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Connected value: true  
  10. D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Paired value: true  
  11. 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

[html]  view plain  copy
  1. else if (name.equals("Discovering")) {  
  2.             Intent intent;  
  3.             adapterProperties.setProperty(name, propValues[1]);  
  4.             if (propValues[1].equals("true")) {//开始扫描  
  5.                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//  
  6.             } else {  
  7.                 // Stop the discovery. //停止扫描  
  8.                 mBluetoothService.cancelDiscovery();  
  9.                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);  
  10.             }  
  11.             mContext.sendBroadcast(intent, BLUETOOTH_PERM);  
  12.         }  
  13. 这样就可以通过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

[html]  view plain  copy
  1. <receiver  
  2.       android:name=".bluetooth.BluetoothDiscoveryReceiver">  
  3.       <intent-filter>  
  4.         <action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />  
  5.         <action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />  
  6.         <category android:name="android.intent.category.DEFAULT" />  
  7.        intent-filter>  
  8. receiver>  

1)、ACTION_DISCOVERY_STARTEDACTION_DISCOVERY_FINISHEDAndroidManifest.xml文件的联系
idh.code\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java

[html]  view plain  copy
  1. public final class BluetoothAdapter {  
  2.     private static final String TAG = "BluetoothAdapter";  
  3. private static final boolean DBG = false;  
  4. …………  
  5.     public static final String ACTION_DISCOVERY_STARTED =  
  6.             "android.bluetooth.adapter.action.DISCOVERY_STARTED";  
  7.     public static final String ACTION_DISCOVERY_FINISHED =  
  8.             "android.bluetooth.adapter.action.DISCOVERY_FINISHED";  
  9. …………  
  10. }  

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这个文件中就一个函数,还是比简单

[html]  view plain  copy
  1. public final class BluetoothDiscoveryReceiver extends BroadcastReceiver {  
  2.     private static final String TAG = "BluetoothDiscoveryReceiver";  
  3.     private static final boolean DEBUG = Debug.isDebug();  
  4.   
  5.     @Override  
  6.     public void onReceive(Context context, Intent intent) {  
  7.         String action = intent.getAction();  
  8.         if (DEBUG) Log.d(TAG, "Received: " + action);  
  9.   
  10.         if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED) ||  
  11.                 action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {      
  12. //共享时间戳,扫描开始和结束的时间。   
  13.    LocalBluetoothPreferences.persistDiscoveringTimestamp(context);  
  14.         }  
  15.     }  
  16. }  

ScanningStateChangedHandler的注册及用途,要用于开始扫描,和扫描显示界面的控制。
这个receiver是在idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothEventManager.java动态注册的,如下:

[html]  view plain  copy
  1. BluetoothEventManager(LocalBluetoothAdapter adapter,  
  2.             CachedBluetoothDeviceManager deviceManager, Context context) {  
  3. mLocalAdapter = adapter;  
  4. …………  
  5. // Bluetooth on/off broadcasts  
  6.  addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());  
  7.   
  8. // Discovery broadcastsaddHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));  
  9.         addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));  
  10. …………  
  11. }  

(1)、ScanningStateChangedHandler函数实现如下:idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothEventManager.java

[html]  view plain  copy
  1.  private class ScanningStateChangedHandler implements Handler {  
  2.         private final boolean mStarted;  
  3.   
  4.         ScanningStateChangedHandler(boolean started) {  
  5.             mStarted = started;  
  6.         }  
  7.         public void onReceive(Context context, Intent intent,  
  8.                 BluetoothDevice device) {  
  9.             synchronized (mCallbacks) {//1)、调用注册的callback  
  10. 中的onScanningStateChanged函数。  
  11.                 for (BluetoothCallback callback : mCallbacks) {  
  12.                     callback.onScanningStateChanged(mStarted);  
  13.                 }  
  14.             }  
  15. //2)、这个函数就是把上次扫描到设备、和之前的设备做相应处理;  
  16.             mDeviceManager.onScanningStateChanged(mStarted);  
  17.             LocalBluetoothPreferences.persistDiscoveringTimestamp(context);  
  18.         }  
  19. }  

1)、调用注册的callback中的callback.onScanningStateChanged(mStarted)函数。
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

[html]  view plain  copy
  1. public void onScanningStateChanged(boolean started) {  
  2.     if (started == false) {//《1》、如果扫描结束;  
  3.         removeOutOfRangeDevices();  
  4.     }  
  5.     updateProgressUi(started);// 《2》、UI显示小圆圈扫描;  

《1》、如果扫描结束;removeOutOfRangeDevices();
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

[html]  view plain  copy
  1. private void removeOutOfRangeDevices() {  
  2.     Collection<CachedBluetoothDevice> cachedDevices =  
  3.             mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();  
  4.     for (CachedBluetoothDevice cachedDevice : cachedDevices) {  
  5.          if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&  
  6.              cachedDevice.isVisible() == false) {  
  7.              BluetoothDevicePreference preference = mDevicePreferenceMap.get(cachedDevice);  
  8.              if (preference != null) {  
  9.                  mDeviceListGroup.removePreference(preference);  
  10.              }  
  11.              mDevicePreferenceMap.remove(cachedDevice);  
  12.           }  
  13.      }  
  14. }  

《2》、UI显示小圆圈扫描,updateProgressUi(started);如下图所示:

idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

[html]  view plain  copy
  1. private void updateProgressUi(boolean start) {  
  2.     if (mDeviceListGroup instanceof ProgressCategory) {  
  3.         ((ProgressCategory) mDeviceListGroup).setProgress(start);  
  4.     }  
  5. }  

2)、这部分的作用,开始扫描,不显示列表中内容,或把之前列表中没扫描到的设备清除
mDeviceManager.onScanningStateChanged(mStarted);
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\CachedBluetoothDevice.java

[html]  view plain  copy
  1. private void updateProgressUi(boolean start) {  
  2.     if (mDeviceListGroup instanceof ProgressCategory) {  
  3.         ((ProgressCategory) mDeviceListGroup).setProgress(start);  
  4.     }  
  5. }  
  6. 2)、这部分的作用,开始扫描,不显示列表中内容,或把之前列表中没扫描到的设备清除  
  7. mDeviceManager.onScanningStateChanged(mStarted);  
  8. idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\ CachedBluetoothDevice.java  
  9.     public synchronized void onScanningStateChanged(boolean started) {  
  10.         // If starting a new scan, clear old visibility  
  11.         // Iterate in reverse order since devices may be removed.  
  12.         //如果开始新的扫描,清除旧的能见设备,迭代反序因为有的设备可能被删除  
  13.         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {  
  14.             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);  
  15.             if (started) {//如果扫描开始就不显示;  
  16.                 cachedDevice.setVisible(false);  
  17.             } else {//对扫描的结果作出判断,如果之前扫描过,这次没有扫描到,就移除列表。  
  18.                 if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&  
  19.                         cachedDevice.isVisible() == false) {  
  20.                     mCachedDevices.remove(cachedDevice);  
  21.                 }  
  22.             }  
  23.         }  
  24.     }  

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 创建过程
1AVDTP l2cap建立过程

2AVDTP相关信令处理流程在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

[html]  view plain  copy
  1. static DBusMessage *sink_connect(DBusConnection *conn,  
  2.                 DBusMessage *msg, void *data)  
  3. {  
  4. …………  
  5.     if (!sink->session)//(1)、如果没有AVDTP会话,获取AVDTP连接状态;  
  6.         sink->session = avdtp_get(&dev->src, &dev->dst);  
  7.   
  8.     if (!sink->session)//相关失败操作  
  9.         return btd_error_failed(msg, "Unable to get a session");  
  10.   
  11.     if (sink->connect || sink->disconnect)//如果正在连接、断开,发送busy消息;  
  12.         return btd_error_busy(msg);  
  13.   
  14.     if (sink->stream_state >= AVDTP_STATE_OPEN)//如果已经打开,发送已经连接消息;  
  15.         return btd_error_already_connected(msg);  
  16.   
  17.     if (!sink_setup_stream(sink, NULL))//(2)、创建AVDTP流;  
  18.         return btd_error_failed(msg, "Failed to create a stream");  
  19.   
  20.     dev->auto_connect = FALSE;  
  21.   
  22.     pending = sink->connect;  
  23.   
  24.     pending->conn = dbus_connection_ref(conn);//(3)、保存客户端dbus信息;  
  25.     pending->msg = dbus_message_ref(msg);  
  26.   
  27.     DBG("stream creation in progress");  
  28.   
  29.     return NULL;  
  30. }  

(1)、如果没有AVDTP会话,获取AVDTP连接状态;

[html]  view plain  copy
  1. sink->session = avdtp_get(&dev->src, &dev->dst);  
  2. idh.code\external\bluetooth\hcidump\parser\avdtp.c  
  3. struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst)  
  4. {  
  5. ………………  
  6.     session = avdtp_get_internal(src, dst);  
  7. ………………  
  8.   
  9. }  
  10. avdtp_get_internal 中设置 session->state状态,  
  11. session->state = AVDTP_SESSION_STATE_DISCONNECTED;  

(2)、创建AVDTP流;
sink_setup_stream(sink,NULL)
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html]  view plain  copy
  1. gboolean sink_setup_stream(struct sink *sink, struct avdtp *session)  
  2. {  
  3. …………  
  4.     avdtp_set_auto_disconnect(sink->session, FALSE);//不能自动断开;  
  5.   
  6.     if (avdtp_discover(sink->session, discovery_complete, sink) < 0)//调用avdtp_discover,  
  7. discovery_complete为回调函数;  
  8.         return FALSE;  
  9.   
  10.     sink->connect = g_new0(struct pending_request, 1);  
  11.   
  12.     return TRUE;  
  13. }  

idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html]  view plain  copy
  1. int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,  
  2.             void *user_data)  
  3. {  
  4.     int err;  
  5.   
  6.     if (session->discov_cb)  
  7.         return -EBUSY;  
  8.   
  9.     if (session->seps) {  
  10.         session->discov_cb = cb;  
  11.         session->user_data = user_data;  
  12.         g_idle_add(process_discover, session);  
  13.         return 0;  
  14.     }  
  15.   
  16.     err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);  
  17. //发送AVDTP_DISCOVER命令出去  
  18.     if (err == 0) {  
  19.         session->discov_cb = cb;  
  20.         session->user_data = user_data;  
  21.     }  
  22.   
  23.     return err;  
  24. }  
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html]  view plain  copy
  1. static int send_request(struct avdtp *session, gboolean priority,  
  2.             struct avdtp_stream *stream, uint8_t signal_id,  
  3.             void *buffer, size_t size)  
  4. {  
  5.     struct pending_req *req;  
  6.   
  7.     if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {  
  8.         DBG("Unable to send requests while aborting");  
  9.         return -EINVAL;  
  10.     }  
  11.   
  12.     req = g_new0(struct pending_req, 1);  
  13.     req->signal_id = signal_id;  
  14.     req->data = g_malloc(size);  
  15.     memcpy(req->data, buffer, size);  
  16.     req->data_size = size;  
  17.     req->stream = stream;  
  18.   
  19.     return send_req(session, priority, req);//这个函数我们后面分析;  
  20. }  

(3)、保存客户端dbus信息;

[html]  view plain  copy
  1. pending->conn = dbus_connection_ref(conn);  
  2.     pending->msg = dbus_message_ref(msg);  

2、send_req 创建L2CAP连接
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html]  view plain  copy
  1. static int send_req(struct avdtp *session, gboolean priority,  
  2.             struct pending_req *req)  
  3. {  
  4.     static int transaction = 0;  
  5.     int err;  
  6.       
  7.     if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//如果AVDTP没有连接,  
  8.         session->io = l2cap_connect(session);//(1)、创建l2cap连接;  
  9.         if (!session->io) {  
  10.             err = -EIO;  
  11.             goto failed;  
  12.         }  
  13.         avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);  
  14.     }  
  15.   
  16.     if (session->state < AVDTP_SESSION_STATE_CONNECTED ||  
  17.             session->req != NULL) {//如果AVDTP没连接  
  18.         queue_request(session, req, priority);//把相关参数放入队列  
  19.         return 0;//在这里返回,后面AVDTP sock建立完成后,会再次调用这个函数;  
  20.     }  
  21.   
  22.     req->transaction = transaction++;  
  23.     transaction %= 16;  
  24.   
  25.     /* FIXME: Should we retry to send if the buffer  
  26.     was not totally sent or in case of EINTR? */  
  27.     if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,  
  28.                 req->signal_id, req->data, req->data_size)) {//(2)、发送相关命令  
  29.         err = -EIO;  
  30.         goto failed;  
  31.     }  
  32. …………  
  33. }  

(1)、创建l2cap连接
sink connect的过程本质上是建立一个avdtp 连接的过程,avdtp是基于l2cap的,包括控制命令的发送和数据的发送都是l2cap的,所以这个图纸表示了建立一个发送控制命令的l2cap的socket,等这个socket建立起来以后,开始发送AVDPT_DISCOVER的请求;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html]  view plain  copy
  1.     session->io = l2cap_connect(session);  
  2. static GIOChannel *l2cap_connect(struct avdtp *session)  
  3. {  
  4.     GError *err = NULL;  
  5.     GIOChannel *io;  
  6.   
  7.     io = bt_io_connect(BT_IO_L2CAP, avdtp_connect_cb, session,  
  8.                 NULL, &err,  
  9.                 BT_IO_OPT_SOURCE_BDADDR, &session->server->src,  
  10.                 BT_IO_OPT_DEST_BDADDR, &session->dst,  
  11.                 BT_IO_OPT_PSM, AVDTP_PSM,  
  12.                 BT_IO_OPT_INVALID);  
  13.     if (!io) {  
  14.         error("%s", err->message);  
  15.         g_error_free(err);  
  16.         return NULL;  
  17.     }  
  18.   
  19.     return io;<strong>  
  20. }  
  21. strong>  

这个函数中注意两点,1)、bt_io_connect;2)、avdtp_connect_cb回调函数;
1)、bt_io_connect
idh.code\external\bluetooth\bluez\btio\btio.c

[html]  view plain  copy
  1. GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,  
  2.                 gpointer user_data, GDestroyNotify destroy,  
  3.                 GError **gerr, BtIOOption opt1, ...)  
  4. {  
  5.     …………  
  6.   
  7.     io = create_io(type, FALSE, &opts, gerr);  
  8.     if (io == NULL)  
  9.         return NULL;  
  10.     sock = g_io_channel_unix_get_fd(io);  
  11.     switch (type) {  
  12.     case BT_IO_L2RAW:  
  13.         err = l2cap_connect(sock, &opts.dst, 0, opts.cid);  
  14.         break;  
  15. //不同协议的连接,如L2CPA、RFCOMM、SCO  
  16.     case BT_IO_L2CAP:  
  17.         err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid);  
  18.         break;  
  19.     case BT_IO_RFCOMM:  
  20.         err = rfcomm_connect(sock, &opts.dst, opts.channel);  
  21.         break;  
  22.     case BT_IO_SCO:  
  23.         err = sco_connect(sock, &opts.dst);  
  24.         break;  
  25. …………  
  26.     connect_add(io, connect, user_data, destroy);  
  27.   
  28.     return io;  
  29. }<strong>  
  30. strong>  

Btio中l2cap_connect的实现:
idh.code\external\bluetooth\bluez\btio\btio.c

[html]  view plain  copy
  1. static int l2cap_connect(int sock, const bdaddr_t *dst,  
  2.                     uint16_t psm, uint16_t cid)  
  3. {  
  4.     int err;  
  5.     struct sockaddr_l2 addr;  
  6.   
  7.     memset(&addr, 0, sizeof(addr));  
  8.     addr.l2_family = AF_BLUETOOTH;  
  9.     bacpy(&addr.l2_bdaddr, dst);  
  10.     if (cid)  
  11.         addr.l2_cid = htobs(cid);  
  12.     else  
  13.         addr.l2_psm = htobs(psm);  
  14.   
  15.     err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));//建立BTPROTO_L2CAP  
  16.     if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))  
  17.         return err;  
  18.   
  19.     return 0;  
  20. }  

2)、avdtp_connect_cb回调函数
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html]  view plain  copy
  1. static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)  
  2. {  
  3. ………………  
  4.     if (session->state == AVDTP_SESSION_STATE_CONNECTING) {//如果处于正在连接状态;  
  5.         DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);  
  6.   
  7.         session->buf = g_malloc0(session->imtu);  
  8.         avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);//设置AVDTP状态为已经连接状态;  
  9.   
  10.         if (session->io_id)  
  11.             g_source_remove(session->io_id);  
  12.   
  13.         /* This watch should be low priority since otherwise the  
  14.          * connect callback might be dispatched before the session  
  15.          * callback if the kernel wakes us up at the same time for  
  16.          * them. This could happen if a headset is very quick in  
  17.          * sending the Start command after connecting the stream  
  18.          * transport channel.  
  19.          */  
  20.         session->io_id = g_io_add_watch_full(chan,  
  21.                         G_PRIORITY_LOW,  
  22.                         G_IO_IN | G_IO_ERR | G_IO_HUP  
  23.                         | G_IO_NVAL,  
  24.                         (GIOFunc) session_cb, session,  
  25.                         NULL);  
  26.   
  27. ………………  
  28.     process_queue(session);//发送DISCOVER  
  29.   
  30.     return;  
  31. …………  
  32. }  

3、process_queue(session)发送DISCOVER命令出去
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html]  view plain  copy
  1. static int process_queue(struct avdtp *session)  
  2. {  
  3. …………  
  4.     *queue = g_slist_remove(*queue, req);  
  5.   
  6.     return send_req(session, FALSE, req);  
  7. }<strong>  
  8. strong>  

这个函数调用send_req,这个函数前面已经调用过,可是现在AVDTP的状态不同,第一次调用AVDTP_SESSION_STATE_DISCONNECTED状态,第二次调用为

AVDTP_SESSION_STATE_CONNECTED状态;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html]  view plain  copy
  1. static int send_req(struct avdtp *session, gboolean priority,  
  2.             struct pending_req *req)  
  3. {  
  4.     static int transaction = 0;  
  5.     int err;  
  6.       
  7.     if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//第二次调用时,就不走这段函数  
  8.         session->io = l2cap_connect(session);  
  9.         if (!session->io) {  
  10.             err = -EIO;  
  11.             goto failed;  
  12.         }  
  13.         avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);  
  14.     }  
  15.   
  16.     if (session->state < AVDTP_SESSION_STATE_CONNECTED ||//第二次调用也越过这段函数  
  17.             session->req != NULL) {  
  18.         queue_request(session, req, priority);  
  19.         return 0;  
  20.     }  
  21.   
  22.     req->transaction = transaction++;  
  23.     transaction %= 16;  
  24.   
  25.     /* FIXME: Should we retry to send if the buffer  
  26.     was not totally sent or in case of EINTR? */  
  27.     if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,  
  28.                 req->signal_id, req->data, req->data_size)) {//avdtp_send就是主要的操作  
  29.         err = -EIO;  
  30.         goto failed;  
  31.     }  

4、avdtp_send的实现
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html]  view plain  copy
  1. static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,  
  2.                 uint8_t message_type, uint8_t signal_id,  
  3.                 void *data, size_t len)  
  4. {  
  5.     …………       
  6.     /* Send the start packet */  
  7.     memset(&start, 0, sizeof(start));  
  8.     start.transaction = transaction;  
  9.     start.packet_type = AVDTP_PKT_TYPE_START;  
  10.     start.message_type = message_type;  
  11.     start.no_of_packets = cont_fragments + 1;  
  12.     start.signal_id = signal_id;  
  13.   
  14.     memcpy(session->buf, &start, sizeof(start));  
  15.     memcpy(session->buf + sizeof(start), data,  
  16.                     session->omtu - sizeof(start));  
  17.   
  18.     if (!try_send(sock, session->buf, session->omtu))  
  19.         return FALSE;  
  20.   
  21. ………………  
  22.         cont.message_type = message_type;  
  23.   
  24.         memcpy(session->buf, &cont, sizeof(cont));  
  25.         memcpy(session->buf + sizeof(cont), data + sent, to_copy);  
  26.   
  27.         if (!try_send(sock, session->buf, to_copy + sizeof(cont)))  
  28.             return FALSE;  
  29.   
  30.         sent += to_copy;  
  31.     }  
  32.   
  33.     return TRUE;  
  34. <strong>  
  35. strong>  

5、Try_sends函数的实现

[html]  view plain  copy
  1. static gboolean try_send(int sk, void *data, size_t len)  
  2. {  
  3.     int err;  
  4.     do {  
  5.         err = send(sk, data, len, 0);  
  6.     } while (err < 0 && errno == EINTR);  
  7.   
  8.     if (err < 0) {  
  9.         error("send: %s (%d)", strerror(errno), errno);  
  10.         return FALSE;  
  11.     } else if ((size_t) err != len) {  
  12.         error("try_send: complete buffer not sent (%d/%zu bytes)",  
  13.                                 err, len);  
  14.         return FALSE;  
  15.     }  
  16.   
  17.     return TRUE;  
  18. }<strong>  
  19. 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关联起来,于是就实现了文件描述符传递。









你可能感兴趣的:(Bluetooth)