chan_kern.c 12.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* 
 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
 * Licensed under the GPL
 */

#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/string.h>
#include <linux/tty_flip.h>
#include <asm/irq.h>
#include "chan_kern.h"
#include "user_util.h"
#include "kern.h"
#include "irq_user.h"
#include "sigio.h"
#include "line.h"
#include "os.h"

22 23 24
/* XXX: could well be moved to somewhere else, if needed. */
static int my_printf(const char * fmt, ...)
	__attribute__ ((format (printf, 1, 2)));
25

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
static int my_printf(const char * fmt, ...)
{
	/* Yes, can be called on atomic context.*/
	char *buf = kmalloc(4096, GFP_ATOMIC);
	va_list args;
	int r;

	if (!buf) {
		/* We print directly fmt.
		 * Yes, yes, yes, feel free to complain. */
		r = strlen(fmt);
	} else {
		va_start(args, fmt);
		r = vsprintf(buf, fmt, args);
		va_end(args);
		fmt = buf;
	}

	if (r)
		r = os_write_file(1, fmt, r);
	return r;

}

#ifdef CONFIG_NOCONFIG_CHAN
/* Despite its name, there's no added trailing newline. */
static int my_puts(const char * buf)
{
	return os_write_file(1, buf, strlen(buf));
}
56

L
Linus Torvalds 已提交
57 58
static void *not_configged_init(char *str, int device, struct chan_opts *opts)
{
59
	my_puts("Using a channel type which is configured out of "
L
Linus Torvalds 已提交
60
	       "UML\n");
J
Jeff Dike 已提交
61
	return NULL;
L
Linus Torvalds 已提交
62 63 64 65 66
}

static int not_configged_open(int input, int output, int primary, void *data,
			      char **dev_out)
{
67
	my_puts("Using a channel type which is configured out of "
L
Linus Torvalds 已提交
68
	       "UML\n");
J
Jeff Dike 已提交
69
	return -ENODEV;
L
Linus Torvalds 已提交
70 71 72 73
}

static void not_configged_close(int fd, void *data)
{
74
	my_puts("Using a channel type which is configured out of "
L
Linus Torvalds 已提交
75 76 77 78 79
	       "UML\n");
}

static int not_configged_read(int fd, char *c_out, void *data)
{
80
	my_puts("Using a channel type which is configured out of "
L
Linus Torvalds 已提交
81
	       "UML\n");
J
Jeff Dike 已提交
82
	return -EIO;
L
Linus Torvalds 已提交
83 84 85 86
}

static int not_configged_write(int fd, const char *buf, int len, void *data)
{
87
	my_puts("Using a channel type which is configured out of "
L
Linus Torvalds 已提交
88
	       "UML\n");
J
Jeff Dike 已提交
89
	return -EIO;
L
Linus Torvalds 已提交
90 91
}

92
static int not_configged_console_write(int fd, const char *buf, int len)
L
Linus Torvalds 已提交
93
{
94
	my_puts("Using a channel type which is configured out of "
L
Linus Torvalds 已提交
95
	       "UML\n");
J
Jeff Dike 已提交
96
	return -EIO;
L
Linus Torvalds 已提交
97 98 99 100 101
}

static int not_configged_window_size(int fd, void *data, unsigned short *rows,
				     unsigned short *cols)
{
102
	my_puts("Using a channel type which is configured out of "
L
Linus Torvalds 已提交
103
	       "UML\n");
J
Jeff Dike 已提交
104
	return -ENODEV;
L
Linus Torvalds 已提交
105 106 107 108
}

static void not_configged_free(void *data)
{
109
	my_puts("Using a channel type which is configured out of "
L
Linus Torvalds 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
	       "UML\n");
}

static struct chan_ops not_configged_ops = {
	.init		= not_configged_init,
	.open		= not_configged_open,
	.close		= not_configged_close,
	.read		= not_configged_read,
	.write		= not_configged_write,
	.console_write	= not_configged_console_write,
	.window_size	= not_configged_window_size,
	.free		= not_configged_free,
	.winch		= 0,
};
#endif /* CONFIG_NOCONFIG_CHAN */

void generic_close(int fd, void *unused)
{
	os_close_file(fd);
}

int generic_read(int fd, char *c_out, void *unused)
{
	int n;

	n = os_read_file(fd, c_out, sizeof(*c_out));

	if(n == -EAGAIN)
J
Jeff Dike 已提交
138
		return 0;
L
Linus Torvalds 已提交
139
	else if(n == 0)
J
Jeff Dike 已提交
140 141
		return -EIO;
	return n;
L
Linus Torvalds 已提交
142 143 144 145 146 147
}

/* XXX Trivial wrapper around os_write_file */

int generic_write(int fd, const char *buf, int n, void *unused)
{
J
Jeff Dike 已提交
148
	return os_write_file(fd, buf, n);
L
Linus Torvalds 已提交
149 150 151 152 153 154 155 156 157 158
}

int generic_window_size(int fd, void *unused, unsigned short *rows_out,
			unsigned short *cols_out)
{
	int rows, cols;
	int ret;

	ret = os_window_size(fd, &rows, &cols);
	if(ret < 0)
J
Jeff Dike 已提交
159
		return ret;
L
Linus Torvalds 已提交
160 161 162 163 164 165

	ret = ((*rows_out != rows) || (*cols_out != cols));

	*rows_out = rows;
	*cols_out = cols;

J
Jeff Dike 已提交
166
	return ret;
L
Linus Torvalds 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
}

void generic_free(void *data)
{
	kfree(data);
}

static void tty_receive_char(struct tty_struct *tty, char ch)
{
	if(tty == NULL) return;

	if(I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
		if(ch == STOP_CHAR(tty)){
			stop_tty(tty);
			return;
		}
		else if(ch == START_CHAR(tty)){
			start_tty(tty);
			return;
		}
	}

J
Jeff Dike 已提交
189
	if((tty->flip.flag_buf_ptr == NULL) ||
L
Linus Torvalds 已提交
190 191 192 193 194
	   (tty->flip.char_buf_ptr == NULL))
		return;
	tty_insert_flip_char(tty, ch, TTY_NORMAL);
}

J
Jeff Dike 已提交
195
static int open_one_chan(struct chan *chan)
L
Linus Torvalds 已提交
196 197 198
{
	int fd;

J
Jeff Dike 已提交
199 200 201 202 203 204 205 206 207
	if(chan->opened)
		return 0;

	if(chan->ops->open == NULL)
		fd = 0;
	else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
				     chan->data, &chan->dev);
	if(fd < 0)
		return fd;
L
Linus Torvalds 已提交
208 209 210
	chan->fd = fd;

	chan->opened = 1;
J
Jeff Dike 已提交
211
	return 0;
L
Linus Torvalds 已提交
212 213 214 215 216 217 218 219 220 221
}

int open_chan(struct list_head *chans)
{
	struct list_head *ele;
	struct chan *chan;
	int ret, err = 0;

	list_for_each(ele, chans){
		chan = list_entry(ele, struct chan, list);
J
Jeff Dike 已提交
222 223 224
		ret = open_one_chan(chan);
		if(chan->primary)
			err = ret;
L
Linus Torvalds 已提交
225
	}
J
Jeff Dike 已提交
226
	return err;
L
Linus Torvalds 已提交
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
}

void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
{
	struct list_head *ele;
	struct chan *chan;

	list_for_each(ele, chans){
		chan = list_entry(ele, struct chan, list);
		if(chan->primary && chan->output && chan->ops->winch){
			register_winch(chan->fd, tty);
			return;
		}
	}
}

void enable_chan(struct list_head *chans, struct tty_struct *tty)
{
	struct list_head *ele;
	struct chan *chan;

	list_for_each(ele, chans){
		chan = list_entry(ele, struct chan, list);
		if(!chan->opened) continue;

		line_setup_irq(chan->fd, chan->input, chan->output, tty);
	}
}

void close_chan(struct list_head *chans)
{
	struct chan *chan;

	/* Close in reverse order as open in case more than one of them
	 * refers to the same device and they save and restore that device's
	 * state.  Then, the first one opened will have the original state,
	 * so it must be the last closed.
	 */
	list_for_each_entry_reverse(chan, chans, list) {
		if(!chan->opened) continue;
		if(chan->ops->close != NULL)
			(*chan->ops->close)(chan->fd, chan->data);
		chan->opened = 0;
		chan->fd = -1;
	}
}

J
Jeff Dike 已提交
274
int write_chan(struct list_head *chans, const char *buf, int len,
L
Linus Torvalds 已提交
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
	       int write_irq)
{
	struct list_head *ele;
	struct chan *chan = NULL;
	int n, ret = 0;

	list_for_each(ele, chans) {
		chan = list_entry(ele, struct chan, list);
		if (!chan->output || (chan->ops->write == NULL))
			continue;
		n = chan->ops->write(chan->fd, buf, len, chan->data);
		if (chan->primary) {
			ret = n;
			if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
				reactivate_fd(chan->fd, write_irq);
		}
	}
J
Jeff Dike 已提交
292
	return ret;
L
Linus Torvalds 已提交
293 294 295 296 297 298 299 300 301 302 303 304
}

int console_write_chan(struct list_head *chans, const char *buf, int len)
{
	struct list_head *ele;
	struct chan *chan;
	int n, ret = 0;

	list_for_each(ele, chans){
		chan = list_entry(ele, struct chan, list);
		if(!chan->output || (chan->ops->console_write == NULL))
			continue;
305
		n = chan->ops->console_write(chan->fd, buf, len);
L
Linus Torvalds 已提交
306 307
		if(chan->primary) ret = n;
	}
J
Jeff Dike 已提交
308
	return ret;
L
Linus Torvalds 已提交
309 310
}

J
Jeff Dike 已提交
311 312
int console_open_chan(struct line *line, struct console *co,
		      struct chan_opts *opts)
L
Linus Torvalds 已提交
313
{
314 315 316 317 318
	int err;

	err = open_chan(&line->chan_list);
	if(err)
		return err;
L
Linus Torvalds 已提交
319 320 321 322 323 324 325 326 327 328 329 330 331 332

	printk("Console initialized on /dev/%s%d\n",co->name,co->index);
	return 0;
}

int chan_window_size(struct list_head *chans, unsigned short *rows_out,
		      unsigned short *cols_out)
{
	struct list_head *ele;
	struct chan *chan;

	list_for_each(ele, chans){
		chan = list_entry(ele, struct chan, list);
		if(chan->primary){
J
Jeff Dike 已提交
333 334 335 336
			if(chan->ops->window_size == NULL)
				return 0;
			return chan->ops->window_size(chan->fd, chan->data,
						      rows_out, cols_out);
L
Linus Torvalds 已提交
337 338
		}
	}
J
Jeff Dike 已提交
339
	return 0;
L
Linus Torvalds 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
}

void free_one_chan(struct chan *chan)
{
	list_del(&chan->list);
	if(chan->ops->free != NULL)
		(*chan->ops->free)(chan->data);
	free_irq_by_fd(chan->fd);
	if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
	kfree(chan);
}

void free_chan(struct list_head *chans)
{
	struct list_head *ele, *next;
	struct chan *chan;

	list_for_each_safe(ele, next, chans){
		chan = list_entry(ele, struct chan, list);
		free_one_chan(chan);
	}
}

static int one_chan_config_string(struct chan *chan, char *str, int size,
				  char **error_out)
{
	int n = 0;

	if(chan == NULL){
		CONFIG_CHUNK(str, size, n, "none", 1);
J
Jeff Dike 已提交
370
		return n;
L
Linus Torvalds 已提交
371 372 373 374 375 376
	}

	CONFIG_CHUNK(str, size, n, chan->ops->type, 0);

	if(chan->dev == NULL){
		CONFIG_CHUNK(str, size, n, "", 1);
J
Jeff Dike 已提交
377
		return n;
L
Linus Torvalds 已提交
378 379 380 381 382
	}

	CONFIG_CHUNK(str, size, n, ":", 0);
	CONFIG_CHUNK(str, size, n, chan->dev, 0);

J
Jeff Dike 已提交
383
	return n;
L
Linus Torvalds 已提交
384 385
}

J
Jeff Dike 已提交
386
static int chan_pair_config_string(struct chan *in, struct chan *out,
L
Linus Torvalds 已提交
387 388 389 390 391 392 393 394 395 396
				   char *str, int size, char **error_out)
{
	int n;

	n = one_chan_config_string(in, str, size, error_out);
	str += n;
	size -= n;

	if(in == out){
		CONFIG_CHUNK(str, size, n, "", 1);
J
Jeff Dike 已提交
397
		return n;
L
Linus Torvalds 已提交
398 399 400 401 402 403 404 405
	}

	CONFIG_CHUNK(str, size, n, ",", 1);
	n = one_chan_config_string(out, str, size, error_out);
	str += n;
	size -= n;
	CONFIG_CHUNK(str, size, n, "", 1);

J
Jeff Dike 已提交
406
	return n;
L
Linus Torvalds 已提交
407 408
}

J
Jeff Dike 已提交
409
int chan_config_string(struct list_head *chans, char *str, int size,
L
Linus Torvalds 已提交
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
		       char **error_out)
{
	struct list_head *ele;
	struct chan *chan, *in = NULL, *out = NULL;

	list_for_each(ele, chans){
		chan = list_entry(ele, struct chan, list);
		if(!chan->primary)
			continue;
		if(chan->input)
			in = chan;
		if(chan->output)
			out = chan;
	}

J
Jeff Dike 已提交
425
	return chan_pair_config_string(in, out, str, size, error_out);
L
Linus Torvalds 已提交
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
}

struct chan_type {
	char *key;
	struct chan_ops *ops;
};

struct chan_type chan_table[] = {
	{ "fd", &fd_ops },

#ifdef CONFIG_NULL_CHAN
	{ "null", &null_ops },
#else
	{ "null", &not_configged_ops },
#endif

#ifdef CONFIG_PORT_CHAN
	{ "port", &port_ops },
#else
	{ "port", &not_configged_ops },
#endif

#ifdef CONFIG_PTY_CHAN
	{ "pty", &pty_ops },
	{ "pts", &pts_ops },
#else
	{ "pty", &not_configged_ops },
	{ "pts", &not_configged_ops },
#endif

#ifdef CONFIG_TTY_CHAN
	{ "tty", &tty_ops },
#else
	{ "tty", &not_configged_ops },
#endif

#ifdef CONFIG_XTERM_CHAN
	{ "xterm", &xterm_ops },
#else
	{ "xterm", &not_configged_ops },
#endif
};

469
static struct chan *parse_chan(char *str, int device, struct chan_opts *opts)
L
Linus Torvalds 已提交
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
{
	struct chan_type *entry;
	struct chan_ops *ops;
	struct chan *chan;
	void *data;
	int i;

	ops = NULL;
	data = NULL;
	for(i = 0; i < sizeof(chan_table)/sizeof(chan_table[0]); i++){
		entry = &chan_table[i];
		if(!strncmp(str, entry->key, strlen(entry->key))){
			ops = entry->ops;
			str += strlen(entry->key);
			break;
		}
	}
	if(ops == NULL){
488
		my_printf("parse_chan couldn't parse \"%s\"\n",
L
Linus Torvalds 已提交
489
		       str);
J
Jeff Dike 已提交
490
		return NULL;
L
Linus Torvalds 已提交
491
	}
J
Jeff Dike 已提交
492 493
	if(ops->init == NULL)
		return NULL;
L
Linus Torvalds 已提交
494
	data = (*ops->init)(str, device, opts);
J
Jeff Dike 已提交
495 496
	if(data == NULL)
		return NULL;
L
Linus Torvalds 已提交
497

498
	chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
J
Jeff Dike 已提交
499 500
	if(chan == NULL)
		return NULL;
L
Linus Torvalds 已提交
501 502 503 504 505 506 507 508
	*chan = ((struct chan) { .list	 	= LIST_HEAD_INIT(chan->list),
				 .primary	= 1,
				 .input		= 0,
				 .output 	= 0,
				 .opened  	= 0,
				 .fd 		= -1,
				 .ops 		= ops,
				 .data 		= data });
J
Jeff Dike 已提交
509
	return chan;
L
Linus Torvalds 已提交
510 511
}

512
int parse_chan_pair(char *str, struct list_head *chans, int device,
L
Linus Torvalds 已提交
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
		    struct chan_opts *opts)
{
	struct chan *new, *chan;
	char *in, *out;

	if(!list_empty(chans)){
		chan = list_entry(chans->next, struct chan, list);
		free_chan(chans);
		INIT_LIST_HEAD(chans);
	}

	out = strchr(str, ',');
	if(out != NULL){
		in = str;
		*out = '\0';
		out++;
529
		new = parse_chan(in, device, opts);
J
Jeff Dike 已提交
530 531 532
		if(new == NULL)
			return -1;

L
Linus Torvalds 已提交
533 534 535
		new->input = 1;
		list_add(&new->list, chans);

536
		new = parse_chan(out, device, opts);
J
Jeff Dike 已提交
537 538 539
		if(new == NULL)
			return -1;

L
Linus Torvalds 已提交
540 541 542 543
		list_add(&new->list, chans);
		new->output = 1;
	}
	else {
544
		new = parse_chan(str, device, opts);
J
Jeff Dike 已提交
545 546 547
		if(new == NULL)
			return -1;

L
Linus Torvalds 已提交
548 549 550 551
		list_add(&new->list, chans);
		new->input = 1;
		new->output = 1;
	}
J
Jeff Dike 已提交
552
	return 0;
L
Linus Torvalds 已提交
553 554 555 556 557 558 559 560 561 562
}

int chan_out_fd(struct list_head *chans)
{
	struct list_head *ele;
	struct chan *chan;

	list_for_each(ele, chans){
		chan = list_entry(ele, struct chan, list);
		if(chan->primary && chan->output)
J
Jeff Dike 已提交
563
			return chan->fd;
L
Linus Torvalds 已提交
564
	}
J
Jeff Dike 已提交
565
	return -1;
L
Linus Torvalds 已提交
566 567 568 569 570 571 572 573 574 575 576 577 578 579
}

void chan_interrupt(struct list_head *chans, struct work_struct *task,
		    struct tty_struct *tty, int irq)
{
	struct list_head *ele, *next;
	struct chan *chan;
	int err;
	char c;

	list_for_each_safe(ele, next, chans){
		chan = list_entry(ele, struct chan, list);
		if(!chan->input || (chan->ops->read == NULL)) continue;
		do {
J
Jeff Dike 已提交
580
			if((tty != NULL) &&
L
Linus Torvalds 已提交
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
			   (tty->flip.count >= TTY_FLIPBUF_SIZE)){
				schedule_work(task);
				goto out;
			}
			err = chan->ops->read(chan->fd, &c, chan->data);
			if(err > 0)
				tty_receive_char(tty, c);
		} while(err > 0);

		if(err == 0) reactivate_fd(chan->fd, irq);
		if(err == -EIO){
			if(chan->primary){
				if(tty != NULL)
					tty_hangup(tty);
				line_disable(tty, irq);
				close_chan(chans);
				return;
			}
			else {
				if(chan->ops->close != NULL)
					chan->ops->close(chan->fd, chan->data);
			}
		}
	}
 out:
	if(tty) tty_flip_buffer_push(tty);
}

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-file-style: "linux"
 * End:
 */