對於藍牙無論最底層的硬件驅動如何實現,都會在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/ hci_H4.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