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