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