8067 lines · 210.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2021 Intel Corporation * Copyright 2023 NXP */ #include <linux/property.h> #include <net/bluetooth/bluetooth.h>#include <net/bluetooth/hci_core.h>#include <net/bluetooth/mgmt.h> #include "hci_codec.h"#include "hci_debugfs.h"#include "smp.h"#include "eir.h"#include "msft.h"#include "aosp.h"#include "leds.h" static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,				  struct sk_buff *skb){	bt_dev_dbg(hdev, "result 0x%2.2x", result); 	if (READ_ONCE(hdev->req_status) != HCI_REQ_PEND)		return; 	hdev->req_result = result;	WRITE_ONCE(hdev->req_status, HCI_REQ_DONE); 	/* Free the request command so it is not used as response */	kfree_skb(hdev->req_skb);	hdev->req_skb = NULL; 	if (skb) {		struct sock *sk = hci_skb_sk(skb); 		/* Drop sk reference if set */		if (sk)			sock_put(sk); 		hdev->req_rsp = skb_get(skb);	} 	wake_up_interruptible(&hdev->req_wait_q);} struct sk_buff *hci_cmd_sync_alloc(struct hci_dev *hdev, u16 opcode, u32 plen,				   const void *param, struct sock *sk){	int len = HCI_COMMAND_HDR_SIZE + plen;	struct hci_command_hdr *hdr;	struct sk_buff *skb; 	skb = bt_skb_alloc(len, GFP_ATOMIC);	if (!skb)		return NULL; 	hdr = skb_put(skb, HCI_COMMAND_HDR_SIZE);	hdr->opcode = cpu_to_le16(opcode);	hdr->plen   = plen; 	if (plen)		skb_put_data(skb, param, plen); 	bt_dev_dbg(hdev, "skb len %d", skb->len); 	hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;	hci_skb_opcode(skb) = opcode; 	/* Grab a reference if command needs to be associated with a sock (e.g.	 * likely mgmt socket that initiated the command).	 */	if (sk) {		hci_skb_sk(skb) = sk;		sock_hold(sk);	} 	return skb;} static void hci_cmd_sync_add(struct hci_request *req, u16 opcode, u32 plen,			     const void *param, u8 event, struct sock *sk){	struct hci_dev *hdev = req->hdev;	struct sk_buff *skb; 	bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen); 	/* If an error occurred during request building, there is no point in	 * queueing the HCI command. We can simply return.	 */	if (req->err)		return; 	skb = hci_cmd_sync_alloc(hdev, opcode, plen, param, sk);	if (!skb) {		bt_dev_err(hdev, "no memory for command (opcode 0x%4.4x)",			   opcode);		req->err = -ENOMEM;		return;	} 	if (skb_queue_empty(&req->cmd_q))		bt_cb(skb)->hci.req_flags |= HCI_REQ_START; 	hci_skb_event(skb) = event; 	skb_queue_tail(&req->cmd_q, skb);} static int hci_req_sync_run(struct hci_request *req){	struct hci_dev *hdev = req->hdev;	struct sk_buff *skb;	unsigned long flags; 	bt_dev_dbg(hdev, "length %u", skb_queue_len(&req->cmd_q)); 	/* If an error occurred during request building, remove all HCI	 * commands queued on the HCI request queue.	 */	if (req->err) {		skb_queue_purge(&req->cmd_q);		return req->err;	} 	/* Do not allow empty requests */	if (skb_queue_empty(&req->cmd_q))		return -ENODATA; 	skb = skb_peek_tail(&req->cmd_q);	bt_cb(skb)->hci.req_complete_skb = hci_cmd_sync_complete;	bt_cb(skb)->hci.req_flags |= HCI_REQ_SKB; 	spin_lock_irqsave(&hdev->cmd_q.lock, flags);	skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);	spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); 	queue_work(hdev->workqueue, &hdev->cmd_work); 	return 0;} static void hci_request_init(struct hci_request *req, struct hci_dev *hdev){	skb_queue_head_init(&req->cmd_q);	req->hdev = hdev;	req->err = 0;} /* This function requires the caller holds hdev->req_lock. */struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,				  const void *param, u8 event, u32 timeout,				  struct sock *sk){	struct hci_request req;	struct sk_buff *skb;	int err = 0; 	bt_dev_dbg(hdev, "Opcode 0x%4.4x", opcode); 	hci_request_init(&req, hdev); 	hci_cmd_sync_add(&req, opcode, plen, param, event, sk); 	WRITE_ONCE(hdev->req_status, HCI_REQ_PEND); 	err = hci_req_sync_run(&req);	if (err < 0)		return ERR_PTR(err); 	err = wait_event_interruptible_timeout(hdev->req_wait_q,					       READ_ONCE(hdev->req_status) != HCI_REQ_PEND,					       timeout); 	if (err == -ERESTARTSYS)		return ERR_PTR(-EINTR); 	switch (READ_ONCE(hdev->req_status)) {	case HCI_REQ_DONE:		err = -bt_to_errno(hdev->req_result);		break; 	case HCI_REQ_CANCELED:		err = -hdev->req_result;		break; 	default:		err = -ETIMEDOUT;		break;	} 	WRITE_ONCE(hdev->req_status, 0);	hdev->req_result = 0;	skb = hdev->req_rsp;	hdev->req_rsp = NULL; 	bt_dev_dbg(hdev, "end: err %d", err); 	if (err < 0) {		kfree_skb(skb);		return ERR_PTR(err);	} 	/* If command return a status event skb will be set to NULL as there are	 * no parameters.	 */	if (!skb)		return ERR_PTR(-ENODATA); 	return skb;}EXPORT_SYMBOL(__hci_cmd_sync_sk); /* This function requires the caller holds hdev->req_lock. */struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,			       const void *param, u32 timeout){	return __hci_cmd_sync_sk(hdev, opcode, plen, param, 0, timeout, NULL);}EXPORT_SYMBOL(__hci_cmd_sync); /* Send HCI command and wait for command complete event */struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,			     const void *param, u32 timeout){	struct sk_buff *skb; 	if (!test_bit(HCI_UP, &hdev->flags))		return ERR_PTR(-ENETDOWN); 	bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen); 	hci_req_sync_lock(hdev);	skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout);	hci_req_sync_unlock(hdev); 	return skb;}EXPORT_SYMBOL(hci_cmd_sync); /* This function requires the caller holds hdev->req_lock. */struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,				  const void *param, u8 event, u32 timeout){	return __hci_cmd_sync_sk(hdev, opcode, plen, param, event, timeout,				 NULL);}EXPORT_SYMBOL(__hci_cmd_sync_ev); /* This function requires the caller holds hdev->req_lock. */int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen,			     const void *param, u8 event, u32 timeout,			     struct sock *sk){	struct sk_buff *skb;	u8 status; 	skb = __hci_cmd_sync_sk(hdev, opcode, plen, param, event, timeout, sk); 	/* If command return a status event, skb will be set to -ENODATA */	if (skb == ERR_PTR(-ENODATA))		return 0; 	if (IS_ERR(skb)) {		if (!event)			bt_dev_err(hdev, "Opcode 0x%4.4x failed: %ld", opcode,				   PTR_ERR(skb));		return PTR_ERR(skb);	} 	status = skb->data[0]; 	kfree_skb(skb); 	return status;}EXPORT_SYMBOL(__hci_cmd_sync_status_sk); int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen,			  const void *param, u32 timeout){	return __hci_cmd_sync_status_sk(hdev, opcode, plen, param, 0, timeout,					NULL);}EXPORT_SYMBOL(__hci_cmd_sync_status); int hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen,			const void *param, u32 timeout){	int err; 	hci_req_sync_lock(hdev);	err = __hci_cmd_sync_status(hdev, opcode, plen, param, timeout);	hci_req_sync_unlock(hdev); 	return err;}EXPORT_SYMBOL(hci_cmd_sync_status); static void hci_cmd_sync_work(struct work_struct *work){	struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_sync_work); 	bt_dev_dbg(hdev, ""); 	/* Dequeue all entries and run them */	while (1) {		struct hci_cmd_sync_work_entry *entry; 		mutex_lock(&hdev->cmd_sync_work_lock);		entry = list_first_entry_or_null(&hdev->cmd_sync_work_list,						 struct hci_cmd_sync_work_entry,						 list);		if (entry)			list_del(&entry->list);		mutex_unlock(&hdev->cmd_sync_work_lock); 		if (!entry)			break; 		bt_dev_dbg(hdev, "entry %p", entry); 		if (entry->func) {			int err; 			hci_req_sync_lock(hdev);			err = entry->func(hdev, entry->data);			if (entry->destroy)				entry->destroy(hdev, entry->data, err);			hci_req_sync_unlock(hdev);		} 		kfree(entry);	}} static void hci_cmd_sync_cancel_work(struct work_struct *work){	struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_sync_cancel_work); 	cancel_delayed_work_sync(&hdev->cmd_timer);	cancel_delayed_work_sync(&hdev->ncmd_timer);	atomic_set(&hdev->cmd_cnt, 1); 	wake_up_interruptible(&hdev->req_wait_q);} static int hci_scan_disable_sync(struct hci_dev *hdev);static int scan_disable_sync(struct hci_dev *hdev, void *data){	return hci_scan_disable_sync(hdev);} static int interleaved_inquiry_sync(struct hci_dev *hdev, void *data){	return hci_inquiry_sync(hdev, DISCOV_INTERLEAVED_INQUIRY_LEN, 0);} static void le_scan_disable(struct work_struct *work){	struct hci_dev *hdev = container_of(work, struct hci_dev,					    le_scan_disable.work);	int status; 	bt_dev_dbg(hdev, "");	hci_dev_lock(hdev); 	if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))		goto _return; 	status = hci_cmd_sync_queue(hdev, scan_disable_sync, NULL, NULL);	if (status) {		bt_dev_err(hdev, "failed to disable LE scan: %d", status);		goto _return;	} 	/* If we were running LE only scan, change discovery state. If	 * we were running both LE and BR/EDR inquiry simultaneously,	 * and BR/EDR inquiry is already finished, stop discovery,	 * otherwise BR/EDR inquiry will stop discovery when finished.	 * If we will resolve remote device name, do not change	 * discovery state.	 */ 	if (hdev->discovery.type == DISCOV_TYPE_LE)		goto discov_stopped; 	if (hdev->discovery.type != DISCOV_TYPE_INTERLEAVED)		goto _return; 	if (hci_test_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY)) {		if (!test_bit(HCI_INQUIRY, &hdev->flags) &&		    hdev->discovery.state != DISCOVERY_RESOLVING)			goto discov_stopped; 		goto _return;	} 	status = hci_cmd_sync_queue(hdev, interleaved_inquiry_sync, NULL, NULL);	if (status) {		bt_dev_err(hdev, "inquiry failed: status %d", status);		goto discov_stopped;	} 	goto _return; discov_stopped:	hci_discovery_set_state(hdev, DISCOVERY_STOPPED); _return:	hci_dev_unlock(hdev);} static int hci_le_set_scan_enable_sync(struct hci_dev *hdev, u8 val,				       u8 filter_dup); static int reenable_adv_sync(struct hci_dev *hdev, void *data){	bt_dev_dbg(hdev, ""); 	if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&	    list_empty(&hdev->adv_instances))		return 0; 	if (hdev->cur_adv_instance) {		return hci_schedule_adv_instance_sync(hdev,						      hdev->cur_adv_instance,						      true);	} else {		if (ext_adv_capable(hdev)) {			hci_start_ext_adv_sync(hdev, 0x00);		} else {			hci_update_adv_data_sync(hdev, 0x00);			hci_update_scan_rsp_data_sync(hdev, 0x00);			hci_enable_advertising_sync(hdev);		}	} 	return 0;} static void reenable_adv(struct work_struct *work){	struct hci_dev *hdev = container_of(work, struct hci_dev,					    reenable_adv_work);	int status; 	bt_dev_dbg(hdev, ""); 	hci_dev_lock(hdev); 	status = hci_cmd_sync_queue(hdev, reenable_adv_sync, NULL, NULL);	if (status)		bt_dev_err(hdev, "failed to reenable ADV: %d", status); 	hci_dev_unlock(hdev);} static void cancel_adv_timeout(struct hci_dev *hdev){	if (hdev->adv_instance_timeout) {		hdev->adv_instance_timeout = 0;		cancel_delayed_work(&hdev->adv_instance_expire);	}} /* For a single instance: * - force == true: The instance will be removed even when its remaining *   lifetime is not zero. * - force == false: the instance will be deactivated but kept stored unless *   the remaining lifetime is zero. * * For instance == 0x00: * - force == true: All instances will be removed regardless of their timeout *   setting. * - force == false: Only instances that have a timeout will be removed. */int hci_clear_adv_instance_sync(struct hci_dev *hdev, struct sock *sk,				u8 instance, bool force){	struct adv_info *adv_instance, *n, *next_instance = NULL;	int err;	u8 rem_inst; 	/* Cancel any timeout concerning the removed instance(s). */	if (!instance || hdev->cur_adv_instance == instance)		cancel_adv_timeout(hdev); 	/* Get the next instance to advertise BEFORE we remove	 * the current one. This can be the same instance again	 * if there is only one instance.	 */	if (instance && hdev->cur_adv_instance == instance)		next_instance = hci_get_next_instance(hdev, instance); 

Lines 1–500 of 8067.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t