-
Notifications
You must be signed in to change notification settings - Fork 54.5k
/
n_tty.c
2468 lines (2211 loc) · 62.4 KB
/
n_tty.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
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
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
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
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
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
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// SPDX-License-Identifier: GPL-1.0+
/*
* n_tty.c --- implements the N_TTY line discipline.
*
* This code used to be in tty_io.c, but things are getting hairy
* enough that it made sense to split things off. (The N_TTY
* processing has changed so much that it's hardly recognizable,
* anyway...)
*
* Note that the open routine for N_TTY is guaranteed never to return
* an error. This is because Linux will fall back to setting a line
* to N_TTY if it can not switch to any other line discipline.
*
* Written by Theodore Ts'o, Copyright 1994.
*
* This file also contains code originally written by Linus Torvalds,
* Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994.
*
* Reduced memory usage for older ARM systems - Russell King.
*
* 2000/01/20 Fixed SMP locking on put_tty_queue using bits of
* the patch by Andrew J. Kroll <ag784@freenet.buffalo.edu>
* who actually finally proved there really was a race.
*
* 2002/03/18 Implemented n_tty_wakeup to send SIGIO POLL_OUTs to
* waiting writing processes-Sapan Bhatia <sapan@corewars.org>.
* Also fixed a bug in BLOCKING mode where n_tty_write returns
* EAGAIN
*/
#include <linux/types.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/fcntl.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/bitops.h>
#include <linux/audit.h>
#include <linux/file.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/ratelimit.h>
#include <linux/vmalloc.h>
#include "tty.h"
/*
* Until this number of characters is queued in the xmit buffer, select will
* return "we have room for writes".
*/
#define WAKEUP_CHARS 256
/*
* This defines the low- and high-watermarks for throttling and
* unthrottling the TTY driver. These watermarks are used for
* controlling the space in the read buffer.
*/
#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
#define TTY_THRESHOLD_UNTHROTTLE 128
/*
* Special byte codes used in the echo buffer to represent operations
* or special handling of characters. Bytes in the echo buffer that
* are not part of such special blocks are treated as normal character
* codes.
*/
#define ECHO_OP_START 0xff
#define ECHO_OP_MOVE_BACK_COL 0x80
#define ECHO_OP_SET_CANON_COL 0x81
#define ECHO_OP_ERASE_TAB 0x82
#define ECHO_COMMIT_WATERMARK 256
#define ECHO_BLOCK 256
#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32)
#undef N_TTY_TRACE
#ifdef N_TTY_TRACE
# define n_tty_trace(f, args...) trace_printk(f, ##args)
#else
# define n_tty_trace(f, args...) no_printk(f, ##args)
#endif
struct n_tty_data {
/* producer-published */
size_t read_head;
size_t commit_head;
size_t canon_head;
size_t echo_head;
size_t echo_commit;
size_t echo_mark;
DECLARE_BITMAP(char_map, 256);
/* private to n_tty_receive_overrun (single-threaded) */
unsigned long overrun_time;
int num_overrun;
/* non-atomic */
bool no_room;
/* must hold exclusive termios_rwsem to reset these */
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
unsigned char push:1;
/* shared by producer and consumer */
char read_buf[N_TTY_BUF_SIZE];
DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE);
unsigned char echo_buf[N_TTY_BUF_SIZE];
/* consumer-published */
size_t read_tail;
size_t line_start;
/* protected by output lock */
unsigned int column;
unsigned int canon_column;
size_t echo_tail;
struct mutex atomic_read_lock;
struct mutex output_lock;
};
#define MASK(x) ((x) & (N_TTY_BUF_SIZE - 1))
static inline size_t read_cnt(struct n_tty_data *ldata)
{
return ldata->read_head - ldata->read_tail;
}
static inline unsigned char read_buf(struct n_tty_data *ldata, size_t i)
{
return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
}
static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i)
{
return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
}
static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i)
{
smp_rmb(); /* Matches smp_wmb() in add_echo_byte(). */
return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)];
}
static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i)
{
return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)];
}
/* If we are not echoing the data, perhaps this is a secret so erase it */
static void zero_buffer(struct tty_struct *tty, u8 *buffer, int size)
{
bool icanon = !!L_ICANON(tty);
bool no_echo = !L_ECHO(tty);
if (icanon && no_echo)
memset(buffer, 0x00, size);
}
static void tty_copy(struct tty_struct *tty, void *to, size_t tail, size_t n)
{
struct n_tty_data *ldata = tty->disc_data;
size_t size = N_TTY_BUF_SIZE - tail;
void *from = read_buf_addr(ldata, tail);
if (n > size) {
tty_audit_add_data(tty, from, size);
memcpy(to, from, size);
zero_buffer(tty, from, size);
to += size;
n -= size;
from = ldata->read_buf;
}
tty_audit_add_data(tty, from, n);
memcpy(to, from, n);
zero_buffer(tty, from, n);
}
/**
* n_tty_kick_worker - start input worker (if required)
* @tty: terminal
*
* Re-schedules the flip buffer work if it may have stopped.
*
* Locking:
* * Caller holds exclusive %termios_rwsem, or
* * n_tty_read()/consumer path:
* holds non-exclusive %termios_rwsem
*/
static void n_tty_kick_worker(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
/* Did the input worker stop? Restart it */
if (unlikely(ldata->no_room)) {
ldata->no_room = 0;
WARN_RATELIMIT(tty->port->itty == NULL,
"scheduling with invalid itty\n");
/* see if ldisc has been killed - if so, this means that
* even though the ldisc has been halted and ->buf.work
* cancelled, ->buf.work is about to be rescheduled
*/
WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags),
"scheduling buffer work for halted ldisc\n");
tty_buffer_restart_work(tty->port);
}
}
static ssize_t chars_in_buffer(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
ssize_t n = 0;
if (!ldata->icanon)
n = ldata->commit_head - ldata->read_tail;
else
n = ldata->canon_head - ldata->read_tail;
return n;
}
/**
* n_tty_write_wakeup - asynchronous I/O notifier
* @tty: tty device
*
* Required for the ptys, serial driver etc. since processes that attach
* themselves to the master and rely on ASYNC IO must be woken up.
*/
static void n_tty_write_wakeup(struct tty_struct *tty)
{
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
}
static void n_tty_check_throttle(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
/*
* Check the remaining room for the input canonicalization
* mode. We don't want to throttle the driver if we're in
* canonical mode and don't have a newline yet!
*/
if (ldata->icanon && ldata->canon_head == ldata->read_tail)
return;
while (1) {
int throttled;
tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
if (N_TTY_BUF_SIZE - read_cnt(ldata) >= TTY_THRESHOLD_THROTTLE)
break;
throttled = tty_throttle_safe(tty);
if (!throttled)
break;
}
__tty_set_flow_change(tty, 0);
}
static void n_tty_check_unthrottle(struct tty_struct *tty)
{
if (tty->driver->type == TTY_DRIVER_TYPE_PTY) {
if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
return;
n_tty_kick_worker(tty);
tty_wakeup(tty->link);
return;
}
/* If there is enough space in the read buffer now, let the
* low-level driver know. We use chars_in_buffer() to
* check the buffer, as it now knows about canonical mode.
* Otherwise, if the driver is throttled and the line is
* longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
* we won't get any more characters.
*/
while (1) {
int unthrottled;
tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
break;
n_tty_kick_worker(tty);
unthrottled = tty_unthrottle_safe(tty);
if (!unthrottled)
break;
}
__tty_set_flow_change(tty, 0);
}
/**
* put_tty_queue - add character to tty
* @c: character
* @ldata: n_tty data
*
* Add a character to the tty read_buf queue.
*
* Locking:
* * n_tty_receive_buf()/producer path:
* caller holds non-exclusive %termios_rwsem
*/
static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
{
*read_buf_addr(ldata, ldata->read_head) = c;
ldata->read_head++;
}
/**
* reset_buffer_flags - reset buffer state
* @ldata: line disc data to reset
*
* Reset the read buffer counters and clear the flags. Called from
* n_tty_open() and n_tty_flush_buffer().
*
* Locking:
* * caller holds exclusive %termios_rwsem, or
* * (locking is not required)
*/
static void reset_buffer_flags(struct n_tty_data *ldata)
{
ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
ldata->commit_head = 0;
ldata->line_start = 0;
ldata->erasing = 0;
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
ldata->push = 0;
}
static void n_tty_packet_mode_flush(struct tty_struct *tty)
{
unsigned long flags;
if (tty->link->ctrl.packet) {
spin_lock_irqsave(&tty->ctrl.lock, flags);
tty->ctrl.pktstatus |= TIOCPKT_FLUSHREAD;
spin_unlock_irqrestore(&tty->ctrl.lock, flags);
wake_up_interruptible(&tty->link->read_wait);
}
}
/**
* n_tty_flush_buffer - clean input queue
* @tty: terminal device
*
* Flush the input buffer. Called when the tty layer wants the buffer flushed
* (eg at hangup) or when the %N_TTY line discipline internally has to clean
* the pending queue (for example some signals).
*
* Holds %termios_rwsem to exclude producer/consumer while buffer indices are
* reset.
*
* Locking: %ctrl.lock, exclusive %termios_rwsem
*/
static void n_tty_flush_buffer(struct tty_struct *tty)
{
down_write(&tty->termios_rwsem);
reset_buffer_flags(tty->disc_data);
n_tty_kick_worker(tty);
if (tty->link)
n_tty_packet_mode_flush(tty);
up_write(&tty->termios_rwsem);
}
/**
* is_utf8_continuation - utf8 multibyte check
* @c: byte to check
*
* Returns: true if the utf8 character @c is a multibyte continuation
* character. We use this to correctly compute the on-screen size of the
* character when printing.
*/
static inline int is_utf8_continuation(unsigned char c)
{
return (c & 0xc0) == 0x80;
}
/**
* is_continuation - multibyte check
* @c: byte to check
* @tty: terminal device
*
* Returns: true if the utf8 character @c is a multibyte continuation character
* and the terminal is in unicode mode.
*/
static inline int is_continuation(unsigned char c, struct tty_struct *tty)
{
return I_IUTF8(tty) && is_utf8_continuation(c);
}
/**
* do_output_char - output one character
* @c: character (or partial unicode symbol)
* @tty: terminal device
* @space: space available in tty driver write buffer
*
* This is a helper function that handles one output character (including
* special characters like TAB, CR, LF, etc.), doing OPOST processing and
* putting the results in the tty driver's write buffer.
*
* Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY and NLDLY.
* They simply aren't relevant in the world today. If you ever need them, add
* them here.
*
* Returns: the number of bytes of buffer space used or -1 if no space left.
*
* Locking: should be called under the %output_lock to protect the column state
* and space left in the buffer.
*/
static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
{
struct n_tty_data *ldata = tty->disc_data;
int spaces;
if (!space)
return -1;
switch (c) {
case '\n':
if (O_ONLRET(tty))
ldata->column = 0;
if (O_ONLCR(tty)) {
if (space < 2)
return -1;
ldata->canon_column = ldata->column = 0;
tty->ops->write(tty, "\r\n", 2);
return 2;
}
ldata->canon_column = ldata->column;
break;
case '\r':
if (O_ONOCR(tty) && ldata->column == 0)
return 0;
if (O_OCRNL(tty)) {
c = '\n';
if (O_ONLRET(tty))
ldata->canon_column = ldata->column = 0;
break;
}
ldata->canon_column = ldata->column = 0;
break;
case '\t':
spaces = 8 - (ldata->column & 7);
if (O_TABDLY(tty) == XTABS) {
if (space < spaces)
return -1;
ldata->column += spaces;
tty->ops->write(tty, " ", spaces);
return spaces;
}
ldata->column += spaces;
break;
case '\b':
if (ldata->column > 0)
ldata->column--;
break;
default:
if (!iscntrl(c)) {
if (O_OLCUC(tty))
c = toupper(c);
if (!is_continuation(c, tty))
ldata->column++;
}
break;
}
tty_put_char(tty, c);
return 1;
}
/**
* process_output - output post processor
* @c: character (or partial unicode symbol)
* @tty: terminal device
*
* Output one character with OPOST processing.
*
* Returns: -1 when the output device is full and the character must be
* retried.
*
* Locking: %output_lock to protect column state and space left (also, this is
*called from n_tty_write() under the tty layer write lock).
*/
static int process_output(unsigned char c, struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
int space, retval;
mutex_lock(&ldata->output_lock);
space = tty_write_room(tty);
retval = do_output_char(c, tty, space);
mutex_unlock(&ldata->output_lock);
if (retval < 0)
return -1;
else
return 0;
}
/**
* process_output_block - block post processor
* @tty: terminal device
* @buf: character buffer
* @nr: number of bytes to output
*
* Output a block of characters with OPOST processing.
*
* This path is used to speed up block console writes, among other things when
* processing blocks of output data. It handles only the simple cases normally
* found and helps to generate blocks of symbols for the console driver and
* thus improve performance.
*
* Returns: the number of characters output.
*
* Locking: %output_lock to protect column state and space left (also, this is
* called from n_tty_write() under the tty layer write lock).
*/
static ssize_t process_output_block(struct tty_struct *tty,
const unsigned char *buf, unsigned int nr)
{
struct n_tty_data *ldata = tty->disc_data;
int space;
int i;
const unsigned char *cp;
mutex_lock(&ldata->output_lock);
space = tty_write_room(tty);
if (space <= 0) {
mutex_unlock(&ldata->output_lock);
return space;
}
if (nr > space)
nr = space;
for (i = 0, cp = buf; i < nr; i++, cp++) {
unsigned char c = *cp;
switch (c) {
case '\n':
if (O_ONLRET(tty))
ldata->column = 0;
if (O_ONLCR(tty))
goto break_out;
ldata->canon_column = ldata->column;
break;
case '\r':
if (O_ONOCR(tty) && ldata->column == 0)
goto break_out;
if (O_OCRNL(tty))
goto break_out;
ldata->canon_column = ldata->column = 0;
break;
case '\t':
goto break_out;
case '\b':
if (ldata->column > 0)
ldata->column--;
break;
default:
if (!iscntrl(c)) {
if (O_OLCUC(tty))
goto break_out;
if (!is_continuation(c, tty))
ldata->column++;
}
break;
}
}
break_out:
i = tty->ops->write(tty, buf, i);
mutex_unlock(&ldata->output_lock);
return i;
}
/**
* __process_echoes - write pending echo characters
* @tty: terminal device
*
* Write previously buffered echo (and other ldisc-generated) characters to the
* tty.
*
* Characters generated by the ldisc (including echoes) need to be buffered
* because the driver's write buffer can fill during heavy program output.
* Echoing straight to the driver will often fail under these conditions,
* causing lost characters and resulting mismatches of ldisc state information.
*
* Since the ldisc state must represent the characters actually sent to the
* driver at the time of the write, operations like certain changes in column
* state are also saved in the buffer and executed here.
*
* A circular fifo buffer is used so that the most recent characters are
* prioritized. Also, when control characters are echoed with a prefixed "^",
* the pair is treated atomically and thus not separated.
*
* Locking: callers must hold %output_lock.
*/
static size_t __process_echoes(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
int space, old_space;
size_t tail;
unsigned char c;
old_space = space = tty_write_room(tty);
tail = ldata->echo_tail;
while (MASK(ldata->echo_commit) != MASK(tail)) {
c = echo_buf(ldata, tail);
if (c == ECHO_OP_START) {
unsigned char op;
int no_space_left = 0;
/*
* Since add_echo_byte() is called without holding
* output_lock, we might see only portion of multi-byte
* operation.
*/
if (MASK(ldata->echo_commit) == MASK(tail + 1))
goto not_yet_stored;
/*
* If the buffer byte is the start of a multi-byte
* operation, get the next byte, which is either the
* op code or a control character value.
*/
op = echo_buf(ldata, tail + 1);
switch (op) {
case ECHO_OP_ERASE_TAB: {
unsigned int num_chars, num_bs;
if (MASK(ldata->echo_commit) == MASK(tail + 2))
goto not_yet_stored;
num_chars = echo_buf(ldata, tail + 2);
/*
* Determine how many columns to go back
* in order to erase the tab.
* This depends on the number of columns
* used by other characters within the tab
* area. If this (modulo 8) count is from
* the start of input rather than from a
* previous tab, we offset by canon column.
* Otherwise, tab spacing is normal.
*/
if (!(num_chars & 0x80))
num_chars += ldata->canon_column;
num_bs = 8 - (num_chars & 7);
if (num_bs > space) {
no_space_left = 1;
break;
}
space -= num_bs;
while (num_bs--) {
tty_put_char(tty, '\b');
if (ldata->column > 0)
ldata->column--;
}
tail += 3;
break;
}
case ECHO_OP_SET_CANON_COL:
ldata->canon_column = ldata->column;
tail += 2;
break;
case ECHO_OP_MOVE_BACK_COL:
if (ldata->column > 0)
ldata->column--;
tail += 2;
break;
case ECHO_OP_START:
/* This is an escaped echo op start code */
if (!space) {
no_space_left = 1;
break;
}
tty_put_char(tty, ECHO_OP_START);
ldata->column++;
space--;
tail += 2;
break;
default:
/*
* If the op is not a special byte code,
* it is a ctrl char tagged to be echoed
* as "^X" (where X is the letter
* representing the control char).
* Note that we must ensure there is
* enough space for the whole ctrl pair.
*
*/
if (space < 2) {
no_space_left = 1;
break;
}
tty_put_char(tty, '^');
tty_put_char(tty, op ^ 0100);
ldata->column += 2;
space -= 2;
tail += 2;
}
if (no_space_left)
break;
} else {
if (O_OPOST(tty)) {
int retval = do_output_char(c, tty, space);
if (retval < 0)
break;
space -= retval;
} else {
if (!space)
break;
tty_put_char(tty, c);
space -= 1;
}
tail += 1;
}
}
/* If the echo buffer is nearly full (so that the possibility exists
* of echo overrun before the next commit), then discard enough
* data at the tail to prevent a subsequent overrun */
while (ldata->echo_commit > tail &&
ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) {
if (echo_buf(ldata, tail) == ECHO_OP_START) {
if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB)
tail += 3;
else
tail += 2;
} else
tail++;
}
not_yet_stored:
ldata->echo_tail = tail;
return old_space - space;
}
static void commit_echoes(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
size_t nr, old, echoed;
size_t head;
mutex_lock(&ldata->output_lock);
head = ldata->echo_head;
ldata->echo_mark = head;
old = ldata->echo_commit - ldata->echo_tail;
/* Process committed echoes if the accumulated # of bytes
* is over the threshold (and try again each time another
* block is accumulated) */
nr = head - ldata->echo_tail;
if (nr < ECHO_COMMIT_WATERMARK ||
(nr % ECHO_BLOCK > old % ECHO_BLOCK)) {
mutex_unlock(&ldata->output_lock);
return;
}
ldata->echo_commit = head;
echoed = __process_echoes(tty);
mutex_unlock(&ldata->output_lock);
if (echoed && tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
static void process_echoes(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
size_t echoed;
if (ldata->echo_mark == ldata->echo_tail)
return;
mutex_lock(&ldata->output_lock);
ldata->echo_commit = ldata->echo_mark;
echoed = __process_echoes(tty);
mutex_unlock(&ldata->output_lock);
if (echoed && tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
/* NB: echo_mark and echo_head should be equivalent here */
static void flush_echoes(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
if ((!L_ECHO(tty) && !L_ECHONL(tty)) ||
ldata->echo_commit == ldata->echo_head)
return;
mutex_lock(&ldata->output_lock);
ldata->echo_commit = ldata->echo_head;
__process_echoes(tty);
mutex_unlock(&ldata->output_lock);
}
/**
* add_echo_byte - add a byte to the echo buffer
* @c: unicode byte to echo
* @ldata: n_tty data
*
* Add a character or operation byte to the echo buffer.
*/
static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
{
*echo_buf_addr(ldata, ldata->echo_head) = c;
smp_wmb(); /* Matches smp_rmb() in echo_buf(). */
ldata->echo_head++;
}
/**
* echo_move_back_col - add operation to move back a column
* @ldata: n_tty data
*
* Add an operation to the echo buffer to move back one column.
*/
static void echo_move_back_col(struct n_tty_data *ldata)
{
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata);
}
/**
* echo_set_canon_col - add operation to set the canon column
* @ldata: n_tty data
*
* Add an operation to the echo buffer to set the canon column to the current
* column.
*/
static void echo_set_canon_col(struct n_tty_data *ldata)
{
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_SET_CANON_COL, ldata);
}
/**
* echo_erase_tab - add operation to erase a tab
* @num_chars: number of character columns already used
* @after_tab: true if num_chars starts after a previous tab
* @ldata: n_tty data
*
* Add an operation to the echo buffer to erase a tab.
*
* Called by the eraser function, which knows how many character columns have
* been used since either a previous tab or the start of input. This
* information will be used later, along with canon column (if applicable), to
* go back the correct number of columns.
*/
static void echo_erase_tab(unsigned int num_chars, int after_tab,
struct n_tty_data *ldata)
{
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_ERASE_TAB, ldata);
/* We only need to know this modulo 8 (tab spacing) */
num_chars &= 7;
/* Set the high bit as a flag if num_chars is after a previous tab */
if (after_tab)
num_chars |= 0x80;
add_echo_byte(num_chars, ldata);
}
/**
* echo_char_raw - echo a character raw
* @c: unicode byte to echo
* @ldata: line disc data
*
* Echo user input back onto the screen. This must be called only when
* L_ECHO(tty) is true. Called from the &tty_driver.receive_buf() path.
*
* This variant does not treat control characters specially.
*/
static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
{
if (c == ECHO_OP_START) {
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_START, ldata);
} else {
add_echo_byte(c, ldata);
}
}
/**
* echo_char - echo a character
* @c: unicode byte to echo
* @tty: terminal device
*
* Echo user input back onto the screen. This must be called only when
* L_ECHO(tty) is true. Called from the &tty_driver.receive_buf() path.
*
* This variant tags control characters to be echoed as "^X" (where X is the
* letter representing the control char).
*/
static void echo_char(unsigned char c, struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
if (c == ECHO_OP_START) {
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_START, ldata);
} else {
if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t')
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(c, ldata);
}
}
/**
* finish_erasing - complete erase
* @ldata: n_tty data
*/
static inline void finish_erasing(struct n_tty_data *ldata)
{
if (ldata->erasing) {
echo_char_raw('/', ldata);
ldata->erasing = 0;
}
}
/**
* eraser - handle erase function
* @c: character input
* @tty: terminal device
*
* Perform erase and necessary output when an erase character is present in the
* stream from the driver layer. Handles the complexities of UTF-8 multibyte
* symbols.
*
* Locking: n_tty_receive_buf()/producer path:
* caller holds non-exclusive %termios_rwsem
*/
static void eraser(unsigned char c, struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
enum { ERASE, WERASE, KILL } kill_type;
size_t head;
size_t cnt;
int seen_alnums;
if (ldata->read_head == ldata->canon_head) {
/* process_output('\a', tty); */ /* what do you think? */
return;
}
if (c == ERASE_CHAR(tty))
kill_type = ERASE;
else if (c == WERASE_CHAR(tty))
kill_type = WERASE;
else {
if (!L_ECHO(tty)) {
ldata->read_head = ldata->canon_head;
return;
}
if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
ldata->read_head = ldata->canon_head;
finish_erasing(ldata);
echo_char(KILL_CHAR(tty), tty);
/* Add a newline if ECHOK is on and ECHOKE is off. */
if (L_ECHOK(tty))
echo_char_raw('\n', ldata);
return;
}
kill_type = KILL;
}
seen_alnums = 0;
while (MASK(ldata->read_head) != MASK(ldata->canon_head)) {
head = ldata->read_head;
/* erase a single possibly multibyte character */
do {
head--;
c = read_buf(ldata, head);
} while (is_continuation(c, tty) &&
MASK(head) != MASK(ldata->canon_head));
/* do not partially erase */
if (is_continuation(c, tty))
break;