blob: 9fdfad649536fbac0c676f84cf46d2bc928485a9 [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"
15#include "user_util.h"
16#include "kern.h"
17#include "irq_user.h"
18#include "sigio.h"
19#include "line.h"
20#include "os.h"
21
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070022#ifdef CONFIG_NOCONFIG_CHAN
Jeff Dikef28169d2007-02-10 01:43:53 -080023static void *not_configged_init(char *str, int device,
24 const struct chan_opts *opts)
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070025{
Jeff Dikef28169d2007-02-10 01:43:53 -080026 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070027 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080028 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070029}
30
31static int not_configged_open(int input, int output, int primary, void *data,
32 char **dev_out)
33{
Jeff Dikef28169d2007-02-10 01:43:53 -080034 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080036 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070037}
38
39static void not_configged_close(int fd, void *data)
40{
Jeff Dikef28169d2007-02-10 01:43:53 -080041 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 "UML\n");
43}
44
45static int not_configged_read(int fd, char *c_out, void *data)
46{
Jeff Dikef28169d2007-02-10 01:43:53 -080047 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080049 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070050}
51
52static int not_configged_write(int fd, const char *buf, int len, void *data)
53{
Jeff Dikef28169d2007-02-10 01:43:53 -080054 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080056 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057}
58
Paolo 'Blaisorblade' Giarrusso55c033c2005-11-13 16:07:11 -080059static int not_configged_console_write(int fd, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060{
Jeff Dikef28169d2007-02-10 01:43:53 -080061 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080063 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070064}
65
66static int not_configged_window_size(int fd, void *data, unsigned short *rows,
67 unsigned short *cols)
68{
Jeff Dikef28169d2007-02-10 01:43:53 -080069 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080071 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070072}
73
74static void not_configged_free(void *data)
75{
Jeff Dikef28169d2007-02-10 01:43:53 -080076 printk("Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 "UML\n");
78}
79
Jeff Dike5e7672e2006-09-27 01:50:33 -070080static const struct chan_ops not_configged_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 .init = not_configged_init,
82 .open = not_configged_open,
83 .close = not_configged_close,
84 .read = not_configged_read,
85 .write = not_configged_write,
86 .console_write = not_configged_console_write,
87 .window_size = not_configged_window_size,
88 .free = not_configged_free,
89 .winch = 0,
90};
91#endif /* CONFIG_NOCONFIG_CHAN */
92
93void generic_close(int fd, void *unused)
94{
95 os_close_file(fd);
96}
97
98int generic_read(int fd, char *c_out, void *unused)
99{
100 int n;
101
102 n = os_read_file(fd, c_out, sizeof(*c_out));
103
104 if(n == -EAGAIN)
Jeff Diked50084a2006-01-06 00:18:50 -0800105 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 else if(n == 0)
Jeff Diked50084a2006-01-06 00:18:50 -0800107 return -EIO;
108 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109}
110
111/* XXX Trivial wrapper around os_write_file */
112
113int generic_write(int fd, const char *buf, int n, void *unused)
114{
Jeff Diked50084a2006-01-06 00:18:50 -0800115 return os_write_file(fd, buf, n);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116}
117
118int generic_window_size(int fd, void *unused, unsigned short *rows_out,
119 unsigned short *cols_out)
120{
121 int rows, cols;
122 int ret;
123
124 ret = os_window_size(fd, &rows, &cols);
125 if(ret < 0)
Jeff Diked50084a2006-01-06 00:18:50 -0800126 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 ret = ((*rows_out != rows) || (*cols_out != cols));
129
130 *rows_out = rows;
131 *cols_out = cols;
132
Jeff Diked50084a2006-01-06 00:18:50 -0800133 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134}
135
136void generic_free(void *data)
137{
138 kfree(data);
139}
140
141static void tty_receive_char(struct tty_struct *tty, char ch)
142{
143 if(tty == NULL) return;
144
145 if(I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
146 if(ch == STOP_CHAR(tty)){
147 stop_tty(tty);
148 return;
149 }
150 else if(ch == START_CHAR(tty)){
151 start_tty(tty);
152 return;
153 }
154 }
155
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 tty_insert_flip_char(tty, ch, TTY_NORMAL);
157}
158
Jeff Diked50084a2006-01-06 00:18:50 -0800159static int open_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160{
161 int fd;
162
Jeff Diked50084a2006-01-06 00:18:50 -0800163 if(chan->opened)
164 return 0;
165
166 if(chan->ops->open == NULL)
167 fd = 0;
168 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
169 chan->data, &chan->dev);
170 if(fd < 0)
171 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 chan->fd = fd;
173
174 chan->opened = 1;
Jeff Diked50084a2006-01-06 00:18:50 -0800175 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176}
177
178int open_chan(struct list_head *chans)
179{
180 struct list_head *ele;
181 struct chan *chan;
182 int ret, err = 0;
183
184 list_for_each(ele, chans){
185 chan = list_entry(ele, struct chan, list);
Jeff Diked50084a2006-01-06 00:18:50 -0800186 ret = open_one_chan(chan);
187 if(chan->primary)
188 err = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 }
Jeff Diked50084a2006-01-06 00:18:50 -0800190 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191}
192
193void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
194{
195 struct list_head *ele;
196 struct chan *chan;
197
198 list_for_each(ele, chans){
199 chan = list_entry(ele, struct chan, list);
200 if(chan->primary && chan->output && chan->ops->winch){
201 register_winch(chan->fd, tty);
202 return;
203 }
204 }
205}
206
Jeff Dike165dc592006-01-06 00:18:57 -0800207void enable_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208{
209 struct list_head *ele;
210 struct chan *chan;
211
Jeff Dike165dc592006-01-06 00:18:57 -0800212 list_for_each(ele, &line->chan_list){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 chan = list_entry(ele, struct chan, list);
Jeff Dike165dc592006-01-06 00:18:57 -0800214 if(open_one_chan(chan))
215 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
Jeff Dike165dc592006-01-06 00:18:57 -0800217 if(chan->enabled)
218 continue;
219 line_setup_irq(chan->fd, chan->input, chan->output, line,
220 chan);
221 chan->enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 }
223}
224
Jeff Dike190c3e42007-02-10 01:43:55 -0800225/* Items are added in IRQ context, when free_irq can't be called, and
226 * removed in process context, when it can.
227 * This handles interrupt sources which disappear, and which need to
228 * be permanently disabled. This is discovered in IRQ context, but
229 * the freeing of the IRQ must be done later.
230 */
231static DEFINE_SPINLOCK(irqs_to_free_lock);
Jeff Dike165dc592006-01-06 00:18:57 -0800232static LIST_HEAD(irqs_to_free);
233
234void free_irqs(void)
235{
236 struct chan *chan;
Jeff Dike190c3e42007-02-10 01:43:55 -0800237 LIST_HEAD(list);
238 struct list_head *ele;
Jeff Dike30762122007-03-29 01:20:30 -0700239 unsigned long flags;
Jeff Dike165dc592006-01-06 00:18:57 -0800240
Jeff Dike30762122007-03-29 01:20:30 -0700241 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800242 list_splice_init(&irqs_to_free, &list);
Jeff Dike30762122007-03-29 01:20:30 -0700243 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800244
245 list_for_each(ele, &list){
246 chan = list_entry(ele, struct chan, free_list);
Jeff Dike165dc592006-01-06 00:18:57 -0800247
248 if(chan->input)
249 free_irq(chan->line->driver->read_irq, chan);
250 if(chan->output)
251 free_irq(chan->line->driver->write_irq, chan);
252 chan->enabled = 0;
253 }
254}
255
256static void close_one_chan(struct chan *chan, int delay_free_irq)
257{
Jeff Dike30762122007-03-29 01:20:30 -0700258 unsigned long flags;
259
Jeff Dike165dc592006-01-06 00:18:57 -0800260 if(!chan->opened)
261 return;
262
263 if(delay_free_irq){
Jeff Dike30762122007-03-29 01:20:30 -0700264 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800265 list_add(&chan->free_list, &irqs_to_free);
Jeff Dike30762122007-03-29 01:20:30 -0700266 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800267 }
268 else {
269 if(chan->input)
270 free_irq(chan->line->driver->read_irq, chan);
271 if(chan->output)
272 free_irq(chan->line->driver->write_irq, chan);
273 chan->enabled = 0;
274 }
275 if(chan->ops->close != NULL)
276 (*chan->ops->close)(chan->fd, chan->data);
277
278 chan->opened = 0;
279 chan->fd = -1;
280}
281
282void close_chan(struct list_head *chans, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
284 struct chan *chan;
285
286 /* Close in reverse order as open in case more than one of them
287 * refers to the same device and they save and restore that device's
288 * state. Then, the first one opened will have the original state,
289 * so it must be the last closed.
290 */
291 list_for_each_entry_reverse(chan, chans, list) {
Jeff Dike165dc592006-01-06 00:18:57 -0800292 close_one_chan(chan, delay_free_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 }
294}
295
Jeff Dikee4dcee82006-01-06 00:18:58 -0800296void deactivate_chan(struct list_head *chans, int irq)
297{
298 struct list_head *ele;
299
300 struct chan *chan;
301 list_for_each(ele, chans) {
302 chan = list_entry(ele, struct chan, list);
303
304 if(chan->enabled && chan->input)
305 deactivate_fd(chan->fd, irq);
306 }
307}
308
309void reactivate_chan(struct list_head *chans, int irq)
310{
311 struct list_head *ele;
312 struct chan *chan;
313
314 list_for_each(ele, chans) {
315 chan = list_entry(ele, struct chan, list);
316
317 if(chan->enabled && chan->input)
318 reactivate_fd(chan->fd, irq);
319 }
320}
321
Jeff Diked50084a2006-01-06 00:18:50 -0800322int write_chan(struct list_head *chans, const char *buf, int len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 int write_irq)
324{
325 struct list_head *ele;
326 struct chan *chan = NULL;
327 int n, ret = 0;
328
329 list_for_each(ele, chans) {
330 chan = list_entry(ele, struct chan, list);
331 if (!chan->output || (chan->ops->write == NULL))
332 continue;
333 n = chan->ops->write(chan->fd, buf, len, chan->data);
334 if (chan->primary) {
335 ret = n;
336 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
337 reactivate_fd(chan->fd, write_irq);
338 }
339 }
Jeff Diked50084a2006-01-06 00:18:50 -0800340 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341}
342
343int console_write_chan(struct list_head *chans, const char *buf, int len)
344{
345 struct list_head *ele;
346 struct chan *chan;
347 int n, ret = 0;
348
349 list_for_each(ele, chans){
350 chan = list_entry(ele, struct chan, list);
351 if(!chan->output || (chan->ops->console_write == NULL))
352 continue;
Paolo 'Blaisorblade' Giarrusso55c033c2005-11-13 16:07:11 -0800353 n = chan->ops->console_write(chan->fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 if(chan->primary) ret = n;
355 }
Jeff Diked50084a2006-01-06 00:18:50 -0800356 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357}
358
Jeff Dikea52f3622007-02-10 01:44:06 -0800359int console_open_chan(struct line *line, struct console *co)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360{
Jeff Dike1f801712006-01-06 00:18:55 -0800361 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Jeff Dike1f801712006-01-06 00:18:55 -0800363 err = open_chan(&line->chan_list);
364 if(err)
365 return err;
366
Jeff Dikea52f3622007-02-10 01:44:06 -0800367 printk("Console initialized on /dev/%s%d\n", co->name, co->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 return 0;
369}
370
371int chan_window_size(struct list_head *chans, unsigned short *rows_out,
372 unsigned short *cols_out)
373{
374 struct list_head *ele;
375 struct chan *chan;
376
377 list_for_each(ele, chans){
378 chan = list_entry(ele, struct chan, list);
379 if(chan->primary){
Jeff Diked50084a2006-01-06 00:18:50 -0800380 if(chan->ops->window_size == NULL)
381 return 0;
382 return chan->ops->window_size(chan->fd, chan->data,
383 rows_out, cols_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 }
385 }
Jeff Diked50084a2006-01-06 00:18:50 -0800386 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387}
388
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800389static void free_one_chan(struct chan *chan, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
391 list_del(&chan->list);
Jeff Dike165dc592006-01-06 00:18:57 -0800392
393 close_one_chan(chan, delay_free_irq);
394
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 if(chan->ops->free != NULL)
396 (*chan->ops->free)(chan->data);
Jeff Dike165dc592006-01-06 00:18:57 -0800397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
399 kfree(chan);
400}
401
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800402static void free_chan(struct list_head *chans, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403{
404 struct list_head *ele, *next;
405 struct chan *chan;
406
407 list_for_each_safe(ele, next, chans){
408 chan = list_entry(ele, struct chan, list);
Jeff Dike165dc592006-01-06 00:18:57 -0800409 free_one_chan(chan, delay_free_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 }
411}
412
413static int one_chan_config_string(struct chan *chan, char *str, int size,
414 char **error_out)
415{
416 int n = 0;
417
418 if(chan == NULL){
419 CONFIG_CHUNK(str, size, n, "none", 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, chan->ops->type, 0);
424
425 if(chan->dev == NULL){
426 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800427 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 }
429
430 CONFIG_CHUNK(str, size, n, ":", 0);
431 CONFIG_CHUNK(str, size, n, chan->dev, 0);
432
Jeff Diked50084a2006-01-06 00:18:50 -0800433 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434}
435
Jeff Diked50084a2006-01-06 00:18:50 -0800436static int chan_pair_config_string(struct chan *in, struct chan *out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 char *str, int size, char **error_out)
438{
439 int n;
440
441 n = one_chan_config_string(in, str, size, error_out);
442 str += n;
443 size -= n;
444
445 if(in == out){
446 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800447 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 }
449
450 CONFIG_CHUNK(str, size, n, ",", 1);
451 n = one_chan_config_string(out, str, size, error_out);
452 str += n;
453 size -= n;
454 CONFIG_CHUNK(str, size, n, "", 1);
455
Jeff Diked50084a2006-01-06 00:18:50 -0800456 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457}
458
Jeff Diked50084a2006-01-06 00:18:50 -0800459int chan_config_string(struct list_head *chans, char *str, int size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 char **error_out)
461{
462 struct list_head *ele;
463 struct chan *chan, *in = NULL, *out = NULL;
464
465 list_for_each(ele, chans){
466 chan = list_entry(ele, struct chan, list);
467 if(!chan->primary)
468 continue;
469 if(chan->input)
470 in = chan;
471 if(chan->output)
472 out = chan;
473 }
474
Jeff Diked50084a2006-01-06 00:18:50 -0800475 return chan_pair_config_string(in, out, str, size, error_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476}
477
478struct chan_type {
479 char *key;
Jeff Dike5e7672e2006-09-27 01:50:33 -0700480 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481};
482
Jeff Dike5e7672e2006-09-27 01:50:33 -0700483static const struct chan_type chan_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 { "fd", &fd_ops },
485
486#ifdef CONFIG_NULL_CHAN
487 { "null", &null_ops },
488#else
489 { "null", &not_configged_ops },
490#endif
491
492#ifdef CONFIG_PORT_CHAN
493 { "port", &port_ops },
494#else
495 { "port", &not_configged_ops },
496#endif
497
498#ifdef CONFIG_PTY_CHAN
499 { "pty", &pty_ops },
500 { "pts", &pts_ops },
501#else
502 { "pty", &not_configged_ops },
503 { "pts", &not_configged_ops },
504#endif
505
506#ifdef CONFIG_TTY_CHAN
507 { "tty", &tty_ops },
508#else
509 { "tty", &not_configged_ops },
510#endif
511
512#ifdef CONFIG_XTERM_CHAN
513 { "xterm", &xterm_ops },
514#else
515 { "xterm", &not_configged_ops },
516#endif
517};
518
Jeff Dike165dc592006-01-06 00:18:57 -0800519static struct chan *parse_chan(struct line *line, char *str, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800520 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521{
Jeff Dike5e7672e2006-09-27 01:50:33 -0700522 const struct chan_type *entry;
523 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 struct chan *chan;
525 void *data;
526 int i;
527
528 ops = NULL;
529 data = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700530 for(i = 0; i < ARRAY_SIZE(chan_table); i++){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 entry = &chan_table[i];
532 if(!strncmp(str, entry->key, strlen(entry->key))){
533 ops = entry->ops;
534 str += strlen(entry->key);
535 break;
536 }
537 }
538 if(ops == NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800539 *error_out = "No match for configured backends";
Jeff Diked50084a2006-01-06 00:18:50 -0800540 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800542
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 data = (*ops->init)(str, device, opts);
Jeff Dikef28169d2007-02-10 01:43:53 -0800544 if(data == NULL){
545 *error_out = "Configuration failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800546 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800547 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
Paolo 'Blaisorblade' Giarrusso79ae2cb2005-09-22 21:44:21 -0700549 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
Jeff Dikef28169d2007-02-10 01:43:53 -0800550 if(chan == NULL){
551 *error_out = "Memory allocation failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800552 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800553 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
Jeff Dike165dc592006-01-06 00:18:57 -0800555 .free_list =
556 LIST_HEAD_INIT(chan->free_list),
557 .line = line,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 .primary = 1,
559 .input = 0,
560 .output = 0,
561 .opened = 0,
Jeff Dike165dc592006-01-06 00:18:57 -0800562 .enabled = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 .fd = -1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 .ops = ops,
565 .data = data });
Jeff Diked50084a2006-01-06 00:18:50 -0800566 return chan;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567}
568
Jeff Dike165dc592006-01-06 00:18:57 -0800569int parse_chan_pair(char *str, struct line *line, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800570 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571{
Jeff Dike165dc592006-01-06 00:18:57 -0800572 struct list_head *chans = &line->chan_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 struct chan *new, *chan;
574 char *in, *out;
575
576 if(!list_empty(chans)){
577 chan = list_entry(chans->next, struct chan, list);
Jeff Dike165dc592006-01-06 00:18:57 -0800578 free_chan(chans, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 INIT_LIST_HEAD(chans);
580 }
581
582 out = strchr(str, ',');
583 if(out != NULL){
584 in = str;
585 *out = '\0';
586 out++;
Jeff Dikef28169d2007-02-10 01:43:53 -0800587 new = parse_chan(line, in, device, opts, error_out);
Jeff Diked50084a2006-01-06 00:18:50 -0800588 if(new == NULL)
589 return -1;
590
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 new->input = 1;
592 list_add(&new->list, chans);
593
Jeff Dikef28169d2007-02-10 01:43:53 -0800594 new = parse_chan(line, out, device, opts, error_out);
Jeff Diked50084a2006-01-06 00:18:50 -0800595 if(new == NULL)
596 return -1;
597
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 list_add(&new->list, chans);
599 new->output = 1;
600 }
601 else {
Jeff Dikef28169d2007-02-10 01:43:53 -0800602 new = parse_chan(line, str, device, opts, error_out);
Jeff Diked50084a2006-01-06 00:18:50 -0800603 if(new == NULL)
604 return -1;
605
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 list_add(&new->list, chans);
607 new->input = 1;
608 new->output = 1;
609 }
Jeff Diked50084a2006-01-06 00:18:50 -0800610 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611}
612
613int chan_out_fd(struct list_head *chans)
614{
615 struct list_head *ele;
616 struct chan *chan;
617
618 list_for_each(ele, chans){
619 chan = list_entry(ele, struct chan, list);
620 if(chan->primary && chan->output)
Jeff Diked50084a2006-01-06 00:18:50 -0800621 return chan->fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 }
Jeff Diked50084a2006-01-06 00:18:50 -0800623 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624}
625
David Howells6d5aefb2006-12-05 19:36:26 +0000626void chan_interrupt(struct list_head *chans, struct delayed_work *task,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 struct tty_struct *tty, int irq)
628{
629 struct list_head *ele, *next;
630 struct chan *chan;
631 int err;
632 char c;
633
634 list_for_each_safe(ele, next, chans){
635 chan = list_entry(ele, struct chan, list);
636 if(!chan->input || (chan->ops->read == NULL)) continue;
637 do {
Alan Cox33f0f882006-01-09 20:54:13 -0800638 if (tty && !tty_buffer_request_room(tty, 1)) {
Jeff Dike9159c9d2006-01-06 00:18:58 -0800639 schedule_delayed_work(task, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 goto out;
641 }
642 err = chan->ops->read(chan->fd, &c, chan->data);
643 if(err > 0)
644 tty_receive_char(tty, c);
645 } while(err > 0);
646
647 if(err == 0) reactivate_fd(chan->fd, irq);
648 if(err == -EIO){
649 if(chan->primary){
650 if(tty != NULL)
651 tty_hangup(tty);
Jeff Dike165dc592006-01-06 00:18:57 -0800652 close_chan(chans, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 return;
654 }
Jeff Dike165dc592006-01-06 00:18:57 -0800655 else close_one_chan(chan, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 }
657 }
658 out:
659 if(tty) tty_flip_buffer_push(tty);
660}