linux/drivers/input/keyboard/sunkbd.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Copyright (c) 1999-2001 Vojtech Pavlik
   4 */
   5
   6/*
   7 * Sun keyboard driver for Linux
   8 */
   9
  10/*
  11 */
  12
  13#include <linux/delay.h>
  14#include <linux/sched.h>
  15#include <linux/slab.h>
  16#include <linux/module.h>
  17#include <linux/interrupt.h>
  18#include <linux/input.h>
  19#include <linux/serio.h>
  20#include <linux/workqueue.h>
  21
  22#define DRIVER_DESC     "Sun keyboard driver"
  23
  24MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
  25MODULE_DESCRIPTION(DRIVER_DESC);
  26MODULE_LICENSE("GPL");
  27
  28static unsigned char sunkbd_keycode[128] = {
  29          0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
  30         65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106,  1,  2,  3,
  31          4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
  32        116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
  33         26, 27,111,127, 71, 72, 73, 74,134,135,107,  0, 29, 30, 31, 32,
  34         33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
  35        104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
  36         79, 80, 81,  0,  0,  0,138, 58,125, 57,126,109, 86, 78
  37};
  38
  39#define SUNKBD_CMD_RESET        0x1
  40#define SUNKBD_CMD_BELLON       0x2
  41#define SUNKBD_CMD_BELLOFF      0x3
  42#define SUNKBD_CMD_CLICK        0xa
  43#define SUNKBD_CMD_NOCLICK      0xb
  44#define SUNKBD_CMD_SETLED       0xe
  45#define SUNKBD_CMD_LAYOUT       0xf
  46
  47#define SUNKBD_RET_RESET        0xff
  48#define SUNKBD_RET_ALLUP        0x7f
  49#define SUNKBD_RET_LAYOUT       0xfe
  50
  51#define SUNKBD_LAYOUT_5_MASK    0x20
  52#define SUNKBD_RELEASE          0x80
  53#define SUNKBD_KEY              0x7f
  54
  55/*
  56 * Per-keyboard data.
  57 */
  58
  59struct sunkbd {
  60        unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)];
  61        struct input_dev *dev;
  62        struct serio *serio;
  63        struct work_struct tq;
  64        wait_queue_head_t wait;
  65        char name[64];
  66        char phys[32];
  67        char type;
  68        bool enabled;
  69        volatile s8 reset;
  70        volatile s8 layout;
  71};
  72
  73/*
  74 * sunkbd_interrupt() is called by the low level driver when a character
  75 * is received.
  76 */
  77
  78static irqreturn_t sunkbd_interrupt(struct serio *serio,
  79                unsigned char data, unsigned int flags)
  80{
  81        struct sunkbd *sunkbd = serio_get_drvdata(serio);
  82
  83        if (sunkbd->reset <= -1) {
  84                /*
  85                 * If cp[i] is 0xff, sunkbd->reset will stay -1.
  86                 * The keyboard sends 0xff 0xff 0xID on powerup.
  87                 */
  88                sunkbd->reset = data;
  89                wake_up_interruptible(&sunkbd->wait);
  90                goto out;
  91        }
  92
  93        if (sunkbd->layout == -1) {
  94                sunkbd->layout = data;
  95                wake_up_interruptible(&sunkbd->wait);
  96                goto out;
  97        }
  98
  99        switch (data) {
 100
 101        case SUNKBD_RET_RESET:
 102                if (sunkbd->enabled)
 103                        schedule_work(&sunkbd->tq);
 104                sunkbd->reset = -1;
 105                break;
 106
 107        case SUNKBD_RET_LAYOUT:
 108                sunkbd->layout = -1;
 109                break;
 110
 111        case SUNKBD_RET_ALLUP: /* All keys released */
 112                break;
 113
 114        default:
 115                if (!sunkbd->enabled)
 116                        break;
 117
 118                if (sunkbd->keycode[data & SUNKBD_KEY]) {
 119                        input_report_key(sunkbd->dev,
 120                                         sunkbd->keycode[data & SUNKBD_KEY],
 121                                         !(data & SUNKBD_RELEASE));
 122                        input_sync(sunkbd->dev);
 123                } else {
 124                        printk(KERN_WARNING
 125                                "sunkbd.c: Unknown key (scancode %#x) %s.\n",
 126                                data & SUNKBD_KEY,
 127                                data & SUNKBD_RELEASE ? "released" : "pressed");
 128                }
 129        }
 130out:
 131        return IRQ_HANDLED;
 132}
 133
 134/*
 135 * sunkbd_event() handles events from the input module.
 136 */
 137
 138static int sunkbd_event(struct input_dev *dev,
 139                        unsigned int type, unsigned int code, int value)
 140{
 141        struct sunkbd *sunkbd = input_get_drvdata(dev);
 142
 143        switch (type) {
 144
 145        case EV_LED:
 146
 147                serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
 148                serio_write(sunkbd->serio,
 149                        (!!test_bit(LED_CAPSL,   dev->led) << 3) |
 150                        (!!test_bit(LED_SCROLLL, dev->led) << 2) |
 151                        (!!test_bit(LED_COMPOSE, dev->led) << 1) |
 152                         !!test_bit(LED_NUML,    dev->led));
 153                return 0;
 154
 155        case EV_SND:
 156
 157                switch (code) {
 158
 159                case SND_CLICK:
 160                        serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
 161                        return 0;
 162
 163                case SND_BELL:
 164                        serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
 165                        return 0;
 166                }
 167
 168                break;
 169        }
 170
 171        return -1;
 172}
 173
 174/*
 175 * sunkbd_initialize() checks for a Sun keyboard attached, and determines
 176 * its type.
 177 */
 178
 179static int sunkbd_initialize(struct sunkbd *sunkbd)
 180{
 181        sunkbd->reset = -2;
 182        serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
 183        wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
 184        if (sunkbd->reset < 0)
 185                return -1;
 186
 187        sunkbd->type = sunkbd->reset;
 188
 189        if (sunkbd->type == 4) {        /* Type 4 keyboard */
 190                sunkbd->layout = -2;
 191                serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
 192                wait_event_interruptible_timeout(sunkbd->wait,
 193                                                 sunkbd->layout >= 0, HZ / 4);
 194                if (sunkbd->layout < 0)
 195                        return -1;
 196                if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
 197                        sunkbd->type = 5;
 198        }
 199
 200        return 0;
 201}
 202
 203/*
 204 * sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers
 205 * they were in.
 206 */
 207
 208static void sunkbd_set_leds_beeps(struct sunkbd *sunkbd)
 209{
 210        serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
 211        serio_write(sunkbd->serio,
 212                (!!test_bit(LED_CAPSL,   sunkbd->dev->led) << 3) |
 213                (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
 214                (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
 215                 !!test_bit(LED_NUML,    sunkbd->dev->led));
 216        serio_write(sunkbd->serio,
 217                SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
 218        serio_write(sunkbd->serio,
 219                SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
 220}
 221
 222
 223/*
 224 * sunkbd_reinit() wait for the keyboard reset to complete and restores state
 225 * of leds and beeps.
 226 */
 227
 228static void sunkbd_reinit(struct work_struct *work)
 229{
 230        struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
 231
 232        /*
 233         * It is OK that we check sunkbd->enabled without pausing serio,
 234         * as we only want to catch true->false transition that will
 235         * happen once and we will be woken up for it.
 236         */
 237        wait_event_interruptible_timeout(sunkbd->wait,
 238                                         sunkbd->reset >= 0 || !sunkbd->enabled,
 239                                         HZ);
 240
 241        if (sunkbd->reset >= 0 && sunkbd->enabled)
 242                sunkbd_set_leds_beeps(sunkbd);
 243}
 244
 245static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
 246{
 247        serio_pause_rx(sunkbd->serio);
 248        sunkbd->enabled = enable;
 249        serio_continue_rx(sunkbd->serio);
 250
 251        if (!enable) {
 252                wake_up_interruptible(&sunkbd->wait);
 253                cancel_work_sync(&sunkbd->tq);
 254        }
 255}
 256
 257/*
 258 * sunkbd_connect() probes for a Sun keyboard and fills the necessary
 259 * structures.
 260 */
 261
 262static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
 263{
 264        struct sunkbd *sunkbd;
 265        struct input_dev *input_dev;
 266        int err = -ENOMEM;
 267        int i;
 268
 269        sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
 270        input_dev = input_allocate_device();
 271        if (!sunkbd || !input_dev)
 272                goto fail1;
 273
 274        sunkbd->serio = serio;
 275        sunkbd->dev = input_dev;
 276        init_waitqueue_head(&sunkbd->wait);
 277        INIT_WORK(&sunkbd->tq, sunkbd_reinit);
 278        snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
 279
 280        serio_set_drvdata(serio, sunkbd);
 281
 282        err = serio_open(serio, drv);
 283        if (err)
 284                goto fail2;
 285
 286        if (sunkbd_initialize(sunkbd) < 0) {
 287                err = -ENODEV;
 288                goto fail3;
 289        }
 290
 291        snprintf(sunkbd->name, sizeof(sunkbd->name),
 292                 "Sun Type %d keyboard", sunkbd->type);
 293        memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
 294
 295        input_dev->name = sunkbd->name;
 296        input_dev->phys = sunkbd->phys;
 297        input_dev->id.bustype = BUS_RS232;
 298        input_dev->id.vendor  = SERIO_SUNKBD;
 299        input_dev->id.product = sunkbd->type;
 300        input_dev->id.version = 0x0100;
 301        input_dev->dev.parent = &serio->dev;
 302
 303        input_set_drvdata(input_dev, sunkbd);
 304
 305        input_dev->event = sunkbd_event;
 306
 307        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
 308                BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
 309        input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
 310                BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
 311        input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
 312
 313        input_dev->keycode = sunkbd->keycode;
 314        input_dev->keycodesize = sizeof(unsigned char);
 315        input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
 316        for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
 317                __set_bit(sunkbd->keycode[i], input_dev->keybit);
 318        __clear_bit(KEY_RESERVED, input_dev->keybit);
 319
 320        sunkbd_enable(sunkbd, true);
 321
 322        err = input_register_device(sunkbd->dev);
 323        if (err)
 324                goto fail4;
 325
 326        return 0;
 327
 328 fail4: sunkbd_enable(sunkbd, false);
 329 fail3: serio_close(serio);
 330 fail2: serio_set_drvdata(serio, NULL);
 331 fail1: input_free_device(input_dev);
 332        kfree(sunkbd);
 333        return err;
 334}
 335
 336/*
 337 * sunkbd_disconnect() unregisters and closes behind us.
 338 */
 339
 340static void sunkbd_disconnect(struct serio *serio)
 341{
 342        struct sunkbd *sunkbd = serio_get_drvdata(serio);
 343
 344        sunkbd_enable(sunkbd, false);
 345        input_unregister_device(sunkbd->dev);
 346        serio_close(serio);
 347        serio_set_drvdata(serio, NULL);
 348        kfree(sunkbd);
 349}
 350
 351static const struct serio_device_id sunkbd_serio_ids[] = {
 352        {
 353                .type   = SERIO_RS232,
 354                .proto  = SERIO_SUNKBD,
 355                .id     = SERIO_ANY,
 356                .extra  = SERIO_ANY,
 357        },
 358        {
 359                .type   = SERIO_RS232,
 360                .proto  = SERIO_UNKNOWN, /* sunkbd does probe */
 361                .id     = SERIO_ANY,
 362                .extra  = SERIO_ANY,
 363        },
 364        { 0 }
 365};
 366
 367MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
 368
 369static struct serio_driver sunkbd_drv = {
 370        .driver         = {
 371                .name   = "sunkbd",
 372        },
 373        .description    = DRIVER_DESC,
 374        .id_table       = sunkbd_serio_ids,
 375        .interrupt      = sunkbd_interrupt,
 376        .connect        = sunkbd_connect,
 377        .disconnect     = sunkbd_disconnect,
 378};
 379
 380module_serio_driver(sunkbd_drv);
 381