linux下的蓝牙驱动程序详解
2015-06-29 21:51
447 查看
1、首先要做Bluez协议栈的移植,这样在开发板上才可以用hciconfig, hcitool等命令。关于bluez协议栈的移植步骤网上很多。
2、该驱动是USB蓝牙设备驱动,分析根据蓝牙驱动的写的顺序进行。因为只是要做数据的传输,所以讲用于语音的等时传输部分去掉了。
首先,定义一个结构体
接下来是入口函数和出口函数
入口函数和出口函数是对该USB设备进行注册和注销的操作。
然后是定义struct usb_driver,并对其成员进行填充。
支持的USB设备表
MODULE_DEVICE_TABLE用于输出到用户空间,以便于知道支持什么设备,第一个参数是所支持的类型,此处为USB。
下面来看看探测函数
要区分一下的是:
bNumInterfaces : 配置所支持的接口数.指该配置配备的接口数量,也表示该配置下接口描述符数量.
bInterfaceNumber: 该接口的编号.
bNumEndpoint : 使用的端点数目.端点0除外.
该函数所做的就是对probe函数中的注册等一系列操作的反操作。
这个函数是probe中对hdev结构体成员的填充的。主要做就是设置data中的flags参数。其中要说的是set_bit函数,例如set(0,&a)指的是对a中的第0位设置为1.
这个函数的作用其实也是在做接收函数的初始化的操作,首先我们先看看err=bcm_submit_intr_urb(hdev,GFP_KERNEL);
在usb_fill_int_urb中有个回调函数,当提交了urb后,将调用该回调函数bcm_intr_complete。
帧的类型:
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链表尾部
下面是批量传输的bulk_urb的初始化操作
该函数的操作与上面那个中断的几乎相同,就是在usb_fill_bulk_urb时使用了批量urb。
此处也与中断的一样。
下面来看看对于发送函数时如何进行操作的。在Linux中,定义了五种HCI数据包类型
COMMAND/ACLDATA/SCODATA/EVENT/VENDOR,我们此处只对其中的COMMAND和ACLDATA进行发送。bcm_send用于提供给HCI去发送数据包。
首先我们要来看看struct sk_buff 这个结构体。
sk_buff是Linux网络代码中最重要的结构体之一。它是Linux在其协议栈里传送的结构体,也就是所谓的“包”,在他里面包含了各层协议的头部,比如ethernet, ip ,tcp ,udp等等。并且他是一个复杂的双向链表,在他结构中有next和prev指针,分别指向链表的下一个节点和前一个节点.
此处的回调函数是bcm_tx_complete
最后是close函数
就是针对data的flags进行位清零设置。
最后
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函数,例如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; }
相关文章推荐
- linux0.12之内存管理(1)-整体框架思路
- linux内核之关于内存屏障
- vi编辑器的常用命令
- Linux性能分析top iostat vmstat free
- linux常见问题
- linux程序设计——调试(第十章)
- Linux驱动修炼之道-驱动中一些常见的宏
- 初窥Linux 之 我最常用的20条命令
- Linux设备驱动中的并发控制总结
- linux下的lvs dr模式配置
- linux系统下VIM使用小结(持续更新)
- Linux kernel parameter command line设置
- Linux下逻辑地址-线性地址-物理地址图解
- Centos6.4 安装bind dns 服务器
- zlib交叉编译,libpng交叉编译
- Linux内核启动参数
- Linux服务器的最大内存和CPU数
- 每天一个linux命令(50):crontab命令
- linux运维常用命令一句话(持续更新中)
- linux运维常用命令一句话(持续更新中)