lkml.org 
[lkml]   [2003]   [Sep]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH] s390 (17/19): vt220 console.
    Add support for vt220 console over sclp.

    diffstat:
    arch/s390/defconfig | 1
    drivers/s390/Kconfig | 13
    drivers/s390/char/Makefile | 1
    drivers/s390/char/sclp.h | 2
    drivers/s390/char/sclp_cpi.c | 5
    drivers/s390/char/sclp_tty.c | 2
    drivers/s390/char/sclp_vt220.c | 817 +++++++++++++++++++++++++++++++++++++++++
    7 files changed, 838 insertions(+), 3 deletions(-)

    diff -urN linux-2.6/arch/s390/defconfig linux-2.6-s390/arch/s390/defconfig
    --- linux-2.6/arch/s390/defconfig Thu Sep 25 18:33:28 2003
    +++ linux-2.6-s390/arch/s390/defconfig Thu Sep 25 18:33:33 2003
    @@ -137,6 +137,7 @@
    CONFIG_SCLP=y
    CONFIG_SCLP_TTY=y
    CONFIG_SCLP_CONSOLE=y
    +# CONFIG_SCLP_VT220_TTY is not set
    CONFIG_SCLP_CPI=m
    CONFIG_S390_TAPE=m

    diff -urN linux-2.6/drivers/s390/Kconfig linux-2.6-s390/drivers/s390/Kconfig
    --- linux-2.6/drivers/s390/Kconfig Thu Sep 25 18:33:28 2003
    +++ linux-2.6-s390/drivers/s390/Kconfig Thu Sep 25 18:33:33 2003
    @@ -101,6 +101,19 @@
    Include support for using an IBM HWC line-mode terminal as the Linux
    system console.

    +config SCLP_VT220_TTY
    + bool "Support for SCLP VT220-compatible terminal"
    + depends on SCLP
    + help
    + Include support for an IBM SCLP VT220-compatible terminal.
    +
    +config SCLP_VT220_CONSOLE
    + bool "Support for console on SCLP VT220-compatible terminal"
    + depends on SCLP_VT220_TTY
    + help
    + Include support for using an IBM SCLP VT220-compatible terminal as a
    + Linux system console.
    +
    config SCLP_CPI
    tristate "Control-Program Identification"
    depends on SCLP
    diff -urN linux-2.6/drivers/s390/char/Makefile linux-2.6-s390/drivers/s390/char/Makefile
    --- linux-2.6/drivers/s390/char/Makefile Mon Sep 8 21:50:03 2003
    +++ linux-2.6-s390/drivers/s390/char/Makefile Thu Sep 25 18:33:33 2003
    @@ -11,6 +11,7 @@
    obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o
    obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
    obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
    +obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
    obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
    obj-$(CONFIG_TN3270) += tub3270.o
    tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o
    diff -urN linux-2.6/drivers/s390/char/sclp.h linux-2.6-s390/drivers/s390/char/sclp.h
    --- linux-2.6/drivers/s390/char/sclp.h Mon Sep 8 21:49:54 2003
    +++ linux-2.6-s390/drivers/s390/char/sclp.h Thu Sep 25 18:33:33 2003
    @@ -26,6 +26,7 @@
    #define EvTyp_CntlProgOpCmd 0x20
    #define EvTyp_CntlProgIdent 0x0B
    #define EvTyp_SigQuiesce 0x1D
    +#define EvTyp_VT220Msg 0x1A

    #define EvTyp_OpCmd_Mask 0x80000000
    #define EvTyp_Msg_Mask 0x40000000
    @@ -34,6 +35,7 @@
    #define EvTyp_CtlProgOpCmd_Mask 0x00000001
    #define EvTyp_CtlProgIdent_Mask 0x00200000
    #define EvTyp_SigQuiesce_Mask 0x00000008
    +#define EvTyp_VT220Msg_Mask 0x00000040

    #define GnrlMsgFlgs_DOM 0x8000
    #define GnrlMsgFlgs_SndAlrm 0x4000
    diff -urN linux-2.6/drivers/s390/char/sclp_cpi.c linux-2.6-s390/drivers/s390/char/sclp_cpi.c
    --- linux-2.6/drivers/s390/char/sclp_cpi.c Mon Sep 8 21:50:06 2003
    +++ linux-2.6-s390/drivers/s390/char/sclp_cpi.c Thu Sep 25 18:33:33 2003
    @@ -9,6 +9,7 @@
    #include <linux/version.h>
    #include <linux/kmod.h>
    #include <linux/module.h>
    +#include <linux/moduleparam.h>
    #include <linux/init.h>
    #include <linux/timer.h>
    #include <linux/string.h>
    @@ -58,12 +59,12 @@
    "or zSeries hardware");

    static char *system_name = NULL;
    -MODULE_PARM(system_name, "s");
    +module_param(system_name, charp, 0);
    MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters");

    static char *sysplex_name = NULL;
    #ifdef ALLOW_SYSPLEX_NAME
    -MODULE_PARM(sysplex_name, "s");
    +module_param(sysplex_name, charp, 0);
    MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters");
    #endif

    diff -urN linux-2.6/drivers/s390/char/sclp_tty.c linux-2.6-s390/drivers/s390/char/sclp_tty.c
    --- linux-2.6/drivers/s390/char/sclp_tty.c Mon Sep 8 21:49:53 2003
    +++ linux-2.6-s390/drivers/s390/char/sclp_tty.c Thu Sep 25 18:33:33 2003
    @@ -779,7 +779,7 @@

    driver->owner = THIS_MODULE;
    driver->driver_name = "sclp_line";
    - driver->name = "ttyS";
    + driver->name = "sclp_line";
    driver->major = TTY_MAJOR;
    driver->minor_start = 64;
    driver->type = TTY_DRIVER_TYPE_SYSTEM;
    diff -urN linux-2.6/drivers/s390/char/sclp_vt220.c linux-2.6-s390/drivers/s390/char/sclp_vt220.c
    --- linux-2.6/drivers/s390/char/sclp_vt220.c Thu Jan 1 01:00:00 1970
    +++ linux-2.6-s390/drivers/s390/char/sclp_vt220.c Thu Sep 25 18:33:33 2003
    @@ -0,0 +1,817 @@
    +/*
    + * drivers/s390/char/sclp_vt220.c
    + * SCLP VT220 terminal driver.
    + *
    + * S390 version
    + * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
    + * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
    + */
    +
    +#include <linux/config.h>
    +#include <linux/module.h>
    +#include <linux/spinlock.h>
    +#include <linux/list.h>
    +#include <linux/wait.h>
    +#include <linux/timer.h>
    +#include <linux/kernel.h>
    +#include <linux/tty.h>
    +#include <linux/tty_driver.h>
    +#include <linux/sched.h>
    +#include <linux/errno.h>
    +#include <linux/mm.h>
    +#include <linux/major.h>
    +#include <linux/console.h>
    +#include <linux/kdev_t.h>
    +#include <linux/bootmem.h>
    +#include <linux/interrupt.h>
    +#include <linux/init.h>
    +#include <asm/uaccess.h>
    +#include "sclp.h"
    +
    +#define SCLP_VT220_PRINT_HEADER "sclp vt220 tty driver: "
    +#define SCLP_VT220_MAJOR TTY_MAJOR
    +#define SCLP_VT220_MINOR 65
    +#define SCLP_VT220_DRIVER_NAME "sclp_vt220"
    +#define SCLP_VT220_DEVICE_NAME "sclp_vt"
    +#define SCLP_VT220_CONSOLE_NAME "ttyS"
    +#define SCLP_VT220_CONSOLE_INDEX 1 /* console=ttyS1 */
    +
    +/* Representation of a single write request */
    +struct sclp_vt220_request {
    + struct list_head list;
    + struct sclp_req sclp_req;
    + int retry_count;
    + struct timer_list retry_timer;
    +};
    +
    +/* VT220 SCCB */
    +struct sclp_vt220_sccb {
    + struct sccb_header header;
    + struct evbuf_header evbuf;
    +};
    +
    +#define SCLP_VT220_MAX_CHARS_PER_BUFFER (PAGE_SIZE - \
    + sizeof(struct sclp_vt220_request) - \
    + sizeof(struct sclp_vt220_sccb))
    +
    +/* Structures and data needed to register tty driver */
    +static struct tty_driver *sclp_vt220_driver;
    +
    +/* The tty_struct that the kernel associated with us */
    +static struct tty_struct *sclp_vt220_tty;
    +
    +/* Lock to protect internal data from concurrent access */
    +static spinlock_t sclp_vt220_lock;
    +
    +/* List of empty pages to be used as write request buffers */
    +static struct list_head sclp_vt220_empty;
    +
    +/* List of pending requests */
    +static struct list_head sclp_vt220_outqueue;
    +
    +/* Number of requests in outqueue */
    +static int sclp_vt220_outqueue_count;
    +
    +/* Wait queue used to delay write requests while we've run out of buffers */
    +static wait_queue_head_t sclp_vt220_waitq;
    +
    +/* Timer used for delaying write requests to merge subsequent messages into
    + * a single buffer */
    +static struct timer_list sclp_vt220_timer;
    +
    +/* Pointer to current request buffer which has been partially filled but not
    + * yet sent */
    +static struct sclp_vt220_request *sclp_vt220_current_request;
    +
    +/* Number of characters in current request buffer */
    +static int sclp_vt220_buffered_chars;
    +
    +/* Flag indicating whether this driver has already been initialized */
    +static int sclp_vt220_initialized = 0;
    +
    +/* Flag indicating that sclp_vt220_current_request should really
    + * have been already queued but wasn't because the SCLP was processing
    + * another buffer */
    +static int sclp_vt220_flush_later;
    +
    +static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf);
    +static void __sclp_vt220_emit(struct sclp_vt220_request *request);
    +static void sclp_vt220_emit_current(void);
    +
    +/* Registration structure for our interest in SCLP event buffers */
    +static struct sclp_register sclp_vt220_register = {
    + .send_mask = EvTyp_VT220Msg_Mask,
    + .receive_mask = EvTyp_VT220Msg_Mask,
    + .state_change_fn = NULL,
    + .receiver_fn = sclp_vt220_receiver_fn
    +};
    +
    +
    +/*
    + * Put provided request buffer back into queue and check emit pending
    + * buffers if necessary.
    + */
    +static void
    +sclp_vt220_process_queue(struct sclp_vt220_request *request)
    +{
    + unsigned long flags;
    + struct sclp_vt220_request *next;
    + void *page;
    +
    + /* Put buffer back to list of empty buffers */
    + page = request->sclp_req.sccb;
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + /* Move request from outqueue to empty queue */
    + list_del(&request->list);
    + sclp_vt220_outqueue_count--;
    + list_add_tail((struct list_head *) page, &sclp_vt220_empty);
    + /* Check if there is a pending buffer on the out queue. */
    + next = NULL;
    + if (!list_empty(&sclp_vt220_outqueue))
    + next = list_entry(sclp_vt220_outqueue.next,
    + struct sclp_vt220_request, list);
    + spin_unlock_irqrestore(&sclp_vt220_lock, flags);
    + if (next != NULL)
    + __sclp_vt220_emit(next);
    + else if (sclp_vt220_flush_later)
    + sclp_vt220_emit_current();
    + wake_up(&sclp_vt220_waitq);
    + /* Check if the tty needs a wake up call */
    + if (sclp_vt220_tty != NULL) {
    + if ((sclp_vt220_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
    + (sclp_vt220_tty->ldisc.write_wakeup != NULL))
    + (sclp_vt220_tty->ldisc.write_wakeup)(sclp_vt220_tty);
    + wake_up_interruptible(&sclp_vt220_tty->write_wait);
    + }
    +}
    +
    +/*
    + * Retry sclp write request after waiting some time for an sclp equipment
    + * check to pass.
    + */
    +static void
    +sclp_vt220_retry(unsigned long data)
    +{
    + struct sclp_vt220_request *request;
    + struct sclp_vt220_sccb *sccb;
    +
    + request = (struct sclp_vt220_request *) data;
    + request->sclp_req.status = SCLP_REQ_FILLED;
    + sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
    + sccb->header.response_code = 0x0000;
    + sclp_add_request(&request->sclp_req);
    +}
    +
    +#define SCLP_BUFFER_MAX_RETRY 5
    +#define SCLP_BUFFER_RETRY_INTERVAL 2
    +
    +/*
    + * Callback through which the result of a write request is reported by the
    + * SCLP.
    + */
    +static void
    +sclp_vt220_callback(struct sclp_req *request, void *data)
    +{
    + struct sclp_vt220_request *vt220_request;
    + struct sclp_vt220_sccb *sccb;
    +
    + vt220_request = (struct sclp_vt220_request *) data;
    + if (request->status == SCLP_REQ_FAILED) {
    + sclp_vt220_process_queue(vt220_request);
    + return;
    + }
    + sccb = (struct sclp_vt220_sccb *) vt220_request->sclp_req.sccb;
    +
    + /* Check SCLP response code and choose suitable action */
    + switch (sccb->header.response_code) {
    + case 0x0020 :
    + break;
    +
    + case 0x05f0: /* Target resource in improper state */
    + break;
    +
    + case 0x0340: /* Contained SCLP equipment check */
    + if (vt220_request->retry_count++ > SCLP_BUFFER_MAX_RETRY)
    + break;
    + /* Remove processed buffers and requeue rest */
    + if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
    + /* Not all buffers were processed */
    + sccb->header.response_code = 0x0000;
    + vt220_request->sclp_req.status = SCLP_REQ_FILLED;
    + sclp_add_request(request);
    + return;
    + }
    + break;
    +
    + case 0x0040: /* SCLP equipment check */
    + if (vt220_request->retry_count++ > SCLP_BUFFER_MAX_RETRY)
    + break;
    + /* Wait some time, then retry request */
    + vt220_request->retry_timer.function = sclp_vt220_retry;
    + vt220_request->retry_timer.data =
    + (unsigned long) vt220_request;
    + vt220_request->retry_timer.expires =
    + jiffies + SCLP_BUFFER_RETRY_INTERVAL*HZ;
    + add_timer(&vt220_request->retry_timer);
    + return;
    +
    + default:
    + break;
    + }
    + sclp_vt220_process_queue(vt220_request);
    +}
    +
    +/*
    + * Emit vt220 request buffer to SCLP.
    + */
    +static void
    +__sclp_vt220_emit(struct sclp_vt220_request *request)
    +{
    + if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) {
    + request->sclp_req.status = SCLP_REQ_FAILED;
    + sclp_vt220_callback(&request->sclp_req, (void *) request);
    + return;
    + }
    + request->sclp_req.command = SCLP_CMDW_WRITEDATA;
    + request->sclp_req.status = SCLP_REQ_FILLED;
    + request->sclp_req.callback = sclp_vt220_callback;
    + request->sclp_req.callback_data = (void *) request;
    +
    + sclp_add_request(&request->sclp_req);
    +}
    +
    +/*
    + * Queue and emit given request.
    + */
    +static void
    +sclp_vt220_emit(struct sclp_vt220_request *request)
    +{
    + unsigned long flags;
    + int count;
    +
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + list_add_tail(&request->list, &sclp_vt220_outqueue);
    + count = sclp_vt220_outqueue_count++;
    + spin_unlock_irqrestore(&sclp_vt220_lock, flags);
    + /* Emit only the first buffer immediately - callback takes care of
    + * the rest */
    + if (count == 0)
    + __sclp_vt220_emit(request);
    +}
    +
    +/*
    + * Queue and emit current request.
    + */
    +static void
    +sclp_vt220_emit_current(void)
    +{
    + unsigned long flags;
    + struct sclp_vt220_request *request;
    + struct sclp_vt220_sccb *sccb;
    +
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + request = NULL;
    + if (sclp_vt220_current_request != NULL) {
    + sccb = (struct sclp_vt220_sccb *)
    + sclp_vt220_current_request->sclp_req.sccb;
    + /* Only emit buffers with content */
    + if (sccb->header.length != sizeof(struct sclp_vt220_sccb)) {
    + request = sclp_vt220_current_request;
    + sclp_vt220_current_request = NULL;
    + if (timer_pending(&sclp_vt220_timer))
    + del_timer(&sclp_vt220_timer);
    + }
    + sclp_vt220_flush_later = 0;
    + }
    + spin_unlock_irqrestore(&sclp_vt220_lock, flags);
    + if (request != NULL)
    + sclp_vt220_emit(request);
    +}
    +
    +#define SCLP_NORMAL_WRITE 0x00
    +
    +/*
    + * Helper function to initialize a page with the sclp request structure.
    + */
    +static struct sclp_vt220_request *
    +sclp_vt220_initialize_page(void *page)
    +{
    + struct sclp_vt220_request *request;
    + struct sclp_vt220_sccb *sccb;
    +
    + /* Place request structure at end of page */
    + request = ((struct sclp_vt220_request *)
    + ((addr_t) page + PAGE_SIZE)) - 1;
    + init_timer(&request->retry_timer);
    + request->retry_count = 0;
    + request->sclp_req.sccb = page;
    + /* SCCB goes at start of page */
    + sccb = (struct sclp_vt220_sccb *) page;
    + memset((void *) sccb, 0, sizeof(struct sclp_vt220_sccb));
    + sccb->header.length = sizeof(struct sclp_vt220_sccb);
    + sccb->header.function_code = SCLP_NORMAL_WRITE;
    + sccb->header.response_code = 0x0000;
    + sccb->evbuf.type = EvTyp_VT220Msg;
    + sccb->evbuf.length = sizeof(struct evbuf_header);
    +
    + return request;
    +}
    +
    +static inline unsigned int
    +sclp_vt220_space_left(struct sclp_vt220_request *request)
    +{
    + struct sclp_vt220_sccb *sccb;
    + sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
    + return PAGE_SIZE - sizeof(struct sclp_vt220_request) -
    + sccb->header.length;
    +}
    +
    +static inline unsigned int
    +sclp_vt220_chars_stored(struct sclp_vt220_request *request)
    +{
    + struct sclp_vt220_sccb *sccb;
    + sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
    + return sccb->evbuf.length - sizeof(struct evbuf_header);
    +}
    +
    +/*
    + * Add msg to buffer associated with request. Return the number of characters
    + * added or -EFAULT on error.
    + */
    +static int
    +sclp_vt220_add_msg(struct sclp_vt220_request *request,
    + const unsigned char *msg, int count, int from_user,
    + int convertlf)
    +{
    + struct sclp_vt220_sccb *sccb;
    + void *buffer;
    + unsigned char c;
    + int from;
    + int to;
    +
    + if (count > sclp_vt220_space_left(request))
    + count = sclp_vt220_space_left(request);
    + if (count <= 0)
    + return 0;
    +
    + sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
    + buffer = (void *) ((addr_t) sccb + sccb->header.length);
    +
    + if (convertlf) {
    + /* Perform Linefeed conversion (0x0a -> 0x0a 0x0d)*/
    + for (from=0, to=0;
    + (from < count) && (to < sclp_vt220_space_left(request));
    + from++) {
    + /* Retrieve character */
    + if (from_user) {
    + if (get_user(c, msg + from) != 0)
    + return -EFAULT;
    + } else
    + c = msg[from];
    + /* Perform conversion */
    + if (c == 0x0a) {
    + if (to + 1 < sclp_vt220_space_left(request)) {
    + ((unsigned char *) buffer)[to++] = c;
    + ((unsigned char *) buffer)[to++] = 0x0d;
    + } else
    + break;
    +
    + } else
    + ((unsigned char *) buffer)[to++] = c;
    + }
    + sccb->header.length += to;
    + sccb->evbuf.length += to;
    + return from;
    + } else {
    + if (from_user) {
    + if (copy_from_user(buffer, (void *) msg, count) != 0)
    + return -EFAULT;
    + }
    + else
    + memcpy(buffer, (const void *) msg, count);
    + sccb->header.length += count;
    + sccb->evbuf.length += count;
    + return count;
    + }
    +}
    +
    +/*
    + * Emit buffer after having waited long enough for more data to arrive.
    + */
    +static void
    +sclp_vt220_timeout(unsigned long data)
    +{
    + sclp_vt220_emit_current();
    +}
    +
    +#define BUFFER_MAX_DELAY HZ/2
    +
    +/*
    + * Internal implementation of the write function. Write COUNT bytes of data
    + * from memory at BUF which may reside in user space (specified by FROM_USER)
    + * to the SCLP interface. In case that the data does not fit into the current
    + * write buffer, emit the current one and allocate a new one. If there are no
    + * more empty buffers available, wait until one gets emptied. If DO_SCHEDULE
    + * is non-zero, the buffer will be scheduled for emitting after a timeout -
    + * otherwise the user has to explicitly call the flush function.
    + * A non-zero CONVERTLF parameter indicates that 0x0a characters in the message
    + * buffer should be converted to 0x0a 0x0d. After completion, return the number
    + * of bytes written.
    + */
    +static int
    +__sclp_vt220_write(int from_user, const unsigned char *buf, int count,
    + int do_schedule, int convertlf)
    +{
    + unsigned long flags;
    + void *page;
    + int written;
    + int overall_written;
    +
    + if (count <= 0)
    + return 0;
    + overall_written = 0;
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + do {
    + /* Create a sclp output buffer if none exists yet */
    + if (sclp_vt220_current_request == NULL) {
    + while (list_empty(&sclp_vt220_empty)) {
    + spin_unlock_irqrestore(&sclp_vt220_lock,
    + flags);
    + if (in_interrupt())
    + sclp_sync_wait();
    + else
    + wait_event(sclp_vt220_waitq,
    + !list_empty(&sclp_vt220_empty));
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + }
    + page = (void *) sclp_vt220_empty.next;
    + list_del((struct list_head *) page);
    + sclp_vt220_current_request =
    + sclp_vt220_initialize_page(page);
    + }
    + /* Try to write the string to the current request buffer */
    + written = sclp_vt220_add_msg(sclp_vt220_current_request,
    + buf, count, from_user, convertlf);
    + if (written > 0)
    + overall_written += written;
    + if (written == -EFAULT || 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.
    + */
    + spin_unlock_irqrestore(&sclp_vt220_lock, flags);
    + sclp_vt220_emit_current();
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + buf += written;
    + count -= written;
    + } while (count > 0);
    + /* Setup timer to output current console buffer after some time */
    + if (sclp_vt220_current_request != NULL &&
    + !timer_pending(&sclp_vt220_timer) && do_schedule) {
    + sclp_vt220_timer.function = sclp_vt220_timeout;
    + sclp_vt220_timer.data = 0UL;
    + sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY;
    + add_timer(&sclp_vt220_timer);
    + }
    + spin_unlock_irqrestore(&sclp_vt220_lock, flags);
    + return overall_written;
    +}
    +
    +/*
    + * 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 int
    +sclp_vt220_write(struct tty_struct *tty, int from_user,
    + const unsigned char *buf, int count)
    +{
    + return __sclp_vt220_write(from_user, buf, count, 1, 0);
    +}
    +
    +#define SCLP_VT220_SESSION_ENDED 0x01
    +#define SCLP_VT220_SESSION_STARTED 0x80
    +#define SCLP_VT220_SESSION_DATA 0x00
    +
    +/*
    + * Called by the SCLP to report incoming event buffers.
    + */
    +static void
    +sclp_vt220_receiver_fn(struct evbuf_header *evbuf)
    +{
    + char *buffer;
    + unsigned int count;
    +
    + /* Ignore input if device is not open */
    + if (sclp_vt220_tty == NULL)
    + return;
    +
    + buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header));
    + count = evbuf->length - sizeof(struct evbuf_header);
    +
    + switch (*buffer) {
    + case SCLP_VT220_SESSION_ENDED:
    + case SCLP_VT220_SESSION_STARTED:
    + break;
    + case SCLP_VT220_SESSION_DATA:
    + /* Send input to line discipline */
    + buffer++;
    + count--;
    + /* Prevent buffer overrun by discarding input. Note that
    + * because buffer_push works asynchronously, we cannot wait
    + * for the buffer to be emptied. */
    + if (count + sclp_vt220_tty->flip.count > TTY_FLIPBUF_SIZE)
    + count = TTY_FLIPBUF_SIZE - sclp_vt220_tty->flip.count;
    + memcpy(sclp_vt220_tty->flip.char_buf_ptr, buffer, count);
    + memset(sclp_vt220_tty->flip.flag_buf_ptr, TTY_NORMAL, count);
    + sclp_vt220_tty->flip.char_buf_ptr += count;
    + sclp_vt220_tty->flip.flag_buf_ptr += count;
    + sclp_vt220_tty->flip.count += count;
    + tty_flip_buffer_push(sclp_vt220_tty);
    + break;
    + }
    +}
    +
    +/*
    + * This routine is called when a particular tty device is opened.
    + */
    +static int
    +sclp_vt220_open(struct tty_struct *tty, struct file *filp)
    +{
    + sclp_vt220_tty = tty;
    + tty->driver_data = NULL;
    + tty->low_latency = 0;
    + return 0;
    +}
    +
    +/*
    + * This routine is called when a particular tty device is closed.
    + */
    +static void
    +sclp_vt220_close(struct tty_struct *tty, struct file *filp)
    +{
    + if (tty->count > 1)
    + return;
    + sclp_vt220_tty = NULL;
    +}
    +
    +/*
    + * This routine is called by the kernel to write a single
    + * character to the tty device. If the kernel uses this routine,
    + * it must call the flush_chars() routine (if defined) when it is
    + * done stuffing characters into the driver.
    + *
    + * NOTE: include/linux/tty_driver.h specifies that a character should be
    + * ignored if there is no room in the queue. This driver implements a different
    + * semantic in that it will block when there is no more room left.
    + */
    +static void
    +sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch)
    +{
    + __sclp_vt220_write(0, &ch, 1, 0, 0);
    +}
    +
    +/*
    + * This routine is called by the kernel after it has written a
    + * series of characters to the tty device using put_char().
    + */
    +static void
    +sclp_vt220_flush_chars(struct tty_struct *tty)
    +{
    + if (sclp_vt220_outqueue_count == 0)
    + sclp_vt220_emit_current();
    + else
    + sclp_vt220_flush_later = 1;
    +}
    +
    +/*
    + * 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.
    + */
    +static int
    +sclp_vt220_write_room(struct tty_struct *tty)
    +{
    + unsigned long flags;
    + struct list_head *l;
    + int count;
    +
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + count = 0;
    + if (sclp_vt220_current_request != NULL)
    + count = sclp_vt220_space_left(sclp_vt220_current_request);
    + list_for_each(l, &sclp_vt220_empty)
    + count += SCLP_VT220_MAX_CHARS_PER_BUFFER;
    + spin_unlock_irqrestore(&sclp_vt220_lock, flags);
    + return count;
    +}
    +
    +/*
    + * Return number of buffered chars.
    + */
    +static int
    +sclp_vt220_chars_in_buffer(struct tty_struct *tty)
    +{
    + unsigned long flags;
    + struct list_head *l;
    + struct sclp_vt220_request *r;
    + int count;
    +
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + count = 0;
    + if (sclp_vt220_current_request != NULL)
    + count = sclp_vt220_chars_stored(sclp_vt220_current_request);
    + list_for_each(l, &sclp_vt220_outqueue) {
    + r = list_entry(l, struct sclp_vt220_request, list);
    + count += sclp_vt220_chars_stored(r);
    + }
    + spin_unlock_irqrestore(&sclp_vt220_lock, flags);
    + return count;
    +}
    +
    +static void
    +__sclp_vt220_flush_buffer(void)
    +{
    + unsigned long flags;
    +
    + sclp_vt220_emit_current();
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + if (timer_pending(&sclp_vt220_timer))
    + del_timer(&sclp_vt220_timer);
    + while (sclp_vt220_outqueue_count > 0) {
    + spin_unlock_irqrestore(&sclp_vt220_lock, flags);
    + sclp_sync_wait();
    + spin_lock_irqsave(&sclp_vt220_lock, flags);
    + }
    + spin_unlock_irqrestore(&sclp_vt220_lock, flags);
    +}
    +
    +/*
    + * Pass on all buffers to the hardware. Return only when there are no more
    + * buffers pending.
    + */
    +static void
    +sclp_vt220_flush_buffer(struct tty_struct *tty)
    +{
    + sclp_vt220_emit_current();
    +}
    +
    +/*
    + * Initialize all relevant components and register driver with system.
    + */
    +static int
    +__sclp_vt220_init(int early)
    +{
    + void *page;
    + int i;
    +
    + if (sclp_vt220_initialized)
    + return 0;
    + sclp_vt220_initialized = 1;
    + spin_lock_init(&sclp_vt220_lock);
    + INIT_LIST_HEAD(&sclp_vt220_empty);
    + INIT_LIST_HEAD(&sclp_vt220_outqueue);
    + init_waitqueue_head(&sclp_vt220_waitq);
    + init_timer(&sclp_vt220_timer);
    + sclp_vt220_current_request = NULL;
    + sclp_vt220_buffered_chars = 0;
    + sclp_vt220_outqueue_count = 0;
    + sclp_vt220_tty = NULL;
    + sclp_vt220_flush_later = 0;
    +
    + /* Allocate pages for output buffering */
    + for (i = 0; i < (early ? MAX_CONSOLE_PAGES : MAX_KMEM_PAGES); i++) {
    + if (early)
    + page = alloc_bootmem_low_pages(PAGE_SIZE);
    + else
    + page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
    + if (!page)
    + return -ENOMEM;
    + list_add_tail((struct list_head *) page, &sclp_vt220_empty);
    + }
    + return 0;
    +}
    +
    +static struct tty_operations sclp_vt220_ops = {
    + .open = sclp_vt220_open,
    + .close = sclp_vt220_close,
    + .write = sclp_vt220_write,
    + .put_char = sclp_vt220_put_char,
    + .flush_chars = sclp_vt220_flush_chars,
    + .write_room = sclp_vt220_write_room,
    + .chars_in_buffer = sclp_vt220_chars_in_buffer,
    + .flush_buffer = sclp_vt220_flush_buffer
    +};
    +
    +/*
    + * Register driver with SCLP and Linux and initialize internal tty structures.
    + */
    +int __init
    +sclp_vt220_tty_init(void)
    +{
    + struct tty_driver *driver;
    + int rc;
    +
    + /* Note: we're not testing for CONSOLE_IS_SCLP here to preserve
    + * symmetry between VM and LPAR systems regarding ttyS1. */
    + driver = alloc_tty_driver(1);
    + if (!driver)
    + return -ENOMEM;
    + rc = __sclp_vt220_init(0);
    + if (rc) {
    + put_tty_driver(driver);
    + return rc;
    + }
    + rc = sclp_register(&sclp_vt220_register);
    + if (rc) {
    + printk(KERN_ERR SCLP_VT220_PRINT_HEADER
    + "could not register tty - "
    + "sclp_register returned %d\n", rc);
    + put_tty_driver(driver);
    + return rc;
    + }
    +
    + driver->owner = THIS_MODULE;
    + driver->driver_name = SCLP_VT220_DRIVER_NAME;
    + driver->name = SCLP_VT220_DEVICE_NAME;
    + driver->major = SCLP_VT220_MAJOR;
    + driver->minor_start = SCLP_VT220_MINOR;
    + driver->type = TTY_DRIVER_TYPE_SYSTEM;
    + driver->subtype = SYSTEM_TYPE_TTY;
    + driver->init_termios = tty_std_termios;
    + driver->flags = TTY_DRIVER_REAL_RAW;
    + tty_set_operations(driver, &sclp_vt220_ops);
    +
    + rc = tty_register_driver(driver);
    + if (rc) {
    + printk(KERN_ERR SCLP_VT220_PRINT_HEADER
    + "could not register tty - "
    + "tty_register_driver returned %d\n", rc);
    + put_tty_driver(driver);
    + return rc;
    + }
    + sclp_vt220_driver = driver;
    + return 0;
    +}
    +
    +module_init(sclp_vt220_tty_init);
    +
    +#ifdef CONFIG_SCLP_VT220_CONSOLE
    +
    +static void
    +sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count)
    +{
    + __sclp_vt220_write(0, (const unsigned char *) buf, count, 1, 1);
    +}
    +
    +static struct tty_driver *
    +sclp_vt220_con_device(struct console *c, int *index)
    +{
    + *index = 0;
    + return sclp_vt220_driver;
    +}
    +
    +/*
    + * This routine is called from panic when the kernel is going to give up.
    + * We have to make sure that all buffers will be flushed to the SCLP.
    + * Note that this function may be called from within an interrupt context.
    + */
    +static void
    +sclp_vt220_con_unblank(void)
    +{
    + __sclp_vt220_flush_buffer();
    +}
    +
    +/* Structure needed to register with printk */
    +static struct console sclp_vt220_console =
    +{
    + .name = SCLP_VT220_CONSOLE_NAME,
    + .write = sclp_vt220_con_write,
    + .device = sclp_vt220_con_device,
    + .unblank = sclp_vt220_con_unblank,
    + .flags = CON_PRINTBUFFER,
    + .index = SCLP_VT220_CONSOLE_INDEX
    +};
    +
    +static int __init
    +sclp_vt220_con_init(void)
    +{
    + int rc;
    +
    + if (!CONSOLE_IS_SCLP)
    + return 0;
    + rc = __sclp_vt220_init(1);
    + if (rc)
    + return rc;
    + /* Attach linux console */
    + register_console(&sclp_vt220_console);
    + return 0;
    +}
    +
    +console_initcall(sclp_vt220_con_init);
    +#endif /* CONFIG_SCLP_VT220_CONSOLE */
    +
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2005-03-22 13:48    [W:4.210 / U:1.568 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site