藍芽驅動相關程式碼
阿新 • • 發佈:2018-12-21
https://blog.csdn.net/absurd/article/details/1852337
HCI在主機端的驅動主要是為上層提供一個統一的介面,讓上層協議不依賴於具體硬體的實現。HCI在硬體中的韌體與HCI在主機端的驅動通訊方式有多種,比如像UART、USB和PC Card等等。
drivers/bluetooth/bfusb.c
static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *id){ const struct firmware *firmware; struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_endpoint *bulk_out_ep; struct usb_host_endpoint *bulk_in_ep; struct hci_dev *hdev; struct bfusb_data *data; /* Check number of endpoints */ if (intf->cur_altsetting->desc.bNumEndpoints < 2) return -EIO; bulk_out_ep = &intf->cur_altsetting->endpoint[0]; bulk_in_ep = &intf->cur_altsetting->endpoint[1]; if (!bulk_out_ep || !bulk_in_ep) { BT_ERR("Bulk endpoints not found"); goto done; } /* Initialize control structure and load firmware */ data = devm_kzalloc(&intf->dev, sizeof(struct bfusb_data), GFP_KERNEL); if (!data) { BT_ERR("Can't allocate memory for control structure"); goto done; } data->udev = udev; data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress; data->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress; data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize); rwlock_init(&data->lock); data->reassembly = NULL; skb_queue_head_init(&data->transmit_q); skb_queue_head_init(&data->pending_q); skb_queue_head_init(&data->completed_q); if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) { BT_ERR("Firmware request failed"); goto done; } if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) { BT_ERR("Firmware loading failed"); goto release; } release_firmware(firmware); /* Initialize and register HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can't allocate HCI device"); goto done; } data->hdev = hdev; hdev->bus = HCI_USB; hci_set_drvdata(hdev, data); SET_HCIDEV_DEV(hdev, &intf->dev); hdev->open = bfusb_open; hdev->close = bfusb_close; hdev->flush = bfusb_flush; hdev->send = bfusb_send_frame; set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); if (hci_register_dev(hdev) < 0) {//在hci_core.c註冊HCI裝置 BT_ERR("Can't register HCI device"); hci_free_dev(hdev); goto done; } usb_set_intfdata(intf, data); return 0; release: release_firmware(firmware); done: return -EIO; }
hci_core.c相當於一個框架,用於把各種具體通訊方式膠合起來,並提供一些公共函式的實現。
//負責傳送CMD的任務 static void hci_cmd_work(struct work_struct *work){ struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_work); struct sk_buff *skb; /* Send queued commands */ if (atomic_read(&hdev->cmd_cnt)) { skb = skb_dequeue(&hdev->cmd_q);//從hdev->cmd_q佇列中取CMD if (!skb) return; kfree_skb(hdev->sent_cmd); hdev->sent_cmd = skb_clone(skb, GFP_KERNEL); if (hdev->sent_cmd) { atomic_dec(&hdev->cmd_cnt); hci_send_frame(hdev, skb);//呼叫hci_send_frame把CMD傳送出去 if (test_bit(HCI_RESET, &hdev->flags)) cancel_delayed_work(&hdev->cmd_timer); else schedule_delayed_work(&hdev->cmd_timer, HCI_CMD_TIMEOUT); } else { skb_queue_head(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); } } } //負責傳送資料的任務 static void hci_tx_work(struct work_struct *work){ struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work); struct sk_buff *skb; if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { /* Schedule queues and send stuff to HCI driver */ hci_sched_acl(hdev);//傳送所有connection中的ACL資料 hci_sched_sco(hdev);//傳送所有connection中的SCO資料 hci_sched_esco(hdev); hci_sched_le(hdev); } /* Send next queued raw (unknown type) packet */ while ((skb = skb_dequeue(&hdev->raw_q)))//傳送hdev->raw_q中的資料包 hci_send_frame(hdev, skb); } //負責接收資料的任務 static void hci_rx_work(struct work_struct *work){ struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work); struct sk_buff *skb; while ((skb = skb_dequeue(&hdev->rx_q))) { //從hdev->rx_q佇列中取資料 /* Send copy to monitor */ hci_send_to_monitor(hdev, skb); if (atomic_read(&hdev->promisc)) { /* Send copy to the sockets */ hci_send_to_sock(hdev, skb); } if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { kfree_skb(skb); continue; } if (test_bit(HCI_INIT, &hdev->flags)) { /* Don't process data packets in this states. */ switch (bt_cb(skb)->pkt_type) { case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: kfree_skb(skb); continue; } } /* Process frame */ switch (bt_cb(skb)->pkt_type) { //根據資料的型別呼叫上層函式處理 case HCI_EVENT_PKT://如連線建立或斷開,認證和加密等事件,控制協議狀態改變。 BT_DBG("%s Event packet", hdev->name); hci_event_packet(hdev, skb); break; case HCI_ACLDATA_PKT://非同步非連線的資料包,提交給上層的L2CAP協議處理 BT_DBG("%s ACL data packet", hdev->name); hci_acldata_packet(hdev, skb); break; case HCI_SCODATA_PKT://同步面向連線的資料包,提交給上層的SCO協議處理 BT_DBG("%s SCO data packet", hdev->name); hci_scodata_packet(hdev, skb); break; default: kfree_skb(skb); break; } } }
//註冊上層協議 int hci_register_cb(struct hci_cb *cb){ mutex_lock(&hci_cb_list_lock); list_add_tail(&cb->list, &hci_cb_list); mutex_unlock(&hci_cb_list_lock); return 0; } //登出上層協議 int hci_unregister_cb(struct hci_cb *cb){ mutex_lock(&hci_cb_list_lock); list_del(&cb->list); mutex_unlock(&hci_cb_list_lock); return 0; }
//傳送SCO資料包
void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb){
struct hci_dev *hdev = conn->hdev;
struct hci_sco_hdr hdr;
hdr.handle = cpu_to_le16(conn->handle);
hdr.dlen = skb->len;
skb_push(skb, HCI_SCO_HDR_SIZE);
skb_reset_transport_header(skb);
memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE);
bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
skb_queue_tail(&conn->data_q, skb);//把要傳送的資料包放入conn->data_q佇列
queue_work(hdev->workqueue, &hdev->tx_work);//排程傳送任務去傳送
}
//傳送ACL資料包
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags){
struct hci_dev *hdev = chan->conn->hdev;
hci_queue_acl(chan, &chan->data_q, skb, flags);//把要傳送的資料包放入chan->data_q佇列
queue_work(hdev->workqueue, &hdev->tx_work);//排程傳送任務去傳送
}
//傳送命令資料
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, const void *param){
struct sk_buff *skb;
skb = hci_prepare_cmd(hdev, opcode, plen, param);
if (!skb) {
BT_ERR("%s no memory for command", hdev->name);
return -ENOMEM;
}
/* Stand-alone HCI commands must be flagged as single-command requests.*/
bt_cb(skb)->hci.req_start = true;
skb_queue_tail(&hdev->cmd_q, skb);//把要傳送的資料包放入hdev->cmd_q佇列
queue_work(hdev->workqueue, &hdev->cmd_work);//排程命令傳送任務去傳送
return 0;
}
net/hci_sock.c
給上層提供一個socket介面,應用程式可以通過socket的方式來訪問HCI。
//初始化sock
int __init hci_sock_init(void){
int err;
err = proto_register(&hci_sk_proto, 0);
if (err < 0)
return err;
err = bt_sock_register(BTPROTO_HCI, &hci_sock_family_ops);//註冊了BTPROTO_HCI型別family
if (err < 0) {
BT_ERR("HCI socket registration failed");
goto error;
}
err = bt_procfs_init(&init_net, "hci", &hci_sk_list, NULL);
if (err < 0) {
BT_ERR("Failed to create HCI proc file");
bt_sock_unregister(BTPROTO_HCI);
goto error;
}
return 0;
error:
proto_unregister(&hci_sk_proto);
return err;
}
//建立sock的函式
static int hci_sock_create(struct net *net, struct socket *sock, int protocol,int kern){
struct sock *sk;
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
sock->ops = &hci_sock_ops;//sock的ops指向hci_sock_ops
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hci_sk_proto, kern);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sock->state = SS_UNCONNECTED;
sk->sk_state = BT_OPEN;
bt_sock_link(&hci_sk_list, sk);
return 0;
}
static const struct proto_ops hci_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = hci_sock_release,
.bind = hci_sock_bind,
.getname = hci_sock_getname,
.sendmsg = hci_sock_sendmsg,
.recvmsg = hci_sock_recvmsg,
.ioctl = hci_sock_ioctl,
.poll = datagram_poll,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = hci_sock_setsockopt,
.getsockopt = hci_sock_getsockopt,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.mmap = sock_no_mmap
};
L2CAP是HCI之上的協議,提供諸如QoS,分組,多路複用,分段和組裝之類的功能。
l2cap_sock.c
//建立sock的函式
static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, int kern){
struct sock *sk;
sock->state = SS_UNCONNECTED;
if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
return -EPERM;
sock->ops = &l2cap_sock_ops;//sock的ops指向l2cap_sock_ops。
sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
if (!sk)
return -ENOMEM;
l2cap_sock_init(sk, NULL);
bt_sock_link(&l2cap_sk_list, sk);
return 0;
}
static const struct proto_ops l2cap_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = l2cap_sock_release,
.bind = l2cap_sock_bind,
.connect = l2cap_sock_connect,
.listen = l2cap_sock_listen,
.accept = l2cap_sock_accept,
.getname = l2cap_sock_getname,
.sendmsg = l2cap_sock_sendmsg,
.recvmsg = l2cap_sock_recvmsg,
.poll = bt_sock_poll,
.ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
.shutdown = l2cap_sock_shutdown,
.setsockopt = l2cap_sock_setsockopt,
.getsockopt = l2cap_sock_getsockopt
};
//把訊息傳遞給下層的裝置
static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len){
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
int err;
err = sock_error(sk);
if (err)
return err;
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
if (sk->sk_state != BT_CONNECTED)
return -ENOTCONN;
lock_sock(sk);
err = bt_sock_wait_ready(sk, msg->msg_flags);
release_sock(sk);
if (err)
return err;
l2cap_chan_lock(chan);
err = l2cap_chan_send(chan, msg, len);//通過l2cap_do_send-->hci_send_acl傳送訊息
l2cap_chan_unlock(chan);
return err;
}
SCO也是執行在HCI之上的協議,它是面向連線的可靠的傳輸方式,主要用於聲音資料傳輸。
net/sco.c
//建立sock的函式
static int sco_sock_create(struct net *net, struct socket *sock, int protocol, int kern){
struct sock *sk;
sock->state = SS_UNCONNECTED;
if (sock->type != SOCK_SEQPACKET)
return -ESOCKTNOSUPPORT;
sock->ops = &sco_sock_ops;//sock的ops指向sco_sock_ops
sk = sco_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
if (!sk)
return -ENOMEM;
sco_sock_init(sk, NULL);
return 0;
}
static const struct proto_ops sco_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = sco_sock_release,
.bind = sco_sock_bind,
.connect = sco_sock_connect,
.listen = sco_sock_listen,
.accept = sco_sock_accept,
.getname = sco_sock_getname,
.sendmsg = sco_sock_sendmsg,
.recvmsg = sco_sock_recvmsg,
.poll = bt_sock_poll,
.ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
.shutdown = sco_sock_shutdown,
.setsockopt = sco_sock_setsockopt,
.getsockopt = sco_sock_getsockopt
};
//把訊息傳遞給下層的裝置
static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len){
struct sock *sk = sock->sk;
int err;
err = sock_error(sk);
if (err)
return err;
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
lock_sock(sk);
if (sk->sk_state == BT_CONNECTED)
err = sco_send_frame(sk, msg, len);//通過呼叫hci_send_sco傳送訊息
else
err = -ENOTCONN;
release_sock(sk);
return err;
}