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