162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * The NFC Controller Interface is the communication protocol between an 462306a36Sopenharmony_ci * NFC Controller (NFCC) and a Device Host (DH). 562306a36Sopenharmony_ci * This is the HCI over NCI implementation, as specified in the 10.2 662306a36Sopenharmony_ci * section of the NCI 1.1 specification. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/skbuff.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "../nfc.h" 1462306a36Sopenharmony_ci#include <net/nfc/nci.h> 1562306a36Sopenharmony_ci#include <net/nfc/nci_core.h> 1662306a36Sopenharmony_ci#include <linux/nfc.h> 1762306a36Sopenharmony_ci#include <linux/kcov.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct nci_data { 2062306a36Sopenharmony_ci u8 conn_id; 2162306a36Sopenharmony_ci u8 pipe; 2262306a36Sopenharmony_ci u8 cmd; 2362306a36Sopenharmony_ci const u8 *data; 2462306a36Sopenharmony_ci u32 data_len; 2562306a36Sopenharmony_ci} __packed; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct nci_hci_create_pipe_params { 2862306a36Sopenharmony_ci u8 src_gate; 2962306a36Sopenharmony_ci u8 dest_host; 3062306a36Sopenharmony_ci u8 dest_gate; 3162306a36Sopenharmony_ci} __packed; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct nci_hci_create_pipe_resp { 3462306a36Sopenharmony_ci u8 src_host; 3562306a36Sopenharmony_ci u8 src_gate; 3662306a36Sopenharmony_ci u8 dest_host; 3762306a36Sopenharmony_ci u8 dest_gate; 3862306a36Sopenharmony_ci u8 pipe; 3962306a36Sopenharmony_ci} __packed; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct nci_hci_delete_pipe_noti { 4262306a36Sopenharmony_ci u8 pipe; 4362306a36Sopenharmony_ci} __packed; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct nci_hci_all_pipe_cleared_noti { 4662306a36Sopenharmony_ci u8 host; 4762306a36Sopenharmony_ci} __packed; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct nci_hcp_message { 5062306a36Sopenharmony_ci u8 header; /* type -cmd,evt,rsp- + instruction */ 5162306a36Sopenharmony_ci u8 data[]; 5262306a36Sopenharmony_ci} __packed; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct nci_hcp_packet { 5562306a36Sopenharmony_ci u8 header; /* cbit+pipe */ 5662306a36Sopenharmony_ci struct nci_hcp_message message; 5762306a36Sopenharmony_ci} __packed; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define NCI_HCI_ANY_SET_PARAMETER 0x01 6062306a36Sopenharmony_ci#define NCI_HCI_ANY_GET_PARAMETER 0x02 6162306a36Sopenharmony_ci#define NCI_HCI_ANY_CLOSE_PIPE 0x04 6262306a36Sopenharmony_ci#define NCI_HCI_ADM_CLEAR_ALL_PIPE 0x14 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define NCI_HFP_NO_CHAINING 0x80 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define NCI_NFCEE_ID_HCI 0x80 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define NCI_EVT_HOT_PLUG 0x03 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01 7162306a36Sopenharmony_ci#define NCI_HCI_ADM_CREATE_PIPE 0x10 7262306a36Sopenharmony_ci#define NCI_HCI_ADM_DELETE_PIPE 0x11 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* HCP headers */ 7562306a36Sopenharmony_ci#define NCI_HCI_HCP_PACKET_HEADER_LEN 1 7662306a36Sopenharmony_ci#define NCI_HCI_HCP_MESSAGE_HEADER_LEN 1 7762306a36Sopenharmony_ci#define NCI_HCI_HCP_HEADER_LEN 2 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* HCP types */ 8062306a36Sopenharmony_ci#define NCI_HCI_HCP_COMMAND 0x00 8162306a36Sopenharmony_ci#define NCI_HCI_HCP_EVENT 0x01 8262306a36Sopenharmony_ci#define NCI_HCI_HCP_RESPONSE 0x02 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define NCI_HCI_ADM_NOTIFY_PIPE_CREATED 0x12 8562306a36Sopenharmony_ci#define NCI_HCI_ADM_NOTIFY_PIPE_DELETED 0x13 8662306a36Sopenharmony_ci#define NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED 0x15 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define NCI_HCI_FRAGMENT 0x7f 8962306a36Sopenharmony_ci#define NCI_HCP_HEADER(type, instr) ((((type) & 0x03) << 6) |\ 9062306a36Sopenharmony_ci ((instr) & 0x3f)) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define NCI_HCP_MSG_GET_TYPE(header) ((header & 0xc0) >> 6) 9362306a36Sopenharmony_ci#define NCI_HCP_MSG_GET_CMD(header) (header & 0x3f) 9462306a36Sopenharmony_ci#define NCI_HCP_MSG_GET_PIPE(header) (header & 0x7f) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int nci_hci_result_to_errno(u8 result) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci switch (result) { 9962306a36Sopenharmony_ci case NCI_HCI_ANY_OK: 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci case NCI_HCI_ANY_E_REG_PAR_UNKNOWN: 10262306a36Sopenharmony_ci return -EOPNOTSUPP; 10362306a36Sopenharmony_ci case NCI_HCI_ANY_E_TIMEOUT: 10462306a36Sopenharmony_ci return -ETIME; 10562306a36Sopenharmony_ci default: 10662306a36Sopenharmony_ci return -1; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* HCI core */ 11162306a36Sopenharmony_cistatic void nci_hci_reset_pipes(struct nci_hci_dev *hdev) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci int i; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci for (i = 0; i < NCI_HCI_MAX_PIPES; i++) { 11662306a36Sopenharmony_ci hdev->pipes[i].gate = NCI_HCI_INVALID_GATE; 11762306a36Sopenharmony_ci hdev->pipes[i].host = NCI_HCI_INVALID_HOST; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci memset(hdev->gate2pipe, NCI_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe)); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void nci_hci_reset_pipes_per_host(struct nci_dev *ndev, u8 host) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int i; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci for (i = 0; i < NCI_HCI_MAX_PIPES; i++) { 12762306a36Sopenharmony_ci if (ndev->hci_dev->pipes[i].host == host) { 12862306a36Sopenharmony_ci ndev->hci_dev->pipes[i].gate = NCI_HCI_INVALID_GATE; 12962306a36Sopenharmony_ci ndev->hci_dev->pipes[i].host = NCI_HCI_INVALID_HOST; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* Fragment HCI data over NCI packet. 13562306a36Sopenharmony_ci * NFC Forum NCI 10.2.2 Data Exchange: 13662306a36Sopenharmony_ci * The payload of the Data Packets sent on the Logical Connection SHALL be 13762306a36Sopenharmony_ci * valid HCP packets, as defined within [ETSI_102622]. Each Data Packet SHALL 13862306a36Sopenharmony_ci * contain a single HCP packet. NCI Segmentation and Reassembly SHALL NOT be 13962306a36Sopenharmony_ci * applied to Data Messages in either direction. The HCI fragmentation mechanism 14062306a36Sopenharmony_ci * is used if required. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_cistatic int nci_hci_send_data(struct nci_dev *ndev, u8 pipe, 14362306a36Sopenharmony_ci const u8 data_type, const u8 *data, 14462306a36Sopenharmony_ci size_t data_len) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci const struct nci_conn_info *conn_info; 14762306a36Sopenharmony_ci struct sk_buff *skb; 14862306a36Sopenharmony_ci int len, i, r; 14962306a36Sopenharmony_ci u8 cb = pipe; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 15262306a36Sopenharmony_ci if (!conn_info) 15362306a36Sopenharmony_ci return -EPROTO; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci i = 0; 15662306a36Sopenharmony_ci skb = nci_skb_alloc(ndev, conn_info->max_pkt_payload_len + 15762306a36Sopenharmony_ci NCI_DATA_HDR_SIZE, GFP_ATOMIC); 15862306a36Sopenharmony_ci if (!skb) 15962306a36Sopenharmony_ci return -ENOMEM; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci skb_reserve(skb, NCI_DATA_HDR_SIZE + 2); 16262306a36Sopenharmony_ci *(u8 *)skb_push(skb, 1) = data_type; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci do { 16562306a36Sopenharmony_ci /* If last packet add NCI_HFP_NO_CHAINING */ 16662306a36Sopenharmony_ci if (i + conn_info->max_pkt_payload_len - 16762306a36Sopenharmony_ci (skb->len + 1) >= data_len) { 16862306a36Sopenharmony_ci cb |= NCI_HFP_NO_CHAINING; 16962306a36Sopenharmony_ci len = data_len - i; 17062306a36Sopenharmony_ci } else { 17162306a36Sopenharmony_ci len = conn_info->max_pkt_payload_len - skb->len - 1; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci *(u8 *)skb_push(skb, 1) = cb; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (len > 0) 17762306a36Sopenharmony_ci skb_put_data(skb, data + i, len); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci r = nci_send_data(ndev, conn_info->conn_id, skb); 18062306a36Sopenharmony_ci if (r < 0) 18162306a36Sopenharmony_ci return r; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci i += len; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (i < data_len) { 18662306a36Sopenharmony_ci skb = nci_skb_alloc(ndev, 18762306a36Sopenharmony_ci conn_info->max_pkt_payload_len + 18862306a36Sopenharmony_ci NCI_DATA_HDR_SIZE, GFP_ATOMIC); 18962306a36Sopenharmony_ci if (!skb) 19062306a36Sopenharmony_ci return -ENOMEM; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci skb_reserve(skb, NCI_DATA_HDR_SIZE + 1); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci } while (i < data_len); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return i; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void nci_hci_send_data_req(struct nci_dev *ndev, const void *opt) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci const struct nci_data *data = opt; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci nci_hci_send_data(ndev, data->pipe, data->cmd, 20462306a36Sopenharmony_ci data->data, data->data_len); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciint nci_hci_send_event(struct nci_dev *ndev, u8 gate, u8 event, 20862306a36Sopenharmony_ci const u8 *param, size_t param_len) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci u8 pipe = ndev->hci_dev->gate2pipe[gate]; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 21362306a36Sopenharmony_ci return -EADDRNOTAVAIL; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return nci_hci_send_data(ndev, pipe, 21662306a36Sopenharmony_ci NCI_HCP_HEADER(NCI_HCI_HCP_EVENT, event), 21762306a36Sopenharmony_ci param, param_len); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_send_event); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ciint nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd, 22262306a36Sopenharmony_ci const u8 *param, size_t param_len, 22362306a36Sopenharmony_ci struct sk_buff **skb) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci const struct nci_hcp_message *message; 22662306a36Sopenharmony_ci const struct nci_conn_info *conn_info; 22762306a36Sopenharmony_ci struct nci_data data; 22862306a36Sopenharmony_ci int r; 22962306a36Sopenharmony_ci u8 pipe = ndev->hci_dev->gate2pipe[gate]; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 23262306a36Sopenharmony_ci return -EADDRNOTAVAIL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 23562306a36Sopenharmony_ci if (!conn_info) 23662306a36Sopenharmony_ci return -EPROTO; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci data.conn_id = conn_info->conn_id; 23962306a36Sopenharmony_ci data.pipe = pipe; 24062306a36Sopenharmony_ci data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, cmd); 24162306a36Sopenharmony_ci data.data = param; 24262306a36Sopenharmony_ci data.data_len = param_len; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci r = nci_request(ndev, nci_hci_send_data_req, &data, 24562306a36Sopenharmony_ci msecs_to_jiffies(NCI_DATA_TIMEOUT)); 24662306a36Sopenharmony_ci if (r == NCI_STATUS_OK) { 24762306a36Sopenharmony_ci message = (struct nci_hcp_message *)conn_info->rx_skb->data; 24862306a36Sopenharmony_ci r = nci_hci_result_to_errno( 24962306a36Sopenharmony_ci NCI_HCP_MSG_GET_CMD(message->header)); 25062306a36Sopenharmony_ci skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (!r && skb) 25362306a36Sopenharmony_ci *skb = conn_info->rx_skb; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return r; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_send_cmd); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciint nci_hci_clear_all_pipes(struct nci_dev *ndev) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci int r; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci r = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE, 26562306a36Sopenharmony_ci NCI_HCI_ADM_CLEAR_ALL_PIPE, NULL, 0, NULL); 26662306a36Sopenharmony_ci if (r < 0) 26762306a36Sopenharmony_ci return r; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci nci_hci_reset_pipes(ndev->hci_dev); 27062306a36Sopenharmony_ci return r; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_clear_all_pipes); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void nci_hci_event_received(struct nci_dev *ndev, u8 pipe, 27562306a36Sopenharmony_ci u8 event, struct sk_buff *skb) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci if (ndev->ops->hci_event_received) 27862306a36Sopenharmony_ci ndev->ops->hci_event_received(ndev, pipe, event, skb); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, 28262306a36Sopenharmony_ci u8 cmd, struct sk_buff *skb) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci u8 gate = ndev->hci_dev->pipes[pipe].gate; 28562306a36Sopenharmony_ci u8 status = NCI_HCI_ANY_OK | ~NCI_HCI_FRAGMENT; 28662306a36Sopenharmony_ci u8 dest_gate, new_pipe; 28762306a36Sopenharmony_ci struct nci_hci_create_pipe_resp *create_info; 28862306a36Sopenharmony_ci struct nci_hci_delete_pipe_noti *delete_info; 28962306a36Sopenharmony_ci struct nci_hci_all_pipe_cleared_noti *cleared_info; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci switch (cmd) { 29462306a36Sopenharmony_ci case NCI_HCI_ADM_NOTIFY_PIPE_CREATED: 29562306a36Sopenharmony_ci if (skb->len != 5) { 29662306a36Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 29762306a36Sopenharmony_ci goto exit; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci create_info = (struct nci_hci_create_pipe_resp *)skb->data; 30062306a36Sopenharmony_ci dest_gate = create_info->dest_gate; 30162306a36Sopenharmony_ci new_pipe = create_info->pipe; 30262306a36Sopenharmony_ci if (new_pipe >= NCI_HCI_MAX_PIPES) { 30362306a36Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 30462306a36Sopenharmony_ci goto exit; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Save the new created pipe and bind with local gate, 30862306a36Sopenharmony_ci * the description for skb->data[3] is destination gate id 30962306a36Sopenharmony_ci * but since we received this cmd from host controller, we 31062306a36Sopenharmony_ci * are the destination and it is our local gate 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci ndev->hci_dev->gate2pipe[dest_gate] = new_pipe; 31362306a36Sopenharmony_ci ndev->hci_dev->pipes[new_pipe].gate = dest_gate; 31462306a36Sopenharmony_ci ndev->hci_dev->pipes[new_pipe].host = 31562306a36Sopenharmony_ci create_info->src_host; 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci case NCI_HCI_ANY_OPEN_PIPE: 31862306a36Sopenharmony_ci /* If the pipe is not created report an error */ 31962306a36Sopenharmony_ci if (gate == NCI_HCI_INVALID_GATE) { 32062306a36Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 32162306a36Sopenharmony_ci goto exit; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case NCI_HCI_ADM_NOTIFY_PIPE_DELETED: 32562306a36Sopenharmony_ci if (skb->len != 1) { 32662306a36Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 32762306a36Sopenharmony_ci goto exit; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci delete_info = (struct nci_hci_delete_pipe_noti *)skb->data; 33062306a36Sopenharmony_ci if (delete_info->pipe >= NCI_HCI_MAX_PIPES) { 33162306a36Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 33262306a36Sopenharmony_ci goto exit; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ndev->hci_dev->pipes[delete_info->pipe].gate = 33662306a36Sopenharmony_ci NCI_HCI_INVALID_GATE; 33762306a36Sopenharmony_ci ndev->hci_dev->pipes[delete_info->pipe].host = 33862306a36Sopenharmony_ci NCI_HCI_INVALID_HOST; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED: 34162306a36Sopenharmony_ci if (skb->len != 1) { 34262306a36Sopenharmony_ci status = NCI_HCI_ANY_E_NOK; 34362306a36Sopenharmony_ci goto exit; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci cleared_info = 34762306a36Sopenharmony_ci (struct nci_hci_all_pipe_cleared_noti *)skb->data; 34862306a36Sopenharmony_ci nci_hci_reset_pipes_per_host(ndev, cleared_info->host); 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci default: 35162306a36Sopenharmony_ci pr_debug("Discarded unknown cmd %x to gate %x\n", cmd, gate); 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (ndev->ops->hci_cmd_received) 35662306a36Sopenharmony_ci ndev->ops->hci_cmd_received(ndev, pipe, cmd, skb); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciexit: 35962306a36Sopenharmony_ci nci_hci_send_data(ndev, pipe, status, NULL, 0); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci kfree_skb(skb); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe, 36562306a36Sopenharmony_ci struct sk_buff *skb) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct nci_conn_info *conn_info; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 37062306a36Sopenharmony_ci if (!conn_info) 37162306a36Sopenharmony_ci goto exit; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci conn_info->rx_skb = skb; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciexit: 37662306a36Sopenharmony_ci nci_req_complete(ndev, NCI_STATUS_OK); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/* Receive hcp message for pipe, with type and cmd. 38062306a36Sopenharmony_ci * skb contains optional message data only. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_cistatic void nci_hci_hcp_message_rx(struct nci_dev *ndev, u8 pipe, 38362306a36Sopenharmony_ci u8 type, u8 instruction, struct sk_buff *skb) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci switch (type) { 38662306a36Sopenharmony_ci case NCI_HCI_HCP_RESPONSE: 38762306a36Sopenharmony_ci nci_hci_resp_received(ndev, pipe, skb); 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case NCI_HCI_HCP_COMMAND: 39062306a36Sopenharmony_ci nci_hci_cmd_received(ndev, pipe, instruction, skb); 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case NCI_HCI_HCP_EVENT: 39362306a36Sopenharmony_ci nci_hci_event_received(ndev, pipe, instruction, skb); 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci default: 39662306a36Sopenharmony_ci pr_err("UNKNOWN MSG Type %d, instruction=%d\n", 39762306a36Sopenharmony_ci type, instruction); 39862306a36Sopenharmony_ci kfree_skb(skb); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci nci_req_complete(ndev, NCI_STATUS_OK); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void nci_hci_msg_rx_work(struct work_struct *work) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct nci_hci_dev *hdev = 40862306a36Sopenharmony_ci container_of(work, struct nci_hci_dev, msg_rx_work); 40962306a36Sopenharmony_ci struct sk_buff *skb; 41062306a36Sopenharmony_ci const struct nci_hcp_message *message; 41162306a36Sopenharmony_ci u8 pipe, type, instruction; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci for (; (skb = skb_dequeue(&hdev->msg_rx_queue)); kcov_remote_stop()) { 41462306a36Sopenharmony_ci kcov_remote_start_common(skb_get_kcov_handle(skb)); 41562306a36Sopenharmony_ci pipe = NCI_HCP_MSG_GET_PIPE(skb->data[0]); 41662306a36Sopenharmony_ci skb_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN); 41762306a36Sopenharmony_ci message = (struct nci_hcp_message *)skb->data; 41862306a36Sopenharmony_ci type = NCI_HCP_MSG_GET_TYPE(message->header); 41962306a36Sopenharmony_ci instruction = NCI_HCP_MSG_GET_CMD(message->header); 42062306a36Sopenharmony_ci skb_pull(skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci nci_hci_hcp_message_rx(hdev->ndev, pipe, 42362306a36Sopenharmony_ci type, instruction, skb); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_civoid nci_hci_data_received_cb(void *context, 42862306a36Sopenharmony_ci struct sk_buff *skb, int err) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct nci_dev *ndev = (struct nci_dev *)context; 43162306a36Sopenharmony_ci struct nci_hcp_packet *packet; 43262306a36Sopenharmony_ci u8 pipe, type; 43362306a36Sopenharmony_ci struct sk_buff *hcp_skb; 43462306a36Sopenharmony_ci struct sk_buff *frag_skb; 43562306a36Sopenharmony_ci int msg_len; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (err) { 43862306a36Sopenharmony_ci nci_req_complete(ndev, err); 43962306a36Sopenharmony_ci return; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci packet = (struct nci_hcp_packet *)skb->data; 44362306a36Sopenharmony_ci if ((packet->header & ~NCI_HCI_FRAGMENT) == 0) { 44462306a36Sopenharmony_ci skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb); 44562306a36Sopenharmony_ci return; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* it's the last fragment. Does it need re-aggregation? */ 44962306a36Sopenharmony_ci if (skb_queue_len(&ndev->hci_dev->rx_hcp_frags)) { 45062306a36Sopenharmony_ci pipe = NCI_HCP_MSG_GET_PIPE(packet->header); 45162306a36Sopenharmony_ci skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci msg_len = 0; 45462306a36Sopenharmony_ci skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) { 45562306a36Sopenharmony_ci msg_len += (frag_skb->len - 45662306a36Sopenharmony_ci NCI_HCI_HCP_PACKET_HEADER_LEN); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci hcp_skb = nfc_alloc_recv_skb(NCI_HCI_HCP_PACKET_HEADER_LEN + 46062306a36Sopenharmony_ci msg_len, GFP_KERNEL); 46162306a36Sopenharmony_ci if (!hcp_skb) { 46262306a36Sopenharmony_ci nci_req_complete(ndev, -ENOMEM); 46362306a36Sopenharmony_ci return; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci skb_put_u8(hcp_skb, pipe); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) { 46962306a36Sopenharmony_ci msg_len = frag_skb->len - NCI_HCI_HCP_PACKET_HEADER_LEN; 47062306a36Sopenharmony_ci skb_put_data(hcp_skb, 47162306a36Sopenharmony_ci frag_skb->data + NCI_HCI_HCP_PACKET_HEADER_LEN, 47262306a36Sopenharmony_ci msg_len); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci skb_queue_purge(&ndev->hci_dev->rx_hcp_frags); 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci packet->header &= NCI_HCI_FRAGMENT; 47862306a36Sopenharmony_ci hcp_skb = skb; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* if this is a response, dispatch immediately to 48262306a36Sopenharmony_ci * unblock waiting cmd context. Otherwise, enqueue to dispatch 48362306a36Sopenharmony_ci * in separate context where handler can also execute command. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci packet = (struct nci_hcp_packet *)hcp_skb->data; 48662306a36Sopenharmony_ci type = NCI_HCP_MSG_GET_TYPE(packet->message.header); 48762306a36Sopenharmony_ci if (type == NCI_HCI_HCP_RESPONSE) { 48862306a36Sopenharmony_ci pipe = NCI_HCP_MSG_GET_PIPE(packet->header); 48962306a36Sopenharmony_ci skb_pull(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN); 49062306a36Sopenharmony_ci nci_hci_hcp_message_rx(ndev, pipe, type, 49162306a36Sopenharmony_ci NCI_STATUS_OK, hcp_skb); 49262306a36Sopenharmony_ci } else { 49362306a36Sopenharmony_ci skb_queue_tail(&ndev->hci_dev->msg_rx_queue, hcp_skb); 49462306a36Sopenharmony_ci schedule_work(&ndev->hci_dev->msg_rx_work); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ciint nci_hci_open_pipe(struct nci_dev *ndev, u8 pipe) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct nci_data data; 50162306a36Sopenharmony_ci const struct nci_conn_info *conn_info; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 50462306a36Sopenharmony_ci if (!conn_info) 50562306a36Sopenharmony_ci return -EPROTO; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci data.conn_id = conn_info->conn_id; 50862306a36Sopenharmony_ci data.pipe = pipe; 50962306a36Sopenharmony_ci data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, 51062306a36Sopenharmony_ci NCI_HCI_ANY_OPEN_PIPE); 51162306a36Sopenharmony_ci data.data = NULL; 51262306a36Sopenharmony_ci data.data_len = 0; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return nci_request(ndev, nci_hci_send_data_req, &data, 51562306a36Sopenharmony_ci msecs_to_jiffies(NCI_DATA_TIMEOUT)); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_open_pipe); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic u8 nci_hci_create_pipe(struct nci_dev *ndev, u8 dest_host, 52062306a36Sopenharmony_ci u8 dest_gate, int *result) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci u8 pipe; 52362306a36Sopenharmony_ci struct sk_buff *skb; 52462306a36Sopenharmony_ci struct nci_hci_create_pipe_params params; 52562306a36Sopenharmony_ci const struct nci_hci_create_pipe_resp *resp; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci pr_debug("gate=%d\n", dest_gate); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci params.src_gate = NCI_HCI_ADMIN_GATE; 53062306a36Sopenharmony_ci params.dest_host = dest_host; 53162306a36Sopenharmony_ci params.dest_gate = dest_gate; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci *result = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE, 53462306a36Sopenharmony_ci NCI_HCI_ADM_CREATE_PIPE, 53562306a36Sopenharmony_ci (u8 *)¶ms, sizeof(params), &skb); 53662306a36Sopenharmony_ci if (*result < 0) 53762306a36Sopenharmony_ci return NCI_HCI_INVALID_PIPE; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci resp = (struct nci_hci_create_pipe_resp *)skb->data; 54062306a36Sopenharmony_ci pipe = resp->pipe; 54162306a36Sopenharmony_ci kfree_skb(skb); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci pr_debug("pipe created=%d\n", pipe); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return pipe; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int nci_hci_delete_pipe(struct nci_dev *ndev, u8 pipe) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci return nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE, 55162306a36Sopenharmony_ci NCI_HCI_ADM_DELETE_PIPE, &pipe, 1, NULL); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ciint nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx, 55562306a36Sopenharmony_ci const u8 *param, size_t param_len) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci const struct nci_hcp_message *message; 55862306a36Sopenharmony_ci const struct nci_conn_info *conn_info; 55962306a36Sopenharmony_ci struct nci_data data; 56062306a36Sopenharmony_ci int r; 56162306a36Sopenharmony_ci u8 *tmp; 56262306a36Sopenharmony_ci u8 pipe = ndev->hci_dev->gate2pipe[gate]; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci pr_debug("idx=%d to gate %d\n", idx, gate); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 56762306a36Sopenharmony_ci return -EADDRNOTAVAIL; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 57062306a36Sopenharmony_ci if (!conn_info) 57162306a36Sopenharmony_ci return -EPROTO; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci tmp = kmalloc(1 + param_len, GFP_KERNEL); 57462306a36Sopenharmony_ci if (!tmp) 57562306a36Sopenharmony_ci return -ENOMEM; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci *tmp = idx; 57862306a36Sopenharmony_ci memcpy(tmp + 1, param, param_len); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci data.conn_id = conn_info->conn_id; 58162306a36Sopenharmony_ci data.pipe = pipe; 58262306a36Sopenharmony_ci data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, 58362306a36Sopenharmony_ci NCI_HCI_ANY_SET_PARAMETER); 58462306a36Sopenharmony_ci data.data = tmp; 58562306a36Sopenharmony_ci data.data_len = param_len + 1; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci r = nci_request(ndev, nci_hci_send_data_req, &data, 58862306a36Sopenharmony_ci msecs_to_jiffies(NCI_DATA_TIMEOUT)); 58962306a36Sopenharmony_ci if (r == NCI_STATUS_OK) { 59062306a36Sopenharmony_ci message = (struct nci_hcp_message *)conn_info->rx_skb->data; 59162306a36Sopenharmony_ci r = nci_hci_result_to_errno( 59262306a36Sopenharmony_ci NCI_HCP_MSG_GET_CMD(message->header)); 59362306a36Sopenharmony_ci skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci kfree(tmp); 59762306a36Sopenharmony_ci return r; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_set_param); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ciint nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx, 60262306a36Sopenharmony_ci struct sk_buff **skb) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci const struct nci_hcp_message *message; 60562306a36Sopenharmony_ci const struct nci_conn_info *conn_info; 60662306a36Sopenharmony_ci struct nci_data data; 60762306a36Sopenharmony_ci int r; 60862306a36Sopenharmony_ci u8 pipe = ndev->hci_dev->gate2pipe[gate]; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci pr_debug("idx=%d to gate %d\n", idx, gate); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 61362306a36Sopenharmony_ci return -EADDRNOTAVAIL; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 61662306a36Sopenharmony_ci if (!conn_info) 61762306a36Sopenharmony_ci return -EPROTO; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci data.conn_id = conn_info->conn_id; 62062306a36Sopenharmony_ci data.pipe = pipe; 62162306a36Sopenharmony_ci data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, 62262306a36Sopenharmony_ci NCI_HCI_ANY_GET_PARAMETER); 62362306a36Sopenharmony_ci data.data = &idx; 62462306a36Sopenharmony_ci data.data_len = 1; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci r = nci_request(ndev, nci_hci_send_data_req, &data, 62762306a36Sopenharmony_ci msecs_to_jiffies(NCI_DATA_TIMEOUT)); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (r == NCI_STATUS_OK) { 63062306a36Sopenharmony_ci message = (struct nci_hcp_message *)conn_info->rx_skb->data; 63162306a36Sopenharmony_ci r = nci_hci_result_to_errno( 63262306a36Sopenharmony_ci NCI_HCP_MSG_GET_CMD(message->header)); 63362306a36Sopenharmony_ci skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!r && skb) 63662306a36Sopenharmony_ci *skb = conn_info->rx_skb; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return r; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_get_param); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ciint nci_hci_connect_gate(struct nci_dev *ndev, 64462306a36Sopenharmony_ci u8 dest_host, u8 dest_gate, u8 pipe) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci bool pipe_created = false; 64762306a36Sopenharmony_ci int r; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (pipe == NCI_HCI_DO_NOT_OPEN_PIPE) 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (ndev->hci_dev->gate2pipe[dest_gate] != NCI_HCI_INVALID_PIPE) 65362306a36Sopenharmony_ci return -EADDRINUSE; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (pipe != NCI_HCI_INVALID_PIPE) 65662306a36Sopenharmony_ci goto open_pipe; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci switch (dest_gate) { 65962306a36Sopenharmony_ci case NCI_HCI_LINK_MGMT_GATE: 66062306a36Sopenharmony_ci pipe = NCI_HCI_LINK_MGMT_PIPE; 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci case NCI_HCI_ADMIN_GATE: 66362306a36Sopenharmony_ci pipe = NCI_HCI_ADMIN_PIPE; 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci default: 66662306a36Sopenharmony_ci pipe = nci_hci_create_pipe(ndev, dest_host, dest_gate, &r); 66762306a36Sopenharmony_ci if (pipe == NCI_HCI_INVALID_PIPE) 66862306a36Sopenharmony_ci return r; 66962306a36Sopenharmony_ci pipe_created = true; 67062306a36Sopenharmony_ci break; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ciopen_pipe: 67462306a36Sopenharmony_ci r = nci_hci_open_pipe(ndev, pipe); 67562306a36Sopenharmony_ci if (r < 0) { 67662306a36Sopenharmony_ci if (pipe_created) { 67762306a36Sopenharmony_ci if (nci_hci_delete_pipe(ndev, pipe) < 0) { 67862306a36Sopenharmony_ci /* TODO: Cannot clean by deleting pipe... 67962306a36Sopenharmony_ci * -> inconsistent state 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci return r; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci ndev->hci_dev->pipes[pipe].gate = dest_gate; 68762306a36Sopenharmony_ci ndev->hci_dev->pipes[pipe].host = dest_host; 68862306a36Sopenharmony_ci ndev->hci_dev->gate2pipe[dest_gate] = pipe; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_connect_gate); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int nci_hci_dev_connect_gates(struct nci_dev *ndev, 69562306a36Sopenharmony_ci u8 gate_count, 69662306a36Sopenharmony_ci const struct nci_hci_gate *gates) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci int r; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci while (gate_count--) { 70162306a36Sopenharmony_ci r = nci_hci_connect_gate(ndev, gates->dest_host, 70262306a36Sopenharmony_ci gates->gate, gates->pipe); 70362306a36Sopenharmony_ci if (r < 0) 70462306a36Sopenharmony_ci return r; 70562306a36Sopenharmony_ci gates++; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ciint nci_hci_dev_session_init(struct nci_dev *ndev) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct nci_conn_info *conn_info; 71462306a36Sopenharmony_ci struct sk_buff *skb; 71562306a36Sopenharmony_ci int r; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci ndev->hci_dev->count_pipes = 0; 71862306a36Sopenharmony_ci ndev->hci_dev->expected_pipes = 0; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci conn_info = ndev->hci_dev->conn_info; 72162306a36Sopenharmony_ci if (!conn_info) 72262306a36Sopenharmony_ci return -EPROTO; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci conn_info->data_exchange_cb = nci_hci_data_received_cb; 72562306a36Sopenharmony_ci conn_info->data_exchange_cb_context = ndev; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci nci_hci_reset_pipes(ndev->hci_dev); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (ndev->hci_dev->init_data.gates[0].gate != NCI_HCI_ADMIN_GATE) 73062306a36Sopenharmony_ci return -EPROTO; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci r = nci_hci_connect_gate(ndev, 73362306a36Sopenharmony_ci ndev->hci_dev->init_data.gates[0].dest_host, 73462306a36Sopenharmony_ci ndev->hci_dev->init_data.gates[0].gate, 73562306a36Sopenharmony_ci ndev->hci_dev->init_data.gates[0].pipe); 73662306a36Sopenharmony_ci if (r < 0) 73762306a36Sopenharmony_ci return r; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE, 74062306a36Sopenharmony_ci NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, &skb); 74162306a36Sopenharmony_ci if (r < 0) 74262306a36Sopenharmony_ci return r; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (skb->len && 74562306a36Sopenharmony_ci skb->len == strlen(ndev->hci_dev->init_data.session_id) && 74662306a36Sopenharmony_ci !memcmp(ndev->hci_dev->init_data.session_id, skb->data, skb->len) && 74762306a36Sopenharmony_ci ndev->ops->hci_load_session) { 74862306a36Sopenharmony_ci /* Restore gate<->pipe table from some proprietary location. */ 74962306a36Sopenharmony_ci r = ndev->ops->hci_load_session(ndev); 75062306a36Sopenharmony_ci } else { 75162306a36Sopenharmony_ci r = nci_hci_clear_all_pipes(ndev); 75262306a36Sopenharmony_ci if (r < 0) 75362306a36Sopenharmony_ci goto exit; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci r = nci_hci_dev_connect_gates(ndev, 75662306a36Sopenharmony_ci ndev->hci_dev->init_data.gate_count, 75762306a36Sopenharmony_ci ndev->hci_dev->init_data.gates); 75862306a36Sopenharmony_ci if (r < 0) 75962306a36Sopenharmony_ci goto exit; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE, 76262306a36Sopenharmony_ci NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, 76362306a36Sopenharmony_ci ndev->hci_dev->init_data.session_id, 76462306a36Sopenharmony_ci strlen(ndev->hci_dev->init_data.session_id)); 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ciexit: 76862306a36Sopenharmony_ci kfree_skb(skb); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return r; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_dev_session_init); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistruct nci_hci_dev *nci_hci_allocate(struct nci_dev *ndev) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci struct nci_hci_dev *hdev; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); 77962306a36Sopenharmony_ci if (!hdev) 78062306a36Sopenharmony_ci return NULL; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci skb_queue_head_init(&hdev->rx_hcp_frags); 78362306a36Sopenharmony_ci INIT_WORK(&hdev->msg_rx_work, nci_hci_msg_rx_work); 78462306a36Sopenharmony_ci skb_queue_head_init(&hdev->msg_rx_queue); 78562306a36Sopenharmony_ci hdev->ndev = ndev; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return hdev; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_civoid nci_hci_deallocate(struct nci_dev *ndev) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci kfree(ndev->hci_dev); 79362306a36Sopenharmony_ci} 794