18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010 48c2ecf20Sopenharmony_ci * Author: Sjur Brendeland 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 158c2ecf20Sopenharmony_ci#include <linux/tty.h> 168c2ecf20Sopenharmony_ci#include <linux/file.h> 178c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 188c2ecf20Sopenharmony_ci#include <net/caif/caif_device.h> 198c2ecf20Sopenharmony_ci#include <net/caif/cfcnfg.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sjur Brendeland"); 258c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CAIF serial device TTY line discipline"); 268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 278c2ecf20Sopenharmony_ciMODULE_ALIAS_LDISC(N_CAIF); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define SEND_QUEUE_LOW 10 308c2ecf20Sopenharmony_ci#define SEND_QUEUE_HIGH 100 318c2ecf20Sopenharmony_ci#define CAIF_SENDING 1 /* Bit 1 = 0x02*/ 328c2ecf20Sopenharmony_ci#define CAIF_FLOW_OFF_SENT 4 /* Bit 4 = 0x10 */ 338c2ecf20Sopenharmony_ci#define MAX_WRITE_CHUNK 4096 348c2ecf20Sopenharmony_ci#define ON 1 358c2ecf20Sopenharmony_ci#define OFF 0 368c2ecf20Sopenharmony_ci#define CAIF_MAX_MTU 4096 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ser_lock); 398c2ecf20Sopenharmony_cistatic LIST_HEAD(ser_list); 408c2ecf20Sopenharmony_cistatic LIST_HEAD(ser_release_list); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic bool ser_loop; 438c2ecf20Sopenharmony_cimodule_param(ser_loop, bool, 0444); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode."); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic bool ser_use_stx = true; 478c2ecf20Sopenharmony_cimodule_param(ser_use_stx, bool, 0444); 488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ser_use_stx, "STX enabled or not."); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic bool ser_use_fcs = true; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cimodule_param(ser_use_fcs, bool, 0444); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not."); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int ser_write_chunk = MAX_WRITE_CHUNK; 568c2ecf20Sopenharmony_cimodule_param(ser_write_chunk, int, 0444); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART."); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic struct dentry *debugfsdir; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int caif_net_open(struct net_device *dev); 638c2ecf20Sopenharmony_cistatic int caif_net_close(struct net_device *dev); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct ser_device { 668c2ecf20Sopenharmony_ci struct caif_dev_common common; 678c2ecf20Sopenharmony_ci struct list_head node; 688c2ecf20Sopenharmony_ci struct net_device *dev; 698c2ecf20Sopenharmony_ci struct sk_buff_head head; 708c2ecf20Sopenharmony_ci struct tty_struct *tty; 718c2ecf20Sopenharmony_ci bool tx_started; 728c2ecf20Sopenharmony_ci unsigned long state; 738c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 748c2ecf20Sopenharmony_ci struct dentry *debugfs_tty_dir; 758c2ecf20Sopenharmony_ci struct debugfs_blob_wrapper tx_blob; 768c2ecf20Sopenharmony_ci struct debugfs_blob_wrapper rx_blob; 778c2ecf20Sopenharmony_ci u8 rx_data[128]; 788c2ecf20Sopenharmony_ci u8 tx_data[128]; 798c2ecf20Sopenharmony_ci u8 tty_status; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#endif 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void caifdev_setup(struct net_device *dev); 858c2ecf20Sopenharmony_cistatic void ldisc_tx_wakeup(struct tty_struct *tty); 868c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 878c2ecf20Sopenharmony_cistatic inline void update_tty_status(struct ser_device *ser) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci ser->tty_status = 908c2ecf20Sopenharmony_ci ser->tty->stopped << 5 | 918c2ecf20Sopenharmony_ci ser->tty->flow_stopped << 3 | 928c2ecf20Sopenharmony_ci ser->tty->packet << 2 | 938c2ecf20Sopenharmony_ci ser->tty->port->low_latency << 1; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_cistatic inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci ser->debugfs_tty_dir = debugfs_create_dir(tty->name, debugfsdir); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci debugfs_create_blob("last_tx_msg", 0400, ser->debugfs_tty_dir, 1008c2ecf20Sopenharmony_ci &ser->tx_blob); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci debugfs_create_blob("last_rx_msg", 0400, ser->debugfs_tty_dir, 1038c2ecf20Sopenharmony_ci &ser->rx_blob); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci debugfs_create_xul("ser_state", 0400, ser->debugfs_tty_dir, 1068c2ecf20Sopenharmony_ci &ser->state); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci debugfs_create_x8("tty_status", 0400, ser->debugfs_tty_dir, 1098c2ecf20Sopenharmony_ci &ser->tty_status); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci ser->tx_blob.data = ser->tx_data; 1128c2ecf20Sopenharmony_ci ser->tx_blob.size = 0; 1138c2ecf20Sopenharmony_ci ser->rx_blob.data = ser->rx_data; 1148c2ecf20Sopenharmony_ci ser->rx_blob.size = 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic inline void debugfs_deinit(struct ser_device *ser) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci debugfs_remove_recursive(ser->debugfs_tty_dir); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci if (size > sizeof(ser->rx_data)) 1258c2ecf20Sopenharmony_ci size = sizeof(ser->rx_data); 1268c2ecf20Sopenharmony_ci memcpy(ser->rx_data, data, size); 1278c2ecf20Sopenharmony_ci ser->rx_blob.data = ser->rx_data; 1288c2ecf20Sopenharmony_ci ser->rx_blob.size = size; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci if (size > sizeof(ser->tx_data)) 1348c2ecf20Sopenharmony_ci size = sizeof(ser->tx_data); 1358c2ecf20Sopenharmony_ci memcpy(ser->tx_data, data, size); 1368c2ecf20Sopenharmony_ci ser->tx_blob.data = ser->tx_data; 1378c2ecf20Sopenharmony_ci ser->tx_blob.size = size; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci#else 1408c2ecf20Sopenharmony_cistatic inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic inline void debugfs_deinit(struct ser_device *ser) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic inline void update_tty_status(struct ser_device *ser) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci#endif 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void ldisc_receive(struct tty_struct *tty, const u8 *data, 1638c2ecf20Sopenharmony_ci char *flags, int count) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 1668c2ecf20Sopenharmony_ci struct ser_device *ser; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ser = tty->disc_data; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * NOTE: flags may contain information about break or overrun. 1738c2ecf20Sopenharmony_ci * This is not yet handled. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * Workaround for garbage at start of transmission, 1798c2ecf20Sopenharmony_ci * only enable if STX handling is not enabled. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci if (!ser->common.use_stx && !ser->tx_started) { 1828c2ecf20Sopenharmony_ci dev_info(&ser->dev->dev, 1838c2ecf20Sopenharmony_ci "Bytes received before initial transmission -" 1848c2ecf20Sopenharmony_ci "bytes discarded.\n"); 1858c2ecf20Sopenharmony_ci return; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci BUG_ON(ser->dev == NULL); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Get a suitable caif packet and copy in data. */ 1918c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(ser->dev, count+1); 1928c2ecf20Sopenharmony_ci if (skb == NULL) 1938c2ecf20Sopenharmony_ci return; 1948c2ecf20Sopenharmony_ci skb_put_data(skb, data, count); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_CAIF); 1978c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 1988c2ecf20Sopenharmony_ci debugfs_rx(ser, data, count); 1998c2ecf20Sopenharmony_ci /* Push received packet up the stack. */ 2008c2ecf20Sopenharmony_ci ret = netif_rx_ni(skb); 2018c2ecf20Sopenharmony_ci if (!ret) { 2028c2ecf20Sopenharmony_ci ser->dev->stats.rx_packets++; 2038c2ecf20Sopenharmony_ci ser->dev->stats.rx_bytes += count; 2048c2ecf20Sopenharmony_ci } else 2058c2ecf20Sopenharmony_ci ++ser->dev->stats.rx_dropped; 2068c2ecf20Sopenharmony_ci update_tty_status(ser); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int handle_tx(struct ser_device *ser) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct tty_struct *tty; 2128c2ecf20Sopenharmony_ci struct sk_buff *skb; 2138c2ecf20Sopenharmony_ci int tty_wr, len, room; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci tty = ser->tty; 2168c2ecf20Sopenharmony_ci ser->tx_started = true; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* Enter critical section */ 2198c2ecf20Sopenharmony_ci if (test_and_set_bit(CAIF_SENDING, &ser->state)) 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* skb_peek is safe because handle_tx is called after skb_queue_tail */ 2238c2ecf20Sopenharmony_ci while ((skb = skb_peek(&ser->head)) != NULL) { 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Make sure you don't write too much */ 2268c2ecf20Sopenharmony_ci len = skb->len; 2278c2ecf20Sopenharmony_ci room = tty_write_room(tty); 2288c2ecf20Sopenharmony_ci if (!room) 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci if (room > ser_write_chunk) 2318c2ecf20Sopenharmony_ci room = ser_write_chunk; 2328c2ecf20Sopenharmony_ci if (len > room) 2338c2ecf20Sopenharmony_ci len = room; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Write to tty or loopback */ 2368c2ecf20Sopenharmony_ci if (!ser_loop) { 2378c2ecf20Sopenharmony_ci tty_wr = tty->ops->write(tty, skb->data, len); 2388c2ecf20Sopenharmony_ci update_tty_status(ser); 2398c2ecf20Sopenharmony_ci } else { 2408c2ecf20Sopenharmony_ci tty_wr = len; 2418c2ecf20Sopenharmony_ci ldisc_receive(tty, skb->data, NULL, len); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci ser->dev->stats.tx_packets++; 2448c2ecf20Sopenharmony_ci ser->dev->stats.tx_bytes += tty_wr; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Error on TTY ?! */ 2478c2ecf20Sopenharmony_ci if (tty_wr < 0) 2488c2ecf20Sopenharmony_ci goto error; 2498c2ecf20Sopenharmony_ci /* Reduce buffer written, and discard if empty */ 2508c2ecf20Sopenharmony_ci skb_pull(skb, tty_wr); 2518c2ecf20Sopenharmony_ci if (skb->len == 0) { 2528c2ecf20Sopenharmony_ci struct sk_buff *tmp = skb_dequeue(&ser->head); 2538c2ecf20Sopenharmony_ci WARN_ON(tmp != skb); 2548c2ecf20Sopenharmony_ci dev_consume_skb_any(skb); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci /* Send flow off if queue is empty */ 2588c2ecf20Sopenharmony_ci if (ser->head.qlen <= SEND_QUEUE_LOW && 2598c2ecf20Sopenharmony_ci test_and_clear_bit(CAIF_FLOW_OFF_SENT, &ser->state) && 2608c2ecf20Sopenharmony_ci ser->common.flowctrl != NULL) 2618c2ecf20Sopenharmony_ci ser->common.flowctrl(ser->dev, ON); 2628c2ecf20Sopenharmony_ci clear_bit(CAIF_SENDING, &ser->state); 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_cierror: 2658c2ecf20Sopenharmony_ci clear_bit(CAIF_SENDING, &ser->state); 2668c2ecf20Sopenharmony_ci return tty_wr; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic netdev_tx_t caif_xmit(struct sk_buff *skb, struct net_device *dev) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct ser_device *ser; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci ser = netdev_priv(dev); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Send flow off once, on high water mark */ 2768c2ecf20Sopenharmony_ci if (ser->head.qlen > SEND_QUEUE_HIGH && 2778c2ecf20Sopenharmony_ci !test_and_set_bit(CAIF_FLOW_OFF_SENT, &ser->state) && 2788c2ecf20Sopenharmony_ci ser->common.flowctrl != NULL) 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci ser->common.flowctrl(ser->dev, OFF); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci skb_queue_tail(&ser->head, skb); 2838c2ecf20Sopenharmony_ci return handle_tx(ser); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic void ldisc_tx_wakeup(struct tty_struct *tty) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct ser_device *ser; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ser = tty->disc_data; 2928c2ecf20Sopenharmony_ci BUG_ON(ser == NULL); 2938c2ecf20Sopenharmony_ci WARN_ON(ser->tty != tty); 2948c2ecf20Sopenharmony_ci handle_tx(ser); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void ser_release(struct work_struct *work) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct list_head list; 3018c2ecf20Sopenharmony_ci struct ser_device *ser, *tmp; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci spin_lock(&ser_lock); 3048c2ecf20Sopenharmony_ci list_replace_init(&ser_release_list, &list); 3058c2ecf20Sopenharmony_ci spin_unlock(&ser_lock); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!list_empty(&list)) { 3088c2ecf20Sopenharmony_ci rtnl_lock(); 3098c2ecf20Sopenharmony_ci list_for_each_entry_safe(ser, tmp, &list, node) { 3108c2ecf20Sopenharmony_ci dev_close(ser->dev); 3118c2ecf20Sopenharmony_ci unregister_netdevice(ser->dev); 3128c2ecf20Sopenharmony_ci debugfs_deinit(ser); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci rtnl_unlock(); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic DECLARE_WORK(ser_release_work, ser_release); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int ldisc_open(struct tty_struct *tty) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct ser_device *ser; 3238c2ecf20Sopenharmony_ci struct net_device *dev; 3248c2ecf20Sopenharmony_ci char name[64]; 3258c2ecf20Sopenharmony_ci int result; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* No write no play */ 3288c2ecf20Sopenharmony_ci if (tty->ops->write == NULL) 3298c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3308c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG)) 3318c2ecf20Sopenharmony_ci return -EPERM; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* release devices to avoid name collision */ 3348c2ecf20Sopenharmony_ci ser_release(NULL); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci result = snprintf(name, sizeof(name), "cf%s", tty->name); 3378c2ecf20Sopenharmony_ci if (result >= IFNAMSIZ) 3388c2ecf20Sopenharmony_ci return -EINVAL; 3398c2ecf20Sopenharmony_ci dev = alloc_netdev(sizeof(*ser), name, NET_NAME_UNKNOWN, 3408c2ecf20Sopenharmony_ci caifdev_setup); 3418c2ecf20Sopenharmony_ci if (!dev) 3428c2ecf20Sopenharmony_ci return -ENOMEM; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ser = netdev_priv(dev); 3458c2ecf20Sopenharmony_ci ser->tty = tty_kref_get(tty); 3468c2ecf20Sopenharmony_ci ser->dev = dev; 3478c2ecf20Sopenharmony_ci debugfs_init(ser, tty); 3488c2ecf20Sopenharmony_ci tty->receive_room = N_TTY_BUF_SIZE; 3498c2ecf20Sopenharmony_ci tty->disc_data = ser; 3508c2ecf20Sopenharmony_ci set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 3518c2ecf20Sopenharmony_ci rtnl_lock(); 3528c2ecf20Sopenharmony_ci result = register_netdevice(dev); 3538c2ecf20Sopenharmony_ci if (result) { 3548c2ecf20Sopenharmony_ci tty_kref_put(tty); 3558c2ecf20Sopenharmony_ci rtnl_unlock(); 3568c2ecf20Sopenharmony_ci free_netdev(dev); 3578c2ecf20Sopenharmony_ci return -ENODEV; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci spin_lock(&ser_lock); 3618c2ecf20Sopenharmony_ci list_add(&ser->node, &ser_list); 3628c2ecf20Sopenharmony_ci spin_unlock(&ser_lock); 3638c2ecf20Sopenharmony_ci rtnl_unlock(); 3648c2ecf20Sopenharmony_ci netif_stop_queue(dev); 3658c2ecf20Sopenharmony_ci update_tty_status(ser); 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void ldisc_close(struct tty_struct *tty) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct ser_device *ser = tty->disc_data; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci tty_kref_put(ser->tty); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci spin_lock(&ser_lock); 3768c2ecf20Sopenharmony_ci list_move(&ser->node, &ser_release_list); 3778c2ecf20Sopenharmony_ci spin_unlock(&ser_lock); 3788c2ecf20Sopenharmony_ci schedule_work(&ser_release_work); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* The line discipline structure. */ 3828c2ecf20Sopenharmony_cistatic struct tty_ldisc_ops caif_ldisc = { 3838c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3848c2ecf20Sopenharmony_ci .magic = TTY_LDISC_MAGIC, 3858c2ecf20Sopenharmony_ci .name = "n_caif", 3868c2ecf20Sopenharmony_ci .open = ldisc_open, 3878c2ecf20Sopenharmony_ci .close = ldisc_close, 3888c2ecf20Sopenharmony_ci .receive_buf = ldisc_receive, 3898c2ecf20Sopenharmony_ci .write_wakeup = ldisc_tx_wakeup 3908c2ecf20Sopenharmony_ci}; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int register_ldisc(void) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci int result; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci result = tty_register_ldisc(N_CAIF, &caif_ldisc); 3978c2ecf20Sopenharmony_ci if (result < 0) { 3988c2ecf20Sopenharmony_ci pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF, 3998c2ecf20Sopenharmony_ci result); 4008c2ecf20Sopenharmony_ci return result; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci return result; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 4058c2ecf20Sopenharmony_ci .ndo_open = caif_net_open, 4068c2ecf20Sopenharmony_ci .ndo_stop = caif_net_close, 4078c2ecf20Sopenharmony_ci .ndo_start_xmit = caif_xmit 4088c2ecf20Sopenharmony_ci}; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic void caifdev_setup(struct net_device *dev) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct ser_device *serdev = netdev_priv(dev); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci dev->features = 0; 4158c2ecf20Sopenharmony_ci dev->netdev_ops = &netdev_ops; 4168c2ecf20Sopenharmony_ci dev->type = ARPHRD_CAIF; 4178c2ecf20Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 4188c2ecf20Sopenharmony_ci dev->mtu = CAIF_MAX_MTU; 4198c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_NO_QUEUE; 4208c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 4218c2ecf20Sopenharmony_ci skb_queue_head_init(&serdev->head); 4228c2ecf20Sopenharmony_ci serdev->common.link_select = CAIF_LINK_LOW_LATENCY; 4238c2ecf20Sopenharmony_ci serdev->common.use_frag = true; 4248c2ecf20Sopenharmony_ci serdev->common.use_stx = ser_use_stx; 4258c2ecf20Sopenharmony_ci serdev->common.use_fcs = ser_use_fcs; 4268c2ecf20Sopenharmony_ci serdev->dev = dev; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int caif_net_open(struct net_device *dev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci netif_wake_queue(dev); 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int caif_net_close(struct net_device *dev) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci netif_stop_queue(dev); 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int __init caif_ser_init(void) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci int ret; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci ret = register_ldisc(); 4478c2ecf20Sopenharmony_ci debugfsdir = debugfs_create_dir("caif_serial", NULL); 4488c2ecf20Sopenharmony_ci return ret; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic void __exit caif_ser_exit(void) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci spin_lock(&ser_lock); 4548c2ecf20Sopenharmony_ci list_splice(&ser_list, &ser_release_list); 4558c2ecf20Sopenharmony_ci spin_unlock(&ser_lock); 4568c2ecf20Sopenharmony_ci ser_release(NULL); 4578c2ecf20Sopenharmony_ci cancel_work_sync(&ser_release_work); 4588c2ecf20Sopenharmony_ci tty_unregister_ldisc(N_CAIF); 4598c2ecf20Sopenharmony_ci debugfs_remove_recursive(debugfsdir); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cimodule_init(caif_ser_init); 4638c2ecf20Sopenharmony_cimodule_exit(caif_ser_exit); 464