]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - arch/um/drivers/chan_kern.c
[PATCH] uml: console whitespace and comment tidying
[mirror_ubuntu-artful-kernel.git] / arch / um / drivers / chan_kern.c
CommitLineData
165dc591 1/*
1da177e4
LT
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
fac97ae0 22#ifdef CONFIG_NOCONFIG_CHAN
f28169d2
JD
23static void *not_configged_init(char *str, int device,
24 const struct chan_opts *opts)
fac97ae0 25{
f28169d2 26 printk("Using a channel type which is configured out of "
1da177e4 27 "UML\n");
d50084a2 28 return NULL;
1da177e4
LT
29}
30
31static int not_configged_open(int input, int output, int primary, void *data,
32 char **dev_out)
33{
f28169d2 34 printk("Using a channel type which is configured out of "
1da177e4 35 "UML\n");
d50084a2 36 return -ENODEV;
1da177e4
LT
37}
38
39static void not_configged_close(int fd, void *data)
40{
f28169d2 41 printk("Using a channel type which is configured out of "
1da177e4
LT
42 "UML\n");
43}
44
45static int not_configged_read(int fd, char *c_out, void *data)
46{
f28169d2 47 printk("Using a channel type which is configured out of "
1da177e4 48 "UML\n");
d50084a2 49 return -EIO;
1da177e4
LT
50}
51
52static int not_configged_write(int fd, const char *buf, int len, void *data)
53{
f28169d2 54 printk("Using a channel type which is configured out of "
1da177e4 55 "UML\n");
d50084a2 56 return -EIO;
1da177e4
LT
57}
58
55c033c1 59static int not_configged_console_write(int fd, const char *buf, int len)
1da177e4 60{
f28169d2 61 printk("Using a channel type which is configured out of "
1da177e4 62 "UML\n");
d50084a2 63 return -EIO;
1da177e4
LT
64}
65
66static int not_configged_window_size(int fd, void *data, unsigned short *rows,
67 unsigned short *cols)
68{
f28169d2 69 printk("Using a channel type which is configured out of "
1da177e4 70 "UML\n");
d50084a2 71 return -ENODEV;
1da177e4
LT
72}
73
74static void not_configged_free(void *data)
75{
f28169d2 76 printk("Using a channel type which is configured out of "
1da177e4
LT
77 "UML\n");
78}
79
5e7672ec 80static const struct chan_ops not_configged_ops = {
1da177e4
LT
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
93void generic_close(int fd, void *unused)
94{
95 os_close_file(fd);
96}
97
98int 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)
d50084a2 105 return 0;
1da177e4 106 else if(n == 0)
d50084a2
JD
107 return -EIO;
108 return n;
1da177e4
LT
109}
110
111/* XXX Trivial wrapper around os_write_file */
112
113int generic_write(int fd, const char *buf, int n, void *unused)
114{
d50084a2 115 return os_write_file(fd, buf, n);
1da177e4
LT
116}
117
118int 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)
d50084a2 126 return ret;
1da177e4
LT
127
128 ret = ((*rows_out != rows) || (*cols_out != cols));
129
130 *rows_out = rows;
131 *cols_out = cols;
132
d50084a2 133 return ret;
1da177e4
LT
134}
135
136void generic_free(void *data)
137{
138 kfree(data);
139}
140
141static 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
1da177e4
LT
156 tty_insert_flip_char(tty, ch, TTY_NORMAL);
157}
158
d50084a2 159static int open_one_chan(struct chan *chan)
1da177e4
LT
160{
161 int fd;
162
d50084a2
JD
163 if(chan->opened)
164 return 0;
165
166 if(chan->ops->open == NULL)
167 fd = 0;
168 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
169 chan->data, &chan->dev);
170 if(fd < 0)
171 return fd;
1da177e4
LT
172 chan->fd = fd;
173
174 chan->opened = 1;
d50084a2 175 return 0;
1da177e4
LT
176}
177
178int open_chan(struct list_head *chans)
179{
180 struct list_head *ele;
181 struct chan *chan;
182 int ret, err = 0;
183
184 list_for_each(ele, chans){
185 chan = list_entry(ele, struct chan, list);
d50084a2
JD
186 ret = open_one_chan(chan);
187 if(chan->primary)
188 err = ret;
1da177e4 189 }
d50084a2 190 return err;
1da177e4
LT
191}
192
193void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
194{
195 struct list_head *ele;
196 struct chan *chan;
197
198 list_for_each(ele, chans){
199 chan = list_entry(ele, struct chan, list);
200 if(chan->primary && chan->output && chan->ops->winch){
201 register_winch(chan->fd, tty);
202 return;
203 }
204 }
205}
206
165dc591 207void enable_chan(struct line *line)
1da177e4
LT
208{
209 struct list_head *ele;
210 struct chan *chan;
211
165dc591 212 list_for_each(ele, &line->chan_list){
1da177e4 213 chan = list_entry(ele, struct chan, list);
165dc591
JD
214 if(open_one_chan(chan))
215 continue;
216
217 if(chan->enabled)
218 continue;
219 line_setup_irq(chan->fd, chan->input, chan->output, line,
220 chan);
221 chan->enabled = 1;
222 }
223}
224
225static LIST_HEAD(irqs_to_free);
226
227void free_irqs(void)
228{
229 struct chan *chan;
230
231 while(!list_empty(&irqs_to_free)){
232 chan = list_entry(irqs_to_free.next, struct chan, free_list);
233 list_del(&chan->free_list);
234
235 if(chan->input)
236 free_irq(chan->line->driver->read_irq, chan);
237 if(chan->output)
238 free_irq(chan->line->driver->write_irq, chan);
239 chan->enabled = 0;
240 }
241}
242
243static void close_one_chan(struct chan *chan, int delay_free_irq)
244{
245 if(!chan->opened)
246 return;
1da177e4 247
165dc591
JD
248 if(delay_free_irq){
249 list_add(&chan->free_list, &irqs_to_free);
250 }
251 else {
252 if(chan->input)
253 free_irq(chan->line->driver->read_irq, chan);
254 if(chan->output)
255 free_irq(chan->line->driver->write_irq, chan);
256 chan->enabled = 0;
1da177e4 257 }
165dc591
JD
258 if(chan->ops->close != NULL)
259 (*chan->ops->close)(chan->fd, chan->data);
260
261 chan->opened = 0;
262 chan->fd = -1;
1da177e4
LT
263}
264
165dc591 265void close_chan(struct list_head *chans, int delay_free_irq)
1da177e4
LT
266{
267 struct chan *chan;
268
269 /* Close in reverse order as open in case more than one of them
270 * refers to the same device and they save and restore that device's
271 * state. Then, the first one opened will have the original state,
272 * so it must be the last closed.
273 */
274 list_for_each_entry_reverse(chan, chans, list) {
165dc591 275 close_one_chan(chan, delay_free_irq);
1da177e4
LT
276 }
277}
278
e4dcee80
JD
279void deactivate_chan(struct list_head *chans, int irq)
280{
281 struct list_head *ele;
282
283 struct chan *chan;
284 list_for_each(ele, chans) {
285 chan = list_entry(ele, struct chan, list);
286
287 if(chan->enabled && chan->input)
288 deactivate_fd(chan->fd, irq);
289 }
290}
291
292void reactivate_chan(struct list_head *chans, int irq)
293{
294 struct list_head *ele;
295 struct chan *chan;
296
297 list_for_each(ele, chans) {
298 chan = list_entry(ele, struct chan, list);
299
300 if(chan->enabled && chan->input)
301 reactivate_fd(chan->fd, irq);
302 }
303}
304
d50084a2 305int write_chan(struct list_head *chans, const char *buf, int len,
1da177e4
LT
306 int write_irq)
307{
308 struct list_head *ele;
309 struct chan *chan = NULL;
310 int n, ret = 0;
311
312 list_for_each(ele, chans) {
313 chan = list_entry(ele, struct chan, list);
314 if (!chan->output || (chan->ops->write == NULL))
315 continue;
316 n = chan->ops->write(chan->fd, buf, len, chan->data);
317 if (chan->primary) {
318 ret = n;
319 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
320 reactivate_fd(chan->fd, write_irq);
321 }
322 }
d50084a2 323 return ret;
1da177e4
LT
324}
325
326int console_write_chan(struct list_head *chans, const char *buf, int len)
327{
328 struct list_head *ele;
329 struct chan *chan;
330 int n, ret = 0;
331
332 list_for_each(ele, chans){
333 chan = list_entry(ele, struct chan, list);
334 if(!chan->output || (chan->ops->console_write == NULL))
335 continue;
55c033c1 336 n = chan->ops->console_write(chan->fd, buf, len);
1da177e4
LT
337 if(chan->primary) ret = n;
338 }
d50084a2 339 return ret;
1da177e4
LT
340}
341
d50084a2 342int console_open_chan(struct line *line, struct console *co,
5e7672ec 343 const struct chan_opts *opts)
1da177e4 344{
1f80171e
JD
345 int err;
346
347 err = open_chan(&line->chan_list);
348 if(err)
349 return err;
1da177e4 350
1da177e4
LT
351 printk("Console initialized on /dev/%s%d\n",co->name,co->index);
352 return 0;
353}
354
355int chan_window_size(struct list_head *chans, unsigned short *rows_out,
356 unsigned short *cols_out)
357{
358 struct list_head *ele;
359 struct chan *chan;
360
361 list_for_each(ele, chans){
362 chan = list_entry(ele, struct chan, list);
363 if(chan->primary){
d50084a2
JD
364 if(chan->ops->window_size == NULL)
365 return 0;
366 return chan->ops->window_size(chan->fd, chan->data,
367 rows_out, cols_out);
1da177e4
LT
368 }
369 }
d50084a2 370 return 0;
1da177e4
LT
371}
372
42947cb9 373static void free_one_chan(struct chan *chan, int delay_free_irq)
1da177e4
LT
374{
375 list_del(&chan->list);
165dc591
JD
376
377 close_one_chan(chan, delay_free_irq);
378
1da177e4
LT
379 if(chan->ops->free != NULL)
380 (*chan->ops->free)(chan->data);
165dc591 381
1da177e4
LT
382 if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
383 kfree(chan);
384}
385
42947cb9 386static void free_chan(struct list_head *chans, int delay_free_irq)
1da177e4
LT
387{
388 struct list_head *ele, *next;
389 struct chan *chan;
390
391 list_for_each_safe(ele, next, chans){
392 chan = list_entry(ele, struct chan, list);
165dc591 393 free_one_chan(chan, delay_free_irq);
1da177e4
LT
394 }
395}
396
397static int one_chan_config_string(struct chan *chan, char *str, int size,
398 char **error_out)
399{
400 int n = 0;
401
402 if(chan == NULL){
403 CONFIG_CHUNK(str, size, n, "none", 1);
d50084a2 404 return n;
1da177e4
LT
405 }
406
407 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
408
409 if(chan->dev == NULL){
410 CONFIG_CHUNK(str, size, n, "", 1);
d50084a2 411 return n;
1da177e4
LT
412 }
413
414 CONFIG_CHUNK(str, size, n, ":", 0);
415 CONFIG_CHUNK(str, size, n, chan->dev, 0);
416
d50084a2 417 return n;
1da177e4
LT
418}
419
d50084a2 420static int chan_pair_config_string(struct chan *in, struct chan *out,
1da177e4
LT
421 char *str, int size, char **error_out)
422{
423 int n;
424
425 n = one_chan_config_string(in, str, size, error_out);
426 str += n;
427 size -= n;
428
429 if(in == out){
430 CONFIG_CHUNK(str, size, n, "", 1);
d50084a2 431 return n;
1da177e4
LT
432 }
433
434 CONFIG_CHUNK(str, size, n, ",", 1);
435 n = one_chan_config_string(out, str, size, error_out);
436 str += n;
437 size -= n;
438 CONFIG_CHUNK(str, size, n, "", 1);
439
d50084a2 440 return n;
1da177e4
LT
441}
442
d50084a2 443int chan_config_string(struct list_head *chans, char *str, int size,
1da177e4
LT
444 char **error_out)
445{
446 struct list_head *ele;
447 struct chan *chan, *in = NULL, *out = NULL;
448
449 list_for_each(ele, chans){
450 chan = list_entry(ele, struct chan, list);
451 if(!chan->primary)
452 continue;
453 if(chan->input)
454 in = chan;
455 if(chan->output)
456 out = chan;
457 }
458
d50084a2 459 return chan_pair_config_string(in, out, str, size, error_out);
1da177e4
LT
460}
461
462struct chan_type {
463 char *key;
5e7672ec 464 const struct chan_ops *ops;
1da177e4
LT
465};
466
5e7672ec 467static const struct chan_type chan_table[] = {
1da177e4
LT
468 { "fd", &fd_ops },
469
470#ifdef CONFIG_NULL_CHAN
471 { "null", &null_ops },
472#else
473 { "null", &not_configged_ops },
474#endif
475
476#ifdef CONFIG_PORT_CHAN
477 { "port", &port_ops },
478#else
479 { "port", &not_configged_ops },
480#endif
481
482#ifdef CONFIG_PTY_CHAN
483 { "pty", &pty_ops },
484 { "pts", &pts_ops },
485#else
486 { "pty", &not_configged_ops },
487 { "pts", &not_configged_ops },
488#endif
489
490#ifdef CONFIG_TTY_CHAN
491 { "tty", &tty_ops },
492#else
493 { "tty", &not_configged_ops },
494#endif
495
496#ifdef CONFIG_XTERM_CHAN
497 { "xterm", &xterm_ops },
498#else
499 { "xterm", &not_configged_ops },
500#endif
501};
502
165dc591 503static struct chan *parse_chan(struct line *line, char *str, int device,
f28169d2 504 const struct chan_opts *opts, char **error_out)
1da177e4 505{
5e7672ec
JD
506 const struct chan_type *entry;
507 const struct chan_ops *ops;
1da177e4
LT
508 struct chan *chan;
509 void *data;
510 int i;
511
512 ops = NULL;
513 data = NULL;
91b165c0 514 for(i = 0; i < ARRAY_SIZE(chan_table); i++){
1da177e4
LT
515 entry = &chan_table[i];
516 if(!strncmp(str, entry->key, strlen(entry->key))){
517 ops = entry->ops;
518 str += strlen(entry->key);
519 break;
520 }
521 }
522 if(ops == NULL){
f28169d2 523 *error_out = "No match for configured backends";
d50084a2 524 return NULL;
1da177e4 525 }
f28169d2 526
1da177e4 527 data = (*ops->init)(str, device, opts);
f28169d2
JD
528 if(data == NULL){
529 *error_out = "Configuration failed";
d50084a2 530 return NULL;
f28169d2 531 }
1da177e4 532
79ae2cb8 533 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
f28169d2
JD
534 if(chan == NULL){
535 *error_out = "Memory allocation failed";
d50084a2 536 return NULL;
f28169d2 537 }
1da177e4 538 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
165dc591
JD
539 .free_list =
540 LIST_HEAD_INIT(chan->free_list),
541 .line = line,
1da177e4
LT
542 .primary = 1,
543 .input = 0,
544 .output = 0,
545 .opened = 0,
165dc591 546 .enabled = 0,
1da177e4 547 .fd = -1,
1da177e4
LT
548 .ops = ops,
549 .data = data });
d50084a2 550 return chan;
1da177e4
LT
551}
552
165dc591 553int parse_chan_pair(char *str, struct line *line, int device,
f28169d2 554 const struct chan_opts *opts, char **error_out)
1da177e4 555{
165dc591 556 struct list_head *chans = &line->chan_list;
1da177e4
LT
557 struct chan *new, *chan;
558 char *in, *out;
559
560 if(!list_empty(chans)){
561 chan = list_entry(chans->next, struct chan, list);
165dc591 562 free_chan(chans, 0);
1da177e4
LT
563 INIT_LIST_HEAD(chans);
564 }
565
566 out = strchr(str, ',');
567 if(out != NULL){
568 in = str;
569 *out = '\0';
570 out++;
f28169d2 571 new = parse_chan(line, in, device, opts, error_out);
d50084a2
JD
572 if(new == NULL)
573 return -1;
574
1da177e4
LT
575 new->input = 1;
576 list_add(&new->list, chans);
577
f28169d2 578 new = parse_chan(line, out, device, opts, error_out);
d50084a2
JD
579 if(new == NULL)
580 return -1;
581
1da177e4
LT
582 list_add(&new->list, chans);
583 new->output = 1;
584 }
585 else {
f28169d2 586 new = parse_chan(line, str, device, opts, error_out);
d50084a2
JD
587 if(new == NULL)
588 return -1;
589
1da177e4
LT
590 list_add(&new->list, chans);
591 new->input = 1;
592 new->output = 1;
593 }
d50084a2 594 return 0;
1da177e4
LT
595}
596
597int chan_out_fd(struct list_head *chans)
598{
599 struct list_head *ele;
600 struct chan *chan;
601
602 list_for_each(ele, chans){
603 chan = list_entry(ele, struct chan, list);
604 if(chan->primary && chan->output)
d50084a2 605 return chan->fd;
1da177e4 606 }
d50084a2 607 return -1;
1da177e4
LT
608}
609
6d5aefb8 610void chan_interrupt(struct list_head *chans, struct delayed_work *task,
1da177e4
LT
611 struct tty_struct *tty, int irq)
612{
613 struct list_head *ele, *next;
614 struct chan *chan;
615 int err;
616 char c;
617
618 list_for_each_safe(ele, next, chans){
619 chan = list_entry(ele, struct chan, list);
620 if(!chan->input || (chan->ops->read == NULL)) continue;
621 do {
33f0f88f 622 if (tty && !tty_buffer_request_room(tty, 1)) {
9159c9df 623 schedule_delayed_work(task, 1);
1da177e4
LT
624 goto out;
625 }
626 err = chan->ops->read(chan->fd, &c, chan->data);
627 if(err > 0)
628 tty_receive_char(tty, c);
629 } while(err > 0);
630
631 if(err == 0) reactivate_fd(chan->fd, irq);
632 if(err == -EIO){
633 if(chan->primary){
634 if(tty != NULL)
635 tty_hangup(tty);
165dc591 636 close_chan(chans, 1);
1da177e4
LT
637 return;
638 }
165dc591 639 else close_one_chan(chan, 1);
1da177e4
LT
640 }
641 }
642 out:
643 if(tty) tty_flip_buffer_push(tty);
644}