chan_kern.c 13.1 KB
Newer Older
1
/*
L
Linus Torvalds 已提交
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
}

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;
		}
	}
}

243
void enable_chan(struct line *line)
L
Linus Torvalds 已提交
244 245 246 247
{
	struct list_head *ele;
	struct chan *chan;

248
	list_for_each(ele, &line->chan_list){
L
Linus Torvalds 已提交
249
		chan = list_entry(ele, struct chan, list);
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
		if(open_one_chan(chan))
			continue;

		if(chan->enabled)
			continue;
		line_setup_irq(chan->fd, chan->input, chan->output, line,
			       chan);
		chan->enabled = 1;
	}
}

static LIST_HEAD(irqs_to_free);

void free_irqs(void)
{
	struct chan *chan;

	while(!list_empty(&irqs_to_free)){
		chan = list_entry(irqs_to_free.next, struct chan, free_list);
		list_del(&chan->free_list);

		if(chan->input)
			free_irq(chan->line->driver->read_irq, chan);
		if(chan->output)
			free_irq(chan->line->driver->write_irq, chan);
		chan->enabled = 0;
	}
}

static void close_one_chan(struct chan *chan, int delay_free_irq)
{
	if(!chan->opened)
		return;
L
Linus Torvalds 已提交
283

284 285 286 287 288 289 290 291 292
	if(delay_free_irq){
		list_add(&chan->free_list, &irqs_to_free);
	}
	else {
		if(chan->input)
			free_irq(chan->line->driver->read_irq, chan);
		if(chan->output)
			free_irq(chan->line->driver->write_irq, chan);
		chan->enabled = 0;
L
Linus Torvalds 已提交
293
	}
294 295 296 297 298
	if(chan->ops->close != NULL)
		(*chan->ops->close)(chan->fd, chan->data);

	chan->opened = 0;
	chan->fd = -1;
L
Linus Torvalds 已提交
299 300
}

301
void close_chan(struct list_head *chans, int delay_free_irq)
L
Linus Torvalds 已提交
302 303 304 305 306 307 308 309 310
{
	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) {
311
		close_one_chan(chan, delay_free_irq);
L
Linus Torvalds 已提交
312 313 314
	}
}

J
Jeff Dike 已提交
315
int write_chan(struct list_head *chans, const char *buf, int len,
L
Linus Torvalds 已提交
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
	       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 已提交
333
	return ret;
L
Linus Torvalds 已提交
334 335 336 337 338 339 340 341 342 343 344 345
}

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;
346
		n = chan->ops->console_write(chan->fd, buf, len);
L
Linus Torvalds 已提交
347 348
		if(chan->primary) ret = n;
	}
J
Jeff Dike 已提交
349
	return ret;
L
Linus Torvalds 已提交
350 351
}

J
Jeff Dike 已提交
352 353
int console_open_chan(struct line *line, struct console *co,
		      struct chan_opts *opts)
L
Linus Torvalds 已提交
354
{
355 356 357 358 359
	int err;

	err = open_chan(&line->chan_list);
	if(err)
		return err;
L
Linus Torvalds 已提交
360 361 362 363 364 365 366 367 368 369 370 371 372 373

	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 已提交
374 375 376 377
			if(chan->ops->window_size == NULL)
				return 0;
			return chan->ops->window_size(chan->fd, chan->data,
						      rows_out, cols_out);
L
Linus Torvalds 已提交
378 379
		}
	}
J
Jeff Dike 已提交
380
	return 0;
L
Linus Torvalds 已提交
381 382
}

383
void free_one_chan(struct chan *chan, int delay_free_irq)
L
Linus Torvalds 已提交
384 385
{
	list_del(&chan->list);
386 387 388

	close_one_chan(chan, delay_free_irq);

L
Linus Torvalds 已提交
389 390
	if(chan->ops->free != NULL)
		(*chan->ops->free)(chan->data);
391

L
Linus Torvalds 已提交
392 393 394 395
	if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
	kfree(chan);
}

396
void free_chan(struct list_head *chans, int delay_free_irq)
L
Linus Torvalds 已提交
397 398 399 400 401 402
{
	struct list_head *ele, *next;
	struct chan *chan;

	list_for_each_safe(ele, next, chans){
		chan = list_entry(ele, struct chan, list);
403
		free_one_chan(chan, delay_free_irq);
L
Linus Torvalds 已提交
404 405 406 407 408 409 410 411 412 413
	}
}

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 已提交
414
		return n;
L
Linus Torvalds 已提交
415 416 417 418 419 420
	}

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

	if(chan->dev == NULL){
		CONFIG_CHUNK(str, size, n, "", 1);
J
Jeff Dike 已提交
421
		return n;
L
Linus Torvalds 已提交
422 423 424 425 426
	}

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

J
Jeff Dike 已提交
427
	return n;
L
Linus Torvalds 已提交
428 429
}

J
Jeff Dike 已提交
430
static int chan_pair_config_string(struct chan *in, struct chan *out,
L
Linus Torvalds 已提交
431 432 433 434 435 436 437 438 439 440
				   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 已提交
441
		return n;
L
Linus Torvalds 已提交
442 443 444 445 446 447 448 449
	}

	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 已提交
450
	return n;
L
Linus Torvalds 已提交
451 452
}

J
Jeff Dike 已提交
453
int chan_config_string(struct list_head *chans, char *str, int size,
L
Linus Torvalds 已提交
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
		       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 已提交
469
	return chan_pair_config_string(in, out, str, size, error_out);
L
Linus Torvalds 已提交
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
}

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
};

513 514
static struct chan *parse_chan(struct line *line, char *str, int device,
			       struct chan_opts *opts)
L
Linus Torvalds 已提交
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
{
	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){
533
		my_printf("parse_chan couldn't parse \"%s\"\n",
L
Linus Torvalds 已提交
534
		       str);
J
Jeff Dike 已提交
535
		return NULL;
L
Linus Torvalds 已提交
536
	}
J
Jeff Dike 已提交
537 538
	if(ops->init == NULL)
		return NULL;
L
Linus Torvalds 已提交
539
	data = (*ops->init)(str, device, opts);
J
Jeff Dike 已提交
540 541
	if(data == NULL)
		return NULL;
L
Linus Torvalds 已提交
542

543
	chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
J
Jeff Dike 已提交
544 545
	if(chan == NULL)
		return NULL;
L
Linus Torvalds 已提交
546
	*chan = ((struct chan) { .list	 	= LIST_HEAD_INIT(chan->list),
547 548 549
				 .free_list 	=
				 	LIST_HEAD_INIT(chan->free_list),
				 .line		= line,
L
Linus Torvalds 已提交
550 551 552 553
				 .primary	= 1,
				 .input		= 0,
				 .output 	= 0,
				 .opened  	= 0,
554
				 .enabled  	= 0,
L
Linus Torvalds 已提交
555 556 557
				 .fd 		= -1,
				 .ops 		= ops,
				 .data 		= data });
J
Jeff Dike 已提交
558
	return chan;
L
Linus Torvalds 已提交
559 560
}

561
int parse_chan_pair(char *str, struct line *line, int device,
L
Linus Torvalds 已提交
562 563
		    struct chan_opts *opts)
{
564
	struct list_head *chans = &line->chan_list;
L
Linus Torvalds 已提交
565 566 567 568 569
	struct chan *new, *chan;
	char *in, *out;

	if(!list_empty(chans)){
		chan = list_entry(chans->next, struct chan, list);
570
		free_chan(chans, 0);
L
Linus Torvalds 已提交
571 572 573 574 575 576 577 578
		INIT_LIST_HEAD(chans);
	}

	out = strchr(str, ',');
	if(out != NULL){
		in = str;
		*out = '\0';
		out++;
579
		new = parse_chan(line, in, device, opts);
J
Jeff Dike 已提交
580 581 582
		if(new == NULL)
			return -1;

L
Linus Torvalds 已提交
583 584 585
		new->input = 1;
		list_add(&new->list, chans);

586
		new = parse_chan(line, out, device, opts);
J
Jeff Dike 已提交
587 588 589
		if(new == NULL)
			return -1;

L
Linus Torvalds 已提交
590 591 592 593
		list_add(&new->list, chans);
		new->output = 1;
	}
	else {
594
		new = parse_chan(line, str, device, opts);
J
Jeff Dike 已提交
595 596 597
		if(new == NULL)
			return -1;

L
Linus Torvalds 已提交
598 599 600 601
		list_add(&new->list, chans);
		new->input = 1;
		new->output = 1;
	}
J
Jeff Dike 已提交
602
	return 0;
L
Linus Torvalds 已提交
603 604 605 606 607 608 609 610 611 612
}

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 已提交
613
			return chan->fd;
L
Linus Torvalds 已提交
614
	}
J
Jeff Dike 已提交
615
	return -1;
L
Linus Torvalds 已提交
616 617 618 619 620 621 622 623 624 625 626 627 628 629
}

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 已提交
630
			if((tty != NULL) &&
L
Linus Torvalds 已提交
631 632 633 634 635 636 637 638 639 640 641 642 643 644
			   (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);
645
				close_chan(chans, 1);
L
Linus Torvalds 已提交
646 647
				return;
			}
648
			else close_one_chan(chan, 1);
L
Linus Torvalds 已提交
649 650 651 652 653
		}
	}
 out:
	if(tty) tty_flip_buffer_push(tty);
}