18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/tty.h> 88c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 98c2ecf20Sopenharmony_ci#include "chan.h" 108c2ecf20Sopenharmony_ci#include <os.h> 118c2ecf20Sopenharmony_ci#include <irq_kern.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#ifdef CONFIG_NOCONFIG_CHAN 148c2ecf20Sopenharmony_cistatic void *not_configged_init(char *str, int device, 158c2ecf20Sopenharmony_ci const struct chan_opts *opts) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci printk(KERN_ERR "Using a channel type which is configured out of " 188c2ecf20Sopenharmony_ci "UML\n"); 198c2ecf20Sopenharmony_ci return NULL; 208c2ecf20Sopenharmony_ci} 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int not_configged_open(int input, int output, int primary, void *data, 238c2ecf20Sopenharmony_ci char **dev_out) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci printk(KERN_ERR "Using a channel type which is configured out of " 268c2ecf20Sopenharmony_ci "UML\n"); 278c2ecf20Sopenharmony_ci return -ENODEV; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void not_configged_close(int fd, void *data) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci printk(KERN_ERR "Using a channel type which is configured out of " 338c2ecf20Sopenharmony_ci "UML\n"); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int not_configged_read(int fd, char *c_out, void *data) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci printk(KERN_ERR "Using a channel type which is configured out of " 398c2ecf20Sopenharmony_ci "UML\n"); 408c2ecf20Sopenharmony_ci return -EIO; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int not_configged_write(int fd, const char *buf, int len, void *data) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci printk(KERN_ERR "Using a channel type which is configured out of " 468c2ecf20Sopenharmony_ci "UML\n"); 478c2ecf20Sopenharmony_ci return -EIO; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int not_configged_console_write(int fd, const char *buf, int len) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci printk(KERN_ERR "Using a channel type which is configured out of " 538c2ecf20Sopenharmony_ci "UML\n"); 548c2ecf20Sopenharmony_ci return -EIO; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int not_configged_window_size(int fd, void *data, unsigned short *rows, 588c2ecf20Sopenharmony_ci unsigned short *cols) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci printk(KERN_ERR "Using a channel type which is configured out of " 618c2ecf20Sopenharmony_ci "UML\n"); 628c2ecf20Sopenharmony_ci return -ENODEV; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void not_configged_free(void *data) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci printk(KERN_ERR "Using a channel type which is configured out of " 688c2ecf20Sopenharmony_ci "UML\n"); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic const struct chan_ops not_configged_ops = { 728c2ecf20Sopenharmony_ci .init = not_configged_init, 738c2ecf20Sopenharmony_ci .open = not_configged_open, 748c2ecf20Sopenharmony_ci .close = not_configged_close, 758c2ecf20Sopenharmony_ci .read = not_configged_read, 768c2ecf20Sopenharmony_ci .write = not_configged_write, 778c2ecf20Sopenharmony_ci .console_write = not_configged_console_write, 788c2ecf20Sopenharmony_ci .window_size = not_configged_window_size, 798c2ecf20Sopenharmony_ci .free = not_configged_free, 808c2ecf20Sopenharmony_ci .winch = 0, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci#endif /* CONFIG_NOCONFIG_CHAN */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int open_one_chan(struct chan *chan) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int fd, err; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (chan->opened) 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (chan->ops->open == NULL) 928c2ecf20Sopenharmony_ci fd = 0; 938c2ecf20Sopenharmony_ci else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary, 948c2ecf20Sopenharmony_ci chan->data, &chan->dev); 958c2ecf20Sopenharmony_ci if (fd < 0) 968c2ecf20Sopenharmony_ci return fd; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci err = os_set_fd_block(fd, 0); 998c2ecf20Sopenharmony_ci if (err) { 1008c2ecf20Sopenharmony_ci (*chan->ops->close)(fd, chan->data); 1018c2ecf20Sopenharmony_ci return err; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci chan->fd = fd; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci chan->opened = 1; 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int open_chan(struct list_head *chans) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct list_head *ele; 1138c2ecf20Sopenharmony_ci struct chan *chan; 1148c2ecf20Sopenharmony_ci int ret, err = 0; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci list_for_each(ele, chans) { 1178c2ecf20Sopenharmony_ci chan = list_entry(ele, struct chan, list); 1188c2ecf20Sopenharmony_ci ret = open_one_chan(chan); 1198c2ecf20Sopenharmony_ci if (chan->primary) 1208c2ecf20Sopenharmony_ci err = ret; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci return err; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_civoid chan_enable_winch(struct chan *chan, struct tty_port *port) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci if (chan && chan->primary && chan->ops->winch) 1288c2ecf20Sopenharmony_ci register_winch(chan->fd, port); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void line_timer_cb(struct work_struct *work) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct line *line = container_of(work, struct line, task.work); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!line->throttled) 1368c2ecf20Sopenharmony_ci chan_interrupt(line, line->driver->read_irq); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciint enable_chan(struct line *line) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct list_head *ele; 1428c2ecf20Sopenharmony_ci struct chan *chan; 1438c2ecf20Sopenharmony_ci int err; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&line->task, line_timer_cb); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci list_for_each(ele, &line->chan_list) { 1488c2ecf20Sopenharmony_ci chan = list_entry(ele, struct chan, list); 1498c2ecf20Sopenharmony_ci err = open_one_chan(chan); 1508c2ecf20Sopenharmony_ci if (err) { 1518c2ecf20Sopenharmony_ci if (chan->primary) 1528c2ecf20Sopenharmony_ci goto out_close; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci continue; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (chan->enabled) 1588c2ecf20Sopenharmony_ci continue; 1598c2ecf20Sopenharmony_ci err = line_setup_irq(chan->fd, chan->input, chan->output, line, 1608c2ecf20Sopenharmony_ci chan); 1618c2ecf20Sopenharmony_ci if (err) 1628c2ecf20Sopenharmony_ci goto out_close; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci chan->enabled = 1; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci out_close: 1708c2ecf20Sopenharmony_ci close_chan(line); 1718c2ecf20Sopenharmony_ci return err; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* Items are added in IRQ context, when free_irq can't be called, and 1758c2ecf20Sopenharmony_ci * removed in process context, when it can. 1768c2ecf20Sopenharmony_ci * This handles interrupt sources which disappear, and which need to 1778c2ecf20Sopenharmony_ci * be permanently disabled. This is discovered in IRQ context, but 1788c2ecf20Sopenharmony_ci * the freeing of the IRQ must be done later. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(irqs_to_free_lock); 1818c2ecf20Sopenharmony_cistatic LIST_HEAD(irqs_to_free); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_civoid free_irqs(void) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct chan *chan; 1868c2ecf20Sopenharmony_ci LIST_HEAD(list); 1878c2ecf20Sopenharmony_ci struct list_head *ele; 1888c2ecf20Sopenharmony_ci unsigned long flags; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci spin_lock_irqsave(&irqs_to_free_lock, flags); 1918c2ecf20Sopenharmony_ci list_splice_init(&irqs_to_free, &list); 1928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&irqs_to_free_lock, flags); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci list_for_each(ele, &list) { 1958c2ecf20Sopenharmony_ci chan = list_entry(ele, struct chan, free_list); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (chan->input && chan->enabled) 1988c2ecf20Sopenharmony_ci um_free_irq(chan->line->driver->read_irq, chan); 1998c2ecf20Sopenharmony_ci if (chan->output && chan->enabled) 2008c2ecf20Sopenharmony_ci um_free_irq(chan->line->driver->write_irq, chan); 2018c2ecf20Sopenharmony_ci chan->enabled = 0; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void close_one_chan(struct chan *chan, int delay_free_irq) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci unsigned long flags; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!chan->opened) 2108c2ecf20Sopenharmony_ci return; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (delay_free_irq) { 2138c2ecf20Sopenharmony_ci spin_lock_irqsave(&irqs_to_free_lock, flags); 2148c2ecf20Sopenharmony_ci list_add(&chan->free_list, &irqs_to_free); 2158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&irqs_to_free_lock, flags); 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci if (chan->input && chan->enabled) 2188c2ecf20Sopenharmony_ci um_free_irq(chan->line->driver->read_irq, chan); 2198c2ecf20Sopenharmony_ci if (chan->output && chan->enabled) 2208c2ecf20Sopenharmony_ci um_free_irq(chan->line->driver->write_irq, chan); 2218c2ecf20Sopenharmony_ci chan->enabled = 0; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci if (chan->ops->close != NULL) 2248c2ecf20Sopenharmony_ci (*chan->ops->close)(chan->fd, chan->data); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci chan->opened = 0; 2278c2ecf20Sopenharmony_ci chan->fd = -1; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_civoid close_chan(struct line *line) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct chan *chan; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Close in reverse order as open in case more than one of them 2358c2ecf20Sopenharmony_ci * refers to the same device and they save and restore that device's 2368c2ecf20Sopenharmony_ci * state. Then, the first one opened will have the original state, 2378c2ecf20Sopenharmony_ci * so it must be the last closed. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci list_for_each_entry_reverse(chan, &line->chan_list, list) { 2408c2ecf20Sopenharmony_ci close_one_chan(chan, 0); 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_civoid deactivate_chan(struct chan *chan, int irq) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci if (chan && chan->enabled) 2478c2ecf20Sopenharmony_ci deactivate_fd(chan->fd, irq); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciint write_chan(struct chan *chan, const char *buf, int len, 2518c2ecf20Sopenharmony_ci int write_irq) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci int n, ret = 0; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (len == 0 || !chan || !chan->ops->write) 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci n = chan->ops->write(chan->fd, buf, len, chan->data); 2598c2ecf20Sopenharmony_ci if (chan->primary) { 2608c2ecf20Sopenharmony_ci ret = n; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciint console_write_chan(struct chan *chan, const char *buf, int len) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci int n, ret = 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (!chan || !chan->ops->console_write) 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci n = chan->ops->console_write(chan->fd, buf, len); 2738c2ecf20Sopenharmony_ci if (chan->primary) 2748c2ecf20Sopenharmony_ci ret = n; 2758c2ecf20Sopenharmony_ci return ret; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ciint console_open_chan(struct line *line, struct console *co) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci int err; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci err = open_chan(&line->chan_list); 2838c2ecf20Sopenharmony_ci if (err) 2848c2ecf20Sopenharmony_ci return err; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name, 2878c2ecf20Sopenharmony_ci co->index); 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ciint chan_window_size(struct line *line, unsigned short *rows_out, 2928c2ecf20Sopenharmony_ci unsigned short *cols_out) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct chan *chan; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci chan = line->chan_in; 2978c2ecf20Sopenharmony_ci if (chan && chan->primary) { 2988c2ecf20Sopenharmony_ci if (chan->ops->window_size == NULL) 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci return chan->ops->window_size(chan->fd, chan->data, 3018c2ecf20Sopenharmony_ci rows_out, cols_out); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci chan = line->chan_out; 3048c2ecf20Sopenharmony_ci if (chan && chan->primary) { 3058c2ecf20Sopenharmony_ci if (chan->ops->window_size == NULL) 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci return chan->ops->window_size(chan->fd, chan->data, 3088c2ecf20Sopenharmony_ci rows_out, cols_out); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic void free_one_chan(struct chan *chan) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci list_del(&chan->list); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci close_one_chan(chan, 0); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (chan->ops->free != NULL) 3208c2ecf20Sopenharmony_ci (*chan->ops->free)(chan->data); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (chan->primary && chan->output) 3238c2ecf20Sopenharmony_ci ignore_sigio_fd(chan->fd); 3248c2ecf20Sopenharmony_ci kfree(chan); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic void free_chan(struct list_head *chans) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct list_head *ele, *next; 3308c2ecf20Sopenharmony_ci struct chan *chan; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci list_for_each_safe(ele, next, chans) { 3338c2ecf20Sopenharmony_ci chan = list_entry(ele, struct chan, list); 3348c2ecf20Sopenharmony_ci free_one_chan(chan); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int one_chan_config_string(struct chan *chan, char *str, int size, 3398c2ecf20Sopenharmony_ci char **error_out) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci int n = 0; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (chan == NULL) { 3448c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, "none", 1); 3458c2ecf20Sopenharmony_ci return n; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, chan->ops->type, 0); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (chan->dev == NULL) { 3518c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, "", 1); 3528c2ecf20Sopenharmony_ci return n; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, ":", 0); 3568c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, chan->dev, 0); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return n; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int chan_pair_config_string(struct chan *in, struct chan *out, 3628c2ecf20Sopenharmony_ci char *str, int size, char **error_out) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci int n; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci n = one_chan_config_string(in, str, size, error_out); 3678c2ecf20Sopenharmony_ci str += n; 3688c2ecf20Sopenharmony_ci size -= n; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (in == out) { 3718c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, "", 1); 3728c2ecf20Sopenharmony_ci return n; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, ",", 1); 3768c2ecf20Sopenharmony_ci n = one_chan_config_string(out, str, size, error_out); 3778c2ecf20Sopenharmony_ci str += n; 3788c2ecf20Sopenharmony_ci size -= n; 3798c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, n, "", 1); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return n; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ciint chan_config_string(struct line *line, char *str, int size, 3858c2ecf20Sopenharmony_ci char **error_out) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct chan *in = line->chan_in, *out = line->chan_out; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (in && !in->primary) 3908c2ecf20Sopenharmony_ci in = NULL; 3918c2ecf20Sopenharmony_ci if (out && !out->primary) 3928c2ecf20Sopenharmony_ci out = NULL; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return chan_pair_config_string(in, out, str, size, error_out); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistruct chan_type { 3988c2ecf20Sopenharmony_ci char *key; 3998c2ecf20Sopenharmony_ci const struct chan_ops *ops; 4008c2ecf20Sopenharmony_ci}; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic const struct chan_type chan_table[] = { 4038c2ecf20Sopenharmony_ci { "fd", &fd_ops }, 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci#ifdef CONFIG_NULL_CHAN 4068c2ecf20Sopenharmony_ci { "null", &null_ops }, 4078c2ecf20Sopenharmony_ci#else 4088c2ecf20Sopenharmony_ci { "null", ¬_configged_ops }, 4098c2ecf20Sopenharmony_ci#endif 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci#ifdef CONFIG_PORT_CHAN 4128c2ecf20Sopenharmony_ci { "port", &port_ops }, 4138c2ecf20Sopenharmony_ci#else 4148c2ecf20Sopenharmony_ci { "port", ¬_configged_ops }, 4158c2ecf20Sopenharmony_ci#endif 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci#ifdef CONFIG_PTY_CHAN 4188c2ecf20Sopenharmony_ci { "pty", &pty_ops }, 4198c2ecf20Sopenharmony_ci { "pts", &pts_ops }, 4208c2ecf20Sopenharmony_ci#else 4218c2ecf20Sopenharmony_ci { "pty", ¬_configged_ops }, 4228c2ecf20Sopenharmony_ci { "pts", ¬_configged_ops }, 4238c2ecf20Sopenharmony_ci#endif 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci#ifdef CONFIG_TTY_CHAN 4268c2ecf20Sopenharmony_ci { "tty", &tty_ops }, 4278c2ecf20Sopenharmony_ci#else 4288c2ecf20Sopenharmony_ci { "tty", ¬_configged_ops }, 4298c2ecf20Sopenharmony_ci#endif 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci#ifdef CONFIG_XTERM_CHAN 4328c2ecf20Sopenharmony_ci { "xterm", &xterm_ops }, 4338c2ecf20Sopenharmony_ci#else 4348c2ecf20Sopenharmony_ci { "xterm", ¬_configged_ops }, 4358c2ecf20Sopenharmony_ci#endif 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic struct chan *parse_chan(struct line *line, char *str, int device, 4398c2ecf20Sopenharmony_ci const struct chan_opts *opts, char **error_out) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci const struct chan_type *entry; 4428c2ecf20Sopenharmony_ci const struct chan_ops *ops; 4438c2ecf20Sopenharmony_ci struct chan *chan; 4448c2ecf20Sopenharmony_ci void *data; 4458c2ecf20Sopenharmony_ci int i; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ops = NULL; 4488c2ecf20Sopenharmony_ci data = NULL; 4498c2ecf20Sopenharmony_ci for(i = 0; i < ARRAY_SIZE(chan_table); i++) { 4508c2ecf20Sopenharmony_ci entry = &chan_table[i]; 4518c2ecf20Sopenharmony_ci if (!strncmp(str, entry->key, strlen(entry->key))) { 4528c2ecf20Sopenharmony_ci ops = entry->ops; 4538c2ecf20Sopenharmony_ci str += strlen(entry->key); 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci if (ops == NULL) { 4588c2ecf20Sopenharmony_ci *error_out = "No match for configured backends"; 4598c2ecf20Sopenharmony_ci return NULL; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci data = (*ops->init)(str, device, opts); 4638c2ecf20Sopenharmony_ci if (data == NULL) { 4648c2ecf20Sopenharmony_ci *error_out = "Configuration failed"; 4658c2ecf20Sopenharmony_ci return NULL; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci chan = kmalloc(sizeof(*chan), GFP_ATOMIC); 4698c2ecf20Sopenharmony_ci if (chan == NULL) { 4708c2ecf20Sopenharmony_ci *error_out = "Memory allocation failed"; 4718c2ecf20Sopenharmony_ci return NULL; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list), 4748c2ecf20Sopenharmony_ci .free_list = 4758c2ecf20Sopenharmony_ci LIST_HEAD_INIT(chan->free_list), 4768c2ecf20Sopenharmony_ci .line = line, 4778c2ecf20Sopenharmony_ci .primary = 1, 4788c2ecf20Sopenharmony_ci .input = 0, 4798c2ecf20Sopenharmony_ci .output = 0, 4808c2ecf20Sopenharmony_ci .opened = 0, 4818c2ecf20Sopenharmony_ci .enabled = 0, 4828c2ecf20Sopenharmony_ci .fd = -1, 4838c2ecf20Sopenharmony_ci .ops = ops, 4848c2ecf20Sopenharmony_ci .data = data }); 4858c2ecf20Sopenharmony_ci return chan; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciint parse_chan_pair(char *str, struct line *line, int device, 4898c2ecf20Sopenharmony_ci const struct chan_opts *opts, char **error_out) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct list_head *chans = &line->chan_list; 4928c2ecf20Sopenharmony_ci struct chan *new; 4938c2ecf20Sopenharmony_ci char *in, *out; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!list_empty(chans)) { 4968c2ecf20Sopenharmony_ci line->chan_in = line->chan_out = NULL; 4978c2ecf20Sopenharmony_ci free_chan(chans); 4988c2ecf20Sopenharmony_ci INIT_LIST_HEAD(chans); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (!str) 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci out = strchr(str, ','); 5058c2ecf20Sopenharmony_ci if (out != NULL) { 5068c2ecf20Sopenharmony_ci in = str; 5078c2ecf20Sopenharmony_ci *out = '\0'; 5088c2ecf20Sopenharmony_ci out++; 5098c2ecf20Sopenharmony_ci new = parse_chan(line, in, device, opts, error_out); 5108c2ecf20Sopenharmony_ci if (new == NULL) 5118c2ecf20Sopenharmony_ci return -1; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci new->input = 1; 5148c2ecf20Sopenharmony_ci list_add(&new->list, chans); 5158c2ecf20Sopenharmony_ci line->chan_in = new; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci new = parse_chan(line, out, device, opts, error_out); 5188c2ecf20Sopenharmony_ci if (new == NULL) 5198c2ecf20Sopenharmony_ci return -1; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci list_add(&new->list, chans); 5228c2ecf20Sopenharmony_ci new->output = 1; 5238c2ecf20Sopenharmony_ci line->chan_out = new; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci else { 5268c2ecf20Sopenharmony_ci new = parse_chan(line, str, device, opts, error_out); 5278c2ecf20Sopenharmony_ci if (new == NULL) 5288c2ecf20Sopenharmony_ci return -1; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci list_add(&new->list, chans); 5318c2ecf20Sopenharmony_ci new->input = 1; 5328c2ecf20Sopenharmony_ci new->output = 1; 5338c2ecf20Sopenharmony_ci line->chan_in = line->chan_out = new; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_civoid chan_interrupt(struct line *line, int irq) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct tty_port *port = &line->port; 5418c2ecf20Sopenharmony_ci struct chan *chan = line->chan_in; 5428c2ecf20Sopenharmony_ci int err; 5438c2ecf20Sopenharmony_ci char c; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (!chan || !chan->ops->read) 5468c2ecf20Sopenharmony_ci goto out; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci do { 5498c2ecf20Sopenharmony_ci if (!tty_buffer_request_room(port, 1)) { 5508c2ecf20Sopenharmony_ci schedule_delayed_work(&line->task, 1); 5518c2ecf20Sopenharmony_ci goto out; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci err = chan->ops->read(chan->fd, &c, chan->data); 5548c2ecf20Sopenharmony_ci if (err > 0) 5558c2ecf20Sopenharmony_ci tty_insert_flip_char(port, c, TTY_NORMAL); 5568c2ecf20Sopenharmony_ci } while (err > 0); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (err == -EIO) { 5598c2ecf20Sopenharmony_ci if (chan->primary) { 5608c2ecf20Sopenharmony_ci tty_port_tty_hangup(&line->port, false); 5618c2ecf20Sopenharmony_ci if (line->chan_out != chan) 5628c2ecf20Sopenharmony_ci close_one_chan(line->chan_out, 1); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci close_one_chan(chan, 1); 5658c2ecf20Sopenharmony_ci if (chan->primary) 5668c2ecf20Sopenharmony_ci return; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci out: 5698c2ecf20Sopenharmony_ci tty_flip_buffer_push(port); 5708c2ecf20Sopenharmony_ci} 571