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 | ||
d5a2ca60 IE |
118 | static __u8 *nci_extract_rf_params_nfcb_passive_poll(struct nci_dev *ndev, |
119 | struct nci_rf_intf_activated_ntf *ntf, __u8 *data) | |
120 | { | |
121 | struct rf_tech_specific_params_nfcb_poll *nfcb_poll; | |
122 | ||
123 | nfcb_poll = &ntf->rf_tech_specific_params.nfcb_poll; | |
124 | ||
125 | nfcb_poll->sensb_res_len = *data++; | |
126 | ||
127 | pr_debug("sensb_res_len %d\n", nfcb_poll->sensb_res_len); | |
128 | ||
129 | memcpy(nfcb_poll->sensb_res, data, nfcb_poll->sensb_res_len); | |
130 | data += nfcb_poll->sensb_res_len; | |
131 | ||
132 | return data; | |
133 | } | |
134 | ||
135 | static __u8 *nci_extract_rf_params_nfcf_passive_poll(struct nci_dev *ndev, | |
136 | struct nci_rf_intf_activated_ntf *ntf, __u8 *data) | |
137 | { | |
138 | struct rf_tech_specific_params_nfcf_poll *nfcf_poll; | |
139 | ||
140 | nfcf_poll = &ntf->rf_tech_specific_params.nfcf_poll; | |
141 | ||
142 | nfcf_poll->bit_rate = *data++; | |
143 | nfcf_poll->sensf_res_len = *data++; | |
144 | ||
145 | pr_debug("bit_rate %d, sensf_res_len %d\n", | |
146 | nfcf_poll->bit_rate, nfcf_poll->sensf_res_len); | |
147 | ||
148 | memcpy(nfcf_poll->sensf_res, data, nfcf_poll->sensf_res_len); | |
149 | data += nfcf_poll->sensf_res_len; | |
150 | ||
151 | return data; | |
152 | } | |
153 | ||
e8c0dacd IE |
154 | static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, |
155 | struct nci_rf_intf_activated_ntf *ntf, __u8 *data) | |
156 | { | |
157 | struct activation_params_nfca_poll_iso_dep *nfca_poll; | |
d5a2ca60 | 158 | struct activation_params_nfcb_poll_iso_dep *nfcb_poll; |
e8c0dacd IE |
159 | |
160 | switch (ntf->activation_rf_tech_and_mode) { | |
161 | case NCI_NFC_A_PASSIVE_POLL_MODE: | |
162 | nfca_poll = &ntf->activation_params.nfca_poll_iso_dep; | |
163 | nfca_poll->rats_res_len = *data++; | |
d5a2ca60 | 164 | pr_debug("rats_res_len %d\n", nfca_poll->rats_res_len); |
e8c0dacd IE |
165 | if (nfca_poll->rats_res_len > 0) { |
166 | memcpy(nfca_poll->rats_res, | |
6a2968aa | 167 | data, |
e8c0dacd | 168 | nfca_poll->rats_res_len); |
6a2968aa IE |
169 | } |
170 | break; | |
171 | ||
d5a2ca60 IE |
172 | case NCI_NFC_B_PASSIVE_POLL_MODE: |
173 | nfcb_poll = &ntf->activation_params.nfcb_poll_iso_dep; | |
174 | nfcb_poll->attrib_res_len = *data++; | |
175 | pr_debug("attrib_res_len %d\n", | |
176 | nfcb_poll->attrib_res_len); | |
177 | if (nfcb_poll->attrib_res_len > 0) { | |
178 | memcpy(nfcb_poll->attrib_res, | |
179 | data, | |
180 | nfcb_poll->attrib_res_len); | |
181 | } | |
182 | break; | |
183 | ||
6a2968aa | 184 | default: |
ed1e0ad8 JP |
185 | pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", |
186 | ntf->activation_rf_tech_and_mode); | |
6a2968aa IE |
187 | return -EPROTO; |
188 | } | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static void nci_target_found(struct nci_dev *ndev, | |
e8c0dacd | 194 | struct nci_rf_intf_activated_ntf *ntf) |
6a2968aa IE |
195 | { |
196 | struct nfc_target nfc_tgt; | |
197 | ||
d5a2ca60 IE |
198 | memset(&nfc_tgt, 0, sizeof(nfc_tgt)); |
199 | ||
200 | if (ntf->rf_protocol == NCI_RF_PROTOCOL_T2T) | |
6a2968aa | 201 | nfc_tgt.supported_protocols = NFC_PROTO_MIFARE_MASK; |
d5a2ca60 | 202 | else if (ntf->rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) |
6a2968aa | 203 | nfc_tgt.supported_protocols = NFC_PROTO_ISO14443_MASK; |
d5a2ca60 IE |
204 | else if (ntf->rf_protocol == NCI_RF_PROTOCOL_T3T) |
205 | nfc_tgt.supported_protocols = NFC_PROTO_FELICA_MASK; | |
6a2968aa IE |
206 | |
207 | if (!(nfc_tgt.supported_protocols & ndev->poll_prots)) { | |
20c239c1 | 208 | pr_debug("the target found does not have the desired protocol\n"); |
6a2968aa IE |
209 | return; |
210 | } | |
211 | ||
20c239c1 JP |
212 | pr_debug("new target found, supported_protocols 0x%x\n", |
213 | nfc_tgt.supported_protocols); | |
6a2968aa | 214 | |
d5a2ca60 IE |
215 | if (ntf->activation_rf_tech_and_mode == NCI_NFC_A_PASSIVE_POLL_MODE) { |
216 | nfc_tgt.sens_res = | |
217 | ntf->rf_tech_specific_params.nfca_poll.sens_res; | |
218 | nfc_tgt.sel_res = | |
219 | ntf->rf_tech_specific_params.nfca_poll.sel_res; | |
220 | nfc_tgt.nfcid1_len = | |
221 | ntf->rf_tech_specific_params.nfca_poll.nfcid1_len; | |
222 | if (nfc_tgt.nfcid1_len > 0) { | |
223 | memcpy(nfc_tgt.nfcid1, | |
224 | ntf->rf_tech_specific_params.nfca_poll.nfcid1, | |
225 | nfc_tgt.nfcid1_len); | |
226 | } | |
227 | } else if (ntf->activation_rf_tech_and_mode == | |
228 | NCI_NFC_B_PASSIVE_POLL_MODE) { | |
229 | nfc_tgt.sensb_res_len = | |
230 | ntf->rf_tech_specific_params.nfcb_poll.sensb_res_len; | |
231 | if (nfc_tgt.sensb_res_len > 0) { | |
232 | memcpy(nfc_tgt.sensb_res, | |
233 | ntf->rf_tech_specific_params.nfcb_poll.sensb_res, | |
234 | nfc_tgt.sensb_res_len); | |
235 | } | |
236 | } else if (ntf->activation_rf_tech_and_mode == | |
237 | NCI_NFC_F_PASSIVE_POLL_MODE) { | |
238 | nfc_tgt.sensf_res_len = | |
239 | ntf->rf_tech_specific_params.nfcf_poll.sensf_res_len; | |
240 | if (nfc_tgt.sensf_res_len > 0) { | |
241 | memcpy(nfc_tgt.sensf_res, | |
242 | ntf->rf_tech_specific_params.nfcf_poll.sensf_res, | |
243 | nfc_tgt.sensf_res_len); | |
244 | } | |
245 | } | |
246 | ||
6a2968aa | 247 | ndev->target_available_prots = nfc_tgt.supported_protocols; |
637d85a7 IE |
248 | ndev->max_data_pkt_payload_size = ntf->max_data_pkt_payload_size; |
249 | ndev->initial_num_credits = ntf->initial_num_credits; | |
250 | ||
251 | /* set the available credits to initial value */ | |
252 | atomic_set(&ndev->credits_cnt, ndev->initial_num_credits); | |
6a2968aa IE |
253 | |
254 | nfc_targets_found(ndev->nfc_dev, &nfc_tgt, 1); | |
255 | } | |
256 | ||
e8c0dacd IE |
257 | static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, |
258 | struct sk_buff *skb) | |
6a2968aa | 259 | { |
e8c0dacd | 260 | struct nci_rf_intf_activated_ntf ntf; |
6a2968aa | 261 | __u8 *data = skb->data; |
e8c0dacd | 262 | int err = 0; |
6a2968aa | 263 | |
8939e47f | 264 | atomic_set(&ndev->state, NCI_POLL_ACTIVE); |
6a2968aa | 265 | |
e8c0dacd | 266 | ntf.rf_discovery_id = *data++; |
637d85a7 | 267 | ntf.rf_interface = *data++; |
6a2968aa | 268 | ntf.rf_protocol = *data++; |
e8c0dacd | 269 | ntf.activation_rf_tech_and_mode = *data++; |
637d85a7 IE |
270 | ntf.max_data_pkt_payload_size = *data++; |
271 | ntf.initial_num_credits = *data++; | |
6a2968aa IE |
272 | ntf.rf_tech_specific_params_len = *data++; |
273 | ||
20c239c1 | 274 | pr_debug("rf_discovery_id %d\n", ntf.rf_discovery_id); |
637d85a7 | 275 | pr_debug("rf_interface 0x%x\n", ntf.rf_interface); |
20c239c1 JP |
276 | pr_debug("rf_protocol 0x%x\n", ntf.rf_protocol); |
277 | pr_debug("activation_rf_tech_and_mode 0x%x\n", | |
278 | ntf.activation_rf_tech_and_mode); | |
637d85a7 IE |
279 | pr_debug("max_data_pkt_payload_size 0x%x\n", |
280 | ntf.max_data_pkt_payload_size); | |
281 | pr_debug("initial_num_credits 0x%x\n", ntf.initial_num_credits); | |
20c239c1 JP |
282 | pr_debug("rf_tech_specific_params_len %d\n", |
283 | ntf.rf_tech_specific_params_len); | |
6a2968aa | 284 | |
e8c0dacd IE |
285 | if (ntf.rf_tech_specific_params_len > 0) { |
286 | switch (ntf.activation_rf_tech_and_mode) { | |
287 | case NCI_NFC_A_PASSIVE_POLL_MODE: | |
288 | data = nci_extract_rf_params_nfca_passive_poll(ndev, | |
289 | &ntf, data); | |
290 | break; | |
291 | ||
d5a2ca60 IE |
292 | case NCI_NFC_B_PASSIVE_POLL_MODE: |
293 | data = nci_extract_rf_params_nfcb_passive_poll(ndev, | |
294 | &ntf, data); | |
295 | break; | |
296 | ||
297 | case NCI_NFC_F_PASSIVE_POLL_MODE: | |
298 | data = nci_extract_rf_params_nfcf_passive_poll(ndev, | |
299 | &ntf, data); | |
300 | break; | |
301 | ||
e8c0dacd | 302 | default: |
ed1e0ad8 JP |
303 | pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", |
304 | ntf.activation_rf_tech_and_mode); | |
e8c0dacd IE |
305 | return; |
306 | } | |
307 | } | |
6a2968aa | 308 | |
e8c0dacd IE |
309 | ntf.data_exch_rf_tech_and_mode = *data++; |
310 | ntf.data_exch_tx_bit_rate = *data++; | |
311 | ntf.data_exch_rx_bit_rate = *data++; | |
312 | ntf.activation_params_len = *data++; | |
313 | ||
20c239c1 JP |
314 | pr_debug("data_exch_rf_tech_and_mode 0x%x\n", |
315 | ntf.data_exch_rf_tech_and_mode); | |
316 | pr_debug("data_exch_tx_bit_rate 0x%x\n", | |
317 | ntf.data_exch_tx_bit_rate); | |
318 | pr_debug("data_exch_rx_bit_rate 0x%x\n", | |
319 | ntf.data_exch_rx_bit_rate); | |
320 | pr_debug("activation_params_len %d\n", | |
321 | ntf.activation_params_len); | |
e8c0dacd IE |
322 | |
323 | if (ntf.activation_params_len > 0) { | |
637d85a7 | 324 | switch (ntf.rf_interface) { |
e8c0dacd IE |
325 | case NCI_RF_INTERFACE_ISO_DEP: |
326 | err = nci_extract_activation_params_iso_dep(ndev, | |
327 | &ntf, data); | |
328 | break; | |
329 | ||
330 | case NCI_RF_INTERFACE_FRAME: | |
331 | /* no activation params */ | |
332 | break; | |
333 | ||
334 | default: | |
637d85a7 IE |
335 | pr_err("unsupported rf_interface 0x%x\n", |
336 | ntf.rf_interface); | |
e8c0dacd IE |
337 | return; |
338 | } | |
6a2968aa IE |
339 | } |
340 | ||
e8c0dacd | 341 | if (!err) |
6a2968aa IE |
342 | nci_target_found(ndev, &ntf); |
343 | } | |
344 | ||
345 | static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, | |
346 | struct sk_buff *skb) | |
347 | { | |
e8c0dacd | 348 | struct nci_rf_deactivate_ntf *ntf = (void *) skb->data; |
6a2968aa | 349 | |
20c239c1 | 350 | pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason); |
6a2968aa | 351 | |
8939e47f | 352 | atomic_set(&ndev->state, NCI_IDLE); |
6a2968aa IE |
353 | ndev->target_active_prot = 0; |
354 | ||
355 | /* drop tx data queue */ | |
356 | skb_queue_purge(&ndev->tx_q); | |
357 | ||
358 | /* drop partial rx data packet */ | |
359 | if (ndev->rx_data_reassembly) { | |
360 | kfree_skb(ndev->rx_data_reassembly); | |
361 | ndev->rx_data_reassembly = 0; | |
362 | } | |
363 | ||
364 | /* complete the data exchange transaction, if exists */ | |
38f04c6b | 365 | if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) |
6a2968aa | 366 | nci_data_exchange_complete(ndev, NULL, -EIO); |
bd7e01bc IE |
367 | |
368 | nci_req_complete(ndev, NCI_STATUS_OK); | |
6a2968aa IE |
369 | } |
370 | ||
371 | void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) | |
372 | { | |
373 | __u16 ntf_opcode = nci_opcode(skb->data); | |
374 | ||
20c239c1 JP |
375 | pr_debug("NCI RX: MT=ntf, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", |
376 | nci_pbf(skb->data), | |
377 | nci_opcode_gid(ntf_opcode), | |
378 | nci_opcode_oid(ntf_opcode), | |
379 | nci_plen(skb->data)); | |
6a2968aa IE |
380 | |
381 | /* strip the nci control header */ | |
382 | skb_pull(skb, NCI_CTRL_HDR_SIZE); | |
383 | ||
384 | switch (ntf_opcode) { | |
385 | case NCI_OP_CORE_CONN_CREDITS_NTF: | |
386 | nci_core_conn_credits_ntf_packet(ndev, skb); | |
387 | break; | |
388 | ||
004161cb IE |
389 | case NCI_OP_CORE_INTF_ERROR_NTF: |
390 | nci_core_conn_intf_error_ntf_packet(ndev, skb); | |
391 | break; | |
392 | ||
e8c0dacd IE |
393 | case NCI_OP_RF_INTF_ACTIVATED_NTF: |
394 | nci_rf_intf_activated_ntf_packet(ndev, skb); | |
6a2968aa IE |
395 | break; |
396 | ||
397 | case NCI_OP_RF_DEACTIVATE_NTF: | |
398 | nci_rf_deactivate_ntf_packet(ndev, skb); | |
399 | break; | |
400 | ||
401 | default: | |
ed1e0ad8 | 402 | pr_err("unknown ntf opcode 0x%x\n", ntf_opcode); |
6a2968aa IE |
403 | break; |
404 | } | |
405 | ||
406 | kfree_skb(skb); | |
407 | } |