📄 hci_usb.c
字号:
/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by 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.*//* * BlueZ 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> * * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $ */#define VERSION "2.1"#include <linux/config.h>#include <linux/module.h>#define __KERNEL_SYSCALLS__#include <linux/version.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/kmod.h>#include <linux/usb.h>#include <net/bluetooth/bluetooth.h>#include <net/bluetooth/hci_core.h>#include "hci_usb.h"#define HCI_MAX_PENDING (HCI_MAX_BULK_RX + HCI_MAX_BULK_TX + 1)#ifndef HCI_USB_DEBUG#undef BT_DBG#define BT_DBG( A... )#undef BT_DMP#define BT_DMP( A... )#endif#ifndef CONFIG_BLUEZ_USB_ZERO_PACKET#undef USB_ZERO_PACKET#define USB_ZERO_PACKET 0#endifstatic 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) }, /* Ericsson with non-standard id */ { USB_DEVICE(0x0bdb, 0x1002) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, bluetooth_ids);static struct usb_device_id ignore_ids[] = { /* Broadcom BCM2033 without firmware */ { USB_DEVICE(0x0a5c, 0x2033) }, { } /* Terminating entry */};static void hci_usb_interrupt(struct urb *urb);static void hci_usb_rx_complete(struct urb *urb);static void hci_usb_tx_complete(struct urb *urb);static struct urb *hci_usb_get_completed(struct hci_usb *husb){ struct sk_buff *skb; struct urb *urb = NULL; skb = skb_dequeue(&husb->completed_q); if (skb) { urb = ((struct hci_usb_scb *) skb->cb)->urb; kfree_skb(skb); } BT_DBG("%s urb %p", husb->hdev.name, urb); return urb;}static int hci_usb_enable_intr(struct hci_usb *husb){ struct urb *urb; int pipe, size; void *buf; BT_DBG("%s", husb->hdev.name); if (!(urb = usb_alloc_urb(0))) return -ENOMEM; if (!(buf = kmalloc(HCI_MAX_EVENT_SIZE, GFP_KERNEL))) { usb_free_urb(urb); return -ENOMEM; } husb->intr_urb = urb; pipe = usb_rcvintpipe(husb->udev, husb->intr_ep); size = usb_maxpacket(husb->udev, pipe, usb_pipeout(pipe)); FILL_INT_URB(urb, husb->udev, pipe, buf, size, hci_usb_interrupt, husb, husb->intr_interval); return usb_submit_urb(urb);}static int hci_usb_disable_intr(struct hci_usb *husb){ struct urb *urb = husb->intr_urb; struct sk_buff *skb; BT_DBG("%s", husb->hdev.name); usb_unlink_urb(urb); usb_free_urb(urb); husb->intr_urb = NULL; skb = husb->intr_skb; if (skb) { husb->intr_skb = NULL; kfree_skb(skb); } return 0;}static int hci_usb_rx_submit(struct hci_usb *husb, struct urb *urb){ struct hci_usb_scb *scb; struct sk_buff *skb; int pipe, size, err; if (!urb && !(urb = usb_alloc_urb(0))) return -ENOMEM; size = HCI_MAX_FRAME_SIZE; if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) { usb_free_urb(urb); return -ENOMEM; } BT_DBG("%s urb %p", husb->hdev.name, urb); skb->dev = (void *) &husb->hdev; skb->pkt_type = HCI_ACLDATA_PKT; scb = (struct hci_usb_scb *) skb->cb; scb->urb = urb; pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep); FILL_BULK_URB(urb, husb->udev, pipe, skb->data, size, hci_usb_rx_complete, skb); urb->transfer_flags = USB_QUEUE_BULK; skb_queue_tail(&husb->pending_q, skb); err = usb_submit_urb(urb); if (err) { BT_ERR("%s bulk rx submit failed urb %p err %d", husb->hdev.name, urb, err); skb_unlink(skb); usb_free_urb(urb); } return err;}/* 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; MOD_INC_USE_COUNT; write_lock_irqsave(&husb->completion_lock, flags); err = hci_usb_enable_intr(husb); if (!err) { for (i = 0; i < HCI_MAX_BULK_TX; i++) hci_usb_rx_submit(husb, NULL); } else { clear_bit(HCI_RUNNING, &hdev->flags); MOD_DEC_USE_COUNT; } 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; BT_DBG("%s", hdev->name); skb_queue_purge(&husb->cmd_q); skb_queue_purge(&husb->acl_q); return 0;}static inline void hci_usb_unlink_urbs(struct hci_usb *husb){ struct sk_buff *skb; struct urb *urb; BT_DBG("%s", husb->hdev.name); while ((skb = skb_dequeue(&husb->pending_q))) { urb = ((struct hci_usb_scb *) skb->cb)->urb; usb_unlink_urb(urb); kfree_skb(skb); } while ((urb = hci_usb_get_completed(husb))) usb_free_urb(urb);}/* 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); write_lock_irqsave(&husb->completion_lock, flags); hci_usb_disable_intr(husb); hci_usb_unlink_urbs(husb); hci_usb_flush(hdev); write_unlock_irqrestore(&husb->completion_lock, flags); MOD_DEC_USE_COUNT; return 0;}static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb){ struct hci_usb_scb *scb = (void *) skb->cb; struct urb *urb = hci_usb_get_completed(husb); struct usb_ctrlrequest *dr; int pipe, err; if (!urb && !(urb = usb_alloc_urb(0))) return -ENOMEM; if (!(dr = kmalloc(sizeof(*dr), GFP_ATOMIC))) { usb_free_urb(urb); return -ENOMEM; } pipe = usb_sndctrlpipe(husb->udev, 0); dr->bRequestType = HCI_CTRL_REQ; dr->bRequest = 0; dr->wIndex = 0; dr->wValue = 0; dr->wLength = __cpu_to_le16(skb->len); FILL_CONTROL_URB(urb, husb->udev, pipe, (void *) dr, skb->data, skb->len, hci_usb_tx_complete, skb); BT_DBG("%s urb %p len %d", husb->hdev.name, urb, skb->len); scb->urb = urb; skb_queue_tail(&husb->pending_q, skb); err = usb_submit_urb(urb); if (err) { BT_ERR("%s ctrl tx submit failed urb %p err %d", husb->hdev.name, urb, err); skb_unlink(skb); usb_free_urb(urb); kfree(dr); } return err;}static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb){ struct hci_usb_scb *scb = (void *) skb->cb; struct urb *urb = hci_usb_get_completed(husb); int pipe, err; if (!urb && !(urb = usb_alloc_urb(0))) return -ENOMEM; pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep); FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len, hci_usb_tx_complete, skb); urb->transfer_flags = USB_QUEUE_BULK | USB_ZERO_PACKET; BT_DBG("%s urb %p len %d", husb->hdev.name, urb, skb->len); scb->urb = urb; skb_queue_tail(&husb->pending_q, skb); err = usb_submit_urb(urb); if (err) { BT_ERR("%s bulk tx submit failed urb %p err %d", husb->hdev.name, urb, err); skb_unlink(skb); usb_free_urb(urb); } return err;}static void hci_usb_tx_process(struct hci_usb *husb){ struct sk_buff *skb; BT_DBG("%s", husb->hdev.name); do { clear_bit(HCI_USB_TX_WAKEUP, &husb->state); /* Process ACL queue */ while (skb_queue_len(&husb->pending_q) < HCI_MAX_PENDING && (skb = skb_dequeue(&husb->acl_q))) { if (hci_usb_send_bulk(husb, skb) < 0) { skb_queue_head(&husb->acl_q, skb); break; } } /* Process command queue */ if (!test_bit(HCI_USB_CTRL_TX, &husb->state) && (skb = skb_dequeue(&husb->cmd_q)) != NULL) { set_bit(HCI_USB_CTRL_TX, &husb->state); if (hci_usb_send_ctrl(husb, skb) < 0) { skb_queue_head(&husb->cmd_q, skb); clear_bit(HCI_USB_CTRL_TX, &husb->state); } } } 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 */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))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -