blob: 73d7bc018ab2c8b293b3c24ccee9cb1cd2c9295f [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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include "os.h"
11
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070012#ifdef CONFIG_NOCONFIG_CHAN
Jeff Dikef28169d2007-02-10 01:43:53 -080013static void *not_configged_init(char *str, int device,
14 const struct chan_opts *opts)
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070015{
Jeff Dikee99525f2007-10-16 01:26:41 -070016 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070017 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080018 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070019}
20
21static int not_configged_open(int input, int output, int primary, void *data,
22 char **dev_out)
23{
Jeff Dikee99525f2007-10-16 01:26:41 -070024 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080026 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070027}
28
29static void not_configged_close(int fd, void *data)
30{
Jeff Dikee99525f2007-10-16 01:26:41 -070031 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070032 "UML\n");
33}
34
35static int not_configged_read(int fd, char *c_out, void *data)
36{
Jeff Dikee99525f2007-10-16 01:26:41 -070037 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080039 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070040}
41
42static int not_configged_write(int fd, const char *buf, int len, void *data)
43{
Jeff Dikee99525f2007-10-16 01:26:41 -070044 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070045 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080046 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047}
48
Paolo 'Blaisorblade' Giarrusso55c033c2005-11-13 16:07:11 -080049static int not_configged_console_write(int fd, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070050{
Jeff Dikee99525f2007-10-16 01:26:41 -070051 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080053 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070054}
55
56static int not_configged_window_size(int fd, void *data, unsigned short *rows,
57 unsigned short *cols)
58{
Jeff Dikee99525f2007-10-16 01:26:41 -070059 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080061 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062}
63
64static void not_configged_free(void *data)
65{
Jeff Dikee99525f2007-10-16 01:26:41 -070066 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 "UML\n");
68}
69
Jeff Dike5e7672e2006-09-27 01:50:33 -070070static const struct chan_ops not_configged_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 .init = not_configged_init,
72 .open = not_configged_open,
73 .close = not_configged_close,
74 .read = not_configged_read,
75 .write = not_configged_write,
76 .console_write = not_configged_console_write,
77 .window_size = not_configged_window_size,
78 .free = not_configged_free,
79 .winch = 0,
80};
81#endif /* CONFIG_NOCONFIG_CHAN */
82
Linus Torvalds1da177e2005-04-16 15:20:36 -070083static void tty_receive_char(struct tty_struct *tty, char ch)
84{
Jeff Dikee99525f2007-10-16 01:26:41 -070085 if (tty == NULL)
86 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Jeff Dikee99525f2007-10-16 01:26:41 -070088 if (I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
89 if (ch == STOP_CHAR(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 stop_tty(tty);
91 return;
92 }
Jeff Dikee99525f2007-10-16 01:26:41 -070093 else if (ch == START_CHAR(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 start_tty(tty);
95 return;
96 }
97 }
98
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 tty_insert_flip_char(tty, ch, TTY_NORMAL);
100}
101
Jeff Diked50084a2006-01-06 00:18:50 -0800102static int open_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103{
Jeff Dike6676ae62007-07-31 00:37:44 -0700104 int fd, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
Jeff Dikee99525f2007-10-16 01:26:41 -0700106 if (chan->opened)
Jeff Diked50084a2006-01-06 00:18:50 -0800107 return 0;
108
Jeff Dikee99525f2007-10-16 01:26:41 -0700109 if (chan->ops->open == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800110 fd = 0;
111 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
112 chan->data, &chan->dev);
Jeff Dikee99525f2007-10-16 01:26:41 -0700113 if (fd < 0)
Jeff Diked50084a2006-01-06 00:18:50 -0800114 return fd;
Jeff Dike6676ae62007-07-31 00:37:44 -0700115
116 err = os_set_fd_block(fd, 0);
117 if (err) {
118 (*chan->ops->close)(fd, chan->data);
119 return err;
120 }
121
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 chan->fd = fd;
123
124 chan->opened = 1;
Jeff Diked50084a2006-01-06 00:18:50 -0800125 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126}
127
WANG Cong3af7cb72008-04-28 02:13:55 -0700128static int open_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
130 struct list_head *ele;
131 struct chan *chan;
132 int ret, err = 0;
133
Jeff Dikee99525f2007-10-16 01:26:41 -0700134 list_for_each(ele, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 chan = list_entry(ele, struct chan, list);
Jeff Diked50084a2006-01-06 00:18:50 -0800136 ret = open_one_chan(chan);
Jeff Dikee99525f2007-10-16 01:26:41 -0700137 if (chan->primary)
Jeff Diked50084a2006-01-06 00:18:50 -0800138 err = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 }
Jeff Diked50084a2006-01-06 00:18:50 -0800140 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141}
142
Al Virobed5e392011-09-08 10:49:34 -0400143void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
Al Virobed5e392011-09-08 10:49:34 -0400145 if (chan && chan->primary && chan->ops->winch)
146 register_winch(chan->fd, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147}
148
Jeff Diked14ad812007-07-15 23:38:54 -0700149int enable_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
151 struct list_head *ele;
152 struct chan *chan;
Jeff Diked14ad812007-07-15 23:38:54 -0700153 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
Jeff Dikee99525f2007-10-16 01:26:41 -0700155 list_for_each(ele, &line->chan_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 chan = list_entry(ele, struct chan, list);
Jeff Diked14ad812007-07-15 23:38:54 -0700157 err = open_one_chan(chan);
158 if (err) {
159 if (chan->primary)
160 goto out_close;
161
Jeff Dike165dc592006-01-06 00:18:57 -0800162 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700163 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164
Jeff Dikee99525f2007-10-16 01:26:41 -0700165 if (chan->enabled)
Jeff Dike165dc592006-01-06 00:18:57 -0800166 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700167 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
168 chan);
169 if (err)
170 goto out_close;
171
Jeff Dike165dc592006-01-06 00:18:57 -0800172 chan->enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 }
Jeff Diked14ad812007-07-15 23:38:54 -0700174
175 return 0;
176
177 out_close:
178 close_chan(&line->chan_list, 0);
179 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
Jeff Dike190c3e42007-02-10 01:43:55 -0800182/* Items are added in IRQ context, when free_irq can't be called, and
183 * removed in process context, when it can.
184 * This handles interrupt sources which disappear, and which need to
185 * be permanently disabled. This is discovered in IRQ context, but
186 * the freeing of the IRQ must be done later.
187 */
188static DEFINE_SPINLOCK(irqs_to_free_lock);
Jeff Dike165dc592006-01-06 00:18:57 -0800189static LIST_HEAD(irqs_to_free);
190
191void free_irqs(void)
192{
193 struct chan *chan;
Jeff Dike190c3e42007-02-10 01:43:55 -0800194 LIST_HEAD(list);
195 struct list_head *ele;
Jeff Dike30762122007-03-29 01:20:30 -0700196 unsigned long flags;
Jeff Dike165dc592006-01-06 00:18:57 -0800197
Jeff Dike30762122007-03-29 01:20:30 -0700198 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800199 list_splice_init(&irqs_to_free, &list);
Jeff Dike30762122007-03-29 01:20:30 -0700200 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800201
Jeff Dikee99525f2007-10-16 01:26:41 -0700202 list_for_each(ele, &list) {
Jeff Dike190c3e42007-02-10 01:43:55 -0800203 chan = list_entry(ele, struct chan, free_list);
Jeff Dike165dc592006-01-06 00:18:57 -0800204
Richard Weinberger47562272010-08-09 17:20:14 -0700205 if (chan->input && chan->enabled)
Jeff Dike165dc592006-01-06 00:18:57 -0800206 free_irq(chan->line->driver->read_irq, chan);
Richard Weinberger47562272010-08-09 17:20:14 -0700207 if (chan->output && chan->enabled)
Jeff Dike165dc592006-01-06 00:18:57 -0800208 free_irq(chan->line->driver->write_irq, chan);
209 chan->enabled = 0;
210 }
211}
212
213static void close_one_chan(struct chan *chan, int delay_free_irq)
214{
Jeff Dike30762122007-03-29 01:20:30 -0700215 unsigned long flags;
216
Jeff Dikee99525f2007-10-16 01:26:41 -0700217 if (!chan->opened)
Jeff Dike165dc592006-01-06 00:18:57 -0800218 return;
219
Jeff Dikee99525f2007-10-16 01:26:41 -0700220 if (delay_free_irq) {
Jeff Dike30762122007-03-29 01:20:30 -0700221 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800222 list_add(&chan->free_list, &irqs_to_free);
Jeff Dike30762122007-03-29 01:20:30 -0700223 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800224 }
225 else {
Richard Weinberger47562272010-08-09 17:20:14 -0700226 if (chan->input && chan->enabled)
Jeff Dike165dc592006-01-06 00:18:57 -0800227 free_irq(chan->line->driver->read_irq, chan);
Richard Weinberger47562272010-08-09 17:20:14 -0700228 if (chan->output && chan->enabled)
Jeff Dike165dc592006-01-06 00:18:57 -0800229 free_irq(chan->line->driver->write_irq, chan);
230 chan->enabled = 0;
231 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700232 if (chan->ops->close != NULL)
Jeff Dike165dc592006-01-06 00:18:57 -0800233 (*chan->ops->close)(chan->fd, chan->data);
234
235 chan->opened = 0;
236 chan->fd = -1;
237}
238
239void close_chan(struct list_head *chans, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240{
241 struct chan *chan;
242
243 /* Close in reverse order as open in case more than one of them
244 * refers to the same device and they save and restore that device's
245 * state. Then, the first one opened will have the original state,
246 * so it must be the last closed.
247 */
248 list_for_each_entry_reverse(chan, chans, list) {
Jeff Dike165dc592006-01-06 00:18:57 -0800249 close_one_chan(chan, delay_free_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 }
251}
252
Al Virobed5e392011-09-08 10:49:34 -0400253void deactivate_chan(struct chan *chan, int irq)
Jeff Dikee4dcee82006-01-06 00:18:58 -0800254{
Al Virobed5e392011-09-08 10:49:34 -0400255 if (chan && chan->enabled)
256 deactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800257}
258
Al Virobed5e392011-09-08 10:49:34 -0400259void reactivate_chan(struct chan *chan, int irq)
Jeff Dikee4dcee82006-01-06 00:18:58 -0800260{
Al Virobed5e392011-09-08 10:49:34 -0400261 if (chan && chan->enabled)
262 reactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800263}
264
Al Virobed5e392011-09-08 10:49:34 -0400265int write_chan(struct chan *chan, const char *buf, int len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 int write_irq)
267{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 int n, ret = 0;
269
Al Virobed5e392011-09-08 10:49:34 -0400270 if (len == 0 || !chan || !chan->ops->write)
Jeff Dikec59dbca2007-10-16 01:26:42 -0700271 return 0;
272
Al Virobed5e392011-09-08 10:49:34 -0400273 n = chan->ops->write(chan->fd, buf, len, chan->data);
274 if (chan->primary) {
275 ret = n;
276 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
277 reactivate_fd(chan->fd, write_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 }
Jeff Diked50084a2006-01-06 00:18:50 -0800279 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280}
281
Al Virobed5e392011-09-08 10:49:34 -0400282int console_write_chan(struct chan *chan, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 int n, ret = 0;
285
Al Virobed5e392011-09-08 10:49:34 -0400286 if (!chan || !chan->ops->console_write)
287 return 0;
Jeff Dikee99525f2007-10-16 01:26:41 -0700288
Al Virobed5e392011-09-08 10:49:34 -0400289 n = chan->ops->console_write(chan->fd, buf, len);
290 if (chan->primary)
291 ret = n;
Jeff Diked50084a2006-01-06 00:18:50 -0800292 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293}
294
Jeff Dikea52f3622007-02-10 01:44:06 -0800295int console_open_chan(struct line *line, struct console *co)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
Jeff Dike1f801712006-01-06 00:18:55 -0800297 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Jeff Dike1f801712006-01-06 00:18:55 -0800299 err = open_chan(&line->chan_list);
Jeff Dikee99525f2007-10-16 01:26:41 -0700300 if (err)
Jeff Dike1f801712006-01-06 00:18:55 -0800301 return err;
302
Jeff Dikee99525f2007-10-16 01:26:41 -0700303 printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
304 co->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 return 0;
306}
307
Al Virobed5e392011-09-08 10:49:34 -0400308int chan_window_size(struct line *line, unsigned short *rows_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 unsigned short *cols_out)
310{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 struct chan *chan;
312
Al Virobed5e392011-09-08 10:49:34 -0400313 chan = line->chan_in;
314 if (chan && chan->primary) {
315 if (chan->ops->window_size == NULL)
316 return 0;
317 return chan->ops->window_size(chan->fd, chan->data,
318 rows_out, cols_out);
319 }
320 chan = line->chan_out;
321 if (chan && chan->primary) {
322 if (chan->ops->window_size == NULL)
323 return 0;
324 return chan->ops->window_size(chan->fd, chan->data,
325 rows_out, cols_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
Jeff Diked50084a2006-01-06 00:18:50 -0800327 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328}
329
Al Viro772bd0a2011-08-18 20:12:39 +0100330static void free_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331{
332 list_del(&chan->list);
Jeff Dike165dc592006-01-06 00:18:57 -0800333
Al Viro772bd0a2011-08-18 20:12:39 +0100334 close_one_chan(chan, 0);
Jeff Dike165dc592006-01-06 00:18:57 -0800335
Jeff Dikee99525f2007-10-16 01:26:41 -0700336 if (chan->ops->free != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 (*chan->ops->free)(chan->data);
Jeff Dike165dc592006-01-06 00:18:57 -0800338
Jeff Dikee99525f2007-10-16 01:26:41 -0700339 if (chan->primary && chan->output)
340 ignore_sigio_fd(chan->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 kfree(chan);
342}
343
Al Viro772bd0a2011-08-18 20:12:39 +0100344static void free_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345{
346 struct list_head *ele, *next;
347 struct chan *chan;
348
Jeff Dikee99525f2007-10-16 01:26:41 -0700349 list_for_each_safe(ele, next, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 chan = list_entry(ele, struct chan, list);
Al Viro772bd0a2011-08-18 20:12:39 +0100351 free_one_chan(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 }
353}
354
355static int one_chan_config_string(struct chan *chan, char *str, int size,
356 char **error_out)
357{
358 int n = 0;
359
Jeff Dikee99525f2007-10-16 01:26:41 -0700360 if (chan == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 CONFIG_CHUNK(str, size, n, "none", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800362 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 }
364
365 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
366
Jeff Dikee99525f2007-10-16 01:26:41 -0700367 if (chan->dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800369 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 }
371
372 CONFIG_CHUNK(str, size, n, ":", 0);
373 CONFIG_CHUNK(str, size, n, chan->dev, 0);
374
Jeff Diked50084a2006-01-06 00:18:50 -0800375 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
377
Jeff Diked50084a2006-01-06 00:18:50 -0800378static int chan_pair_config_string(struct chan *in, struct chan *out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 char *str, int size, char **error_out)
380{
381 int n;
382
383 n = one_chan_config_string(in, str, size, error_out);
384 str += n;
385 size -= n;
386
Jeff Dikee99525f2007-10-16 01:26:41 -0700387 if (in == out) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800389 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 }
391
392 CONFIG_CHUNK(str, size, n, ",", 1);
393 n = one_chan_config_string(out, str, size, error_out);
394 str += n;
395 size -= n;
396 CONFIG_CHUNK(str, size, n, "", 1);
397
Jeff Diked50084a2006-01-06 00:18:50 -0800398 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399}
400
Al Virobed5e392011-09-08 10:49:34 -0400401int chan_config_string(struct line *line, char *str, int size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 char **error_out)
403{
Al Virobed5e392011-09-08 10:49:34 -0400404 struct chan *in = line->chan_in, *out = line->chan_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
Al Virobed5e392011-09-08 10:49:34 -0400406 if (in && !in->primary)
407 in = NULL;
408 if (out && !out->primary)
409 out = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
Jeff Diked50084a2006-01-06 00:18:50 -0800411 return chan_pair_config_string(in, out, str, size, error_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412}
413
414struct chan_type {
415 char *key;
Jeff Dike5e7672e2006-09-27 01:50:33 -0700416 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417};
418
Jeff Dike5e7672e2006-09-27 01:50:33 -0700419static const struct chan_type chan_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 { "fd", &fd_ops },
421
422#ifdef CONFIG_NULL_CHAN
423 { "null", &null_ops },
424#else
425 { "null", &not_configged_ops },
426#endif
427
428#ifdef CONFIG_PORT_CHAN
429 { "port", &port_ops },
430#else
431 { "port", &not_configged_ops },
432#endif
433
434#ifdef CONFIG_PTY_CHAN
435 { "pty", &pty_ops },
436 { "pts", &pts_ops },
437#else
438 { "pty", &not_configged_ops },
439 { "pts", &not_configged_ops },
440#endif
441
442#ifdef CONFIG_TTY_CHAN
443 { "tty", &tty_ops },
444#else
445 { "tty", &not_configged_ops },
446#endif
447
448#ifdef CONFIG_XTERM_CHAN
449 { "xterm", &xterm_ops },
450#else
451 { "xterm", &not_configged_ops },
452#endif
453};
454
Jeff Dike165dc592006-01-06 00:18:57 -0800455static struct chan *parse_chan(struct line *line, char *str, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800456 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457{
Jeff Dike5e7672e2006-09-27 01:50:33 -0700458 const struct chan_type *entry;
459 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 struct chan *chan;
461 void *data;
462 int i;
463
464 ops = NULL;
465 data = NULL;
Jeff Dikee99525f2007-10-16 01:26:41 -0700466 for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 entry = &chan_table[i];
Jeff Dikee99525f2007-10-16 01:26:41 -0700468 if (!strncmp(str, entry->key, strlen(entry->key))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 ops = entry->ops;
470 str += strlen(entry->key);
471 break;
472 }
473 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700474 if (ops == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800475 *error_out = "No match for configured backends";
Jeff Diked50084a2006-01-06 00:18:50 -0800476 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800478
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 data = (*ops->init)(str, device, opts);
Jeff Dikee99525f2007-10-16 01:26:41 -0700480 if (data == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800481 *error_out = "Configuration failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800482 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800483 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
Paolo 'Blaisorblade' Giarrusso79ae2cb2005-09-22 21:44:21 -0700485 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
Jeff Dikee99525f2007-10-16 01:26:41 -0700486 if (chan == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800487 *error_out = "Memory allocation failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800488 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800489 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
Jeff Dike165dc592006-01-06 00:18:57 -0800491 .free_list =
492 LIST_HEAD_INIT(chan->free_list),
493 .line = line,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 .primary = 1,
495 .input = 0,
496 .output = 0,
497 .opened = 0,
Jeff Dike165dc592006-01-06 00:18:57 -0800498 .enabled = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 .fd = -1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 .ops = ops,
501 .data = data });
Jeff Diked50084a2006-01-06 00:18:50 -0800502 return chan;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503}
504
Jeff Dike165dc592006-01-06 00:18:57 -0800505int parse_chan_pair(char *str, struct line *line, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800506 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507{
Jeff Dike165dc592006-01-06 00:18:57 -0800508 struct list_head *chans = &line->chan_list;
Richard Weinbergerf1c93e42011-07-25 17:12:55 -0700509 struct chan *new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 char *in, *out;
511
Jeff Dikee99525f2007-10-16 01:26:41 -0700512 if (!list_empty(chans)) {
Al Viroee485072011-09-08 07:07:26 -0400513 line->chan_in = line->chan_out = NULL;
Al Viro772bd0a2011-08-18 20:12:39 +0100514 free_chan(chans);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 INIT_LIST_HEAD(chans);
516 }
517
Al Viro31efceb2011-09-09 19:14:02 -0400518 if (!str)
519 return 0;
520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 out = strchr(str, ',');
Jeff Dikee99525f2007-10-16 01:26:41 -0700522 if (out != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 in = str;
524 *out = '\0';
525 out++;
Jeff Dikef28169d2007-02-10 01:43:53 -0800526 new = parse_chan(line, in, 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 new->input = 1;
531 list_add(&new->list, chans);
Al Viroee485072011-09-08 07:07:26 -0400532 line->chan_in = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Jeff Dikef28169d2007-02-10 01:43:53 -0800534 new = parse_chan(line, out, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700535 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800536 return -1;
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 list_add(&new->list, chans);
539 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400540 line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 }
542 else {
Jeff Dikef28169d2007-02-10 01:43:53 -0800543 new = parse_chan(line, str, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700544 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800545 return -1;
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 list_add(&new->list, chans);
548 new->input = 1;
549 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400550 line->chan_in = line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 }
Jeff Diked50084a2006-01-06 00:18:50 -0800552 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553}
554
Al Virobed5e392011-09-08 10:49:34 -0400555void chan_interrupt(struct line *line, struct delayed_work *task,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 struct tty_struct *tty, int irq)
557{
Al Virobed5e392011-09-08 10:49:34 -0400558 struct chan *chan = line->chan_in;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 int err;
560 char c;
561
Al Virobed5e392011-09-08 10:49:34 -0400562 if (!chan || !chan->ops->read)
563 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Al Virobed5e392011-09-08 10:49:34 -0400565 do {
566 if (tty && !tty_buffer_request_room(tty, 1)) {
567 schedule_delayed_work(task, 1);
568 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 }
Al Virobed5e392011-09-08 10:49:34 -0400570 err = chan->ops->read(chan->fd, &c, chan->data);
571 if (err > 0)
572 tty_receive_char(tty, c);
573 } while (err > 0);
574
575 if (err == 0)
576 reactivate_fd(chan->fd, irq);
577 if (err == -EIO) {
578 if (chan->primary) {
579 if (tty != NULL)
580 tty_hangup(tty);
581 close_chan(&line->chan_list, 1);
582 return;
583 }
584 else close_one_chan(chan, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 }
586 out:
Jeff Dikee99525f2007-10-16 01:26:41 -0700587 if (tty)
588 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589}