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