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                schedule_work(&sunkbd->tq);
 103                sunkbd->reset = -1;
 104                break;
 105
 106        case SUNKBD_RET_LAYOUT:
 107                sunkbd->layout = -1;
 108                break;
 109
 110        case SUNKBD_RET_ALLUP: /* All keys released */
 111                break;
 112
 113        default:
 114                if (!sunkbd->enabled)
 115                        break;
 116
 117                if (sunkbd->keycode[data & SUNKBD_KEY]) {
 118                        input_report_key(sunkbd->dev,
 119                                         sunkbd->keycode[data & SUNKBD_KEY],
 120                                         !(data & SUNKBD_RELEASE));
 121                        input_sync(sunkbd->dev);
 122                } else {
 123                        printk(KERN_WARNING
 124                                "sunkbd.c: Unknown key (scancode %#x) %s.\n",
 125                                data & SUNKBD_KEY,
 126                                data & SUNKBD_RELEASE ? "released" : "pressed");
 127                }
 128        }
 129out:
 130        return IRQ_HANDLED;
 131}
 132
 133/*
 134 * sunkbd_event() handles events from the input module.
 135 */
 136
 137static int sunkbd_event(struct input_dev *dev,
 138                        unsigned int type, unsigned int code, int value)
 139{
 140        struct sunkbd *sunkbd = input_get_drvdata(dev);
 141
 142        switch (type) {
 143
 144        case EV_LED:
 145
 146                serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
 147                serio_write(sunkbd->serio,
 148                        (!!test_bit(LED_CAPSL,   dev->led) << 3) |
 149                        (!!test_bit(LED_SCROLLL, dev->led) << 2) |
 150                        (!!test_bit(LED_COMPOSE, dev->led) << 1) |
 151                         !!test_bit(LED_NUML,    dev->led));
 152                return 0;
 153
 154        case EV_SND:
 155
 156                switch (code) {
 157
 158                case SND_CLICK:
 159                        serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
 160                        return 0;
 161
 162                case SND_BELL:
 163                        serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
 164                        return 0;
 165                }
 166
 167                break;
 168        }
 169
 170        return -1;
 171}
 172
 173/*
 174 * sunkbd_initialize() checks for a Sun keyboard attached, and determines
 175 * its type.
 176 */
 177
 178static int sunkbd_initialize(struct sunkbd *sunkbd)
 179{
 180        sunkbd->reset = -2;
 181        serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
 182        wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
 183        if (sunkbd->reset < 0)
 184                return -1;
 185
 186        sunkbd->type = sunkbd->reset;
 187
 188        if (sunkbd->type == 4) {        /* Type 4 keyboard */
 189                sunkbd->layout = -2;
 190                serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
 191                wait_event_interruptible_timeout(sunkbd->wait,
 192                                                 sunkbd->layout >= 0, HZ / 4);
 193                if (sunkbd->layout < 0)
 194                        return -1;
 195                if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
 196                        sunkbd->type = 5;
 197        }
 198
 199        return 0;
 200}
 201
 202/*
 203 * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
 204 * were in.
 205 */
 206
 207static void sunkbd_reinit(struct work_struct *work)
 208{
 209        struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
 210
 211        wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
 212
 213        serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
 214        serio_write(sunkbd->serio,
 215                (!!test_bit(LED_CAPSL,   sunkbd->dev->led) << 3) |
 216                (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
 217                (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
 218                 !!test_bit(LED_NUML,    sunkbd->dev->led));
 219        serio_write(sunkbd->serio,
 220                SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
 221        serio_write(sunkbd->serio,
 222                SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
 223}
 224
 225static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
 226{
 227        serio_pause_rx(sunkbd->serio);
 228        sunkbd->enabled = enable;
 229        serio_continue_rx(sunkbd->serio);
 230}
 231
 232/*
 233 * sunkbd_connect() probes for a Sun keyboard and fills the necessary
 234 * structures.
 235 */
 236
 237static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
 238{
 239        struct sunkbd *sunkbd;
 240        struct input_dev *input_dev;
 241        int err = -ENOMEM;
 242        int i;
 243
 244        sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
 245        input_dev = input_allocate_device();
 246        if (!sunkbd || !input_dev)
 247                goto fail1;
 248
 249        sunkbd->serio = serio;
 250        sunkbd->dev = input_dev;
 251        init_waitqueue_head(&sunkbd->wait);
 252        INIT_WORK(&sunkbd->tq, sunkbd_reinit);
 253        snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
 254
 255        serio_set_drvdata(serio, sunkbd);
 256
 257        err = serio_open(serio, drv);
 258        if (err)
 259                goto fail2;
 260
 261        if (sunkbd_initialize(sunkbd) < 0) {
 262                err = -ENODEV;
 263                goto fail3;
 264        }
 265
 266        snprintf(sunkbd->name, sizeof(sunkbd->name),
 267                 "Sun Type %d keyboard", sunkbd->type);
 268        memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
 269
 270        input_dev->name = sunkbd->name;
 271        input_dev->phys = sunkbd->phys;
 272        input_dev->id.bustype = BUS_RS232;
 273        input_dev->id.vendor  = SERIO_SUNKBD;
 274        input_dev->id.product = sunkbd->type;
 275        input_dev->id.version = 0x0100;
 276        input_dev->dev.parent = &serio->dev;
 277
 278        input_set_drvdata(input_dev, sunkbd);
 279
 280        input_dev->event = sunkbd_event;
 281
 282        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
 283                BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
 284        input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
 285                BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
 286        input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
 287
 288        input_dev->keycode = sunkbd->keycode;
 289        input_dev->keycodesize = sizeof(unsigned char);
 290        input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
 291        for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
 292                __set_bit(sunkbd->keycode[i], input_dev->keybit);
 293        __clear_bit(KEY_RESERVED, input_dev->keybit);
 294
 295        sunkbd_enable(sunkbd, true);
 296
 297        err = input_register_device(sunkbd->dev);
 298        if (err)
 299                goto fail4;
 300
 301        return 0;
 302
 303 fail4: sunkbd_enable(sunkbd, false);
 304 fail3: serio_close(serio);
 305 fail2: serio_set_drvdata(serio, NULL);
 306 fail1: input_free_device(input_dev);
 307        kfree(sunkbd);
 308        return err;
 309}
 310
 311/*
 312 * sunkbd_disconnect() unregisters and closes behind us.
 313 */
 314
 315static void sunkbd_disconnect(struct serio *serio)
 316{
 317        struct sunkbd *sunkbd = serio_get_drvdata(serio);
 318
 319        sunkbd_enable(sunkbd, false);
 320        input_unregister_device(sunkbd->dev);
 321        serio_close(serio);
 322        serio_set_drvdata(serio, NULL);
 323        kfree(sunkbd);
 324}
 325
 326static const struct serio_device_id sunkbd_serio_ids[] = {
 327        {
 328                .type   = SERIO_RS232,
 329                .proto  = SERIO_SUNKBD,
 330                .id     = SERIO_ANY,
 331                .extra  = SERIO_ANY,
 332        },
 333        {
 334                .type   = SERIO_RS232,
 335                .proto  = SERIO_UNKNOWN, /* sunkbd does probe */
 336                .id     = SERIO_ANY,
 337                .extra  = SERIO_ANY,
 338        },
 339        { 0 }
 340};
 341
 342MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
 343
 344static struct serio_driver sunkbd_drv = {
 345        .driver         = {
 346                .name   = "sunkbd",
 347        },
 348        .description    = DRIVER_DESC,
 349        .id_table       = sunkbd_serio_ids,
 350        .interrupt      = sunkbd_interrupt,
 351        .connect        = sunkbd_connect,
 352        .disconnect     = sunkbd_disconnect,
 353};
 354
 355module_serio_driver(sunkbd_drv);
 356