linux/arch/um/drivers/chan_kern.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
   3 * Licensed under the GPL
   4 */
   5
   6#include <linux/slab.h>
   7#include <linux/tty.h>
   8#include <linux/tty_flip.h>
   9#include "chan.h"
  10#include "os.h"
  11#include "irq_kern.h"
  12
  13#ifdef CONFIG_NOCONFIG_CHAN
  14static void *not_configged_init(char *str, int device,
  15                                const struct chan_opts *opts)
  16{
  17        printk(KERN_ERR "Using a channel type which is configured out of "
  18               "UML\n");
  19        return NULL;
  20}
  21
  22static int not_configged_open(int input, int output, int primary, void *data,
  23                              char **dev_out)
  24{
  25        printk(KERN_ERR "Using a channel type which is configured out of "
  26               "UML\n");
  27        return -ENODEV;
  28}
  29
  30static void not_configged_close(int fd, void *data)
  31{
  32        printk(KERN_ERR "Using a channel type which is configured out of "
  33               "UML\n");
  34}
  35
  36static int not_configged_read(int fd, char *c_out, void *data)
  37{
  38        printk(KERN_ERR "Using a channel type which is configured out of "
  39               "UML\n");
  40        return -EIO;
  41}
  42
  43static int not_configged_write(int fd, const char *buf, int len, void *data)
  44{
  45        printk(KERN_ERR "Using a channel type which is configured out of "
  46               "UML\n");
  47        return -EIO;
  48}
  49
  50static int not_configged_console_write(int fd, const char *buf, int len)
  51{
  52        printk(KERN_ERR "Using a channel type which is configured out of "
  53               "UML\n");
  54        return -EIO;
  55}
  56
  57static int not_configged_window_size(int fd, void *data, unsigned short *rows,
  58                                     unsigned short *cols)
  59{
  60        printk(KERN_ERR "Using a channel type which is configured out of "
  61               "UML\n");
  62        return -ENODEV;
  63}
  64
  65static void not_configged_free(void *data)
  66{
  67        printk(KERN_ERR "Using a channel type which is configured out of "
  68               "UML\n");
  69}
  70
  71static const struct chan_ops not_configged_ops = {
  72        .init           = not_configged_init,
  73        .open           = not_configged_open,
  74        .close          = not_configged_close,
  75        .read           = not_configged_read,
  76        .write          = not_configged_write,
  77        .console_write  = not_configged_console_write,
  78        .window_size    = not_configged_window_size,
  79        .free           = not_configged_free,
  80        .winch          = 0,
  81};
  82#endif /* CONFIG_NOCONFIG_CHAN */
  83
  84static void tty_receive_char(struct tty_struct *tty, char ch)
  85{
  86        if (tty == NULL)
  87                return;
  88
  89        if (I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
  90                if (ch == STOP_CHAR(tty)) {
  91                        stop_tty(tty);
  92                        return;
  93                }
  94                else if (ch == START_CHAR(tty)) {
  95                        start_tty(tty);
  96                        return;
  97                }
  98        }
  99
 100        tty_insert_flip_char(tty, ch, TTY_NORMAL);
 101}
 102
 103static int open_one_chan(struct chan *chan)
 104{
 105        int fd, err;
 106
 107        if (chan->opened)
 108                return 0;
 109
 110        if (chan->ops->open == NULL)
 111                fd = 0;
 112        else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
 113                                     chan->data, &chan->dev);
 114        if (fd < 0)
 115                return fd;
 116
 117        err = os_set_fd_block(fd, 0);
 118        if (err) {
 119                (*chan->ops->close)(fd, chan->data);
 120                return err;
 121        }
 122
 123        chan->fd = fd;
 124
 125        chan->opened = 1;
 126        return 0;
 127}
 128
 129static int open_chan(struct list_head *chans)
 130{
 131        struct list_head *ele;
 132        struct chan *chan;
 133        int ret, err = 0;
 134
 135        list_for_each(ele, chans) {
 136                chan = list_entry(ele, struct chan, list);
 137                ret = open_one_chan(chan);
 138                if (chan->primary)
 139                        err = ret;
 140        }
 141        return err;
 142}
 143
 144void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
 145{
 146        if (chan && chan->primary && chan->ops->winch)
 147                register_winch(chan->fd, tty);
 148}
 149
 150static void line_timer_cb(struct work_struct *work)
 151{
 152        struct line *line = container_of(work, struct line, task.work);
 153
 154        if (!line->throttled)
 155                chan_interrupt(line, line->tty, line->driver->read_irq);
 156}
 157
 158int enable_chan(struct line *line)
 159{
 160        struct list_head *ele;
 161        struct chan *chan;
 162        int err;
 163
 164        INIT_DELAYED_WORK(&line->task, line_timer_cb);
 165
 166        list_for_each(ele, &line->chan_list) {
 167                chan = list_entry(ele, struct chan, list);
 168                err = open_one_chan(chan);
 169                if (err) {
 170                        if (chan->primary)
 171                                goto out_close;
 172
 173                        continue;
 174                }
 175
 176                if (chan->enabled)
 177                        continue;
 178                err = line_setup_irq(chan->fd, chan->input, chan->output, line,
 179                                     chan);
 180                if (err)
 181                        goto out_close;
 182
 183                chan->enabled = 1;
 184        }
 185
 186        return 0;
 187
 188 out_close:
 189        close_chan(line);
 190        return err;
 191}
 192
 193/* Items are added in IRQ context, when free_irq can't be called, and
 194 * removed in process context, when it can.
 195 * This handles interrupt sources which disappear, and which need to
 196 * be permanently disabled.  This is discovered in IRQ context, but
 197 * the freeing of the IRQ must be done later.
 198 */
 199static DEFINE_SPINLOCK(irqs_to_free_lock);
 200static LIST_HEAD(irqs_to_free);
 201
 202void free_irqs(void)
 203{
 204        struct chan *chan;
 205        LIST_HEAD(list);
 206        struct list_head *ele;
 207        unsigned long flags;
 208
 209        spin_lock_irqsave(&irqs_to_free_lock, flags);
 210        list_splice_init(&irqs_to_free, &list);
 211        spin_unlock_irqrestore(&irqs_to_free_lock, flags);
 212
 213        list_for_each(ele, &list) {
 214                chan = list_entry(ele, struct chan, free_list);
 215
 216                if (chan->input && chan->enabled)
 217                        um_free_irq(chan->line->driver->read_irq, chan);
 218                if (chan->output && chan->enabled)
 219                        um_free_irq(chan->line->driver->write_irq, chan);
 220                chan->enabled = 0;
 221        }
 222}
 223
 224static void close_one_chan(struct chan *chan, int delay_free_irq)
 225{
 226        unsigned long flags;
 227
 228        if (!chan->opened)
 229                return;
 230
 231        if (delay_free_irq) {
 232                spin_lock_irqsave(&irqs_to_free_lock, flags);
 233                list_add(&chan->free_list, &irqs_to_free);
 234                spin_unlock_irqrestore(&irqs_to_free_lock, flags);
 235        }
 236        else {
 237                if (chan->input && chan->enabled)
 238                        um_free_irq(chan->line->driver->read_irq, chan);
 239                if (chan->output && chan->enabled)
 240                        um_free_irq(chan->line->driver->write_irq, chan);
 241                chan->enabled = 0;
 242        }
 243        if (chan->ops->close != NULL)
 244                (*chan->ops->close)(chan->fd, chan->data);
 245
 246        chan->opened = 0;
 247        chan->fd = -1;
 248}
 249
 250void close_chan(struct line *line)
 251{
 252        struct chan *chan;
 253
 254        /* Close in reverse order as open in case more than one of them
 255         * refers to the same device and they save and restore that device's
 256         * state.  Then, the first one opened will have the original state,
 257         * so it must be the last closed.
 258         */
 259        list_for_each_entry_reverse(chan, &line->chan_list, list) {
 260                close_one_chan(chan, 0);
 261        }
 262}
 263
 264void deactivate_chan(struct chan *chan, int irq)
 265{
 266        if (chan && chan->enabled)
 267                deactivate_fd(chan->fd, irq);
 268}
 269
 270void reactivate_chan(struct chan *chan, int irq)
 271{
 272        if (chan && chan->enabled)
 273                reactivate_fd(chan->fd, irq);
 274}
 275
 276int write_chan(struct chan *chan, const char *buf, int len,
 277               int write_irq)
 278{
 279        int n, ret = 0;
 280
 281        if (len == 0 || !chan || !chan->ops->write)
 282                return 0;
 283
 284        n = chan->ops->write(chan->fd, buf, len, chan->data);
 285        if (chan->primary) {
 286                ret = n;
 287                if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
 288                        reactivate_fd(chan->fd, write_irq);
 289        }
 290        return ret;
 291}
 292
 293int console_write_chan(struct chan *chan, const char *buf, int len)
 294{
 295        int n, ret = 0;
 296
 297        if (!chan || !chan->ops->console_write)
 298                return 0;
 299
 300        n = chan->ops->console_write(chan->fd, buf, len);
 301        if (chan->primary)
 302                ret = n;
 303        return ret;
 304}
 305
 306int console_open_chan(struct line *line, struct console *co)
 307{
 308        int err;
 309
 310        err = open_chan(&line->chan_list);
 311        if (err)
 312                return err;
 313
 314        printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
 315               co->index);
 316        return 0;
 317}
 318
 319int chan_window_size(struct line *line, unsigned short *rows_out,
 320                      unsigned short *cols_out)
 321{
 322        struct chan *chan;
 323
 324        chan = line->chan_in;
 325        if (chan && chan->primary) {
 326                if (chan->ops->window_size == NULL)
 327                        return 0;
 328                return chan->ops->window_size(chan->fd, chan->data,
 329                                              rows_out, cols_out);
 330        }
 331        chan = line->chan_out;
 332        if (chan && chan->primary) {
 333                if (chan->ops->window_size == NULL)
 334                        return 0;
 335                return chan->ops->window_size(chan->fd, chan->data,
 336                                              rows_out, cols_out);
 337        }
 338        return 0;
 339}
 340
 341static void free_one_chan(struct chan *chan)
 342{
 343        list_del(&chan->list);
 344
 345        close_one_chan(chan, 0);
 346
 347        if (chan->ops->free != NULL)
 348                (*chan->ops->free)(chan->data);
 349
 350        if (chan->primary && chan->output)
 351                ignore_sigio_fd(chan->fd);
 352        kfree(chan);
 353}
 354
 355static void free_chan(struct list_head *chans)
 356{
 357        struct list_head *ele, *next;
 358        struct chan *chan;
 359
 360        list_for_each_safe(ele, next, chans) {
 361                chan = list_entry(ele, struct chan, list);
 362                free_one_chan(chan);
 363        }
 364}
 365
 366static int one_chan_config_string(struct chan *chan, char *str, int size,
 367                                  char **error_out)
 368{
 369        int n = 0;
 370
 371        if (chan == NULL) {
 372                CONFIG_CHUNK(str, size, n, "none", 1);
 373                return n;
 374        }
 375
 376        CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
 377
 378        if (chan->dev == NULL) {
 379                CONFIG_CHUNK(str, size, n, "", 1);
 380                return n;
 381        }
 382
 383        CONFIG_CHUNK(str, size, n, ":", 0);
 384        CONFIG_CHUNK(str, size, n, chan->dev, 0);
 385
 386        return n;
 387}
 388
 389static int chan_pair_config_string(struct chan *in, struct chan *out,
 390                                   char *str, int size, char **error_out)
 391{
 392        int n;
 393
 394        n = one_chan_config_string(in, str, size, error_out);
 395        str += n;
 396        size -= n;
 397
 398        if (in == out) {
 399                CONFIG_CHUNK(str, size, n, "", 1);
 400                return n;
 401        }
 402
 403        CONFIG_CHUNK(str, size, n, ",", 1);
 404        n = one_chan_config_string(out, str, size, error_out);
 405        str += n;
 406        size -= n;
 407        CONFIG_CHUNK(str, size, n, "", 1);
 408
 409        return n;
 410}
 411
 412int chan_config_string(struct line *line, char *str, int size,
 413                       char **error_out)
 414{
 415        struct chan *in = line->chan_in, *out = line->chan_out;
 416
 417        if (in && !in->primary)
 418                in = NULL;
 419        if (out && !out->primary)
 420                out = NULL;
 421
 422        return chan_pair_config_string(in, out, str, size, error_out);
 423}
 424
 425struct chan_type {
 426        char *key;
 427        const struct chan_ops *ops;
 428};
 429
 430static const struct chan_type chan_table[] = {
 431        { "fd", &fd_ops },
 432
 433#ifdef CONFIG_NULL_CHAN
 434        { "null", &null_ops },
 435#else
 436        { "null", &not_configged_ops },
 437#endif
 438
 439#ifdef CONFIG_PORT_CHAN
 440        { "port", &port_ops },
 441#else
 442        { "port", &not_configged_ops },
 443#endif
 444
 445#ifdef CONFIG_PTY_CHAN
 446        { "pty", &pty_ops },
 447        { "pts", &pts_ops },
 448#else
 449        { "pty", &not_configged_ops },
 450        { "pts", &not_configged_ops },
 451#endif
 452
 453#ifdef CONFIG_TTY_CHAN
 454        { "tty", &tty_ops },
 455#else
 456        { "tty", &not_configged_ops },
 457#endif
 458
 459#ifdef CONFIG_XTERM_CHAN
 460        { "xterm", &xterm_ops },
 461#else
 462        { "xterm", &not_configged_ops },
 463#endif
 464};
 465
 466static struct chan *parse_chan(struct line *line, char *str, int device,
 467                               const struct chan_opts *opts, char **error_out)
 468{
 469        const struct chan_type *entry;
 470        const struct chan_ops *ops;
 471        struct chan *chan;
 472        void *data;
 473        int i;
 474
 475        ops = NULL;
 476        data = NULL;
 477        for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
 478                entry = &chan_table[i];
 479                if (!strncmp(str, entry->key, strlen(entry->key))) {
 480                        ops = entry->ops;
 481                        str += strlen(entry->key);
 482                        break;
 483                }
 484        }
 485        if (ops == NULL) {
 486                *error_out = "No match for configured backends";
 487                return NULL;
 488        }
 489
 490        data = (*ops->init)(str, device, opts);
 491        if (data == NULL) {
 492                *error_out = "Configuration failed";
 493                return NULL;
 494        }
 495
 496        chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
 497        if (chan == NULL) {
 498                *error_out = "Memory allocation failed";
 499                return NULL;
 500        }
 501        *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
 502                                 .free_list     =
 503                                        LIST_HEAD_INIT(chan->free_list),
 504                                 .line          = line,
 505                                 .primary       = 1,
 506                                 .input         = 0,
 507                                 .output        = 0,
 508                                 .opened        = 0,
 509                                 .enabled       = 0,
 510                                 .fd            = -1,
 511                                 .ops           = ops,
 512                                 .data          = data });
 513        return chan;
 514}
 515
 516int parse_chan_pair(char *str, struct line *line, int device,
 517                    const struct chan_opts *opts, char **error_out)
 518{
 519        struct list_head *chans = &line->chan_list;
 520        struct chan *new;
 521        char *in, *out;
 522
 523        if (!list_empty(chans)) {
 524                line->chan_in = line->chan_out = NULL;
 525                free_chan(chans);
 526                INIT_LIST_HEAD(chans);
 527        }
 528
 529        if (!str)
 530                return 0;
 531
 532        out = strchr(str, ',');
 533        if (out != NULL) {
 534                in = str;
 535                *out = '\0';
 536                out++;
 537                new = parse_chan(line, in, device, opts, error_out);
 538                if (new == NULL)
 539                        return -1;
 540
 541                new->input = 1;
 542                list_add(&new->list, chans);
 543                line->chan_in = new;
 544
 545                new = parse_chan(line, out, device, opts, error_out);
 546                if (new == NULL)
 547                        return -1;
 548
 549                list_add(&new->list, chans);
 550                new->output = 1;
 551                line->chan_out = new;
 552        }
 553        else {
 554                new = parse_chan(line, str, device, opts, error_out);
 555                if (new == NULL)
 556                        return -1;
 557
 558                list_add(&new->list, chans);
 559                new->input = 1;
 560                new->output = 1;
 561                line->chan_in = line->chan_out = new;
 562        }
 563        return 0;
 564}
 565
 566void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
 567{
 568        struct chan *chan = line->chan_in;
 569        int err;
 570        char c;
 571
 572        if (!chan || !chan->ops->read)
 573                goto out;
 574
 575        do {
 576                if (tty && !tty_buffer_request_room(tty, 1)) {
 577                        schedule_delayed_work(&line->task, 1);
 578                        goto out;
 579                }
 580                err = chan->ops->read(chan->fd, &c, chan->data);
 581                if (err > 0)
 582                        tty_receive_char(tty, c);
 583        } while (err > 0);
 584
 585        if (err == 0)
 586                reactivate_fd(chan->fd, irq);
 587        if (err == -EIO) {
 588                if (chan->primary) {
 589                        if (tty != NULL)
 590                                tty_hangup(tty);
 591                        if (line->chan_out != chan)
 592                                close_one_chan(line->chan_out, 1);
 593                }
 594                close_one_chan(chan, 1);
 595                if (chan->primary)
 596                        return;
 597        }
 598 out:
 599        if (tty)
 600                tty_flip_buffer_push(tty);
 601}
 602