18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012 Intel Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "hci: %s: " fmt, __func__ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/nfc.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <net/nfc/nfc.h> 148c2ecf20Sopenharmony_ci#include <net/nfc/hci.h> 158c2ecf20Sopenharmony_ci#include <net/nfc/llc.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "hci.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Largest headroom needed for outgoing HCI commands */ 208c2ecf20Sopenharmony_ci#define HCI_CMDS_HEADROOM 1 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciint nfc_hci_result_to_errno(u8 result) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci switch (result) { 258c2ecf20Sopenharmony_ci case NFC_HCI_ANY_OK: 268c2ecf20Sopenharmony_ci return 0; 278c2ecf20Sopenharmony_ci case NFC_HCI_ANY_E_REG_PAR_UNKNOWN: 288c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 298c2ecf20Sopenharmony_ci case NFC_HCI_ANY_E_TIMEOUT: 308c2ecf20Sopenharmony_ci return -ETIME; 318c2ecf20Sopenharmony_ci default: 328c2ecf20Sopenharmony_ci return -1; 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_result_to_errno); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_civoid nfc_hci_reset_pipes(struct nfc_hci_dev *hdev) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci int i = 0; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci for (i = 0; i < NFC_HCI_MAX_PIPES; i++) { 428c2ecf20Sopenharmony_ci hdev->pipes[i].gate = NFC_HCI_INVALID_GATE; 438c2ecf20Sopenharmony_ci hdev->pipes[i].dest_host = NFC_HCI_INVALID_HOST; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe)); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_reset_pipes); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_civoid nfc_hci_reset_pipes_per_host(struct nfc_hci_dev *hdev, u8 host) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci int i = 0; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci for (i = 0; i < NFC_HCI_MAX_PIPES; i++) { 548c2ecf20Sopenharmony_ci if (hdev->pipes[i].dest_host != host) 558c2ecf20Sopenharmony_ci continue; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci hdev->pipes[i].gate = NFC_HCI_INVALID_GATE; 588c2ecf20Sopenharmony_ci hdev->pipes[i].dest_host = NFC_HCI_INVALID_HOST; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_reset_pipes_per_host); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void nfc_hci_msg_tx_work(struct work_struct *work) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = container_of(work, struct nfc_hci_dev, 668c2ecf20Sopenharmony_ci msg_tx_work); 678c2ecf20Sopenharmony_ci struct hci_msg *msg; 688c2ecf20Sopenharmony_ci struct sk_buff *skb; 698c2ecf20Sopenharmony_ci int r = 0; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci mutex_lock(&hdev->msg_tx_mutex); 728c2ecf20Sopenharmony_ci if (hdev->shutting_down) 738c2ecf20Sopenharmony_ci goto exit; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (hdev->cmd_pending_msg) { 768c2ecf20Sopenharmony_ci if (timer_pending(&hdev->cmd_timer) == 0) { 778c2ecf20Sopenharmony_ci if (hdev->cmd_pending_msg->cb) 788c2ecf20Sopenharmony_ci hdev->cmd_pending_msg->cb(hdev-> 798c2ecf20Sopenharmony_ci cmd_pending_msg-> 808c2ecf20Sopenharmony_ci cb_context, 818c2ecf20Sopenharmony_ci NULL, 828c2ecf20Sopenharmony_ci -ETIME); 838c2ecf20Sopenharmony_ci kfree(hdev->cmd_pending_msg); 848c2ecf20Sopenharmony_ci hdev->cmd_pending_msg = NULL; 858c2ecf20Sopenharmony_ci } else { 868c2ecf20Sopenharmony_ci goto exit; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cinext_msg: 918c2ecf20Sopenharmony_ci if (list_empty(&hdev->msg_tx_queue)) 928c2ecf20Sopenharmony_ci goto exit; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci msg = list_first_entry(&hdev->msg_tx_queue, struct hci_msg, msg_l); 958c2ecf20Sopenharmony_ci list_del(&msg->msg_l); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci pr_debug("msg_tx_queue has a cmd to send\n"); 988c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&msg->msg_frags)) != NULL) { 998c2ecf20Sopenharmony_ci r = nfc_llc_xmit_from_hci(hdev->llc, skb); 1008c2ecf20Sopenharmony_ci if (r < 0) { 1018c2ecf20Sopenharmony_ci kfree_skb(skb); 1028c2ecf20Sopenharmony_ci skb_queue_purge(&msg->msg_frags); 1038c2ecf20Sopenharmony_ci if (msg->cb) 1048c2ecf20Sopenharmony_ci msg->cb(msg->cb_context, NULL, r); 1058c2ecf20Sopenharmony_ci kfree(msg); 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (r) 1118c2ecf20Sopenharmony_ci goto next_msg; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (msg->wait_response == false) { 1148c2ecf20Sopenharmony_ci kfree(msg); 1158c2ecf20Sopenharmony_ci goto next_msg; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci hdev->cmd_pending_msg = msg; 1198c2ecf20Sopenharmony_ci mod_timer(&hdev->cmd_timer, jiffies + 1208c2ecf20Sopenharmony_ci msecs_to_jiffies(hdev->cmd_pending_msg->completion_delay)); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ciexit: 1238c2ecf20Sopenharmony_ci mutex_unlock(&hdev->msg_tx_mutex); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void nfc_hci_msg_rx_work(struct work_struct *work) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = container_of(work, struct nfc_hci_dev, 1298c2ecf20Sopenharmony_ci msg_rx_work); 1308c2ecf20Sopenharmony_ci struct sk_buff *skb; 1318c2ecf20Sopenharmony_ci struct hcp_message *message; 1328c2ecf20Sopenharmony_ci u8 pipe; 1338c2ecf20Sopenharmony_ci u8 type; 1348c2ecf20Sopenharmony_ci u8 instruction; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&hdev->msg_rx_queue)) != NULL) { 1378c2ecf20Sopenharmony_ci pipe = skb->data[0]; 1388c2ecf20Sopenharmony_ci skb_pull(skb, NFC_HCI_HCP_PACKET_HEADER_LEN); 1398c2ecf20Sopenharmony_ci message = (struct hcp_message *)skb->data; 1408c2ecf20Sopenharmony_ci type = HCP_MSG_GET_TYPE(message->header); 1418c2ecf20Sopenharmony_ci instruction = HCP_MSG_GET_CMD(message->header); 1428c2ecf20Sopenharmony_ci skb_pull(skb, NFC_HCI_HCP_MESSAGE_HEADER_LEN); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, skb); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void __nfc_hci_cmd_completion(struct nfc_hci_dev *hdev, int err, 1498c2ecf20Sopenharmony_ci struct sk_buff *skb) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci del_timer_sync(&hdev->cmd_timer); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (hdev->cmd_pending_msg->cb) 1548c2ecf20Sopenharmony_ci hdev->cmd_pending_msg->cb(hdev->cmd_pending_msg->cb_context, 1558c2ecf20Sopenharmony_ci skb, err); 1568c2ecf20Sopenharmony_ci else 1578c2ecf20Sopenharmony_ci kfree_skb(skb); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci kfree(hdev->cmd_pending_msg); 1608c2ecf20Sopenharmony_ci hdev->cmd_pending_msg = NULL; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci schedule_work(&hdev->msg_tx_work); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_civoid nfc_hci_resp_received(struct nfc_hci_dev *hdev, u8 result, 1668c2ecf20Sopenharmony_ci struct sk_buff *skb) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci mutex_lock(&hdev->msg_tx_mutex); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (hdev->cmd_pending_msg == NULL) { 1718c2ecf20Sopenharmony_ci kfree_skb(skb); 1728c2ecf20Sopenharmony_ci goto exit; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci __nfc_hci_cmd_completion(hdev, nfc_hci_result_to_errno(result), skb); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciexit: 1788c2ecf20Sopenharmony_ci mutex_unlock(&hdev->msg_tx_mutex); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_civoid nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, 1828c2ecf20Sopenharmony_ci struct sk_buff *skb) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci u8 status = NFC_HCI_ANY_OK; 1858c2ecf20Sopenharmony_ci struct hci_create_pipe_resp *create_info; 1868c2ecf20Sopenharmony_ci struct hci_delete_pipe_noti *delete_info; 1878c2ecf20Sopenharmony_ci struct hci_all_pipe_cleared_noti *cleared_info; 1888c2ecf20Sopenharmony_ci u8 gate; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci pr_debug("from pipe %x cmd %x\n", pipe, cmd); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (pipe >= NFC_HCI_MAX_PIPES) { 1938c2ecf20Sopenharmony_ci status = NFC_HCI_ANY_E_NOK; 1948c2ecf20Sopenharmony_ci goto exit; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci gate = hdev->pipes[pipe].gate; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci switch (cmd) { 2008c2ecf20Sopenharmony_ci case NFC_HCI_ADM_NOTIFY_PIPE_CREATED: 2018c2ecf20Sopenharmony_ci if (skb->len != 5) { 2028c2ecf20Sopenharmony_ci status = NFC_HCI_ANY_E_NOK; 2038c2ecf20Sopenharmony_ci goto exit; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci create_info = (struct hci_create_pipe_resp *)skb->data; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (create_info->pipe >= NFC_HCI_MAX_PIPES) { 2088c2ecf20Sopenharmony_ci status = NFC_HCI_ANY_E_NOK; 2098c2ecf20Sopenharmony_ci goto exit; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Save the new created pipe and bind with local gate, 2138c2ecf20Sopenharmony_ci * the description for skb->data[3] is destination gate id 2148c2ecf20Sopenharmony_ci * but since we received this cmd from host controller, we 2158c2ecf20Sopenharmony_ci * are the destination and it is our local gate 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ci hdev->gate2pipe[create_info->dest_gate] = create_info->pipe; 2188c2ecf20Sopenharmony_ci hdev->pipes[create_info->pipe].gate = create_info->dest_gate; 2198c2ecf20Sopenharmony_ci hdev->pipes[create_info->pipe].dest_host = 2208c2ecf20Sopenharmony_ci create_info->src_host; 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci case NFC_HCI_ANY_OPEN_PIPE: 2238c2ecf20Sopenharmony_ci if (gate == NFC_HCI_INVALID_GATE) { 2248c2ecf20Sopenharmony_ci status = NFC_HCI_ANY_E_NOK; 2258c2ecf20Sopenharmony_ci goto exit; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci case NFC_HCI_ADM_NOTIFY_PIPE_DELETED: 2298c2ecf20Sopenharmony_ci if (skb->len != 1) { 2308c2ecf20Sopenharmony_ci status = NFC_HCI_ANY_E_NOK; 2318c2ecf20Sopenharmony_ci goto exit; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci delete_info = (struct hci_delete_pipe_noti *)skb->data; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (delete_info->pipe >= NFC_HCI_MAX_PIPES) { 2368c2ecf20Sopenharmony_ci status = NFC_HCI_ANY_E_NOK; 2378c2ecf20Sopenharmony_ci goto exit; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci hdev->pipes[delete_info->pipe].gate = NFC_HCI_INVALID_GATE; 2418c2ecf20Sopenharmony_ci hdev->pipes[delete_info->pipe].dest_host = NFC_HCI_INVALID_HOST; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case NFC_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED: 2448c2ecf20Sopenharmony_ci if (skb->len != 1) { 2458c2ecf20Sopenharmony_ci status = NFC_HCI_ANY_E_NOK; 2468c2ecf20Sopenharmony_ci goto exit; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci cleared_info = (struct hci_all_pipe_cleared_noti *)skb->data; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci nfc_hci_reset_pipes_per_host(hdev, cleared_info->host); 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci default: 2538c2ecf20Sopenharmony_ci pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate); 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (hdev->ops->cmd_received) 2588c2ecf20Sopenharmony_ci hdev->ops->cmd_received(hdev, pipe, cmd, skb); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ciexit: 2618c2ecf20Sopenharmony_ci nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_RESPONSE, 2628c2ecf20Sopenharmony_ci status, NULL, 0, NULL, NULL, 0); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci kfree_skb(skb); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ciu32 nfc_hci_sak_to_protocol(u8 sak) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci switch (NFC_HCI_TYPE_A_SEL_PROT(sak)) { 2708c2ecf20Sopenharmony_ci case NFC_HCI_TYPE_A_SEL_PROT_MIFARE: 2718c2ecf20Sopenharmony_ci return NFC_PROTO_MIFARE_MASK; 2728c2ecf20Sopenharmony_ci case NFC_HCI_TYPE_A_SEL_PROT_ISO14443: 2738c2ecf20Sopenharmony_ci return NFC_PROTO_ISO14443_MASK; 2748c2ecf20Sopenharmony_ci case NFC_HCI_TYPE_A_SEL_PROT_DEP: 2758c2ecf20Sopenharmony_ci return NFC_PROTO_NFC_DEP_MASK; 2768c2ecf20Sopenharmony_ci case NFC_HCI_TYPE_A_SEL_PROT_ISO14443_DEP: 2778c2ecf20Sopenharmony_ci return NFC_PROTO_ISO14443_MASK | NFC_PROTO_NFC_DEP_MASK; 2788c2ecf20Sopenharmony_ci default: 2798c2ecf20Sopenharmony_ci return 0xffffffff; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_sak_to_protocol); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciint nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct nfc_target *targets; 2878c2ecf20Sopenharmony_ci struct sk_buff *atqa_skb = NULL; 2888c2ecf20Sopenharmony_ci struct sk_buff *sak_skb = NULL; 2898c2ecf20Sopenharmony_ci struct sk_buff *uid_skb = NULL; 2908c2ecf20Sopenharmony_ci int r; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci pr_debug("from gate %d\n", gate); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci targets = kzalloc(sizeof(struct nfc_target), GFP_KERNEL); 2958c2ecf20Sopenharmony_ci if (targets == NULL) 2968c2ecf20Sopenharmony_ci return -ENOMEM; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci switch (gate) { 2998c2ecf20Sopenharmony_ci case NFC_HCI_RF_READER_A_GATE: 3008c2ecf20Sopenharmony_ci r = nfc_hci_get_param(hdev, NFC_HCI_RF_READER_A_GATE, 3018c2ecf20Sopenharmony_ci NFC_HCI_RF_READER_A_ATQA, &atqa_skb); 3028c2ecf20Sopenharmony_ci if (r < 0) 3038c2ecf20Sopenharmony_ci goto exit; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci r = nfc_hci_get_param(hdev, NFC_HCI_RF_READER_A_GATE, 3068c2ecf20Sopenharmony_ci NFC_HCI_RF_READER_A_SAK, &sak_skb); 3078c2ecf20Sopenharmony_ci if (r < 0) 3088c2ecf20Sopenharmony_ci goto exit; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (atqa_skb->len != 2 || sak_skb->len != 1) { 3118c2ecf20Sopenharmony_ci r = -EPROTO; 3128c2ecf20Sopenharmony_ci goto exit; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci targets->supported_protocols = 3168c2ecf20Sopenharmony_ci nfc_hci_sak_to_protocol(sak_skb->data[0]); 3178c2ecf20Sopenharmony_ci if (targets->supported_protocols == 0xffffffff) { 3188c2ecf20Sopenharmony_ci r = -EPROTO; 3198c2ecf20Sopenharmony_ci goto exit; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci targets->sens_res = be16_to_cpu(*(__be16 *)atqa_skb->data); 3238c2ecf20Sopenharmony_ci targets->sel_res = sak_skb->data[0]; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci r = nfc_hci_get_param(hdev, NFC_HCI_RF_READER_A_GATE, 3268c2ecf20Sopenharmony_ci NFC_HCI_RF_READER_A_UID, &uid_skb); 3278c2ecf20Sopenharmony_ci if (r < 0) 3288c2ecf20Sopenharmony_ci goto exit; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (uid_skb->len == 0 || uid_skb->len > NFC_NFCID1_MAXSIZE) { 3318c2ecf20Sopenharmony_ci r = -EPROTO; 3328c2ecf20Sopenharmony_ci goto exit; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci memcpy(targets->nfcid1, uid_skb->data, uid_skb->len); 3368c2ecf20Sopenharmony_ci targets->nfcid1_len = uid_skb->len; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (hdev->ops->complete_target_discovered) { 3398c2ecf20Sopenharmony_ci r = hdev->ops->complete_target_discovered(hdev, gate, 3408c2ecf20Sopenharmony_ci targets); 3418c2ecf20Sopenharmony_ci if (r < 0) 3428c2ecf20Sopenharmony_ci goto exit; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci case NFC_HCI_RF_READER_B_GATE: 3468c2ecf20Sopenharmony_ci targets->supported_protocols = NFC_PROTO_ISO14443_B_MASK; 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci default: 3498c2ecf20Sopenharmony_ci if (hdev->ops->target_from_gate) 3508c2ecf20Sopenharmony_ci r = hdev->ops->target_from_gate(hdev, gate, targets); 3518c2ecf20Sopenharmony_ci else 3528c2ecf20Sopenharmony_ci r = -EPROTO; 3538c2ecf20Sopenharmony_ci if (r < 0) 3548c2ecf20Sopenharmony_ci goto exit; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (hdev->ops->complete_target_discovered) { 3578c2ecf20Sopenharmony_ci r = hdev->ops->complete_target_discovered(hdev, gate, 3588c2ecf20Sopenharmony_ci targets); 3598c2ecf20Sopenharmony_ci if (r < 0) 3608c2ecf20Sopenharmony_ci goto exit; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* if driver set the new gate, we will skip the old one */ 3668c2ecf20Sopenharmony_ci if (targets->hci_reader_gate == 0x00) 3678c2ecf20Sopenharmony_ci targets->hci_reader_gate = gate; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci r = nfc_targets_found(hdev->ndev, targets, 1); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ciexit: 3728c2ecf20Sopenharmony_ci kfree(targets); 3738c2ecf20Sopenharmony_ci kfree_skb(atqa_skb); 3748c2ecf20Sopenharmony_ci kfree_skb(sak_skb); 3758c2ecf20Sopenharmony_ci kfree_skb(uid_skb); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return r; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_target_discovered); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_civoid nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, 3828c2ecf20Sopenharmony_ci struct sk_buff *skb) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci int r = 0; 3858c2ecf20Sopenharmony_ci u8 gate; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (pipe >= NFC_HCI_MAX_PIPES) { 3888c2ecf20Sopenharmony_ci pr_err("Discarded event %x to invalid pipe %x\n", event, pipe); 3898c2ecf20Sopenharmony_ci goto exit; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci gate = hdev->pipes[pipe].gate; 3938c2ecf20Sopenharmony_ci if (gate == NFC_HCI_INVALID_GATE) { 3948c2ecf20Sopenharmony_ci pr_err("Discarded event %x to unopened pipe %x\n", event, pipe); 3958c2ecf20Sopenharmony_ci goto exit; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (hdev->ops->event_received) { 3998c2ecf20Sopenharmony_ci r = hdev->ops->event_received(hdev, pipe, event, skb); 4008c2ecf20Sopenharmony_ci if (r <= 0) 4018c2ecf20Sopenharmony_ci goto exit_noskb; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci switch (event) { 4058c2ecf20Sopenharmony_ci case NFC_HCI_EVT_TARGET_DISCOVERED: 4068c2ecf20Sopenharmony_ci if (skb->len < 1) { /* no status data? */ 4078c2ecf20Sopenharmony_ci r = -EPROTO; 4088c2ecf20Sopenharmony_ci goto exit; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (skb->data[0] == 3) { 4128c2ecf20Sopenharmony_ci /* TODO: Multiple targets in field, none activated 4138c2ecf20Sopenharmony_ci * poll is supposedly stopped, but there is no 4148c2ecf20Sopenharmony_ci * single target to activate, so nothing to report 4158c2ecf20Sopenharmony_ci * up. 4168c2ecf20Sopenharmony_ci * if we need to restart poll, we must save the 4178c2ecf20Sopenharmony_ci * protocols from the initial poll and reuse here. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (skb->data[0] != 0) { 4228c2ecf20Sopenharmony_ci r = -EPROTO; 4238c2ecf20Sopenharmony_ci goto exit; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci r = nfc_hci_target_discovered(hdev, gate); 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci default: 4298c2ecf20Sopenharmony_ci pr_info("Discarded unknown event %x to gate %x\n", event, gate); 4308c2ecf20Sopenharmony_ci r = -EINVAL; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ciexit: 4358c2ecf20Sopenharmony_ci kfree_skb(skb); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ciexit_noskb: 4388c2ecf20Sopenharmony_ci if (r) 4398c2ecf20Sopenharmony_ci nfc_hci_driver_failure(hdev, r); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void nfc_hci_cmd_timeout(struct timer_list *t) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = from_timer(hdev, t, cmd_timer); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci schedule_work(&hdev->msg_tx_work); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int hci_dev_connect_gates(struct nfc_hci_dev *hdev, u8 gate_count, 4508c2ecf20Sopenharmony_ci struct nfc_hci_gate *gates) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci int r; 4538c2ecf20Sopenharmony_ci while (gate_count--) { 4548c2ecf20Sopenharmony_ci r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, 4558c2ecf20Sopenharmony_ci gates->gate, gates->pipe); 4568c2ecf20Sopenharmony_ci if (r < 0) 4578c2ecf20Sopenharmony_ci return r; 4588c2ecf20Sopenharmony_ci gates++; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int hci_dev_session_init(struct nfc_hci_dev *hdev) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 4678c2ecf20Sopenharmony_ci int r; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (hdev->init_data.gates[0].gate != NFC_HCI_ADMIN_GATE) 4708c2ecf20Sopenharmony_ci return -EPROTO; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, 4738c2ecf20Sopenharmony_ci hdev->init_data.gates[0].gate, 4748c2ecf20Sopenharmony_ci hdev->init_data.gates[0].pipe); 4758c2ecf20Sopenharmony_ci if (r < 0) 4768c2ecf20Sopenharmony_ci goto exit; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci r = nfc_hci_get_param(hdev, NFC_HCI_ADMIN_GATE, 4798c2ecf20Sopenharmony_ci NFC_HCI_ADMIN_SESSION_IDENTITY, &skb); 4808c2ecf20Sopenharmony_ci if (r < 0) 4818c2ecf20Sopenharmony_ci goto disconnect_all; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (skb->len && skb->len == strlen(hdev->init_data.session_id) && 4848c2ecf20Sopenharmony_ci (memcmp(hdev->init_data.session_id, skb->data, 4858c2ecf20Sopenharmony_ci skb->len) == 0) && hdev->ops->load_session) { 4868c2ecf20Sopenharmony_ci /* Restore gate<->pipe table from some proprietary location. */ 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci r = hdev->ops->load_session(hdev); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (r < 0) 4918c2ecf20Sopenharmony_ci goto disconnect_all; 4928c2ecf20Sopenharmony_ci } else { 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci r = nfc_hci_disconnect_all_gates(hdev); 4958c2ecf20Sopenharmony_ci if (r < 0) 4968c2ecf20Sopenharmony_ci goto exit; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count, 4998c2ecf20Sopenharmony_ci hdev->init_data.gates); 5008c2ecf20Sopenharmony_ci if (r < 0) 5018c2ecf20Sopenharmony_ci goto disconnect_all; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, 5048c2ecf20Sopenharmony_ci NFC_HCI_ADMIN_SESSION_IDENTITY, 5058c2ecf20Sopenharmony_ci hdev->init_data.session_id, 5068c2ecf20Sopenharmony_ci strlen(hdev->init_data.session_id)); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci if (r == 0) 5098c2ecf20Sopenharmony_ci goto exit; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cidisconnect_all: 5128c2ecf20Sopenharmony_ci nfc_hci_disconnect_all_gates(hdev); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciexit: 5158c2ecf20Sopenharmony_ci kfree_skb(skb); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return r; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int hci_dev_version(struct nfc_hci_dev *hdev) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci int r; 5238c2ecf20Sopenharmony_ci struct sk_buff *skb; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE, 5268c2ecf20Sopenharmony_ci NFC_HCI_ID_MGMT_VERSION_SW, &skb); 5278c2ecf20Sopenharmony_ci if (r == -EOPNOTSUPP) { 5288c2ecf20Sopenharmony_ci pr_info("Software/Hardware info not available\n"); 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci if (r < 0) 5328c2ecf20Sopenharmony_ci return r; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (skb->len != 3) { 5358c2ecf20Sopenharmony_ci kfree_skb(skb); 5368c2ecf20Sopenharmony_ci return -EINVAL; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci hdev->sw_romlib = (skb->data[0] & 0xf0) >> 4; 5408c2ecf20Sopenharmony_ci hdev->sw_patch = skb->data[0] & 0x0f; 5418c2ecf20Sopenharmony_ci hdev->sw_flashlib_major = skb->data[1]; 5428c2ecf20Sopenharmony_ci hdev->sw_flashlib_minor = skb->data[2]; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci kfree_skb(skb); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE, 5478c2ecf20Sopenharmony_ci NFC_HCI_ID_MGMT_VERSION_HW, &skb); 5488c2ecf20Sopenharmony_ci if (r < 0) 5498c2ecf20Sopenharmony_ci return r; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (skb->len != 3) { 5528c2ecf20Sopenharmony_ci kfree_skb(skb); 5538c2ecf20Sopenharmony_ci return -EINVAL; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci hdev->hw_derivative = (skb->data[0] & 0xe0) >> 5; 5578c2ecf20Sopenharmony_ci hdev->hw_version = skb->data[0] & 0x1f; 5588c2ecf20Sopenharmony_ci hdev->hw_mpw = (skb->data[1] & 0xc0) >> 6; 5598c2ecf20Sopenharmony_ci hdev->hw_software = skb->data[1] & 0x3f; 5608c2ecf20Sopenharmony_ci hdev->hw_bsid = skb->data[2]; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci kfree_skb(skb); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci pr_info("SOFTWARE INFO:\n"); 5658c2ecf20Sopenharmony_ci pr_info("RomLib : %d\n", hdev->sw_romlib); 5668c2ecf20Sopenharmony_ci pr_info("Patch : %d\n", hdev->sw_patch); 5678c2ecf20Sopenharmony_ci pr_info("FlashLib Major : %d\n", hdev->sw_flashlib_major); 5688c2ecf20Sopenharmony_ci pr_info("FlashLib Minor : %d\n", hdev->sw_flashlib_minor); 5698c2ecf20Sopenharmony_ci pr_info("HARDWARE INFO:\n"); 5708c2ecf20Sopenharmony_ci pr_info("Derivative : %d\n", hdev->hw_derivative); 5718c2ecf20Sopenharmony_ci pr_info("HW Version : %d\n", hdev->hw_version); 5728c2ecf20Sopenharmony_ci pr_info("#MPW : %d\n", hdev->hw_mpw); 5738c2ecf20Sopenharmony_ci pr_info("Software : %d\n", hdev->hw_software); 5748c2ecf20Sopenharmony_ci pr_info("BSID Version : %d\n", hdev->hw_bsid); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return 0; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int hci_dev_up(struct nfc_dev *nfc_dev) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 5828c2ecf20Sopenharmony_ci int r = 0; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (hdev->ops->open) { 5858c2ecf20Sopenharmony_ci r = hdev->ops->open(hdev); 5868c2ecf20Sopenharmony_ci if (r < 0) 5878c2ecf20Sopenharmony_ci return r; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci r = nfc_llc_start(hdev->llc); 5918c2ecf20Sopenharmony_ci if (r < 0) 5928c2ecf20Sopenharmony_ci goto exit_close; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci r = hci_dev_session_init(hdev); 5958c2ecf20Sopenharmony_ci if (r < 0) 5968c2ecf20Sopenharmony_ci goto exit_llc; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, 5998c2ecf20Sopenharmony_ci NFC_HCI_EVT_END_OPERATION, NULL, 0); 6008c2ecf20Sopenharmony_ci if (r < 0) 6018c2ecf20Sopenharmony_ci goto exit_llc; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (hdev->ops->hci_ready) { 6048c2ecf20Sopenharmony_ci r = hdev->ops->hci_ready(hdev); 6058c2ecf20Sopenharmony_ci if (r < 0) 6068c2ecf20Sopenharmony_ci goto exit_llc; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci r = hci_dev_version(hdev); 6108c2ecf20Sopenharmony_ci if (r < 0) 6118c2ecf20Sopenharmony_ci goto exit_llc; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ciexit_llc: 6168c2ecf20Sopenharmony_ci nfc_llc_stop(hdev->llc); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ciexit_close: 6198c2ecf20Sopenharmony_ci if (hdev->ops->close) 6208c2ecf20Sopenharmony_ci hdev->ops->close(hdev); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return r; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int hci_dev_down(struct nfc_dev *nfc_dev) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci nfc_llc_stop(hdev->llc); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (hdev->ops->close) 6328c2ecf20Sopenharmony_ci hdev->ops->close(hdev); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci nfc_hci_reset_pipes(hdev); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return 0; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int hci_start_poll(struct nfc_dev *nfc_dev, 6408c2ecf20Sopenharmony_ci u32 im_protocols, u32 tm_protocols) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (hdev->ops->start_poll) 6458c2ecf20Sopenharmony_ci return hdev->ops->start_poll(hdev, im_protocols, tm_protocols); 6468c2ecf20Sopenharmony_ci else 6478c2ecf20Sopenharmony_ci return nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, 6488c2ecf20Sopenharmony_ci NFC_HCI_EVT_READER_REQUESTED, 6498c2ecf20Sopenharmony_ci NULL, 0); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic void hci_stop_poll(struct nfc_dev *nfc_dev) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (hdev->ops->stop_poll) 6578c2ecf20Sopenharmony_ci hdev->ops->stop_poll(hdev); 6588c2ecf20Sopenharmony_ci else 6598c2ecf20Sopenharmony_ci nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, 6608c2ecf20Sopenharmony_ci NFC_HCI_EVT_END_OPERATION, NULL, 0); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, 6648c2ecf20Sopenharmony_ci __u8 comm_mode, __u8 *gb, size_t gb_len) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (!hdev->ops->dep_link_up) 6698c2ecf20Sopenharmony_ci return 0; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return hdev->ops->dep_link_up(hdev, target, comm_mode, 6728c2ecf20Sopenharmony_ci gb, gb_len); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic int hci_dep_link_down(struct nfc_dev *nfc_dev) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (!hdev->ops->dep_link_down) 6808c2ecf20Sopenharmony_ci return 0; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci return hdev->ops->dep_link_down(hdev); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic int hci_activate_target(struct nfc_dev *nfc_dev, 6868c2ecf20Sopenharmony_ci struct nfc_target *target, u32 protocol) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci return 0; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic void hci_deactivate_target(struct nfc_dev *nfc_dev, 6928c2ecf20Sopenharmony_ci struct nfc_target *target, 6938c2ecf20Sopenharmony_ci u8 mode) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci#define HCI_CB_TYPE_TRANSCEIVE 1 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic void hci_transceive_cb(void *context, struct sk_buff *skb, int err) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = context; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci switch (hdev->async_cb_type) { 7048c2ecf20Sopenharmony_ci case HCI_CB_TYPE_TRANSCEIVE: 7058c2ecf20Sopenharmony_ci /* 7068c2ecf20Sopenharmony_ci * TODO: Check RF Error indicator to make sure data is valid. 7078c2ecf20Sopenharmony_ci * It seems that HCI cmd can complete without error, but data 7088c2ecf20Sopenharmony_ci * can be invalid if an RF error occured? Ignore for now. 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ci if (err == 0) 7118c2ecf20Sopenharmony_ci skb_trim(skb, skb->len - 1); /* RF Err ind */ 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci hdev->async_cb(hdev->async_cb_context, skb, err); 7148c2ecf20Sopenharmony_ci break; 7158c2ecf20Sopenharmony_ci default: 7168c2ecf20Sopenharmony_ci if (err == 0) 7178c2ecf20Sopenharmony_ci kfree_skb(skb); 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, 7238c2ecf20Sopenharmony_ci struct sk_buff *skb, data_exchange_cb_t cb, 7248c2ecf20Sopenharmony_ci void *cb_context) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 7278c2ecf20Sopenharmony_ci int r; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci pr_debug("target_idx=%d\n", target->idx); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci switch (target->hci_reader_gate) { 7328c2ecf20Sopenharmony_ci case NFC_HCI_RF_READER_A_GATE: 7338c2ecf20Sopenharmony_ci case NFC_HCI_RF_READER_B_GATE: 7348c2ecf20Sopenharmony_ci if (hdev->ops->im_transceive) { 7358c2ecf20Sopenharmony_ci r = hdev->ops->im_transceive(hdev, target, skb, cb, 7368c2ecf20Sopenharmony_ci cb_context); 7378c2ecf20Sopenharmony_ci if (r <= 0) /* handled */ 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci *(u8 *)skb_push(skb, 1) = 0; /* CTR, see spec:10.2.2.1 */ 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci hdev->async_cb_type = HCI_CB_TYPE_TRANSCEIVE; 7448c2ecf20Sopenharmony_ci hdev->async_cb = cb; 7458c2ecf20Sopenharmony_ci hdev->async_cb_context = cb_context; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci r = nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, 7488c2ecf20Sopenharmony_ci NFC_HCI_WR_XCHG_DATA, skb->data, 7498c2ecf20Sopenharmony_ci skb->len, hci_transceive_cb, hdev); 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci default: 7528c2ecf20Sopenharmony_ci if (hdev->ops->im_transceive) { 7538c2ecf20Sopenharmony_ci r = hdev->ops->im_transceive(hdev, target, skb, cb, 7548c2ecf20Sopenharmony_ci cb_context); 7558c2ecf20Sopenharmony_ci if (r == 1) 7568c2ecf20Sopenharmony_ci r = -ENOTSUPP; 7578c2ecf20Sopenharmony_ci } else { 7588c2ecf20Sopenharmony_ci r = -ENOTSUPP; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci break; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci kfree_skb(skb); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci return r; 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (!hdev->ops->tm_send) { 7738c2ecf20Sopenharmony_ci kfree_skb(skb); 7748c2ecf20Sopenharmony_ci return -ENOTSUPP; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci return hdev->ops->tm_send(hdev, skb); 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic int hci_check_presence(struct nfc_dev *nfc_dev, 7818c2ecf20Sopenharmony_ci struct nfc_target *target) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (!hdev->ops->check_presence) 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci return hdev->ops->check_presence(hdev, target); 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic int hci_discover_se(struct nfc_dev *nfc_dev) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (hdev->ops->discover_se) 7968c2ecf20Sopenharmony_ci return hdev->ops->discover_se(hdev); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci return 0; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic int hci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (hdev->ops->enable_se) 8068c2ecf20Sopenharmony_ci return hdev->ops->enable_se(hdev, se_idx); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return 0; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (hdev->ops->disable_se) 8168c2ecf20Sopenharmony_ci return hdev->ops->disable_se(hdev, se_idx); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci return 0; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic int hci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, 8228c2ecf20Sopenharmony_ci u8 *apdu, size_t apdu_length, 8238c2ecf20Sopenharmony_ci se_io_cb_t cb, void *cb_context) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (hdev->ops->se_io) 8288c2ecf20Sopenharmony_ci return hdev->ops->se_io(hdev, se_idx, apdu, 8298c2ecf20Sopenharmony_ci apdu_length, cb, cb_context); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic void nfc_hci_failure(struct nfc_hci_dev *hdev, int err) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci mutex_lock(&hdev->msg_tx_mutex); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (hdev->cmd_pending_msg == NULL) { 8398c2ecf20Sopenharmony_ci nfc_driver_failure(hdev->ndev, err); 8408c2ecf20Sopenharmony_ci goto exit; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci __nfc_hci_cmd_completion(hdev, err, NULL); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ciexit: 8468c2ecf20Sopenharmony_ci mutex_unlock(&hdev->msg_tx_mutex); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic void nfc_hci_llc_failure(struct nfc_hci_dev *hdev, int err) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci nfc_hci_failure(hdev, err); 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct hcp_packet *packet; 8578c2ecf20Sopenharmony_ci u8 type; 8588c2ecf20Sopenharmony_ci u8 instruction; 8598c2ecf20Sopenharmony_ci struct sk_buff *hcp_skb; 8608c2ecf20Sopenharmony_ci u8 pipe; 8618c2ecf20Sopenharmony_ci struct sk_buff *frag_skb; 8628c2ecf20Sopenharmony_ci int msg_len; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci packet = (struct hcp_packet *)skb->data; 8658c2ecf20Sopenharmony_ci if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) { 8668c2ecf20Sopenharmony_ci skb_queue_tail(&hdev->rx_hcp_frags, skb); 8678c2ecf20Sopenharmony_ci return; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* it's the last fragment. Does it need re-aggregation? */ 8718c2ecf20Sopenharmony_ci if (skb_queue_len(&hdev->rx_hcp_frags)) { 8728c2ecf20Sopenharmony_ci pipe = packet->header & NFC_HCI_FRAGMENT; 8738c2ecf20Sopenharmony_ci skb_queue_tail(&hdev->rx_hcp_frags, skb); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci msg_len = 0; 8768c2ecf20Sopenharmony_ci skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) { 8778c2ecf20Sopenharmony_ci msg_len += (frag_skb->len - 8788c2ecf20Sopenharmony_ci NFC_HCI_HCP_PACKET_HEADER_LEN); 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci hcp_skb = nfc_alloc_recv_skb(NFC_HCI_HCP_PACKET_HEADER_LEN + 8828c2ecf20Sopenharmony_ci msg_len, GFP_KERNEL); 8838c2ecf20Sopenharmony_ci if (hcp_skb == NULL) { 8848c2ecf20Sopenharmony_ci nfc_hci_failure(hdev, -ENOMEM); 8858c2ecf20Sopenharmony_ci return; 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci skb_put_u8(hcp_skb, pipe); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) { 8918c2ecf20Sopenharmony_ci msg_len = frag_skb->len - NFC_HCI_HCP_PACKET_HEADER_LEN; 8928c2ecf20Sopenharmony_ci skb_put_data(hcp_skb, 8938c2ecf20Sopenharmony_ci frag_skb->data + NFC_HCI_HCP_PACKET_HEADER_LEN, 8948c2ecf20Sopenharmony_ci msg_len); 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci skb_queue_purge(&hdev->rx_hcp_frags); 8988c2ecf20Sopenharmony_ci } else { 8998c2ecf20Sopenharmony_ci packet->header &= NFC_HCI_FRAGMENT; 9008c2ecf20Sopenharmony_ci hcp_skb = skb; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* if this is a response, dispatch immediately to 9048c2ecf20Sopenharmony_ci * unblock waiting cmd context. Otherwise, enqueue to dispatch 9058c2ecf20Sopenharmony_ci * in separate context where handler can also execute command. 9068c2ecf20Sopenharmony_ci */ 9078c2ecf20Sopenharmony_ci packet = (struct hcp_packet *)hcp_skb->data; 9088c2ecf20Sopenharmony_ci type = HCP_MSG_GET_TYPE(packet->message.header); 9098c2ecf20Sopenharmony_ci if (type == NFC_HCI_HCP_RESPONSE) { 9108c2ecf20Sopenharmony_ci pipe = packet->header; 9118c2ecf20Sopenharmony_ci instruction = HCP_MSG_GET_CMD(packet->message.header); 9128c2ecf20Sopenharmony_ci skb_pull(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN + 9138c2ecf20Sopenharmony_ci NFC_HCI_HCP_MESSAGE_HEADER_LEN); 9148c2ecf20Sopenharmony_ci nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb); 9158c2ecf20Sopenharmony_ci } else { 9168c2ecf20Sopenharmony_ci skb_queue_tail(&hdev->msg_rx_queue, hcp_skb); 9178c2ecf20Sopenharmony_ci schedule_work(&hdev->msg_rx_work); 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic int hci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (!hdev->ops->fw_download) 9268c2ecf20Sopenharmony_ci return -ENOTSUPP; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci return hdev->ops->fw_download(hdev, firmware_name); 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic struct nfc_ops hci_nfc_ops = { 9328c2ecf20Sopenharmony_ci .dev_up = hci_dev_up, 9338c2ecf20Sopenharmony_ci .dev_down = hci_dev_down, 9348c2ecf20Sopenharmony_ci .start_poll = hci_start_poll, 9358c2ecf20Sopenharmony_ci .stop_poll = hci_stop_poll, 9368c2ecf20Sopenharmony_ci .dep_link_up = hci_dep_link_up, 9378c2ecf20Sopenharmony_ci .dep_link_down = hci_dep_link_down, 9388c2ecf20Sopenharmony_ci .activate_target = hci_activate_target, 9398c2ecf20Sopenharmony_ci .deactivate_target = hci_deactivate_target, 9408c2ecf20Sopenharmony_ci .im_transceive = hci_transceive, 9418c2ecf20Sopenharmony_ci .tm_send = hci_tm_send, 9428c2ecf20Sopenharmony_ci .check_presence = hci_check_presence, 9438c2ecf20Sopenharmony_ci .fw_download = hci_fw_download, 9448c2ecf20Sopenharmony_ci .discover_se = hci_discover_se, 9458c2ecf20Sopenharmony_ci .enable_se = hci_enable_se, 9468c2ecf20Sopenharmony_ci .disable_se = hci_disable_se, 9478c2ecf20Sopenharmony_ci .se_io = hci_se_io, 9488c2ecf20Sopenharmony_ci}; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistruct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, 9518c2ecf20Sopenharmony_ci struct nfc_hci_init_data *init_data, 9528c2ecf20Sopenharmony_ci unsigned long quirks, 9538c2ecf20Sopenharmony_ci u32 protocols, 9548c2ecf20Sopenharmony_ci const char *llc_name, 9558c2ecf20Sopenharmony_ci int tx_headroom, 9568c2ecf20Sopenharmony_ci int tx_tailroom, 9578c2ecf20Sopenharmony_ci int max_link_payload) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci struct nfc_hci_dev *hdev; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci if (ops->xmit == NULL) 9628c2ecf20Sopenharmony_ci return NULL; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (protocols == 0) 9658c2ecf20Sopenharmony_ci return NULL; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci hdev = kzalloc(sizeof(struct nfc_hci_dev), GFP_KERNEL); 9688c2ecf20Sopenharmony_ci if (hdev == NULL) 9698c2ecf20Sopenharmony_ci return NULL; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci hdev->llc = nfc_llc_allocate(llc_name, hdev, ops->xmit, 9728c2ecf20Sopenharmony_ci nfc_hci_recv_from_llc, tx_headroom, 9738c2ecf20Sopenharmony_ci tx_tailroom, nfc_hci_llc_failure); 9748c2ecf20Sopenharmony_ci if (hdev->llc == NULL) { 9758c2ecf20Sopenharmony_ci kfree(hdev); 9768c2ecf20Sopenharmony_ci return NULL; 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, 9808c2ecf20Sopenharmony_ci tx_headroom + HCI_CMDS_HEADROOM, 9818c2ecf20Sopenharmony_ci tx_tailroom); 9828c2ecf20Sopenharmony_ci if (!hdev->ndev) { 9838c2ecf20Sopenharmony_ci nfc_llc_free(hdev->llc); 9848c2ecf20Sopenharmony_ci kfree(hdev); 9858c2ecf20Sopenharmony_ci return NULL; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci hdev->ops = ops; 9898c2ecf20Sopenharmony_ci hdev->max_data_link_payload = max_link_payload; 9908c2ecf20Sopenharmony_ci hdev->init_data = *init_data; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci nfc_set_drvdata(hdev->ndev, hdev); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci nfc_hci_reset_pipes(hdev); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci hdev->quirks = quirks; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci return hdev; 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_allocate_device); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_civoid nfc_hci_free_device(struct nfc_hci_dev *hdev) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci nfc_free_device(hdev->ndev); 10058c2ecf20Sopenharmony_ci nfc_llc_free(hdev->llc); 10068c2ecf20Sopenharmony_ci kfree(hdev); 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_free_device); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ciint nfc_hci_register_device(struct nfc_hci_dev *hdev) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci mutex_init(&hdev->msg_tx_mutex); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&hdev->msg_tx_queue); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci timer_setup(&hdev->cmd_timer, nfc_hci_cmd_timeout, 0); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci skb_queue_head_init(&hdev->rx_hcp_frags); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci skb_queue_head_init(&hdev->msg_rx_queue); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci return nfc_register_device(hdev->ndev); 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_register_device); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_civoid nfc_hci_unregister_device(struct nfc_hci_dev *hdev) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci struct hci_msg *msg, *n; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci mutex_lock(&hdev->msg_tx_mutex); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (hdev->cmd_pending_msg) { 10378c2ecf20Sopenharmony_ci if (hdev->cmd_pending_msg->cb) 10388c2ecf20Sopenharmony_ci hdev->cmd_pending_msg->cb( 10398c2ecf20Sopenharmony_ci hdev->cmd_pending_msg->cb_context, 10408c2ecf20Sopenharmony_ci NULL, -ESHUTDOWN); 10418c2ecf20Sopenharmony_ci kfree(hdev->cmd_pending_msg); 10428c2ecf20Sopenharmony_ci hdev->cmd_pending_msg = NULL; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci hdev->shutting_down = true; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci mutex_unlock(&hdev->msg_tx_mutex); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci del_timer_sync(&hdev->cmd_timer); 10508c2ecf20Sopenharmony_ci cancel_work_sync(&hdev->msg_tx_work); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci cancel_work_sync(&hdev->msg_rx_work); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci nfc_unregister_device(hdev->ndev); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci skb_queue_purge(&hdev->rx_hcp_frags); 10578c2ecf20Sopenharmony_ci skb_queue_purge(&hdev->msg_rx_queue); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci list_for_each_entry_safe(msg, n, &hdev->msg_tx_queue, msg_l) { 10608c2ecf20Sopenharmony_ci list_del(&msg->msg_l); 10618c2ecf20Sopenharmony_ci skb_queue_purge(&msg->msg_frags); 10628c2ecf20Sopenharmony_ci kfree(msg); 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_unregister_device); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_civoid nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci hdev->clientdata = clientdata; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_set_clientdata); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_civoid *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci return hdev->clientdata; 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_get_clientdata); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_civoid nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci nfc_hci_failure(hdev, err); 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_driver_failure); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_civoid nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci nfc_llc_rcv_from_drv(hdev->llc, skb); 10888c2ecf20Sopenharmony_ci} 10898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_recv_frame); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic int __init nfc_hci_init(void) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci return nfc_llc_init(); 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic void __exit nfc_hci_exit(void) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci nfc_llc_exit(); 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cisubsys_initcall(nfc_hci_init); 11028c2ecf20Sopenharmony_cimodule_exit(nfc_hci_exit); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NFC HCI Core"); 1106