精华内容
下载资源
问答
  • blue_cr:Crystal中的BlueZ蓝牙绑定
  • bluez蓝牙协议栈交叉编译移植教程
  • 基于Jetson nano搭建Bluez蓝牙协议栈开发环境

    0、序

      BlueZ 是官方 Linux Bluetooth 协议栈。本文就如何在Jetson nano开发平台中搭建基于Bluez的蓝牙开发环境进行相关的介绍。主要参考SIG联盟提供的《Developer Study Guide - How to Deploy BlueZ on a Raspberry Pi Board as a Bluetooth Mesh Provisioner》,此外还涉及Jetson nano平台Linux内核的更新。上述资料的传送门:https://www.bluetooth.com/bluetooth-resources/

    1、相关依赖库安装

    1.1、库安装

    sudo apt-get install libglib2.0-dev libdbus-1-dev libdbus-c++-dev libudev-dev libical-dev libreadline-dev
    

    1.2、json-c安装

    wget https://s3.amazonaws.com/json-c_releases/releases/json-c-0.13.tar.gz
    tar -xvf json-c-0.13.tar.gz 
    cd json-c-0.13/
    ./configure --prefix=/usr --disable-static && make
    sudo make install
    

    2、Bluez的安装

    2.1、编译安装

    wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.54.tar.xz
    tar -xvf bluez-5.54.tar.xz 
    cd bluez-5.54/
    ./configure --enable-mesh --enable-testing --enable-tools --prefix=/usr --mandir=/usr/share/man -- sysconfdir=/etc --localstatedir=/var 
    sudo make 
    sudo make install
    

    2.2、检查配置文件

      check Bluez service配置文件,确保service配置文件中的ExecStart的值为“/usr/libexec/bluetooth/bluetoothd

    vim /lib/systemd/system/bluetooth.service
    

    在这里插入图片描述

    2.3、环境配置

      备份原可执行程序,并创建以下symlink。

    sudo cp /usr/lib/bluetooth/bluetoothd /usr/lib/bluetooth/bluetoothd-550.orig
    sudo ln -sf /usr/libexec/bluetooth/bluetoothd /usr/lib/bluetooth/bluetoothd 
    sudo systemctl daemon-reload 
    cd ~/.config/ 
    mkdir meshctl 
    cp ~/bluez-5.54/tools/mesh-gatt/local_node.json ~/.config/meshctl/ 
    cp ~/bluez-5.54/tools/mesh-gatt/prov_db.json ~/.config/meshctl/
    

    2.4、检查安装的Bluez的版本

    bluetoothd -v 
    meshctl -v 
    mesh-cfgclient -v
    

    在这里插入图片描述
      安装完上述的相关工具之后,在运行mesh-cfgctl时出现为定义符号的错误,通过将编译链中的动态库更换成自行编译安装生成的json-c的动态库即可。
    在这里插入图片描述

    3、更新Linux Kernel

      但是运行meshctl出现以下错误:

    Failed to parse provisioning database file /home/colin/.config/meshctl/prov_db.json
    free(): double free detected in tcache 2
    Aborted
    

      出现上述问题是因为Linux内核并没有正确地安装AEAD-AES_CCM套件,重新配置内核:从NVIDIA Jetson的下载中心下载相关内核及组件源码,注意需要下载与当前使用相同版本的。内核下载传送门:https://developer.nvidia.com/embedded/downloads,如下图所示,注意下载与你正运行的系统版本相同的源码Sources,避免到时候更换kernel之后无法开机。
    在这里插入图片描述
    下载L4T Sources后进入kernel目录,进行内核的配置
    1)将原系统的内核配置输出至.config

    zcat /proc/config.gz > .config
    

    2)内核配置

    make menuconfig
    

      运行命令make menuconfig之后出现以下错误的话是缺少相关依赖库未安装,安装好即可。

    colin@Blanc:~/works/Linux_for_Tegra/source/public/kernel/kernel-4.9$ make menuconfig
    HOSTCC scripts/basic/fixdep
    HOSTCC scripts/kconfig/mconf.o
    <command-line>:0:12: fatal error: curses.h: No such file or directory
    compilation terminated.
    scripts/Makefile.host:118: recipe for target 'scripts/kconfig/mconf.o' failed
    make[1]: *** [scripts/kconfig/mconf.o] Error 1
    Makefile:565: recipe for target 'menuconfig' failed
    make: *** [menuconfig] Error 2
    

      安装依赖库

    sudo apt-get install libncurses5-dev
    

    好的,正式进入内核配置:

    3.1、内核配置

    1)使能Networking support
    在这里插入图片描述
    2)使能Crytographic API
    在这里插入图片描述
    3)进入Crytographic API–>,使能CCM support、CMAC support、User-space interface for hash algorithms、User-space interface for symmetric key cipher algorithms、User-space interface for AEAD cipher algorithms,如下列图片所示:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    3.2、编译内核

    
    1) make prepare
    2) make modules_prepare
    
    jetson nano是4核的,故:
    3) make -j4
    

    3.3、安装modules

    make modules// 模块编译
    sudo make modules_install // 安装模块到/lib/modules
    

    3.4、替换新内核

    sudo cp ./arch/arm64/boot/zImage /boot/zImage
    sudo cp ./arch/arm64/boot/Image /boot/Image
    

    3.5、重启and检验

    sudo reboot
    

    在这里插入图片描述
      重启后检查系统的版本,能发现内核print的时间是新的,上图中我是在2020-12-20-15:33重新编译更换的新内核,与此同时正确地安装AEAD-AES_CCM套件,运行meshctl能正常的加载database文件并运行,搞定-OK!
    在这里插入图片描述

    展开全文
  • coBlue:基于bluez蓝牙协议栈(BLE终端),使用低功耗蓝牙进行远程命令,文件传输
  • 里面包括 bluez-libs-3.36.tar.gz , bluez-utils-3.36.tar.gz, expat-2.2.7.tar.xz, glib-2.54.2.tar.xz, libusb-1.0.20.tar.bz2,dbus-1.11.10.tar.gz
  • bluez蓝牙协议

    千次阅读 2013-03-12 14:51:41
    昨天看了一下介绍蓝牙协议文档,今天索性对照看了看kernel里的代码(bluez),这里记点笔记,还是继承了老毛病,只关注整体流程而忽略细节,先了解个大概,等真正需要时再仔细分析。 net/hci_core.c HCI在主机端...

    昨天看了一下介绍蓝牙协议文档,今天索性对照看了看kernel里的代码(bluez),这里记点笔记,还是继承了老毛病,只关注整体流程而忽略细节,先了解个大概,等真正需要时再仔细分析。

    net/hci_core.c


    HCI
    在主机端的驱动主要是为上层提供一个统一的接口,让上层协议不依赖于具体硬件的实现。HCI在硬件中的固件与HCI在主机端的驱动通信方式有多种,比如像UARTUSBPC Card等等。hci_core.c相当于一个框架,用于把各种具体通信方式胶合起来,并提供一些公共函数的实现。

    hci_cmd_task
    是负责发送CMD的任务,它从hdev->cmd_q队列中取CMD,然后调用hci_send_frame把CMD发送出去,hci_send_frame又会调用实际的HCI驱动的send函数发送数据。

    hci_rx_task
    是负责接收数据的任务,它从hdev->rx_q队列中取数据,然后根据数据的类型调用上层函数处理。数据包有三种类型:

    1.   HCI_EVENT_PKT: 用于处理一些通信事件,比如连接建立,连接断开,认证和加密等事件,这些事件控制协议状态的改变。

    2.   HCI_ACLDATA_PKT: 异步非连接的数据包,通过hci_acldata_packet提交给上层的L2CAP协议处理(hci_proto[HCI_PROTO_L2CAP])。

    3.   HCI_SCODATA_PKT: 同步面向连接的数据包,通过hci_scodata_packet提供给上层的SCO协议处理(hci_proto[HCI_PROTO_SCO])。


    hci_tx_task 是负责发送数据的任务,发送所有connection中的ACLSCO数据,以及hdev->raw_q中的数据包。

    HCI
    为上层提供的接口主要有:

    1.   hci_send_sco:发送SCO数据包,把要发送的数据包放入connection的发送队列中,然后调度发送任务去发送。

    2.   hci_send_acl:发送ACL数据包,把要发送的数据包放入connection的发送队列中,然后调度发送任务去发送。

    3.   hci_send_cmd:发送命令数据,把要发送的数据包放入hdev->cmd_q队列中,然后调度命令发送任务去发送。

    4.   hci_register_proto/hci_unregister_proto:注册/注销上层协议,HCI会把接收到的数据转发给这些上层协议。

    5.   hci_register_dev/hci_unregister_dev: 注册/注销设备,HCI会把要发送的数据通过这些设备发送出去。

    6.   其它一些公共函数。


    net/hci_conn.c
    提供了一些连接管理,论证和加密的函数。

    net/hci_event.c
    事件处理函数,负责状态机的维护,这些事件通常会使连接从一个状态转换另一个状态。

    1.   hci_si_event:用于发送事件。

    2.   hci_event_packet:用于处理底层上报的事件,从hci_rx_task处调用过来。


    net/hci_sock.c
    给上层提供一个socket接口,应用程序可以通过socket的方式来访问HCI

    1.   hci_sock_init:中注册了BTPROTO_HCI类型family

    2.   hci_sock_create:创建sock的函数,它的sockops指向hci_sock_ops

    3.   hci_sock_setsockopt/hci_sock_getsockopt:设置/获取sock的一些选项。

    4.   hci_sock_sendmsg:发送消息,根据消息的类型把消息放到适当的队列中。

    5.   hci_sock_recvmsg:接收消息,从接收队列中取消息。

    6.   hci_sock_recvmsgioctl函数。


    net/hci_sysfs.c
    提供一些sysfs文件系统接口。

    net/l2cap.c
    L2CAP
    HCI之上的协议,提供诸如QoS,分组,多路复用,分段和组装之类的功能。

    通过bt_sock_register为上层提供一个sock接口:

    1.   l2cap_sock_create:创建sock的函数,它的sockops指向l2cap_sock_ops

    2.   l2cap_sock_setsockopt/l2cap_sock_getsockopt设置/获取sock的一些选项。

    3.   l2cap_sock_sendmsg:发送消息,通过HCI提供hci_send_acl函数把消息传递给下层的设备。

    4.   bt_sock_recvmsg:接收消息,从接收队列中取消息。


    通过hci_register_proto向其下的HCI注册协议:

    1.   l2cap_connect_ind:处理连接请求。

    2.   l2cap_connect_cfm:确认连接。

    3.   l2cap_disconn:处理断开请求。

    4.   l2cap_auth_cfm:认证确认。

    5.   l2cap_encrypt_cfm:加密确认。

    6.   l2cap_recv_acldata:处理来自HCI的数据。


    net/sco.c
    SCO
    也是运行在HCI之上的协议,它是面向连接的可靠的传输方式,主要用于声音数据传输。

    通过bt_sock_register为上层提供一个sock接口:

    1.   sco_sock_create:创建sock的函数,它的sockops指向sco_sock_ops

    2.   sco_sock_setsockopt/sco_sock_getsockopt设置/获取sock的一些选项。

    3.   sco_sock_sendmsg:发送消息,通过HCI提供sco_send_frame函数把消息传递给下层的设备。

    4.   bt_sock_recvmsg:接收消息,从接收队列中取消息。


    通过hci_register_proto向其下的HCI注册协议:

    1.   sco_connect_ind:处理连接请求。

    2.   sco_connect_cfm:确认连接。

    3.   sco_disconn_ind:处理断开请求。

    4.   sco_recv_scodata: 处理来自HCI数据。


    rfcomm/*
    rfcomm
    是基于l2CAP之上的协议,它在蓝牙协议之上封装传统的RS232串口。

    drivers/bluetooth
    前面我们介绍的都是HCI及其上层的协议,HCI下层的实现就是HCI驱动程序,这些驱动程序用于与蓝牙硬件通信,通信的方式常见的有USBUARTPC card等几种。这里我们看看USB的方式:

    drivers/bluetooth/hci_usb.c

    1.   hci_usb_probe: 调用hci_register_dev向前面说的hci_core注册HCI设备。

    2.   hci_usb_send_frame:用于提供给HCI去发送数据包。它把数据包放到传输队列__transmit_q(husb, bt_cb(skb)->pkt_type)之中,然后调用hci_usb_tx_process去传输数据。

    3.   hci_usb_tx_process:根据数据的类型去调用hci_usb_send_ctrl /hci_usb_send_isoc /hci_usb_send_bulk把数据通过USB发送给硬件。

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


    if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI))

    在此之前,因为hci_sock.c已经被built-in.所以hci_sock_init()已经被执行。

    另外,因为hci usb driver也已经insmod。所以hci_usb_init()被执行,这个driver指出,当USB Bluetooth Dongle插入时,调用hci_usb_probe()。这时,就会指出openhci_usb_open()


    hci_usb_open()
    中则设置hci_dev->flag=HCI_RUNNING.
    并向USB Core提交一个中断URB。当有中断URBUSB Core出来完毕后,调用hci_usb_rx_complete()。它则调用__recv_frame()。它处理所有USB Dongle通过中断URB送来的数据。


    利用hci_init_req()发送request.
    hci_init_req
    ()调用hci_send_cmd()发送命令。
    hci_send_cmd()
    则调用skb_queue_tail()将命令skb添加到发送队列。再调用hci_sched_cmd()去调用发送命令去发送。发送命令为:hci_cmd_task()
    hci_cmd_task()-
    hci_send_frame()-hci_send_frame()-hdev->send(skb);=hci_usb_send_frame();发送命令给USB bluetooth dongle.

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

    ——————————————————————————————————

    1.Bluetooth USB dongle插入USB接口时,driver/bluetooth/hci_usb.cprobe程序被调用。probe会调用hci_register_dev()--tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
    hci_rx_task() 连上rx_task->func.


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

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

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

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


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

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

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

    3.这个连接的state改为BT_CLOSED

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

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

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

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

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

    展开全文
  • 由于项目功能的要求,需要移植bluez蓝牙协议栈到S3C2410开发平台上,在移植的过程中出现了很多问题,比如依赖库的版本不匹配,交叉编译器版本过高或者过低等问题,为了能让大家少走移植的弯路,现将自己亲测的组合...

    由于项目功能的要求,需要移植bluez蓝牙协议栈到S3C2410开发平台上,在移植的过程中出现了很多问题,比如依赖库的版本不匹配,交叉编译器版本过高或者过低等问题,为了能让大家少走移植的弯路,现将自己亲测的组合版本以及编译过程总结如下,同时还附带有编译过程的全称视频,只是没有声音,因为是在实验室没有麦,大家将就以下,虽然没有声音但是不影响编译过程,下载地址为:http://115.com/file/aq7pjuh6#
    交叉编译bluez协议栈.avi。

    需要的源码包有:

    bluez-libs-3.36.tar.gz

    libxml2-2.6.11.tar.gz

    glib-2.16.5.tar.gz

    libsndfile-1.0.17.tar.gz

    libusb-0.1.12.tar.gz

    dbus-1.0.2.tar.gz

    bluez-utils-3.36.tar.gz

    openobex-1.3.tar.gz

    具体的编译安装步骤如下:

    1、交叉编译并安装bluez-libs

    进入源码包所在路径,解压源码包到当前路径,进入源码包目录,依次执行以下命令,在执行命令期间不出现错误即可完成交叉编译安装工作:

    #./configure -prefix=/root/bluez/bluez-libs --host=arm-linux 

    #make

    #make install

    #cp /root/bluez/bluelib/lib/pkgconfig/bluez.pc /usr/lib/pkgconfig

    2、交叉编译并安装libxml

    解压并进入源码目录,依次执行以下命令:

    #./configure --prefix=/root/bluez/libxml --host=arm-linux CC=arm-linux-gcc

    #make

    #make install 

    #cp /root/bluez/libxml/lib/pkgconfig/libxml-2.0.pc  /usr/lib/pkgconfig

    3、交叉编译并安装glib

    解压并进入源码所在目录,依次执行以下命令:

    #apt-get install gettext 

    gettext用于系统的国际化和本地化,可以在编译程序的时候使用本国语言支持(NLS),可以使程序的输出使用用户设置的语言而不是英文。 

    #apt-get install libglib2.0-dev 

    glib 是 GTK+和 GNOME 工程的基础底层核心程序库,是一个综合用途的实用的轻量级的C 程序库。

    #echo ac_cv_type_long_long=yes > arm-linux.cache 

    #echo glib_cv_stack_grows=no >> arm-linux.cache 

    #echo glib_cv_uscore=no >> arm-linux.cache 

    #echo ac_cv_func_posix_getpwuid_r=yes >> arm-linux.cache 

    #echo ac_cv_func_posix_getgrgid_r=yes>> arm-linux.cache 

    #./configure -prefix=/root/bluez/glib --host=arm-linux CC=arm-linux-gcc    cache-file=arm-linux.cache 

    #make 

    #make install 

    #cp /root/bluez/glib/lib/pkgconfig/* /usr/lib/pkgconfig

    4、交叉编译并安装libsndfile

    解压并进入源码包目录,依次执行以下命令:

    #./configure -prefix=/root/bluez/libsndfile --host=arm-linux CC=arm-linux-gcc

    #make

    #make install

    #cp /root/bluez/libsndfile/lib/pkgconfig/sndfile.pc /usr/lib/pkgconfig

    5、交叉编译并安装libusb

    解压并进入源码包所在目录,依次执行以下命令:

    #./configure -prefix=/root/bluez/libusb --host=arm-linux CC=arm-linux-gc

    #make

    #make install

    #cp /root/bluez/libusb/lib/pkgconfig/libusb.pc /usr/lib/pkgconfig

    6、交叉编译并安装dbus

    解压并进入源码包所在目录,依次执行以下命令:

    #echo ac_cv_have_abstract_sockets=yes > arm-linux.cache

    #./configure --host=arm-linux --prefix=/root/bluez/dbus CC=arm-linux-gcc --cache-file=arm-linux.cache --without-x

    #make

    #make install

    #cp /home/song/install_bluez/dbus/lib/pkgconfig/dbus-1.pc /usr/lib/pkgconfig/

    7、交叉编译并安装bluez-utils

    解压并进入源码包所在目录,依次执行以下操作:

    打开network/bridge.h文件,添加以下宏定义:

    #define SIOCBRADDBR     0x89a0

    #define SIOCBRDELBR     0x89a1 

    #define SIOCBRADDIF     0x89a2  

    #define SIOCBRDELIF     0x89a3   

    打开input/storage.c文件,加入以下宏定义:

    #define ENOKEY 161

    执行以下命令安装依赖库:

    #apt-get install libbluetooth-dev

    #apt-get install libcwiid1

    最后依次执行以下命令完成编译安装:

    #./configure --prefix=/root/bluez/bluez-utils --host=arm-linux CC="arm-linux-gcc –shared -I/root/bluez/bluez-libs/include -L/root/bluez/bluez-libs/lib" --disable-audio

    #make

    #make install

    8、交叉编译并安装openobex

    解压并进入源码包所在目录,依次执行以下命令:

    #./configure --prefix=/root/bluez/openobex --host=arm-linux CC="arm-linux-gcc  -I/root/bluez/bluez-libs/include -L/root/bluez/bluez-libs/lib" --enable-bluetooth --disable-usb --enable-apps

    #make

    #make install

     

    总结:bluez-utils的编译安装目录下生成的操作蓝牙的工具,bluez-libs安装目录下生成的是蓝牙编程通信时所以来的库,其他的都是编译这两个所依赖的库,也就是说需要移植到板子上的只有bluez-libs和bluez-utils安装目录下的相关文件。

     

    展开全文
  • 之所以现在还没有全面实现“把蓝牙变成大白菜”的目标,作为Linux下实现蓝牙协议的官方组织BlueZ难咎其责:由于不提供step by step的编译安装文档,这对于还没有深刻理解编译工具链和shell脚本编程的普通Linux嵌入式...
  • 原文网址:... 关键词:蓝牙blueZ UARTHCI_UARTH4HCIL2CAPRFCOMM版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明...

    原文网址:http://blog.sina.com.cn/s/blog_602c72c50102uzoj.html

    关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM 
    版本:基于android4.2之前版本 bluez
    内核:linux/linux3.08
    系统:android/android4.1.3.4
    作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)
    欢迎指正错误,共同学习、共同进步!!
    一、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官方提供)

    LinuxKernel层:

    bluez协议栈、uart驱动, h4协议, hci,l2cap, sco, rfcomm

    Library层:

    libbluedroid.so 等

    Framework层:

    实现了Headset /Handsfree 和 A2DP/AVRCP profile,但其实现方式不同Handset/Handfree是直接 在bluez的RFCOMM Socket上开发的,没有利用bluez的audio plugin,而A2DP/AVRCP是在bluez的audio plugin基础上开发的,大大降低了实现的难度。

    二、蓝牙通过Hciattach启动串口流程:
    1、hciattach总体流程

    2、展讯hciattach代码实现流程:

    三、具体代码分析
    1、initrc中定义
    idh.code\device\sprd\sp8830ec_nwcn\init.sc8830.rc

    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

    1. nt main(int argc, char *argv[])  
    2. {  
    3. ………………  
    4.     for (n = 0; optind <</span> 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)、解析驱动的位置;

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

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

    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等着到其相对应的初始化配置函数。

    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]中的数值初始化;

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

    (4)、初始化串口;

    1. n = init_uart(dev, u, send_break, raw);  
    2. idh.code\external\bluetooth\bluez\tools\hciattach.c  
    3.   
    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 <</span> 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) <</span> 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) <</span> 0) {//启动新的串口设置  
    38.   perror(“Can’t set port settings”);  
    39.   return -1;  
    40.  }  
    41.    
    42.  if (set_speed(fd, &ti, u->init_speed) <</span> 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) <</span> 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.    
    69.  if (set_speed(fd, &ti, u->speed) <</span> 0) {  
    70.   perror(“Can’t set baud rate”);  
    71.   return -1;  
    72.  }  
    73.    
    74.  i = N_HCI;  
    75.  if (ioctl(fd, TIOCSETD, &i) <</span> 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.   
    79.   
    80. #define N_TTY  0  
    81. ……  
    82. #define N_HCI  15    
    83.   
    84.   perror(“Can’t set line discipline”);  
    85.   return -1;  
    86.  }  
    87.  if (ioctl(fd, HCIUARTSETPROTO, u->proto) <</span> 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

    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.       
    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.               
    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.               
    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参数写入;频率、主从设备设定等……

    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

    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文件。   

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

    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

    1. static int __init hci_uart_init(void)  
    2. {  
    3.     static struct tty_ldisc_ops hci_uart_ldisc;  
    4.     int err;  
    5.       
    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

    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

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

    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;

    转载于:https://www.cnblogs.com/wi100sh/p/4330336.html

    展开全文
  • bluez4.60蓝牙移植

    2012-10-09 14:48:52
    基于bluez4.60版本,完整的嵌入式蓝牙移植,在imx51 平台测试过,文档在bt_sdk_0828.tgz里

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,464
精华内容 1,385
关键字:

bluez蓝牙