162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *    SCLP line mode terminal driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  S390 version
662306a36Sopenharmony_ci *    Copyright IBM Corp. 1999
762306a36Sopenharmony_ci *    Author(s): Martin Peschke <mpeschke@de.ibm.com>
862306a36Sopenharmony_ci *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kmod.h>
1262306a36Sopenharmony_ci#include <linux/tty.h>
1362306a36Sopenharmony_ci#include <linux/tty_driver.h>
1462306a36Sopenharmony_ci#include <linux/tty_flip.h>
1562306a36Sopenharmony_ci#include <linux/err.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/gfp.h>
1962306a36Sopenharmony_ci#include <linux/uaccess.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "ctrlchar.h"
2262306a36Sopenharmony_ci#include "sclp.h"
2362306a36Sopenharmony_ci#include "sclp_rw.h"
2462306a36Sopenharmony_ci#include "sclp_tty.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * size of a buffer that collects single characters coming in
2862306a36Sopenharmony_ci * via sclp_tty_put_char()
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci#define SCLP_TTY_BUF_SIZE 512
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * There is exactly one SCLP terminal, so we can keep things simple
3462306a36Sopenharmony_ci * and allocate all variables statically.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* Lock to guard over changes to global variables. */
3862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(sclp_tty_lock);
3962306a36Sopenharmony_ci/* List of free pages that can be used for console output buffering. */
4062306a36Sopenharmony_cistatic LIST_HEAD(sclp_tty_pages);
4162306a36Sopenharmony_ci/* List of full struct sclp_buffer structures ready for output. */
4262306a36Sopenharmony_cistatic LIST_HEAD(sclp_tty_outqueue);
4362306a36Sopenharmony_ci/* Counter how many buffers are emitted. */
4462306a36Sopenharmony_cistatic int sclp_tty_buffer_count;
4562306a36Sopenharmony_ci/* Pointer to current console buffer. */
4662306a36Sopenharmony_cistatic struct sclp_buffer *sclp_ttybuf;
4762306a36Sopenharmony_ci/* Timer for delayed output of console messages. */
4862306a36Sopenharmony_cistatic struct timer_list sclp_tty_timer;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic struct tty_port sclp_port;
5162306a36Sopenharmony_cistatic u8 sclp_tty_chars[SCLP_TTY_BUF_SIZE];
5262306a36Sopenharmony_cistatic unsigned short int sclp_tty_chars_count;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct tty_driver *sclp_tty_driver;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int sclp_tty_tolower;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define SCLP_TTY_COLUMNS 320
5962306a36Sopenharmony_ci#define SPACES_PER_TAB 8
6062306a36Sopenharmony_ci#define CASE_DELIMITER 0x6c /* to separate upper and lower case (% in EBCDIC) */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* This routine is called whenever we try to open a SCLP terminal. */
6362306a36Sopenharmony_cistatic int
6462306a36Sopenharmony_cisclp_tty_open(struct tty_struct *tty, struct file *filp)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	tty_port_tty_set(&sclp_port, tty);
6762306a36Sopenharmony_ci	tty->driver_data = NULL;
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* This routine is called when the SCLP terminal is closed. */
7262306a36Sopenharmony_cistatic void
7362306a36Sopenharmony_cisclp_tty_close(struct tty_struct *tty, struct file *filp)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	if (tty->count > 1)
7662306a36Sopenharmony_ci		return;
7762306a36Sopenharmony_ci	tty_port_tty_set(&sclp_port, NULL);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * This routine returns the numbers of characters the tty driver
8262306a36Sopenharmony_ci * will accept for queuing to be written.  This number is subject
8362306a36Sopenharmony_ci * to change as output buffers get emptied, or if the output flow
8462306a36Sopenharmony_ci * control is acted. This is not an exact number because not every
8562306a36Sopenharmony_ci * character needs the same space in the sccb. The worst case is
8662306a36Sopenharmony_ci * a string of newlines. Every newline creates a new message which
8762306a36Sopenharmony_ci * needs 82 bytes.
8862306a36Sopenharmony_ci */
8962306a36Sopenharmony_cistatic unsigned int
9062306a36Sopenharmony_cisclp_tty_write_room (struct tty_struct *tty)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	unsigned long flags;
9362306a36Sopenharmony_ci	struct list_head *l;
9462306a36Sopenharmony_ci	unsigned int count;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	spin_lock_irqsave(&sclp_tty_lock, flags);
9762306a36Sopenharmony_ci	count = 0;
9862306a36Sopenharmony_ci	if (sclp_ttybuf != NULL)
9962306a36Sopenharmony_ci		count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct msg_buf);
10062306a36Sopenharmony_ci	list_for_each(l, &sclp_tty_pages)
10162306a36Sopenharmony_ci		count += NR_EMPTY_MSG_PER_SCCB;
10262306a36Sopenharmony_ci	spin_unlock_irqrestore(&sclp_tty_lock, flags);
10362306a36Sopenharmony_ci	return count;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void
10762306a36Sopenharmony_cisclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	unsigned long flags;
11062306a36Sopenharmony_ci	void *page;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	do {
11362306a36Sopenharmony_ci		page = sclp_unmake_buffer(buffer);
11462306a36Sopenharmony_ci		spin_lock_irqsave(&sclp_tty_lock, flags);
11562306a36Sopenharmony_ci		/* Remove buffer from outqueue */
11662306a36Sopenharmony_ci		list_del(&buffer->list);
11762306a36Sopenharmony_ci		sclp_tty_buffer_count--;
11862306a36Sopenharmony_ci		list_add_tail((struct list_head *) page, &sclp_tty_pages);
11962306a36Sopenharmony_ci		/* Check if there is a pending buffer on the out queue. */
12062306a36Sopenharmony_ci		buffer = NULL;
12162306a36Sopenharmony_ci		if (!list_empty(&sclp_tty_outqueue))
12262306a36Sopenharmony_ci			buffer = list_entry(sclp_tty_outqueue.next,
12362306a36Sopenharmony_ci					    struct sclp_buffer, list);
12462306a36Sopenharmony_ci		spin_unlock_irqrestore(&sclp_tty_lock, flags);
12562306a36Sopenharmony_ci	} while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback));
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	tty_port_tty_wakeup(&sclp_port);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic inline void
13162306a36Sopenharmony_ci__sclp_ttybuf_emit(struct sclp_buffer *buffer)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	unsigned long flags;
13462306a36Sopenharmony_ci	int count;
13562306a36Sopenharmony_ci	int rc;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	spin_lock_irqsave(&sclp_tty_lock, flags);
13862306a36Sopenharmony_ci	list_add_tail(&buffer->list, &sclp_tty_outqueue);
13962306a36Sopenharmony_ci	count = sclp_tty_buffer_count++;
14062306a36Sopenharmony_ci	spin_unlock_irqrestore(&sclp_tty_lock, flags);
14162306a36Sopenharmony_ci	if (count)
14262306a36Sopenharmony_ci		return;
14362306a36Sopenharmony_ci	rc = sclp_emit_buffer(buffer, sclp_ttybuf_callback);
14462306a36Sopenharmony_ci	if (rc)
14562306a36Sopenharmony_ci		sclp_ttybuf_callback(buffer, rc);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/*
14962306a36Sopenharmony_ci * When this routine is called from the timer then we flush the
15062306a36Sopenharmony_ci * temporary write buffer.
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistatic void
15362306a36Sopenharmony_cisclp_tty_timeout(struct timer_list *unused)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	unsigned long flags;
15662306a36Sopenharmony_ci	struct sclp_buffer *buf;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	spin_lock_irqsave(&sclp_tty_lock, flags);
15962306a36Sopenharmony_ci	buf = sclp_ttybuf;
16062306a36Sopenharmony_ci	sclp_ttybuf = NULL;
16162306a36Sopenharmony_ci	spin_unlock_irqrestore(&sclp_tty_lock, flags);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (buf != NULL) {
16462306a36Sopenharmony_ci		__sclp_ttybuf_emit(buf);
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/*
16962306a36Sopenharmony_ci * Write a string to the sclp tty.
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_cistatic int sclp_tty_write_string(const u8 *str, int count, int may_fail)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	unsigned long flags;
17462306a36Sopenharmony_ci	void *page;
17562306a36Sopenharmony_ci	int written;
17662306a36Sopenharmony_ci	int overall_written;
17762306a36Sopenharmony_ci	struct sclp_buffer *buf;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (count <= 0)
18062306a36Sopenharmony_ci		return 0;
18162306a36Sopenharmony_ci	overall_written = 0;
18262306a36Sopenharmony_ci	spin_lock_irqsave(&sclp_tty_lock, flags);
18362306a36Sopenharmony_ci	do {
18462306a36Sopenharmony_ci		/* Create a sclp output buffer if none exists yet */
18562306a36Sopenharmony_ci		if (sclp_ttybuf == NULL) {
18662306a36Sopenharmony_ci			while (list_empty(&sclp_tty_pages)) {
18762306a36Sopenharmony_ci				spin_unlock_irqrestore(&sclp_tty_lock, flags);
18862306a36Sopenharmony_ci				if (may_fail)
18962306a36Sopenharmony_ci					goto out;
19062306a36Sopenharmony_ci				else
19162306a36Sopenharmony_ci					sclp_sync_wait();
19262306a36Sopenharmony_ci				spin_lock_irqsave(&sclp_tty_lock, flags);
19362306a36Sopenharmony_ci			}
19462306a36Sopenharmony_ci			page = sclp_tty_pages.next;
19562306a36Sopenharmony_ci			list_del((struct list_head *) page);
19662306a36Sopenharmony_ci			sclp_ttybuf = sclp_make_buffer(page, SCLP_TTY_COLUMNS,
19762306a36Sopenharmony_ci						       SPACES_PER_TAB);
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci		/* try to write the string to the current output buffer */
20062306a36Sopenharmony_ci		written = sclp_write(sclp_ttybuf, str, count);
20162306a36Sopenharmony_ci		overall_written += written;
20262306a36Sopenharmony_ci		if (written == count)
20362306a36Sopenharmony_ci			break;
20462306a36Sopenharmony_ci		/*
20562306a36Sopenharmony_ci		 * Not all characters could be written to the current
20662306a36Sopenharmony_ci		 * output buffer. Emit the buffer, create a new buffer
20762306a36Sopenharmony_ci		 * and then output the rest of the string.
20862306a36Sopenharmony_ci		 */
20962306a36Sopenharmony_ci		buf = sclp_ttybuf;
21062306a36Sopenharmony_ci		sclp_ttybuf = NULL;
21162306a36Sopenharmony_ci		spin_unlock_irqrestore(&sclp_tty_lock, flags);
21262306a36Sopenharmony_ci		__sclp_ttybuf_emit(buf);
21362306a36Sopenharmony_ci		spin_lock_irqsave(&sclp_tty_lock, flags);
21462306a36Sopenharmony_ci		str += written;
21562306a36Sopenharmony_ci		count -= written;
21662306a36Sopenharmony_ci	} while (count > 0);
21762306a36Sopenharmony_ci	/* Setup timer to output current console buffer after 1/10 second */
21862306a36Sopenharmony_ci	if (sclp_ttybuf && sclp_chars_in_buffer(sclp_ttybuf) &&
21962306a36Sopenharmony_ci	    !timer_pending(&sclp_tty_timer)) {
22062306a36Sopenharmony_ci		mod_timer(&sclp_tty_timer, jiffies + HZ / 10);
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	spin_unlock_irqrestore(&sclp_tty_lock, flags);
22362306a36Sopenharmony_ciout:
22462306a36Sopenharmony_ci	return overall_written;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/*
22862306a36Sopenharmony_ci * This routine is called by the kernel to write a series of characters to the
22962306a36Sopenharmony_ci * tty device. The characters may come from user space or kernel space. This
23062306a36Sopenharmony_ci * routine will return the number of characters actually accepted for writing.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_cistatic ssize_t
23362306a36Sopenharmony_cisclp_tty_write(struct tty_struct *tty, const u8 *buf, size_t count)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	if (sclp_tty_chars_count > 0) {
23662306a36Sopenharmony_ci		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
23762306a36Sopenharmony_ci		sclp_tty_chars_count = 0;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci	return sclp_tty_write_string(buf, count, 1);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/*
24362306a36Sopenharmony_ci * This routine is called by the kernel to write a single character to the tty
24462306a36Sopenharmony_ci * device. If the kernel uses this routine, it must call the flush_chars()
24562306a36Sopenharmony_ci * routine (if defined) when it is done stuffing characters into the driver.
24662306a36Sopenharmony_ci *
24762306a36Sopenharmony_ci * Characters provided to sclp_tty_put_char() are buffered by the SCLP driver.
24862306a36Sopenharmony_ci * If the given character is a '\n' the contents of the SCLP write buffer
24962306a36Sopenharmony_ci * - including previous characters from sclp_tty_put_char() and strings from
25062306a36Sopenharmony_ci * sclp_write() without final '\n' - will be written.
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_cistatic int
25362306a36Sopenharmony_cisclp_tty_put_char(struct tty_struct *tty, u8 ch)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	sclp_tty_chars[sclp_tty_chars_count++] = ch;
25662306a36Sopenharmony_ci	if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) {
25762306a36Sopenharmony_ci		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
25862306a36Sopenharmony_ci		sclp_tty_chars_count = 0;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci	return 1;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci/*
26462306a36Sopenharmony_ci * This routine is called by the kernel after it has written a series of
26562306a36Sopenharmony_ci * characters to the tty device using put_char().
26662306a36Sopenharmony_ci */
26762306a36Sopenharmony_cistatic void
26862306a36Sopenharmony_cisclp_tty_flush_chars(struct tty_struct *tty)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	if (sclp_tty_chars_count > 0) {
27162306a36Sopenharmony_ci		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
27262306a36Sopenharmony_ci		sclp_tty_chars_count = 0;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * This routine returns the number of characters in the write buffer of the
27862306a36Sopenharmony_ci * SCLP driver. The provided number includes all characters that are stored
27962306a36Sopenharmony_ci * in the SCCB (will be written next time the SCLP is not busy) as well as
28062306a36Sopenharmony_ci * characters in the write buffer (will not be written as long as there is a
28162306a36Sopenharmony_ci * final line feed missing).
28262306a36Sopenharmony_ci */
28362306a36Sopenharmony_cistatic unsigned int
28462306a36Sopenharmony_cisclp_tty_chars_in_buffer(struct tty_struct *tty)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	unsigned long flags;
28762306a36Sopenharmony_ci	struct sclp_buffer *t;
28862306a36Sopenharmony_ci	unsigned int count = 0;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	spin_lock_irqsave(&sclp_tty_lock, flags);
29162306a36Sopenharmony_ci	if (sclp_ttybuf != NULL)
29262306a36Sopenharmony_ci		count = sclp_chars_in_buffer(sclp_ttybuf);
29362306a36Sopenharmony_ci	list_for_each_entry(t, &sclp_tty_outqueue, list) {
29462306a36Sopenharmony_ci		count += sclp_chars_in_buffer(t);
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci	spin_unlock_irqrestore(&sclp_tty_lock, flags);
29762306a36Sopenharmony_ci	return count;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/*
30162306a36Sopenharmony_ci * removes all content from buffers of low level driver
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_cistatic void
30462306a36Sopenharmony_cisclp_tty_flush_buffer(struct tty_struct *tty)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	if (sclp_tty_chars_count > 0) {
30762306a36Sopenharmony_ci		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
30862306a36Sopenharmony_ci		sclp_tty_chars_count = 0;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/*
31362306a36Sopenharmony_ci * push input to tty
31462306a36Sopenharmony_ci */
31562306a36Sopenharmony_cistatic void
31662306a36Sopenharmony_cisclp_tty_input(unsigned char* buf, unsigned int count)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct tty_struct *tty = tty_port_tty_get(&sclp_port);
31962306a36Sopenharmony_ci	unsigned int cchar;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/*
32262306a36Sopenharmony_ci	 * If this tty driver is currently closed
32362306a36Sopenharmony_ci	 * then throw the received input away.
32462306a36Sopenharmony_ci	 */
32562306a36Sopenharmony_ci	if (tty == NULL)
32662306a36Sopenharmony_ci		return;
32762306a36Sopenharmony_ci	cchar = ctrlchar_handle(buf, count, tty);
32862306a36Sopenharmony_ci	switch (cchar & CTRLCHAR_MASK) {
32962306a36Sopenharmony_ci	case CTRLCHAR_SYSRQ:
33062306a36Sopenharmony_ci		break;
33162306a36Sopenharmony_ci	case CTRLCHAR_CTRL:
33262306a36Sopenharmony_ci		tty_insert_flip_char(&sclp_port, cchar, TTY_NORMAL);
33362306a36Sopenharmony_ci		tty_flip_buffer_push(&sclp_port);
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case CTRLCHAR_NONE:
33662306a36Sopenharmony_ci		/* send (normal) input to line discipline */
33762306a36Sopenharmony_ci		if (count < 2 ||
33862306a36Sopenharmony_ci		    (strncmp((const char *) buf + count - 2, "^n", 2) &&
33962306a36Sopenharmony_ci		     strncmp((const char *) buf + count - 2, "\252n", 2))) {
34062306a36Sopenharmony_ci			/* add the auto \n */
34162306a36Sopenharmony_ci			tty_insert_flip_string(&sclp_port, buf, count);
34262306a36Sopenharmony_ci			tty_insert_flip_char(&sclp_port, '\n', TTY_NORMAL);
34362306a36Sopenharmony_ci		} else
34462306a36Sopenharmony_ci			tty_insert_flip_string(&sclp_port, buf, count - 2);
34562306a36Sopenharmony_ci		tty_flip_buffer_push(&sclp_port);
34662306a36Sopenharmony_ci		break;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	tty_kref_put(tty);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci/*
35262306a36Sopenharmony_ci * get a EBCDIC string in upper/lower case,
35362306a36Sopenharmony_ci * find out characters in lower/upper case separated by a special character,
35462306a36Sopenharmony_ci * modifiy original string,
35562306a36Sopenharmony_ci * returns length of resulting string
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_cistatic int sclp_switch_cases(unsigned char *buf, int count)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	unsigned char *ip, *op;
36062306a36Sopenharmony_ci	int toggle;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* initially changing case is off */
36362306a36Sopenharmony_ci	toggle = 0;
36462306a36Sopenharmony_ci	ip = op = buf;
36562306a36Sopenharmony_ci	while (count-- > 0) {
36662306a36Sopenharmony_ci		/* compare with special character */
36762306a36Sopenharmony_ci		if (*ip == CASE_DELIMITER) {
36862306a36Sopenharmony_ci			/* followed by another special character? */
36962306a36Sopenharmony_ci			if (count && ip[1] == CASE_DELIMITER) {
37062306a36Sopenharmony_ci				/*
37162306a36Sopenharmony_ci				 * ... then put a single copy of the special
37262306a36Sopenharmony_ci				 * character to the output string
37362306a36Sopenharmony_ci				 */
37462306a36Sopenharmony_ci				*op++ = *ip++;
37562306a36Sopenharmony_ci				count--;
37662306a36Sopenharmony_ci			} else
37762306a36Sopenharmony_ci				/*
37862306a36Sopenharmony_ci				 * ... special character follower by a normal
37962306a36Sopenharmony_ci				 * character toggles the case change behaviour
38062306a36Sopenharmony_ci				 */
38162306a36Sopenharmony_ci				toggle = ~toggle;
38262306a36Sopenharmony_ci			/* skip special character */
38362306a36Sopenharmony_ci			ip++;
38462306a36Sopenharmony_ci		} else
38562306a36Sopenharmony_ci			/* not the special character */
38662306a36Sopenharmony_ci			if (toggle)
38762306a36Sopenharmony_ci				/* but case switching is on */
38862306a36Sopenharmony_ci				if (sclp_tty_tolower)
38962306a36Sopenharmony_ci					/* switch to uppercase */
39062306a36Sopenharmony_ci					*op++ = _ebc_toupper[(int) *ip++];
39162306a36Sopenharmony_ci				else
39262306a36Sopenharmony_ci					/* switch to lowercase */
39362306a36Sopenharmony_ci					*op++ = _ebc_tolower[(int) *ip++];
39462306a36Sopenharmony_ci			else
39562306a36Sopenharmony_ci				/* no case switching, copy the character */
39662306a36Sopenharmony_ci				*op++ = *ip++;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci	/* return length of reformatted string. */
39962306a36Sopenharmony_ci	return op - buf;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic void sclp_get_input(struct gds_subvector *sv)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	unsigned char *str;
40562306a36Sopenharmony_ci	int count;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	str = (unsigned char *) (sv + 1);
40862306a36Sopenharmony_ci	count = sv->length - sizeof(*sv);
40962306a36Sopenharmony_ci	if (sclp_tty_tolower)
41062306a36Sopenharmony_ci		EBC_TOLOWER(str, count);
41162306a36Sopenharmony_ci	count = sclp_switch_cases(str, count);
41262306a36Sopenharmony_ci	/* convert EBCDIC to ASCII (modify original input in SCCB) */
41362306a36Sopenharmony_ci	sclp_ebcasc_str(str, count);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* transfer input to high level driver */
41662306a36Sopenharmony_ci	sclp_tty_input(str, count);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic inline void sclp_eval_selfdeftextmsg(struct gds_subvector *sv)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	void *end;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	end = (void *) sv + sv->length;
42462306a36Sopenharmony_ci	for (sv = sv + 1; (void *) sv < end; sv = (void *) sv + sv->length)
42562306a36Sopenharmony_ci		if (sv->key == 0x30)
42662306a36Sopenharmony_ci			sclp_get_input(sv);
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic inline void sclp_eval_textcmd(struct gds_vector *v)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct gds_subvector *sv;
43262306a36Sopenharmony_ci	void *end;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	end = (void *) v + v->length;
43562306a36Sopenharmony_ci	for (sv = (struct gds_subvector *) (v + 1);
43662306a36Sopenharmony_ci	     (void *) sv < end; sv = (void *) sv + sv->length)
43762306a36Sopenharmony_ci		if (sv->key == GDS_KEY_SELFDEFTEXTMSG)
43862306a36Sopenharmony_ci			sclp_eval_selfdeftextmsg(sv);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic inline void sclp_eval_cpmsu(struct gds_vector *v)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	void *end;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	end = (void *) v + v->length;
44762306a36Sopenharmony_ci	for (v = v + 1; (void *) v < end; v = (void *) v + v->length)
44862306a36Sopenharmony_ci		if (v->gds_id == GDS_ID_TEXTCMD)
44962306a36Sopenharmony_ci			sclp_eval_textcmd(v);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic inline void sclp_eval_mdsmu(struct gds_vector *v)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	v = sclp_find_gds_vector(v + 1, (void *) v + v->length, GDS_ID_CPMSU);
45662306a36Sopenharmony_ci	if (v)
45762306a36Sopenharmony_ci		sclp_eval_cpmsu(v);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic void sclp_tty_receiver(struct evbuf_header *evbuf)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct gds_vector *v;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
46562306a36Sopenharmony_ci				 GDS_ID_MDSMU);
46662306a36Sopenharmony_ci	if (v)
46762306a36Sopenharmony_ci		sclp_eval_mdsmu(v);
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic void
47162306a36Sopenharmony_cisclp_tty_state_change(struct sclp_register *reg)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic struct sclp_register sclp_input_event =
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	.receive_mask = EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK,
47862306a36Sopenharmony_ci	.state_change_fn = sclp_tty_state_change,
47962306a36Sopenharmony_ci	.receiver_fn = sclp_tty_receiver
48062306a36Sopenharmony_ci};
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic const struct tty_operations sclp_ops = {
48362306a36Sopenharmony_ci	.open = sclp_tty_open,
48462306a36Sopenharmony_ci	.close = sclp_tty_close,
48562306a36Sopenharmony_ci	.write = sclp_tty_write,
48662306a36Sopenharmony_ci	.put_char = sclp_tty_put_char,
48762306a36Sopenharmony_ci	.flush_chars = sclp_tty_flush_chars,
48862306a36Sopenharmony_ci	.write_room = sclp_tty_write_room,
48962306a36Sopenharmony_ci	.chars_in_buffer = sclp_tty_chars_in_buffer,
49062306a36Sopenharmony_ci	.flush_buffer = sclp_tty_flush_buffer,
49162306a36Sopenharmony_ci};
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic int __init
49462306a36Sopenharmony_cisclp_tty_init(void)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct tty_driver *driver;
49762306a36Sopenharmony_ci	void *page;
49862306a36Sopenharmony_ci	int i;
49962306a36Sopenharmony_ci	int rc;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* z/VM multiplexes the line mode output on the 32xx screen */
50262306a36Sopenharmony_ci	if (MACHINE_IS_VM && !CONSOLE_IS_SCLP)
50362306a36Sopenharmony_ci		return 0;
50462306a36Sopenharmony_ci	if (!sclp.has_linemode)
50562306a36Sopenharmony_ci		return 0;
50662306a36Sopenharmony_ci	driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW);
50762306a36Sopenharmony_ci	if (IS_ERR(driver))
50862306a36Sopenharmony_ci		return PTR_ERR(driver);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	rc = sclp_rw_init();
51162306a36Sopenharmony_ci	if (rc) {
51262306a36Sopenharmony_ci		tty_driver_kref_put(driver);
51362306a36Sopenharmony_ci		return rc;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci	/* Allocate pages for output buffering */
51662306a36Sopenharmony_ci	for (i = 0; i < MAX_KMEM_PAGES; i++) {
51762306a36Sopenharmony_ci		page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
51862306a36Sopenharmony_ci		if (page == NULL) {
51962306a36Sopenharmony_ci			tty_driver_kref_put(driver);
52062306a36Sopenharmony_ci			return -ENOMEM;
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci		list_add_tail((struct list_head *) page, &sclp_tty_pages);
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci	timer_setup(&sclp_tty_timer, sclp_tty_timeout, 0);
52562306a36Sopenharmony_ci	sclp_ttybuf = NULL;
52662306a36Sopenharmony_ci	sclp_tty_buffer_count = 0;
52762306a36Sopenharmony_ci	if (MACHINE_IS_VM) {
52862306a36Sopenharmony_ci		/* case input lines to lowercase */
52962306a36Sopenharmony_ci		sclp_tty_tolower = 1;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci	sclp_tty_chars_count = 0;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	rc = sclp_register(&sclp_input_event);
53462306a36Sopenharmony_ci	if (rc) {
53562306a36Sopenharmony_ci		tty_driver_kref_put(driver);
53662306a36Sopenharmony_ci		return rc;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	tty_port_init(&sclp_port);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	driver->driver_name = "sclp_line";
54262306a36Sopenharmony_ci	driver->name = "sclp_line";
54362306a36Sopenharmony_ci	driver->major = TTY_MAJOR;
54462306a36Sopenharmony_ci	driver->minor_start = 64;
54562306a36Sopenharmony_ci	driver->type = TTY_DRIVER_TYPE_SYSTEM;
54662306a36Sopenharmony_ci	driver->subtype = SYSTEM_TYPE_TTY;
54762306a36Sopenharmony_ci	driver->init_termios = tty_std_termios;
54862306a36Sopenharmony_ci	driver->init_termios.c_iflag = IGNBRK | IGNPAR;
54962306a36Sopenharmony_ci	driver->init_termios.c_oflag = ONLCR;
55062306a36Sopenharmony_ci	driver->init_termios.c_lflag = ISIG | ECHO;
55162306a36Sopenharmony_ci	tty_set_operations(driver, &sclp_ops);
55262306a36Sopenharmony_ci	tty_port_link_device(&sclp_port, driver, 0);
55362306a36Sopenharmony_ci	rc = tty_register_driver(driver);
55462306a36Sopenharmony_ci	if (rc) {
55562306a36Sopenharmony_ci		tty_driver_kref_put(driver);
55662306a36Sopenharmony_ci		tty_port_destroy(&sclp_port);
55762306a36Sopenharmony_ci		return rc;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci	sclp_tty_driver = driver;
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_cidevice_initcall(sclp_tty_init);
563