blob: 14a12d6b3df6e89c695860170ce10145d1556f0d [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * 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
22#ifdef CONFIG_NOCONFIG_CHAN
Jeff Dike84ddaa82005-05-20 13:59:12 -070023
24/* The printk's here are wrong because we are complaining that there is no
25 * output device, but printk is printing to that output device. The user will
26 * never see the error. printf would be better, except it can't run on a
27 * kernel stack because it will overflow it.
28 * Use printk for now since that will avoid crashing.
29 */
30
Linus Torvalds1da177e2005-04-16 15:20:36 -070031static void *not_configged_init(char *str, int device, struct chan_opts *opts)
32{
Jeff Dike84ddaa82005-05-20 13:59:12 -070033 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 "UML\n");
35 return(NULL);
36}
37
38static int not_configged_open(int input, int output, int primary, void *data,
39 char **dev_out)
40{
Jeff Dike84ddaa82005-05-20 13:59:12 -070041 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 "UML\n");
43 return(-ENODEV);
44}
45
46static void not_configged_close(int fd, void *data)
47{
Jeff Dike84ddaa82005-05-20 13:59:12 -070048 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 "UML\n");
50}
51
52static int not_configged_read(int fd, char *c_out, void *data)
53{
Jeff Dike84ddaa82005-05-20 13:59:12 -070054 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 "UML\n");
56 return(-EIO);
57}
58
59static int not_configged_write(int fd, const char *buf, int len, void *data)
60{
Jeff Dike84ddaa82005-05-20 13:59:12 -070061 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 "UML\n");
63 return(-EIO);
64}
65
66static int not_configged_console_write(int fd, const char *buf, int len,
67 void *data)
68{
Jeff Dike84ddaa82005-05-20 13:59:12 -070069 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 "UML\n");
71 return(-EIO);
72}
73
74static int not_configged_window_size(int fd, void *data, unsigned short *rows,
75 unsigned short *cols)
76{
Jeff Dike84ddaa82005-05-20 13:59:12 -070077 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 "UML\n");
79 return(-ENODEV);
80}
81
82static void not_configged_free(void *data)
83{
Paolo 'Blaisorblade' Giarrussob97b77c2005-05-01 08:58:56 -070084 printf(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 "UML\n");
86}
87
88static struct chan_ops not_configged_ops = {
89 .init = not_configged_init,
90 .open = not_configged_open,
91 .close = not_configged_close,
92 .read = not_configged_read,
93 .write = not_configged_write,
94 .console_write = not_configged_console_write,
95 .window_size = not_configged_window_size,
96 .free = not_configged_free,
97 .winch = 0,
98};
99#endif /* CONFIG_NOCONFIG_CHAN */
100
101void generic_close(int fd, void *unused)
102{
103 os_close_file(fd);
104}
105
106int generic_read(int fd, char *c_out, void *unused)
107{
108 int n;
109
110 n = os_read_file(fd, c_out, sizeof(*c_out));
111
112 if(n == -EAGAIN)
113 return(0);
114 else if(n == 0)
115 return(-EIO);
116 return(n);
117}
118
119/* XXX Trivial wrapper around os_write_file */
120
121int generic_write(int fd, const char *buf, int n, void *unused)
122{
123 return(os_write_file(fd, buf, n));
124}
125
126int generic_window_size(int fd, void *unused, unsigned short *rows_out,
127 unsigned short *cols_out)
128{
129 int rows, cols;
130 int ret;
131
132 ret = os_window_size(fd, &rows, &cols);
133 if(ret < 0)
134 return(ret);
135
136 ret = ((*rows_out != rows) || (*cols_out != cols));
137
138 *rows_out = rows;
139 *cols_out = cols;
140
141 return(ret);
142}
143
144void generic_free(void *data)
145{
146 kfree(data);
147}
148
149static void tty_receive_char(struct tty_struct *tty, char ch)
150{
151 if(tty == NULL) return;
152
153 if(I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
154 if(ch == STOP_CHAR(tty)){
155 stop_tty(tty);
156 return;
157 }
158 else if(ch == START_CHAR(tty)){
159 start_tty(tty);
160 return;
161 }
162 }
163
164 if((tty->flip.flag_buf_ptr == NULL) ||
165 (tty->flip.char_buf_ptr == NULL))
166 return;
167 tty_insert_flip_char(tty, ch, TTY_NORMAL);
168}
169
170static int open_one_chan(struct chan *chan, int input, int output, int primary)
171{
172 int fd;
173
174 if(chan->opened) return(0);
175 if(chan->ops->open == NULL) fd = 0;
176 else fd = (*chan->ops->open)(input, output, primary, chan->data,
177 &chan->dev);
178 if(fd < 0) return(fd);
179 chan->fd = fd;
180
181 chan->opened = 1;
182 return(0);
183}
184
185int open_chan(struct list_head *chans)
186{
187 struct list_head *ele;
188 struct chan *chan;
189 int ret, err = 0;
190
191 list_for_each(ele, chans){
192 chan = list_entry(ele, struct chan, list);
193 ret = open_one_chan(chan, chan->input, chan->output,
194 chan->primary);
195 if(chan->primary) err = ret;
196 }
197 return(err);
198}
199
200void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
201{
202 struct list_head *ele;
203 struct chan *chan;
204
205 list_for_each(ele, chans){
206 chan = list_entry(ele, struct chan, list);
207 if(chan->primary && chan->output && chan->ops->winch){
208 register_winch(chan->fd, tty);
209 return;
210 }
211 }
212}
213
214void enable_chan(struct list_head *chans, struct tty_struct *tty)
215{
216 struct list_head *ele;
217 struct chan *chan;
218
219 list_for_each(ele, chans){
220 chan = list_entry(ele, struct chan, list);
221 if(!chan->opened) continue;
222
223 line_setup_irq(chan->fd, chan->input, chan->output, tty);
224 }
225}
226
227void close_chan(struct list_head *chans)
228{
229 struct chan *chan;
230
231 /* Close in reverse order as open in case more than one of them
232 * refers to the same device and they save and restore that device's
233 * state. Then, the first one opened will have the original state,
234 * so it must be the last closed.
235 */
236 list_for_each_entry_reverse(chan, chans, list) {
237 if(!chan->opened) continue;
238 if(chan->ops->close != NULL)
239 (*chan->ops->close)(chan->fd, chan->data);
240 chan->opened = 0;
241 chan->fd = -1;
242 }
243}
244
245int write_chan(struct list_head *chans, const char *buf, int len,
246 int write_irq)
247{
248 struct list_head *ele;
249 struct chan *chan = NULL;
250 int n, ret = 0;
251
252 list_for_each(ele, chans) {
253 chan = list_entry(ele, struct chan, list);
254 if (!chan->output || (chan->ops->write == NULL))
255 continue;
256 n = chan->ops->write(chan->fd, buf, len, chan->data);
257 if (chan->primary) {
258 ret = n;
259 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
260 reactivate_fd(chan->fd, write_irq);
261 }
262 }
263 return(ret);
264}
265
266int console_write_chan(struct list_head *chans, const char *buf, int len)
267{
268 struct list_head *ele;
269 struct chan *chan;
270 int n, ret = 0;
271
272 list_for_each(ele, chans){
273 chan = list_entry(ele, struct chan, list);
274 if(!chan->output || (chan->ops->console_write == NULL))
275 continue;
276 n = chan->ops->console_write(chan->fd, buf, len, chan->data);
277 if(chan->primary) ret = n;
278 }
279 return(ret);
280}
281
282int console_open_chan(struct line *line, struct console *co, struct chan_opts *opts)
283{
284 if (!list_empty(&line->chan_list))
285 return 0;
286
287 if (0 != parse_chan_pair(line->init_str, &line->chan_list,
288 line->init_pri, co->index, opts))
289 return -1;
290 if (0 != open_chan(&line->chan_list))
291 return -1;
292 printk("Console initialized on /dev/%s%d\n",co->name,co->index);
293 return 0;
294}
295
296int chan_window_size(struct list_head *chans, unsigned short *rows_out,
297 unsigned short *cols_out)
298{
299 struct list_head *ele;
300 struct chan *chan;
301
302 list_for_each(ele, chans){
303 chan = list_entry(ele, struct chan, list);
304 if(chan->primary){
305 if(chan->ops->window_size == NULL) return(0);
306 return(chan->ops->window_size(chan->fd, chan->data,
307 rows_out, cols_out));
308 }
309 }
310 return(0);
311}
312
313void free_one_chan(struct chan *chan)
314{
315 list_del(&chan->list);
316 if(chan->ops->free != NULL)
317 (*chan->ops->free)(chan->data);
318 free_irq_by_fd(chan->fd);
319 if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
320 kfree(chan);
321}
322
323void free_chan(struct list_head *chans)
324{
325 struct list_head *ele, *next;
326 struct chan *chan;
327
328 list_for_each_safe(ele, next, chans){
329 chan = list_entry(ele, struct chan, list);
330 free_one_chan(chan);
331 }
332}
333
334static int one_chan_config_string(struct chan *chan, char *str, int size,
335 char **error_out)
336{
337 int n = 0;
338
339 if(chan == NULL){
340 CONFIG_CHUNK(str, size, n, "none", 1);
341 return(n);
342 }
343
344 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
345
346 if(chan->dev == NULL){
347 CONFIG_CHUNK(str, size, n, "", 1);
348 return(n);
349 }
350
351 CONFIG_CHUNK(str, size, n, ":", 0);
352 CONFIG_CHUNK(str, size, n, chan->dev, 0);
353
354 return(n);
355}
356
357static int chan_pair_config_string(struct chan *in, struct chan *out,
358 char *str, int size, char **error_out)
359{
360 int n;
361
362 n = one_chan_config_string(in, str, size, error_out);
363 str += n;
364 size -= n;
365
366 if(in == out){
367 CONFIG_CHUNK(str, size, n, "", 1);
368 return(n);
369 }
370
371 CONFIG_CHUNK(str, size, n, ",", 1);
372 n = one_chan_config_string(out, str, size, error_out);
373 str += n;
374 size -= n;
375 CONFIG_CHUNK(str, size, n, "", 1);
376
377 return(n);
378}
379
380int chan_config_string(struct list_head *chans, char *str, int size,
381 char **error_out)
382{
383 struct list_head *ele;
384 struct chan *chan, *in = NULL, *out = NULL;
385
386 list_for_each(ele, chans){
387 chan = list_entry(ele, struct chan, list);
388 if(!chan->primary)
389 continue;
390 if(chan->input)
391 in = chan;
392 if(chan->output)
393 out = chan;
394 }
395
396 return(chan_pair_config_string(in, out, str, size, error_out));
397}
398
399struct chan_type {
400 char *key;
401 struct chan_ops *ops;
402};
403
404struct chan_type chan_table[] = {
405 { "fd", &fd_ops },
406
407#ifdef CONFIG_NULL_CHAN
408 { "null", &null_ops },
409#else
410 { "null", &not_configged_ops },
411#endif
412
413#ifdef CONFIG_PORT_CHAN
414 { "port", &port_ops },
415#else
416 { "port", &not_configged_ops },
417#endif
418
419#ifdef CONFIG_PTY_CHAN
420 { "pty", &pty_ops },
421 { "pts", &pts_ops },
422#else
423 { "pty", &not_configged_ops },
424 { "pts", &not_configged_ops },
425#endif
426
427#ifdef CONFIG_TTY_CHAN
428 { "tty", &tty_ops },
429#else
430 { "tty", &not_configged_ops },
431#endif
432
433#ifdef CONFIG_XTERM_CHAN
434 { "xterm", &xterm_ops },
435#else
436 { "xterm", &not_configged_ops },
437#endif
438};
439
440static struct chan *parse_chan(char *str, int pri, int device,
441 struct chan_opts *opts)
442{
443 struct chan_type *entry;
444 struct chan_ops *ops;
445 struct chan *chan;
446 void *data;
447 int i;
448
449 ops = NULL;
450 data = NULL;
451 for(i = 0; i < sizeof(chan_table)/sizeof(chan_table[0]); i++){
452 entry = &chan_table[i];
453 if(!strncmp(str, entry->key, strlen(entry->key))){
454 ops = entry->ops;
455 str += strlen(entry->key);
456 break;
457 }
458 }
459 if(ops == NULL){
460 printk(KERN_ERR "parse_chan couldn't parse \"%s\"\n",
461 str);
462 return(NULL);
463 }
464 if(ops->init == NULL) return(NULL);
465 data = (*ops->init)(str, device, opts);
466 if(data == NULL) return(NULL);
467
468 chan = kmalloc(sizeof(*chan), GFP_KERNEL);
469 if(chan == NULL) return(NULL);
470 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
471 .primary = 1,
472 .input = 0,
473 .output = 0,
474 .opened = 0,
475 .fd = -1,
476 .pri = pri,
477 .ops = ops,
478 .data = data });
479 return(chan);
480}
481
482int parse_chan_pair(char *str, struct list_head *chans, int pri, int device,
483 struct chan_opts *opts)
484{
485 struct chan *new, *chan;
486 char *in, *out;
487
488 if(!list_empty(chans)){
489 chan = list_entry(chans->next, struct chan, list);
490 if(chan->pri >= pri) return(0);
491 free_chan(chans);
492 INIT_LIST_HEAD(chans);
493 }
494
495 out = strchr(str, ',');
496 if(out != NULL){
497 in = str;
498 *out = '\0';
499 out++;
500 new = parse_chan(in, pri, device, opts);
501 if(new == NULL) return(-1);
502 new->input = 1;
503 list_add(&new->list, chans);
504
505 new = parse_chan(out, pri, device, opts);
506 if(new == NULL) return(-1);
507 list_add(&new->list, chans);
508 new->output = 1;
509 }
510 else {
511 new = parse_chan(str, pri, device, opts);
512 if(new == NULL) return(-1);
513 list_add(&new->list, chans);
514 new->input = 1;
515 new->output = 1;
516 }
517 return(0);
518}
519
520int chan_out_fd(struct list_head *chans)
521{
522 struct list_head *ele;
523 struct chan *chan;
524
525 list_for_each(ele, chans){
526 chan = list_entry(ele, struct chan, list);
527 if(chan->primary && chan->output)
528 return(chan->fd);
529 }
530 return(-1);
531}
532
533void chan_interrupt(struct list_head *chans, struct work_struct *task,
534 struct tty_struct *tty, int irq)
535{
536 struct list_head *ele, *next;
537 struct chan *chan;
538 int err;
539 char c;
540
541 list_for_each_safe(ele, next, chans){
542 chan = list_entry(ele, struct chan, list);
543 if(!chan->input || (chan->ops->read == NULL)) continue;
544 do {
545 if((tty != NULL) &&
546 (tty->flip.count >= TTY_FLIPBUF_SIZE)){
547 schedule_work(task);
548 goto out;
549 }
550 err = chan->ops->read(chan->fd, &c, chan->data);
551 if(err > 0)
552 tty_receive_char(tty, c);
553 } while(err > 0);
554
555 if(err == 0) reactivate_fd(chan->fd, irq);
556 if(err == -EIO){
557 if(chan->primary){
558 if(tty != NULL)
559 tty_hangup(tty);
560 line_disable(tty, irq);
561 close_chan(chans);
562 free_chan(chans);
563 return;
564 }
565 else {
566 if(chan->ops->close != NULL)
567 chan->ops->close(chan->fd, chan->data);
568 free_one_chan(chan);
569 }
570 }
571 }
572 out:
573 if(tty) tty_flip_buffer_push(tty);
574}
575
576/*
577 * Overrides for Emacs so that we follow Linus's tabbing style.
578 * Emacs will notice this stuff at the end of the file and automatically
579 * adjust the settings for this buffer only. This must remain at the end
580 * of the file.
581 * ---------------------------------------------------------------------------
582 * Local variables:
583 * c-file-style: "linux"
584 * End:
585 */