1. 程式人生 > >linux下的藍芽驅動程式詳解

linux下的藍芽驅動程式詳解

1、首先要做Bluez協議棧的移植,這樣在開發板上才可以用hciconfig, hcitool等命令。關於bluez協議棧的移植步驟網上很多。

2、該驅動是USB藍芽裝置驅動,分析根據藍芽驅動的寫的順序進行。因為只是要做資料的傳輸,所以講用於語音的等時傳輸部分去掉了。

首先,定義一個結構體

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;
}

接下來是入口函式和出口函式

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,並對其成員進行填充。

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,    //允許低功率態的傳輸
};

支援的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函式,例如set0&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_fragmentbt協議棧資料接收函式。 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,我們此處只對其中的COMMANDACLDATA進行傳送。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_buffLinux網路程式碼中最重要的結構體之一。它是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;
}

就是針對dataflags進行位清零設定。

最後

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;
}