標籤:des style blog http color 檔案
關於HCI規範相關內容,請看<Bluetooth HCI介紹>
首先我們要瞭解,在Linux上實現的是HCI的Host部分
1. 相關資料結構 1.1 hci_dev
在Linux中hci_dev用來表示一個HCI Host(對應於一個Control)
| 成員 |
作用 |
| char name[8] |
藍芽名稱 |
| __u8 bus |
HCI匯流排類型,有HCI_USB,HCI_PCCARD,HCI_UART,HCI_PCI等 |
| __u8 dev_type |
HCI Controller類型,有HCI_BREDR,HCI_AMP兩種 |
| bdaddr_t bdaddr |
Bluetooth device address,48 bits |
| __u8 dev_name[248] |
藍芽本地名稱,可在使用者空間設定 |
| unsigned int flags |
進程的標記, 如PF_STARTING |
| __u16 pkt_type |
HCI packet types, 有HCI_DM1,HCI_DM3,HCI_DH1,HCI_HV1等 |
| __u16 link_policy |
Link policies, 有HCI_LP_RSWITCH,HCI_LP_HOLD,HCI_LP_SNIFF,HCI_LP_PARK |
| __u16 link_mode |
Link modes, 有HCI_LM_ACCEPT,HCI_LM_MASTER,HCI_LM_AUTH等 |
struct workqueue_struct* workqueue |
工作隊列, 在hci_register_dev中初始化,用於控制下面的(延遲)工作隊列任務 |
struct work_struct power_on rx_work cmd_work tx_work |
工作隊列任務, 對應的核心線程為 hci_power_on hci_rx_work hci_cmd_work hci_tx_work |
struct delayed_work power_off discov_off service_cache |
延遲工作隊列任務, 對應的核心線程為 hci_power_off hci_discov_off service_cache_off |
| struct sk_buff_head rx_q raw_q cmd_q |
資料包鏈表 raw_q/cmd_q用於存放待發送的資料包 rx_q用於存放待接收的資料包 |
2. 初始化
HCI部分的初始化是由函數hci_sock_init()來完成
hci_sock_init() -> proto_register() -> bt_sock_register() -> bt_procfs_init()
proto_register將HCI協議加到一個prot_list中(用於/proc/net/protocols)
bt_sock_register向PF_BLUETOOTH註冊協議,即將BTPROTO_HCI添加到bt_proto數組中
同時註冊HCI通訊端建立函數hci_sock_create()
bt_procfs_init向Proc檔案系統添加HCI部分
3. HCI通訊端
在初始化中註冊了HCI通訊端建立函數hci_sock_create()
注意: BTPROTO_HCI只支援SOCK_RAW類型的通訊端
hci_sock_create()主要是在使用者在建立BTPROTO_HCI類型的SOCK_RAW通訊端時調用
包括一些常規的初始化如分配記憶體,初始化HCI通訊端相關資料
最重要的是定義了HCI通訊端操作集hci_sock_ops
當綁定一個HCI通訊端時,有三種通道RAW/MONITOR/CONTROL(RAW為預設的通道)
MONITOR: 用於通知使用者裝置事件,如HCI_DEV_UP/HCI_DEV_SUSPEND,用hci_notify()來完成
4. 發送資料
在Linux中,定義了五種HCI資料包類型
COMMAND/ACLDATA/SCODATA/EVENT/VENDOR
在hci_sock_ops定義了資料發送函數hci_sock_sendmsg()
依據HCI通訊端所綁定的通道的不同,有不同的處理
1) MONITOR: 不做處理
2) CONTROL: 交由mgmt_control()來處理
3) RAW: 根據HCI資料包類型的不同放到raw_q/cmd_q,然後調度工作隊列
調用hci_tx_work/hci_cmd_work進行處理
hci_tx_work函數內容如下
static void hci_tx_work(struct work_struct *work){ struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work); struct sk_buff *skb; BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt, hdev->le_cnt); /* Schedule queues and send stuff to HCI driver */ hci_sched_acl(hdev); hci_sched_sco(hdev); hci_sched_esco(hdev); hci_sched_le(hdev); /* Send next queued raw (unknown type) packet */ while ((skb = skb_dequeue(&hdev->raw_q))) hci_send_frame(skb);}
將不同類型的資料包發送至驅動部分(hci_send_frame函數會調用hci_dev->send)
5. 接收資料
驅動空間接收到來自Controller的資料後,調用hci_recv_frame()
hci_recv_frame()將資料包放入到隊列rx_q中,然後進行工作隊列rx_work調度
從而觸發rx_work對應的處理函數hci_rx_work()
-------------------------------------------------------------------------
若socket處於混雜模式(promisc,即RAW類型的HCI通訊端)
則調用hci_send_to_sock(),然後調用sock_queue_rcv_skb()
sock_queue_rcv_skb()會把資料包添加到隊列sk_receive_queue的尾部
#### 然後中間不知發送了什麼,就接著調用下面的過程…… ####
hci_sock_recvmsg() –> hci_sock_cmsg() –> put_cmsg()發送到原始通訊端
-------------------------------------------------------------------------
hci_rx_work()會根據幀的類型進行不同的處理
1) HCI_EVENT_PKT: hci_event_packet() 處理來自Controller的事件
2) HCI_ACLDATA_PKT: hci_acldata_packet() 處理ACL類型的資料包
3) HCI_SCODATA_PKT: hci_scodata_packet() 處理SCO類型的資料包
hci_acldata_packet和hci_scodata_packet會把資料包往上層的協議發送
6. IOCTL
hci_sock_ioctl函數負責處理ioctl事件,主要包括三類
1) 設定裝置狀態 如HCIDEVUP/HCIDEVDOWN/HCIDEVRESET/HCIDEVRESTAT
2) 擷取裝置資訊 如HCIGETDEVLIST/HCIGETDEVINFO/HCIGETCONNLIST/HCIGETCONNINFO/HCIGETAUTHINFO
3) 設定Controler 如HCISETSCAN/HCISETAUTH/HCISETENCRYPT,向控制器發出命令並等待回應
hci_dev_cmd()->hci_request()->hci_xxx_req()
值得注意的就是HCIDEVUP, 通過調用hci_dev_open來開啟藍牙裝置(會調用hci_dev->open)
7. 驅動部分
驅動部分的代碼位於drivers/bluetooth
一般的藍牙堆疊初始化過程是
xxx_init() –> xxx_probe() –> hci_alloc_dev() –> 初始化hci_dev結構體 –> hci_register_dev()
其中,初始化hci_dev結構體部分主要包括:類型(bus),操作集(open,close,flush,send,ioctl)
通常驅動會自訂一個裝置相關的結構體,進行硬體相關的資料初始化
但該結構體都有一個成員變數是hci_dev結構體
資料的接收函數通常是由中斷來完成,在藍芽中HCI是傳輸在UART/USB等之上,故中斷的方式也是依靠其他模組
通常會在xxx_probe或open函數中顯式或隱式的進行接收函數的註冊
xxx_receive() –> hci_recv_frame() –> 將資料幀放到hci_dev->rx_q鏈表尾部 –> 調度工作隊列中的rx_work
除了hci_recv_frame外,還有兩個bt協議棧資料接收函數hci_recv_fragment和hci_recv_stream_fragment
7. API7.1 為底層提供的API
hci_alloc_dev/hci_free_dev
hci_register_dev/hci_unregister_dev
hci_suspend_dev/hci_resume_dev
hci_recv_frame/hci_recv_fragment/hci_recv_stream_fragment
7.2 為上層提供的API
bt_sock_register/bt_sock_unregister
hci_register_cb/hci_unregister_cb
參考:
<Linux workqueue工作原理>
<Bluetooth UART介面driver初始化分析>