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