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