linux下的藍牙驅動程序詳解

一、首先要作Bluez協議棧的移植,這樣在開發板上才能夠用hciconfig, hcitool等命令。關於bluez協議棧的移植步驟網上不少。網絡

二、該驅動是USB藍牙設備驅動,分析根據藍牙驅動的寫的順序進行。由於只是要作數據的傳輸,因此講用於語音的等時傳輸部分去掉了。tcp

首先,定義一個結構體函數

    struct bcm_data ={
        struct usb_endpoint_descriptor *intr_ep;
        struct usb_endpoint_descriptor *bulk_tx_ep;     //批量傳輸的收端點
        struct usb_endpoint_descriptor *bulk_rx_ep;    //批量傳輸的收端點
     
        struct usb_anchor tx_anchor;             //用於阻塞操做
        struct usb_anchor intr_anchor;
        struct usb_anchor bulk_anchor;
     
        struct usb_device *udev;
        struct usb_interface *intf;
     
        unsigned long flags;
     
        __u8 cmdreq_type;
    }ui

接下來是入口函數和出口函數指針

    static int __init bcm_driver_init(void)
    {
        usb_register(&bcm_driver);
        return 0;
    }
     
    static void __exit bcm_driver_exit(void)
    {
        usb_deregister(&bcm_driver);
    }
    module_init(bcm_driver_init);
    module_exit(bcm_driver_exit);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("WillwWu")接口


入口函數和出口函數是對該USB設備進行註冊和註銷的操做。事件

而後是定義struct usb_driver,並對其成員進行填充。ip

    static struct usb_driver bcm_driver={
        .name           = "BCMT",
        .probe        = bcm_probe,       //探測函數
        .disconnect    = bcm_disconnect,
        .id_table        = bcm_table,        //所支持的USB設備表
        .supports_autosuspend = 1,        //支持自動掛起,如果設置爲0則不支持
        .disable_hub_initiated_lpm = 1,    //容許低功率態的傳輸
    };ci

支持的USB設備表開發

    static usb_device_id bcm_table[]={
        {    USB_DEVICE(0x0a5c, 0x2148)},
            {},
    }
    MODULE_DEVICE_TABLE(usb, bcm_table);


MODULE_DEVICE_TABLE用於輸出到用戶空間,以便於知道支持什麼設備,第一個參數是所支持的類型,此處爲USB。

下面來看看探測函數

    static int bcm_probe (struct usb_interface *intf ,const struct usb_device_id * id)
    {
        struct usb_endpoint_descriptor *ep_desc;
        struct hci_dev  *hdev;
        struct bcm_data *data;
        int  i,err;
     
        if(intf->cur_altsetting->desc.bInterfaceNumber !=0)   //該接口的編號,端點0保留
            return -ENODEV;
        data=kzalloc( sizeof(*data) ,  GFP_KERNEL)
            if(!data)
                return -ENOMEM;
        for(i=0;i<intf->cur_altsetting->desc.bNumEndpoints;i++){   //對端點描述符進行分配
                ep_desc = &intf->cur_altsetting->endpoint[i].desc;
                if(!data->intr_ep && usb_endpoint_is_int_in(ep_desc)){
                    data->intr_ep=ep_desc;
                    }
                if(!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)){
     
                    data->bulk_tx_ep=ep_desc;
                    }
                if(!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)){
                    data->bulk_rx_ep=ep_desc;
                    }
                if(!data->intr_ep||!data->bulk_tx_ep||!data->bulk_rx_ep){
                    kfree(data);
                    return -ENODEV;
            }    
            }
        data->cmdreq_type=USB_TYPE_CLASS;
        data->udev=interface_to_usbdev(intf); //從接口描述符獲取usb_device結構體信息並賦值
        data->intf=intf;
     
        init_usb_anchor(&data->tx_anchor);    //初始化阻塞
        init_usb_anchor(&data->intr_anchor);
        init_usb_anchor(&data->bulk_anchor);
     
        hdev=hci_alloc_dev();        //申請一個hci_dev
        if(!hdev){
            kfree(data);
            return -ENOMEM;
            }
        hdev->bus = HCI_USB;
        hci_set_drvdata(hdev, data);    //將data中的數據保存到hdev中
        data->hdev=hdev;
        SET_HCIDEV_DEV(hdev, intf->dev);
        /*設置hdev的各成員的函數指針*/
        hdev->open = bcm_open;  
        hdev->close = bcm_close;
        hdev->flush  = bcm_flush
        hdev->send  =bcm_send;
        
        if (!reset)
            set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
        err=hci_register_dev(hdev) //註冊hci_dev
        if (err < 0) {
            hci_free_dev(hdev);
            kfree(data);
            return err;
                }
        usb_set_intfdata(intf, data);  //將data中的數據保存到intf中
        
        return 0;
    }

要區分一下的是:

bNumInterfaces : 配置所支持的接口數.指該配置配備的接口數量,也表示該配置下接口描述符數量.

bInterfaceNumber: 該接口的編號.

bNumEndpoint : 使用的端點數目.端點0除外.

    static void bcm_disconnect(struct usb_interface *intf)
    {
        struct bcm_data *data;
        struct hci_dev *hdev;
     
        if(!data)
            return ;
        hdev = data->hdev;
        intf = data->intf;
        usb_set_intfdata(intf, NULL);
        hci_unregister_dev( hdev);
        hci_free_dev( hdev);
        kfree(data);
    }

該函數所作的就是對probe函數中的註冊等一系列操做的反操做。

    static int bcm_open(struct hci_dev *hdev)
    {
        ……
        if(test_and_set_bit(HCI_RUNNING, &hdev->flags))
            return 0;
        if(test_and_set_bit(BCM_INTR_RUNNING,&data->flags))//BCM_INTR_RUNNING=0
            return 0;
        err=bcm_submit_intr_urb(hdev,GFP_KERNEL);
        if(err<0)
            goto error;
        set_bit(BCM_BULK_RUNNING,&data->flags);    //BCM_BULK_RUNNING=1                
        err=bcm_submit_bulk_urb(hdev,GFP_KERNEL);
    ……
    error:
        clear_bit(HCI_RUNNING, &hdev->flags);
        clear_bit(BCM_INTR_RUNNING,&data->flags);
        clear_bit(BCM_BULK_RUNNING,&data->flags);
        return err;
    }


這個函數是probe中對hdev結構體成員的填充的。主要作就是設置data中的flags參數。其中要說的是set_bit函數,例如set(0,&a)指的是對a中的第0位設置爲1.

這個函數的做用其實也是在作接收函數的初始化的操做,首先咱們先看看err=bcm_submit_intr_urb(hdev,GFP_KERNEL);

    static int bcm_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
    {
        struct bcm_data *data=hci_get_drvdata(hdev) //獲取data數據
        struct urb *urb;
        unsigned char *buf;
        unsigned int pipe;
        int err,size;
     
        if (!data->intr_ep)
            return -ENODEV;
        urb=usb_alloc_urb(0, mem_flags);    分配一個urb
        if(!urb)
            return -ENOMEM;
        size=le16_to_cpu(data->intr_ep->wMaxPacketSize);   //設置最大包的長度大小
        buf=kzalloc(size, mem_flags);                 //分配一個緩衝區
        pipe=usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress); //設置USB的接收端點
        usb_fill_int_urb(urb, data->udev, pipe, buf, size, bcm_intr_complete, hdev ,data->intr_ep->bInterval);     //這個時候就要對urb進行填充了,使用了中斷urb
        urb->transfer_flags |=URB_FREE_BUFFER;//Free transfer buffer with the URB
        usb_anchor_urb(urb, &data->intr_anchor);
        err = usb_submit_urb(urb, mem_flags); //將填充的urb提交給usb core處理。
        if(err<0)
            usb_unanchor_urb(urb);
        usb_free_urb(urb);   //防止重複提交,先進行釋放。
        return err;
    }

在usb_fill_int_urb中有個回調函數,當提交了urb後,將調用該回調函數bcm_intr_complete。

    static void bcm_intr_complete(struct urb *)
    {
        struct hci_dev *hdev = urb->context;
        struct bcm_data *data = hci_get_drvdata(hdev);
        int err;
     
        if(test_bit(HCI_RUNNING, &hdev->flags))
            return
    /*判斷urb是否發送成功,若status爲0,則表示數據被髮送或者接受成功*/
        if(urb->status==0){
            hdev->stat.byte_rx+=urb->actual_length;
            if(hci_recv_fragment( hdev,HCI_EVENT_PKT, urb->transfer_buffer, urb->actual_length)<0)
                hdev->stat.err_rx++;
            }
        if(!test_bit(BCM_INTR_RUNNING, &data->flags));
            return;
        usb_anchor_urb(urb, &data->intr_anchor);
        err=usb_submit_urb(urb, GFP_KERNEL);
        if(err<0){
            usb_unanchor_urb(urb);
        }
    }


幀的類型:

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_recv_fragment是bt協議棧數據接收函數。 hci_recv_fragmen 將數據幀放到hci_dev->rx_q鏈表尾部

    int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count)
    {
        int rem = 0;
     
        if (type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT)
            return -EILSEQ;
     
        while (count) {
            rem = hci_reassembly(hdev, type, data, count, type - 1);
            if (rem < 0)
                return rem;
     
            data += (count - rem);
            count = rem;
        }
     
        return rem;
    }


下面是批量傳輸的bulk_urb的初始化操做

    static int bcm_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
    {
        struct bcm_data *data=hci_get_drvdata(hdev);
        struct urb *urb;
        unsigned *buf;
        unsigned int pipe;
        int err,size = HCI_MAX_FRAME_SIZE;
     
        if(!data->bulk_rx_ep)
            return -ENODEV;
        urb=usb_alloc_urb(0, mem_flags);
        if(!urb)
            return -ENOMEM;
        buf=kzalloc(size, mem_flags);
        pipe=usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
        usb_fill_bulk_urb(urb, data->udev, pipe, buf, size, bcm_bulk_complete, hdev);
        usb_anchor_urb(urb, &data->bulk_anchor);
        err=usb_submit_urb(urb, mem_flags);
        if(err<0)
            usb_unanchor_urb( urb)
        usb_free_urb(urb);
        return err;
     
    }


該函數的操做與上面那個中斷的幾乎相同,就是在usb_fill_bulk_urb時使用了批量urb。

    static void bcm_bulk_complete(struct urb *)
    {
        struct hci_dev *hdev = urb->context;
        struct bcm_data *data = hci_get_drvdata(hdev);
        int err;
     
        if(test_bit(HCI_RUNNING, &hdev->flags))
            return
        if(urb->status==0){
            hdev->stat.byte_rx+=urb->actual_length;
            if(hci_recv_fragment( hdev,HCI_ACLDATA_PKT, urb->transfer_buffer, urb->actual_length)<0)
                hdev->stat.err_rx++;
            }
        if(!test_bit(BCM_BULK_RUNNING, &data->flags));
            return;
        usb_anchor_urb(urb,& data->bulk_anchor);
        err=usb_submit_urb(urb, GFP_KERNEL);
        if(err<0){
            usb_unanchor_urb(urb);
        }
    }


此處也與中斷的同樣。

下面來看看對於發送函數時如何進行操做的。在Linux中,定義了五種HCI數據包類型

COMMAND/ACLDATA/SCODATA/EVENT/VENDOR,咱們此處只對其中的COMMAND和ACLDATA進行發送。bcm_send用於提供給HCI去發送數據包。

    static int bcm_send (struct sk_buff *skb)
    {
        struct hci_dev *hdev = (struct hci_dev *) skb->dev;
        struct bcm_data *data=hci_get_drvdata( hdev);
        struct urb *urb;
        struct usb_ctrlrequest *cr;
        unsigned int pipe;
     
        if(!test_bit(HCI_RUNNING,&hdev->flags))     //每一步都要首先檢測是否正在運行
            return -EBUSY;
        switch(bt_cb(skb)->pkt_type){           //從skb中的控制buffer中取出包的類型
            case HCI_COMMAND_PKT:
                urb=usb_alloc_urb(0, GFP_ATOMIC);
                if(!urb)
                    return -ENOMEM;
                cr=kmalloc(sizeof(*cr), GFP_ATOMIC);
                if(!cr){
                    usb_free_urb(urb);
                    return -ENOMEM;
                    }
                cr->bRequestType = data->cmdreq_type;
                cr->bRequest     = 0;
                cr->wIndex       = 0;
                cr->wValue       = 0;
                cr->wLength      = __cpu_to_le16(skb->len);
     
                pipe = usb_sndctrlpipe(data->udev, 0x00);
     /*填充控制URB,這裏咱們須要注意的是,此處的數據緩衝區和數據的長度,都是由skb中的結構體成員進行設置的*/
                usb_fill_control_urb(urb, data->udev, pipe, (void *) cr,skb->data, skb->len, bcm_tx_complete, skb);
                hdev->stat.cmd_tx++;
                break;
            case HCI_ACLDATA_PKT
                urb=usb_alloc_urb(0, GFP_ATOMIC);
                if(!urb)
                    return -ENOMEM;
                pipe=usb_sndbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
                usb_fill_bulk_urb( urb, data->udev, pipe, skb->data, skb->len, bcm_tx_complete, skb);   //填充批量URB
                hdev->stat.acl_tx++;
                        break;
            default:
                return -EILSEQ;
            }
            usb_anchor_urb(urb, &data->tx_anchor);
            err=usb_submit_urb(urb,GFP_ATOMIC);
            if(err<0){
                kfree(urb->setup_packet);
                usb_unanchor_urb(urb);
                }
            return err;
    }


首先咱們要來看看struct sk_buff 這個結構體。

sk_buff是Linux網絡代碼中最重要的結構體之一。它是Linux在其協議棧裏傳送的結構體,也就是所謂的「包」,在他裏面包含了各層協議的頭部,好比ethernet, ip ,tcp ,udp等等。而且他是一個複雜的雙向鏈表,在他結構中有next和prev指針,分別指向鏈表的下一個節點和前一個節點.

此處的回調函數是bcm_tx_complete

    static void bcm_tx_complete(struct urb *)
    {    
        struct sk_buff *skb=urb->context;
        struct hci_dev *hdev = (struct hci_dev *)skb->dev;
        struct bcm_data *data= hci_get_drvdata(hdev);
     
        if(!test_bit(HCI_RUNNING,&hdev->flags));
            goto done ;
        if(!urb->status)
            hdev->stat.byte_tx+=urb->transfer_buffer_length;
        else
            hdev->stat.err_tx++;
    done:
        kfree(urb->setup_packet);
        kfree_skb(skb);
    }


最後是close函數

    static int bcm_close(struct hci_dev *hdev)
    {
        struct bcm_data *data = hci_get_drvdata(hdev);
        if(!test_and_clear_bit(HCI_RUNNING,&hdev->flags))
            return 0;
        clear_bit(BCM_INTR_RUNNING, &data->flags);
        clear_bit(BCM_BULK_RUNNING, &data->flags);
        data->intf->needs_remote_wakeup=0;
        return 0;
    }


就是針對data的flags進行位清零設置。

最後

    static int bcm_flush (struct hci_dev *hdev)     {         struct bcm_data *data=hci_get_drvdata( hdev)         usb_kill_anchored_urbs(&data->tx_anchor);  //取消傳輸請求         return 0;     }

相關文章
相關標籤/搜索