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