162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) "hci: %s: " fmt, __func__ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <net/nfc/hci.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "hci.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define MAX_FWI 4949 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, 2062306a36Sopenharmony_ci const u8 *param, size_t param_len, 2162306a36Sopenharmony_ci data_exchange_cb_t cb, void *cb_context) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n", pipe, 2462306a36Sopenharmony_ci cmd, param_len); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* TODO: Define hci cmd execution delay. Should it be the same 2762306a36Sopenharmony_ci * for all commands? 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd, 3062306a36Sopenharmony_ci param, param_len, cb, cb_context, MAX_FWI); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * HCI command execution completion callback. 3562306a36Sopenharmony_ci * err will be a standard linux error (may be converted from HCI response) 3662306a36Sopenharmony_ci * skb contains the response data and must be disposed, or may be NULL if 3762306a36Sopenharmony_ci * an error occurred 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci pr_debug("HCI Cmd completed with result=%d\n", err); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci hcp_ew->exec_result = err; 4662306a36Sopenharmony_ci if (hcp_ew->exec_result == 0) 4762306a36Sopenharmony_ci hcp_ew->result_skb = skb; 4862306a36Sopenharmony_ci else 4962306a36Sopenharmony_ci kfree_skb(skb); 5062306a36Sopenharmony_ci hcp_ew->exec_complete = true; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci wake_up(hcp_ew->wq); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, 5662306a36Sopenharmony_ci const u8 *param, size_t param_len, 5762306a36Sopenharmony_ci struct sk_buff **skb) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci DECLARE_WAIT_QUEUE_HEAD_ONSTACK(ew_wq); 6062306a36Sopenharmony_ci struct hcp_exec_waiter hcp_ew; 6162306a36Sopenharmony_ci hcp_ew.wq = &ew_wq; 6262306a36Sopenharmony_ci hcp_ew.exec_complete = false; 6362306a36Sopenharmony_ci hcp_ew.result_skb = NULL; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n", pipe, 6662306a36Sopenharmony_ci cmd, param_len); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* TODO: Define hci cmd execution delay. Should it be the same 6962306a36Sopenharmony_ci * for all commands? 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci hcp_ew.exec_result = nfc_hci_hcp_message_tx(hdev, pipe, 7262306a36Sopenharmony_ci NFC_HCI_HCP_COMMAND, cmd, 7362306a36Sopenharmony_ci param, param_len, 7462306a36Sopenharmony_ci nfc_hci_execute_cb, &hcp_ew, 7562306a36Sopenharmony_ci MAX_FWI); 7662306a36Sopenharmony_ci if (hcp_ew.exec_result < 0) 7762306a36Sopenharmony_ci return hcp_ew.exec_result; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci wait_event(ew_wq, hcp_ew.exec_complete == true); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (hcp_ew.exec_result == 0) { 8262306a36Sopenharmony_ci if (skb) 8362306a36Sopenharmony_ci *skb = hcp_ew.result_skb; 8462306a36Sopenharmony_ci else 8562306a36Sopenharmony_ci kfree_skb(hcp_ew.result_skb); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return hcp_ew.exec_result; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciint nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event, 9262306a36Sopenharmony_ci const u8 *param, size_t param_len) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci u8 pipe; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci pr_debug("%d to gate %d\n", event, gate); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci pipe = hdev->gate2pipe[gate]; 9962306a36Sopenharmony_ci if (pipe == NFC_HCI_INVALID_PIPE) 10062306a36Sopenharmony_ci return -EADDRNOTAVAIL; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_EVENT, event, 10362306a36Sopenharmony_ci param, param_len, NULL, NULL, 0); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_send_event); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * Execute an hci command sent to gate. 10962306a36Sopenharmony_ci * skb will contain response data if success. skb can be NULL if you are not 11062306a36Sopenharmony_ci * interested by the response. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ciint nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd, 11362306a36Sopenharmony_ci const u8 *param, size_t param_len, struct sk_buff **skb) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci u8 pipe; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci pipe = hdev->gate2pipe[gate]; 11862306a36Sopenharmony_ci if (pipe == NFC_HCI_INVALID_PIPE) 11962306a36Sopenharmony_ci return -EADDRNOTAVAIL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return nfc_hci_execute_cmd(hdev, pipe, cmd, param, param_len, skb); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_send_cmd); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciint nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd, 12662306a36Sopenharmony_ci const u8 *param, size_t param_len, 12762306a36Sopenharmony_ci data_exchange_cb_t cb, void *cb_context) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci u8 pipe; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci pipe = hdev->gate2pipe[gate]; 13262306a36Sopenharmony_ci if (pipe == NFC_HCI_INVALID_PIPE) 13362306a36Sopenharmony_ci return -EADDRNOTAVAIL; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len, 13662306a36Sopenharmony_ci cb, cb_context); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_send_cmd_async); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciint nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx, 14162306a36Sopenharmony_ci const u8 *param, size_t param_len) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int r; 14462306a36Sopenharmony_ci u8 *tmp; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* TODO ELa: reg idx must be inserted before param, but we don't want 14762306a36Sopenharmony_ci * to ask the caller to do it to keep a simpler API. 14862306a36Sopenharmony_ci * For now, just create a new temporary param buffer. This is far from 14962306a36Sopenharmony_ci * optimal though, and the plan is to modify APIs to pass idx down to 15062306a36Sopenharmony_ci * nfc_hci_hcp_message_tx where the frame is actually built, thereby 15162306a36Sopenharmony_ci * eliminating the need for the temp allocation-copy here. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci pr_debug("idx=%d to gate %d\n", idx, gate); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci tmp = kmalloc(1 + param_len, GFP_KERNEL); 15762306a36Sopenharmony_ci if (tmp == NULL) 15862306a36Sopenharmony_ci return -ENOMEM; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci *tmp = idx; 16162306a36Sopenharmony_ci memcpy(tmp + 1, param, param_len); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci r = nfc_hci_send_cmd(hdev, gate, NFC_HCI_ANY_SET_PARAMETER, 16462306a36Sopenharmony_ci tmp, param_len + 1, NULL); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci kfree(tmp); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return r; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_set_param); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciint nfc_hci_get_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx, 17362306a36Sopenharmony_ci struct sk_buff **skb) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci pr_debug("gate=%d regidx=%d\n", gate, idx); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return nfc_hci_send_cmd(hdev, gate, NFC_HCI_ANY_GET_PARAMETER, 17862306a36Sopenharmony_ci &idx, 1, skb); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_get_param); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int nfc_hci_open_pipe(struct nfc_hci_dev *hdev, u8 pipe) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct sk_buff *skb; 18562306a36Sopenharmony_ci int r; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci pr_debug("pipe=%d\n", pipe); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci r = nfc_hci_execute_cmd(hdev, pipe, NFC_HCI_ANY_OPEN_PIPE, 19062306a36Sopenharmony_ci NULL, 0, &skb); 19162306a36Sopenharmony_ci if (r == 0) { 19262306a36Sopenharmony_ci /* dest host other than host controller will send 19362306a36Sopenharmony_ci * number of pipes already open on this gate before 19462306a36Sopenharmony_ci * execution. The number can be found in skb->data[0] 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci kfree_skb(skb); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return r; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int nfc_hci_close_pipe(struct nfc_hci_dev *hdev, u8 pipe) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci return nfc_hci_execute_cmd(hdev, pipe, NFC_HCI_ANY_CLOSE_PIPE, 20562306a36Sopenharmony_ci NULL, 0, NULL); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic u8 nfc_hci_create_pipe(struct nfc_hci_dev *hdev, u8 dest_host, 20962306a36Sopenharmony_ci u8 dest_gate, int *result) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct sk_buff *skb; 21262306a36Sopenharmony_ci struct hci_create_pipe_params params; 21362306a36Sopenharmony_ci struct hci_create_pipe_resp *resp; 21462306a36Sopenharmony_ci u8 pipe; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci pr_debug("gate=%d\n", dest_gate); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci params.src_gate = NFC_HCI_ADMIN_GATE; 21962306a36Sopenharmony_ci params.dest_host = dest_host; 22062306a36Sopenharmony_ci params.dest_gate = dest_gate; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci *result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, 22362306a36Sopenharmony_ci NFC_HCI_ADM_CREATE_PIPE, 22462306a36Sopenharmony_ci (u8 *) ¶ms, sizeof(params), &skb); 22562306a36Sopenharmony_ci if (*result < 0) 22662306a36Sopenharmony_ci return NFC_HCI_INVALID_PIPE; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci resp = (struct hci_create_pipe_resp *)skb->data; 22962306a36Sopenharmony_ci pipe = resp->pipe; 23062306a36Sopenharmony_ci kfree_skb(skb); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci pr_debug("pipe created=%d\n", pipe); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return pipe; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, 24062306a36Sopenharmony_ci NFC_HCI_ADM_DELETE_PIPE, &pipe, 1, NULL); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci u8 param[2]; 24662306a36Sopenharmony_ci size_t param_len = 2; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* TODO: Find out what the identity reference data is 24962306a36Sopenharmony_ci * and fill param with it. HCI spec 6.1.3.5 */ 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks)) 25262306a36Sopenharmony_ci param_len = 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, 25562306a36Sopenharmony_ci NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len, 25662306a36Sopenharmony_ci NULL); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciint nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int r; 26262306a36Sopenharmony_ci u8 pipe = hdev->gate2pipe[gate]; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (pipe == NFC_HCI_INVALID_PIPE) 26562306a36Sopenharmony_ci return -EADDRNOTAVAIL; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci r = nfc_hci_close_pipe(hdev, pipe); 26862306a36Sopenharmony_ci if (r < 0) 26962306a36Sopenharmony_ci return r; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (pipe != NFC_HCI_LINK_MGMT_PIPE && pipe != NFC_HCI_ADMIN_PIPE) { 27262306a36Sopenharmony_ci r = nfc_hci_delete_pipe(hdev, pipe); 27362306a36Sopenharmony_ci if (r < 0) 27462306a36Sopenharmony_ci return r; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci hdev->gate2pipe[gate] = NFC_HCI_INVALID_PIPE; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_disconnect_gate); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciint nfc_hci_disconnect_all_gates(struct nfc_hci_dev *hdev) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci int r; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci r = nfc_hci_clear_all_pipes(hdev); 28862306a36Sopenharmony_ci if (r < 0) 28962306a36Sopenharmony_ci return r; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci nfc_hci_reset_pipes(hdev); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_disconnect_all_gates); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ciint nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate, 29862306a36Sopenharmony_ci u8 pipe) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci bool pipe_created = false; 30162306a36Sopenharmony_ci int r; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (pipe == NFC_HCI_DO_NOT_CREATE_PIPE) 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE) 30762306a36Sopenharmony_ci return -EADDRINUSE; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (pipe != NFC_HCI_INVALID_PIPE) 31062306a36Sopenharmony_ci goto open_pipe; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci switch (dest_gate) { 31362306a36Sopenharmony_ci case NFC_HCI_LINK_MGMT_GATE: 31462306a36Sopenharmony_ci pipe = NFC_HCI_LINK_MGMT_PIPE; 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci case NFC_HCI_ADMIN_GATE: 31762306a36Sopenharmony_ci pipe = NFC_HCI_ADMIN_PIPE; 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci default: 32062306a36Sopenharmony_ci pipe = nfc_hci_create_pipe(hdev, dest_host, dest_gate, &r); 32162306a36Sopenharmony_ci if (pipe == NFC_HCI_INVALID_PIPE) 32262306a36Sopenharmony_ci return r; 32362306a36Sopenharmony_ci pipe_created = true; 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciopen_pipe: 32862306a36Sopenharmony_ci r = nfc_hci_open_pipe(hdev, pipe); 32962306a36Sopenharmony_ci if (r < 0) { 33062306a36Sopenharmony_ci if (pipe_created) 33162306a36Sopenharmony_ci if (nfc_hci_delete_pipe(hdev, pipe) < 0) { 33262306a36Sopenharmony_ci /* TODO: Cannot clean by deleting pipe... 33362306a36Sopenharmony_ci * -> inconsistent state */ 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci return r; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci hdev->pipes[pipe].gate = dest_gate; 33962306a36Sopenharmony_ci hdev->pipes[pipe].dest_host = dest_host; 34062306a36Sopenharmony_ci hdev->gate2pipe[dest_gate] = pipe; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_connect_gate); 345