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 | * | |
6 | * Copyright (C) 2011 Texas Instruments, Inc. | |
772dccf4 | 7 | * Copyright (C) 2014 Marvell International Ltd. |
6a2968aa IE |
8 | * |
9 | * Written by Ilan Elias <ilane@ti.com> | |
10 | * | |
11 | * Acknowledgements: | |
12 | * This file is based on hci_core.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 | |
8a70e7f8 | 18 | #include <linux/module.h> |
b6355e97 | 19 | #include <linux/kernel.h> |
6a2968aa IE |
20 | #include <linux/types.h> |
21 | #include <linux/workqueue.h> | |
22 | #include <linux/completion.h> | |
bc3b2d7f | 23 | #include <linux/export.h> |
6a2968aa IE |
24 | #include <linux/sched.h> |
25 | #include <linux/bitops.h> | |
26 | #include <linux/skbuff.h> | |
7e8cdc97 | 27 | #include <linux/kcov.h> |
6a2968aa IE |
28 | |
29 | #include "../nfc.h" | |
30 | #include <net/nfc/nci.h> | |
31 | #include <net/nfc/nci_core.h> | |
32 | #include <linux/nfc.h> | |
33 | ||
b16ae716 CR |
34 | struct core_conn_create_data { |
35 | int length; | |
36 | struct nci_core_conn_create_cmd *cmd; | |
37 | }; | |
38 | ||
6a2968aa IE |
39 | static void nci_cmd_work(struct work_struct *work); |
40 | static void nci_rx_work(struct work_struct *work); | |
41 | static void nci_tx_work(struct work_struct *work); | |
42 | ||
4aeee687 CR |
43 | struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev, |
44 | int conn_id) | |
45 | { | |
46 | struct nci_conn_info *conn_info; | |
47 | ||
48 | list_for_each_entry(conn_info, &ndev->conn_info_list, list) { | |
49 | if (conn_info->conn_id == conn_id) | |
50 | return conn_info; | |
51 | } | |
52 | ||
53 | return NULL; | |
54 | } | |
55 | ||
9b8d1a4c | 56 | int nci_get_conn_info_by_dest_type_params(struct nci_dev *ndev, u8 dest_type, |
ddecf555 | 57 | const struct dest_spec_params *params) |
85b9ce9a | 58 | { |
ddecf555 | 59 | const struct nci_conn_info *conn_info; |
85b9ce9a RD |
60 | |
61 | list_for_each_entry(conn_info, &ndev->conn_info_list, list) { | |
9b8d1a4c CR |
62 | if (conn_info->dest_type == dest_type) { |
63 | if (!params) | |
64 | return conn_info->conn_id; | |
03036184 GS |
65 | |
66 | if (params->id == conn_info->dest_params->id && | |
67 | params->protocol == conn_info->dest_params->protocol) | |
68 | return conn_info->conn_id; | |
9b8d1a4c | 69 | } |
85b9ce9a RD |
70 | } |
71 | ||
72 | return -EINVAL; | |
73 | } | |
9b8d1a4c | 74 | EXPORT_SYMBOL(nci_get_conn_info_by_dest_type_params); |
85b9ce9a | 75 | |
6a2968aa IE |
76 | /* ---- NCI requests ---- */ |
77 | ||
78 | void nci_req_complete(struct nci_dev *ndev, int result) | |
79 | { | |
80 | if (ndev->req_status == NCI_REQ_PEND) { | |
81 | ndev->req_result = result; | |
82 | ndev->req_status = NCI_REQ_DONE; | |
83 | complete(&ndev->req_completion); | |
84 | } | |
85 | } | |
2df7f8c6 | 86 | EXPORT_SYMBOL(nci_req_complete); |
6a2968aa IE |
87 | |
88 | static void nci_req_cancel(struct nci_dev *ndev, int err) | |
89 | { | |
90 | if (ndev->req_status == NCI_REQ_PEND) { | |
91 | ndev->req_result = err; | |
92 | ndev->req_status = NCI_REQ_CANCELED; | |
93 | complete(&ndev->req_completion); | |
94 | } | |
95 | } | |
96 | ||
97 | /* Execute request and wait for completion. */ | |
98 | static int __nci_request(struct nci_dev *ndev, | |
35d7a6f1 KK |
99 | void (*req)(struct nci_dev *ndev, const void *opt), |
100 | const void *opt, __u32 timeout) | |
6a2968aa IE |
101 | { |
102 | int rc = 0; | |
f8c141c3 | 103 | long completion_rc; |
6a2968aa IE |
104 | |
105 | ndev->req_status = NCI_REQ_PEND; | |
106 | ||
9bec44bf | 107 | reinit_completion(&ndev->req_completion); |
6a2968aa | 108 | req(ndev, opt); |
eb9bc6e9 SO |
109 | completion_rc = |
110 | wait_for_completion_interruptible_timeout(&ndev->req_completion, | |
111 | timeout); | |
6a2968aa | 112 | |
20c239c1 | 113 | pr_debug("wait_for_completion return %ld\n", completion_rc); |
6a2968aa IE |
114 | |
115 | if (completion_rc > 0) { | |
116 | switch (ndev->req_status) { | |
117 | case NCI_REQ_DONE: | |
118 | rc = nci_to_errno(ndev->req_result); | |
119 | break; | |
120 | ||
121 | case NCI_REQ_CANCELED: | |
122 | rc = -ndev->req_result; | |
123 | break; | |
124 | ||
125 | default: | |
126 | rc = -ETIMEDOUT; | |
127 | break; | |
128 | } | |
129 | } else { | |
ed1e0ad8 JP |
130 | pr_err("wait_for_completion_interruptible_timeout failed %ld\n", |
131 | completion_rc); | |
6a2968aa IE |
132 | |
133 | rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc)); | |
134 | } | |
135 | ||
136 | ndev->req_status = ndev->req_result = 0; | |
137 | ||
138 | return rc; | |
139 | } | |
140 | ||
11f54f22 CR |
141 | inline int nci_request(struct nci_dev *ndev, |
142 | void (*req)(struct nci_dev *ndev, | |
35d7a6f1 KK |
143 | const void *opt), |
144 | const void *opt, __u32 timeout) | |
6a2968aa IE |
145 | { |
146 | int rc; | |
147 | ||
6a2968aa IE |
148 | /* Serialize all requests */ |
149 | mutex_lock(&ndev->req_lock); | |
86cdf8e3 LM |
150 | /* check the state after obtaing the lock against any races |
151 | * from nci_close_device when the device gets removed. | |
152 | */ | |
153 | if (test_bit(NCI_UP, &ndev->flags)) | |
154 | rc = __nci_request(ndev, req, opt, timeout); | |
155 | else | |
156 | rc = -ENETDOWN; | |
6a2968aa IE |
157 | mutex_unlock(&ndev->req_lock); |
158 | ||
159 | return rc; | |
160 | } | |
161 | ||
35d7a6f1 | 162 | static void nci_reset_req(struct nci_dev *ndev, const void *opt) |
6a2968aa | 163 | { |
e8c0dacd IE |
164 | struct nci_core_reset_cmd cmd; |
165 | ||
166 | cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; | |
167 | nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); | |
6a2968aa IE |
168 | } |
169 | ||
35d7a6f1 | 170 | static void nci_init_req(struct nci_dev *ndev, const void *opt) |
6a2968aa | 171 | { |
bcd684aa BJ |
172 | u8 plen = 0; |
173 | ||
174 | if (opt) | |
175 | plen = sizeof(struct nci_core_init_v2_cmd); | |
176 | ||
35d7a6f1 | 177 | nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, plen, opt); |
6a2968aa IE |
178 | } |
179 | ||
35d7a6f1 | 180 | static void nci_init_complete_req(struct nci_dev *ndev, const void *opt) |
6a2968aa | 181 | { |
2eb1dc10 IE |
182 | struct nci_rf_disc_map_cmd cmd; |
183 | struct disc_map_config *cfg = cmd.mapping_configs; | |
184 | __u8 *num = &cmd.num_mapping_configs; | |
6a2968aa IE |
185 | int i; |
186 | ||
6a2968aa | 187 | /* set rf mapping configurations */ |
2eb1dc10 | 188 | *num = 0; |
6a2968aa IE |
189 | |
190 | /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ | |
191 | for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { | |
192 | if (ndev->supported_rf_interfaces[i] == | |
eb9bc6e9 | 193 | NCI_RF_INTERFACE_ISO_DEP) { |
2eb1dc10 | 194 | cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; |
637d85a7 IE |
195 | cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | |
196 | NCI_DISC_MAP_MODE_LISTEN; | |
197 | cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP; | |
2eb1dc10 | 198 | (*num)++; |
6a2968aa | 199 | } else if (ndev->supported_rf_interfaces[i] == |
eb9bc6e9 | 200 | NCI_RF_INTERFACE_NFC_DEP) { |
2eb1dc10 | 201 | cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; |
637d85a7 IE |
202 | cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | |
203 | NCI_DISC_MAP_MODE_LISTEN; | |
204 | cfg[*num].rf_interface = NCI_RF_INTERFACE_NFC_DEP; | |
2eb1dc10 | 205 | (*num)++; |
6a2968aa IE |
206 | } |
207 | ||
2eb1dc10 | 208 | if (*num == NCI_MAX_NUM_MAPPING_CONFIGS) |
6a2968aa IE |
209 | break; |
210 | } | |
211 | ||
212 | nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, | |
eb9bc6e9 | 213 | (1 + ((*num) * sizeof(struct disc_map_config))), &cmd); |
6a2968aa IE |
214 | } |
215 | ||
7e035230 | 216 | struct nci_set_config_param { |
ddecf555 KK |
217 | __u8 id; |
218 | size_t len; | |
219 | const __u8 *val; | |
7e035230 IE |
220 | }; |
221 | ||
35d7a6f1 | 222 | static void nci_set_config_req(struct nci_dev *ndev, const void *opt) |
7e035230 | 223 | { |
35d7a6f1 | 224 | const struct nci_set_config_param *param = opt; |
7e035230 IE |
225 | struct nci_core_set_config_cmd cmd; |
226 | ||
227 | BUG_ON(param->len > NCI_MAX_PARAM_LEN); | |
228 | ||
229 | cmd.num_params = 1; | |
230 | cmd.param.id = param->id; | |
231 | cmd.param.len = param->len; | |
232 | memcpy(cmd.param.val, param->val, param->len); | |
233 | ||
234 | nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd); | |
235 | } | |
236 | ||
772dccf4 JL |
237 | struct nci_rf_discover_param { |
238 | __u32 im_protocols; | |
239 | __u32 tm_protocols; | |
240 | }; | |
241 | ||
35d7a6f1 | 242 | static void nci_rf_discover_req(struct nci_dev *ndev, const void *opt) |
6a2968aa | 243 | { |
35d7a6f1 | 244 | const struct nci_rf_discover_param *param = opt; |
6a2968aa | 245 | struct nci_rf_disc_cmd cmd; |
6a2968aa IE |
246 | |
247 | cmd.num_disc_configs = 0; | |
248 | ||
249 | if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && | |
772dccf4 JL |
250 | (param->im_protocols & NFC_PROTO_JEWEL_MASK || |
251 | param->im_protocols & NFC_PROTO_MIFARE_MASK || | |
252 | param->im_protocols & NFC_PROTO_ISO14443_MASK || | |
253 | param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { | |
637d85a7 | 254 | cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = |
eb9bc6e9 | 255 | NCI_NFC_A_PASSIVE_POLL_MODE; |
6a2968aa IE |
256 | cmd.disc_configs[cmd.num_disc_configs].frequency = 1; |
257 | cmd.num_disc_configs++; | |
258 | } | |
259 | ||
260 | if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && | |
772dccf4 | 261 | (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) { |
637d85a7 | 262 | cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = |
eb9bc6e9 | 263 | NCI_NFC_B_PASSIVE_POLL_MODE; |
6a2968aa IE |
264 | cmd.disc_configs[cmd.num_disc_configs].frequency = 1; |
265 | cmd.num_disc_configs++; | |
266 | } | |
267 | ||
268 | if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && | |
772dccf4 JL |
269 | (param->im_protocols & NFC_PROTO_FELICA_MASK || |
270 | param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { | |
637d85a7 | 271 | cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = |
eb9bc6e9 | 272 | NCI_NFC_F_PASSIVE_POLL_MODE; |
6a2968aa IE |
273 | cmd.disc_configs[cmd.num_disc_configs].frequency = 1; |
274 | cmd.num_disc_configs++; | |
275 | } | |
276 | ||
cfdbeeaf | 277 | if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && |
772dccf4 | 278 | (param->im_protocols & NFC_PROTO_ISO15693_MASK)) { |
cfdbeeaf VC |
279 | cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = |
280 | NCI_NFC_V_PASSIVE_POLL_MODE; | |
281 | cmd.disc_configs[cmd.num_disc_configs].frequency = 1; | |
282 | cmd.num_disc_configs++; | |
283 | } | |
284 | ||
772dccf4 JL |
285 | if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) && |
286 | (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) { | |
287 | cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = | |
288 | NCI_NFC_A_PASSIVE_LISTEN_MODE; | |
289 | cmd.disc_configs[cmd.num_disc_configs].frequency = 1; | |
290 | cmd.num_disc_configs++; | |
291 | cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = | |
292 | NCI_NFC_F_PASSIVE_LISTEN_MODE; | |
293 | cmd.disc_configs[cmd.num_disc_configs].frequency = 1; | |
294 | cmd.num_disc_configs++; | |
295 | } | |
296 | ||
6a2968aa | 297 | nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, |
eb9bc6e9 SO |
298 | (1 + (cmd.num_disc_configs * sizeof(struct disc_config))), |
299 | &cmd); | |
6a2968aa IE |
300 | } |
301 | ||
019c4fba IE |
302 | struct nci_rf_discover_select_param { |
303 | __u8 rf_discovery_id; | |
304 | __u8 rf_protocol; | |
305 | }; | |
306 | ||
35d7a6f1 | 307 | static void nci_rf_discover_select_req(struct nci_dev *ndev, const void *opt) |
019c4fba | 308 | { |
35d7a6f1 | 309 | const struct nci_rf_discover_select_param *param = opt; |
019c4fba IE |
310 | struct nci_rf_discover_select_cmd cmd; |
311 | ||
312 | cmd.rf_discovery_id = param->rf_discovery_id; | |
313 | cmd.rf_protocol = param->rf_protocol; | |
314 | ||
315 | switch (cmd.rf_protocol) { | |
316 | case NCI_RF_PROTOCOL_ISO_DEP: | |
317 | cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP; | |
318 | break; | |
319 | ||
320 | case NCI_RF_PROTOCOL_NFC_DEP: | |
321 | cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP; | |
322 | break; | |
323 | ||
324 | default: | |
325 | cmd.rf_interface = NCI_RF_INTERFACE_FRAME; | |
326 | break; | |
327 | } | |
328 | ||
329 | nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD, | |
eb9bc6e9 | 330 | sizeof(struct nci_rf_discover_select_cmd), &cmd); |
019c4fba IE |
331 | } |
332 | ||
35d7a6f1 | 333 | static void nci_rf_deactivate_req(struct nci_dev *ndev, const void *opt) |
6a2968aa IE |
334 | { |
335 | struct nci_rf_deactivate_cmd cmd; | |
336 | ||
35d7a6f1 | 337 | cmd.type = (unsigned long)opt; |
6a2968aa IE |
338 | |
339 | nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, | |
eb9bc6e9 | 340 | sizeof(struct nci_rf_deactivate_cmd), &cmd); |
6a2968aa IE |
341 | } |
342 | ||
7bc4824e | 343 | struct nci_cmd_param { |
759afb8d CR |
344 | __u16 opcode; |
345 | size_t len; | |
ddecf555 | 346 | const __u8 *payload; |
759afb8d CR |
347 | }; |
348 | ||
35d7a6f1 | 349 | static void nci_generic_req(struct nci_dev *ndev, const void *opt) |
759afb8d | 350 | { |
35d7a6f1 | 351 | const struct nci_cmd_param *param = opt; |
759afb8d CR |
352 | |
353 | nci_send_cmd(ndev, param->opcode, param->len, param->payload); | |
354 | } | |
355 | ||
ddecf555 | 356 | int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, const __u8 *payload) |
759afb8d | 357 | { |
7bc4824e | 358 | struct nci_cmd_param param; |
759afb8d CR |
359 | |
360 | param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid); | |
361 | param.len = len; | |
362 | param.payload = payload; | |
363 | ||
35d7a6f1 | 364 | return __nci_request(ndev, nci_generic_req, ¶m, |
759afb8d CR |
365 | msecs_to_jiffies(NCI_CMD_TIMEOUT)); |
366 | } | |
367 | EXPORT_SYMBOL(nci_prop_cmd); | |
368 | ||
ddecf555 KK |
369 | int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, |
370 | const __u8 *payload) | |
7bc4824e RD |
371 | { |
372 | struct nci_cmd_param param; | |
373 | ||
374 | param.opcode = opcode; | |
375 | param.len = len; | |
376 | param.payload = payload; | |
377 | ||
35d7a6f1 | 378 | return __nci_request(ndev, nci_generic_req, ¶m, |
7bc4824e RD |
379 | msecs_to_jiffies(NCI_CMD_TIMEOUT)); |
380 | } | |
381 | EXPORT_SYMBOL(nci_core_cmd); | |
382 | ||
025a0cb8 RB |
383 | int nci_core_reset(struct nci_dev *ndev) |
384 | { | |
35d7a6f1 | 385 | return __nci_request(ndev, nci_reset_req, (void *)0, |
025a0cb8 RB |
386 | msecs_to_jiffies(NCI_RESET_TIMEOUT)); |
387 | } | |
388 | EXPORT_SYMBOL(nci_core_reset); | |
389 | ||
390 | int nci_core_init(struct nci_dev *ndev) | |
391 | { | |
35d7a6f1 | 392 | return __nci_request(ndev, nci_init_req, (void *)0, |
025a0cb8 RB |
393 | msecs_to_jiffies(NCI_INIT_TIMEOUT)); |
394 | } | |
395 | EXPORT_SYMBOL(nci_core_init); | |
396 | ||
1c53855f CR |
397 | struct nci_loopback_data { |
398 | u8 conn_id; | |
399 | struct sk_buff *data; | |
400 | }; | |
401 | ||
35d7a6f1 | 402 | static void nci_send_data_req(struct nci_dev *ndev, const void *opt) |
1c53855f | 403 | { |
35d7a6f1 | 404 | const struct nci_loopback_data *data = opt; |
1c53855f CR |
405 | |
406 | nci_send_data(ndev, data->conn_id, data->data); | |
407 | } | |
408 | ||
409 | static void nci_nfcc_loopback_cb(void *context, struct sk_buff *skb, int err) | |
410 | { | |
411 | struct nci_dev *ndev = (struct nci_dev *)context; | |
77411df5 | 412 | struct nci_conn_info *conn_info; |
1c53855f CR |
413 | |
414 | conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id); | |
415 | if (!conn_info) { | |
416 | nci_req_complete(ndev, NCI_STATUS_REJECTED); | |
417 | return; | |
418 | } | |
419 | ||
420 | conn_info->rx_skb = skb; | |
421 | ||
422 | nci_req_complete(ndev, NCI_STATUS_OK); | |
423 | } | |
424 | ||
ddecf555 | 425 | int nci_nfcc_loopback(struct nci_dev *ndev, const void *data, size_t data_len, |
1c53855f CR |
426 | struct sk_buff **resp) |
427 | { | |
428 | int r; | |
429 | struct nci_loopback_data loopback_data; | |
430 | struct nci_conn_info *conn_info; | |
431 | struct sk_buff *skb; | |
432 | int conn_id = nci_get_conn_info_by_dest_type_params(ndev, | |
433 | NCI_DESTINATION_NFCC_LOOPBACK, NULL); | |
434 | ||
435 | if (conn_id < 0) { | |
436 | r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCC_LOOPBACK, | |
437 | 0, 0, NULL); | |
438 | if (r != NCI_STATUS_OK) | |
439 | return r; | |
440 | ||
441 | conn_id = nci_get_conn_info_by_dest_type_params(ndev, | |
442 | NCI_DESTINATION_NFCC_LOOPBACK, | |
443 | NULL); | |
444 | } | |
445 | ||
446 | conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id); | |
447 | if (!conn_info) | |
448 | return -EPROTO; | |
449 | ||
450 | /* store cb and context to be used on receiving data */ | |
451 | conn_info->data_exchange_cb = nci_nfcc_loopback_cb; | |
452 | conn_info->data_exchange_cb_context = ndev; | |
453 | ||
454 | skb = nci_skb_alloc(ndev, NCI_DATA_HDR_SIZE + data_len, GFP_KERNEL); | |
455 | if (!skb) | |
456 | return -ENOMEM; | |
457 | ||
458 | skb_reserve(skb, NCI_DATA_HDR_SIZE); | |
59ae1d12 | 459 | skb_put_data(skb, data, data_len); |
1c53855f CR |
460 | |
461 | loopback_data.conn_id = conn_id; | |
462 | loopback_data.data = skb; | |
463 | ||
464 | ndev->cur_conn_id = conn_id; | |
35d7a6f1 | 465 | r = nci_request(ndev, nci_send_data_req, &loopback_data, |
1c53855f CR |
466 | msecs_to_jiffies(NCI_DATA_TIMEOUT)); |
467 | if (r == NCI_STATUS_OK && resp) | |
468 | *resp = conn_info->rx_skb; | |
469 | ||
470 | return r; | |
471 | } | |
472 | EXPORT_SYMBOL(nci_nfcc_loopback); | |
473 | ||
6a2968aa IE |
474 | static int nci_open_device(struct nci_dev *ndev) |
475 | { | |
476 | int rc = 0; | |
477 | ||
478 | mutex_lock(&ndev->req_lock); | |
479 | ||
48b71a9e LM |
480 | if (test_bit(NCI_UNREG, &ndev->flags)) { |
481 | rc = -ENODEV; | |
482 | goto done; | |
483 | } | |
484 | ||
6a2968aa IE |
485 | if (test_bit(NCI_UP, &ndev->flags)) { |
486 | rc = -EALREADY; | |
487 | goto done; | |
488 | } | |
489 | ||
490 | if (ndev->ops->open(ndev)) { | |
491 | rc = -EIO; | |
492 | goto done; | |
493 | } | |
494 | ||
495 | atomic_set(&ndev->cmd_cnt, 1); | |
496 | ||
497 | set_bit(NCI_INIT, &ndev->flags); | |
498 | ||
c39daeee CR |
499 | if (ndev->ops->init) |
500 | rc = ndev->ops->init(ndev); | |
501 | ||
502 | if (!rc) { | |
35d7a6f1 | 503 | rc = __nci_request(ndev, nci_reset_req, (void *)0, |
c39daeee CR |
504 | msecs_to_jiffies(NCI_RESET_TIMEOUT)); |
505 | } | |
6a2968aa | 506 | |
81859ab8 CR |
507 | if (!rc && ndev->ops->setup) { |
508 | rc = ndev->ops->setup(ndev); | |
509 | } | |
86e8586e | 510 | |
6a2968aa | 511 | if (!rc) { |
bcd684aa BJ |
512 | struct nci_core_init_v2_cmd nci_init_v2_cmd = { |
513 | .feature1 = NCI_FEATURE_DISABLE, | |
514 | .feature2 = NCI_FEATURE_DISABLE | |
515 | }; | |
35d7a6f1 | 516 | const void *opt = NULL; |
bcd684aa | 517 | |
4964e5a1 | 518 | if (ndev->nci_ver & NCI_VER_2_MASK) |
35d7a6f1 | 519 | opt = &nci_init_v2_cmd; |
bcd684aa BJ |
520 | |
521 | rc = __nci_request(ndev, nci_init_req, opt, | |
eb9bc6e9 | 522 | msecs_to_jiffies(NCI_INIT_TIMEOUT)); |
6a2968aa IE |
523 | } |
524 | ||
e4dbd625 | 525 | if (!rc && ndev->ops->post_setup) |
fdf79bd4 | 526 | rc = ndev->ops->post_setup(ndev); |
fdf79bd4 | 527 | |
6a2968aa | 528 | if (!rc) { |
35d7a6f1 | 529 | rc = __nci_request(ndev, nci_init_complete_req, (void *)0, |
eb9bc6e9 | 530 | msecs_to_jiffies(NCI_INIT_TIMEOUT)); |
6a2968aa IE |
531 | } |
532 | ||
533 | clear_bit(NCI_INIT, &ndev->flags); | |
534 | ||
535 | if (!rc) { | |
536 | set_bit(NCI_UP, &ndev->flags); | |
019c4fba | 537 | nci_clear_target_list(ndev); |
8939e47f | 538 | atomic_set(&ndev->state, NCI_IDLE); |
6a2968aa IE |
539 | } else { |
540 | /* Init failed, cleanup */ | |
541 | skb_queue_purge(&ndev->cmd_q); | |
542 | skb_queue_purge(&ndev->rx_q); | |
543 | skb_queue_purge(&ndev->tx_q); | |
544 | ||
545 | ndev->ops->close(ndev); | |
0ad6bded | 546 | ndev->flags &= BIT(NCI_UNREG); |
6a2968aa IE |
547 | } |
548 | ||
549 | done: | |
550 | mutex_unlock(&ndev->req_lock); | |
551 | return rc; | |
552 | } | |
553 | ||
554 | static int nci_close_device(struct nci_dev *ndev) | |
555 | { | |
556 | nci_req_cancel(ndev, ENODEV); | |
48b71a9e LM |
557 | |
558 | /* This mutex needs to be held as a barrier for | |
559 | * caller nci_unregister_device | |
560 | */ | |
6a2968aa IE |
561 | mutex_lock(&ndev->req_lock); |
562 | ||
563 | if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { | |
ef27324e LM |
564 | /* Need to flush the cmd wq in case |
565 | * there is a queued/running cmd_work | |
566 | */ | |
567 | flush_workqueue(ndev->cmd_wq); | |
6a2968aa | 568 | del_timer_sync(&ndev->cmd_timer); |
c4bf98b2 | 569 | del_timer_sync(&ndev->data_timer); |
6a2968aa IE |
570 | mutex_unlock(&ndev->req_lock); |
571 | return 0; | |
572 | } | |
573 | ||
574 | /* Drop RX and TX queues */ | |
575 | skb_queue_purge(&ndev->rx_q); | |
576 | skb_queue_purge(&ndev->tx_q); | |
577 | ||
578 | /* Flush RX and TX wq */ | |
579 | flush_workqueue(ndev->rx_wq); | |
580 | flush_workqueue(ndev->tx_wq); | |
581 | ||
582 | /* Reset device */ | |
583 | skb_queue_purge(&ndev->cmd_q); | |
584 | atomic_set(&ndev->cmd_cnt, 1); | |
585 | ||
586 | set_bit(NCI_INIT, &ndev->flags); | |
35d7a6f1 | 587 | __nci_request(ndev, nci_reset_req, (void *)0, |
eb9bc6e9 | 588 | msecs_to_jiffies(NCI_RESET_TIMEOUT)); |
0e70cba7 CR |
589 | |
590 | /* After this point our queues are empty | |
591 | * and no works are scheduled. | |
592 | */ | |
593 | ndev->ops->close(ndev); | |
594 | ||
6a2968aa IE |
595 | clear_bit(NCI_INIT, &ndev->flags); |
596 | ||
597 | /* Flush cmd wq */ | |
598 | flush_workqueue(ndev->cmd_wq); | |
599 | ||
f011539e BJ |
600 | del_timer_sync(&ndev->cmd_timer); |
601 | ||
48b71a9e LM |
602 | /* Clear flags except NCI_UNREG */ |
603 | ndev->flags &= BIT(NCI_UNREG); | |
6a2968aa IE |
604 | |
605 | mutex_unlock(&ndev->req_lock); | |
606 | ||
607 | return 0; | |
608 | } | |
609 | ||
610 | /* NCI command timer function */ | |
e99e88a9 | 611 | static void nci_cmd_timer(struct timer_list *t) |
6a2968aa | 612 | { |
e99e88a9 | 613 | struct nci_dev *ndev = from_timer(ndev, t, cmd_timer); |
6a2968aa | 614 | |
6a2968aa IE |
615 | atomic_set(&ndev->cmd_cnt, 1); |
616 | queue_work(ndev->cmd_wq, &ndev->cmd_work); | |
617 | } | |
618 | ||
c4bf98b2 | 619 | /* NCI data exchange timer function */ |
e99e88a9 | 620 | static void nci_data_timer(struct timer_list *t) |
c4bf98b2 | 621 | { |
e99e88a9 | 622 | struct nci_dev *ndev = from_timer(ndev, t, data_timer); |
c4bf98b2 IE |
623 | |
624 | set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); | |
625 | queue_work(ndev->rx_wq, &ndev->rx_work); | |
626 | } | |
627 | ||
6a2968aa IE |
628 | static int nci_dev_up(struct nfc_dev *nfc_dev) |
629 | { | |
630 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
631 | ||
6a2968aa IE |
632 | return nci_open_device(ndev); |
633 | } | |
634 | ||
635 | static int nci_dev_down(struct nfc_dev *nfc_dev) | |
636 | { | |
637 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
638 | ||
6a2968aa IE |
639 | return nci_close_device(ndev); |
640 | } | |
641 | ||
ddecf555 | 642 | int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, const __u8 *val) |
22c15bf3 AK |
643 | { |
644 | struct nci_set_config_param param; | |
645 | ||
646 | if (!val || !len) | |
647 | return 0; | |
648 | ||
649 | param.id = id; | |
650 | param.len = len; | |
651 | param.val = val; | |
652 | ||
35d7a6f1 | 653 | return __nci_request(ndev, nci_set_config_req, ¶m, |
22c15bf3 AK |
654 | msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); |
655 | } | |
656 | EXPORT_SYMBOL(nci_set_config); | |
657 | ||
35d7a6f1 | 658 | static void nci_nfcee_discover_req(struct nci_dev *ndev, const void *opt) |
af9c8aa6 CR |
659 | { |
660 | struct nci_nfcee_discover_cmd cmd; | |
35d7a6f1 | 661 | __u8 action = (unsigned long)opt; |
af9c8aa6 CR |
662 | |
663 | cmd.discovery_action = action; | |
664 | ||
665 | nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd); | |
666 | } | |
667 | ||
668 | int nci_nfcee_discover(struct nci_dev *ndev, u8 action) | |
669 | { | |
35d7a6f1 KK |
670 | unsigned long opt = action; |
671 | ||
672 | return __nci_request(ndev, nci_nfcee_discover_req, (void *)opt, | |
af9c8aa6 CR |
673 | msecs_to_jiffies(NCI_CMD_TIMEOUT)); |
674 | } | |
675 | EXPORT_SYMBOL(nci_nfcee_discover); | |
676 | ||
35d7a6f1 | 677 | static void nci_nfcee_mode_set_req(struct nci_dev *ndev, const void *opt) |
f7f793f3 | 678 | { |
35d7a6f1 | 679 | const struct nci_nfcee_mode_set_cmd *cmd = opt; |
f7f793f3 CR |
680 | |
681 | nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD, | |
682 | sizeof(struct nci_nfcee_mode_set_cmd), cmd); | |
683 | } | |
684 | ||
685 | int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode) | |
686 | { | |
687 | struct nci_nfcee_mode_set_cmd cmd; | |
688 | ||
689 | cmd.nfcee_id = nfcee_id; | |
690 | cmd.nfcee_mode = nfcee_mode; | |
691 | ||
35d7a6f1 | 692 | return __nci_request(ndev, nci_nfcee_mode_set_req, &cmd, |
21d19f87 | 693 | msecs_to_jiffies(NCI_CMD_TIMEOUT)); |
f7f793f3 CR |
694 | } |
695 | EXPORT_SYMBOL(nci_nfcee_mode_set); | |
696 | ||
35d7a6f1 | 697 | static void nci_core_conn_create_req(struct nci_dev *ndev, const void *opt) |
736bb957 | 698 | { |
35d7a6f1 | 699 | const struct core_conn_create_data *data = opt; |
b16ae716 CR |
700 | |
701 | nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, data->length, data->cmd); | |
736bb957 CR |
702 | } |
703 | ||
b16ae716 CR |
704 | int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type, |
705 | u8 number_destination_params, | |
706 | size_t params_len, | |
ddecf555 | 707 | const struct core_conn_create_dest_spec_params *params) |
736bb957 | 708 | { |
b16ae716 CR |
709 | int r; |
710 | struct nci_core_conn_create_cmd *cmd; | |
711 | struct core_conn_create_data data; | |
712 | ||
713 | data.length = params_len + sizeof(struct nci_core_conn_create_cmd); | |
714 | cmd = kzalloc(data.length, GFP_KERNEL); | |
715 | if (!cmd) | |
716 | return -ENOMEM; | |
717 | ||
718 | cmd->destination_type = destination_type; | |
719 | cmd->number_destination_params = number_destination_params; | |
b16ae716 CR |
720 | |
721 | data.cmd = cmd; | |
caa575a8 | 722 | |
18836029 CR |
723 | if (params) { |
724 | memcpy(cmd->params, params, params_len); | |
725 | if (params->length > 0) | |
9b8d1a4c CR |
726 | memcpy(&ndev->cur_params, |
727 | ¶ms->value[DEST_SPEC_PARAMS_ID_INDEX], | |
728 | sizeof(struct dest_spec_params)); | |
18836029 | 729 | else |
9b8d1a4c | 730 | ndev->cur_params.id = 0; |
18836029 | 731 | } else { |
9b8d1a4c | 732 | ndev->cur_params.id = 0; |
18836029 | 733 | } |
9b8d1a4c | 734 | ndev->cur_dest_type = destination_type; |
b16ae716 | 735 | |
35d7a6f1 | 736 | r = __nci_request(ndev, nci_core_conn_create_req, &data, |
b16ae716 CR |
737 | msecs_to_jiffies(NCI_CMD_TIMEOUT)); |
738 | kfree(cmd); | |
739 | return r; | |
736bb957 CR |
740 | } |
741 | EXPORT_SYMBOL(nci_core_conn_create); | |
742 | ||
35d7a6f1 | 743 | static void nci_core_conn_close_req(struct nci_dev *ndev, const void *opt) |
736bb957 | 744 | { |
35d7a6f1 | 745 | __u8 conn_id = (unsigned long)opt; |
736bb957 CR |
746 | |
747 | nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id); | |
748 | } | |
749 | ||
750 | int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id) | |
751 | { | |
35d7a6f1 KK |
752 | unsigned long opt = conn_id; |
753 | ||
de5ea851 | 754 | ndev->cur_conn_id = conn_id; |
35d7a6f1 | 755 | return __nci_request(ndev, nci_core_conn_close_req, (void *)opt, |
21d19f87 | 756 | msecs_to_jiffies(NCI_CMD_TIMEOUT)); |
736bb957 CR |
757 | } |
758 | EXPORT_SYMBOL(nci_core_conn_close); | |
759 | ||
7e035230 IE |
760 | static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) |
761 | { | |
762 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
763 | struct nci_set_config_param param; | |
529ee066 | 764 | int rc; |
7e035230 IE |
765 | |
766 | param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); | |
767 | if ((param.val == NULL) || (param.len == 0)) | |
f9fc36f4 | 768 | return 0; |
7e035230 | 769 | |
460d8f97 | 770 | if (param.len > NFC_MAX_GT_LEN) |
7e035230 IE |
771 | return -EINVAL; |
772 | ||
7e035230 | 773 | param.id = NCI_PN_ATR_REQ_GEN_BYTES; |
7e035230 | 774 | |
35d7a6f1 | 775 | rc = nci_request(ndev, nci_set_config_req, ¶m, |
529ee066 JL |
776 | msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); |
777 | if (rc) | |
778 | return rc; | |
779 | ||
780 | param.id = NCI_LN_ATR_RES_GEN_BYTES; | |
781 | ||
35d7a6f1 | 782 | return nci_request(ndev, nci_set_config_req, ¶m, |
f9fc36f4 | 783 | msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); |
7e035230 IE |
784 | } |
785 | ||
90d78c13 JL |
786 | static int nci_set_listen_parameters(struct nfc_dev *nfc_dev) |
787 | { | |
788 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
789 | int rc; | |
790 | __u8 val; | |
791 | ||
792 | val = NCI_LA_SEL_INFO_NFC_DEP_MASK; | |
793 | ||
794 | rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val); | |
795 | if (rc) | |
796 | return rc; | |
797 | ||
798 | val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK; | |
799 | ||
800 | rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val); | |
801 | if (rc) | |
802 | return rc; | |
803 | ||
804 | val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424; | |
805 | ||
806 | return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val); | |
807 | } | |
808 | ||
fe7c5800 SO |
809 | static int nci_start_poll(struct nfc_dev *nfc_dev, |
810 | __u32 im_protocols, __u32 tm_protocols) | |
6a2968aa IE |
811 | { |
812 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
772dccf4 | 813 | struct nci_rf_discover_param param; |
6a2968aa IE |
814 | int rc; |
815 | ||
019c4fba | 816 | if ((atomic_read(&ndev->state) == NCI_DISCOVERY) || |
eb9bc6e9 | 817 | (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) { |
ed1e0ad8 | 818 | pr_err("unable to start poll, since poll is already active\n"); |
6a2968aa IE |
819 | return -EBUSY; |
820 | } | |
821 | ||
de054799 | 822 | if (ndev->target_active_prot) { |
ed1e0ad8 | 823 | pr_err("there is an active target\n"); |
de054799 IE |
824 | return -EBUSY; |
825 | } | |
826 | ||
019c4fba | 827 | if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) || |
eb9bc6e9 | 828 | (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { |
019c4fba | 829 | pr_debug("target active or w4 select, implicitly deactivate\n"); |
6a2968aa | 830 | |
9295b5b5 | 831 | rc = nci_request(ndev, nci_rf_deactivate_req, |
35d7a6f1 | 832 | (void *)NCI_DEACTIVATE_TYPE_IDLE_MODE, |
eb9bc6e9 | 833 | msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); |
6a2968aa IE |
834 | if (rc) |
835 | return -EBUSY; | |
836 | } | |
837 | ||
529ee066 | 838 | if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { |
7e035230 IE |
839 | rc = nci_set_local_general_bytes(nfc_dev); |
840 | if (rc) { | |
841 | pr_err("failed to set local general bytes\n"); | |
842 | return rc; | |
843 | } | |
844 | } | |
845 | ||
90d78c13 JL |
846 | if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { |
847 | rc = nci_set_listen_parameters(nfc_dev); | |
848 | if (rc) | |
849 | pr_err("failed to set listen parameters\n"); | |
850 | } | |
851 | ||
772dccf4 JL |
852 | param.im_protocols = im_protocols; |
853 | param.tm_protocols = tm_protocols; | |
35d7a6f1 | 854 | rc = nci_request(ndev, nci_rf_discover_req, ¶m, |
eb9bc6e9 | 855 | msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); |
6a2968aa IE |
856 | |
857 | if (!rc) | |
fe7c5800 | 858 | ndev->poll_prots = im_protocols; |
6a2968aa IE |
859 | |
860 | return rc; | |
861 | } | |
862 | ||
863 | static void nci_stop_poll(struct nfc_dev *nfc_dev) | |
864 | { | |
865 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
866 | ||
019c4fba | 867 | if ((atomic_read(&ndev->state) != NCI_DISCOVERY) && |
eb9bc6e9 | 868 | (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) { |
ed1e0ad8 | 869 | pr_err("unable to stop poll, since poll is not active\n"); |
6a2968aa IE |
870 | return; |
871 | } | |
872 | ||
35d7a6f1 KK |
873 | nci_request(ndev, nci_rf_deactivate_req, |
874 | (void *)NCI_DEACTIVATE_TYPE_IDLE_MODE, | |
eb9bc6e9 | 875 | msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); |
6a2968aa IE |
876 | } |
877 | ||
90099433 EL |
878 | static int nci_activate_target(struct nfc_dev *nfc_dev, |
879 | struct nfc_target *target, __u32 protocol) | |
6a2968aa IE |
880 | { |
881 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
019c4fba | 882 | struct nci_rf_discover_select_param param; |
ddecf555 | 883 | const struct nfc_target *nci_target = NULL; |
019c4fba IE |
884 | int i; |
885 | int rc = 0; | |
6a2968aa | 886 | |
90099433 | 887 | pr_debug("target_idx %d, protocol 0x%x\n", target->idx, protocol); |
6a2968aa | 888 | |
019c4fba | 889 | if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) && |
eb9bc6e9 | 890 | (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { |
ed1e0ad8 | 891 | pr_err("there is no available target to activate\n"); |
6a2968aa IE |
892 | return -EINVAL; |
893 | } | |
894 | ||
895 | if (ndev->target_active_prot) { | |
ed1e0ad8 | 896 | pr_err("there is already an active target\n"); |
6a2968aa IE |
897 | return -EBUSY; |
898 | } | |
899 | ||
019c4fba | 900 | for (i = 0; i < ndev->n_targets; i++) { |
90099433 EL |
901 | if (ndev->targets[i].idx == target->idx) { |
902 | nci_target = &ndev->targets[i]; | |
019c4fba IE |
903 | break; |
904 | } | |
905 | } | |
906 | ||
90099433 | 907 | if (!nci_target) { |
019c4fba IE |
908 | pr_err("unable to find the selected target\n"); |
909 | return -EINVAL; | |
910 | } | |
911 | ||
354a6e70 JC |
912 | if (protocol >= NFC_PROTO_MAX) { |
913 | pr_err("the requested nfc protocol is invalid\n"); | |
914 | return -EINVAL; | |
915 | } | |
916 | ||
90099433 | 917 | if (!(nci_target->supported_protocols & (1 << protocol))) { |
ed1e0ad8 JP |
918 | pr_err("target does not support the requested protocol 0x%x\n", |
919 | protocol); | |
6a2968aa IE |
920 | return -EINVAL; |
921 | } | |
922 | ||
019c4fba | 923 | if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { |
90099433 | 924 | param.rf_discovery_id = nci_target->logical_idx; |
019c4fba IE |
925 | |
926 | if (protocol == NFC_PROTO_JEWEL) | |
927 | param.rf_protocol = NCI_RF_PROTOCOL_T1T; | |
928 | else if (protocol == NFC_PROTO_MIFARE) | |
929 | param.rf_protocol = NCI_RF_PROTOCOL_T2T; | |
930 | else if (protocol == NFC_PROTO_FELICA) | |
931 | param.rf_protocol = NCI_RF_PROTOCOL_T3T; | |
01d719a2 SO |
932 | else if (protocol == NFC_PROTO_ISO14443 || |
933 | protocol == NFC_PROTO_ISO14443_B) | |
019c4fba IE |
934 | param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; |
935 | else | |
936 | param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; | |
937 | ||
35d7a6f1 | 938 | rc = nci_request(ndev, nci_rf_discover_select_req, ¶m, |
eb9bc6e9 | 939 | msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT)); |
019c4fba | 940 | } |
6a2968aa | 941 | |
019c4fba IE |
942 | if (!rc) |
943 | ndev->target_active_prot = protocol; | |
944 | ||
945 | return rc; | |
6a2968aa IE |
946 | } |
947 | ||
90099433 | 948 | static void nci_deactivate_target(struct nfc_dev *nfc_dev, |
96d4581f CR |
949 | struct nfc_target *target, |
950 | __u8 mode) | |
6a2968aa IE |
951 | { |
952 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
35d7a6f1 | 953 | unsigned long nci_mode = NCI_DEACTIVATE_TYPE_IDLE_MODE; |
6a2968aa | 954 | |
6a2968aa | 955 | if (!ndev->target_active_prot) { |
ed1e0ad8 | 956 | pr_err("unable to deactivate target, no active target\n"); |
6a2968aa IE |
957 | return; |
958 | } | |
959 | ||
960 | ndev->target_active_prot = 0; | |
961 | ||
96d4581f CR |
962 | switch (mode) { |
963 | case NFC_TARGET_MODE_SLEEP: | |
964 | nci_mode = NCI_DEACTIVATE_TYPE_SLEEP_MODE; | |
965 | break; | |
966 | } | |
967 | ||
8939e47f | 968 | if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { |
35d7a6f1 | 969 | nci_request(ndev, nci_rf_deactivate_req, (void *)nci_mode, |
eb9bc6e9 | 970 | msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); |
6a2968aa IE |
971 | } |
972 | } | |
973 | ||
767f19ae IE |
974 | static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, |
975 | __u8 comm_mode, __u8 *gb, size_t gb_len) | |
976 | { | |
977 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
978 | int rc; | |
979 | ||
980 | pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode); | |
981 | ||
982 | rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP); | |
983 | if (rc) | |
984 | return rc; | |
985 | ||
986 | rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb, | |
987 | ndev->remote_gb_len); | |
988 | if (!rc) | |
989 | rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE, | |
990 | NFC_RF_INITIATOR); | |
991 | ||
992 | return rc; | |
993 | } | |
994 | ||
995 | static int nci_dep_link_down(struct nfc_dev *nfc_dev) | |
996 | { | |
d7979e13 JL |
997 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); |
998 | int rc; | |
999 | ||
d7979e13 | 1000 | if (nfc_dev->rf_mode == NFC_RF_INITIATOR) { |
96d4581f | 1001 | nci_deactivate_target(nfc_dev, NULL, NCI_DEACTIVATE_TYPE_IDLE_MODE); |
d7979e13 JL |
1002 | } else { |
1003 | if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE || | |
1004 | atomic_read(&ndev->state) == NCI_DISCOVERY) { | |
35d7a6f1 KK |
1005 | nci_request(ndev, nci_rf_deactivate_req, (void *)0, |
1006 | msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); | |
d7979e13 JL |
1007 | } |
1008 | ||
1009 | rc = nfc_tm_deactivated(nfc_dev); | |
1010 | if (rc) | |
1011 | pr_err("error when signaling tm deactivation\n"); | |
1012 | } | |
767f19ae IE |
1013 | |
1014 | return 0; | |
1015 | } | |
1016 | ||
1017 | ||
be9ae4ce SO |
1018 | static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, |
1019 | struct sk_buff *skb, | |
1020 | data_exchange_cb_t cb, void *cb_context) | |
6a2968aa IE |
1021 | { |
1022 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
38f04c6b | 1023 | int rc; |
77411df5 | 1024 | struct nci_conn_info *conn_info; |
4aeee687 | 1025 | |
12bdf27d | 1026 | conn_info = ndev->rf_conn_info; |
4aeee687 CR |
1027 | if (!conn_info) |
1028 | return -EPROTO; | |
6a2968aa | 1029 | |
90099433 | 1030 | pr_debug("target_idx %d, len %d\n", target->idx, skb->len); |
6a2968aa IE |
1031 | |
1032 | if (!ndev->target_active_prot) { | |
ed1e0ad8 | 1033 | pr_err("unable to exchange data, no active target\n"); |
6a2968aa IE |
1034 | return -EINVAL; |
1035 | } | |
1036 | ||
38f04c6b IE |
1037 | if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags)) |
1038 | return -EBUSY; | |
1039 | ||
6a2968aa | 1040 | /* store cb and context to be used on receiving data */ |
4aeee687 CR |
1041 | conn_info->data_exchange_cb = cb; |
1042 | conn_info->data_exchange_cb_context = cb_context; | |
6a2968aa | 1043 | |
e8c0dacd | 1044 | rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); |
38f04c6b IE |
1045 | if (rc) |
1046 | clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); | |
1047 | ||
1048 | return rc; | |
6a2968aa IE |
1049 | } |
1050 | ||
485f442f JL |
1051 | static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) |
1052 | { | |
1053 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
1054 | int rc; | |
1055 | ||
1056 | rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); | |
1057 | if (rc) | |
1058 | pr_err("unable to send data\n"); | |
1059 | ||
1060 | return rc; | |
1061 | } | |
1062 | ||
0a946301 SO |
1063 | static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx) |
1064 | { | |
93bca2bf CR |
1065 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); |
1066 | ||
1067 | if (ndev->ops->enable_se) | |
1068 | return ndev->ops->enable_se(ndev, se_idx); | |
1069 | ||
0a946301 SO |
1070 | return 0; |
1071 | } | |
1072 | ||
1073 | static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) | |
1074 | { | |
e9ef9431 CR |
1075 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); |
1076 | ||
1077 | if (ndev->ops->disable_se) | |
1078 | return ndev->ops->disable_se(ndev, se_idx); | |
1079 | ||
0a946301 SO |
1080 | return 0; |
1081 | } | |
1082 | ||
1083 | static int nci_discover_se(struct nfc_dev *nfc_dev) | |
1084 | { | |
fa00e8fe | 1085 | int r; |
ba4db551 CR |
1086 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); |
1087 | ||
fa00e8fe CR |
1088 | if (ndev->ops->discover_se) { |
1089 | r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE); | |
1090 | if (r != NCI_STATUS_OK) | |
1091 | return -EPROTO; | |
1092 | ||
ba4db551 | 1093 | return ndev->ops->discover_se(ndev); |
fa00e8fe | 1094 | } |
ba4db551 | 1095 | |
0a946301 SO |
1096 | return 0; |
1097 | } | |
1098 | ||
a688bf55 CR |
1099 | static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, |
1100 | u8 *apdu, size_t apdu_length, | |
1101 | se_io_cb_t cb, void *cb_context) | |
1102 | { | |
1103 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
1104 | ||
1105 | if (ndev->ops->se_io) | |
1106 | return ndev->ops->se_io(ndev, se_idx, apdu, | |
1107 | apdu_length, cb, cb_context); | |
1108 | ||
1109 | return 0; | |
1110 | } | |
1111 | ||
25af01ed CP |
1112 | static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name) |
1113 | { | |
1114 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | |
1115 | ||
1116 | if (!ndev->ops->fw_download) | |
1117 | return -ENOTSUPP; | |
1118 | ||
1119 | return ndev->ops->fw_download(ndev, firmware_name); | |
1120 | } | |
1121 | ||
f6c802a7 | 1122 | static const struct nfc_ops nci_nfc_ops = { |
6a2968aa IE |
1123 | .dev_up = nci_dev_up, |
1124 | .dev_down = nci_dev_down, | |
1125 | .start_poll = nci_start_poll, | |
1126 | .stop_poll = nci_stop_poll, | |
767f19ae IE |
1127 | .dep_link_up = nci_dep_link_up, |
1128 | .dep_link_down = nci_dep_link_down, | |
6a2968aa IE |
1129 | .activate_target = nci_activate_target, |
1130 | .deactivate_target = nci_deactivate_target, | |
be9ae4ce | 1131 | .im_transceive = nci_transceive, |
485f442f | 1132 | .tm_send = nci_tm_send, |
0a946301 SO |
1133 | .enable_se = nci_enable_se, |
1134 | .disable_se = nci_disable_se, | |
1135 | .discover_se = nci_discover_se, | |
a688bf55 | 1136 | .se_io = nci_se_io, |
25af01ed | 1137 | .fw_download = nci_fw_download, |
6a2968aa IE |
1138 | }; |
1139 | ||
1140 | /* ---- Interface to NCI drivers ---- */ | |
6a2968aa IE |
1141 | /** |
1142 | * nci_allocate_device - allocate a new nci device | |
1143 | * | |
1144 | * @ops: device operations | |
1145 | * @supported_protocols: NFC protocols supported by the device | |
7cdda1c1 AL |
1146 | * @tx_headroom: Reserved space at beginning of skb |
1147 | * @tx_tailroom: Reserved space at end of skb | |
6a2968aa | 1148 | */ |
b9c28286 | 1149 | struct nci_dev *nci_allocate_device(const struct nci_ops *ops, |
eb9bc6e9 SO |
1150 | __u32 supported_protocols, |
1151 | int tx_headroom, int tx_tailroom) | |
6a2968aa | 1152 | { |
8ebafde0 | 1153 | struct nci_dev *ndev; |
6a2968aa | 1154 | |
24bf3304 | 1155 | pr_debug("supported_protocols 0x%x\n", supported_protocols); |
6a2968aa IE |
1156 | |
1157 | if (!ops->open || !ops->close || !ops->send) | |
8ebafde0 | 1158 | return NULL; |
6a2968aa IE |
1159 | |
1160 | if (!supported_protocols) | |
8ebafde0 | 1161 | return NULL; |
6a2968aa IE |
1162 | |
1163 | ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL); | |
1164 | if (!ndev) | |
8ebafde0 | 1165 | return NULL; |
6a2968aa IE |
1166 | |
1167 | ndev->ops = ops; | |
b6355e97 SO |
1168 | |
1169 | if (ops->n_prop_ops > NCI_MAX_PROPRIETARY_CMD) { | |
1170 | pr_err("Too many proprietary commands: %zd\n", | |
1171 | ops->n_prop_ops); | |
b9c28286 | 1172 | goto free_nci; |
b6355e97 SO |
1173 | } |
1174 | ||
6a2968aa IE |
1175 | ndev->tx_headroom = tx_headroom; |
1176 | ndev->tx_tailroom = tx_tailroom; | |
9bec44bf | 1177 | init_completion(&ndev->req_completion); |
6a2968aa IE |
1178 | |
1179 | ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, | |
eb9bc6e9 SO |
1180 | supported_protocols, |
1181 | tx_headroom + NCI_DATA_HDR_SIZE, | |
1182 | tx_tailroom); | |
6a2968aa | 1183 | if (!ndev->nfc_dev) |
11f54f22 CR |
1184 | goto free_nci; |
1185 | ||
1186 | ndev->hci_dev = nci_hci_allocate(ndev); | |
1187 | if (!ndev->hci_dev) | |
1188 | goto free_nfc; | |
6a2968aa IE |
1189 | |
1190 | nfc_set_drvdata(ndev->nfc_dev, ndev); | |
1191 | ||
8ebafde0 | 1192 | return ndev; |
6a2968aa | 1193 | |
11f54f22 | 1194 | free_nfc: |
20777bc5 | 1195 | nfc_free_device(ndev->nfc_dev); |
11f54f22 | 1196 | free_nci: |
6a2968aa | 1197 | kfree(ndev); |
8ebafde0 | 1198 | return NULL; |
6a2968aa IE |
1199 | } |
1200 | EXPORT_SYMBOL(nci_allocate_device); | |
1201 | ||
1202 | /** | |
1203 | * nci_free_device - deallocate nci device | |
1204 | * | |
1205 | * @ndev: The nci device to deallocate | |
1206 | */ | |
1207 | void nci_free_device(struct nci_dev *ndev) | |
1208 | { | |
6a2968aa | 1209 | nfc_free_device(ndev->nfc_dev); |
e0652f8b | 1210 | nci_hci_deallocate(ndev); |
bfb007ae FP |
1211 | |
1212 | /* drop partial rx data packet if present */ | |
1213 | if (ndev->rx_data_reassembly) | |
1214 | kfree_skb(ndev->rx_data_reassembly); | |
6a2968aa IE |
1215 | kfree(ndev); |
1216 | } | |
1217 | EXPORT_SYMBOL(nci_free_device); | |
1218 | ||
1219 | /** | |
1220 | * nci_register_device - register a nci device in the nfc subsystem | |
1221 | * | |
ffbab1c9 | 1222 | * @ndev: The nci device to register |
6a2968aa IE |
1223 | */ |
1224 | int nci_register_device(struct nci_dev *ndev) | |
1225 | { | |
1226 | int rc; | |
1227 | struct device *dev = &ndev->nfc_dev->dev; | |
1228 | char name[32]; | |
1229 | ||
6a2968aa IE |
1230 | ndev->flags = 0; |
1231 | ||
1232 | INIT_WORK(&ndev->cmd_work, nci_cmd_work); | |
1233 | snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev)); | |
1234 | ndev->cmd_wq = create_singlethread_workqueue(name); | |
1235 | if (!ndev->cmd_wq) { | |
1236 | rc = -ENOMEM; | |
3c1c0f5d | 1237 | goto exit; |
6a2968aa IE |
1238 | } |
1239 | ||
1240 | INIT_WORK(&ndev->rx_work, nci_rx_work); | |
1241 | snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev)); | |
1242 | ndev->rx_wq = create_singlethread_workqueue(name); | |
1243 | if (!ndev->rx_wq) { | |
1244 | rc = -ENOMEM; | |
1245 | goto destroy_cmd_wq_exit; | |
1246 | } | |
1247 | ||
1248 | INIT_WORK(&ndev->tx_work, nci_tx_work); | |
1249 | snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev)); | |
1250 | ndev->tx_wq = create_singlethread_workqueue(name); | |
1251 | if (!ndev->tx_wq) { | |
1252 | rc = -ENOMEM; | |
1253 | goto destroy_rx_wq_exit; | |
1254 | } | |
1255 | ||
1256 | skb_queue_head_init(&ndev->cmd_q); | |
1257 | skb_queue_head_init(&ndev->rx_q); | |
1258 | skb_queue_head_init(&ndev->tx_q); | |
1259 | ||
e99e88a9 KC |
1260 | timer_setup(&ndev->cmd_timer, nci_cmd_timer, 0); |
1261 | timer_setup(&ndev->data_timer, nci_data_timer, 0); | |
6a2968aa IE |
1262 | |
1263 | mutex_init(&ndev->req_lock); | |
4aeee687 | 1264 | INIT_LIST_HEAD(&ndev->conn_info_list); |
6a2968aa | 1265 | |
3c1c0f5d VC |
1266 | rc = nfc_register_device(ndev->nfc_dev); |
1267 | if (rc) | |
0b4a66a3 | 1268 | goto destroy_tx_wq_exit; |
3c1c0f5d | 1269 | |
6a2968aa IE |
1270 | goto exit; |
1271 | ||
0b4a66a3 WH |
1272 | destroy_tx_wq_exit: |
1273 | destroy_workqueue(ndev->tx_wq); | |
1274 | ||
6a2968aa IE |
1275 | destroy_rx_wq_exit: |
1276 | destroy_workqueue(ndev->rx_wq); | |
1277 | ||
1278 | destroy_cmd_wq_exit: | |
1279 | destroy_workqueue(ndev->cmd_wq); | |
1280 | ||
6a2968aa IE |
1281 | exit: |
1282 | return rc; | |
1283 | } | |
1284 | EXPORT_SYMBOL(nci_register_device); | |
1285 | ||
1286 | /** | |
1287 | * nci_unregister_device - unregister a nci device in the nfc subsystem | |
1288 | * | |
ffbab1c9 | 1289 | * @ndev: The nci device to unregister |
6a2968aa IE |
1290 | */ |
1291 | void nci_unregister_device(struct nci_dev *ndev) | |
1292 | { | |
77411df5 | 1293 | struct nci_conn_info *conn_info, *n; |
4aeee687 | 1294 | |
48b71a9e LM |
1295 | /* This set_bit is not protected with specialized barrier, |
1296 | * However, it is fine because the mutex_lock(&ndev->req_lock); | |
1297 | * in nci_close_device() will help to emit one. | |
1298 | */ | |
1299 | set_bit(NCI_UNREG, &ndev->flags); | |
1300 | ||
6a2968aa IE |
1301 | nci_close_device(ndev); |
1302 | ||
1303 | destroy_workqueue(ndev->cmd_wq); | |
1304 | destroy_workqueue(ndev->rx_wq); | |
1305 | destroy_workqueue(ndev->tx_wq); | |
1306 | ||
4aeee687 CR |
1307 | list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) { |
1308 | list_del(&conn_info->list); | |
1309 | /* conn_info is allocated with devm_kzalloc */ | |
1310 | } | |
1311 | ||
6a2968aa IE |
1312 | nfc_unregister_device(ndev->nfc_dev); |
1313 | } | |
1314 | EXPORT_SYMBOL(nci_unregister_device); | |
1315 | ||
1316 | /** | |
1317 | * nci_recv_frame - receive frame from NCI drivers | |
1318 | * | |
1095e69f | 1319 | * @ndev: The nci device |
6a2968aa IE |
1320 | * @skb: The sk_buff to receive |
1321 | */ | |
1095e69f | 1322 | int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) |
6a2968aa | 1323 | { |
24bf3304 | 1324 | pr_debug("len %d\n", skb->len); |
6a2968aa | 1325 | |
874934f4 SJ |
1326 | if (!ndev || (!test_bit(NCI_UP, &ndev->flags) && |
1327 | !test_bit(NCI_INIT, &ndev->flags))) { | |
6a2968aa IE |
1328 | kfree_skb(skb); |
1329 | return -ENXIO; | |
1330 | } | |
1331 | ||
1332 | /* Queue frame for rx worker thread */ | |
1333 | skb_queue_tail(&ndev->rx_q, skb); | |
1334 | queue_work(ndev->rx_wq, &ndev->rx_work); | |
1335 | ||
1336 | return 0; | |
1337 | } | |
1338 | EXPORT_SYMBOL(nci_recv_frame); | |
1339 | ||
e5629d29 | 1340 | int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) |
6a2968aa | 1341 | { |
24bf3304 | 1342 | pr_debug("len %d\n", skb->len); |
6a2968aa IE |
1343 | |
1344 | if (!ndev) { | |
1345 | kfree_skb(skb); | |
1346 | return -ENODEV; | |
1347 | } | |
1348 | ||
1349 | /* Get rid of skb owner, prior to sending to the driver. */ | |
1350 | skb_orphan(skb); | |
1351 | ||
05158296 HT |
1352 | /* Send copy to sniffer */ |
1353 | nfc_send_to_raw_sock(ndev->nfc_dev, skb, | |
1354 | RAW_PAYLOAD_NCI, NFC_DIRECTION_TX); | |
1355 | ||
1095e69f | 1356 | return ndev->ops->send(ndev, skb); |
6a2968aa | 1357 | } |
e5629d29 | 1358 | EXPORT_SYMBOL(nci_send_frame); |
6a2968aa IE |
1359 | |
1360 | /* Send NCI command */ | |
48d54403 | 1361 | int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, const void *payload) |
6a2968aa IE |
1362 | { |
1363 | struct nci_ctrl_hdr *hdr; | |
1364 | struct sk_buff *skb; | |
1365 | ||
24bf3304 | 1366 | pr_debug("opcode 0x%x, plen %d\n", opcode, plen); |
6a2968aa IE |
1367 | |
1368 | skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); | |
1369 | if (!skb) { | |
ed1e0ad8 | 1370 | pr_err("no memory for command\n"); |
6a2968aa IE |
1371 | return -ENOMEM; |
1372 | } | |
1373 | ||
4df864c1 | 1374 | hdr = skb_put(skb, NCI_CTRL_HDR_SIZE); |
6a2968aa IE |
1375 | hdr->gid = nci_opcode_gid(opcode); |
1376 | hdr->oid = nci_opcode_oid(opcode); | |
1377 | hdr->plen = plen; | |
1378 | ||
1379 | nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT); | |
1380 | nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); | |
1381 | ||
1382 | if (plen) | |
59ae1d12 | 1383 | skb_put_data(skb, payload, plen); |
6a2968aa | 1384 | |
6a2968aa IE |
1385 | skb_queue_tail(&ndev->cmd_q, skb); |
1386 | queue_work(ndev->cmd_wq, &ndev->cmd_work); | |
1387 | ||
1388 | return 0; | |
1389 | } | |
e5629d29 | 1390 | EXPORT_SYMBOL(nci_send_cmd); |
6a2968aa | 1391 | |
b6355e97 | 1392 | /* Proprietary commands API */ |
cb8caa3c KK |
1393 | static const struct nci_driver_ops *ops_cmd_lookup(const struct nci_driver_ops *ops, |
1394 | size_t n_ops, | |
1395 | __u16 opcode) | |
b6355e97 SO |
1396 | { |
1397 | size_t i; | |
cb8caa3c | 1398 | const struct nci_driver_ops *op; |
b6355e97 | 1399 | |
0a97a3cb | 1400 | if (!ops || !n_ops) |
b6355e97 SO |
1401 | return NULL; |
1402 | ||
0a97a3cb RD |
1403 | for (i = 0; i < n_ops; i++) { |
1404 | op = &ops[i]; | |
1405 | if (op->opcode == opcode) | |
1406 | return op; | |
b6355e97 SO |
1407 | } |
1408 | ||
1409 | return NULL; | |
1410 | } | |
1411 | ||
0a97a3cb | 1412 | static int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode, |
cb8caa3c | 1413 | struct sk_buff *skb, const struct nci_driver_ops *ops, |
0a97a3cb | 1414 | size_t n_ops) |
b6355e97 | 1415 | { |
cb8caa3c | 1416 | const struct nci_driver_ops *op; |
b6355e97 | 1417 | |
0a97a3cb RD |
1418 | op = ops_cmd_lookup(ops, n_ops, rsp_opcode); |
1419 | if (!op || !op->rsp) | |
b6355e97 SO |
1420 | return -ENOTSUPP; |
1421 | ||
0a97a3cb | 1422 | return op->rsp(ndev, skb); |
b6355e97 SO |
1423 | } |
1424 | ||
0a97a3cb | 1425 | static int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode, |
cb8caa3c | 1426 | struct sk_buff *skb, const struct nci_driver_ops *ops, |
0a97a3cb | 1427 | size_t n_ops) |
b6355e97 | 1428 | { |
cb8caa3c | 1429 | const struct nci_driver_ops *op; |
b6355e97 | 1430 | |
0a97a3cb RD |
1431 | op = ops_cmd_lookup(ops, n_ops, ntf_opcode); |
1432 | if (!op || !op->ntf) | |
b6355e97 SO |
1433 | return -ENOTSUPP; |
1434 | ||
0a97a3cb RD |
1435 | return op->ntf(ndev, skb); |
1436 | } | |
1437 | ||
f1163174 RD |
1438 | int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, |
1439 | struct sk_buff *skb) | |
0a97a3cb RD |
1440 | { |
1441 | return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops, | |
1442 | ndev->ops->n_prop_ops); | |
1443 | } | |
1444 | ||
f1163174 RD |
1445 | int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, |
1446 | struct sk_buff *skb) | |
0a97a3cb RD |
1447 | { |
1448 | return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops, | |
1449 | ndev->ops->n_prop_ops); | |
1450 | } | |
1451 | ||
f1163174 RD |
1452 | int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, |
1453 | struct sk_buff *skb) | |
0a97a3cb RD |
1454 | { |
1455 | return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops, | |
1456 | ndev->ops->n_core_ops); | |
1457 | } | |
1458 | ||
f1163174 RD |
1459 | int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, |
1460 | struct sk_buff *skb) | |
0a97a3cb RD |
1461 | { |
1462 | return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops, | |
1463 | ndev->ops->n_core_ops); | |
b6355e97 SO |
1464 | } |
1465 | ||
e4a87abf RY |
1466 | static bool nci_valid_size(struct sk_buff *skb) |
1467 | { | |
1468 | BUILD_BUG_ON(NCI_CTRL_HDR_SIZE != NCI_DATA_HDR_SIZE); | |
1469 | unsigned int hdr_size = NCI_CTRL_HDR_SIZE; | |
1470 | ||
1471 | if (skb->len < hdr_size || | |
1472 | !nci_plen(skb->data) || | |
1473 | skb->len < hdr_size + nci_plen(skb->data)) { | |
1474 | return false; | |
1475 | } | |
1476 | return true; | |
1477 | } | |
1478 | ||
6a2968aa IE |
1479 | /* ---- NCI TX Data worker thread ---- */ |
1480 | ||
1481 | static void nci_tx_work(struct work_struct *work) | |
1482 | { | |
1483 | struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); | |
77411df5 | 1484 | struct nci_conn_info *conn_info; |
6a2968aa IE |
1485 | struct sk_buff *skb; |
1486 | ||
4aeee687 CR |
1487 | conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id); |
1488 | if (!conn_info) | |
1489 | return; | |
1490 | ||
1491 | pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt)); | |
6a2968aa IE |
1492 | |
1493 | /* Send queued tx data */ | |
4aeee687 | 1494 | while (atomic_read(&conn_info->credits_cnt)) { |
6a2968aa IE |
1495 | skb = skb_dequeue(&ndev->tx_q); |
1496 | if (!skb) | |
1497 | return; | |
7e8cdc97 | 1498 | kcov_remote_start_common(skb_get_kcov_handle(skb)); |
6a2968aa | 1499 | |
db98c829 | 1500 | /* Check if data flow control is used */ |
4aeee687 | 1501 | if (atomic_read(&conn_info->credits_cnt) != |
eb9bc6e9 | 1502 | NCI_DATA_FLOW_CONTROL_NOT_USED) |
4aeee687 | 1503 | atomic_dec(&conn_info->credits_cnt); |
6a2968aa | 1504 | |
20c239c1 JP |
1505 | pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", |
1506 | nci_pbf(skb->data), | |
1507 | nci_conn_id(skb->data), | |
1508 | nci_plen(skb->data)); | |
6a2968aa | 1509 | |
1095e69f | 1510 | nci_send_frame(ndev, skb); |
c4bf98b2 IE |
1511 | |
1512 | mod_timer(&ndev->data_timer, | |
eb9bc6e9 | 1513 | jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); |
7e8cdc97 | 1514 | kcov_remote_stop(); |
6a2968aa IE |
1515 | } |
1516 | } | |
1517 | ||
1518 | /* ----- NCI RX worker thread (data & control) ----- */ | |
1519 | ||
1520 | static void nci_rx_work(struct work_struct *work) | |
1521 | { | |
1522 | struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); | |
1523 | struct sk_buff *skb; | |
1524 | ||
7e8cdc97 DV |
1525 | for (; (skb = skb_dequeue(&ndev->rx_q)); kcov_remote_stop()) { |
1526 | kcov_remote_start_common(skb_get_kcov_handle(skb)); | |
05158296 HT |
1527 | |
1528 | /* Send copy to sniffer */ | |
1529 | nfc_send_to_raw_sock(ndev->nfc_dev, skb, | |
1530 | RAW_PAYLOAD_NCI, NFC_DIRECTION_RX); | |
1531 | ||
e4a87abf | 1532 | if (!nci_valid_size(skb)) { |
d24b0353 | 1533 | kfree_skb(skb); |
6671e352 | 1534 | continue; |
d24b0353 RY |
1535 | } |
1536 | ||
6a2968aa IE |
1537 | /* Process frame */ |
1538 | switch (nci_mt(skb->data)) { | |
1539 | case NCI_MT_RSP_PKT: | |
1540 | nci_rsp_packet(ndev, skb); | |
1541 | break; | |
1542 | ||
1543 | case NCI_MT_NTF_PKT: | |
1544 | nci_ntf_packet(ndev, skb); | |
1545 | break; | |
1546 | ||
1547 | case NCI_MT_DATA_PKT: | |
1548 | nci_rx_data_packet(ndev, skb); | |
1549 | break; | |
1550 | ||
1551 | default: | |
ed1e0ad8 | 1552 | pr_err("unknown MT 0x%x\n", nci_mt(skb->data)); |
6a2968aa IE |
1553 | kfree_skb(skb); |
1554 | break; | |
1555 | } | |
1556 | } | |
c4bf98b2 | 1557 | |
d3295869 | 1558 | /* check if a data exchange timeout has occurred */ |
c4bf98b2 IE |
1559 | if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) { |
1560 | /* complete the data exchange transaction, if exists */ | |
1561 | if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) | |
4aeee687 CR |
1562 | nci_data_exchange_complete(ndev, NULL, |
1563 | ndev->cur_conn_id, | |
1564 | -ETIMEDOUT); | |
c4bf98b2 IE |
1565 | |
1566 | clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); | |
1567 | } | |
6a2968aa IE |
1568 | } |
1569 | ||
1570 | /* ----- NCI TX CMD worker thread ----- */ | |
1571 | ||
1572 | static void nci_cmd_work(struct work_struct *work) | |
1573 | { | |
1574 | struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); | |
1575 | struct sk_buff *skb; | |
1576 | ||
24bf3304 | 1577 | pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt)); |
6a2968aa IE |
1578 | |
1579 | /* Send queued command */ | |
1580 | if (atomic_read(&ndev->cmd_cnt)) { | |
1581 | skb = skb_dequeue(&ndev->cmd_q); | |
1582 | if (!skb) | |
1583 | return; | |
1584 | ||
7e8cdc97 | 1585 | kcov_remote_start_common(skb_get_kcov_handle(skb)); |
6a2968aa IE |
1586 | atomic_dec(&ndev->cmd_cnt); |
1587 | ||
20c239c1 JP |
1588 | pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", |
1589 | nci_pbf(skb->data), | |
1590 | nci_opcode_gid(nci_opcode(skb->data)), | |
1591 | nci_opcode_oid(nci_opcode(skb->data)), | |
1592 | nci_plen(skb->data)); | |
6a2968aa | 1593 | |
1095e69f | 1594 | nci_send_frame(ndev, skb); |
6a2968aa IE |
1595 | |
1596 | mod_timer(&ndev->cmd_timer, | |
eb9bc6e9 | 1597 | jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); |
7e8cdc97 | 1598 | kcov_remote_stop(); |
6a2968aa IE |
1599 | } |
1600 | } | |
8a70e7f8 | 1601 | |
95c236cc | 1602 | MODULE_DESCRIPTION("NFC Controller Interface"); |
8a70e7f8 | 1603 | MODULE_LICENSE("GPL"); |