162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/tty.h>
862306a36Sopenharmony_ci#include <linux/tty_flip.h>
962306a36Sopenharmony_ci#include "chan.h"
1062306a36Sopenharmony_ci#include <os.h>
1162306a36Sopenharmony_ci#include <irq_kern.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#ifdef CONFIG_NOCONFIG_CHAN
1462306a36Sopenharmony_cistatic void *not_configged_init(char *str, int device,
1562306a36Sopenharmony_ci				const struct chan_opts *opts)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
1862306a36Sopenharmony_ci	       "UML\n");
1962306a36Sopenharmony_ci	return NULL;
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int not_configged_open(int input, int output, int primary, void *data,
2362306a36Sopenharmony_ci			      char **dev_out)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
2662306a36Sopenharmony_ci	       "UML\n");
2762306a36Sopenharmony_ci	return -ENODEV;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void not_configged_close(int fd, void *data)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
3362306a36Sopenharmony_ci	       "UML\n");
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int not_configged_read(int fd, char *c_out, void *data)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
3962306a36Sopenharmony_ci	       "UML\n");
4062306a36Sopenharmony_ci	return -EIO;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int not_configged_write(int fd, const char *buf, int len, void *data)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
4662306a36Sopenharmony_ci	       "UML\n");
4762306a36Sopenharmony_ci	return -EIO;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int not_configged_console_write(int fd, const char *buf, int len)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
5362306a36Sopenharmony_ci	       "UML\n");
5462306a36Sopenharmony_ci	return -EIO;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int not_configged_window_size(int fd, void *data, unsigned short *rows,
5862306a36Sopenharmony_ci				     unsigned short *cols)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
6162306a36Sopenharmony_ci	       "UML\n");
6262306a36Sopenharmony_ci	return -ENODEV;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void not_configged_free(void *data)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	printk(KERN_ERR "Using a channel type which is configured out of "
6862306a36Sopenharmony_ci	       "UML\n");
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic const struct chan_ops not_configged_ops = {
7262306a36Sopenharmony_ci	.init		= not_configged_init,
7362306a36Sopenharmony_ci	.open		= not_configged_open,
7462306a36Sopenharmony_ci	.close		= not_configged_close,
7562306a36Sopenharmony_ci	.read		= not_configged_read,
7662306a36Sopenharmony_ci	.write		= not_configged_write,
7762306a36Sopenharmony_ci	.console_write	= not_configged_console_write,
7862306a36Sopenharmony_ci	.window_size	= not_configged_window_size,
7962306a36Sopenharmony_ci	.free		= not_configged_free,
8062306a36Sopenharmony_ci	.winch		= 0,
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci#endif /* CONFIG_NOCONFIG_CHAN */
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int open_one_chan(struct chan *chan)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	int fd, err;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (chan->opened)
8962306a36Sopenharmony_ci		return 0;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (chan->ops->open == NULL)
9262306a36Sopenharmony_ci		fd = 0;
9362306a36Sopenharmony_ci	else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
9462306a36Sopenharmony_ci				     chan->data, &chan->dev);
9562306a36Sopenharmony_ci	if (fd < 0)
9662306a36Sopenharmony_ci		return fd;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	err = os_set_fd_block(fd, 0);
9962306a36Sopenharmony_ci	if (err) {
10062306a36Sopenharmony_ci		(*chan->ops->close)(fd, chan->data);
10162306a36Sopenharmony_ci		return err;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	chan->fd = fd;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	chan->opened = 1;
10762306a36Sopenharmony_ci	return 0;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int open_chan(struct list_head *chans)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct list_head *ele;
11362306a36Sopenharmony_ci	struct chan *chan;
11462306a36Sopenharmony_ci	int ret, err = 0;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	list_for_each(ele, chans) {
11762306a36Sopenharmony_ci		chan = list_entry(ele, struct chan, list);
11862306a36Sopenharmony_ci		ret = open_one_chan(chan);
11962306a36Sopenharmony_ci		if (chan->primary)
12062306a36Sopenharmony_ci			err = ret;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci	return err;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_civoid chan_enable_winch(struct chan *chan, struct tty_port *port)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	if (chan && chan->primary && chan->ops->winch)
12862306a36Sopenharmony_ci		register_winch(chan->fd, port);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void line_timer_cb(struct work_struct *work)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct line *line = container_of(work, struct line, task.work);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (!line->throttled)
13662306a36Sopenharmony_ci		chan_interrupt(line, line->read_irq);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciint enable_chan(struct line *line)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct list_head *ele;
14262306a36Sopenharmony_ci	struct chan *chan;
14362306a36Sopenharmony_ci	int err;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&line->task, line_timer_cb);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	list_for_each(ele, &line->chan_list) {
14862306a36Sopenharmony_ci		chan = list_entry(ele, struct chan, list);
14962306a36Sopenharmony_ci		err = open_one_chan(chan);
15062306a36Sopenharmony_ci		if (err) {
15162306a36Sopenharmony_ci			if (chan->primary)
15262306a36Sopenharmony_ci				goto out_close;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci			continue;
15562306a36Sopenharmony_ci		}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		if (chan->enabled)
15862306a36Sopenharmony_ci			continue;
15962306a36Sopenharmony_ci		err = line_setup_irq(chan->fd, chan->input, chan->output, line,
16062306a36Sopenharmony_ci				     chan);
16162306a36Sopenharmony_ci		if (err)
16262306a36Sopenharmony_ci			goto out_close;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		chan->enabled = 1;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return 0;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci out_close:
17062306a36Sopenharmony_ci	close_chan(line);
17162306a36Sopenharmony_ci	return err;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/* Items are added in IRQ context, when free_irq can't be called, and
17562306a36Sopenharmony_ci * removed in process context, when it can.
17662306a36Sopenharmony_ci * This handles interrupt sources which disappear, and which need to
17762306a36Sopenharmony_ci * be permanently disabled.  This is discovered in IRQ context, but
17862306a36Sopenharmony_ci * the freeing of the IRQ must be done later.
17962306a36Sopenharmony_ci */
18062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(irqs_to_free_lock);
18162306a36Sopenharmony_cistatic LIST_HEAD(irqs_to_free);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_civoid free_irqs(void)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct chan *chan;
18662306a36Sopenharmony_ci	LIST_HEAD(list);
18762306a36Sopenharmony_ci	struct list_head *ele;
18862306a36Sopenharmony_ci	unsigned long flags;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	spin_lock_irqsave(&irqs_to_free_lock, flags);
19162306a36Sopenharmony_ci	list_splice_init(&irqs_to_free, &list);
19262306a36Sopenharmony_ci	spin_unlock_irqrestore(&irqs_to_free_lock, flags);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	list_for_each(ele, &list) {
19562306a36Sopenharmony_ci		chan = list_entry(ele, struct chan, free_list);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		if (chan->input && chan->enabled)
19862306a36Sopenharmony_ci			um_free_irq(chan->line->read_irq, chan);
19962306a36Sopenharmony_ci		if (chan->output && chan->enabled)
20062306a36Sopenharmony_ci			um_free_irq(chan->line->write_irq, chan);
20162306a36Sopenharmony_ci		chan->enabled = 0;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic void close_one_chan(struct chan *chan, int delay_free_irq)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	unsigned long flags;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (!chan->opened)
21062306a36Sopenharmony_ci		return;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (delay_free_irq) {
21362306a36Sopenharmony_ci		spin_lock_irqsave(&irqs_to_free_lock, flags);
21462306a36Sopenharmony_ci		list_add(&chan->free_list, &irqs_to_free);
21562306a36Sopenharmony_ci		spin_unlock_irqrestore(&irqs_to_free_lock, flags);
21662306a36Sopenharmony_ci	} else {
21762306a36Sopenharmony_ci		if (chan->input && chan->enabled)
21862306a36Sopenharmony_ci			um_free_irq(chan->line->read_irq, chan);
21962306a36Sopenharmony_ci		if (chan->output && chan->enabled)
22062306a36Sopenharmony_ci			um_free_irq(chan->line->write_irq, chan);
22162306a36Sopenharmony_ci		chan->enabled = 0;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	if (chan->ops->close != NULL)
22462306a36Sopenharmony_ci		(*chan->ops->close)(chan->fd, chan->data);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	chan->opened = 0;
22762306a36Sopenharmony_ci	chan->fd = -1;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_civoid close_chan(struct line *line)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct chan *chan;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* Close in reverse order as open in case more than one of them
23562306a36Sopenharmony_ci	 * refers to the same device and they save and restore that device's
23662306a36Sopenharmony_ci	 * state.  Then, the first one opened will have the original state,
23762306a36Sopenharmony_ci	 * so it must be the last closed.
23862306a36Sopenharmony_ci	 */
23962306a36Sopenharmony_ci	list_for_each_entry_reverse(chan, &line->chan_list, list) {
24062306a36Sopenharmony_ci		close_one_chan(chan, 0);
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_civoid deactivate_chan(struct chan *chan, int irq)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	if (chan && chan->enabled)
24762306a36Sopenharmony_ci		deactivate_fd(chan->fd, irq);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciint write_chan(struct chan *chan, const char *buf, int len,
25162306a36Sopenharmony_ci	       int write_irq)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	int n, ret = 0;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (len == 0 || !chan || !chan->ops->write)
25662306a36Sopenharmony_ci		return 0;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	n = chan->ops->write(chan->fd, buf, len, chan->data);
25962306a36Sopenharmony_ci	if (chan->primary) {
26062306a36Sopenharmony_ci		ret = n;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	return ret;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ciint console_write_chan(struct chan *chan, const char *buf, int len)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	int n, ret = 0;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (!chan || !chan->ops->console_write)
27062306a36Sopenharmony_ci		return 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	n = chan->ops->console_write(chan->fd, buf, len);
27362306a36Sopenharmony_ci	if (chan->primary)
27462306a36Sopenharmony_ci		ret = n;
27562306a36Sopenharmony_ci	return ret;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ciint console_open_chan(struct line *line, struct console *co)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	int err;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	err = open_chan(&line->chan_list);
28362306a36Sopenharmony_ci	if (err)
28462306a36Sopenharmony_ci		return err;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
28762306a36Sopenharmony_ci	       co->index);
28862306a36Sopenharmony_ci	return 0;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ciint chan_window_size(struct line *line, unsigned short *rows_out,
29262306a36Sopenharmony_ci		      unsigned short *cols_out)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct chan *chan;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	chan = line->chan_in;
29762306a36Sopenharmony_ci	if (chan && chan->primary) {
29862306a36Sopenharmony_ci		if (chan->ops->window_size == NULL)
29962306a36Sopenharmony_ci			return 0;
30062306a36Sopenharmony_ci		return chan->ops->window_size(chan->fd, chan->data,
30162306a36Sopenharmony_ci					      rows_out, cols_out);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci	chan = line->chan_out;
30462306a36Sopenharmony_ci	if (chan && chan->primary) {
30562306a36Sopenharmony_ci		if (chan->ops->window_size == NULL)
30662306a36Sopenharmony_ci			return 0;
30762306a36Sopenharmony_ci		return chan->ops->window_size(chan->fd, chan->data,
30862306a36Sopenharmony_ci					      rows_out, cols_out);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	return 0;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic void free_one_chan(struct chan *chan)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	list_del(&chan->list);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	close_one_chan(chan, 0);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (chan->ops->free != NULL)
32062306a36Sopenharmony_ci		(*chan->ops->free)(chan->data);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (chan->primary && chan->output)
32362306a36Sopenharmony_ci		ignore_sigio_fd(chan->fd);
32462306a36Sopenharmony_ci	kfree(chan);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic void free_chan(struct list_head *chans)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct list_head *ele, *next;
33062306a36Sopenharmony_ci	struct chan *chan;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	list_for_each_safe(ele, next, chans) {
33362306a36Sopenharmony_ci		chan = list_entry(ele, struct chan, list);
33462306a36Sopenharmony_ci		free_one_chan(chan);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic int one_chan_config_string(struct chan *chan, char *str, int size,
33962306a36Sopenharmony_ci				  char **error_out)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int n = 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (chan == NULL) {
34462306a36Sopenharmony_ci		CONFIG_CHUNK(str, size, n, "none", 1);
34562306a36Sopenharmony_ci		return n;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (chan->dev == NULL) {
35162306a36Sopenharmony_ci		CONFIG_CHUNK(str, size, n, "", 1);
35262306a36Sopenharmony_ci		return n;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	CONFIG_CHUNK(str, size, n, ":", 0);
35662306a36Sopenharmony_ci	CONFIG_CHUNK(str, size, n, chan->dev, 0);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return n;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int chan_pair_config_string(struct chan *in, struct chan *out,
36262306a36Sopenharmony_ci				   char *str, int size, char **error_out)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	int n;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	n = one_chan_config_string(in, str, size, error_out);
36762306a36Sopenharmony_ci	str += n;
36862306a36Sopenharmony_ci	size -= n;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (in == out) {
37162306a36Sopenharmony_ci		CONFIG_CHUNK(str, size, n, "", 1);
37262306a36Sopenharmony_ci		return n;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	CONFIG_CHUNK(str, size, n, ",", 1);
37662306a36Sopenharmony_ci	n = one_chan_config_string(out, str, size, error_out);
37762306a36Sopenharmony_ci	str += n;
37862306a36Sopenharmony_ci	size -= n;
37962306a36Sopenharmony_ci	CONFIG_CHUNK(str, size, n, "", 1);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return n;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ciint chan_config_string(struct line *line, char *str, int size,
38562306a36Sopenharmony_ci		       char **error_out)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct chan *in = line->chan_in, *out = line->chan_out;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (in && !in->primary)
39062306a36Sopenharmony_ci		in = NULL;
39162306a36Sopenharmony_ci	if (out && !out->primary)
39262306a36Sopenharmony_ci		out = NULL;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return chan_pair_config_string(in, out, str, size, error_out);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistruct chan_type {
39862306a36Sopenharmony_ci	char *key;
39962306a36Sopenharmony_ci	const struct chan_ops *ops;
40062306a36Sopenharmony_ci};
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic const struct chan_type chan_table[] = {
40362306a36Sopenharmony_ci	{ "fd", &fd_ops },
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci#ifdef CONFIG_NULL_CHAN
40662306a36Sopenharmony_ci	{ "null", &null_ops },
40762306a36Sopenharmony_ci#else
40862306a36Sopenharmony_ci	{ "null", &not_configged_ops },
40962306a36Sopenharmony_ci#endif
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci#ifdef CONFIG_PORT_CHAN
41262306a36Sopenharmony_ci	{ "port", &port_ops },
41362306a36Sopenharmony_ci#else
41462306a36Sopenharmony_ci	{ "port", &not_configged_ops },
41562306a36Sopenharmony_ci#endif
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci#ifdef CONFIG_PTY_CHAN
41862306a36Sopenharmony_ci	{ "pty", &pty_ops },
41962306a36Sopenharmony_ci	{ "pts", &pts_ops },
42062306a36Sopenharmony_ci#else
42162306a36Sopenharmony_ci	{ "pty", &not_configged_ops },
42262306a36Sopenharmony_ci	{ "pts", &not_configged_ops },
42362306a36Sopenharmony_ci#endif
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci#ifdef CONFIG_TTY_CHAN
42662306a36Sopenharmony_ci	{ "tty", &tty_ops },
42762306a36Sopenharmony_ci#else
42862306a36Sopenharmony_ci	{ "tty", &not_configged_ops },
42962306a36Sopenharmony_ci#endif
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci#ifdef CONFIG_XTERM_CHAN
43262306a36Sopenharmony_ci	{ "xterm", &xterm_ops },
43362306a36Sopenharmony_ci#else
43462306a36Sopenharmony_ci	{ "xterm", &not_configged_ops },
43562306a36Sopenharmony_ci#endif
43662306a36Sopenharmony_ci};
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic struct chan *parse_chan(struct line *line, char *str, int device,
43962306a36Sopenharmony_ci			       const struct chan_opts *opts, char **error_out)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	const struct chan_type *entry;
44262306a36Sopenharmony_ci	const struct chan_ops *ops;
44362306a36Sopenharmony_ci	struct chan *chan;
44462306a36Sopenharmony_ci	void *data;
44562306a36Sopenharmony_ci	int i;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ops = NULL;
44862306a36Sopenharmony_ci	data = NULL;
44962306a36Sopenharmony_ci	for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
45062306a36Sopenharmony_ci		entry = &chan_table[i];
45162306a36Sopenharmony_ci		if (!strncmp(str, entry->key, strlen(entry->key))) {
45262306a36Sopenharmony_ci			ops = entry->ops;
45362306a36Sopenharmony_ci			str += strlen(entry->key);
45462306a36Sopenharmony_ci			break;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci	if (ops == NULL) {
45862306a36Sopenharmony_ci		*error_out = "No match for configured backends";
45962306a36Sopenharmony_ci		return NULL;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	data = (*ops->init)(str, device, opts);
46362306a36Sopenharmony_ci	if (data == NULL) {
46462306a36Sopenharmony_ci		*error_out = "Configuration failed";
46562306a36Sopenharmony_ci		return NULL;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
46962306a36Sopenharmony_ci	if (chan == NULL) {
47062306a36Sopenharmony_ci		*error_out = "Memory allocation failed";
47162306a36Sopenharmony_ci		return NULL;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci	*chan = ((struct chan) { .list	 	= LIST_HEAD_INIT(chan->list),
47462306a36Sopenharmony_ci				 .free_list 	=
47562306a36Sopenharmony_ci				 	LIST_HEAD_INIT(chan->free_list),
47662306a36Sopenharmony_ci				 .line		= line,
47762306a36Sopenharmony_ci				 .primary	= 1,
47862306a36Sopenharmony_ci				 .input		= 0,
47962306a36Sopenharmony_ci				 .output 	= 0,
48062306a36Sopenharmony_ci				 .opened  	= 0,
48162306a36Sopenharmony_ci				 .enabled  	= 0,
48262306a36Sopenharmony_ci				 .fd 		= -1,
48362306a36Sopenharmony_ci				 .ops 		= ops,
48462306a36Sopenharmony_ci				 .data 		= data });
48562306a36Sopenharmony_ci	return chan;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ciint parse_chan_pair(char *str, struct line *line, int device,
48962306a36Sopenharmony_ci		    const struct chan_opts *opts, char **error_out)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct list_head *chans = &line->chan_list;
49262306a36Sopenharmony_ci	struct chan *new;
49362306a36Sopenharmony_ci	char *in, *out;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (!list_empty(chans)) {
49662306a36Sopenharmony_ci		line->chan_in = line->chan_out = NULL;
49762306a36Sopenharmony_ci		free_chan(chans);
49862306a36Sopenharmony_ci		INIT_LIST_HEAD(chans);
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (!str)
50262306a36Sopenharmony_ci		return 0;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	out = strchr(str, ',');
50562306a36Sopenharmony_ci	if (out != NULL) {
50662306a36Sopenharmony_ci		in = str;
50762306a36Sopenharmony_ci		*out = '\0';
50862306a36Sopenharmony_ci		out++;
50962306a36Sopenharmony_ci		new = parse_chan(line, in, device, opts, error_out);
51062306a36Sopenharmony_ci		if (new == NULL)
51162306a36Sopenharmony_ci			return -1;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		new->input = 1;
51462306a36Sopenharmony_ci		list_add(&new->list, chans);
51562306a36Sopenharmony_ci		line->chan_in = new;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		new = parse_chan(line, out, device, opts, error_out);
51862306a36Sopenharmony_ci		if (new == NULL)
51962306a36Sopenharmony_ci			return -1;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		list_add(&new->list, chans);
52262306a36Sopenharmony_ci		new->output = 1;
52362306a36Sopenharmony_ci		line->chan_out = new;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci	else {
52662306a36Sopenharmony_ci		new = parse_chan(line, str, device, opts, error_out);
52762306a36Sopenharmony_ci		if (new == NULL)
52862306a36Sopenharmony_ci			return -1;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		list_add(&new->list, chans);
53162306a36Sopenharmony_ci		new->input = 1;
53262306a36Sopenharmony_ci		new->output = 1;
53362306a36Sopenharmony_ci		line->chan_in = line->chan_out = new;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_civoid chan_interrupt(struct line *line, int irq)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct tty_port *port = &line->port;
54162306a36Sopenharmony_ci	struct chan *chan = line->chan_in;
54262306a36Sopenharmony_ci	int err;
54362306a36Sopenharmony_ci	char c;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (!chan || !chan->ops->read)
54662306a36Sopenharmony_ci		goto out;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	do {
54962306a36Sopenharmony_ci		if (!tty_buffer_request_room(port, 1)) {
55062306a36Sopenharmony_ci			schedule_delayed_work(&line->task, 1);
55162306a36Sopenharmony_ci			goto out;
55262306a36Sopenharmony_ci		}
55362306a36Sopenharmony_ci		err = chan->ops->read(chan->fd, &c, chan->data);
55462306a36Sopenharmony_ci		if (err > 0)
55562306a36Sopenharmony_ci			tty_insert_flip_char(port, c, TTY_NORMAL);
55662306a36Sopenharmony_ci	} while (err > 0);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (err == -EIO) {
55962306a36Sopenharmony_ci		if (chan->primary) {
56062306a36Sopenharmony_ci			tty_port_tty_hangup(&line->port, false);
56162306a36Sopenharmony_ci			if (line->chan_out != chan)
56262306a36Sopenharmony_ci				close_one_chan(line->chan_out, 1);
56362306a36Sopenharmony_ci		}
56462306a36Sopenharmony_ci		close_one_chan(chan, 1);
56562306a36Sopenharmony_ci		if (chan->primary)
56662306a36Sopenharmony_ci			return;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci out:
56962306a36Sopenharmony_ci	tty_flip_buffer_push(port);
57062306a36Sopenharmony_ci}
571