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