root/arch/um/drivers/chan_kern.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. not_configged_init
  2. not_configged_open
  3. not_configged_close
  4. not_configged_read
  5. not_configged_write
  6. not_configged_console_write
  7. not_configged_window_size
  8. not_configged_free
  9. open_one_chan
  10. open_chan
  11. chan_enable_winch
  12. line_timer_cb
  13. enable_chan
  14. free_irqs
  15. close_one_chan
  16. close_chan
  17. deactivate_chan
  18. write_chan
  19. console_write_chan
  20. console_open_chan
  21. chan_window_size
  22. free_one_chan
  23. free_chan
  24. one_chan_config_string
  25. chan_pair_config_string
  26. chan_config_string
  27. parse_chan
  28. parse_chan_pair
  29. chan_interrupt

   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
  14 static 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 
  22 static 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 
  30 static 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 
  36 static 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 
  43 static 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 
  50 static 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 
  57 static 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 
  65 static 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 
  71 static 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 
  84 static 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 
 110 static 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 
 125 void 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 
 131 static 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 
 139 int 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  */
 180 static DEFINE_SPINLOCK(irqs_to_free_lock);
 181 static LIST_HEAD(irqs_to_free);
 182 
 183 void 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 
 205 static 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 
 230 void 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 
 244 void deactivate_chan(struct chan *chan, int irq)
 245 {
 246         if (chan && chan->enabled)
 247                 deactivate_fd(chan->fd, irq);
 248 }
 249 
 250 int 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 
 265 int 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 
 278 int 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 
 291 int 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 
 313 static 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 
 327 static 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 
 338 static 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 
 361 static 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 
 384 int 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 
 397 struct chan_type {
 398         char *key;
 399         const struct chan_ops *ops;
 400 };
 401 
 402 static 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 
 438 static 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 
 488 int 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 
 538 void 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 }

/* [<][>][^][v][top][bottom][index][help] */