chan_kern.c 12.0 KB
Newer Older
1
/*
J
Jeff Dike 已提交
2
 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
L
Linus Torvalds 已提交
3 4 5 6 7 8
 * Licensed under the GPL
 */

#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
9
#include "chan.h"
L
Linus Torvalds 已提交
10 11
#include "os.h"

12
#ifdef CONFIG_NOCONFIG_CHAN
13 14
static void *not_configged_init(char *str, int device,
				const struct chan_opts *opts)
15
{
J
Jeff Dike 已提交
16
	printk(KERN_ERR "Using a channel type which is configured out of "
L
Linus Torvalds 已提交
17
	       "UML\n");
J
Jeff Dike 已提交
18
	return NULL;
L
Linus Torvalds 已提交
19 20 21 22 23
}

static int not_configged_open(int input, int output, int primary, void *data,
			      char **dev_out)
{
J
Jeff Dike 已提交
24
	printk(KERN_ERR "Using a channel type which is configured out of "
L
Linus Torvalds 已提交
25
	       "UML\n");
J
Jeff Dike 已提交
26
	return -ENODEV;
L
Linus Torvalds 已提交
27 28 29 30
}

static void not_configged_close(int fd, void *data)
{
J
Jeff Dike 已提交
31
	printk(KERN_ERR "Using a channel type which is configured out of "
L
Linus Torvalds 已提交
32 33 34 35 36
	       "UML\n");
}

static int not_configged_read(int fd, char *c_out, void *data)
{
J
Jeff Dike 已提交
37
	printk(KERN_ERR "Using a channel type which is configured out of "
L
Linus Torvalds 已提交
38
	       "UML\n");
J
Jeff Dike 已提交
39
	return -EIO;
L
Linus Torvalds 已提交
40 41 42 43
}

static int not_configged_write(int fd, const char *buf, int len, void *data)
{
J
Jeff Dike 已提交
44
	printk(KERN_ERR "Using a channel type which is configured out of "
L
Linus Torvalds 已提交
45
	       "UML\n");
J
Jeff Dike 已提交
46
	return -EIO;
L
Linus Torvalds 已提交
47 48
}

49
static int not_configged_console_write(int fd, const char *buf, int len)
L
Linus Torvalds 已提交
50
{
J
Jeff Dike 已提交
51
	printk(KERN_ERR "Using a channel type which is configured out of "
L
Linus Torvalds 已提交
52
	       "UML\n");
J
Jeff Dike 已提交
53
	return -EIO;
L
Linus Torvalds 已提交
54 55 56 57 58
}

static int not_configged_window_size(int fd, void *data, unsigned short *rows,
				     unsigned short *cols)
{
J
Jeff Dike 已提交
59
	printk(KERN_ERR "Using a channel type which is configured out of "
L
Linus Torvalds 已提交
60
	       "UML\n");
J
Jeff Dike 已提交
61
	return -ENODEV;
L
Linus Torvalds 已提交
62 63 64 65
}

static void not_configged_free(void *data)
{
J
Jeff Dike 已提交
66
	printk(KERN_ERR "Using a channel type which is configured out of "
L
Linus Torvalds 已提交
67 68 69
	       "UML\n");
}

J
Jeff Dike 已提交
70
static const struct chan_ops not_configged_ops = {
L
Linus Torvalds 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84
	.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 */

static void tty_receive_char(struct tty_struct *tty, char ch)
{
J
Jeff Dike 已提交
85 86
	if (tty == NULL)
		return;
L
Linus Torvalds 已提交
87

J
Jeff Dike 已提交
88 89
	if (I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
		if (ch == STOP_CHAR(tty)) {
L
Linus Torvalds 已提交
90 91 92
			stop_tty(tty);
			return;
		}
J
Jeff Dike 已提交
93
		else if (ch == START_CHAR(tty)) {
L
Linus Torvalds 已提交
94 95 96 97 98 99 100 101
			start_tty(tty);
			return;
		}
	}

	tty_insert_flip_char(tty, ch, TTY_NORMAL);
}

J
Jeff Dike 已提交
102
static int open_one_chan(struct chan *chan)
L
Linus Torvalds 已提交
103
{
104
	int fd, err;
L
Linus Torvalds 已提交
105

J
Jeff Dike 已提交
106
	if (chan->opened)
J
Jeff Dike 已提交
107 108
		return 0;

J
Jeff Dike 已提交
109
	if (chan->ops->open == NULL)
J
Jeff Dike 已提交
110 111 112
		fd = 0;
	else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
				     chan->data, &chan->dev);
J
Jeff Dike 已提交
113
	if (fd < 0)
J
Jeff Dike 已提交
114
		return fd;
115 116 117 118 119 120 121

	err = os_set_fd_block(fd, 0);
	if (err) {
		(*chan->ops->close)(fd, chan->data);
		return err;
	}

L
Linus Torvalds 已提交
122 123 124
	chan->fd = fd;

	chan->opened = 1;
J
Jeff Dike 已提交
125
	return 0;
L
Linus Torvalds 已提交
126 127
}

W
WANG Cong 已提交
128
static int open_chan(struct list_head *chans)
L
Linus Torvalds 已提交
129 130 131 132 133
{
	struct list_head *ele;
	struct chan *chan;
	int ret, err = 0;

J
Jeff Dike 已提交
134
	list_for_each(ele, chans) {
L
Linus Torvalds 已提交
135
		chan = list_entry(ele, struct chan, list);
J
Jeff Dike 已提交
136
		ret = open_one_chan(chan);
J
Jeff Dike 已提交
137
		if (chan->primary)
J
Jeff Dike 已提交
138
			err = ret;
L
Linus Torvalds 已提交
139
	}
J
Jeff Dike 已提交
140
	return err;
L
Linus Torvalds 已提交
141 142
}

143
void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
L
Linus Torvalds 已提交
144
{
145 146
	if (chan && chan->primary && chan->ops->winch)
		register_winch(chan->fd, tty);
L
Linus Torvalds 已提交
147 148
}

149
int enable_chan(struct line *line)
L
Linus Torvalds 已提交
150 151 152
{
	struct list_head *ele;
	struct chan *chan;
153
	int err;
L
Linus Torvalds 已提交
154

J
Jeff Dike 已提交
155
	list_for_each(ele, &line->chan_list) {
L
Linus Torvalds 已提交
156
		chan = list_entry(ele, struct chan, list);
157 158 159 160 161
		err = open_one_chan(chan);
		if (err) {
			if (chan->primary)
				goto out_close;

162
			continue;
163
		}
164

J
Jeff Dike 已提交
165
		if (chan->enabled)
166
			continue;
167 168 169 170 171
		err = line_setup_irq(chan->fd, chan->input, chan->output, line,
				     chan);
		if (err)
			goto out_close;

172 173
		chan->enabled = 1;
	}
174 175 176 177 178 179

	return 0;

 out_close:
	close_chan(&line->chan_list, 0);
	return err;
180 181
}

182 183 184 185 186 187 188
/* Items are added in IRQ context, when free_irq can't be called, and
 * removed in process context, when it can.
 * This handles interrupt sources which disappear, and which need to
 * be permanently disabled.  This is discovered in IRQ context, but
 * the freeing of the IRQ must be done later.
 */
static DEFINE_SPINLOCK(irqs_to_free_lock);
189 190 191 192 193
static LIST_HEAD(irqs_to_free);

void free_irqs(void)
{
	struct chan *chan;
194 195
	LIST_HEAD(list);
	struct list_head *ele;
J
Jeff Dike 已提交
196
	unsigned long flags;
197

J
Jeff Dike 已提交
198
	spin_lock_irqsave(&irqs_to_free_lock, flags);
199
	list_splice_init(&irqs_to_free, &list);
J
Jeff Dike 已提交
200
	spin_unlock_irqrestore(&irqs_to_free_lock, flags);
201

J
Jeff Dike 已提交
202
	list_for_each(ele, &list) {
203
		chan = list_entry(ele, struct chan, free_list);
204

205
		if (chan->input && chan->enabled)
206
			free_irq(chan->line->driver->read_irq, chan);
207
		if (chan->output && chan->enabled)
208 209 210 211 212 213 214
			free_irq(chan->line->driver->write_irq, chan);
		chan->enabled = 0;
	}
}

static void close_one_chan(struct chan *chan, int delay_free_irq)
{
J
Jeff Dike 已提交
215 216
	unsigned long flags;

J
Jeff Dike 已提交
217
	if (!chan->opened)
218
		return;
L
Linus Torvalds 已提交
219

J
Jeff Dike 已提交
220
	if (delay_free_irq) {
J
Jeff Dike 已提交
221
		spin_lock_irqsave(&irqs_to_free_lock, flags);
222
		list_add(&chan->free_list, &irqs_to_free);
J
Jeff Dike 已提交
223
		spin_unlock_irqrestore(&irqs_to_free_lock, flags);
224 225
	}
	else {
226
		if (chan->input && chan->enabled)
227
			free_irq(chan->line->driver->read_irq, chan);
228
		if (chan->output && chan->enabled)
229 230
			free_irq(chan->line->driver->write_irq, chan);
		chan->enabled = 0;
L
Linus Torvalds 已提交
231
	}
J
Jeff Dike 已提交
232
	if (chan->ops->close != NULL)
233 234 235 236
		(*chan->ops->close)(chan->fd, chan->data);

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

239
void close_chan(struct list_head *chans, int delay_free_irq)
L
Linus Torvalds 已提交
240 241 242 243 244 245 246 247 248
{
	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) {
249
		close_one_chan(chan, delay_free_irq);
L
Linus Torvalds 已提交
250 251 252
	}
}

253
void deactivate_chan(struct chan *chan, int irq)
254
{
255 256
	if (chan && chan->enabled)
		deactivate_fd(chan->fd, irq);
257 258
}

259
void reactivate_chan(struct chan *chan, int irq)
260
{
261 262
	if (chan && chan->enabled)
		reactivate_fd(chan->fd, irq);
263 264
}

265
int write_chan(struct chan *chan, const char *buf, int len,
L
Linus Torvalds 已提交
266 267 268 269
	       int write_irq)
{
	int n, ret = 0;

270
	if (len == 0 || !chan || !chan->ops->write)
J
Jeff Dike 已提交
271 272
		return 0;

273 274 275 276 277
	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);
L
Linus Torvalds 已提交
278
	}
J
Jeff Dike 已提交
279
	return ret;
L
Linus Torvalds 已提交
280 281
}

282
int console_write_chan(struct chan *chan, const char *buf, int len)
L
Linus Torvalds 已提交
283 284 285
{
	int n, ret = 0;

286 287
	if (!chan || !chan->ops->console_write)
		return 0;
J
Jeff Dike 已提交
288

289 290 291
	n = chan->ops->console_write(chan->fd, buf, len);
	if (chan->primary)
		ret = n;
J
Jeff Dike 已提交
292
	return ret;
L
Linus Torvalds 已提交
293 294
}

295
int console_open_chan(struct line *line, struct console *co)
L
Linus Torvalds 已提交
296
{
297 298 299
	int err;

	err = open_chan(&line->chan_list);
J
Jeff Dike 已提交
300
	if (err)
301
		return err;
L
Linus Torvalds 已提交
302

J
Jeff Dike 已提交
303 304
	printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
	       co->index);
L
Linus Torvalds 已提交
305 306 307
	return 0;
}

308
int chan_window_size(struct line *line, unsigned short *rows_out,
L
Linus Torvalds 已提交
309 310 311 312
		      unsigned short *cols_out)
{
	struct chan *chan;

313 314 315 316 317 318 319 320 321 322 323 324 325
	chan = line->chan_in;
	if (chan && chan->primary) {
		if (chan->ops->window_size == NULL)
			return 0;
		return chan->ops->window_size(chan->fd, chan->data,
					      rows_out, cols_out);
	}
	chan = line->chan_out;
	if (chan && chan->primary) {
		if (chan->ops->window_size == NULL)
			return 0;
		return chan->ops->window_size(chan->fd, chan->data,
					      rows_out, cols_out);
L
Linus Torvalds 已提交
326
	}
J
Jeff Dike 已提交
327
	return 0;
L
Linus Torvalds 已提交
328 329
}

330
static void free_one_chan(struct chan *chan)
L
Linus Torvalds 已提交
331 332
{
	list_del(&chan->list);
333

334
	close_one_chan(chan, 0);
335

J
Jeff Dike 已提交
336
	if (chan->ops->free != NULL)
L
Linus Torvalds 已提交
337
		(*chan->ops->free)(chan->data);
338

J
Jeff Dike 已提交
339 340
	if (chan->primary && chan->output)
		ignore_sigio_fd(chan->fd);
L
Linus Torvalds 已提交
341 342 343
	kfree(chan);
}

344
static void free_chan(struct list_head *chans)
L
Linus Torvalds 已提交
345 346 347 348
{
	struct list_head *ele, *next;
	struct chan *chan;

J
Jeff Dike 已提交
349
	list_for_each_safe(ele, next, chans) {
L
Linus Torvalds 已提交
350
		chan = list_entry(ele, struct chan, list);
351
		free_one_chan(chan);
L
Linus Torvalds 已提交
352 353 354 355 356 357 358 359
	}
}

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

J
Jeff Dike 已提交
360
	if (chan == NULL) {
L
Linus Torvalds 已提交
361
		CONFIG_CHUNK(str, size, n, "none", 1);
J
Jeff Dike 已提交
362
		return n;
L
Linus Torvalds 已提交
363 364 365 366
	}

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

J
Jeff Dike 已提交
367
	if (chan->dev == NULL) {
L
Linus Torvalds 已提交
368
		CONFIG_CHUNK(str, size, n, "", 1);
J
Jeff Dike 已提交
369
		return n;
L
Linus Torvalds 已提交
370 371 372 373 374
	}

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

J
Jeff Dike 已提交
375
	return n;
L
Linus Torvalds 已提交
376 377
}

J
Jeff Dike 已提交
378
static int chan_pair_config_string(struct chan *in, struct chan *out,
L
Linus Torvalds 已提交
379 380 381 382 383 384 385 386
				   char *str, int size, char **error_out)
{
	int n;

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

J
Jeff Dike 已提交
387
	if (in == out) {
L
Linus Torvalds 已提交
388
		CONFIG_CHUNK(str, size, n, "", 1);
J
Jeff Dike 已提交
389
		return n;
L
Linus Torvalds 已提交
390 391 392 393 394 395 396 397
	}

	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 已提交
398
	return n;
L
Linus Torvalds 已提交
399 400
}

401
int chan_config_string(struct line *line, char *str, int size,
L
Linus Torvalds 已提交
402 403
		       char **error_out)
{
404
	struct chan *in = line->chan_in, *out = line->chan_out;
L
Linus Torvalds 已提交
405

406 407 408 409
	if (in && !in->primary)
		in = NULL;
	if (out && !out->primary)
		out = NULL;
L
Linus Torvalds 已提交
410

J
Jeff Dike 已提交
411
	return chan_pair_config_string(in, out, str, size, error_out);
L
Linus Torvalds 已提交
412 413 414 415
}

struct chan_type {
	char *key;
J
Jeff Dike 已提交
416
	const struct chan_ops *ops;
L
Linus Torvalds 已提交
417 418
};

J
Jeff Dike 已提交
419
static const struct chan_type chan_table[] = {
L
Linus Torvalds 已提交
420 421 422 423 424 425 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
	{ "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
};

455
static struct chan *parse_chan(struct line *line, char *str, int device,
456
			       const struct chan_opts *opts, char **error_out)
L
Linus Torvalds 已提交
457
{
J
Jeff Dike 已提交
458 459
	const struct chan_type *entry;
	const struct chan_ops *ops;
L
Linus Torvalds 已提交
460 461 462 463 464 465
	struct chan *chan;
	void *data;
	int i;

	ops = NULL;
	data = NULL;
J
Jeff Dike 已提交
466
	for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
L
Linus Torvalds 已提交
467
		entry = &chan_table[i];
J
Jeff Dike 已提交
468
		if (!strncmp(str, entry->key, strlen(entry->key))) {
L
Linus Torvalds 已提交
469 470 471 472 473
			ops = entry->ops;
			str += strlen(entry->key);
			break;
		}
	}
J
Jeff Dike 已提交
474
	if (ops == NULL) {
475
		*error_out = "No match for configured backends";
J
Jeff Dike 已提交
476
		return NULL;
L
Linus Torvalds 已提交
477
	}
478

L
Linus Torvalds 已提交
479
	data = (*ops->init)(str, device, opts);
J
Jeff Dike 已提交
480
	if (data == NULL) {
481
		*error_out = "Configuration failed";
J
Jeff Dike 已提交
482
		return NULL;
483
	}
L
Linus Torvalds 已提交
484

485
	chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
J
Jeff Dike 已提交
486
	if (chan == NULL) {
487
		*error_out = "Memory allocation failed";
J
Jeff Dike 已提交
488
		return NULL;
489
	}
L
Linus Torvalds 已提交
490
	*chan = ((struct chan) { .list	 	= LIST_HEAD_INIT(chan->list),
491 492 493
				 .free_list 	=
				 	LIST_HEAD_INIT(chan->free_list),
				 .line		= line,
L
Linus Torvalds 已提交
494 495 496 497
				 .primary	= 1,
				 .input		= 0,
				 .output 	= 0,
				 .opened  	= 0,
498
				 .enabled  	= 0,
L
Linus Torvalds 已提交
499 500 501
				 .fd 		= -1,
				 .ops 		= ops,
				 .data 		= data });
J
Jeff Dike 已提交
502
	return chan;
L
Linus Torvalds 已提交
503 504
}

505
int parse_chan_pair(char *str, struct line *line, int device,
506
		    const struct chan_opts *opts, char **error_out)
L
Linus Torvalds 已提交
507
{
508
	struct list_head *chans = &line->chan_list;
R
Richard Weinberger 已提交
509
	struct chan *new;
L
Linus Torvalds 已提交
510 511
	char *in, *out;

J
Jeff Dike 已提交
512
	if (!list_empty(chans)) {
A
Al Viro 已提交
513
		line->chan_in = line->chan_out = NULL;
514
		free_chan(chans);
L
Linus Torvalds 已提交
515 516 517
		INIT_LIST_HEAD(chans);
	}

518 519 520
	if (!str)
		return 0;

L
Linus Torvalds 已提交
521
	out = strchr(str, ',');
J
Jeff Dike 已提交
522
	if (out != NULL) {
L
Linus Torvalds 已提交
523 524 525
		in = str;
		*out = '\0';
		out++;
526
		new = parse_chan(line, in, device, opts, error_out);
J
Jeff Dike 已提交
527
		if (new == NULL)
J
Jeff Dike 已提交
528 529
			return -1;

L
Linus Torvalds 已提交
530 531
		new->input = 1;
		list_add(&new->list, chans);
A
Al Viro 已提交
532
		line->chan_in = new;
L
Linus Torvalds 已提交
533

534
		new = parse_chan(line, out, device, opts, error_out);
J
Jeff Dike 已提交
535
		if (new == NULL)
J
Jeff Dike 已提交
536 537
			return -1;

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

L
Linus Torvalds 已提交
547 548 549
		list_add(&new->list, chans);
		new->input = 1;
		new->output = 1;
A
Al Viro 已提交
550
		line->chan_in = line->chan_out = new;
L
Linus Torvalds 已提交
551
	}
J
Jeff Dike 已提交
552
	return 0;
L
Linus Torvalds 已提交
553 554
}

555
void chan_interrupt(struct line *line, struct delayed_work *task,
L
Linus Torvalds 已提交
556 557
		    struct tty_struct *tty, int irq)
{
558
	struct chan *chan = line->chan_in;
L
Linus Torvalds 已提交
559 560 561
	int err;
	char c;

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
	if (!chan || !chan->ops->read)
		goto out;

	do {
		if (tty && !tty_buffer_request_room(tty, 1)) {
			schedule_delayed_work(task, 1);
			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);
			close_chan(&line->chan_list, 1);
			return;
L
Linus Torvalds 已提交
583
		}
584
		else close_one_chan(chan, 1);
L
Linus Torvalds 已提交
585 586
	}
 out:
J
Jeff Dike 已提交
587 588
	if (tty)
		tty_flip_buffer_push(tty);
L
Linus Torvalds 已提交
589
}