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