对于蓝牙无论最底层的硬件驱动如何实现,都会在HCI层进行统一。也就是说,HCI在主机端的驱动主要是为上层提供统一接口,让上层协议不依赖于具体的硬件实现。HCI在硬件中的固件与HCI在主机端的驱动通信方式有多种,比如UART,USB和SDIO等。
HCI层在所有的设备面前都被抽象为一个hci_dev结构体,因此,无论实际的设备是哪种蓝牙设备、通过什么方式连接到主机,都需要向HCI层和蓝牙核心层注册一个hci_dev设备,注册过程由hci_registe_dev()函数来完成,同时也可以通过hci_unregister_dev()函数卸载一个蓝牙设备。
具体的蓝牙驱动有很多,常用的在linux内核都自带有驱动。比如:hci_vhci.c为蓝牙虚拟主控制器驱动程序,hci_uart.c(或者hci_ldisc.c)为串口接口主控制器驱动程序,btusb.c为USB接口主控制器驱动程序,btsdio.c为SDIO主控制器驱动程序。
总结一下,蓝牙驱动的三个步骤:
1, 串口驱动必须要先就绪(uart蓝牙而言),这是cpu和蓝牙模块之间的桥梁。
2, 蓝牙初始化,模块上电和PSKEY的设置。
3, 通过hciattach建立串口和蓝牙协议层之间的数据连接通道。
蓝牙模块上电
一般是通过一个GPIO来控制的,通常是先高再低再高;
PSKEY的设置
通过串口发送命令给蓝牙模块,对于串口必须要知道的是要能通讯,必须得设好波特率,另外一方面蓝牙模块的晶振频率也必须要设,否则它不知道该怎么跳了;当然不同的芯片可能初始化的过程也不一样,也许还要下载firmware等等,一般是通过bccmd来完成的。
经过上面的设置基本上蓝牙模块以及可以正常工作了;
但是还没有和上面的协议层建立纽带关系,也就是说从uart收到的数据还没有传给hci层;
如何把uart也就是蓝牙模块传上来的数据交给hci层,在驱动里面是通过一个叫做disc的机制完成的,这个机制本意是用来做过滤或者限制收上来的字符的,但是在蓝牙驱动里面则直接把数据传给了蓝牙协议层,再也不回到串口的控制了;
Uart交给hci层的前面对比图:
简单总结一下,数据的流程,
基本上是:
1, uart口取得蓝牙模块的数据;
2, uart口通过ldisc传给hci_uart;
3, hci_uart传给在其上的bcsp;
4, bcsp传给hci层;
5, hci层传给l2cap层
6, l2cap层再传给rfcomm;
Rfkill平台设备部分
static structresource nabi2_bcm4330_rfkill_resources[] = {
{
.name = "bcm4330_nshutdown_gpio",
.start = TEGRA_GPIO_PD0,
.end = TEGRA_GPIO_PD0,
.flags = IORESOURCE_IO,
},
{
.name = "bcm4330_nreset_gpio",
.start = TEGRA_GPIO_PU0,
.end = TEGRA_GPIO_PU0,
.flags = IORESOURCE_IO,
},
};
static structplatform_device nabi2_bcm4330_rfkill_device = {
.name = "bcm4330_rfkill",
.id = -1,
.num_resources = ARRAY_SIZE(nabi2_bcm4330_rfkill_resources),
.resource = nabi2_bcm4330_rfkill_resources,
};
Rfkill平台驱动部分(主要用来给蓝牙上电和下电)
static intbcm4330_bt_rfkill_set_power(void *data, bool blocked)
{
模块上电和下电操作。
}
static const structrfkill_ops bcm4330_bt_rfkill_ops = {
.set_block =bcm4330_bt_rfkill_set_power,
};
static int bcm4330_rfkill_probe(structplatform_device *pdev){
……
bt_rfkill = rfkill_alloc("bcm4330Bluetooth", &pdev->dev,
RFKILL_TYPE_BLUETOOTH,&bcm4330_bt_rfkill_ops, NULL);
rfkill_register(bt_rfkill);
……
}
static struct platform_driverbcm4330_rfkill_driver = {
.probe= bcm4330_rfkill_probe,
.driver= {
.name = "bcm4330_rfkill",
.owner = THIS_MODULE,
},
};
static int __init bcm4330_rfkill_init(void)
{
returnplatform_driver_register(&bcm4330_rfkill_driver);
}
Rfkill驱动很简单,仅仅是注册了一个bcm4330_rfkill_driver的平台驱动。在彼配的时候会注册一个rfkill_ops bcm4330_bt_rfkill_ops 函数集。当用户操作/sys/class/rfkill/rfkill0/state时将会调用到bcm4330_bt_rfkill_set_power这个函数,用来给蓝牙上电和关电。
蓝牙驱动在内核里面有(个别产品有自己专门的驱动除外,即便如此专门的驱动实现架构也跟内核自带的一模一样),对于串口蓝牙的驱动是:/kernel/drivers/Bluetooth/hci_ldisc.c
这个驱动其实没什么好讲的,hci提供了统一的接口函数,不管是蓝牙模块是usb,sdio,uart……都只需要实现这些接口就行了。
所有蓝牙驱动都只是围绕一个hci_dev结构体,然后完成下面这些函数就行了。
hdev->open = hci_uart_open;
hdev->close = hci_uart_close;
hdev->flush = hci_uart_flush;
hdev->send = hci_uart_send_frame;
hdev->destruct = hci_uart_destruct;
所有的接口在hci层做了统一,hci层负责跟蓝牙协议通信,所以你只要实现hci层提供的接口,协议你无需多管。
蓝牙初始化XXX_init.rc文件添加如下:
……
# bluetooth
# bluetooth MAC address programming
chown bluetooth bluetooth /system/etc/bluetooth #设备该目录有蓝牙权限
setprop ro.bt.bdaddr_path"/system/etc/bluetooth/bdaddr" #设置蓝牙地址路径
chmod 0660/sys/class/rfkill/rfkill0/state #rfkill文件具有可读可写权限
chmod 0660/sys/class/rfkill/rfkill0/type
chown bluetooth bluetooth/sys/class/rfkill/rfkill0/state #rfkill文件具有蓝牙权限
chown bluetooth bluetooth/sys/class/rfkill/rfkill0/type
#BCM #bccmd 主要用来初始化蓝牙,比如说上电,设置波特率,下载firmware……
#具体参照brcm_patchram_plus.c这个文件有详细的使用说明
service hciattach/system/bin/brcm_patchram_plus --enable_hci --scopcm=0,2,0,0,0,0,0,0,0,0 \
--baudrate 3000000 --patchram /etc/firmware/bcm4330.hcd--enable_lpm --tosleep=5000 --create_bdaddr /dev/ttyHS2
#创建蓝牙地址(因为bcm4330没有物理蓝牙地址),使用uart2和hci连接。
user bluetooth
group bluetooth net_bt_admin
disabled
……
蓝牙驱动依赖是蓝牙协议在android里面已支持bluez协议,我们只需要要配制的时候选上bluez协议就可以了。
CONFIG_BT =y
CONFIG_BT_RFCOMM =y
CONFIG_BT_BNEP = y
CONFIG_BT_CMTP =y
CONFIG_BT_L2CAP=y
CONFIG_BT_SCO=y
如果是linux的话,或者你还需要自己再移植bluez协议。
好了,到这里蓝牙就可以使用了,进入蓝牙测试。
1、 蓝牙上电
# echo 1 >/sys/class/rfkill/rfkill0/state
2、 蓝牙初始化
# XXX_init.rc里面做好了。
3、 测试蓝牙驱动
# hciconfig
说明蓝牙驱动已经正常初始化并加载了。
4、 激活蓝牙
#hciconfig hci0 up
# hciconfig
说明蓝牙已经初激活了。
5、 查看蓝牙地址
# hcitool dev
6、 扫描周围蓝牙设备
# hcitool scan
出现上面的类似画面说明蓝牙已经工作正常了。
7、上面是用命令测试,要在android上使用的话,还需要把蓝牙配制上。
找到相对应的BoadConfig.mk文件设置BOARD_HAVE_BLUETOOTH。
BOARD_HAVE_BLUETOOTH:= true