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