LCOV - code coverage report
Current view: top level - arch/um/drivers - chan_kern.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 86 235 36.6 %
Date: 2023-04-06 08:38:28 Functions: 9 28 32.1 %

          Line data    Source code
       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          15 : static void *not_configged_init(char *str, int device,
      15             :                                 const struct chan_opts *opts)
      16             : {
      17          15 :         printk(KERN_ERR "Using a channel type which is configured out of "
      18             :                "UML\n");
      19          15 :         return NULL;
      20             : }
      21             : 
      22           0 : static int not_configged_open(int input, int output, int primary, void *data,
      23             :                               char **dev_out)
      24             : {
      25           0 :         printk(KERN_ERR "Using a channel type which is configured out of "
      26             :                "UML\n");
      27           0 :         return -ENODEV;
      28             : }
      29             : 
      30           0 : static void not_configged_close(int fd, void *data)
      31             : {
      32           0 :         printk(KERN_ERR "Using a channel type which is configured out of "
      33             :                "UML\n");
      34           0 : }
      35             : 
      36           0 : static int not_configged_read(int fd, char *c_out, void *data)
      37             : {
      38           0 :         printk(KERN_ERR "Using a channel type which is configured out of "
      39             :                "UML\n");
      40           0 :         return -EIO;
      41             : }
      42             : 
      43           0 : static int not_configged_write(int fd, const char *buf, int len, void *data)
      44             : {
      45           0 :         printk(KERN_ERR "Using a channel type which is configured out of "
      46             :                "UML\n");
      47           0 :         return -EIO;
      48             : }
      49             : 
      50           0 : static int not_configged_console_write(int fd, const char *buf, int len)
      51             : {
      52           0 :         printk(KERN_ERR "Using a channel type which is configured out of "
      53             :                "UML\n");
      54           0 :         return -EIO;
      55             : }
      56             : 
      57           0 : static int not_configged_window_size(int fd, void *data, unsigned short *rows,
      58             :                                      unsigned short *cols)
      59             : {
      60           0 :         printk(KERN_ERR "Using a channel type which is configured out of "
      61             :                "UML\n");
      62           0 :         return -ENODEV;
      63             : }
      64             : 
      65           0 : static void not_configged_free(void *data)
      66             : {
      67           0 :         printk(KERN_ERR "Using a channel type which is configured out of "
      68             :                "UML\n");
      69           0 : }
      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           2 : static int open_one_chan(struct chan *chan)
      85             : {
      86             :         int fd, err;
      87             : 
      88           2 :         if (chan->opened)
      89             :                 return 0;
      90             : 
      91           2 :         if (chan->ops->open == NULL)
      92             :                 fd = 0;
      93           2 :         else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
      94             :                                      chan->data, &chan->dev);
      95           2 :         if (fd < 0)
      96             :                 return fd;
      97             : 
      98           2 :         err = os_set_fd_block(fd, 0);
      99           2 :         if (err) {
     100           0 :                 (*chan->ops->close)(fd, chan->data);
     101           0 :                 return err;
     102             :         }
     103             : 
     104           2 :         chan->fd = fd;
     105             : 
     106           2 :         chan->opened = 1;
     107           2 :         return 0;
     108             : }
     109             : 
     110           1 : static int open_chan(struct list_head *chans)
     111             : {
     112             :         struct list_head *ele;
     113             :         struct chan *chan;
     114           1 :         int ret, err = 0;
     115             : 
     116           3 :         list_for_each(ele, chans) {
     117           2 :                 chan = list_entry(ele, struct chan, list);
     118           2 :                 ret = open_one_chan(chan);
     119           2 :                 if (chan->primary)
     120           2 :                         err = ret;
     121             :         }
     122           1 :         return err;
     123             : }
     124             : 
     125           0 : void chan_enable_winch(struct chan *chan, struct tty_port *port)
     126             : {
     127           0 :         if (chan && chan->primary && chan->ops->winch)
     128           0 :                 register_winch(chan->fd, port);
     129           0 : }
     130             : 
     131           0 : static void line_timer_cb(struct work_struct *work)
     132             : {
     133           0 :         struct line *line = container_of(work, struct line, task.work);
     134             : 
     135           0 :         if (!line->throttled)
     136           0 :                 chan_interrupt(line, line->read_irq);
     137           0 : }
     138             : 
     139           0 : int enable_chan(struct line *line)
     140             : {
     141             :         struct list_head *ele;
     142             :         struct chan *chan;
     143             :         int err;
     144             : 
     145           0 :         INIT_DELAYED_WORK(&line->task, line_timer_cb);
     146             : 
     147           0 :         list_for_each(ele, &line->chan_list) {
     148           0 :                 chan = list_entry(ele, struct chan, list);
     149           0 :                 err = open_one_chan(chan);
     150           0 :                 if (err) {
     151           0 :                         if (chan->primary)
     152             :                                 goto out_close;
     153             : 
     154           0 :                         continue;
     155             :                 }
     156             : 
     157           0 :                 if (chan->enabled)
     158           0 :                         continue;
     159           0 :                 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
     160             :                                      chan);
     161           0 :                 if (err)
     162             :                         goto out_close;
     163             : 
     164           0 :                 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           0 : void free_irqs(void)
     184             : {
     185             :         struct chan *chan;
     186           0 :         LIST_HEAD(list);
     187             :         struct list_head *ele;
     188             :         unsigned long flags;
     189             : 
     190           0 :         spin_lock_irqsave(&irqs_to_free_lock, flags);
     191           0 :         list_splice_init(&irqs_to_free, &list);
     192           0 :         spin_unlock_irqrestore(&irqs_to_free_lock, flags);
     193             : 
     194           0 :         list_for_each(ele, &list) {
     195           0 :                 chan = list_entry(ele, struct chan, free_list);
     196             : 
     197           0 :                 if (chan->input && chan->enabled)
     198           0 :                         um_free_irq(chan->line->read_irq, chan);
     199           0 :                 if (chan->output && chan->enabled)
     200           0 :                         um_free_irq(chan->line->write_irq, chan);
     201           0 :                 chan->enabled = 0;
     202             :         }
     203           0 : }
     204             : 
     205           2 : static void close_one_chan(struct chan *chan, int delay_free_irq)
     206             : {
     207             :         unsigned long flags;
     208             : 
     209           2 :         if (!chan->opened)
     210             :                 return;
     211             : 
     212           2 :         if (delay_free_irq) {
     213           0 :                 spin_lock_irqsave(&irqs_to_free_lock, flags);
     214           0 :                 list_add(&chan->free_list, &irqs_to_free);
     215             :                 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
     216             :         } else {
     217           2 :                 if (chan->input && chan->enabled)
     218           0 :                         um_free_irq(chan->line->read_irq, chan);
     219           2 :                 if (chan->output && chan->enabled)
     220           0 :                         um_free_irq(chan->line->write_irq, chan);
     221           2 :                 chan->enabled = 0;
     222             :         }
     223           2 :         if (chan->ops->close != NULL)
     224           2 :                 (*chan->ops->close)(chan->fd, chan->data);
     225             : 
     226           2 :         chan->opened = 0;
     227           2 :         chan->fd = -1;
     228             : }
     229             : 
     230          16 : 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          18 :         list_for_each_entry_reverse(chan, &line->chan_list, list) {
     240           2 :                 close_one_chan(chan, 0);
     241             :         }
     242          16 : }
     243             : 
     244           0 : void deactivate_chan(struct chan *chan, int irq)
     245             : {
     246           0 :         if (chan && chan->enabled)
     247           0 :                 deactivate_fd(chan->fd, irq);
     248           0 : }
     249             : 
     250           0 : int write_chan(struct chan *chan, const char *buf, int len,
     251             :                int write_irq)
     252             : {
     253           0 :         int n, ret = 0;
     254             : 
     255           0 :         if (len == 0 || !chan || !chan->ops->write)
     256             :                 return 0;
     257             : 
     258           0 :         n = chan->ops->write(chan->fd, buf, len, chan->data);
     259           0 :         if (chan->primary) {
     260           0 :                 ret = n;
     261             :         }
     262             :         return ret;
     263             : }
     264             : 
     265         759 : int console_write_chan(struct chan *chan, const char *buf, int len)
     266             : {
     267         759 :         int n, ret = 0;
     268             : 
     269         759 :         if (!chan || !chan->ops->console_write)
     270             :                 return 0;
     271             : 
     272         759 :         n = chan->ops->console_write(chan->fd, buf, len);
     273         759 :         if (chan->primary)
     274         759 :                 ret = n;
     275             :         return ret;
     276             : }
     277             : 
     278           1 : int console_open_chan(struct line *line, struct console *co)
     279             : {
     280             :         int err;
     281             : 
     282           1 :         err = open_chan(&line->chan_list);
     283           1 :         if (err)
     284             :                 return err;
     285             : 
     286           1 :         printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
     287             :                co->index);
     288           1 :         return 0;
     289             : }
     290             : 
     291           0 : int chan_window_size(struct line *line, unsigned short *rows_out,
     292             :                       unsigned short *cols_out)
     293             : {
     294             :         struct chan *chan;
     295             : 
     296           0 :         chan = line->chan_in;
     297           0 :         if (chan && chan->primary) {
     298           0 :                 if (chan->ops->window_size == NULL)
     299             :                         return 0;
     300           0 :                 return chan->ops->window_size(chan->fd, chan->data,
     301             :                                               rows_out, cols_out);
     302             :         }
     303           0 :         chan = line->chan_out;
     304           0 :         if (chan && chan->primary) {
     305           0 :                 if (chan->ops->window_size == NULL)
     306             :                         return 0;
     307           0 :                 return chan->ops->window_size(chan->fd, chan->data,
     308             :                                               rows_out, cols_out);
     309             :         }
     310             :         return 0;
     311             : }
     312             : 
     313           0 : static void free_one_chan(struct chan *chan)
     314             : {
     315           0 :         list_del(&chan->list);
     316             : 
     317           0 :         close_one_chan(chan, 0);
     318             : 
     319           0 :         if (chan->ops->free != NULL)
     320           0 :                 (*chan->ops->free)(chan->data);
     321             : 
     322           0 :         if (chan->primary && chan->output)
     323           0 :                 ignore_sigio_fd(chan->fd);
     324           0 :         kfree(chan);
     325           0 : }
     326             : 
     327             : static void free_chan(struct list_head *chans)
     328             : {
     329             :         struct list_head *ele, *next;
     330             :         struct chan *chan;
     331             : 
     332           0 :         list_for_each_safe(ele, next, chans) {
     333           0 :                 chan = list_entry(ele, struct chan, list);
     334           0 :                 free_one_chan(chan);
     335             :         }
     336             : }
     337             : 
     338           0 : static int one_chan_config_string(struct chan *chan, char *str, int size,
     339             :                                   char **error_out)
     340             : {
     341           0 :         int n = 0;
     342             : 
     343           0 :         if (chan == NULL) {
     344           0 :                 CONFIG_CHUNK(str, size, n, "none", 1);
     345             :                 return n;
     346             :         }
     347             : 
     348           0 :         CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
     349             : 
     350           0 :         if (chan->dev == NULL) {
     351           0 :                 CONFIG_CHUNK(str, size, n, "", 1);
     352             :                 return n;
     353             :         }
     354             : 
     355           0 :         CONFIG_CHUNK(str, size, n, ":", 0);
     356           0 :         CONFIG_CHUNK(str, size, n, chan->dev, 0);
     357             : 
     358             :         return n;
     359             : }
     360             : 
     361           0 : 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           0 :         n = one_chan_config_string(in, str, size, error_out);
     367           0 :         str += n;
     368           0 :         size -= n;
     369             : 
     370           0 :         if (in == out) {
     371           0 :                 CONFIG_CHUNK(str, size, n, "", 1);
     372             :                 return n;
     373             :         }
     374             : 
     375           0 :         CONFIG_CHUNK(str, size, n, ",", 1);
     376           0 :         n = one_chan_config_string(out, str, size, error_out);
     377           0 :         str += n;
     378           0 :         size -= n;
     379           0 :         CONFIG_CHUNK(str, size, n, "", 1);
     380             : 
     381             :         return n;
     382             : }
     383             : 
     384           0 : int chan_config_string(struct line *line, char *str, int size,
     385             :                        char **error_out)
     386             : {
     387           0 :         struct chan *in = line->chan_in, *out = line->chan_out;
     388             : 
     389           0 :         if (in && !in->primary)
     390           0 :                 in = NULL;
     391           0 :         if (out && !out->primary)
     392           0 :                 out = NULL;
     393             : 
     394           0 :         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          17 : 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          17 :         ops = NULL;
     448          17 :         data = NULL;
     449         107 :         for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
     450         107 :                 entry = &chan_table[i];
     451         107 :                 if (!strncmp(str, entry->key, strlen(entry->key))) {
     452          17 :                         ops = entry->ops;
     453          17 :                         str += strlen(entry->key);
     454          17 :                         break;
     455             :                 }
     456             :         }
     457          17 :         if (ops == NULL) {
     458           0 :                 *error_out = "No match for configured backends";
     459           0 :                 return NULL;
     460             :         }
     461             : 
     462          17 :         data = (*ops->init)(str, device, opts);
     463          17 :         if (data == NULL) {
     464          15 :                 *error_out = "Configuration failed";
     465          15 :                 return NULL;
     466             :         }
     467             : 
     468           2 :         chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
     469           2 :         if (chan == NULL) {
     470           0 :                 *error_out = "Memory allocation failed";
     471           0 :                 return NULL;
     472             :         }
     473           4 :         *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
     474             :                                  .free_list     =
     475           2 :                                         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           2 :         return chan;
     486             : }
     487             : 
     488          16 : int parse_chan_pair(char *str, struct line *line, int device,
     489             :                     const struct chan_opts *opts, char **error_out)
     490             : {
     491          16 :         struct list_head *chans = &line->chan_list;
     492             :         struct chan *new;
     493             :         char *in, *out;
     494             : 
     495          16 :         if (!list_empty(chans)) {
     496           0 :                 line->chan_in = line->chan_out = NULL;
     497           0 :                 free_chan(chans);
     498             :                 INIT_LIST_HEAD(chans);
     499             :         }
     500             : 
     501          16 :         if (!str)
     502             :                 return 0;
     503             : 
     504          16 :         out = strchr(str, ',');
     505          16 :         if (out != NULL) {
     506           1 :                 in = str;
     507           1 :                 *out = '\0';
     508           1 :                 out++;
     509           1 :                 new = parse_chan(line, in, device, opts, error_out);
     510           1 :                 if (new == NULL)
     511             :                         return -1;
     512             : 
     513           1 :                 new->input = 1;
     514           2 :                 list_add(&new->list, chans);
     515           1 :                 line->chan_in = new;
     516             : 
     517           1 :                 new = parse_chan(line, out, device, opts, error_out);
     518           1 :                 if (new == NULL)
     519             :                         return -1;
     520             : 
     521           2 :                 list_add(&new->list, chans);
     522           1 :                 new->output = 1;
     523           1 :                 line->chan_out = new;
     524             :         }
     525             :         else {
     526          15 :                 new = parse_chan(line, str, device, opts, error_out);
     527          15 :                 if (new == NULL)
     528             :                         return -1;
     529             : 
     530           0 :                 list_add(&new->list, chans);
     531           0 :                 new->input = 1;
     532           0 :                 new->output = 1;
     533           0 :                 line->chan_in = line->chan_out = new;
     534             :         }
     535             :         return 0;
     536             : }
     537             : 
     538           0 : void chan_interrupt(struct line *line, int irq)
     539             : {
     540           0 :         struct tty_port *port = &line->port;
     541           0 :         struct chan *chan = line->chan_in;
     542             :         int err;
     543             :         char c;
     544             : 
     545           0 :         if (!chan || !chan->ops->read)
     546             :                 goto out;
     547             : 
     548             :         do {
     549           0 :                 if (!tty_buffer_request_room(port, 1)) {
     550           0 :                         schedule_delayed_work(&line->task, 1);
     551             :                         goto out;
     552             :                 }
     553           0 :                 err = chan->ops->read(chan->fd, &c, chan->data);
     554           0 :                 if (err > 0)
     555           0 :                         tty_insert_flip_char(port, c, TTY_NORMAL);
     556           0 :         } while (err > 0);
     557             : 
     558           0 :         if (err == -EIO) {
     559           0 :                 if (chan->primary) {
     560           0 :                         tty_port_tty_hangup(&line->port, false);
     561           0 :                         if (line->chan_out != chan)
     562           0 :                                 close_one_chan(line->chan_out, 1);
     563             :                 }
     564           0 :                 close_one_chan(chan, 1);
     565           0 :                 if (chan->primary)
     566           0 :                         return;
     567             :         }
     568             :  out:
     569           0 :         tty_flip_buffer_push(port);
     570             : }

Generated by: LCOV version 1.14