linux/drivers/s390/char/keyboard.c
<<
>>
Prefs
   1/*
   2 *  drivers/s390/char/keyboard.c
   3 *    ebcdic keycode functions for s390 console drivers
   4 *
   5 *  S390 version
   6 *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
   7 *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/sched.h>
  12#include <linux/sysrq.h>
  13
  14#include <linux/consolemap.h>
  15#include <linux/kbd_kern.h>
  16#include <linux/kbd_diacr.h>
  17#include <asm/uaccess.h>
  18
  19#include "keyboard.h"
  20
  21/*
  22 * Handler Tables.
  23 */
  24#define K_HANDLERS\
  25        k_self,         k_fn,           k_spec,         k_ignore,\
  26        k_dead,         k_ignore,       k_ignore,       k_ignore,\
  27        k_ignore,       k_ignore,       k_ignore,       k_ignore,\
  28        k_ignore,       k_ignore,       k_ignore,       k_ignore
  29
  30typedef void (k_handler_fn)(struct kbd_data *, unsigned char);
  31static k_handler_fn K_HANDLERS;
  32static k_handler_fn *k_handler[16] = { K_HANDLERS };
  33
  34/* maximum values each key_handler can handle */
  35static const int kbd_max_vals[] = {
  36        255, ARRAY_SIZE(func_table) - 1, NR_FN_HANDLER - 1, 0,
  37        NR_DEAD - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
  38};
  39static const int KBD_NR_TYPES = ARRAY_SIZE(kbd_max_vals);
  40
  41static unsigned char ret_diacr[NR_DEAD] = {
  42        '`', '\'', '^', '~', '"', ','
  43};
  44
  45/*
  46 * Alloc/free of kbd_data structures.
  47 */
  48struct kbd_data *
  49kbd_alloc(void) {
  50        struct kbd_data *kbd;
  51        int i, len;
  52
  53        kbd = kzalloc(sizeof(struct kbd_data), GFP_KERNEL);
  54        if (!kbd)
  55                goto out;
  56        kbd->key_maps = kzalloc(sizeof(key_maps), GFP_KERNEL);
  57        if (!kbd->key_maps)
  58                goto out_kbd;
  59        for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
  60                if (key_maps[i]) {
  61                        kbd->key_maps[i] =
  62                                kmalloc(sizeof(u_short)*NR_KEYS, GFP_KERNEL);
  63                        if (!kbd->key_maps[i])
  64                                goto out_maps;
  65                        memcpy(kbd->key_maps[i], key_maps[i],
  66                               sizeof(u_short)*NR_KEYS);
  67                }
  68        }
  69        kbd->func_table = kzalloc(sizeof(func_table), GFP_KERNEL);
  70        if (!kbd->func_table)
  71                goto out_maps;
  72        for (i = 0; i < ARRAY_SIZE(func_table); i++) {
  73                if (func_table[i]) {
  74                        len = strlen(func_table[i]) + 1;
  75                        kbd->func_table[i] = kmalloc(len, GFP_KERNEL);
  76                        if (!kbd->func_table[i])
  77                                goto out_func;
  78                        memcpy(kbd->func_table[i], func_table[i], len);
  79                }
  80        }
  81        kbd->fn_handler =
  82                kzalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL);
  83        if (!kbd->fn_handler)
  84                goto out_func;
  85        kbd->accent_table =
  86                kmalloc(sizeof(struct kbdiacruc)*MAX_DIACR, GFP_KERNEL);
  87        if (!kbd->accent_table)
  88                goto out_fn_handler;
  89        memcpy(kbd->accent_table, accent_table,
  90               sizeof(struct kbdiacruc)*MAX_DIACR);
  91        kbd->accent_table_size = accent_table_size;
  92        return kbd;
  93
  94out_fn_handler:
  95        kfree(kbd->fn_handler);
  96out_func:
  97        for (i = 0; i < ARRAY_SIZE(func_table); i++)
  98                kfree(kbd->func_table[i]);
  99        kfree(kbd->func_table);
 100out_maps:
 101        for (i = 0; i < ARRAY_SIZE(key_maps); i++)
 102                kfree(kbd->key_maps[i]);
 103        kfree(kbd->key_maps);
 104out_kbd:
 105        kfree(kbd);
 106out:
 107        return NULL;
 108}
 109
 110void
 111kbd_free(struct kbd_data *kbd)
 112{
 113        int i;
 114
 115        kfree(kbd->accent_table);
 116        kfree(kbd->fn_handler);
 117        for (i = 0; i < ARRAY_SIZE(func_table); i++)
 118                kfree(kbd->func_table[i]);
 119        kfree(kbd->func_table);
 120        for (i = 0; i < ARRAY_SIZE(key_maps); i++)
 121                kfree(kbd->key_maps[i]);
 122        kfree(kbd->key_maps);
 123        kfree(kbd);
 124}
 125
 126/*
 127 * Generate ascii -> ebcdic translation table from kbd_data.
 128 */
 129void
 130kbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc)
 131{
 132        unsigned short *keymap, keysym;
 133        int i, j, k;
 134
 135        memset(ascebc, 0x40, 256);
 136        for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
 137                keymap = kbd->key_maps[i];
 138                if (!keymap)
 139                        continue;
 140                for (j = 0; j < NR_KEYS; j++) {
 141                        k = ((i & 1) << 7) + j;
 142                        keysym = keymap[j];
 143                        if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
 144                            KTYP(keysym) == (KT_LETTER | 0xf0))
 145                                ascebc[KVAL(keysym)] = k;
 146                        else if (KTYP(keysym) == (KT_DEAD | 0xf0))
 147                                ascebc[ret_diacr[KVAL(keysym)]] = k;
 148                }
 149        }
 150}
 151
 152#if 0
 153/*
 154 * Generate ebcdic -> ascii translation table from kbd_data.
 155 */
 156void
 157kbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc)
 158{
 159        unsigned short *keymap, keysym;
 160        int i, j, k;
 161
 162        memset(ebcasc, ' ', 256);
 163        for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
 164                keymap = kbd->key_maps[i];
 165                if (!keymap)
 166                        continue;
 167                for (j = 0; j < NR_KEYS; j++) {
 168                        keysym = keymap[j];
 169                        k = ((i & 1) << 7) + j;
 170                        if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
 171                            KTYP(keysym) == (KT_LETTER | 0xf0))
 172                                ebcasc[k] = KVAL(keysym);
 173                        else if (KTYP(keysym) == (KT_DEAD | 0xf0))
 174                                ebcasc[k] = ret_diacr[KVAL(keysym)];
 175                }
 176        }
 177}
 178#endif
 179
 180/*
 181 * We have a combining character DIACR here, followed by the character CH.
 182 * If the combination occurs in the table, return the corresponding value.
 183 * Otherwise, if CH is a space or equals DIACR, return DIACR.
 184 * Otherwise, conclude that DIACR was not combining after all,
 185 * queue it and return CH.
 186 */
 187static unsigned int
 188handle_diacr(struct kbd_data *kbd, unsigned int ch)
 189{
 190        int i, d;
 191
 192        d = kbd->diacr;
 193        kbd->diacr = 0;
 194
 195        for (i = 0; i < kbd->accent_table_size; i++) {
 196                if (kbd->accent_table[i].diacr == d &&
 197                    kbd->accent_table[i].base == ch)
 198                        return kbd->accent_table[i].result;
 199        }
 200
 201        if (ch == ' ' || ch == d)
 202                return d;
 203
 204        kbd_put_queue(kbd->tty, d);
 205        return ch;
 206}
 207
 208/*
 209 * Handle dead key.
 210 */
 211static void
 212k_dead(struct kbd_data *kbd, unsigned char value)
 213{
 214        value = ret_diacr[value];
 215        kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
 216}
 217
 218/*
 219 * Normal character handler.
 220 */
 221static void
 222k_self(struct kbd_data *kbd, unsigned char value)
 223{
 224        if (kbd->diacr)
 225                value = handle_diacr(kbd, value);
 226        kbd_put_queue(kbd->tty, value);
 227}
 228
 229/*
 230 * Special key handlers
 231 */
 232static void
 233k_ignore(struct kbd_data *kbd, unsigned char value)
 234{
 235}
 236
 237/*
 238 * Function key handler.
 239 */
 240static void
 241k_fn(struct kbd_data *kbd, unsigned char value)
 242{
 243        if (kbd->func_table[value])
 244                kbd_puts_queue(kbd->tty, kbd->func_table[value]);
 245}
 246
 247static void
 248k_spec(struct kbd_data *kbd, unsigned char value)
 249{
 250        if (value >= NR_FN_HANDLER)
 251                return;
 252        if (kbd->fn_handler[value])
 253                kbd->fn_handler[value](kbd);
 254}
 255
 256/*
 257 * Put utf8 character to tty flip buffer.
 258 * UTF-8 is defined for words of up to 31 bits,
 259 * but we need only 16 bits here
 260 */
 261static void
 262to_utf8(struct tty_struct *tty, ushort c) 
 263{
 264        if (c < 0x80)
 265                /*  0******* */
 266                kbd_put_queue(tty, c);
 267        else if (c < 0x800) {
 268                /* 110***** 10****** */
 269                kbd_put_queue(tty, 0xc0 | (c >> 6));
 270                kbd_put_queue(tty, 0x80 | (c & 0x3f));
 271        } else {
 272                /* 1110**** 10****** 10****** */
 273                kbd_put_queue(tty, 0xe0 | (c >> 12));
 274                kbd_put_queue(tty, 0x80 | ((c >> 6) & 0x3f));
 275                kbd_put_queue(tty, 0x80 | (c & 0x3f));
 276        }
 277}
 278
 279/*
 280 * Process keycode.
 281 */
 282void
 283kbd_keycode(struct kbd_data *kbd, unsigned int keycode)
 284{
 285        unsigned short keysym;
 286        unsigned char type, value;
 287
 288        if (!kbd || !kbd->tty)
 289                return;
 290
 291        if (keycode >= 384)
 292                keysym = kbd->key_maps[5][keycode - 384];
 293        else if (keycode >= 256)
 294                keysym = kbd->key_maps[4][keycode - 256];
 295        else if (keycode >= 128)
 296                keysym = kbd->key_maps[1][keycode - 128];
 297        else
 298                keysym = kbd->key_maps[0][keycode];
 299
 300        type = KTYP(keysym);
 301        if (type >= 0xf0) {
 302                type -= 0xf0;
 303                if (type == KT_LETTER)
 304                        type = KT_LATIN;
 305                value = KVAL(keysym);
 306#ifdef CONFIG_MAGIC_SYSRQ              /* Handle the SysRq Hack */
 307                if (kbd->sysrq) {
 308                        if (kbd->sysrq == K(KT_LATIN, '-')) {
 309                                kbd->sysrq = 0;
 310                                handle_sysrq(value, kbd->tty);
 311                                return;
 312                        }
 313                        if (value == '-') {
 314                                kbd->sysrq = K(KT_LATIN, '-');
 315                                return;
 316                        }
 317                        /* Incomplete sysrq sequence. */
 318                        (*k_handler[KTYP(kbd->sysrq)])(kbd, KVAL(kbd->sysrq));
 319                        kbd->sysrq = 0;
 320                } else if ((type == KT_LATIN && value == '^') ||
 321                           (type == KT_DEAD && ret_diacr[value] == '^')) {
 322                        kbd->sysrq = K(type, value);
 323                        return;
 324                }
 325#endif
 326                (*k_handler[type])(kbd, value);
 327        } else
 328                to_utf8(kbd->tty, keysym);
 329}
 330
 331/*
 332 * Ioctl stuff.
 333 */
 334static int
 335do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe,
 336              int cmd, int perm)
 337{
 338        struct kbentry tmp;
 339        ushort *key_map, val, ov;
 340
 341        if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
 342                return -EFAULT;
 343#if NR_KEYS < 256
 344        if (tmp.kb_index >= NR_KEYS)
 345                return -EINVAL;
 346#endif
 347#if MAX_NR_KEYMAPS < 256
 348        if (tmp.kb_table >= MAX_NR_KEYMAPS)
 349                return -EINVAL; 
 350#endif
 351
 352        switch (cmd) {
 353        case KDGKBENT:
 354                key_map = kbd->key_maps[tmp.kb_table];
 355                if (key_map) {
 356                    val = U(key_map[tmp.kb_index]);
 357                    if (KTYP(val) >= KBD_NR_TYPES)
 358                        val = K_HOLE;
 359                } else
 360                    val = (tmp.kb_index ? K_HOLE : K_NOSUCHMAP);
 361                return put_user(val, &user_kbe->kb_value);
 362        case KDSKBENT:
 363                if (!perm)
 364                        return -EPERM;
 365                if (!tmp.kb_index && tmp.kb_value == K_NOSUCHMAP) {
 366                        /* disallocate map */
 367                        key_map = kbd->key_maps[tmp.kb_table];
 368                        if (key_map) {
 369                            kbd->key_maps[tmp.kb_table] = NULL;
 370                            kfree(key_map);
 371                        }
 372                        break;
 373                }
 374
 375                if (KTYP(tmp.kb_value) >= KBD_NR_TYPES)
 376                        return -EINVAL;
 377                if (KVAL(tmp.kb_value) > kbd_max_vals[KTYP(tmp.kb_value)])
 378                        return -EINVAL;
 379
 380                if (!(key_map = kbd->key_maps[tmp.kb_table])) {
 381                        int j;
 382
 383                        key_map = kmalloc(sizeof(plain_map),
 384                                                     GFP_KERNEL);
 385                        if (!key_map)
 386                                return -ENOMEM;
 387                        kbd->key_maps[tmp.kb_table] = key_map;
 388                        for (j = 0; j < NR_KEYS; j++)
 389                                key_map[j] = U(K_HOLE);
 390                }
 391                ov = U(key_map[tmp.kb_index]);
 392                if (tmp.kb_value == ov)
 393                        break;  /* nothing to do */
 394                /*
 395                 * Attention Key.
 396                 */
 397                if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) &&
 398                    !capable(CAP_SYS_ADMIN))
 399                        return -EPERM;
 400                key_map[tmp.kb_index] = U(tmp.kb_value);
 401                break;
 402        }
 403        return 0;
 404}
 405
 406static int
 407do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs,
 408               int cmd, int perm)
 409{
 410        unsigned char kb_func;
 411        char *p;
 412        int len;
 413
 414        /* Get u_kbs->kb_func. */
 415        if (get_user(kb_func, &u_kbs->kb_func))
 416                return -EFAULT;
 417#if MAX_NR_FUNC < 256
 418        if (kb_func >= MAX_NR_FUNC)
 419                return -EINVAL;
 420#endif
 421
 422        switch (cmd) {
 423        case KDGKBSENT:
 424                p = kbd->func_table[kb_func];
 425                if (p) {
 426                        len = strlen(p);
 427                        if (len >= sizeof(u_kbs->kb_string))
 428                                len = sizeof(u_kbs->kb_string) - 1;
 429                        if (copy_to_user(u_kbs->kb_string, p, len))
 430                                return -EFAULT;
 431                } else
 432                        len = 0;
 433                if (put_user('\0', u_kbs->kb_string + len))
 434                        return -EFAULT;
 435                break;
 436        case KDSKBSENT:
 437                if (!perm)
 438                        return -EPERM;
 439                len = strnlen_user(u_kbs->kb_string,
 440                                   sizeof(u_kbs->kb_string) - 1);
 441                if (!len)
 442                        return -EFAULT;
 443                if (len > sizeof(u_kbs->kb_string) - 1)
 444                        return -EINVAL;
 445                p = kmalloc(len + 1, GFP_KERNEL);
 446                if (!p)
 447                        return -ENOMEM;
 448                if (copy_from_user(p, u_kbs->kb_string, len)) {
 449                        kfree(p);
 450                        return -EFAULT;
 451                }
 452                p[len] = 0;
 453                kfree(kbd->func_table[kb_func]);
 454                kbd->func_table[kb_func] = p;
 455                break;
 456        }
 457        return 0;
 458}
 459
 460int
 461kbd_ioctl(struct kbd_data *kbd, struct file *file,
 462          unsigned int cmd, unsigned long arg)
 463{
 464        void __user *argp;
 465        int ct, perm;
 466
 467        argp = (void __user *)arg;
 468
 469        /*
 470         * To have permissions to do most of the vt ioctls, we either have
 471         * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
 472         */
 473        perm = current->signal->tty == kbd->tty || capable(CAP_SYS_TTY_CONFIG);
 474        switch (cmd) {
 475        case KDGKBTYPE:
 476                return put_user(KB_101, (char __user *)argp);
 477        case KDGKBENT:
 478        case KDSKBENT:
 479                return do_kdsk_ioctl(kbd, argp, cmd, perm);
 480        case KDGKBSENT:
 481        case KDSKBSENT:
 482                return do_kdgkb_ioctl(kbd, argp, cmd, perm);
 483        case KDGKBDIACR:
 484        {
 485                struct kbdiacrs __user *a = argp;
 486                struct kbdiacr diacr;
 487                int i;
 488
 489                if (put_user(kbd->accent_table_size, &a->kb_cnt))
 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
 504                ct = kbd->accent_table_size;
 505                if (put_user(ct, &a->kb_cnt))
 506                        return -EFAULT;
 507                if (copy_to_user(a->kbdiacruc, kbd->accent_table,
 508                                 ct * sizeof(struct kbdiacruc)))
 509                        return -EFAULT;
 510                return 0;
 511        }
 512        case KDSKBDIACR:
 513        {
 514                struct kbdiacrs __user *a = argp;
 515                struct kbdiacr diacr;
 516                int i;
 517
 518                if (!perm)
 519                        return -EPERM;
 520                if (get_user(ct, &a->kb_cnt))
 521                        return -EFAULT;
 522                if (ct >= MAX_DIACR)
 523                        return -EINVAL;
 524                kbd->accent_table_size = ct;
 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))
 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;
 548                return 0;
 549        }
 550        default:
 551                return -ENOIOCTLCMD;
 552        }
 553}
 554
 555EXPORT_SYMBOL(kbd_ioctl);
 556EXPORT_SYMBOL(kbd_ascebc);
 557EXPORT_SYMBOL(kbd_free);
 558EXPORT_SYMBOL(kbd_alloc);
 559EXPORT_SYMBOL(kbd_keycode);
 560