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);
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函数中的注册等⼀系列操作的反操作。
……
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。
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-&_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;
昂达v711
data += (count - rem);
count = rem;
}
return rem;
}
下⾯是批量传输的bulk_urb的初始化操作

本文发布于:2024-09-22 19:34:40,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/596852.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:函数   数据   端点   传输   结构   协议   操作   成员
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议