summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Pitre <nicolas.pitre@linaro.org>2018-06-26 23:56:41 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-06-28 08:38:12 -0400
commitd21b0be246bf3bbf569e6e239f56abb529c7154e (patch)
treed3d40eba5bb90d02875e55f49e624e9d3b7a82f9
parentd8ae7242718738ee1bf9bfdd632d2a4b150fdd26 (diff)
vt: introduce unicode mode for /dev/vcs
Now that the core vt code knows how to preserve unicode values for each displayed character, it is then possible to let user space access it via /dev/vcs*. Unicode characters are presented as 32 bit values in native endianity via the /dev/vcsu* devices, mimicking the simple /dev/vcs* devices. Unicode with attributes (similarly to /dev/vcsa*) is not supported at the moment. Data is available only as long as the console is in UTF-8 mode. ENODATA is returned otherwise. This was tested with the latest development version (to become version 5.7) of BRLTTY. Amongst other things, this allows ⠋⠕⠗ ⠞⠓⠊⠎ ⠃⠗⠁⠊⠇⠇⠑⠀⠞⠑⠭⠞⠀to appear directly on braille displays regardless of the console font being used. Signed-off-by: Nicolas Pitre <nico@linaro.org> Tested-by: Dave Mielke <Dave@mielke.cc> Acked-by: Adam Borowski <kilobyte@angband.pl> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/vt/vc_screen.c89
-rw-r--r--drivers/tty/vt/vt.c61
-rw-r--r--include/linux/selection.h5
3 files changed, 141 insertions, 14 deletions
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index e4a66e1fd05f..9c44252e52a3 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -10,6 +10,12 @@
10 * Attribute/character pair is in native endianity. 10 * Attribute/character pair is in native endianity.
11 * [minor: N+128] 11 * [minor: N+128]
12 * 12 *
13 * /dev/vcsuN: similar to /dev/vcsaN but using 4-byte unicode values
14 * instead of 1-byte screen glyph values.
15 * [minor: N+64]
16 *
17 * /dev/vcsuaN: same idea as /dev/vcsaN for unicode (not yet implemented).
18 *
13 * This replaces screendump and part of selection, so that the system 19 * This replaces screendump and part of selection, so that the system
14 * administrator can control access using file system permissions. 20 * administrator can control access using file system permissions.
15 * 21 *
@@ -51,6 +57,26 @@
51 57
52#define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE) 58#define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE)
53 59
60/*
61 * Our minor space:
62 *
63 * 0 ... 63 glyph mode without attributes
64 * 64 ... 127 unicode mode without attributes
65 * 128 ... 191 glyph mode with attributes
66 * 192 ... 255 unused (reserved for unicode with attributes)
67 *
68 * This relies on MAX_NR_CONSOLES being <= 63, meaning 63 actual consoles
69 * with minors 0, 64, 128 and 192 being proxies for the foreground console.
70 */
71#if MAX_NR_CONSOLES > 63
72#warning "/dev/vcs* devices may not accommodate more than 63 consoles"
73#endif
74
75#define console(inode) (iminor(inode) & 63)
76#define use_unicode(inode) (iminor(inode) & 64)
77#define use_attributes(inode) (iminor(inode) & 128)
78
79
54struct vcs_poll_data { 80struct vcs_poll_data {
55 struct notifier_block notifier; 81 struct notifier_block notifier;
56 unsigned int cons_num; 82 unsigned int cons_num;
@@ -102,7 +128,7 @@ vcs_poll_data_get(struct file *file)
102 poll = kzalloc(sizeof(*poll), GFP_KERNEL); 128 poll = kzalloc(sizeof(*poll), GFP_KERNEL);
103 if (!poll) 129 if (!poll)
104 return NULL; 130 return NULL;
105 poll->cons_num = iminor(file_inode(file)) & 127; 131 poll->cons_num = console(file_inode(file));
106 init_waitqueue_head(&poll->waitq); 132 init_waitqueue_head(&poll->waitq);
107 poll->notifier.notifier_call = vcs_notifier; 133 poll->notifier.notifier_call = vcs_notifier;
108 if (register_vt_notifier(&poll->notifier) != 0) { 134 if (register_vt_notifier(&poll->notifier) != 0) {
@@ -140,7 +166,7 @@ vcs_poll_data_get(struct file *file)
140static struct vc_data* 166static struct vc_data*
141vcs_vc(struct inode *inode, int *viewed) 167vcs_vc(struct inode *inode, int *viewed)
142{ 168{
143 unsigned int currcons = iminor(inode) & 127; 169 unsigned int currcons = console(inode);
144 170
145 WARN_CONSOLE_UNLOCKED(); 171 WARN_CONSOLE_UNLOCKED();
146 172
@@ -164,7 +190,6 @@ static int
164vcs_size(struct inode *inode) 190vcs_size(struct inode *inode)
165{ 191{
166 int size; 192 int size;
167 int minor = iminor(inode);
168 struct vc_data *vc; 193 struct vc_data *vc;
169 194
170 WARN_CONSOLE_UNLOCKED(); 195 WARN_CONSOLE_UNLOCKED();
@@ -175,8 +200,12 @@ vcs_size(struct inode *inode)
175 200
176 size = vc->vc_rows * vc->vc_cols; 201 size = vc->vc_rows * vc->vc_cols;
177 202
178 if (minor & 128) 203 if (use_attributes(inode)) {
204 if (use_unicode(inode))
205 return -EOPNOTSUPP;
179 size = 2*size + HEADER_SIZE; 206 size = 2*size + HEADER_SIZE;
207 } else if (use_unicode(inode))
208 size *= 4;
180 return size; 209 return size;
181} 210}
182 211
@@ -197,12 +226,10 @@ static ssize_t
197vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 226vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
198{ 227{
199 struct inode *inode = file_inode(file); 228 struct inode *inode = file_inode(file);
200 unsigned int currcons = iminor(inode);
201 struct vc_data *vc; 229 struct vc_data *vc;
202 struct vcs_poll_data *poll; 230 struct vcs_poll_data *poll;
203 long pos; 231 long pos, read;
204 long attr, read; 232 int attr, uni_mode, row, col, maxcol, viewed;
205 int col, maxcol, viewed;
206 unsigned short *org = NULL; 233 unsigned short *org = NULL;
207 ssize_t ret; 234 ssize_t ret;
208 char *con_buf; 235 char *con_buf;
@@ -218,7 +245,8 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
218 */ 245 */
219 console_lock(); 246 console_lock();
220 247
221 attr = (currcons & 128); 248 uni_mode = use_unicode(inode);
249 attr = use_attributes(inode);
222 ret = -ENXIO; 250 ret = -ENXIO;
223 vc = vcs_vc(inode, &viewed); 251 vc = vcs_vc(inode, &viewed);
224 if (!vc) 252 if (!vc)
@@ -227,6 +255,10 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
227 ret = -EINVAL; 255 ret = -EINVAL;
228 if (pos < 0) 256 if (pos < 0)
229 goto unlock_out; 257 goto unlock_out;
258 /* we enforce 32-bit alignment for pos and count in unicode mode */
259 if (uni_mode && (pos | count) & 3)
260 goto unlock_out;
261
230 poll = file->private_data; 262 poll = file->private_data;
231 if (count && poll) 263 if (count && poll)
232 poll->seen_last_update = true; 264 poll->seen_last_update = true;
@@ -266,7 +298,27 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
266 con_buf_start = con_buf0 = con_buf; 298 con_buf_start = con_buf0 = con_buf;
267 orig_count = this_round; 299 orig_count = this_round;
268 maxcol = vc->vc_cols; 300 maxcol = vc->vc_cols;
269 if (!attr) { 301 if (uni_mode) {
302 unsigned int nr;
303
304 ret = vc_uniscr_check(vc);
305 if (ret)
306 break;
307 p /= 4;
308 row = p / vc->vc_cols;
309 col = p % maxcol;
310 nr = maxcol - col;
311 do {
312 if (nr > this_round/4)
313 nr = this_round/4;
314 vc_uniscr_copy_line(vc, con_buf0, row, col, nr);
315 con_buf0 += nr * 4;
316 this_round -= nr * 4;
317 row++;
318 col = 0;
319 nr = maxcol;
320 } while (this_round);
321 } else if (!attr) {
270 org = screen_pos(vc, p, viewed); 322 org = screen_pos(vc, p, viewed);
271 col = p % maxcol; 323 col = p % maxcol;
272 p += maxcol - col; 324 p += maxcol - col;
@@ -375,7 +427,6 @@ static ssize_t
375vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 427vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
376{ 428{
377 struct inode *inode = file_inode(file); 429 struct inode *inode = file_inode(file);
378 unsigned int currcons = iminor(inode);
379 struct vc_data *vc; 430 struct vc_data *vc;
380 long pos; 431 long pos;
381 long attr, size, written; 432 long attr, size, written;
@@ -396,7 +447,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
396 */ 447 */
397 console_lock(); 448 console_lock();
398 449
399 attr = (currcons & 128); 450 attr = use_attributes(inode);
400 ret = -ENXIO; 451 ret = -ENXIO;
401 vc = vcs_vc(inode, &viewed); 452 vc = vcs_vc(inode, &viewed);
402 if (!vc) 453 if (!vc)
@@ -593,9 +644,15 @@ vcs_fasync(int fd, struct file *file, int on)
593static int 644static int
594vcs_open(struct inode *inode, struct file *filp) 645vcs_open(struct inode *inode, struct file *filp)
595{ 646{
596 unsigned int currcons = iminor(inode) & 127; 647 unsigned int currcons = console(inode);
648 bool attr = use_attributes(inode);
649 bool uni_mode = use_unicode(inode);
597 int ret = 0; 650 int ret = 0;
598 651
652 /* we currently don't support attributes in unicode mode */
653 if (attr && uni_mode)
654 return -EOPNOTSUPP;
655
599 console_lock(); 656 console_lock();
600 if(currcons && !vc_cons_allocated(currcons-1)) 657 if(currcons && !vc_cons_allocated(currcons-1))
601 ret = -ENXIO; 658 ret = -ENXIO;
@@ -628,6 +685,8 @@ void vcs_make_sysfs(int index)
628{ 685{
629 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL, 686 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,
630 "vcs%u", index + 1); 687 "vcs%u", index + 1);
688 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL,
689 "vcsu%u", index + 1);
631 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL, 690 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,
632 "vcsa%u", index + 1); 691 "vcsa%u", index + 1);
633} 692}
@@ -635,6 +694,7 @@ void vcs_make_sysfs(int index)
635void vcs_remove_sysfs(int index) 694void vcs_remove_sysfs(int index)
636{ 695{
637 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1)); 696 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1));
697 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 65));
638 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129)); 698 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129));
639} 699}
640 700
@@ -647,6 +707,7 @@ int __init vcs_init(void)
647 vc_class = class_create(THIS_MODULE, "vc"); 707 vc_class = class_create(THIS_MODULE, "vc");
648 708
649 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); 709 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
710 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu");
650 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); 711 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
651 for (i = 0; i < MIN_NR_CONSOLES; i++) 712 for (i = 0; i < MIN_NR_CONSOLES; i++)
652 vcs_make_sysfs(i); 713 vcs_make_sysfs(i);
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 7b636638b36d..062ce6be7957 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -481,6 +481,67 @@ static void vc_uniscr_copy_area(struct uni_screen *dst,
481 } 481 }
482} 482}
483 483
484/*
485 * Called from vcs_read() to make sure unicode screen retrieval is possible.
486 * This will initialize the unicode screen buffer if not already done.
487 * This returns 0 if OK, or a negative error code otherwise.
488 * In particular, -ENODATA is returned if the console is not in UTF-8 mode.
489 */
490int vc_uniscr_check(struct vc_data *vc)
491{
492 struct uni_screen *uniscr;
493 unsigned short *p;
494 int x, y, mask;
495
496 if (__is_defined(NO_VC_UNI_SCREEN))
497 return -EOPNOTSUPP;
498
499 WARN_CONSOLE_UNLOCKED();
500
501 if (!vc->vc_utf)
502 return -ENODATA;
503
504 if (vc->vc_uni_screen)
505 return 0;
506
507 uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
508 if (!uniscr)
509 return -ENOMEM;
510
511 /*
512 * Let's populate it initially with (imperfect) reverse translation.
513 * This is the next best thing we can do short of having it enabled
514 * from the start even when no users rely on this functionality. True
515 * unicode content will be available after a complete screen refresh.
516 */
517 p = (unsigned short *)vc->vc_origin;
518 mask = vc->vc_hi_font_mask | 0xff;
519 for (y = 0; y < vc->vc_rows; y++) {
520 char32_t *line = uniscr->lines[y];
521 for (x = 0; x < vc->vc_cols; x++) {
522 u16 glyph = scr_readw(p++) & mask;
523 line[x] = inverse_translate(vc, glyph, true);
524 }
525 }
526
527 vc->vc_uni_screen = uniscr;
528 return 0;
529}
530
531/*
532 * Called from vcs_read() to get the unicode data from the screen.
533 * This must be preceded by a successful call to vc_uniscr_check() once
534 * the console lock has been taken.
535 */
536void vc_uniscr_copy_line(struct vc_data *vc, void *dest,
537 unsigned int row, unsigned int col, unsigned int nr)
538{
539 struct uni_screen *uniscr = get_vc_uniscr(vc);
540
541 BUG_ON(!uniscr);
542 memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t));
543}
544
484 545
485static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, 546static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
486 enum con_scroll dir, unsigned int nr) 547 enum con_scroll dir, unsigned int nr)
diff --git a/include/linux/selection.h b/include/linux/selection.h
index 5b278ce99d8d..2b34df9f1e26 100644
--- a/include/linux/selection.h
+++ b/include/linux/selection.h
@@ -42,4 +42,9 @@ extern u16 vcs_scr_readw(struct vc_data *vc, const u16 *org);
42extern void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org); 42extern void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org);
43extern void vcs_scr_updated(struct vc_data *vc); 43extern void vcs_scr_updated(struct vc_data *vc);
44 44
45extern int vc_uniscr_check(struct vc_data *vc);
46extern void vc_uniscr_copy_line(struct vc_data *vc, void *dest,
47 unsigned int row, unsigned int col,
48 unsigned int nr);
49
45#endif 50#endif