Blame view
net/nfc/core.c
24.2 KB
1ccea77e2
|
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
3e256b8f8
|
2 3 4 5 6 7 |
/* * Copyright (C) 2011 Instituto Nokia de Tecnologia * * Authors: * Lauro Ramos Venancio <lauro.venancio@openbossa.org> * Aloisio Almeida Jr <aloisio.almeida@openbossa.org> |
3e256b8f8
|
8 |
*/ |
52858b51b
|
9 |
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ |
ed1e0ad88
|
10 |
|
3e256b8f8
|
11 12 13 14 |
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> |
be055b2f8
|
15 |
#include <linux/rfkill.h> |
7c7cd3bfe
|
16 |
#include <linux/nfc.h> |
3e256b8f8
|
17 |
|
5df16cad4
|
18 |
#include <net/genetlink.h> |
3e256b8f8
|
19 20 21 |
#include "nfc.h" #define VERSION "0.1" |
c8d56ae78
|
22 |
#define NFC_CHECK_PRES_FREQ_MS 2000 |
3e256b8f8
|
23 24 |
int nfc_devlist_generation; DEFINE_MUTEX(nfc_devlist_mutex); |
7eda8b8e9
|
25 26 |
/* NFC device ID bitmap */ static DEFINE_IDA(nfc_index_ida); |
9ea7187c5
|
27 |
int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name) |
9674da875
|
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
{ int rc = 0; pr_debug("%s do firmware %s ", dev_name(&dev->dev), firmware_name); device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } if (dev->dev_up) { rc = -EBUSY; goto error; } |
9ea7187c5
|
45 |
if (!dev->ops->fw_download) { |
9674da875
|
46 47 48 |
rc = -EOPNOTSUPP; goto error; } |
9ea7187c5
|
49 50 |
dev->fw_download_in_progress = true; rc = dev->ops->fw_download(dev, firmware_name); |
9674da875
|
51 |
if (rc) |
9ea7187c5
|
52 |
dev->fw_download_in_progress = false; |
9674da875
|
53 54 55 56 57 |
error: device_unlock(&dev->dev); return rc; } |
352a5f5fb
|
58 59 60 61 62 63 64 65 66 |
/** * nfc_fw_download_done - inform that a firmware download was completed * * @dev: The nfc device to which firmware was downloaded * @firmware_name: The firmware filename * @result: The positive value of a standard errno value */ int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name, u32 result) |
9674da875
|
67 |
{ |
9ea7187c5
|
68 |
dev->fw_download_in_progress = false; |
9674da875
|
69 |
|
352a5f5fb
|
70 |
return nfc_genl_fw_download_done(dev, firmware_name, result); |
9674da875
|
71 |
} |
9ea7187c5
|
72 |
EXPORT_SYMBOL(nfc_fw_download_done); |
9674da875
|
73 |
|
3e256b8f8
|
74 |
/** |
8b3fe7b59
|
75 76 77 78 79 80 81 82 83 |
* nfc_dev_up - turn on the NFC device * * @dev: The nfc device to be turned on * * The device remains up until the nfc_dev_down function is called. */ int nfc_dev_up(struct nfc_dev *dev) { int rc = 0; |
20c239c13
|
84 85 |
pr_debug("dev_name=%s ", dev_name(&dev->dev)); |
8b3fe7b59
|
86 87 |
device_lock(&dev->dev); |
be055b2f8
|
88 89 90 91 |
if (dev->rfkill && rfkill_blocked(dev->rfkill)) { rc = -ERFKILL; goto error; } |
8b3fe7b59
|
92 93 94 95 |
if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } |
9ea7187c5
|
96 |
if (dev->fw_download_in_progress) { |
9674da875
|
97 98 99 |
rc = -EBUSY; goto error; } |
8b3fe7b59
|
100 101 102 103 104 105 106 107 108 109 |
if (dev->dev_up) { rc = -EALREADY; goto error; } if (dev->ops->dev_up) rc = dev->ops->dev_up(dev); if (!rc) dev->dev_up = true; |
0a946301c
|
110 |
/* We have to enable the device before discovering SEs */ |
a434c2407
|
111 112 113 |
if (dev->ops->discover_se && dev->ops->discover_se(dev)) pr_err("SE discovery failed "); |
0a946301c
|
114 |
|
8b3fe7b59
|
115 116 117 118 119 120 121 122 123 124 125 126 127 |
error: device_unlock(&dev->dev); return rc; } /** * nfc_dev_down - turn off the NFC device * * @dev: The nfc device to be turned off */ int nfc_dev_down(struct nfc_dev *dev) { int rc = 0; |
20c239c13
|
128 129 |
pr_debug("dev_name=%s ", dev_name(&dev->dev)); |
8b3fe7b59
|
130 131 132 133 134 135 136 137 138 139 140 141 |
device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } if (!dev->dev_up) { rc = -EALREADY; goto error; } |
900994332
|
142 |
if (dev->polling || dev->active_target) { |
8b3fe7b59
|
143 144 145 146 147 148 149 150 151 152 153 154 155 |
rc = -EBUSY; goto error; } if (dev->ops->dev_down) dev->ops->dev_down(dev); dev->dev_up = false; error: device_unlock(&dev->dev); return rc; } |
be055b2f8
|
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
static int nfc_rfkill_set_block(void *data, bool blocked) { struct nfc_dev *dev = data; pr_debug("%s blocked %d", dev_name(&dev->dev), blocked); if (!blocked) return 0; nfc_dev_down(dev); return 0; } static const struct rfkill_ops nfc_rfkill_ops = { .set_block = nfc_rfkill_set_block, }; |
8b3fe7b59
|
173 |
/** |
3e256b8f8
|
174 175 176 177 178 179 180 181 |
* nfc_start_poll - start polling for nfc targets * * @dev: The nfc device that must start polling * @protocols: bitset of nfc protocols that must be used for polling * * The device remains polling for targets until a target is found or * the nfc_stop_poll function is called. */ |
fe7c58007
|
182 |
int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols) |
3e256b8f8
|
183 184 |
{ int rc; |
fe7c58007
|
185 186 187 |
pr_debug("dev_name %s initiator protocols 0x%x target protocols 0x%x ", dev_name(&dev->dev), im_protocols, tm_protocols); |
3e256b8f8
|
188 |
|
fe7c58007
|
189 |
if (!im_protocols && !tm_protocols) |
3e256b8f8
|
190 191 192 193 194 195 196 197 |
return -EINVAL; device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } |
7757dc8a3
|
198 199 200 201 |
if (!dev->dev_up) { rc = -ENODEV; goto error; } |
3e256b8f8
|
202 203 204 205 |
if (dev->polling) { rc = -EBUSY; goto error; } |
fe7c58007
|
206 |
rc = dev->ops->start_poll(dev, im_protocols, tm_protocols); |
f212ad5e9
|
207 |
if (!rc) { |
3e256b8f8
|
208 |
dev->polling = true; |
f212ad5e9
|
209 210 |
dev->rf_mode = NFC_RF_NONE; } |
3e256b8f8
|
211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
error: device_unlock(&dev->dev); return rc; } /** * nfc_stop_poll - stop polling for nfc targets * * @dev: The nfc device that must stop polling */ int nfc_stop_poll(struct nfc_dev *dev) { int rc = 0; |
20c239c13
|
225 226 |
pr_debug("dev_name=%s ", dev_name(&dev->dev)); |
3e256b8f8
|
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } if (!dev->polling) { rc = -EINVAL; goto error; } dev->ops->stop_poll(dev); dev->polling = false; |
5bcf099c1
|
242 |
dev->rf_mode = NFC_RF_NONE; |
3e256b8f8
|
243 244 245 246 247 |
error: device_unlock(&dev->dev); return rc; } |
900994332
|
248 249 250 |
static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx) { int i; |
0f4507722
|
251 |
for (i = 0; i < dev->n_targets; i++) { |
900994332
|
252 253 254 255 256 257 |
if (dev->targets[i].idx == target_idx) return &dev->targets[i]; } return NULL; } |
47807d3db
|
258 |
int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode) |
1ed28f610
|
259 260 |
{ int rc = 0; |
47807d3db
|
261 262 |
u8 *gb; size_t gb_len; |
900994332
|
263 |
struct nfc_target *target; |
1ed28f610
|
264 |
|
47807d3db
|
265 266 |
pr_debug("dev_name=%s comm %d ", dev_name(&dev->dev), comm_mode); |
1ed28f610
|
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
if (!dev->ops->dep_link_up) return -EOPNOTSUPP; device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } if (dev->dep_link_up == true) { rc = -EALREADY; goto error; } |
47807d3db
|
282 283 284 285 286 |
gb = nfc_llcp_general_bytes(dev, &gb_len); if (gb_len > NFC_MAX_GT_LEN) { rc = -EINVAL; goto error; } |
900994332
|
287 288 289 290 291 292 293 |
target = nfc_find_target(dev, target_index); if (target == NULL) { rc = -ENOTCONN; goto error; } rc = dev->ops->dep_link_up(dev, target, comm_mode, gb, gb_len); |
f212ad5e9
|
294 |
if (!rc) { |
900994332
|
295 |
dev->active_target = target; |
f212ad5e9
|
296 297 |
dev->rf_mode = NFC_RF_INITIATOR; } |
1ed28f610
|
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
error: device_unlock(&dev->dev); return rc; } int nfc_dep_link_down(struct nfc_dev *dev) { int rc = 0; pr_debug("dev_name=%s ", dev_name(&dev->dev)); if (!dev->ops->dep_link_down) return -EOPNOTSUPP; device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } if (dev->dep_link_up == false) { rc = -EALREADY; goto error; } |
1ed28f610
|
325 326 327 |
rc = dev->ops->dep_link_down(dev); if (!rc) { dev->dep_link_up = false; |
900994332
|
328 |
dev->active_target = NULL; |
5bcf099c1
|
329 |
dev->rf_mode = NFC_RF_NONE; |
d646960f7
|
330 |
nfc_llcp_mac_is_down(dev); |
1ed28f610
|
331 332 333 334 335 |
nfc_genl_dep_link_down_event(dev); } error: device_unlock(&dev->dev); |
5bcf099c1
|
336 |
|
1ed28f610
|
337 338 339 340 |
return rc; } int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx, |
0a40acb24
|
341 |
u8 comm_mode, u8 rf_mode) |
1ed28f610
|
342 343 |
{ dev->dep_link_up = true; |
1ed28f610
|
344 |
|
d31652a26
|
345 |
if (!dev->active_target && rf_mode == NFC_RF_INITIATOR) { |
e29a9e2ae
|
346 347 348 349 350 351 352 353 354 355 356 |
struct nfc_target *target; target = nfc_find_target(dev, target_idx); if (target == NULL) return -ENOTCONN; dev->active_target = target; } dev->polling = false; dev->rf_mode = rf_mode; |
d646960f7
|
357 |
nfc_llcp_mac_is_up(dev, target_idx, comm_mode, rf_mode); |
1ed28f610
|
358 359 360 |
return nfc_genl_dep_link_up_event(dev, target_idx, comm_mode, rf_mode); } EXPORT_SYMBOL(nfc_dep_link_is_up); |
3e256b8f8
|
361 362 363 364 365 366 367 368 369 370 |
/** * nfc_activate_target - prepare the target for data exchange * * @dev: The nfc device that found the target * @target_idx: index of the target that must be activated * @protocol: nfc protocol that will be used for data exchange */ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol) { int rc; |
900994332
|
371 |
struct nfc_target *target; |
3e256b8f8
|
372 |
|
20c239c13
|
373 374 375 |
pr_debug("dev_name=%s target_idx=%u protocol=%u ", dev_name(&dev->dev), target_idx, protocol); |
3e256b8f8
|
376 377 378 379 380 381 382 |
device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } |
900994332
|
383 384 385 386 387 388 389 390 391 392 393 394 |
if (dev->active_target) { rc = -EBUSY; goto error; } target = nfc_find_target(dev, target_idx); if (target == NULL) { rc = -ENOTCONN; goto error; } rc = dev->ops->activate_target(dev, target, protocol); |
c8d56ae78
|
395 |
if (!rc) { |
900994332
|
396 |
dev->active_target = target; |
f212ad5e9
|
397 |
dev->rf_mode = NFC_RF_INITIATOR; |
3e256b8f8
|
398 |
|
f0c910381
|
399 |
if (dev->ops->check_presence && !dev->shutting_down) |
c8d56ae78
|
400 401 402 |
mod_timer(&dev->check_pres_timer, jiffies + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); } |
3e256b8f8
|
403 404 405 406 407 408 409 410 411 412 413 414 |
error: device_unlock(&dev->dev); return rc; } /** * nfc_deactivate_target - deactivate a nfc target * * @dev: The nfc device that found the target * @target_idx: index of the target that must be deactivated */ |
96d4581f0
|
415 |
int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx, u8 mode) |
3e256b8f8
|
416 417 |
{ int rc = 0; |
20c239c13
|
418 419 420 |
pr_debug("dev_name=%s target_idx=%u ", dev_name(&dev->dev), target_idx); |
3e256b8f8
|
421 422 423 424 425 426 427 |
device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } |
900994332
|
428 429 430 431 432 433 434 435 436 |
if (dev->active_target == NULL) { rc = -ENOTCONN; goto error; } if (dev->active_target->idx != target_idx) { rc = -ENOTCONN; goto error; } |
c8d56ae78
|
437 438 |
if (dev->ops->check_presence) del_timer_sync(&dev->check_pres_timer); |
96d4581f0
|
439 |
dev->ops->deactivate_target(dev, dev->active_target, mode); |
900994332
|
440 |
dev->active_target = NULL; |
3e256b8f8
|
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
error: device_unlock(&dev->dev); return rc; } /** * nfc_data_exchange - transceive data * * @dev: The nfc device that found the target * @target_idx: index of the target * @skb: data to be sent * @cb: callback called when the response is received * @cb_context: parameter for the callback function * * The user must wait for the callback before calling this function again. */ |
0a40acb24
|
458 459 |
int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context) |
3e256b8f8
|
460 461 |
{ int rc; |
20c239c13
|
462 463 464 |
pr_debug("dev_name=%s target_idx=%u skb->len=%u ", dev_name(&dev->dev), target_idx, skb->len); |
3e256b8f8
|
465 466 467 468 469 470 471 472 |
device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; kfree_skb(skb); goto error; } |
be9ae4ce4
|
473 474 475 476 477 478 |
if (dev->rf_mode == NFC_RF_INITIATOR && dev->active_target != NULL) { if (dev->active_target->idx != target_idx) { rc = -EADDRNOTAVAIL; kfree_skb(skb); goto error; } |
144612cac
|
479 |
|
be9ae4ce4
|
480 481 482 483 484 |
if (dev->ops->check_presence) del_timer_sync(&dev->check_pres_timer); rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb, cb_context); |
f0c910381
|
485 |
if (!rc && dev->ops->check_presence && !dev->shutting_down) |
be9ae4ce4
|
486 487 488 489 490 491 |
mod_timer(&dev->check_pres_timer, jiffies + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) { rc = dev->ops->tm_send(dev, skb); } else { rc = -ENOTCONN; |
144612cac
|
492 493 494 |
kfree_skb(skb); goto error; } |
c8d56ae78
|
495 |
|
3e256b8f8
|
496 497 498 499 |
error: device_unlock(&dev->dev); return rc; } |
d8eb18eec
|
500 |
struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx) |
c531c9ec2
|
501 |
{ |
156cef80f
|
502 |
struct nfc_se *se; |
c531c9ec2
|
503 |
|
156cef80f
|
504 |
list_for_each_entry(se, &dev->secure_elements, list) |
c531c9ec2
|
505 506 507 508 509 |
if (se->idx == se_idx) return se; return NULL; } |
d8eb18eec
|
510 |
EXPORT_SYMBOL(nfc_find_se); |
c531c9ec2
|
511 512 513 |
int nfc_enable_se(struct nfc_dev *dev, u32 se_idx) { |
c531c9ec2
|
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
struct nfc_se *se; int rc; pr_debug("%s se index %d ", dev_name(&dev->dev), se_idx); device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } if (!dev->dev_up) { rc = -ENODEV; goto error; } if (dev->polling) { rc = -EBUSY; goto error; } if (!dev->ops->enable_se || !dev->ops->disable_se) { rc = -EOPNOTSUPP; goto error; } |
d8eb18eec
|
541 |
se = nfc_find_se(dev, se_idx); |
c531c9ec2
|
542 543 544 545 |
if (!se) { rc = -EINVAL; goto error; } |
2c3832834
|
546 |
if (se->state == NFC_SE_ENABLED) { |
c531c9ec2
|
547 548 549 550 551 |
rc = -EALREADY; goto error; } rc = dev->ops->enable_se(dev, se_idx); |
39525ee1d
|
552 553 |
if (rc >= 0) se->state = NFC_SE_ENABLED; |
c531c9ec2
|
554 555 556 557 558 559 560 561 |
error: device_unlock(&dev->dev); return rc; } int nfc_disable_se(struct nfc_dev *dev, u32 se_idx) { |
c531c9ec2
|
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 |
struct nfc_se *se; int rc; pr_debug("%s se index %d ", dev_name(&dev->dev), se_idx); device_lock(&dev->dev); if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error; } if (!dev->dev_up) { rc = -ENODEV; goto error; } if (!dev->ops->enable_se || !dev->ops->disable_se) { rc = -EOPNOTSUPP; goto error; } |
d8eb18eec
|
584 |
se = nfc_find_se(dev, se_idx); |
c531c9ec2
|
585 586 587 588 |
if (!se) { rc = -EINVAL; goto error; } |
2c3832834
|
589 |
if (se->state == NFC_SE_DISABLED) { |
c531c9ec2
|
590 591 592 593 594 |
rc = -EALREADY; goto error; } rc = dev->ops->disable_se(dev, se_idx); |
39525ee1d
|
595 596 |
if (rc >= 0) se->state = NFC_SE_DISABLED; |
c531c9ec2
|
597 598 599 600 601 |
error: device_unlock(&dev->dev); return rc; } |
541d920b0
|
602 603 |
int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len) { |
0a40acb24
|
604 605 |
pr_debug("dev_name=%s gb_len=%d ", dev_name(&dev->dev), gb_len); |
541d920b0
|
606 |
|
d646960f7
|
607 |
return nfc_llcp_set_remote_gb(dev, gb, gb_len); |
541d920b0
|
608 609 |
} EXPORT_SYMBOL(nfc_set_remote_general_bytes); |
ab73b7513
|
610 611 612 613 614 615 616 617 |
u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len) { pr_debug("dev_name=%s ", dev_name(&dev->dev)); return nfc_llcp_general_bytes(dev, gb_len); } EXPORT_SYMBOL(nfc_get_local_general_bytes); |
73167ced3
|
618 619 620 621 622 623 624 625 626 627 628 |
int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb) { /* Only LLCP target mode for now */ if (dev->dep_link_up == false) { kfree_skb(skb); return -ENOLINK; } return nfc_llcp_data_received(dev, skb); } EXPORT_SYMBOL(nfc_tm_data_received); |
fc40a8c1a
|
629 630 631 632 633 634 635 636 637 638 639 640 641 642 |
int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode, u8 *gb, size_t gb_len) { int rc; device_lock(&dev->dev); dev->polling = false; if (gb != NULL) { rc = nfc_set_remote_general_bytes(dev, gb, gb_len); if (rc < 0) goto out; } |
f212ad5e9
|
643 |
dev->rf_mode = NFC_RF_TARGET; |
fc40a8c1a
|
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 |
if (protocol == NFC_PROTO_NFC_DEP_MASK) nfc_dep_link_is_up(dev, 0, comm_mode, NFC_RF_TARGET); rc = nfc_genl_tm_activated(dev, protocol); out: device_unlock(&dev->dev); return rc; } EXPORT_SYMBOL(nfc_tm_activated); int nfc_tm_deactivated(struct nfc_dev *dev) { dev->dep_link_up = false; |
5bcf099c1
|
659 |
dev->rf_mode = NFC_RF_NONE; |
fc40a8c1a
|
660 661 662 663 |
return nfc_genl_tm_deactivated(dev); } EXPORT_SYMBOL(nfc_tm_deactivated); |
3e256b8f8
|
664 |
/** |
7c7cd3bfe
|
665 |
* nfc_alloc_send_skb - allocate a skb for data exchange responses |
3e256b8f8
|
666 667 |
* * @size: size to allocate |
3e256b8f8
|
668 |
*/ |
7c7cd3bfe
|
669 |
struct sk_buff *nfc_alloc_send_skb(struct nfc_dev *dev, struct sock *sk, |
0a40acb24
|
670 671 |
unsigned int flags, unsigned int size, unsigned int *err) |
7c7cd3bfe
|
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 |
{ struct sk_buff *skb; unsigned int total_size; total_size = size + dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; skb = sock_alloc_send_skb(sk, total_size, flags & MSG_DONTWAIT, err); if (skb) skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); return skb; } /** * nfc_alloc_recv_skb - allocate a skb for data exchange responses * * @size: size to allocate * @gfp: gfp flags */ struct sk_buff *nfc_alloc_recv_skb(unsigned int size, gfp_t gfp) |
3e256b8f8
|
693 694 695 696 697 698 699 700 701 702 703 704 |
{ struct sk_buff *skb; unsigned int total_size; total_size = size + 1; skb = alloc_skb(total_size, gfp); if (skb) skb_reserve(skb, 1); return skb; } |
7c7cd3bfe
|
705 |
EXPORT_SYMBOL(nfc_alloc_recv_skb); |
3e256b8f8
|
706 |
|
4d12b8b12
|
707 708 709 710 711 |
/** * nfc_targets_found - inform that targets were found * * @dev: The nfc device that found the targets * @targets: array of nfc targets found |
ffbab1c93
|
712 |
* @n_targets: targets array size |
4d12b8b12
|
713 714 715 716 |
* * The device driver must call this function when one or many nfc targets * are found. After calling this function, the device driver must stop * polling for targets. |
d94f9c55f
|
717 718 |
* NOTE: This function can be called with targets=NULL and n_targets=0 to * notify a driver error, meaning that the polling operation cannot complete. |
d4ccb1328
|
719 720 721 |
* IMPORTANT: this function must not be called from an atomic context. * In addition, it must also not be called from a context that would prevent * the NFC Core to call other nfc ops entry point concurrently. |
4d12b8b12
|
722 |
*/ |
0a40acb24
|
723 724 |
int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, int n_targets) |
4d12b8b12
|
725 |
{ |
c4fbb6515
|
726 |
int i; |
20c239c13
|
727 728 |
pr_debug("dev_name=%s n_targets=%d ", dev_name(&dev->dev), n_targets); |
4d12b8b12
|
729 |
|
c4fbb6515
|
730 |
for (i = 0; i < n_targets; i++) |
01ae0eea9
|
731 |
targets[i].idx = dev->target_next_idx++; |
c4fbb6515
|
732 |
|
d4ccb1328
|
733 |
device_lock(&dev->dev); |
4d12b8b12
|
734 |
|
8668fdd6e
|
735 736 737 738 739 740 |
if (dev->polling == false) { device_unlock(&dev->dev); return 0; } dev->polling = false; |
4d12b8b12
|
741 742 743 |
dev->targets_generation++; kfree(dev->targets); |
d94f9c55f
|
744 |
dev->targets = NULL; |
4d12b8b12
|
745 |
|
d94f9c55f
|
746 747 748 749 750 751 752 753 754 755 |
if (targets) { dev->targets = kmemdup(targets, n_targets * sizeof(struct nfc_target), GFP_ATOMIC); if (!dev->targets) { dev->n_targets = 0; device_unlock(&dev->dev); return -ENOMEM; } |
4d12b8b12
|
756 757 758 |
} dev->n_targets = n_targets; |
d4ccb1328
|
759 |
device_unlock(&dev->dev); |
4d12b8b12
|
760 761 762 763 764 765 |
nfc_genl_targets_found(dev); return 0; } EXPORT_SYMBOL(nfc_targets_found); |
d4ccb1328
|
766 767 768 769 770 771 772 773 774 775 776 777 |
/** * nfc_target_lost - inform that an activated target went out of field * * @dev: The nfc device that had the activated target in field * @target_idx: the nfc index of the target * * The device driver must call this function when the activated target * goes out of the field. * IMPORTANT: this function must not be called from an atomic context. * In addition, it must also not be called from a context that would prevent * the NFC Core to call other nfc ops entry point concurrently. */ |
e1da0efa2
|
778 779 780 781 782 783 784 |
int nfc_target_lost(struct nfc_dev *dev, u32 target_idx) { struct nfc_target *tg; int i; pr_debug("dev_name %s n_target %d ", dev_name(&dev->dev), target_idx); |
d4ccb1328
|
785 |
device_lock(&dev->dev); |
e1da0efa2
|
786 787 788 789 790 791 792 793 |
for (i = 0; i < dev->n_targets; i++) { tg = &dev->targets[i]; if (tg->idx == target_idx) break; } if (i == dev->n_targets) { |
d4ccb1328
|
794 |
device_unlock(&dev->dev); |
e1da0efa2
|
795 796 797 798 799 |
return -EINVAL; } dev->targets_generation++; dev->n_targets--; |
900994332
|
800 |
dev->active_target = NULL; |
e1da0efa2
|
801 802 803 804 805 806 807 808 |
if (dev->n_targets) { memcpy(&dev->targets[i], &dev->targets[i + 1], (dev->n_targets - i) * sizeof(struct nfc_target)); } else { kfree(dev->targets); dev->targets = NULL; } |
d4ccb1328
|
809 |
device_unlock(&dev->dev); |
e1da0efa2
|
810 811 812 813 814 815 |
nfc_genl_target_lost(dev, target_idx); return 0; } EXPORT_SYMBOL(nfc_target_lost); |
9eb334ac1
|
816 |
inline void nfc_driver_failure(struct nfc_dev *dev, int err) |
456411ca8
|
817 |
{ |
9eb334ac1
|
818 |
nfc_targets_found(dev, NULL, 0); |
456411ca8
|
819 820 |
} EXPORT_SYMBOL(nfc_driver_failure); |
fed7c25ec
|
821 822 |
int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type) { |
c531c9ec2
|
823 |
struct nfc_se *se; |
2757c3723
|
824 |
int rc; |
fed7c25ec
|
825 826 827 |
pr_debug("%s se index %d ", dev_name(&dev->dev), se_idx); |
d8eb18eec
|
828 |
se = nfc_find_se(dev, se_idx); |
c531c9ec2
|
829 830 |
if (se) return -EALREADY; |
fed7c25ec
|
831 832 833 834 835 836 837 838 839 840 841 |
se = kzalloc(sizeof(struct nfc_se), GFP_KERNEL); if (!se) return -ENOMEM; se->idx = se_idx; se->type = type; se->state = NFC_SE_DISABLED; INIT_LIST_HEAD(&se->list); list_add(&se->list, &dev->secure_elements); |
2757c3723
|
842 843 844 845 846 847 848 |
rc = nfc_genl_se_added(dev, se_idx, type); if (rc < 0) { list_del(&se->list); kfree(se); return rc; } |
fed7c25ec
|
849 850 851 852 853 854 855 |
return 0; } EXPORT_SYMBOL(nfc_add_se); int nfc_remove_se(struct nfc_dev *dev, u32 se_idx) { struct nfc_se *se, *n; |
2757c3723
|
856 |
int rc; |
fed7c25ec
|
857 858 859 860 861 862 |
pr_debug("%s se index %d ", dev_name(&dev->dev), se_idx); list_for_each_entry_safe(se, n, &dev->secure_elements, list) if (se->idx == se_idx) { |
2757c3723
|
863 864 865 |
rc = nfc_genl_se_removed(dev, se_idx); if (rc < 0) return rc; |
fed7c25ec
|
866 867 868 869 870 871 872 873 874 |
list_del(&se->list); kfree(se); return 0; } return -EINVAL; } EXPORT_SYMBOL(nfc_remove_se); |
447b27c4f
|
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 |
int nfc_se_transaction(struct nfc_dev *dev, u8 se_idx, struct nfc_evt_transaction *evt_transaction) { int rc; pr_debug("transaction: %x ", se_idx); device_lock(&dev->dev); if (!evt_transaction) { rc = -EPROTO; goto out; } rc = nfc_genl_se_transaction(dev, se_idx, evt_transaction); out: device_unlock(&dev->dev); return rc; } EXPORT_SYMBOL(nfc_se_transaction); |
9afec6d38
|
896 897 898 899 900 901 902 903 904 905 906 907 908 |
int nfc_se_connectivity(struct nfc_dev *dev, u8 se_idx) { int rc; pr_debug("connectivity: %x ", se_idx); device_lock(&dev->dev); rc = nfc_genl_se_connectivity(dev, se_idx); device_unlock(&dev->dev); return rc; } EXPORT_SYMBOL(nfc_se_connectivity); |
3e256b8f8
|
909 910 911 |
static void nfc_release(struct device *d) { struct nfc_dev *dev = to_nfc_dev(d); |
ee656e9d0
|
912 |
struct nfc_se *se, *n; |
3e256b8f8
|
913 |
|
20c239c13
|
914 915 |
pr_debug("dev_name=%s ", dev_name(&dev->dev)); |
3e256b8f8
|
916 |
|
4d12b8b12
|
917 918 |
nfc_genl_data_exit(&dev->genl_data); kfree(dev->targets); |
ee656e9d0
|
919 920 921 922 923 924 |
list_for_each_entry_safe(se, n, &dev->secure_elements, list) { nfc_genl_se_removed(dev, se->idx); list_del(&se->list); kfree(se); } |
20777bc57
|
925 |
ida_simple_remove(&nfc_index_ida, dev->idx); |
3e256b8f8
|
926 927 |
kfree(dev); } |
c8d56ae78
|
928 929 930 931 932 933 934 |
static void nfc_check_pres_work(struct work_struct *work) { struct nfc_dev *dev = container_of(work, struct nfc_dev, check_pres_work); int rc; device_lock(&dev->dev); |
900994332
|
935 936 |
if (dev->active_target && timer_pending(&dev->check_pres_timer) == 0) { rc = dev->ops->check_presence(dev, dev->active_target); |
632c016ab
|
937 938 |
if (rc == -EOPNOTSUPP) goto exit; |
f0c910381
|
939 |
if (rc) { |
d4ccb1328
|
940 941 942 943 |
u32 active_target_idx = dev->active_target->idx; device_unlock(&dev->dev); nfc_target_lost(dev, active_target_idx); return; |
c8d56ae78
|
944 |
} |
f0c910381
|
945 946 947 948 |
if (!dev->shutting_down) mod_timer(&dev->check_pres_timer, jiffies + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); |
c8d56ae78
|
949 |
} |
632c016ab
|
950 |
exit: |
c8d56ae78
|
951 952 |
device_unlock(&dev->dev); } |
4b519bb49
|
953 |
static void nfc_check_pres_timeout(struct timer_list *t) |
c8d56ae78
|
954 |
{ |
4b519bb49
|
955 |
struct nfc_dev *dev = from_timer(dev, t, check_pres_timer); |
c8d56ae78
|
956 |
|
916082b07
|
957 |
schedule_work(&dev->check_pres_work); |
c8d56ae78
|
958 |
} |
3e256b8f8
|
959 960 961 962 963 |
struct class nfc_class = { .name = "nfc", .dev_release = nfc_release, }; EXPORT_SYMBOL(nfc_class); |
9f3b795a6
|
964 |
static int match_idx(struct device *d, const void *data) |
3e256b8f8
|
965 966 |
{ struct nfc_dev *dev = to_nfc_dev(d); |
9f3b795a6
|
967 |
const unsigned int *idx = data; |
3e256b8f8
|
968 969 970 |
return dev->idx == *idx; } |
95c961747
|
971 |
struct nfc_dev *nfc_get_device(unsigned int idx) |
3e256b8f8
|
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 |
{ struct device *d; d = class_find_device(&nfc_class, NULL, &idx, match_idx); if (!d) return NULL; return to_nfc_dev(d); } /** * nfc_allocate_device - allocate a new nfc device * * @ops: device operations * @supported_protocols: NFC protocols supported by the device */ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, |
0a40acb24
|
989 990 |
u32 supported_protocols, int tx_headroom, int tx_tailroom) |
3e256b8f8
|
991 |
{ |
3e256b8f8
|
992 |
struct nfc_dev *dev; |
20777bc57
|
993 |
int rc; |
3e256b8f8
|
994 995 |
if (!ops->start_poll || !ops->stop_poll || !ops->activate_target || |
be9ae4ce4
|
996 |
!ops->deactivate_target || !ops->im_transceive) |
3e256b8f8
|
997 998 999 1000 1001 1002 1003 1004 |
return NULL; if (!supported_protocols) return NULL; dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL); if (!dev) return NULL; |
20777bc57
|
1005 1006 1007 1008 1009 1010 1011 1012 |
rc = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL); if (rc < 0) goto err_free_dev; dev->idx = rc; dev->dev.class = &nfc_class; dev_set_name(&dev->dev, "nfc%d", dev->idx); device_initialize(&dev->dev); |
3e256b8f8
|
1013 1014 |
dev->ops = ops; dev->supported_protocols = supported_protocols; |
e8753043f
|
1015 1016 |
dev->tx_headroom = tx_headroom; dev->tx_tailroom = tx_tailroom; |
fed7c25ec
|
1017 |
INIT_LIST_HEAD(&dev->secure_elements); |
3e256b8f8
|
1018 |
|
4d12b8b12
|
1019 |
nfc_genl_data_init(&dev->genl_data); |
5bcf099c1
|
1020 |
dev->rf_mode = NFC_RF_NONE; |
d4ccb1328
|
1021 |
|
4d12b8b12
|
1022 1023 |
/* first generation must not be 0 */ dev->targets_generation = 1; |
c8d56ae78
|
1024 |
if (ops->check_presence) { |
4b519bb49
|
1025 |
timer_setup(&dev->check_pres_timer, nfc_check_pres_timeout, 0); |
c8d56ae78
|
1026 |
INIT_WORK(&dev->check_pres_work, nfc_check_pres_work); |
c8d56ae78
|
1027 |
} |
3e256b8f8
|
1028 |
return dev; |
20777bc57
|
1029 1030 1031 |
err_free_dev: kfree(dev); |
c45e3e4c5
|
1032 |
return NULL; |
3e256b8f8
|
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 |
} EXPORT_SYMBOL(nfc_allocate_device); /** * nfc_register_device - register a nfc device in the nfc subsystem * * @dev: The nfc device to register */ int nfc_register_device(struct nfc_dev *dev) { int rc; |
20c239c13
|
1044 1045 |
pr_debug("dev_name=%s ", dev_name(&dev->dev)); |
3e256b8f8
|
1046 1047 1048 1049 1050 |
mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; rc = device_add(&dev->dev); mutex_unlock(&nfc_devlist_mutex); |
4d12b8b12
|
1051 1052 |
if (rc < 0) return rc; |
d646960f7
|
1053 1054 1055 1056 |
rc = nfc_llcp_register_device(dev); if (rc) pr_err("Could not register llcp device "); |
4d12b8b12
|
1057 1058 |
rc = nfc_genl_device_added(dev); if (rc) |
20c239c13
|
1059 1060 1061 |
pr_debug("The userspace won't be notified that the device %s was added ", dev_name(&dev->dev)); |
4d12b8b12
|
1062 |
|
be055b2f8
|
1063 1064 1065 1066 1067 1068 1069 1070 |
dev->rfkill = rfkill_alloc(dev_name(&dev->dev), &dev->dev, RFKILL_TYPE_NFC, &nfc_rfkill_ops, dev); if (dev->rfkill) { if (rfkill_register(dev->rfkill) < 0) { rfkill_destroy(dev->rfkill); dev->rfkill = NULL; } } |
4d12b8b12
|
1071 |
return 0; |
3e256b8f8
|
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 |
} EXPORT_SYMBOL(nfc_register_device); /** * nfc_unregister_device - unregister a nfc device in the nfc subsystem * * @dev: The nfc device to unregister */ void nfc_unregister_device(struct nfc_dev *dev) { |
20777bc57
|
1082 |
int rc; |
4d12b8b12
|
1083 |
|
20c239c13
|
1084 1085 |
pr_debug("dev_name=%s ", dev_name(&dev->dev)); |
3e256b8f8
|
1086 |
|
be055b2f8
|
1087 1088 1089 1090 |
if (dev->rfkill) { rfkill_unregister(dev->rfkill); rfkill_destroy(dev->rfkill); } |
f0c910381
|
1091 1092 1093 1094 1095 1096 1097 |
if (dev->ops->check_presence) { device_lock(&dev->dev); dev->shutting_down = true; device_unlock(&dev->dev); del_timer_sync(&dev->check_pres_timer); cancel_work_sync(&dev->check_pres_work); } |
3e256b8f8
|
1098 |
|
f0c910381
|
1099 1100 1101 1102 1103 |
rc = nfc_genl_device_removed(dev); if (rc) pr_debug("The userspace won't be notified that the device %s " "was removed ", dev_name(&dev->dev)); |
4d12b8b12
|
1104 |
|
d646960f7
|
1105 |
nfc_llcp_unregister_device(dev); |
f0c910381
|
1106 1107 1108 1109 |
mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; device_del(&dev->dev); mutex_unlock(&nfc_devlist_mutex); |
3e256b8f8
|
1110 1111 1112 1113 1114 |
} EXPORT_SYMBOL(nfc_unregister_device); static int __init nfc_init(void) { |
4d12b8b12
|
1115 |
int rc; |
ed1e0ad88
|
1116 1117 |
pr_info("NFC Core ver %s ", VERSION); |
3e256b8f8
|
1118 |
|
4d12b8b12
|
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 |
rc = class_register(&nfc_class); if (rc) return rc; rc = nfc_genl_init(); if (rc) goto err_genl; /* the first generation must not be 0 */ nfc_devlist_generation = 1; |
23b7869c0
|
1129 1130 1131 |
rc = rawsock_init(); if (rc) goto err_rawsock; |
d646960f7
|
1132 1133 1134 |
rc = nfc_llcp_init(); if (rc) goto err_llcp_sock; |
c7fe3b52c
|
1135 1136 1137 |
rc = af_nfc_init(); if (rc) goto err_af_nfc; |
4d12b8b12
|
1138 |
return 0; |
c7fe3b52c
|
1139 |
err_af_nfc: |
d646960f7
|
1140 1141 |
nfc_llcp_exit(); err_llcp_sock: |
23b7869c0
|
1142 1143 |
rawsock_exit(); err_rawsock: |
c7fe3b52c
|
1144 |
nfc_genl_exit(); |
4d12b8b12
|
1145 1146 1147 |
err_genl: class_unregister(&nfc_class); return rc; |
3e256b8f8
|
1148 1149 1150 1151 |
} static void __exit nfc_exit(void) { |
c7fe3b52c
|
1152 |
af_nfc_exit(); |
d646960f7
|
1153 |
nfc_llcp_exit(); |
23b7869c0
|
1154 |
rawsock_exit(); |
4d12b8b12
|
1155 |
nfc_genl_exit(); |
3e256b8f8
|
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 |
class_unregister(&nfc_class); } subsys_initcall(nfc_init); module_exit(nfc_exit); MODULE_AUTHOR("Lauro Ramos Venancio <lauro.venancio@openbossa.org>"); MODULE_DESCRIPTION("NFC Core ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); |
1155bb617
|
1166 |
MODULE_ALIAS_NETPROTO(PF_NFC); |
5df16cad4
|
1167 |
MODULE_ALIAS_GENL_FAMILY(NFC_GENL_NAME); |