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

📄 hci_usb.c

📁 HCI USB driver for Linux Bluetooth protocol stack
💻 C
📖 第 1 页 / 共 2 页
字号:
		}	} while(test_bit(HCI_USB_TX_WAKEUP, &husb->state));}static inline void hci_usb_tx_wakeup(struct hci_usb *husb){	/* Serialize TX queue processing to avoid data reordering */	if (!test_and_set_bit(HCI_USB_TX_PROCESS, &husb->state)) {		hci_usb_tx_process(husb);		clear_bit(HCI_USB_TX_PROCESS, &husb->state);	} else		set_bit(HCI_USB_TX_WAKEUP, &husb->state);}/* Send frames from HCI layer */static int hci_usb_send_frame(struct sk_buff *skb){	struct hci_dev *hdev = (struct hci_dev *) skb->dev;	struct hci_usb *husb;	if (!hdev) {		BT_ERR("frame for uknown device (hdev=NULL)");		return -ENODEV;	}	if (!test_bit(HCI_RUNNING, &hdev->flags))		return -EBUSY;	BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);	husb = (struct hci_usb *) hdev->driver_data;	switch (bt_cb(skb)->pkt_type) {	case HCI_COMMAND_PKT:		hdev->stat.cmd_tx++;		break;	case HCI_ACLDATA_PKT:		hdev->stat.acl_tx++;		break;#ifdef CONFIG_BT_HCIUSB_SCO	case HCI_SCODATA_PKT:		hdev->stat.sco_tx++;		break;#endif	default:		kfree_skb(skb);		return 0;	}	read_lock(&husb->completion_lock);	skb_queue_tail(__transmit_q(husb, bt_cb(skb)->pkt_type), skb);	hci_usb_tx_wakeup(husb);	read_unlock(&husb->completion_lock);	return 0;}static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int count){	BT_DBG("%s type %d data %p count %d", husb->hdev->name, type, data, count);	husb->hdev->stat.byte_rx += count;	while (count) {		struct sk_buff *skb = __reassembly(husb, type);		struct { int expect; } *scb;		int len = 0;			if (!skb) {			/* Start of the frame */			switch (type) {			case HCI_EVENT_PKT:				if (count >= HCI_EVENT_HDR_SIZE) {					struct hci_event_hdr *h = data;					len = HCI_EVENT_HDR_SIZE + h->plen;				} else					return -EILSEQ;				break;			case HCI_ACLDATA_PKT:				if (count >= HCI_ACL_HDR_SIZE) {					struct hci_acl_hdr *h = data;					len = HCI_ACL_HDR_SIZE + __le16_to_cpu(h->dlen);				} else					return -EILSEQ;				break;#ifdef CONFIG_BT_HCIUSB_SCO			case HCI_SCODATA_PKT:				if (count >= HCI_SCO_HDR_SIZE) {					struct hci_sco_hdr *h = data;					len = HCI_SCO_HDR_SIZE + h->dlen;				} else					return -EILSEQ;				break;#endif			}			BT_DBG("new packet len %d", len);			skb = bt_skb_alloc(len, GFP_ATOMIC);			if (!skb) {				BT_ERR("%s no memory for the packet", husb->hdev->name);				return -ENOMEM;			}			skb->dev = (void *) husb->hdev;			bt_cb(skb)->pkt_type = type;				__reassembly(husb, type) = skb;			scb = (void *) skb->cb;			scb->expect = len;		} else {			/* Continuation */			scb = (void *) skb->cb;			len = scb->expect;		}		len = min(len, count);				memcpy(skb_put(skb, len), data, len);		scb->expect -= len;		if (!scb->expect) {			/* Complete frame */			__reassembly(husb, type) = NULL;			bt_cb(skb)->pkt_type = type;			hci_recv_frame(skb);		}		count -= len; data += len;	}	return 0;}static void hci_usb_rx_complete(struct urb *urb){	struct _urb *_urb = container_of(urb, struct _urb, urb);	struct hci_usb *husb = (void *) urb->context;	struct hci_dev *hdev = husb->hdev;	int err, count = urb->actual_length;	BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,			_urb->type, urb->status, count, urb->transfer_flags);	read_lock(&husb->completion_lock);	if (!test_bit(HCI_RUNNING, &hdev->flags))		goto unlock;	if (urb->status || !count)		goto resubmit;	if (_urb->type == HCI_SCODATA_PKT) {#ifdef CONFIG_BT_HCIUSB_SCO		int i;		for (i=0; i < urb->number_of_packets; i++) {			BT_DBG("desc %d status %d offset %d len %d", i,					urb->iso_frame_desc[i].status,					urb->iso_frame_desc[i].offset,					urb->iso_frame_desc[i].actual_length);				if (!urb->iso_frame_desc[i].status)				__recv_frame(husb, _urb->type, 					urb->transfer_buffer + urb->iso_frame_desc[i].offset,					urb->iso_frame_desc[i].actual_length);		}#else		;#endif	} else {		err = __recv_frame(husb, _urb->type, urb->transfer_buffer, count);		if (err < 0) { 			BT_ERR("%s corrupted packet: type %d count %d",					husb->hdev->name, _urb->type, count);			hdev->stat.err_rx++;		}	}resubmit:	urb->dev = husb->udev;	err = usb_submit_urb(urb, GFP_ATOMIC);	BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,			_urb->type, err);unlock:	read_unlock(&husb->completion_lock);}static void hci_usb_tx_complete(struct urb *urb){	struct _urb *_urb = container_of(urb, struct _urb, urb);	struct hci_usb *husb = (void *) urb->context;	struct hci_dev *hdev = husb->hdev;	BT_DBG("%s urb %p status %d flags %x", hdev->name, urb,			urb->status, urb->transfer_flags);	atomic_dec(__pending_tx(husb, _urb->type));	urb->transfer_buffer = NULL;	kfree_skb((struct sk_buff *) _urb->priv);	if (!test_bit(HCI_RUNNING, &hdev->flags))		return;	if (!urb->status)		hdev->stat.byte_tx += urb->transfer_buffer_length;	else		hdev->stat.err_tx++;	read_lock(&husb->completion_lock);	_urb_unlink(_urb);	_urb_queue_tail(__completed_q(husb, _urb->type), _urb);	hci_usb_tx_wakeup(husb);	read_unlock(&husb->completion_lock);}static void hci_usb_destruct(struct hci_dev *hdev){	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;	BT_DBG("%s", hdev->name);	kfree(husb);}static void hci_usb_notify(struct hci_dev *hdev, unsigned int evt){	BT_DBG("%s evt %d", hdev->name, evt);}static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id){	struct usb_device *udev = interface_to_usbdev(intf);	struct usb_host_endpoint *bulk_out_ep = NULL;	struct usb_host_endpoint *bulk_in_ep = NULL;	struct usb_host_endpoint *intr_in_ep = NULL;	struct usb_host_endpoint  *ep;	struct usb_host_interface *uif;	struct usb_interface *isoc_iface;	struct hci_usb *husb;	struct hci_dev *hdev;	int i, e, size, isoc_ifnum, isoc_alts;	BT_DBG("udev %p intf %p", udev, intf);	if (!id->driver_info) {		const struct usb_device_id *match;		match = usb_match_id(intf, blacklist_ids);		if (match)			id = match;	}	if (ignore || id->driver_info & HCI_IGNORE)		return -ENODEV;	if (ignore_dga && id->driver_info & HCI_DIGIANSWER)		return -ENODEV;	if (ignore_csr && id->driver_info & HCI_CSR)		return -ENODEV;	if (ignore_sniffer && id->driver_info & HCI_SNIFFER)		return -ENODEV;	if (intf->cur_altsetting->desc.bInterfaceNumber > 0)		return -ENODEV;	/* Find endpoints that we need */	uif = intf->cur_altsetting;	for (e = 0; e < uif->desc.bNumEndpoints; e++) {		ep = &uif->endpoint[e];		switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {		case USB_ENDPOINT_XFER_INT:			if (ep->desc.bEndpointAddress & USB_DIR_IN)				intr_in_ep = ep;			break;		case USB_ENDPOINT_XFER_BULK:			if (ep->desc.bEndpointAddress & USB_DIR_IN)				bulk_in_ep  = ep;			else				bulk_out_ep = ep;			break;		}	}	if (!bulk_in_ep || !bulk_out_ep || !intr_in_ep) {		BT_DBG("Bulk endpoints not found");		goto done;	}	if (!(husb = kzalloc(sizeof(struct hci_usb), GFP_KERNEL))) {		BT_ERR("Can't allocate: control structure");		goto done;	}	husb->udev = udev;	husb->bulk_out_ep = bulk_out_ep;	husb->bulk_in_ep  = bulk_in_ep;	husb->intr_in_ep  = intr_in_ep;	if (id->driver_info & HCI_DIGIANSWER)		husb->ctrl_req = USB_TYPE_VENDOR;	else		husb->ctrl_req = USB_TYPE_CLASS;	/* Find isochronous endpoints that we can use */	size = 0; 	isoc_iface = NULL;	isoc_alts  = 0;	isoc_ifnum = 1;#ifdef CONFIG_BT_HCIUSB_SCO	if (isoc && !(id->driver_info & (HCI_BROKEN_ISOC | HCI_SNIFFER)))		isoc_iface = usb_ifnum_to_if(udev, isoc_ifnum);	if (isoc_iface) {		int a;		struct usb_host_endpoint *isoc_out_ep = NULL;		struct usb_host_endpoint *isoc_in_ep = NULL;		for (a = 0; a < isoc_iface->num_altsetting; a++) {			uif = &isoc_iface->altsetting[a];			for (e = 0; e < uif->desc.bNumEndpoints; e++) {				ep = &uif->endpoint[e];				switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {				case USB_ENDPOINT_XFER_ISOC:					if (le16_to_cpu(ep->desc.wMaxPacketSize) < size ||							uif->desc.bAlternateSetting != isoc)						break;					size = le16_to_cpu(ep->desc.wMaxPacketSize);					isoc_alts = uif->desc.bAlternateSetting;					if (ep->desc.bEndpointAddress & USB_DIR_IN)						isoc_in_ep  = ep;					else						isoc_out_ep = ep;					break;				}			}		}		if (!isoc_in_ep || !isoc_out_ep)			BT_DBG("Isoc endpoints not found");		else {			BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);			if (usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb) != 0)				BT_ERR("Can't claim isoc interface");			else if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {				BT_ERR("Can't set isoc interface settings");				husb->isoc_iface = isoc_iface;				usb_driver_release_interface(&hci_usb_driver, isoc_iface);				husb->isoc_iface = NULL;			} else {				husb->isoc_iface  = isoc_iface;				husb->isoc_in_ep  = isoc_in_ep;				husb->isoc_out_ep = isoc_out_ep;			}		}	}#endif	rwlock_init(&husb->completion_lock);	for (i = 0; i < 4; i++) {		skb_queue_head_init(&husb->transmit_q[i]);		_urb_queue_init(&husb->pending_q[i]);		_urb_queue_init(&husb->completed_q[i]);	}	/* Initialize and register HCI device */	hdev = hci_alloc_dev();	if (!hdev) {		BT_ERR("Can't allocate HCI device");		goto probe_error;	}	husb->hdev = hdev;	hdev->type = HCI_USB;	hdev->driver_data = husb;	SET_HCIDEV_DEV(hdev, &intf->dev);	hdev->open     = hci_usb_open;	hdev->close    = hci_usb_close;	hdev->flush    = hci_usb_flush;	hdev->send     = hci_usb_send_frame;	hdev->destruct = hci_usb_destruct;	hdev->notify   = hci_usb_notify;	hdev->owner = THIS_MODULE;	if (reset || id->driver_info & HCI_RESET)		set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);	if (force_scofix || id->driver_info & HCI_WRONG_SCO_MTU) {		if (!disable_scofix)			set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);	}	if (id->driver_info & HCI_SNIFFER) {		if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)			set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);	}	if (id->driver_info & HCI_BCM92035) {		unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };		struct sk_buff *skb;		skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);		if (skb) {			memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));			skb_queue_tail(&hdev->driver_init, skb);		}	}	if (hci_register_dev(hdev) < 0) {		BT_ERR("Can't register HCI device");		hci_free_dev(hdev);		goto probe_error;	}	usb_set_intfdata(intf, husb);	return 0;probe_error:	if (husb->isoc_iface)		usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);	kfree(husb);done:	return -EIO;}static void hci_usb_disconnect(struct usb_interface *intf){	struct hci_usb *husb = usb_get_intfdata(intf);	struct hci_dev *hdev;	if (!husb || intf == husb->isoc_iface)		return;	usb_set_intfdata(intf, NULL);	hdev = husb->hdev;	BT_DBG("%s", hdev->name);	hci_usb_close(hdev);	if (husb->isoc_iface)		usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);	if (hci_unregister_dev(hdev) < 0)		BT_ERR("Can't unregister HCI device %s", hdev->name);	hci_free_dev(hdev);}static int hci_usb_suspend(struct usb_interface *intf, pm_message_t message){	struct hci_usb *husb = usb_get_intfdata(intf);	struct list_head killed;	unsigned long flags;	int i;	if (!husb || intf == husb->isoc_iface)		return 0;	hci_suspend_dev(husb->hdev);	INIT_LIST_HEAD(&killed);	for (i = 0; i < 4; i++) {		struct _urb_queue *q = &husb->pending_q[i];		struct _urb *_urb, *_tmp;		while ((_urb = _urb_dequeue(q))) {			/* reset queue since _urb_dequeue sets it to NULL */			_urb->queue = q;			usb_kill_urb(&_urb->urb);			list_add(&_urb->list, &killed);		}		spin_lock_irqsave(&q->lock, flags);		list_for_each_entry_safe(_urb, _tmp, &killed, list) {			list_move_tail(&_urb->list, &q->head);		}		spin_unlock_irqrestore(&q->lock, flags);	}	return 0;}static int hci_usb_resume(struct usb_interface *intf){	struct hci_usb *husb = usb_get_intfdata(intf);	unsigned long flags;	int i, err = 0;	if (!husb || intf == husb->isoc_iface)		return 0;		for (i = 0; i < 4; i++) {		struct _urb_queue *q = &husb->pending_q[i];		struct _urb *_urb;		spin_lock_irqsave(&q->lock, flags);		list_for_each_entry(_urb, &q->head, list) {			err = usb_submit_urb(&_urb->urb, GFP_ATOMIC);			if (err)				break;		}		spin_unlock_irqrestore(&q->lock, flags);		if (err)			return -EIO;	}	hci_resume_dev(husb->hdev);	return 0;}static struct usb_driver hci_usb_driver = {	.name		= "hci_usb",	.probe		= hci_usb_probe,	.disconnect	= hci_usb_disconnect,	.suspend	= hci_usb_suspend,	.resume		= hci_usb_resume,	.id_table	= bluetooth_ids,};static int __init hci_usb_init(void){	int err;	BT_INFO("HCI USB driver ver %s", VERSION);	if ((err = usb_register(&hci_usb_driver)) < 0)		BT_ERR("Failed to register HCI USB driver");	return err;}static void __exit hci_usb_exit(void){	usb_deregister(&hci_usb_driver);}module_init(hci_usb_init);module_exit(hci_usb_exit);module_param(ignore, bool, 0644);MODULE_PARM_DESC(ignore, "Ignore devices from the matching table");module_param(ignore_dga, bool, 0644);MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");module_param(ignore_csr, bool, 0644);MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");module_param(ignore_sniffer, bool, 0644);MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");module_param(disable_scofix, bool, 0644);MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");module_param(force_scofix, bool, 0644);MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");module_param(reset, bool, 0644);MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");#ifdef CONFIG_BT_HCIUSB_SCOmodule_param(isoc, int, 0644);MODULE_PARM_DESC(isoc, "Set isochronous transfers for SCO over HCI support");#endifMODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");MODULE_DESCRIPTION("Bluetooth HCI USB driver ver " VERSION);MODULE_VERSION(VERSION);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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