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 * Drop oldest console buffer if sclp_con_drop is set
 134 */
 135static int
 136sclp_console_drop_buffer(void)
 137{
 138        struct list_head *list;
 139        struct sclp_buffer *buffer;
 140        void *page;
 141
 142        if (!sclp_console_drop)
 143                return 0;
 144        list = sclp_con_outqueue.next;
 145        if (sclp_con_queue_running)
 146                /* The first element is in I/O */
 147                list = list->next;
 148        if (list == &sclp_con_outqueue)
 149                return 0;
 150        list_del(list);
 151        buffer = list_entry(list, struct sclp_buffer, list);
 152        page = sclp_unmake_buffer(buffer);
 153        list_add_tail((struct list_head *) page, &sclp_con_pages);
 154        return 1;
 155}
 156
 157/*
 158 * Writes the given message to S390 system console
 159 */
 160static void
 161sclp_console_write(struct console *console, const char *message,
 162                   unsigned int count)
 163{
 164        unsigned long flags;
 165        void *page;
 166        int written;
 167
 168        if (count == 0)
 169                return;
 170        spin_lock_irqsave(&sclp_con_lock, flags);
 171        /*
 172         * process escape characters, write message into buffer,
 173         * send buffer to SCLP
 174         */
 175        do {
 176                /* make sure we have a console output buffer */
 177                if (sclp_conbuf == NULL) {
 178                        if (list_empty(&sclp_con_pages))
 179                                sclp_console_full++;
 180                        while (list_empty(&sclp_con_pages)) {
 181                                if (sclp_con_suspended)
 182                                        goto out;
 183                                if (sclp_console_drop_buffer())
 184                                        break;
 185                                spin_unlock_irqrestore(&sclp_con_lock, flags);
 186                                sclp_sync_wait();
 187                                spin_lock_irqsave(&sclp_con_lock, flags);
 188                        }
 189                        page = sclp_con_pages.next;
 190                        list_del((struct list_head *) page);
 191                        sclp_conbuf = sclp_make_buffer(page, sclp_con_columns,
 192                                                       sclp_con_width_htab);
 193                }
 194                /* try to write the string to the current output buffer */
 195                written = sclp_write(sclp_conbuf, (const unsigned char *)
 196                                     message, count);
 197                if (written == count)
 198                        break;
 199                /*
 200                 * Not all characters could be written to the current
 201                 * output buffer. Emit the buffer, create a new buffer
 202                 * and then output the rest of the string.
 203                 */
 204                spin_unlock_irqrestore(&sclp_con_lock, flags);
 205                sclp_conbuf_emit();
 206                spin_lock_irqsave(&sclp_con_lock, flags);
 207                message += written;
 208                count -= written;
 209        } while (count > 0);
 210        /* Setup timer to output current console buffer after 1/10 second */
 211        if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 &&
 212            !timer_pending(&sclp_con_timer)) {
 213                init_timer(&sclp_con_timer);
 214                sclp_con_timer.function = sclp_console_timeout;
 215                sclp_con_timer.data = 0UL;
 216                sclp_con_timer.expires = jiffies + HZ/10;
 217                add_timer(&sclp_con_timer);
 218        }
 219out:
 220        spin_unlock_irqrestore(&sclp_con_lock, flags);
 221}
 222
 223static struct tty_driver *
 224sclp_console_device(struct console *c, int *index)
 225{
 226        *index = c->index;
 227        return sclp_tty_driver;
 228}
 229
 230/*
 231 * Make sure that all buffers will be flushed to the SCLP.
 232 */
 233static void
 234sclp_console_flush(void)
 235{
 236        sclp_conbuf_emit();
 237        sclp_console_sync_queue();
 238}
 239
 240/*
 241 * Resume console: If there are cached messages, emit them.
 242 */
 243static void sclp_console_resume(void)
 244{
 245        unsigned long flags;
 246
 247        spin_lock_irqsave(&sclp_con_lock, flags);
 248        sclp_con_suspended = 0;
 249        spin_unlock_irqrestore(&sclp_con_lock, flags);
 250        sclp_conbuf_emit();
 251}
 252
 253/*
 254 * Suspend console: Set suspend flag and flush console
 255 */
 256static void sclp_console_suspend(void)
 257{
 258        unsigned long flags;
 259
 260        spin_lock_irqsave(&sclp_con_lock, flags);
 261        sclp_con_suspended = 1;
 262        spin_unlock_irqrestore(&sclp_con_lock, flags);
 263        sclp_console_flush();
 264}
 265
 266static int sclp_console_notify(struct notifier_block *self,
 267                               unsigned long event, void *data)
 268{
 269        sclp_console_flush();
 270        return NOTIFY_OK;
 271}
 272
 273static struct notifier_block on_panic_nb = {
 274        .notifier_call = sclp_console_notify,
 275        .priority = SCLP_PANIC_PRIO_CLIENT,
 276};
 277
 278static struct notifier_block on_reboot_nb = {
 279        .notifier_call = sclp_console_notify,
 280        .priority = 1,
 281};
 282
 283/*
 284 * used to register the SCLP console to the kernel and to
 285 * give printk necessary information
 286 */
 287static struct console sclp_console =
 288{
 289        .name = sclp_console_name,
 290        .write = sclp_console_write,
 291        .device = sclp_console_device,
 292        .flags = CON_PRINTBUFFER,
 293        .index = 0 /* ttyS0 */
 294};
 295
 296/*
 297 * This function is called for SCLP suspend and resume events.
 298 */
 299void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event)
 300{
 301        switch (sclp_pm_event) {
 302        case SCLP_PM_EVENT_FREEZE:
 303                sclp_console_suspend();
 304                break;
 305        case SCLP_PM_EVENT_RESTORE:
 306        case SCLP_PM_EVENT_THAW:
 307                sclp_console_resume();
 308                break;
 309        }
 310}
 311
 312/*
 313 * called by console_init() in drivers/char/tty_io.c at boot-time.
 314 */
 315static int __init
 316sclp_console_init(void)
 317{
 318        void *page;
 319        int i;
 320        int rc;
 321
 322        if (!CONSOLE_IS_SCLP)
 323                return 0;
 324        rc = sclp_rw_init();
 325        if (rc)
 326                return rc;
 327        /* Allocate pages for output buffering */
 328        INIT_LIST_HEAD(&sclp_con_pages);
 329        for (i = 0; i < sclp_console_pages; i++) {
 330                page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 331                list_add_tail(page, &sclp_con_pages);
 332        }
 333        INIT_LIST_HEAD(&sclp_con_outqueue);
 334        spin_lock_init(&sclp_con_lock);
 335        sclp_conbuf = NULL;
 336        init_timer(&sclp_con_timer);
 337
 338        /* Set output format */
 339        if (MACHINE_IS_VM)
 340                /*
 341                 * save 4 characters for the CPU number
 342                 * written at start of each line by VM/CP
 343                 */
 344                sclp_con_columns = 76;
 345        else
 346                sclp_con_columns = 80;
 347        sclp_con_width_htab = 8;
 348
 349        /* enable printk-access to this driver */
 350        atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
 351        register_reboot_notifier(&on_reboot_nb);
 352        register_console(&sclp_console);
 353        return 0;
 354}
 355
 356console_initcall(sclp_console_init);
 357