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