blob: acbe6c67afbaa1efe09952b9c1987b17b5afa58b [file] [log] [blame]
Jeff Dike165dc592006-01-06 00:18:57 -08001/*
Jeff Dikee99525f2007-10-16 01:26:41 -07002 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Licensed under the GPL
4 */
5
Linus Torvalds1da177e2005-04-16 15:20:36 -07006#include <linux/slab.h>
7#include <linux/tty.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/tty_flip.h>
Al Viro510c72a32011-08-18 20:08:29 +01009#include "chan.h"
Al Viro37185b32012-10-08 03:27:32 +010010#include <os.h>
11#include <irq_kern.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070013#ifdef CONFIG_NOCONFIG_CHAN
Jeff Dikef28169d2007-02-10 01:43:53 -080014static void *not_configged_init(char *str, int device,
15 const struct chan_opts *opts)
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070016{
Jeff Dikee99525f2007-10-16 01:26:41 -070017 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070018 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080019 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070020}
21
22static int not_configged_open(int input, int output, int primary, void *data,
23 char **dev_out)
24{
Jeff Dikee99525f2007-10-16 01:26:41 -070025 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070026 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080027 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070028}
29
30static void not_configged_close(int fd, void *data)
31{
Jeff Dikee99525f2007-10-16 01:26:41 -070032 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 "UML\n");
34}
35
36static int not_configged_read(int fd, char *c_out, void *data)
37{
Jeff Dikee99525f2007-10-16 01:26:41 -070038 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080040 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070041}
42
43static int not_configged_write(int fd, const char *buf, int len, void *data)
44{
Jeff Dikee99525f2007-10-16 01:26:41 -070045 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080047 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070048}
49
Paolo 'Blaisorblade' Giarrusso55c033c2005-11-13 16:07:11 -080050static int not_configged_console_write(int fd, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070051{
Jeff Dikee99525f2007-10-16 01:26:41 -070052 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080054 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070055}
56
57static int not_configged_window_size(int fd, void *data, unsigned short *rows,
58 unsigned short *cols)
59{
Jeff Dikee99525f2007-10-16 01:26:41 -070060 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080062 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063}
64
65static void not_configged_free(void *data)
66{
Jeff Dikee99525f2007-10-16 01:26:41 -070067 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 "UML\n");
69}
70
Jeff Dike5e7672e2006-09-27 01:50:33 -070071static const struct chan_ops not_configged_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 .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
Jeff Diked50084a2006-01-06 00:18:50 -080084static int open_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085{
Jeff Dike6676ae62007-07-31 00:37:44 -070086 int fd, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Jeff Dikee99525f2007-10-16 01:26:41 -070088 if (chan->opened)
Jeff Diked50084a2006-01-06 00:18:50 -080089 return 0;
90
Jeff Dikee99525f2007-10-16 01:26:41 -070091 if (chan->ops->open == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -080092 fd = 0;
93 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
94 chan->data, &chan->dev);
Jeff Dikee99525f2007-10-16 01:26:41 -070095 if (fd < 0)
Jeff Diked50084a2006-01-06 00:18:50 -080096 return fd;
Jeff Dike6676ae62007-07-31 00:37:44 -070097
98 err = os_set_fd_block(fd, 0);
99 if (err) {
100 (*chan->ops->close)(fd, chan->data);
101 return err;
102 }
103
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 chan->fd = fd;
105
106 chan->opened = 1;
Jeff Diked50084a2006-01-06 00:18:50 -0800107 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108}
109
WANG Cong3af7cb72008-04-28 02:13:55 -0700110static int open_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
112 struct list_head *ele;
113 struct chan *chan;
114 int ret, err = 0;
115
Jeff Dikee99525f2007-10-16 01:26:41 -0700116 list_for_each(ele, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 chan = list_entry(ele, struct chan, list);
Jeff Diked50084a2006-01-06 00:18:50 -0800118 ret = open_one_chan(chan);
Jeff Dikee99525f2007-10-16 01:26:41 -0700119 if (chan->primary)
Jeff Diked50084a2006-01-06 00:18:50 -0800120 err = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 }
Jeff Diked50084a2006-01-06 00:18:50 -0800122 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123}
124
Richard Weinberger2116bda2013-03-11 10:05:45 +0100125void chan_enable_winch(struct chan *chan, struct tty_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126{
Al Virobed5e392011-09-08 10:49:34 -0400127 if (chan && chan->primary && chan->ops->winch)
Richard Weinberger2116bda2013-03-11 10:05:45 +0100128 register_winch(chan->fd, port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129}
130
Al Viro0fcd7192011-09-10 08:17:04 -0400131static 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)
Jiri Slaby2e124b42013-01-03 15:53:06 +0100136 chan_interrupt(line, line->driver->read_irq);
Al Viro0fcd7192011-09-10 08:17:04 -0400137}
138
Jeff Diked14ad812007-07-15 23:38:54 -0700139int enable_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140{
141 struct list_head *ele;
142 struct chan *chan;
Jeff Diked14ad812007-07-15 23:38:54 -0700143 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
Al Viro0fcd7192011-09-10 08:17:04 -0400145 INIT_DELAYED_WORK(&line->task, line_timer_cb);
146
Jeff Dikee99525f2007-10-16 01:26:41 -0700147 list_for_each(ele, &line->chan_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 chan = list_entry(ele, struct chan, list);
Jeff Diked14ad812007-07-15 23:38:54 -0700149 err = open_one_chan(chan);
150 if (err) {
151 if (chan->primary)
152 goto out_close;
153
Jeff Dike165dc592006-01-06 00:18:57 -0800154 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700155 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
Jeff Dikee99525f2007-10-16 01:26:41 -0700157 if (chan->enabled)
Jeff Dike165dc592006-01-06 00:18:57 -0800158 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700159 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
160 chan);
161 if (err)
162 goto out_close;
163
Jeff Dike165dc592006-01-06 00:18:57 -0800164 chan->enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 }
Jeff Diked14ad812007-07-15 23:38:54 -0700166
167 return 0;
168
169 out_close:
Al Viro10c890c02011-09-10 08:39:18 -0400170 close_chan(line);
Jeff Diked14ad812007-07-15 23:38:54 -0700171 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172}
173
Jeff Dike190c3e42007-02-10 01:43:55 -0800174/* 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);
Jeff Dike165dc592006-01-06 00:18:57 -0800181static LIST_HEAD(irqs_to_free);
182
183void free_irqs(void)
184{
185 struct chan *chan;
Jeff Dike190c3e42007-02-10 01:43:55 -0800186 LIST_HEAD(list);
187 struct list_head *ele;
Jeff Dike30762122007-03-29 01:20:30 -0700188 unsigned long flags;
Jeff Dike165dc592006-01-06 00:18:57 -0800189
Jeff Dike30762122007-03-29 01:20:30 -0700190 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800191 list_splice_init(&irqs_to_free, &list);
Jeff Dike30762122007-03-29 01:20:30 -0700192 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800193
Jeff Dikee99525f2007-10-16 01:26:41 -0700194 list_for_each(ele, &list) {
Jeff Dike190c3e42007-02-10 01:43:55 -0800195 chan = list_entry(ele, struct chan, free_list);
Jeff Dike165dc592006-01-06 00:18:57 -0800196
Richard Weinberger47562272010-08-09 17:20:14 -0700197 if (chan->input && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200198 um_free_irq(chan->line->driver->read_irq, chan);
Richard Weinberger47562272010-08-09 17:20:14 -0700199 if (chan->output && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200200 um_free_irq(chan->line->driver->write_irq, chan);
Jeff Dike165dc592006-01-06 00:18:57 -0800201 chan->enabled = 0;
202 }
203}
204
205static void close_one_chan(struct chan *chan, int delay_free_irq)
206{
Jeff Dike30762122007-03-29 01:20:30 -0700207 unsigned long flags;
208
Jeff Dikee99525f2007-10-16 01:26:41 -0700209 if (!chan->opened)
Jeff Dike165dc592006-01-06 00:18:57 -0800210 return;
211
Jeff Dikee99525f2007-10-16 01:26:41 -0700212 if (delay_free_irq) {
Jeff Dike30762122007-03-29 01:20:30 -0700213 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800214 list_add(&chan->free_list, &irqs_to_free);
Jeff Dike30762122007-03-29 01:20:30 -0700215 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800216 }
217 else {
Richard Weinberger47562272010-08-09 17:20:14 -0700218 if (chan->input && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200219 um_free_irq(chan->line->driver->read_irq, chan);
Richard Weinberger47562272010-08-09 17:20:14 -0700220 if (chan->output && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200221 um_free_irq(chan->line->driver->write_irq, chan);
Jeff Dike165dc592006-01-06 00:18:57 -0800222 chan->enabled = 0;
223 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700224 if (chan->ops->close != NULL)
Jeff Dike165dc592006-01-06 00:18:57 -0800225 (*chan->ops->close)(chan->fd, chan->data);
226
227 chan->opened = 0;
228 chan->fd = -1;
229}
230
Al Viro10c890c02011-09-10 08:39:18 -0400231void close_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232{
233 struct chan *chan;
234
235 /* Close in reverse order as open in case more than one of them
236 * refers to the same device and they save and restore that device's
237 * state. Then, the first one opened will have the original state,
238 * so it must be the last closed.
239 */
Al Viro10c890c02011-09-10 08:39:18 -0400240 list_for_each_entry_reverse(chan, &line->chan_list, list) {
241 close_one_chan(chan, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 }
243}
244
Al Virobed5e392011-09-08 10:49:34 -0400245void deactivate_chan(struct chan *chan, int irq)
Jeff Dikee4dcee82006-01-06 00:18:58 -0800246{
Al Virobed5e392011-09-08 10:49:34 -0400247 if (chan && chan->enabled)
248 deactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800249}
250
Al Virobed5e392011-09-08 10:49:34 -0400251void reactivate_chan(struct chan *chan, int irq)
Jeff Dikee4dcee82006-01-06 00:18:58 -0800252{
Al Virobed5e392011-09-08 10:49:34 -0400253 if (chan && chan->enabled)
254 reactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800255}
256
Al Virobed5e392011-09-08 10:49:34 -0400257int write_chan(struct chan *chan, const char *buf, int len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 int write_irq)
259{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 int n, ret = 0;
261
Al Virobed5e392011-09-08 10:49:34 -0400262 if (len == 0 || !chan || !chan->ops->write)
Jeff Dikec59dbca2007-10-16 01:26:42 -0700263 return 0;
264
Al Virobed5e392011-09-08 10:49:34 -0400265 n = chan->ops->write(chan->fd, buf, len, chan->data);
266 if (chan->primary) {
267 ret = n;
268 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
269 reactivate_fd(chan->fd, write_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 }
Jeff Diked50084a2006-01-06 00:18:50 -0800271 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272}
273
Al Virobed5e392011-09-08 10:49:34 -0400274int console_write_chan(struct chan *chan, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 int n, ret = 0;
277
Al Virobed5e392011-09-08 10:49:34 -0400278 if (!chan || !chan->ops->console_write)
279 return 0;
Jeff Dikee99525f2007-10-16 01:26:41 -0700280
Al Virobed5e392011-09-08 10:49:34 -0400281 n = chan->ops->console_write(chan->fd, buf, len);
282 if (chan->primary)
283 ret = n;
Jeff Diked50084a2006-01-06 00:18:50 -0800284 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285}
286
Jeff Dikea52f3622007-02-10 01:44:06 -0800287int console_open_chan(struct line *line, struct console *co)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288{
Jeff Dike1f801712006-01-06 00:18:55 -0800289 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
Jeff Dike1f801712006-01-06 00:18:55 -0800291 err = open_chan(&line->chan_list);
Jeff Dikee99525f2007-10-16 01:26:41 -0700292 if (err)
Jeff Dike1f801712006-01-06 00:18:55 -0800293 return err;
294
Jeff Dikee99525f2007-10-16 01:26:41 -0700295 printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
296 co->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 return 0;
298}
299
Al Virobed5e392011-09-08 10:49:34 -0400300int chan_window_size(struct line *line, unsigned short *rows_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 unsigned short *cols_out)
302{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 struct chan *chan;
304
Al Virobed5e392011-09-08 10:49:34 -0400305 chan = line->chan_in;
306 if (chan && chan->primary) {
307 if (chan->ops->window_size == NULL)
308 return 0;
309 return chan->ops->window_size(chan->fd, chan->data,
310 rows_out, cols_out);
311 }
312 chan = line->chan_out;
313 if (chan && chan->primary) {
314 if (chan->ops->window_size == NULL)
315 return 0;
316 return chan->ops->window_size(chan->fd, chan->data,
317 rows_out, cols_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 }
Jeff Diked50084a2006-01-06 00:18:50 -0800319 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320}
321
Al Viro772bd0a2011-08-18 20:12:39 +0100322static void free_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323{
324 list_del(&chan->list);
Jeff Dike165dc592006-01-06 00:18:57 -0800325
Al Viro772bd0a2011-08-18 20:12:39 +0100326 close_one_chan(chan, 0);
Jeff Dike165dc592006-01-06 00:18:57 -0800327
Jeff Dikee99525f2007-10-16 01:26:41 -0700328 if (chan->ops->free != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 (*chan->ops->free)(chan->data);
Jeff Dike165dc592006-01-06 00:18:57 -0800330
Jeff Dikee99525f2007-10-16 01:26:41 -0700331 if (chan->primary && chan->output)
332 ignore_sigio_fd(chan->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 kfree(chan);
334}
335
Al Viro772bd0a2011-08-18 20:12:39 +0100336static void free_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337{
338 struct list_head *ele, *next;
339 struct chan *chan;
340
Jeff Dikee99525f2007-10-16 01:26:41 -0700341 list_for_each_safe(ele, next, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 chan = list_entry(ele, struct chan, list);
Al Viro772bd0a2011-08-18 20:12:39 +0100343 free_one_chan(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 }
345}
346
347static int one_chan_config_string(struct chan *chan, char *str, int size,
348 char **error_out)
349{
350 int n = 0;
351
Jeff Dikee99525f2007-10-16 01:26:41 -0700352 if (chan == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 CONFIG_CHUNK(str, size, n, "none", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800354 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 }
356
357 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
358
Jeff Dikee99525f2007-10-16 01:26:41 -0700359 if (chan->dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800361 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 }
363
364 CONFIG_CHUNK(str, size, n, ":", 0);
365 CONFIG_CHUNK(str, size, n, chan->dev, 0);
366
Jeff Diked50084a2006-01-06 00:18:50 -0800367 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368}
369
Jeff Diked50084a2006-01-06 00:18:50 -0800370static int chan_pair_config_string(struct chan *in, struct chan *out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 char *str, int size, char **error_out)
372{
373 int n;
374
375 n = one_chan_config_string(in, str, size, error_out);
376 str += n;
377 size -= n;
378
Jeff Dikee99525f2007-10-16 01:26:41 -0700379 if (in == out) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800381 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 }
383
384 CONFIG_CHUNK(str, size, n, ",", 1);
385 n = one_chan_config_string(out, str, size, error_out);
386 str += n;
387 size -= n;
388 CONFIG_CHUNK(str, size, n, "", 1);
389
Jeff Diked50084a2006-01-06 00:18:50 -0800390 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391}
392
Al Virobed5e392011-09-08 10:49:34 -0400393int chan_config_string(struct line *line, char *str, int size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 char **error_out)
395{
Al Virobed5e392011-09-08 10:49:34 -0400396 struct chan *in = line->chan_in, *out = line->chan_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Al Virobed5e392011-09-08 10:49:34 -0400398 if (in && !in->primary)
399 in = NULL;
400 if (out && !out->primary)
401 out = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402
Jeff Diked50084a2006-01-06 00:18:50 -0800403 return chan_pair_config_string(in, out, str, size, error_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404}
405
406struct chan_type {
407 char *key;
Jeff Dike5e7672e2006-09-27 01:50:33 -0700408 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409};
410
Jeff Dike5e7672e2006-09-27 01:50:33 -0700411static const struct chan_type chan_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 { "fd", &fd_ops },
413
414#ifdef CONFIG_NULL_CHAN
415 { "null", &null_ops },
416#else
417 { "null", &not_configged_ops },
418#endif
419
420#ifdef CONFIG_PORT_CHAN
421 { "port", &port_ops },
422#else
423 { "port", &not_configged_ops },
424#endif
425
426#ifdef CONFIG_PTY_CHAN
427 { "pty", &pty_ops },
428 { "pts", &pts_ops },
429#else
430 { "pty", &not_configged_ops },
431 { "pts", &not_configged_ops },
432#endif
433
434#ifdef CONFIG_TTY_CHAN
435 { "tty", &tty_ops },
436#else
437 { "tty", &not_configged_ops },
438#endif
439
440#ifdef CONFIG_XTERM_CHAN
441 { "xterm", &xterm_ops },
442#else
443 { "xterm", &not_configged_ops },
444#endif
445};
446
Jeff Dike165dc592006-01-06 00:18:57 -0800447static struct chan *parse_chan(struct line *line, char *str, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800448 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449{
Jeff Dike5e7672e2006-09-27 01:50:33 -0700450 const struct chan_type *entry;
451 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 struct chan *chan;
453 void *data;
454 int i;
455
456 ops = NULL;
457 data = NULL;
Jeff Dikee99525f2007-10-16 01:26:41 -0700458 for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 entry = &chan_table[i];
Jeff Dikee99525f2007-10-16 01:26:41 -0700460 if (!strncmp(str, entry->key, strlen(entry->key))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 ops = entry->ops;
462 str += strlen(entry->key);
463 break;
464 }
465 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700466 if (ops == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800467 *error_out = "No match for configured backends";
Jeff Diked50084a2006-01-06 00:18:50 -0800468 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800470
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 data = (*ops->init)(str, device, opts);
Jeff Dikee99525f2007-10-16 01:26:41 -0700472 if (data == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800473 *error_out = "Configuration failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800474 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800475 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
Paolo 'Blaisorblade' Giarrusso79ae2cb2005-09-22 21:44:21 -0700477 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
Jeff Dikee99525f2007-10-16 01:26:41 -0700478 if (chan == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800479 *error_out = "Memory allocation failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800480 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800481 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
Jeff Dike165dc592006-01-06 00:18:57 -0800483 .free_list =
484 LIST_HEAD_INIT(chan->free_list),
485 .line = line,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 .primary = 1,
487 .input = 0,
488 .output = 0,
489 .opened = 0,
Jeff Dike165dc592006-01-06 00:18:57 -0800490 .enabled = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 .fd = -1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 .ops = ops,
493 .data = data });
Jeff Diked50084a2006-01-06 00:18:50 -0800494 return chan;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495}
496
Jeff Dike165dc592006-01-06 00:18:57 -0800497int parse_chan_pair(char *str, struct line *line, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800498 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499{
Jeff Dike165dc592006-01-06 00:18:57 -0800500 struct list_head *chans = &line->chan_list;
Richard Weinbergerf1c93e42011-07-25 17:12:55 -0700501 struct chan *new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 char *in, *out;
503
Jeff Dikee99525f2007-10-16 01:26:41 -0700504 if (!list_empty(chans)) {
Al Viroee485072011-09-08 07:07:26 -0400505 line->chan_in = line->chan_out = NULL;
Al Viro772bd0a2011-08-18 20:12:39 +0100506 free_chan(chans);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 INIT_LIST_HEAD(chans);
508 }
509
Al Viro31efceb2011-09-09 19:14:02 -0400510 if (!str)
511 return 0;
512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 out = strchr(str, ',');
Jeff Dikee99525f2007-10-16 01:26:41 -0700514 if (out != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 in = str;
516 *out = '\0';
517 out++;
Jeff Dikef28169d2007-02-10 01:43:53 -0800518 new = parse_chan(line, in, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700519 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800520 return -1;
521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 new->input = 1;
523 list_add(&new->list, chans);
Al Viroee485072011-09-08 07:07:26 -0400524 line->chan_in = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
Jeff Dikef28169d2007-02-10 01:43:53 -0800526 new = parse_chan(line, out, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700527 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800528 return -1;
529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 list_add(&new->list, chans);
531 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400532 line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 }
534 else {
Jeff Dikef28169d2007-02-10 01:43:53 -0800535 new = parse_chan(line, str, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700536 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800537 return -1;
538
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 list_add(&new->list, chans);
540 new->input = 1;
541 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400542 line->chan_in = line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 }
Jeff Diked50084a2006-01-06 00:18:50 -0800544 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545}
546
Jiri Slaby2e124b42013-01-03 15:53:06 +0100547void chan_interrupt(struct line *line, int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548{
Jiri Slaby227434f2013-01-03 15:53:01 +0100549 struct tty_port *port = &line->port;
Al Virobed5e392011-09-08 10:49:34 -0400550 struct chan *chan = line->chan_in;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 int err;
552 char c;
553
Al Virobed5e392011-09-08 10:49:34 -0400554 if (!chan || !chan->ops->read)
555 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Al Virobed5e392011-09-08 10:49:34 -0400557 do {
Jiri Slaby227434f2013-01-03 15:53:01 +0100558 if (!tty_buffer_request_room(port, 1)) {
Al Viro0fcd7192011-09-10 08:17:04 -0400559 schedule_delayed_work(&line->task, 1);
Al Virobed5e392011-09-08 10:49:34 -0400560 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 }
Al Virobed5e392011-09-08 10:49:34 -0400562 err = chan->ops->read(chan->fd, &c, chan->data);
563 if (err > 0)
Jiri Slaby92a19f92013-01-03 15:53:03 +0100564 tty_insert_flip_char(port, c, TTY_NORMAL);
Al Virobed5e392011-09-08 10:49:34 -0400565 } while (err > 0);
566
567 if (err == 0)
568 reactivate_fd(chan->fd, irq);
569 if (err == -EIO) {
570 if (chan->primary) {
Jiri Slabyaa27a092013-03-07 13:12:30 +0100571 tty_port_tty_hangup(&line->port, false);
Al Viro10c890c02011-09-10 08:39:18 -0400572 if (line->chan_out != chan)
573 close_one_chan(line->chan_out, 1);
Al Virobed5e392011-09-08 10:49:34 -0400574 }
Al Viro10c890c02011-09-10 08:39:18 -0400575 close_one_chan(chan, 1);
576 if (chan->primary)
577 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 }
579 out:
Jiri Slaby2e124b42013-01-03 15:53:06 +0100580 tty_flip_buffer_push(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581}