linux/net/nfc/nci/uart.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2015, Marvell International Ltd.
   4 *
   5 * Inspired (hugely) by HCI LDISC implementation in Bluetooth.
   6 *
   7 *  Copyright (C) 2000-2001  Qualcomm Incorporated
   8 *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
   9 *  Copyright (C) 2004-2005  Marcel Holtmann <marcel@holtmann.org>
  10 */
  11
  12#include <linux/module.h>
  13
  14#include <linux/kernel.h>
  15#include <linux/init.h>
  16#include <linux/types.h>
  17#include <linux/fcntl.h>
  18#include <linux/interrupt.h>
  19#include <linux/ptrace.h>
  20#include <linux/poll.h>
  21
  22#include <linux/slab.h>
  23#include <linux/tty.h>
  24#include <linux/errno.h>
  25#include <linux/string.h>
  26#include <linux/signal.h>
  27#include <linux/ioctl.h>
  28#include <linux/skbuff.h>
  29
  30#include <net/nfc/nci.h>
  31#include <net/nfc/nci_core.h>
  32
  33/* TX states  */
  34#define NCI_UART_SENDING        1
  35#define NCI_UART_TX_WAKEUP      2
  36
  37static struct nci_uart *nci_uart_drivers[NCI_UART_DRIVER_MAX];
  38
  39static inline struct sk_buff *nci_uart_dequeue(struct nci_uart *nu)
  40{
  41        struct sk_buff *skb = nu->tx_skb;
  42
  43        if (!skb)
  44                skb = skb_dequeue(&nu->tx_q);
  45        else
  46                nu->tx_skb = NULL;
  47
  48        return skb;
  49}
  50
  51static inline int nci_uart_queue_empty(struct nci_uart *nu)
  52{
  53        if (nu->tx_skb)
  54                return 0;
  55
  56        return skb_queue_empty(&nu->tx_q);
  57}
  58
  59static int nci_uart_tx_wakeup(struct nci_uart *nu)
  60{
  61        if (test_and_set_bit(NCI_UART_SENDING, &nu->tx_state)) {
  62                set_bit(NCI_UART_TX_WAKEUP, &nu->tx_state);
  63                return 0;
  64        }
  65
  66        schedule_work(&nu->write_work);
  67
  68        return 0;
  69}
  70
  71static void nci_uart_write_work(struct work_struct *work)
  72{
  73        struct nci_uart *nu = container_of(work, struct nci_uart, write_work);
  74        struct tty_struct *tty = nu->tty;
  75        struct sk_buff *skb;
  76
  77restart:
  78        clear_bit(NCI_UART_TX_WAKEUP, &nu->tx_state);
  79
  80        if (nu->ops.tx_start)
  81                nu->ops.tx_start(nu);
  82
  83        while ((skb = nci_uart_dequeue(nu))) {
  84                int len;
  85
  86                set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
  87                len = tty->ops->write(tty, skb->data, skb->len);
  88                skb_pull(skb, len);
  89                if (skb->len) {
  90                        nu->tx_skb = skb;
  91                        break;
  92                }
  93                kfree_skb(skb);
  94        }
  95
  96        if (test_bit(NCI_UART_TX_WAKEUP, &nu->tx_state))
  97                goto restart;
  98
  99        if (nu->ops.tx_done && nci_uart_queue_empty(nu))
 100                nu->ops.tx_done(nu);
 101
 102        clear_bit(NCI_UART_SENDING, &nu->tx_state);
 103}
 104
 105static int nci_uart_set_driver(struct tty_struct *tty, unsigned int driver)
 106{
 107        struct nci_uart *nu = NULL;
 108        int ret;
 109
 110        if (driver >= NCI_UART_DRIVER_MAX)
 111                return -EINVAL;
 112
 113        if (!nci_uart_drivers[driver])
 114                return -ENOENT;
 115
 116        nu = kzalloc(sizeof(*nu), GFP_KERNEL);
 117        if (!nu)
 118                return -ENOMEM;
 119
 120        memcpy(nu, nci_uart_drivers[driver], sizeof(struct nci_uart));
 121        nu->tty = tty;
 122        tty->disc_data = nu;
 123        skb_queue_head_init(&nu->tx_q);
 124        INIT_WORK(&nu->write_work, nci_uart_write_work);
 125        spin_lock_init(&nu->rx_lock);
 126
 127        ret = nu->ops.open(nu);
 128        if (ret) {
 129                tty->disc_data = NULL;
 130                kfree(nu);
 131        } else if (!try_module_get(nu->owner)) {
 132                nu->ops.close(nu);
 133                tty->disc_data = NULL;
 134                kfree(nu);
 135                return -ENOENT;
 136        }
 137        return ret;
 138}
 139
 140/* ------ LDISC part ------ */
 141
 142/* nci_uart_tty_open
 143 *
 144 *     Called when line discipline changed to NCI_UART.
 145 *
 146 * Arguments:
 147 *     tty    pointer to tty info structure
 148 * Return Value:
 149 *     0 if success, otherwise error code
 150 */
 151static int nci_uart_tty_open(struct tty_struct *tty)
 152{
 153        /* Error if the tty has no write op instead of leaving an exploitable
 154         * hole
 155         */
 156        if (!tty->ops->write)
 157                return -EOPNOTSUPP;
 158
 159        tty->disc_data = NULL;
 160        tty->receive_room = 65536;
 161
 162        /* Flush any pending characters in the driver */
 163        tty_driver_flush_buffer(tty);
 164
 165        return 0;
 166}
 167
 168/* nci_uart_tty_close()
 169 *
 170 *    Called when the line discipline is changed to something
 171 *    else, the tty is closed, or the tty detects a hangup.
 172 */
 173static void nci_uart_tty_close(struct tty_struct *tty)
 174{
 175        struct nci_uart *nu = (void *)tty->disc_data;
 176
 177        /* Detach from the tty */
 178        tty->disc_data = NULL;
 179
 180        if (!nu)
 181                return;
 182
 183        kfree_skb(nu->tx_skb);
 184        kfree_skb(nu->rx_skb);
 185
 186        skb_queue_purge(&nu->tx_q);
 187
 188        nu->ops.close(nu);
 189        nu->tty = NULL;
 190        module_put(nu->owner);
 191
 192        cancel_work_sync(&nu->write_work);
 193
 194        kfree(nu);
 195}
 196
 197/* nci_uart_tty_wakeup()
 198 *
 199 *    Callback for transmit wakeup. Called when low level
 200 *    device driver can accept more send data.
 201 *
 202 * Arguments:        tty    pointer to associated tty instance data
 203 * Return Value:    None
 204 */
 205static void nci_uart_tty_wakeup(struct tty_struct *tty)
 206{
 207        struct nci_uart *nu = (void *)tty->disc_data;
 208
 209        if (!nu)
 210                return;
 211
 212        clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
 213
 214        if (tty != nu->tty)
 215                return;
 216
 217        nci_uart_tx_wakeup(nu);
 218}
 219
 220/* -- Default recv_buf handler --
 221 *
 222 * This handler supposes that NCI frames are sent over UART link without any
 223 * framing. It reads NCI header, retrieve the packet size and once all packet
 224 * bytes are received it passes it to nci_uart driver for processing.
 225 */
 226static int nci_uart_default_recv_buf(struct nci_uart *nu, const u8 *data,
 227                                     int count)
 228{
 229        int chunk_len;
 230
 231        if (!nu->ndev) {
 232                nfc_err(nu->tty->dev,
 233                        "receive data from tty but no NCI dev is attached yet, drop buffer\n");
 234                return 0;
 235        }
 236
 237        /* Decode all incoming data in packets
 238         * and enqueue then for processing.
 239         */
 240        while (count > 0) {
 241                /* If this is the first data of a packet, allocate a buffer */
 242                if (!nu->rx_skb) {
 243                        nu->rx_packet_len = -1;
 244                        nu->rx_skb = nci_skb_alloc(nu->ndev,
 245                                                   NCI_MAX_PACKET_SIZE,
 246                                                   GFP_ATOMIC);
 247                        if (!nu->rx_skb)
 248                                return -ENOMEM;
 249                }
 250
 251                /* Eat byte after byte till full packet header is received */
 252                if (nu->rx_skb->len < NCI_CTRL_HDR_SIZE) {
 253                        skb_put_u8(nu->rx_skb, *data++);
 254                        --count;
 255                        continue;
 256                }
 257
 258                /* Header was received but packet len was not read */
 259                if (nu->rx_packet_len < 0)
 260                        nu->rx_packet_len = NCI_CTRL_HDR_SIZE +
 261                                nci_plen(nu->rx_skb->data);
 262
 263                /* Compute how many bytes are missing and how many bytes can
 264                 * be consumed.
 265                 */
 266                chunk_len = nu->rx_packet_len - nu->rx_skb->len;
 267                if (count < chunk_len)
 268                        chunk_len = count;
 269                skb_put_data(nu->rx_skb, data, chunk_len);
 270                data += chunk_len;
 271                count -= chunk_len;
 272
 273                /* Check if packet is fully received */
 274                if (nu->rx_packet_len == nu->rx_skb->len) {
 275                        /* Pass RX packet to driver */
 276                        if (nu->ops.recv(nu, nu->rx_skb) != 0)
 277                                nfc_err(nu->tty->dev, "corrupted RX packet\n");
 278                        /* Next packet will be a new one */
 279                        nu->rx_skb = NULL;
 280                }
 281        }
 282
 283        return 0;
 284}
 285
 286/* nci_uart_tty_receive()
 287 *
 288 *     Called by tty low level driver when receive data is
 289 *     available.
 290 *
 291 * Arguments:  tty          pointer to tty instance data
 292 *             data         pointer to received data
 293 *             flags        pointer to flags for data
 294 *             count        count of received data in bytes
 295 *
 296 * Return Value:    None
 297 */
 298static void nci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
 299                                 const char *flags, int count)
 300{
 301        struct nci_uart *nu = (void *)tty->disc_data;
 302
 303        if (!nu || tty != nu->tty)
 304                return;
 305
 306        spin_lock(&nu->rx_lock);
 307        nci_uart_default_recv_buf(nu, data, count);
 308        spin_unlock(&nu->rx_lock);
 309
 310        tty_unthrottle(tty);
 311}
 312
 313/* nci_uart_tty_ioctl()
 314 *
 315 *    Process IOCTL system call for the tty device.
 316 *
 317 * Arguments:
 318 *
 319 *    tty        pointer to tty instance data
 320 *    file       pointer to open file object for device
 321 *    cmd        IOCTL command code
 322 *    arg        argument for IOCTL call (cmd dependent)
 323 *
 324 * Return Value:    Command dependent
 325 */
 326static int nci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
 327                              unsigned int cmd, unsigned long arg)
 328{
 329        struct nci_uart *nu = (void *)tty->disc_data;
 330        int err = 0;
 331
 332        switch (cmd) {
 333        case NCIUARTSETDRIVER:
 334                if (!nu)
 335                        return nci_uart_set_driver(tty, (unsigned int)arg);
 336                else
 337                        return -EBUSY;
 338                break;
 339        default:
 340                err = n_tty_ioctl_helper(tty, cmd, arg);
 341                break;
 342        }
 343
 344        return err;
 345}
 346
 347/* We don't provide read/write/poll interface for user space. */
 348static ssize_t nci_uart_tty_read(struct tty_struct *tty, struct file *file,
 349                                 unsigned char *buf, size_t nr,
 350                                 void **cookie, unsigned long offset)
 351{
 352        return 0;
 353}
 354
 355static ssize_t nci_uart_tty_write(struct tty_struct *tty, struct file *file,
 356                                  const unsigned char *data, size_t count)
 357{
 358        return 0;
 359}
 360
 361static __poll_t nci_uart_tty_poll(struct tty_struct *tty,
 362                                      struct file *filp, poll_table *wait)
 363{
 364        return 0;
 365}
 366
 367static int nci_uart_send(struct nci_uart *nu, struct sk_buff *skb)
 368{
 369        /* Queue TX packet */
 370        skb_queue_tail(&nu->tx_q, skb);
 371
 372        /* Try to start TX (if possible) */
 373        nci_uart_tx_wakeup(nu);
 374
 375        return 0;
 376}
 377
 378int nci_uart_register(struct nci_uart *nu)
 379{
 380        if (!nu || !nu->ops.open ||
 381            !nu->ops.recv || !nu->ops.close)
 382                return -EINVAL;
 383
 384        /* Set the send callback */
 385        nu->ops.send = nci_uart_send;
 386
 387        /* Add this driver in the driver list */
 388        if (nci_uart_drivers[nu->driver]) {
 389                pr_err("driver %d is already registered\n", nu->driver);
 390                return -EBUSY;
 391        }
 392        nci_uart_drivers[nu->driver] = nu;
 393
 394        pr_info("NCI uart driver '%s [%d]' registered\n", nu->name, nu->driver);
 395
 396        return 0;
 397}
 398EXPORT_SYMBOL_GPL(nci_uart_register);
 399
 400void nci_uart_unregister(struct nci_uart *nu)
 401{
 402        pr_info("NCI uart driver '%s [%d]' unregistered\n", nu->name,
 403                nu->driver);
 404
 405        /* Remove this driver from the driver list */
 406        nci_uart_drivers[nu->driver] = NULL;
 407}
 408EXPORT_SYMBOL_GPL(nci_uart_unregister);
 409
 410void nci_uart_set_config(struct nci_uart *nu, int baudrate, int flow_ctrl)
 411{
 412        struct ktermios new_termios;
 413
 414        if (!nu->tty)
 415                return;
 416
 417        down_read(&nu->tty->termios_rwsem);
 418        new_termios = nu->tty->termios;
 419        up_read(&nu->tty->termios_rwsem);
 420        tty_termios_encode_baud_rate(&new_termios, baudrate, baudrate);
 421
 422        if (flow_ctrl)
 423                new_termios.c_cflag |= CRTSCTS;
 424        else
 425                new_termios.c_cflag &= ~CRTSCTS;
 426
 427        tty_set_termios(nu->tty, &new_termios);
 428}
 429EXPORT_SYMBOL_GPL(nci_uart_set_config);
 430
 431static struct tty_ldisc_ops nci_uart_ldisc = {
 432        .owner          = THIS_MODULE,
 433        .num            = N_NCI,
 434        .name           = "n_nci",
 435        .open           = nci_uart_tty_open,
 436        .close          = nci_uart_tty_close,
 437        .read           = nci_uart_tty_read,
 438        .write          = nci_uart_tty_write,
 439        .poll           = nci_uart_tty_poll,
 440        .receive_buf    = nci_uart_tty_receive,
 441        .write_wakeup   = nci_uart_tty_wakeup,
 442        .ioctl          = nci_uart_tty_ioctl,
 443        .compat_ioctl   = nci_uart_tty_ioctl,
 444};
 445
 446static int __init nci_uart_init(void)
 447{
 448        return tty_register_ldisc(&nci_uart_ldisc);
 449}
 450
 451static void __exit nci_uart_exit(void)
 452{
 453        tty_unregister_ldisc(&nci_uart_ldisc);
 454}
 455
 456module_init(nci_uart_init);
 457module_exit(nci_uart_exit);
 458
 459MODULE_AUTHOR("Marvell International Ltd.");
 460MODULE_DESCRIPTION("NFC NCI UART driver");
 461MODULE_LICENSE("GPL");
 462MODULE_ALIAS_LDISC(N_NCI);
 463