Commit 04c71976500352d02f60616d2b960267d8c5fe24

Authored by Samuel Thibault
Committed by Linus Torvalds
1 parent abdbf94d7c

unicode diacritics support

There have been issues with non-latin1 diacritics and unicode.
http://bugzilla.kernel.org/show_bug.cgi?id=7746

Git 759448f459234bfcf34b82471f0dba77a9aca498 `Kernel utf-8 handling'
partly resolved it by adding conversion between diacritics and
unicode. The patch below goes further by just turning diacritics into
unicode, hence providing better future support. The kbd support can be
fetched from
http://bugzilla.kernel.org/attachment.cgi?id=12313

This was tested in all of latin1, latin9, latin2 and unicode with french
and czech dead keys.

Turn the kernel accent_table into unicode, and extend ioctls KDGKBDIACR
and KDSKBDIACR into their equivalents KDGKBDIACRUC and KDSKBDIACR.

New function int conv_uni_to_8bit(u32 uni) for converting unicode into 8bit
_input_.  No, we don't want to store the translation, as it is potentially
sparse and large.

Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Cc: Jan Engelhardt <jengelh@gmx.de>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 12 changed files with 158 additions and 36 deletions Side-by-side Diff

drivers/acorn/char/defkeymap-l7200.c
... ... @@ -346,7 +346,7 @@
346 346 0,
347 347 };
348 348  
349   -struct kbdiacr accent_table[MAX_DIACR] = {
  349 +struct kbdiacruc accent_table[MAX_DIACR] = {
350 350 {'`', 'A', '\300'}, {'`', 'a', '\340'},
351 351 {'\'', 'A', '\301'}, {'\'', 'a', '\341'},
352 352 {'^', 'A', '\302'}, {'^', 'a', '\342'},
drivers/char/consolemap.c
... ... @@ -669,17 +669,27 @@
669 669 p->readonly = rdonly;
670 670 }
671 671  
  672 +/*
  673 + * Always use USER_MAP. These functions are used by the keyboard,
  674 + * which shouldn't be affected by G0/G1 switching, etc.
  675 + * If the user map still contains default values, i.e. the
  676 + * direct-to-font mapping, then assume user is using Latin1.
  677 + */
672 678 /* may be called during an interrupt */
673 679 u32 conv_8bit_to_uni(unsigned char c)
674 680 {
675   - /*
676   - * Always use USER_MAP. This function is used by the keyboard,
677   - * which shouldn't be affected by G0/G1 switching, etc.
678   - * If the user map still contains default values, i.e. the
679   - * direct-to-font mapping, then assume user is using Latin1.
680   - */
681 681 unsigned short uni = translations[USER_MAP][c];
682 682 return uni == (0xf000 | c) ? c : uni;
  683 +}
  684 +
  685 +int conv_uni_to_8bit(u32 uni)
  686 +{
  687 + int c;
  688 + for (c = 0; c < 0x100; c++)
  689 + if (translations[USER_MAP][c] == uni ||
  690 + (translations[USER_MAP][c] == (c | 0xf000) && uni == c))
  691 + return c;
  692 + return -1;
683 693 }
684 694  
685 695 int
drivers/char/defkeymap.c_shipped
... ... @@ -222,7 +222,7 @@
222 222 NULL,
223 223 };
224 224  
225   -struct kbdiacr accent_table[MAX_DIACR] = {
  225 +struct kbdiacruc accent_table[MAX_DIACR] = {
226 226 {'`', 'A', '\300'}, {'`', 'a', '\340'},
227 227 {'\'', 'A', '\301'}, {'\'', 'a', '\341'},
228 228 {'^', 'A', '\302'}, {'^', 'a', '\342'},
drivers/char/keyboard.c
... ... @@ -38,6 +38,7 @@
38 38 #include <linux/kbd_kern.h>
39 39 #include <linux/kbd_diacr.h>
40 40 #include <linux/vt_kern.h>
  41 +#include <linux/consolemap.h>
41 42 #include <linux/sysrq.h>
42 43 #include <linux/input.h>
43 44 #include <linux/reboot.h>
... ... @@ -403,9 +404,12 @@
403 404 return d;
404 405  
405 406 if (kbd->kbdmode == VC_UNICODE)
406   - to_utf8(vc, conv_8bit_to_uni(d));
407   - else if (d < 0x100)
408   - put_queue(vc, d);
  407 + to_utf8(vc, d);
  408 + else {
  409 + int c = conv_uni_to_8bit(d);
  410 + if (c != -1)
  411 + put_queue(vc, c);
  412 + }
409 413  
410 414 return ch;
411 415 }
... ... @@ -417,9 +421,12 @@
417 421 {
418 422 if (diacr) {
419 423 if (kbd->kbdmode == VC_UNICODE)
420   - to_utf8(vc, conv_8bit_to_uni(diacr));
421   - else if (diacr < 0x100)
422   - put_queue(vc, diacr);
  424 + to_utf8(vc, diacr);
  425 + else {
  426 + int c = conv_uni_to_8bit(diacr);
  427 + if (c != -1)
  428 + put_queue(vc, c);
  429 + }
423 430 diacr = 0;
424 431 }
425 432 put_queue(vc, 13);
... ... @@ -627,9 +634,12 @@
627 634 return;
628 635 }
629 636 if (kbd->kbdmode == VC_UNICODE)
630   - to_utf8(vc, conv_8bit_to_uni(value));
631   - else if (value < 0x100)
632   - put_queue(vc, value);
  637 + to_utf8(vc, value);
  638 + else {
  639 + int c = conv_uni_to_8bit(value);
  640 + if (c != -1)
  641 + put_queue(vc, c);
  642 + }
633 643 }
634 644  
635 645 /*
... ... @@ -646,7 +656,12 @@
646 656  
647 657 static void k_self(struct vc_data *vc, unsigned char value, char up_flag)
648 658 {
649   - k_unicode(vc, value, up_flag);
  659 + unsigned int uni;
  660 + if (kbd->kbdmode == VC_UNICODE)
  661 + uni = value;
  662 + else
  663 + uni = conv_8bit_to_uni(value);
  664 + k_unicode(vc, uni, up_flag);
650 665 }
651 666  
652 667 static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag)
drivers/char/vt_ioctl.c
... ... @@ -23,6 +23,7 @@
23 23 #include <linux/major.h>
24 24 #include <linux/fs.h>
25 25 #include <linux/console.h>
  26 +#include <linux/consolemap.h>
26 27 #include <linux/signal.h>
27 28 #include <linux/timex.h>
28 29  
29 30  
30 31  
31 32  
32 33  
... ... @@ -582,18 +583,37 @@
582 583 case KDGKBDIACR:
583 584 {
584 585 struct kbdiacrs __user *a = up;
  586 + struct kbdiacr diacr;
  587 + int i;
585 588  
586 589 if (put_user(accent_table_size, &a->kb_cnt))
587 590 return -EFAULT;
588   - if (copy_to_user(a->kbdiacr, accent_table, accent_table_size*sizeof(struct kbdiacr)))
  591 + for (i = 0; i < accent_table_size; i++) {
  592 + diacr.diacr = conv_uni_to_8bit(accent_table[i].diacr);
  593 + diacr.base = conv_uni_to_8bit(accent_table[i].base);
  594 + diacr.result = conv_uni_to_8bit(accent_table[i].result);
  595 + if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr)))
  596 + return -EFAULT;
  597 + }
  598 + return 0;
  599 + }
  600 + case KDGKBDIACRUC:
  601 + {
  602 + struct kbdiacrsuc __user *a = up;
  603 +
  604 + if (put_user(accent_table_size, &a->kb_cnt))
589 605 return -EFAULT;
  606 + if (copy_to_user(a->kbdiacruc, accent_table, accent_table_size*sizeof(struct kbdiacruc)))
  607 + return -EFAULT;
590 608 return 0;
591 609 }
592 610  
593 611 case KDSKBDIACR:
594 612 {
595 613 struct kbdiacrs __user *a = up;
  614 + struct kbdiacr diacr;
596 615 unsigned int ct;
  616 + int i;
597 617  
598 618 if (!perm)
599 619 return -EPERM;
... ... @@ -602,7 +622,29 @@
602 622 if (ct >= MAX_DIACR)
603 623 return -EINVAL;
604 624 accent_table_size = ct;
605   - if (copy_from_user(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr)))
  625 + for (i = 0; i < ct; i++) {
  626 + if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr)))
  627 + return -EFAULT;
  628 + accent_table[i].diacr = conv_8bit_to_uni(diacr.diacr);
  629 + accent_table[i].base = conv_8bit_to_uni(diacr.base);
  630 + accent_table[i].result = conv_8bit_to_uni(diacr.result);
  631 + }
  632 + return 0;
  633 + }
  634 +
  635 + case KDSKBDIACRUC:
  636 + {
  637 + struct kbdiacrsuc __user *a = up;
  638 + unsigned int ct;
  639 +
  640 + if (!perm)
  641 + return -EPERM;
  642 + if (get_user(ct,&a->kb_cnt))
  643 + return -EFAULT;
  644 + if (ct >= MAX_DIACR)
  645 + return -EINVAL;
  646 + accent_table_size = ct;
  647 + if (copy_from_user(accent_table, a->kbdiacruc, ct*sizeof(struct kbdiacruc)))
606 648 return -EFAULT;
607 649 return 0;
608 650 }
drivers/s390/char/defkeymap.c
... ... @@ -150,7 +150,7 @@
150 150 NULL,
151 151 };
152 152  
153   -struct kbdiacr accent_table[MAX_DIACR] = {
  153 +struct kbdiacruc accent_table[MAX_DIACR] = {
154 154 {'^', 'c', '\003'}, {'^', 'd', '\004'},
155 155 {'^', 'z', '\032'}, {'^', '\012', '\000'},
156 156 };
drivers/s390/char/keyboard.c
... ... @@ -11,6 +11,7 @@
11 11 #include <linux/sched.h>
12 12 #include <linux/sysrq.h>
13 13  
  14 +#include <linux/consolemap.h>
14 15 #include <linux/kbd_kern.h>
15 16 #include <linux/kbd_diacr.h>
16 17 #include <asm/uaccess.h>
17 18  
... ... @@ -82,11 +83,11 @@
82 83 if (!kbd->fn_handler)
83 84 goto out_func;
84 85 kbd->accent_table =
85   - kmalloc(sizeof(struct kbdiacr)*MAX_DIACR, GFP_KERNEL);
  86 + kmalloc(sizeof(struct kbdiacruc)*MAX_DIACR, GFP_KERNEL);
86 87 if (!kbd->accent_table)
87 88 goto out_fn_handler;
88 89 memcpy(kbd->accent_table, accent_table,
89   - sizeof(struct kbdiacr)*MAX_DIACR);
  90 + sizeof(struct kbdiacruc)*MAX_DIACR);
90 91 kbd->accent_table_size = accent_table_size;
91 92 return kbd;
92 93  
... ... @@ -183,8 +184,8 @@
183 184 * Otherwise, conclude that DIACR was not combining after all,
184 185 * queue it and return CH.
185 186 */
186   -static unsigned char
187   -handle_diacr(struct kbd_data *kbd, unsigned char ch)
  187 +static unsigned int
  188 +handle_diacr(struct kbd_data *kbd, unsigned int ch)
188 189 {
189 190 int i, d;
190 191  
... ... @@ -460,7 +461,6 @@
460 461 kbd_ioctl(struct kbd_data *kbd, struct file *file,
461 462 unsigned int cmd, unsigned long arg)
462 463 {
463   - struct kbdiacrs __user *a;
464 464 void __user *argp;
465 465 int ct, perm;
466 466  
467 467  
468 468  
469 469  
470 470  
471 471  
... ... @@ -481,17 +481,40 @@
481 481 case KDSKBSENT:
482 482 return do_kdgkb_ioctl(kbd, argp, cmd, perm);
483 483 case KDGKBDIACR:
484   - a = argp;
  484 + {
  485 + struct kbdiacrs __user *a = argp;
  486 + struct kbdiacr diacr;
  487 + int i;
485 488  
486 489 if (put_user(kbd->accent_table_size, &a->kb_cnt))
487 490 return -EFAULT;
  491 + for (i = 0; i < kbd->accent_table_size; i++) {
  492 + diacr.diacr = kbd->accent_table[i].diacr;
  493 + diacr.base = kbd->accent_table[i].base;
  494 + diacr.result = kbd->accent_table[i].result;
  495 + if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr)))
  496 + return -EFAULT;
  497 + }
  498 + return 0;
  499 + }
  500 + case KDGKBDIACRUC:
  501 + {
  502 + struct kbdiacrsuc __user *a = argp;
  503 +
488 504 ct = kbd->accent_table_size;
489   - if (copy_to_user(a->kbdiacr, kbd->accent_table,
490   - ct * sizeof(struct kbdiacr)))
  505 + if (put_user(ct, &a->kb_cnt))
491 506 return -EFAULT;
  507 + if (copy_to_user(a->kbdiacruc, kbd->accent_table,
  508 + ct * sizeof(struct kbdiacruc)))
  509 + return -EFAULT;
492 510 return 0;
  511 + }
493 512 case KDSKBDIACR:
494   - a = argp;
  513 + {
  514 + struct kbdiacrs __user *a = argp;
  515 + struct kbdiacr diacr;
  516 + int i;
  517 +
495 518 if (!perm)
496 519 return -EPERM;
497 520 if (get_user(ct, &a->kb_cnt))
498 521  
499 522  
... ... @@ -499,10 +522,31 @@
499 522 if (ct >= MAX_DIACR)
500 523 return -EINVAL;
501 524 kbd->accent_table_size = ct;
502   - if (copy_from_user(kbd->accent_table, a->kbdiacr,
503   - ct * sizeof(struct kbdiacr)))
  525 + for (i = 0; i < ct; i++) {
  526 + if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr)))
  527 + return -EFAULT;
  528 + kbd->accent_table[i].diacr = diacr.diacr;
  529 + kbd->accent_table[i].base = diacr.base;
  530 + kbd->accent_table[i].result = diacr.result;
  531 + }
  532 + return 0;
  533 + }
  534 + case KDSKBDIACRUC:
  535 + {
  536 + struct kbdiacrsuc __user *a = argp;
  537 +
  538 + if (!perm)
  539 + return -EPERM;
  540 + if (get_user(ct, &a->kb_cnt))
504 541 return -EFAULT;
  542 + if (ct >= MAX_DIACR)
  543 + return -EINVAL;
  544 + kbd->accent_table_size = ct;
  545 + if (copy_from_user(kbd->accent_table, a->kbdiacruc,
  546 + ct * sizeof(struct kbdiacruc)))
  547 + return -EFAULT;
505 548 return 0;
  549 + }
506 550 default:
507 551 return -ENOIOCTLCMD;
508 552 }
drivers/s390/char/keyboard.h
... ... @@ -25,9 +25,9 @@
25 25 unsigned short **key_maps;
26 26 char **func_table;
27 27 fn_handler_fn **fn_handler;
28   - struct kbdiacr *accent_table;
  28 + struct kbdiacruc *accent_table;
29 29 unsigned int accent_table_size;
30   - unsigned char diacr;
  30 + unsigned int diacr;
31 31 unsigned short sysrq;
32 32 };
33 33  
drivers/tc/lk201-map.c_shipped
... ... @@ -225,7 +225,7 @@
225 225 0,
226 226 };
227 227  
228   -struct kbdiacr accent_table[MAX_DIACR] = {
  228 +struct kbdiacruc accent_table[MAX_DIACR] = {
229 229 {'`', 'A', 'Ŕ'}, {'`', 'a', 'ŕ'},
230 230 {'\'', 'A', 'Á'}, {'\'', 'a', 'á'},
231 231 {'^', 'A', 'Â'}, {'^', 'a', 'â'},
include/linux/consolemap.h
... ... @@ -16,5 +16,6 @@
16 16 extern unsigned short *set_translate(int m, struct vc_data *vc);
17 17 extern int conv_uni_to_pc(struct vc_data *conp, long ucs);
18 18 extern u32 conv_8bit_to_uni(unsigned char c);
  19 +extern int conv_uni_to_8bit(u32 uni);
19 20 void console_map_init(void);
include/linux/kbd_diacr.h
... ... @@ -2,7 +2,7 @@
2 2 #define _DIACR_H
3 3 #include <linux/kd.h>
4 4  
5   -extern struct kbdiacr accent_table[];
  5 +extern struct kbdiacruc accent_table[];
6 6 extern unsigned int accent_table_size;
7 7  
8 8 #endif /* _DIACR_H */
... ... @@ -125,6 +125,16 @@
125 125 #define KDGKBDIACR 0x4B4A /* read kernel accent table */
126 126 #define KDSKBDIACR 0x4B4B /* write kernel accent table */
127 127  
  128 +struct kbdiacruc {
  129 + __u32 diacr, base, result;
  130 +};
  131 +struct kbdiacrsuc {
  132 + unsigned int kb_cnt; /* number of entries in following array */
  133 + struct kbdiacruc kbdiacruc[256]; /* MAX_DIACR from keyboard.h */
  134 +};
  135 +#define KDGKBDIACRUC 0x4BFA /* read kernel accent table - UCS */
  136 +#define KDSKBDIACRUC 0x4BFB /* write kernel accent table - UCS */
  137 +
128 138 struct kbkeycode {
129 139 unsigned int scancode, keycode;
130 140 };