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

📄 hci_core.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/*   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.*//* Bluetooth HCI core. */#include <linux/module.h>#include <linux/kmod.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/fcntl.h>#include <linux/init.h>#include <linux/skbuff.h>#include <linux/interrupt.h>#include <linux/notifier.h>#include <net/sock.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/unaligned.h>#include <net/bluetooth/bluetooth.h>#include <net/bluetooth/hci_core.h>#ifndef CONFIG_BT_HCI_CORE_DEBUG#undef  BT_DBG#define BT_DBG(D...)#endifstatic void hci_cmd_task(unsigned long arg);static void hci_rx_task(unsigned long arg);static void hci_tx_task(unsigned long arg);static void hci_notify(struct hci_dev *hdev, int event);static DEFINE_RWLOCK(hci_task_lock);/* HCI device list */LIST_HEAD(hci_dev_list);DEFINE_RWLOCK(hci_dev_list_lock);/* HCI callback list */LIST_HEAD(hci_cb_list);DEFINE_RWLOCK(hci_cb_list_lock);/* HCI protocols */#define HCI_MAX_PROTO	2struct hci_proto *hci_proto[HCI_MAX_PROTO];/* HCI notifiers list */static ATOMIC_NOTIFIER_HEAD(hci_notifier);/* ---- HCI notifications ---- */int hci_register_notifier(struct notifier_block *nb){	return atomic_notifier_chain_register(&hci_notifier, nb);}int hci_unregister_notifier(struct notifier_block *nb){	return atomic_notifier_chain_unregister(&hci_notifier, nb);}static void hci_notify(struct hci_dev *hdev, int event){	atomic_notifier_call_chain(&hci_notifier, event, hdev);}/* ---- HCI requests ---- */void hci_req_complete(struct hci_dev *hdev, int result){	BT_DBG("%s result 0x%2.2x", hdev->name, result);	if (hdev->req_status == HCI_REQ_PEND) {		hdev->req_result = result;		hdev->req_status = HCI_REQ_DONE;		wake_up_interruptible(&hdev->req_wait_q);	}}static void hci_req_cancel(struct hci_dev *hdev, int err){	BT_DBG("%s err 0x%2.2x", hdev->name, err);	if (hdev->req_status == HCI_REQ_PEND) {		hdev->req_result = err;		hdev->req_status = HCI_REQ_CANCELED;		wake_up_interruptible(&hdev->req_wait_q);	}}/* Execute request and wait for completion. */static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),				unsigned long opt, __u32 timeout){	DECLARE_WAITQUEUE(wait, current);	int err = 0;	BT_DBG("%s start", hdev->name);	hdev->req_status = HCI_REQ_PEND;	add_wait_queue(&hdev->req_wait_q, &wait);	set_current_state(TASK_INTERRUPTIBLE);	req(hdev, opt);	schedule_timeout(timeout);	remove_wait_queue(&hdev->req_wait_q, &wait);	if (signal_pending(current))		return -EINTR;	switch (hdev->req_status) {	case HCI_REQ_DONE:		err = -bt_err(hdev->req_result);		break;	case HCI_REQ_CANCELED:		err = -hdev->req_result;		break;	default:		err = -ETIMEDOUT;		break;	}	hdev->req_status = hdev->req_result = 0;	BT_DBG("%s end: err %d", hdev->name, err);	return err;}static inline int hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),				unsigned long opt, __u32 timeout){	int ret;	/* Serialize all requests */	hci_req_lock(hdev);	ret = __hci_request(hdev, req, opt, timeout);	hci_req_unlock(hdev);	return ret;}static void hci_reset_req(struct hci_dev *hdev, unsigned long opt){	BT_DBG("%s %ld", hdev->name, opt);	/* Reset device */	hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);}static void hci_init_req(struct hci_dev *hdev, unsigned long opt){	struct sk_buff *skb;	__le16 param;	__u8 flt_type;	BT_DBG("%s %ld", hdev->name, opt);	/* Driver initialization */	/* Special commands */	while ((skb = skb_dequeue(&hdev->driver_init))) {		bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;		skb->dev = (void *) hdev;		skb_queue_tail(&hdev->cmd_q, skb);		hci_sched_cmd(hdev);	}	skb_queue_purge(&hdev->driver_init);	/* Mandatory initialization */	/* Reset */	if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks))			hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);	/* Read Local Supported Features */	hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);	/* Read Local Version */	hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL);	/* Read Buffer Size (ACL mtu, max pkt, etc.) */	hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);#if 0	/* Host buffer size */	{		struct hci_cp_host_buffer_size cp;		cp.acl_mtu = cpu_to_le16(HCI_MAX_ACL_SIZE);		cp.sco_mtu = HCI_MAX_SCO_SIZE;		cp.acl_max_pkt = cpu_to_le16(0xffff);		cp.sco_max_pkt = cpu_to_le16(0xffff);		hci_send_cmd(hdev, HCI_OP_HOST_BUFFER_SIZE, sizeof(cp), &cp);	}#endif	/* Read BD Address */	hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);	/* Read Class of Device */	hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);	/* Read Local Name */	hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);	/* Read Voice Setting */	hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);	/* Optional initialization */	/* Clear Event Filters */	flt_type = HCI_FLT_CLEAR_ALL;	hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type);	/* Page timeout ~20 secs */	param = cpu_to_le16(0x8000);	hci_send_cmd(hdev, HCI_OP_WRITE_PG_TIMEOUT, 2, &param);	/* Connection accept timeout ~20 secs */	param = cpu_to_le16(0x7d00);	hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);}static void hci_scan_req(struct hci_dev *hdev, unsigned long opt){	__u8 scan = opt;	BT_DBG("%s %x", hdev->name, scan);	/* Inquiry and Page scans */	hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);}static void hci_auth_req(struct hci_dev *hdev, unsigned long opt){	__u8 auth = opt;	BT_DBG("%s %x", hdev->name, auth);	/* Authentication */	hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth);}static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt){	__u8 encrypt = opt;	BT_DBG("%s %x", hdev->name, encrypt);	/* Authentication */	hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt);}/* Get HCI device by index. * Device is held on return. */struct hci_dev *hci_dev_get(int index){	struct hci_dev *hdev = NULL;	struct list_head *p;	BT_DBG("%d", index);	if (index < 0)		return NULL;	read_lock(&hci_dev_list_lock);	list_for_each(p, &hci_dev_list) {		struct hci_dev *d = list_entry(p, struct hci_dev, list);		if (d->id == index) {			hdev = hci_dev_hold(d);			break;		}	}	read_unlock(&hci_dev_list_lock);	return hdev;}/* ---- Inquiry support ---- */static void inquiry_cache_flush(struct hci_dev *hdev){	struct inquiry_cache *cache = &hdev->inq_cache;	struct inquiry_entry *next  = cache->list, *e;	BT_DBG("cache %p", cache);	cache->list = NULL;	while ((e = next)) {		next = e->next;		kfree(e);	}}struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr){	struct inquiry_cache *cache = &hdev->inq_cache;	struct inquiry_entry *e;	BT_DBG("cache %p, %s", cache, batostr(bdaddr));	for (e = cache->list; e; e = e->next)		if (!bacmp(&e->data.bdaddr, bdaddr))			break;	return e;}void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data){	struct inquiry_cache *cache = &hdev->inq_cache;	struct inquiry_entry *e;	BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr));	if (!(e = hci_inquiry_cache_lookup(hdev, &data->bdaddr))) {		/* Entry not in the cache. Add new one. */		if (!(e = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))			return;		e->next     = cache->list;		cache->list = e;	}	memcpy(&e->data, data, sizeof(*data));	e->timestamp = jiffies;	cache->timestamp = jiffies;}static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf){	struct inquiry_cache *cache = &hdev->inq_cache;	struct inquiry_info *info = (struct inquiry_info *) buf;	struct inquiry_entry *e;	int copied = 0;	for (e = cache->list; e && copied < num; e = e->next, copied++) {		struct inquiry_data *data = &e->data;		bacpy(&info->bdaddr, &data->bdaddr);		info->pscan_rep_mode	= data->pscan_rep_mode;		info->pscan_period_mode	= data->pscan_period_mode;		info->pscan_mode	= data->pscan_mode;		memcpy(info->dev_class, data->dev_class, 3);		info->clock_offset	= data->clock_offset;		info++;	}	BT_DBG("cache %p, copied %d", cache, copied);	return copied;}static void hci_inq_req(struct hci_dev *hdev, unsigned long opt){	struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;	struct hci_cp_inquiry cp;	BT_DBG("%s", hdev->name);	if (test_bit(HCI_INQUIRY, &hdev->flags))		return;	/* Start Inquiry */	memcpy(&cp.lap, &ir->lap, 3);	cp.length  = ir->length;	cp.num_rsp = ir->num_rsp;	hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);}int hci_inquiry(void __user *arg){	__u8 __user *ptr = arg;	struct hci_inquiry_req ir;	struct hci_dev *hdev;	int err = 0, do_inquiry = 0, max_rsp;	long timeo;	__u8 *buf;	if (copy_from_user(&ir, ptr, sizeof(ir)))		return -EFAULT;	if (!(hdev = hci_dev_get(ir.dev_id)))		return -ENODEV;	hci_dev_lock_bh(hdev);	if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||					inquiry_cache_empty(hdev) ||					ir.flags & IREQ_CACHE_FLUSH) {		inquiry_cache_flush(hdev);		do_inquiry = 1;	}	hci_dev_unlock_bh(hdev);	timeo = ir.length * msecs_to_jiffies(2000);	if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)		goto done;	/* for unlimited number of responses we will use buffer with 255 entries */	max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp;	/* cache_dump can't sleep. Therefore we allocate temp buffer and then	 * copy it to the user space.	 */	if (!(buf = kmalloc(sizeof(struct inquiry_info) * max_rsp, GFP_KERNEL))) {		err = -ENOMEM;		goto done;	}	hci_dev_lock_bh(hdev);	ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);	hci_dev_unlock_bh(hdev);	BT_DBG("num_rsp %d", ir.num_rsp);	if (!copy_to_user(ptr, &ir, sizeof(ir))) {		ptr += sizeof(ir);		if (copy_to_user(ptr, buf, sizeof(struct inquiry_info) *					ir.num_rsp))			err = -EFAULT;	} else		err = -EFAULT;	kfree(buf);done:	hci_dev_put(hdev);	return err;}/* ---- HCI ioctl helpers ---- */int hci_dev_open(__u16 dev){	struct hci_dev *hdev;	int ret = 0;	if (!(hdev = hci_dev_get(dev)))		return -ENODEV;	BT_DBG("%s %p", hdev->name, hdev);	hci_req_lock(hdev);	if (test_bit(HCI_UP, &hdev->flags)) {		ret = -EALREADY;		goto done;	}	if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))		set_bit(HCI_RAW, &hdev->flags);	if (hdev->open(hdev)) {		ret = -EIO;		goto done;	}	if (!test_bit(HCI_RAW, &hdev->flags)) {		atomic_set(&hdev->cmd_cnt, 1);		set_bit(HCI_INIT, &hdev->flags);		//__hci_request(hdev, hci_reset_req, 0, HZ);		ret = __hci_request(hdev, hci_init_req, 0,					msecs_to_jiffies(HCI_INIT_TIMEOUT));		clear_bit(HCI_INIT, &hdev->flags);	}	if (!ret) {		hci_dev_hold(hdev);		set_bit(HCI_UP, &hdev->flags);		hci_notify(hdev, HCI_DEV_UP);	} else {		/* Init failed, cleanup */		tasklet_kill(&hdev->rx_task);		tasklet_kill(&hdev->tx_task);		tasklet_kill(&hdev->cmd_task);		skb_queue_purge(&hdev->cmd_q);		skb_queue_purge(&hdev->rx_q);		if (hdev->flush)			hdev->flush(hdev);		if (hdev->sent_cmd) {			kfree_skb(hdev->sent_cmd);			hdev->sent_cmd = NULL;		}		hdev->close(hdev);		hdev->flags = 0;	}done:	hci_req_unlock(hdev);	hci_dev_put(hdev);	return ret;

⌨️ 快捷键说明

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