aboutsummaryrefslogtreecommitdiffstats
path: root/net/nfc
diff options
context:
space:
mode:
authorVincent Cuissard <cuissard@marvell.com>2015-06-11 05:25:47 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2015-06-11 17:37:37 -0400
commit9961127d4bce6325e9a0b0fb105e0c85a6c62cb7 (patch)
tree33d59530eba0b11a835b869af86ee69e17ea7e34 /net/nfc
parent4a2b947f56b33cde68d6e0543160ea09ce651fd9 (diff)
NFC: nci: add generic uart support
Some NFC controller supports UART as host interface. As with SPI, a lot of code can be shared between vendor drivers. This patch add the generic support of UART and provides some extension API for vendor specific needs. This code is strongly inspired by the Bluetooth HCI ldisc implementation. NCI UART vendor drivers will have to register themselves to this layer via nci_uart_register. Underlying tty will have to be configured from user land thanks to an ioctl. Signed-off-by: Vincent Cuissard <cuissard@marvell.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'net/nfc')
-rw-r--r--net/nfc/nci/Kconfig7
-rw-r--r--net/nfc/nci/Makefile3
-rw-r--r--net/nfc/nci/uart.c495
3 files changed, 505 insertions, 0 deletions
diff --git a/net/nfc/nci/Kconfig b/net/nfc/nci/Kconfig
index a4f1e42e3481..901c1ddba841 100644
--- a/net/nfc/nci/Kconfig
+++ b/net/nfc/nci/Kconfig
@@ -19,3 +19,10 @@ config NFC_NCI_SPI
19 an NFC Controller (NFCC) and a Device Host (DH). 19 an NFC Controller (NFCC) and a Device Host (DH).
20 20
21 Say yes if you use an NCI driver that requires SPI link layer. 21 Say yes if you use an NCI driver that requires SPI link layer.
22
23config NFC_NCI_UART
24 depends on NFC_NCI && TTY
25 tristate "NCI over UART protocol support"
26 default n
27 help
28 Say yes if you use an NCI driver that requires UART link layer.
diff --git a/net/nfc/nci/Makefile b/net/nfc/nci/Makefile
index 7ed8949266cc..b4b85b82e988 100644
--- a/net/nfc/nci/Makefile
+++ b/net/nfc/nci/Makefile
@@ -7,3 +7,6 @@ obj-$(CONFIG_NFC_NCI) += nci.o
7nci-objs := core.o data.o lib.o ntf.o rsp.o hci.o 7nci-objs := core.o data.o lib.o ntf.o rsp.o hci.o
8 8
9nci-$(CONFIG_NFC_NCI_SPI) += spi.o 9nci-$(CONFIG_NFC_NCI_SPI) += spi.o
10
11nci_uart-y += uart.o
12obj-$(CONFIG_NFC_NCI_UART) += nci_uart.o
diff --git a/net/nfc/nci/uart.c b/net/nfc/nci/uart.c
new file mode 100644
index 000000000000..70c279543074
--- /dev/null
+++ b/net/nfc/nci/uart.c
@@ -0,0 +1,495 @@
1/*
2 * Copyright (C) 2015, Marvell International Ltd.
3 *
4 * This software file (the "File") is distributed by Marvell International
5 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
6 * (the "License"). You may use, redistribute and/or modify this File in
7 * accordance with the terms and conditions of the License, a copy of which
8 * is available on the worldwide web at
9 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
10 *
11 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
12 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
13 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
14 * this warranty disclaimer.
15
16 */
17
18/* Inspired (hugely) by HCI LDISC implementation in Bluetooth.
19 *
20 * Copyright (C) 2000-2001 Qualcomm Incorporated
21 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
22 * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
23 */
24
25#include <linux/module.h>
26
27#include <linux/kernel.h>
28#include <linux/init.h>
29#include <linux/types.h>
30#include <linux/fcntl.h>
31#include <linux/interrupt.h>
32#include <linux/ptrace.h>
33#include <linux/poll.h>
34
35#include <linux/slab.h>
36#include <linux/tty.h>
37#include <linux/errno.h>
38#include <linux/string.h>
39#include <linux/signal.h>
40#include <linux/ioctl.h>
41#include <linux/skbuff.h>
42
43#include <net/nfc/nci.h>
44#include <net/nfc/nci_core.h>
45
46/* TX states */
47#define NCI_UART_SENDING 1
48#define NCI_UART_TX_WAKEUP 2
49
50static struct nci_uart *nci_uart_drivers[NCI_UART_DRIVER_MAX];
51
52static inline struct sk_buff *nci_uart_dequeue(struct nci_uart *nu)
53{
54 struct sk_buff *skb = nu->tx_skb;
55
56 if (!skb)
57 skb = skb_dequeue(&nu->tx_q);
58 else
59 nu->tx_skb = NULL;
60
61 return skb;
62}
63
64static inline int nci_uart_queue_empty(struct nci_uart *nu)
65{
66 if (nu->tx_skb)
67 return 0;
68
69 return skb_queue_empty(&nu->tx_q);
70}
71
72static int nci_uart_tx_wakeup(struct nci_uart *nu)
73{
74 if (test_and_set_bit(NCI_UART_SENDING, &nu->tx_state)) {
75 set_bit(NCI_UART_TX_WAKEUP, &nu->tx_state);
76 return 0;
77 }
78
79 schedule_work(&nu->write_work);
80
81 return 0;
82}
83
84static void nci_uart_write_work(struct work_struct *work)
85{
86 struct nci_uart *nu = container_of(work, struct nci_uart, write_work);
87 struct tty_struct *tty = nu->tty;
88 struct sk_buff *skb;
89
90restart:
91 clear_bit(NCI_UART_TX_WAKEUP, &nu->tx_state);
92
93 if (nu->ops.tx_start)
94 nu->ops.tx_start(nu);
95
96 while ((skb = nci_uart_dequeue(nu))) {
97 int len;
98
99 set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
100 len = tty->ops->write(tty, skb->data, skb->len);
101 skb_pull(skb, len);
102 if (skb->len) {
103 nu->tx_skb = skb;
104 break;
105 }
106 kfree_skb(skb);
107 }
108
109 if (test_bit(NCI_UART_TX_WAKEUP, &nu->tx_state))
110 goto restart;
111
112 if (nu->ops.tx_done && nci_uart_queue_empty(nu))
113 nu->ops.tx_done(nu);
114
115 clear_bit(NCI_UART_SENDING, &nu->tx_state);
116}
117
118static int nci_uart_set_driver(struct tty_struct *tty, unsigned int driver)
119{
120 struct nci_uart *nu = NULL;
121 int ret;
122
123 if (driver >= NCI_UART_DRIVER_MAX)
124 return -EINVAL;
125
126 if (!nci_uart_drivers[driver])
127 return -ENOENT;
128
129 nu = kzalloc(sizeof(*nu), GFP_KERNEL);
130 if (!nu)
131 return -ENOMEM;
132
133 memcpy(nu, nci_uart_drivers[driver], sizeof(struct nci_uart));
134 nu->tty = tty;
135 tty->disc_data = nu;
136 skb_queue_head_init(&nu->tx_q);
137 INIT_WORK(&nu->write_work, nci_uart_write_work);
138 spin_lock_init(&nu->rx_lock);
139
140 ret = nu->ops.open(nu);
141 if (ret) {
142 tty->disc_data = NULL;
143 kfree(nu);
144 } else if (!try_module_get(nu->owner)) {
145 nu->ops.close(nu);
146 tty->disc_data = NULL;
147 kfree(nu);
148 return -ENOENT;
149 }
150 return ret;
151}
152
153/* ------ LDISC part ------ */
154
155/* nci_uart_tty_open
156 *
157 * Called when line discipline changed to NCI_UART.
158 *
159 * Arguments:
160 * tty pointer to tty info structure
161 * Return Value:
162 * 0 if success, otherwise error code
163 */
164static int nci_uart_tty_open(struct tty_struct *tty)
165{
166 /* Error if the tty has no write op instead of leaving an exploitable
167 * hole
168 */
169 if (!tty->ops->write)
170 return -EOPNOTSUPP;
171
172 tty->disc_data = NULL;
173 tty->receive_room = 65536;
174
175 /* Flush any pending characters in the driver and line discipline. */
176
177 /* FIXME: why is this needed. Note don't use ldisc_ref here as the
178 * open path is before the ldisc is referencable.
179 */
180
181 if (tty->ldisc->ops->flush_buffer)
182 tty->ldisc->ops->flush_buffer(tty);
183 tty_driver_flush_buffer(tty);
184
185 return 0;
186}
187
188/* nci_uart_tty_close()
189 *
190 * Called when the line discipline is changed to something
191 * else, the tty is closed, or the tty detects a hangup.
192 */
193static void nci_uart_tty_close(struct tty_struct *tty)
194{
195 struct nci_uart *nu = (void *)tty->disc_data;
196
197 /* Detach from the tty */
198 tty->disc_data = NULL;
199
200 if (!nu)
201 return;
202
203 if (nu->tx_skb)
204 kfree_skb(nu->tx_skb);
205 if (nu->rx_skb)
206 kfree_skb(nu->rx_skb);
207
208 skb_queue_purge(&nu->tx_q);
209
210 nu->ops.close(nu);
211 nu->tty = NULL;
212 module_put(nu->owner);
213
214 cancel_work_sync(&nu->write_work);
215
216 kfree(nu);
217}
218
219/* nci_uart_tty_wakeup()
220 *
221 * Callback for transmit wakeup. Called when low level
222 * device driver can accept more send data.
223 *
224 * Arguments: tty pointer to associated tty instance data
225 * Return Value: None
226 */
227static void nci_uart_tty_wakeup(struct tty_struct *tty)
228{
229 struct nci_uart *nu = (void *)tty->disc_data;
230
231 if (!nu)
232 return;
233
234 clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
235
236 if (tty != nu->tty)
237 return;
238
239 nci_uart_tx_wakeup(nu);
240}
241
242/* nci_uart_tty_receive()
243 *
244 * Called by tty low level driver when receive data is
245 * available.
246 *
247 * Arguments: tty pointer to tty isntance data
248 * data pointer to received data
249 * flags pointer to flags for data
250 * count count of received data in bytes
251 *
252 * Return Value: None
253 */
254static void nci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
255 char *flags, int count)
256{
257 struct nci_uart *nu = (void *)tty->disc_data;
258
259 if (!nu || tty != nu->tty)
260 return;
261
262 spin_lock(&nu->rx_lock);
263 nu->ops.recv_buf(nu, (void *)data, flags, count);
264 spin_unlock(&nu->rx_lock);
265
266 tty_unthrottle(tty);
267}
268
269/* nci_uart_tty_ioctl()
270 *
271 * Process IOCTL system call for the tty device.
272 *
273 * Arguments:
274 *
275 * tty pointer to tty instance data
276 * file pointer to open file object for device
277 * cmd IOCTL command code
278 * arg argument for IOCTL call (cmd dependent)
279 *
280 * Return Value: Command dependent
281 */
282static int nci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
283 unsigned int cmd, unsigned long arg)
284{
285 struct nci_uart *nu = (void *)tty->disc_data;
286 int err = 0;
287
288 switch (cmd) {
289 case NCIUARTSETDRIVER:
290 if (!nu)
291 return nci_uart_set_driver(tty, (unsigned int)arg);
292 else
293 return -EBUSY;
294 break;
295 default:
296 err = n_tty_ioctl_helper(tty, file, cmd, arg);
297 break;
298 }
299
300 return err;
301}
302
303/* We don't provide read/write/poll interface for user space. */
304static ssize_t nci_uart_tty_read(struct tty_struct *tty, struct file *file,
305 unsigned char __user *buf, size_t nr)
306{
307 return 0;
308}
309
310static ssize_t nci_uart_tty_write(struct tty_struct *tty, struct file *file,
311 const unsigned char *data, size_t count)
312{
313 return 0;
314}
315
316static unsigned int nci_uart_tty_poll(struct tty_struct *tty,
317 struct file *filp, poll_table *wait)
318{
319 return 0;
320}
321
322static int nci_uart_send(struct nci_uart *nu, struct sk_buff *skb)
323{
324 /* Queue TX packet */
325 skb_queue_tail(&nu->tx_q, skb);
326
327 /* Try to start TX (if possible) */
328 nci_uart_tx_wakeup(nu);
329
330 return 0;
331}
332
333/* -- Default recv_buf handler --
334 *
335 * This handler supposes that NCI frames are sent over UART link without any
336 * framing. It reads NCI header, retrieve the packet size and once all packet
337 * bytes are received it passes it to nci_uart driver for processing.
338 */
339static int nci_uart_default_recv_buf(struct nci_uart *nu, const u8 *data,
340 char *flags, int count)
341{
342 int chunk_len;
343
344 if (!nu->ndev) {
345 nfc_err(nu->tty->dev,
346 "receive data from tty but no NCI dev is attached yet, drop buffer\n");
347 return 0;
348 }
349
350 /* Decode all incoming data in packets
351 * and enqueue then for processing.
352 */
353 while (count > 0) {
354 /* If this is the first data of a packet, allocate a buffer */
355 if (!nu->rx_skb) {
356 nu->rx_packet_len = -1;
357 nu->rx_skb = nci_skb_alloc(nu->ndev,
358 NCI_MAX_PACKET_SIZE,
359 GFP_KERNEL);
360 if (!nu->rx_skb)
361 return -ENOMEM;
362 }
363
364 /* Eat byte after byte till full packet header is received */
365 if (nu->rx_skb->len < NCI_CTRL_HDR_SIZE) {
366 *skb_put(nu->rx_skb, 1) = *data++;
367 --count;
368 continue;
369 }
370
371 /* Header was received but packet len was not read */
372 if (nu->rx_packet_len < 0)
373 nu->rx_packet_len = NCI_CTRL_HDR_SIZE +
374 nci_plen(nu->rx_skb->data);
375
376 /* Compute how many bytes are missing and how many bytes can
377 * be consumed.
378 */
379 chunk_len = nu->rx_packet_len - nu->rx_skb->len;
380 if (count < chunk_len)
381 chunk_len = count;
382 memcpy(skb_put(nu->rx_skb, chunk_len), data, chunk_len);
383 data += chunk_len;
384 count -= chunk_len;
385
386 /* Chcek if packet is fully received */
387 if (nu->rx_packet_len == nu->rx_skb->len) {
388 /* Pass RX packet to driver */
389 if (nu->ops.recv(nu, nu->rx_skb) != 0)
390 nfc_err(nu->tty->dev, "corrupted RX packet\n");
391 /* Next packet will be a new one */
392 nu->rx_skb = NULL;
393 }
394 }
395
396 return 0;
397}
398
399/* -- Default recv handler -- */
400static int nci_uart_default_recv(struct nci_uart *nu, struct sk_buff *skb)
401{
402 return nci_recv_frame(nu->ndev, skb);
403}
404
405int nci_uart_register(struct nci_uart *nu)
406{
407 if (!nu || !nu->ops.open ||
408 !nu->ops.recv || !nu->ops.close)
409 return -EINVAL;
410
411 /* Set the send callback */
412 nu->ops.send = nci_uart_send;
413
414 /* Install default handlers if not overridden */
415 if (!nu->ops.recv_buf)
416 nu->ops.recv_buf = nci_uart_default_recv_buf;
417 if (!nu->ops.recv)
418 nu->ops.recv = nci_uart_default_recv;
419
420 /* Add this driver in the driver list */
421 if (!nci_uart_drivers[nu->driver]) {
422 pr_err("driver %d is already registered\n", nu->driver);
423 return -EBUSY;
424 }
425 nci_uart_drivers[nu->driver] = nu;
426
427 pr_info("NCI uart driver '%s [%d]' registered\n", nu->name, nu->driver);
428
429 return 0;
430}
431EXPORT_SYMBOL_GPL(nci_uart_register);
432
433void nci_uart_unregister(struct nci_uart *nu)
434{
435 pr_info("NCI uart driver '%s [%d]' unregistered\n", nu->name,
436 nu->driver);
437
438 /* Remove this driver from the driver list */
439 nci_uart_drivers[nu->driver] = NULL;
440}
441EXPORT_SYMBOL_GPL(nci_uart_unregister);
442
443void nci_uart_set_config(struct nci_uart *nu, int baudrate, int flow_ctrl)
444{
445 struct ktermios new_termios;
446
447 if (!nu->tty)
448 return;
449
450 down_read(&nu->tty->termios_rwsem);
451 new_termios = nu->tty->termios;
452 up_read(&nu->tty->termios_rwsem);
453 tty_termios_encode_baud_rate(&new_termios, baudrate, baudrate);
454
455 if (flow_ctrl)
456 new_termios.c_cflag |= CRTSCTS;
457 else
458 new_termios.c_cflag &= ~CRTSCTS;
459
460 tty_set_termios(nu->tty, &new_termios);
461}
462EXPORT_SYMBOL_GPL(nci_uart_set_config);
463
464static struct tty_ldisc_ops nci_uart_ldisc = {
465 .magic = TTY_LDISC_MAGIC,
466 .owner = THIS_MODULE,
467 .name = "n_nci",
468 .open = nci_uart_tty_open,
469 .close = nci_uart_tty_close,
470 .read = nci_uart_tty_read,
471 .write = nci_uart_tty_write,
472 .poll = nci_uart_tty_poll,
473 .receive_buf = nci_uart_tty_receive,
474 .write_wakeup = nci_uart_tty_wakeup,
475 .ioctl = nci_uart_tty_ioctl,
476};
477
478static int __init nci_uart_init(void)
479{
480 memset(nci_uart_drivers, 0, sizeof(nci_uart_drivers));
481 return tty_register_ldisc(N_NCI, &nci_uart_ldisc);
482}
483
484static void __exit nci_uart_exit(void)
485{
486 tty_unregister_ldisc(N_NCI);
487}
488
489module_init(nci_uart_init);
490module_exit(nci_uart_exit);
491
492MODULE_AUTHOR("Marvell International Ltd.");
493MODULE_DESCRIPTION("NFC NCI UART driver");
494MODULE_LICENSE("GPL");
495MODULE_ALIAS_LDISC(N_NCI);