diff options
author | Lauro Ramos Venancio <lauro.venancio@openbossa.org> | 2011-07-01 18:31:36 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-07-05 15:26:58 -0400 |
commit | 23b7869c0fd08d73c9f83a2db88a13312d6198bb (patch) | |
tree | bc14ecbeac8998fddf48a52709c65ca5cdbbf6b0 /net/nfc | |
parent | c7fe3b52c1283b8ba810eb6ecddf1c8a0bcc13ab (diff) |
NFC: add the NFC socket raw protocol
This socket protocol is used to perform data exchange with NFC
targets.
Signed-off-by: Lauro Ramos Venancio <lauro.venancio@openbossa.org>
Signed-off-by: Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/nfc')
-rw-r--r-- | net/nfc/Makefile | 2 | ||||
-rw-r--r-- | net/nfc/core.c | 7 | ||||
-rw-r--r-- | net/nfc/nfc.h | 14 | ||||
-rw-r--r-- | net/nfc/rawsock.c | 354 |
4 files changed, 376 insertions, 1 deletions
diff --git a/net/nfc/Makefile b/net/nfc/Makefile index e081fdb86a59..16250c353851 100644 --- a/net/nfc/Makefile +++ b/net/nfc/Makefile | |||
@@ -4,4 +4,4 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_NFC) += nfc.o | 5 | obj-$(CONFIG_NFC) += nfc.o |
6 | 6 | ||
7 | nfc-objs := core.o netlink.o af_nfc.o | 7 | nfc-objs := core.o netlink.o af_nfc.o rawsock.o |
diff --git a/net/nfc/core.c b/net/nfc/core.c index e804dc50f42f..b6fd4e1f2057 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c | |||
@@ -432,6 +432,10 @@ static int __init nfc_init(void) | |||
432 | /* the first generation must not be 0 */ | 432 | /* the first generation must not be 0 */ |
433 | nfc_devlist_generation = 1; | 433 | nfc_devlist_generation = 1; |
434 | 434 | ||
435 | rc = rawsock_init(); | ||
436 | if (rc) | ||
437 | goto err_rawsock; | ||
438 | |||
435 | rc = af_nfc_init(); | 439 | rc = af_nfc_init(); |
436 | if (rc) | 440 | if (rc) |
437 | goto err_af_nfc; | 441 | goto err_af_nfc; |
@@ -439,6 +443,8 @@ static int __init nfc_init(void) | |||
439 | return 0; | 443 | return 0; |
440 | 444 | ||
441 | err_af_nfc: | 445 | err_af_nfc: |
446 | rawsock_exit(); | ||
447 | err_rawsock: | ||
442 | nfc_genl_exit(); | 448 | nfc_genl_exit(); |
443 | err_genl: | 449 | err_genl: |
444 | class_unregister(&nfc_class); | 450 | class_unregister(&nfc_class); |
@@ -448,6 +454,7 @@ err_genl: | |||
448 | static void __exit nfc_exit(void) | 454 | static void __exit nfc_exit(void) |
449 | { | 455 | { |
450 | af_nfc_exit(); | 456 | af_nfc_exit(); |
457 | rawsock_exit(); | ||
451 | nfc_genl_exit(); | 458 | nfc_genl_exit(); |
452 | class_unregister(&nfc_class); | 459 | class_unregister(&nfc_class); |
453 | } | 460 | } |
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 8335f4de8f4f..aaf9832298f3 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h | |||
@@ -42,6 +42,20 @@ struct nfc_protocol { | |||
42 | const struct nfc_protocol *nfc_proto); | 42 | const struct nfc_protocol *nfc_proto); |
43 | }; | 43 | }; |
44 | 44 | ||
45 | struct nfc_rawsock { | ||
46 | struct sock sk; | ||
47 | struct nfc_dev *dev; | ||
48 | u32 target_idx; | ||
49 | struct work_struct tx_work; | ||
50 | bool tx_work_scheduled; | ||
51 | }; | ||
52 | #define nfc_rawsock(sk) ((struct nfc_rawsock *) sk) | ||
53 | #define to_rawsock_sk(_tx_work) \ | ||
54 | ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work)) | ||
55 | |||
56 | int __init rawsock_init(void); | ||
57 | void rawsock_exit(void); | ||
58 | |||
45 | int __init af_nfc_init(void); | 59 | int __init af_nfc_init(void); |
46 | void af_nfc_exit(void); | 60 | void af_nfc_exit(void); |
47 | int nfc_proto_register(const struct nfc_protocol *nfc_proto); | 61 | int nfc_proto_register(const struct nfc_protocol *nfc_proto); |
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c new file mode 100644 index 000000000000..52de84a55115 --- /dev/null +++ b/net/nfc/rawsock.c | |||
@@ -0,0 +1,354 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Instituto Nokia de Tecnologia | ||
3 | * | ||
4 | * Authors: | ||
5 | * Aloisio Almeida Jr <aloisio.almeida@openbossa.org> | ||
6 | * Lauro Ramos Venancio <lauro.venancio@openbossa.org> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the | ||
20 | * Free Software Foundation, Inc., | ||
21 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
22 | */ | ||
23 | |||
24 | #include <net/tcp_states.h> | ||
25 | #include <linux/nfc.h> | ||
26 | |||
27 | #include "nfc.h" | ||
28 | |||
29 | static void rawsock_write_queue_purge(struct sock *sk) | ||
30 | { | ||
31 | nfc_dbg("sk=%p", sk); | ||
32 | |||
33 | spin_lock_bh(&sk->sk_write_queue.lock); | ||
34 | __skb_queue_purge(&sk->sk_write_queue); | ||
35 | nfc_rawsock(sk)->tx_work_scheduled = false; | ||
36 | spin_unlock_bh(&sk->sk_write_queue.lock); | ||
37 | } | ||
38 | |||
39 | static void rawsock_report_error(struct sock *sk, int err) | ||
40 | { | ||
41 | nfc_dbg("sk=%p err=%d", sk, err); | ||
42 | |||
43 | sk->sk_shutdown = SHUTDOWN_MASK; | ||
44 | sk->sk_err = -err; | ||
45 | sk->sk_error_report(sk); | ||
46 | |||
47 | rawsock_write_queue_purge(sk); | ||
48 | } | ||
49 | |||
50 | static int rawsock_release(struct socket *sock) | ||
51 | { | ||
52 | struct sock *sk = sock->sk; | ||
53 | |||
54 | nfc_dbg("sock=%p", sock); | ||
55 | |||
56 | sock_orphan(sk); | ||
57 | sock_put(sk); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int rawsock_connect(struct socket *sock, struct sockaddr *_addr, | ||
63 | int len, int flags) | ||
64 | { | ||
65 | struct sock *sk = sock->sk; | ||
66 | struct sockaddr_nfc *addr = (struct sockaddr_nfc *)_addr; | ||
67 | struct nfc_dev *dev; | ||
68 | int rc = 0; | ||
69 | |||
70 | nfc_dbg("sock=%p sk=%p flags=%d", sock, sk, flags); | ||
71 | |||
72 | if (!addr || len < sizeof(struct sockaddr_nfc) || | ||
73 | addr->sa_family != AF_NFC) | ||
74 | return -EINVAL; | ||
75 | |||
76 | nfc_dbg("addr dev_idx=%u target_idx=%u protocol=%u", addr->dev_idx, | ||
77 | addr->target_idx, addr->nfc_protocol); | ||
78 | |||
79 | lock_sock(sk); | ||
80 | |||
81 | if (sock->state == SS_CONNECTED) { | ||
82 | rc = -EISCONN; | ||
83 | goto error; | ||
84 | } | ||
85 | |||
86 | dev = nfc_get_device(addr->dev_idx); | ||
87 | if (!dev) { | ||
88 | rc = -ENODEV; | ||
89 | goto error; | ||
90 | } | ||
91 | |||
92 | if (addr->target_idx > dev->target_idx - 1 || | ||
93 | addr->target_idx < dev->target_idx - dev->n_targets) { | ||
94 | rc = -EINVAL; | ||
95 | goto error; | ||
96 | } | ||
97 | |||
98 | if (addr->target_idx > dev->target_idx - 1 || | ||
99 | addr->target_idx < dev->target_idx - dev->n_targets) { | ||
100 | rc = -EINVAL; | ||
101 | goto error; | ||
102 | } | ||
103 | |||
104 | rc = nfc_activate_target(dev, addr->target_idx, addr->nfc_protocol); | ||
105 | if (rc) | ||
106 | goto put_dev; | ||
107 | |||
108 | nfc_rawsock(sk)->dev = dev; | ||
109 | nfc_rawsock(sk)->target_idx = addr->target_idx; | ||
110 | sock->state = SS_CONNECTED; | ||
111 | sk->sk_state = TCP_ESTABLISHED; | ||
112 | sk->sk_state_change(sk); | ||
113 | |||
114 | release_sock(sk); | ||
115 | return 0; | ||
116 | |||
117 | put_dev: | ||
118 | nfc_put_device(dev); | ||
119 | error: | ||
120 | release_sock(sk); | ||
121 | return rc; | ||
122 | } | ||
123 | |||
124 | static int rawsock_add_header(struct sk_buff *skb) | ||
125 | { | ||
126 | |||
127 | if (skb_cow_head(skb, 1)) | ||
128 | return -ENOMEM; | ||
129 | |||
130 | *skb_push(skb, 1) = 0; | ||
131 | |||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb, | ||
136 | int err) | ||
137 | { | ||
138 | struct sock *sk = (struct sock *) context; | ||
139 | |||
140 | BUG_ON(in_irq()); | ||
141 | |||
142 | nfc_dbg("sk=%p err=%d", sk, err); | ||
143 | |||
144 | if (err) | ||
145 | goto error; | ||
146 | |||
147 | err = rawsock_add_header(skb); | ||
148 | if (err) | ||
149 | goto error; | ||
150 | |||
151 | err = sock_queue_rcv_skb(sk, skb); | ||
152 | if (err) | ||
153 | goto error; | ||
154 | |||
155 | spin_lock_bh(&sk->sk_write_queue.lock); | ||
156 | if (!skb_queue_empty(&sk->sk_write_queue)) | ||
157 | schedule_work(&nfc_rawsock(sk)->tx_work); | ||
158 | else | ||
159 | nfc_rawsock(sk)->tx_work_scheduled = false; | ||
160 | spin_unlock_bh(&sk->sk_write_queue.lock); | ||
161 | |||
162 | sock_put(sk); | ||
163 | return; | ||
164 | |||
165 | error: | ||
166 | rawsock_report_error(sk, err); | ||
167 | sock_put(sk); | ||
168 | } | ||
169 | |||
170 | static void rawsock_tx_work(struct work_struct *work) | ||
171 | { | ||
172 | struct sock *sk = to_rawsock_sk(work); | ||
173 | struct nfc_dev *dev = nfc_rawsock(sk)->dev; | ||
174 | u32 target_idx = nfc_rawsock(sk)->target_idx; | ||
175 | struct sk_buff *skb; | ||
176 | int rc; | ||
177 | |||
178 | nfc_dbg("sk=%p target_idx=%u", sk, target_idx); | ||
179 | |||
180 | if (sk->sk_shutdown & SEND_SHUTDOWN) { | ||
181 | rawsock_write_queue_purge(sk); | ||
182 | return; | ||
183 | } | ||
184 | |||
185 | skb = skb_dequeue(&sk->sk_write_queue); | ||
186 | |||
187 | sock_hold(sk); | ||
188 | rc = nfc_data_exchange(dev, target_idx, skb, | ||
189 | rawsock_data_exchange_complete, sk); | ||
190 | if (rc) { | ||
191 | rawsock_report_error(sk, rc); | ||
192 | sock_put(sk); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock, | ||
197 | struct msghdr *msg, size_t len) | ||
198 | { | ||
199 | struct sock *sk = sock->sk; | ||
200 | struct sk_buff *skb; | ||
201 | int rc; | ||
202 | |||
203 | nfc_dbg("sock=%p sk=%p len=%zu", sock, sk, len); | ||
204 | |||
205 | if (msg->msg_namelen) | ||
206 | return -EOPNOTSUPP; | ||
207 | |||
208 | if (sock->state != SS_CONNECTED) | ||
209 | return -ENOTCONN; | ||
210 | |||
211 | skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT, | ||
212 | &rc); | ||
213 | if (!skb) | ||
214 | return rc; | ||
215 | |||
216 | rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); | ||
217 | if (rc < 0) { | ||
218 | kfree_skb(skb); | ||
219 | return rc; | ||
220 | } | ||
221 | |||
222 | spin_lock_bh(&sk->sk_write_queue.lock); | ||
223 | __skb_queue_tail(&sk->sk_write_queue, skb); | ||
224 | if (!nfc_rawsock(sk)->tx_work_scheduled) { | ||
225 | schedule_work(&nfc_rawsock(sk)->tx_work); | ||
226 | nfc_rawsock(sk)->tx_work_scheduled = true; | ||
227 | } | ||
228 | spin_unlock_bh(&sk->sk_write_queue.lock); | ||
229 | |||
230 | return len; | ||
231 | } | ||
232 | |||
233 | static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, | ||
234 | struct msghdr *msg, size_t len, int flags) | ||
235 | { | ||
236 | int noblock = flags & MSG_DONTWAIT; | ||
237 | struct sock *sk = sock->sk; | ||
238 | struct sk_buff *skb; | ||
239 | int copied; | ||
240 | int rc; | ||
241 | |||
242 | nfc_dbg("sock=%p sk=%p len=%zu flags=%d", sock, sk, len, flags); | ||
243 | |||
244 | skb = skb_recv_datagram(sk, flags, noblock, &rc); | ||
245 | if (!skb) | ||
246 | return rc; | ||
247 | |||
248 | msg->msg_namelen = 0; | ||
249 | |||
250 | copied = skb->len; | ||
251 | if (len < copied) { | ||
252 | msg->msg_flags |= MSG_TRUNC; | ||
253 | copied = len; | ||
254 | } | ||
255 | |||
256 | rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | ||
257 | |||
258 | skb_free_datagram(sk, skb); | ||
259 | |||
260 | return rc ? : copied; | ||
261 | } | ||
262 | |||
263 | |||
264 | static const struct proto_ops rawsock_ops = { | ||
265 | .family = PF_NFC, | ||
266 | .owner = THIS_MODULE, | ||
267 | .release = rawsock_release, | ||
268 | .bind = sock_no_bind, | ||
269 | .connect = rawsock_connect, | ||
270 | .socketpair = sock_no_socketpair, | ||
271 | .accept = sock_no_accept, | ||
272 | .getname = sock_no_getname, | ||
273 | .poll = datagram_poll, | ||
274 | .ioctl = sock_no_ioctl, | ||
275 | .listen = sock_no_listen, | ||
276 | .shutdown = sock_no_shutdown, | ||
277 | .setsockopt = sock_no_setsockopt, | ||
278 | .getsockopt = sock_no_getsockopt, | ||
279 | .sendmsg = rawsock_sendmsg, | ||
280 | .recvmsg = rawsock_recvmsg, | ||
281 | .mmap = sock_no_mmap, | ||
282 | }; | ||
283 | |||
284 | static void rawsock_destruct(struct sock *sk) | ||
285 | { | ||
286 | nfc_dbg("sk=%p", sk); | ||
287 | |||
288 | if (sk->sk_state == TCP_ESTABLISHED) { | ||
289 | nfc_deactivate_target(nfc_rawsock(sk)->dev, | ||
290 | nfc_rawsock(sk)->target_idx); | ||
291 | nfc_put_device(nfc_rawsock(sk)->dev); | ||
292 | } | ||
293 | |||
294 | skb_queue_purge(&sk->sk_receive_queue); | ||
295 | |||
296 | if (!sock_flag(sk, SOCK_DEAD)) { | ||
297 | nfc_err("Freeing alive NFC raw socket %p", sk); | ||
298 | return; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | static int rawsock_create(struct net *net, struct socket *sock, | ||
303 | const struct nfc_protocol *nfc_proto) | ||
304 | { | ||
305 | struct sock *sk; | ||
306 | |||
307 | nfc_dbg("sock=%p", sock); | ||
308 | |||
309 | if (sock->type != SOCK_SEQPACKET) | ||
310 | return -ESOCKTNOSUPPORT; | ||
311 | |||
312 | sock->ops = &rawsock_ops; | ||
313 | |||
314 | sk = sk_alloc(net, PF_NFC, GFP_KERNEL, nfc_proto->proto); | ||
315 | if (!sk) | ||
316 | return -ENOMEM; | ||
317 | |||
318 | sock_init_data(sock, sk); | ||
319 | sk->sk_protocol = nfc_proto->id; | ||
320 | sk->sk_destruct = rawsock_destruct; | ||
321 | sock->state = SS_UNCONNECTED; | ||
322 | |||
323 | INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work); | ||
324 | nfc_rawsock(sk)->tx_work_scheduled = false; | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static struct proto rawsock_proto = { | ||
330 | .name = "NFC_RAW", | ||
331 | .owner = THIS_MODULE, | ||
332 | .obj_size = sizeof(struct nfc_rawsock), | ||
333 | }; | ||
334 | |||
335 | static const struct nfc_protocol rawsock_nfc_proto = { | ||
336 | .id = NFC_SOCKPROTO_RAW, | ||
337 | .proto = &rawsock_proto, | ||
338 | .owner = THIS_MODULE, | ||
339 | .create = rawsock_create | ||
340 | }; | ||
341 | |||
342 | int __init rawsock_init(void) | ||
343 | { | ||
344 | int rc; | ||
345 | |||
346 | rc = nfc_proto_register(&rawsock_nfc_proto); | ||
347 | |||
348 | return rc; | ||
349 | } | ||
350 | |||
351 | void rawsock_exit(void) | ||
352 | { | ||
353 | nfc_proto_unregister(&rawsock_nfc_proto); | ||
354 | } | ||