blob: 3568ae16786d513830040ddb020c33237649198b [file] [log] [blame]
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -03001/*
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
Samuel Ortiz52858b52011-12-14 16:43:05 +010024#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
Joe Perches20c239c2011-11-29 11:37:33 -080025
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -030026#include <net/genetlink.h>
27#include <linux/nfc.h>
28#include <linux/slab.h>
29
30#include "nfc.h"
31
Thierry Escande52feb442012-10-17 14:43:39 +020032#include "llcp/llcp.h"
33
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -030034static struct genl_multicast_group nfc_genl_event_mcgrp = {
35 .name = NFC_GENL_MCAST_EVENT_NAME,
36};
37
H Hartley Sweetene5fe4cf2012-05-07 12:31:28 +020038static struct genl_family nfc_genl_family = {
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -030039 .id = GENL_ID_GENERATE,
40 .hdrsize = 0,
41 .name = NFC_GENL_NAME,
42 .version = NFC_GENL_VERSION,
43 .maxattr = NFC_ATTR_MAX,
44};
45
46static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
47 [NFC_ATTR_DEVICE_INDEX] = { .type = NLA_U32 },
48 [NFC_ATTR_DEVICE_NAME] = { .type = NLA_STRING,
49 .len = NFC_DEVICE_NAME_MAXSIZE },
50 [NFC_ATTR_PROTOCOLS] = { .type = NLA_U32 },
Samuel Ortiz1ed28f62011-12-14 16:43:09 +010051 [NFC_ATTR_COMM_MODE] = { .type = NLA_U8 },
52 [NFC_ATTR_RF_MODE] = { .type = NLA_U8 },
Samuel Ortizc970a1a2012-03-05 01:03:34 +010053 [NFC_ATTR_DEVICE_POWERED] = { .type = NLA_U8 },
Samuel Ortizfe7c5802012-05-15 15:57:06 +020054 [NFC_ATTR_IM_PROTOCOLS] = { .type = NLA_U32 },
55 [NFC_ATTR_TM_PROTOCOLS] = { .type = NLA_U32 },
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -030056};
57
58static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +010059 struct netlink_callback *cb, int flags)
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -030060{
61 void *hdr;
62
Eric W. Biederman15e47302012-09-07 20:12:54 +000063 hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +010064 &nfc_genl_family, flags, NFC_CMD_GET_TARGET);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -030065 if (!hdr)
66 return -EMSGSIZE;
67
68 genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
69
David S. Miller1e6428d2012-03-29 23:23:57 -040070 if (nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target->idx) ||
71 nla_put_u32(msg, NFC_ATTR_PROTOCOLS, target->supported_protocols) ||
72 nla_put_u16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res) ||
73 nla_put_u8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res))
74 goto nla_put_failure;
75 if (target->nfcid1_len > 0 &&
76 nla_put(msg, NFC_ATTR_TARGET_NFCID1, target->nfcid1_len,
77 target->nfcid1))
78 goto nla_put_failure;
79 if (target->sensb_res_len > 0 &&
80 nla_put(msg, NFC_ATTR_TARGET_SENSB_RES, target->sensb_res_len,
81 target->sensb_res))
82 goto nla_put_failure;
83 if (target->sensf_res_len > 0 &&
84 nla_put(msg, NFC_ATTR_TARGET_SENSF_RES, target->sensf_res_len,
85 target->sensf_res))
86 goto nla_put_failure;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -030087
88 return genlmsg_end(msg, hdr);
89
90nla_put_failure:
91 genlmsg_cancel(msg, hdr);
92 return -EMSGSIZE;
93}
94
95static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb)
96{
97 struct nfc_dev *dev;
98 int rc;
99 u32 idx;
100
101 rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100102 nfc_genl_family.attrbuf,
103 nfc_genl_family.maxattr,
104 nfc_genl_policy);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300105 if (rc < 0)
106 return ERR_PTR(rc);
107
108 if (!nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX])
109 return ERR_PTR(-EINVAL);
110
111 idx = nla_get_u32(nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]);
112
113 dev = nfc_get_device(idx);
114 if (!dev)
115 return ERR_PTR(-ENODEV);
116
117 return dev;
118}
119
120static int nfc_genl_dump_targets(struct sk_buff *skb,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100121 struct netlink_callback *cb)
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300122{
123 int i = cb->args[0];
124 struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
125 int rc;
126
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300127 if (!dev) {
128 dev = __get_device_from_cb(cb);
129 if (IS_ERR(dev))
130 return PTR_ERR(dev);
131
132 cb->args[1] = (long) dev;
133 }
134
Eric Lapuyaded4ccb132012-05-07 12:31:15 +0200135 device_lock(&dev->dev);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300136
137 cb->seq = dev->targets_generation;
138
139 while (i < dev->n_targets) {
140 rc = nfc_genl_send_target(skb, &dev->targets[i], cb,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100141 NLM_F_MULTI);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300142 if (rc < 0)
143 break;
144
145 i++;
146 }
147
Eric Lapuyaded4ccb132012-05-07 12:31:15 +0200148 device_unlock(&dev->dev);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300149
150 cb->args[0] = i;
151
152 return skb->len;
153}
154
155static int nfc_genl_dump_targets_done(struct netlink_callback *cb)
156{
157 struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
158
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300159 if (dev)
160 nfc_put_device(dev);
161
162 return 0;
163}
164
165int nfc_genl_targets_found(struct nfc_dev *dev)
166{
167 struct sk_buff *msg;
168 void *hdr;
169
Eric W. Biederman15e47302012-09-07 20:12:54 +0000170 dev->genl_data.poll_req_portid = 0;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300171
Thomas Graf58050fc2012-06-28 03:57:45 +0000172 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300173 if (!msg)
174 return -ENOMEM;
175
176 hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100177 NFC_EVENT_TARGETS_FOUND);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300178 if (!hdr)
179 goto free_msg;
180
David S. Miller1e6428d2012-03-29 23:23:57 -0400181 if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
182 goto nla_put_failure;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300183
184 genlmsg_end(msg, hdr);
185
186 return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
187
188nla_put_failure:
189 genlmsg_cancel(msg, hdr);
190free_msg:
191 nlmsg_free(msg);
192 return -EMSGSIZE;
193}
194
Samuel Ortiz8112a5c2012-04-10 19:43:04 +0200195int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx)
196{
197 struct sk_buff *msg;
198 void *hdr;
199
Thomas Graf58050fc2012-06-28 03:57:45 +0000200 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Samuel Ortiz8112a5c2012-04-10 19:43:04 +0200201 if (!msg)
202 return -ENOMEM;
203
204 hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
205 NFC_EVENT_TARGET_LOST);
206 if (!hdr)
207 goto free_msg;
208
John W. Linville59ef43e2012-04-18 14:17:13 -0400209 if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
210 nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target_idx))
211 goto nla_put_failure;
Samuel Ortiz8112a5c2012-04-10 19:43:04 +0200212
213 genlmsg_end(msg, hdr);
214
215 genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
216
217 return 0;
218
219nla_put_failure:
220 genlmsg_cancel(msg, hdr);
221free_msg:
222 nlmsg_free(msg);
223 return -EMSGSIZE;
224}
225
Samuel Ortizfc40a8c2012-06-01 13:21:13 +0200226int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol)
227{
228 struct sk_buff *msg;
229 void *hdr;
230
Thomas Graf58050fc2012-06-28 03:57:45 +0000231 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Samuel Ortizfc40a8c2012-06-01 13:21:13 +0200232 if (!msg)
233 return -ENOMEM;
234
235 hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
236 NFC_EVENT_TM_ACTIVATED);
237 if (!hdr)
238 goto free_msg;
239
240 if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
241 goto nla_put_failure;
242 if (nla_put_u32(msg, NFC_ATTR_TM_PROTOCOLS, protocol))
243 goto nla_put_failure;
244
245 genlmsg_end(msg, hdr);
246
247 genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
248
249 return 0;
250
251nla_put_failure:
252 genlmsg_cancel(msg, hdr);
253free_msg:
254 nlmsg_free(msg);
255 return -EMSGSIZE;
256}
257
258int nfc_genl_tm_deactivated(struct nfc_dev *dev)
259{
260 struct sk_buff *msg;
261 void *hdr;
262
Thomas Graf58050fc2012-06-28 03:57:45 +0000263 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Samuel Ortizfc40a8c2012-06-01 13:21:13 +0200264 if (!msg)
265 return -ENOMEM;
266
267 hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
268 NFC_EVENT_TM_DEACTIVATED);
269 if (!hdr)
270 goto free_msg;
271
272 if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
273 goto nla_put_failure;
274
275 genlmsg_end(msg, hdr);
276
277 genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
278
279 return 0;
280
281nla_put_failure:
282 genlmsg_cancel(msg, hdr);
283free_msg:
284 nlmsg_free(msg);
285 return -EMSGSIZE;
286}
287
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300288int nfc_genl_device_added(struct nfc_dev *dev)
289{
290 struct sk_buff *msg;
291 void *hdr;
292
Thomas Graf58050fc2012-06-28 03:57:45 +0000293 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300294 if (!msg)
295 return -ENOMEM;
296
297 hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100298 NFC_EVENT_DEVICE_ADDED);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300299 if (!hdr)
300 goto free_msg;
301
David S. Miller1e6428d2012-03-29 23:23:57 -0400302 if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
303 nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
304 nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
305 nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up))
306 goto nla_put_failure;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300307
308 genlmsg_end(msg, hdr);
309
310 genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
311
312 return 0;
313
314nla_put_failure:
315 genlmsg_cancel(msg, hdr);
316free_msg:
317 nlmsg_free(msg);
318 return -EMSGSIZE;
319}
320
321int nfc_genl_device_removed(struct nfc_dev *dev)
322{
323 struct sk_buff *msg;
324 void *hdr;
325
Thomas Graf58050fc2012-06-28 03:57:45 +0000326 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300327 if (!msg)
328 return -ENOMEM;
329
330 hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100331 NFC_EVENT_DEVICE_REMOVED);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300332 if (!hdr)
333 goto free_msg;
334
David S. Miller1e6428d2012-03-29 23:23:57 -0400335 if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
336 goto nla_put_failure;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300337
338 genlmsg_end(msg, hdr);
339
340 genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
341
342 return 0;
343
344nla_put_failure:
345 genlmsg_cancel(msg, hdr);
346free_msg:
347 nlmsg_free(msg);
348 return -EMSGSIZE;
349}
350
351static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
Eric W. Biederman15e47302012-09-07 20:12:54 +0000352 u32 portid, u32 seq,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100353 struct netlink_callback *cb,
354 int flags)
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300355{
356 void *hdr;
357
Eric W. Biederman15e47302012-09-07 20:12:54 +0000358 hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100359 NFC_CMD_GET_DEVICE);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300360 if (!hdr)
361 return -EMSGSIZE;
362
363 if (cb)
364 genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
365
David S. Miller1e6428d2012-03-29 23:23:57 -0400366 if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
367 nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
368 nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
Thierry Escande7ad39392012-10-05 11:19:58 +0200369 nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
370 nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
David S. Miller1e6428d2012-03-29 23:23:57 -0400371 goto nla_put_failure;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300372
373 return genlmsg_end(msg, hdr);
374
375nla_put_failure:
376 genlmsg_cancel(msg, hdr);
377 return -EMSGSIZE;
378}
379
380static int nfc_genl_dump_devices(struct sk_buff *skb,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100381 struct netlink_callback *cb)
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300382{
383 struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
384 struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
385 bool first_call = false;
386
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300387 if (!iter) {
388 first_call = true;
389 iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
390 if (!iter)
391 return -ENOMEM;
392 cb->args[0] = (long) iter;
393 }
394
395 mutex_lock(&nfc_devlist_mutex);
396
397 cb->seq = nfc_devlist_generation;
398
399 if (first_call) {
400 nfc_device_iter_init(iter);
401 dev = nfc_device_iter_next(iter);
402 }
403
404 while (dev) {
405 int rc;
406
Eric W. Biederman15e47302012-09-07 20:12:54 +0000407 rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).portid,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100408 cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300409 if (rc < 0)
410 break;
411
412 dev = nfc_device_iter_next(iter);
413 }
414
415 mutex_unlock(&nfc_devlist_mutex);
416
417 cb->args[1] = (long) dev;
418
419 return skb->len;
420}
421
422static int nfc_genl_dump_devices_done(struct netlink_callback *cb)
423{
424 struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
425
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300426 nfc_device_iter_exit(iter);
427 kfree(iter);
428
429 return 0;
430}
431
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100432int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100433 u8 comm_mode, u8 rf_mode)
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100434{
435 struct sk_buff *msg;
436 void *hdr;
437
438 pr_debug("DEP link is up\n");
439
Thomas Graf58050fc2012-06-28 03:57:45 +0000440 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100441 if (!msg)
442 return -ENOMEM;
443
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100444 hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, NFC_CMD_DEP_LINK_UP);
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100445 if (!hdr)
446 goto free_msg;
447
David S. Miller1e6428d2012-03-29 23:23:57 -0400448 if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
449 goto nla_put_failure;
450 if (rf_mode == NFC_RF_INITIATOR &&
451 nla_put_u32(msg, NFC_ATTR_TARGET_INDEX, target_idx))
452 goto nla_put_failure;
453 if (nla_put_u8(msg, NFC_ATTR_COMM_MODE, comm_mode) ||
454 nla_put_u8(msg, NFC_ATTR_RF_MODE, rf_mode))
455 goto nla_put_failure;
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100456
457 genlmsg_end(msg, hdr);
458
459 dev->dep_link_up = true;
460
461 genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
462
463 return 0;
464
465nla_put_failure:
466 genlmsg_cancel(msg, hdr);
467free_msg:
468 nlmsg_free(msg);
469 return -EMSGSIZE;
470}
471
472int nfc_genl_dep_link_down_event(struct nfc_dev *dev)
473{
474 struct sk_buff *msg;
475 void *hdr;
476
477 pr_debug("DEP link is down\n");
478
Thomas Graf58050fc2012-06-28 03:57:45 +0000479 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100480 if (!msg)
481 return -ENOMEM;
482
483 hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100484 NFC_CMD_DEP_LINK_DOWN);
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100485 if (!hdr)
486 goto free_msg;
487
David S. Miller1e6428d2012-03-29 23:23:57 -0400488 if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
489 goto nla_put_failure;
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100490
491 genlmsg_end(msg, hdr);
492
493 genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
494
495 return 0;
496
497nla_put_failure:
498 genlmsg_cancel(msg, hdr);
499free_msg:
500 nlmsg_free(msg);
501 return -EMSGSIZE;
502}
503
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300504static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info)
505{
506 struct sk_buff *msg;
507 struct nfc_dev *dev;
508 u32 idx;
509 int rc = -ENOBUFS;
510
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300511 if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
512 return -EINVAL;
513
514 idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
515
516 dev = nfc_get_device(idx);
517 if (!dev)
518 return -ENODEV;
519
Thomas Graf58050fc2012-06-28 03:57:45 +0000520 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300521 if (!msg) {
522 rc = -ENOMEM;
523 goto out_putdev;
524 }
525
Eric W. Biederman15e47302012-09-07 20:12:54 +0000526 rc = nfc_genl_send_device(msg, dev, info->snd_portid, info->snd_seq,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100527 NULL, 0);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300528 if (rc < 0)
529 goto out_free;
530
531 nfc_put_device(dev);
532
533 return genlmsg_reply(msg, info);
534
535out_free:
536 nlmsg_free(msg);
537out_putdev:
538 nfc_put_device(dev);
539 return rc;
540}
541
Ilan Elias8b3fe7b2011-09-18 11:19:33 +0300542static int nfc_genl_dev_up(struct sk_buff *skb, struct genl_info *info)
543{
544 struct nfc_dev *dev;
545 int rc;
546 u32 idx;
547
Ilan Elias8b3fe7b2011-09-18 11:19:33 +0300548 if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
549 return -EINVAL;
550
551 idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
552
553 dev = nfc_get_device(idx);
554 if (!dev)
555 return -ENODEV;
556
557 rc = nfc_dev_up(dev);
558
559 nfc_put_device(dev);
560 return rc;
561}
562
563static int nfc_genl_dev_down(struct sk_buff *skb, struct genl_info *info)
564{
565 struct nfc_dev *dev;
566 int rc;
567 u32 idx;
568
Ilan Elias8b3fe7b2011-09-18 11:19:33 +0300569 if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
570 return -EINVAL;
571
572 idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
573
574 dev = nfc_get_device(idx);
575 if (!dev)
576 return -ENODEV;
577
578 rc = nfc_dev_down(dev);
579
580 nfc_put_device(dev);
581 return rc;
582}
583
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300584static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
585{
586 struct nfc_dev *dev;
587 int rc;
588 u32 idx;
Samuel Ortizfe7c5802012-05-15 15:57:06 +0200589 u32 im_protocols = 0, tm_protocols = 0;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300590
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100591 pr_debug("Poll start\n");
592
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300593 if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
Samuel Ortizfe7c5802012-05-15 15:57:06 +0200594 ((!info->attrs[NFC_ATTR_IM_PROTOCOLS] &&
595 !info->attrs[NFC_ATTR_PROTOCOLS]) &&
Szymon Janc0f450772012-10-17 15:23:39 +0200596 !info->attrs[NFC_ATTR_TM_PROTOCOLS]))
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300597 return -EINVAL;
598
599 idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
Samuel Ortizfe7c5802012-05-15 15:57:06 +0200600
601 if (info->attrs[NFC_ATTR_TM_PROTOCOLS])
602 tm_protocols = nla_get_u32(info->attrs[NFC_ATTR_TM_PROTOCOLS]);
Samuel Ortizfe7c5802012-05-15 15:57:06 +0200603
604 if (info->attrs[NFC_ATTR_IM_PROTOCOLS])
605 im_protocols = nla_get_u32(info->attrs[NFC_ATTR_IM_PROTOCOLS]);
Samuel Ortiz5e50ee32012-05-31 11:48:58 +0200606 else if (info->attrs[NFC_ATTR_PROTOCOLS])
607 im_protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300608
609 dev = nfc_get_device(idx);
610 if (!dev)
611 return -ENODEV;
612
613 mutex_lock(&dev->genl_data.genl_data_mutex);
614
Samuel Ortizfe7c5802012-05-15 15:57:06 +0200615 rc = nfc_start_poll(dev, im_protocols, tm_protocols);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300616 if (!rc)
Eric W. Biederman15e47302012-09-07 20:12:54 +0000617 dev->genl_data.poll_req_portid = info->snd_portid;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300618
619 mutex_unlock(&dev->genl_data.genl_data_mutex);
620
621 nfc_put_device(dev);
622 return rc;
623}
624
625static int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info)
626{
627 struct nfc_dev *dev;
628 int rc;
629 u32 idx;
630
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300631 if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
632 return -EINVAL;
633
634 idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
635
636 dev = nfc_get_device(idx);
637 if (!dev)
638 return -ENODEV;
639
Samuel Ortiza831b912012-06-28 16:41:57 +0200640 device_lock(&dev->dev);
641
642 if (!dev->polling) {
643 device_unlock(&dev->dev);
644 return -EINVAL;
645 }
646
647 device_unlock(&dev->dev);
648
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300649 mutex_lock(&dev->genl_data.genl_data_mutex);
650
Eric W. Biederman15e47302012-09-07 20:12:54 +0000651 if (dev->genl_data.poll_req_portid != info->snd_portid) {
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300652 rc = -EBUSY;
653 goto out;
654 }
655
656 rc = nfc_stop_poll(dev);
Eric W. Biederman15e47302012-09-07 20:12:54 +0000657 dev->genl_data.poll_req_portid = 0;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300658
659out:
660 mutex_unlock(&dev->genl_data.genl_data_mutex);
661 nfc_put_device(dev);
662 return rc;
663}
664
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100665static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info)
666{
667 struct nfc_dev *dev;
668 int rc, tgt_idx;
669 u32 idx;
Samuel Ortiz47807d32012-03-05 01:03:50 +0100670 u8 comm;
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100671
672 pr_debug("DEP link up\n");
673
674 if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
Samuel Ortiz47807d32012-03-05 01:03:50 +0100675 !info->attrs[NFC_ATTR_COMM_MODE])
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100676 return -EINVAL;
677
678 idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
679 if (!info->attrs[NFC_ATTR_TARGET_INDEX])
680 tgt_idx = NFC_TARGET_IDX_ANY;
681 else
682 tgt_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
683
684 comm = nla_get_u8(info->attrs[NFC_ATTR_COMM_MODE]);
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100685
686 if (comm != NFC_COMM_ACTIVE && comm != NFC_COMM_PASSIVE)
687 return -EINVAL;
688
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100689 dev = nfc_get_device(idx);
690 if (!dev)
691 return -ENODEV;
692
Samuel Ortiz47807d32012-03-05 01:03:50 +0100693 rc = nfc_dep_link_up(dev, tgt_idx, comm);
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100694
695 nfc_put_device(dev);
696
697 return rc;
698}
699
700static int nfc_genl_dep_link_down(struct sk_buff *skb, struct genl_info *info)
701{
702 struct nfc_dev *dev;
703 int rc;
704 u32 idx;
705
706 if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
707 return -EINVAL;
708
709 idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
710
711 dev = nfc_get_device(idx);
712 if (!dev)
713 return -ENODEV;
714
715 rc = nfc_dep_link_down(dev);
716
717 nfc_put_device(dev);
718 return rc;
719}
720
Thierry Escande52feb442012-10-17 14:43:39 +0200721static int nfc_genl_send_params(struct sk_buff *msg,
722 struct nfc_llcp_local *local,
723 u32 portid, u32 seq)
724{
725 void *hdr;
726
727 hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, 0,
728 NFC_CMD_LLC_GET_PARAMS);
729 if (!hdr)
730 return -EMSGSIZE;
731
732 if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, local->dev->idx) ||
733 nla_put_u8(msg, NFC_ATTR_LLC_PARAM_LTO, local->lto) ||
734 nla_put_u8(msg, NFC_ATTR_LLC_PARAM_RW, local->rw) ||
735 nla_put_u16(msg, NFC_ATTR_LLC_PARAM_MIUX, be16_to_cpu(local->miux)))
736 goto nla_put_failure;
737
738 return genlmsg_end(msg, hdr);
739
740nla_put_failure:
741
742 genlmsg_cancel(msg, hdr);
743 return -EMSGSIZE;
744}
745
746static int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info)
747{
748 struct nfc_dev *dev;
749 struct nfc_llcp_local *local;
750 int rc = 0;
751 struct sk_buff *msg = NULL;
752 u32 idx;
753
754 if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
755 return -EINVAL;
756
757 idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
758
759 dev = nfc_get_device(idx);
760 if (!dev)
761 return -ENODEV;
762
763 device_lock(&dev->dev);
764
765 local = nfc_llcp_find_local(dev);
766 if (!local) {
767 rc = -ENODEV;
768 goto exit;
769 }
770
771 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
772 if (!msg) {
773 rc = -ENOMEM;
774 goto exit;
775 }
776
777 rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq);
778
779exit:
780 device_unlock(&dev->dev);
781
782 nfc_put_device(dev);
783
784 if (rc < 0) {
785 if (msg)
786 nlmsg_free(msg);
787
788 return rc;
789 }
790
791 return genlmsg_reply(msg, info);
792}
793
794static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info)
795{
796 struct nfc_dev *dev;
797 struct nfc_llcp_local *local;
798 u8 rw = 0;
799 u16 miux = 0;
800 u32 idx;
801 int rc = 0;
802
803 if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
804 (!info->attrs[NFC_ATTR_LLC_PARAM_LTO] &&
805 !info->attrs[NFC_ATTR_LLC_PARAM_RW] &&
806 !info->attrs[NFC_ATTR_LLC_PARAM_MIUX]))
807 return -EINVAL;
808
809 if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) {
810 rw = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_RW]);
811
812 if (rw > LLCP_MAX_RW)
813 return -EINVAL;
814 }
815
816 if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) {
817 miux = nla_get_u16(info->attrs[NFC_ATTR_LLC_PARAM_MIUX]);
818
819 if (miux > LLCP_MAX_MIUX)
820 return -EINVAL;
821 }
822
823 idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
824
825 dev = nfc_get_device(idx);
826 if (!dev)
827 return -ENODEV;
828
829 device_lock(&dev->dev);
830
831 local = nfc_llcp_find_local(dev);
832 if (!local) {
833 nfc_put_device(dev);
834 rc = -ENODEV;
835 goto exit;
836 }
837
838 if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) {
839 if (dev->dep_link_up) {
840 rc = -EINPROGRESS;
841 goto exit;
842 }
843
844 local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]);
845 }
846
847 if (info->attrs[NFC_ATTR_LLC_PARAM_RW])
848 local->rw = rw;
849
850 if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX])
851 local->miux = cpu_to_be16(miux);
852
853exit:
854 device_unlock(&dev->dev);
855
856 nfc_put_device(dev);
857
858 return rc;
859}
860
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300861static struct genl_ops nfc_genl_ops[] = {
862 {
863 .cmd = NFC_CMD_GET_DEVICE,
864 .doit = nfc_genl_get_device,
865 .dumpit = nfc_genl_dump_devices,
866 .done = nfc_genl_dump_devices_done,
867 .policy = nfc_genl_policy,
868 },
869 {
Ilan Elias8b3fe7b2011-09-18 11:19:33 +0300870 .cmd = NFC_CMD_DEV_UP,
871 .doit = nfc_genl_dev_up,
872 .policy = nfc_genl_policy,
873 },
874 {
875 .cmd = NFC_CMD_DEV_DOWN,
876 .doit = nfc_genl_dev_down,
877 .policy = nfc_genl_policy,
878 },
879 {
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300880 .cmd = NFC_CMD_START_POLL,
881 .doit = nfc_genl_start_poll,
882 .policy = nfc_genl_policy,
883 },
884 {
885 .cmd = NFC_CMD_STOP_POLL,
886 .doit = nfc_genl_stop_poll,
887 .policy = nfc_genl_policy,
888 },
889 {
Samuel Ortiz1ed28f62011-12-14 16:43:09 +0100890 .cmd = NFC_CMD_DEP_LINK_UP,
891 .doit = nfc_genl_dep_link_up,
892 .policy = nfc_genl_policy,
893 },
894 {
895 .cmd = NFC_CMD_DEP_LINK_DOWN,
896 .doit = nfc_genl_dep_link_down,
897 .policy = nfc_genl_policy,
898 },
899 {
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300900 .cmd = NFC_CMD_GET_TARGET,
901 .dumpit = nfc_genl_dump_targets,
902 .done = nfc_genl_dump_targets_done,
903 .policy = nfc_genl_policy,
904 },
Thierry Escande52feb442012-10-17 14:43:39 +0200905 {
906 .cmd = NFC_CMD_LLC_GET_PARAMS,
907 .doit = nfc_genl_llc_get_params,
908 .policy = nfc_genl_policy,
909 },
910 {
911 .cmd = NFC_CMD_LLC_SET_PARAMS,
912 .doit = nfc_genl_llc_set_params,
913 .policy = nfc_genl_policy,
914 },
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300915};
916
Szymon Janc3c0cc8a2012-09-26 14:17:12 +0200917
918struct urelease_work {
919 struct work_struct w;
John W. Linvillec4876062012-09-28 11:11:16 -0400920 int portid;
Szymon Janc3c0cc8a2012-09-26 14:17:12 +0200921};
922
923static void nfc_urelease_event_work(struct work_struct *work)
924{
925 struct urelease_work *w = container_of(work, struct urelease_work, w);
926 struct class_dev_iter iter;
927 struct nfc_dev *dev;
928
John W. Linvillec4876062012-09-28 11:11:16 -0400929 pr_debug("portid %d\n", w->portid);
Szymon Janc3c0cc8a2012-09-26 14:17:12 +0200930
931 mutex_lock(&nfc_devlist_mutex);
932
933 nfc_device_iter_init(&iter);
934 dev = nfc_device_iter_next(&iter);
935
936 while (dev) {
937 mutex_lock(&dev->genl_data.genl_data_mutex);
938
John W. Linvillec4876062012-09-28 11:11:16 -0400939 if (dev->genl_data.poll_req_portid == w->portid) {
Szymon Janc3c0cc8a2012-09-26 14:17:12 +0200940 nfc_stop_poll(dev);
John W. Linvillec4876062012-09-28 11:11:16 -0400941 dev->genl_data.poll_req_portid = 0;
Szymon Janc3c0cc8a2012-09-26 14:17:12 +0200942 }
943
944 mutex_unlock(&dev->genl_data.genl_data_mutex);
945
946 dev = nfc_device_iter_next(&iter);
947 }
948
949 nfc_device_iter_exit(&iter);
950
951 mutex_unlock(&nfc_devlist_mutex);
952
953 kfree(w);
954}
955
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300956static int nfc_genl_rcv_nl_event(struct notifier_block *this,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +0100957 unsigned long event, void *ptr)
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300958{
959 struct netlink_notify *n = ptr;
Szymon Janc3c0cc8a2012-09-26 14:17:12 +0200960 struct urelease_work *w;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300961
962 if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
963 goto out;
964
Eric W. Biederman15e47302012-09-07 20:12:54 +0000965 pr_debug("NETLINK_URELEASE event from id %d\n", n->portid);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300966
Szymon Janc3c0cc8a2012-09-26 14:17:12 +0200967 w = kmalloc(sizeof(*w), GFP_ATOMIC);
968 if (w) {
969 INIT_WORK((struct work_struct *) w, nfc_urelease_event_work);
John W. Linvillec4876062012-09-28 11:11:16 -0400970 w->portid = n->portid;
Szymon Janc3c0cc8a2012-09-26 14:17:12 +0200971 schedule_work((struct work_struct *) w);
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300972 }
973
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300974out:
975 return NOTIFY_DONE;
976}
977
978void nfc_genl_data_init(struct nfc_genl_data *genl_data)
979{
Eric W. Biederman15e47302012-09-07 20:12:54 +0000980 genl_data->poll_req_portid = 0;
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -0300981 mutex_init(&genl_data->genl_data_mutex);
982}
983
984void nfc_genl_data_exit(struct nfc_genl_data *genl_data)
985{
986 mutex_destroy(&genl_data->genl_data_mutex);
987}
988
989static struct notifier_block nl_notifier = {
990 .notifier_call = nfc_genl_rcv_nl_event,
991};
992
993/**
994 * nfc_genl_init() - Initialize netlink interface
995 *
996 * This initialization function registers the nfc netlink family.
997 */
998int __init nfc_genl_init(void)
999{
1000 int rc;
1001
1002 rc = genl_register_family_with_ops(&nfc_genl_family, nfc_genl_ops,
Samuel Ortiz0a40acb2012-03-05 01:03:53 +01001003 ARRAY_SIZE(nfc_genl_ops));
Lauro Ramos Venancio4d12b8b2011-07-01 19:31:34 -03001004 if (rc)
1005 return rc;
1006
1007 rc = genl_register_mc_group(&nfc_genl_family, &nfc_genl_event_mcgrp);
1008
1009 netlink_register_notifier(&nl_notifier);
1010
1011 return rc;
1012}
1013
1014/**
1015 * nfc_genl_exit() - Deinitialize netlink interface
1016 *
1017 * This exit function unregisters the nfc netlink family.
1018 */
1019void nfc_genl_exit(void)
1020{
1021 netlink_unregister_notifier(&nl_notifier);
1022 genl_unregister_family(&nfc_genl_family);
1023}