2019-05-19 21:51:43 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2011-12-14 23:43:12 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011 Intel Corporation. All rights reserved.
|
2014-10-21 22:52:50 +08:00
|
|
|
* Copyright (C) 2014 Marvell International Ltd.
|
2011-12-14 23:43:12 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#define pr_fmt(fmt) "llcp: %s: " fmt, __func__
|
|
|
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/nfc.h>
|
|
|
|
|
2013-04-26 17:49:40 +08:00
|
|
|
#include "nfc.h"
|
2011-12-14 23:43:12 +08:00
|
|
|
#include "llcp.h"
|
|
|
|
|
|
|
|
static u8 llcp_magic[3] = {0x46, 0x66, 0x6d};
|
|
|
|
|
2014-02-22 22:14:18 +08:00
|
|
|
static LIST_HEAD(llcp_devices);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
void nfc_llcp_sock_link(struct llcp_sock_list *l, struct sock *sk)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
2012-05-04 23:04:19 +08:00
|
|
|
write_lock(&l->lock);
|
|
|
|
sk_add_node(sk, &l->head);
|
|
|
|
write_unlock(&l->lock);
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *sk)
|
|
|
|
{
|
|
|
|
write_lock(&l->lock);
|
|
|
|
sk_del_node_init(sk);
|
|
|
|
write_unlock(&l->lock);
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2013-04-02 16:25:15 +08:00
|
|
|
void nfc_llcp_socket_remote_param_init(struct nfc_llcp_sock *sock)
|
|
|
|
{
|
|
|
|
sock->remote_rw = LLCP_DEFAULT_RW;
|
|
|
|
sock->remote_miu = LLCP_MAX_MIU + 1;
|
|
|
|
}
|
|
|
|
|
2012-10-27 00:20:10 +08:00
|
|
|
static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = sock->local;
|
|
|
|
struct sk_buff *s, *tmp;
|
|
|
|
|
|
|
|
pr_debug("%p\n", &sock->sk);
|
|
|
|
|
|
|
|
skb_queue_purge(&sock->tx_queue);
|
|
|
|
skb_queue_purge(&sock->tx_pending_queue);
|
|
|
|
|
|
|
|
if (local == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Search for local pending SKBs that are related to this socket */
|
|
|
|
skb_queue_walk_safe(&local->tx_queue, s, tmp) {
|
|
|
|
if (s->sk != &sock->sk)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
skb_unlink(s, &local->tx_queue);
|
|
|
|
kfree_skb(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-03 22:34:19 +08:00
|
|
|
static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool device,
|
2013-02-22 00:01:06 +08:00
|
|
|
int err)
|
2012-05-04 23:04:19 +08:00
|
|
|
{
|
|
|
|
struct sock *sk;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
struct hlist_node *tmp;
|
2012-05-04 23:04:19 +08:00
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-10-27 00:20:10 +08:00
|
|
|
skb_queue_purge(&local->tx_queue);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
write_lock(&local->sockets.lock);
|
2012-03-05 08:03:51 +08:00
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each_safe(sk, tmp, &local->sockets.head) {
|
2012-05-04 23:04:19 +08:00
|
|
|
llcp_sock = nfc_llcp_sock(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-09-26 20:22:10 +08:00
|
|
|
bh_lock_sock(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-10-27 00:20:10 +08:00
|
|
|
nfc_llcp_socket_purge(llcp_sock);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
if (sk->sk_state == LLCP_CONNECTED)
|
|
|
|
nfc_put_device(llcp_sock->dev);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
if (sk->sk_state == LLCP_LISTEN) {
|
2011-12-14 23:43:12 +08:00
|
|
|
struct nfc_llcp_sock *lsk, *n;
|
|
|
|
struct sock *accept_sk;
|
|
|
|
|
2012-10-17 21:23:39 +08:00
|
|
|
list_for_each_entry_safe(lsk, n,
|
|
|
|
&llcp_sock->accept_queue,
|
2012-03-05 08:03:52 +08:00
|
|
|
accept_queue) {
|
2011-12-14 23:43:12 +08:00
|
|
|
accept_sk = &lsk->sk;
|
2012-09-26 20:22:10 +08:00
|
|
|
bh_lock_sock(accept_sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
nfc_llcp_accept_unlink(accept_sk);
|
|
|
|
|
2013-02-22 00:01:06 +08:00
|
|
|
if (err)
|
|
|
|
accept_sk->sk_err = err;
|
2011-12-14 23:43:12 +08:00
|
|
|
accept_sk->sk_state = LLCP_CLOSED;
|
2013-02-22 00:01:06 +08:00
|
|
|
accept_sk->sk_state_change(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-09-26 20:22:10 +08:00
|
|
|
bh_unlock_sock(accept_sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
2012-10-15 21:09:52 +08:00
|
|
|
}
|
|
|
|
|
2013-02-22 00:01:06 +08:00
|
|
|
if (err)
|
|
|
|
sk->sk_err = err;
|
2012-05-04 23:04:19 +08:00
|
|
|
sk->sk_state = LLCP_CLOSED;
|
2013-02-22 00:01:06 +08:00
|
|
|
sk->sk_state_change(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-09-26 20:22:10 +08:00
|
|
|
bh_unlock_sock(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
sk_del_node_init(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
write_unlock(&local->sockets.lock);
|
2013-02-21 23:33:30 +08:00
|
|
|
|
2013-04-03 22:34:19 +08:00
|
|
|
/* If we still have a device, we keep the RAW sockets alive */
|
|
|
|
if (device == true)
|
2013-02-21 23:33:30 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
write_lock(&local->raw_sockets.lock);
|
|
|
|
|
|
|
|
sk_for_each_safe(sk, tmp, &local->raw_sockets.head) {
|
|
|
|
llcp_sock = nfc_llcp_sock(sk);
|
|
|
|
|
|
|
|
bh_lock_sock(sk);
|
|
|
|
|
|
|
|
nfc_llcp_socket_purge(llcp_sock);
|
|
|
|
|
2013-02-22 00:01:06 +08:00
|
|
|
if (err)
|
|
|
|
sk->sk_err = err;
|
2013-02-21 23:33:30 +08:00
|
|
|
sk->sk_state = LLCP_CLOSED;
|
|
|
|
sk->sk_state_change(sk);
|
|
|
|
|
|
|
|
bh_unlock_sock(sk);
|
|
|
|
|
|
|
|
sk_del_node_init(sk);
|
|
|
|
}
|
|
|
|
|
|
|
|
write_unlock(&local->raw_sockets.lock);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2012-05-04 17:24:16 +08:00
|
|
|
struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local)
|
|
|
|
{
|
|
|
|
kref_get(&local->ref);
|
|
|
|
|
|
|
|
return local;
|
|
|
|
}
|
|
|
|
|
2013-04-03 22:40:52 +08:00
|
|
|
static void local_cleanup(struct nfc_llcp_local *local)
|
2012-05-04 17:24:16 +08:00
|
|
|
{
|
2013-04-03 22:40:52 +08:00
|
|
|
nfc_llcp_socket_release(local, false, ENXIO);
|
2012-05-04 17:24:16 +08:00
|
|
|
del_timer_sync(&local->link_timer);
|
|
|
|
skb_queue_purge(&local->tx_queue);
|
2012-08-23 07:22:16 +08:00
|
|
|
cancel_work_sync(&local->tx_work);
|
|
|
|
cancel_work_sync(&local->rx_work);
|
|
|
|
cancel_work_sync(&local->timeout_work);
|
2012-05-04 17:24:16 +08:00
|
|
|
kfree_skb(local->rx_pending);
|
2024-06-12 13:13:20 +08:00
|
|
|
local->rx_pending = NULL;
|
2013-03-04 22:43:32 +08:00
|
|
|
del_timer_sync(&local->sdreq_timer);
|
|
|
|
cancel_work_sync(&local->sdreq_timeout_work);
|
2013-02-15 17:43:06 +08:00
|
|
|
nfc_llcp_free_sdp_tlv_list(&local->pending_sdreqs);
|
2013-02-21 22:40:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void local_release(struct kref *ref)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = container_of(ref, struct nfc_llcp_local, ref);
|
|
|
|
|
|
|
|
list_del(&local->list);
|
2013-04-03 22:40:52 +08:00
|
|
|
local_cleanup(local);
|
2012-05-04 17:24:16 +08:00
|
|
|
kfree(local);
|
|
|
|
}
|
|
|
|
|
|
|
|
int nfc_llcp_local_put(struct nfc_llcp_local *local)
|
|
|
|
{
|
2012-05-04 23:04:19 +08:00
|
|
|
if (local == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2012-05-04 17:24:16 +08:00
|
|
|
return kref_put(&local->ref, local_release);
|
|
|
|
}
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
|
|
|
|
u8 ssap, u8 dsap)
|
|
|
|
{
|
|
|
|
struct sock *sk;
|
2012-10-17 03:15:59 +08:00
|
|
|
struct nfc_llcp_sock *llcp_sock, *tmp_sock;
|
2012-06-25 21:46:28 +08:00
|
|
|
|
|
|
|
pr_debug("ssap dsap %d %d\n", ssap, dsap);
|
|
|
|
|
|
|
|
if (ssap == 0 && dsap == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
read_lock(&local->sockets.lock);
|
|
|
|
|
|
|
|
llcp_sock = NULL;
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each(sk, &local->sockets.head) {
|
2012-10-17 03:15:59 +08:00
|
|
|
tmp_sock = nfc_llcp_sock(sk);
|
2012-06-25 21:46:28 +08:00
|
|
|
|
2012-10-17 03:15:59 +08:00
|
|
|
if (tmp_sock->ssap == ssap && tmp_sock->dsap == dsap) {
|
|
|
|
llcp_sock = tmp_sock;
|
2012-06-25 21:46:28 +08:00
|
|
|
break;
|
2012-10-17 03:15:59 +08:00
|
|
|
}
|
2012-06-25 21:46:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
read_unlock(&local->sockets.lock);
|
|
|
|
|
|
|
|
if (llcp_sock == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sock_hold(&llcp_sock->sk);
|
|
|
|
|
|
|
|
return llcp_sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock)
|
|
|
|
{
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
static void nfc_llcp_timeout_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
|
2012-03-05 08:03:52 +08:00
|
|
|
timeout_work);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
nfc_dep_link_down(local->dev);
|
|
|
|
}
|
|
|
|
|
2017-10-11 18:33:44 +08:00
|
|
|
static void nfc_llcp_symm_timer(struct timer_list *t)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
2017-10-11 18:33:44 +08:00
|
|
|
struct nfc_llcp_local *local = from_timer(local, t, link_timer);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
pr_err("SYMM timeout\n");
|
|
|
|
|
2012-10-03 07:01:31 +08:00
|
|
|
schedule_work(&local->timeout_work);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2013-03-04 22:43:32 +08:00
|
|
|
static void nfc_llcp_sdreq_timeout_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
unsigned long time;
|
|
|
|
HLIST_HEAD(nl_sdres_list);
|
|
|
|
struct hlist_node *n;
|
|
|
|
struct nfc_llcp_sdp_tlv *sdp;
|
|
|
|
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
|
|
|
|
sdreq_timeout_work);
|
|
|
|
|
|
|
|
mutex_lock(&local->sdreq_lock);
|
|
|
|
|
|
|
|
time = jiffies - msecs_to_jiffies(3 * local->remote_lto);
|
|
|
|
|
|
|
|
hlist_for_each_entry_safe(sdp, n, &local->pending_sdreqs, node) {
|
|
|
|
if (time_after(sdp->time, time))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sdp->sap = LLCP_SDP_UNBOUND;
|
|
|
|
|
|
|
|
hlist_del(&sdp->node);
|
|
|
|
|
|
|
|
hlist_add_head(&sdp->node, &nl_sdres_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hlist_empty(&local->pending_sdreqs))
|
|
|
|
mod_timer(&local->sdreq_timer,
|
|
|
|
jiffies + msecs_to_jiffies(3 * local->remote_lto));
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdreq_lock);
|
|
|
|
|
|
|
|
if (!hlist_empty(&nl_sdres_list))
|
|
|
|
nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);
|
|
|
|
}
|
|
|
|
|
2017-10-11 18:33:44 +08:00
|
|
|
static void nfc_llcp_sdreq_timer(struct timer_list *t)
|
2013-03-04 22:43:32 +08:00
|
|
|
{
|
2017-10-11 18:33:44 +08:00
|
|
|
struct nfc_llcp_local *local = from_timer(local, t, sdreq_timer);
|
2013-03-04 22:43:32 +08:00
|
|
|
|
|
|
|
schedule_work(&local->sdreq_timeout_work);
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
|
|
|
|
{
|
2014-02-26 10:26:45 +08:00
|
|
|
struct nfc_llcp_local *local;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2014-02-26 10:26:45 +08:00
|
|
|
list_for_each_entry(local, &llcp_devices, list)
|
2011-12-14 23:43:12 +08:00
|
|
|
if (local->dev == dev)
|
|
|
|
return local;
|
|
|
|
|
|
|
|
pr_debug("No device found\n");
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *wks[] = {
|
|
|
|
NULL,
|
|
|
|
NULL, /* SDP */
|
|
|
|
"urn:nfc:sn:ip",
|
|
|
|
"urn:nfc:sn:obex",
|
|
|
|
"urn:nfc:sn:snep",
|
|
|
|
};
|
|
|
|
|
|
|
|
static int nfc_llcp_wks_sap(char *service_name, size_t service_name_len)
|
|
|
|
{
|
|
|
|
int sap, num_wks;
|
|
|
|
|
|
|
|
pr_debug("%s\n", service_name);
|
|
|
|
|
|
|
|
if (service_name == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
num_wks = ARRAY_SIZE(wks);
|
|
|
|
|
2012-03-05 08:03:52 +08:00
|
|
|
for (sap = 0; sap < num_wks; sap++) {
|
2011-12-14 23:43:12 +08:00
|
|
|
if (wks[sap] == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (strncmp(wks[sap], service_name, service_name_len) == 0)
|
|
|
|
return sap;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
static
|
|
|
|
struct nfc_llcp_sock *nfc_llcp_sock_from_sn(struct nfc_llcp_local *local,
|
|
|
|
u8 *sn, size_t sn_len)
|
|
|
|
{
|
|
|
|
struct sock *sk;
|
|
|
|
struct nfc_llcp_sock *llcp_sock, *tmp_sock;
|
|
|
|
|
|
|
|
pr_debug("sn %zd %p\n", sn_len, sn);
|
|
|
|
|
|
|
|
if (sn == NULL || sn_len == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
read_lock(&local->sockets.lock);
|
|
|
|
|
|
|
|
llcp_sock = NULL;
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each(sk, &local->sockets.head) {
|
2012-06-25 21:46:28 +08:00
|
|
|
tmp_sock = nfc_llcp_sock(sk);
|
|
|
|
|
|
|
|
pr_debug("llcp sock %p\n", tmp_sock);
|
|
|
|
|
2012-10-15 21:08:29 +08:00
|
|
|
if (tmp_sock->sk.sk_type == SOCK_STREAM &&
|
|
|
|
tmp_sock->sk.sk_state != LLCP_LISTEN)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (tmp_sock->sk.sk_type == SOCK_DGRAM &&
|
|
|
|
tmp_sock->sk.sk_state != LLCP_BOUND)
|
2012-06-25 21:46:28 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (tmp_sock->service_name == NULL ||
|
|
|
|
tmp_sock->service_name_len == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (tmp_sock->service_name_len != sn_len)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (memcmp(sn, tmp_sock->service_name, sn_len) == 0) {
|
|
|
|
llcp_sock = tmp_sock;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
read_unlock(&local->sockets.lock);
|
|
|
|
|
|
|
|
pr_debug("Found llcp sock %p\n", llcp_sock);
|
|
|
|
|
|
|
|
return llcp_sock;
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
|
2012-03-05 08:03:52 +08:00
|
|
|
struct nfc_llcp_sock *sock)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
|
|
|
if (sock->service_name != NULL && sock->service_name_len > 0) {
|
|
|
|
int ssap = nfc_llcp_wks_sap(sock->service_name,
|
2012-03-05 08:03:52 +08:00
|
|
|
sock->service_name_len);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
if (ssap > 0) {
|
|
|
|
pr_debug("WKS %d\n", ssap);
|
|
|
|
|
|
|
|
/* This is a WKS, let's check if it's free */
|
|
|
|
if (local->local_wks & BIT(ssap)) {
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
2012-03-05 08:03:38 +08:00
|
|
|
set_bit(ssap, &local->local_wks);
|
2011-12-14 23:43:12 +08:00
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return ssap;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-06-25 21:46:28 +08:00
|
|
|
* Check if there already is a non WKS socket bound
|
|
|
|
* to this service name.
|
2011-12-14 23:43:12 +08:00
|
|
|
*/
|
2012-06-25 21:46:28 +08:00
|
|
|
if (nfc_llcp_sock_from_sn(local, sock->service_name,
|
|
|
|
sock->service_name_len) != NULL) {
|
2011-12-14 23:43:12 +08:00
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
return LLCP_SDP_UNBOUND;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-06-22 23:15:20 +08:00
|
|
|
} else if (sock->ssap != 0 && sock->ssap < LLCP_WKS_NUM_SAP) {
|
|
|
|
if (!test_bit(sock->ssap, &local->local_wks)) {
|
|
|
|
set_bit(sock->ssap, &local->local_wks);
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-06-22 23:15:20 +08:00
|
|
|
return sock->ssap;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 nfc_llcp_get_local_ssap(struct nfc_llcp_local *local)
|
|
|
|
{
|
|
|
|
u8 local_ssap;
|
|
|
|
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
|
|
|
local_ssap = find_first_zero_bit(&local->local_sap, LLCP_LOCAL_NUM_SAP);
|
|
|
|
if (local_ssap == LLCP_LOCAL_NUM_SAP) {
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
2012-03-05 08:03:38 +08:00
|
|
|
set_bit(local_ssap, &local->local_sap);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return local_ssap + LLCP_LOCAL_SAP_OFFSET;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap)
|
|
|
|
{
|
|
|
|
u8 local_ssap;
|
|
|
|
unsigned long *sdp;
|
|
|
|
|
|
|
|
if (ssap < LLCP_WKS_NUM_SAP) {
|
|
|
|
local_ssap = ssap;
|
|
|
|
sdp = &local->local_wks;
|
|
|
|
} else if (ssap < LLCP_LOCAL_NUM_SAP) {
|
2012-06-25 21:46:28 +08:00
|
|
|
atomic_t *client_cnt;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
local_ssap = ssap - LLCP_WKS_NUM_SAP;
|
|
|
|
sdp = &local->local_sdp;
|
2012-06-25 21:46:28 +08:00
|
|
|
client_cnt = &local->local_sdp_cnt[local_ssap];
|
|
|
|
|
|
|
|
pr_debug("%d clients\n", atomic_read(client_cnt));
|
|
|
|
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
|
|
|
if (atomic_dec_and_test(client_cnt)) {
|
|
|
|
struct nfc_llcp_sock *l_sock;
|
|
|
|
|
|
|
|
pr_debug("No more clients for SAP %d\n", ssap);
|
|
|
|
|
|
|
|
clear_bit(local_ssap, sdp);
|
|
|
|
|
|
|
|
/* Find the listening sock and set it back to UNBOUND */
|
|
|
|
l_sock = nfc_llcp_sock_get(local, ssap, LLCP_SAP_SDP);
|
|
|
|
if (l_sock) {
|
|
|
|
l_sock->ssap = LLCP_SDP_UNBOUND;
|
|
|
|
nfc_llcp_sock_put(l_sock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return;
|
2011-12-14 23:43:12 +08:00
|
|
|
} else if (ssap < LLCP_MAX_SAP) {
|
|
|
|
local_ssap = ssap - LLCP_LOCAL_NUM_SAP;
|
|
|
|
sdp = &local->local_sap;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
2012-03-05 08:03:38 +08:00
|
|
|
clear_bit(local_ssap, sdp);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
}
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
static u8 nfc_llcp_reserve_sdp_ssap(struct nfc_llcp_local *local)
|
|
|
|
{
|
|
|
|
u8 ssap;
|
|
|
|
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
|
|
|
ssap = find_first_zero_bit(&local->local_sdp, LLCP_SDP_NUM_SAP);
|
|
|
|
if (ssap == LLCP_SDP_NUM_SAP) {
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("SDP ssap %d\n", LLCP_WKS_NUM_SAP + ssap);
|
|
|
|
|
|
|
|
set_bit(ssap, &local->local_sdp);
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_WKS_NUM_SAP + ssap;
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
|
|
|
|
{
|
2019-02-22 15:37:58 +08:00
|
|
|
u8 *gb_cur, version, version_length;
|
|
|
|
u8 lto_length, wks_length, miux_length;
|
|
|
|
u8 *version_tlv = NULL, *lto_tlv = NULL,
|
|
|
|
*wks_tlv = NULL, *miux_tlv = NULL;
|
2013-06-03 18:10:04 +08:00
|
|
|
__be16 wks = cpu_to_be16(local->local_wks);
|
2011-12-14 23:43:12 +08:00
|
|
|
u8 gb_len = 0;
|
2012-09-02 21:21:46 +08:00
|
|
|
int ret = 0;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
version = LLCP_VERSION_11;
|
|
|
|
version_tlv = nfc_llcp_build_tlv(LLCP_TLV_VERSION, &version,
|
2012-03-05 08:03:52 +08:00
|
|
|
1, &version_length);
|
2019-02-22 15:37:58 +08:00
|
|
|
if (!version_tlv) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
gb_len += version_length;
|
|
|
|
|
2012-10-17 20:43:39 +08:00
|
|
|
lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &local->lto, 1, <o_length);
|
2019-02-22 15:37:58 +08:00
|
|
|
if (!lto_tlv) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
gb_len += lto_length;
|
|
|
|
|
|
|
|
pr_debug("Local wks 0x%lx\n", local->local_wks);
|
2013-06-03 18:10:04 +08:00
|
|
|
wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&wks, 2, &wks_length);
|
2019-02-22 15:37:58 +08:00
|
|
|
if (!wks_tlv) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
gb_len += wks_length;
|
|
|
|
|
2012-10-17 20:43:39 +08:00
|
|
|
miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
|
2012-04-11 01:43:19 +08:00
|
|
|
&miux_length);
|
2019-02-22 15:37:58 +08:00
|
|
|
if (!miux_tlv) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-04-11 01:43:19 +08:00
|
|
|
gb_len += miux_length;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
gb_len += ARRAY_SIZE(llcp_magic);
|
|
|
|
|
|
|
|
if (gb_len > NFC_MAX_GT_LEN) {
|
2012-09-02 21:21:46 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
gb_cur = local->gb;
|
|
|
|
|
|
|
|
memcpy(gb_cur, llcp_magic, ARRAY_SIZE(llcp_magic));
|
|
|
|
gb_cur += ARRAY_SIZE(llcp_magic);
|
|
|
|
|
|
|
|
memcpy(gb_cur, version_tlv, version_length);
|
|
|
|
gb_cur += version_length;
|
|
|
|
|
|
|
|
memcpy(gb_cur, lto_tlv, lto_length);
|
|
|
|
gb_cur += lto_length;
|
|
|
|
|
|
|
|
memcpy(gb_cur, wks_tlv, wks_length);
|
|
|
|
gb_cur += wks_length;
|
|
|
|
|
2012-04-11 01:43:19 +08:00
|
|
|
memcpy(gb_cur, miux_tlv, miux_length);
|
|
|
|
gb_cur += miux_length;
|
|
|
|
|
2012-09-02 21:21:46 +08:00
|
|
|
local->gb_len = gb_len;
|
|
|
|
|
|
|
|
out:
|
2011-12-14 23:43:12 +08:00
|
|
|
kfree(version_tlv);
|
|
|
|
kfree(lto_tlv);
|
2012-09-02 21:21:46 +08:00
|
|
|
kfree(wks_tlv);
|
|
|
|
kfree(miux_tlv);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-09-02 21:21:46 +08:00
|
|
|
return ret;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2012-06-22 08:04:53 +08:00
|
|
|
u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = nfc_llcp_find_local(dev);
|
|
|
|
if (local == NULL) {
|
|
|
|
*general_bytes_len = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
nfc_llcp_build_gb(local);
|
|
|
|
|
|
|
|
*general_bytes_len = local->gb_len;
|
|
|
|
|
|
|
|
return local->gb;
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len)
|
|
|
|
{
|
2014-02-25 09:18:10 +08:00
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
if (gb_len < 3 || gb_len > NFC_MAX_GT_LEN)
|
|
|
|
return -EINVAL;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2014-02-25 09:18:10 +08:00
|
|
|
local = nfc_llcp_find_local(dev);
|
2011-12-14 23:43:12 +08:00
|
|
|
if (local == NULL) {
|
|
|
|
pr_err("No LLCP device\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(local->remote_gb, 0, NFC_MAX_GT_LEN);
|
|
|
|
memcpy(local->remote_gb, gb, gb_len);
|
|
|
|
local->remote_gb_len = gb_len;
|
|
|
|
|
|
|
|
if (memcmp(local->remote_gb, llcp_magic, 3)) {
|
|
|
|
pr_err("MAC does not support LLCP\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2012-05-08 04:03:34 +08:00
|
|
|
return nfc_llcp_parse_gb_tlv(local,
|
|
|
|
&local->remote_gb[3],
|
|
|
|
local->remote_gb_len - 3);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static u8 nfc_llcp_dsap(struct sk_buff *pdu)
|
|
|
|
{
|
|
|
|
return (pdu->data[0] & 0xfc) >> 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 nfc_llcp_ptype(struct sk_buff *pdu)
|
|
|
|
{
|
|
|
|
return ((pdu->data[0] & 0x03) << 2) | ((pdu->data[1] & 0xc0) >> 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 nfc_llcp_ssap(struct sk_buff *pdu)
|
|
|
|
{
|
|
|
|
return pdu->data[1] & 0x3f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 nfc_llcp_ns(struct sk_buff *pdu)
|
|
|
|
{
|
|
|
|
return pdu->data[2] >> 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 nfc_llcp_nr(struct sk_buff *pdu)
|
|
|
|
{
|
|
|
|
return pdu->data[2] & 0xf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_set_nrns(struct nfc_llcp_sock *sock, struct sk_buff *pdu)
|
|
|
|
{
|
2012-04-11 01:43:14 +08:00
|
|
|
pdu->data[2] = (sock->send_n << 4) | (sock->recv_n);
|
2011-12-14 23:43:12 +08:00
|
|
|
sock->send_n = (sock->send_n + 1) % 16;
|
|
|
|
sock->recv_ack_n = (sock->recv_n - 1) % 16;
|
|
|
|
}
|
|
|
|
|
2012-09-27 00:16:44 +08:00
|
|
|
void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
|
|
|
|
struct sk_buff *skb, u8 direction)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb_copy = NULL, *nskb;
|
|
|
|
struct sock *sk;
|
|
|
|
u8 *data;
|
|
|
|
|
|
|
|
read_lock(&local->raw_sockets.lock);
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each(sk, &local->raw_sockets.head) {
|
2012-09-27 00:16:44 +08:00
|
|
|
if (sk->sk_state != LLCP_BOUND)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (skb_copy == NULL) {
|
2014-06-12 06:36:26 +08:00
|
|
|
skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE,
|
|
|
|
GFP_ATOMIC, true);
|
2012-09-27 00:16:44 +08:00
|
|
|
|
|
|
|
if (skb_copy == NULL)
|
|
|
|
continue;
|
|
|
|
|
2014-05-05 18:43:31 +08:00
|
|
|
data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE);
|
2012-09-27 00:16:44 +08:00
|
|
|
|
|
|
|
data[0] = local->dev ? local->dev->idx : 0xFF;
|
2014-05-05 18:43:31 +08:00
|
|
|
data[1] = direction & 0x01;
|
|
|
|
data[1] |= (RAW_PAYLOAD_LLCP << 1);
|
2012-09-27 00:16:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
nskb = skb_clone(skb_copy, GFP_ATOMIC);
|
|
|
|
if (!nskb)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (sock_queue_rcv_skb(sk, nskb))
|
|
|
|
kfree_skb(nskb);
|
|
|
|
}
|
|
|
|
|
|
|
|
read_unlock(&local->raw_sockets.lock);
|
|
|
|
|
|
|
|
kfree_skb(skb_copy);
|
|
|
|
}
|
|
|
|
|
2012-05-30 23:48:29 +08:00
|
|
|
static void nfc_llcp_tx_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
|
|
|
|
tx_work);
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct sock *sk;
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
|
|
|
|
skb = skb_dequeue(&local->tx_queue);
|
|
|
|
if (skb != NULL) {
|
|
|
|
sk = skb->sk;
|
|
|
|
llcp_sock = nfc_llcp_sock(sk);
|
2012-10-05 06:37:22 +08:00
|
|
|
|
|
|
|
if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
|
2013-06-05 23:15:59 +08:00
|
|
|
kfree_skb(skb);
|
2012-10-05 06:37:22 +08:00
|
|
|
nfc_llcp_send_symm(local->dev);
|
2013-05-28 21:03:17 +08:00
|
|
|
} else if (llcp_sock && !llcp_sock->remote_ready) {
|
|
|
|
skb_queue_head(&local->tx_queue, skb);
|
|
|
|
nfc_llcp_send_symm(local->dev);
|
2012-10-05 06:37:22 +08:00
|
|
|
} else {
|
2012-11-02 06:36:07 +08:00
|
|
|
struct sk_buff *copy_skb = NULL;
|
|
|
|
u8 ptype = nfc_llcp_ptype(skb);
|
2012-05-30 23:48:29 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
pr_debug("Sending pending skb\n");
|
2016-06-07 22:21:53 +08:00
|
|
|
print_hex_dump_debug("LLCP Tx: ", DUMP_PREFIX_OFFSET,
|
|
|
|
16, 1, skb->data, skb->len, true);
|
2012-05-30 23:48:29 +08:00
|
|
|
|
2013-06-04 17:34:51 +08:00
|
|
|
if (ptype == LLCP_PDU_DISC && sk != NULL &&
|
|
|
|
sk->sk_state == LLCP_DISCONNECTING) {
|
|
|
|
nfc_llcp_sock_unlink(&local->sockets, sk);
|
|
|
|
sock_orphan(sk);
|
|
|
|
sock_put(sk);
|
|
|
|
}
|
|
|
|
|
2012-11-02 06:36:07 +08:00
|
|
|
if (ptype == LLCP_PDU_I)
|
|
|
|
copy_skb = skb_copy(skb, GFP_ATOMIC);
|
|
|
|
|
2012-11-27 22:44:24 +08:00
|
|
|
__net_timestamp(skb);
|
|
|
|
|
2012-09-27 00:16:44 +08:00
|
|
|
nfc_llcp_send_to_raw_sock(local, skb,
|
2014-05-05 18:43:31 +08:00
|
|
|
NFC_DIRECTION_TX);
|
2012-09-27 00:16:44 +08:00
|
|
|
|
2012-05-30 23:48:29 +08:00
|
|
|
ret = nfc_data_exchange(local->dev, local->target_idx,
|
|
|
|
skb, nfc_llcp_recv, local);
|
|
|
|
|
2012-11-02 06:36:07 +08:00
|
|
|
if (ret) {
|
|
|
|
kfree_skb(copy_skb);
|
|
|
|
goto out;
|
2012-05-30 23:48:29 +08:00
|
|
|
}
|
2012-11-02 06:36:07 +08:00
|
|
|
|
|
|
|
if (ptype == LLCP_PDU_I && copy_skb)
|
|
|
|
skb_queue_tail(&llcp_sock->tx_pending_queue,
|
|
|
|
copy_skb);
|
2012-05-30 23:48:29 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
nfc_llcp_send_symm(local->dev);
|
|
|
|
}
|
|
|
|
|
2012-11-02 06:36:07 +08:00
|
|
|
out:
|
2012-05-30 23:48:29 +08:00
|
|
|
mod_timer(&local->link_timer,
|
|
|
|
jiffies + msecs_to_jiffies(2 * local->remote_lto));
|
|
|
|
}
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
static struct nfc_llcp_sock *nfc_llcp_connecting_sock_get(struct nfc_llcp_local *local,
|
|
|
|
u8 ssap)
|
|
|
|
{
|
|
|
|
struct sock *sk;
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
|
|
|
|
read_lock(&local->connecting_sockets.lock);
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each(sk, &local->connecting_sockets.head) {
|
2012-05-04 23:04:19 +08:00
|
|
|
llcp_sock = nfc_llcp_sock(sk);
|
|
|
|
|
2012-05-21 17:44:42 +08:00
|
|
|
if (llcp_sock->ssap == ssap) {
|
|
|
|
sock_hold(&llcp_sock->sk);
|
2012-05-04 23:04:19 +08:00
|
|
|
goto out;
|
2012-05-21 17:44:42 +08:00
|
|
|
}
|
2012-05-04 23:04:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
llcp_sock = NULL;
|
|
|
|
|
|
|
|
out:
|
|
|
|
read_unlock(&local->connecting_sockets.lock);
|
|
|
|
|
|
|
|
return llcp_sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
|
|
|
|
u8 *sn, size_t sn_len)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
llcp_sock = nfc_llcp_sock_from_sn(local, sn, sn_len);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
if (llcp_sock == NULL)
|
|
|
|
return NULL;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
sock_hold(&llcp_sock->sk);
|
|
|
|
|
|
|
|
return llcp_sock;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len)
|
|
|
|
{
|
|
|
|
u8 *tlv = &skb->data[2], type, length;
|
|
|
|
size_t tlv_array_len = skb->len - LLCP_HEADER_SIZE, offset = 0;
|
|
|
|
|
|
|
|
while (offset < tlv_array_len) {
|
|
|
|
type = tlv[0];
|
|
|
|
length = tlv[1];
|
|
|
|
|
|
|
|
pr_debug("type 0x%x length %d\n", type, length);
|
|
|
|
|
|
|
|
if (type == LLCP_TLV_SN) {
|
|
|
|
*sn_len = length;
|
|
|
|
return &tlv[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += length + 2;
|
|
|
|
tlv += length + 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-10-15 22:14:37 +08:00
|
|
|
static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
struct nfc_llcp_ui_cb *ui_cb;
|
|
|
|
u8 dsap, ssap;
|
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
|
|
|
ui_cb = nfc_llcp_ui_skb_cb(skb);
|
|
|
|
ui_cb->dsap = dsap;
|
|
|
|
ui_cb->ssap = ssap;
|
|
|
|
|
|
|
|
pr_debug("%d %d\n", dsap, ssap);
|
|
|
|
|
|
|
|
/* We're looking for a bound socket, not a client one */
|
|
|
|
llcp_sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
|
|
|
|
if (llcp_sock == NULL || llcp_sock->sk.sk_type != SOCK_DGRAM)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* There is no sequence with UI frames */
|
|
|
|
skb_pull(skb, LLCP_HEADER_SIZE);
|
2012-12-07 23:37:30 +08:00
|
|
|
if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
|
|
|
|
/*
|
|
|
|
* UI frames will be freed from the socket layer, so we
|
|
|
|
* need to keep them alive until someone receives them.
|
|
|
|
*/
|
|
|
|
skb_get(skb);
|
|
|
|
} else {
|
|
|
|
pr_err("Receive queue is full\n");
|
2012-10-15 22:14:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
|
2012-03-05 08:03:52 +08:00
|
|
|
struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
struct sock *new_sk, *parent;
|
|
|
|
struct nfc_llcp_sock *sock, *new_sock;
|
2012-05-04 23:04:19 +08:00
|
|
|
u8 dsap, ssap, reason;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
|
|
|
pr_debug("%d %d\n", dsap, ssap);
|
|
|
|
|
|
|
|
if (dsap != LLCP_SAP_SDP) {
|
2012-05-04 23:04:19 +08:00
|
|
|
sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
|
|
|
|
if (sock == NULL || sock->sk.sk_state != LLCP_LISTEN) {
|
2011-12-14 23:43:12 +08:00
|
|
|
reason = LLCP_DM_NOBOUND;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
u8 *sn;
|
|
|
|
size_t sn_len;
|
|
|
|
|
|
|
|
sn = nfc_llcp_connect_sn(skb, &sn_len);
|
|
|
|
if (sn == NULL) {
|
|
|
|
reason = LLCP_DM_NOBOUND;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("Service name length %zu\n", sn_len);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
sock = nfc_llcp_sock_get_sn(local, sn, sn_len);
|
|
|
|
if (sock == NULL) {
|
|
|
|
reason = LLCP_DM_NOBOUND;
|
|
|
|
goto fail;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
lock_sock(&sock->sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
parent = &sock->sk;
|
|
|
|
|
|
|
|
if (sk_acceptq_is_full(parent)) {
|
|
|
|
reason = LLCP_DM_REJ;
|
|
|
|
release_sock(&sock->sk);
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
if (sock->ssap == LLCP_SDP_UNBOUND) {
|
|
|
|
u8 ssap = nfc_llcp_reserve_sdp_ssap(local);
|
|
|
|
|
|
|
|
pr_debug("First client, reserving %d\n", ssap);
|
|
|
|
|
|
|
|
if (ssap == LLCP_SAP_MAX) {
|
|
|
|
reason = LLCP_DM_REJ;
|
|
|
|
release_sock(&sock->sk);
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
sock->ssap = ssap;
|
|
|
|
}
|
|
|
|
|
2015-05-09 10:09:13 +08:00
|
|
|
new_sk = nfc_llcp_sock_alloc(NULL, parent->sk_type, GFP_ATOMIC, 0);
|
2011-12-14 23:43:12 +08:00
|
|
|
if (new_sk == NULL) {
|
|
|
|
reason = LLCP_DM_REJ;
|
|
|
|
release_sock(&sock->sk);
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_sock = nfc_llcp_sock(new_sk);
|
|
|
|
new_sock->dev = local->dev;
|
2012-05-04 17:24:16 +08:00
|
|
|
new_sock->local = nfc_llcp_local_get(local);
|
2013-02-22 18:38:05 +08:00
|
|
|
new_sock->rw = sock->rw;
|
|
|
|
new_sock->miux = sock->miux;
|
2011-12-14 23:43:12 +08:00
|
|
|
new_sock->nfc_protocol = sock->nfc_protocol;
|
|
|
|
new_sock->dsap = ssap;
|
2012-06-19 03:38:09 +08:00
|
|
|
new_sock->target_idx = local->target_idx;
|
2011-12-14 23:43:12 +08:00
|
|
|
new_sock->parent = parent;
|
2012-06-25 21:46:28 +08:00
|
|
|
new_sock->ssap = sock->ssap;
|
|
|
|
if (sock->ssap < LLCP_LOCAL_NUM_SAP && sock->ssap >= LLCP_WKS_NUM_SAP) {
|
|
|
|
atomic_t *client_count;
|
|
|
|
|
|
|
|
pr_debug("reserved_ssap %d for %p\n", sock->ssap, new_sock);
|
|
|
|
|
|
|
|
client_count =
|
|
|
|
&local->local_sdp_cnt[sock->ssap - LLCP_WKS_NUM_SAP];
|
|
|
|
|
|
|
|
atomic_inc(client_count);
|
|
|
|
new_sock->reserved_ssap = sock->ssap;
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-08 04:03:34 +08:00
|
|
|
nfc_llcp_parse_connection_tlv(new_sock, &skb->data[LLCP_HEADER_SIZE],
|
|
|
|
skb->len - LLCP_HEADER_SIZE);
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
pr_debug("new sock %p sk %p\n", new_sock, &new_sock->sk);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
nfc_llcp_sock_link(&local->sockets, new_sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
nfc_llcp_accept_enqueue(&sock->sk, new_sk);
|
|
|
|
|
|
|
|
nfc_get_device(local->dev->idx);
|
|
|
|
|
|
|
|
new_sk->sk_state = LLCP_CONNECTED;
|
|
|
|
|
|
|
|
/* Wake the listening processes */
|
2014-04-12 04:15:36 +08:00
|
|
|
parent->sk_data_ready(parent);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
/* Send CC */
|
|
|
|
nfc_llcp_send_cc(new_sock);
|
|
|
|
|
|
|
|
release_sock(&sock->sk);
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
/* Send DM */
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, reason);
|
|
|
|
}
|
|
|
|
|
2012-03-05 08:03:42 +08:00
|
|
|
int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)
|
2012-03-05 08:03:35 +08:00
|
|
|
{
|
2012-03-05 08:03:42 +08:00
|
|
|
int nr_frames = 0;
|
2012-03-05 08:03:35 +08:00
|
|
|
struct nfc_llcp_local *local = sock->local;
|
|
|
|
|
|
|
|
pr_debug("Remote ready %d tx queue len %d remote rw %d",
|
2012-03-05 08:03:52 +08:00
|
|
|
sock->remote_ready, skb_queue_len(&sock->tx_pending_queue),
|
2013-02-22 08:12:28 +08:00
|
|
|
sock->remote_rw);
|
2012-03-05 08:03:35 +08:00
|
|
|
|
|
|
|
/* Try to queue some I frames for transmission */
|
|
|
|
while (sock->remote_ready &&
|
2013-02-22 08:12:28 +08:00
|
|
|
skb_queue_len(&sock->tx_pending_queue) < sock->remote_rw) {
|
2012-05-30 23:48:29 +08:00
|
|
|
struct sk_buff *pdu;
|
2012-03-05 08:03:35 +08:00
|
|
|
|
|
|
|
pdu = skb_dequeue(&sock->tx_queue);
|
|
|
|
if (pdu == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Update N(S)/N(R) */
|
|
|
|
nfc_llcp_set_nrns(sock, pdu);
|
|
|
|
|
|
|
|
skb_queue_tail(&local->tx_queue, pdu);
|
2012-03-05 08:03:42 +08:00
|
|
|
nr_frames++;
|
2012-03-05 08:03:35 +08:00
|
|
|
}
|
2012-03-05 08:03:42 +08:00
|
|
|
|
|
|
|
return nr_frames;
|
2012-03-05 08:03:35 +08:00
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
|
2012-03-05 08:03:52 +08:00
|
|
|
struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
struct sock *sk;
|
|
|
|
u8 dsap, ssap, ptype, ns, nr;
|
|
|
|
|
|
|
|
ptype = nfc_llcp_ptype(skb);
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
ns = nfc_llcp_ns(skb);
|
|
|
|
nr = nfc_llcp_nr(skb);
|
|
|
|
|
|
|
|
pr_debug("%d %d R %d S %d\n", dsap, ssap, nr, ns);
|
|
|
|
|
|
|
|
llcp_sock = nfc_llcp_sock_get(local, dsap, ssap);
|
|
|
|
if (llcp_sock == NULL) {
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_NOCONN);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sk = &llcp_sock->sk;
|
|
|
|
lock_sock(sk);
|
|
|
|
if (sk->sk_state == LLCP_CLOSED) {
|
|
|
|
release_sock(sk);
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pass the payload upstream */
|
|
|
|
if (ptype == LLCP_PDU_I) {
|
|
|
|
pr_debug("I frame, queueing on %p\n", &llcp_sock->sk);
|
|
|
|
|
2012-03-05 08:03:36 +08:00
|
|
|
if (ns == llcp_sock->recv_n)
|
|
|
|
llcp_sock->recv_n = (llcp_sock->recv_n + 1) % 16;
|
|
|
|
else
|
|
|
|
pr_err("Received out of sequence I PDU\n");
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE);
|
2012-12-07 23:37:30 +08:00
|
|
|
if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
|
|
|
|
/*
|
|
|
|
* I frames will be freed from the socket layer, so we
|
|
|
|
* need to keep them alive until someone receives them.
|
|
|
|
*/
|
|
|
|
skb_get(skb);
|
|
|
|
} else {
|
|
|
|
pr_err("Receive queue is full\n");
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove skbs from the pending queue */
|
|
|
|
if (llcp_sock->send_ack_n != nr) {
|
|
|
|
struct sk_buff *s, *tmp;
|
2012-11-26 15:40:04 +08:00
|
|
|
u8 n;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
llcp_sock->send_ack_n = nr;
|
|
|
|
|
2012-05-30 23:48:29 +08:00
|
|
|
/* Remove and free all skbs until ns == nr */
|
|
|
|
skb_queue_walk_safe(&llcp_sock->tx_pending_queue, s, tmp) {
|
2012-11-26 15:40:04 +08:00
|
|
|
n = nfc_llcp_ns(s);
|
|
|
|
|
2012-05-30 23:48:29 +08:00
|
|
|
skb_unlink(s, &llcp_sock->tx_pending_queue);
|
|
|
|
kfree_skb(s);
|
|
|
|
|
2012-11-26 15:40:04 +08:00
|
|
|
if (n == nr)
|
2012-05-30 23:48:29 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Re-queue the remaining skbs for transmission */
|
|
|
|
skb_queue_reverse_walk_safe(&llcp_sock->tx_pending_queue,
|
|
|
|
s, tmp) {
|
|
|
|
skb_unlink(s, &llcp_sock->tx_pending_queue);
|
|
|
|
skb_queue_head(&local->tx_queue, s);
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2012-03-05 08:03:36 +08:00
|
|
|
if (ptype == LLCP_PDU_RR)
|
|
|
|
llcp_sock->remote_ready = true;
|
2012-03-05 08:03:52 +08:00
|
|
|
else if (ptype == LLCP_PDU_RNR)
|
2012-03-05 08:03:36 +08:00
|
|
|
llcp_sock->remote_ready = false;
|
|
|
|
|
2012-05-31 00:06:11 +08:00
|
|
|
if (nfc_llcp_queue_i_frames(llcp_sock) == 0 && ptype == LLCP_PDU_I)
|
2012-03-05 08:03:42 +08:00
|
|
|
nfc_llcp_send_rr(llcp_sock);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
release_sock(sk);
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_recv_disc(struct nfc_llcp_local *local,
|
2012-03-05 08:03:52 +08:00
|
|
|
struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
struct sock *sk;
|
|
|
|
u8 dsap, ssap;
|
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
2013-04-03 22:44:44 +08:00
|
|
|
if ((dsap == 0) && (ssap == 0)) {
|
|
|
|
pr_debug("Connection termination");
|
|
|
|
nfc_dep_link_down(local->dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
llcp_sock = nfc_llcp_sock_get(local, dsap, ssap);
|
|
|
|
if (llcp_sock == NULL) {
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_NOCONN);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sk = &llcp_sock->sk;
|
|
|
|
lock_sock(sk);
|
2012-10-27 00:20:10 +08:00
|
|
|
|
|
|
|
nfc_llcp_socket_purge(llcp_sock);
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
if (sk->sk_state == LLCP_CLOSED) {
|
|
|
|
release_sock(sk);
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sk->sk_state == LLCP_CONNECTED) {
|
|
|
|
nfc_put_device(local->dev);
|
|
|
|
sk->sk_state = LLCP_CLOSED;
|
|
|
|
sk->sk_state_change(sk);
|
|
|
|
}
|
|
|
|
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_DISC);
|
|
|
|
|
|
|
|
release_sock(sk);
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
2012-03-05 08:03:52 +08:00
|
|
|
static void nfc_llcp_recv_cc(struct nfc_llcp_local *local, struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
2012-05-07 18:31:19 +08:00
|
|
|
struct sock *sk;
|
2011-12-14 23:43:12 +08:00
|
|
|
u8 dsap, ssap;
|
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
llcp_sock = nfc_llcp_connecting_sock_get(local, dsap);
|
2011-12-14 23:43:12 +08:00
|
|
|
if (llcp_sock == NULL) {
|
|
|
|
pr_err("Invalid CC\n");
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_NOCONN);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-07 18:31:19 +08:00
|
|
|
sk = &llcp_sock->sk;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
/* Unlink from connecting and link to the client array */
|
|
|
|
nfc_llcp_sock_unlink(&local->connecting_sockets, sk);
|
|
|
|
nfc_llcp_sock_link(&local->sockets, sk);
|
|
|
|
llcp_sock->dsap = ssap;
|
|
|
|
|
2012-05-08 04:03:34 +08:00
|
|
|
nfc_llcp_parse_connection_tlv(llcp_sock, &skb->data[LLCP_HEADER_SIZE],
|
|
|
|
skb->len - LLCP_HEADER_SIZE);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-07 18:31:19 +08:00
|
|
|
sk->sk_state = LLCP_CONNECTED;
|
|
|
|
sk->sk_state_change(sk);
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
2012-06-26 22:13:29 +08:00
|
|
|
static void nfc_llcp_recv_dm(struct nfc_llcp_local *local, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
struct sock *sk;
|
|
|
|
u8 dsap, ssap, reason;
|
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
reason = skb->data[2];
|
|
|
|
|
|
|
|
pr_debug("%d %d reason %d\n", ssap, dsap, reason);
|
|
|
|
|
|
|
|
switch (reason) {
|
|
|
|
case LLCP_DM_NOBOUND:
|
|
|
|
case LLCP_DM_REJ:
|
|
|
|
llcp_sock = nfc_llcp_connecting_sock_get(local, dsap);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
llcp_sock = nfc_llcp_sock_get(local, dsap, ssap);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (llcp_sock == NULL) {
|
2012-10-17 03:15:59 +08:00
|
|
|
pr_debug("Already closed\n");
|
2012-06-26 22:13:29 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sk = &llcp_sock->sk;
|
|
|
|
|
|
|
|
sk->sk_err = ENXIO;
|
|
|
|
sk->sk_state = LLCP_CLOSED;
|
|
|
|
sk->sk_state_change(sk);
|
|
|
|
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
2012-10-05 07:21:47 +08:00
|
|
|
static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
u8 dsap, ssap, *tlv, type, length, tid, sap;
|
|
|
|
u16 tlv_len, offset;
|
|
|
|
char *service_name;
|
|
|
|
size_t service_name_len;
|
2013-02-15 17:43:05 +08:00
|
|
|
struct nfc_llcp_sdp_tlv *sdp;
|
|
|
|
HLIST_HEAD(llc_sdres_list);
|
|
|
|
size_t sdres_tlvs_len;
|
2013-02-15 17:43:06 +08:00
|
|
|
HLIST_HEAD(nl_sdres_list);
|
2012-10-05 07:21:47 +08:00
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
|
|
|
pr_debug("%d %d\n", dsap, ssap);
|
|
|
|
|
|
|
|
if (dsap != LLCP_SAP_SDP || ssap != LLCP_SAP_SDP) {
|
|
|
|
pr_err("Wrong SNL SAP\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tlv = &skb->data[LLCP_HEADER_SIZE];
|
|
|
|
tlv_len = skb->len - LLCP_HEADER_SIZE;
|
|
|
|
offset = 0;
|
2013-02-15 17:43:05 +08:00
|
|
|
sdres_tlvs_len = 0;
|
2012-10-05 07:21:47 +08:00
|
|
|
|
2012-10-17 21:23:39 +08:00
|
|
|
while (offset < tlv_len) {
|
2012-10-05 07:21:47 +08:00
|
|
|
type = tlv[0];
|
|
|
|
length = tlv[1];
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case LLCP_TLV_SDREQ:
|
|
|
|
tid = tlv[2];
|
|
|
|
service_name = (char *) &tlv[3];
|
|
|
|
service_name_len = length - 1;
|
|
|
|
|
2012-10-15 20:28:13 +08:00
|
|
|
pr_debug("Looking for %.16s\n", service_name);
|
2012-10-05 07:21:47 +08:00
|
|
|
|
|
|
|
if (service_name_len == strlen("urn:nfc:sn:sdp") &&
|
|
|
|
!strncmp(service_name, "urn:nfc:sn:sdp",
|
|
|
|
service_name_len)) {
|
|
|
|
sap = 1;
|
2013-02-15 17:43:05 +08:00
|
|
|
goto add_snl;
|
2012-10-15 20:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
llcp_sock = nfc_llcp_sock_from_sn(local, service_name,
|
|
|
|
service_name_len);
|
|
|
|
if (!llcp_sock) {
|
|
|
|
sap = 0;
|
2013-02-15 17:43:05 +08:00
|
|
|
goto add_snl;
|
2012-10-15 20:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We found a socket but its ssap has not been reserved
|
|
|
|
* yet. We need to assign it for good and send a reply.
|
|
|
|
* The ssap will be freed when the socket is closed.
|
|
|
|
*/
|
|
|
|
if (llcp_sock->ssap == LLCP_SDP_UNBOUND) {
|
|
|
|
atomic_t *client_count;
|
|
|
|
|
|
|
|
sap = nfc_llcp_reserve_sdp_ssap(local);
|
|
|
|
|
|
|
|
pr_debug("Reserving %d\n", sap);
|
|
|
|
|
|
|
|
if (sap == LLCP_SAP_MAX) {
|
|
|
|
sap = 0;
|
2013-02-15 17:43:05 +08:00
|
|
|
goto add_snl;
|
2012-10-15 20:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
client_count =
|
|
|
|
&local->local_sdp_cnt[sap -
|
|
|
|
LLCP_WKS_NUM_SAP];
|
|
|
|
|
|
|
|
atomic_inc(client_count);
|
|
|
|
|
|
|
|
llcp_sock->ssap = sap;
|
|
|
|
llcp_sock->reserved_ssap = sap;
|
2012-10-05 07:21:47 +08:00
|
|
|
} else {
|
2012-10-15 20:28:13 +08:00
|
|
|
sap = llcp_sock->ssap;
|
2012-10-05 07:21:47 +08:00
|
|
|
}
|
|
|
|
|
2012-10-15 20:28:13 +08:00
|
|
|
pr_debug("%p %d\n", llcp_sock, sap);
|
|
|
|
|
2013-02-15 17:43:05 +08:00
|
|
|
add_snl:
|
|
|
|
sdp = nfc_llcp_build_sdres_tlv(tid, sap);
|
|
|
|
if (sdp == NULL)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
sdres_tlvs_len += sdp->tlv_len;
|
|
|
|
hlist_add_head(&sdp->node, &llc_sdres_list);
|
2012-10-05 07:21:47 +08:00
|
|
|
break;
|
|
|
|
|
2013-02-15 17:43:06 +08:00
|
|
|
case LLCP_TLV_SDRES:
|
|
|
|
mutex_lock(&local->sdreq_lock);
|
|
|
|
|
|
|
|
pr_debug("LLCP_TLV_SDRES: searching tid %d\n", tlv[2]);
|
|
|
|
|
|
|
|
hlist_for_each_entry(sdp, &local->pending_sdreqs, node) {
|
|
|
|
if (sdp->tid != tlv[2])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sdp->sap = tlv[3];
|
|
|
|
|
|
|
|
pr_debug("Found: uri=%s, sap=%d\n",
|
|
|
|
sdp->uri, sdp->sap);
|
|
|
|
|
|
|
|
hlist_del(&sdp->node);
|
|
|
|
|
|
|
|
hlist_add_head(&sdp->node, &nl_sdres_list);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdreq_lock);
|
|
|
|
break;
|
|
|
|
|
2012-10-05 07:21:47 +08:00
|
|
|
default:
|
|
|
|
pr_err("Invalid SNL tlv value 0x%x\n", type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += length + 2;
|
|
|
|
tlv += length + 2;
|
|
|
|
}
|
2013-02-15 17:43:05 +08:00
|
|
|
|
|
|
|
exit:
|
2013-02-15 17:43:06 +08:00
|
|
|
if (!hlist_empty(&nl_sdres_list))
|
|
|
|
nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);
|
|
|
|
|
2013-02-15 17:43:05 +08:00
|
|
|
if (!hlist_empty(&llc_sdres_list))
|
|
|
|
nfc_llcp_send_snl_sdres(local, &llc_sdres_list, sdres_tlvs_len);
|
2012-10-05 07:21:47 +08:00
|
|
|
}
|
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
static void nfc_llcp_recv_agf(struct nfc_llcp_local *local, struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
2013-03-29 18:47:43 +08:00
|
|
|
u8 ptype;
|
|
|
|
u16 pdu_len;
|
|
|
|
struct sk_buff *new_skb;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
if (skb->len <= LLCP_HEADER_SIZE) {
|
|
|
|
pr_err("Malformed AGF PDU\n");
|
2011-12-14 23:43:12 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
skb_pull(skb, LLCP_HEADER_SIZE);
|
|
|
|
|
|
|
|
while (skb->len > LLCP_AGF_PDU_HEADER_SIZE) {
|
|
|
|
pdu_len = skb->data[0] << 8 | skb->data[1];
|
|
|
|
|
|
|
|
skb_pull(skb, LLCP_AGF_PDU_HEADER_SIZE);
|
|
|
|
|
|
|
|
if (pdu_len < LLCP_HEADER_SIZE || pdu_len > skb->len) {
|
|
|
|
pr_err("Malformed AGF PDU\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptype = nfc_llcp_ptype(skb);
|
|
|
|
|
|
|
|
if (ptype == LLCP_PDU_SYMM || ptype == LLCP_PDU_AGF)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
new_skb = nfc_alloc_recv_skb(pdu_len, GFP_KERNEL);
|
|
|
|
if (new_skb == NULL) {
|
|
|
|
pr_err("Could not allocate PDU\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:20 +08:00
|
|
|
skb_put_data(new_skb, skb->data, pdu_len);
|
2013-03-29 18:47:43 +08:00
|
|
|
|
|
|
|
nfc_llcp_rx_skb(local, new_skb);
|
|
|
|
|
|
|
|
kfree_skb(new_skb);
|
|
|
|
next:
|
|
|
|
skb_pull(skb, pdu_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
u8 dsap, ssap, ptype;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
ptype = nfc_llcp_ptype(skb);
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
|
|
|
pr_debug("ptype 0x%x dsap 0x%x ssap 0x%x\n", ptype, dsap, ssap);
|
|
|
|
|
2012-04-11 01:43:13 +08:00
|
|
|
if (ptype != LLCP_PDU_SYMM)
|
2016-06-07 22:21:53 +08:00
|
|
|
print_hex_dump_debug("LLCP Rx: ", DUMP_PREFIX_OFFSET, 16, 1,
|
|
|
|
skb->data, skb->len, true);
|
2012-04-11 01:43:13 +08:00
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
switch (ptype) {
|
|
|
|
case LLCP_PDU_SYMM:
|
|
|
|
pr_debug("SYMM\n");
|
|
|
|
break;
|
|
|
|
|
2012-10-15 22:14:37 +08:00
|
|
|
case LLCP_PDU_UI:
|
|
|
|
pr_debug("UI\n");
|
|
|
|
nfc_llcp_recv_ui(local, skb);
|
|
|
|
break;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
case LLCP_PDU_CONNECT:
|
|
|
|
pr_debug("CONNECT\n");
|
|
|
|
nfc_llcp_recv_connect(local, skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LLCP_PDU_DISC:
|
|
|
|
pr_debug("DISC\n");
|
|
|
|
nfc_llcp_recv_disc(local, skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LLCP_PDU_CC:
|
|
|
|
pr_debug("CC\n");
|
|
|
|
nfc_llcp_recv_cc(local, skb);
|
|
|
|
break;
|
|
|
|
|
2012-06-26 22:13:29 +08:00
|
|
|
case LLCP_PDU_DM:
|
|
|
|
pr_debug("DM\n");
|
|
|
|
nfc_llcp_recv_dm(local, skb);
|
|
|
|
break;
|
|
|
|
|
2012-10-05 07:21:47 +08:00
|
|
|
case LLCP_PDU_SNL:
|
|
|
|
pr_debug("SNL\n");
|
|
|
|
nfc_llcp_recv_snl(local, skb);
|
|
|
|
break;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
case LLCP_PDU_I:
|
|
|
|
case LLCP_PDU_RR:
|
2012-03-05 08:03:36 +08:00
|
|
|
case LLCP_PDU_RNR:
|
2011-12-14 23:43:12 +08:00
|
|
|
pr_debug("I frame\n");
|
|
|
|
nfc_llcp_recv_hdlc(local, skb);
|
|
|
|
break;
|
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
case LLCP_PDU_AGF:
|
|
|
|
pr_debug("AGF frame\n");
|
|
|
|
nfc_llcp_recv_agf(local, skb);
|
|
|
|
break;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
2013-03-29 18:47:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_rx_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
|
|
|
|
rx_work);
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
skb = local->rx_pending;
|
|
|
|
if (skb == NULL) {
|
|
|
|
pr_debug("No pending SKB\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
__net_timestamp(skb);
|
|
|
|
|
2014-05-05 18:43:31 +08:00
|
|
|
nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_RX);
|
2013-03-29 18:47:43 +08:00
|
|
|
|
|
|
|
nfc_llcp_rx_skb(local, skb);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-10-03 07:01:31 +08:00
|
|
|
schedule_work(&local->tx_work);
|
2011-12-14 23:43:12 +08:00
|
|
|
kfree_skb(local->rx_pending);
|
|
|
|
local->rx_pending = NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-07 23:37:30 +08:00
|
|
|
static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
local->rx_pending = skb;
|
|
|
|
del_timer(&local->link_timer);
|
|
|
|
schedule_work(&local->rx_work);
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = (struct nfc_llcp_local *) data;
|
|
|
|
|
|
|
|
pr_debug("Received an LLCP PDU\n");
|
|
|
|
if (err < 0) {
|
2012-03-05 08:03:52 +08:00
|
|
|
pr_err("err %d\n", err);
|
2011-12-14 23:43:12 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-12-07 23:37:30 +08:00
|
|
|
__nfc_llcp_recv(local, skb);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2012-05-31 06:05:50 +08:00
|
|
|
int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = nfc_llcp_find_local(dev);
|
2014-10-21 22:52:50 +08:00
|
|
|
if (local == NULL) {
|
|
|
|
kfree_skb(skb);
|
2012-05-31 06:05:50 +08:00
|
|
|
return -ENODEV;
|
2014-10-21 22:52:50 +08:00
|
|
|
}
|
2012-05-31 06:05:50 +08:00
|
|
|
|
2012-12-07 23:37:30 +08:00
|
|
|
__nfc_llcp_recv(local, skb);
|
2012-05-31 06:05:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
void nfc_llcp_mac_is_down(struct nfc_dev *dev)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = nfc_llcp_find_local(dev);
|
|
|
|
if (local == NULL)
|
|
|
|
return;
|
|
|
|
|
2013-04-02 16:25:15 +08:00
|
|
|
local->remote_miu = LLCP_DEFAULT_MIU;
|
|
|
|
local->remote_lto = LLCP_DEFAULT_LTO;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
/* Close and purge all existing sockets */
|
2013-02-22 00:01:06 +08:00
|
|
|
nfc_llcp_socket_release(local, true, 0);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
|
|
|
|
u8 comm_mode, u8 rf_mode)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
pr_debug("rf mode %d\n", rf_mode);
|
|
|
|
|
|
|
|
local = nfc_llcp_find_local(dev);
|
|
|
|
if (local == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
local->target_idx = target_idx;
|
|
|
|
local->comm_mode = comm_mode;
|
|
|
|
local->rf_mode = rf_mode;
|
|
|
|
|
|
|
|
if (rf_mode == NFC_RF_INITIATOR) {
|
|
|
|
pr_debug("Queueing Tx work\n");
|
|
|
|
|
2012-10-03 07:01:31 +08:00
|
|
|
schedule_work(&local->tx_work);
|
2011-12-14 23:43:12 +08:00
|
|
|
} else {
|
|
|
|
mod_timer(&local->link_timer,
|
2012-03-05 08:03:52 +08:00
|
|
|
jiffies + msecs_to_jiffies(local->remote_lto));
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int nfc_llcp_register_device(struct nfc_dev *ndev)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = kzalloc(sizeof(struct nfc_llcp_local), GFP_KERNEL);
|
|
|
|
if (local == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
local->dev = ndev;
|
|
|
|
INIT_LIST_HEAD(&local->list);
|
2012-05-04 17:24:16 +08:00
|
|
|
kref_init(&local->ref);
|
2011-12-14 23:43:12 +08:00
|
|
|
mutex_init(&local->sdp_lock);
|
2017-10-11 18:33:44 +08:00
|
|
|
timer_setup(&local->link_timer, nfc_llcp_symm_timer, 0);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
skb_queue_head_init(&local->tx_queue);
|
|
|
|
INIT_WORK(&local->tx_work, nfc_llcp_tx_work);
|
|
|
|
|
|
|
|
local->rx_pending = NULL;
|
|
|
|
INIT_WORK(&local->rx_work, nfc_llcp_rx_work);
|
|
|
|
|
|
|
|
INIT_WORK(&local->timeout_work, nfc_llcp_timeout_work);
|
|
|
|
|
2012-09-25 18:42:50 +08:00
|
|
|
rwlock_init(&local->sockets.lock);
|
|
|
|
rwlock_init(&local->connecting_sockets.lock);
|
2012-09-27 00:16:44 +08:00
|
|
|
rwlock_init(&local->raw_sockets.lock);
|
2012-05-04 23:04:19 +08:00
|
|
|
|
2012-10-17 20:43:39 +08:00
|
|
|
local->lto = 150; /* 1500 ms */
|
|
|
|
local->rw = LLCP_MAX_RW;
|
|
|
|
local->miux = cpu_to_be16(LLCP_MAX_MIUX);
|
2013-05-28 21:41:32 +08:00
|
|
|
local->local_wks = 0x1; /* LLC Link Management */
|
2012-10-17 20:43:39 +08:00
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
nfc_llcp_build_gb(local);
|
|
|
|
|
|
|
|
local->remote_miu = LLCP_DEFAULT_MIU;
|
|
|
|
local->remote_lto = LLCP_DEFAULT_LTO;
|
|
|
|
|
2013-02-15 17:43:06 +08:00
|
|
|
mutex_init(&local->sdreq_lock);
|
|
|
|
INIT_HLIST_HEAD(&local->pending_sdreqs);
|
2017-10-11 18:33:44 +08:00
|
|
|
timer_setup(&local->sdreq_timer, nfc_llcp_sdreq_timer, 0);
|
2013-03-04 22:43:32 +08:00
|
|
|
INIT_WORK(&local->sdreq_timeout_work, nfc_llcp_sdreq_timeout_work);
|
2013-02-15 17:43:06 +08:00
|
|
|
|
2012-10-12 21:25:43 +08:00
|
|
|
list_add(&local->list, &llcp_devices);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_llcp_unregister_device(struct nfc_dev *dev)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = nfc_llcp_find_local(dev);
|
|
|
|
|
|
|
|
if (local == NULL) {
|
|
|
|
pr_debug("No such device\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-03 22:40:52 +08:00
|
|
|
local_cleanup(local);
|
2013-02-21 22:40:04 +08:00
|
|
|
|
2012-05-04 17:24:16 +08:00
|
|
|
nfc_llcp_local_put(local);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int __init nfc_llcp_init(void)
|
|
|
|
{
|
|
|
|
return nfc_llcp_sock_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_llcp_exit(void)
|
|
|
|
{
|
|
|
|
nfc_llcp_sock_exit();
|
|
|
|
}
|