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