blob: 629b00e3b0b0c36f39800836d8525afd14456999 [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
92void generic_close(int fd, void *unused)
93{
94 os_close_file(fd);
95}
96
97int generic_read(int fd, char *c_out, void *unused)
98{
99 int n;
100
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700101 n = os_read_file(fd, c_out, sizeof(*c_out));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
103 if(n == -EAGAIN)
Jeff Diked50084a2006-01-06 00:18:50 -0800104 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 else if(n == 0)
Jeff Diked50084a2006-01-06 00:18:50 -0800106 return -EIO;
107 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108}
109
110/* XXX Trivial wrapper around os_write_file */
111
112int generic_write(int fd, const char *buf, int n, void *unused)
113{
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700114 return os_write_file(fd, buf, n);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115}
116
117int generic_window_size(int fd, void *unused, unsigned short *rows_out,
118 unsigned short *cols_out)
119{
120 int rows, cols;
121 int ret;
122
123 ret = os_window_size(fd, &rows, &cols);
124 if(ret < 0)
Jeff Diked50084a2006-01-06 00:18:50 -0800125 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 ret = ((*rows_out != rows) || (*cols_out != cols));
128
129 *rows_out = rows;
130 *cols_out = cols;
131
Jeff Diked50084a2006-01-06 00:18:50 -0800132 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133}
134
135void generic_free(void *data)
136{
137 kfree(data);
138}
139
140static void tty_receive_char(struct tty_struct *tty, char ch)
141{
142 if(tty == NULL) return;
143
144 if(I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
145 if(ch == STOP_CHAR(tty)){
146 stop_tty(tty);
147 return;
148 }
149 else if(ch == START_CHAR(tty)){
150 start_tty(tty);
151 return;
152 }
153 }
154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 tty_insert_flip_char(tty, ch, TTY_NORMAL);
156}
157
Jeff Diked50084a2006-01-06 00:18:50 -0800158static int open_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159{
Jeff Dike6676ae62007-07-31 00:37:44 -0700160 int fd, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
Jeff Diked50084a2006-01-06 00:18:50 -0800162 if(chan->opened)
163 return 0;
164
165 if(chan->ops->open == NULL)
166 fd = 0;
167 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
168 chan->data, &chan->dev);
169 if(fd < 0)
170 return fd;
Jeff Dike6676ae62007-07-31 00:37:44 -0700171
172 err = os_set_fd_block(fd, 0);
173 if (err) {
174 (*chan->ops->close)(fd, chan->data);
175 return err;
176 }
177
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 chan->fd = fd;
179
180 chan->opened = 1;
Jeff Diked50084a2006-01-06 00:18:50 -0800181 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182}
183
184int open_chan(struct list_head *chans)
185{
186 struct list_head *ele;
187 struct chan *chan;
188 int ret, err = 0;
189
190 list_for_each(ele, chans){
191 chan = list_entry(ele, struct chan, list);
Jeff Diked50084a2006-01-06 00:18:50 -0800192 ret = open_one_chan(chan);
193 if(chan->primary)
194 err = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 }
Jeff Diked50084a2006-01-06 00:18:50 -0800196 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197}
198
199void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
200{
201 struct list_head *ele;
202 struct chan *chan;
203
204 list_for_each(ele, chans){
205 chan = list_entry(ele, struct chan, list);
206 if(chan->primary && chan->output && chan->ops->winch){
207 register_winch(chan->fd, tty);
208 return;
209 }
210 }
211}
212
Jeff Diked14ad812007-07-15 23:38:54 -0700213int enable_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214{
215 struct list_head *ele;
216 struct chan *chan;
Jeff Diked14ad812007-07-15 23:38:54 -0700217 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
Jeff Dike165dc592006-01-06 00:18:57 -0800219 list_for_each(ele, &line->chan_list){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 chan = list_entry(ele, struct chan, list);
Jeff Diked14ad812007-07-15 23:38:54 -0700221 err = open_one_chan(chan);
222 if (err) {
223 if (chan->primary)
224 goto out_close;
225
Jeff Dike165dc592006-01-06 00:18:57 -0800226 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700227 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
Jeff Dike165dc592006-01-06 00:18:57 -0800229 if(chan->enabled)
230 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700231 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
232 chan);
233 if (err)
234 goto out_close;
235
Jeff Dike165dc592006-01-06 00:18:57 -0800236 chan->enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 }
Jeff Diked14ad812007-07-15 23:38:54 -0700238
239 return 0;
240
241 out_close:
242 close_chan(&line->chan_list, 0);
243 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244}
245
Jeff Dike190c3e42007-02-10 01:43:55 -0800246/* Items are added in IRQ context, when free_irq can't be called, and
247 * removed in process context, when it can.
248 * This handles interrupt sources which disappear, and which need to
249 * be permanently disabled. This is discovered in IRQ context, but
250 * the freeing of the IRQ must be done later.
251 */
252static DEFINE_SPINLOCK(irqs_to_free_lock);
Jeff Dike165dc592006-01-06 00:18:57 -0800253static LIST_HEAD(irqs_to_free);
254
255void free_irqs(void)
256{
257 struct chan *chan;
Jeff Dike190c3e42007-02-10 01:43:55 -0800258 LIST_HEAD(list);
259 struct list_head *ele;
Jeff Dike30762122007-03-29 01:20:30 -0700260 unsigned long flags;
Jeff Dike165dc592006-01-06 00:18:57 -0800261
Jeff Dike30762122007-03-29 01:20:30 -0700262 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800263 list_splice_init(&irqs_to_free, &list);
Jeff Dike30762122007-03-29 01:20:30 -0700264 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800265
266 list_for_each(ele, &list){
267 chan = list_entry(ele, struct chan, free_list);
Jeff Dike165dc592006-01-06 00:18:57 -0800268
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}
276
277static void close_one_chan(struct chan *chan, int delay_free_irq)
278{
Jeff Dike30762122007-03-29 01:20:30 -0700279 unsigned long flags;
280
Jeff Dike165dc592006-01-06 00:18:57 -0800281 if(!chan->opened)
282 return;
283
284 if(delay_free_irq){
Jeff Dike30762122007-03-29 01:20:30 -0700285 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800286 list_add(&chan->free_list, &irqs_to_free);
Jeff Dike30762122007-03-29 01:20:30 -0700287 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800288 }
289 else {
290 if(chan->input)
291 free_irq(chan->line->driver->read_irq, chan);
292 if(chan->output)
293 free_irq(chan->line->driver->write_irq, chan);
294 chan->enabled = 0;
295 }
296 if(chan->ops->close != NULL)
297 (*chan->ops->close)(chan->fd, chan->data);
298
299 chan->opened = 0;
300 chan->fd = -1;
301}
302
303void close_chan(struct list_head *chans, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
305 struct chan *chan;
306
307 /* Close in reverse order as open in case more than one of them
308 * refers to the same device and they save and restore that device's
309 * state. Then, the first one opened will have the original state,
310 * so it must be the last closed.
311 */
312 list_for_each_entry_reverse(chan, chans, list) {
Jeff Dike165dc592006-01-06 00:18:57 -0800313 close_one_chan(chan, delay_free_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 }
315}
316
Jeff Dikee4dcee82006-01-06 00:18:58 -0800317void deactivate_chan(struct list_head *chans, int irq)
318{
319 struct list_head *ele;
320
321 struct chan *chan;
322 list_for_each(ele, chans) {
323 chan = list_entry(ele, struct chan, list);
324
325 if(chan->enabled && chan->input)
326 deactivate_fd(chan->fd, irq);
327 }
328}
329
330void reactivate_chan(struct list_head *chans, int irq)
331{
332 struct list_head *ele;
333 struct chan *chan;
334
335 list_for_each(ele, chans) {
336 chan = list_entry(ele, struct chan, list);
337
338 if(chan->enabled && chan->input)
339 reactivate_fd(chan->fd, irq);
340 }
341}
342
Jeff Diked50084a2006-01-06 00:18:50 -0800343int write_chan(struct list_head *chans, const char *buf, int len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 int write_irq)
345{
346 struct list_head *ele;
347 struct chan *chan = NULL;
348 int n, ret = 0;
349
350 list_for_each(ele, chans) {
351 chan = list_entry(ele, struct chan, list);
352 if (!chan->output || (chan->ops->write == NULL))
353 continue;
354 n = chan->ops->write(chan->fd, buf, len, chan->data);
355 if (chan->primary) {
356 ret = n;
357 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
358 reactivate_fd(chan->fd, write_irq);
359 }
360 }
Jeff Diked50084a2006-01-06 00:18:50 -0800361 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362}
363
364int console_write_chan(struct list_head *chans, const char *buf, int len)
365{
366 struct list_head *ele;
367 struct chan *chan;
368 int n, ret = 0;
369
370 list_for_each(ele, chans){
371 chan = list_entry(ele, struct chan, list);
372 if(!chan->output || (chan->ops->console_write == NULL))
373 continue;
Paolo 'Blaisorblade' Giarrusso55c033c2005-11-13 16:07:11 -0800374 n = chan->ops->console_write(chan->fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 if(chan->primary) ret = n;
376 }
Jeff Diked50084a2006-01-06 00:18:50 -0800377 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378}
379
Jeff Dikea52f3622007-02-10 01:44:06 -0800380int console_open_chan(struct line *line, struct console *co)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381{
Jeff Dike1f801712006-01-06 00:18:55 -0800382 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383
Jeff Dike1f801712006-01-06 00:18:55 -0800384 err = open_chan(&line->chan_list);
385 if(err)
386 return err;
387
Jeff Dikea52f3622007-02-10 01:44:06 -0800388 printk("Console initialized on /dev/%s%d\n", co->name, co->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 return 0;
390}
391
392int chan_window_size(struct list_head *chans, unsigned short *rows_out,
393 unsigned short *cols_out)
394{
395 struct list_head *ele;
396 struct chan *chan;
397
398 list_for_each(ele, chans){
399 chan = list_entry(ele, struct chan, list);
400 if(chan->primary){
Jeff Diked50084a2006-01-06 00:18:50 -0800401 if(chan->ops->window_size == NULL)
402 return 0;
403 return chan->ops->window_size(chan->fd, chan->data,
404 rows_out, cols_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 }
406 }
Jeff Diked50084a2006-01-06 00:18:50 -0800407 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408}
409
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800410static void free_one_chan(struct chan *chan, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
412 list_del(&chan->list);
Jeff Dike165dc592006-01-06 00:18:57 -0800413
414 close_one_chan(chan, delay_free_irq);
415
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 if(chan->ops->free != NULL)
417 (*chan->ops->free)(chan->data);
Jeff Dike165dc592006-01-06 00:18:57 -0800418
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
420 kfree(chan);
421}
422
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800423static void free_chan(struct list_head *chans, int delay_free_irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424{
425 struct list_head *ele, *next;
426 struct chan *chan;
427
428 list_for_each_safe(ele, next, chans){
429 chan = list_entry(ele, struct chan, list);
Jeff Dike165dc592006-01-06 00:18:57 -0800430 free_one_chan(chan, delay_free_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 }
432}
433
434static int one_chan_config_string(struct chan *chan, char *str, int size,
435 char **error_out)
436{
437 int n = 0;
438
439 if(chan == NULL){
440 CONFIG_CHUNK(str, size, n, "none", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800441 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 }
443
444 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
445
446 if(chan->dev == NULL){
447 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800448 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 }
450
451 CONFIG_CHUNK(str, size, n, ":", 0);
452 CONFIG_CHUNK(str, size, n, chan->dev, 0);
453
Jeff Diked50084a2006-01-06 00:18:50 -0800454 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455}
456
Jeff Diked50084a2006-01-06 00:18:50 -0800457static int chan_pair_config_string(struct chan *in, struct chan *out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 char *str, int size, char **error_out)
459{
460 int n;
461
462 n = one_chan_config_string(in, str, size, error_out);
463 str += n;
464 size -= n;
465
466 if(in == out){
467 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800468 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 }
470
471 CONFIG_CHUNK(str, size, n, ",", 1);
472 n = one_chan_config_string(out, str, size, error_out);
473 str += n;
474 size -= n;
475 CONFIG_CHUNK(str, size, n, "", 1);
476
Jeff Diked50084a2006-01-06 00:18:50 -0800477 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478}
479
Jeff Diked50084a2006-01-06 00:18:50 -0800480int chan_config_string(struct list_head *chans, char *str, int size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 char **error_out)
482{
483 struct list_head *ele;
484 struct chan *chan, *in = NULL, *out = NULL;
485
486 list_for_each(ele, chans){
487 chan = list_entry(ele, struct chan, list);
488 if(!chan->primary)
489 continue;
490 if(chan->input)
491 in = chan;
492 if(chan->output)
493 out = chan;
494 }
495
Jeff Diked50084a2006-01-06 00:18:50 -0800496 return chan_pair_config_string(in, out, str, size, error_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497}
498
499struct chan_type {
500 char *key;
Jeff Dike5e7672e2006-09-27 01:50:33 -0700501 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502};
503
Jeff Dike5e7672e2006-09-27 01:50:33 -0700504static const struct chan_type chan_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 { "fd", &fd_ops },
506
507#ifdef CONFIG_NULL_CHAN
508 { "null", &null_ops },
509#else
510 { "null", &not_configged_ops },
511#endif
512
513#ifdef CONFIG_PORT_CHAN
514 { "port", &port_ops },
515#else
516 { "port", &not_configged_ops },
517#endif
518
519#ifdef CONFIG_PTY_CHAN
520 { "pty", &pty_ops },
521 { "pts", &pts_ops },
522#else
523 { "pty", &not_configged_ops },
524 { "pts", &not_configged_ops },
525#endif
526
527#ifdef CONFIG_TTY_CHAN
528 { "tty", &tty_ops },
529#else
530 { "tty", &not_configged_ops },
531#endif
532
533#ifdef CONFIG_XTERM_CHAN
534 { "xterm", &xterm_ops },
535#else
536 { "xterm", &not_configged_ops },
537#endif
538};
539
Jeff Dike165dc592006-01-06 00:18:57 -0800540static struct chan *parse_chan(struct line *line, char *str, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800541 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542{
Jeff Dike5e7672e2006-09-27 01:50:33 -0700543 const struct chan_type *entry;
544 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 struct chan *chan;
546 void *data;
547 int i;
548
549 ops = NULL;
550 data = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700551 for(i = 0; i < ARRAY_SIZE(chan_table); i++){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 entry = &chan_table[i];
553 if(!strncmp(str, entry->key, strlen(entry->key))){
554 ops = entry->ops;
555 str += strlen(entry->key);
556 break;
557 }
558 }
559 if(ops == NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800560 *error_out = "No match for configured backends";
Jeff Diked50084a2006-01-06 00:18:50 -0800561 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 data = (*ops->init)(str, device, opts);
Jeff Dikef28169d2007-02-10 01:43:53 -0800565 if(data == NULL){
566 *error_out = "Configuration failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800567 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800568 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
Paolo 'Blaisorblade' Giarrusso79ae2cb2005-09-22 21:44:21 -0700570 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
Jeff Dikef28169d2007-02-10 01:43:53 -0800571 if(chan == NULL){
572 *error_out = "Memory allocation failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800573 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800574 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
Jeff Dike165dc592006-01-06 00:18:57 -0800576 .free_list =
577 LIST_HEAD_INIT(chan->free_list),
578 .line = line,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 .primary = 1,
580 .input = 0,
581 .output = 0,
582 .opened = 0,
Jeff Dike165dc592006-01-06 00:18:57 -0800583 .enabled = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 .fd = -1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 .ops = ops,
586 .data = data });
Jeff Diked50084a2006-01-06 00:18:50 -0800587 return chan;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588}
589
Jeff Dike165dc592006-01-06 00:18:57 -0800590int parse_chan_pair(char *str, struct line *line, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800591 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
Jeff Dike165dc592006-01-06 00:18:57 -0800593 struct list_head *chans = &line->chan_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 struct chan *new, *chan;
595 char *in, *out;
596
597 if(!list_empty(chans)){
598 chan = list_entry(chans->next, struct chan, list);
Jeff Dike165dc592006-01-06 00:18:57 -0800599 free_chan(chans, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 INIT_LIST_HEAD(chans);
601 }
602
603 out = strchr(str, ',');
604 if(out != NULL){
605 in = str;
606 *out = '\0';
607 out++;
Jeff Dikef28169d2007-02-10 01:43:53 -0800608 new = parse_chan(line, in, device, opts, error_out);
Jeff Diked50084a2006-01-06 00:18:50 -0800609 if(new == NULL)
610 return -1;
611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 new->input = 1;
613 list_add(&new->list, chans);
614
Jeff Dikef28169d2007-02-10 01:43:53 -0800615 new = parse_chan(line, out, device, opts, error_out);
Jeff Diked50084a2006-01-06 00:18:50 -0800616 if(new == NULL)
617 return -1;
618
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 list_add(&new->list, chans);
620 new->output = 1;
621 }
622 else {
Jeff Dikef28169d2007-02-10 01:43:53 -0800623 new = parse_chan(line, str, device, opts, error_out);
Jeff Diked50084a2006-01-06 00:18:50 -0800624 if(new == NULL)
625 return -1;
626
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 list_add(&new->list, chans);
628 new->input = 1;
629 new->output = 1;
630 }
Jeff Diked50084a2006-01-06 00:18:50 -0800631 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632}
633
634int chan_out_fd(struct list_head *chans)
635{
636 struct list_head *ele;
637 struct chan *chan;
638
639 list_for_each(ele, chans){
640 chan = list_entry(ele, struct chan, list);
641 if(chan->primary && chan->output)
Jeff Diked50084a2006-01-06 00:18:50 -0800642 return chan->fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 }
Jeff Diked50084a2006-01-06 00:18:50 -0800644 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645}
646
David Howells6d5aefb2006-12-05 19:36:26 +0000647void chan_interrupt(struct list_head *chans, struct delayed_work *task,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 struct tty_struct *tty, int irq)
649{
650 struct list_head *ele, *next;
651 struct chan *chan;
652 int err;
653 char c;
654
655 list_for_each_safe(ele, next, chans){
656 chan = list_entry(ele, struct chan, list);
657 if(!chan->input || (chan->ops->read == NULL)) continue;
658 do {
Alan Cox33f0f882006-01-09 20:54:13 -0800659 if (tty && !tty_buffer_request_room(tty, 1)) {
Jeff Dike9159c9d2006-01-06 00:18:58 -0800660 schedule_delayed_work(task, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 goto out;
662 }
663 err = chan->ops->read(chan->fd, &c, chan->data);
664 if(err > 0)
665 tty_receive_char(tty, c);
666 } while(err > 0);
667
668 if(err == 0) reactivate_fd(chan->fd, irq);
669 if(err == -EIO){
670 if(chan->primary){
671 if(tty != NULL)
672 tty_hangup(tty);
Jeff Dike165dc592006-01-06 00:18:57 -0800673 close_chan(chans, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 return;
675 }
Jeff Dike165dc592006-01-06 00:18:57 -0800676 else close_one_chan(chan, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 }
678 }
679 out:
680 if(tty) tty_flip_buffer_push(tty);
681}