18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * The NFC Controller Interface is the communication protocol between an 48c2ecf20Sopenharmony_ci * NFC Controller (NFCC) and a Device Host (DH). 58c2ecf20Sopenharmony_ci * This is the HCI over NCI implementation, as specified in the 10.2 68c2ecf20Sopenharmony_ci * section of the NCI 1.1 specification. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "../nfc.h" 148c2ecf20Sopenharmony_ci#include <net/nfc/nci.h> 158c2ecf20Sopenharmony_ci#include <net/nfc/nci_core.h> 168c2ecf20Sopenharmony_ci#include <linux/nfc.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct nci_data { 198c2ecf20Sopenharmony_ci u8 conn_id; 208c2ecf20Sopenharmony_ci u8 pipe; 218c2ecf20Sopenharmony_ci u8 cmd; 228c2ecf20Sopenharmony_ci const u8 *data; 238c2ecf20Sopenharmony_ci u32 data_len; 248c2ecf20Sopenharmony_ci} __packed; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct nci_hci_create_pipe_params { 278c2ecf20Sopenharmony_ci u8 src_gate; 288c2ecf20Sopenharmony_ci u8 dest_host; 298c2ecf20Sopenharmony_ci u8 dest_gate; 308c2ecf20Sopenharmony_ci} __packed; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct nci_hci_create_pipe_resp { 338c2ecf20Sopenharmony_ci u8 src_host; 348c2ecf20Sopenharmony_ci u8 src_gate; 358c2ecf20Sopenharmony_ci u8 dest_host; 368c2ecf20Sopenharmony_ci u8 dest_gate; 378c2ecf20Sopenharmony_ci u8 pipe; 388c2ecf20Sopenharmony_ci} __packed; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct nci_hci_delete_pipe_noti { 418c2ecf20Sopenharmony_ci u8 pipe; 428c2ecf20Sopenharmony_ci} __packed; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct nci_hci_all_pipe_cleared_noti { 458c2ecf20Sopenharmony_ci u8 host; 468c2ecf20Sopenharmony_ci} __packed; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct nci_hcp_message { 498c2ecf20Sopenharmony_ci u8 header; /* type -cmd,evt,rsp- + instruction */ 508c2ecf20Sopenharmony_ci u8 data[]; 518c2ecf20Sopenharmony_ci} __packed; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct nci_hcp_packet { 548c2ecf20Sopenharmony_ci u8 header; /* cbit+pipe */ 558c2ecf20Sopenharmony_ci struct nci_hcp_message message; 568c2ecf20Sopenharmony_ci} __packed; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define NCI_HCI_ANY_SET_PARAMETER 0x01 598c2ecf20Sopenharmony_ci#define NCI_HCI_ANY_GET_PARAMETER 0x02 608c2ecf20Sopenharmony_ci#define NCI_HCI_ANY_CLOSE_PIPE 0x04 618c2ecf20Sopenharmony_ci#define NCI_HCI_ADM_CLEAR_ALL_PIPE 0x14 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define NCI_HFP_NO_CHAINING 0x80 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define NCI_NFCEE_ID_HCI 0x80 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define NCI_EVT_HOT_PLUG 0x03 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01 708c2ecf20Sopenharmony_ci#define NCI_HCI_ADM_CREATE_PIPE 0x10 718c2ecf20Sopenharmony_ci#define NCI_HCI_ADM_DELETE_PIPE 0x11 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* HCP headers */ 748c2ecf20Sopenharmony_ci#define NCI_HCI_HCP_PACKET_HEADER_LEN 1 758c2ecf20Sopenharmony_ci#define NCI_HCI_HCP_MESSAGE_HEADER_LEN 1 768c2ecf20Sopenharmony_ci#define NCI_HCI_HCP_HEADER_LEN 2 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* HCP types */ 798c2ecf20Sopenharmony_ci#define NCI_HCI_HCP_COMMAND 0x00 808c2ecf20Sopenharmony_ci#define NCI_HCI_HCP_EVENT 0x01 818c2ecf20Sopenharmony_ci#define NCI_HCI_HCP_RESPONSE 0x02 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define NCI_HCI_ADM_NOTIFY_PIPE_CREATED 0x12 848c2ecf20Sopenharmony_ci#define NCI_HCI_ADM_NOTIFY_PIPE_DELETED 0x13 858c2ecf20Sopenharmony_ci#define NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED 0x15 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define NCI_HCI_FRAGMENT 0x7f 888c2ecf20Sopenharmony_ci#define NCI_HCP_HEADER(type, instr) ((((type) & 0x03) << 6) |\ 898c2ecf20Sopenharmony_ci ((instr) & 0x3f)) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define NCI_HCP_MSG_GET_TYPE(header) ((header & 0xc0) >> 6) 928c2ecf20Sopenharmony_ci#define NCI_HCP_MSG_GET_CMD(header) (header & 0x3f) 938c2ecf20Sopenharmony_ci#define NCI_HCP_MSG_GET_PIPE(header) (header & 0x7f) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int nci_hci_result_to_errno(u8 result) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci switch (result) { 988c2ecf20Sopenharmony_ci case NCI_HCI_ANY_OK: 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci case NCI_HCI_ANY_E_REG_PAR_UNKNOWN: 1018c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1028c2ecf20Sopenharmony_ci case NCI_HCI_ANY_E_TIMEOUT: 1038c2ecf20Sopenharmony_ci return -ETIME; 1048c2ecf20Sopenharmony_ci default: 1058c2ecf20Sopenharmony_ci return -1; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* HCI core */ 1108c2ecf20Sopenharmony_cistatic void nci_hci_reset_pipes(struct nci_hci_dev *hdev) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int i; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci for (i = 0; i < NCI_HCI_MAX_PIPES; i++) { 1158c2ecf20Sopenharmony_ci hdev->pipes[i].gate = NCI_HCI_INVALID_GATE; 1168c2ecf20Sopenharmony_ci hdev->pipes[i].host = NCI_HCI_INVALID_HOST; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci memset(hdev->gate2pipe, NCI_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe)); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void nci_hci_reset_pipes_per_host(struct nci_dev *ndev, u8 host) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int i; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci for (i = 0; i < NCI_HCI_MAX_PIPES; i++) { 1268c2ecf20Sopenharmony_ci if (ndev->hci_dev->pipes[i].host == host) { 1278c2ecf20Sopenharmony_ci ndev->hci_dev->pipes[i].gate = NCI_HCI_INVALID_GATE; 1288c2ecf20Sopenharmony_ci ndev->hci_dev->pipes[i].host = NCI_HCI_INVALID_HOST; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* Fragment HCI data over NCI packet. 1348c2ecf20Sopenharmony_ci * NFC Forum NCI 10.2.2 Data Exchange: 1358c2ecf20Sopenharmony_ci * The payload of the Data Packets sent on the Logical Connection SHALL be 1368c2ecf20Sopenharmony_ci * valid HCP packets, as defined within [ETSI_102622]. Each Data Packet SHALL 1378c2ecf20Sopenharmony_ci * contain a single HCP packet. NCI Segmentation and Reassembly SHALL NOT be 1388c2ecf20Sopenharmony_ci * applied to Data Messages in either direction. The HCI fragmentation mechanism 1398c2ecf20Sopenharmony_ci * is used if required. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_cistatic int nci_hci_send_data(struct nci_dev *ndev, u8 pipe, 1428c2ecf20Sopenharmony_ci const u8 data_type, const u8 *data, 1438c2ecf20Sopenharmony_ci size_t data_len) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 1468c2ecf20Sopenharmony_ci struct sk_buff *skb; 1478c2ecf20Sopenharmony_ci int len, i, r; 1488c2ecf20Sopenharmony_ci u8 cb = pipe; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 1518c2ecf20Sopenharmony_ci if (!conn_info) 1528c2ecf20Sopenharmony_ci return -EPROTO; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci i = 0; 1558c2ecf20Sopenharmony_ci skb = nci_skb_alloc(ndev, conn_info->max_pkt_payload_len + 1568c2ecf20Sopenharmony_ci NCI_DATA_HDR_SIZE, GFP_ATOMIC); 1578c2ecf20Sopenharmony_ci if (!skb) 1588c2ecf20Sopenharmony_ci return -ENOMEM; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci skb_reserve(skb, NCI_DATA_HDR_SIZE + 2); 1618c2ecf20Sopenharmony_ci *(u8 *)skb_push(skb, 1) = data_type; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci do { 1648c2ecf20Sopenharmony_ci len = conn_info->max_pkt_payload_len; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* If last packet add NCI_HFP_NO_CHAINING */ 1678c2ecf20Sopenharmony_ci if (i + conn_info->max_pkt_payload_len - 1688c2ecf20Sopenharmony_ci (skb->len + 1) >= data_len) { 1698c2ecf20Sopenharmony_ci cb |= NCI_HFP_NO_CHAINING; 1708c2ecf20Sopenharmony_ci len = data_len - i; 1718c2ecf20Sopenharmony_ci } else { 1728c2ecf20Sopenharmony_ci len = conn_info->max_pkt_payload_len - skb->len - 1; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci *(u8 *)skb_push(skb, 1) = cb; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (len > 0) 1788c2ecf20Sopenharmony_ci skb_put_data(skb, data + i, len); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci r = nci_send_data(ndev, conn_info->conn_id, skb); 1818c2ecf20Sopenharmony_ci if (r < 0) 1828c2ecf20Sopenharmony_ci return r; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci i += len; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (i < data_len) { 1878c2ecf20Sopenharmony_ci skb = nci_skb_alloc(ndev, 1888c2ecf20Sopenharmony_ci conn_info->max_pkt_payload_len + 1898c2ecf20Sopenharmony_ci NCI_DATA_HDR_SIZE, GFP_ATOMIC); 1908c2ecf20Sopenharmony_ci if (!skb) 1918c2ecf20Sopenharmony_ci return -ENOMEM; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci skb_reserve(skb, NCI_DATA_HDR_SIZE + 1); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci } while (i < data_len); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return i; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void nci_hci_send_data_req(struct nci_dev *ndev, unsigned long opt) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct nci_data *data = (struct nci_data *)opt; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci nci_hci_send_data(ndev, data->pipe, data->cmd, 2058c2ecf20Sopenharmony_ci data->data, data->data_len); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciint nci_hci_send_event(struct nci_dev *ndev, u8 gate, u8 event, 2098c2ecf20Sopenharmony_ci const u8 *param, size_t param_len) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci u8 pipe = ndev->hci_dev->gate2pipe[gate]; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 2148c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return nci_hci_send_data(ndev, pipe, 2178c2ecf20Sopenharmony_ci NCI_HCP_HEADER(NCI_HCI_HCP_EVENT, event), 2188c2ecf20Sopenharmony_ci param, param_len); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_hci_send_event); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciint nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd, 2238c2ecf20Sopenharmony_ci const u8 *param, size_t param_len, 2248c2ecf20Sopenharmony_ci struct sk_buff **skb) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct nci_hcp_message *message; 2278c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 2288c2ecf20Sopenharmony_ci struct nci_data data; 2298c2ecf20Sopenharmony_ci int r; 2308c2ecf20Sopenharmony_ci u8 pipe = ndev->hci_dev->gate2pipe[gate]; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 2338c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 2368c2ecf20Sopenharmony_ci if (!conn_info) 2378c2ecf20Sopenharmony_ci return -EPROTO; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci data.conn_id = conn_info->conn_id; 2408c2ecf20Sopenharmony_ci data.pipe = pipe; 2418c2ecf20Sopenharmony_ci data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, cmd); 2428c2ecf20Sopenharmony_ci data.data = param; 2438c2ecf20Sopenharmony_ci data.data_len = param_len; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data, 2468c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_DATA_TIMEOUT)); 2478c2ecf20Sopenharmony_ci if (r == NCI_STATUS_OK) { 2488c2ecf20Sopenharmony_ci message = (struct nci_hcp_message *)conn_info->rx_skb->data; 2498c2ecf20Sopenharmony_ci r = nci_hci_result_to_errno( 2508c2ecf20Sopenharmony_ci NCI_HCP_MSG_GET_CMD(message->header)); 2518c2ecf20Sopenharmony_ci skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!r && skb) 2548c2ecf20Sopenharmony_ci *skb = conn_info->rx_skb; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return r; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_hci_send_cmd); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ciint nci_hci_clear_all_pipes(struct nci_dev *ndev) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci int r; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci r = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE, 2668c2ecf20Sopenharmony_ci NCI_HCI_ADM_CLEAR_ALL_PIPE, NULL, 0, NULL); 2678c2ecf20Sopenharmony_ci if (r < 0) 2688c2ecf20Sopenharmony_ci return r; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci nci_hci_reset_pipes(ndev->hci_dev); 2718c2ecf20Sopenharmony_ci return r; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_hci_clear_all_pipes); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic void nci_hci_event_received(struct nci_dev *ndev, u8 pipe, 2768c2ecf20Sopenharmony_ci u8 event, struct sk_buff *skb) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci if (ndev->ops->hci_event_received) 2798c2ecf20Sopenharmony_ci ndev->ops->hci_event_received(ndev, pipe, event, skb); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic void nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, 2838c2ecf20Sopenharmony_ci u8 cmd, struct sk_buff *skb) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci u8 gate = ndev->hci_dev->pipes[pipe].gate; 2868c2ecf20Sopenharmony_ci u8 status = NCI_HCI_ANY_OK | ~NCI_HCI_FRAGMENT; 2878c2ecf20Sopenharmony_ci u8 dest_gate, new_pipe; 2888c2ecf20Sopenharmony_ci struct nci_hci_create_pipe_resp *create_info; 2898c2ecf20Sopenharmony_ci struct nci_hci_delete_pipe_noti *delete_info; 2908c2ecf20Sopenharmony_ci struct nci_hci_all_pipe_cleared_noti *cleared_info; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci switch (cmd) { 2958c2ecf20Sopenharmony_ci case NCI_HCI_ADM_NOTIFY_PIPE_CREATED: 2968c2ecf20Sopenharmony_ci if (skb->len != 5) { 2978c2ecf20Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 2988c2ecf20Sopenharmony_ci goto exit; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci create_info = (struct nci_hci_create_pipe_resp *)skb->data; 3018c2ecf20Sopenharmony_ci dest_gate = create_info->dest_gate; 3028c2ecf20Sopenharmony_ci new_pipe = create_info->pipe; 3038c2ecf20Sopenharmony_ci if (new_pipe >= NCI_HCI_MAX_PIPES) { 3048c2ecf20Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 3058c2ecf20Sopenharmony_ci goto exit; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Save the new created pipe and bind with local gate, 3098c2ecf20Sopenharmony_ci * the description for skb->data[3] is destination gate id 3108c2ecf20Sopenharmony_ci * but since we received this cmd from host controller, we 3118c2ecf20Sopenharmony_ci * are the destination and it is our local gate 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci ndev->hci_dev->gate2pipe[dest_gate] = new_pipe; 3148c2ecf20Sopenharmony_ci ndev->hci_dev->pipes[new_pipe].gate = dest_gate; 3158c2ecf20Sopenharmony_ci ndev->hci_dev->pipes[new_pipe].host = 3168c2ecf20Sopenharmony_ci create_info->src_host; 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci case NCI_HCI_ANY_OPEN_PIPE: 3198c2ecf20Sopenharmony_ci /* If the pipe is not created report an error */ 3208c2ecf20Sopenharmony_ci if (gate == NCI_HCI_INVALID_GATE) { 3218c2ecf20Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 3228c2ecf20Sopenharmony_ci goto exit; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci case NCI_HCI_ADM_NOTIFY_PIPE_DELETED: 3268c2ecf20Sopenharmony_ci if (skb->len != 1) { 3278c2ecf20Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 3288c2ecf20Sopenharmony_ci goto exit; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci delete_info = (struct nci_hci_delete_pipe_noti *)skb->data; 3318c2ecf20Sopenharmony_ci if (delete_info->pipe >= NCI_HCI_MAX_PIPES) { 3328c2ecf20Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 3338c2ecf20Sopenharmony_ci goto exit; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ndev->hci_dev->pipes[delete_info->pipe].gate = 3378c2ecf20Sopenharmony_ci NCI_HCI_INVALID_GATE; 3388c2ecf20Sopenharmony_ci ndev->hci_dev->pipes[delete_info->pipe].host = 3398c2ecf20Sopenharmony_ci NCI_HCI_INVALID_HOST; 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci case NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED: 3428c2ecf20Sopenharmony_ci if (skb->len != 1) { 3438c2ecf20Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 3448c2ecf20Sopenharmony_ci goto exit; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci cleared_info = 3488c2ecf20Sopenharmony_ci (struct nci_hci_all_pipe_cleared_noti *)skb->data; 3498c2ecf20Sopenharmony_ci nci_hci_reset_pipes_per_host(ndev, cleared_info->host); 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci default: 3528c2ecf20Sopenharmony_ci pr_debug("Discarded unknown cmd %x to gate %x\n", cmd, gate); 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (ndev->ops->hci_cmd_received) 3578c2ecf20Sopenharmony_ci ndev->ops->hci_cmd_received(ndev, pipe, cmd, skb); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ciexit: 3608c2ecf20Sopenharmony_ci nci_hci_send_data(ndev, pipe, status, NULL, 0); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci kfree_skb(skb); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe, 3668c2ecf20Sopenharmony_ci u8 result, struct sk_buff *skb) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 3698c2ecf20Sopenharmony_ci u8 status = result; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 3728c2ecf20Sopenharmony_ci if (!conn_info) { 3738c2ecf20Sopenharmony_ci status = NCI_STATUS_REJECTED; 3748c2ecf20Sopenharmony_ci goto exit; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci conn_info->rx_skb = skb; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ciexit: 3808c2ecf20Sopenharmony_ci nci_req_complete(ndev, NCI_STATUS_OK); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* Receive hcp message for pipe, with type and cmd. 3848c2ecf20Sopenharmony_ci * skb contains optional message data only. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_cistatic void nci_hci_hcp_message_rx(struct nci_dev *ndev, u8 pipe, 3878c2ecf20Sopenharmony_ci u8 type, u8 instruction, struct sk_buff *skb) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci switch (type) { 3908c2ecf20Sopenharmony_ci case NCI_HCI_HCP_RESPONSE: 3918c2ecf20Sopenharmony_ci nci_hci_resp_received(ndev, pipe, instruction, skb); 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci case NCI_HCI_HCP_COMMAND: 3948c2ecf20Sopenharmony_ci nci_hci_cmd_received(ndev, pipe, instruction, skb); 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci case NCI_HCI_HCP_EVENT: 3978c2ecf20Sopenharmony_ci nci_hci_event_received(ndev, pipe, instruction, skb); 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci default: 4008c2ecf20Sopenharmony_ci pr_err("UNKNOWN MSG Type %d, instruction=%d\n", 4018c2ecf20Sopenharmony_ci type, instruction); 4028c2ecf20Sopenharmony_ci kfree_skb(skb); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci nci_req_complete(ndev, NCI_STATUS_OK); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic void nci_hci_msg_rx_work(struct work_struct *work) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct nci_hci_dev *hdev = 4128c2ecf20Sopenharmony_ci container_of(work, struct nci_hci_dev, msg_rx_work); 4138c2ecf20Sopenharmony_ci struct sk_buff *skb; 4148c2ecf20Sopenharmony_ci struct nci_hcp_message *message; 4158c2ecf20Sopenharmony_ci u8 pipe, type, instruction; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&hdev->msg_rx_queue)) != NULL) { 4188c2ecf20Sopenharmony_ci pipe = NCI_HCP_MSG_GET_PIPE(skb->data[0]); 4198c2ecf20Sopenharmony_ci skb_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN); 4208c2ecf20Sopenharmony_ci message = (struct nci_hcp_message *)skb->data; 4218c2ecf20Sopenharmony_ci type = NCI_HCP_MSG_GET_TYPE(message->header); 4228c2ecf20Sopenharmony_ci instruction = NCI_HCP_MSG_GET_CMD(message->header); 4238c2ecf20Sopenharmony_ci skb_pull(skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci nci_hci_hcp_message_rx(hdev->ndev, pipe, 4268c2ecf20Sopenharmony_ci type, instruction, skb); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_civoid nci_hci_data_received_cb(void *context, 4318c2ecf20Sopenharmony_ci struct sk_buff *skb, int err) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct nci_dev *ndev = (struct nci_dev *)context; 4348c2ecf20Sopenharmony_ci struct nci_hcp_packet *packet; 4358c2ecf20Sopenharmony_ci u8 pipe, type; 4368c2ecf20Sopenharmony_ci struct sk_buff *hcp_skb; 4378c2ecf20Sopenharmony_ci struct sk_buff *frag_skb; 4388c2ecf20Sopenharmony_ci int msg_len; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci pr_debug("\n"); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (err) { 4438c2ecf20Sopenharmony_ci nci_req_complete(ndev, err); 4448c2ecf20Sopenharmony_ci return; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci packet = (struct nci_hcp_packet *)skb->data; 4488c2ecf20Sopenharmony_ci if ((packet->header & ~NCI_HCI_FRAGMENT) == 0) { 4498c2ecf20Sopenharmony_ci skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb); 4508c2ecf20Sopenharmony_ci return; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* it's the last fragment. Does it need re-aggregation? */ 4548c2ecf20Sopenharmony_ci if (skb_queue_len(&ndev->hci_dev->rx_hcp_frags)) { 4558c2ecf20Sopenharmony_ci pipe = NCI_HCP_MSG_GET_PIPE(packet->header); 4568c2ecf20Sopenharmony_ci skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci msg_len = 0; 4598c2ecf20Sopenharmony_ci skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) { 4608c2ecf20Sopenharmony_ci msg_len += (frag_skb->len - 4618c2ecf20Sopenharmony_ci NCI_HCI_HCP_PACKET_HEADER_LEN); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci hcp_skb = nfc_alloc_recv_skb(NCI_HCI_HCP_PACKET_HEADER_LEN + 4658c2ecf20Sopenharmony_ci msg_len, GFP_KERNEL); 4668c2ecf20Sopenharmony_ci if (!hcp_skb) { 4678c2ecf20Sopenharmony_ci nci_req_complete(ndev, -ENOMEM); 4688c2ecf20Sopenharmony_ci return; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci skb_put_u8(hcp_skb, pipe); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) { 4748c2ecf20Sopenharmony_ci msg_len = frag_skb->len - NCI_HCI_HCP_PACKET_HEADER_LEN; 4758c2ecf20Sopenharmony_ci skb_put_data(hcp_skb, 4768c2ecf20Sopenharmony_ci frag_skb->data + NCI_HCI_HCP_PACKET_HEADER_LEN, 4778c2ecf20Sopenharmony_ci msg_len); 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci skb_queue_purge(&ndev->hci_dev->rx_hcp_frags); 4818c2ecf20Sopenharmony_ci } else { 4828c2ecf20Sopenharmony_ci packet->header &= NCI_HCI_FRAGMENT; 4838c2ecf20Sopenharmony_ci hcp_skb = skb; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* if this is a response, dispatch immediately to 4878c2ecf20Sopenharmony_ci * unblock waiting cmd context. Otherwise, enqueue to dispatch 4888c2ecf20Sopenharmony_ci * in separate context where handler can also execute command. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci packet = (struct nci_hcp_packet *)hcp_skb->data; 4918c2ecf20Sopenharmony_ci type = NCI_HCP_MSG_GET_TYPE(packet->message.header); 4928c2ecf20Sopenharmony_ci if (type == NCI_HCI_HCP_RESPONSE) { 4938c2ecf20Sopenharmony_ci pipe = NCI_HCP_MSG_GET_PIPE(packet->header); 4948c2ecf20Sopenharmony_ci skb_pull(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN); 4958c2ecf20Sopenharmony_ci nci_hci_hcp_message_rx(ndev, pipe, type, 4968c2ecf20Sopenharmony_ci NCI_STATUS_OK, hcp_skb); 4978c2ecf20Sopenharmony_ci } else { 4988c2ecf20Sopenharmony_ci skb_queue_tail(&ndev->hci_dev->msg_rx_queue, hcp_skb); 4998c2ecf20Sopenharmony_ci schedule_work(&ndev->hci_dev->msg_rx_work); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ciint nci_hci_open_pipe(struct nci_dev *ndev, u8 pipe) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct nci_data data; 5068c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 5098c2ecf20Sopenharmony_ci if (!conn_info) 5108c2ecf20Sopenharmony_ci return -EPROTO; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci data.conn_id = conn_info->conn_id; 5138c2ecf20Sopenharmony_ci data.pipe = pipe; 5148c2ecf20Sopenharmony_ci data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, 5158c2ecf20Sopenharmony_ci NCI_HCI_ANY_OPEN_PIPE); 5168c2ecf20Sopenharmony_ci data.data = NULL; 5178c2ecf20Sopenharmony_ci data.data_len = 0; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return nci_request(ndev, nci_hci_send_data_req, 5208c2ecf20Sopenharmony_ci (unsigned long)&data, 5218c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_DATA_TIMEOUT)); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_hci_open_pipe); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic u8 nci_hci_create_pipe(struct nci_dev *ndev, u8 dest_host, 5268c2ecf20Sopenharmony_ci u8 dest_gate, int *result) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci u8 pipe; 5298c2ecf20Sopenharmony_ci struct sk_buff *skb; 5308c2ecf20Sopenharmony_ci struct nci_hci_create_pipe_params params; 5318c2ecf20Sopenharmony_ci struct nci_hci_create_pipe_resp *resp; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci pr_debug("gate=%d\n", dest_gate); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci params.src_gate = NCI_HCI_ADMIN_GATE; 5368c2ecf20Sopenharmony_ci params.dest_host = dest_host; 5378c2ecf20Sopenharmony_ci params.dest_gate = dest_gate; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci *result = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE, 5408c2ecf20Sopenharmony_ci NCI_HCI_ADM_CREATE_PIPE, 5418c2ecf20Sopenharmony_ci (u8 *)¶ms, sizeof(params), &skb); 5428c2ecf20Sopenharmony_ci if (*result < 0) 5438c2ecf20Sopenharmony_ci return NCI_HCI_INVALID_PIPE; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci resp = (struct nci_hci_create_pipe_resp *)skb->data; 5468c2ecf20Sopenharmony_ci pipe = resp->pipe; 5478c2ecf20Sopenharmony_ci kfree_skb(skb); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci pr_debug("pipe created=%d\n", pipe); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return pipe; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int nci_hci_delete_pipe(struct nci_dev *ndev, u8 pipe) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci pr_debug("\n"); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE, 5598c2ecf20Sopenharmony_ci NCI_HCI_ADM_DELETE_PIPE, &pipe, 1, NULL); 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ciint nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx, 5638c2ecf20Sopenharmony_ci const u8 *param, size_t param_len) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct nci_hcp_message *message; 5668c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 5678c2ecf20Sopenharmony_ci struct nci_data data; 5688c2ecf20Sopenharmony_ci int r; 5698c2ecf20Sopenharmony_ci u8 *tmp; 5708c2ecf20Sopenharmony_ci u8 pipe = ndev->hci_dev->gate2pipe[gate]; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci pr_debug("idx=%d to gate %d\n", idx, gate); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 5758c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 5788c2ecf20Sopenharmony_ci if (!conn_info) 5798c2ecf20Sopenharmony_ci return -EPROTO; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci tmp = kmalloc(1 + param_len, GFP_KERNEL); 5828c2ecf20Sopenharmony_ci if (!tmp) 5838c2ecf20Sopenharmony_ci return -ENOMEM; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci *tmp = idx; 5868c2ecf20Sopenharmony_ci memcpy(tmp + 1, param, param_len); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci data.conn_id = conn_info->conn_id; 5898c2ecf20Sopenharmony_ci data.pipe = pipe; 5908c2ecf20Sopenharmony_ci data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, 5918c2ecf20Sopenharmony_ci NCI_HCI_ANY_SET_PARAMETER); 5928c2ecf20Sopenharmony_ci data.data = tmp; 5938c2ecf20Sopenharmony_ci data.data_len = param_len + 1; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci r = nci_request(ndev, nci_hci_send_data_req, 5968c2ecf20Sopenharmony_ci (unsigned long)&data, 5978c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_DATA_TIMEOUT)); 5988c2ecf20Sopenharmony_ci if (r == NCI_STATUS_OK) { 5998c2ecf20Sopenharmony_ci message = (struct nci_hcp_message *)conn_info->rx_skb->data; 6008c2ecf20Sopenharmony_ci r = nci_hci_result_to_errno( 6018c2ecf20Sopenharmony_ci NCI_HCP_MSG_GET_CMD(message->header)); 6028c2ecf20Sopenharmony_ci skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci kfree(tmp); 6068c2ecf20Sopenharmony_ci return r; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_hci_set_param); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ciint nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx, 6118c2ecf20Sopenharmony_ci struct sk_buff **skb) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct nci_hcp_message *message; 6148c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 6158c2ecf20Sopenharmony_ci struct nci_data data; 6168c2ecf20Sopenharmony_ci int r; 6178c2ecf20Sopenharmony_ci u8 pipe = ndev->hci_dev->gate2pipe[gate]; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci pr_debug("idx=%d to gate %d\n", idx, gate); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 6228c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 6258c2ecf20Sopenharmony_ci if (!conn_info) 6268c2ecf20Sopenharmony_ci return -EPROTO; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci data.conn_id = conn_info->conn_id; 6298c2ecf20Sopenharmony_ci data.pipe = pipe; 6308c2ecf20Sopenharmony_ci data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, 6318c2ecf20Sopenharmony_ci NCI_HCI_ANY_GET_PARAMETER); 6328c2ecf20Sopenharmony_ci data.data = &idx; 6338c2ecf20Sopenharmony_ci data.data_len = 1; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data, 6368c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_DATA_TIMEOUT)); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (r == NCI_STATUS_OK) { 6398c2ecf20Sopenharmony_ci message = (struct nci_hcp_message *)conn_info->rx_skb->data; 6408c2ecf20Sopenharmony_ci r = nci_hci_result_to_errno( 6418c2ecf20Sopenharmony_ci NCI_HCP_MSG_GET_CMD(message->header)); 6428c2ecf20Sopenharmony_ci skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (!r && skb) 6458c2ecf20Sopenharmony_ci *skb = conn_info->rx_skb; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci return r; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_hci_get_param); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ciint nci_hci_connect_gate(struct nci_dev *ndev, 6538c2ecf20Sopenharmony_ci u8 dest_host, u8 dest_gate, u8 pipe) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci bool pipe_created = false; 6568c2ecf20Sopenharmony_ci int r; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (pipe == NCI_HCI_DO_NOT_OPEN_PIPE) 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (ndev->hci_dev->gate2pipe[dest_gate] != NCI_HCI_INVALID_PIPE) 6628c2ecf20Sopenharmony_ci return -EADDRINUSE; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (pipe != NCI_HCI_INVALID_PIPE) 6658c2ecf20Sopenharmony_ci goto open_pipe; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci switch (dest_gate) { 6688c2ecf20Sopenharmony_ci case NCI_HCI_LINK_MGMT_GATE: 6698c2ecf20Sopenharmony_ci pipe = NCI_HCI_LINK_MGMT_PIPE; 6708c2ecf20Sopenharmony_ci break; 6718c2ecf20Sopenharmony_ci case NCI_HCI_ADMIN_GATE: 6728c2ecf20Sopenharmony_ci pipe = NCI_HCI_ADMIN_PIPE; 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci default: 6758c2ecf20Sopenharmony_ci pipe = nci_hci_create_pipe(ndev, dest_host, dest_gate, &r); 6768c2ecf20Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 6778c2ecf20Sopenharmony_ci return r; 6788c2ecf20Sopenharmony_ci pipe_created = true; 6798c2ecf20Sopenharmony_ci break; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ciopen_pipe: 6838c2ecf20Sopenharmony_ci r = nci_hci_open_pipe(ndev, pipe); 6848c2ecf20Sopenharmony_ci if (r < 0) { 6858c2ecf20Sopenharmony_ci if (pipe_created) { 6868c2ecf20Sopenharmony_ci if (nci_hci_delete_pipe(ndev, pipe) < 0) { 6878c2ecf20Sopenharmony_ci /* TODO: Cannot clean by deleting pipe... 6888c2ecf20Sopenharmony_ci * -> inconsistent state 6898c2ecf20Sopenharmony_ci */ 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci return r; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci ndev->hci_dev->pipes[pipe].gate = dest_gate; 6968c2ecf20Sopenharmony_ci ndev->hci_dev->pipes[pipe].host = dest_host; 6978c2ecf20Sopenharmony_ci ndev->hci_dev->gate2pipe[dest_gate] = pipe; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return 0; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_hci_connect_gate); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic int nci_hci_dev_connect_gates(struct nci_dev *ndev, 7048c2ecf20Sopenharmony_ci u8 gate_count, 7058c2ecf20Sopenharmony_ci struct nci_hci_gate *gates) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci int r; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci while (gate_count--) { 7108c2ecf20Sopenharmony_ci r = nci_hci_connect_gate(ndev, gates->dest_host, 7118c2ecf20Sopenharmony_ci gates->gate, gates->pipe); 7128c2ecf20Sopenharmony_ci if (r < 0) 7138c2ecf20Sopenharmony_ci return r; 7148c2ecf20Sopenharmony_ci gates++; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return 0; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ciint nci_hci_dev_session_init(struct nci_dev *ndev) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 7238c2ecf20Sopenharmony_ci struct sk_buff *skb; 7248c2ecf20Sopenharmony_ci int r; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci ndev->hci_dev->count_pipes = 0; 7278c2ecf20Sopenharmony_ci ndev->hci_dev->expected_pipes = 0; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 7308c2ecf20Sopenharmony_ci if (!conn_info) 7318c2ecf20Sopenharmony_ci return -EPROTO; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci conn_info->data_exchange_cb = nci_hci_data_received_cb; 7348c2ecf20Sopenharmony_ci conn_info->data_exchange_cb_context = ndev; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci nci_hci_reset_pipes(ndev->hci_dev); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (ndev->hci_dev->init_data.gates[0].gate != NCI_HCI_ADMIN_GATE) 7398c2ecf20Sopenharmony_ci return -EPROTO; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci r = nci_hci_connect_gate(ndev, 7428c2ecf20Sopenharmony_ci ndev->hci_dev->init_data.gates[0].dest_host, 7438c2ecf20Sopenharmony_ci ndev->hci_dev->init_data.gates[0].gate, 7448c2ecf20Sopenharmony_ci ndev->hci_dev->init_data.gates[0].pipe); 7458c2ecf20Sopenharmony_ci if (r < 0) 7468c2ecf20Sopenharmony_ci return r; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE, 7498c2ecf20Sopenharmony_ci NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, &skb); 7508c2ecf20Sopenharmony_ci if (r < 0) 7518c2ecf20Sopenharmony_ci return r; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (skb->len && 7548c2ecf20Sopenharmony_ci skb->len == strlen(ndev->hci_dev->init_data.session_id) && 7558c2ecf20Sopenharmony_ci !memcmp(ndev->hci_dev->init_data.session_id, skb->data, skb->len) && 7568c2ecf20Sopenharmony_ci ndev->ops->hci_load_session) { 7578c2ecf20Sopenharmony_ci /* Restore gate<->pipe table from some proprietary location. */ 7588c2ecf20Sopenharmony_ci r = ndev->ops->hci_load_session(ndev); 7598c2ecf20Sopenharmony_ci } else { 7608c2ecf20Sopenharmony_ci r = nci_hci_clear_all_pipes(ndev); 7618c2ecf20Sopenharmony_ci if (r < 0) 7628c2ecf20Sopenharmony_ci goto exit; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci r = nci_hci_dev_connect_gates(ndev, 7658c2ecf20Sopenharmony_ci ndev->hci_dev->init_data.gate_count, 7668c2ecf20Sopenharmony_ci ndev->hci_dev->init_data.gates); 7678c2ecf20Sopenharmony_ci if (r < 0) 7688c2ecf20Sopenharmony_ci goto exit; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE, 7718c2ecf20Sopenharmony_ci NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, 7728c2ecf20Sopenharmony_ci ndev->hci_dev->init_data.session_id, 7738c2ecf20Sopenharmony_ci strlen(ndev->hci_dev->init_data.session_id)); 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ciexit: 7778c2ecf20Sopenharmony_ci kfree_skb(skb); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci return r; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_hci_dev_session_init); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistruct nci_hci_dev *nci_hci_allocate(struct nci_dev *ndev) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct nci_hci_dev *hdev; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); 7888c2ecf20Sopenharmony_ci if (!hdev) 7898c2ecf20Sopenharmony_ci return NULL; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci skb_queue_head_init(&hdev->rx_hcp_frags); 7928c2ecf20Sopenharmony_ci INIT_WORK(&hdev->msg_rx_work, nci_hci_msg_rx_work); 7938c2ecf20Sopenharmony_ci skb_queue_head_init(&hdev->msg_rx_queue); 7948c2ecf20Sopenharmony_ci hdev->ndev = ndev; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return hdev; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_civoid nci_hci_deallocate(struct nci_dev *ndev) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci kfree(ndev->hci_dev); 8028c2ecf20Sopenharmony_ci} 803