blob: c3bba73e4be6d88be5d813d94dcdf01eb2ef4857 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070084static void tty_receive_char(struct tty_struct *tty, char ch)
85{
Jeff Dikee99525f2007-10-16 01:26:41 -070086 if (tty == NULL)
87 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Jeff Dikee99525f2007-10-16 01:26:41 -070089 if (I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
90 if (ch == STOP_CHAR(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 stop_tty(tty);
92 return;
93 }
Jeff Dikee99525f2007-10-16 01:26:41 -070094 else if (ch == START_CHAR(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 start_tty(tty);
96 return;
97 }
98 }
99
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 tty_insert_flip_char(tty, ch, TTY_NORMAL);
101}
102
Jeff Diked50084a2006-01-06 00:18:50 -0800103static int open_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104{
Jeff Dike6676ae62007-07-31 00:37:44 -0700105 int fd, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106
Jeff Dikee99525f2007-10-16 01:26:41 -0700107 if (chan->opened)
Jeff Diked50084a2006-01-06 00:18:50 -0800108 return 0;
109
Jeff Dikee99525f2007-10-16 01:26:41 -0700110 if (chan->ops->open == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800111 fd = 0;
112 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
113 chan->data, &chan->dev);
Jeff Dikee99525f2007-10-16 01:26:41 -0700114 if (fd < 0)
Jeff Diked50084a2006-01-06 00:18:50 -0800115 return fd;
Jeff Dike6676ae62007-07-31 00:37:44 -0700116
117 err = os_set_fd_block(fd, 0);
118 if (err) {
119 (*chan->ops->close)(fd, chan->data);
120 return err;
121 }
122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 chan->fd = fd;
124
125 chan->opened = 1;
Jeff Diked50084a2006-01-06 00:18:50 -0800126 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127}
128
WANG Cong3af7cb72008-04-28 02:13:55 -0700129static int open_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130{
131 struct list_head *ele;
132 struct chan *chan;
133 int ret, err = 0;
134
Jeff Dikee99525f2007-10-16 01:26:41 -0700135 list_for_each(ele, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 chan = list_entry(ele, struct chan, list);
Jeff Diked50084a2006-01-06 00:18:50 -0800137 ret = open_one_chan(chan);
Jeff Dikee99525f2007-10-16 01:26:41 -0700138 if (chan->primary)
Jeff Diked50084a2006-01-06 00:18:50 -0800139 err = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 }
Jeff Diked50084a2006-01-06 00:18:50 -0800141 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142}
143
Al Virobed5e392011-09-08 10:49:34 -0400144void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145{
Al Virobed5e392011-09-08 10:49:34 -0400146 if (chan && chan->primary && chan->ops->winch)
147 register_winch(chan->fd, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148}
149
Al Viro0fcd7192011-09-10 08:17:04 -0400150static void line_timer_cb(struct work_struct *work)
151{
152 struct line *line = container_of(work, struct line, task.work);
Jiri Slaby6fc58842012-06-04 13:35:27 +0200153 struct tty_struct *tty = tty_port_tty_get(&line->port);
Al Viro0fcd7192011-09-10 08:17:04 -0400154
155 if (!line->throttled)
Jiri Slaby6fc58842012-06-04 13:35:27 +0200156 chan_interrupt(line, tty, line->driver->read_irq);
157 tty_kref_put(tty);
Al Viro0fcd7192011-09-10 08:17:04 -0400158}
159
Jeff Diked14ad812007-07-15 23:38:54 -0700160int enable_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161{
162 struct list_head *ele;
163 struct chan *chan;
Jeff Diked14ad812007-07-15 23:38:54 -0700164 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165
Al Viro0fcd7192011-09-10 08:17:04 -0400166 INIT_DELAYED_WORK(&line->task, line_timer_cb);
167
Jeff Dikee99525f2007-10-16 01:26:41 -0700168 list_for_each(ele, &line->chan_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 chan = list_entry(ele, struct chan, list);
Jeff Diked14ad812007-07-15 23:38:54 -0700170 err = open_one_chan(chan);
171 if (err) {
172 if (chan->primary)
173 goto out_close;
174
Jeff Dike165dc592006-01-06 00:18:57 -0800175 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700176 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
Jeff Dikee99525f2007-10-16 01:26:41 -0700178 if (chan->enabled)
Jeff Dike165dc592006-01-06 00:18:57 -0800179 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700180 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
181 chan);
182 if (err)
183 goto out_close;
184
Jeff Dike165dc592006-01-06 00:18:57 -0800185 chan->enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 }
Jeff Diked14ad812007-07-15 23:38:54 -0700187
188 return 0;
189
190 out_close:
Al Viro10c890c02011-09-10 08:39:18 -0400191 close_chan(line);
Jeff Diked14ad812007-07-15 23:38:54 -0700192 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193}
194
Jeff Dike190c3e42007-02-10 01:43:55 -0800195/* Items are added in IRQ context, when free_irq can't be called, and
196 * removed in process context, when it can.
197 * This handles interrupt sources which disappear, and which need to
198 * be permanently disabled. This is discovered in IRQ context, but
199 * the freeing of the IRQ must be done later.
200 */
201static DEFINE_SPINLOCK(irqs_to_free_lock);
Jeff Dike165dc592006-01-06 00:18:57 -0800202static LIST_HEAD(irqs_to_free);
203
204void free_irqs(void)
205{
206 struct chan *chan;
Jeff Dike190c3e42007-02-10 01:43:55 -0800207 LIST_HEAD(list);
208 struct list_head *ele;
Jeff Dike30762122007-03-29 01:20:30 -0700209 unsigned long flags;
Jeff Dike165dc592006-01-06 00:18:57 -0800210
Jeff Dike30762122007-03-29 01:20:30 -0700211 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800212 list_splice_init(&irqs_to_free, &list);
Jeff Dike30762122007-03-29 01:20:30 -0700213 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800214
Jeff Dikee99525f2007-10-16 01:26:41 -0700215 list_for_each(ele, &list) {
Jeff Dike190c3e42007-02-10 01:43:55 -0800216 chan = list_entry(ele, struct chan, free_list);
Jeff Dike165dc592006-01-06 00:18:57 -0800217
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 }
224}
225
226static void close_one_chan(struct chan *chan, int delay_free_irq)
227{
Jeff Dike30762122007-03-29 01:20:30 -0700228 unsigned long flags;
229
Jeff Dikee99525f2007-10-16 01:26:41 -0700230 if (!chan->opened)
Jeff Dike165dc592006-01-06 00:18:57 -0800231 return;
232
Jeff Dikee99525f2007-10-16 01:26:41 -0700233 if (delay_free_irq) {
Jeff Dike30762122007-03-29 01:20:30 -0700234 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800235 list_add(&chan->free_list, &irqs_to_free);
Jeff Dike30762122007-03-29 01:20:30 -0700236 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800237 }
238 else {
Richard Weinberger47562272010-08-09 17:20:14 -0700239 if (chan->input && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200240 um_free_irq(chan->line->driver->read_irq, chan);
Richard Weinberger47562272010-08-09 17:20:14 -0700241 if (chan->output && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200242 um_free_irq(chan->line->driver->write_irq, chan);
Jeff Dike165dc592006-01-06 00:18:57 -0800243 chan->enabled = 0;
244 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700245 if (chan->ops->close != NULL)
Jeff Dike165dc592006-01-06 00:18:57 -0800246 (*chan->ops->close)(chan->fd, chan->data);
247
248 chan->opened = 0;
249 chan->fd = -1;
250}
251
Al Viro10c890c02011-09-10 08:39:18 -0400252void close_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253{
254 struct chan *chan;
255
256 /* Close in reverse order as open in case more than one of them
257 * refers to the same device and they save and restore that device's
258 * state. Then, the first one opened will have the original state,
259 * so it must be the last closed.
260 */
Al Viro10c890c02011-09-10 08:39:18 -0400261 list_for_each_entry_reverse(chan, &line->chan_list, list) {
262 close_one_chan(chan, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 }
264}
265
Al Virobed5e392011-09-08 10:49:34 -0400266void deactivate_chan(struct chan *chan, int irq)
Jeff Dikee4dcee82006-01-06 00:18:58 -0800267{
Al Virobed5e392011-09-08 10:49:34 -0400268 if (chan && chan->enabled)
269 deactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800270}
271
Al Virobed5e392011-09-08 10:49:34 -0400272void reactivate_chan(struct chan *chan, int irq)
Jeff Dikee4dcee82006-01-06 00:18:58 -0800273{
Al Virobed5e392011-09-08 10:49:34 -0400274 if (chan && chan->enabled)
275 reactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800276}
277
Al Virobed5e392011-09-08 10:49:34 -0400278int write_chan(struct chan *chan, const char *buf, int len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 int write_irq)
280{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 int n, ret = 0;
282
Al Virobed5e392011-09-08 10:49:34 -0400283 if (len == 0 || !chan || !chan->ops->write)
Jeff Dikec59dbca2007-10-16 01:26:42 -0700284 return 0;
285
Al Virobed5e392011-09-08 10:49:34 -0400286 n = chan->ops->write(chan->fd, buf, len, chan->data);
287 if (chan->primary) {
288 ret = n;
289 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
290 reactivate_fd(chan->fd, write_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 }
Jeff Diked50084a2006-01-06 00:18:50 -0800292 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293}
294
Al Virobed5e392011-09-08 10:49:34 -0400295int console_write_chan(struct chan *chan, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 int n, ret = 0;
298
Al Virobed5e392011-09-08 10:49:34 -0400299 if (!chan || !chan->ops->console_write)
300 return 0;
Jeff Dikee99525f2007-10-16 01:26:41 -0700301
Al Virobed5e392011-09-08 10:49:34 -0400302 n = chan->ops->console_write(chan->fd, buf, len);
303 if (chan->primary)
304 ret = n;
Jeff Diked50084a2006-01-06 00:18:50 -0800305 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306}
307
Jeff Dikea52f3622007-02-10 01:44:06 -0800308int console_open_chan(struct line *line, struct console *co)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309{
Jeff Dike1f801712006-01-06 00:18:55 -0800310 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
Jeff Dike1f801712006-01-06 00:18:55 -0800312 err = open_chan(&line->chan_list);
Jeff Dikee99525f2007-10-16 01:26:41 -0700313 if (err)
Jeff Dike1f801712006-01-06 00:18:55 -0800314 return err;
315
Jeff Dikee99525f2007-10-16 01:26:41 -0700316 printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
317 co->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 return 0;
319}
320
Al Virobed5e392011-09-08 10:49:34 -0400321int chan_window_size(struct line *line, unsigned short *rows_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 unsigned short *cols_out)
323{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 struct chan *chan;
325
Al Virobed5e392011-09-08 10:49:34 -0400326 chan = line->chan_in;
327 if (chan && chan->primary) {
328 if (chan->ops->window_size == NULL)
329 return 0;
330 return chan->ops->window_size(chan->fd, chan->data,
331 rows_out, cols_out);
332 }
333 chan = line->chan_out;
334 if (chan && chan->primary) {
335 if (chan->ops->window_size == NULL)
336 return 0;
337 return chan->ops->window_size(chan->fd, chan->data,
338 rows_out, cols_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 }
Jeff Diked50084a2006-01-06 00:18:50 -0800340 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341}
342
Al Viro772bd0a2011-08-18 20:12:39 +0100343static void free_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344{
345 list_del(&chan->list);
Jeff Dike165dc592006-01-06 00:18:57 -0800346
Al Viro772bd0a2011-08-18 20:12:39 +0100347 close_one_chan(chan, 0);
Jeff Dike165dc592006-01-06 00:18:57 -0800348
Jeff Dikee99525f2007-10-16 01:26:41 -0700349 if (chan->ops->free != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 (*chan->ops->free)(chan->data);
Jeff Dike165dc592006-01-06 00:18:57 -0800351
Jeff Dikee99525f2007-10-16 01:26:41 -0700352 if (chan->primary && chan->output)
353 ignore_sigio_fd(chan->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 kfree(chan);
355}
356
Al Viro772bd0a2011-08-18 20:12:39 +0100357static void free_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
359 struct list_head *ele, *next;
360 struct chan *chan;
361
Jeff Dikee99525f2007-10-16 01:26:41 -0700362 list_for_each_safe(ele, next, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 chan = list_entry(ele, struct chan, list);
Al Viro772bd0a2011-08-18 20:12:39 +0100364 free_one_chan(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 }
366}
367
368static int one_chan_config_string(struct chan *chan, char *str, int size,
369 char **error_out)
370{
371 int n = 0;
372
Jeff Dikee99525f2007-10-16 01:26:41 -0700373 if (chan == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 CONFIG_CHUNK(str, size, n, "none", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800375 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 }
377
378 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
379
Jeff Dikee99525f2007-10-16 01:26:41 -0700380 if (chan->dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800382 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 }
384
385 CONFIG_CHUNK(str, size, n, ":", 0);
386 CONFIG_CHUNK(str, size, n, chan->dev, 0);
387
Jeff Diked50084a2006-01-06 00:18:50 -0800388 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389}
390
Jeff Diked50084a2006-01-06 00:18:50 -0800391static int chan_pair_config_string(struct chan *in, struct chan *out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 char *str, int size, char **error_out)
393{
394 int n;
395
396 n = one_chan_config_string(in, str, size, error_out);
397 str += n;
398 size -= n;
399
Jeff Dikee99525f2007-10-16 01:26:41 -0700400 if (in == out) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800402 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 }
404
405 CONFIG_CHUNK(str, size, n, ",", 1);
406 n = one_chan_config_string(out, str, size, error_out);
407 str += n;
408 size -= n;
409 CONFIG_CHUNK(str, size, n, "", 1);
410
Jeff Diked50084a2006-01-06 00:18:50 -0800411 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412}
413
Al Virobed5e392011-09-08 10:49:34 -0400414int chan_config_string(struct line *line, char *str, int size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 char **error_out)
416{
Al Virobed5e392011-09-08 10:49:34 -0400417 struct chan *in = line->chan_in, *out = line->chan_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
Al Virobed5e392011-09-08 10:49:34 -0400419 if (in && !in->primary)
420 in = NULL;
421 if (out && !out->primary)
422 out = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423
Jeff Diked50084a2006-01-06 00:18:50 -0800424 return chan_pair_config_string(in, out, str, size, error_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425}
426
427struct chan_type {
428 char *key;
Jeff Dike5e7672e2006-09-27 01:50:33 -0700429 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430};
431
Jeff Dike5e7672e2006-09-27 01:50:33 -0700432static const struct chan_type chan_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 { "fd", &fd_ops },
434
435#ifdef CONFIG_NULL_CHAN
436 { "null", &null_ops },
437#else
438 { "null", &not_configged_ops },
439#endif
440
441#ifdef CONFIG_PORT_CHAN
442 { "port", &port_ops },
443#else
444 { "port", &not_configged_ops },
445#endif
446
447#ifdef CONFIG_PTY_CHAN
448 { "pty", &pty_ops },
449 { "pts", &pts_ops },
450#else
451 { "pty", &not_configged_ops },
452 { "pts", &not_configged_ops },
453#endif
454
455#ifdef CONFIG_TTY_CHAN
456 { "tty", &tty_ops },
457#else
458 { "tty", &not_configged_ops },
459#endif
460
461#ifdef CONFIG_XTERM_CHAN
462 { "xterm", &xterm_ops },
463#else
464 { "xterm", &not_configged_ops },
465#endif
466};
467
Jeff Dike165dc592006-01-06 00:18:57 -0800468static struct chan *parse_chan(struct line *line, char *str, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800469 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470{
Jeff Dike5e7672e2006-09-27 01:50:33 -0700471 const struct chan_type *entry;
472 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 struct chan *chan;
474 void *data;
475 int i;
476
477 ops = NULL;
478 data = NULL;
Jeff Dikee99525f2007-10-16 01:26:41 -0700479 for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 entry = &chan_table[i];
Jeff Dikee99525f2007-10-16 01:26:41 -0700481 if (!strncmp(str, entry->key, strlen(entry->key))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 ops = entry->ops;
483 str += strlen(entry->key);
484 break;
485 }
486 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700487 if (ops == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800488 *error_out = "No match for configured backends";
Jeff Diked50084a2006-01-06 00:18:50 -0800489 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800491
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 data = (*ops->init)(str, device, opts);
Jeff Dikee99525f2007-10-16 01:26:41 -0700493 if (data == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800494 *error_out = "Configuration failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800495 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800496 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Paolo 'Blaisorblade' Giarrusso79ae2cb2005-09-22 21:44:21 -0700498 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
Jeff Dikee99525f2007-10-16 01:26:41 -0700499 if (chan == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800500 *error_out = "Memory allocation failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800501 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800502 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
Jeff Dike165dc592006-01-06 00:18:57 -0800504 .free_list =
505 LIST_HEAD_INIT(chan->free_list),
506 .line = line,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 .primary = 1,
508 .input = 0,
509 .output = 0,
510 .opened = 0,
Jeff Dike165dc592006-01-06 00:18:57 -0800511 .enabled = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 .fd = -1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 .ops = ops,
514 .data = data });
Jeff Diked50084a2006-01-06 00:18:50 -0800515 return chan;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
Jeff Dike165dc592006-01-06 00:18:57 -0800518int parse_chan_pair(char *str, struct line *line, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800519 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520{
Jeff Dike165dc592006-01-06 00:18:57 -0800521 struct list_head *chans = &line->chan_list;
Richard Weinbergerf1c93e42011-07-25 17:12:55 -0700522 struct chan *new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 char *in, *out;
524
Jeff Dikee99525f2007-10-16 01:26:41 -0700525 if (!list_empty(chans)) {
Al Viroee485072011-09-08 07:07:26 -0400526 line->chan_in = line->chan_out = NULL;
Al Viro772bd0a2011-08-18 20:12:39 +0100527 free_chan(chans);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 INIT_LIST_HEAD(chans);
529 }
530
Al Viro31efceb2011-09-09 19:14:02 -0400531 if (!str)
532 return 0;
533
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 out = strchr(str, ',');
Jeff Dikee99525f2007-10-16 01:26:41 -0700535 if (out != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 in = str;
537 *out = '\0';
538 out++;
Jeff Dikef28169d2007-02-10 01:43:53 -0800539 new = parse_chan(line, in, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700540 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800541 return -1;
542
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 new->input = 1;
544 list_add(&new->list, chans);
Al Viroee485072011-09-08 07:07:26 -0400545 line->chan_in = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Jeff Dikef28169d2007-02-10 01:43:53 -0800547 new = parse_chan(line, out, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700548 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800549 return -1;
550
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 list_add(&new->list, chans);
552 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400553 line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 }
555 else {
Jeff Dikef28169d2007-02-10 01:43:53 -0800556 new = parse_chan(line, str, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700557 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800558 return -1;
559
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 list_add(&new->list, chans);
561 new->input = 1;
562 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400563 line->chan_in = line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 }
Jeff Diked50084a2006-01-06 00:18:50 -0800565 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}
567
Al Viro0fcd7192011-09-10 08:17:04 -0400568void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
Al Virobed5e392011-09-08 10:49:34 -0400570 struct chan *chan = line->chan_in;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 int err;
572 char c;
573
Al Virobed5e392011-09-08 10:49:34 -0400574 if (!chan || !chan->ops->read)
575 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Al Virobed5e392011-09-08 10:49:34 -0400577 do {
578 if (tty && !tty_buffer_request_room(tty, 1)) {
Al Viro0fcd7192011-09-10 08:17:04 -0400579 schedule_delayed_work(&line->task, 1);
Al Virobed5e392011-09-08 10:49:34 -0400580 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 }
Al Virobed5e392011-09-08 10:49:34 -0400582 err = chan->ops->read(chan->fd, &c, chan->data);
583 if (err > 0)
584 tty_receive_char(tty, c);
585 } while (err > 0);
586
587 if (err == 0)
588 reactivate_fd(chan->fd, irq);
589 if (err == -EIO) {
590 if (chan->primary) {
591 if (tty != NULL)
592 tty_hangup(tty);
Al Viro10c890c02011-09-10 08:39:18 -0400593 if (line->chan_out != chan)
594 close_one_chan(line->chan_out, 1);
Al Virobed5e392011-09-08 10:49:34 -0400595 }
Al Viro10c890c02011-09-10 08:39:18 -0400596 close_one_chan(chan, 1);
597 if (chan->primary)
598 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 }
600 out:
Jeff Dikee99525f2007-10-16 01:26:41 -0700601 if (tty)
602 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603}