diff options
author | Lauro Ramos Venancio <lauro.venancio@openbossa.org> | 2011-07-01 18:31:34 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-07-05 15:26:57 -0400 |
commit | 4d12b8b129f170d0fc3188de1e51a2a1b0f87730 (patch) | |
tree | e37bbec5da917fee80706516c56fb41fcd03a1b5 | |
parent | 3e256b8f8dfa309a80b5dece388d85d9a9801a29 (diff) |
NFC: add nfc generic netlink interface
The NFC generic netlink interface exports the NFC control operations
to the user space.
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>
Reviewed-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | include/linux/nfc.h | 112 | ||||
-rw-r--r-- | include/net/nfc.h | 21 | ||||
-rw-r--r-- | net/nfc/Makefile | 2 | ||||
-rw-r--r-- | net/nfc/core.c | 93 | ||||
-rw-r--r-- | net/nfc/netlink.c | 537 | ||||
-rw-r--r-- | net/nfc/nfc.h | 11 |
6 files changed, 773 insertions, 3 deletions
diff --git a/include/linux/nfc.h b/include/linux/nfc.h new file mode 100644 index 000000000000..1170476d270a --- /dev/null +++ b/include/linux/nfc.h | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Instituto Nokia de Tecnologia | ||
3 | * | ||
4 | * Authors: | ||
5 | * Lauro Ramos Venancio <lauro.venancio@openbossa.org> | ||
6 | * Aloisio Almeida Jr <aloisio.almeida@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 | #ifndef __LINUX_NFC_H | ||
25 | #define __LINUX_NFC_H | ||
26 | |||
27 | #define NFC_GENL_NAME "nfc" | ||
28 | #define NFC_GENL_VERSION 1 | ||
29 | |||
30 | #define NFC_GENL_MCAST_EVENT_NAME "events" | ||
31 | |||
32 | /** | ||
33 | * enum nfc_commands - supported nfc commands | ||
34 | * | ||
35 | * @NFC_CMD_UNSPEC: unspecified command | ||
36 | * | ||
37 | * @NFC_CMD_GET_DEVICE: request information about a device (requires | ||
38 | * %NFC_ATTR_DEVICE_INDEX) or dump request to get a list of all nfc devices | ||
39 | * @NFC_CMD_START_POLL: start polling for targets using the given protocols | ||
40 | * (requires %NFC_ATTR_DEVICE_INDEX and %NFC_ATTR_PROTOCOLS) | ||
41 | * @NFC_CMD_STOP_POLL: stop polling for targets (requires | ||
42 | * %NFC_ATTR_DEVICE_INDEX) | ||
43 | * @NFC_CMD_GET_TARGET: dump all targets found by the previous poll (requires | ||
44 | * %NFC_ATTR_DEVICE_INDEX) | ||
45 | * @NFC_EVENT_TARGETS_FOUND: event emitted when a new target is found | ||
46 | * (it sends %NFC_ATTR_DEVICE_INDEX) | ||
47 | * @NFC_EVENT_DEVICE_ADDED: event emitted when a new device is registred | ||
48 | * (it sends %NFC_ATTR_DEVICE_NAME, %NFC_ATTR_DEVICE_INDEX and | ||
49 | * %NFC_ATTR_PROTOCOLS) | ||
50 | * @NFC_EVENT_DEVICE_REMOVED: event emitted when a device is removed | ||
51 | * (it sends %NFC_ATTR_DEVICE_INDEX) | ||
52 | */ | ||
53 | enum nfc_commands { | ||
54 | NFC_CMD_UNSPEC, | ||
55 | NFC_CMD_GET_DEVICE, | ||
56 | NFC_CMD_START_POLL, | ||
57 | NFC_CMD_STOP_POLL, | ||
58 | NFC_CMD_GET_TARGET, | ||
59 | NFC_EVENT_TARGETS_FOUND, | ||
60 | NFC_EVENT_DEVICE_ADDED, | ||
61 | NFC_EVENT_DEVICE_REMOVED, | ||
62 | /* private: internal use only */ | ||
63 | __NFC_CMD_AFTER_LAST | ||
64 | }; | ||
65 | #define NFC_CMD_MAX (__NFC_CMD_AFTER_LAST - 1) | ||
66 | |||
67 | /** | ||
68 | * enum nfc_attrs - supported nfc attributes | ||
69 | * | ||
70 | * @NFC_ATTR_UNSPEC: unspecified attribute | ||
71 | * | ||
72 | * @NFC_ATTR_DEVICE_INDEX: index of nfc device | ||
73 | * @NFC_ATTR_DEVICE_NAME: device name, max 8 chars | ||
74 | * @NFC_ATTR_PROTOCOLS: nfc protocols - bitwise or-ed combination from | ||
75 | * NFC_PROTO_*_MASK constants | ||
76 | * @NFC_ATTR_TARGET_INDEX: index of the nfc target | ||
77 | * @NFC_ATTR_TARGET_SENS_RES: NFC-A targets extra information such as NFCID | ||
78 | * @NFC_ATTR_TARGET_SEL_RES: NFC-A targets extra information (useful if the | ||
79 | * target is not NFC-Forum compliant) | ||
80 | */ | ||
81 | enum nfc_attrs { | ||
82 | NFC_ATTR_UNSPEC, | ||
83 | NFC_ATTR_DEVICE_INDEX, | ||
84 | NFC_ATTR_DEVICE_NAME, | ||
85 | NFC_ATTR_PROTOCOLS, | ||
86 | NFC_ATTR_TARGET_INDEX, | ||
87 | NFC_ATTR_TARGET_SENS_RES, | ||
88 | NFC_ATTR_TARGET_SEL_RES, | ||
89 | /* private: internal use only */ | ||
90 | __NFC_ATTR_AFTER_LAST | ||
91 | }; | ||
92 | #define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1) | ||
93 | |||
94 | #define NFC_DEVICE_NAME_MAXSIZE 8 | ||
95 | |||
96 | /* NFC protocols */ | ||
97 | #define NFC_PROTO_JEWEL 1 | ||
98 | #define NFC_PROTO_MIFARE 2 | ||
99 | #define NFC_PROTO_FELICA 3 | ||
100 | #define NFC_PROTO_ISO14443 4 | ||
101 | #define NFC_PROTO_NFC_DEP 5 | ||
102 | |||
103 | #define NFC_PROTO_MAX 6 | ||
104 | |||
105 | /* NFC protocols masks used in bitsets */ | ||
106 | #define NFC_PROTO_JEWEL_MASK (1 << NFC_PROTO_JEWEL) | ||
107 | #define NFC_PROTO_MIFARE_MASK (1 << NFC_PROTO_MIFARE) | ||
108 | #define NFC_PROTO_FELICA_MASK (1 << NFC_PROTO_FELICA) | ||
109 | #define NFC_PROTO_ISO14443_MASK (1 << NFC_PROTO_ISO14443) | ||
110 | #define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP) | ||
111 | |||
112 | #endif /*__LINUX_NFC_H */ | ||
diff --git a/include/net/nfc.h b/include/net/nfc.h index c57b579c8301..cc0130312f70 100644 --- a/include/net/nfc.h +++ b/include/net/nfc.h | |||
@@ -58,10 +58,28 @@ struct nfc_ops { | |||
58 | void *cb_context); | 58 | void *cb_context); |
59 | }; | 59 | }; |
60 | 60 | ||
61 | struct nfc_target { | ||
62 | u32 idx; | ||
63 | u32 supported_protocols; | ||
64 | u16 sens_res; | ||
65 | u8 sel_res; | ||
66 | }; | ||
67 | |||
68 | struct nfc_genl_data { | ||
69 | u32 poll_req_pid; | ||
70 | struct mutex genl_data_mutex; | ||
71 | }; | ||
72 | |||
61 | struct nfc_dev { | 73 | struct nfc_dev { |
62 | unsigned idx; | 74 | unsigned idx; |
75 | unsigned target_idx; | ||
76 | struct nfc_target *targets; | ||
77 | int n_targets; | ||
78 | int targets_generation; | ||
79 | spinlock_t targets_lock; | ||
63 | struct device dev; | 80 | struct device dev; |
64 | bool polling; | 81 | bool polling; |
82 | struct nfc_genl_data genl_data; | ||
65 | u32 supported_protocols; | 83 | u32 supported_protocols; |
66 | 84 | ||
67 | struct nfc_ops *ops; | 85 | struct nfc_ops *ops; |
@@ -132,4 +150,7 @@ static inline const char *nfc_device_name(struct nfc_dev *dev) | |||
132 | 150 | ||
133 | struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp); | 151 | struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp); |
134 | 152 | ||
153 | int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, | ||
154 | int ntargets); | ||
155 | |||
135 | #endif /* __NET_NFC_H */ | 156 | #endif /* __NET_NFC_H */ |
diff --git a/net/nfc/Makefile b/net/nfc/Makefile index 28bee5958687..fa6fc16246d6 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 | 7 | nfc-objs := core.o netlink.o |
diff --git a/net/nfc/core.c b/net/nfc/core.c index 19f8035a1ba9..c70f607455c5 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c | |||
@@ -233,12 +233,60 @@ struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp) | |||
233 | } | 233 | } |
234 | EXPORT_SYMBOL(nfc_alloc_skb); | 234 | EXPORT_SYMBOL(nfc_alloc_skb); |
235 | 235 | ||
236 | /** | ||
237 | * nfc_targets_found - inform that targets were found | ||
238 | * | ||
239 | * @dev: The nfc device that found the targets | ||
240 | * @targets: array of nfc targets found | ||
241 | * @ntargets: targets array size | ||
242 | * | ||
243 | * The device driver must call this function when one or many nfc targets | ||
244 | * are found. After calling this function, the device driver must stop | ||
245 | * polling for targets. | ||
246 | */ | ||
247 | int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, | ||
248 | int n_targets) | ||
249 | { | ||
250 | int i; | ||
251 | |||
252 | nfc_dbg("dev_name=%s n_targets=%d", dev_name(&dev->dev), n_targets); | ||
253 | |||
254 | dev->polling = false; | ||
255 | |||
256 | for (i = 0; i < n_targets; i++) | ||
257 | targets[i].idx = dev->target_idx++; | ||
258 | |||
259 | spin_lock_bh(&dev->targets_lock); | ||
260 | |||
261 | dev->targets_generation++; | ||
262 | |||
263 | kfree(dev->targets); | ||
264 | dev->targets = kmemdup(targets, n_targets * sizeof(struct nfc_target), | ||
265 | GFP_ATOMIC); | ||
266 | |||
267 | if (!dev->targets) { | ||
268 | dev->n_targets = 0; | ||
269 | spin_unlock_bh(&dev->targets_lock); | ||
270 | return -ENOMEM; | ||
271 | } | ||
272 | |||
273 | dev->n_targets = n_targets; | ||
274 | spin_unlock_bh(&dev->targets_lock); | ||
275 | |||
276 | nfc_genl_targets_found(dev); | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | EXPORT_SYMBOL(nfc_targets_found); | ||
281 | |||
236 | static void nfc_release(struct device *d) | 282 | static void nfc_release(struct device *d) |
237 | { | 283 | { |
238 | struct nfc_dev *dev = to_nfc_dev(d); | 284 | struct nfc_dev *dev = to_nfc_dev(d); |
239 | 285 | ||
240 | nfc_dbg("dev_name=%s", dev_name(&dev->dev)); | 286 | nfc_dbg("dev_name=%s", dev_name(&dev->dev)); |
241 | 287 | ||
288 | nfc_genl_data_exit(&dev->genl_data); | ||
289 | kfree(dev->targets); | ||
242 | kfree(dev); | 290 | kfree(dev); |
243 | } | 291 | } |
244 | 292 | ||
@@ -298,6 +346,12 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, | |||
298 | dev->ops = ops; | 346 | dev->ops = ops; |
299 | dev->supported_protocols = supported_protocols; | 347 | dev->supported_protocols = supported_protocols; |
300 | 348 | ||
349 | spin_lock_init(&dev->targets_lock); | ||
350 | nfc_genl_data_init(&dev->genl_data); | ||
351 | |||
352 | /* first generation must not be 0 */ | ||
353 | dev->targets_generation = 1; | ||
354 | |||
301 | return dev; | 355 | return dev; |
302 | } | 356 | } |
303 | EXPORT_SYMBOL(nfc_allocate_device); | 357 | EXPORT_SYMBOL(nfc_allocate_device); |
@@ -318,7 +372,16 @@ int nfc_register_device(struct nfc_dev *dev) | |||
318 | rc = device_add(&dev->dev); | 372 | rc = device_add(&dev->dev); |
319 | mutex_unlock(&nfc_devlist_mutex); | 373 | mutex_unlock(&nfc_devlist_mutex); |
320 | 374 | ||
321 | return rc; | 375 | if (rc < 0) |
376 | return rc; | ||
377 | |||
378 | rc = nfc_genl_device_added(dev); | ||
379 | if (rc) | ||
380 | nfc_dbg("The userspace won't be notified that the device %s was" | ||
381 | " added", dev_name(&dev->dev)); | ||
382 | |||
383 | |||
384 | return 0; | ||
322 | } | 385 | } |
323 | EXPORT_SYMBOL(nfc_register_device); | 386 | EXPORT_SYMBOL(nfc_register_device); |
324 | 387 | ||
@@ -329,6 +392,8 @@ EXPORT_SYMBOL(nfc_register_device); | |||
329 | */ | 392 | */ |
330 | void nfc_unregister_device(struct nfc_dev *dev) | 393 | void nfc_unregister_device(struct nfc_dev *dev) |
331 | { | 394 | { |
395 | int rc; | ||
396 | |||
332 | nfc_dbg("dev_name=%s", dev_name(&dev->dev)); | 397 | nfc_dbg("dev_name=%s", dev_name(&dev->dev)); |
333 | 398 | ||
334 | mutex_lock(&nfc_devlist_mutex); | 399 | mutex_lock(&nfc_devlist_mutex); |
@@ -341,18 +406,42 @@ void nfc_unregister_device(struct nfc_dev *dev) | |||
341 | device_unlock(&dev->dev); | 406 | device_unlock(&dev->dev); |
342 | 407 | ||
343 | mutex_unlock(&nfc_devlist_mutex); | 408 | mutex_unlock(&nfc_devlist_mutex); |
409 | |||
410 | rc = nfc_genl_device_removed(dev); | ||
411 | if (rc) | ||
412 | nfc_dbg("The userspace won't be notified that the device %s" | ||
413 | " was removed", dev_name(&dev->dev)); | ||
414 | |||
344 | } | 415 | } |
345 | EXPORT_SYMBOL(nfc_unregister_device); | 416 | EXPORT_SYMBOL(nfc_unregister_device); |
346 | 417 | ||
347 | static int __init nfc_init(void) | 418 | static int __init nfc_init(void) |
348 | { | 419 | { |
420 | int rc; | ||
421 | |||
349 | nfc_info("NFC Core ver %s", VERSION); | 422 | nfc_info("NFC Core ver %s", VERSION); |
350 | 423 | ||
351 | return class_register(&nfc_class); | 424 | rc = class_register(&nfc_class); |
425 | if (rc) | ||
426 | return rc; | ||
427 | |||
428 | rc = nfc_genl_init(); | ||
429 | if (rc) | ||
430 | goto err_genl; | ||
431 | |||
432 | /* the first generation must not be 0 */ | ||
433 | nfc_devlist_generation = 1; | ||
434 | |||
435 | return 0; | ||
436 | |||
437 | err_genl: | ||
438 | class_unregister(&nfc_class); | ||
439 | return rc; | ||
352 | } | 440 | } |
353 | 441 | ||
354 | static void __exit nfc_exit(void) | 442 | static void __exit nfc_exit(void) |
355 | { | 443 | { |
444 | nfc_genl_exit(); | ||
356 | class_unregister(&nfc_class); | 445 | class_unregister(&nfc_class); |
357 | } | 446 | } |
358 | 447 | ||
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c new file mode 100644 index 000000000000..ccdff7953f7d --- /dev/null +++ b/net/nfc/netlink.c | |||
@@ -0,0 +1,537 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Instituto Nokia de Tecnologia | ||
3 | * | ||
4 | * Authors: | ||
5 | * Lauro Ramos Venancio <lauro.venancio@openbossa.org> | ||
6 | * Aloisio Almeida Jr <aloisio.almeida@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/genetlink.h> | ||
25 | #include <linux/nfc.h> | ||
26 | #include <linux/slab.h> | ||
27 | |||
28 | #include "nfc.h" | ||
29 | |||
30 | static struct genl_multicast_group nfc_genl_event_mcgrp = { | ||
31 | .name = NFC_GENL_MCAST_EVENT_NAME, | ||
32 | }; | ||
33 | |||
34 | struct genl_family nfc_genl_family = { | ||
35 | .id = GENL_ID_GENERATE, | ||
36 | .hdrsize = 0, | ||
37 | .name = NFC_GENL_NAME, | ||
38 | .version = NFC_GENL_VERSION, | ||
39 | .maxattr = NFC_ATTR_MAX, | ||
40 | }; | ||
41 | |||
42 | static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { | ||
43 | [NFC_ATTR_DEVICE_INDEX] = { .type = NLA_U32 }, | ||
44 | [NFC_ATTR_DEVICE_NAME] = { .type = NLA_STRING, | ||
45 | .len = NFC_DEVICE_NAME_MAXSIZE }, | ||
46 | [NFC_ATTR_PROTOCOLS] = { .type = NLA_U32 }, | ||
47 | }; | ||
48 | |||
49 | static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target, | ||
50 | struct netlink_callback *cb, int flags) | ||
51 | { | ||
52 | void *hdr; | ||
53 | |||
54 | nfc_dbg("entry"); | ||
55 | |||
56 | hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, | ||
57 | &nfc_genl_family, flags, NFC_CMD_GET_TARGET); | ||
58 | if (!hdr) | ||
59 | return -EMSGSIZE; | ||
60 | |||
61 | genl_dump_check_consistent(cb, hdr, &nfc_genl_family); | ||
62 | |||
63 | NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target->idx); | ||
64 | NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, | ||
65 | target->supported_protocols); | ||
66 | NLA_PUT_U16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res); | ||
67 | NLA_PUT_U8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res); | ||
68 | |||
69 | return genlmsg_end(msg, hdr); | ||
70 | |||
71 | nla_put_failure: | ||
72 | genlmsg_cancel(msg, hdr); | ||
73 | return -EMSGSIZE; | ||
74 | } | ||
75 | |||
76 | static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb) | ||
77 | { | ||
78 | struct nfc_dev *dev; | ||
79 | int rc; | ||
80 | u32 idx; | ||
81 | |||
82 | rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize, | ||
83 | nfc_genl_family.attrbuf, | ||
84 | nfc_genl_family.maxattr, | ||
85 | nfc_genl_policy); | ||
86 | if (rc < 0) | ||
87 | return ERR_PTR(rc); | ||
88 | |||
89 | if (!nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]) | ||
90 | return ERR_PTR(-EINVAL); | ||
91 | |||
92 | idx = nla_get_u32(nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]); | ||
93 | |||
94 | dev = nfc_get_device(idx); | ||
95 | if (!dev) | ||
96 | return ERR_PTR(-ENODEV); | ||
97 | |||
98 | return dev; | ||
99 | } | ||
100 | |||
101 | static int nfc_genl_dump_targets(struct sk_buff *skb, | ||
102 | struct netlink_callback *cb) | ||
103 | { | ||
104 | int i = cb->args[0]; | ||
105 | struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; | ||
106 | int rc; | ||
107 | |||
108 | nfc_dbg("entry"); | ||
109 | |||
110 | if (!dev) { | ||
111 | dev = __get_device_from_cb(cb); | ||
112 | if (IS_ERR(dev)) | ||
113 | return PTR_ERR(dev); | ||
114 | |||
115 | cb->args[1] = (long) dev; | ||
116 | } | ||
117 | |||
118 | spin_lock_bh(&dev->targets_lock); | ||
119 | |||
120 | cb->seq = dev->targets_generation; | ||
121 | |||
122 | while (i < dev->n_targets) { | ||
123 | rc = nfc_genl_send_target(skb, &dev->targets[i], cb, | ||
124 | NLM_F_MULTI); | ||
125 | if (rc < 0) | ||
126 | break; | ||
127 | |||
128 | i++; | ||
129 | } | ||
130 | |||
131 | spin_unlock_bh(&dev->targets_lock); | ||
132 | |||
133 | cb->args[0] = i; | ||
134 | |||
135 | return skb->len; | ||
136 | } | ||
137 | |||
138 | static int nfc_genl_dump_targets_done(struct netlink_callback *cb) | ||
139 | { | ||
140 | struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; | ||
141 | |||
142 | nfc_dbg("entry"); | ||
143 | |||
144 | if (dev) | ||
145 | nfc_put_device(dev); | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | int nfc_genl_targets_found(struct nfc_dev *dev) | ||
151 | { | ||
152 | struct sk_buff *msg; | ||
153 | void *hdr; | ||
154 | |||
155 | nfc_dbg("entry"); | ||
156 | |||
157 | dev->genl_data.poll_req_pid = 0; | ||
158 | |||
159 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); | ||
160 | if (!msg) | ||
161 | return -ENOMEM; | ||
162 | |||
163 | hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, | ||
164 | NFC_EVENT_TARGETS_FOUND); | ||
165 | if (!hdr) | ||
166 | goto free_msg; | ||
167 | |||
168 | NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx); | ||
169 | |||
170 | genlmsg_end(msg, hdr); | ||
171 | |||
172 | return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC); | ||
173 | |||
174 | nla_put_failure: | ||
175 | genlmsg_cancel(msg, hdr); | ||
176 | free_msg: | ||
177 | nlmsg_free(msg); | ||
178 | return -EMSGSIZE; | ||
179 | } | ||
180 | |||
181 | int nfc_genl_device_added(struct nfc_dev *dev) | ||
182 | { | ||
183 | struct sk_buff *msg; | ||
184 | void *hdr; | ||
185 | |||
186 | nfc_dbg("entry"); | ||
187 | |||
188 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
189 | if (!msg) | ||
190 | return -ENOMEM; | ||
191 | |||
192 | hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, | ||
193 | NFC_EVENT_DEVICE_ADDED); | ||
194 | if (!hdr) | ||
195 | goto free_msg; | ||
196 | |||
197 | NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)); | ||
198 | NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx); | ||
199 | NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols); | ||
200 | |||
201 | genlmsg_end(msg, hdr); | ||
202 | |||
203 | genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); | ||
204 | |||
205 | return 0; | ||
206 | |||
207 | nla_put_failure: | ||
208 | genlmsg_cancel(msg, hdr); | ||
209 | free_msg: | ||
210 | nlmsg_free(msg); | ||
211 | return -EMSGSIZE; | ||
212 | } | ||
213 | |||
214 | int nfc_genl_device_removed(struct nfc_dev *dev) | ||
215 | { | ||
216 | struct sk_buff *msg; | ||
217 | void *hdr; | ||
218 | |||
219 | nfc_dbg("entry"); | ||
220 | |||
221 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
222 | if (!msg) | ||
223 | return -ENOMEM; | ||
224 | |||
225 | hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, | ||
226 | NFC_EVENT_DEVICE_REMOVED); | ||
227 | if (!hdr) | ||
228 | goto free_msg; | ||
229 | |||
230 | NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx); | ||
231 | |||
232 | genlmsg_end(msg, hdr); | ||
233 | |||
234 | genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); | ||
235 | |||
236 | return 0; | ||
237 | |||
238 | nla_put_failure: | ||
239 | genlmsg_cancel(msg, hdr); | ||
240 | free_msg: | ||
241 | nlmsg_free(msg); | ||
242 | return -EMSGSIZE; | ||
243 | } | ||
244 | |||
245 | static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, | ||
246 | u32 pid, u32 seq, | ||
247 | struct netlink_callback *cb, | ||
248 | int flags) | ||
249 | { | ||
250 | void *hdr; | ||
251 | |||
252 | nfc_dbg("entry"); | ||
253 | |||
254 | hdr = genlmsg_put(msg, pid, seq, &nfc_genl_family, flags, | ||
255 | NFC_CMD_GET_DEVICE); | ||
256 | if (!hdr) | ||
257 | return -EMSGSIZE; | ||
258 | |||
259 | if (cb) | ||
260 | genl_dump_check_consistent(cb, hdr, &nfc_genl_family); | ||
261 | |||
262 | NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)); | ||
263 | NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx); | ||
264 | NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols); | ||
265 | |||
266 | return genlmsg_end(msg, hdr); | ||
267 | |||
268 | nla_put_failure: | ||
269 | genlmsg_cancel(msg, hdr); | ||
270 | return -EMSGSIZE; | ||
271 | } | ||
272 | |||
273 | static int nfc_genl_dump_devices(struct sk_buff *skb, | ||
274 | struct netlink_callback *cb) | ||
275 | { | ||
276 | struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; | ||
277 | struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; | ||
278 | bool first_call = false; | ||
279 | |||
280 | nfc_dbg("entry"); | ||
281 | |||
282 | if (!iter) { | ||
283 | first_call = true; | ||
284 | iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL); | ||
285 | if (!iter) | ||
286 | return -ENOMEM; | ||
287 | cb->args[0] = (long) iter; | ||
288 | } | ||
289 | |||
290 | mutex_lock(&nfc_devlist_mutex); | ||
291 | |||
292 | cb->seq = nfc_devlist_generation; | ||
293 | |||
294 | if (first_call) { | ||
295 | nfc_device_iter_init(iter); | ||
296 | dev = nfc_device_iter_next(iter); | ||
297 | } | ||
298 | |||
299 | while (dev) { | ||
300 | int rc; | ||
301 | |||
302 | rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).pid, | ||
303 | cb->nlh->nlmsg_seq, | ||
304 | cb, NLM_F_MULTI); | ||
305 | if (rc < 0) | ||
306 | break; | ||
307 | |||
308 | dev = nfc_device_iter_next(iter); | ||
309 | } | ||
310 | |||
311 | mutex_unlock(&nfc_devlist_mutex); | ||
312 | |||
313 | cb->args[1] = (long) dev; | ||
314 | |||
315 | return skb->len; | ||
316 | } | ||
317 | |||
318 | static int nfc_genl_dump_devices_done(struct netlink_callback *cb) | ||
319 | { | ||
320 | struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; | ||
321 | |||
322 | nfc_dbg("entry"); | ||
323 | |||
324 | nfc_device_iter_exit(iter); | ||
325 | kfree(iter); | ||
326 | |||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info) | ||
331 | { | ||
332 | struct sk_buff *msg; | ||
333 | struct nfc_dev *dev; | ||
334 | u32 idx; | ||
335 | int rc = -ENOBUFS; | ||
336 | |||
337 | nfc_dbg("entry"); | ||
338 | |||
339 | if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) | ||
340 | return -EINVAL; | ||
341 | |||
342 | idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); | ||
343 | |||
344 | dev = nfc_get_device(idx); | ||
345 | if (!dev) | ||
346 | return -ENODEV; | ||
347 | |||
348 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
349 | if (!msg) { | ||
350 | rc = -ENOMEM; | ||
351 | goto out_putdev; | ||
352 | } | ||
353 | |||
354 | rc = nfc_genl_send_device(msg, dev, info->snd_pid, info->snd_seq, | ||
355 | NULL, 0); | ||
356 | if (rc < 0) | ||
357 | goto out_free; | ||
358 | |||
359 | nfc_put_device(dev); | ||
360 | |||
361 | return genlmsg_reply(msg, info); | ||
362 | |||
363 | out_free: | ||
364 | nlmsg_free(msg); | ||
365 | out_putdev: | ||
366 | nfc_put_device(dev); | ||
367 | return rc; | ||
368 | } | ||
369 | |||
370 | static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) | ||
371 | { | ||
372 | struct nfc_dev *dev; | ||
373 | int rc; | ||
374 | u32 idx; | ||
375 | u32 protocols; | ||
376 | |||
377 | nfc_dbg("entry"); | ||
378 | |||
379 | if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || | ||
380 | !info->attrs[NFC_ATTR_PROTOCOLS]) | ||
381 | return -EINVAL; | ||
382 | |||
383 | idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); | ||
384 | protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]); | ||
385 | |||
386 | dev = nfc_get_device(idx); | ||
387 | if (!dev) | ||
388 | return -ENODEV; | ||
389 | |||
390 | mutex_lock(&dev->genl_data.genl_data_mutex); | ||
391 | |||
392 | rc = nfc_start_poll(dev, protocols); | ||
393 | if (!rc) | ||
394 | dev->genl_data.poll_req_pid = info->snd_pid; | ||
395 | |||
396 | mutex_unlock(&dev->genl_data.genl_data_mutex); | ||
397 | |||
398 | nfc_put_device(dev); | ||
399 | return rc; | ||
400 | } | ||
401 | |||
402 | static int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info) | ||
403 | { | ||
404 | struct nfc_dev *dev; | ||
405 | int rc; | ||
406 | u32 idx; | ||
407 | |||
408 | nfc_dbg("entry"); | ||
409 | |||
410 | if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) | ||
411 | return -EINVAL; | ||
412 | |||
413 | idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); | ||
414 | |||
415 | dev = nfc_get_device(idx); | ||
416 | if (!dev) | ||
417 | return -ENODEV; | ||
418 | |||
419 | mutex_lock(&dev->genl_data.genl_data_mutex); | ||
420 | |||
421 | if (dev->genl_data.poll_req_pid != info->snd_pid) { | ||
422 | rc = -EBUSY; | ||
423 | goto out; | ||
424 | } | ||
425 | |||
426 | rc = nfc_stop_poll(dev); | ||
427 | dev->genl_data.poll_req_pid = 0; | ||
428 | |||
429 | out: | ||
430 | mutex_unlock(&dev->genl_data.genl_data_mutex); | ||
431 | nfc_put_device(dev); | ||
432 | return rc; | ||
433 | } | ||
434 | |||
435 | static struct genl_ops nfc_genl_ops[] = { | ||
436 | { | ||
437 | .cmd = NFC_CMD_GET_DEVICE, | ||
438 | .doit = nfc_genl_get_device, | ||
439 | .dumpit = nfc_genl_dump_devices, | ||
440 | .done = nfc_genl_dump_devices_done, | ||
441 | .policy = nfc_genl_policy, | ||
442 | }, | ||
443 | { | ||
444 | .cmd = NFC_CMD_START_POLL, | ||
445 | .doit = nfc_genl_start_poll, | ||
446 | .policy = nfc_genl_policy, | ||
447 | }, | ||
448 | { | ||
449 | .cmd = NFC_CMD_STOP_POLL, | ||
450 | .doit = nfc_genl_stop_poll, | ||
451 | .policy = nfc_genl_policy, | ||
452 | }, | ||
453 | { | ||
454 | .cmd = NFC_CMD_GET_TARGET, | ||
455 | .dumpit = nfc_genl_dump_targets, | ||
456 | .done = nfc_genl_dump_targets_done, | ||
457 | .policy = nfc_genl_policy, | ||
458 | }, | ||
459 | }; | ||
460 | |||
461 | static int nfc_genl_rcv_nl_event(struct notifier_block *this, | ||
462 | unsigned long event, void *ptr) | ||
463 | { | ||
464 | struct netlink_notify *n = ptr; | ||
465 | struct class_dev_iter iter; | ||
466 | struct nfc_dev *dev; | ||
467 | |||
468 | if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC) | ||
469 | goto out; | ||
470 | |||
471 | nfc_dbg("NETLINK_URELEASE event from id %d", n->pid); | ||
472 | |||
473 | nfc_device_iter_init(&iter); | ||
474 | dev = nfc_device_iter_next(&iter); | ||
475 | |||
476 | while (dev) { | ||
477 | mutex_lock(&dev->genl_data.genl_data_mutex); | ||
478 | if (dev->genl_data.poll_req_pid == n->pid) { | ||
479 | nfc_stop_poll(dev); | ||
480 | dev->genl_data.poll_req_pid = 0; | ||
481 | } | ||
482 | mutex_unlock(&dev->genl_data.genl_data_mutex); | ||
483 | dev = nfc_device_iter_next(&iter); | ||
484 | } | ||
485 | |||
486 | nfc_device_iter_exit(&iter); | ||
487 | |||
488 | out: | ||
489 | return NOTIFY_DONE; | ||
490 | } | ||
491 | |||
492 | void nfc_genl_data_init(struct nfc_genl_data *genl_data) | ||
493 | { | ||
494 | genl_data->poll_req_pid = 0; | ||
495 | mutex_init(&genl_data->genl_data_mutex); | ||
496 | } | ||
497 | |||
498 | void nfc_genl_data_exit(struct nfc_genl_data *genl_data) | ||
499 | { | ||
500 | mutex_destroy(&genl_data->genl_data_mutex); | ||
501 | } | ||
502 | |||
503 | static struct notifier_block nl_notifier = { | ||
504 | .notifier_call = nfc_genl_rcv_nl_event, | ||
505 | }; | ||
506 | |||
507 | /** | ||
508 | * nfc_genl_init() - Initialize netlink interface | ||
509 | * | ||
510 | * This initialization function registers the nfc netlink family. | ||
511 | */ | ||
512 | int __init nfc_genl_init(void) | ||
513 | { | ||
514 | int rc; | ||
515 | |||
516 | rc = genl_register_family_with_ops(&nfc_genl_family, nfc_genl_ops, | ||
517 | ARRAY_SIZE(nfc_genl_ops)); | ||
518 | if (rc) | ||
519 | return rc; | ||
520 | |||
521 | rc = genl_register_mc_group(&nfc_genl_family, &nfc_genl_event_mcgrp); | ||
522 | |||
523 | netlink_register_notifier(&nl_notifier); | ||
524 | |||
525 | return rc; | ||
526 | } | ||
527 | |||
528 | /** | ||
529 | * nfc_genl_exit() - Deinitialize netlink interface | ||
530 | * | ||
531 | * This exit function unregisters the nfc netlink family. | ||
532 | */ | ||
533 | void nfc_genl_exit(void) | ||
534 | { | ||
535 | netlink_unregister_notifier(&nl_notifier); | ||
536 | genl_unregister_family(&nfc_genl_family); | ||
537 | } | ||
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 55f83f86fe2c..2b31e808e6fb 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h | |||
@@ -36,6 +36,17 @@ int nfc_printk(const char *level, const char *fmt, ...); | |||
36 | extern int nfc_devlist_generation; | 36 | extern int nfc_devlist_generation; |
37 | extern struct mutex nfc_devlist_mutex; | 37 | extern struct mutex nfc_devlist_mutex; |
38 | 38 | ||
39 | int __init nfc_genl_init(void); | ||
40 | void nfc_genl_exit(void); | ||
41 | |||
42 | void nfc_genl_data_init(struct nfc_genl_data *genl_data); | ||
43 | void nfc_genl_data_exit(struct nfc_genl_data *genl_data); | ||
44 | |||
45 | int nfc_genl_targets_found(struct nfc_dev *dev); | ||
46 | |||
47 | int nfc_genl_device_added(struct nfc_dev *dev); | ||
48 | int nfc_genl_device_removed(struct nfc_dev *dev); | ||
49 | |||
39 | struct nfc_dev *nfc_get_device(unsigned idx); | 50 | struct nfc_dev *nfc_get_device(unsigned idx); |
40 | 51 | ||
41 | static inline void nfc_put_device(struct nfc_dev *dev) | 52 | static inline void nfc_put_device(struct nfc_dev *dev) |