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
  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 chan *chan, struct tty_struct *tty)
 144{
 145        if (chan && chan->primary && chan->ops->winch)
 146                register_winch(chan->fd, tty);
 147}
 148
 149static void line_timer_cb(struct work_struct *work)
 150{
 151        struct line *line = container_of(work, struct line, task.work);
 152
 153        if (!line->throttled)
 154                chan_interrupt(line, line->tty, line->driver->read_irq);
 155}
 156
 157int enable_chan(struct line *line)
 158{
 159        struct list_head *ele;
 160        struct chan *chan;
 161        int err;
 162
 163        INIT_DELAYED_WORK(&line->task, line_timer_cb);
 164
 165        list_for_each(ele, &line->chan_list) {
 166                chan = list_entry(ele, struct chan, list);
 167                err = open_one_chan(chan);
 168                if (err) {
 169                        if (chan->primary)
 170                                goto out_close;
 171
 172                        continue;
 173                }
 174
 175                if (chan->enabled)
 176                        continue;
 177                err = line_setup_irq(chan->fd, chan->input, chan->output, line,
 178                                     chan);
 179                if (err)
 180                        goto out_close;
 181
 182                chan->enabled = 1;
 183        }
 184
 185        return 0;
 186
 187 out_close:
 188        close_chan(line);
 189        return err;
 190}
 191
 192/* Items are added in IRQ context, when free_irq can't be called, and
 193 * removed in process context, when it can.
 194 * This handles interrupt sources which disappear, and which need to
 195 * be permanently disabled.  This is discovered in IRQ context, but
 196 * the freeing of the IRQ must be done later.
 197 */
 198static DEFINE_SPINLOCK(irqs_to_free_lock);
 199static LIST_HEAD(irqs_to_free);
 200
 201void free_irqs(void)
 202{
 203        struct chan *chan;
 204        LIST_HEAD(list);
 205        struct list_head *ele;
 206        unsigned long flags;
 207
 208        spin_lock_irqsave(&irqs_to_free_lock, flags);
 209        list_splice_init(&irqs_to_free, &list);
 210        spin_unlock_irqrestore(&irqs_to_free_lock, flags);
 211
 212        list_for_each(ele, &list) {
 213                chan = list_entry(ele, struct chan, free_list);
 214
 215                if (chan->input && chan->enabled)
 216                        free_irq(chan->line->driver->read_irq, chan);
 217                if (chan->output && chan->enabled)
 218                        free_irq(chan->line->driver->write_irq, chan);
 219                chan->enabled = 0;
 220        }
 221}
 222
 223static void close_one_chan(struct chan *chan, int delay_free_irq)
 224{
 225        unsigned long flags;
 226
 227        if (!chan->opened)
 228                return;
 229
 230        if (delay_free_irq) {
 231                spin_lock_irqsave(&irqs_to_free_lock, flags);
 232                list_add(&chan->free_list, &irqs_to_free);
 233                spin_unlock_irqrestore(&irqs_to_free_lock, flags);
 234        }
 235        else {
 236                if (chan->input && chan->enabled)
 237                        free_irq(chan->line->driver->read_irq, chan);
 238                if (chan->output && chan->enabled)
 239                        free_irq(chan->line->driver->write_irq, chan);
 240                chan->enabled = 0;
 241        }
 242        if (chan->ops->close != NULL)
 243                (*chan->ops->close)(chan->fd, chan->data);
 244
 245        chan->opened = 0;
 246        chan->fd = -1;
 247}
 248
 249void close_chan(struct line *line)
 250{
 251        struct chan *chan;
 252
 253        /* Close in reverse order as open in case more than one of them
 254         * refers to the same device and they save and restore that device's
 255         * state.  Then, the first one opened will have the original state,
 256         * so it must be the last closed.
 257         */
 258        list_for_each_entry_reverse(chan, &line->chan_list, list) {
 259                close_one_chan(chan, 0);
 260        }
 261}
 262
 263void deactivate_chan(struct chan *chan, int irq)
 264{
 265        if (chan && chan->enabled)
 266                deactivate_fd(chan->fd, irq);
 267}
 268
 269void reactivate_chan(struct chan *chan, int irq)
 270{
 271        if (chan && chan->enabled)
 272                reactivate_fd(chan->fd, irq);
 273}
 274
 275int write_chan(struct chan *chan, const char *buf, int len,
 276               int write_irq)
 277{
 278        int n, ret = 0;
 279
 280        if (len == 0 || !chan || !chan->ops->write)
 281                return 0;
 282
 283        n = chan->ops->write(chan->fd, buf, len, chan->data);
 284        if (chan->primary) {
 285                ret = n;
 286                if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
 287                        reactivate_fd(chan->fd, write_irq);
 288        }
 289        return ret;
 290}
 291
 292int console_write_chan(struct chan *chan, const char *buf, int len)
 293{
 294        int n, ret = 0;
 295
 296        if (!chan || !chan->ops->console_write)
 297                return 0;
 298
 299        n = chan->ops->console_write(chan->fd, buf, len);
 300        if (chan->primary)
 301                ret = n;
 302        return ret;
 303}
 304
 305int console_open_chan(struct line *line, struct console *co)
 306{
 307        int err;
 308
 309        err = open_chan(&line->chan_list);
 310        if (err)
 311                return err;
 312
 313        printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
 314               co->index);
 315        return 0;
 316}
 317
 318int chan_window_size(struct line *line, unsigned short *rows_out,
 319                      unsigned short *cols_out)
 320{
 321        struct chan *chan;
 322
 323        chan = line->chan_in;
 324        if (chan && chan->primary) {
 325                if (chan->ops->window_size == NULL)
 326                        return 0;
 327                return chan->ops->window_size(chan->fd, chan->data,
 328                                              rows_out, cols_out);
 329        }
 330        chan = line->chan_out;
 331        if (chan && chan->primary) {
 332                if (chan->ops->window_size == NULL)
 333                        return 0;
 334                return chan->ops->window_size(chan->fd, chan->data,
 335                                              rows_out, cols_out);
 336        }
 337        return 0;
 338}
 339
 340static void free_one_chan(struct chan *chan)
 341{
 342        list_del(&chan->list);
 343
 344        close_one_chan(chan, 0);
 345
 346        if (chan->ops->free != NULL)
 347                (*chan->ops->free)(chan->data);
 348
 349        if (chan->primary && chan->output)
 350                ignore_sigio_fd(chan->fd);
 351        kfree(chan);
 352}
 353
 354static void free_chan(struct list_head *chans)
 355{
 356        struct list_head *ele, *next;
 357        struct chan *chan;
 358
 359        list_for_each_safe(ele, next, chans) {
 360                chan = list_entry(ele, struct chan, list);
 361                free_one_chan(chan);
 362        }
 363}
 364
 365static int one_chan_config_string(struct chan *chan, char *str, int size,
 366                                  char **error_out)
 367{
 368        int n = 0;
 369
 370        if (chan == NULL) {
 371                CONFIG_CHUNK(str, size, n, "none", 1);
 372                return n;
 373        }
 374
 375        CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
 376
 377        if (chan->dev == NULL) {
 378                CONFIG_CHUNK(str, size, n, "", 1);
 379                return n;
 380        }
 381
 382        CONFIG_CHUNK(str, size, n, ":", 0);
 383        CONFIG_CHUNK(str, size, n, chan->dev, 0);
 384
 385        return n;
 386}
 387
 388static int chan_pair_config_string(struct chan *in, struct chan *out,
 389                                   char *str, int size, char **error_out)
 390{
 391        int n;
 392
 393        n = one_chan_config_string(in, str, size, error_out);
 394        str += n;
 395        size -= n;
 396
 397        if (in == out) {
 398                CONFIG_CHUNK(str, size, n, "", 1);
 399                return n;
 400        }
 401
 402        CONFIG_CHUNK(str, size, n, ",", 1);
 403        n = one_chan_config_string(out, str, size, error_out);
 404        str += n;
 405        size -= n;
 406        CONFIG_CHUNK(str, size, n, "", 1);
 407
 408        return n;
 409}
 410
 411int chan_config_string(struct line *line, char *str, int size,
 412                       char **error_out)
 413{
 414        struct chan *in = line->chan_in, *out = line->chan_out;
 415
 416        if (in && !in->primary)
 417                in = NULL;
 418        if (out && !out->primary)
 419                out = NULL;
 420
 421        return chan_pair_config_string(in, out, str, size, error_out);
 422}
 423
 424struct chan_type {
 425        char *key;
 426        const struct chan_ops *ops;
 427};
 428
 429static const struct chan_type chan_table[] = {
 430        { "fd", &fd_ops },
 431
 432#ifdef CONFIG_NULL_CHAN
 433        { "null", &null_ops },
 434#else
 435        { "null", &not_configged_ops },
 436#endif
 437
 438#ifdef CONFIG_PORT_CHAN
 439        { "port", &port_ops },
 440#else
 441        { "port", &not_configged_ops },
 442#endif
 443
 444#ifdef CONFIG_PTY_CHAN
 445        { "pty", &pty_ops },
 446        { "pts", &pts_ops },
 447#else
 448        { "pty", &not_configged_ops },
 449        { "pts", &not_configged_ops },
 450#endif
 451
 452#ifdef CONFIG_TTY_CHAN
 453        { "tty", &tty_ops },
 454#else
 455        { "tty", &not_configged_ops },
 456#endif
 457
 458#ifdef CONFIG_XTERM_CHAN
 459        { "xterm", &xterm_ops },
 460#else
 461        { "xterm", &not_configged_ops },
 462#endif
 463};
 464
 465static struct chan *parse_chan(struct line *line, char *str, int device,
 466                               const struct chan_opts *opts, char **error_out)
 467{
 468        const struct chan_type *entry;
 469        const struct chan_ops *ops;
 470        struct chan *chan;
 471        void *data;
 472        int i;
 473
 474        ops = NULL;
 475        data = NULL;
 476        for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
 477                entry = &chan_table[i];
 478                if (!strncmp(str, entry->key, strlen(entry->key))) {
 479                        ops = entry->ops;
 480                        str += strlen(entry->key);
 481                        break;
 482                }
 483        }
 484        if (ops == NULL) {
 485                *error_out = "No match for configured backends";
 486                return NULL;
 487        }
 488
 489        data = (*ops->init)(str, device, opts);
 490        if (data == NULL) {
 491                *error_out = "Configuration failed";
 492                return NULL;
 493        }
 494
 495        chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
 496        if (chan == NULL) {
 497                *error_out = "Memory allocation failed";
 498                return NULL;
 499        }
 500        *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
 501                                 .free_list     =
 502                                        LIST_HEAD_INIT(chan->free_list),
 503                                 .line          = line,
 504                                 .primary       = 1,
 505                                 .input         = 0,
 506                                 .output        = 0,
 507                                 .opened        = 0,
 508                                 .enabled       = 0,
 509                                 .fd            = -1,
 510                                 .ops           = ops,
 511                                 .data          = data });
 512        return chan;
 513}
 514
 515int parse_chan_pair(char *str, struct line *line, int device,
 516                    const struct chan_opts *opts, char **error_out)
 517{
 518        struct list_head *chans = &line->chan_list;
 519        struct chan *new;
 520        char *in, *out;
 521
 522        if (!list_empty(chans)) {
 523                line->chan_in = line->chan_out = NULL;
 524                free_chan(chans);
 525                INIT_LIST_HEAD(chans);
 526        }
 527
 528        if (!str)
 529                return 0;
 530
 531        out = strchr(str, ',');
 532        if (out != NULL) {
 533                in = str;
 534                *out = '\0';
 535                out++;
 536                new = parse_chan(line, in, device, opts, error_out);
 537                if (new == NULL)
 538                        return -1;
 539
 540                new->input = 1;
 541                list_add(&new->list, chans);
 542                line->chan_in = new;
 543
 544                new = parse_chan(line, out, device, opts, error_out);
 545                if (new == NULL)
 546                        return -1;
 547
 548                list_add(&new->list, chans);
 549                new->output = 1;
 550                line->chan_out = new;
 551        }
 552        else {
 553                new = parse_chan(line, str, device, opts, error_out);
 554                if (new == NULL)
 555                        return -1;
 556
 557                list_add(&new->list, chans);
 558                new->input = 1;
 559                new->output = 1;
 560                line->chan_in = line->chan_out = new;
 561        }
 562        return 0;
 563}
 564
 565void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
 566{
 567        struct chan *chan = line->chan_in;
 568        int err;
 569        char c;
 570
 571        if (!chan || !chan->ops->read)
 572                goto out;
 573
 574        do {
 575                if (tty && !tty_buffer_request_room(tty, 1)) {
 576                        schedule_delayed_work(&line->task, 1);
 577                        goto out;
 578                }
 579                err = chan->ops->read(chan->fd, &c, chan->data);
 580                if (err > 0)
 581                        tty_receive_char(tty, c);
 582        } while (err > 0);
 583
 584        if (err == 0)
 585                reactivate_fd(chan->fd, irq);
 586        if (err == -EIO) {
 587                if (chan->primary) {
 588                        if (tty != NULL)
 589                                tty_hangup(tty);
 590                        if (line->chan_out != chan)
 591                                close_one_chan(line->chan_out, 1);
 592                }
 593                close_one_chan(chan, 1);
 594                if (chan->primary)
 595                        return;
 596        }
 597 out:
 598        if (tty)
 599                tty_flip_buffer_push(tty);
 600}
 601
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.