xref: /linux/net/nfc/nci/rsp.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26a2968aaSIlan Elias /*
36a2968aaSIlan Elias  *  The NFC Controller Interface is the communication protocol between an
46a2968aaSIlan Elias  *  NFC Controller (NFCC) and a Device Host (DH).
56a2968aaSIlan Elias  *
66a2968aaSIlan Elias  *  Copyright (C) 2011 Texas Instruments, Inc.
76a2968aaSIlan Elias  *
86a2968aaSIlan Elias  *  Written by Ilan Elias <ilane@ti.com>
96a2968aaSIlan Elias  *
106a2968aaSIlan Elias  *  Acknowledgements:
116a2968aaSIlan Elias  *  This file is based on hci_event.c, which was written
126a2968aaSIlan Elias  *  by Maxim Krasnyansky.
136a2968aaSIlan Elias  */
146a2968aaSIlan Elias 
1552858b51SSamuel Ortiz #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
16ed1e0ad8SJoe Perches 
176a2968aaSIlan Elias #include <linux/types.h>
186a2968aaSIlan Elias #include <linux/interrupt.h>
196a2968aaSIlan Elias #include <linux/bitops.h>
206a2968aaSIlan Elias #include <linux/skbuff.h>
216a2968aaSIlan Elias 
226a2968aaSIlan Elias #include "../nfc.h"
236a2968aaSIlan Elias #include <net/nfc/nci.h>
246a2968aaSIlan Elias #include <net/nfc/nci_core.h>
256a2968aaSIlan Elias 
266a2968aaSIlan Elias /* Handle NCI Response packets */
276a2968aaSIlan Elias 
nci_core_reset_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)28ddecf555SKrzysztof Kozlowski static void nci_core_reset_rsp_packet(struct nci_dev *ndev,
29ddecf555SKrzysztof Kozlowski 				      const struct sk_buff *skb)
306a2968aaSIlan Elias {
31ddecf555SKrzysztof Kozlowski 	const struct nci_core_reset_rsp *rsp = (void *)skb->data;
326a2968aaSIlan Elias 
3324bf3304SJoe Perches 	pr_debug("status 0x%x\n", rsp->status);
346a2968aaSIlan Elias 
35bcd684aaSBongsu Jeon 	/* Handle NCI 1.x ver */
36bcd684aaSBongsu Jeon 	if (skb->len != 1) {
37e8c0dacdSIlan Elias 		if (rsp->status == NCI_STATUS_OK) {
386a2968aaSIlan Elias 			ndev->nci_ver = rsp->nci_ver;
3920c239c1SJoe Perches 			pr_debug("nci_ver 0x%x, config_status 0x%x\n",
40e8c0dacdSIlan Elias 				 rsp->nci_ver, rsp->config_status);
41e8c0dacdSIlan Elias 		}
426a2968aaSIlan Elias 
436a2968aaSIlan Elias 		nci_req_complete(ndev, rsp->status);
446a2968aaSIlan Elias 	}
45bcd684aaSBongsu Jeon }
466a2968aaSIlan Elias 
nci_core_init_rsp_packet_v1(struct nci_dev * ndev,const struct sk_buff * skb)47ddecf555SKrzysztof Kozlowski static u8 nci_core_init_rsp_packet_v1(struct nci_dev *ndev,
48ddecf555SKrzysztof Kozlowski 				      const struct sk_buff *skb)
496a2968aaSIlan Elias {
50ddecf555SKrzysztof Kozlowski 	const struct nci_core_init_rsp_1 *rsp_1 = (void *)skb->data;
51ddecf555SKrzysztof Kozlowski 	const struct nci_core_init_rsp_2 *rsp_2;
526a2968aaSIlan Elias 
5324bf3304SJoe Perches 	pr_debug("status 0x%x\n", rsp_1->status);
546a2968aaSIlan Elias 
556a2968aaSIlan Elias 	if (rsp_1->status != NCI_STATUS_OK)
56bcd684aaSBongsu Jeon 		return rsp_1->status;
576a2968aaSIlan Elias 
586a2968aaSIlan Elias 	ndev->nfcc_features = __le32_to_cpu(rsp_1->nfcc_features);
596a2968aaSIlan Elias 	ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces;
606a2968aaSIlan Elias 
616a2968aaSIlan Elias 	ndev->num_supported_rf_interfaces =
62bcd684aaSBongsu Jeon 		min((int)ndev->num_supported_rf_interfaces,
63bcd684aaSBongsu Jeon 		    NCI_MAX_SUPPORTED_RF_INTERFACES);
646a2968aaSIlan Elias 
656a2968aaSIlan Elias 	memcpy(ndev->supported_rf_interfaces,
666a2968aaSIlan Elias 	       rsp_1->supported_rf_interfaces,
676a2968aaSIlan Elias 	       ndev->num_supported_rf_interfaces);
686a2968aaSIlan Elias 
69e8c0dacdSIlan Elias 	rsp_2 = (void *) (skb->data + 6 + rsp_1->num_supported_rf_interfaces);
706a2968aaSIlan Elias 
71eb9bc6e9SSamuel Ortiz 	ndev->max_logical_connections = rsp_2->max_logical_connections;
726a2968aaSIlan Elias 	ndev->max_routing_table_size =
736a2968aaSIlan Elias 		__le16_to_cpu(rsp_2->max_routing_table_size);
74e8c0dacdSIlan Elias 	ndev->max_ctrl_pkt_payload_len =
75e8c0dacdSIlan Elias 		rsp_2->max_ctrl_pkt_payload_len;
76e8c0dacdSIlan Elias 	ndev->max_size_for_large_params =
77e8c0dacdSIlan Elias 		__le16_to_cpu(rsp_2->max_size_for_large_params);
78e8c0dacdSIlan Elias 	ndev->manufact_id =
79e8c0dacdSIlan Elias 		rsp_2->manufact_id;
80e8c0dacdSIlan Elias 	ndev->manufact_specific_info =
81e8c0dacdSIlan Elias 		__le32_to_cpu(rsp_2->manufact_specific_info);
82e8c0dacdSIlan Elias 
83bcd684aaSBongsu Jeon 	return NCI_STATUS_OK;
84bcd684aaSBongsu Jeon }
85bcd684aaSBongsu Jeon 
nci_core_init_rsp_packet_v2(struct nci_dev * ndev,const struct sk_buff * skb)86ddecf555SKrzysztof Kozlowski static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev,
87ddecf555SKrzysztof Kozlowski 				      const struct sk_buff *skb)
88bcd684aaSBongsu Jeon {
89ddecf555SKrzysztof Kozlowski 	const struct nci_core_init_rsp_nci_ver2 *rsp = (void *)skb->data;
90ddecf555SKrzysztof Kozlowski 	const u8 *supported_rf_interface = rsp->supported_rf_interfaces;
91bcd684aaSBongsu Jeon 	u8 rf_interface_idx = 0;
92bcd684aaSBongsu Jeon 	u8 rf_extension_cnt = 0;
93bcd684aaSBongsu Jeon 
94bcd684aaSBongsu Jeon 	pr_debug("status %x\n", rsp->status);
95bcd684aaSBongsu Jeon 
96bcd684aaSBongsu Jeon 	if (rsp->status != NCI_STATUS_OK)
97bcd684aaSBongsu Jeon 		return rsp->status;
98bcd684aaSBongsu Jeon 
99bcd684aaSBongsu Jeon 	ndev->nfcc_features = __le32_to_cpu(rsp->nfcc_features);
100bcd684aaSBongsu Jeon 	ndev->num_supported_rf_interfaces = rsp->num_supported_rf_interfaces;
101bcd684aaSBongsu Jeon 
102bcd684aaSBongsu Jeon 	ndev->num_supported_rf_interfaces =
103bcd684aaSBongsu Jeon 		min((int)ndev->num_supported_rf_interfaces,
104bcd684aaSBongsu Jeon 		    NCI_MAX_SUPPORTED_RF_INTERFACES);
105bcd684aaSBongsu Jeon 
106bcd684aaSBongsu Jeon 	while (rf_interface_idx < ndev->num_supported_rf_interfaces) {
107bcd684aaSBongsu Jeon 		ndev->supported_rf_interfaces[rf_interface_idx++] = *supported_rf_interface++;
108bcd684aaSBongsu Jeon 
109bcd684aaSBongsu Jeon 		/* skip rf extension parameters */
110bcd684aaSBongsu Jeon 		rf_extension_cnt = *supported_rf_interface++;
111bcd684aaSBongsu Jeon 		supported_rf_interface += rf_extension_cnt;
112bcd684aaSBongsu Jeon 	}
113bcd684aaSBongsu Jeon 
114bcd684aaSBongsu Jeon 	ndev->max_logical_connections = rsp->max_logical_connections;
115bcd684aaSBongsu Jeon 	ndev->max_routing_table_size =
116bcd684aaSBongsu Jeon 			__le16_to_cpu(rsp->max_routing_table_size);
117bcd684aaSBongsu Jeon 	ndev->max_ctrl_pkt_payload_len =
118bcd684aaSBongsu Jeon 			rsp->max_ctrl_pkt_payload_len;
119bcd684aaSBongsu Jeon 	ndev->max_size_for_large_params = NCI_MAX_LARGE_PARAMS_NCI_v2;
120bcd684aaSBongsu Jeon 
121bcd684aaSBongsu Jeon 	return NCI_STATUS_OK;
122bcd684aaSBongsu Jeon }
123bcd684aaSBongsu Jeon 
nci_core_init_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)124ddecf555SKrzysztof Kozlowski static void nci_core_init_rsp_packet(struct nci_dev *ndev, const struct sk_buff *skb)
125bcd684aaSBongsu Jeon {
126bcd684aaSBongsu Jeon 	u8 status = 0;
127bcd684aaSBongsu Jeon 
128bcd684aaSBongsu Jeon 	if (!(ndev->nci_ver & NCI_VER_2_MASK))
129bcd684aaSBongsu Jeon 		status = nci_core_init_rsp_packet_v1(ndev, skb);
130bcd684aaSBongsu Jeon 	else
131bcd684aaSBongsu Jeon 		status = nci_core_init_rsp_packet_v2(ndev, skb);
132bcd684aaSBongsu Jeon 
133bcd684aaSBongsu Jeon 	if (status != NCI_STATUS_OK)
134bcd684aaSBongsu Jeon 		goto exit;
135bcd684aaSBongsu Jeon 
13620c239c1SJoe Perches 	pr_debug("nfcc_features 0x%x\n",
1376a2968aaSIlan Elias 		 ndev->nfcc_features);
13820c239c1SJoe Perches 	pr_debug("num_supported_rf_interfaces %d\n",
1396a2968aaSIlan Elias 		 ndev->num_supported_rf_interfaces);
14020c239c1SJoe Perches 	pr_debug("supported_rf_interfaces[0] 0x%x\n",
1416a2968aaSIlan Elias 		 ndev->supported_rf_interfaces[0]);
14220c239c1SJoe Perches 	pr_debug("supported_rf_interfaces[1] 0x%x\n",
1436a2968aaSIlan Elias 		 ndev->supported_rf_interfaces[1]);
14420c239c1SJoe Perches 	pr_debug("supported_rf_interfaces[2] 0x%x\n",
1456a2968aaSIlan Elias 		 ndev->supported_rf_interfaces[2]);
14620c239c1SJoe Perches 	pr_debug("supported_rf_interfaces[3] 0x%x\n",
1476a2968aaSIlan Elias 		 ndev->supported_rf_interfaces[3]);
14820c239c1SJoe Perches 	pr_debug("max_logical_connections %d\n",
1496a2968aaSIlan Elias 		 ndev->max_logical_connections);
15020c239c1SJoe Perches 	pr_debug("max_routing_table_size %d\n",
1516a2968aaSIlan Elias 		 ndev->max_routing_table_size);
15220c239c1SJoe Perches 	pr_debug("max_ctrl_pkt_payload_len %d\n",
153e8c0dacdSIlan Elias 		 ndev->max_ctrl_pkt_payload_len);
15420c239c1SJoe Perches 	pr_debug("max_size_for_large_params %d\n",
155e8c0dacdSIlan Elias 		 ndev->max_size_for_large_params);
15620c239c1SJoe Perches 	pr_debug("manufact_id 0x%x\n",
157e8c0dacdSIlan Elias 		 ndev->manufact_id);
15820c239c1SJoe Perches 	pr_debug("manufact_specific_info 0x%x\n",
159e8c0dacdSIlan Elias 		 ndev->manufact_specific_info);
1606a2968aaSIlan Elias 
161e8c0dacdSIlan Elias exit:
162bcd684aaSBongsu Jeon 	nci_req_complete(ndev, status);
1636a2968aaSIlan Elias }
1646a2968aaSIlan Elias 
nci_core_set_config_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)1657e035230SIlan Elias static void nci_core_set_config_rsp_packet(struct nci_dev *ndev,
166ddecf555SKrzysztof Kozlowski 					   const struct sk_buff *skb)
1677e035230SIlan Elias {
168ddecf555SKrzysztof Kozlowski 	const struct nci_core_set_config_rsp *rsp = (void *)skb->data;
1697e035230SIlan Elias 
1707e035230SIlan Elias 	pr_debug("status 0x%x\n", rsp->status);
1717e035230SIlan Elias 
1727e035230SIlan Elias 	nci_req_complete(ndev, rsp->status);
1737e035230SIlan Elias }
1747e035230SIlan Elias 
nci_rf_disc_map_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)1756a2968aaSIlan Elias static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev,
176ddecf555SKrzysztof Kozlowski 				       const struct sk_buff *skb)
1776a2968aaSIlan Elias {
1786a2968aaSIlan Elias 	__u8 status = skb->data[0];
1796a2968aaSIlan Elias 
18024bf3304SJoe Perches 	pr_debug("status 0x%x\n", status);
1816a2968aaSIlan Elias 
1826a2968aaSIlan Elias 	nci_req_complete(ndev, status);
1836a2968aaSIlan Elias }
1846a2968aaSIlan Elias 
nci_rf_disc_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)185ddecf555SKrzysztof Kozlowski static void nci_rf_disc_rsp_packet(struct nci_dev *ndev,
186ddecf555SKrzysztof Kozlowski 				   const struct sk_buff *skb)
1876a2968aaSIlan Elias {
1884aeee687SChristophe Ricard 	struct nci_conn_info *conn_info;
1896a2968aaSIlan Elias 	__u8 status = skb->data[0];
1906a2968aaSIlan Elias 
19124bf3304SJoe Perches 	pr_debug("status 0x%x\n", status);
1926a2968aaSIlan Elias 
1934aeee687SChristophe Ricard 	if (status == NCI_STATUS_OK) {
1948939e47fSIlan Elias 		atomic_set(&ndev->state, NCI_DISCOVERY);
1956a2968aaSIlan Elias 
19612bdf27dSChristophe Ricard 		conn_info = ndev->rf_conn_info;
1974aeee687SChristophe Ricard 		if (!conn_info) {
1984aeee687SChristophe Ricard 			conn_info = devm_kzalloc(&ndev->nfc_dev->dev,
1994aeee687SChristophe Ricard 						 sizeof(struct nci_conn_info),
2004aeee687SChristophe Ricard 						 GFP_KERNEL);
2014aeee687SChristophe Ricard 			if (!conn_info) {
2024aeee687SChristophe Ricard 				status = NCI_STATUS_REJECTED;
2034aeee687SChristophe Ricard 				goto exit;
2044aeee687SChristophe Ricard 			}
2054aeee687SChristophe Ricard 			conn_info->conn_id = NCI_STATIC_RF_CONN_ID;
2064aeee687SChristophe Ricard 			INIT_LIST_HEAD(&conn_info->list);
2074aeee687SChristophe Ricard 			list_add(&conn_info->list, &ndev->conn_info_list);
20812bdf27dSChristophe Ricard 			ndev->rf_conn_info = conn_info;
2094aeee687SChristophe Ricard 		}
2104aeee687SChristophe Ricard 	}
2114aeee687SChristophe Ricard 
2124aeee687SChristophe Ricard exit:
2136a2968aaSIlan Elias 	nci_req_complete(ndev, status);
2146a2968aaSIlan Elias }
2156a2968aaSIlan Elias 
nci_rf_disc_select_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)216019c4fbaSIlan Elias static void nci_rf_disc_select_rsp_packet(struct nci_dev *ndev,
217ddecf555SKrzysztof Kozlowski 					  const struct sk_buff *skb)
218019c4fbaSIlan Elias {
219019c4fbaSIlan Elias 	__u8 status = skb->data[0];
220019c4fbaSIlan Elias 
221019c4fbaSIlan Elias 	pr_debug("status 0x%x\n", status);
222019c4fbaSIlan Elias 
223019c4fbaSIlan Elias 	/* Complete the request on intf_activated_ntf or generic_error_ntf */
224019c4fbaSIlan Elias 	if (status != NCI_STATUS_OK)
225019c4fbaSIlan Elias 		nci_req_complete(ndev, status);
226019c4fbaSIlan Elias }
227019c4fbaSIlan Elias 
nci_rf_deactivate_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)2286a2968aaSIlan Elias static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev,
229ddecf555SKrzysztof Kozlowski 					 const struct sk_buff *skb)
2306a2968aaSIlan Elias {
2316a2968aaSIlan Elias 	__u8 status = skb->data[0];
2326a2968aaSIlan Elias 
23324bf3304SJoe Perches 	pr_debug("status 0x%x\n", status);
2346a2968aaSIlan Elias 
235bd7e01bcSIlan Elias 	/* If target was active, complete the request only in deactivate_ntf */
236bd7e01bcSIlan Elias 	if ((status != NCI_STATUS_OK) ||
2378939e47fSIlan Elias 	    (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) {
238019c4fbaSIlan Elias 		nci_clear_target_list(ndev);
2398939e47fSIlan Elias 		atomic_set(&ndev->state, NCI_IDLE);
2406a2968aaSIlan Elias 		nci_req_complete(ndev, status);
2416a2968aaSIlan Elias 	}
2428939e47fSIlan Elias }
2436a2968aaSIlan Elias 
nci_nfcee_discover_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)244af9c8aa6SChristophe Ricard static void nci_nfcee_discover_rsp_packet(struct nci_dev *ndev,
245ddecf555SKrzysztof Kozlowski 					  const struct sk_buff *skb)
246af9c8aa6SChristophe Ricard {
247ddecf555SKrzysztof Kozlowski 	const struct nci_nfcee_discover_rsp *discover_rsp;
248af9c8aa6SChristophe Ricard 
249af9c8aa6SChristophe Ricard 	if (skb->len != 2) {
250af9c8aa6SChristophe Ricard 		nci_req_complete(ndev, NCI_STATUS_NFCEE_PROTOCOL_ERROR);
251af9c8aa6SChristophe Ricard 		return;
252af9c8aa6SChristophe Ricard 	}
253af9c8aa6SChristophe Ricard 
254af9c8aa6SChristophe Ricard 	discover_rsp = (struct nci_nfcee_discover_rsp *)skb->data;
255af9c8aa6SChristophe Ricard 
256af9c8aa6SChristophe Ricard 	if (discover_rsp->status != NCI_STATUS_OK ||
257af9c8aa6SChristophe Ricard 	    discover_rsp->num_nfcee == 0)
258af9c8aa6SChristophe Ricard 		nci_req_complete(ndev, discover_rsp->status);
259af9c8aa6SChristophe Ricard }
260af9c8aa6SChristophe Ricard 
nci_nfcee_mode_set_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)261f7f793f3SChristophe Ricard static void nci_nfcee_mode_set_rsp_packet(struct nci_dev *ndev,
262ddecf555SKrzysztof Kozlowski 					  const struct sk_buff *skb)
263f7f793f3SChristophe Ricard {
264f7f793f3SChristophe Ricard 	__u8 status = skb->data[0];
265f7f793f3SChristophe Ricard 
266f7f793f3SChristophe Ricard 	pr_debug("status 0x%x\n", status);
267f7f793f3SChristophe Ricard 	nci_req_complete(ndev, status);
268f7f793f3SChristophe Ricard }
269f7f793f3SChristophe Ricard 
nci_core_conn_create_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)270736bb957SChristophe Ricard static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev,
271ddecf555SKrzysztof Kozlowski 					    const struct sk_buff *skb)
272736bb957SChristophe Ricard {
273736bb957SChristophe Ricard 	__u8 status = skb->data[0];
2749b8d1a4cSChristophe Ricard 	struct nci_conn_info *conn_info = NULL;
275ddecf555SKrzysztof Kozlowski 	const struct nci_core_conn_create_rsp *rsp;
276736bb957SChristophe Ricard 
277736bb957SChristophe Ricard 	pr_debug("status 0x%x\n", status);
278736bb957SChristophe Ricard 
279736bb957SChristophe Ricard 	if (status == NCI_STATUS_OK) {
280736bb957SChristophe Ricard 		rsp = (struct nci_core_conn_create_rsp *)skb->data;
281736bb957SChristophe Ricard 
28215d4a8daSChristophe Ricard 		conn_info = devm_kzalloc(&ndev->nfc_dev->dev,
28315d4a8daSChristophe Ricard 					 sizeof(*conn_info), GFP_KERNEL);
28415d4a8daSChristophe Ricard 		if (!conn_info) {
285736bb957SChristophe Ricard 			status = NCI_STATUS_REJECTED;
286736bb957SChristophe Ricard 			goto exit;
287736bb957SChristophe Ricard 		}
288736bb957SChristophe Ricard 
2899b8d1a4cSChristophe Ricard 		conn_info->dest_params = devm_kzalloc(&ndev->nfc_dev->dev,
2909b8d1a4cSChristophe Ricard 						sizeof(struct dest_spec_params),
2919b8d1a4cSChristophe Ricard 						GFP_KERNEL);
2929b8d1a4cSChristophe Ricard 		if (!conn_info->dest_params) {
2939b8d1a4cSChristophe Ricard 			status = NCI_STATUS_REJECTED;
2949b8d1a4cSChristophe Ricard 			goto free_conn_info;
2959b8d1a4cSChristophe Ricard 		}
2969b8d1a4cSChristophe Ricard 
2979b8d1a4cSChristophe Ricard 		conn_info->dest_type = ndev->cur_dest_type;
2989b8d1a4cSChristophe Ricard 		conn_info->dest_params->id = ndev->cur_params.id;
2999b8d1a4cSChristophe Ricard 		conn_info->dest_params->protocol = ndev->cur_params.protocol;
30015d4a8daSChristophe Ricard 		conn_info->conn_id = rsp->conn_id;
30115d4a8daSChristophe Ricard 
30215d4a8daSChristophe Ricard 		/* Note: data_exchange_cb and data_exchange_cb_context need to
30315d4a8daSChristophe Ricard 		 * be specify out of nci_core_conn_create_rsp_packet
30415d4a8daSChristophe Ricard 		 */
30515d4a8daSChristophe Ricard 
30615d4a8daSChristophe Ricard 		INIT_LIST_HEAD(&conn_info->list);
30715d4a8daSChristophe Ricard 		list_add(&conn_info->list, &ndev->conn_info_list);
30815d4a8daSChristophe Ricard 
3099b8d1a4cSChristophe Ricard 		if (ndev->cur_params.id == ndev->hci_dev->nfcee_id)
31015d4a8daSChristophe Ricard 			ndev->hci_dev->conn_info = conn_info;
31115d4a8daSChristophe Ricard 
312736bb957SChristophe Ricard 		conn_info->conn_id = rsp->conn_id;
313736bb957SChristophe Ricard 		conn_info->max_pkt_payload_len = rsp->max_ctrl_pkt_payload_len;
3143ba5c846SChristophe Ricard 		atomic_set(&conn_info->credits_cnt, rsp->credits_cnt);
315736bb957SChristophe Ricard 	}
316736bb957SChristophe Ricard 
3179b8d1a4cSChristophe Ricard free_conn_info:
3189b8d1a4cSChristophe Ricard 	if (status == NCI_STATUS_REJECTED)
3199b8d1a4cSChristophe Ricard 		devm_kfree(&ndev->nfc_dev->dev, conn_info);
320736bb957SChristophe Ricard exit:
3219b8d1a4cSChristophe Ricard 
322736bb957SChristophe Ricard 	nci_req_complete(ndev, status);
323736bb957SChristophe Ricard }
324736bb957SChristophe Ricard 
nci_core_conn_close_rsp_packet(struct nci_dev * ndev,const struct sk_buff * skb)325736bb957SChristophe Ricard static void nci_core_conn_close_rsp_packet(struct nci_dev *ndev,
326ddecf555SKrzysztof Kozlowski 					   const struct sk_buff *skb)
327736bb957SChristophe Ricard {
32815d4a8daSChristophe Ricard 	struct nci_conn_info *conn_info;
329736bb957SChristophe Ricard 	__u8 status = skb->data[0];
330736bb957SChristophe Ricard 
331736bb957SChristophe Ricard 	pr_debug("status 0x%x\n", status);
33215d4a8daSChristophe Ricard 	if (status == NCI_STATUS_OK) {
333de5ea851SChristophe Ricard 		conn_info = nci_get_conn_info_by_conn_id(ndev,
334de5ea851SChristophe Ricard 							 ndev->cur_conn_id);
33515d4a8daSChristophe Ricard 		if (conn_info) {
33615d4a8daSChristophe Ricard 			list_del(&conn_info->list);
337*1b1499a8SLin Ma 			if (conn_info == ndev->rf_conn_info)
338*1b1499a8SLin Ma 				ndev->rf_conn_info = NULL;
33915d4a8daSChristophe Ricard 			devm_kfree(&ndev->nfc_dev->dev, conn_info);
34015d4a8daSChristophe Ricard 		}
34115d4a8daSChristophe Ricard 	}
342736bb957SChristophe Ricard 	nci_req_complete(ndev, status);
343736bb957SChristophe Ricard }
344736bb957SChristophe Ricard 
nci_rsp_packet(struct nci_dev * ndev,struct sk_buff * skb)3456a2968aaSIlan Elias void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
3466a2968aaSIlan Elias {
3476a2968aaSIlan Elias 	__u16 rsp_opcode = nci_opcode(skb->data);
3486a2968aaSIlan Elias 
3496a2968aaSIlan Elias 	/* we got a rsp, stop the cmd timer */
3506a2968aaSIlan Elias 	del_timer(&ndev->cmd_timer);
3516a2968aaSIlan Elias 
35220c239c1SJoe Perches 	pr_debug("NCI RX: MT=rsp, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n",
3536a2968aaSIlan Elias 		 nci_pbf(skb->data),
3546a2968aaSIlan Elias 		 nci_opcode_gid(rsp_opcode),
3556a2968aaSIlan Elias 		 nci_opcode_oid(rsp_opcode),
3566a2968aaSIlan Elias 		 nci_plen(skb->data));
3576a2968aaSIlan Elias 
3586a2968aaSIlan Elias 	/* strip the nci control header */
3596a2968aaSIlan Elias 	skb_pull(skb, NCI_CTRL_HDR_SIZE);
3606a2968aaSIlan Elias 
361b6355e97SSamuel Ortiz 	if (nci_opcode_gid(rsp_opcode) == NCI_GID_PROPRIETARY) {
362b6355e97SSamuel Ortiz 		if (nci_prop_rsp_packet(ndev, rsp_opcode, skb) == -ENOTSUPP) {
363b6355e97SSamuel Ortiz 			pr_err("unsupported rsp opcode 0x%x\n",
364b6355e97SSamuel Ortiz 			       rsp_opcode);
365b6355e97SSamuel Ortiz 		}
366b6355e97SSamuel Ortiz 
367b6355e97SSamuel Ortiz 		goto end;
368b6355e97SSamuel Ortiz 	}
369b6355e97SSamuel Ortiz 
3706a2968aaSIlan Elias 	switch (rsp_opcode) {
3716a2968aaSIlan Elias 	case NCI_OP_CORE_RESET_RSP:
3726a2968aaSIlan Elias 		nci_core_reset_rsp_packet(ndev, skb);
3736a2968aaSIlan Elias 		break;
3746a2968aaSIlan Elias 
3756a2968aaSIlan Elias 	case NCI_OP_CORE_INIT_RSP:
3766a2968aaSIlan Elias 		nci_core_init_rsp_packet(ndev, skb);
3776a2968aaSIlan Elias 		break;
3786a2968aaSIlan Elias 
3797e035230SIlan Elias 	case NCI_OP_CORE_SET_CONFIG_RSP:
3807e035230SIlan Elias 		nci_core_set_config_rsp_packet(ndev, skb);
3817e035230SIlan Elias 		break;
3827e035230SIlan Elias 
383736bb957SChristophe Ricard 	case NCI_OP_CORE_CONN_CREATE_RSP:
384736bb957SChristophe Ricard 		nci_core_conn_create_rsp_packet(ndev, skb);
385736bb957SChristophe Ricard 		break;
386736bb957SChristophe Ricard 
387736bb957SChristophe Ricard 	case NCI_OP_CORE_CONN_CLOSE_RSP:
388736bb957SChristophe Ricard 		nci_core_conn_close_rsp_packet(ndev, skb);
389736bb957SChristophe Ricard 		break;
390736bb957SChristophe Ricard 
3916a2968aaSIlan Elias 	case NCI_OP_RF_DISCOVER_MAP_RSP:
3926a2968aaSIlan Elias 		nci_rf_disc_map_rsp_packet(ndev, skb);
3936a2968aaSIlan Elias 		break;
3946a2968aaSIlan Elias 
3956a2968aaSIlan Elias 	case NCI_OP_RF_DISCOVER_RSP:
3966a2968aaSIlan Elias 		nci_rf_disc_rsp_packet(ndev, skb);
3976a2968aaSIlan Elias 		break;
3986a2968aaSIlan Elias 
399019c4fbaSIlan Elias 	case NCI_OP_RF_DISCOVER_SELECT_RSP:
400019c4fbaSIlan Elias 		nci_rf_disc_select_rsp_packet(ndev, skb);
401019c4fbaSIlan Elias 		break;
402019c4fbaSIlan Elias 
4036a2968aaSIlan Elias 	case NCI_OP_RF_DEACTIVATE_RSP:
4046a2968aaSIlan Elias 		nci_rf_deactivate_rsp_packet(ndev, skb);
4056a2968aaSIlan Elias 		break;
4066a2968aaSIlan Elias 
407af9c8aa6SChristophe Ricard 	case NCI_OP_NFCEE_DISCOVER_RSP:
408af9c8aa6SChristophe Ricard 		nci_nfcee_discover_rsp_packet(ndev, skb);
409af9c8aa6SChristophe Ricard 		break;
410af9c8aa6SChristophe Ricard 
411f7f793f3SChristophe Ricard 	case NCI_OP_NFCEE_MODE_SET_RSP:
412f7f793f3SChristophe Ricard 		nci_nfcee_mode_set_rsp_packet(ndev, skb);
413f7f793f3SChristophe Ricard 		break;
414f7f793f3SChristophe Ricard 
4156a2968aaSIlan Elias 	default:
416ed1e0ad8SJoe Perches 		pr_err("unknown rsp opcode 0x%x\n", rsp_opcode);
4176a2968aaSIlan Elias 		break;
4186a2968aaSIlan Elias 	}
4196a2968aaSIlan Elias 
4200a97a3cbSRobert Dolca 	nci_core_rsp_packet(ndev, rsp_opcode, skb);
421b6355e97SSamuel Ortiz end:
4226a2968aaSIlan Elias 	kfree_skb(skb);
4236a2968aaSIlan Elias 
4246a2968aaSIlan Elias 	/* trigger the next cmd */
4256a2968aaSIlan Elias 	atomic_set(&ndev->cmd_cnt, 1);
4266a2968aaSIlan Elias 	if (!skb_queue_empty(&ndev->cmd_q))
4276a2968aaSIlan Elias 		queue_work(ndev->cmd_wq, &ndev->cmd_work);
4286a2968aaSIlan Elias }
429