[PATCH] uml: irq locking fixes
[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         unsigned long flags;
240
241         spin_lock_irqsave(&irqs_to_free_lock, flags);
242         list_splice_init(&irqs_to_free, &list);
243         spin_unlock_irqrestore(&irqs_to_free_lock, flags);
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         unsigned long flags;
259
260         if(!chan->opened)
261                 return;
262
263         if(delay_free_irq){
264                 spin_lock_irqsave(&irqs_to_free_lock, flags);
265                 list_add(&chan->free_list, &irqs_to_free);
266                 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
267         }
268         else {
269                 if(chan->input)
270                         free_irq(chan->line->driver->read_irq, chan);
271                 if(chan->output)
272                         free_irq(chan->line->driver->write_irq, chan);
273                 chan->enabled = 0;
274         }
275         if(chan->ops->close != NULL)
276                 (*chan->ops->close)(chan->fd, chan->data);
277
278         chan->opened = 0;
279         chan->fd = -1;
280 }
281
282 void close_chan(struct list_head *chans, int delay_free_irq)
283 {
284         struct chan *chan;
285
286         /* Close in reverse order as open in case more than one of them
287          * refers to the same device and they save and restore that device's
288          * state.  Then, the first one opened will have the original state,
289          * so it must be the last closed.
290          */
291         list_for_each_entry_reverse(chan, chans, list) {
292                 close_one_chan(chan, delay_free_irq);
293         }
294 }
295
296 void deactivate_chan(struct list_head *chans, int irq)
297 {
298         struct list_head *ele;
299
300         struct chan *chan;
301         list_for_each(ele, chans) {
302                 chan = list_entry(ele, struct chan, list);
303
304                 if(chan->enabled && chan->input)
305                         deactivate_fd(chan->fd, irq);
306         }
307 }
308
309 void reactivate_chan(struct list_head *chans, int irq)
310 {
311         struct list_head *ele;
312         struct chan *chan;
313
314         list_for_each(ele, chans) {
315                 chan = list_entry(ele, struct chan, list);
316
317                 if(chan->enabled && chan->input)
318                         reactivate_fd(chan->fd, irq);
319         }
320 }
321
322 int write_chan(struct list_head *chans, const char *buf, int len,
323                int write_irq)
324 {
325         struct list_head *ele;
326         struct chan *chan = NULL;
327         int n, ret = 0;
328
329         list_for_each(ele, chans) {
330                 chan = list_entry(ele, struct chan, list);
331                 if (!chan->output || (chan->ops->write == NULL))
332                         continue;
333                 n = chan->ops->write(chan->fd, buf, len, chan->data);
334                 if (chan->primary) {
335                         ret = n;
336                         if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
337                                 reactivate_fd(chan->fd, write_irq);
338                 }
339         }
340         return ret;
341 }
342
343 int console_write_chan(struct list_head *chans, const char *buf, int len)
344 {
345         struct list_head *ele;
346         struct chan *chan;
347         int n, ret = 0;
348
349         list_for_each(ele, chans){
350                 chan = list_entry(ele, struct chan, list);
351                 if(!chan->output || (chan->ops->console_write == NULL))
352                         continue;
353                 n = chan->ops->console_write(chan->fd, buf, len);
354                 if(chan->primary) ret = n;
355         }
356         return ret;
357 }
358
359 int console_open_chan(struct line *line, struct console *co)
360 {
361         int err;
362
363         err = open_chan(&line->chan_list);
364         if(err)
365                 return err;
366
367         printk("Console initialized on /dev/%s%d\n", co->name, co->index);
368         return 0;
369 }
370
371 int chan_window_size(struct list_head *chans, unsigned short *rows_out,
372                       unsigned short *cols_out)
373 {
374         struct list_head *ele;
375         struct chan *chan;
376
377         list_for_each(ele, chans){
378                 chan = list_entry(ele, struct chan, list);
379                 if(chan->primary){
380                         if(chan->ops->window_size == NULL)
381                                 return 0;
382                         return chan->ops->window_size(chan->fd, chan->data,
383                                                       rows_out, cols_out);
384                 }
385         }
386         return 0;
387 }
388
389 static void free_one_chan(struct chan *chan, int delay_free_irq)
390 {
391         list_del(&chan->list);
392
393         close_one_chan(chan, delay_free_irq);
394
395         if(chan->ops->free != NULL)
396                 (*chan->ops->free)(chan->data);
397
398         if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
399         kfree(chan);
400 }
401
402 static void free_chan(struct list_head *chans, int delay_free_irq)
403 {
404         struct list_head *ele, *next;
405         struct chan *chan;
406
407         list_for_each_safe(ele, next, chans){
408                 chan = list_entry(ele, struct chan, list);
409                 free_one_chan(chan, delay_free_irq);
410         }
411 }
412
413 static int one_chan_config_string(struct chan *chan, char *str, int size,
414                                   char **error_out)
415 {
416         int n = 0;
417
418         if(chan == NULL){
419                 CONFIG_CHUNK(str, size, n, "none", 1);
420                 return n;
421         }
422
423         CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
424
425         if(chan->dev == NULL){
426                 CONFIG_CHUNK(str, size, n, "", 1);
427                 return n;
428         }
429
430         CONFIG_CHUNK(str, size, n, ":", 0);
431         CONFIG_CHUNK(str, size, n, chan->dev, 0);
432
433         return n;
434 }
435
436 static int chan_pair_config_string(struct chan *in, struct chan *out,
437                                    char *str, int size, char **error_out)
438 {
439         int n;
440
441         n = one_chan_config_string(in, str, size, error_out);
442         str += n;
443         size -= n;
444
445         if(in == out){
446                 CONFIG_CHUNK(str, size, n, "", 1);
447                 return n;
448         }
449
450         CONFIG_CHUNK(str, size, n, ",", 1);
451         n = one_chan_config_string(out, str, size, error_out);
452         str += n;
453         size -= n;
454         CONFIG_CHUNK(str, size, n, "", 1);
455
456         return n;
457 }
458
459 int chan_config_string(struct list_head *chans, char *str, int size,
460                        char **error_out)
461 {
462         struct list_head *ele;
463         struct chan *chan, *in = NULL, *out = NULL;
464
465         list_for_each(ele, chans){
466                 chan = list_entry(ele, struct chan, list);
467                 if(!chan->primary)
468                         continue;
469                 if(chan->input)
470                         in = chan;
471                 if(chan->output)
472                         out = chan;
473         }
474
475         return chan_pair_config_string(in, out, str, size, error_out);
476 }
477
478 struct chan_type {
479         char *key;
480         const struct chan_ops *ops;
481 };
482
483 static const struct chan_type chan_table[] = {
484         { "fd", &fd_ops },
485
486 #ifdef CONFIG_NULL_CHAN
487         { "null", &null_ops },
488 #else
489         { "null", &not_configged_ops },
490 #endif
491
492 #ifdef CONFIG_PORT_CHAN
493         { "port", &port_ops },
494 #else
495         { "port", &not_configged_ops },
496 #endif
497
498 #ifdef CONFIG_PTY_CHAN
499         { "pty", &pty_ops },
500         { "pts", &pts_ops },
501 #else
502         { "pty", &not_configged_ops },
503         { "pts", &not_configged_ops },
504 #endif
505
506 #ifdef CONFIG_TTY_CHAN
507         { "tty", &tty_ops },
508 #else
509         { "tty", &not_configged_ops },
510 #endif
511
512 #ifdef CONFIG_XTERM_CHAN
513         { "xterm", &xterm_ops },
514 #else
515         { "xterm", &not_configged_ops },
516 #endif
517 };
518
519 static struct chan *parse_chan(struct line *line, char *str, int device,
520                                const struct chan_opts *opts, char **error_out)
521 {
522         const struct chan_type *entry;
523         const struct chan_ops *ops;
524         struct chan *chan;
525         void *data;
526         int i;
527
528         ops = NULL;
529         data = NULL;
530         for(i = 0; i < ARRAY_SIZE(chan_table); i++){
531                 entry = &chan_table[i];
532                 if(!strncmp(str, entry->key, strlen(entry->key))){
533                         ops = entry->ops;
534                         str += strlen(entry->key);
535                         break;
536                 }
537         }
538         if(ops == NULL){
539                 *error_out = "No match for configured backends";
540                 return NULL;
541         }
542
543         data = (*ops->init)(str, device, opts);
544         if(data == NULL){
545                 *error_out = "Configuration failed";
546                 return NULL;
547         }
548
549         chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
550         if(chan == NULL){
551                 *error_out = "Memory allocation failed";
552                 return NULL;
553         }
554         *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
555                                  .free_list     =
556                                         LIST_HEAD_INIT(chan->free_list),
557                                  .line          = line,
558                                  .primary       = 1,
559                                  .input         = 0,
560                                  .output        = 0,
561                                  .opened        = 0,
562                                  .enabled       = 0,
563                                  .fd            = -1,
564                                  .ops           = ops,
565                                  .data          = data });
566         return chan;
567 }
568
569 int parse_chan_pair(char *str, struct line *line, int device,
570                     const struct chan_opts *opts, char **error_out)
571 {
572         struct list_head *chans = &line->chan_list;
573         struct chan *new, *chan;
574         char *in, *out;
575
576         if(!list_empty(chans)){
577                 chan = list_entry(chans->next, struct chan, list);
578                 free_chan(chans, 0);
579                 INIT_LIST_HEAD(chans);
580         }
581
582         out = strchr(str, ',');
583         if(out != NULL){
584                 in = str;
585                 *out = '\0';
586                 out++;
587                 new = parse_chan(line, in, device, opts, error_out);
588                 if(new == NULL)
589                         return -1;
590
591                 new->input = 1;
592                 list_add(&new->list, chans);
593
594                 new = parse_chan(line, out, device, opts, error_out);
595                 if(new == NULL)
596                         return -1;
597
598                 list_add(&new->list, chans);
599                 new->output = 1;
600         }
601         else {
602                 new = parse_chan(line, str, device, opts, error_out);
603                 if(new == NULL)
604                         return -1;
605
606                 list_add(&new->list, chans);
607                 new->input = 1;
608                 new->output = 1;
609         }
610         return 0;
611 }
612
613 int chan_out_fd(struct list_head *chans)
614 {
615         struct list_head *ele;
616         struct chan *chan;
617
618         list_for_each(ele, chans){
619                 chan = list_entry(ele, struct chan, list);
620                 if(chan->primary && chan->output)
621                         return chan->fd;
622         }
623         return -1;
624 }
625
626 void chan_interrupt(struct list_head *chans, struct delayed_work *task,
627                     struct tty_struct *tty, int irq)
628 {
629         struct list_head *ele, *next;
630         struct chan *chan;
631         int err;
632         char c;
633
634         list_for_each_safe(ele, next, chans){
635                 chan = list_entry(ele, struct chan, list);
636                 if(!chan->input || (chan->ops->read == NULL)) continue;
637                 do {
638                         if (tty && !tty_buffer_request_room(tty, 1)) {
639                                 schedule_delayed_work(task, 1);
640                                 goto out;
641                         }
642                         err = chan->ops->read(chan->fd, &c, chan->data);
643                         if(err > 0)
644                                 tty_receive_char(tty, c);
645                 } while(err > 0);
646
647                 if(err == 0) reactivate_fd(chan->fd, irq);
648                 if(err == -EIO){
649                         if(chan->primary){
650                                 if(tty != NULL)
651                                         tty_hangup(tty);
652                                 close_chan(chans, 1);
653                                 return;
654                         }
655                         else close_one_chan(chan, 1);
656                 }
657         }
658  out:
659         if(tty) tty_flip_buffer_push(tty);
660 }