Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
6a2968aa IE |
2 | /* |
3 | * The NFC Controller Interface is the communication protocol between an | |
4 | * NFC Controller (NFCC) and a Device Host (DH). | |
5 | * | |
cfdbeeaf | 6 | * Copyright (C) 2014 Marvell International Ltd. |
6a2968aa IE |
7 | * Copyright (C) 2011 Texas Instruments, Inc. |
8 | * | |
9 | * Written by Ilan Elias <ilane@ti.com> | |
10 | * | |
11 | * Acknowledgements: | |
12 | * This file is based on hci_event.c, which was written | |
13 | * by Maxim Krasnyansky. | |
6a2968aa IE |
14 | */ |
15 | ||
52858b51 | 16 | #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ |
ed1e0ad8 | 17 | |
6a2968aa IE |
18 | #include <linux/types.h> |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/bitops.h> | |
21 | #include <linux/skbuff.h> | |
22 | ||
23 | #include "../nfc.h" | |
24 | #include <net/nfc/nci.h> | |
25 | #include <net/nfc/nci_core.h> | |
26 | #include <linux/nfc.h> | |
27 | ||
28 | /* Handle NCI Notification packets */ | |
29 | ||
bcd684aa | 30 | static void nci_core_reset_ntf_packet(struct nci_dev *ndev, |
ddecf555 | 31 | const struct sk_buff *skb) |
bcd684aa BJ |
32 | { |
33 | /* Handle NCI 2.x core reset notification */ | |
ddecf555 | 34 | const struct nci_core_reset_ntf *ntf = (void *)skb->data; |
bcd684aa BJ |
35 | |
36 | ndev->nci_ver = ntf->nci_ver; | |
37 | pr_debug("nci_ver 0x%x, config_status 0x%x\n", | |
38 | ntf->nci_ver, ntf->config_status); | |
39 | ||
40 | ndev->manufact_id = ntf->manufact_id; | |
41 | ndev->manufact_specific_info = | |
42 | __le32_to_cpu(ntf->manufact_specific_info); | |
43 | ||
44 | nci_req_complete(ndev, NCI_STATUS_OK); | |
45 | } | |
46 | ||
6a2968aa | 47 | static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, |
eb9bc6e9 | 48 | struct sk_buff *skb) |
6a2968aa IE |
49 | { |
50 | struct nci_core_conn_credit_ntf *ntf = (void *) skb->data; | |
77411df5 | 51 | struct nci_conn_info *conn_info; |
6a2968aa IE |
52 | int i; |
53 | ||
24bf3304 | 54 | pr_debug("num_entries %d\n", ntf->num_entries); |
6a2968aa IE |
55 | |
56 | if (ntf->num_entries > NCI_MAX_NUM_CONN) | |
57 | ntf->num_entries = NCI_MAX_NUM_CONN; | |
58 | ||
59 | /* update the credits */ | |
60 | for (i = 0; i < ntf->num_entries; i++) { | |
637d85a7 IE |
61 | ntf->conn_entries[i].conn_id = |
62 | nci_conn_id(&ntf->conn_entries[i].conn_id); | |
63 | ||
20c239c1 JP |
64 | pr_debug("entry[%d]: conn_id %d, credits %d\n", |
65 | i, ntf->conn_entries[i].conn_id, | |
66 | ntf->conn_entries[i].credits); | |
6a2968aa | 67 | |
4aeee687 CR |
68 | conn_info = nci_get_conn_info_by_conn_id(ndev, |
69 | ntf->conn_entries[i].conn_id); | |
70 | if (!conn_info) | |
71 | return; | |
72 | ||
73 | atomic_add(ntf->conn_entries[i].credits, | |
74 | &conn_info->credits_cnt); | |
6a2968aa IE |
75 | } |
76 | ||
77 | /* trigger the next tx */ | |
78 | if (!skb_queue_empty(&ndev->tx_q)) | |
79 | queue_work(ndev->tx_wq, &ndev->tx_work); | |
80 | } | |
81 | ||
019c4fba | 82 | static void nci_core_generic_error_ntf_packet(struct nci_dev *ndev, |
ddecf555 | 83 | const struct sk_buff *skb) |
019c4fba IE |
84 | { |
85 | __u8 status = skb->data[0]; | |
86 | ||
87 | pr_debug("status 0x%x\n", status); | |
88 | ||
89 | if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { | |
90 | /* Activation failed, so complete the request | |
eb9bc6e9 | 91 | (the state remains the same) */ |
019c4fba IE |
92 | nci_req_complete(ndev, status); |
93 | } | |
94 | } | |
95 | ||
004161cb IE |
96 | static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev, |
97 | struct sk_buff *skb) | |
98 | { | |
99 | struct nci_core_intf_error_ntf *ntf = (void *) skb->data; | |
100 | ||
101 | ntf->conn_id = nci_conn_id(&ntf->conn_id); | |
102 | ||
103 | pr_debug("status 0x%x, conn_id %d\n", ntf->status, ntf->conn_id); | |
104 | ||
105 | /* complete the data exchange transaction, if exists */ | |
106 | if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) | |
4aeee687 | 107 | nci_data_exchange_complete(ndev, NULL, ntf->conn_id, -EIO); |
004161cb IE |
108 | } |
109 | ||
ddecf555 KK |
110 | static const __u8 * |
111 | nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev, | |
112 | struct rf_tech_specific_params_nfca_poll *nfca_poll, | |
113 | const __u8 *data) | |
6a2968aa | 114 | { |
3ff24012 | 115 | nfca_poll->sens_res = __le16_to_cpu(*((__le16 *)data)); |
6a2968aa IE |
116 | data += 2; |
117 | ||
67de956f | 118 | nfca_poll->nfcid1_len = min_t(__u8, *data++, NFC_NFCID1_MAXSIZE); |
6a2968aa | 119 | |
20c239c1 JP |
120 | pr_debug("sens_res 0x%x, nfcid1_len %d\n", |
121 | nfca_poll->sens_res, nfca_poll->nfcid1_len); | |
6a2968aa IE |
122 | |
123 | memcpy(nfca_poll->nfcid1, data, nfca_poll->nfcid1_len); | |
124 | data += nfca_poll->nfcid1_len; | |
125 | ||
126 | nfca_poll->sel_res_len = *data++; | |
127 | ||
128 | if (nfca_poll->sel_res_len != 0) | |
129 | nfca_poll->sel_res = *data++; | |
130 | ||
20c239c1 JP |
131 | pr_debug("sel_res_len %d, sel_res 0x%x\n", |
132 | nfca_poll->sel_res_len, | |
133 | nfca_poll->sel_res); | |
e8c0dacd IE |
134 | |
135 | return data; | |
136 | } | |
137 | ||
ddecf555 KK |
138 | static const __u8 * |
139 | nci_extract_rf_params_nfcb_passive_poll(struct nci_dev *ndev, | |
140 | struct rf_tech_specific_params_nfcb_poll *nfcb_poll, | |
141 | const __u8 *data) | |
d5a2ca60 | 142 | { |
67de956f | 143 | nfcb_poll->sensb_res_len = min_t(__u8, *data++, NFC_SENSB_RES_MAXSIZE); |
d5a2ca60 IE |
144 | |
145 | pr_debug("sensb_res_len %d\n", nfcb_poll->sensb_res_len); | |
146 | ||
147 | memcpy(nfcb_poll->sensb_res, data, nfcb_poll->sensb_res_len); | |
148 | data += nfcb_poll->sensb_res_len; | |
149 | ||
150 | return data; | |
151 | } | |
152 | ||
ddecf555 KK |
153 | static const __u8 * |
154 | nci_extract_rf_params_nfcf_passive_poll(struct nci_dev *ndev, | |
155 | struct rf_tech_specific_params_nfcf_poll *nfcf_poll, | |
156 | const __u8 *data) | |
d5a2ca60 | 157 | { |
d5a2ca60 | 158 | nfcf_poll->bit_rate = *data++; |
67de956f | 159 | nfcf_poll->sensf_res_len = min_t(__u8, *data++, NFC_SENSF_RES_MAXSIZE); |
d5a2ca60 IE |
160 | |
161 | pr_debug("bit_rate %d, sensf_res_len %d\n", | |
eb9bc6e9 | 162 | nfcf_poll->bit_rate, nfcf_poll->sensf_res_len); |
d5a2ca60 IE |
163 | |
164 | memcpy(nfcf_poll->sensf_res, data, nfcf_poll->sensf_res_len); | |
165 | data += nfcf_poll->sensf_res_len; | |
166 | ||
167 | return data; | |
168 | } | |
169 | ||
ddecf555 KK |
170 | static const __u8 * |
171 | nci_extract_rf_params_nfcv_passive_poll(struct nci_dev *ndev, | |
172 | struct rf_tech_specific_params_nfcv_poll *nfcv_poll, | |
173 | const __u8 *data) | |
cfdbeeaf VC |
174 | { |
175 | ++data; | |
176 | nfcv_poll->dsfid = *data++; | |
177 | memcpy(nfcv_poll->uid, data, NFC_ISO15693_UID_MAXSIZE); | |
178 | data += NFC_ISO15693_UID_MAXSIZE; | |
179 | return data; | |
180 | } | |
181 | ||
ddecf555 KK |
182 | static const __u8 * |
183 | nci_extract_rf_params_nfcf_passive_listen(struct nci_dev *ndev, | |
184 | struct rf_tech_specific_params_nfcf_listen *nfcf_listen, | |
185 | const __u8 *data) | |
a99903ec JL |
186 | { |
187 | nfcf_listen->local_nfcid2_len = min_t(__u8, *data++, | |
188 | NFC_NFCID2_MAXSIZE); | |
189 | memcpy(nfcf_listen->local_nfcid2, data, nfcf_listen->local_nfcid2_len); | |
190 | data += nfcf_listen->local_nfcid2_len; | |
191 | ||
192 | return data; | |
193 | } | |
194 | ||
c7dea252 | 195 | static __u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol) |
9e87f9a9 CR |
196 | { |
197 | if (ndev->ops->get_rfprotocol) | |
198 | return ndev->ops->get_rfprotocol(ndev, rf_protocol); | |
199 | return 0; | |
200 | } | |
201 | ||
019c4fba IE |
202 | static int nci_add_new_protocol(struct nci_dev *ndev, |
203 | struct nfc_target *target, | |
204 | __u8 rf_protocol, | |
205 | __u8 rf_tech_and_mode, | |
ddecf555 | 206 | const void *params) |
019c4fba | 207 | { |
ddecf555 KK |
208 | const struct rf_tech_specific_params_nfca_poll *nfca_poll; |
209 | const struct rf_tech_specific_params_nfcb_poll *nfcb_poll; | |
210 | const struct rf_tech_specific_params_nfcf_poll *nfcf_poll; | |
211 | const struct rf_tech_specific_params_nfcv_poll *nfcv_poll; | |
019c4fba IE |
212 | __u32 protocol; |
213 | ||
bb15b217 CR |
214 | if (rf_protocol == NCI_RF_PROTOCOL_T1T) |
215 | protocol = NFC_PROTO_JEWEL_MASK; | |
216 | else if (rf_protocol == NCI_RF_PROTOCOL_T2T) | |
019c4fba IE |
217 | protocol = NFC_PROTO_MIFARE_MASK; |
218 | else if (rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) | |
01d719a2 SO |
219 | if (rf_tech_and_mode == NCI_NFC_A_PASSIVE_POLL_MODE) |
220 | protocol = NFC_PROTO_ISO14443_MASK; | |
221 | else | |
222 | protocol = NFC_PROTO_ISO14443_B_MASK; | |
019c4fba IE |
223 | else if (rf_protocol == NCI_RF_PROTOCOL_T3T) |
224 | protocol = NFC_PROTO_FELICA_MASK; | |
767f19ae IE |
225 | else if (rf_protocol == NCI_RF_PROTOCOL_NFC_DEP) |
226 | protocol = NFC_PROTO_NFC_DEP_MASK; | |
cfdbeeaf VC |
227 | else if (rf_protocol == NCI_RF_PROTOCOL_T5T) |
228 | protocol = NFC_PROTO_ISO15693_MASK; | |
019c4fba | 229 | else |
9e87f9a9 | 230 | protocol = nci_get_prop_rf_protocol(ndev, rf_protocol); |
019c4fba IE |
231 | |
232 | if (!(protocol & ndev->poll_prots)) { | |
233 | pr_err("the target found does not have the desired protocol\n"); | |
234 | return -EPROTO; | |
235 | } | |
236 | ||
237 | if (rf_tech_and_mode == NCI_NFC_A_PASSIVE_POLL_MODE) { | |
238 | nfca_poll = (struct rf_tech_specific_params_nfca_poll *)params; | |
239 | ||
240 | target->sens_res = nfca_poll->sens_res; | |
241 | target->sel_res = nfca_poll->sel_res; | |
242 | target->nfcid1_len = nfca_poll->nfcid1_len; | |
e329e710 KC |
243 | if (target->nfcid1_len > ARRAY_SIZE(target->nfcid1)) |
244 | return -EPROTO; | |
019c4fba IE |
245 | if (target->nfcid1_len > 0) { |
246 | memcpy(target->nfcid1, nfca_poll->nfcid1, | |
eb9bc6e9 | 247 | target->nfcid1_len); |
019c4fba IE |
248 | } |
249 | } else if (rf_tech_and_mode == NCI_NFC_B_PASSIVE_POLL_MODE) { | |
250 | nfcb_poll = (struct rf_tech_specific_params_nfcb_poll *)params; | |
251 | ||
252 | target->sensb_res_len = nfcb_poll->sensb_res_len; | |
e329e710 KC |
253 | if (target->sensb_res_len > ARRAY_SIZE(target->sensb_res)) |
254 | return -EPROTO; | |
019c4fba IE |
255 | if (target->sensb_res_len > 0) { |
256 | memcpy(target->sensb_res, nfcb_poll->sensb_res, | |
eb9bc6e9 | 257 | target->sensb_res_len); |
019c4fba IE |
258 | } |
259 | } else if (rf_tech_and_mode == NCI_NFC_F_PASSIVE_POLL_MODE) { | |
260 | nfcf_poll = (struct rf_tech_specific_params_nfcf_poll *)params; | |
261 | ||
262 | target->sensf_res_len = nfcf_poll->sensf_res_len; | |
e329e710 KC |
263 | if (target->sensf_res_len > ARRAY_SIZE(target->sensf_res)) |
264 | return -EPROTO; | |
019c4fba IE |
265 | if (target->sensf_res_len > 0) { |
266 | memcpy(target->sensf_res, nfcf_poll->sensf_res, | |
eb9bc6e9 | 267 | target->sensf_res_len); |
019c4fba | 268 | } |
cfdbeeaf VC |
269 | } else if (rf_tech_and_mode == NCI_NFC_V_PASSIVE_POLL_MODE) { |
270 | nfcv_poll = (struct rf_tech_specific_params_nfcv_poll *)params; | |
271 | ||
272 | target->is_iso15693 = 1; | |
273 | target->iso15693_dsfid = nfcv_poll->dsfid; | |
274 | memcpy(target->iso15693_uid, nfcv_poll->uid, NFC_ISO15693_UID_MAXSIZE); | |
019c4fba IE |
275 | } else { |
276 | pr_err("unsupported rf_tech_and_mode 0x%x\n", rf_tech_and_mode); | |
277 | return -EPROTO; | |
278 | } | |
279 | ||
280 | target->supported_protocols |= protocol; | |
281 | ||
282 | pr_debug("protocol 0x%x\n", protocol); | |
283 | ||
284 | return 0; | |
285 | } | |
286 | ||
287 | static void nci_add_new_target(struct nci_dev *ndev, | |
ddecf555 | 288 | const struct nci_rf_discover_ntf *ntf) |
019c4fba IE |
289 | { |
290 | struct nfc_target *target; | |
291 | int i, rc; | |
292 | ||
293 | for (i = 0; i < ndev->n_targets; i++) { | |
294 | target = &ndev->targets[i]; | |
c4fbb651 | 295 | if (target->logical_idx == ntf->rf_discovery_id) { |
019c4fba IE |
296 | /* This target already exists, add the new protocol */ |
297 | nci_add_new_protocol(ndev, target, ntf->rf_protocol, | |
eb9bc6e9 SO |
298 | ntf->rf_tech_and_mode, |
299 | &ntf->rf_tech_specific_params); | |
019c4fba IE |
300 | return; |
301 | } | |
302 | } | |
303 | ||
304 | /* This is a new target, check if we've enough room */ | |
305 | if (ndev->n_targets == NCI_MAX_DISCOVERED_TARGETS) { | |
306 | pr_debug("not enough room, ignoring new target...\n"); | |
307 | return; | |
308 | } | |
309 | ||
310 | target = &ndev->targets[ndev->n_targets]; | |
311 | ||
312 | rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol, | |
eb9bc6e9 SO |
313 | ntf->rf_tech_and_mode, |
314 | &ntf->rf_tech_specific_params); | |
019c4fba | 315 | if (!rc) { |
c4fbb651 | 316 | target->logical_idx = ntf->rf_discovery_id; |
019c4fba IE |
317 | ndev->n_targets++; |
318 | ||
c4fbb651 | 319 | pr_debug("logical idx %d, n_targets %d\n", target->logical_idx, |
eb9bc6e9 | 320 | ndev->n_targets); |
019c4fba IE |
321 | } |
322 | } | |
323 | ||
324 | void nci_clear_target_list(struct nci_dev *ndev) | |
325 | { | |
326 | memset(ndev->targets, 0, | |
eb9bc6e9 | 327 | (sizeof(struct nfc_target)*NCI_MAX_DISCOVERED_TARGETS)); |
019c4fba IE |
328 | |
329 | ndev->n_targets = 0; | |
330 | } | |
331 | ||
332 | static void nci_rf_discover_ntf_packet(struct nci_dev *ndev, | |
ddecf555 | 333 | const struct sk_buff *skb) |
019c4fba IE |
334 | { |
335 | struct nci_rf_discover_ntf ntf; | |
ddecf555 | 336 | const __u8 *data = skb->data; |
019c4fba IE |
337 | bool add_target = true; |
338 | ||
339 | ntf.rf_discovery_id = *data++; | |
340 | ntf.rf_protocol = *data++; | |
341 | ntf.rf_tech_and_mode = *data++; | |
342 | ntf.rf_tech_specific_params_len = *data++; | |
343 | ||
344 | pr_debug("rf_discovery_id %d\n", ntf.rf_discovery_id); | |
345 | pr_debug("rf_protocol 0x%x\n", ntf.rf_protocol); | |
346 | pr_debug("rf_tech_and_mode 0x%x\n", ntf.rf_tech_and_mode); | |
347 | pr_debug("rf_tech_specific_params_len %d\n", | |
eb9bc6e9 | 348 | ntf.rf_tech_specific_params_len); |
019c4fba IE |
349 | |
350 | if (ntf.rf_tech_specific_params_len > 0) { | |
351 | switch (ntf.rf_tech_and_mode) { | |
352 | case NCI_NFC_A_PASSIVE_POLL_MODE: | |
353 | data = nci_extract_rf_params_nfca_passive_poll(ndev, | |
354 | &(ntf.rf_tech_specific_params.nfca_poll), data); | |
355 | break; | |
356 | ||
357 | case NCI_NFC_B_PASSIVE_POLL_MODE: | |
358 | data = nci_extract_rf_params_nfcb_passive_poll(ndev, | |
359 | &(ntf.rf_tech_specific_params.nfcb_poll), data); | |
360 | break; | |
361 | ||
362 | case NCI_NFC_F_PASSIVE_POLL_MODE: | |
363 | data = nci_extract_rf_params_nfcf_passive_poll(ndev, | |
364 | &(ntf.rf_tech_specific_params.nfcf_poll), data); | |
365 | break; | |
366 | ||
cfdbeeaf VC |
367 | case NCI_NFC_V_PASSIVE_POLL_MODE: |
368 | data = nci_extract_rf_params_nfcv_passive_poll(ndev, | |
369 | &(ntf.rf_tech_specific_params.nfcv_poll), data); | |
370 | break; | |
371 | ||
019c4fba IE |
372 | default: |
373 | pr_err("unsupported rf_tech_and_mode 0x%x\n", | |
374 | ntf.rf_tech_and_mode); | |
375 | data += ntf.rf_tech_specific_params_len; | |
376 | add_target = false; | |
377 | } | |
378 | } | |
379 | ||
380 | ntf.ntf_type = *data++; | |
381 | pr_debug("ntf_type %d\n", ntf.ntf_type); | |
382 | ||
383 | if (add_target == true) | |
384 | nci_add_new_target(ndev, &ntf); | |
385 | ||
386 | if (ntf.ntf_type == NCI_DISCOVER_NTF_TYPE_MORE) { | |
387 | atomic_set(&ndev->state, NCI_W4_ALL_DISCOVERIES); | |
388 | } else { | |
389 | atomic_set(&ndev->state, NCI_W4_HOST_SELECT); | |
390 | nfc_targets_found(ndev->nfc_dev, ndev->targets, | |
eb9bc6e9 | 391 | ndev->n_targets); |
019c4fba IE |
392 | } |
393 | } | |
394 | ||
e8c0dacd | 395 | static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, |
ddecf555 KK |
396 | struct nci_rf_intf_activated_ntf *ntf, |
397 | const __u8 *data) | |
e8c0dacd IE |
398 | { |
399 | struct activation_params_nfca_poll_iso_dep *nfca_poll; | |
d5a2ca60 | 400 | struct activation_params_nfcb_poll_iso_dep *nfcb_poll; |
e8c0dacd IE |
401 | |
402 | switch (ntf->activation_rf_tech_and_mode) { | |
403 | case NCI_NFC_A_PASSIVE_POLL_MODE: | |
404 | nfca_poll = &ntf->activation_params.nfca_poll_iso_dep; | |
9907cda9 | 405 | nfca_poll->rats_res_len = min_t(__u8, *data++, NFC_ATS_MAXSIZE); |
d5a2ca60 | 406 | pr_debug("rats_res_len %d\n", nfca_poll->rats_res_len); |
e8c0dacd IE |
407 | if (nfca_poll->rats_res_len > 0) { |
408 | memcpy(nfca_poll->rats_res, | |
eb9bc6e9 | 409 | data, nfca_poll->rats_res_len); |
6a2968aa IE |
410 | } |
411 | break; | |
412 | ||
d5a2ca60 IE |
413 | case NCI_NFC_B_PASSIVE_POLL_MODE: |
414 | nfcb_poll = &ntf->activation_params.nfcb_poll_iso_dep; | |
67de956f | 415 | nfcb_poll->attrib_res_len = min_t(__u8, *data++, 50); |
eb9bc6e9 | 416 | pr_debug("attrib_res_len %d\n", nfcb_poll->attrib_res_len); |
d5a2ca60 IE |
417 | if (nfcb_poll->attrib_res_len > 0) { |
418 | memcpy(nfcb_poll->attrib_res, | |
eb9bc6e9 | 419 | data, nfcb_poll->attrib_res_len); |
d5a2ca60 IE |
420 | } |
421 | break; | |
422 | ||
6a2968aa | 423 | default: |
ed1e0ad8 JP |
424 | pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", |
425 | ntf->activation_rf_tech_and_mode); | |
019c4fba | 426 | return NCI_STATUS_RF_PROTOCOL_ERROR; |
6a2968aa IE |
427 | } |
428 | ||
019c4fba | 429 | return NCI_STATUS_OK; |
6a2968aa IE |
430 | } |
431 | ||
ac206838 | 432 | static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev, |
ddecf555 KK |
433 | struct nci_rf_intf_activated_ntf *ntf, |
434 | const __u8 *data) | |
ac206838 IE |
435 | { |
436 | struct activation_params_poll_nfc_dep *poll; | |
a99903ec | 437 | struct activation_params_listen_nfc_dep *listen; |
ac206838 IE |
438 | |
439 | switch (ntf->activation_rf_tech_and_mode) { | |
440 | case NCI_NFC_A_PASSIVE_POLL_MODE: | |
441 | case NCI_NFC_F_PASSIVE_POLL_MODE: | |
442 | poll = &ntf->activation_params.poll_nfc_dep; | |
a99903ec JL |
443 | poll->atr_res_len = min_t(__u8, *data++, |
444 | NFC_ATR_RES_MAXSIZE - 2); | |
ac206838 | 445 | pr_debug("atr_res_len %d\n", poll->atr_res_len); |
c79d9f9e HT |
446 | if (poll->atr_res_len > 0) |
447 | memcpy(poll->atr_res, data, poll->atr_res_len); | |
ac206838 IE |
448 | break; |
449 | ||
a99903ec JL |
450 | case NCI_NFC_A_PASSIVE_LISTEN_MODE: |
451 | case NCI_NFC_F_PASSIVE_LISTEN_MODE: | |
452 | listen = &ntf->activation_params.listen_nfc_dep; | |
453 | listen->atr_req_len = min_t(__u8, *data++, | |
454 | NFC_ATR_REQ_MAXSIZE - 2); | |
455 | pr_debug("atr_req_len %d\n", listen->atr_req_len); | |
456 | if (listen->atr_req_len > 0) | |
457 | memcpy(listen->atr_req, data, listen->atr_req_len); | |
458 | break; | |
459 | ||
ac206838 IE |
460 | default: |
461 | pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", | |
462 | ntf->activation_rf_tech_and_mode); | |
463 | return NCI_STATUS_RF_PROTOCOL_ERROR; | |
464 | } | |
465 | ||
466 | return NCI_STATUS_OK; | |
467 | } | |
468 | ||
019c4fba | 469 | static void nci_target_auto_activated(struct nci_dev *ndev, |
ddecf555 | 470 | const struct nci_rf_intf_activated_ntf *ntf) |
6a2968aa | 471 | { |
019c4fba IE |
472 | struct nfc_target *target; |
473 | int rc; | |
6a2968aa | 474 | |
019c4fba | 475 | target = &ndev->targets[ndev->n_targets]; |
d5a2ca60 | 476 | |
019c4fba | 477 | rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol, |
eb9bc6e9 SO |
478 | ntf->activation_rf_tech_and_mode, |
479 | &ntf->rf_tech_specific_params); | |
019c4fba | 480 | if (rc) |
6a2968aa | 481 | return; |
6a2968aa | 482 | |
c4fbb651 | 483 | target->logical_idx = ntf->rf_discovery_id; |
019c4fba | 484 | ndev->n_targets++; |
d5a2ca60 | 485 | |
c4fbb651 SO |
486 | pr_debug("logical idx %d, n_targets %d\n", |
487 | target->logical_idx, ndev->n_targets); | |
637d85a7 | 488 | |
019c4fba | 489 | nfc_targets_found(ndev->nfc_dev, ndev->targets, ndev->n_targets); |
6a2968aa IE |
490 | } |
491 | ||
a99903ec | 492 | static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev, |
ddecf555 | 493 | const struct nci_rf_intf_activated_ntf *ntf) |
a99903ec JL |
494 | { |
495 | ndev->remote_gb_len = 0; | |
496 | ||
497 | if (ntf->activation_params_len <= 0) | |
498 | return NCI_STATUS_OK; | |
499 | ||
500 | switch (ntf->activation_rf_tech_and_mode) { | |
501 | case NCI_NFC_A_PASSIVE_POLL_MODE: | |
502 | case NCI_NFC_F_PASSIVE_POLL_MODE: | |
a99903ec JL |
503 | ndev->remote_gb_len = min_t(__u8, |
504 | (ntf->activation_params.poll_nfc_dep.atr_res_len | |
505 | - NFC_ATR_RES_GT_OFFSET), | |
e479ce47 | 506 | NFC_ATR_RES_GB_MAXSIZE); |
a99903ec | 507 | memcpy(ndev->remote_gb, |
e479ce47 | 508 | (ntf->activation_params.poll_nfc_dep.atr_res |
a99903ec JL |
509 | + NFC_ATR_RES_GT_OFFSET), |
510 | ndev->remote_gb_len); | |
511 | break; | |
512 | ||
513 | case NCI_NFC_A_PASSIVE_LISTEN_MODE: | |
514 | case NCI_NFC_F_PASSIVE_LISTEN_MODE: | |
a99903ec JL |
515 | ndev->remote_gb_len = min_t(__u8, |
516 | (ntf->activation_params.listen_nfc_dep.atr_req_len | |
517 | - NFC_ATR_REQ_GT_OFFSET), | |
e479ce47 | 518 | NFC_ATR_REQ_GB_MAXSIZE); |
a99903ec JL |
519 | memcpy(ndev->remote_gb, |
520 | (ntf->activation_params.listen_nfc_dep.atr_req | |
521 | + NFC_ATR_REQ_GT_OFFSET), | |
522 | ndev->remote_gb_len); | |
523 | break; | |
524 | ||
525 | default: | |
526 | pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", | |
527 | ntf->activation_rf_tech_and_mode); | |
528 | return NCI_STATUS_RF_PROTOCOL_ERROR; | |
529 | } | |
530 | ||
531 | return NCI_STATUS_OK; | |
532 | } | |
533 | ||
9907cda9 JŠ |
534 | static int nci_store_ats_nfc_iso_dep(struct nci_dev *ndev, |
535 | const struct nci_rf_intf_activated_ntf *ntf) | |
536 | { | |
537 | ndev->target_ats_len = 0; | |
538 | ||
539 | if (ntf->activation_params_len <= 0) | |
540 | return NCI_STATUS_OK; | |
541 | ||
542 | if (ntf->activation_params.nfca_poll_iso_dep.rats_res_len > NFC_ATS_MAXSIZE) { | |
543 | pr_debug("ATS too long\n"); | |
544 | return NCI_STATUS_RF_PROTOCOL_ERROR; | |
545 | } | |
546 | ||
547 | if (ntf->activation_params.nfca_poll_iso_dep.rats_res_len > 0) { | |
548 | ndev->target_ats_len = ntf->activation_params.nfca_poll_iso_dep.rats_res_len; | |
549 | memcpy(ndev->target_ats, ntf->activation_params.nfca_poll_iso_dep.rats_res, | |
550 | ndev->target_ats_len); | |
551 | } | |
552 | ||
553 | return NCI_STATUS_OK; | |
554 | } | |
555 | ||
e8c0dacd | 556 | static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, |
ddecf555 | 557 | const struct sk_buff *skb) |
6a2968aa | 558 | { |
77411df5 | 559 | struct nci_conn_info *conn_info; |
e8c0dacd | 560 | struct nci_rf_intf_activated_ntf ntf; |
ddecf555 | 561 | const __u8 *data = skb->data; |
019c4fba | 562 | int err = NCI_STATUS_OK; |
6a2968aa | 563 | |
e8c0dacd | 564 | ntf.rf_discovery_id = *data++; |
637d85a7 | 565 | ntf.rf_interface = *data++; |
6a2968aa | 566 | ntf.rf_protocol = *data++; |
e8c0dacd | 567 | ntf.activation_rf_tech_and_mode = *data++; |
637d85a7 IE |
568 | ntf.max_data_pkt_payload_size = *data++; |
569 | ntf.initial_num_credits = *data++; | |
6a2968aa IE |
570 | ntf.rf_tech_specific_params_len = *data++; |
571 | ||
20c239c1 | 572 | pr_debug("rf_discovery_id %d\n", ntf.rf_discovery_id); |
637d85a7 | 573 | pr_debug("rf_interface 0x%x\n", ntf.rf_interface); |
20c239c1 JP |
574 | pr_debug("rf_protocol 0x%x\n", ntf.rf_protocol); |
575 | pr_debug("activation_rf_tech_and_mode 0x%x\n", | |
576 | ntf.activation_rf_tech_and_mode); | |
637d85a7 IE |
577 | pr_debug("max_data_pkt_payload_size 0x%x\n", |
578 | ntf.max_data_pkt_payload_size); | |
eb9bc6e9 SO |
579 | pr_debug("initial_num_credits 0x%x\n", |
580 | ntf.initial_num_credits); | |
20c239c1 JP |
581 | pr_debug("rf_tech_specific_params_len %d\n", |
582 | ntf.rf_tech_specific_params_len); | |
6a2968aa | 583 | |
6095b0f0 CR |
584 | /* If this contains a value of 0x00 (NFCEE Direct RF |
585 | * Interface) then all following parameters SHALL contain a | |
586 | * value of 0 and SHALL be ignored. | |
587 | */ | |
588 | if (ntf.rf_interface == NCI_RF_INTERFACE_NFCEE_DIRECT) | |
589 | goto listen; | |
590 | ||
e8c0dacd IE |
591 | if (ntf.rf_tech_specific_params_len > 0) { |
592 | switch (ntf.activation_rf_tech_and_mode) { | |
593 | case NCI_NFC_A_PASSIVE_POLL_MODE: | |
594 | data = nci_extract_rf_params_nfca_passive_poll(ndev, | |
019c4fba | 595 | &(ntf.rf_tech_specific_params.nfca_poll), data); |
e8c0dacd IE |
596 | break; |
597 | ||
d5a2ca60 IE |
598 | case NCI_NFC_B_PASSIVE_POLL_MODE: |
599 | data = nci_extract_rf_params_nfcb_passive_poll(ndev, | |
019c4fba | 600 | &(ntf.rf_tech_specific_params.nfcb_poll), data); |
d5a2ca60 IE |
601 | break; |
602 | ||
603 | case NCI_NFC_F_PASSIVE_POLL_MODE: | |
604 | data = nci_extract_rf_params_nfcf_passive_poll(ndev, | |
019c4fba | 605 | &(ntf.rf_tech_specific_params.nfcf_poll), data); |
d5a2ca60 IE |
606 | break; |
607 | ||
cfdbeeaf VC |
608 | case NCI_NFC_V_PASSIVE_POLL_MODE: |
609 | data = nci_extract_rf_params_nfcv_passive_poll(ndev, | |
610 | &(ntf.rf_tech_specific_params.nfcv_poll), data); | |
611 | break; | |
612 | ||
a99903ec JL |
613 | case NCI_NFC_A_PASSIVE_LISTEN_MODE: |
614 | /* no RF technology specific parameters */ | |
615 | break; | |
616 | ||
617 | case NCI_NFC_F_PASSIVE_LISTEN_MODE: | |
618 | data = nci_extract_rf_params_nfcf_passive_listen(ndev, | |
619 | &(ntf.rf_tech_specific_params.nfcf_listen), | |
620 | data); | |
621 | break; | |
622 | ||
e8c0dacd | 623 | default: |
ed1e0ad8 JP |
624 | pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", |
625 | ntf.activation_rf_tech_and_mode); | |
019c4fba IE |
626 | err = NCI_STATUS_RF_PROTOCOL_ERROR; |
627 | goto exit; | |
e8c0dacd IE |
628 | } |
629 | } | |
6a2968aa | 630 | |
e8c0dacd IE |
631 | ntf.data_exch_rf_tech_and_mode = *data++; |
632 | ntf.data_exch_tx_bit_rate = *data++; | |
633 | ntf.data_exch_rx_bit_rate = *data++; | |
634 | ntf.activation_params_len = *data++; | |
635 | ||
20c239c1 JP |
636 | pr_debug("data_exch_rf_tech_and_mode 0x%x\n", |
637 | ntf.data_exch_rf_tech_and_mode); | |
eb9bc6e9 SO |
638 | pr_debug("data_exch_tx_bit_rate 0x%x\n", ntf.data_exch_tx_bit_rate); |
639 | pr_debug("data_exch_rx_bit_rate 0x%x\n", ntf.data_exch_rx_bit_rate); | |
640 | pr_debug("activation_params_len %d\n", ntf.activation_params_len); | |
e8c0dacd IE |
641 | |
642 | if (ntf.activation_params_len > 0) { | |
637d85a7 | 643 | switch (ntf.rf_interface) { |
e8c0dacd IE |
644 | case NCI_RF_INTERFACE_ISO_DEP: |
645 | err = nci_extract_activation_params_iso_dep(ndev, | |
eb9bc6e9 | 646 | &ntf, data); |
e8c0dacd IE |
647 | break; |
648 | ||
ac206838 IE |
649 | case NCI_RF_INTERFACE_NFC_DEP: |
650 | err = nci_extract_activation_params_nfc_dep(ndev, | |
651 | &ntf, data); | |
652 | break; | |
653 | ||
e8c0dacd IE |
654 | case NCI_RF_INTERFACE_FRAME: |
655 | /* no activation params */ | |
656 | break; | |
657 | ||
658 | default: | |
637d85a7 IE |
659 | pr_err("unsupported rf_interface 0x%x\n", |
660 | ntf.rf_interface); | |
019c4fba IE |
661 | err = NCI_STATUS_RF_PROTOCOL_ERROR; |
662 | break; | |
e8c0dacd | 663 | } |
6a2968aa IE |
664 | } |
665 | ||
019c4fba IE |
666 | exit: |
667 | if (err == NCI_STATUS_OK) { | |
12bdf27d | 668 | conn_info = ndev->rf_conn_info; |
4aeee687 CR |
669 | if (!conn_info) |
670 | return; | |
671 | ||
672 | conn_info->max_pkt_payload_len = ntf.max_data_pkt_payload_size; | |
673 | conn_info->initial_num_credits = ntf.initial_num_credits; | |
019c4fba IE |
674 | |
675 | /* set the available credits to initial value */ | |
4aeee687 CR |
676 | atomic_set(&conn_info->credits_cnt, |
677 | conn_info->initial_num_credits); | |
767f19ae IE |
678 | |
679 | /* store general bytes to be reported later in dep_link_up */ | |
680 | if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) { | |
a99903ec JL |
681 | err = nci_store_general_bytes_nfc_dep(ndev, &ntf); |
682 | if (err != NCI_STATUS_OK) | |
683 | pr_err("unable to store general bytes\n"); | |
767f19ae | 684 | } |
9907cda9 JŠ |
685 | |
686 | /* store ATS to be reported later in nci_activate_target */ | |
687 | if (ntf.rf_interface == NCI_RF_INTERFACE_ISO_DEP && | |
688 | ntf.activation_rf_tech_and_mode == NCI_NFC_A_PASSIVE_POLL_MODE) { | |
689 | err = nci_store_ats_nfc_iso_dep(ndev, &ntf); | |
690 | if (err != NCI_STATUS_OK) | |
691 | pr_err("unable to store ATS\n"); | |
692 | } | |
019c4fba IE |
693 | } |
694 | ||
a99903ec JL |
695 | if (!(ntf.activation_rf_tech_and_mode & NCI_RF_TECH_MODE_LISTEN_MASK)) { |
696 | /* Poll mode */ | |
697 | if (atomic_read(&ndev->state) == NCI_DISCOVERY) { | |
698 | /* A single target was found and activated | |
699 | * automatically */ | |
700 | atomic_set(&ndev->state, NCI_POLL_ACTIVE); | |
701 | if (err == NCI_STATUS_OK) | |
702 | nci_target_auto_activated(ndev, &ntf); | |
703 | } else { /* ndev->state == NCI_W4_HOST_SELECT */ | |
704 | /* A selected target was activated, so complete the | |
705 | * request */ | |
706 | atomic_set(&ndev->state, NCI_POLL_ACTIVE); | |
707 | nci_req_complete(ndev, err); | |
708 | } | |
709 | } else { | |
6095b0f0 | 710 | listen: |
a99903ec JL |
711 | /* Listen mode */ |
712 | atomic_set(&ndev->state, NCI_LISTEN_ACTIVE); | |
713 | if (err == NCI_STATUS_OK && | |
714 | ntf.rf_protocol == NCI_RF_PROTOCOL_NFC_DEP) { | |
715 | err = nfc_tm_activated(ndev->nfc_dev, | |
716 | NFC_PROTO_NFC_DEP_MASK, | |
717 | NFC_COMM_PASSIVE, | |
718 | ndev->remote_gb, | |
719 | ndev->remote_gb_len); | |
720 | if (err != NCI_STATUS_OK) | |
721 | pr_err("error when signaling tm activation\n"); | |
722 | } | |
019c4fba | 723 | } |
6a2968aa IE |
724 | } |
725 | ||
726 | static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, | |
ddecf555 | 727 | const struct sk_buff *skb) |
6a2968aa | 728 | { |
ddecf555 KK |
729 | const struct nci_conn_info *conn_info; |
730 | const struct nci_rf_deactivate_ntf *ntf = (void *)skb->data; | |
6a2968aa | 731 | |
20c239c1 | 732 | pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason); |
6a2968aa | 733 | |
12bdf27d | 734 | conn_info = ndev->rf_conn_info; |
4aeee687 CR |
735 | if (!conn_info) |
736 | return; | |
737 | ||
6a2968aa IE |
738 | /* drop tx data queue */ |
739 | skb_queue_purge(&ndev->tx_q); | |
740 | ||
741 | /* drop partial rx data packet */ | |
742 | if (ndev->rx_data_reassembly) { | |
743 | kfree_skb(ndev->rx_data_reassembly); | |
799030b7 | 744 | ndev->rx_data_reassembly = NULL; |
6a2968aa IE |
745 | } |
746 | ||
747 | /* complete the data exchange transaction, if exists */ | |
38f04c6b | 748 | if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) |
4aeee687 CR |
749 | nci_data_exchange_complete(ndev, NULL, NCI_STATIC_RF_CONN_ID, |
750 | -EIO); | |
bd7e01bc | 751 | |
4391590c CR |
752 | switch (ntf->type) { |
753 | case NCI_DEACTIVATE_TYPE_IDLE_MODE: | |
754 | nci_clear_target_list(ndev); | |
6ff5462b | 755 | atomic_set(&ndev->state, NCI_IDLE); |
4391590c CR |
756 | break; |
757 | case NCI_DEACTIVATE_TYPE_SLEEP_MODE: | |
758 | case NCI_DEACTIVATE_TYPE_SLEEP_AF_MODE: | |
759 | atomic_set(&ndev->state, NCI_W4_HOST_SELECT); | |
760 | break; | |
761 | case NCI_DEACTIVATE_TYPE_DISCOVERY: | |
762 | nci_clear_target_list(ndev); | |
763 | atomic_set(&ndev->state, NCI_DISCOVERY); | |
764 | break; | |
765 | } | |
766 | ||
bd7e01bc | 767 | nci_req_complete(ndev, NCI_STATUS_OK); |
6a2968aa IE |
768 | } |
769 | ||
af9c8aa6 | 770 | static void nci_nfcee_discover_ntf_packet(struct nci_dev *ndev, |
ddecf555 | 771 | const struct sk_buff *skb) |
af9c8aa6 CR |
772 | { |
773 | u8 status = NCI_STATUS_OK; | |
ddecf555 | 774 | const struct nci_nfcee_discover_ntf *nfcee_ntf = |
af9c8aa6 CR |
775 | (struct nci_nfcee_discover_ntf *)skb->data; |
776 | ||
11f54f22 CR |
777 | /* NFCForum NCI 9.2.1 HCI Network Specific Handling |
778 | * If the NFCC supports the HCI Network, it SHALL return one, | |
779 | * and only one, NFCEE_DISCOVER_NTF with a Protocol type of | |
780 | * “HCI Access”, even if the HCI Network contains multiple NFCEEs. | |
781 | */ | |
15d4a8da | 782 | ndev->hci_dev->nfcee_id = nfcee_ntf->nfcee_id; |
9b8d1a4c | 783 | ndev->cur_params.id = nfcee_ntf->nfcee_id; |
11f54f22 | 784 | |
af9c8aa6 CR |
785 | nci_req_complete(ndev, status); |
786 | } | |
787 | ||
6a2968aa IE |
788 | void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) |
789 | { | |
790 | __u16 ntf_opcode = nci_opcode(skb->data); | |
791 | ||
20c239c1 JP |
792 | pr_debug("NCI RX: MT=ntf, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", |
793 | nci_pbf(skb->data), | |
794 | nci_opcode_gid(ntf_opcode), | |
795 | nci_opcode_oid(ntf_opcode), | |
796 | nci_plen(skb->data)); | |
6a2968aa IE |
797 | |
798 | /* strip the nci control header */ | |
799 | skb_pull(skb, NCI_CTRL_HDR_SIZE); | |
800 | ||
b6355e97 | 801 | if (nci_opcode_gid(ntf_opcode) == NCI_GID_PROPRIETARY) { |
0a97a3cb | 802 | if (nci_prop_ntf_packet(ndev, ntf_opcode, skb) == -ENOTSUPP) { |
b6355e97 SO |
803 | pr_err("unsupported ntf opcode 0x%x\n", |
804 | ntf_opcode); | |
805 | } | |
806 | ||
807 | goto end; | |
808 | } | |
809 | ||
6a2968aa | 810 | switch (ntf_opcode) { |
bcd684aa BJ |
811 | case NCI_OP_CORE_RESET_NTF: |
812 | nci_core_reset_ntf_packet(ndev, skb); | |
813 | break; | |
814 | ||
6a2968aa IE |
815 | case NCI_OP_CORE_CONN_CREDITS_NTF: |
816 | nci_core_conn_credits_ntf_packet(ndev, skb); | |
817 | break; | |
818 | ||
019c4fba IE |
819 | case NCI_OP_CORE_GENERIC_ERROR_NTF: |
820 | nci_core_generic_error_ntf_packet(ndev, skb); | |
821 | break; | |
822 | ||
004161cb IE |
823 | case NCI_OP_CORE_INTF_ERROR_NTF: |
824 | nci_core_conn_intf_error_ntf_packet(ndev, skb); | |
825 | break; | |
826 | ||
019c4fba IE |
827 | case NCI_OP_RF_DISCOVER_NTF: |
828 | nci_rf_discover_ntf_packet(ndev, skb); | |
829 | break; | |
830 | ||
e8c0dacd IE |
831 | case NCI_OP_RF_INTF_ACTIVATED_NTF: |
832 | nci_rf_intf_activated_ntf_packet(ndev, skb); | |
6a2968aa IE |
833 | break; |
834 | ||
835 | case NCI_OP_RF_DEACTIVATE_NTF: | |
836 | nci_rf_deactivate_ntf_packet(ndev, skb); | |
837 | break; | |
838 | ||
af9c8aa6 CR |
839 | case NCI_OP_NFCEE_DISCOVER_NTF: |
840 | nci_nfcee_discover_ntf_packet(ndev, skb); | |
841 | break; | |
a41bb844 CR |
842 | |
843 | case NCI_OP_RF_NFCEE_ACTION_NTF: | |
a41bb844 CR |
844 | break; |
845 | ||
6a2968aa | 846 | default: |
ed1e0ad8 | 847 | pr_err("unknown ntf opcode 0x%x\n", ntf_opcode); |
6a2968aa IE |
848 | break; |
849 | } | |
850 | ||
0a97a3cb | 851 | nci_core_ntf_packet(ndev, ntf_opcode, skb); |
b6355e97 | 852 | end: |
6a2968aa IE |
853 | kfree_skb(skb); |
854 | } |