v4.19.13 snapshot.
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
new file mode 100644
index 0000000..05588f9
--- /dev/null
+++ b/arch/um/drivers/chan_kern.c
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
+ * Licensed under the GPL
+ */
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include "chan.h"
+#include <os.h>
+#include <irq_kern.h>
+
+#ifdef CONFIG_NOCONFIG_CHAN
+static void *not_configged_init(char *str, int device,
+				const struct chan_opts *opts)
+{
+	printk(KERN_ERR "Using a channel type which is configured out of "
+	       "UML\n");
+	return NULL;
+}
+
+static int not_configged_open(int input, int output, int primary, void *data,
+			      char **dev_out)
+{
+	printk(KERN_ERR "Using a channel type which is configured out of "
+	       "UML\n");
+	return -ENODEV;
+}
+
+static void not_configged_close(int fd, void *data)
+{
+	printk(KERN_ERR "Using a channel type which is configured out of "
+	       "UML\n");
+}
+
+static int not_configged_read(int fd, char *c_out, void *data)
+{
+	printk(KERN_ERR "Using a channel type which is configured out of "
+	       "UML\n");
+	return -EIO;
+}
+
+static int not_configged_write(int fd, const char *buf, int len, void *data)
+{
+	printk(KERN_ERR "Using a channel type which is configured out of "
+	       "UML\n");
+	return -EIO;
+}
+
+static int not_configged_console_write(int fd, const char *buf, int len)
+{
+	printk(KERN_ERR "Using a channel type which is configured out of "
+	       "UML\n");
+	return -EIO;
+}
+
+static int not_configged_window_size(int fd, void *data, unsigned short *rows,
+				     unsigned short *cols)
+{
+	printk(KERN_ERR "Using a channel type which is configured out of "
+	       "UML\n");
+	return -ENODEV;
+}
+
+static void not_configged_free(void *data)
+{
+	printk(KERN_ERR "Using a channel type which is configured out of "
+	       "UML\n");
+}
+
+static const struct chan_ops not_configged_ops = {
+	.init		= not_configged_init,
+	.open		= not_configged_open,
+	.close		= not_configged_close,
+	.read		= not_configged_read,
+	.write		= not_configged_write,
+	.console_write	= not_configged_console_write,
+	.window_size	= not_configged_window_size,
+	.free		= not_configged_free,
+	.winch		= 0,
+};
+#endif /* CONFIG_NOCONFIG_CHAN */
+
+static int open_one_chan(struct chan *chan)
+{
+	int fd, err;
+
+	if (chan->opened)
+		return 0;
+
+	if (chan->ops->open == NULL)
+		fd = 0;
+	else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
+				     chan->data, &chan->dev);
+	if (fd < 0)
+		return fd;
+
+	err = os_set_fd_block(fd, 0);
+	if (err) {
+		(*chan->ops->close)(fd, chan->data);
+		return err;
+	}
+
+	chan->fd = fd;
+
+	chan->opened = 1;
+	return 0;
+}
+
+static int open_chan(struct list_head *chans)
+{
+	struct list_head *ele;
+	struct chan *chan;
+	int ret, err = 0;
+
+	list_for_each(ele, chans) {
+		chan = list_entry(ele, struct chan, list);
+		ret = open_one_chan(chan);
+		if (chan->primary)
+			err = ret;
+	}
+	return err;
+}
+
+void chan_enable_winch(struct chan *chan, struct tty_port *port)
+{
+	if (chan && chan->primary && chan->ops->winch)
+		register_winch(chan->fd, port);
+}
+
+static void line_timer_cb(struct work_struct *work)
+{
+	struct line *line = container_of(work, struct line, task.work);
+
+	if (!line->throttled)
+		chan_interrupt(line, line->driver->read_irq);
+}
+
+int enable_chan(struct line *line)
+{
+	struct list_head *ele;
+	struct chan *chan;
+	int err;
+
+	INIT_DELAYED_WORK(&line->task, line_timer_cb);
+
+	list_for_each(ele, &line->chan_list) {
+		chan = list_entry(ele, struct chan, list);
+		err = open_one_chan(chan);
+		if (err) {
+			if (chan->primary)
+				goto out_close;
+
+			continue;
+		}
+
+		if (chan->enabled)
+			continue;
+		err = line_setup_irq(chan->fd, chan->input, chan->output, line,
+				     chan);
+		if (err)
+			goto out_close;
+
+		chan->enabled = 1;
+	}
+
+	return 0;
+
+ out_close:
+	close_chan(line);
+	return err;
+}
+
+static void close_one_chan(struct chan *chan, int delay_free_irq)
+{
+	if (!chan->opened)
+		return;
+
+    /* we can safely call free now - it will be marked
+     *  as free and freed once the IRQ stopped processing
+     */
+	if (chan->input && chan->enabled)
+		um_free_irq(chan->line->driver->read_irq, chan);
+	if (chan->output && chan->enabled)
+		um_free_irq(chan->line->driver->write_irq, chan);
+	chan->enabled = 0;
+	if (chan->ops->close != NULL)
+		(*chan->ops->close)(chan->fd, chan->data);
+
+	chan->opened = 0;
+	chan->fd = -1;
+}
+
+void close_chan(struct line *line)
+{
+	struct chan *chan;
+
+	/* Close in reverse order as open in case more than one of them
+	 * refers to the same device and they save and restore that device's
+	 * state.  Then, the first one opened will have the original state,
+	 * so it must be the last closed.
+	 */
+	list_for_each_entry_reverse(chan, &line->chan_list, list) {
+		close_one_chan(chan, 0);
+	}
+}
+
+void deactivate_chan(struct chan *chan, int irq)
+{
+	if (chan && chan->enabled)
+		deactivate_fd(chan->fd, irq);
+}
+
+void reactivate_chan(struct chan *chan, int irq)
+{
+	if (chan && chan->enabled)
+		reactivate_fd(chan->fd, irq);
+}
+
+int write_chan(struct chan *chan, const char *buf, int len,
+	       int write_irq)
+{
+	int n, ret = 0;
+
+	if (len == 0 || !chan || !chan->ops->write)
+		return 0;
+
+	n = chan->ops->write(chan->fd, buf, len, chan->data);
+	if (chan->primary) {
+		ret = n;
+		if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
+			reactivate_fd(chan->fd, write_irq);
+	}
+	return ret;
+}
+
+int console_write_chan(struct chan *chan, const char *buf, int len)
+{
+	int n, ret = 0;
+
+	if (!chan || !chan->ops->console_write)
+		return 0;
+
+	n = chan->ops->console_write(chan->fd, buf, len);
+	if (chan->primary)
+		ret = n;
+	return ret;
+}
+
+int console_open_chan(struct line *line, struct console *co)
+{
+	int err;
+
+	err = open_chan(&line->chan_list);
+	if (err)
+		return err;
+
+	printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
+	       co->index);
+	return 0;
+}
+
+int chan_window_size(struct line *line, unsigned short *rows_out,
+		      unsigned short *cols_out)
+{
+	struct chan *chan;
+
+	chan = line->chan_in;
+	if (chan && chan->primary) {
+		if (chan->ops->window_size == NULL)
+			return 0;
+		return chan->ops->window_size(chan->fd, chan->data,
+					      rows_out, cols_out);
+	}
+	chan = line->chan_out;
+	if (chan && chan->primary) {
+		if (chan->ops->window_size == NULL)
+			return 0;
+		return chan->ops->window_size(chan->fd, chan->data,
+					      rows_out, cols_out);
+	}
+	return 0;
+}
+
+static void free_one_chan(struct chan *chan)
+{
+	list_del(&chan->list);
+
+	close_one_chan(chan, 0);
+
+	if (chan->ops->free != NULL)
+		(*chan->ops->free)(chan->data);
+
+	if (chan->primary && chan->output)
+		ignore_sigio_fd(chan->fd);
+	kfree(chan);
+}
+
+static void free_chan(struct list_head *chans)
+{
+	struct list_head *ele, *next;
+	struct chan *chan;
+
+	list_for_each_safe(ele, next, chans) {
+		chan = list_entry(ele, struct chan, list);
+		free_one_chan(chan);
+	}
+}
+
+static int one_chan_config_string(struct chan *chan, char *str, int size,
+				  char **error_out)
+{
+	int n = 0;
+
+	if (chan == NULL) {
+		CONFIG_CHUNK(str, size, n, "none", 1);
+		return n;
+	}
+
+	CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
+
+	if (chan->dev == NULL) {
+		CONFIG_CHUNK(str, size, n, "", 1);
+		return n;
+	}
+
+	CONFIG_CHUNK(str, size, n, ":", 0);
+	CONFIG_CHUNK(str, size, n, chan->dev, 0);
+
+	return n;
+}
+
+static int chan_pair_config_string(struct chan *in, struct chan *out,
+				   char *str, int size, char **error_out)
+{
+	int n;
+
+	n = one_chan_config_string(in, str, size, error_out);
+	str += n;
+	size -= n;
+
+	if (in == out) {
+		CONFIG_CHUNK(str, size, n, "", 1);
+		return n;
+	}
+
+	CONFIG_CHUNK(str, size, n, ",", 1);
+	n = one_chan_config_string(out, str, size, error_out);
+	str += n;
+	size -= n;
+	CONFIG_CHUNK(str, size, n, "", 1);
+
+	return n;
+}
+
+int chan_config_string(struct line *line, char *str, int size,
+		       char **error_out)
+{
+	struct chan *in = line->chan_in, *out = line->chan_out;
+
+	if (in && !in->primary)
+		in = NULL;
+	if (out && !out->primary)
+		out = NULL;
+
+	return chan_pair_config_string(in, out, str, size, error_out);
+}
+
+struct chan_type {
+	char *key;
+	const struct chan_ops *ops;
+};
+
+static const struct chan_type chan_table[] = {
+	{ "fd", &fd_ops },
+
+#ifdef CONFIG_NULL_CHAN
+	{ "null", &null_ops },
+#else
+	{ "null", &not_configged_ops },
+#endif
+
+#ifdef CONFIG_PORT_CHAN
+	{ "port", &port_ops },
+#else
+	{ "port", &not_configged_ops },
+#endif
+
+#ifdef CONFIG_PTY_CHAN
+	{ "pty", &pty_ops },
+	{ "pts", &pts_ops },
+#else
+	{ "pty", &not_configged_ops },
+	{ "pts", &not_configged_ops },
+#endif
+
+#ifdef CONFIG_TTY_CHAN
+	{ "tty", &tty_ops },
+#else
+	{ "tty", &not_configged_ops },
+#endif
+
+#ifdef CONFIG_XTERM_CHAN
+	{ "xterm", &xterm_ops },
+#else
+	{ "xterm", &not_configged_ops },
+#endif
+};
+
+static struct chan *parse_chan(struct line *line, char *str, int device,
+			       const struct chan_opts *opts, char **error_out)
+{
+	const struct chan_type *entry;
+	const struct chan_ops *ops;
+	struct chan *chan;
+	void *data;
+	int i;
+
+	ops = NULL;
+	data = NULL;
+	for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
+		entry = &chan_table[i];
+		if (!strncmp(str, entry->key, strlen(entry->key))) {
+			ops = entry->ops;
+			str += strlen(entry->key);
+			break;
+		}
+	}
+	if (ops == NULL) {
+		*error_out = "No match for configured backends";
+		return NULL;
+	}
+
+	data = (*ops->init)(str, device, opts);
+	if (data == NULL) {
+		*error_out = "Configuration failed";
+		return NULL;
+	}
+
+	chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
+	if (chan == NULL) {
+		*error_out = "Memory allocation failed";
+		return NULL;
+	}
+	*chan = ((struct chan) { .list	 	= LIST_HEAD_INIT(chan->list),
+				 .free_list 	=
+				 	LIST_HEAD_INIT(chan->free_list),
+				 .line		= line,
+				 .primary	= 1,
+				 .input		= 0,
+				 .output 	= 0,
+				 .opened  	= 0,
+				 .enabled  	= 0,
+				 .fd 		= -1,
+				 .ops 		= ops,
+				 .data 		= data });
+	return chan;
+}
+
+int parse_chan_pair(char *str, struct line *line, int device,
+		    const struct chan_opts *opts, char **error_out)
+{
+	struct list_head *chans = &line->chan_list;
+	struct chan *new;
+	char *in, *out;
+
+	if (!list_empty(chans)) {
+		line->chan_in = line->chan_out = NULL;
+		free_chan(chans);
+		INIT_LIST_HEAD(chans);
+	}
+
+	if (!str)
+		return 0;
+
+	out = strchr(str, ',');
+	if (out != NULL) {
+		in = str;
+		*out = '\0';
+		out++;
+		new = parse_chan(line, in, device, opts, error_out);
+		if (new == NULL)
+			return -1;
+
+		new->input = 1;
+		list_add(&new->list, chans);
+		line->chan_in = new;
+
+		new = parse_chan(line, out, device, opts, error_out);
+		if (new == NULL)
+			return -1;
+
+		list_add(&new->list, chans);
+		new->output = 1;
+		line->chan_out = new;
+	}
+	else {
+		new = parse_chan(line, str, device, opts, error_out);
+		if (new == NULL)
+			return -1;
+
+		list_add(&new->list, chans);
+		new->input = 1;
+		new->output = 1;
+		line->chan_in = line->chan_out = new;
+	}
+	return 0;
+}
+
+void chan_interrupt(struct line *line, int irq)
+{
+	struct tty_port *port = &line->port;
+	struct chan *chan = line->chan_in;
+	int err;
+	char c;
+
+	if (!chan || !chan->ops->read)
+		goto out;
+
+	do {
+		if (!tty_buffer_request_room(port, 1)) {
+			schedule_delayed_work(&line->task, 1);
+			goto out;
+		}
+		err = chan->ops->read(chan->fd, &c, chan->data);
+		if (err > 0)
+			tty_insert_flip_char(port, c, TTY_NORMAL);
+	} while (err > 0);
+
+	if (err == 0)
+		reactivate_fd(chan->fd, irq);
+	if (err == -EIO) {
+		if (chan->primary) {
+			tty_port_tty_hangup(&line->port, false);
+			if (line->chan_out != chan)
+				close_one_chan(line->chan_out, 1);
+		}
+		close_one_chan(chan, 1);
+		if (chan->primary)
+			return;
+	}
+ out:
+	tty_flip_buffer_push(port);
+}