linux/arch/ia64/hp/sim/simserial.c
<<
>>
Prefs
   1/*
   2 * Simulated Serial Driver (fake serial)
   3 *
   4 * This driver is mostly used for bringup purposes and will go away.
   5 * It has a strong dependency on the system console. All outputs
   6 * are rerouted to the same facility as the one used by printk which, in our
   7 * case means sys_sim.c console (goes via the simulator).
   8 *
   9 * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co
  10 *      Stephane Eranian <eranian@hpl.hp.com>
  11 *      David Mosberger-Tang <davidm@hpl.hp.com>
  12 */
  13
  14#include <linux/init.h>
  15#include <linux/errno.h>
  16#include <linux/sched.h>
  17#include <linux/tty.h>
  18#include <linux/tty_flip.h>
  19#include <linux/major.h>
  20#include <linux/fcntl.h>
  21#include <linux/mm.h>
  22#include <linux/seq_file.h>
  23#include <linux/slab.h>
  24#include <linux/capability.h>
  25#include <linux/circ_buf.h>
  26#include <linux/console.h>
  27#include <linux/irq.h>
  28#include <linux/module.h>
  29#include <linux/serial.h>
  30#include <linux/sysrq.h>
  31#include <linux/uaccess.h>
  32
  33#include <asm/hpsim.h>
  34
  35#include "hpsim_ssc.h"
  36
  37#undef SIMSERIAL_DEBUG  /* define this to get some debug information */
  38
  39#define KEYBOARD_INTR   3       /* must match with simulator! */
  40
  41#define NR_PORTS        1       /* only one port for now */
  42
  43struct serial_state {
  44        struct tty_port port;
  45        struct circ_buf xmit;
  46        int irq;
  47        int x_char;
  48};
  49
  50static struct serial_state rs_table[NR_PORTS];
  51
  52struct tty_driver *hp_simserial_driver;
  53
  54static struct console *console;
  55
  56static void receive_chars(struct tty_port *port)
  57{
  58        unsigned char ch;
  59        static unsigned char seen_esc = 0;
  60
  61        while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) {
  62                if (ch == 27 && seen_esc == 0) {
  63                        seen_esc = 1;
  64                        continue;
  65                } else if (seen_esc == 1 && ch == 'O') {
  66                        seen_esc = 2;
  67                        continue;
  68                } else if (seen_esc == 2) {
  69                        if (ch == 'P') /* F1 */
  70                                show_state();
  71#ifdef CONFIG_MAGIC_SYSRQ
  72                        if (ch == 'S') { /* F4 */
  73                                do {
  74                                        ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR);
  75                                } while (!ch);
  76                                handle_sysrq(ch);
  77                        }
  78#endif
  79                        seen_esc = 0;
  80                        continue;
  81                }
  82                seen_esc = 0;
  83
  84                if (tty_insert_flip_char(port, ch, TTY_NORMAL) == 0)
  85                        break;
  86        }
  87        tty_flip_buffer_push(port);
  88}
  89
  90/*
  91 * This is the serial driver's interrupt routine for a single port
  92 */
  93static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
  94{
  95        struct serial_state *info = dev_id;
  96
  97        receive_chars(&info->port);
  98
  99        return IRQ_HANDLED;
 100}
 101
 102/*
 103 * -------------------------------------------------------------------
 104 * Here ends the serial interrupt routines.
 105 * -------------------------------------------------------------------
 106 */
 107
 108static int rs_put_char(struct tty_struct *tty, unsigned char ch)
 109{
 110        struct serial_state *info = tty->driver_data;
 111        unsigned long flags;
 112
 113        if (!info->xmit.buf)
 114                return 0;
 115
 116        local_irq_save(flags);
 117        if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {
 118                local_irq_restore(flags);
 119                return 0;
 120        }
 121        info->xmit.buf[info->xmit.head] = ch;
 122        info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
 123        local_irq_restore(flags);
 124        return 1;
 125}
 126
 127static void transmit_chars(struct tty_struct *tty, struct serial_state *info,
 128                int *intr_done)
 129{
 130        int count;
 131        unsigned long flags;
 132
 133        local_irq_save(flags);
 134
 135        if (info->x_char) {
 136                char c = info->x_char;
 137
 138                console->write(console, &c, 1);
 139
 140                info->x_char = 0;
 141
 142                goto out;
 143        }
 144
 145        if (info->xmit.head == info->xmit.tail || tty->stopped) {
 146#ifdef SIMSERIAL_DEBUG
 147                printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",
 148                       info->xmit.head, info->xmit.tail, tty->stopped);
 149#endif
 150                goto out;
 151        }
 152        /*
 153         * We removed the loop and try to do it in to chunks. We need
 154         * 2 operations maximum because it's a ring buffer.
 155         *
 156         * First from current to tail if possible.
 157         * Then from the beginning of the buffer until necessary
 158         */
 159
 160        count = min(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
 161                    SERIAL_XMIT_SIZE - info->xmit.tail);
 162        console->write(console, info->xmit.buf+info->xmit.tail, count);
 163
 164        info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1);
 165
 166        /*
 167         * We have more at the beginning of the buffer
 168         */
 169        count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 170        if (count) {
 171                console->write(console, info->xmit.buf, count);
 172                info->xmit.tail += count;
 173        }
 174out:
 175        local_irq_restore(flags);
 176}
 177
 178static void rs_flush_chars(struct tty_struct *tty)
 179{
 180        struct serial_state *info = tty->driver_data;
 181
 182        if (info->xmit.head == info->xmit.tail || tty->stopped ||
 183                        !info->xmit.buf)
 184                return;
 185
 186        transmit_chars(tty, info, NULL);
 187}
 188
 189static int rs_write(struct tty_struct * tty,
 190                    const unsigned char *buf, int count)
 191{
 192        struct serial_state *info = tty->driver_data;
 193        int     c, ret = 0;
 194        unsigned long flags;
 195
 196        if (!info->xmit.buf)
 197                return 0;
 198
 199        local_irq_save(flags);
 200        while (1) {
 201                c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 202                if (count < c)
 203                        c = count;
 204                if (c <= 0) {
 205                        break;
 206                }
 207                memcpy(info->xmit.buf + info->xmit.head, buf, c);
 208                info->xmit.head = ((info->xmit.head + c) &
 209                                   (SERIAL_XMIT_SIZE-1));
 210                buf += c;
 211                count -= c;
 212                ret += c;
 213        }
 214        local_irq_restore(flags);
 215        /*
 216         * Hey, we transmit directly from here in our case
 217         */
 218        if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) &&
 219                        !tty->stopped)
 220                transmit_chars(tty, info, NULL);
 221
 222        return ret;
 223}
 224
 225static int rs_write_room(struct tty_struct *tty)
 226{
 227        struct serial_state *info = tty->driver_data;
 228
 229        return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 230}
 231
 232static int rs_chars_in_buffer(struct tty_struct *tty)
 233{
 234        struct serial_state *info = tty->driver_data;
 235
 236        return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 237}
 238
 239static void rs_flush_buffer(struct tty_struct *tty)
 240{
 241        struct serial_state *info = tty->driver_data;
 242        unsigned long flags;
 243
 244        local_irq_save(flags);
 245        info->xmit.head = info->xmit.tail = 0;
 246        local_irq_restore(flags);
 247
 248        tty_wakeup(tty);
 249}
 250
 251/*
 252 * This function is used to send a high-priority XON/XOFF character to
 253 * the device
 254 */
 255static void rs_send_xchar(struct tty_struct *tty, char ch)
 256{
 257        struct serial_state *info = tty->driver_data;
 258
 259        info->x_char = ch;
 260        if (ch) {
 261                /*
 262                 * I guess we could call console->write() directly but
 263                 * let's do that for now.
 264                 */
 265                transmit_chars(tty, info, NULL);
 266        }
 267}
 268
 269/*
 270 * ------------------------------------------------------------
 271 * rs_throttle()
 272 *
 273 * This routine is called by the upper-layer tty layer to signal that
 274 * incoming characters should be throttled.
 275 * ------------------------------------------------------------
 276 */
 277static void rs_throttle(struct tty_struct * tty)
 278{
 279        if (I_IXOFF(tty))
 280                rs_send_xchar(tty, STOP_CHAR(tty));
 281
 282        printk(KERN_INFO "simrs_throttle called\n");
 283}
 284
 285static void rs_unthrottle(struct tty_struct * tty)
 286{
 287        struct serial_state *info = tty->driver_data;
 288
 289        if (I_IXOFF(tty)) {
 290                if (info->x_char)
 291                        info->x_char = 0;
 292                else
 293                        rs_send_xchar(tty, START_CHAR(tty));
 294        }
 295        printk(KERN_INFO "simrs_unthrottle called\n");
 296}
 297
 298static int rs_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
 299{
 300        if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
 301            (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
 302            (cmd != TIOCMIWAIT)) {
 303                if (tty->flags & (1 << TTY_IO_ERROR))
 304                    return -EIO;
 305        }
 306
 307        switch (cmd) {
 308        case TIOCGSERIAL:
 309        case TIOCSSERIAL:
 310        case TIOCSERGSTRUCT:
 311        case TIOCMIWAIT:
 312                return 0;
 313        case TIOCSERCONFIG:
 314        case TIOCSERGETLSR: /* Get line status register */
 315                return -EINVAL;
 316        case TIOCSERGWILD:
 317        case TIOCSERSWILD:
 318                /* "setserial -W" is called in Debian boot */
 319                printk (KERN_INFO "TIOCSER?WILD ioctl obsolete, ignored.\n");
 320                return 0;
 321        }
 322        return -ENOIOCTLCMD;
 323}
 324
 325#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
 326
 327/*
 328 * This routine will shutdown a serial port; interrupts are disabled, and
 329 * DTR is dropped if the hangup on close termio flag is on.
 330 */
 331static void shutdown(struct tty_port *port)
 332{
 333        struct serial_state *info = container_of(port, struct serial_state,
 334                        port);
 335        unsigned long flags;
 336
 337        local_irq_save(flags);
 338        if (info->irq)
 339                free_irq(info->irq, info);
 340
 341        if (info->xmit.buf) {
 342                free_page((unsigned long) info->xmit.buf);
 343                info->xmit.buf = NULL;
 344        }
 345        local_irq_restore(flags);
 346}
 347
 348static void rs_close(struct tty_struct *tty, struct file * filp)
 349{
 350        struct serial_state *info = tty->driver_data;
 351
 352        tty_port_close(&info->port, tty, filp);
 353}
 354
 355static void rs_hangup(struct tty_struct *tty)
 356{
 357        struct serial_state *info = tty->driver_data;
 358
 359        rs_flush_buffer(tty);
 360        tty_port_hangup(&info->port);
 361}
 362
 363static int activate(struct tty_port *port, struct tty_struct *tty)
 364{
 365        struct serial_state *state = container_of(port, struct serial_state,
 366                        port);
 367        unsigned long flags, page;
 368        int retval = 0;
 369
 370        page = get_zeroed_page(GFP_KERNEL);
 371        if (!page)
 372                return -ENOMEM;
 373
 374        local_irq_save(flags);
 375
 376        if (state->xmit.buf)
 377                free_page(page);
 378        else
 379                state->xmit.buf = (unsigned char *) page;
 380
 381        if (state->irq) {
 382                retval = request_irq(state->irq, rs_interrupt_single, 0,
 383                                "simserial", state);
 384                if (retval)
 385                        goto errout;
 386        }
 387
 388        state->xmit.head = state->xmit.tail = 0;
 389
 390        /*
 391         * Set up the tty->alt_speed kludge
 392         */
 393        if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
 394                tty->alt_speed = 57600;
 395        if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
 396                tty->alt_speed = 115200;
 397        if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
 398                tty->alt_speed = 230400;
 399        if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
 400                tty->alt_speed = 460800;
 401
 402errout:
 403        local_irq_restore(flags);
 404        return retval;
 405}
 406
 407
 408/*
 409 * This routine is called whenever a serial port is opened.  It
 410 * enables interrupts for a serial port, linking in its async structure into
 411 * the IRQ chain.   It also performs the serial-specific
 412 * initialization for the tty structure.
 413 */
 414static int rs_open(struct tty_struct *tty, struct file * filp)
 415{
 416        struct serial_state *info = rs_table + tty->index;
 417        struct tty_port *port = &info->port;
 418
 419        tty->driver_data = info;
 420        port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 421
 422        /*
 423         * figure out which console to use (should be one already)
 424         */
 425        console = console_drivers;
 426        while (console) {
 427                if ((console->flags & CON_ENABLED) && console->write) break;
 428                console = console->next;
 429        }
 430
 431        return tty_port_open(port, tty, filp);
 432}
 433
 434/*
 435 * /proc fs routines....
 436 */
 437
 438static int rs_proc_show(struct seq_file *m, void *v)
 439{
 440        int i;
 441
 442        seq_printf(m, "simserinfo:1.0\n");
 443        for (i = 0; i < NR_PORTS; i++)
 444                seq_printf(m, "%d: uart:16550 port:3F8 irq:%d\n",
 445                       i, rs_table[i].irq);
 446        return 0;
 447}
 448
 449static int rs_proc_open(struct inode *inode, struct file *file)
 450{
 451        return single_open(file, rs_proc_show, NULL);
 452}
 453
 454static const struct file_operations rs_proc_fops = {
 455        .owner          = THIS_MODULE,
 456        .open           = rs_proc_open,
 457        .read           = seq_read,
 458        .llseek         = seq_lseek,
 459        .release        = single_release,
 460};
 461
 462static const struct tty_operations hp_ops = {
 463        .open = rs_open,
 464        .close = rs_close,
 465        .write = rs_write,
 466        .put_char = rs_put_char,
 467        .flush_chars = rs_flush_chars,
 468        .write_room = rs_write_room,
 469        .chars_in_buffer = rs_chars_in_buffer,
 470        .flush_buffer = rs_flush_buffer,
 471        .ioctl = rs_ioctl,
 472        .throttle = rs_throttle,
 473        .unthrottle = rs_unthrottle,
 474        .send_xchar = rs_send_xchar,
 475        .hangup = rs_hangup,
 476        .proc_fops = &rs_proc_fops,
 477};
 478
 479static const struct tty_port_operations hp_port_ops = {
 480        .activate = activate,
 481        .shutdown = shutdown,
 482};
 483
 484static int __init simrs_init(void)
 485{
 486        struct serial_state *state;
 487        int retval;
 488
 489        if (!ia64_platform_is("hpsim"))
 490                return -ENODEV;
 491
 492        hp_simserial_driver = alloc_tty_driver(NR_PORTS);
 493        if (!hp_simserial_driver)
 494                return -ENOMEM;
 495
 496        printk(KERN_INFO "SimSerial driver with no serial options enabled\n");
 497
 498        /* Initialize the tty_driver structure */
 499
 500        hp_simserial_driver->driver_name = "simserial";
 501        hp_simserial_driver->name = "ttyS";
 502        hp_simserial_driver->major = TTY_MAJOR;
 503        hp_simserial_driver->minor_start = 64;
 504        hp_simserial_driver->type = TTY_DRIVER_TYPE_SERIAL;
 505        hp_simserial_driver->subtype = SERIAL_TYPE_NORMAL;
 506        hp_simserial_driver->init_termios = tty_std_termios;
 507        hp_simserial_driver->init_termios.c_cflag =
 508                B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 509        hp_simserial_driver->flags = TTY_DRIVER_REAL_RAW;
 510        tty_set_operations(hp_simserial_driver, &hp_ops);
 511
 512        state = rs_table;
 513        tty_port_init(&state->port);
 514        state->port.ops = &hp_port_ops;
 515        state->port.close_delay = 0; /* XXX really 0? */
 516
 517        retval = hpsim_get_irq(KEYBOARD_INTR);
 518        if (retval < 0) {
 519                printk(KERN_ERR "%s: out of interrupt vectors!\n",
 520                                __func__);
 521                goto err_free_tty;
 522        }
 523
 524        state->irq = retval;
 525
 526        /* the port is imaginary */
 527        printk(KERN_INFO "ttyS0 at 0x03f8 (irq = %d) is a 16550\n", state->irq);
 528
 529        tty_port_link_device(&state->port, hp_simserial_driver, 0);
 530        retval = tty_register_driver(hp_simserial_driver);
 531        if (retval) {
 532                printk(KERN_ERR "Couldn't register simserial driver\n");
 533                goto err_free_tty;
 534        }
 535
 536        return 0;
 537err_free_tty:
 538        put_tty_driver(hp_simserial_driver);
 539        tty_port_destroy(&state->port);
 540        return retval;
 541}
 542
 543#ifndef MODULE
 544__initcall(simrs_init);
 545#endif
 546