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