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