Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/um/drivers/chan_kern.c
10818 views
1
/*
2
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
3
* Licensed under the GPL
4
*/
5
6
#include <linux/slab.h>
7
#include <linux/tty.h>
8
#include <linux/tty_flip.h>
9
#include "chan_kern.h"
10
#include "os.h"
11
12
#ifdef CONFIG_NOCONFIG_CHAN
13
static void *not_configged_init(char *str, int device,
14
const struct chan_opts *opts)
15
{
16
printk(KERN_ERR "Using a channel type which is configured out of "
17
"UML\n");
18
return NULL;
19
}
20
21
static int not_configged_open(int input, int output, int primary, void *data,
22
char **dev_out)
23
{
24
printk(KERN_ERR "Using a channel type which is configured out of "
25
"UML\n");
26
return -ENODEV;
27
}
28
29
static void not_configged_close(int fd, void *data)
30
{
31
printk(KERN_ERR "Using a channel type which is configured out of "
32
"UML\n");
33
}
34
35
static int not_configged_read(int fd, char *c_out, void *data)
36
{
37
printk(KERN_ERR "Using a channel type which is configured out of "
38
"UML\n");
39
return -EIO;
40
}
41
42
static int not_configged_write(int fd, const char *buf, int len, void *data)
43
{
44
printk(KERN_ERR "Using a channel type which is configured out of "
45
"UML\n");
46
return -EIO;
47
}
48
49
static int not_configged_console_write(int fd, const char *buf, int len)
50
{
51
printk(KERN_ERR "Using a channel type which is configured out of "
52
"UML\n");
53
return -EIO;
54
}
55
56
static int not_configged_window_size(int fd, void *data, unsigned short *rows,
57
unsigned short *cols)
58
{
59
printk(KERN_ERR "Using a channel type which is configured out of "
60
"UML\n");
61
return -ENODEV;
62
}
63
64
static void not_configged_free(void *data)
65
{
66
printk(KERN_ERR "Using a channel type which is configured out of "
67
"UML\n");
68
}
69
70
static const struct chan_ops not_configged_ops = {
71
.init = not_configged_init,
72
.open = not_configged_open,
73
.close = not_configged_close,
74
.read = not_configged_read,
75
.write = not_configged_write,
76
.console_write = not_configged_console_write,
77
.window_size = not_configged_window_size,
78
.free = not_configged_free,
79
.winch = 0,
80
};
81
#endif /* CONFIG_NOCONFIG_CHAN */
82
83
static void tty_receive_char(struct tty_struct *tty, char ch)
84
{
85
if (tty == NULL)
86
return;
87
88
if (I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
89
if (ch == STOP_CHAR(tty)) {
90
stop_tty(tty);
91
return;
92
}
93
else if (ch == START_CHAR(tty)) {
94
start_tty(tty);
95
return;
96
}
97
}
98
99
tty_insert_flip_char(tty, ch, TTY_NORMAL);
100
}
101
102
static int open_one_chan(struct chan *chan)
103
{
104
int fd, err;
105
106
if (chan->opened)
107
return 0;
108
109
if (chan->ops->open == NULL)
110
fd = 0;
111
else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
112
chan->data, &chan->dev);
113
if (fd < 0)
114
return fd;
115
116
err = os_set_fd_block(fd, 0);
117
if (err) {
118
(*chan->ops->close)(fd, chan->data);
119
return err;
120
}
121
122
chan->fd = fd;
123
124
chan->opened = 1;
125
return 0;
126
}
127
128
static int open_chan(struct list_head *chans)
129
{
130
struct list_head *ele;
131
struct chan *chan;
132
int ret, err = 0;
133
134
list_for_each(ele, chans) {
135
chan = list_entry(ele, struct chan, list);
136
ret = open_one_chan(chan);
137
if (chan->primary)
138
err = ret;
139
}
140
return err;
141
}
142
143
void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
144
{
145
struct list_head *ele;
146
struct chan *chan;
147
148
list_for_each(ele, chans) {
149
chan = list_entry(ele, struct chan, list);
150
if (chan->primary && chan->output && chan->ops->winch) {
151
register_winch(chan->fd, tty);
152
return;
153
}
154
}
155
}
156
157
int enable_chan(struct line *line)
158
{
159
struct list_head *ele;
160
struct chan *chan;
161
int err;
162
163
list_for_each(ele, &line->chan_list) {
164
chan = list_entry(ele, struct chan, list);
165
err = open_one_chan(chan);
166
if (err) {
167
if (chan->primary)
168
goto out_close;
169
170
continue;
171
}
172
173
if (chan->enabled)
174
continue;
175
err = line_setup_irq(chan->fd, chan->input, chan->output, line,
176
chan);
177
if (err)
178
goto out_close;
179
180
chan->enabled = 1;
181
}
182
183
return 0;
184
185
out_close:
186
close_chan(&line->chan_list, 0);
187
return err;
188
}
189
190
/* Items are added in IRQ context, when free_irq can't be called, and
191
* removed in process context, when it can.
192
* This handles interrupt sources which disappear, and which need to
193
* be permanently disabled. This is discovered in IRQ context, but
194
* the freeing of the IRQ must be done later.
195
*/
196
static DEFINE_SPINLOCK(irqs_to_free_lock);
197
static LIST_HEAD(irqs_to_free);
198
199
void free_irqs(void)
200
{
201
struct chan *chan;
202
LIST_HEAD(list);
203
struct list_head *ele;
204
unsigned long flags;
205
206
spin_lock_irqsave(&irqs_to_free_lock, flags);
207
list_splice_init(&irqs_to_free, &list);
208
spin_unlock_irqrestore(&irqs_to_free_lock, flags);
209
210
list_for_each(ele, &list) {
211
chan = list_entry(ele, struct chan, free_list);
212
213
if (chan->input && chan->enabled)
214
free_irq(chan->line->driver->read_irq, chan);
215
if (chan->output && chan->enabled)
216
free_irq(chan->line->driver->write_irq, chan);
217
chan->enabled = 0;
218
}
219
}
220
221
static void close_one_chan(struct chan *chan, int delay_free_irq)
222
{
223
unsigned long flags;
224
225
if (!chan->opened)
226
return;
227
228
if (delay_free_irq) {
229
spin_lock_irqsave(&irqs_to_free_lock, flags);
230
list_add(&chan->free_list, &irqs_to_free);
231
spin_unlock_irqrestore(&irqs_to_free_lock, flags);
232
}
233
else {
234
if (chan->input && chan->enabled)
235
free_irq(chan->line->driver->read_irq, chan);
236
if (chan->output && chan->enabled)
237
free_irq(chan->line->driver->write_irq, chan);
238
chan->enabled = 0;
239
}
240
if (chan->ops->close != NULL)
241
(*chan->ops->close)(chan->fd, chan->data);
242
243
chan->opened = 0;
244
chan->fd = -1;
245
}
246
247
void close_chan(struct list_head *chans, int delay_free_irq)
248
{
249
struct chan *chan;
250
251
/* Close in reverse order as open in case more than one of them
252
* refers to the same device and they save and restore that device's
253
* state. Then, the first one opened will have the original state,
254
* so it must be the last closed.
255
*/
256
list_for_each_entry_reverse(chan, chans, list) {
257
close_one_chan(chan, delay_free_irq);
258
}
259
}
260
261
void deactivate_chan(struct list_head *chans, int irq)
262
{
263
struct list_head *ele;
264
265
struct chan *chan;
266
list_for_each(ele, chans) {
267
chan = list_entry(ele, struct chan, list);
268
269
if (chan->enabled && chan->input)
270
deactivate_fd(chan->fd, irq);
271
}
272
}
273
274
void reactivate_chan(struct list_head *chans, int irq)
275
{
276
struct list_head *ele;
277
struct chan *chan;
278
279
list_for_each(ele, chans) {
280
chan = list_entry(ele, struct chan, list);
281
282
if (chan->enabled && chan->input)
283
reactivate_fd(chan->fd, irq);
284
}
285
}
286
287
int write_chan(struct list_head *chans, const char *buf, int len,
288
int write_irq)
289
{
290
struct list_head *ele;
291
struct chan *chan = NULL;
292
int n, ret = 0;
293
294
if (len == 0)
295
return 0;
296
297
list_for_each(ele, chans) {
298
chan = list_entry(ele, struct chan, list);
299
if (!chan->output || (chan->ops->write == NULL))
300
continue;
301
302
n = chan->ops->write(chan->fd, buf, len, chan->data);
303
if (chan->primary) {
304
ret = n;
305
if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
306
reactivate_fd(chan->fd, write_irq);
307
}
308
}
309
return ret;
310
}
311
312
int console_write_chan(struct list_head *chans, const char *buf, int len)
313
{
314
struct list_head *ele;
315
struct chan *chan;
316
int n, ret = 0;
317
318
list_for_each(ele, chans) {
319
chan = list_entry(ele, struct chan, list);
320
if (!chan->output || (chan->ops->console_write == NULL))
321
continue;
322
323
n = chan->ops->console_write(chan->fd, buf, len);
324
if (chan->primary)
325
ret = n;
326
}
327
return ret;
328
}
329
330
int console_open_chan(struct line *line, struct console *co)
331
{
332
int err;
333
334
err = open_chan(&line->chan_list);
335
if (err)
336
return err;
337
338
printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
339
co->index);
340
return 0;
341
}
342
343
int chan_window_size(struct list_head *chans, unsigned short *rows_out,
344
unsigned short *cols_out)
345
{
346
struct list_head *ele;
347
struct chan *chan;
348
349
list_for_each(ele, chans) {
350
chan = list_entry(ele, struct chan, list);
351
if (chan->primary) {
352
if (chan->ops->window_size == NULL)
353
return 0;
354
return chan->ops->window_size(chan->fd, chan->data,
355
rows_out, cols_out);
356
}
357
}
358
return 0;
359
}
360
361
static void free_one_chan(struct chan *chan, int delay_free_irq)
362
{
363
list_del(&chan->list);
364
365
close_one_chan(chan, delay_free_irq);
366
367
if (chan->ops->free != NULL)
368
(*chan->ops->free)(chan->data);
369
370
if (chan->primary && chan->output)
371
ignore_sigio_fd(chan->fd);
372
kfree(chan);
373
}
374
375
static void free_chan(struct list_head *chans, int delay_free_irq)
376
{
377
struct list_head *ele, *next;
378
struct chan *chan;
379
380
list_for_each_safe(ele, next, chans) {
381
chan = list_entry(ele, struct chan, list);
382
free_one_chan(chan, delay_free_irq);
383
}
384
}
385
386
static int one_chan_config_string(struct chan *chan, char *str, int size,
387
char **error_out)
388
{
389
int n = 0;
390
391
if (chan == NULL) {
392
CONFIG_CHUNK(str, size, n, "none", 1);
393
return n;
394
}
395
396
CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
397
398
if (chan->dev == NULL) {
399
CONFIG_CHUNK(str, size, n, "", 1);
400
return n;
401
}
402
403
CONFIG_CHUNK(str, size, n, ":", 0);
404
CONFIG_CHUNK(str, size, n, chan->dev, 0);
405
406
return n;
407
}
408
409
static int chan_pair_config_string(struct chan *in, struct chan *out,
410
char *str, int size, char **error_out)
411
{
412
int n;
413
414
n = one_chan_config_string(in, str, size, error_out);
415
str += n;
416
size -= n;
417
418
if (in == out) {
419
CONFIG_CHUNK(str, size, n, "", 1);
420
return n;
421
}
422
423
CONFIG_CHUNK(str, size, n, ",", 1);
424
n = one_chan_config_string(out, str, size, error_out);
425
str += n;
426
size -= n;
427
CONFIG_CHUNK(str, size, n, "", 1);
428
429
return n;
430
}
431
432
int chan_config_string(struct list_head *chans, char *str, int size,
433
char **error_out)
434
{
435
struct list_head *ele;
436
struct chan *chan, *in = NULL, *out = NULL;
437
438
list_for_each(ele, chans) {
439
chan = list_entry(ele, struct chan, list);
440
if (!chan->primary)
441
continue;
442
if (chan->input)
443
in = chan;
444
if (chan->output)
445
out = chan;
446
}
447
448
return chan_pair_config_string(in, out, str, size, error_out);
449
}
450
451
struct chan_type {
452
char *key;
453
const struct chan_ops *ops;
454
};
455
456
static const struct chan_type chan_table[] = {
457
{ "fd", &fd_ops },
458
459
#ifdef CONFIG_NULL_CHAN
460
{ "null", &null_ops },
461
#else
462
{ "null", &not_configged_ops },
463
#endif
464
465
#ifdef CONFIG_PORT_CHAN
466
{ "port", &port_ops },
467
#else
468
{ "port", &not_configged_ops },
469
#endif
470
471
#ifdef CONFIG_PTY_CHAN
472
{ "pty", &pty_ops },
473
{ "pts", &pts_ops },
474
#else
475
{ "pty", &not_configged_ops },
476
{ "pts", &not_configged_ops },
477
#endif
478
479
#ifdef CONFIG_TTY_CHAN
480
{ "tty", &tty_ops },
481
#else
482
{ "tty", &not_configged_ops },
483
#endif
484
485
#ifdef CONFIG_XTERM_CHAN
486
{ "xterm", &xterm_ops },
487
#else
488
{ "xterm", &not_configged_ops },
489
#endif
490
};
491
492
static struct chan *parse_chan(struct line *line, char *str, int device,
493
const struct chan_opts *opts, char **error_out)
494
{
495
const struct chan_type *entry;
496
const struct chan_ops *ops;
497
struct chan *chan;
498
void *data;
499
int i;
500
501
ops = NULL;
502
data = NULL;
503
for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
504
entry = &chan_table[i];
505
if (!strncmp(str, entry->key, strlen(entry->key))) {
506
ops = entry->ops;
507
str += strlen(entry->key);
508
break;
509
}
510
}
511
if (ops == NULL) {
512
*error_out = "No match for configured backends";
513
return NULL;
514
}
515
516
data = (*ops->init)(str, device, opts);
517
if (data == NULL) {
518
*error_out = "Configuration failed";
519
return NULL;
520
}
521
522
chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
523
if (chan == NULL) {
524
*error_out = "Memory allocation failed";
525
return NULL;
526
}
527
*chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
528
.free_list =
529
LIST_HEAD_INIT(chan->free_list),
530
.line = line,
531
.primary = 1,
532
.input = 0,
533
.output = 0,
534
.opened = 0,
535
.enabled = 0,
536
.fd = -1,
537
.ops = ops,
538
.data = data });
539
return chan;
540
}
541
542
int parse_chan_pair(char *str, struct line *line, int device,
543
const struct chan_opts *opts, char **error_out)
544
{
545
struct list_head *chans = &line->chan_list;
546
struct chan *new, *chan;
547
char *in, *out;
548
549
if (!list_empty(chans)) {
550
chan = list_entry(chans->next, struct chan, list);
551
free_chan(chans, 0);
552
INIT_LIST_HEAD(chans);
553
}
554
555
out = strchr(str, ',');
556
if (out != NULL) {
557
in = str;
558
*out = '\0';
559
out++;
560
new = parse_chan(line, in, device, opts, error_out);
561
if (new == NULL)
562
return -1;
563
564
new->input = 1;
565
list_add(&new->list, chans);
566
567
new = parse_chan(line, out, device, opts, error_out);
568
if (new == NULL)
569
return -1;
570
571
list_add(&new->list, chans);
572
new->output = 1;
573
}
574
else {
575
new = parse_chan(line, str, device, opts, error_out);
576
if (new == NULL)
577
return -1;
578
579
list_add(&new->list, chans);
580
new->input = 1;
581
new->output = 1;
582
}
583
return 0;
584
}
585
586
void chan_interrupt(struct list_head *chans, struct delayed_work *task,
587
struct tty_struct *tty, int irq)
588
{
589
struct list_head *ele, *next;
590
struct chan *chan;
591
int err;
592
char c;
593
594
list_for_each_safe(ele, next, chans) {
595
chan = list_entry(ele, struct chan, list);
596
if (!chan->input || (chan->ops->read == NULL))
597
continue;
598
do {
599
if (tty && !tty_buffer_request_room(tty, 1)) {
600
schedule_delayed_work(task, 1);
601
goto out;
602
}
603
err = chan->ops->read(chan->fd, &c, chan->data);
604
if (err > 0)
605
tty_receive_char(tty, c);
606
} while (err > 0);
607
608
if (err == 0)
609
reactivate_fd(chan->fd, irq);
610
if (err == -EIO) {
611
if (chan->primary) {
612
if (tty != NULL)
613
tty_hangup(tty);
614
close_chan(chans, 1);
615
return;
616
}
617
else close_one_chan(chan, 1);
618
}
619
}
620
out:
621
if (tty)
622
tty_flip_buffer_push(tty);
623
}
624
625