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
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;
-
}
文章来源:https://blog.csdn.net/gotowu/article/details/46687633