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