1. 程式人生 > >藍芽驅動相關程式碼

藍芽驅動相關程式碼

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