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