linux/arch/alpha/kernel/srmcons.c
<<
>>
Prefs
   1/*
   2 *      linux/arch/alpha/kernel/srmcons.c
   3 *
   4 * Callback based driver for SRM Console console device.
   5 * (TTY driver and console driver)
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/init.h>
  10#include <linux/console.h>
  11#include <linux/delay.h>
  12#include <linux/mm.h>
  13#include <linux/slab.h>
  14#include <linux/spinlock.h>
  15#include <linux/timer.h>
  16#include <linux/tty.h>
  17#include <linux/tty_driver.h>
  18#include <linux/tty_flip.h>
  19
  20#include <asm/console.h>
  21#include <asm/uaccess.h>
  22
  23
  24static DEFINE_SPINLOCK(srmcons_callback_lock);
  25static int srm_is_registered_console = 0;
  26
  27/* 
  28 * The TTY driver
  29 */
  30#define MAX_SRM_CONSOLE_DEVICES 1       /* only support 1 console device */
  31
  32struct srmcons_private {
  33        struct tty_struct *tty;
  34        struct timer_list timer;
  35        spinlock_t lock;
  36};
  37
  38typedef union _srmcons_result {
  39        struct {
  40                unsigned long c :61;
  41                unsigned long status :3;
  42        } bits;
  43        long as_long;
  44} srmcons_result;
  45
  46/* called with callback_lock held */
  47static int
  48srmcons_do_receive_chars(struct tty_struct *tty)
  49{
  50        srmcons_result result;
  51        int count = 0, loops = 0;
  52
  53        do {
  54                result.as_long = callback_getc(0);
  55                if (result.bits.status < 2) {
  56                        tty_insert_flip_char(tty, (char)result.bits.c, 0);
  57                        count++;
  58                }
  59        } while((result.bits.status & 1) && (++loops < 10));
  60
  61        if (count)
  62                tty_schedule_flip(tty);
  63
  64        return count;
  65}
  66
  67static void
  68srmcons_receive_chars(unsigned long data)
  69{
  70        struct srmcons_private *srmconsp = (struct srmcons_private *)data;
  71        unsigned long flags;
  72        int incr = 10;
  73
  74        local_irq_save(flags);
  75        if (spin_trylock(&srmcons_callback_lock)) {
  76                if (!srmcons_do_receive_chars(srmconsp->tty))
  77                        incr = 100;
  78                spin_unlock(&srmcons_callback_lock);
  79        } 
  80
  81        spin_lock(&srmconsp->lock);
  82        if (srmconsp->tty) {
  83                srmconsp->timer.expires = jiffies + incr;
  84                add_timer(&srmconsp->timer);
  85        }
  86        spin_unlock(&srmconsp->lock);
  87
  88        local_irq_restore(flags);
  89}
  90
  91/* called with callback_lock held */
  92static int
  93srmcons_do_write(struct tty_struct *tty, const char *buf, int count)
  94{
  95        static char str_cr[1] = "\r";
  96        long c, remaining = count;
  97        srmcons_result result;
  98        char *cur;
  99        int need_cr;
 100
 101        for (cur = (char *)buf; remaining > 0; ) {
 102                need_cr = 0;
 103                /* 
 104                 * Break it up into reasonable size chunks to allow a chance
 105                 * for input to get in
 106                 */
 107                for (c = 0; c < min_t(long, 128L, remaining) && !need_cr; c++)
 108                        if (cur[c] == '\n')
 109                                need_cr = 1;
 110                
 111                while (c > 0) {
 112                        result.as_long = callback_puts(0, cur, c);
 113                        c -= result.bits.c;
 114                        remaining -= result.bits.c;
 115                        cur += result.bits.c;
 116
 117                        /*
 118                         * Check for pending input iff a tty was provided
 119                         */
 120                        if (tty)
 121                                srmcons_do_receive_chars(tty);
 122                }
 123
 124                while (need_cr) {
 125                        result.as_long = callback_puts(0, str_cr, 1);
 126                        if (result.bits.c > 0)
 127                                need_cr = 0;
 128                }
 129        }
 130        return count;
 131}
 132
 133static int
 134srmcons_write(struct tty_struct *tty,
 135              const unsigned char *buf, int count)
 136{
 137        unsigned long flags;
 138
 139        spin_lock_irqsave(&srmcons_callback_lock, flags);
 140        srmcons_do_write(tty, (const char *) buf, count);
 141        spin_unlock_irqrestore(&srmcons_callback_lock, flags);
 142
 143        return count;
 144}
 145
 146static int
 147srmcons_write_room(struct tty_struct *tty)
 148{
 149        return 512;
 150}
 151
 152static int
 153srmcons_chars_in_buffer(struct tty_struct *tty)
 154{
 155        return 0;
 156}
 157
 158static int
 159srmcons_get_private_struct(struct srmcons_private **ps)
 160{
 161        static struct srmcons_private *srmconsp = NULL;
 162        static DEFINE_SPINLOCK(srmconsp_lock);
 163        unsigned long flags;
 164        int retval = 0;
 165
 166        if (srmconsp == NULL) {
 167                srmconsp = kmalloc(sizeof(*srmconsp), GFP_KERNEL);
 168                spin_lock_irqsave(&srmconsp_lock, flags);
 169
 170                if (srmconsp == NULL)
 171                        retval = -ENOMEM;
 172                else {
 173                        srmconsp->tty = NULL;
 174                        spin_lock_init(&srmconsp->lock);
 175                        init_timer(&srmconsp->timer);
 176                }
 177
 178                spin_unlock_irqrestore(&srmconsp_lock, flags);
 179        }
 180
 181        *ps = srmconsp;
 182        return retval;
 183}
 184
 185static int
 186srmcons_open(struct tty_struct *tty, struct file *filp)
 187{
 188        struct srmcons_private *srmconsp;
 189        unsigned long flags;
 190        int retval;
 191
 192        retval = srmcons_get_private_struct(&srmconsp);
 193        if (retval)
 194                return retval;
 195
 196        spin_lock_irqsave(&srmconsp->lock, flags);
 197
 198        if (!srmconsp->tty) {
 199                tty->driver_data = srmconsp;
 200
 201                srmconsp->tty = tty;
 202                srmconsp->timer.function = srmcons_receive_chars;
 203                srmconsp->timer.data = (unsigned long)srmconsp;
 204                srmconsp->timer.expires = jiffies + 10;
 205                add_timer(&srmconsp->timer);
 206        }
 207
 208        spin_unlock_irqrestore(&srmconsp->lock, flags);
 209
 210        return 0;
 211}
 212
 213static void
 214srmcons_close(struct tty_struct *tty, struct file *filp)
 215{
 216        struct srmcons_private *srmconsp = tty->driver_data;
 217        unsigned long flags;
 218
 219        spin_lock_irqsave(&srmconsp->lock, flags);
 220
 221        if (tty->count == 1) {
 222                srmconsp->tty = NULL;
 223                del_timer(&srmconsp->timer);
 224        }
 225
 226        spin_unlock_irqrestore(&srmconsp->lock, flags);
 227}
 228
 229
 230static struct tty_driver *srmcons_driver;
 231
 232static const struct tty_operations srmcons_ops = {
 233        .open           = srmcons_open,
 234        .close          = srmcons_close,
 235        .write          = srmcons_write,
 236        .write_room     = srmcons_write_room,
 237        .chars_in_buffer= srmcons_chars_in_buffer,
 238};
 239
 240static int __init
 241srmcons_init(void)
 242{
 243        if (srm_is_registered_console) {
 244                struct tty_driver *driver;
 245                int err;
 246
 247                driver = alloc_tty_driver(MAX_SRM_CONSOLE_DEVICES);
 248                if (!driver)
 249                        return -ENOMEM;
 250                driver->driver_name = "srm";
 251                driver->name = "srm";
 252                driver->major = 0;      /* dynamic */
 253                driver->minor_start = 0;
 254                driver->type = TTY_DRIVER_TYPE_SYSTEM;
 255                driver->subtype = SYSTEM_TYPE_SYSCONS;
 256                driver->init_termios = tty_std_termios;
 257                tty_set_operations(driver, &srmcons_ops);
 258                err = tty_register_driver(driver);
 259                if (err) {
 260                        put_tty_driver(driver);
 261                        return err;
 262                }
 263                srmcons_driver = driver;
 264        }
 265
 266        return -ENODEV;
 267}
 268
 269module_init(srmcons_init);
 270
 271
 272/*
 273 * The console driver
 274 */
 275static void
 276srm_console_write(struct console *co, const char *s, unsigned count)
 277{
 278        unsigned long flags;
 279
 280        spin_lock_irqsave(&srmcons_callback_lock, flags);
 281        srmcons_do_write(NULL, s, count);
 282        spin_unlock_irqrestore(&srmcons_callback_lock, flags);
 283}
 284
 285static struct tty_driver *
 286srm_console_device(struct console *co, int *index)
 287{
 288        *index = co->index;
 289        return srmcons_driver;
 290}
 291
 292static int
 293srm_console_setup(struct console *co, char *options)
 294{
 295        return 0;
 296}
 297
 298static struct console srmcons = {
 299        .name           = "srm",
 300        .write          = srm_console_write,
 301        .device         = srm_console_device,
 302        .setup          = srm_console_setup,
 303        .flags          = CON_PRINTBUFFER | CON_BOOT,
 304        .index          = -1,
 305};
 306
 307void __init
 308register_srm_console(void)
 309{
 310        if (!srm_is_registered_console) {
 311                callback_open_console();
 312                register_console(&srmcons);
 313                srm_is_registered_console = 1;
 314        }
 315}
 316
 317void __init
 318unregister_srm_console(void)
 319{
 320        if (srm_is_registered_console) {
 321                callback_close_console();
 322                unregister_console(&srmcons);
 323                srm_is_registered_console = 0;
 324        }
 325}
 326