18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SCLP line mode terminal driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * S390 version 68c2ecf20Sopenharmony_ci * Copyright IBM Corp. 1999 78c2ecf20Sopenharmony_ci * Author(s): Martin Peschke <mpeschke@de.ibm.com> 88c2ecf20Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kmod.h> 128c2ecf20Sopenharmony_ci#include <linux/tty.h> 138c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 148c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/gfp.h> 198c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "ctrlchar.h" 228c2ecf20Sopenharmony_ci#include "sclp.h" 238c2ecf20Sopenharmony_ci#include "sclp_rw.h" 248c2ecf20Sopenharmony_ci#include "sclp_tty.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * size of a buffer that collects single characters coming in 288c2ecf20Sopenharmony_ci * via sclp_tty_put_char() 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define SCLP_TTY_BUF_SIZE 512 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * There is exactly one SCLP terminal, so we can keep things simple 348c2ecf20Sopenharmony_ci * and allocate all variables statically. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Lock to guard over changes to global variables. */ 388c2ecf20Sopenharmony_cistatic spinlock_t sclp_tty_lock; 398c2ecf20Sopenharmony_ci/* List of free pages that can be used for console output buffering. */ 408c2ecf20Sopenharmony_cistatic struct list_head sclp_tty_pages; 418c2ecf20Sopenharmony_ci/* List of full struct sclp_buffer structures ready for output. */ 428c2ecf20Sopenharmony_cistatic struct list_head sclp_tty_outqueue; 438c2ecf20Sopenharmony_ci/* Counter how many buffers are emitted. */ 448c2ecf20Sopenharmony_cistatic int sclp_tty_buffer_count; 458c2ecf20Sopenharmony_ci/* Pointer to current console buffer. */ 468c2ecf20Sopenharmony_cistatic struct sclp_buffer *sclp_ttybuf; 478c2ecf20Sopenharmony_ci/* Timer for delayed output of console messages. */ 488c2ecf20Sopenharmony_cistatic struct timer_list sclp_tty_timer; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct tty_port sclp_port; 518c2ecf20Sopenharmony_cistatic unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE]; 528c2ecf20Sopenharmony_cistatic unsigned short int sclp_tty_chars_count; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct tty_driver *sclp_tty_driver; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int sclp_tty_tolower; 578c2ecf20Sopenharmony_cistatic int sclp_tty_columns = 80; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define SPACES_PER_TAB 8 608c2ecf20Sopenharmony_ci#define CASE_DELIMITER 0x6c /* to separate upper and lower case (% in EBCDIC) */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* This routine is called whenever we try to open a SCLP terminal. */ 638c2ecf20Sopenharmony_cistatic int 648c2ecf20Sopenharmony_cisclp_tty_open(struct tty_struct *tty, struct file *filp) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci tty_port_tty_set(&sclp_port, tty); 678c2ecf20Sopenharmony_ci tty->driver_data = NULL; 688c2ecf20Sopenharmony_ci sclp_port.low_latency = 0; 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* This routine is called when the SCLP terminal is closed. */ 738c2ecf20Sopenharmony_cistatic void 748c2ecf20Sopenharmony_cisclp_tty_close(struct tty_struct *tty, struct file *filp) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci if (tty->count > 1) 778c2ecf20Sopenharmony_ci return; 788c2ecf20Sopenharmony_ci tty_port_tty_set(&sclp_port, NULL); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * This routine returns the numbers of characters the tty driver 838c2ecf20Sopenharmony_ci * will accept for queuing to be written. This number is subject 848c2ecf20Sopenharmony_ci * to change as output buffers get emptied, or if the output flow 858c2ecf20Sopenharmony_ci * control is acted. This is not an exact number because not every 868c2ecf20Sopenharmony_ci * character needs the same space in the sccb. The worst case is 878c2ecf20Sopenharmony_ci * a string of newlines. Every newline creates a new message which 888c2ecf20Sopenharmony_ci * needs 82 bytes. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_cistatic int 918c2ecf20Sopenharmony_cisclp_tty_write_room (struct tty_struct *tty) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci unsigned long flags; 948c2ecf20Sopenharmony_ci struct list_head *l; 958c2ecf20Sopenharmony_ci int count; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_tty_lock, flags); 988c2ecf20Sopenharmony_ci count = 0; 998c2ecf20Sopenharmony_ci if (sclp_ttybuf != NULL) 1008c2ecf20Sopenharmony_ci count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct msg_buf); 1018c2ecf20Sopenharmony_ci list_for_each(l, &sclp_tty_pages) 1028c2ecf20Sopenharmony_ci count += NR_EMPTY_MSG_PER_SCCB; 1038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_tty_lock, flags); 1048c2ecf20Sopenharmony_ci return count; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void 1088c2ecf20Sopenharmony_cisclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci unsigned long flags; 1118c2ecf20Sopenharmony_ci void *page; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci do { 1148c2ecf20Sopenharmony_ci page = sclp_unmake_buffer(buffer); 1158c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_tty_lock, flags); 1168c2ecf20Sopenharmony_ci /* Remove buffer from outqueue */ 1178c2ecf20Sopenharmony_ci list_del(&buffer->list); 1188c2ecf20Sopenharmony_ci sclp_tty_buffer_count--; 1198c2ecf20Sopenharmony_ci list_add_tail((struct list_head *) page, &sclp_tty_pages); 1208c2ecf20Sopenharmony_ci /* Check if there is a pending buffer on the out queue. */ 1218c2ecf20Sopenharmony_ci buffer = NULL; 1228c2ecf20Sopenharmony_ci if (!list_empty(&sclp_tty_outqueue)) 1238c2ecf20Sopenharmony_ci buffer = list_entry(sclp_tty_outqueue.next, 1248c2ecf20Sopenharmony_ci struct sclp_buffer, list); 1258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_tty_lock, flags); 1268c2ecf20Sopenharmony_ci } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci tty_port_tty_wakeup(&sclp_port); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic inline void 1328c2ecf20Sopenharmony_ci__sclp_ttybuf_emit(struct sclp_buffer *buffer) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci unsigned long flags; 1358c2ecf20Sopenharmony_ci int count; 1368c2ecf20Sopenharmony_ci int rc; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_tty_lock, flags); 1398c2ecf20Sopenharmony_ci list_add_tail(&buffer->list, &sclp_tty_outqueue); 1408c2ecf20Sopenharmony_ci count = sclp_tty_buffer_count++; 1418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_tty_lock, flags); 1428c2ecf20Sopenharmony_ci if (count) 1438c2ecf20Sopenharmony_ci return; 1448c2ecf20Sopenharmony_ci rc = sclp_emit_buffer(buffer, sclp_ttybuf_callback); 1458c2ecf20Sopenharmony_ci if (rc) 1468c2ecf20Sopenharmony_ci sclp_ttybuf_callback(buffer, rc); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * When this routine is called from the timer then we flush the 1518c2ecf20Sopenharmony_ci * temporary write buffer. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_cistatic void 1548c2ecf20Sopenharmony_cisclp_tty_timeout(struct timer_list *unused) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci unsigned long flags; 1578c2ecf20Sopenharmony_ci struct sclp_buffer *buf; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_tty_lock, flags); 1608c2ecf20Sopenharmony_ci buf = sclp_ttybuf; 1618c2ecf20Sopenharmony_ci sclp_ttybuf = NULL; 1628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_tty_lock, flags); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (buf != NULL) { 1658c2ecf20Sopenharmony_ci __sclp_ttybuf_emit(buf); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* 1708c2ecf20Sopenharmony_ci * Write a string to the sclp tty. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_cistatic int sclp_tty_write_string(const unsigned char *str, int count, int may_fail) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci unsigned long flags; 1758c2ecf20Sopenharmony_ci void *page; 1768c2ecf20Sopenharmony_ci int written; 1778c2ecf20Sopenharmony_ci int overall_written; 1788c2ecf20Sopenharmony_ci struct sclp_buffer *buf; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (count <= 0) 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci overall_written = 0; 1838c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_tty_lock, flags); 1848c2ecf20Sopenharmony_ci do { 1858c2ecf20Sopenharmony_ci /* Create a sclp output buffer if none exists yet */ 1868c2ecf20Sopenharmony_ci if (sclp_ttybuf == NULL) { 1878c2ecf20Sopenharmony_ci while (list_empty(&sclp_tty_pages)) { 1888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_tty_lock, flags); 1898c2ecf20Sopenharmony_ci if (may_fail) 1908c2ecf20Sopenharmony_ci goto out; 1918c2ecf20Sopenharmony_ci else 1928c2ecf20Sopenharmony_ci sclp_sync_wait(); 1938c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_tty_lock, flags); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci page = sclp_tty_pages.next; 1968c2ecf20Sopenharmony_ci list_del((struct list_head *) page); 1978c2ecf20Sopenharmony_ci sclp_ttybuf = sclp_make_buffer(page, sclp_tty_columns, 1988c2ecf20Sopenharmony_ci SPACES_PER_TAB); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci /* try to write the string to the current output buffer */ 2018c2ecf20Sopenharmony_ci written = sclp_write(sclp_ttybuf, str, count); 2028c2ecf20Sopenharmony_ci overall_written += written; 2038c2ecf20Sopenharmony_ci if (written == count) 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci /* 2068c2ecf20Sopenharmony_ci * Not all characters could be written to the current 2078c2ecf20Sopenharmony_ci * output buffer. Emit the buffer, create a new buffer 2088c2ecf20Sopenharmony_ci * and then output the rest of the string. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci buf = sclp_ttybuf; 2118c2ecf20Sopenharmony_ci sclp_ttybuf = NULL; 2128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_tty_lock, flags); 2138c2ecf20Sopenharmony_ci __sclp_ttybuf_emit(buf); 2148c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_tty_lock, flags); 2158c2ecf20Sopenharmony_ci str += written; 2168c2ecf20Sopenharmony_ci count -= written; 2178c2ecf20Sopenharmony_ci } while (count > 0); 2188c2ecf20Sopenharmony_ci /* Setup timer to output current console buffer after 1/10 second */ 2198c2ecf20Sopenharmony_ci if (sclp_ttybuf && sclp_chars_in_buffer(sclp_ttybuf) && 2208c2ecf20Sopenharmony_ci !timer_pending(&sclp_tty_timer)) { 2218c2ecf20Sopenharmony_ci mod_timer(&sclp_tty_timer, jiffies + HZ / 10); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_tty_lock, flags); 2248c2ecf20Sopenharmony_ciout: 2258c2ecf20Sopenharmony_ci return overall_written; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* 2298c2ecf20Sopenharmony_ci * This routine is called by the kernel to write a series of characters to the 2308c2ecf20Sopenharmony_ci * tty device. The characters may come from user space or kernel space. This 2318c2ecf20Sopenharmony_ci * routine will return the number of characters actually accepted for writing. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_cistatic int 2348c2ecf20Sopenharmony_cisclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci if (sclp_tty_chars_count > 0) { 2378c2ecf20Sopenharmony_ci sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); 2388c2ecf20Sopenharmony_ci sclp_tty_chars_count = 0; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci return sclp_tty_write_string(buf, count, 1); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* 2448c2ecf20Sopenharmony_ci * This routine is called by the kernel to write a single character to the tty 2458c2ecf20Sopenharmony_ci * device. If the kernel uses this routine, it must call the flush_chars() 2468c2ecf20Sopenharmony_ci * routine (if defined) when it is done stuffing characters into the driver. 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * Characters provided to sclp_tty_put_char() are buffered by the SCLP driver. 2498c2ecf20Sopenharmony_ci * If the given character is a '\n' the contents of the SCLP write buffer 2508c2ecf20Sopenharmony_ci * - including previous characters from sclp_tty_put_char() and strings from 2518c2ecf20Sopenharmony_ci * sclp_write() without final '\n' - will be written. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_cistatic int 2548c2ecf20Sopenharmony_cisclp_tty_put_char(struct tty_struct *tty, unsigned char ch) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci sclp_tty_chars[sclp_tty_chars_count++] = ch; 2578c2ecf20Sopenharmony_ci if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) { 2588c2ecf20Sopenharmony_ci sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); 2598c2ecf20Sopenharmony_ci sclp_tty_chars_count = 0; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci return 1; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/* 2658c2ecf20Sopenharmony_ci * This routine is called by the kernel after it has written a series of 2668c2ecf20Sopenharmony_ci * characters to the tty device using put_char(). 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_cistatic void 2698c2ecf20Sopenharmony_cisclp_tty_flush_chars(struct tty_struct *tty) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci if (sclp_tty_chars_count > 0) { 2728c2ecf20Sopenharmony_ci sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); 2738c2ecf20Sopenharmony_ci sclp_tty_chars_count = 0; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * This routine returns the number of characters in the write buffer of the 2798c2ecf20Sopenharmony_ci * SCLP driver. The provided number includes all characters that are stored 2808c2ecf20Sopenharmony_ci * in the SCCB (will be written next time the SCLP is not busy) as well as 2818c2ecf20Sopenharmony_ci * characters in the write buffer (will not be written as long as there is a 2828c2ecf20Sopenharmony_ci * final line feed missing). 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistatic int 2858c2ecf20Sopenharmony_cisclp_tty_chars_in_buffer(struct tty_struct *tty) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci unsigned long flags; 2888c2ecf20Sopenharmony_ci struct list_head *l; 2898c2ecf20Sopenharmony_ci struct sclp_buffer *t; 2908c2ecf20Sopenharmony_ci int count; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_tty_lock, flags); 2938c2ecf20Sopenharmony_ci count = 0; 2948c2ecf20Sopenharmony_ci if (sclp_ttybuf != NULL) 2958c2ecf20Sopenharmony_ci count = sclp_chars_in_buffer(sclp_ttybuf); 2968c2ecf20Sopenharmony_ci list_for_each(l, &sclp_tty_outqueue) { 2978c2ecf20Sopenharmony_ci t = list_entry(l, struct sclp_buffer, list); 2988c2ecf20Sopenharmony_ci count += sclp_chars_in_buffer(t); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_tty_lock, flags); 3018c2ecf20Sopenharmony_ci return count; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* 3058c2ecf20Sopenharmony_ci * removes all content from buffers of low level driver 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_cistatic void 3088c2ecf20Sopenharmony_cisclp_tty_flush_buffer(struct tty_struct *tty) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci if (sclp_tty_chars_count > 0) { 3118c2ecf20Sopenharmony_ci sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); 3128c2ecf20Sopenharmony_ci sclp_tty_chars_count = 0; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/* 3178c2ecf20Sopenharmony_ci * push input to tty 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_cistatic void 3208c2ecf20Sopenharmony_cisclp_tty_input(unsigned char* buf, unsigned int count) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct tty_struct *tty = tty_port_tty_get(&sclp_port); 3238c2ecf20Sopenharmony_ci unsigned int cchar; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * If this tty driver is currently closed 3278c2ecf20Sopenharmony_ci * then throw the received input away. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci if (tty == NULL) 3308c2ecf20Sopenharmony_ci return; 3318c2ecf20Sopenharmony_ci cchar = ctrlchar_handle(buf, count, tty); 3328c2ecf20Sopenharmony_ci switch (cchar & CTRLCHAR_MASK) { 3338c2ecf20Sopenharmony_ci case CTRLCHAR_SYSRQ: 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci case CTRLCHAR_CTRL: 3368c2ecf20Sopenharmony_ci tty_insert_flip_char(&sclp_port, cchar, TTY_NORMAL); 3378c2ecf20Sopenharmony_ci tty_flip_buffer_push(&sclp_port); 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci case CTRLCHAR_NONE: 3408c2ecf20Sopenharmony_ci /* send (normal) input to line discipline */ 3418c2ecf20Sopenharmony_ci if (count < 2 || 3428c2ecf20Sopenharmony_ci (strncmp((const char *) buf + count - 2, "^n", 2) && 3438c2ecf20Sopenharmony_ci strncmp((const char *) buf + count - 2, "\252n", 2))) { 3448c2ecf20Sopenharmony_ci /* add the auto \n */ 3458c2ecf20Sopenharmony_ci tty_insert_flip_string(&sclp_port, buf, count); 3468c2ecf20Sopenharmony_ci tty_insert_flip_char(&sclp_port, '\n', TTY_NORMAL); 3478c2ecf20Sopenharmony_ci } else 3488c2ecf20Sopenharmony_ci tty_insert_flip_string(&sclp_port, buf, count - 2); 3498c2ecf20Sopenharmony_ci tty_flip_buffer_push(&sclp_port); 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci tty_kref_put(tty); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* 3568c2ecf20Sopenharmony_ci * get a EBCDIC string in upper/lower case, 3578c2ecf20Sopenharmony_ci * find out characters in lower/upper case separated by a special character, 3588c2ecf20Sopenharmony_ci * modifiy original string, 3598c2ecf20Sopenharmony_ci * returns length of resulting string 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_cistatic int sclp_switch_cases(unsigned char *buf, int count) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci unsigned char *ip, *op; 3648c2ecf20Sopenharmony_ci int toggle; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* initially changing case is off */ 3678c2ecf20Sopenharmony_ci toggle = 0; 3688c2ecf20Sopenharmony_ci ip = op = buf; 3698c2ecf20Sopenharmony_ci while (count-- > 0) { 3708c2ecf20Sopenharmony_ci /* compare with special character */ 3718c2ecf20Sopenharmony_ci if (*ip == CASE_DELIMITER) { 3728c2ecf20Sopenharmony_ci /* followed by another special character? */ 3738c2ecf20Sopenharmony_ci if (count && ip[1] == CASE_DELIMITER) { 3748c2ecf20Sopenharmony_ci /* 3758c2ecf20Sopenharmony_ci * ... then put a single copy of the special 3768c2ecf20Sopenharmony_ci * character to the output string 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci *op++ = *ip++; 3798c2ecf20Sopenharmony_ci count--; 3808c2ecf20Sopenharmony_ci } else 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * ... special character follower by a normal 3838c2ecf20Sopenharmony_ci * character toggles the case change behaviour 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci toggle = ~toggle; 3868c2ecf20Sopenharmony_ci /* skip special character */ 3878c2ecf20Sopenharmony_ci ip++; 3888c2ecf20Sopenharmony_ci } else 3898c2ecf20Sopenharmony_ci /* not the special character */ 3908c2ecf20Sopenharmony_ci if (toggle) 3918c2ecf20Sopenharmony_ci /* but case switching is on */ 3928c2ecf20Sopenharmony_ci if (sclp_tty_tolower) 3938c2ecf20Sopenharmony_ci /* switch to uppercase */ 3948c2ecf20Sopenharmony_ci *op++ = _ebc_toupper[(int) *ip++]; 3958c2ecf20Sopenharmony_ci else 3968c2ecf20Sopenharmony_ci /* switch to lowercase */ 3978c2ecf20Sopenharmony_ci *op++ = _ebc_tolower[(int) *ip++]; 3988c2ecf20Sopenharmony_ci else 3998c2ecf20Sopenharmony_ci /* no case switching, copy the character */ 4008c2ecf20Sopenharmony_ci *op++ = *ip++; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci /* return length of reformatted string. */ 4038c2ecf20Sopenharmony_ci return op - buf; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic void sclp_get_input(struct gds_subvector *sv) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci unsigned char *str; 4098c2ecf20Sopenharmony_ci int count; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci str = (unsigned char *) (sv + 1); 4128c2ecf20Sopenharmony_ci count = sv->length - sizeof(*sv); 4138c2ecf20Sopenharmony_ci if (sclp_tty_tolower) 4148c2ecf20Sopenharmony_ci EBC_TOLOWER(str, count); 4158c2ecf20Sopenharmony_ci count = sclp_switch_cases(str, count); 4168c2ecf20Sopenharmony_ci /* convert EBCDIC to ASCII (modify original input in SCCB) */ 4178c2ecf20Sopenharmony_ci sclp_ebcasc_str(str, count); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* transfer input to high level driver */ 4208c2ecf20Sopenharmony_ci sclp_tty_input(str, count); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic inline void sclp_eval_selfdeftextmsg(struct gds_subvector *sv) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci void *end; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci end = (void *) sv + sv->length; 4288c2ecf20Sopenharmony_ci for (sv = sv + 1; (void *) sv < end; sv = (void *) sv + sv->length) 4298c2ecf20Sopenharmony_ci if (sv->key == 0x30) 4308c2ecf20Sopenharmony_ci sclp_get_input(sv); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic inline void sclp_eval_textcmd(struct gds_vector *v) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct gds_subvector *sv; 4368c2ecf20Sopenharmony_ci void *end; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci end = (void *) v + v->length; 4398c2ecf20Sopenharmony_ci for (sv = (struct gds_subvector *) (v + 1); 4408c2ecf20Sopenharmony_ci (void *) sv < end; sv = (void *) sv + sv->length) 4418c2ecf20Sopenharmony_ci if (sv->key == GDS_KEY_SELFDEFTEXTMSG) 4428c2ecf20Sopenharmony_ci sclp_eval_selfdeftextmsg(sv); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic inline void sclp_eval_cpmsu(struct gds_vector *v) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci void *end; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci end = (void *) v + v->length; 4518c2ecf20Sopenharmony_ci for (v = v + 1; (void *) v < end; v = (void *) v + v->length) 4528c2ecf20Sopenharmony_ci if (v->gds_id == GDS_ID_TEXTCMD) 4538c2ecf20Sopenharmony_ci sclp_eval_textcmd(v); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic inline void sclp_eval_mdsmu(struct gds_vector *v) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci v = sclp_find_gds_vector(v + 1, (void *) v + v->length, GDS_ID_CPMSU); 4608c2ecf20Sopenharmony_ci if (v) 4618c2ecf20Sopenharmony_ci sclp_eval_cpmsu(v); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic void sclp_tty_receiver(struct evbuf_header *evbuf) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct gds_vector *v; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length, 4698c2ecf20Sopenharmony_ci GDS_ID_MDSMU); 4708c2ecf20Sopenharmony_ci if (v) 4718c2ecf20Sopenharmony_ci sclp_eval_mdsmu(v); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic void 4758c2ecf20Sopenharmony_cisclp_tty_state_change(struct sclp_register *reg) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic struct sclp_register sclp_input_event = 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci .receive_mask = EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK, 4828c2ecf20Sopenharmony_ci .state_change_fn = sclp_tty_state_change, 4838c2ecf20Sopenharmony_ci .receiver_fn = sclp_tty_receiver 4848c2ecf20Sopenharmony_ci}; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic const struct tty_operations sclp_ops = { 4878c2ecf20Sopenharmony_ci .open = sclp_tty_open, 4888c2ecf20Sopenharmony_ci .close = sclp_tty_close, 4898c2ecf20Sopenharmony_ci .write = sclp_tty_write, 4908c2ecf20Sopenharmony_ci .put_char = sclp_tty_put_char, 4918c2ecf20Sopenharmony_ci .flush_chars = sclp_tty_flush_chars, 4928c2ecf20Sopenharmony_ci .write_room = sclp_tty_write_room, 4938c2ecf20Sopenharmony_ci .chars_in_buffer = sclp_tty_chars_in_buffer, 4948c2ecf20Sopenharmony_ci .flush_buffer = sclp_tty_flush_buffer, 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int __init 4988c2ecf20Sopenharmony_cisclp_tty_init(void) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct tty_driver *driver; 5018c2ecf20Sopenharmony_ci void *page; 5028c2ecf20Sopenharmony_ci int i; 5038c2ecf20Sopenharmony_ci int rc; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* z/VM multiplexes the line mode output on the 32xx screen */ 5068c2ecf20Sopenharmony_ci if (MACHINE_IS_VM && !CONSOLE_IS_SCLP) 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci if (!sclp.has_linemode) 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci driver = alloc_tty_driver(1); 5118c2ecf20Sopenharmony_ci if (!driver) 5128c2ecf20Sopenharmony_ci return -ENOMEM; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci rc = sclp_rw_init(); 5158c2ecf20Sopenharmony_ci if (rc) { 5168c2ecf20Sopenharmony_ci put_tty_driver(driver); 5178c2ecf20Sopenharmony_ci return rc; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci /* Allocate pages for output buffering */ 5208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sclp_tty_pages); 5218c2ecf20Sopenharmony_ci for (i = 0; i < MAX_KMEM_PAGES; i++) { 5228c2ecf20Sopenharmony_ci page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 5238c2ecf20Sopenharmony_ci if (page == NULL) { 5248c2ecf20Sopenharmony_ci put_tty_driver(driver); 5258c2ecf20Sopenharmony_ci return -ENOMEM; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci list_add_tail((struct list_head *) page, &sclp_tty_pages); 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sclp_tty_outqueue); 5308c2ecf20Sopenharmony_ci spin_lock_init(&sclp_tty_lock); 5318c2ecf20Sopenharmony_ci timer_setup(&sclp_tty_timer, sclp_tty_timeout, 0); 5328c2ecf20Sopenharmony_ci sclp_ttybuf = NULL; 5338c2ecf20Sopenharmony_ci sclp_tty_buffer_count = 0; 5348c2ecf20Sopenharmony_ci if (MACHINE_IS_VM) { 5358c2ecf20Sopenharmony_ci /* 5368c2ecf20Sopenharmony_ci * save 4 characters for the CPU number 5378c2ecf20Sopenharmony_ci * written at start of each line by VM/CP 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci sclp_tty_columns = 76; 5408c2ecf20Sopenharmony_ci /* case input lines to lowercase */ 5418c2ecf20Sopenharmony_ci sclp_tty_tolower = 1; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci sclp_tty_chars_count = 0; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci rc = sclp_register(&sclp_input_event); 5468c2ecf20Sopenharmony_ci if (rc) { 5478c2ecf20Sopenharmony_ci put_tty_driver(driver); 5488c2ecf20Sopenharmony_ci return rc; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci tty_port_init(&sclp_port); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci driver->driver_name = "sclp_line"; 5548c2ecf20Sopenharmony_ci driver->name = "sclp_line"; 5558c2ecf20Sopenharmony_ci driver->major = TTY_MAJOR; 5568c2ecf20Sopenharmony_ci driver->minor_start = 64; 5578c2ecf20Sopenharmony_ci driver->type = TTY_DRIVER_TYPE_SYSTEM; 5588c2ecf20Sopenharmony_ci driver->subtype = SYSTEM_TYPE_TTY; 5598c2ecf20Sopenharmony_ci driver->init_termios = tty_std_termios; 5608c2ecf20Sopenharmony_ci driver->init_termios.c_iflag = IGNBRK | IGNPAR; 5618c2ecf20Sopenharmony_ci driver->init_termios.c_oflag = ONLCR; 5628c2ecf20Sopenharmony_ci driver->init_termios.c_lflag = ISIG | ECHO; 5638c2ecf20Sopenharmony_ci driver->flags = TTY_DRIVER_REAL_RAW; 5648c2ecf20Sopenharmony_ci tty_set_operations(driver, &sclp_ops); 5658c2ecf20Sopenharmony_ci tty_port_link_device(&sclp_port, driver, 0); 5668c2ecf20Sopenharmony_ci rc = tty_register_driver(driver); 5678c2ecf20Sopenharmony_ci if (rc) { 5688c2ecf20Sopenharmony_ci put_tty_driver(driver); 5698c2ecf20Sopenharmony_ci tty_port_destroy(&sclp_port); 5708c2ecf20Sopenharmony_ci return rc; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci sclp_tty_driver = driver; 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_cidevice_initcall(sclp_tty_init); 576