162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010 462306a36Sopenharmony_ci * Author: Sjur Brendeland 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/hardirq.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/skbuff.h> 1362306a36Sopenharmony_ci#include <linux/netdevice.h> 1462306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1562306a36Sopenharmony_ci#include <linux/tty.h> 1662306a36Sopenharmony_ci#include <linux/file.h> 1762306a36Sopenharmony_ci#include <linux/if_arp.h> 1862306a36Sopenharmony_ci#include <net/caif/caif_device.h> 1962306a36Sopenharmony_ci#include <net/caif/cfcnfg.h> 2062306a36Sopenharmony_ci#include <linux/err.h> 2162306a36Sopenharmony_ci#include <linux/debugfs.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2462306a36Sopenharmony_ciMODULE_AUTHOR("Sjur Brendeland"); 2562306a36Sopenharmony_ciMODULE_DESCRIPTION("CAIF serial device TTY line discipline"); 2662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2762306a36Sopenharmony_ciMODULE_ALIAS_LDISC(N_CAIF); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define SEND_QUEUE_LOW 10 3062306a36Sopenharmony_ci#define SEND_QUEUE_HIGH 100 3162306a36Sopenharmony_ci#define CAIF_SENDING 1 /* Bit 1 = 0x02*/ 3262306a36Sopenharmony_ci#define CAIF_FLOW_OFF_SENT 4 /* Bit 4 = 0x10 */ 3362306a36Sopenharmony_ci#define MAX_WRITE_CHUNK 4096 3462306a36Sopenharmony_ci#define ON 1 3562306a36Sopenharmony_ci#define OFF 0 3662306a36Sopenharmony_ci#define CAIF_MAX_MTU 4096 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ser_lock); 3962306a36Sopenharmony_cistatic LIST_HEAD(ser_list); 4062306a36Sopenharmony_cistatic LIST_HEAD(ser_release_list); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic bool ser_loop; 4362306a36Sopenharmony_cimodule_param(ser_loop, bool, 0444); 4462306a36Sopenharmony_ciMODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode."); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic bool ser_use_stx = true; 4762306a36Sopenharmony_cimodule_param(ser_use_stx, bool, 0444); 4862306a36Sopenharmony_ciMODULE_PARM_DESC(ser_use_stx, "STX enabled or not."); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic bool ser_use_fcs = true; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cimodule_param(ser_use_fcs, bool, 0444); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not."); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int ser_write_chunk = MAX_WRITE_CHUNK; 5662306a36Sopenharmony_cimodule_param(ser_write_chunk, int, 0444); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciMODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART."); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic struct dentry *debugfsdir; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int caif_net_open(struct net_device *dev); 6362306a36Sopenharmony_cistatic int caif_net_close(struct net_device *dev); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct ser_device { 6662306a36Sopenharmony_ci struct caif_dev_common common; 6762306a36Sopenharmony_ci struct list_head node; 6862306a36Sopenharmony_ci struct net_device *dev; 6962306a36Sopenharmony_ci struct sk_buff_head head; 7062306a36Sopenharmony_ci struct tty_struct *tty; 7162306a36Sopenharmony_ci bool tx_started; 7262306a36Sopenharmony_ci unsigned long state; 7362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 7462306a36Sopenharmony_ci struct dentry *debugfs_tty_dir; 7562306a36Sopenharmony_ci struct debugfs_blob_wrapper tx_blob; 7662306a36Sopenharmony_ci struct debugfs_blob_wrapper rx_blob; 7762306a36Sopenharmony_ci u8 rx_data[128]; 7862306a36Sopenharmony_ci u8 tx_data[128]; 7962306a36Sopenharmony_ci u8 tty_status; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#endif 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void caifdev_setup(struct net_device *dev); 8562306a36Sopenharmony_cistatic void ldisc_tx_wakeup(struct tty_struct *tty); 8662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 8762306a36Sopenharmony_cistatic inline void update_tty_status(struct ser_device *ser) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci ser->tty_status = 9062306a36Sopenharmony_ci ser->tty->flow.stopped << 5 | 9162306a36Sopenharmony_ci ser->tty->flow.tco_stopped << 3 | 9262306a36Sopenharmony_ci ser->tty->ctrl.packet << 2; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_cistatic inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci ser->debugfs_tty_dir = debugfs_create_dir(tty->name, debugfsdir); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci debugfs_create_blob("last_tx_msg", 0400, ser->debugfs_tty_dir, 9962306a36Sopenharmony_ci &ser->tx_blob); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci debugfs_create_blob("last_rx_msg", 0400, ser->debugfs_tty_dir, 10262306a36Sopenharmony_ci &ser->rx_blob); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci debugfs_create_xul("ser_state", 0400, ser->debugfs_tty_dir, 10562306a36Sopenharmony_ci &ser->state); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci debugfs_create_x8("tty_status", 0400, ser->debugfs_tty_dir, 10862306a36Sopenharmony_ci &ser->tty_status); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci ser->tx_blob.data = ser->tx_data; 11162306a36Sopenharmony_ci ser->tx_blob.size = 0; 11262306a36Sopenharmony_ci ser->rx_blob.data = ser->rx_data; 11362306a36Sopenharmony_ci ser->rx_blob.size = 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic inline void debugfs_deinit(struct ser_device *ser) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci debugfs_remove_recursive(ser->debugfs_tty_dir); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci if (size > sizeof(ser->rx_data)) 12462306a36Sopenharmony_ci size = sizeof(ser->rx_data); 12562306a36Sopenharmony_ci memcpy(ser->rx_data, data, size); 12662306a36Sopenharmony_ci ser->rx_blob.data = ser->rx_data; 12762306a36Sopenharmony_ci ser->rx_blob.size = size; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci if (size > sizeof(ser->tx_data)) 13362306a36Sopenharmony_ci size = sizeof(ser->tx_data); 13462306a36Sopenharmony_ci memcpy(ser->tx_data, data, size); 13562306a36Sopenharmony_ci ser->tx_blob.data = ser->tx_data; 13662306a36Sopenharmony_ci ser->tx_blob.size = size; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci#else 13962306a36Sopenharmony_cistatic inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic inline void debugfs_deinit(struct ser_device *ser) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic inline void update_tty_status(struct ser_device *ser) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#endif 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void ldisc_receive(struct tty_struct *tty, const u8 *data, 16262306a36Sopenharmony_ci const u8 *flags, size_t count) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct sk_buff *skb = NULL; 16562306a36Sopenharmony_ci struct ser_device *ser; 16662306a36Sopenharmony_ci int ret; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ser = tty->disc_data; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * NOTE: flags may contain information about break or overrun. 17262306a36Sopenharmony_ci * This is not yet handled. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* 17762306a36Sopenharmony_ci * Workaround for garbage at start of transmission, 17862306a36Sopenharmony_ci * only enable if STX handling is not enabled. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci if (!ser->common.use_stx && !ser->tx_started) { 18162306a36Sopenharmony_ci dev_info(&ser->dev->dev, 18262306a36Sopenharmony_ci "Bytes received before initial transmission -" 18362306a36Sopenharmony_ci "bytes discarded.\n"); 18462306a36Sopenharmony_ci return; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci BUG_ON(ser->dev == NULL); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Get a suitable caif packet and copy in data. */ 19062306a36Sopenharmony_ci skb = netdev_alloc_skb(ser->dev, count+1); 19162306a36Sopenharmony_ci if (skb == NULL) 19262306a36Sopenharmony_ci return; 19362306a36Sopenharmony_ci skb_put_data(skb, data, count); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci skb->protocol = htons(ETH_P_CAIF); 19662306a36Sopenharmony_ci skb_reset_mac_header(skb); 19762306a36Sopenharmony_ci debugfs_rx(ser, data, count); 19862306a36Sopenharmony_ci /* Push received packet up the stack. */ 19962306a36Sopenharmony_ci ret = netif_rx(skb); 20062306a36Sopenharmony_ci if (!ret) { 20162306a36Sopenharmony_ci ser->dev->stats.rx_packets++; 20262306a36Sopenharmony_ci ser->dev->stats.rx_bytes += count; 20362306a36Sopenharmony_ci } else 20462306a36Sopenharmony_ci ++ser->dev->stats.rx_dropped; 20562306a36Sopenharmony_ci update_tty_status(ser); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int handle_tx(struct ser_device *ser) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct tty_struct *tty; 21162306a36Sopenharmony_ci struct sk_buff *skb; 21262306a36Sopenharmony_ci int tty_wr, len, room; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci tty = ser->tty; 21562306a36Sopenharmony_ci ser->tx_started = true; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Enter critical section */ 21862306a36Sopenharmony_ci if (test_and_set_bit(CAIF_SENDING, &ser->state)) 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* skb_peek is safe because handle_tx is called after skb_queue_tail */ 22262306a36Sopenharmony_ci while ((skb = skb_peek(&ser->head)) != NULL) { 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Make sure you don't write too much */ 22562306a36Sopenharmony_ci len = skb->len; 22662306a36Sopenharmony_ci room = tty_write_room(tty); 22762306a36Sopenharmony_ci if (!room) 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci if (room > ser_write_chunk) 23062306a36Sopenharmony_ci room = ser_write_chunk; 23162306a36Sopenharmony_ci if (len > room) 23262306a36Sopenharmony_ci len = room; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Write to tty or loopback */ 23562306a36Sopenharmony_ci if (!ser_loop) { 23662306a36Sopenharmony_ci tty_wr = tty->ops->write(tty, skb->data, len); 23762306a36Sopenharmony_ci update_tty_status(ser); 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci tty_wr = len; 24062306a36Sopenharmony_ci ldisc_receive(tty, skb->data, NULL, len); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci ser->dev->stats.tx_packets++; 24362306a36Sopenharmony_ci ser->dev->stats.tx_bytes += tty_wr; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Error on TTY ?! */ 24662306a36Sopenharmony_ci if (tty_wr < 0) 24762306a36Sopenharmony_ci goto error; 24862306a36Sopenharmony_ci /* Reduce buffer written, and discard if empty */ 24962306a36Sopenharmony_ci skb_pull(skb, tty_wr); 25062306a36Sopenharmony_ci if (skb->len == 0) { 25162306a36Sopenharmony_ci struct sk_buff *tmp = skb_dequeue(&ser->head); 25262306a36Sopenharmony_ci WARN_ON(tmp != skb); 25362306a36Sopenharmony_ci dev_consume_skb_any(skb); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci /* Send flow off if queue is empty */ 25762306a36Sopenharmony_ci if (ser->head.qlen <= SEND_QUEUE_LOW && 25862306a36Sopenharmony_ci test_and_clear_bit(CAIF_FLOW_OFF_SENT, &ser->state) && 25962306a36Sopenharmony_ci ser->common.flowctrl != NULL) 26062306a36Sopenharmony_ci ser->common.flowctrl(ser->dev, ON); 26162306a36Sopenharmony_ci clear_bit(CAIF_SENDING, &ser->state); 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_cierror: 26462306a36Sopenharmony_ci clear_bit(CAIF_SENDING, &ser->state); 26562306a36Sopenharmony_ci return tty_wr; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic netdev_tx_t caif_xmit(struct sk_buff *skb, struct net_device *dev) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct ser_device *ser; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci ser = netdev_priv(dev); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Send flow off once, on high water mark */ 27562306a36Sopenharmony_ci if (ser->head.qlen > SEND_QUEUE_HIGH && 27662306a36Sopenharmony_ci !test_and_set_bit(CAIF_FLOW_OFF_SENT, &ser->state) && 27762306a36Sopenharmony_ci ser->common.flowctrl != NULL) 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ser->common.flowctrl(ser->dev, OFF); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci skb_queue_tail(&ser->head, skb); 28262306a36Sopenharmony_ci return handle_tx(ser); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void ldisc_tx_wakeup(struct tty_struct *tty) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct ser_device *ser; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ser = tty->disc_data; 29162306a36Sopenharmony_ci BUG_ON(ser == NULL); 29262306a36Sopenharmony_ci WARN_ON(ser->tty != tty); 29362306a36Sopenharmony_ci handle_tx(ser); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic void ser_release(struct work_struct *work) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct list_head list; 30062306a36Sopenharmony_ci struct ser_device *ser, *tmp; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci spin_lock(&ser_lock); 30362306a36Sopenharmony_ci list_replace_init(&ser_release_list, &list); 30462306a36Sopenharmony_ci spin_unlock(&ser_lock); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!list_empty(&list)) { 30762306a36Sopenharmony_ci rtnl_lock(); 30862306a36Sopenharmony_ci list_for_each_entry_safe(ser, tmp, &list, node) { 30962306a36Sopenharmony_ci dev_close(ser->dev); 31062306a36Sopenharmony_ci unregister_netdevice(ser->dev); 31162306a36Sopenharmony_ci debugfs_deinit(ser); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci rtnl_unlock(); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic DECLARE_WORK(ser_release_work, ser_release); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int ldisc_open(struct tty_struct *tty) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct ser_device *ser; 32262306a36Sopenharmony_ci struct net_device *dev; 32362306a36Sopenharmony_ci char name[64]; 32462306a36Sopenharmony_ci int result; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* No write no play */ 32762306a36Sopenharmony_ci if (tty->ops->write == NULL) 32862306a36Sopenharmony_ci return -EOPNOTSUPP; 32962306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG)) 33062306a36Sopenharmony_ci return -EPERM; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* release devices to avoid name collision */ 33362306a36Sopenharmony_ci ser_release(NULL); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci result = snprintf(name, sizeof(name), "cf%s", tty->name); 33662306a36Sopenharmony_ci if (result >= IFNAMSIZ) 33762306a36Sopenharmony_ci return -EINVAL; 33862306a36Sopenharmony_ci dev = alloc_netdev(sizeof(*ser), name, NET_NAME_UNKNOWN, 33962306a36Sopenharmony_ci caifdev_setup); 34062306a36Sopenharmony_ci if (!dev) 34162306a36Sopenharmony_ci return -ENOMEM; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ser = netdev_priv(dev); 34462306a36Sopenharmony_ci ser->tty = tty_kref_get(tty); 34562306a36Sopenharmony_ci ser->dev = dev; 34662306a36Sopenharmony_ci debugfs_init(ser, tty); 34762306a36Sopenharmony_ci tty->receive_room = N_TTY_BUF_SIZE; 34862306a36Sopenharmony_ci tty->disc_data = ser; 34962306a36Sopenharmony_ci set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 35062306a36Sopenharmony_ci rtnl_lock(); 35162306a36Sopenharmony_ci result = register_netdevice(dev); 35262306a36Sopenharmony_ci if (result) { 35362306a36Sopenharmony_ci tty_kref_put(tty); 35462306a36Sopenharmony_ci rtnl_unlock(); 35562306a36Sopenharmony_ci free_netdev(dev); 35662306a36Sopenharmony_ci return -ENODEV; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci spin_lock(&ser_lock); 36062306a36Sopenharmony_ci list_add(&ser->node, &ser_list); 36162306a36Sopenharmony_ci spin_unlock(&ser_lock); 36262306a36Sopenharmony_ci rtnl_unlock(); 36362306a36Sopenharmony_ci netif_stop_queue(dev); 36462306a36Sopenharmony_ci update_tty_status(ser); 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void ldisc_close(struct tty_struct *tty) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct ser_device *ser = tty->disc_data; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci tty_kref_put(ser->tty); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci spin_lock(&ser_lock); 37562306a36Sopenharmony_ci list_move(&ser->node, &ser_release_list); 37662306a36Sopenharmony_ci spin_unlock(&ser_lock); 37762306a36Sopenharmony_ci schedule_work(&ser_release_work); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* The line discipline structure. */ 38162306a36Sopenharmony_cistatic struct tty_ldisc_ops caif_ldisc = { 38262306a36Sopenharmony_ci .owner = THIS_MODULE, 38362306a36Sopenharmony_ci .num = N_CAIF, 38462306a36Sopenharmony_ci .name = "n_caif", 38562306a36Sopenharmony_ci .open = ldisc_open, 38662306a36Sopenharmony_ci .close = ldisc_close, 38762306a36Sopenharmony_ci .receive_buf = ldisc_receive, 38862306a36Sopenharmony_ci .write_wakeup = ldisc_tx_wakeup 38962306a36Sopenharmony_ci}; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 39262306a36Sopenharmony_ci .ndo_open = caif_net_open, 39362306a36Sopenharmony_ci .ndo_stop = caif_net_close, 39462306a36Sopenharmony_ci .ndo_start_xmit = caif_xmit 39562306a36Sopenharmony_ci}; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void caifdev_setup(struct net_device *dev) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct ser_device *serdev = netdev_priv(dev); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci dev->features = 0; 40262306a36Sopenharmony_ci dev->netdev_ops = &netdev_ops; 40362306a36Sopenharmony_ci dev->type = ARPHRD_CAIF; 40462306a36Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 40562306a36Sopenharmony_ci dev->mtu = CAIF_MAX_MTU; 40662306a36Sopenharmony_ci dev->priv_flags |= IFF_NO_QUEUE; 40762306a36Sopenharmony_ci dev->needs_free_netdev = true; 40862306a36Sopenharmony_ci skb_queue_head_init(&serdev->head); 40962306a36Sopenharmony_ci serdev->common.link_select = CAIF_LINK_LOW_LATENCY; 41062306a36Sopenharmony_ci serdev->common.use_frag = true; 41162306a36Sopenharmony_ci serdev->common.use_stx = ser_use_stx; 41262306a36Sopenharmony_ci serdev->common.use_fcs = ser_use_fcs; 41362306a36Sopenharmony_ci serdev->dev = dev; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int caif_net_open(struct net_device *dev) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci netif_wake_queue(dev); 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int caif_net_close(struct net_device *dev) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci netif_stop_queue(dev); 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int __init caif_ser_init(void) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci int ret; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci ret = tty_register_ldisc(&caif_ldisc); 43462306a36Sopenharmony_ci if (ret < 0) 43562306a36Sopenharmony_ci pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF, ret); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci debugfsdir = debugfs_create_dir("caif_serial", NULL); 43862306a36Sopenharmony_ci return ret; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic void __exit caif_ser_exit(void) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci spin_lock(&ser_lock); 44462306a36Sopenharmony_ci list_splice(&ser_list, &ser_release_list); 44562306a36Sopenharmony_ci spin_unlock(&ser_lock); 44662306a36Sopenharmony_ci ser_release(NULL); 44762306a36Sopenharmony_ci cancel_work_sync(&ser_release_work); 44862306a36Sopenharmony_ci tty_unregister_ldisc(&caif_ldisc); 44962306a36Sopenharmony_ci debugfs_remove_recursive(debugfsdir); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cimodule_init(caif_ser_init); 45362306a36Sopenharmony_cimodule_exit(caif_ser_exit); 454