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
 174static void close_one_chan(struct chan *chan, int delay_free_irq)
 175{
 176        if (!chan->opened)
 177                return;
 178
 179    /* we can safely call free now - it will be marked
 180     *  as free and freed once the IRQ stopped processing
 181     */
 182        if (chan->input && chan->enabled)
 183                um_free_irq(chan->line->driver->read_irq, chan);
 184        if (chan->output && chan->enabled)
 185                um_free_irq(chan->line->driver->write_irq, chan);
 186        chan->enabled = 0;
 187        if (chan->ops->close != NULL)
 188                (*chan->ops->close)(chan->fd, chan->data);
 189
 190        chan->opened = 0;
 191        chan->fd = -1;
 192}
 193
 194void close_chan(struct line *line)
 195{
 196        struct chan *chan;
 197
 198        /* Close in reverse order as open in case more than one of them
 199         * refers to the same device and they save and restore that device's
 200         * state.  Then, the first one opened will have the original state,
 201         * so it must be the last closed.
 202         */
 203        list_for_each_entry_reverse(chan, &line->chan_list, list) {
 204                close_one_chan(chan, 0);
 205        }
 206}
 207
 208void deactivate_chan(struct chan *chan, int irq)
 209{
 210        if (chan && chan->enabled)
 211                deactivate_fd(chan->fd, irq);
 212}
 213
 214void reactivate_chan(struct chan *chan, int irq)
 215{
 216        if (chan && chan->enabled)
 217                reactivate_fd(chan->fd, irq);
 218}
 219
 220int write_chan(struct chan *chan, const char *buf, int len,
 221               int write_irq)
 222{
 223        int n, ret = 0;
 224
 225        if (len == 0 || !chan || !chan->ops->write)
 226                return 0;
 227
 228        n = chan->ops->write(chan->fd, buf, len, chan->data);
 229        if (chan->primary) {
 230                ret = n;
 231                if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
 232                        reactivate_fd(chan->fd, write_irq);
 233        }
 234        return ret;
 235}
 236
 237int console_write_chan(struct chan *chan, const char *buf, int len)
 238{
 239        int n, ret = 0;
 240
 241        if (!chan || !chan->ops->console_write)
 242                return 0;
 243
 244        n = chan->ops->console_write(chan->fd, buf, len);
 245        if (chan->primary)
 246                ret = n;
 247        return ret;
 248}
 249
 250int console_open_chan(struct line *line, struct console *co)
 251{
 252        int err;
 253
 254        err = open_chan(&line->chan_list);
 255        if (err)
 256                return err;
 257
 258        printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
 259               co->index);
 260        return 0;
 261}
 262
 263int chan_window_size(struct line *line, unsigned short *rows_out,
 264                      unsigned short *cols_out)
 265{
 266        struct chan *chan;
 267
 268        chan = line->chan_in;
 269        if (chan && chan->primary) {
 270                if (chan->ops->window_size == NULL)
 271                        return 0;
 272                return chan->ops->window_size(chan->fd, chan->data,
 273                                              rows_out, cols_out);
 274        }
 275        chan = line->chan_out;
 276        if (chan && chan->primary) {
 277                if (chan->ops->window_size == NULL)
 278                        return 0;
 279                return chan->ops->window_size(chan->fd, chan->data,
 280                                              rows_out, cols_out);
 281        }
 282        return 0;
 283}
 284
 285static void free_one_chan(struct chan *chan)
 286{
 287        list_del(&chan->list);
 288
 289        close_one_chan(chan, 0);
 290
 291        if (chan->ops->free != NULL)
 292                (*chan->ops->free)(chan->data);
 293
 294        if (chan->primary && chan->output)
 295                ignore_sigio_fd(chan->fd);
 296        kfree(chan);
 297}
 298
 299static void free_chan(struct list_head *chans)
 300{
 301        struct list_head *ele, *next;
 302        struct chan *chan;
 303
 304        list_for_each_safe(ele, next, chans) {
 305                chan = list_entry(ele, struct chan, list);
 306                free_one_chan(chan);
 307        }
 308}
 309
 310static int one_chan_config_string(struct chan *chan, char *str, int size,
 311                                  char **error_out)
 312{
 313        int n = 0;
 314
 315        if (chan == NULL) {
 316                CONFIG_CHUNK(str, size, n, "none", 1);
 317                return n;
 318        }
 319
 320        CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
 321
 322        if (chan->dev == NULL) {
 323                CONFIG_CHUNK(str, size, n, "", 1);
 324                return n;
 325        }
 326
 327        CONFIG_CHUNK(str, size, n, ":", 0);
 328        CONFIG_CHUNK(str, size, n, chan->dev, 0);
 329
 330        return n;
 331}
 332
 333static int chan_pair_config_string(struct chan *in, struct chan *out,
 334                                   char *str, int size, char **error_out)
 335{
 336        int n;
 337
 338        n = one_chan_config_string(in, str, size, error_out);
 339        str += n;
 340        size -= n;
 341
 342        if (in == out) {
 343                CONFIG_CHUNK(str, size, n, "", 1);
 344                return n;
 345        }
 346
 347        CONFIG_CHUNK(str, size, n, ",", 1);
 348        n = one_chan_config_string(out, str, size, error_out);
 349        str += n;
 350        size -= n;
 351        CONFIG_CHUNK(str, size, n, "", 1);
 352
 353        return n;
 354}
 355
 356int chan_config_string(struct line *line, char *str, int size,
 357                       char **error_out)
 358{
 359        struct chan *in = line->chan_in, *out = line->chan_out;
 360
 361        if (in && !in->primary)
 362                in = NULL;
 363        if (out && !out->primary)
 364                out = NULL;
 365
 366        return chan_pair_config_string(in, out, str, size, error_out);
 367}
 368
 369struct chan_type {
 370        char *key;
 371        const struct chan_ops *ops;
 372};
 373
 374static const struct chan_type chan_table[] = {
 375        { "fd", &fd_ops },
 376
 377#ifdef CONFIG_NULL_CHAN
 378        { "null", &null_ops },
 379#else
 380        { "null", &not_configged_ops },
 381#endif
 382
 383#ifdef CONFIG_PORT_CHAN
 384        { "port", &port_ops },
 385#else
 386        { "port", &not_configged_ops },
 387#endif
 388
 389#ifdef CONFIG_PTY_CHAN
 390        { "pty", &pty_ops },
 391        { "pts", &pts_ops },
 392#else
 393        { "pty", &not_configged_ops },
 394        { "pts", &not_configged_ops },
 395#endif
 396
 397#ifdef CONFIG_TTY_CHAN
 398        { "tty", &tty_ops },
 399#else
 400        { "tty", &not_configged_ops },
 401#endif
 402
 403#ifdef CONFIG_XTERM_CHAN
 404        { "xterm", &xterm_ops },
 405#else
 406        { "xterm", &not_configged_ops },
 407#endif
 408};
 409
 410static struct chan *parse_chan(struct line *line, char *str, int device,
 411                               const struct chan_opts *opts, char **error_out)
 412{
 413        const struct chan_type *entry;
 414        const struct chan_ops *ops;
 415        struct chan *chan;
 416        void *data;
 417        int i;
 418
 419        ops = NULL;
 420        data = NULL;
 421        for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
 422                entry = &chan_table[i];
 423                if (!strncmp(str, entry->key, strlen(entry->key))) {
 424                        ops = entry->ops;
 425                        str += strlen(entry->key);
 426                        break;
 427                }
 428        }
 429        if (ops == NULL) {
 430                *error_out = "No match for configured backends";
 431                return NULL;
 432        }
 433
 434        data = (*ops->init)(str, device, opts);
 435        if (data == NULL) {
 436                *error_out = "Configuration failed";
 437                return NULL;
 438        }
 439
 440        chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
 441        if (chan == NULL) {
 442                *error_out = "Memory allocation failed";
 443                return NULL;
 444        }
 445        *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
 446                                 .free_list     =
 447                                        LIST_HEAD_INIT(chan->free_list),
 448                                 .line          = line,
 449                                 .primary       = 1,
 450                                 .input         = 0,
 451                                 .output        = 0,
 452                                 .opened        = 0,
 453                                 .enabled       = 0,
 454                                 .fd            = -1,
 455                                 .ops           = ops,
 456                                 .data          = data });
 457        return chan;
 458}
 459
 460int parse_chan_pair(char *str, struct line *line, int device,
 461                    const struct chan_opts *opts, char **error_out)
 462{
 463        struct list_head *chans = &line->chan_list;
 464        struct chan *new;
 465        char *in, *out;
 466
 467        if (!list_empty(chans)) {
 468                line->chan_in = line->chan_out = NULL;
 469                free_chan(chans);
 470                INIT_LIST_HEAD(chans);
 471        }
 472
 473        if (!str)
 474                return 0;
 475
 476        out = strchr(str, ',');
 477        if (out != NULL) {
 478                in = str;
 479                *out = '\0';
 480                out++;
 481                new = parse_chan(line, in, device, opts, error_out);
 482                if (new == NULL)
 483                        return -1;
 484
 485                new->input = 1;
 486                list_add(&new->list, chans);
 487                line->chan_in = new;
 488
 489                new = parse_chan(line, out, device, opts, error_out);
 490                if (new == NULL)
 491                        return -1;
 492
 493                list_add(&new->list, chans);
 494                new->output = 1;
 495                line->chan_out = new;
 496        }
 497        else {
 498                new = parse_chan(line, str, device, opts, error_out);
 499                if (new == NULL)
 500                        return -1;
 501
 502                list_add(&new->list, chans);
 503                new->input = 1;
 504                new->output = 1;
 505                line->chan_in = line->chan_out = new;
 506        }
 507        return 0;
 508}
 509
 510void chan_interrupt(struct line *line, int irq)
 511{
 512        struct tty_port *port = &line->port;
 513        struct chan *chan = line->chan_in;
 514        int err;
 515        char c;
 516
 517        if (!chan || !chan->ops->read)
 518                goto out;
 519
 520        do {
 521                if (!tty_buffer_request_room(port, 1)) {
 522                        schedule_delayed_work(&line->task, 1);
 523                        goto out;
 524                }
 525                err = chan->ops->read(chan->fd, &c, chan->data);
 526                if (err > 0)
 527                        tty_insert_flip_char(port, c, TTY_NORMAL);
 528        } while (err > 0);
 529
 530        if (err == 0)
 531                reactivate_fd(chan->fd, irq);
 532        if (err == -EIO) {
 533                if (chan->primary) {
 534                        tty_port_tty_hangup(&line->port, false);
 535                        if (line->chan_out != chan)
 536                                close_one_chan(line->chan_out, 1);
 537                }
 538                close_one_chan(chan, 1);
 539                if (chan->primary)
 540                        return;
 541        }
 542 out:
 543        tty_flip_buffer_push(port);
 544}
 545