18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci#include <linux/tty.h>
88c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
98c2ecf20Sopenharmony_ci#include "chan.h"
108c2ecf20Sopenharmony_ci#include <os.h>
118c2ecf20Sopenharmony_ci#include <irq_kern.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#ifdef CONFIG_NOCONFIG_CHAN
148c2ecf20Sopenharmony_cistatic void *not_configged_init(char *str, int device,
158c2ecf20Sopenharmony_ci				const struct chan_opts *opts)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
188c2ecf20Sopenharmony_ci	       "UML\n");
198c2ecf20Sopenharmony_ci	return NULL;
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int not_configged_open(int input, int output, int primary, void *data,
238c2ecf20Sopenharmony_ci			      char **dev_out)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
268c2ecf20Sopenharmony_ci	       "UML\n");
278c2ecf20Sopenharmony_ci	return -ENODEV;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void not_configged_close(int fd, void *data)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
338c2ecf20Sopenharmony_ci	       "UML\n");
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int not_configged_read(int fd, char *c_out, void *data)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
398c2ecf20Sopenharmony_ci	       "UML\n");
408c2ecf20Sopenharmony_ci	return -EIO;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int not_configged_write(int fd, const char *buf, int len, void *data)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
468c2ecf20Sopenharmony_ci	       "UML\n");
478c2ecf20Sopenharmony_ci	return -EIO;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int not_configged_console_write(int fd, const char *buf, int len)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
538c2ecf20Sopenharmony_ci	       "UML\n");
548c2ecf20Sopenharmony_ci	return -EIO;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int not_configged_window_size(int fd, void *data, unsigned short *rows,
588c2ecf20Sopenharmony_ci				     unsigned short *cols)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
618c2ecf20Sopenharmony_ci	       "UML\n");
628c2ecf20Sopenharmony_ci	return -ENODEV;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void not_configged_free(void *data)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
688c2ecf20Sopenharmony_ci	       "UML\n");
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic const struct chan_ops not_configged_ops = {
728c2ecf20Sopenharmony_ci	.init		= not_configged_init,
738c2ecf20Sopenharmony_ci	.open		= not_configged_open,
748c2ecf20Sopenharmony_ci	.close		= not_configged_close,
758c2ecf20Sopenharmony_ci	.read		= not_configged_read,
768c2ecf20Sopenharmony_ci	.write		= not_configged_write,
778c2ecf20Sopenharmony_ci	.console_write	= not_configged_console_write,
788c2ecf20Sopenharmony_ci	.window_size	= not_configged_window_size,
798c2ecf20Sopenharmony_ci	.free		= not_configged_free,
808c2ecf20Sopenharmony_ci	.winch		= 0,
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci#endif /* CONFIG_NOCONFIG_CHAN */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int open_one_chan(struct chan *chan)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	int fd, err;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (chan->opened)
898c2ecf20Sopenharmony_ci		return 0;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (chan->ops->open == NULL)
928c2ecf20Sopenharmony_ci		fd = 0;
938c2ecf20Sopenharmony_ci	else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
948c2ecf20Sopenharmony_ci				     chan->data, &chan->dev);
958c2ecf20Sopenharmony_ci	if (fd < 0)
968c2ecf20Sopenharmony_ci		return fd;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	err = os_set_fd_block(fd, 0);
998c2ecf20Sopenharmony_ci	if (err) {
1008c2ecf20Sopenharmony_ci		(*chan->ops->close)(fd, chan->data);
1018c2ecf20Sopenharmony_ci		return err;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	chan->fd = fd;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	chan->opened = 1;
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int open_chan(struct list_head *chans)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct list_head *ele;
1138c2ecf20Sopenharmony_ci	struct chan *chan;
1148c2ecf20Sopenharmony_ci	int ret, err = 0;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	list_for_each(ele, chans) {
1178c2ecf20Sopenharmony_ci		chan = list_entry(ele, struct chan, list);
1188c2ecf20Sopenharmony_ci		ret = open_one_chan(chan);
1198c2ecf20Sopenharmony_ci		if (chan->primary)
1208c2ecf20Sopenharmony_ci			err = ret;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	return err;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_civoid chan_enable_winch(struct chan *chan, struct tty_port *port)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	if (chan && chan->primary && chan->ops->winch)
1288c2ecf20Sopenharmony_ci		register_winch(chan->fd, port);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic void line_timer_cb(struct work_struct *work)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct line *line = container_of(work, struct line, task.work);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (!line->throttled)
1368c2ecf20Sopenharmony_ci		chan_interrupt(line, line->driver->read_irq);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciint enable_chan(struct line *line)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct list_head *ele;
1428c2ecf20Sopenharmony_ci	struct chan *chan;
1438c2ecf20Sopenharmony_ci	int err;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&line->task, line_timer_cb);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	list_for_each(ele, &line->chan_list) {
1488c2ecf20Sopenharmony_ci		chan = list_entry(ele, struct chan, list);
1498c2ecf20Sopenharmony_ci		err = open_one_chan(chan);
1508c2ecf20Sopenharmony_ci		if (err) {
1518c2ecf20Sopenharmony_ci			if (chan->primary)
1528c2ecf20Sopenharmony_ci				goto out_close;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci			continue;
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		if (chan->enabled)
1588c2ecf20Sopenharmony_ci			continue;
1598c2ecf20Sopenharmony_ci		err = line_setup_irq(chan->fd, chan->input, chan->output, line,
1608c2ecf20Sopenharmony_ci				     chan);
1618c2ecf20Sopenharmony_ci		if (err)
1628c2ecf20Sopenharmony_ci			goto out_close;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		chan->enabled = 1;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci out_close:
1708c2ecf20Sopenharmony_ci	close_chan(line);
1718c2ecf20Sopenharmony_ci	return err;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/* Items are added in IRQ context, when free_irq can't be called, and
1758c2ecf20Sopenharmony_ci * removed in process context, when it can.
1768c2ecf20Sopenharmony_ci * This handles interrupt sources which disappear, and which need to
1778c2ecf20Sopenharmony_ci * be permanently disabled.  This is discovered in IRQ context, but
1788c2ecf20Sopenharmony_ci * the freeing of the IRQ must be done later.
1798c2ecf20Sopenharmony_ci */
1808c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(irqs_to_free_lock);
1818c2ecf20Sopenharmony_cistatic LIST_HEAD(irqs_to_free);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_civoid free_irqs(void)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct chan *chan;
1868c2ecf20Sopenharmony_ci	LIST_HEAD(list);
1878c2ecf20Sopenharmony_ci	struct list_head *ele;
1888c2ecf20Sopenharmony_ci	unsigned long flags;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&irqs_to_free_lock, flags);
1918c2ecf20Sopenharmony_ci	list_splice_init(&irqs_to_free, &list);
1928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&irqs_to_free_lock, flags);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	list_for_each(ele, &list) {
1958c2ecf20Sopenharmony_ci		chan = list_entry(ele, struct chan, free_list);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		if (chan->input && chan->enabled)
1988c2ecf20Sopenharmony_ci			um_free_irq(chan->line->driver->read_irq, chan);
1998c2ecf20Sopenharmony_ci		if (chan->output && chan->enabled)
2008c2ecf20Sopenharmony_ci			um_free_irq(chan->line->driver->write_irq, chan);
2018c2ecf20Sopenharmony_ci		chan->enabled = 0;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void close_one_chan(struct chan *chan, int delay_free_irq)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	unsigned long flags;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (!chan->opened)
2108c2ecf20Sopenharmony_ci		return;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (delay_free_irq) {
2138c2ecf20Sopenharmony_ci		spin_lock_irqsave(&irqs_to_free_lock, flags);
2148c2ecf20Sopenharmony_ci		list_add(&chan->free_list, &irqs_to_free);
2158c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&irqs_to_free_lock, flags);
2168c2ecf20Sopenharmony_ci	} else {
2178c2ecf20Sopenharmony_ci		if (chan->input && chan->enabled)
2188c2ecf20Sopenharmony_ci			um_free_irq(chan->line->driver->read_irq, chan);
2198c2ecf20Sopenharmony_ci		if (chan->output && chan->enabled)
2208c2ecf20Sopenharmony_ci			um_free_irq(chan->line->driver->write_irq, chan);
2218c2ecf20Sopenharmony_ci		chan->enabled = 0;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci	if (chan->ops->close != NULL)
2248c2ecf20Sopenharmony_ci		(*chan->ops->close)(chan->fd, chan->data);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	chan->opened = 0;
2278c2ecf20Sopenharmony_ci	chan->fd = -1;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_civoid close_chan(struct line *line)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct chan *chan;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* Close in reverse order as open in case more than one of them
2358c2ecf20Sopenharmony_ci	 * refers to the same device and they save and restore that device's
2368c2ecf20Sopenharmony_ci	 * state.  Then, the first one opened will have the original state,
2378c2ecf20Sopenharmony_ci	 * so it must be the last closed.
2388c2ecf20Sopenharmony_ci	 */
2398c2ecf20Sopenharmony_ci	list_for_each_entry_reverse(chan, &line->chan_list, list) {
2408c2ecf20Sopenharmony_ci		close_one_chan(chan, 0);
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_civoid deactivate_chan(struct chan *chan, int irq)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	if (chan && chan->enabled)
2478c2ecf20Sopenharmony_ci		deactivate_fd(chan->fd, irq);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ciint write_chan(struct chan *chan, const char *buf, int len,
2518c2ecf20Sopenharmony_ci	       int write_irq)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	int n, ret = 0;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (len == 0 || !chan || !chan->ops->write)
2568c2ecf20Sopenharmony_ci		return 0;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	n = chan->ops->write(chan->fd, buf, len, chan->data);
2598c2ecf20Sopenharmony_ci	if (chan->primary) {
2608c2ecf20Sopenharmony_ci		ret = n;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci	return ret;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ciint console_write_chan(struct chan *chan, const char *buf, int len)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	int n, ret = 0;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (!chan || !chan->ops->console_write)
2708c2ecf20Sopenharmony_ci		return 0;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	n = chan->ops->console_write(chan->fd, buf, len);
2738c2ecf20Sopenharmony_ci	if (chan->primary)
2748c2ecf20Sopenharmony_ci		ret = n;
2758c2ecf20Sopenharmony_ci	return ret;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ciint console_open_chan(struct line *line, struct console *co)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	int err;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	err = open_chan(&line->chan_list);
2838c2ecf20Sopenharmony_ci	if (err)
2848c2ecf20Sopenharmony_ci		return err;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
2878c2ecf20Sopenharmony_ci	       co->index);
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ciint chan_window_size(struct line *line, unsigned short *rows_out,
2928c2ecf20Sopenharmony_ci		      unsigned short *cols_out)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct chan *chan;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	chan = line->chan_in;
2978c2ecf20Sopenharmony_ci	if (chan && chan->primary) {
2988c2ecf20Sopenharmony_ci		if (chan->ops->window_size == NULL)
2998c2ecf20Sopenharmony_ci			return 0;
3008c2ecf20Sopenharmony_ci		return chan->ops->window_size(chan->fd, chan->data,
3018c2ecf20Sopenharmony_ci					      rows_out, cols_out);
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	chan = line->chan_out;
3048c2ecf20Sopenharmony_ci	if (chan && chan->primary) {
3058c2ecf20Sopenharmony_ci		if (chan->ops->window_size == NULL)
3068c2ecf20Sopenharmony_ci			return 0;
3078c2ecf20Sopenharmony_ci		return chan->ops->window_size(chan->fd, chan->data,
3088c2ecf20Sopenharmony_ci					      rows_out, cols_out);
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	return 0;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic void free_one_chan(struct chan *chan)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	list_del(&chan->list);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	close_one_chan(chan, 0);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (chan->ops->free != NULL)
3208c2ecf20Sopenharmony_ci		(*chan->ops->free)(chan->data);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (chan->primary && chan->output)
3238c2ecf20Sopenharmony_ci		ignore_sigio_fd(chan->fd);
3248c2ecf20Sopenharmony_ci	kfree(chan);
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic void free_chan(struct list_head *chans)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct list_head *ele, *next;
3308c2ecf20Sopenharmony_ci	struct chan *chan;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	list_for_each_safe(ele, next, chans) {
3338c2ecf20Sopenharmony_ci		chan = list_entry(ele, struct chan, list);
3348c2ecf20Sopenharmony_ci		free_one_chan(chan);
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic int one_chan_config_string(struct chan *chan, char *str, int size,
3398c2ecf20Sopenharmony_ci				  char **error_out)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	int n = 0;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (chan == NULL) {
3448c2ecf20Sopenharmony_ci		CONFIG_CHUNK(str, size, n, "none", 1);
3458c2ecf20Sopenharmony_ci		return n;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (chan->dev == NULL) {
3518c2ecf20Sopenharmony_ci		CONFIG_CHUNK(str, size, n, "", 1);
3528c2ecf20Sopenharmony_ci		return n;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	CONFIG_CHUNK(str, size, n, ":", 0);
3568c2ecf20Sopenharmony_ci	CONFIG_CHUNK(str, size, n, chan->dev, 0);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return n;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int chan_pair_config_string(struct chan *in, struct chan *out,
3628c2ecf20Sopenharmony_ci				   char *str, int size, char **error_out)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	int n;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	n = one_chan_config_string(in, str, size, error_out);
3678c2ecf20Sopenharmony_ci	str += n;
3688c2ecf20Sopenharmony_ci	size -= n;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (in == out) {
3718c2ecf20Sopenharmony_ci		CONFIG_CHUNK(str, size, n, "", 1);
3728c2ecf20Sopenharmony_ci		return n;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	CONFIG_CHUNK(str, size, n, ",", 1);
3768c2ecf20Sopenharmony_ci	n = one_chan_config_string(out, str, size, error_out);
3778c2ecf20Sopenharmony_ci	str += n;
3788c2ecf20Sopenharmony_ci	size -= n;
3798c2ecf20Sopenharmony_ci	CONFIG_CHUNK(str, size, n, "", 1);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return n;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ciint chan_config_string(struct line *line, char *str, int size,
3858c2ecf20Sopenharmony_ci		       char **error_out)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct chan *in = line->chan_in, *out = line->chan_out;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (in && !in->primary)
3908c2ecf20Sopenharmony_ci		in = NULL;
3918c2ecf20Sopenharmony_ci	if (out && !out->primary)
3928c2ecf20Sopenharmony_ci		out = NULL;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return chan_pair_config_string(in, out, str, size, error_out);
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistruct chan_type {
3988c2ecf20Sopenharmony_ci	char *key;
3998c2ecf20Sopenharmony_ci	const struct chan_ops *ops;
4008c2ecf20Sopenharmony_ci};
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic const struct chan_type chan_table[] = {
4038c2ecf20Sopenharmony_ci	{ "fd", &fd_ops },
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci#ifdef CONFIG_NULL_CHAN
4068c2ecf20Sopenharmony_ci	{ "null", &null_ops },
4078c2ecf20Sopenharmony_ci#else
4088c2ecf20Sopenharmony_ci	{ "null", &not_configged_ops },
4098c2ecf20Sopenharmony_ci#endif
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci#ifdef CONFIG_PORT_CHAN
4128c2ecf20Sopenharmony_ci	{ "port", &port_ops },
4138c2ecf20Sopenharmony_ci#else
4148c2ecf20Sopenharmony_ci	{ "port", &not_configged_ops },
4158c2ecf20Sopenharmony_ci#endif
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci#ifdef CONFIG_PTY_CHAN
4188c2ecf20Sopenharmony_ci	{ "pty", &pty_ops },
4198c2ecf20Sopenharmony_ci	{ "pts", &pts_ops },
4208c2ecf20Sopenharmony_ci#else
4218c2ecf20Sopenharmony_ci	{ "pty", &not_configged_ops },
4228c2ecf20Sopenharmony_ci	{ "pts", &not_configged_ops },
4238c2ecf20Sopenharmony_ci#endif
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci#ifdef CONFIG_TTY_CHAN
4268c2ecf20Sopenharmony_ci	{ "tty", &tty_ops },
4278c2ecf20Sopenharmony_ci#else
4288c2ecf20Sopenharmony_ci	{ "tty", &not_configged_ops },
4298c2ecf20Sopenharmony_ci#endif
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci#ifdef CONFIG_XTERM_CHAN
4328c2ecf20Sopenharmony_ci	{ "xterm", &xterm_ops },
4338c2ecf20Sopenharmony_ci#else
4348c2ecf20Sopenharmony_ci	{ "xterm", &not_configged_ops },
4358c2ecf20Sopenharmony_ci#endif
4368c2ecf20Sopenharmony_ci};
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic struct chan *parse_chan(struct line *line, char *str, int device,
4398c2ecf20Sopenharmony_ci			       const struct chan_opts *opts, char **error_out)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	const struct chan_type *entry;
4428c2ecf20Sopenharmony_ci	const struct chan_ops *ops;
4438c2ecf20Sopenharmony_ci	struct chan *chan;
4448c2ecf20Sopenharmony_ci	void *data;
4458c2ecf20Sopenharmony_ci	int i;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	ops = NULL;
4488c2ecf20Sopenharmony_ci	data = NULL;
4498c2ecf20Sopenharmony_ci	for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
4508c2ecf20Sopenharmony_ci		entry = &chan_table[i];
4518c2ecf20Sopenharmony_ci		if (!strncmp(str, entry->key, strlen(entry->key))) {
4528c2ecf20Sopenharmony_ci			ops = entry->ops;
4538c2ecf20Sopenharmony_ci			str += strlen(entry->key);
4548c2ecf20Sopenharmony_ci			break;
4558c2ecf20Sopenharmony_ci		}
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	if (ops == NULL) {
4588c2ecf20Sopenharmony_ci		*error_out = "No match for configured backends";
4598c2ecf20Sopenharmony_ci		return NULL;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	data = (*ops->init)(str, device, opts);
4638c2ecf20Sopenharmony_ci	if (data == NULL) {
4648c2ecf20Sopenharmony_ci		*error_out = "Configuration failed";
4658c2ecf20Sopenharmony_ci		return NULL;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
4698c2ecf20Sopenharmony_ci	if (chan == NULL) {
4708c2ecf20Sopenharmony_ci		*error_out = "Memory allocation failed";
4718c2ecf20Sopenharmony_ci		return NULL;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci	*chan = ((struct chan) { .list	 	= LIST_HEAD_INIT(chan->list),
4748c2ecf20Sopenharmony_ci				 .free_list 	=
4758c2ecf20Sopenharmony_ci				 	LIST_HEAD_INIT(chan->free_list),
4768c2ecf20Sopenharmony_ci				 .line		= line,
4778c2ecf20Sopenharmony_ci				 .primary	= 1,
4788c2ecf20Sopenharmony_ci				 .input		= 0,
4798c2ecf20Sopenharmony_ci				 .output 	= 0,
4808c2ecf20Sopenharmony_ci				 .opened  	= 0,
4818c2ecf20Sopenharmony_ci				 .enabled  	= 0,
4828c2ecf20Sopenharmony_ci				 .fd 		= -1,
4838c2ecf20Sopenharmony_ci				 .ops 		= ops,
4848c2ecf20Sopenharmony_ci				 .data 		= data });
4858c2ecf20Sopenharmony_ci	return chan;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ciint parse_chan_pair(char *str, struct line *line, int device,
4898c2ecf20Sopenharmony_ci		    const struct chan_opts *opts, char **error_out)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct list_head *chans = &line->chan_list;
4928c2ecf20Sopenharmony_ci	struct chan *new;
4938c2ecf20Sopenharmony_ci	char *in, *out;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	if (!list_empty(chans)) {
4968c2ecf20Sopenharmony_ci		line->chan_in = line->chan_out = NULL;
4978c2ecf20Sopenharmony_ci		free_chan(chans);
4988c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(chans);
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (!str)
5028c2ecf20Sopenharmony_ci		return 0;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	out = strchr(str, ',');
5058c2ecf20Sopenharmony_ci	if (out != NULL) {
5068c2ecf20Sopenharmony_ci		in = str;
5078c2ecf20Sopenharmony_ci		*out = '\0';
5088c2ecf20Sopenharmony_ci		out++;
5098c2ecf20Sopenharmony_ci		new = parse_chan(line, in, device, opts, error_out);
5108c2ecf20Sopenharmony_ci		if (new == NULL)
5118c2ecf20Sopenharmony_ci			return -1;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci		new->input = 1;
5148c2ecf20Sopenharmony_ci		list_add(&new->list, chans);
5158c2ecf20Sopenharmony_ci		line->chan_in = new;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		new = parse_chan(line, out, device, opts, error_out);
5188c2ecf20Sopenharmony_ci		if (new == NULL)
5198c2ecf20Sopenharmony_ci			return -1;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci		list_add(&new->list, chans);
5228c2ecf20Sopenharmony_ci		new->output = 1;
5238c2ecf20Sopenharmony_ci		line->chan_out = new;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci	else {
5268c2ecf20Sopenharmony_ci		new = parse_chan(line, str, device, opts, error_out);
5278c2ecf20Sopenharmony_ci		if (new == NULL)
5288c2ecf20Sopenharmony_ci			return -1;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		list_add(&new->list, chans);
5318c2ecf20Sopenharmony_ci		new->input = 1;
5328c2ecf20Sopenharmony_ci		new->output = 1;
5338c2ecf20Sopenharmony_ci		line->chan_in = line->chan_out = new;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci	return 0;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_civoid chan_interrupt(struct line *line, int irq)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct tty_port *port = &line->port;
5418c2ecf20Sopenharmony_ci	struct chan *chan = line->chan_in;
5428c2ecf20Sopenharmony_ci	int err;
5438c2ecf20Sopenharmony_ci	char c;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (!chan || !chan->ops->read)
5468c2ecf20Sopenharmony_ci		goto out;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	do {
5498c2ecf20Sopenharmony_ci		if (!tty_buffer_request_room(port, 1)) {
5508c2ecf20Sopenharmony_ci			schedule_delayed_work(&line->task, 1);
5518c2ecf20Sopenharmony_ci			goto out;
5528c2ecf20Sopenharmony_ci		}
5538c2ecf20Sopenharmony_ci		err = chan->ops->read(chan->fd, &c, chan->data);
5548c2ecf20Sopenharmony_ci		if (err > 0)
5558c2ecf20Sopenharmony_ci			tty_insert_flip_char(port, c, TTY_NORMAL);
5568c2ecf20Sopenharmony_ci	} while (err > 0);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (err == -EIO) {
5598c2ecf20Sopenharmony_ci		if (chan->primary) {
5608c2ecf20Sopenharmony_ci			tty_port_tty_hangup(&line->port, false);
5618c2ecf20Sopenharmony_ci			if (line->chan_out != chan)
5628c2ecf20Sopenharmony_ci				close_one_chan(line->chan_out, 1);
5638c2ecf20Sopenharmony_ci		}
5648c2ecf20Sopenharmony_ci		close_one_chan(chan, 1);
5658c2ecf20Sopenharmony_ci		if (chan->primary)
5668c2ecf20Sopenharmony_ci			return;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci out:
5698c2ecf20Sopenharmony_ci	tty_flip_buffer_push(port);
5708c2ecf20Sopenharmony_ci}
571