linux 蓝牙驱动 chi,linux下的蓝牙驱动程序详解

http://blog.csdn.net/gotowu/article/details/46687633

1、首先要做Bluez协议栈的移植,这样在开发板上才可以用hciconfig, hcitool等命令。关于bluez协议栈的移植步骤网上很多。

2、该驱动是USB蓝牙设备驱动,分析根据蓝牙驱动的写的顺序进行。因为只是要做数据的传输,所以讲用于语音的等时传输部分去掉了。

首先,定义一个结构体

[cpp]  view plain

copy

structbcm_data ={

structusb_endpoint_descriptor *intr_ep;

structusb_endpoint_descriptor *bulk_tx_ep;//批量传输的收端点

structusb_endpoint_descriptor *bulk_rx_ep;//批量传输的收端点

structusb_anchor tx_anchor;//用于阻塞操作

structusb_anchor intr_anchor;

structusb_anchor bulk_anchor;

structusb_device *udev;

structusb_interface *intf;

unsigned longflags;

__u8 cmdreq_type;

}

接下来是入口函数和出口函数

[cpp]  view plain

copy

staticint__init bcm_driver_init(void)

{

usb_register(&bcm_driver);

return0;

}

staticvoid__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,并对其成员进行填充。

[cpp]  view plain

copy

staticstructusb_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设备表

[cpp]  view plain

copy

staticusb_device_id bcm_table[]={

{   USB_DEVICE(0x0a5c, 0x2148)},

{},

}

MODULE_DEVICE_TABLE(usb, bcm_table);

MODULE_DEVICE_TABLE用于输出到用户空间,以便于知道支持什么设备,第一个参数是所支持的类型,此处为USB。

下面来看看探测函数

[cpp]  view plain

copy

staticintbcm_probe (structusb_interface *intf ,conststructusb_device_id * id)

{

structusb_endpoint_descriptor *ep_desc;

structhci_dev  *hdev;

structbcm_data *data;

inti,err;

if(intf->cur_altsetting->desc.bInterfaceNumber !=0)//该接口的编号,端点0保留

return-ENODEV;

data=kzalloc( sizeof(*data) ,  GFP_KERNEL)

if(!data)

return-ENOMEM;

for(i=0;icur_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

hci_free_dev(hdev);

kfree(data);

returnerr;

}

usb_set_intfdata(intf, data);  //将data中的数据保存到intf中

return0;

}

要区分一下的是:

bNumInterfaces : 配置所支持的接口数.指该配置配备的接口数量,也表示该配置下接口描述符数量.

bInterfaceNumber: 该接口的编号.

bNumEndpoint : 使用的端点数目.端点0除外.

[cpp]  view plain

copy

staticvoidbcm_disconnect(structusb_interface *intf)

{

structbcm_data *data;

structhci_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函数中的注册等一系列操作的反操作。

[cpp]  view plain

copy

staticintbcm_open(structhci_dev *hdev)

{

……

if(test_and_set_bit(HCI_RUNNING, &hdev->flags))

return0;

if(test_and_set_bit(BCM_INTR_RUNNING,&data->flags))//BCM_INTR_RUNNING=0

return0;

err=bcm_submit_intr_urb(hdev,GFP_KERNEL);

if(err<0)

gotoerror;

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

returnerr;

}

这个函数是probe中对hdev结构体成员的填充的。主要做就是设置data中的flags参数。其中要说的是set_bit函数,例如set(0,&a)指的是对a中的第0位设置为1.

这个函数的作用其实也是在做接收函数的初始化的操作,首先我们先看看err=bcm_submit_intr_urb(hdev,GFP_KERNEL);

[cpp]  view plain

copy

staticintbcm_submit_intr_urb(structhci_dev *hdev, gfp_t mem_flags)

{

structbcm_data *data=hci_get_drvdata(hdev)//获取data数据

structurb *urb;

unsigned char*buf;

unsigned intpipe;

interr,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);   //防止重复提交,先进行释放。

returnerr;

}

在usb_fill_int_urb中有个回调函数,当提交了urb后,将调用该回调函数bcm_intr_complete。

[cpp]  view plain

copy

staticvoidbcm_intr_complete(structurb *)

{

structhci_dev *hdev = urb->context;

structbcm_data *data = hci_get_drvdata(hdev);

interr;

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链表尾部

[cpp]  view plain

copy

inthci_recv_fragment(structhci_dev *hdev,inttype,void*data,intcount)

{

intrem = 0;

if(type  HCI_EVENT_PKT)

return-EILSEQ;

while(count) {

rem = hci_reassembly(hdev, type, data, count, type - 1);

if(rem

returnrem;

data += (count - rem);

count = rem;

}

returnrem;

}

下面是批量传输的bulk_urb的初始化操作

[cpp]  view plain

copy

staticintbcm_submit_bulk_urb(structhci_dev *hdev, gfp_t mem_flags)

{

structbcm_data *data=hci_get_drvdata(hdev);

structurb *urb;

unsigned *buf;

unsigned intpipe;

interr,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);

returnerr;

}

该函数的操作与上面那个中断的几乎相同,就是在usb_fill_bulk_urb时使用了批量urb。

[cpp]  view plain

copy

staticvoidbcm_bulk_complete(structurb *)

{

structhci_dev *hdev = urb->context;

structbcm_data *data = hci_get_drvdata(hdev);

interr;

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去发送数据包。

[cpp]  view plain

copy

staticintbcm_send (structsk_buff *skb)

{

structhci_dev *hdev = (structhci_dev *) skb->dev;

structbcm_data *data=hci_get_drvdata( hdev);

structurb *urb;

structusb_ctrlrequest *cr;

unsigned intpipe;

if(!test_bit(HCI_RUNNING,&hdev->flags))//每一步都要首先检测是否正在运行

return-EBUSY;

switch(bt_cb(skb)->pkt_type){//从skb中的控制buffer中取出包的类型

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

caseHCI_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);

}

returnerr;

}

首先我们要来看看struct sk_buff 这个结构体。

sk_buff是Linux网络代码中最重要的结构体之一。它是Linux在其协议栈里传送的结构体,也就是所谓的“包”,在他里面包含了各层协议的头部,比如ethernet, ip ,tcp ,udp等等。并且他是一个复杂的双向链表,在他结构中有next和prev指针,分别指向链表的下一个节点和前一个节点.

此处的回调函数是bcm_tx_complete

[cpp]  view plain

copy

staticvoidbcm_tx_complete(structurb *)

{

structsk_buff *skb=urb->context;

structhci_dev *hdev = (structhci_dev *)skb->dev;

structbcm_data *data= hci_get_drvdata(hdev);

if(!test_bit(HCI_RUNNING,&hdev->flags));

gotodone ;

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函数

[cpp]  view plain

copy

staticintbcm_close(structhci_dev *hdev)

{

structbcm_data *data = hci_get_drvdata(hdev);

if(!test_and_clear_bit(HCI_RUNNING,&hdev->flags))

return0;

clear_bit(BCM_INTR_RUNNING, &data->flags);

clear_bit(BCM_BULK_RUNNING, &data->flags);

data->intf->needs_remote_wakeup=0;

return0;

}

就是针对data的flags进行位清零设置。

最后

[cpp]  view plain

copy

staticintbcm_flush (structhci_dev *hdev)

{

structbcm_data *data=hci_get_drvdata( hdev)

usb_kill_anchored_urbs(&data->tx_anchor);  //取消传输请求

return0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值