⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hci_usb.c

📁 嵌入式Linux的蓝牙模块驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/*    HCI USB driver for Linux Bluetooth protocol stack (BlueZ)   Copyright (C) 2000-2001 Qualcomm Incorporated   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>   Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>   This program is free software; you can redistribute it and/or modify   it under the terms of the GNU General Public License version 2 as   published by the Free Software Foundation;   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,    COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS    SOFTWARE IS DISCLAIMED.*//* * Bluetooth HCI USB driver. * Based on original USB Bluetooth driver for Linux kernel *    Copyright (c) 2000 Greg Kroah-Hartman        <greg@kroah.com> *    Copyright (c) 2000 Mark Douglas Corner       <mcorner@umich.edu> * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/unistd.h>#include <linux/types.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/skbuff.h>#include <linux/usb.h>#include <net/bluetooth/bluetooth.h>#include <net/bluetooth/hci_core.h>#include "hci_usb.h"#ifndef CONFIG_BT_HCIUSB_DEBUG#undef  BT_DBG#define BT_DBG(D...)#undef  BT_DMP#define BT_DMP(D...)#endif#ifndef CONFIG_BT_HCIUSB_ZERO_PACKET#undef  URB_ZERO_PACKET#define URB_ZERO_PACKET 0#endif#define VERSION "2.7"static struct usb_driver hci_usb_driver; static struct usb_device_id bluetooth_ids[] = {	/* Generic Bluetooth USB device */	{ USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },	/* AVM BlueFRITZ! USB v2.0 */	{ USB_DEVICE(0x057c, 0x3800) },	/* Bluetooth Ultraport Module from IBM */	{ USB_DEVICE(0x04bf, 0x030a) },	/* ALPS Modules with non-standard id */	{ USB_DEVICE(0x044e, 0x3001) },	{ USB_DEVICE(0x044e, 0x3002) },	/* Ericsson with non-standard id */	{ USB_DEVICE(0x0bdb, 0x1002) },	{ }	/* Terminating entry */};MODULE_DEVICE_TABLE (usb, bluetooth_ids);static struct usb_device_id blacklist_ids[] = {	/* Broadcom BCM2033 without firmware */	{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = HCI_IGNORE },	/* Broadcom BCM2035 */	{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = HCI_RESET },	/* ISSC Bluetooth Adapter v3.1 */	{ USB_DEVICE(0x1131, 0x1001), .driver_info = HCI_RESET },	/* Digianswer device */	{ USB_DEVICE(0x08fd, 0x0001), .driver_info = HCI_DIGIANSWER },	/* RTX Telecom based adapter with buggy SCO support */	{ USB_DEVICE(0x0400, 0x0807), .driver_info = HCI_BROKEN_ISOC },	{ }	/* Terminating entry */};struct _urb *_urb_alloc(int isoc, int gfp){	struct _urb *_urb = kmalloc(sizeof(struct _urb) +				sizeof(struct usb_iso_packet_descriptor) * isoc, gfp);	if (_urb) {		memset(_urb, 0, sizeof(*_urb));		usb_init_urb(&_urb->urb);	}	return _urb;}struct _urb *_urb_dequeue(struct _urb_queue *q){	struct _urb *_urb = NULL;	unsigned long flags;	spin_lock_irqsave(&q->lock, flags);	{		struct list_head *head = &q->head;		struct list_head *next = head->next;		if (next != head) {			_urb = list_entry(next, struct _urb, list);			list_del(next); _urb->queue = NULL;		}	}	spin_unlock_irqrestore(&q->lock, flags);	return _urb;}static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs);static void hci_usb_tx_complete(struct urb *urb, struct pt_regs *regs);#define __pending_tx(husb, type)  (&husb->pending_tx[type-1])#define __pending_q(husb, type)   (&husb->pending_q[type-1])#define __completed_q(husb, type) (&husb->completed_q[type-1])#define __transmit_q(husb, type)  (&husb->transmit_q[type-1])#define __reassembly(husb, type)  (husb->reassembly[type-1])static inline struct _urb *__get_completed(struct hci_usb *husb, int type){	return _urb_dequeue(__completed_q(husb, type)); }#ifdef CONFIG_BT_HCIUSB_SCOstatic void __fill_isoc_desc(struct urb *urb, int len, int mtu){	int offset = 0, i;	BT_DBG("len %d mtu %d", len, mtu);	for (i=0; i < HCI_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) {		urb->iso_frame_desc[i].offset = offset;		urb->iso_frame_desc[i].length = mtu;		BT_DBG("desc %d offset %d len %d", i, offset, mtu);	}	if (len && i < HCI_MAX_ISOC_FRAMES) {		urb->iso_frame_desc[i].offset = offset;		urb->iso_frame_desc[i].length = len;		BT_DBG("desc %d offset %d len %d", i, offset, len);		i++;	}	urb->number_of_packets = i;}#endifstatic int hci_usb_intr_rx_submit(struct hci_usb *husb){	struct _urb *_urb;	struct urb *urb;	int err, pipe, interval, size;	void *buf;	BT_DBG("%s", husb->hdev->name);	size = husb->intr_in_ep->desc.wMaxPacketSize;	buf = kmalloc(size, GFP_ATOMIC);	if (!buf)		return -ENOMEM;	_urb = _urb_alloc(0, GFP_ATOMIC);	if (!_urb) {		kfree(buf);		return -ENOMEM;	}	_urb->type = HCI_EVENT_PKT;	_urb_queue_tail(__pending_q(husb, _urb->type), _urb);	urb = &_urb->urb;	pipe     = usb_rcvintpipe(husb->udev, husb->intr_in_ep->desc.bEndpointAddress);	interval = husb->intr_in_ep->desc.bInterval;	usb_fill_int_urb(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb, interval);		err = usb_submit_urb(urb, GFP_ATOMIC);	if (err) {		BT_ERR("%s intr rx submit failed urb %p err %d",				husb->hdev->name, urb, err);		_urb_unlink(_urb);		_urb_free(_urb);		kfree(buf);	}	return err;}static int hci_usb_bulk_rx_submit(struct hci_usb *husb){	struct _urb *_urb;	struct urb *urb;	int err, pipe, size = HCI_MAX_FRAME_SIZE;	void *buf;	buf = kmalloc(size, GFP_ATOMIC);	if (!buf)		return -ENOMEM;	_urb = _urb_alloc(0, GFP_ATOMIC);	if (!_urb) {		kfree(buf);		return -ENOMEM;	}	_urb->type = HCI_ACLDATA_PKT;	_urb_queue_tail(__pending_q(husb, _urb->type), _urb);	urb  = &_urb->urb;	pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep->desc.bEndpointAddress);	usb_fill_bulk_urb(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb);	urb->transfer_flags = 0;	BT_DBG("%s urb %p", husb->hdev->name, urb);	err = usb_submit_urb(urb, GFP_ATOMIC);	if (err) {		BT_ERR("%s bulk rx submit failed urb %p err %d",				husb->hdev->name, urb, err);		_urb_unlink(_urb);		_urb_free(_urb);		kfree(buf);	}	return err;}#ifdef CONFIG_BT_HCIUSB_SCOstatic int hci_usb_isoc_rx_submit(struct hci_usb *husb){	struct _urb *_urb;	struct urb *urb;	int err, mtu, size;	void *buf;	mtu  = husb->isoc_in_ep->desc.wMaxPacketSize;	size = mtu * HCI_MAX_ISOC_FRAMES;	buf = kmalloc(size, GFP_ATOMIC);	if (!buf)		return -ENOMEM;	_urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);	if (!_urb) {		kfree(buf);		return -ENOMEM;	}	_urb->type = HCI_SCODATA_PKT;	_urb_queue_tail(__pending_q(husb, _urb->type), _urb);	urb = &_urb->urb;	urb->context  = husb;	urb->dev      = husb->udev;	urb->pipe     = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->desc.bEndpointAddress);	urb->complete = hci_usb_rx_complete;	urb->interval = husb->isoc_in_ep->desc.bInterval;	urb->transfer_buffer_length = size;	urb->transfer_buffer = buf;	urb->transfer_flags  = URB_ISO_ASAP;	__fill_isoc_desc(urb, size, mtu);	BT_DBG("%s urb %p", husb->hdev->name, urb);	err = usb_submit_urb(urb, GFP_ATOMIC);	if (err) {		BT_ERR("%s isoc rx submit failed urb %p err %d",				husb->hdev->name, urb, err);		_urb_unlink(_urb);		_urb_free(_urb);		kfree(buf);	}	return err;}#endif/* Initialize device */static int hci_usb_open(struct hci_dev *hdev){	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;	int i, err;	unsigned long flags;	BT_DBG("%s", hdev->name);	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))		return 0;	write_lock_irqsave(&husb->completion_lock, flags);	err = hci_usb_intr_rx_submit(husb);	if (!err) {		for (i = 0; i < HCI_MAX_BULK_RX; i++)			hci_usb_bulk_rx_submit(husb);#ifdef CONFIG_BT_HCIUSB_SCO		if (husb->isoc_iface)			for (i = 0; i < HCI_MAX_ISOC_RX; i++)				hci_usb_isoc_rx_submit(husb);#endif	} else {		clear_bit(HCI_RUNNING, &hdev->flags);	}	write_unlock_irqrestore(&husb->completion_lock, flags);	return err;}/* Reset device */static int hci_usb_flush(struct hci_dev *hdev){	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;	int i;	BT_DBG("%s", hdev->name);	for (i = 0; i < 4; i++)		skb_queue_purge(&husb->transmit_q[i]);	return 0;}static void hci_usb_unlink_urbs(struct hci_usb *husb){	int i;	BT_DBG("%s", husb->hdev->name);	for (i = 0; i < 4; i++) {		struct _urb *_urb;		struct urb *urb;		/* Kill pending requests */		while ((_urb = _urb_dequeue(&husb->pending_q[i]))) {			urb = &_urb->urb;			BT_DBG("%s unlinking _urb %p type %d urb %p", 					husb->hdev->name, _urb, _urb->type, urb);			usb_kill_urb(urb);			_urb_queue_tail(__completed_q(husb, _urb->type), _urb);		}		/* Release completed requests */		while ((_urb = _urb_dequeue(&husb->completed_q[i]))) {			urb = &_urb->urb;			BT_DBG("%s freeing _urb %p type %d urb %p",					husb->hdev->name, _urb, _urb->type, urb);			if (urb->setup_packet)				kfree(urb->setup_packet);			if (urb->transfer_buffer)				kfree(urb->transfer_buffer);			_urb_free(_urb);		}		/* Release reassembly buffers */		if (husb->reassembly[i]) {			kfree_skb(husb->reassembly[i]);			husb->reassembly[i] = NULL;		}	}}/* Close device */static int hci_usb_close(struct hci_dev *hdev){	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;	unsigned long flags;	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))		return 0;	BT_DBG("%s", hdev->name);	/* Synchronize with completion handlers */	write_lock_irqsave(&husb->completion_lock, flags);	write_unlock_irqrestore(&husb->completion_lock, flags);	hci_usb_unlink_urbs(husb);	hci_usb_flush(hdev);	return 0;}static int __tx_submit(struct hci_usb *husb, struct _urb *_urb){	struct urb *urb = &_urb->urb;	int err;	BT_DBG("%s urb %p type %d", husb->hdev->name, urb, _urb->type);	_urb_queue_tail(__pending_q(husb, _urb->type), _urb);	err = usb_submit_urb(urb, GFP_ATOMIC);	if (err) {		BT_ERR("%s tx submit failed urb %p type %d err %d",				husb->hdev->name, urb, _urb->type, err);		_urb_unlink(_urb);		_urb_queue_tail(__completed_q(husb, _urb->type), _urb);	} else		atomic_inc(__pending_tx(husb, _urb->type));	return err;}static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb){	struct _urb *_urb = __get_completed(husb, skb->pkt_type);	struct usb_ctrlrequest *dr;	struct urb *urb;	if (!_urb) {		_urb = _urb_alloc(0, GFP_ATOMIC);		if (!_urb)			return -ENOMEM;		_urb->type = skb->pkt_type;		dr = kmalloc(sizeof(*dr), GFP_ATOMIC);		if (!dr) {			_urb_free(_urb);			return -ENOMEM;		}	} else		dr = (void *) _urb->urb.setup_packet;	dr->bRequestType = husb->ctrl_req;	dr->bRequest = 0;	dr->wIndex   = 0;	dr->wValue   = 0;	dr->wLength  = __cpu_to_le16(skb->len);	urb = &_urb->urb;	usb_fill_control_urb(urb, husb->udev, usb_sndctrlpipe(husb->udev, 0),		(void *) dr, skb->data, skb->len, hci_usb_tx_complete, husb);	BT_DBG("%s skb %p len %d", husb->hdev->name, skb, skb->len);		_urb->priv = skb;	return __tx_submit(husb, _urb);}static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb){	struct _urb *_urb = __get_completed(husb, skb->pkt_type);	struct urb *urb;	int pipe;	if (!_urb) {		_urb = _urb_alloc(0, GFP_ATOMIC);		if (!_urb)			return -ENOMEM;		_urb->type = skb->pkt_type;	}	urb  = &_urb->urb;	pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep->desc.bEndpointAddress);	usb_fill_bulk_urb(urb, husb->udev, pipe, skb->data, skb->len, 			hci_usb_tx_complete, husb);	urb->transfer_flags = URB_ZERO_PACKET;	BT_DBG("%s skb %p len %d", husb->hdev->name, skb, skb->len);	_urb->priv = skb;	return __tx_submit(husb, _urb);}#ifdef CONFIG_BT_HCIUSB_SCOstatic inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb){	struct _urb *_urb = __get_completed(husb, skb->pkt_type);	struct urb *urb;	if (!_urb) {		_urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);		if (!_urb)			return -ENOMEM;		_urb->type = skb->pkt_type;	}	BT_DBG("%s skb %p len %d", husb->hdev->name, skb, skb->len);	urb = &_urb->urb;	urb->context  = husb;	urb->dev      = husb->udev;	urb->pipe     = usb_sndisocpipe(husb->udev, husb->isoc_out_ep->desc.bEndpointAddress);	urb->complete = hci_usb_tx_complete;	urb->transfer_flags = URB_ISO_ASAP;	urb->interval = husb->isoc_out_ep->desc.bInterval;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -