blob: 9a6222f1a51b8a95c63384710c7fe6f3e538f872 [file] [log] [blame]
Jeff Dike165dc592006-01-06 00:18:57 -08001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
5
6#include <linux/stddef.h>
7#include <linux/kernel.h>
8#include <linux/list.h>
9#include <linux/slab.h>
10#include <linux/tty.h>
11#include <linux/string.h>
12#include <linux/tty_flip.h>
13#include <asm/irq.h>
14#include "chan_kern.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include "kern.h"
16#include "irq_user.h"
17#include "sigio.h"
18#include "line.h"
19#include "os.h"
20
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070021#ifdef CONFIG_NOCONFIG_CHAN
Jeff Dikef28169d2007-02-10 01:43:53 -080022static void *not_configged_init(char *str, int device,
23 const struct chan_opts *opts)
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070024{
Jeff Dikef28169d2007-02-10 01:43:53 -080025 printk("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 NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070028}
29
30static int not_configged_open(int input, int output, int primary, void *data,
31 char **dev_out)
32{
Jeff Dikef28169d2007-02-10 01:43:53 -080033 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080035 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070036}
37
38static void not_configged_close(int fd, void *data)
39{
Jeff Dikef28169d2007-02-10 01:43:53 -080040 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 "UML\n");
42}
43
44static int not_configged_read(int fd, char *c_out, void *data)
45{
Jeff Dikef28169d2007-02-10 01:43:53 -080046 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080048 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070049}
50
51static int not_configged_write(int fd, const char *buf, int len, void *data)
52{
Jeff Dikef28169d2007-02-10 01:43:53 -080053 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080055 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056}
57
Paolo 'Blaisorblade' Giarrusso55c033c2005-11-13 16:07:11 -080058static int not_configged_console_write(int fd, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059{
Jeff Dikef28169d2007-02-10 01:43:53 -080060 printk("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 -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063}
64
65static int not_configged_window_size(int fd, void *data, unsigned short *rows,
66 unsigned short *cols)
67{
Jeff Dikef28169d2007-02-10 01:43:53 -080068 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080070 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070071}
72
73static void not_configged_free(void *data)
74{
Jeff Dikef28169d2007-02-10 01:43:53 -080075 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 "UML\n");
77}
78
Jeff Dike5e7672e2006-09-27 01:50:33 -070079static const struct chan_ops not_configged_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 .init = not_configged_init,
81 .open = not_configged_open,
82 .close = not_configged_close,
83 .read = not_configged_read,
84 .write = not_configged_write,
85 .console_write = not_configged_console_write,
86 .window_size = not_configged_window_size,
87 .free = not_configged_free,
88 .winch = 0,
89};
90#endif /* CONFIG_NOCONFIG_CHAN */
91
Linus Torvalds1da177e2005-04-16 15:20:36 -070092static void tty_receive_char(struct tty_struct *tty, char ch)
93{
94 if(tty == NULL) return;
95
96 if(I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
97 if(ch == STOP_CHAR(tty)){
98 stop_tty(tty);
99 return;
100 }
101 else if(ch == START_CHAR(tty)){
102 start_tty(tty);
103 return;
104 }
105 }
106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 tty_insert_flip_char(tty, ch, TTY_NORMAL);
108}
109
Jeff Diked50084a2006-01-06 00:18:50 -0800110static int open_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
Jeff Dike6676ae62007-07-31 00:37:44 -0700112 int fd, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
Jeff Diked50084a2006-01-06 00:18:50 -0800114 if(chan->opened)
115 return 0;
116
117 if(chan->ops->open == NULL)
118 fd = 0;
119 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
120 chan->data, &chan->dev);
121 if(fd < 0)
122 return fd;
Jeff Dike6676ae62007-07-31 00:37:44 -0700123
124 err = os_set_fd_block(fd, 0);
125 if (err) {
126 (*chan->ops->close)(fd, chan->data);
127 return err;
128 }
129
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 chan->fd = fd;
131
132 chan->opened = 1;
Jeff Diked50084a2006-01-06 00:18:50 -0800133 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134}
135
136int open_chan(struct list_head *chans)
137{
138 struct list_head *ele;
139 struct chan *chan;
140 int ret, err = 0;
141
142 list_for_each(ele, chans){
143 chan = list_entry(ele, struct chan, list);
Jeff Diked50084a2006-01-06 00:18:50 -0800144 ret = open_one_chan(chan);
145 if(chan->primary)
146 err = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 }
Jeff Diked50084a2006-01-06 00:18:50 -0800148 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149}
150
151void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
152{
153 struct list_head *ele;
154 struct chan *chan;
155
156 list_for_each(ele, chans){
157 chan = list_entry(ele, struct chan, list);
158 if(chan->primary && chan->output && chan->ops->winch){
159 register_winch(chan->fd, tty);
160 return;
161 }
162 }
163}
164
Jeff Diked14ad812007-07-15 23:38:54 -0700165int enable_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
167 struct list_head *ele;
168 struct chan *chan;
Jeff Diked14ad812007-07-15 23:38:54 -0700169 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
Jeff Dike165dc592006-01-06 00:18:57 -0800171 list_for_each(ele, &line->chan_list){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 chan = list_entry(ele, struct chan, list);
Jeff Diked14ad812007-07-15 23:38:54 -0700173 err = open_one_chan(chan);
174 if (err) {
175 if (chan->primary)
176 goto out_close;
177
Jeff Dike165dc592006-01-06 00:18:57 -0800178 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700179 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
Jeff Dike165dc592006-01-06 00:18:57 -0800181 if(chan->enabled)
182 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700183 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
184 chan);
185 if (err)
186 goto out_close;
187
Jeff Dike165dc592006-01-06 00:18:57 -0800188 chan->enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 }
Jeff Diked14ad812007-07-15 23:38:54 -0700190
191 return 0;
192
193 out_close:
194 close_chan(&line->chan_list, 0);
195 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196}
197
Jeff Dike190c3e42007-02-10 01:43:55 -0800198/* Items are added in IRQ context, when free_irq can't be called, and
199 * removed in process context, when it can.
200 * This handles interrupt sources which disappear, and which need to
201 * be permanently disabled. This is discovered in IRQ context, but
202 * the freeing of the IRQ must be done later.
203 */
204static DEFINE_SPINLOCK(irqs_to_free_lock);
Jeff Dike165dc592006-01-06 00:18:57 -0800205static LIST_HEAD(irqs_to_free);
206
207void free_irqs(void)
208{
209 struct chan *chan;
Jeff Dike190c3e42007-02-10 01:43:55 -0800210 LIST_HEAD(list);
211 struct list_head *ele;
Jeff Dike30762122007-03-29 01:20:30 -0700212 unsigned long flags;
Jeff Dike165dc592006-01-06 00:18:57 -0800213
Jeff Dike30762122007-03-29 01:20:30 -0700214 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800215 list_splice_init(&irqs_to_free, &list);
Jeff Dike30762122007-03-29 01:20:30 -0700216 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800217
218 list_for_each(ele, &list){
219 chan = list_entry(ele, struct chan, free_list);
Jeff Dike165dc592006-01-06 00:18:57 -0800220
221 if(chan->input)
222 free_irq(chan->line->driver->read_irq, chan);
223 if(chan->output)
224 free_irq(chan->line->driver->write_irq, chan);
225 chan->enabled = 0;
226 }
227}
228
229static void close_one_chan(struct chan *chan, int delay_free_irq)
230{
Jeff Dike30762122007-03-29 01:20:30 -0700231 unsigned long flags;
232
Jeff Dike165dc592006-01-06 00:18:57 -0800233 if(!chan->opened)
234 return;
235
236 if(delay_free_irq){
Jeff Dike30762122007-03-29 01:20:30 -0700237 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800238 list_add(&chan->free_list, &irqs_to_free);
Jeff Dike30762122007-03-29 01:20:30 -0700239 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800240 }
241 else {
242 if(chan->input)
243 free_irq(chan->line->driver->read_irq, chan);
244 if(chan->output)
245 free_irq(chan->line->driver->write_irq, chan);
246 chan->enabled = 0;
247 }
248 if(chan->ops->close != NULL)
249 (*chan->ops->close)(chan->fd, chan->data);
250
251 chan->opened = 0;
252 chan->fd = -1;
253}
254
255void close_chan(struct list_head *chans, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
257 struct chan *chan;
258
259 /* Close in reverse order as open in case more than one of them
260 * refers to the same device and they save and restore that device's
261 * state. Then, the first one opened will have the original state,
262 * so it must be the last closed.
263 */
264 list_for_each_entry_reverse(chan, chans, list) {
Jeff Dike165dc592006-01-06 00:18:57 -0800265 close_one_chan(chan, delay_free_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 }
267}
268
Jeff Dikee4dcee82006-01-06 00:18:58 -0800269void deactivate_chan(struct list_head *chans, int irq)
270{
271 struct list_head *ele;
272
273 struct chan *chan;
274 list_for_each(ele, chans) {
275 chan = list_entry(ele, struct chan, list);
276
277 if(chan->enabled && chan->input)
278 deactivate_fd(chan->fd, irq);
279 }
280}
281
282void reactivate_chan(struct list_head *chans, int irq)
283{
284 struct list_head *ele;
285 struct chan *chan;
286
287 list_for_each(ele, chans) {
288 chan = list_entry(ele, struct chan, list);
289
290 if(chan->enabled && chan->input)
291 reactivate_fd(chan->fd, irq);
292 }
293}
294
Jeff Diked50084a2006-01-06 00:18:50 -0800295int write_chan(struct list_head *chans, const char *buf, int len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 int write_irq)
297{
298 struct list_head *ele;
299 struct chan *chan = NULL;
300 int n, ret = 0;
301
302 list_for_each(ele, chans) {
303 chan = list_entry(ele, struct chan, list);
304 if (!chan->output || (chan->ops->write == NULL))
305 continue;
306 n = chan->ops->write(chan->fd, buf, len, chan->data);
307 if (chan->primary) {
308 ret = n;
309 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
310 reactivate_fd(chan->fd, write_irq);
311 }
312 }
Jeff Diked50084a2006-01-06 00:18:50 -0800313 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314}
315
316int console_write_chan(struct list_head *chans, const char *buf, int len)
317{
318 struct list_head *ele;
319 struct chan *chan;
320 int n, ret = 0;
321
322 list_for_each(ele, chans){
323 chan = list_entry(ele, struct chan, list);
324 if(!chan->output || (chan->ops->console_write == NULL))
325 continue;
Paolo 'Blaisorblade' Giarrusso55c033c2005-11-13 16:07:11 -0800326 n = chan->ops->console_write(chan->fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 if(chan->primary) ret = n;
328 }
Jeff Diked50084a2006-01-06 00:18:50 -0800329 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330}
331
Jeff Dikea52f3622007-02-10 01:44:06 -0800332int console_open_chan(struct line *line, struct console *co)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
Jeff Dike1f801712006-01-06 00:18:55 -0800334 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
Jeff Dike1f801712006-01-06 00:18:55 -0800336 err = open_chan(&line->chan_list);
337 if(err)
338 return err;
339
Jeff Dikea52f3622007-02-10 01:44:06 -0800340 printk("Console initialized on /dev/%s%d\n", co->name, co->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 return 0;
342}
343
344int chan_window_size(struct list_head *chans, unsigned short *rows_out,
345 unsigned short *cols_out)
346{
347 struct list_head *ele;
348 struct chan *chan;
349
350 list_for_each(ele, chans){
351 chan = list_entry(ele, struct chan, list);
352 if(chan->primary){
Jeff Diked50084a2006-01-06 00:18:50 -0800353 if(chan->ops->window_size == NULL)
354 return 0;
355 return chan->ops->window_size(chan->fd, chan->data,
356 rows_out, cols_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 }
358 }
Jeff Diked50084a2006-01-06 00:18:50 -0800359 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360}
361
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800362static void free_one_chan(struct chan *chan, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
364 list_del(&chan->list);
Jeff Dike165dc592006-01-06 00:18:57 -0800365
366 close_one_chan(chan, delay_free_irq);
367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 if(chan->ops->free != NULL)
369 (*chan->ops->free)(chan->data);
Jeff Dike165dc592006-01-06 00:18:57 -0800370
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
372 kfree(chan);
373}
374
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800375static void free_chan(struct list_head *chans, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376{
377 struct list_head *ele, *next;
378 struct chan *chan;
379
380 list_for_each_safe(ele, next, chans){
381 chan = list_entry(ele, struct chan, list);
Jeff Dike165dc592006-01-06 00:18:57 -0800382 free_one_chan(chan, delay_free_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 }
384}
385
386static int one_chan_config_string(struct chan *chan, char *str, int size,
387 char **error_out)
388{
389 int n = 0;
390
391 if(chan == NULL){
392 CONFIG_CHUNK(str, size, n, "none", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800393 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 }
395
396 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
397
398 if(chan->dev == NULL){
399 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800400 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 }
402
403 CONFIG_CHUNK(str, size, n, ":", 0);
404 CONFIG_CHUNK(str, size, n, chan->dev, 0);
405
Jeff Diked50084a2006-01-06 00:18:50 -0800406 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
Jeff Diked50084a2006-01-06 00:18:50 -0800409static int chan_pair_config_string(struct chan *in, struct chan *out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 char *str, int size, char **error_out)
411{
412 int n;
413
414 n = one_chan_config_string(in, str, size, error_out);
415 str += n;
416 size -= n;
417
418 if(in == out){
419 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800420 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 }
422
423 CONFIG_CHUNK(str, size, n, ",", 1);
424 n = one_chan_config_string(out, str, size, error_out);
425 str += n;
426 size -= n;
427 CONFIG_CHUNK(str, size, n, "", 1);
428
Jeff Diked50084a2006-01-06 00:18:50 -0800429 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430}
431
Jeff Diked50084a2006-01-06 00:18:50 -0800432int chan_config_string(struct list_head *chans, char *str, int size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 char **error_out)
434{
435 struct list_head *ele;
436 struct chan *chan, *in = NULL, *out = NULL;
437
438 list_for_each(ele, chans){
439 chan = list_entry(ele, struct chan, list);
440 if(!chan->primary)
441 continue;
442 if(chan->input)
443 in = chan;
444 if(chan->output)
445 out = chan;
446 }
447
Jeff Diked50084a2006-01-06 00:18:50 -0800448 return chan_pair_config_string(in, out, str, size, error_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449}
450
451struct chan_type {
452 char *key;
Jeff Dike5e7672e2006-09-27 01:50:33 -0700453 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454};
455
Jeff Dike5e7672e2006-09-27 01:50:33 -0700456static const struct chan_type chan_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 { "fd", &fd_ops },
458
459#ifdef CONFIG_NULL_CHAN
460 { "null", &null_ops },
461#else
462 { "null", &not_configged_ops },
463#endif
464
465#ifdef CONFIG_PORT_CHAN
466 { "port", &port_ops },
467#else
468 { "port", &not_configged_ops },
469#endif
470
471#ifdef CONFIG_PTY_CHAN
472 { "pty", &pty_ops },
473 { "pts", &pts_ops },
474#else
475 { "pty", &not_configged_ops },
476 { "pts", &not_configged_ops },
477#endif
478
479#ifdef CONFIG_TTY_CHAN
480 { "tty", &tty_ops },
481#else
482 { "tty", &not_configged_ops },
483#endif
484
485#ifdef CONFIG_XTERM_CHAN
486 { "xterm", &xterm_ops },
487#else
488 { "xterm", &not_configged_ops },
489#endif
490};
491
Jeff Dike165dc592006-01-06 00:18:57 -0800492static struct chan *parse_chan(struct line *line, char *str, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800493 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494{
Jeff Dike5e7672e2006-09-27 01:50:33 -0700495 const struct chan_type *entry;
496 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 struct chan *chan;
498 void *data;
499 int i;
500
501 ops = NULL;
502 data = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700503 for(i = 0; i < ARRAY_SIZE(chan_table); i++){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 entry = &chan_table[i];
505 if(!strncmp(str, entry->key, strlen(entry->key))){
506 ops = entry->ops;
507 str += strlen(entry->key);
508 break;
509 }
510 }
511 if(ops == NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800512 *error_out = "No match for configured backends";
Jeff Diked50084a2006-01-06 00:18:50 -0800513 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800515
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 data = (*ops->init)(str, device, opts);
Jeff Dikef28169d2007-02-10 01:43:53 -0800517 if(data == NULL){
518 *error_out = "Configuration failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800519 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800520 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
Paolo 'Blaisorblade' Giarrusso79ae2cb2005-09-22 21:44:21 -0700522 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
Jeff Dikef28169d2007-02-10 01:43:53 -0800523 if(chan == NULL){
524 *error_out = "Memory allocation failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800525 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800526 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
Jeff Dike165dc592006-01-06 00:18:57 -0800528 .free_list =
529 LIST_HEAD_INIT(chan->free_list),
530 .line = line,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 .primary = 1,
532 .input = 0,
533 .output = 0,
534 .opened = 0,
Jeff Dike165dc592006-01-06 00:18:57 -0800535 .enabled = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 .fd = -1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 .ops = ops,
538 .data = data });
Jeff Diked50084a2006-01-06 00:18:50 -0800539 return chan;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540}
541
Jeff Dike165dc592006-01-06 00:18:57 -0800542int parse_chan_pair(char *str, struct line *line, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800543 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544{
Jeff Dike165dc592006-01-06 00:18:57 -0800545 struct list_head *chans = &line->chan_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 struct chan *new, *chan;
547 char *in, *out;
548
549 if(!list_empty(chans)){
550 chan = list_entry(chans->next, struct chan, list);
Jeff Dike165dc592006-01-06 00:18:57 -0800551 free_chan(chans, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 INIT_LIST_HEAD(chans);
553 }
554
555 out = strchr(str, ',');
556 if(out != NULL){
557 in = str;
558 *out = '\0';
559 out++;
Jeff Dikef28169d2007-02-10 01:43:53 -0800560 new = parse_chan(line, in, device, opts, error_out);
Jeff Diked50084a2006-01-06 00:18:50 -0800561 if(new == NULL)
562 return -1;
563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 new->input = 1;
565 list_add(&new->list, chans);
566
Jeff Dikef28169d2007-02-10 01:43:53 -0800567 new = parse_chan(line, out, device, opts, error_out);
Jeff Diked50084a2006-01-06 00:18:50 -0800568 if(new == NULL)
569 return -1;
570
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 list_add(&new->list, chans);
572 new->output = 1;
573 }
574 else {
Jeff Dikef28169d2007-02-10 01:43:53 -0800575 new = parse_chan(line, str, device, opts, error_out);
Jeff Diked50084a2006-01-06 00:18:50 -0800576 if(new == NULL)
577 return -1;
578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 list_add(&new->list, chans);
580 new->input = 1;
581 new->output = 1;
582 }
Jeff Diked50084a2006-01-06 00:18:50 -0800583 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584}
585
586int chan_out_fd(struct list_head *chans)
587{
588 struct list_head *ele;
589 struct chan *chan;
590
591 list_for_each(ele, chans){
592 chan = list_entry(ele, struct chan, list);
593 if(chan->primary && chan->output)
Jeff Diked50084a2006-01-06 00:18:50 -0800594 return chan->fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 }
Jeff Diked50084a2006-01-06 00:18:50 -0800596 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597}
598
David Howells6d5aefb2006-12-05 19:36:26 +0000599void chan_interrupt(struct list_head *chans, struct delayed_work *task,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 struct tty_struct *tty, int irq)
601{
602 struct list_head *ele, *next;
603 struct chan *chan;
604 int err;
605 char c;
606
607 list_for_each_safe(ele, next, chans){
608 chan = list_entry(ele, struct chan, list);
609 if(!chan->input || (chan->ops->read == NULL)) continue;
610 do {
Alan Cox33f0f882006-01-09 20:54:13 -0800611 if (tty && !tty_buffer_request_room(tty, 1)) {
Jeff Dike9159c9d2006-01-06 00:18:58 -0800612 schedule_delayed_work(task, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 goto out;
614 }
615 err = chan->ops->read(chan->fd, &c, chan->data);
616 if(err > 0)
617 tty_receive_char(tty, c);
618 } while(err > 0);
619
620 if(err == 0) reactivate_fd(chan->fd, irq);
621 if(err == -EIO){
622 if(chan->primary){
623 if(tty != NULL)
624 tty_hangup(tty);
Jeff Dike165dc592006-01-06 00:18:57 -0800625 close_chan(chans, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 return;
627 }
Jeff Dike165dc592006-01-06 00:18:57 -0800628 else close_one_chan(chan, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 }
630 }
631 out:
632 if(tty) tty_flip_buffer_push(tty);
633}