Linux Bluetooth核心分析之HCI部分

來源:互聯網
上載者:User

標籤: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初始化分析>

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.