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