📄 sclp_tty.c
字号:
/* * drivers/s390/char/sclp_tty.c * SCLP line mode terminal driver. * * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> */#include <linux/config.h>#include <linux/module.h>#include <linux/kmod.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/sched.h>#include <linux/wait.h>#include <linux/slab.h>#include <linux/err.h>#include <linux/init.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include "ctrlchar.h"#include "sclp.h"#include "sclp_rw.h"#include "sclp_tty.h"#define SCLP_TTY_PRINT_HEADER "sclp tty driver: "/* * size of a buffer that collects single characters coming in * via sclp_tty_put_char() */#define SCLP_TTY_BUF_SIZE 512/* * There is exactly one SCLP terminal, so we can keep things simple * and allocate all variables statically. *//* Lock to guard over changes to global variables. */static spinlock_t sclp_tty_lock;/* List of free pages that can be used for console output buffering. */static struct list_head sclp_tty_pages;/* List of full struct sclp_buffer structures ready for output. */static struct list_head sclp_tty_outqueue;/* Counter how many buffers are emitted. */static int sclp_tty_buffer_count;/* Pointer to current console buffer. */static struct sclp_buffer *sclp_ttybuf;/* Timer for delayed output of console messages. */static struct timer_list sclp_tty_timer;/* Waitqueue to wait for buffers to get empty. */static wait_queue_head_t sclp_tty_waitq;static struct tty_struct *sclp_tty;static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];static unsigned short int sclp_tty_chars_count;struct tty_driver *sclp_tty_driver;extern struct termios tty_std_termios;static struct sclp_ioctls sclp_ioctls;static struct sclp_ioctls sclp_ioctls_init ={ 8, /* 1 hor. tab. = 8 spaces */ 0, /* no echo of input by this driver */ 80, /* 80 characters/line */ 1, /* write after 1/10 s without final new line */ MAX_KMEM_PAGES, /* quick fix: avoid __alloc_pages */ MAX_KMEM_PAGES, /* take 32/64 pages from kernel memory, */ 0, /* do not convert to lower case */ 0x6c /* to seprate upper and lower case */ /* ('%' in EBCDIC) */};/* This routine is called whenever we try to open a SCLP terminal. */static intsclp_tty_open(struct tty_struct *tty, struct file *filp){ sclp_tty = tty; tty->driver_data = NULL; tty->low_latency = 0; return 0;}/* This routine is called when the SCLP terminal is closed. */static voidsclp_tty_close(struct tty_struct *tty, struct file *filp){ if (tty->count > 1) return; sclp_tty = NULL;}/* execute commands to control the i/o behaviour of the SCLP tty at runtime */static intsclp_tty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ unsigned long flags; unsigned int obuf; int check; int rc; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; rc = 0; check = 0; switch (cmd) { case TIOCSCLPSHTAB: /* set width of horizontal tab */ if (get_user(sclp_ioctls.htab, (unsigned short __user *) arg)) rc = -EFAULT; else check = 1; break; case TIOCSCLPGHTAB: /* get width of horizontal tab */ if (put_user(sclp_ioctls.htab, (unsigned short __user *) arg)) rc = -EFAULT; break; case TIOCSCLPSECHO: /* enable/disable echo of input */ if (get_user(sclp_ioctls.echo, (unsigned char __user *) arg)) rc = -EFAULT; break; case TIOCSCLPGECHO: /* Is echo of input enabled ? */ if (put_user(sclp_ioctls.echo, (unsigned char __user *) arg)) rc = -EFAULT; break; case TIOCSCLPSCOLS: /* set number of columns for output */ if (get_user(sclp_ioctls.columns, (unsigned short __user *) arg)) rc = -EFAULT; else check = 1; break; case TIOCSCLPGCOLS: /* get number of columns for output */ if (put_user(sclp_ioctls.columns, (unsigned short __user *) arg)) rc = -EFAULT; break; case TIOCSCLPSNL: /* enable/disable writing without final new line character */ if (get_user(sclp_ioctls.final_nl, (signed char __user *) arg)) rc = -EFAULT; break; case TIOCSCLPGNL: /* Is writing without final new line character enabled ? */ if (put_user(sclp_ioctls.final_nl, (signed char __user *) arg)) rc = -EFAULT; break; case TIOCSCLPSOBUF: /* * set the maximum buffers size for output, will be rounded * up to next 4kB boundary and stored as number of SCCBs * (4kB Buffers) limitation: 256 x 4kB */ if (get_user(obuf, (unsigned int __user *) arg) == 0) { if (obuf & 0xFFF) sclp_ioctls.max_sccb = (obuf >> 12) + 1; else sclp_ioctls.max_sccb = (obuf >> 12); } else rc = -EFAULT; break; case TIOCSCLPGOBUF: /* get the maximum buffers size for output */ obuf = sclp_ioctls.max_sccb << 12; if (put_user(obuf, (unsigned int __user *) arg)) rc = -EFAULT; break; case TIOCSCLPGKBUF: /* get the number of buffers got from kernel at startup */ if (put_user(sclp_ioctls.kmem_sccb, (unsigned short __user *) arg)) rc = -EFAULT; break; case TIOCSCLPSCASE: /* enable/disable conversion from upper to lower case */ if (get_user(sclp_ioctls.tolower, (unsigned char __user *) arg)) rc = -EFAULT; break; case TIOCSCLPGCASE: /* Is conversion from upper to lower case of input enabled? */ if (put_user(sclp_ioctls.tolower, (unsigned char __user *) arg)) rc = -EFAULT; break; case TIOCSCLPSDELIM: /* * set special character used for separating upper and * lower case, 0x00 disables this feature */ if (get_user(sclp_ioctls.delim, (unsigned char __user *) arg)) rc = -EFAULT; break; case TIOCSCLPGDELIM: /* * get special character used for separating upper and * lower case, 0x00 disables this feature */ if (put_user(sclp_ioctls.delim, (unsigned char __user *) arg)) rc = -EFAULT; break; case TIOCSCLPSINIT: /* set initial (default) sclp ioctls */ sclp_ioctls = sclp_ioctls_init; check = 1; break; default: rc = -ENOIOCTLCMD; break; } if (check) { spin_lock_irqsave(&sclp_tty_lock, flags); if (sclp_ttybuf != NULL) { sclp_set_htab(sclp_ttybuf, sclp_ioctls.htab); sclp_set_columns(sclp_ttybuf, sclp_ioctls.columns); } spin_unlock_irqrestore(&sclp_tty_lock, flags); } return rc;}/* * This routine returns the numbers of characters the tty driver * will accept for queuing to be written. This number is subject * to change as output buffers get emptied, or if the output flow * control is acted. This is not an exact number because not every * character needs the same space in the sccb. The worst case is * a string of newlines. Every newlines creates a new mto which * needs 8 bytes. */static intsclp_tty_write_room (struct tty_struct *tty){ unsigned long flags; struct list_head *l; int count; spin_lock_irqsave(&sclp_tty_lock, flags); count = 0; if (sclp_ttybuf != NULL) count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto); list_for_each(l, &sclp_tty_pages) count += NR_EMPTY_MTO_PER_SCCB; spin_unlock_irqrestore(&sclp_tty_lock, flags); return count;}static voidsclp_ttybuf_callback(struct sclp_buffer *buffer, int rc){ unsigned long flags; void *page; do { page = sclp_unmake_buffer(buffer); spin_lock_irqsave(&sclp_tty_lock, flags); /* Remove buffer from outqueue */ list_del(&buffer->list); sclp_tty_buffer_count--; list_add_tail((struct list_head *) page, &sclp_tty_pages); /* Check if there is a pending buffer on the out queue. */ buffer = NULL; if (!list_empty(&sclp_tty_outqueue)) buffer = list_entry(sclp_tty_outqueue.next, struct sclp_buffer, list); spin_unlock_irqrestore(&sclp_tty_lock, flags); } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); wake_up(&sclp_tty_waitq); /* check if the tty needs a wake up call */ if (sclp_tty != NULL) { tty_wakeup(sclp_tty); }}static inline void__sclp_ttybuf_emit(struct sclp_buffer *buffer){ unsigned long flags; int count; int rc; spin_lock_irqsave(&sclp_tty_lock, flags); list_add_tail(&buffer->list, &sclp_tty_outqueue); count = sclp_tty_buffer_count++; spin_unlock_irqrestore(&sclp_tty_lock, flags); if (count) return; rc = sclp_emit_buffer(buffer, sclp_ttybuf_callback); if (rc) sclp_ttybuf_callback(buffer, rc);}/* * When this routine is called from the timer then we flush the * temporary write buffer. */static voidsclp_tty_timeout(unsigned long data){ unsigned long flags; struct sclp_buffer *buf; spin_lock_irqsave(&sclp_tty_lock, flags); buf = sclp_ttybuf; sclp_ttybuf = NULL; spin_unlock_irqrestore(&sclp_tty_lock, flags); if (buf != NULL) { __sclp_ttybuf_emit(buf); }}/* * Write a string to the sclp tty. */static voidsclp_tty_write_string(const unsigned char *str, int count){ unsigned long flags; void *page; int written; struct sclp_buffer *buf; if (count <= 0) return; spin_lock_irqsave(&sclp_tty_lock, flags); do { /* Create a sclp output buffer if none exists yet */ if (sclp_ttybuf == NULL) { while (list_empty(&sclp_tty_pages)) { spin_unlock_irqrestore(&sclp_tty_lock, flags); if (in_interrupt()) sclp_sync_wait(); else wait_event(sclp_tty_waitq, !list_empty(&sclp_tty_pages)); spin_lock_irqsave(&sclp_tty_lock, flags); } page = sclp_tty_pages.next; list_del((struct list_head *) page); sclp_ttybuf = sclp_make_buffer(page, sclp_ioctls.columns, sclp_ioctls.htab); } /* try to write the string to the current output buffer */ written = sclp_write(sclp_ttybuf, str, count); if (written == count) break; /* * Not all characters could be written to the current * output buffer. Emit the buffer, create a new buffer * and then output the rest of the string. */ buf = sclp_ttybuf; sclp_ttybuf = NULL; spin_unlock_irqrestore(&sclp_tty_lock, flags); __sclp_ttybuf_emit(buf); spin_lock_irqsave(&sclp_tty_lock, flags); str += written; count -= written; } while (count > 0); /* Setup timer to output current console buffer after 1/10 second */ if (sclp_ioctls.final_nl) { if (sclp_ttybuf != NULL && sclp_chars_in_buffer(sclp_ttybuf) != 0 && !timer_pending(&sclp_tty_timer)) { init_timer(&sclp_tty_timer); sclp_tty_timer.function = sclp_tty_timeout; sclp_tty_timer.data = 0UL; sclp_tty_timer.expires = jiffies + HZ/10; add_timer(&sclp_tty_timer); } } else { if (sclp_ttybuf != NULL && sclp_chars_in_buffer(sclp_ttybuf) != 0) { buf = sclp_ttybuf; sclp_ttybuf = NULL; spin_unlock_irqrestore(&sclp_tty_lock, flags); __sclp_ttybuf_emit(buf); spin_lock_irqsave(&sclp_tty_lock, flags); } } spin_unlock_irqrestore(&sclp_tty_lock, flags);}/* * This routine is called by the kernel to write a series of characters to the * tty device. The characters may come from user space or kernel space. This * routine will return the number of characters actually accepted for writing. */static intsclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count){ if (sclp_tty_chars_count > 0) { sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); sclp_tty_chars_count = 0; } sclp_tty_write_string(buf, count); return count;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -