xref: /kernel/linux/linux-5.10/arch/um/drivers/line.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/irqreturn.h>
78c2ecf20Sopenharmony_ci#include <linux/kd.h>
88c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "chan.h"
128c2ecf20Sopenharmony_ci#include <irq_kern.h>
138c2ecf20Sopenharmony_ci#include <irq_user.h>
148c2ecf20Sopenharmony_ci#include <kern_util.h>
158c2ecf20Sopenharmony_ci#include <os.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define LINE_BUFSIZE 4096
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic irqreturn_t line_interrupt(int irq, void *data)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct chan *chan = data;
228c2ecf20Sopenharmony_ci	struct line *line = chan->line;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	if (line)
258c2ecf20Sopenharmony_ci		chan_interrupt(line, irq);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * Returns the free space inside the ring buffer of this line.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * Should be called while holding line->lock (this does not modify data).
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistatic int write_room(struct line *line)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	int n;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (line->buffer == NULL)
408c2ecf20Sopenharmony_ci		return LINE_BUFSIZE - 1;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* This is for the case where the buffer is wrapped! */
438c2ecf20Sopenharmony_ci	n = line->head - line->tail;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (n <= 0)
468c2ecf20Sopenharmony_ci		n += LINE_BUFSIZE; /* The other case */
478c2ecf20Sopenharmony_ci	return n - 1;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ciint line_write_room(struct tty_struct *tty)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
538c2ecf20Sopenharmony_ci	unsigned long flags;
548c2ecf20Sopenharmony_ci	int room;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&line->lock, flags);
578c2ecf20Sopenharmony_ci	room = write_room(line);
588c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&line->lock, flags);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return room;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciint line_chars_in_buffer(struct tty_struct *tty)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
668c2ecf20Sopenharmony_ci	unsigned long flags;
678c2ecf20Sopenharmony_ci	int ret;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&line->lock, flags);
708c2ecf20Sopenharmony_ci	/* write_room subtracts 1 for the needed NULL, so we readd it.*/
718c2ecf20Sopenharmony_ci	ret = LINE_BUFSIZE - (write_room(line) + 1);
728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&line->lock, flags);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return ret;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci * This copies the content of buf into the circular buffer associated with
798c2ecf20Sopenharmony_ci * this line.
808c2ecf20Sopenharmony_ci * The return value is the number of characters actually copied, i.e. the ones
818c2ecf20Sopenharmony_ci * for which there was space: this function is not supposed to ever flush out
828c2ecf20Sopenharmony_ci * the circular buffer.
838c2ecf20Sopenharmony_ci *
848c2ecf20Sopenharmony_ci * Must be called while holding line->lock!
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_cistatic int buffer_data(struct line *line, const char *buf, int len)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int end, room;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (line->buffer == NULL) {
918c2ecf20Sopenharmony_ci		line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC);
928c2ecf20Sopenharmony_ci		if (line->buffer == NULL) {
938c2ecf20Sopenharmony_ci			printk(KERN_ERR "buffer_data - atomic allocation "
948c2ecf20Sopenharmony_ci			       "failed\n");
958c2ecf20Sopenharmony_ci			return 0;
968c2ecf20Sopenharmony_ci		}
978c2ecf20Sopenharmony_ci		line->head = line->buffer;
988c2ecf20Sopenharmony_ci		line->tail = line->buffer;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	room = write_room(line);
1028c2ecf20Sopenharmony_ci	len = (len > room) ? room : len;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	end = line->buffer + LINE_BUFSIZE - line->tail;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (len < end) {
1078c2ecf20Sopenharmony_ci		memcpy(line->tail, buf, len);
1088c2ecf20Sopenharmony_ci		line->tail += len;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	else {
1118c2ecf20Sopenharmony_ci		/* The circular buffer is wrapping */
1128c2ecf20Sopenharmony_ci		memcpy(line->tail, buf, end);
1138c2ecf20Sopenharmony_ci		buf += end;
1148c2ecf20Sopenharmony_ci		memcpy(line->buffer, buf, len - end);
1158c2ecf20Sopenharmony_ci		line->tail = line->buffer + len - end;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return len;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/*
1228c2ecf20Sopenharmony_ci * Flushes the ring buffer to the output channels. That is, write_chan is
1238c2ecf20Sopenharmony_ci * called, passing it line->head as buffer, and an appropriate count.
1248c2ecf20Sopenharmony_ci *
1258c2ecf20Sopenharmony_ci * On exit, returns 1 when the buffer is empty,
1268c2ecf20Sopenharmony_ci * 0 when the buffer is not empty on exit,
1278c2ecf20Sopenharmony_ci * and -errno when an error occurred.
1288c2ecf20Sopenharmony_ci *
1298c2ecf20Sopenharmony_ci * Must be called while holding line->lock!*/
1308c2ecf20Sopenharmony_cistatic int flush_buffer(struct line *line)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int n, count;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if ((line->buffer == NULL) || (line->head == line->tail))
1358c2ecf20Sopenharmony_ci		return 1;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (line->tail < line->head) {
1388c2ecf20Sopenharmony_ci		/* line->buffer + LINE_BUFSIZE is the end of the buffer! */
1398c2ecf20Sopenharmony_ci		count = line->buffer + LINE_BUFSIZE - line->head;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		n = write_chan(line->chan_out, line->head, count,
1428c2ecf20Sopenharmony_ci			       line->driver->write_irq);
1438c2ecf20Sopenharmony_ci		if (n < 0)
1448c2ecf20Sopenharmony_ci			return n;
1458c2ecf20Sopenharmony_ci		if (n == count) {
1468c2ecf20Sopenharmony_ci			/*
1478c2ecf20Sopenharmony_ci			 * We have flushed from ->head to buffer end, now we
1488c2ecf20Sopenharmony_ci			 * must flush only from the beginning to ->tail.
1498c2ecf20Sopenharmony_ci			 */
1508c2ecf20Sopenharmony_ci			line->head = line->buffer;
1518c2ecf20Sopenharmony_ci		} else {
1528c2ecf20Sopenharmony_ci			line->head += n;
1538c2ecf20Sopenharmony_ci			return 0;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	count = line->tail - line->head;
1588c2ecf20Sopenharmony_ci	n = write_chan(line->chan_out, line->head, count,
1598c2ecf20Sopenharmony_ci		       line->driver->write_irq);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (n < 0)
1628c2ecf20Sopenharmony_ci		return n;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	line->head += n;
1658c2ecf20Sopenharmony_ci	return line->head == line->tail;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_civoid line_flush_buffer(struct tty_struct *tty)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
1718c2ecf20Sopenharmony_ci	unsigned long flags;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&line->lock, flags);
1748c2ecf20Sopenharmony_ci	flush_buffer(line);
1758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&line->lock, flags);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/*
1798c2ecf20Sopenharmony_ci * We map both ->flush_chars and ->put_char (which go in pair) onto
1808c2ecf20Sopenharmony_ci * ->flush_buffer and ->write. Hope it's not that bad.
1818c2ecf20Sopenharmony_ci */
1828c2ecf20Sopenharmony_civoid line_flush_chars(struct tty_struct *tty)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	line_flush_buffer(tty);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ciint line_write(struct tty_struct *tty, const unsigned char *buf, int len)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
1908c2ecf20Sopenharmony_ci	unsigned long flags;
1918c2ecf20Sopenharmony_ci	int n, ret = 0;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&line->lock, flags);
1948c2ecf20Sopenharmony_ci	if (line->head != line->tail)
1958c2ecf20Sopenharmony_ci		ret = buffer_data(line, buf, len);
1968c2ecf20Sopenharmony_ci	else {
1978c2ecf20Sopenharmony_ci		n = write_chan(line->chan_out, buf, len,
1988c2ecf20Sopenharmony_ci			       line->driver->write_irq);
1998c2ecf20Sopenharmony_ci		if (n < 0) {
2008c2ecf20Sopenharmony_ci			ret = n;
2018c2ecf20Sopenharmony_ci			goto out_up;
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		len -= n;
2058c2ecf20Sopenharmony_ci		ret += n;
2068c2ecf20Sopenharmony_ci		if (len > 0)
2078c2ecf20Sopenharmony_ci			ret += buffer_data(line, buf + n, len);
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ciout_up:
2108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&line->lock, flags);
2118c2ecf20Sopenharmony_ci	return ret;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_civoid line_set_termios(struct tty_struct *tty, struct ktermios * old)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	/* nothing */
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_civoid line_throttle(struct tty_struct *tty)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	deactivate_chan(line->chan_in, line->driver->read_irq);
2248c2ecf20Sopenharmony_ci	line->throttled = 1;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_civoid line_unthrottle(struct tty_struct *tty)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	line->throttled = 0;
2328c2ecf20Sopenharmony_ci	chan_interrupt(line, line->driver->read_irq);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic irqreturn_t line_write_interrupt(int irq, void *data)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct chan *chan = data;
2388c2ecf20Sopenharmony_ci	struct line *line = chan->line;
2398c2ecf20Sopenharmony_ci	int err;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/*
2428c2ecf20Sopenharmony_ci	 * Interrupts are disabled here because genirq keep irqs disabled when
2438c2ecf20Sopenharmony_ci	 * calling the action handler.
2448c2ecf20Sopenharmony_ci	 */
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	spin_lock(&line->lock);
2478c2ecf20Sopenharmony_ci	err = flush_buffer(line);
2488c2ecf20Sopenharmony_ci	if (err == 0) {
2498c2ecf20Sopenharmony_ci		spin_unlock(&line->lock);
2508c2ecf20Sopenharmony_ci		return IRQ_NONE;
2518c2ecf20Sopenharmony_ci	} else if ((err < 0) && (err != -EAGAIN)) {
2528c2ecf20Sopenharmony_ci		line->head = line->buffer;
2538c2ecf20Sopenharmony_ci		line->tail = line->buffer;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	spin_unlock(&line->lock);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	tty_port_tty_wakeup(&line->port);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ciint line_setup_irq(int fd, int input, int output, struct line *line, void *data)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	const struct line_driver *driver = line->driver;
2658c2ecf20Sopenharmony_ci	int err = 0;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (input)
2688c2ecf20Sopenharmony_ci		err = um_request_irq(driver->read_irq, fd, IRQ_READ,
2698c2ecf20Sopenharmony_ci				     line_interrupt, IRQF_SHARED,
2708c2ecf20Sopenharmony_ci				     driver->read_irq_name, data);
2718c2ecf20Sopenharmony_ci	if (err)
2728c2ecf20Sopenharmony_ci		return err;
2738c2ecf20Sopenharmony_ci	if (output)
2748c2ecf20Sopenharmony_ci		err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
2758c2ecf20Sopenharmony_ci				     line_write_interrupt, IRQF_SHARED,
2768c2ecf20Sopenharmony_ci				     driver->write_irq_name, data);
2778c2ecf20Sopenharmony_ci	return err;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int line_activate(struct tty_port *port, struct tty_struct *tty)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	int ret;
2838c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	ret = enable_chan(line);
2868c2ecf20Sopenharmony_ci	if (ret)
2878c2ecf20Sopenharmony_ci		return ret;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (!line->sigio) {
2908c2ecf20Sopenharmony_ci		chan_enable_winch(line->chan_out, port);
2918c2ecf20Sopenharmony_ci		line->sigio = 1;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	chan_window_size(line, &tty->winsize.ws_row,
2958c2ecf20Sopenharmony_ci		&tty->winsize.ws_col);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic void unregister_winch(struct tty_struct *tty);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic void line_destruct(struct tty_port *port)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct tty_struct *tty = tty_port_tty_get(port);
3058c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (line->sigio) {
3088c2ecf20Sopenharmony_ci		unregister_winch(tty);
3098c2ecf20Sopenharmony_ci		line->sigio = 0;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic const struct tty_port_operations line_port_ops = {
3148c2ecf20Sopenharmony_ci	.activate = line_activate,
3158c2ecf20Sopenharmony_ci	.destruct = line_destruct,
3168c2ecf20Sopenharmony_ci};
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ciint line_open(struct tty_struct *tty, struct file *filp)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return tty_port_open(&line->port, tty, filp);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ciint line_install(struct tty_driver *driver, struct tty_struct *tty,
3268c2ecf20Sopenharmony_ci		 struct line *line)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	int ret;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ret = tty_standard_install(driver, tty);
3318c2ecf20Sopenharmony_ci	if (ret)
3328c2ecf20Sopenharmony_ci		return ret;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	tty->driver_data = line;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return 0;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_civoid line_close(struct tty_struct *tty, struct file * filp)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	tty_port_close(&line->port, tty, filp);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_civoid line_hangup(struct tty_struct *tty)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct line *line = tty->driver_data;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	tty_port_hangup(&line->port);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_civoid close_lines(struct line *lines, int nlines)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	int i;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	for(i = 0; i < nlines; i++)
3588c2ecf20Sopenharmony_ci		close_chan(&lines[i]);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ciint setup_one_line(struct line *lines, int n, char *init,
3628c2ecf20Sopenharmony_ci		   const struct chan_opts *opts, char **error_out)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct line *line = &lines[n];
3658c2ecf20Sopenharmony_ci	struct tty_driver *driver = line->driver->driver;
3668c2ecf20Sopenharmony_ci	int err = -EINVAL;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (line->port.count) {
3698c2ecf20Sopenharmony_ci		*error_out = "Device is already open";
3708c2ecf20Sopenharmony_ci		goto out;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (!strcmp(init, "none")) {
3748c2ecf20Sopenharmony_ci		if (line->valid) {
3758c2ecf20Sopenharmony_ci			line->valid = 0;
3768c2ecf20Sopenharmony_ci			kfree(line->init_str);
3778c2ecf20Sopenharmony_ci			tty_unregister_device(driver, n);
3788c2ecf20Sopenharmony_ci			parse_chan_pair(NULL, line, n, opts, error_out);
3798c2ecf20Sopenharmony_ci			err = 0;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci	} else {
3828c2ecf20Sopenharmony_ci		char *new = kstrdup(init, GFP_KERNEL);
3838c2ecf20Sopenharmony_ci		if (!new) {
3848c2ecf20Sopenharmony_ci			*error_out = "Failed to allocate memory";
3858c2ecf20Sopenharmony_ci			return -ENOMEM;
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci		if (line->valid) {
3888c2ecf20Sopenharmony_ci			tty_unregister_device(driver, n);
3898c2ecf20Sopenharmony_ci			kfree(line->init_str);
3908c2ecf20Sopenharmony_ci		}
3918c2ecf20Sopenharmony_ci		line->init_str = new;
3928c2ecf20Sopenharmony_ci		line->valid = 1;
3938c2ecf20Sopenharmony_ci		err = parse_chan_pair(new, line, n, opts, error_out);
3948c2ecf20Sopenharmony_ci		if (!err) {
3958c2ecf20Sopenharmony_ci			struct device *d = tty_port_register_device(&line->port,
3968c2ecf20Sopenharmony_ci					driver, n, NULL);
3978c2ecf20Sopenharmony_ci			if (IS_ERR(d)) {
3988c2ecf20Sopenharmony_ci				*error_out = "Failed to register device";
3998c2ecf20Sopenharmony_ci				err = PTR_ERR(d);
4008c2ecf20Sopenharmony_ci				parse_chan_pair(NULL, line, n, opts, error_out);
4018c2ecf20Sopenharmony_ci			}
4028c2ecf20Sopenharmony_ci		}
4038c2ecf20Sopenharmony_ci		if (err) {
4048c2ecf20Sopenharmony_ci			line->init_str = NULL;
4058c2ecf20Sopenharmony_ci			line->valid = 0;
4068c2ecf20Sopenharmony_ci			kfree(new);
4078c2ecf20Sopenharmony_ci		}
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ciout:
4108c2ecf20Sopenharmony_ci	return err;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/*
4148c2ecf20Sopenharmony_ci * Common setup code for both startup command line and mconsole initialization.
4158c2ecf20Sopenharmony_ci * @lines contains the array (of size @num) to modify;
4168c2ecf20Sopenharmony_ci * @init is the setup string;
4178c2ecf20Sopenharmony_ci * @error_out is an error string in the case of failure;
4188c2ecf20Sopenharmony_ci */
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ciint line_setup(char **conf, unsigned int num, char **def,
4218c2ecf20Sopenharmony_ci	       char *init, char *name)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	char *error;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (*init == '=') {
4268c2ecf20Sopenharmony_ci		/*
4278c2ecf20Sopenharmony_ci		 * We said con=/ssl= instead of con#=, so we are configuring all
4288c2ecf20Sopenharmony_ci		 * consoles at once.
4298c2ecf20Sopenharmony_ci		 */
4308c2ecf20Sopenharmony_ci		*def = init + 1;
4318c2ecf20Sopenharmony_ci	} else {
4328c2ecf20Sopenharmony_ci		char *end;
4338c2ecf20Sopenharmony_ci		unsigned n = simple_strtoul(init, &end, 0);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		if (*end != '=') {
4368c2ecf20Sopenharmony_ci			error = "Couldn't parse device number";
4378c2ecf20Sopenharmony_ci			goto out;
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci		if (n >= num) {
4408c2ecf20Sopenharmony_ci			error = "Device number out of range";
4418c2ecf20Sopenharmony_ci			goto out;
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci		conf[n] = end + 1;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci	return 0;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ciout:
4488c2ecf20Sopenharmony_ci	printk(KERN_ERR "Failed to set up %s with "
4498c2ecf20Sopenharmony_ci	       "configuration string \"%s\" : %s\n", name, init, error);
4508c2ecf20Sopenharmony_ci	return -EINVAL;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ciint line_config(struct line *lines, unsigned int num, char *str,
4548c2ecf20Sopenharmony_ci		const struct chan_opts *opts, char **error_out)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	char *end;
4578c2ecf20Sopenharmony_ci	int n;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	if (*str == '=') {
4608c2ecf20Sopenharmony_ci		*error_out = "Can't configure all devices from mconsole";
4618c2ecf20Sopenharmony_ci		return -EINVAL;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	n = simple_strtoul(str, &end, 0);
4658c2ecf20Sopenharmony_ci	if (*end++ != '=') {
4668c2ecf20Sopenharmony_ci		*error_out = "Couldn't parse device number";
4678c2ecf20Sopenharmony_ci		return -EINVAL;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci	if (n >= num) {
4708c2ecf20Sopenharmony_ci		*error_out = "Device number out of range";
4718c2ecf20Sopenharmony_ci		return -EINVAL;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	return setup_one_line(lines, n, end, opts, error_out);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ciint line_get_config(char *name, struct line *lines, unsigned int num, char *str,
4788c2ecf20Sopenharmony_ci		    int size, char **error_out)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct line *line;
4818c2ecf20Sopenharmony_ci	char *end;
4828c2ecf20Sopenharmony_ci	int dev, n = 0;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	dev = simple_strtoul(name, &end, 0);
4858c2ecf20Sopenharmony_ci	if ((*end != '\0') || (end == name)) {
4868c2ecf20Sopenharmony_ci		*error_out = "line_get_config failed to parse device number";
4878c2ecf20Sopenharmony_ci		return 0;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if ((dev < 0) || (dev >= num)) {
4918c2ecf20Sopenharmony_ci		*error_out = "device number out of range";
4928c2ecf20Sopenharmony_ci		return 0;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	line = &lines[dev];
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (!line->valid)
4988c2ecf20Sopenharmony_ci		CONFIG_CHUNK(str, size, n, "none", 1);
4998c2ecf20Sopenharmony_ci	else {
5008c2ecf20Sopenharmony_ci		struct tty_struct *tty = tty_port_tty_get(&line->port);
5018c2ecf20Sopenharmony_ci		if (tty == NULL) {
5028c2ecf20Sopenharmony_ci			CONFIG_CHUNK(str, size, n, line->init_str, 1);
5038c2ecf20Sopenharmony_ci		} else {
5048c2ecf20Sopenharmony_ci			n = chan_config_string(line, str, size, error_out);
5058c2ecf20Sopenharmony_ci			tty_kref_put(tty);
5068c2ecf20Sopenharmony_ci		}
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	return n;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ciint line_id(char **str, int *start_out, int *end_out)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	char *end;
5158c2ecf20Sopenharmony_ci	int n;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	n = simple_strtoul(*str, &end, 0);
5188c2ecf20Sopenharmony_ci	if ((*end != '\0') || (end == *str))
5198c2ecf20Sopenharmony_ci		return -1;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	*str = end;
5228c2ecf20Sopenharmony_ci	*start_out = n;
5238c2ecf20Sopenharmony_ci	*end_out = n;
5248c2ecf20Sopenharmony_ci	return n;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ciint line_remove(struct line *lines, unsigned int num, int n, char **error_out)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	if (n >= num) {
5308c2ecf20Sopenharmony_ci		*error_out = "Device number out of range";
5318c2ecf20Sopenharmony_ci		return -EINVAL;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci	return setup_one_line(lines, n, "none", NULL, error_out);
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ciint register_lines(struct line_driver *line_driver,
5378c2ecf20Sopenharmony_ci		   const struct tty_operations *ops,
5388c2ecf20Sopenharmony_ci		   struct line *lines, int nlines)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct tty_driver *driver = alloc_tty_driver(nlines);
5418c2ecf20Sopenharmony_ci	int err;
5428c2ecf20Sopenharmony_ci	int i;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (!driver)
5458c2ecf20Sopenharmony_ci		return -ENOMEM;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	driver->driver_name = line_driver->name;
5488c2ecf20Sopenharmony_ci	driver->name = line_driver->device_name;
5498c2ecf20Sopenharmony_ci	driver->major = line_driver->major;
5508c2ecf20Sopenharmony_ci	driver->minor_start = line_driver->minor_start;
5518c2ecf20Sopenharmony_ci	driver->type = line_driver->type;
5528c2ecf20Sopenharmony_ci	driver->subtype = line_driver->subtype;
5538c2ecf20Sopenharmony_ci	driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
5548c2ecf20Sopenharmony_ci	driver->init_termios = tty_std_termios;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	for (i = 0; i < nlines; i++) {
5578c2ecf20Sopenharmony_ci		tty_port_init(&lines[i].port);
5588c2ecf20Sopenharmony_ci		lines[i].port.ops = &line_port_ops;
5598c2ecf20Sopenharmony_ci		spin_lock_init(&lines[i].lock);
5608c2ecf20Sopenharmony_ci		lines[i].driver = line_driver;
5618c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&lines[i].chan_list);
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci	tty_set_operations(driver, ops);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	err = tty_register_driver(driver);
5668c2ecf20Sopenharmony_ci	if (err) {
5678c2ecf20Sopenharmony_ci		printk(KERN_ERR "register_lines : can't register %s driver\n",
5688c2ecf20Sopenharmony_ci		       line_driver->name);
5698c2ecf20Sopenharmony_ci		put_tty_driver(driver);
5708c2ecf20Sopenharmony_ci		for (i = 0; i < nlines; i++)
5718c2ecf20Sopenharmony_ci			tty_port_destroy(&lines[i].port);
5728c2ecf20Sopenharmony_ci		return err;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	line_driver->driver = driver;
5768c2ecf20Sopenharmony_ci	mconsole_register_dev(&line_driver->mc);
5778c2ecf20Sopenharmony_ci	return 0;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(winch_handler_lock);
5818c2ecf20Sopenharmony_cistatic LIST_HEAD(winch_handlers);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cistruct winch {
5848c2ecf20Sopenharmony_ci	struct list_head list;
5858c2ecf20Sopenharmony_ci	int fd;
5868c2ecf20Sopenharmony_ci	int tty_fd;
5878c2ecf20Sopenharmony_ci	int pid;
5888c2ecf20Sopenharmony_ci	struct tty_port *port;
5898c2ecf20Sopenharmony_ci	unsigned long stack;
5908c2ecf20Sopenharmony_ci	struct work_struct work;
5918c2ecf20Sopenharmony_ci};
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic void __free_winch(struct work_struct *work)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	struct winch *winch = container_of(work, struct winch, work);
5968c2ecf20Sopenharmony_ci	um_free_irq(WINCH_IRQ, winch);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (winch->pid != -1)
5998c2ecf20Sopenharmony_ci		os_kill_process(winch->pid, 1);
6008c2ecf20Sopenharmony_ci	if (winch->stack != 0)
6018c2ecf20Sopenharmony_ci		free_stack(winch->stack, 0);
6028c2ecf20Sopenharmony_ci	kfree(winch);
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic void free_winch(struct winch *winch)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	int fd = winch->fd;
6088c2ecf20Sopenharmony_ci	winch->fd = -1;
6098c2ecf20Sopenharmony_ci	if (fd != -1)
6108c2ecf20Sopenharmony_ci		os_close_file(fd);
6118c2ecf20Sopenharmony_ci	list_del(&winch->list);
6128c2ecf20Sopenharmony_ci	__free_winch(&winch->work);
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic irqreturn_t winch_interrupt(int irq, void *data)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct winch *winch = data;
6188c2ecf20Sopenharmony_ci	struct tty_struct *tty;
6198c2ecf20Sopenharmony_ci	struct line *line;
6208c2ecf20Sopenharmony_ci	int fd = winch->fd;
6218c2ecf20Sopenharmony_ci	int err;
6228c2ecf20Sopenharmony_ci	char c;
6238c2ecf20Sopenharmony_ci	struct pid *pgrp;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (fd != -1) {
6268c2ecf20Sopenharmony_ci		err = generic_read(fd, &c, NULL);
6278c2ecf20Sopenharmony_ci		if (err < 0) {
6288c2ecf20Sopenharmony_ci			if (err != -EAGAIN) {
6298c2ecf20Sopenharmony_ci				winch->fd = -1;
6308c2ecf20Sopenharmony_ci				list_del(&winch->list);
6318c2ecf20Sopenharmony_ci				os_close_file(fd);
6328c2ecf20Sopenharmony_ci				printk(KERN_ERR "winch_interrupt : "
6338c2ecf20Sopenharmony_ci				       "read failed, errno = %d\n", -err);
6348c2ecf20Sopenharmony_ci				printk(KERN_ERR "fd %d is losing SIGWINCH "
6358c2ecf20Sopenharmony_ci				       "support\n", winch->tty_fd);
6368c2ecf20Sopenharmony_ci				INIT_WORK(&winch->work, __free_winch);
6378c2ecf20Sopenharmony_ci				schedule_work(&winch->work);
6388c2ecf20Sopenharmony_ci				return IRQ_HANDLED;
6398c2ecf20Sopenharmony_ci			}
6408c2ecf20Sopenharmony_ci			goto out;
6418c2ecf20Sopenharmony_ci		}
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(winch->port);
6448c2ecf20Sopenharmony_ci	if (tty != NULL) {
6458c2ecf20Sopenharmony_ci		line = tty->driver_data;
6468c2ecf20Sopenharmony_ci		if (line != NULL) {
6478c2ecf20Sopenharmony_ci			chan_window_size(line, &tty->winsize.ws_row,
6488c2ecf20Sopenharmony_ci					 &tty->winsize.ws_col);
6498c2ecf20Sopenharmony_ci			pgrp = tty_get_pgrp(tty);
6508c2ecf20Sopenharmony_ci			if (pgrp)
6518c2ecf20Sopenharmony_ci				kill_pgrp(pgrp, SIGWINCH, 1);
6528c2ecf20Sopenharmony_ci			put_pid(pgrp);
6538c2ecf20Sopenharmony_ci		}
6548c2ecf20Sopenharmony_ci		tty_kref_put(tty);
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci out:
6578c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_civoid register_winch_irq(int fd, int tty_fd, int pid, struct tty_port *port,
6618c2ecf20Sopenharmony_ci			unsigned long stack)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct winch *winch;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	winch = kmalloc(sizeof(*winch), GFP_KERNEL);
6668c2ecf20Sopenharmony_ci	if (winch == NULL) {
6678c2ecf20Sopenharmony_ci		printk(KERN_ERR "register_winch_irq - kmalloc failed\n");
6688c2ecf20Sopenharmony_ci		goto cleanup;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	*winch = ((struct winch) { .list  	= LIST_HEAD_INIT(winch->list),
6728c2ecf20Sopenharmony_ci				   .fd  	= fd,
6738c2ecf20Sopenharmony_ci				   .tty_fd 	= tty_fd,
6748c2ecf20Sopenharmony_ci				   .pid  	= pid,
6758c2ecf20Sopenharmony_ci				   .port 	= port,
6768c2ecf20Sopenharmony_ci				   .stack	= stack });
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt,
6798c2ecf20Sopenharmony_ci			   IRQF_SHARED, "winch", winch) < 0) {
6808c2ecf20Sopenharmony_ci		printk(KERN_ERR "register_winch_irq - failed to register "
6818c2ecf20Sopenharmony_ci		       "IRQ\n");
6828c2ecf20Sopenharmony_ci		goto out_free;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	spin_lock(&winch_handler_lock);
6868c2ecf20Sopenharmony_ci	list_add(&winch->list, &winch_handlers);
6878c2ecf20Sopenharmony_ci	spin_unlock(&winch_handler_lock);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	return;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci out_free:
6928c2ecf20Sopenharmony_ci	kfree(winch);
6938c2ecf20Sopenharmony_ci cleanup:
6948c2ecf20Sopenharmony_ci	os_kill_process(pid, 1);
6958c2ecf20Sopenharmony_ci	os_close_file(fd);
6968c2ecf20Sopenharmony_ci	if (stack != 0)
6978c2ecf20Sopenharmony_ci		free_stack(stack, 0);
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic void unregister_winch(struct tty_struct *tty)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	struct list_head *ele, *next;
7038c2ecf20Sopenharmony_ci	struct winch *winch;
7048c2ecf20Sopenharmony_ci	struct tty_struct *wtty;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	spin_lock(&winch_handler_lock);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	list_for_each_safe(ele, next, &winch_handlers) {
7098c2ecf20Sopenharmony_ci		winch = list_entry(ele, struct winch, list);
7108c2ecf20Sopenharmony_ci		wtty = tty_port_tty_get(winch->port);
7118c2ecf20Sopenharmony_ci		if (wtty == tty) {
7128c2ecf20Sopenharmony_ci			free_winch(winch);
7138c2ecf20Sopenharmony_ci			break;
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci		tty_kref_put(wtty);
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci	spin_unlock(&winch_handler_lock);
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic void winch_cleanup(void)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct list_head *ele, *next;
7238c2ecf20Sopenharmony_ci	struct winch *winch;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	spin_lock(&winch_handler_lock);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	list_for_each_safe(ele, next, &winch_handlers) {
7288c2ecf20Sopenharmony_ci		winch = list_entry(ele, struct winch, list);
7298c2ecf20Sopenharmony_ci		free_winch(winch);
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	spin_unlock(&winch_handler_lock);
7338c2ecf20Sopenharmony_ci}
7348c2ecf20Sopenharmony_ci__uml_exitcall(winch_cleanup);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cichar *add_xterm_umid(char *base)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	char *umid, *title;
7398c2ecf20Sopenharmony_ci	int len;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	umid = get_umid();
7428c2ecf20Sopenharmony_ci	if (*umid == '\0')
7438c2ecf20Sopenharmony_ci		return base;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	len = strlen(base) + strlen(" ()") + strlen(umid) + 1;
7468c2ecf20Sopenharmony_ci	title = kmalloc(len, GFP_KERNEL);
7478c2ecf20Sopenharmony_ci	if (title == NULL) {
7488c2ecf20Sopenharmony_ci		printk(KERN_ERR "Failed to allocate buffer for xterm title\n");
7498c2ecf20Sopenharmony_ci		return base;
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	snprintf(title, len, "%s (%s)", base, umid);
7538c2ecf20Sopenharmony_ci	return title;
7548c2ecf20Sopenharmony_ci}
755