]> Pileus Git - ~andy/linux/blob - arch/um/drivers/chan_kern.c
Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[~andy/linux] / arch / um / drivers / chan_kern.c
1 /*
2  * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
3  * Licensed under the GPL
4  */
5
6 #include <linux/slab.h>
7 #include <linux/tty.h>
8 #include <linux/tty_flip.h>
9 #include "chan.h"
10 #include <os.h>
11 #include <irq_kern.h>
12
13 #ifdef CONFIG_NOCONFIG_CHAN
14 static void *not_configged_init(char *str, int device,
15                                 const struct chan_opts *opts)
16 {
17         printk(KERN_ERR "Using a channel type which is configured out of "
18                "UML\n");
19         return NULL;
20 }
21
22 static int not_configged_open(int input, int output, int primary, void *data,
23                               char **dev_out)
24 {
25         printk(KERN_ERR "Using a channel type which is configured out of "
26                "UML\n");
27         return -ENODEV;
28 }
29
30 static void not_configged_close(int fd, void *data)
31 {
32         printk(KERN_ERR "Using a channel type which is configured out of "
33                "UML\n");
34 }
35
36 static int not_configged_read(int fd, char *c_out, void *data)
37 {
38         printk(KERN_ERR "Using a channel type which is configured out of "
39                "UML\n");
40         return -EIO;
41 }
42
43 static int not_configged_write(int fd, const char *buf, int len, void *data)
44 {
45         printk(KERN_ERR "Using a channel type which is configured out of "
46                "UML\n");
47         return -EIO;
48 }
49
50 static int not_configged_console_write(int fd, const char *buf, int len)
51 {
52         printk(KERN_ERR "Using a channel type which is configured out of "
53                "UML\n");
54         return -EIO;
55 }
56
57 static int not_configged_window_size(int fd, void *data, unsigned short *rows,
58                                      unsigned short *cols)
59 {
60         printk(KERN_ERR "Using a channel type which is configured out of "
61                "UML\n");
62         return -ENODEV;
63 }
64
65 static void not_configged_free(void *data)
66 {
67         printk(KERN_ERR "Using a channel type which is configured out of "
68                "UML\n");
69 }
70
71 static const struct chan_ops not_configged_ops = {
72         .init           = not_configged_init,
73         .open           = not_configged_open,
74         .close          = not_configged_close,
75         .read           = not_configged_read,
76         .write          = not_configged_write,
77         .console_write  = not_configged_console_write,
78         .window_size    = not_configged_window_size,
79         .free           = not_configged_free,
80         .winch          = 0,
81 };
82 #endif /* CONFIG_NOCONFIG_CHAN */
83
84 static void tty_receive_char(struct tty_struct *tty, char ch)
85 {
86         if (tty == NULL)
87                 return;
88
89         if (I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
90                 if (ch == STOP_CHAR(tty)) {
91                         stop_tty(tty);
92                         return;
93                 }
94                 else if (ch == START_CHAR(tty)) {
95                         start_tty(tty);
96                         return;
97                 }
98         }
99
100         tty_insert_flip_char(tty, ch, TTY_NORMAL);
101 }
102
103 static int open_one_chan(struct chan *chan)
104 {
105         int fd, err;
106
107         if (chan->opened)
108                 return 0;
109
110         if (chan->ops->open == NULL)
111                 fd = 0;
112         else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
113                                      chan->data, &chan->dev);
114         if (fd < 0)
115                 return fd;
116
117         err = os_set_fd_block(fd, 0);
118         if (err) {
119                 (*chan->ops->close)(fd, chan->data);
120                 return err;
121         }
122
123         chan->fd = fd;
124
125         chan->opened = 1;
126         return 0;
127 }
128
129 static int open_chan(struct list_head *chans)
130 {
131         struct list_head *ele;
132         struct chan *chan;
133         int ret, err = 0;
134
135         list_for_each(ele, chans) {
136                 chan = list_entry(ele, struct chan, list);
137                 ret = open_one_chan(chan);
138                 if (chan->primary)
139                         err = ret;
140         }
141         return err;
142 }
143
144 void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
145 {
146         if (chan && chan->primary && chan->ops->winch)
147                 register_winch(chan->fd, tty);
148 }
149
150 static void line_timer_cb(struct work_struct *work)
151 {
152         struct line *line = container_of(work, struct line, task.work);
153         struct tty_struct *tty = tty_port_tty_get(&line->port);
154
155         if (!line->throttled)
156                 chan_interrupt(line, tty, line->driver->read_irq);
157         tty_kref_put(tty);
158 }
159
160 int enable_chan(struct line *line)
161 {
162         struct list_head *ele;
163         struct chan *chan;
164         int err;
165
166         INIT_DELAYED_WORK(&line->task, line_timer_cb);
167
168         list_for_each(ele, &line->chan_list) {
169                 chan = list_entry(ele, struct chan, list);
170                 err = open_one_chan(chan);
171                 if (err) {
172                         if (chan->primary)
173                                 goto out_close;
174
175                         continue;
176                 }
177
178                 if (chan->enabled)
179                         continue;
180                 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
181                                      chan);
182                 if (err)
183                         goto out_close;
184
185                 chan->enabled = 1;
186         }
187
188         return 0;
189
190  out_close:
191         close_chan(line);
192         return err;
193 }
194
195 /* Items are added in IRQ context, when free_irq can't be called, and
196  * removed in process context, when it can.
197  * This handles interrupt sources which disappear, and which need to
198  * be permanently disabled.  This is discovered in IRQ context, but
199  * the freeing of the IRQ must be done later.
200  */
201 static DEFINE_SPINLOCK(irqs_to_free_lock);
202 static LIST_HEAD(irqs_to_free);
203
204 void free_irqs(void)
205 {
206         struct chan *chan;
207         LIST_HEAD(list);
208         struct list_head *ele;
209         unsigned long flags;
210
211         spin_lock_irqsave(&irqs_to_free_lock, flags);
212         list_splice_init(&irqs_to_free, &list);
213         spin_unlock_irqrestore(&irqs_to_free_lock, flags);
214
215         list_for_each(ele, &list) {
216                 chan = list_entry(ele, struct chan, free_list);
217
218                 if (chan->input && chan->enabled)
219                         um_free_irq(chan->line->driver->read_irq, chan);
220                 if (chan->output && chan->enabled)
221                         um_free_irq(chan->line->driver->write_irq, chan);
222                 chan->enabled = 0;
223         }
224 }
225
226 static void close_one_chan(struct chan *chan, int delay_free_irq)
227 {
228         unsigned long flags;
229
230         if (!chan->opened)
231                 return;
232
233         if (delay_free_irq) {
234                 spin_lock_irqsave(&irqs_to_free_lock, flags);
235                 list_add(&chan->free_list, &irqs_to_free);
236                 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
237         }
238         else {
239                 if (chan->input && chan->enabled)
240                         um_free_irq(chan->line->driver->read_irq, chan);
241                 if (chan->output && chan->enabled)
242                         um_free_irq(chan->line->driver->write_irq, chan);
243                 chan->enabled = 0;
244         }
245         if (chan->ops->close != NULL)
246                 (*chan->ops->close)(chan->fd, chan->data);
247
248         chan->opened = 0;
249         chan->fd = -1;
250 }
251
252 void close_chan(struct line *line)
253 {
254         struct chan *chan;
255
256         /* Close in reverse order as open in case more than one of them
257          * refers to the same device and they save and restore that device's
258          * state.  Then, the first one opened will have the original state,
259          * so it must be the last closed.
260          */
261         list_for_each_entry_reverse(chan, &line->chan_list, list) {
262                 close_one_chan(chan, 0);
263         }
264 }
265
266 void deactivate_chan(struct chan *chan, int irq)
267 {
268         if (chan && chan->enabled)
269                 deactivate_fd(chan->fd, irq);
270 }
271
272 void reactivate_chan(struct chan *chan, int irq)
273 {
274         if (chan && chan->enabled)
275                 reactivate_fd(chan->fd, irq);
276 }
277
278 int write_chan(struct chan *chan, const char *buf, int len,
279                int write_irq)
280 {
281         int n, ret = 0;
282
283         if (len == 0 || !chan || !chan->ops->write)
284                 return 0;
285
286         n = chan->ops->write(chan->fd, buf, len, chan->data);
287         if (chan->primary) {
288                 ret = n;
289                 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
290                         reactivate_fd(chan->fd, write_irq);
291         }
292         return ret;
293 }
294
295 int console_write_chan(struct chan *chan, const char *buf, int len)
296 {
297         int n, ret = 0;
298
299         if (!chan || !chan->ops->console_write)
300                 return 0;
301
302         n = chan->ops->console_write(chan->fd, buf, len);
303         if (chan->primary)
304                 ret = n;
305         return ret;
306 }
307
308 int console_open_chan(struct line *line, struct console *co)
309 {
310         int err;
311
312         err = open_chan(&line->chan_list);
313         if (err)
314                 return err;
315
316         printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
317                co->index);
318         return 0;
319 }
320
321 int chan_window_size(struct line *line, unsigned short *rows_out,
322                       unsigned short *cols_out)
323 {
324         struct chan *chan;
325
326         chan = line->chan_in;
327         if (chan && chan->primary) {
328                 if (chan->ops->window_size == NULL)
329                         return 0;
330                 return chan->ops->window_size(chan->fd, chan->data,
331                                               rows_out, cols_out);
332         }
333         chan = line->chan_out;
334         if (chan && chan->primary) {
335                 if (chan->ops->window_size == NULL)
336                         return 0;
337                 return chan->ops->window_size(chan->fd, chan->data,
338                                               rows_out, cols_out);
339         }
340         return 0;
341 }
342
343 static void free_one_chan(struct chan *chan)
344 {
345         list_del(&chan->list);
346
347         close_one_chan(chan, 0);
348
349         if (chan->ops->free != NULL)
350                 (*chan->ops->free)(chan->data);
351
352         if (chan->primary && chan->output)
353                 ignore_sigio_fd(chan->fd);
354         kfree(chan);
355 }
356
357 static void free_chan(struct list_head *chans)
358 {
359         struct list_head *ele, *next;
360         struct chan *chan;
361
362         list_for_each_safe(ele, next, chans) {
363                 chan = list_entry(ele, struct chan, list);
364                 free_one_chan(chan);
365         }
366 }
367
368 static int one_chan_config_string(struct chan *chan, char *str, int size,
369                                   char **error_out)
370 {
371         int n = 0;
372
373         if (chan == NULL) {
374                 CONFIG_CHUNK(str, size, n, "none", 1);
375                 return n;
376         }
377
378         CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
379
380         if (chan->dev == NULL) {
381                 CONFIG_CHUNK(str, size, n, "", 1);
382                 return n;
383         }
384
385         CONFIG_CHUNK(str, size, n, ":", 0);
386         CONFIG_CHUNK(str, size, n, chan->dev, 0);
387
388         return n;
389 }
390
391 static int chan_pair_config_string(struct chan *in, struct chan *out,
392                                    char *str, int size, char **error_out)
393 {
394         int n;
395
396         n = one_chan_config_string(in, str, size, error_out);
397         str += n;
398         size -= n;
399
400         if (in == out) {
401                 CONFIG_CHUNK(str, size, n, "", 1);
402                 return n;
403         }
404
405         CONFIG_CHUNK(str, size, n, ",", 1);
406         n = one_chan_config_string(out, str, size, error_out);
407         str += n;
408         size -= n;
409         CONFIG_CHUNK(str, size, n, "", 1);
410
411         return n;
412 }
413
414 int chan_config_string(struct line *line, char *str, int size,
415                        char **error_out)
416 {
417         struct chan *in = line->chan_in, *out = line->chan_out;
418
419         if (in && !in->primary)
420                 in = NULL;
421         if (out && !out->primary)
422                 out = NULL;
423
424         return chan_pair_config_string(in, out, str, size, error_out);
425 }
426
427 struct chan_type {
428         char *key;
429         const struct chan_ops *ops;
430 };
431
432 static const struct chan_type chan_table[] = {
433         { "fd", &fd_ops },
434
435 #ifdef CONFIG_NULL_CHAN
436         { "null", &null_ops },
437 #else
438         { "null", &not_configged_ops },
439 #endif
440
441 #ifdef CONFIG_PORT_CHAN
442         { "port", &port_ops },
443 #else
444         { "port", &not_configged_ops },
445 #endif
446
447 #ifdef CONFIG_PTY_CHAN
448         { "pty", &pty_ops },
449         { "pts", &pts_ops },
450 #else
451         { "pty", &not_configged_ops },
452         { "pts", &not_configged_ops },
453 #endif
454
455 #ifdef CONFIG_TTY_CHAN
456         { "tty", &tty_ops },
457 #else
458         { "tty", &not_configged_ops },
459 #endif
460
461 #ifdef CONFIG_XTERM_CHAN
462         { "xterm", &xterm_ops },
463 #else
464         { "xterm", &not_configged_ops },
465 #endif
466 };
467
468 static struct chan *parse_chan(struct line *line, char *str, int device,
469                                const struct chan_opts *opts, char **error_out)
470 {
471         const struct chan_type *entry;
472         const struct chan_ops *ops;
473         struct chan *chan;
474         void *data;
475         int i;
476
477         ops = NULL;
478         data = NULL;
479         for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
480                 entry = &chan_table[i];
481                 if (!strncmp(str, entry->key, strlen(entry->key))) {
482                         ops = entry->ops;
483                         str += strlen(entry->key);
484                         break;
485                 }
486         }
487         if (ops == NULL) {
488                 *error_out = "No match for configured backends";
489                 return NULL;
490         }
491
492         data = (*ops->init)(str, device, opts);
493         if (data == NULL) {
494                 *error_out = "Configuration failed";
495                 return NULL;
496         }
497
498         chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
499         if (chan == NULL) {
500                 *error_out = "Memory allocation failed";
501                 return NULL;
502         }
503         *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
504                                  .free_list     =
505                                         LIST_HEAD_INIT(chan->free_list),
506                                  .line          = line,
507                                  .primary       = 1,
508                                  .input         = 0,
509                                  .output        = 0,
510                                  .opened        = 0,
511                                  .enabled       = 0,
512                                  .fd            = -1,
513                                  .ops           = ops,
514                                  .data          = data });
515         return chan;
516 }
517
518 int parse_chan_pair(char *str, struct line *line, int device,
519                     const struct chan_opts *opts, char **error_out)
520 {
521         struct list_head *chans = &line->chan_list;
522         struct chan *new;
523         char *in, *out;
524
525         if (!list_empty(chans)) {
526                 line->chan_in = line->chan_out = NULL;
527                 free_chan(chans);
528                 INIT_LIST_HEAD(chans);
529         }
530
531         if (!str)
532                 return 0;
533
534         out = strchr(str, ',');
535         if (out != NULL) {
536                 in = str;
537                 *out = '\0';
538                 out++;
539                 new = parse_chan(line, in, device, opts, error_out);
540                 if (new == NULL)
541                         return -1;
542
543                 new->input = 1;
544                 list_add(&new->list, chans);
545                 line->chan_in = new;
546
547                 new = parse_chan(line, out, device, opts, error_out);
548                 if (new == NULL)
549                         return -1;
550
551                 list_add(&new->list, chans);
552                 new->output = 1;
553                 line->chan_out = new;
554         }
555         else {
556                 new = parse_chan(line, str, device, opts, error_out);
557                 if (new == NULL)
558                         return -1;
559
560                 list_add(&new->list, chans);
561                 new->input = 1;
562                 new->output = 1;
563                 line->chan_in = line->chan_out = new;
564         }
565         return 0;
566 }
567
568 void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
569 {
570         struct chan *chan = line->chan_in;
571         int err;
572         char c;
573
574         if (!chan || !chan->ops->read)
575                 goto out;
576
577         do {
578                 if (tty && !tty_buffer_request_room(tty, 1)) {
579                         schedule_delayed_work(&line->task, 1);
580                         goto out;
581                 }
582                 err = chan->ops->read(chan->fd, &c, chan->data);
583                 if (err > 0)
584                         tty_receive_char(tty, c);
585         } while (err > 0);
586
587         if (err == 0)
588                 reactivate_fd(chan->fd, irq);
589         if (err == -EIO) {
590                 if (chan->primary) {
591                         if (tty != NULL)
592                                 tty_hangup(tty);
593                         if (line->chan_out != chan)
594                                 close_one_chan(line->chan_out, 1);
595                 }
596                 close_one_chan(chan, 1);
597                 if (chan->primary)
598                         return;
599         }
600  out:
601         if (tty)
602                 tty_flip_buffer_push(tty);
603 }