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