linux/drivers/s390/char/sclp_con.c
<<
>>
Prefs
   1/*
   2 * SCLP line mode console driver
   3 *
   4 * Copyright IBM Corp. 1999, 2009
   5 * Author(s): Martin Peschke <mpeschke@de.ibm.com>
   6 *            Martin Schwidefsky <schwidefsky@de.ibm.com>
   7 */
   8
   9#include <linux/kmod.h>
  10#include <linux/console.h>
  11#include <linux/init.h>
  12#include <linux/timer.h>
  13#include <linux/jiffies.h>
  14#include <linux/termios.h>
  15#include <linux/err.h>
  16#include <linux/reboot.h>
  17#include <linux/gfp.h>
  18
  19#include "sclp.h"
  20#include "sclp_rw.h"
  21#include "sclp_tty.h"
  22
  23#define sclp_console_major 4            /* TTYAUX_MAJOR */
  24#define sclp_console_minor 64
  25#define sclp_console_name  "ttyS"
  26
  27/* Lock to guard over changes to global variables */
  28static spinlock_t sclp_con_lock;
  29/* List of free pages that can be used for console output buffering */
  30static struct list_head sclp_con_pages;
  31/* List of full struct sclp_buffer structures ready for output */
  32static struct list_head sclp_con_outqueue;
  33/* Pointer to current console buffer */
  34static struct sclp_buffer *sclp_conbuf;
  35/* Timer for delayed output of console messages */
  36static struct timer_list sclp_con_timer;
  37/* Suspend mode flag */
  38static int sclp_con_suspended;
  39/* Flag that output queue is currently running */
  40static int sclp_con_queue_running;
  41
  42/* Output format for console messages */
  43static unsigned short sclp_con_columns;
  44static unsigned short sclp_con_width_htab;
  45
  46static void
  47sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
  48{
  49        unsigned long flags;
  50        void *page;
  51
  52        do {
  53                page = sclp_unmake_buffer(buffer);
  54                spin_lock_irqsave(&sclp_con_lock, flags);
  55
  56                /* Remove buffer from outqueue */
  57                list_del(&buffer->list);
  58                list_add_tail((struct list_head *) page, &sclp_con_pages);
  59
  60                /* Check if there is a pending buffer on the out queue. */
  61                buffer = NULL;
  62                if (!list_empty(&sclp_con_outqueue))
  63                        buffer = list_first_entry(&sclp_con_outqueue,
  64                                                  struct sclp_buffer, list);
  65                if (!buffer || sclp_con_suspended) {
  66                        sclp_con_queue_running = 0;
  67                        spin_unlock_irqrestore(&sclp_con_lock, flags);
  68                        break;
  69                }
  70                spin_unlock_irqrestore(&sclp_con_lock, flags);
  71        } while (sclp_emit_buffer(buffer, sclp_conbuf_callback));
  72}
  73
  74/*
  75 * Finalize and emit first pending buffer.
  76 */
  77static void sclp_conbuf_emit(void)
  78{
  79        struct sclp_buffer* buffer;
  80        unsigned long flags;
  81        int rc;
  82
  83        spin_lock_irqsave(&sclp_con_lock, flags);
  84        if (sclp_conbuf)
  85                list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue);
  86        sclp_conbuf = NULL;
  87        if (sclp_con_queue_running || sclp_con_suspended)
  88                goto out_unlock;
  89        if (list_empty(&sclp_con_outqueue))
  90                goto out_unlock;
  91        buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer,
  92                                  list);
  93        sclp_con_queue_running = 1;
  94        spin_unlock_irqrestore(&sclp_con_lock, flags);
  95
  96        rc = sclp_emit_buffer(buffer, sclp_conbuf_callback);
  97        if (rc)
  98                sclp_conbuf_callback(buffer, rc);
  99        return;
 100out_unlock:
 101        spin_unlock_irqrestore(&sclp_con_lock, flags);
 102}
 103
 104/*
 105 * Wait until out queue is empty
 106 */
 107static void sclp_console_sync_queue(void)
 108{
 109        unsigned long flags;
 110
 111        spin_lock_irqsave(&sclp_con_lock, flags);
 112        if (timer_pending(&sclp_con_timer))
 113                del_timer(&sclp_con_timer);
 114        while (sclp_con_queue_running) {
 115                spin_unlock_irqrestore(&sclp_con_lock, flags);
 116                sclp_sync_wait();
 117                spin_lock_irqsave(&sclp_con_lock, flags);
 118        }
 119        spin_unlock_irqrestore(&sclp_con_lock, flags);
 120}
 121
 122/*
 123 * When this routine is called from the timer then we flush the
 124 * temporary write buffer without further waiting on a final new line.
 125 */
 126static void
 127sclp_console_timeout(unsigned long data)
 128{
 129        sclp_conbuf_emit();
 130}
 131
 132/*
 133 * Writes the given message to S390 system console
 134 */
 135static void
 136sclp_console_write(struct console *console, const char *message,
 137                   unsigned int count)
 138{
 139        unsigned long flags;
 140        void *page;
 141        int written;
 142
 143        if (count == 0)
 144                return;
 145        spin_lock_irqsave(&sclp_con_lock, flags);
 146        /*
 147         * process escape characters, write message into buffer,
 148         * send buffer to SCLP
 149         */
 150        do {
 151                /* make sure we have a console output buffer */
 152                if (sclp_conbuf == NULL) {
 153                        while (list_empty(&sclp_con_pages)) {
 154                                if (sclp_con_suspended)
 155                                        goto out;
 156                                spin_unlock_irqrestore(&sclp_con_lock, flags);
 157                                sclp_sync_wait();
 158                                spin_lock_irqsave(&sclp_con_lock, flags);
 159                        }
 160                        page = sclp_con_pages.next;
 161                        list_del((struct list_head *) page);
 162                        sclp_conbuf = sclp_make_buffer(page, sclp_con_columns,
 163                                                       sclp_con_width_htab);
 164                }
 165                /* try to write the string to the current output buffer */
 166                written = sclp_write(sclp_conbuf, (const unsigned char *)
 167                                     message, count);
 168                if (written == count)
 169                        break;
 170                /*
 171                 * Not all characters could be written to the current
 172                 * output buffer. Emit the buffer, create a new buffer
 173                 * and then output the rest of the string.
 174                 */
 175                spin_unlock_irqrestore(&sclp_con_lock, flags);
 176                sclp_conbuf_emit();
 177                spin_lock_irqsave(&sclp_con_lock, flags);
 178                message += written;
 179                count -= written;
 180        } while (count > 0);
 181        /* Setup timer to output current console buffer after 1/10 second */
 182        if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 &&
 183            !timer_pending(&sclp_con_timer)) {
 184                init_timer(&sclp_con_timer);
 185                sclp_con_timer.function = sclp_console_timeout;
 186                sclp_con_timer.data = 0UL;
 187                sclp_con_timer.expires = jiffies + HZ/10;
 188                add_timer(&sclp_con_timer);
 189        }
 190out:
 191        spin_unlock_irqrestore(&sclp_con_lock, flags);
 192}
 193
 194static struct tty_driver *
 195sclp_console_device(struct console *c, int *index)
 196{
 197        *index = c->index;
 198        return sclp_tty_driver;
 199}
 200
 201/*
 202 * Make sure that all buffers will be flushed to the SCLP.
 203 */
 204static void
 205sclp_console_flush(void)
 206{
 207        sclp_conbuf_emit();
 208        sclp_console_sync_queue();
 209}
 210
 211/*
 212 * Resume console: If there are cached messages, emit them.
 213 */
 214static void sclp_console_resume(void)
 215{
 216        unsigned long flags;
 217
 218        spin_lock_irqsave(&sclp_con_lock, flags);
 219        sclp_con_suspended = 0;
 220        spin_unlock_irqrestore(&sclp_con_lock, flags);
 221        sclp_conbuf_emit();
 222}
 223
 224/*
 225 * Suspend console: Set suspend flag and flush console
 226 */
 227static void sclp_console_suspend(void)
 228{
 229        unsigned long flags;
 230
 231        spin_lock_irqsave(&sclp_con_lock, flags);
 232        sclp_con_suspended = 1;
 233        spin_unlock_irqrestore(&sclp_con_lock, flags);
 234        sclp_console_flush();
 235}
 236
 237static int sclp_console_notify(struct notifier_block *self,
 238                               unsigned long event, void *data)
 239{
 240        sclp_console_flush();
 241        return NOTIFY_OK;
 242}
 243
 244static struct notifier_block on_panic_nb = {
 245        .notifier_call = sclp_console_notify,
 246        .priority = SCLP_PANIC_PRIO_CLIENT,
 247};
 248
 249static struct notifier_block on_reboot_nb = {
 250        .notifier_call = sclp_console_notify,
 251        .priority = 1,
 252};
 253
 254/*
 255 * used to register the SCLP console to the kernel and to
 256 * give printk necessary information
 257 */
 258static struct console sclp_console =
 259{
 260        .name = sclp_console_name,
 261        .write = sclp_console_write,
 262        .device = sclp_console_device,
 263        .flags = CON_PRINTBUFFER,
 264        .index = 0 /* ttyS0 */
 265};
 266
 267/*
 268 * This function is called for SCLP suspend and resume events.
 269 */
 270void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event)
 271{
 272        switch (sclp_pm_event) {
 273        case SCLP_PM_EVENT_FREEZE:
 274                sclp_console_suspend();
 275                break;
 276        case SCLP_PM_EVENT_RESTORE:
 277        case SCLP_PM_EVENT_THAW:
 278                sclp_console_resume();
 279                break;
 280        }
 281}
 282
 283/*
 284 * called by console_init() in drivers/char/tty_io.c at boot-time.
 285 */
 286static int __init
 287sclp_console_init(void)
 288{
 289        void *page;
 290        int i;
 291        int rc;
 292
 293        if (!CONSOLE_IS_SCLP)
 294                return 0;
 295        rc = sclp_rw_init();
 296        if (rc)
 297                return rc;
 298        /* Allocate pages for output buffering */
 299        INIT_LIST_HEAD(&sclp_con_pages);
 300        for (i = 0; i < MAX_CONSOLE_PAGES; i++) {
 301                page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 302                list_add_tail(page, &sclp_con_pages);
 303        }
 304        INIT_LIST_HEAD(&sclp_con_outqueue);
 305        spin_lock_init(&sclp_con_lock);
 306        sclp_conbuf = NULL;
 307        init_timer(&sclp_con_timer);
 308
 309        /* Set output format */
 310        if (MACHINE_IS_VM)
 311                /*
 312                 * save 4 characters for the CPU number
 313                 * written at start of each line by VM/CP
 314                 */
 315                sclp_con_columns = 76;
 316        else
 317                sclp_con_columns = 80;
 318        sclp_con_width_htab = 8;
 319
 320        /* enable printk-access to this driver */
 321        atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
 322        register_reboot_notifier(&on_reboot_nb);
 323        register_console(&sclp_console);
 324        return 0;
 325}
 326
 327console_initcall(sclp_console_init);
 328