Commit | Line | Data |
---|---|---|
6a2968aa IE |
1 | /* |
2 | * The NFC Controller Interface is the communication protocol between an | |
3 | * NFC Controller (NFCC) and a Device Host (DH). | |
4 | * | |
5 | * Copyright (C) 2011 Texas Instruments, Inc. | |
6 | * | |
7 | * Written by Ilan Elias <ilane@ti.com> | |
8 | * | |
9 | * Acknowledgements: | |
10 | * This file is based on hci_event.c, which was written | |
11 | * by Maxim Krasnyansky. | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of the GNU General Public License version 2 | |
15 | * as published by the Free Software Foundation | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with this program; if not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
25 | * | |
26 | */ | |
27 | ||
52858b51 | 28 | #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ |
ed1e0ad8 | 29 | |
6a2968aa IE |
30 | #include <linux/types.h> |
31 | #include <linux/interrupt.h> | |
32 | #include <linux/bitops.h> | |
33 | #include <linux/skbuff.h> | |
34 | ||
35 | #include "../nfc.h" | |
36 | #include <net/nfc/nci.h> | |
37 | #include <net/nfc/nci_core.h> | |
38 | #include <linux/nfc.h> | |
39 | ||
40 | /* Handle NCI Notification packets */ | |
41 | ||
42 | static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, | |
43 | struct sk_buff *skb) | |
44 | { | |
45 | struct nci_core_conn_credit_ntf *ntf = (void *) skb->data; | |
46 | int i; | |
47 | ||
24bf3304 | 48 | pr_debug("num_entries %d\n", ntf->num_entries); |
6a2968aa IE |
49 | |
50 | if (ntf->num_entries > NCI_MAX_NUM_CONN) | |
51 | ntf->num_entries = NCI_MAX_NUM_CONN; | |
52 | ||
53 | /* update the credits */ | |
54 | for (i = 0; i < ntf->num_entries; i++) { | |
637d85a7 IE |
55 | ntf->conn_entries[i].conn_id = |
56 | nci_conn_id(&ntf->conn_entries[i].conn_id); | |
57 | ||
20c239c1 JP |
58 | pr_debug("entry[%d]: conn_id %d, credits %d\n", |
59 | i, ntf->conn_entries[i].conn_id, | |
60 | ntf->conn_entries[i].credits); | |
6a2968aa | 61 | |
e8c0dacd | 62 | if (ntf->conn_entries[i].conn_id == NCI_STATIC_RF_CONN_ID) { |
6a2968aa IE |
63 | /* found static rf connection */ |
64 | atomic_add(ntf->conn_entries[i].credits, | |
65 | &ndev->credits_cnt); | |
66 | } | |
67 | } | |
68 | ||
69 | /* trigger the next tx */ | |
70 | if (!skb_queue_empty(&ndev->tx_q)) | |
71 | queue_work(ndev->tx_wq, &ndev->tx_work); | |
72 | } | |
73 | ||
004161cb IE |
74 | static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev, |
75 | struct sk_buff *skb) | |
76 | { | |
77 | struct nci_core_intf_error_ntf *ntf = (void *) skb->data; | |
78 | ||
79 | ntf->conn_id = nci_conn_id(&ntf->conn_id); | |
80 | ||
81 | pr_debug("status 0x%x, conn_id %d\n", ntf->status, ntf->conn_id); | |
82 | ||
83 | /* complete the data exchange transaction, if exists */ | |
84 | if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) | |
85 | nci_data_exchange_complete(ndev, NULL, -EIO); | |
86 | } | |
87 | ||
e8c0dacd IE |
88 | static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev, |
89 | struct nci_rf_intf_activated_ntf *ntf, __u8 *data) | |
6a2968aa IE |
90 | { |
91 | struct rf_tech_specific_params_nfca_poll *nfca_poll; | |
6a2968aa IE |
92 | |
93 | nfca_poll = &ntf->rf_tech_specific_params.nfca_poll; | |
6a2968aa IE |
94 | |
95 | nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data)); | |
96 | data += 2; | |
97 | ||
98 | nfca_poll->nfcid1_len = *data++; | |
99 | ||
20c239c1 JP |
100 | pr_debug("sens_res 0x%x, nfcid1_len %d\n", |
101 | nfca_poll->sens_res, nfca_poll->nfcid1_len); | |
6a2968aa IE |
102 | |
103 | memcpy(nfca_poll->nfcid1, data, nfca_poll->nfcid1_len); | |
104 | data += nfca_poll->nfcid1_len; | |
105 | ||
106 | nfca_poll->sel_res_len = *data++; | |
107 | ||
108 | if (nfca_poll->sel_res_len != 0) | |
109 | nfca_poll->sel_res = *data++; | |
110 | ||
20c239c1 JP |
111 | pr_debug("sel_res_len %d, sel_res 0x%x\n", |
112 | nfca_poll->sel_res_len, | |
113 | nfca_poll->sel_res); | |
e8c0dacd IE |
114 | |
115 | return data; | |
116 | } | |
117 | ||
118 | static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, | |
119 | struct nci_rf_intf_activated_ntf *ntf, __u8 *data) | |
120 | { | |
121 | struct activation_params_nfca_poll_iso_dep *nfca_poll; | |
122 | ||
123 | switch (ntf->activation_rf_tech_and_mode) { | |
124 | case NCI_NFC_A_PASSIVE_POLL_MODE: | |
125 | nfca_poll = &ntf->activation_params.nfca_poll_iso_dep; | |
126 | nfca_poll->rats_res_len = *data++; | |
127 | if (nfca_poll->rats_res_len > 0) { | |
128 | memcpy(nfca_poll->rats_res, | |
6a2968aa | 129 | data, |
e8c0dacd | 130 | nfca_poll->rats_res_len); |
6a2968aa IE |
131 | } |
132 | break; | |
133 | ||
6a2968aa | 134 | default: |
ed1e0ad8 JP |
135 | pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", |
136 | ntf->activation_rf_tech_and_mode); | |
6a2968aa IE |
137 | return -EPROTO; |
138 | } | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | static void nci_target_found(struct nci_dev *ndev, | |
e8c0dacd | 144 | struct nci_rf_intf_activated_ntf *ntf) |
6a2968aa IE |
145 | { |
146 | struct nfc_target nfc_tgt; | |
147 | ||
148 | if (ntf->rf_protocol == NCI_RF_PROTOCOL_T2T) /* T2T MifareUL */ | |
149 | nfc_tgt.supported_protocols = NFC_PROTO_MIFARE_MASK; | |
150 | else if (ntf->rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) /* 4A */ | |
151 | nfc_tgt.supported_protocols = NFC_PROTO_ISO14443_MASK; | |
e8c0dacd IE |
152 | else |
153 | nfc_tgt.supported_protocols = 0; | |
6a2968aa IE |
154 | |
155 | nfc_tgt.sens_res = ntf->rf_tech_specific_params.nfca_poll.sens_res; | |
156 | nfc_tgt.sel_res = ntf->rf_tech_specific_params.nfca_poll.sel_res; | |
157 | ||
158 | if (!(nfc_tgt.supported_protocols & ndev->poll_prots)) { | |
20c239c1 | 159 | pr_debug("the target found does not have the desired protocol\n"); |
6a2968aa IE |
160 | return; |
161 | } | |
162 | ||
20c239c1 JP |
163 | pr_debug("new target found, supported_protocols 0x%x\n", |
164 | nfc_tgt.supported_protocols); | |
6a2968aa IE |
165 | |
166 | ndev->target_available_prots = nfc_tgt.supported_protocols; | |
637d85a7 IE |
167 | ndev->max_data_pkt_payload_size = ntf->max_data_pkt_payload_size; |
168 | ndev->initial_num_credits = ntf->initial_num_credits; | |
169 | ||
170 | /* set the available credits to initial value */ | |
171 | atomic_set(&ndev->credits_cnt, ndev->initial_num_credits); | |
6a2968aa IE |
172 | |
173 | nfc_targets_found(ndev->nfc_dev, &nfc_tgt, 1); | |
174 | } | |
175 | ||
e8c0dacd IE |
176 | static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, |
177 | struct sk_buff *skb) | |
6a2968aa | 178 | { |
e8c0dacd | 179 | struct nci_rf_intf_activated_ntf ntf; |
6a2968aa | 180 | __u8 *data = skb->data; |
e8c0dacd | 181 | int err = 0; |
6a2968aa IE |
182 | |
183 | clear_bit(NCI_DISCOVERY, &ndev->flags); | |
184 | set_bit(NCI_POLL_ACTIVE, &ndev->flags); | |
185 | ||
e8c0dacd | 186 | ntf.rf_discovery_id = *data++; |
637d85a7 | 187 | ntf.rf_interface = *data++; |
6a2968aa | 188 | ntf.rf_protocol = *data++; |
e8c0dacd | 189 | ntf.activation_rf_tech_and_mode = *data++; |
637d85a7 IE |
190 | ntf.max_data_pkt_payload_size = *data++; |
191 | ntf.initial_num_credits = *data++; | |
6a2968aa IE |
192 | ntf.rf_tech_specific_params_len = *data++; |
193 | ||
20c239c1 | 194 | pr_debug("rf_discovery_id %d\n", ntf.rf_discovery_id); |
637d85a7 | 195 | pr_debug("rf_interface 0x%x\n", ntf.rf_interface); |
20c239c1 JP |
196 | pr_debug("rf_protocol 0x%x\n", ntf.rf_protocol); |
197 | pr_debug("activation_rf_tech_and_mode 0x%x\n", | |
198 | ntf.activation_rf_tech_and_mode); | |
637d85a7 IE |
199 | pr_debug("max_data_pkt_payload_size 0x%x\n", |
200 | ntf.max_data_pkt_payload_size); | |
201 | pr_debug("initial_num_credits 0x%x\n", ntf.initial_num_credits); | |
20c239c1 JP |
202 | pr_debug("rf_tech_specific_params_len %d\n", |
203 | ntf.rf_tech_specific_params_len); | |
6a2968aa | 204 | |
e8c0dacd IE |
205 | if (ntf.rf_tech_specific_params_len > 0) { |
206 | switch (ntf.activation_rf_tech_and_mode) { | |
207 | case NCI_NFC_A_PASSIVE_POLL_MODE: | |
208 | data = nci_extract_rf_params_nfca_passive_poll(ndev, | |
209 | &ntf, data); | |
210 | break; | |
211 | ||
212 | default: | |
ed1e0ad8 JP |
213 | pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", |
214 | ntf.activation_rf_tech_and_mode); | |
e8c0dacd IE |
215 | return; |
216 | } | |
217 | } | |
6a2968aa | 218 | |
e8c0dacd IE |
219 | ntf.data_exch_rf_tech_and_mode = *data++; |
220 | ntf.data_exch_tx_bit_rate = *data++; | |
221 | ntf.data_exch_rx_bit_rate = *data++; | |
222 | ntf.activation_params_len = *data++; | |
223 | ||
20c239c1 JP |
224 | pr_debug("data_exch_rf_tech_and_mode 0x%x\n", |
225 | ntf.data_exch_rf_tech_and_mode); | |
226 | pr_debug("data_exch_tx_bit_rate 0x%x\n", | |
227 | ntf.data_exch_tx_bit_rate); | |
228 | pr_debug("data_exch_rx_bit_rate 0x%x\n", | |
229 | ntf.data_exch_rx_bit_rate); | |
230 | pr_debug("activation_params_len %d\n", | |
231 | ntf.activation_params_len); | |
e8c0dacd IE |
232 | |
233 | if (ntf.activation_params_len > 0) { | |
637d85a7 | 234 | switch (ntf.rf_interface) { |
e8c0dacd IE |
235 | case NCI_RF_INTERFACE_ISO_DEP: |
236 | err = nci_extract_activation_params_iso_dep(ndev, | |
237 | &ntf, data); | |
238 | break; | |
239 | ||
240 | case NCI_RF_INTERFACE_FRAME: | |
241 | /* no activation params */ | |
242 | break; | |
243 | ||
244 | default: | |
637d85a7 IE |
245 | pr_err("unsupported rf_interface 0x%x\n", |
246 | ntf.rf_interface); | |
e8c0dacd IE |
247 | return; |
248 | } | |
6a2968aa IE |
249 | } |
250 | ||
e8c0dacd | 251 | if (!err) |
6a2968aa IE |
252 | nci_target_found(ndev, &ntf); |
253 | } | |
254 | ||
255 | static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, | |
256 | struct sk_buff *skb) | |
257 | { | |
e8c0dacd | 258 | struct nci_rf_deactivate_ntf *ntf = (void *) skb->data; |
6a2968aa | 259 | |
20c239c1 | 260 | pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason); |
6a2968aa IE |
261 | |
262 | clear_bit(NCI_POLL_ACTIVE, &ndev->flags); | |
263 | ndev->target_active_prot = 0; | |
264 | ||
265 | /* drop tx data queue */ | |
266 | skb_queue_purge(&ndev->tx_q); | |
267 | ||
268 | /* drop partial rx data packet */ | |
269 | if (ndev->rx_data_reassembly) { | |
270 | kfree_skb(ndev->rx_data_reassembly); | |
271 | ndev->rx_data_reassembly = 0; | |
272 | } | |
273 | ||
274 | /* complete the data exchange transaction, if exists */ | |
38f04c6b | 275 | if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) |
6a2968aa IE |
276 | nci_data_exchange_complete(ndev, NULL, -EIO); |
277 | } | |
278 | ||
279 | void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) | |
280 | { | |
281 | __u16 ntf_opcode = nci_opcode(skb->data); | |
282 | ||
20c239c1 JP |
283 | pr_debug("NCI RX: MT=ntf, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", |
284 | nci_pbf(skb->data), | |
285 | nci_opcode_gid(ntf_opcode), | |
286 | nci_opcode_oid(ntf_opcode), | |
287 | nci_plen(skb->data)); | |
6a2968aa IE |
288 | |
289 | /* strip the nci control header */ | |
290 | skb_pull(skb, NCI_CTRL_HDR_SIZE); | |
291 | ||
292 | switch (ntf_opcode) { | |
293 | case NCI_OP_CORE_CONN_CREDITS_NTF: | |
294 | nci_core_conn_credits_ntf_packet(ndev, skb); | |
295 | break; | |
296 | ||
004161cb IE |
297 | case NCI_OP_CORE_INTF_ERROR_NTF: |
298 | nci_core_conn_intf_error_ntf_packet(ndev, skb); | |
299 | break; | |
300 | ||
e8c0dacd IE |
301 | case NCI_OP_RF_INTF_ACTIVATED_NTF: |
302 | nci_rf_intf_activated_ntf_packet(ndev, skb); | |
6a2968aa IE |
303 | break; |
304 | ||
305 | case NCI_OP_RF_DEACTIVATE_NTF: | |
306 | nci_rf_deactivate_ntf_packet(ndev, skb); | |
307 | break; | |
308 | ||
309 | default: | |
ed1e0ad8 | 310 | pr_err("unknown ntf opcode 0x%x\n", ntf_opcode); |
6a2968aa IE |
311 | break; |
312 | } | |
313 | ||
314 | kfree_skb(skb); | |
315 | } |