Merge branch 'upstream' from master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[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                                        void *data)
94 {
95         my_puts("Using a channel type which is configured out of "
96                "UML\n");
97         return(-EIO);
98 }
99
100 static int not_configged_window_size(int fd, void *data, unsigned short *rows,
101                                      unsigned short *cols)
102 {
103         my_puts("Using a channel type which is configured out of "
104                "UML\n");
105         return(-ENODEV);
106 }
107
108 static void not_configged_free(void *data)
109 {
110         my_puts("Using a channel type which is configured out of "
111                "UML\n");
112 }
113
114 static 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
127 void generic_close(int fd, void *unused)
128 {
129         os_close_file(fd);
130 }
131
132 int 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
147 int generic_write(int fd, const char *buf, int n, void *unused)
148 {
149         return(os_write_file(fd, buf, n));
150 }
151
152 int 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
170 void generic_free(void *data)
171 {
172         kfree(data);
173 }
174
175 static 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
196 static 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
211 int 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
226 void 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
240 void 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
253 void 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
271 int 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
292 int 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
308 int 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
322 int 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
339 void 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
349 void 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
360 static 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
383 static 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
406 int 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
425 struct chan_type {
426         char *key;
427         struct chan_ops *ops;
428 };
429
430 struct 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
466 static 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){
486                 my_printf("parse_chan couldn't parse \"%s\"\n",
487                        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
494         chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
495         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
508 int 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
546 int 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
559 void 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  */