um: get rid of pointless include "..." where include <...> will do
[linux-2.6-block.git] / arch / um / drivers / chan_kern.c
CommitLineData
165dc591 1/*
e99525f9 2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
1da177e4
LT
3 * Licensed under the GPL
4 */
5
1da177e4
LT
6#include <linux/slab.h>
7#include <linux/tty.h>
1da177e4 8#include <linux/tty_flip.h>
510c72a3 9#include "chan.h"
37185b33
AV
10#include <os.h>
11#include <irq_kern.h>
1da177e4 12
fac97ae0 13#ifdef CONFIG_NOCONFIG_CHAN
f28169d2
JD
14static void *not_configged_init(char *str, int device,
15 const struct chan_opts *opts)
fac97ae0 16{
e99525f9 17 printk(KERN_ERR "Using a channel type which is configured out of "
1da177e4 18 "UML\n");
d50084a2 19 return NULL;
1da177e4
LT
20}
21
22static int not_configged_open(int input, int output, int primary, void *data,
23 char **dev_out)
24{
e99525f9 25 printk(KERN_ERR "Using a channel type which is configured out of "
1da177e4 26 "UML\n");
d50084a2 27 return -ENODEV;
1da177e4
LT
28}
29
30static void not_configged_close(int fd, void *data)
31{
e99525f9 32 printk(KERN_ERR "Using a channel type which is configured out of "
1da177e4
LT
33 "UML\n");
34}
35
36static int not_configged_read(int fd, char *c_out, void *data)
37{
e99525f9 38 printk(KERN_ERR "Using a channel type which is configured out of "
1da177e4 39 "UML\n");
d50084a2 40 return -EIO;
1da177e4
LT
41}
42
43static int not_configged_write(int fd, const char *buf, int len, void *data)
44{
e99525f9 45 printk(KERN_ERR "Using a channel type which is configured out of "
1da177e4 46 "UML\n");
d50084a2 47 return -EIO;
1da177e4
LT
48}
49
55c033c1 50static int not_configged_console_write(int fd, const char *buf, int len)
1da177e4 51{
e99525f9 52 printk(KERN_ERR "Using a channel type which is configured out of "
1da177e4 53 "UML\n");
d50084a2 54 return -EIO;
1da177e4
LT
55}
56
57static int not_configged_window_size(int fd, void *data, unsigned short *rows,
58 unsigned short *cols)
59{
e99525f9 60 printk(KERN_ERR "Using a channel type which is configured out of "
1da177e4 61 "UML\n");
d50084a2 62 return -ENODEV;
1da177e4
LT
63}
64
65static void not_configged_free(void *data)
66{
e99525f9 67 printk(KERN_ERR "Using a channel type which is configured out of "
1da177e4
LT
68 "UML\n");
69}
70
5e7672ec 71static const struct chan_ops not_configged_ops = {
1da177e4
LT
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
1da177e4
LT
84static void tty_receive_char(struct tty_struct *tty, char ch)
85{
e99525f9
JD
86 if (tty == NULL)
87 return;
1da177e4 88
e99525f9
JD
89 if (I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
90 if (ch == STOP_CHAR(tty)) {
1da177e4
LT
91 stop_tty(tty);
92 return;
93 }
e99525f9 94 else if (ch == START_CHAR(tty)) {
1da177e4
LT
95 start_tty(tty);
96 return;
97 }
98 }
99
1da177e4
LT
100 tty_insert_flip_char(tty, ch, TTY_NORMAL);
101}
102
d50084a2 103static int open_one_chan(struct chan *chan)
1da177e4 104{
6676ae62 105 int fd, err;
1da177e4 106
e99525f9 107 if (chan->opened)
d50084a2
JD
108 return 0;
109
e99525f9 110 if (chan->ops->open == NULL)
d50084a2
JD
111 fd = 0;
112 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
113 chan->data, &chan->dev);
e99525f9 114 if (fd < 0)
d50084a2 115 return fd;
6676ae62
JD
116
117 err = os_set_fd_block(fd, 0);
118 if (err) {
119 (*chan->ops->close)(fd, chan->data);
120 return err;
121 }
122
1da177e4
LT
123 chan->fd = fd;
124
125 chan->opened = 1;
d50084a2 126 return 0;
1da177e4
LT
127}
128
3af7cb7b 129static int open_chan(struct list_head *chans)
1da177e4
LT
130{
131 struct list_head *ele;
132 struct chan *chan;
133 int ret, err = 0;
134
e99525f9 135 list_for_each(ele, chans) {
1da177e4 136 chan = list_entry(ele, struct chan, list);
d50084a2 137 ret = open_one_chan(chan);
e99525f9 138 if (chan->primary)
d50084a2 139 err = ret;
1da177e4 140 }
d50084a2 141 return err;
1da177e4
LT
142}
143
bed5e39c 144void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
1da177e4 145{
bed5e39c
AV
146 if (chan && chan->primary && chan->ops->winch)
147 register_winch(chan->fd, tty);
1da177e4
LT
148}
149
0fcd7199
AV
150static void line_timer_cb(struct work_struct *work)
151{
152 struct line *line = container_of(work, struct line, task.work);
6fc58845 153 struct tty_struct *tty = tty_port_tty_get(&line->port);
0fcd7199
AV
154
155 if (!line->throttled)
6fc58845
JS
156 chan_interrupt(line, tty, line->driver->read_irq);
157 tty_kref_put(tty);
0fcd7199
AV
158}
159
d14ad81f 160int enable_chan(struct line *line)
1da177e4
LT
161{
162 struct list_head *ele;
163 struct chan *chan;
d14ad81f 164 int err;
1da177e4 165
0fcd7199
AV
166 INIT_DELAYED_WORK(&line->task, line_timer_cb);
167
e99525f9 168 list_for_each(ele, &line->chan_list) {
1da177e4 169 chan = list_entry(ele, struct chan, list);
d14ad81f
JD
170 err = open_one_chan(chan);
171 if (err) {
172 if (chan->primary)
173 goto out_close;
174
165dc591 175 continue;
d14ad81f 176 }
165dc591 177
e99525f9 178 if (chan->enabled)
165dc591 179 continue;
d14ad81f
JD
180 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
181 chan);
182 if (err)
183 goto out_close;
184
165dc591
JD
185 chan->enabled = 1;
186 }
d14ad81f
JD
187
188 return 0;
189
190 out_close:
10c890c0 191 close_chan(line);
d14ad81f 192 return err;
165dc591
JD
193}
194
190c3e45
JD
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 */
201static DEFINE_SPINLOCK(irqs_to_free_lock);
165dc591
JD
202static LIST_HEAD(irqs_to_free);
203
204void free_irqs(void)
205{
206 struct chan *chan;
190c3e45
JD
207 LIST_HEAD(list);
208 struct list_head *ele;
3076212f 209 unsigned long flags;
190c3e45 210
3076212f 211 spin_lock_irqsave(&irqs_to_free_lock, flags);
190c3e45 212 list_splice_init(&irqs_to_free, &list);
3076212f 213 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
165dc591 214
e99525f9 215 list_for_each(ele, &list) {
190c3e45 216 chan = list_entry(ele, struct chan, free_list);
165dc591 217
47562277 218 if (chan->input && chan->enabled)
fa7a0449 219 um_free_irq(chan->line->driver->read_irq, chan);
47562277 220 if (chan->output && chan->enabled)
fa7a0449 221 um_free_irq(chan->line->driver->write_irq, chan);
165dc591
JD
222 chan->enabled = 0;
223 }
224}
225
226static void close_one_chan(struct chan *chan, int delay_free_irq)
227{
3076212f
JD
228 unsigned long flags;
229
e99525f9 230 if (!chan->opened)
165dc591 231 return;
1da177e4 232
e99525f9 233 if (delay_free_irq) {
3076212f 234 spin_lock_irqsave(&irqs_to_free_lock, flags);
165dc591 235 list_add(&chan->free_list, &irqs_to_free);
3076212f 236 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
165dc591
JD
237 }
238 else {
47562277 239 if (chan->input && chan->enabled)
fa7a0449 240 um_free_irq(chan->line->driver->read_irq, chan);
47562277 241 if (chan->output && chan->enabled)
fa7a0449 242 um_free_irq(chan->line->driver->write_irq, chan);
165dc591 243 chan->enabled = 0;
1da177e4 244 }
e99525f9 245 if (chan->ops->close != NULL)
165dc591
JD
246 (*chan->ops->close)(chan->fd, chan->data);
247
248 chan->opened = 0;
249 chan->fd = -1;
1da177e4
LT
250}
251
10c890c0 252void close_chan(struct line *line)
1da177e4
LT
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 */
10c890c0
AV
261 list_for_each_entry_reverse(chan, &line->chan_list, list) {
262 close_one_chan(chan, 0);
1da177e4
LT
263 }
264}
265
bed5e39c 266void deactivate_chan(struct chan *chan, int irq)
e4dcee80 267{
bed5e39c
AV
268 if (chan && chan->enabled)
269 deactivate_fd(chan->fd, irq);
e4dcee80
JD
270}
271
bed5e39c 272void reactivate_chan(struct chan *chan, int irq)
e4dcee80 273{
bed5e39c
AV
274 if (chan && chan->enabled)
275 reactivate_fd(chan->fd, irq);
e4dcee80
JD
276}
277
bed5e39c 278int write_chan(struct chan *chan, const char *buf, int len,
1da177e4
LT
279 int write_irq)
280{
1da177e4
LT
281 int n, ret = 0;
282
bed5e39c 283 if (len == 0 || !chan || !chan->ops->write)
c59dbcad
JD
284 return 0;
285
bed5e39c
AV
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);
1da177e4 291 }
d50084a2 292 return ret;
1da177e4
LT
293}
294
bed5e39c 295int console_write_chan(struct chan *chan, const char *buf, int len)
1da177e4 296{
1da177e4
LT
297 int n, ret = 0;
298
bed5e39c
AV
299 if (!chan || !chan->ops->console_write)
300 return 0;
e99525f9 301
bed5e39c
AV
302 n = chan->ops->console_write(chan->fd, buf, len);
303 if (chan->primary)
304 ret = n;
d50084a2 305 return ret;
1da177e4
LT
306}
307
a52f362f 308int console_open_chan(struct line *line, struct console *co)
1da177e4 309{
1f80171e
JD
310 int err;
311
312 err = open_chan(&line->chan_list);
e99525f9 313 if (err)
1f80171e 314 return err;
1da177e4 315
e99525f9
JD
316 printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
317 co->index);
1da177e4
LT
318 return 0;
319}
320
bed5e39c 321int chan_window_size(struct line *line, unsigned short *rows_out,
1da177e4
LT
322 unsigned short *cols_out)
323{
1da177e4
LT
324 struct chan *chan;
325
bed5e39c
AV
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);
1da177e4 339 }
d50084a2 340 return 0;
1da177e4
LT
341}
342
772bd0a5 343static void free_one_chan(struct chan *chan)
1da177e4
LT
344{
345 list_del(&chan->list);
165dc591 346
772bd0a5 347 close_one_chan(chan, 0);
165dc591 348
e99525f9 349 if (chan->ops->free != NULL)
1da177e4 350 (*chan->ops->free)(chan->data);
165dc591 351
e99525f9
JD
352 if (chan->primary && chan->output)
353 ignore_sigio_fd(chan->fd);
1da177e4
LT
354 kfree(chan);
355}
356
772bd0a5 357static void free_chan(struct list_head *chans)
1da177e4
LT
358{
359 struct list_head *ele, *next;
360 struct chan *chan;
361
e99525f9 362 list_for_each_safe(ele, next, chans) {
1da177e4 363 chan = list_entry(ele, struct chan, list);
772bd0a5 364 free_one_chan(chan);
1da177e4
LT
365 }
366}
367
368static int one_chan_config_string(struct chan *chan, char *str, int size,
369 char **error_out)
370{
371 int n = 0;
372
e99525f9 373 if (chan == NULL) {
1da177e4 374 CONFIG_CHUNK(str, size, n, "none", 1);
d50084a2 375 return n;
1da177e4
LT
376 }
377
378 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
379
e99525f9 380 if (chan->dev == NULL) {
1da177e4 381 CONFIG_CHUNK(str, size, n, "", 1);
d50084a2 382 return n;
1da177e4
LT
383 }
384
385 CONFIG_CHUNK(str, size, n, ":", 0);
386 CONFIG_CHUNK(str, size, n, chan->dev, 0);
387
d50084a2 388 return n;
1da177e4
LT
389}
390
d50084a2 391static int chan_pair_config_string(struct chan *in, struct chan *out,
1da177e4
LT
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
e99525f9 400 if (in == out) {
1da177e4 401 CONFIG_CHUNK(str, size, n, "", 1);
d50084a2 402 return n;
1da177e4
LT
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
d50084a2 411 return n;
1da177e4
LT
412}
413
bed5e39c 414int chan_config_string(struct line *line, char *str, int size,
1da177e4
LT
415 char **error_out)
416{
bed5e39c 417 struct chan *in = line->chan_in, *out = line->chan_out;
1da177e4 418
bed5e39c
AV
419 if (in && !in->primary)
420 in = NULL;
421 if (out && !out->primary)
422 out = NULL;
1da177e4 423
d50084a2 424 return chan_pair_config_string(in, out, str, size, error_out);
1da177e4
LT
425}
426
427struct chan_type {
428 char *key;
5e7672ec 429 const struct chan_ops *ops;
1da177e4
LT
430};
431
5e7672ec 432static const struct chan_type chan_table[] = {
1da177e4
LT
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
165dc591 468static struct chan *parse_chan(struct line *line, char *str, int device,
f28169d2 469 const struct chan_opts *opts, char **error_out)
1da177e4 470{
5e7672ec
JD
471 const struct chan_type *entry;
472 const struct chan_ops *ops;
1da177e4
LT
473 struct chan *chan;
474 void *data;
475 int i;
476
477 ops = NULL;
478 data = NULL;
e99525f9 479 for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
1da177e4 480 entry = &chan_table[i];
e99525f9 481 if (!strncmp(str, entry->key, strlen(entry->key))) {
1da177e4
LT
482 ops = entry->ops;
483 str += strlen(entry->key);
484 break;
485 }
486 }
e99525f9 487 if (ops == NULL) {
f28169d2 488 *error_out = "No match for configured backends";
d50084a2 489 return NULL;
1da177e4 490 }
f28169d2 491
1da177e4 492 data = (*ops->init)(str, device, opts);
e99525f9 493 if (data == NULL) {
f28169d2 494 *error_out = "Configuration failed";
d50084a2 495 return NULL;
f28169d2 496 }
1da177e4 497
79ae2cb8 498 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
e99525f9 499 if (chan == NULL) {
f28169d2 500 *error_out = "Memory allocation failed";
d50084a2 501 return NULL;
f28169d2 502 }
1da177e4 503 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
165dc591
JD
504 .free_list =
505 LIST_HEAD_INIT(chan->free_list),
506 .line = line,
1da177e4
LT
507 .primary = 1,
508 .input = 0,
509 .output = 0,
510 .opened = 0,
165dc591 511 .enabled = 0,
1da177e4 512 .fd = -1,
1da177e4
LT
513 .ops = ops,
514 .data = data });
d50084a2 515 return chan;
1da177e4
LT
516}
517
165dc591 518int parse_chan_pair(char *str, struct line *line, int device,
f28169d2 519 const struct chan_opts *opts, char **error_out)
1da177e4 520{
165dc591 521 struct list_head *chans = &line->chan_list;
f1c93e49 522 struct chan *new;
1da177e4
LT
523 char *in, *out;
524
e99525f9 525 if (!list_empty(chans)) {
ee485070 526 line->chan_in = line->chan_out = NULL;
772bd0a5 527 free_chan(chans);
1da177e4
LT
528 INIT_LIST_HEAD(chans);
529 }
530
31efcebb
AV
531 if (!str)
532 return 0;
533
1da177e4 534 out = strchr(str, ',');
e99525f9 535 if (out != NULL) {
1da177e4
LT
536 in = str;
537 *out = '\0';
538 out++;
f28169d2 539 new = parse_chan(line, in, device, opts, error_out);
e99525f9 540 if (new == NULL)
d50084a2
JD
541 return -1;
542
1da177e4
LT
543 new->input = 1;
544 list_add(&new->list, chans);
ee485070 545 line->chan_in = new;
1da177e4 546
f28169d2 547 new = parse_chan(line, out, device, opts, error_out);
e99525f9 548 if (new == NULL)
d50084a2
JD
549 return -1;
550
1da177e4
LT
551 list_add(&new->list, chans);
552 new->output = 1;
ee485070 553 line->chan_out = new;
1da177e4
LT
554 }
555 else {
f28169d2 556 new = parse_chan(line, str, device, opts, error_out);
e99525f9 557 if (new == NULL)
d50084a2
JD
558 return -1;
559
1da177e4
LT
560 list_add(&new->list, chans);
561 new->input = 1;
562 new->output = 1;
ee485070 563 line->chan_in = line->chan_out = new;
1da177e4 564 }
d50084a2 565 return 0;
1da177e4
LT
566}
567
0fcd7199 568void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
1da177e4 569{
bed5e39c 570 struct chan *chan = line->chan_in;
1da177e4
LT
571 int err;
572 char c;
573
bed5e39c
AV
574 if (!chan || !chan->ops->read)
575 goto out;
576
577 do {
578 if (tty && !tty_buffer_request_room(tty, 1)) {
0fcd7199 579 schedule_delayed_work(&line->task, 1);
bed5e39c
AV
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);
10c890c0
AV
593 if (line->chan_out != chan)
594 close_one_chan(line->chan_out, 1);
1da177e4 595 }
10c890c0
AV
596 close_one_chan(chan, 1);
597 if (chan->primary)
598 return;
1da177e4
LT
599 }
600 out:
e99525f9
JD
601 if (tty)
602 tty_flip_buffer_push(tty);
1da177e4 603}