18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2015, Marvell International Ltd. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by Marvell International 58c2ecf20Sopenharmony_ci * Ltd. under the terms of the GNU General Public License Version 2, June 1991 68c2ecf20Sopenharmony_ci * (the "License"). You may use, redistribute and/or modify this File in 78c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which 88c2ecf20Sopenharmony_ci * is available on the worldwide web at 98c2ecf20Sopenharmony_ci * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 128c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 138c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 148c2ecf20Sopenharmony_ci * this warranty disclaimer. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* Inspired (hugely) by HCI LDISC implementation in Bluetooth. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Copyright (C) 2000-2001 Qualcomm Incorporated 208c2ecf20Sopenharmony_ci * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> 218c2ecf20Sopenharmony_ci * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org> 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/init.h> 288c2ecf20Sopenharmony_ci#include <linux/types.h> 298c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 308c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 318c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 328c2ecf20Sopenharmony_ci#include <linux/poll.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/slab.h> 358c2ecf20Sopenharmony_ci#include <linux/tty.h> 368c2ecf20Sopenharmony_ci#include <linux/errno.h> 378c2ecf20Sopenharmony_ci#include <linux/string.h> 388c2ecf20Sopenharmony_ci#include <linux/signal.h> 398c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 408c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <net/nfc/nci.h> 438c2ecf20Sopenharmony_ci#include <net/nfc/nci_core.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* TX states */ 468c2ecf20Sopenharmony_ci#define NCI_UART_SENDING 1 478c2ecf20Sopenharmony_ci#define NCI_UART_TX_WAKEUP 2 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic struct nci_uart *nci_uart_drivers[NCI_UART_DRIVER_MAX]; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic inline struct sk_buff *nci_uart_dequeue(struct nci_uart *nu) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct sk_buff *skb = nu->tx_skb; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (!skb) 568c2ecf20Sopenharmony_ci skb = skb_dequeue(&nu->tx_q); 578c2ecf20Sopenharmony_ci else 588c2ecf20Sopenharmony_ci nu->tx_skb = NULL; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return skb; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic inline int nci_uart_queue_empty(struct nci_uart *nu) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci if (nu->tx_skb) 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return skb_queue_empty(&nu->tx_q); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int nci_uart_tx_wakeup(struct nci_uart *nu) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci if (test_and_set_bit(NCI_UART_SENDING, &nu->tx_state)) { 748c2ecf20Sopenharmony_ci set_bit(NCI_UART_TX_WAKEUP, &nu->tx_state); 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci schedule_work(&nu->write_work); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void nci_uart_write_work(struct work_struct *work) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct nci_uart *nu = container_of(work, struct nci_uart, write_work); 868c2ecf20Sopenharmony_ci struct tty_struct *tty = nu->tty; 878c2ecf20Sopenharmony_ci struct sk_buff *skb; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cirestart: 908c2ecf20Sopenharmony_ci clear_bit(NCI_UART_TX_WAKEUP, &nu->tx_state); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (nu->ops.tx_start) 938c2ecf20Sopenharmony_ci nu->ops.tx_start(nu); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci while ((skb = nci_uart_dequeue(nu))) { 968c2ecf20Sopenharmony_ci int len; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 998c2ecf20Sopenharmony_ci len = tty->ops->write(tty, skb->data, skb->len); 1008c2ecf20Sopenharmony_ci skb_pull(skb, len); 1018c2ecf20Sopenharmony_ci if (skb->len) { 1028c2ecf20Sopenharmony_ci nu->tx_skb = skb; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci kfree_skb(skb); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (test_bit(NCI_UART_TX_WAKEUP, &nu->tx_state)) 1098c2ecf20Sopenharmony_ci goto restart; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (nu->ops.tx_done && nci_uart_queue_empty(nu)) 1128c2ecf20Sopenharmony_ci nu->ops.tx_done(nu); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci clear_bit(NCI_UART_SENDING, &nu->tx_state); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int nci_uart_set_driver(struct tty_struct *tty, unsigned int driver) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct nci_uart *nu = NULL; 1208c2ecf20Sopenharmony_ci int ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (driver >= NCI_UART_DRIVER_MAX) 1238c2ecf20Sopenharmony_ci return -EINVAL; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (!nci_uart_drivers[driver]) 1268c2ecf20Sopenharmony_ci return -ENOENT; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci nu = kzalloc(sizeof(*nu), GFP_KERNEL); 1298c2ecf20Sopenharmony_ci if (!nu) 1308c2ecf20Sopenharmony_ci return -ENOMEM; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci memcpy(nu, nci_uart_drivers[driver], sizeof(struct nci_uart)); 1338c2ecf20Sopenharmony_ci nu->tty = tty; 1348c2ecf20Sopenharmony_ci tty->disc_data = nu; 1358c2ecf20Sopenharmony_ci skb_queue_head_init(&nu->tx_q); 1368c2ecf20Sopenharmony_ci INIT_WORK(&nu->write_work, nci_uart_write_work); 1378c2ecf20Sopenharmony_ci spin_lock_init(&nu->rx_lock); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ret = nu->ops.open(nu); 1408c2ecf20Sopenharmony_ci if (ret) { 1418c2ecf20Sopenharmony_ci tty->disc_data = NULL; 1428c2ecf20Sopenharmony_ci kfree(nu); 1438c2ecf20Sopenharmony_ci } else if (!try_module_get(nu->owner)) { 1448c2ecf20Sopenharmony_ci nu->ops.close(nu); 1458c2ecf20Sopenharmony_ci tty->disc_data = NULL; 1468c2ecf20Sopenharmony_ci kfree(nu); 1478c2ecf20Sopenharmony_ci return -ENOENT; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* ------ LDISC part ------ */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* nci_uart_tty_open 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * Called when line discipline changed to NCI_UART. 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Arguments: 1598c2ecf20Sopenharmony_ci * tty pointer to tty info structure 1608c2ecf20Sopenharmony_ci * Return Value: 1618c2ecf20Sopenharmony_ci * 0 if success, otherwise error code 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_cistatic int nci_uart_tty_open(struct tty_struct *tty) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci /* Error if the tty has no write op instead of leaving an exploitable 1668c2ecf20Sopenharmony_ci * hole 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci if (!tty->ops->write) 1698c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci tty->disc_data = NULL; 1728c2ecf20Sopenharmony_ci tty->receive_room = 65536; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* Flush any pending characters in the driver */ 1758c2ecf20Sopenharmony_ci tty_driver_flush_buffer(tty); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* nci_uart_tty_close() 1818c2ecf20Sopenharmony_ci * 1828c2ecf20Sopenharmony_ci * Called when the line discipline is changed to something 1838c2ecf20Sopenharmony_ci * else, the tty is closed, or the tty detects a hangup. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic void nci_uart_tty_close(struct tty_struct *tty) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct nci_uart *nu = (void *)tty->disc_data; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Detach from the tty */ 1908c2ecf20Sopenharmony_ci tty->disc_data = NULL; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (!nu) 1938c2ecf20Sopenharmony_ci return; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci kfree_skb(nu->tx_skb); 1968c2ecf20Sopenharmony_ci kfree_skb(nu->rx_skb); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci skb_queue_purge(&nu->tx_q); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci nu->ops.close(nu); 2018c2ecf20Sopenharmony_ci nu->tty = NULL; 2028c2ecf20Sopenharmony_ci module_put(nu->owner); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci cancel_work_sync(&nu->write_work); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci kfree(nu); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* nci_uart_tty_wakeup() 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * Callback for transmit wakeup. Called when low level 2128c2ecf20Sopenharmony_ci * device driver can accept more send data. 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * Arguments: tty pointer to associated tty instance data 2158c2ecf20Sopenharmony_ci * Return Value: None 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_cistatic void nci_uart_tty_wakeup(struct tty_struct *tty) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct nci_uart *nu = (void *)tty->disc_data; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!nu) 2228c2ecf20Sopenharmony_ci return; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (tty != nu->tty) 2278c2ecf20Sopenharmony_ci return; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci nci_uart_tx_wakeup(nu); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* nci_uart_tty_receive() 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * Called by tty low level driver when receive data is 2358c2ecf20Sopenharmony_ci * available. 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * Arguments: tty pointer to tty isntance data 2388c2ecf20Sopenharmony_ci * data pointer to received data 2398c2ecf20Sopenharmony_ci * flags pointer to flags for data 2408c2ecf20Sopenharmony_ci * count count of received data in bytes 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * Return Value: None 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_cistatic void nci_uart_tty_receive(struct tty_struct *tty, const u8 *data, 2458c2ecf20Sopenharmony_ci char *flags, int count) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct nci_uart *nu = (void *)tty->disc_data; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (!nu || tty != nu->tty) 2508c2ecf20Sopenharmony_ci return; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci spin_lock(&nu->rx_lock); 2538c2ecf20Sopenharmony_ci nu->ops.recv_buf(nu, (void *)data, flags, count); 2548c2ecf20Sopenharmony_ci spin_unlock(&nu->rx_lock); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci tty_unthrottle(tty); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* nci_uart_tty_ioctl() 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * Process IOCTL system call for the tty device. 2628c2ecf20Sopenharmony_ci * 2638c2ecf20Sopenharmony_ci * Arguments: 2648c2ecf20Sopenharmony_ci * 2658c2ecf20Sopenharmony_ci * tty pointer to tty instance data 2668c2ecf20Sopenharmony_ci * file pointer to open file object for device 2678c2ecf20Sopenharmony_ci * cmd IOCTL command code 2688c2ecf20Sopenharmony_ci * arg argument for IOCTL call (cmd dependent) 2698c2ecf20Sopenharmony_ci * 2708c2ecf20Sopenharmony_ci * Return Value: Command dependent 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic int nci_uart_tty_ioctl(struct tty_struct *tty, struct file *file, 2738c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct nci_uart *nu = (void *)tty->disc_data; 2768c2ecf20Sopenharmony_ci int err = 0; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci switch (cmd) { 2798c2ecf20Sopenharmony_ci case NCIUARTSETDRIVER: 2808c2ecf20Sopenharmony_ci if (!nu) 2818c2ecf20Sopenharmony_ci return nci_uart_set_driver(tty, (unsigned int)arg); 2828c2ecf20Sopenharmony_ci else 2838c2ecf20Sopenharmony_ci return -EBUSY; 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci default: 2868c2ecf20Sopenharmony_ci err = n_tty_ioctl_helper(tty, file, cmd, arg); 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return err; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* We don't provide read/write/poll interface for user space. */ 2948c2ecf20Sopenharmony_cistatic ssize_t nci_uart_tty_read(struct tty_struct *tty, struct file *file, 2958c2ecf20Sopenharmony_ci unsigned char *buf, size_t nr, 2968c2ecf20Sopenharmony_ci void **cookie, unsigned long offset) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic ssize_t nci_uart_tty_write(struct tty_struct *tty, struct file *file, 3028c2ecf20Sopenharmony_ci const unsigned char *data, size_t count) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic __poll_t nci_uart_tty_poll(struct tty_struct *tty, 3088c2ecf20Sopenharmony_ci struct file *filp, poll_table *wait) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int nci_uart_send(struct nci_uart *nu, struct sk_buff *skb) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci /* Queue TX packet */ 3168c2ecf20Sopenharmony_ci skb_queue_tail(&nu->tx_q, skb); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Try to start TX (if possible) */ 3198c2ecf20Sopenharmony_ci nci_uart_tx_wakeup(nu); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/* -- Default recv_buf handler -- 3258c2ecf20Sopenharmony_ci * 3268c2ecf20Sopenharmony_ci * This handler supposes that NCI frames are sent over UART link without any 3278c2ecf20Sopenharmony_ci * framing. It reads NCI header, retrieve the packet size and once all packet 3288c2ecf20Sopenharmony_ci * bytes are received it passes it to nci_uart driver for processing. 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_cistatic int nci_uart_default_recv_buf(struct nci_uart *nu, const u8 *data, 3318c2ecf20Sopenharmony_ci char *flags, int count) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci int chunk_len; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (!nu->ndev) { 3368c2ecf20Sopenharmony_ci nfc_err(nu->tty->dev, 3378c2ecf20Sopenharmony_ci "receive data from tty but no NCI dev is attached yet, drop buffer\n"); 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* Decode all incoming data in packets 3428c2ecf20Sopenharmony_ci * and enqueue then for processing. 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ci while (count > 0) { 3458c2ecf20Sopenharmony_ci /* If this is the first data of a packet, allocate a buffer */ 3468c2ecf20Sopenharmony_ci if (!nu->rx_skb) { 3478c2ecf20Sopenharmony_ci nu->rx_packet_len = -1; 3488c2ecf20Sopenharmony_ci nu->rx_skb = nci_skb_alloc(nu->ndev, 3498c2ecf20Sopenharmony_ci NCI_MAX_PACKET_SIZE, 3508c2ecf20Sopenharmony_ci GFP_ATOMIC); 3518c2ecf20Sopenharmony_ci if (!nu->rx_skb) 3528c2ecf20Sopenharmony_ci return -ENOMEM; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Eat byte after byte till full packet header is received */ 3568c2ecf20Sopenharmony_ci if (nu->rx_skb->len < NCI_CTRL_HDR_SIZE) { 3578c2ecf20Sopenharmony_ci skb_put_u8(nu->rx_skb, *data++); 3588c2ecf20Sopenharmony_ci --count; 3598c2ecf20Sopenharmony_ci continue; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Header was received but packet len was not read */ 3638c2ecf20Sopenharmony_ci if (nu->rx_packet_len < 0) 3648c2ecf20Sopenharmony_ci nu->rx_packet_len = NCI_CTRL_HDR_SIZE + 3658c2ecf20Sopenharmony_ci nci_plen(nu->rx_skb->data); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Compute how many bytes are missing and how many bytes can 3688c2ecf20Sopenharmony_ci * be consumed. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci chunk_len = nu->rx_packet_len - nu->rx_skb->len; 3718c2ecf20Sopenharmony_ci if (count < chunk_len) 3728c2ecf20Sopenharmony_ci chunk_len = count; 3738c2ecf20Sopenharmony_ci skb_put_data(nu->rx_skb, data, chunk_len); 3748c2ecf20Sopenharmony_ci data += chunk_len; 3758c2ecf20Sopenharmony_ci count -= chunk_len; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Chcek if packet is fully received */ 3788c2ecf20Sopenharmony_ci if (nu->rx_packet_len == nu->rx_skb->len) { 3798c2ecf20Sopenharmony_ci /* Pass RX packet to driver */ 3808c2ecf20Sopenharmony_ci if (nu->ops.recv(nu, nu->rx_skb) != 0) 3818c2ecf20Sopenharmony_ci nfc_err(nu->tty->dev, "corrupted RX packet\n"); 3828c2ecf20Sopenharmony_ci /* Next packet will be a new one */ 3838c2ecf20Sopenharmony_ci nu->rx_skb = NULL; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/* -- Default recv handler -- */ 3918c2ecf20Sopenharmony_cistatic int nci_uart_default_recv(struct nci_uart *nu, struct sk_buff *skb) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci return nci_recv_frame(nu->ndev, skb); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciint nci_uart_register(struct nci_uart *nu) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci if (!nu || !nu->ops.open || 3998c2ecf20Sopenharmony_ci !nu->ops.recv || !nu->ops.close) 4008c2ecf20Sopenharmony_ci return -EINVAL; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Set the send callback */ 4038c2ecf20Sopenharmony_ci nu->ops.send = nci_uart_send; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* Install default handlers if not overridden */ 4068c2ecf20Sopenharmony_ci if (!nu->ops.recv_buf) 4078c2ecf20Sopenharmony_ci nu->ops.recv_buf = nci_uart_default_recv_buf; 4088c2ecf20Sopenharmony_ci if (!nu->ops.recv) 4098c2ecf20Sopenharmony_ci nu->ops.recv = nci_uart_default_recv; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Add this driver in the driver list */ 4128c2ecf20Sopenharmony_ci if (nci_uart_drivers[nu->driver]) { 4138c2ecf20Sopenharmony_ci pr_err("driver %d is already registered\n", nu->driver); 4148c2ecf20Sopenharmony_ci return -EBUSY; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci nci_uart_drivers[nu->driver] = nu; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci pr_info("NCI uart driver '%s [%d]' registered\n", nu->name, nu->driver); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nci_uart_register); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_civoid nci_uart_unregister(struct nci_uart *nu) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci pr_info("NCI uart driver '%s [%d]' unregistered\n", nu->name, 4278c2ecf20Sopenharmony_ci nu->driver); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Remove this driver from the driver list */ 4308c2ecf20Sopenharmony_ci nci_uart_drivers[nu->driver] = NULL; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nci_uart_unregister); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_civoid nci_uart_set_config(struct nci_uart *nu, int baudrate, int flow_ctrl) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct ktermios new_termios; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (!nu->tty) 4398c2ecf20Sopenharmony_ci return; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci down_read(&nu->tty->termios_rwsem); 4428c2ecf20Sopenharmony_ci new_termios = nu->tty->termios; 4438c2ecf20Sopenharmony_ci up_read(&nu->tty->termios_rwsem); 4448c2ecf20Sopenharmony_ci tty_termios_encode_baud_rate(&new_termios, baudrate, baudrate); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (flow_ctrl) 4478c2ecf20Sopenharmony_ci new_termios.c_cflag |= CRTSCTS; 4488c2ecf20Sopenharmony_ci else 4498c2ecf20Sopenharmony_ci new_termios.c_cflag &= ~CRTSCTS; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci tty_set_termios(nu->tty, &new_termios); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nci_uart_set_config); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic struct tty_ldisc_ops nci_uart_ldisc = { 4568c2ecf20Sopenharmony_ci .magic = TTY_LDISC_MAGIC, 4578c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4588c2ecf20Sopenharmony_ci .name = "n_nci", 4598c2ecf20Sopenharmony_ci .open = nci_uart_tty_open, 4608c2ecf20Sopenharmony_ci .close = nci_uart_tty_close, 4618c2ecf20Sopenharmony_ci .read = nci_uart_tty_read, 4628c2ecf20Sopenharmony_ci .write = nci_uart_tty_write, 4638c2ecf20Sopenharmony_ci .poll = nci_uart_tty_poll, 4648c2ecf20Sopenharmony_ci .receive_buf = nci_uart_tty_receive, 4658c2ecf20Sopenharmony_ci .write_wakeup = nci_uart_tty_wakeup, 4668c2ecf20Sopenharmony_ci .ioctl = nci_uart_tty_ioctl, 4678c2ecf20Sopenharmony_ci .compat_ioctl = nci_uart_tty_ioctl, 4688c2ecf20Sopenharmony_ci}; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int __init nci_uart_init(void) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci memset(nci_uart_drivers, 0, sizeof(nci_uart_drivers)); 4738c2ecf20Sopenharmony_ci return tty_register_ldisc(N_NCI, &nci_uart_ldisc); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic void __exit nci_uart_exit(void) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci tty_unregister_ldisc(N_NCI); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cimodule_init(nci_uart_init); 4828c2ecf20Sopenharmony_cimodule_exit(nci_uart_exit); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 4858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NFC NCI UART driver"); 4868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4878c2ecf20Sopenharmony_ciMODULE_ALIAS_LDISC(N_NCI); 488