Merge branch 'for-linus' of git://git.linaro.org/people/rmk/linux-arm
[deliverable/linux.git] / 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 }
This page took 0.065488 seconds and 5 git commands to generate.