Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *    ebcdic keycode functions for s390 console drivers
0004  *
0005  *  S390 version
0006  *    Copyright IBM Corp. 2003
0007  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/sched/signal.h>
0012 #include <linux/slab.h>
0013 #include <linux/sysrq.h>
0014 
0015 #include <linux/consolemap.h>
0016 #include <linux/kbd_kern.h>
0017 #include <linux/kbd_diacr.h>
0018 #include <linux/uaccess.h>
0019 
0020 #include "keyboard.h"
0021 
0022 /*
0023  * Handler Tables.
0024  */
0025 #define K_HANDLERS\
0026     k_self,     k_fn,       k_spec,     k_ignore,\
0027     k_dead,     k_ignore,   k_ignore,   k_ignore,\
0028     k_ignore,   k_ignore,   k_ignore,   k_ignore,\
0029     k_ignore,   k_ignore,   k_ignore,   k_ignore
0030 
0031 typedef void (k_handler_fn)(struct kbd_data *, unsigned char);
0032 static k_handler_fn K_HANDLERS;
0033 static k_handler_fn *k_handler[16] = { K_HANDLERS };
0034 
0035 /* maximum values each key_handler can handle */
0036 static const int kbd_max_vals[] = {
0037     255, ARRAY_SIZE(func_table) - 1, NR_FN_HANDLER - 1, 0,
0038     NR_DEAD - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
0039 };
0040 static const int KBD_NR_TYPES = ARRAY_SIZE(kbd_max_vals);
0041 
0042 static const unsigned char ret_diacr[NR_DEAD] = {
0043     '`',    /* dead_grave */
0044     '\'',   /* dead_acute */
0045     '^',    /* dead_circumflex */
0046     '~',    /* dead_tilda */
0047     '"',    /* dead_diaeresis */
0048     ',',    /* dead_cedilla */
0049     '_',    /* dead_macron */
0050     'U',    /* dead_breve */
0051     '.',    /* dead_abovedot */
0052     '*',    /* dead_abovering */
0053     '=',    /* dead_doubleacute */
0054     'c',    /* dead_caron */
0055     'k',    /* dead_ogonek */
0056     'i',    /* dead_iota */
0057     '#',    /* dead_voiced_sound */
0058     'o',    /* dead_semivoiced_sound */
0059     '!',    /* dead_belowdot */
0060     '?',    /* dead_hook */
0061     '+',    /* dead_horn */
0062     '-',    /* dead_stroke */
0063     ')',    /* dead_abovecomma */
0064     '(',    /* dead_abovereversedcomma */
0065     ':',    /* dead_doublegrave */
0066     'n',    /* dead_invertedbreve */
0067     ';',    /* dead_belowcomma */
0068     '$',    /* dead_currency */
0069     '@',    /* dead_greek */
0070 };
0071 
0072 /*
0073  * Alloc/free of kbd_data structures.
0074  */
0075 struct kbd_data *
0076 kbd_alloc(void) {
0077     struct kbd_data *kbd;
0078     int i;
0079 
0080     kbd = kzalloc(sizeof(struct kbd_data), GFP_KERNEL);
0081     if (!kbd)
0082         goto out;
0083     kbd->key_maps = kzalloc(sizeof(ebc_key_maps), GFP_KERNEL);
0084     if (!kbd->key_maps)
0085         goto out_kbd;
0086     for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
0087         if (ebc_key_maps[i]) {
0088             kbd->key_maps[i] = kmemdup(ebc_key_maps[i],
0089                            sizeof(u_short) * NR_KEYS,
0090                            GFP_KERNEL);
0091             if (!kbd->key_maps[i])
0092                 goto out_maps;
0093         }
0094     }
0095     kbd->func_table = kzalloc(sizeof(ebc_func_table), GFP_KERNEL);
0096     if (!kbd->func_table)
0097         goto out_maps;
0098     for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++) {
0099         if (ebc_func_table[i]) {
0100             kbd->func_table[i] = kstrdup(ebc_func_table[i],
0101                              GFP_KERNEL);
0102             if (!kbd->func_table[i])
0103                 goto out_func;
0104         }
0105     }
0106     kbd->fn_handler =
0107         kcalloc(NR_FN_HANDLER, sizeof(fn_handler_fn *), GFP_KERNEL);
0108     if (!kbd->fn_handler)
0109         goto out_func;
0110     kbd->accent_table = kmemdup(ebc_accent_table,
0111                     sizeof(struct kbdiacruc) * MAX_DIACR,
0112                     GFP_KERNEL);
0113     if (!kbd->accent_table)
0114         goto out_fn_handler;
0115     kbd->accent_table_size = ebc_accent_table_size;
0116     return kbd;
0117 
0118 out_fn_handler:
0119     kfree(kbd->fn_handler);
0120 out_func:
0121     for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++)
0122         kfree(kbd->func_table[i]);
0123     kfree(kbd->func_table);
0124 out_maps:
0125     for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++)
0126         kfree(kbd->key_maps[i]);
0127     kfree(kbd->key_maps);
0128 out_kbd:
0129     kfree(kbd);
0130 out:
0131     return NULL;
0132 }
0133 
0134 void
0135 kbd_free(struct kbd_data *kbd)
0136 {
0137     int i;
0138 
0139     kfree(kbd->accent_table);
0140     kfree(kbd->fn_handler);
0141     for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++)
0142         kfree(kbd->func_table[i]);
0143     kfree(kbd->func_table);
0144     for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++)
0145         kfree(kbd->key_maps[i]);
0146     kfree(kbd->key_maps);
0147     kfree(kbd);
0148 }
0149 
0150 /*
0151  * Generate ascii -> ebcdic translation table from kbd_data.
0152  */
0153 void
0154 kbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc)
0155 {
0156     unsigned short *keymap, keysym;
0157     int i, j, k;
0158 
0159     memset(ascebc, 0x40, 256);
0160     for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
0161         keymap = kbd->key_maps[i];
0162         if (!keymap)
0163             continue;
0164         for (j = 0; j < NR_KEYS; j++) {
0165             k = ((i & 1) << 7) + j;
0166             keysym = keymap[j];
0167             if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
0168                 KTYP(keysym) == (KT_LETTER | 0xf0))
0169                 ascebc[KVAL(keysym)] = k;
0170             else if (KTYP(keysym) == (KT_DEAD | 0xf0))
0171                 ascebc[ret_diacr[KVAL(keysym)]] = k;
0172         }
0173     }
0174 }
0175 
0176 #if 0
0177 /*
0178  * Generate ebcdic -> ascii translation table from kbd_data.
0179  */
0180 void
0181 kbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc)
0182 {
0183     unsigned short *keymap, keysym;
0184     int i, j, k;
0185 
0186     memset(ebcasc, ' ', 256);
0187     for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
0188         keymap = kbd->key_maps[i];
0189         if (!keymap)
0190             continue;
0191         for (j = 0; j < NR_KEYS; j++) {
0192             keysym = keymap[j];
0193             k = ((i & 1) << 7) + j;
0194             if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
0195                 KTYP(keysym) == (KT_LETTER | 0xf0))
0196                 ebcasc[k] = KVAL(keysym);
0197             else if (KTYP(keysym) == (KT_DEAD | 0xf0))
0198                 ebcasc[k] = ret_diacr[KVAL(keysym)];
0199         }
0200     }
0201 }
0202 #endif
0203 
0204 /*
0205  * We have a combining character DIACR here, followed by the character CH.
0206  * If the combination occurs in the table, return the corresponding value.
0207  * Otherwise, if CH is a space or equals DIACR, return DIACR.
0208  * Otherwise, conclude that DIACR was not combining after all,
0209  * queue it and return CH.
0210  */
0211 static unsigned int
0212 handle_diacr(struct kbd_data *kbd, unsigned int ch)
0213 {
0214     int i, d;
0215 
0216     d = kbd->diacr;
0217     kbd->diacr = 0;
0218 
0219     for (i = 0; i < kbd->accent_table_size; i++) {
0220         if (kbd->accent_table[i].diacr == d &&
0221             kbd->accent_table[i].base == ch)
0222             return kbd->accent_table[i].result;
0223     }
0224 
0225     if (ch == ' ' || ch == d)
0226         return d;
0227 
0228     kbd_put_queue(kbd->port, d);
0229     return ch;
0230 }
0231 
0232 /*
0233  * Handle dead key.
0234  */
0235 static void
0236 k_dead(struct kbd_data *kbd, unsigned char value)
0237 {
0238     value = ret_diacr[value];
0239     kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
0240 }
0241 
0242 /*
0243  * Normal character handler.
0244  */
0245 static void
0246 k_self(struct kbd_data *kbd, unsigned char value)
0247 {
0248     if (kbd->diacr)
0249         value = handle_diacr(kbd, value);
0250     kbd_put_queue(kbd->port, value);
0251 }
0252 
0253 /*
0254  * Special key handlers
0255  */
0256 static void
0257 k_ignore(struct kbd_data *kbd, unsigned char value)
0258 {
0259 }
0260 
0261 /*
0262  * Function key handler.
0263  */
0264 static void
0265 k_fn(struct kbd_data *kbd, unsigned char value)
0266 {
0267     if (kbd->func_table[value])
0268         kbd_puts_queue(kbd->port, kbd->func_table[value]);
0269 }
0270 
0271 static void
0272 k_spec(struct kbd_data *kbd, unsigned char value)
0273 {
0274     if (value >= NR_FN_HANDLER)
0275         return;
0276     if (kbd->fn_handler[value])
0277         kbd->fn_handler[value](kbd);
0278 }
0279 
0280 /*
0281  * Put utf8 character to tty flip buffer.
0282  * UTF-8 is defined for words of up to 31 bits,
0283  * but we need only 16 bits here
0284  */
0285 static void
0286 to_utf8(struct tty_port *port, ushort c)
0287 {
0288     if (c < 0x80)
0289         /*  0******* */
0290         kbd_put_queue(port, c);
0291     else if (c < 0x800) {
0292         /* 110***** 10****** */
0293         kbd_put_queue(port, 0xc0 | (c >> 6));
0294         kbd_put_queue(port, 0x80 | (c & 0x3f));
0295     } else {
0296         /* 1110**** 10****** 10****** */
0297         kbd_put_queue(port, 0xe0 | (c >> 12));
0298         kbd_put_queue(port, 0x80 | ((c >> 6) & 0x3f));
0299         kbd_put_queue(port, 0x80 | (c & 0x3f));
0300     }
0301 }
0302 
0303 /*
0304  * Process keycode.
0305  */
0306 void
0307 kbd_keycode(struct kbd_data *kbd, unsigned int keycode)
0308 {
0309     unsigned short keysym;
0310     unsigned char type, value;
0311 
0312     if (!kbd)
0313         return;
0314 
0315     if (keycode >= 384)
0316         keysym = kbd->key_maps[5][keycode - 384];
0317     else if (keycode >= 256)
0318         keysym = kbd->key_maps[4][keycode - 256];
0319     else if (keycode >= 128)
0320         keysym = kbd->key_maps[1][keycode - 128];
0321     else
0322         keysym = kbd->key_maps[0][keycode];
0323 
0324     type = KTYP(keysym);
0325     if (type >= 0xf0) {
0326         type -= 0xf0;
0327         if (type == KT_LETTER)
0328             type = KT_LATIN;
0329         value = KVAL(keysym);
0330 #ifdef CONFIG_MAGIC_SYSRQ          /* Handle the SysRq Hack */
0331         if (kbd->sysrq) {
0332             if (kbd->sysrq == K(KT_LATIN, '-')) {
0333                 kbd->sysrq = 0;
0334                 handle_sysrq(value);
0335                 return;
0336             }
0337             if (value == '-') {
0338                 kbd->sysrq = K(KT_LATIN, '-');
0339                 return;
0340             }
0341             /* Incomplete sysrq sequence. */
0342             (*k_handler[KTYP(kbd->sysrq)])(kbd, KVAL(kbd->sysrq));
0343             kbd->sysrq = 0;
0344         } else if ((type == KT_LATIN && value == '^') ||
0345                (type == KT_DEAD && ret_diacr[value] == '^')) {
0346             kbd->sysrq = K(type, value);
0347             return;
0348         }
0349 #endif
0350         (*k_handler[type])(kbd, value);
0351     } else
0352         to_utf8(kbd->port, keysym);
0353 }
0354 
0355 /*
0356  * Ioctl stuff.
0357  */
0358 static int
0359 do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe,
0360           int cmd, int perm)
0361 {
0362     struct kbentry tmp;
0363     unsigned long kb_index, kb_table;
0364     ushort *key_map, val, ov;
0365 
0366     if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
0367         return -EFAULT;
0368     kb_index = (unsigned long) tmp.kb_index;
0369 #if NR_KEYS < 256
0370     if (kb_index >= NR_KEYS)
0371         return -EINVAL;
0372 #endif
0373     kb_table = (unsigned long) tmp.kb_table;
0374 #if MAX_NR_KEYMAPS < 256
0375     if (kb_table >= MAX_NR_KEYMAPS)
0376         return -EINVAL; 
0377     kb_table = array_index_nospec(kb_table , MAX_NR_KEYMAPS);
0378 #endif
0379 
0380     switch (cmd) {
0381     case KDGKBENT:
0382         key_map = kbd->key_maps[kb_table];
0383         if (key_map) {
0384             val = U(key_map[kb_index]);
0385             if (KTYP(val) >= KBD_NR_TYPES)
0386             val = K_HOLE;
0387         } else
0388             val = (kb_index ? K_HOLE : K_NOSUCHMAP);
0389         return put_user(val, &user_kbe->kb_value);
0390     case KDSKBENT:
0391         if (!perm)
0392             return -EPERM;
0393         if (!kb_index && tmp.kb_value == K_NOSUCHMAP) {
0394             /* disallocate map */
0395             key_map = kbd->key_maps[kb_table];
0396             if (key_map) {
0397                 kbd->key_maps[kb_table] = NULL;
0398                 kfree(key_map);
0399             }
0400             break;
0401         }
0402 
0403         if (KTYP(tmp.kb_value) >= KBD_NR_TYPES)
0404             return -EINVAL;
0405         if (KVAL(tmp.kb_value) > kbd_max_vals[KTYP(tmp.kb_value)])
0406             return -EINVAL;
0407 
0408         if (!(key_map = kbd->key_maps[kb_table])) {
0409             int j;
0410 
0411             key_map = kmalloc(sizeof(plain_map),
0412                              GFP_KERNEL);
0413             if (!key_map)
0414                 return -ENOMEM;
0415             kbd->key_maps[kb_table] = key_map;
0416             for (j = 0; j < NR_KEYS; j++)
0417                 key_map[j] = U(K_HOLE);
0418         }
0419         ov = U(key_map[kb_index]);
0420         if (tmp.kb_value == ov)
0421             break;  /* nothing to do */
0422         /*
0423          * Attention Key.
0424          */
0425         if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) &&
0426             !capable(CAP_SYS_ADMIN))
0427             return -EPERM;
0428         key_map[kb_index] = U(tmp.kb_value);
0429         break;
0430     }
0431     return 0;
0432 }
0433 
0434 static int
0435 do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs,
0436            int cmd, int perm)
0437 {
0438     unsigned char kb_func;
0439     char *p;
0440     int len;
0441 
0442     /* Get u_kbs->kb_func. */
0443     if (get_user(kb_func, &u_kbs->kb_func))
0444         return -EFAULT;
0445 #if MAX_NR_FUNC < 256
0446     if (kb_func >= MAX_NR_FUNC)
0447         return -EINVAL;
0448 #endif
0449 
0450     switch (cmd) {
0451     case KDGKBSENT:
0452         p = kbd->func_table[kb_func];
0453         if (p) {
0454             len = strlen(p);
0455             if (len >= sizeof(u_kbs->kb_string))
0456                 len = sizeof(u_kbs->kb_string) - 1;
0457             if (copy_to_user(u_kbs->kb_string, p, len))
0458                 return -EFAULT;
0459         } else
0460             len = 0;
0461         if (put_user('\0', u_kbs->kb_string + len))
0462             return -EFAULT;
0463         break;
0464     case KDSKBSENT:
0465         if (!perm)
0466             return -EPERM;
0467         p = strndup_user(u_kbs->kb_string, sizeof(u_kbs->kb_string));
0468         if (IS_ERR(p))
0469             return PTR_ERR(p);
0470         kfree(kbd->func_table[kb_func]);
0471         kbd->func_table[kb_func] = p;
0472         break;
0473     }
0474     return 0;
0475 }
0476 
0477 int kbd_ioctl(struct kbd_data *kbd, unsigned int cmd, unsigned long arg)
0478 {
0479     struct tty_struct *tty;
0480     void __user *argp;
0481     unsigned int ct;
0482     int perm;
0483 
0484     argp = (void __user *)arg;
0485 
0486     /*
0487      * To have permissions to do most of the vt ioctls, we either have
0488      * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
0489      */
0490     tty = tty_port_tty_get(kbd->port);
0491     /* FIXME this test is pretty racy */
0492     perm = current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG);
0493     tty_kref_put(tty);
0494     switch (cmd) {
0495     case KDGKBTYPE:
0496         return put_user(KB_101, (char __user *)argp);
0497     case KDGKBENT:
0498     case KDSKBENT:
0499         return do_kdsk_ioctl(kbd, argp, cmd, perm);
0500     case KDGKBSENT:
0501     case KDSKBSENT:
0502         return do_kdgkb_ioctl(kbd, argp, cmd, perm);
0503     case KDGKBDIACR:
0504     {
0505         struct kbdiacrs __user *a = argp;
0506         struct kbdiacr diacr;
0507         int i;
0508 
0509         if (put_user(kbd->accent_table_size, &a->kb_cnt))
0510             return -EFAULT;
0511         for (i = 0; i < kbd->accent_table_size; i++) {
0512             diacr.diacr = kbd->accent_table[i].diacr;
0513             diacr.base = kbd->accent_table[i].base;
0514             diacr.result = kbd->accent_table[i].result;
0515             if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr)))
0516             return -EFAULT;
0517         }
0518         return 0;
0519     }
0520     case KDGKBDIACRUC:
0521     {
0522         struct kbdiacrsuc __user *a = argp;
0523 
0524         ct = kbd->accent_table_size;
0525         if (put_user(ct, &a->kb_cnt))
0526             return -EFAULT;
0527         if (copy_to_user(a->kbdiacruc, kbd->accent_table,
0528                  ct * sizeof(struct kbdiacruc)))
0529             return -EFAULT;
0530         return 0;
0531     }
0532     case KDSKBDIACR:
0533     {
0534         struct kbdiacrs __user *a = argp;
0535         struct kbdiacr diacr;
0536         int i;
0537 
0538         if (!perm)
0539             return -EPERM;
0540         if (get_user(ct, &a->kb_cnt))
0541             return -EFAULT;
0542         if (ct >= MAX_DIACR)
0543             return -EINVAL;
0544         kbd->accent_table_size = ct;
0545         for (i = 0; i < ct; i++) {
0546             if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr)))
0547                 return -EFAULT;
0548             kbd->accent_table[i].diacr = diacr.diacr;
0549             kbd->accent_table[i].base = diacr.base;
0550             kbd->accent_table[i].result = diacr.result;
0551         }
0552         return 0;
0553     }
0554     case KDSKBDIACRUC:
0555     {
0556         struct kbdiacrsuc __user *a = argp;
0557 
0558         if (!perm)
0559             return -EPERM;
0560         if (get_user(ct, &a->kb_cnt))
0561             return -EFAULT;
0562         if (ct >= MAX_DIACR)
0563             return -EINVAL;
0564         kbd->accent_table_size = ct;
0565         if (copy_from_user(kbd->accent_table, a->kbdiacruc,
0566                    ct * sizeof(struct kbdiacruc)))
0567             return -EFAULT;
0568         return 0;
0569     }
0570     default:
0571         return -ENOIOCTLCMD;
0572     }
0573 }
0574 
0575 EXPORT_SYMBOL(kbd_ioctl);
0576 EXPORT_SYMBOL(kbd_ascebc);
0577 EXPORT_SYMBOL(kbd_free);
0578 EXPORT_SYMBOL(kbd_alloc);
0579 EXPORT_SYMBOL(kbd_keycode);