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