Subversion Repositories shark

Rev

Rev 522 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * AT and PS/2 keyboard driver
 *
 * Copyright (c) 1999-2002 Vojtech Pavlik
 */


/*
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */


/*
 * This driver can handle standard AT keyboards and PS/2 keyboards in
 * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb
 * input-only controllers and AT keyboards connected over a one way RS232
 * converter.
 */


#include <linuxcomp.h>

#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/workqueue.h>
#include <linux/timer.h>

//#define ATKBD_DEBUG

MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("AT and PS/2 keyboard driver");
MODULE_PARM(atkbd_set, "1i");
MODULE_PARM(atkbd_reset, "1i");
MODULE_PARM(atkbd_softrepeat, "1i");
MODULE_LICENSE("GPL");

static int atkbd_set = 2;
#if defined(__i386__) || defined (__x86_64__)
static int atkbd_reset;
#else
static int atkbd_reset = 1;
#endif
static int atkbd_softrepeat;

/*
 * Scancode to keycode tables. These are just the default setting, and
 * are loadable via an userland utility.
 */


static unsigned char atkbd_set2_keycode[512] = {
          0, 67, 65, 63, 61, 59, 60, 88,  0, 68, 66, 64, 62, 15, 41, 85,
          0, 56, 42,182, 29, 16,  2, 89,  0,  0, 44, 31, 30, 17,  3, 90,
          0, 46, 45, 32, 18,  5,  4, 91, 90, 57, 47, 33, 20, 19,  6,  0,
         91, 49, 48, 35, 34, 21,  7,  0,  0,  0, 50, 36, 22,  8,  9,  0,
          0, 51, 37, 23, 24, 11, 10,  0,  0, 52, 53, 38, 39, 25, 12,  0,
        122, 89, 40,120, 26, 13,  0,  0, 58, 54, 28, 27,  0, 43,  0,  0,
         85, 86, 90, 91, 92, 93, 14, 94, 95, 79,183, 75, 71,121,  0,123,
         82, 83, 80, 76, 77, 72,  1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
          0,  0,  0, 65, 99,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,
          0,  0, 92, 90, 85,  0,137,  0,  0,  0,  0, 91, 89,144,115,  0,
        217,100,255,  0, 97,165,164,  0,156,  0,  0,140,115,  0,  0,125,
        173,114,  0,113,152,163,151,126,128,166,  0,140,  0,147,  0,127,
        159,167,115,160,164,  0,  0,116,158,  0,150,166,  0,  0,  0,142,
        157,  0,114,166,168,  0,  0,213,155,  0, 98,113,  0,163,  0,138,
        226,  0,  0,  0,  0,  0,153,140,  0,255, 96,  0,  0,  0,143,  0,
        133,  0,116,  0,143,  0,174,133,  0,107,  0,105,102,  0,  0,112,
        110,111,108,112,106,103,  0,119,  0,118,109,  0, 99,104,119
};

static unsigned char atkbd_set3_keycode[512] = {
          0,  0,  0,  0,  0,  0,  0, 59,  1,138,128,129,130, 15, 41, 60,
        131, 29, 42, 86, 58, 16,  2, 61,133, 56, 44, 31, 30, 17,  3, 62,
        134, 46, 45, 32, 18,  5,  4, 63,135, 57, 47, 33, 20, 19,  6, 64,
        136, 49, 48, 35, 34, 21,  7, 65,137,100, 50, 36, 22,  8,  9, 66,
        125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68,
        113,114, 40, 84, 26, 13, 87, 99, 97, 54, 28, 27, 43, 84, 88, 70,
        108,105,119,103,111,107, 14,110,  0, 79,106, 75, 71,109,102,104,
         82, 83, 80, 76, 77, 72, 69, 98,  0, 96, 81,  0, 78, 73, 55, 85,
         89, 90, 91, 92, 74,185,184,182,  0,  0,  0,125,126,127,112,  0,
          0,139,150,163,165,115,152,150,166,140,160,154,113,114,167,168,
        148,149,147,140,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255
};

static unsigned char atkbd_unxlate_table[128] = {
          0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
         21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
         35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
         50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88,  5,  6,  4, 12,  3,
         11,  2, 10,  1,  9,119,126,108,117,125,123,107,115,116,121,105,
        114,122,112,113,127, 96, 97,120,  7, 15, 23, 31, 39, 47, 55, 63,
         71, 79, 86, 94,  8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
         19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
};

#define ATKBD_CMD_SETLEDS       0x10ed
#define ATKBD_CMD_GSCANSET      0x11f0
#define ATKBD_CMD_SSCANSET      0x10f0
#define ATKBD_CMD_GETID         0x02f2
#define ATKBD_CMD_SETREP        0x10f3
#define ATKBD_CMD_ENABLE        0x00f4
#define ATKBD_CMD_RESET_DIS     0x00f5
#define ATKBD_CMD_SETALL_MBR    0x00fa
#define ATKBD_CMD_RESET_BAT     0x02ff
#define ATKBD_CMD_RESEND        0x00fe
#define ATKBD_CMD_EX_ENABLE     0x10ea
#define ATKBD_CMD_EX_SETLEDS    0x20eb
#define ATKBD_CMD_OK_GETID      0x02e8

#define ATKBD_RET_ACK           0xfa
#define ATKBD_RET_NAK           0xfe
#define ATKBD_RET_BAT           0xaa
#define ATKBD_RET_EMUL0         0xe0
#define ATKBD_RET_EMULX         0x80
#define ATKBD_RET_EMUL1         0xe1
#define ATKBD_RET_RELEASE       0xf0

#define ATKBD_KEY_UNKNOWN         0
#define ATKBD_KEY_NULL          255

/*
 * The atkbd control structure
 */


struct atkbd {
        unsigned char keycode[512];
        struct input_dev dev;
        struct serio *serio;
        struct timer_list timer;
        char name[64];
        char phys[32];
        unsigned char cmdbuf[4];
        unsigned char cmdcnt;
        unsigned char set;
        unsigned char release;
        int lastkey;
        volatile signed char ack;
        unsigned char emul;
        unsigned short id;
        unsigned char write;
        unsigned char translated;
        unsigned char resend;
        unsigned char bat_xl;
        unsigned int last;
        unsigned long time;
};

/*
 * atkbd_interrupt(). Here takes place processing of data received from
 * the keyboard into events.
 */


static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                        unsigned int flags, struct pt_regs *regs)
{
        struct atkbd *atkbd = serio->private;
        unsigned int code = data;
        int value;

#ifdef ATKBD_DEBUG
        printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
#endif

#if !defined(__i386__) && !defined (__x86_64__)
        if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) {
                printk("atkbd.c: frame/parity error: %02x\n", flags);
                serio_write(serio, ATKBD_CMD_RESEND);
                atkbd->resend = 1;
                goto out;
        }
       
        if (!flags && data == ATKBD_RET_ACK)
                atkbd->resend = 0;
#endif

        switch (code) {
                case ATKBD_RET_ACK:
                        atkbd->ack = 1;
                        goto out;
                case ATKBD_RET_NAK:
                        atkbd->ack = -1;
                        goto out;
        }

        if (atkbd->translated) do {

                if (atkbd->emul != 1) {
                        if (code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1)
                                break;
                        if (code == ATKBD_RET_BAT) {
                                if (!atkbd->bat_xl)
                                        break;
                                atkbd->bat_xl = 0;
                        }
                        if (code == (ATKBD_RET_BAT & 0x7f))
                                atkbd->bat_xl = 1;
                }

                if (code < 0x80) {
                        code = atkbd_unxlate_table[code];
                        break;
                }

                if (atkbd->cmdcnt)
                        break;

                code = atkbd_unxlate_table[code & 0x7f];
                atkbd->release = 1;

        } while (0);

        if (atkbd->cmdcnt) {
                atkbd->cmdbuf[--atkbd->cmdcnt] = code;
                goto out;
        }

        switch (code) {
                case ATKBD_RET_BAT:
                        serio_rescan(atkbd->serio);
                        goto out;
                case ATKBD_RET_EMUL0:
                        atkbd->emul = 1;
                        goto out;
                case ATKBD_RET_EMUL1:
                        atkbd->emul = 2;
                        goto out;
                case ATKBD_RET_RELEASE:
                        atkbd->release = 1;
                        goto out;
        }

        if (atkbd->emul) {
                if (--atkbd->emul)
                        goto out;
                code |= 0x100;
        }

        switch (atkbd->keycode[code]) {
                case ATKBD_KEY_NULL:
                        break;
                case ATKBD_KEY_UNKNOWN:
                        printk(KERN_WARNING "atkbd.c: Unknown key %s (%s set %d, code %#x, data %#x, on %s).\n",
                                atkbd->release ? "released" : "pressed",
                                atkbd->translated ? "translated" : "raw",
                                atkbd->set, code, data, serio->phys);
                        break;
                default:
                        value = atkbd->release ? 0 :
                                (1 + (!atkbd_softrepeat && test_bit(atkbd->keycode[code], atkbd->dev.key)));

                        switch (value) {        /* Workaround Toshiba laptop multiple keypress */
                                case 0:
                                        atkbd->last = 0;
                                        break;
                                case 1:
                                        atkbd->last = code;
                                        atkbd->time = jiffies26 + (atkbd->dev.rep[REP_DELAY] * HZ + 500) / 1000 / 2;
                                        break;
                                case 2:
                                        if (!time_after(jiffies26, atkbd->time) && atkbd->last == code)
                                                value = 1;
                                        break;
                        }

                        input_regs(&atkbd->dev, regs);
                        input_event(&atkbd->dev, EV_KEY, atkbd->keycode[code], value);
                        input_sync(&atkbd->dev);
        }

        atkbd->release = 0;
out:
        return IRQ_HANDLED;
}

/*
 * atkbd_sendbyte() sends a byte to the keyboard, and waits for
 * acknowledge. It doesn't handle resends according to the keyboard
 * protocol specs, because if these are needed, the keyboard needs
 * replacement anyway, and they only make a mess in the protocol.
 */


static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
{
        int timeout = 20000; /* 200 msec */
        atkbd->ack = 0;

#ifdef ATKBD_DEBUG
        printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
#endif
        if (serio_write(atkbd->serio, byte))
                return -1;

        while (!atkbd->ack && timeout--) udelay(10);

        return -(atkbd->ack <= 0);
}

/*
 * atkbd_command() sends a command, and its parameters to the keyboard,
 * then waits for the response and puts it in the param array.
 */


static int atkbd_command(struct atkbd *atkbd, unsigned char *param, int command)
{
        int timeout = 500000; /* 500 msec */
        int send = (command >> 12) & 0xf;
        int receive = (command >> 8) & 0xf;
        int i;

        atkbd->cmdcnt = receive;

        if (command == ATKBD_CMD_RESET_BAT)
                timeout = 2000000; /* 2 sec */
       
        if (command & 0xff)
                if (atkbd_sendbyte(atkbd, command & 0xff))
                        return (atkbd->cmdcnt = 0) - 1;

        for (i = 0; i < send; i++)
                if (atkbd_sendbyte(atkbd, param[i]))
                        return (atkbd->cmdcnt = 0) - 1;

        while (atkbd->cmdcnt && timeout--) {

                if (atkbd->cmdcnt == 1 &&
                    command == ATKBD_CMD_RESET_BAT && timeout > 100000)
                        timeout = 100000;

                if (atkbd->cmdcnt == 1 && command == ATKBD_CMD_GETID &&
                    atkbd->cmdbuf[1] != 0xab && atkbd->cmdbuf[1] != 0xac) {
                        atkbd->cmdcnt = 0;
                        break;
                }
       
                udelay(1);
        }

        if (param)
                for (i = 0; i < receive; i++)
                        param[i] = atkbd->cmdbuf[(receive - 1) - i];

        if (command == ATKBD_CMD_RESET_BAT && atkbd->cmdcnt == 1)
                atkbd->cmdcnt = 0;

        if (atkbd->cmdcnt) {
                atkbd->cmdcnt = 0;
                return -1;
        }

        return 0;
}

/*
 * Event callback from the input module. Events that change the state of
 * the hardware are processed here.
 */


static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
        struct atkbd *atkbd = dev->private;
        struct { int p; u8 v; } period[] =     
                { {30, 0x00}, {25, 0x02}, {20, 0x04}, {15, 0x08}, {10, 0x0c}, {7, 0x10}, {5, 0x14}, {0, 0x14} };
        struct { int d; u8 v; } delay[] =
                { {1000, 0x60}, {750, 0x40}, {500, 0x20}, {250, 0x00}, {0, 0x00} };
        char param[2];
        int i, j;

        if (!atkbd->write)
                return -1;

        switch (type) {

                case EV_LED:

                        *param = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
                               | (test_bit(LED_NUML,    dev->led) ? 2 : 0)
                               | (test_bit(LED_CAPSL,   dev->led) ? 4 : 0);
                        atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS);

                        if (atkbd->set == 4) {
                                param[0] = 0;
                                param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
                                         | (test_bit(LED_SLEEP,   dev->led) ? 0x02 : 0)
                                         | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
                                         | (test_bit(LED_MISC,    dev->led) ? 0x10 : 0)
                                         | (test_bit(LED_MUTE,    dev->led) ? 0x20 : 0);
                                atkbd_command(atkbd, param, ATKBD_CMD_EX_SETLEDS);
                        }

                        return 0;


                case EV_REP:

                        if (atkbd_softrepeat) return 0;

                        i = j = 0;
                        while (period[i].p > dev->rep[REP_PERIOD]) i++;
                        while (delay[j].d > dev->rep[REP_DELAY]) j++;
                        dev->rep[REP_PERIOD] = period[i].p;
                        dev->rep[REP_DELAY] = delay[j].d;
                        param[0] = period[i].v | delay[j].v;
                        atkbd_command(atkbd, param, ATKBD_CMD_SETREP);

                        return 0;
                       
                case EV_PWR:
                        if (code) {
                                /* Enable the keyboard to receive keystrokes. */
                                if (atkbd_command(atkbd, NULL, ATKBD_CMD_ENABLE)) {
                                        printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n", atkbd->serio->phys);
                                        return -1;
                                }
                        } else {
                                /* Disable the keyboard to receive keystrokes. */
                                if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_DIS)) {
                                        printk(KERN_ERR "atkbd.c: Failed to disable keyboard on %s\n", atkbd->serio->phys);
                                        return -1;
                                }
                        }
                        return 0;      
        }

        return -1;
}

/*
 * atkbd_probe() probes for an AT keyboard on a serio port.
 */


static int atkbd_probe(struct atkbd *atkbd)
{
        unsigned char param[2];

/*
 * Some systems, where the bit-twiddling when testing the io-lines of the
 * controller may confuse the keyboard need a full reset of the keyboard. On
 * these systems the BIOS also usually doesn't do it for us.
 */


        if (atkbd_reset)
                if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT))
                        printk(KERN_WARNING "atkbd.c: keyboard reset failed on %s\n", atkbd->serio->phys);

/*
 * Then we check the keyboard ID. We should get 0xab83 under normal conditions.
 * Some keyboards report different values, but the first byte is always 0xab or
 * 0xac. Some old AT keyboards don't report anything. If a mouse is connected, this
 * should make sure we don't try to set the LEDs on it.
 */


        if (atkbd_command(atkbd, param, ATKBD_CMD_GETID)) {

/*
 * If the get ID command failed, we check if we can at least set the LEDs on
 * the keyboard. This should work on every keyboard out there. It also turns
 * the LEDs off, which we want anyway.
 */

                param[0] = 0;
                if (atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS))
                        return -1;
                atkbd->id = 0xabba;
                return 0;
        }

        if (param[0] != 0xab && param[0] != 0xac)
                return -1;
        atkbd->id = (param[0] << 8) | param[1];


        return 0;
}

/*
 * atkbd_set_3 checks if a keyboard has a working Set 3 support, and
 * sets it into that. Unfortunately there are keyboards that can be switched
 * to Set 3, but don't work well in that (BTC Multimedia ...)
 */


static int atkbd_set_3(struct atkbd *atkbd)
{
        unsigned char param[2];

/*
 * For known special keyboards we can go ahead and set the correct set.
 * We check for NCD PS/2 Sun, NorthGate OmniKey 101 and
 * IBM RapidAccess / IBM EzButton / Chicony KBP-8993 keyboards.
 */


        if (atkbd->translated)
                return 2;

        if (atkbd->id == 0xaca1) {
                param[0] = 3;
                atkbd_command(atkbd, param, ATKBD_CMD_SSCANSET);
                return 3;
        }

        if (atkbd_set != 2)
                if (!atkbd_command(atkbd, param, ATKBD_CMD_OK_GETID)) {
                        atkbd->id = param[0] << 8 | param[1];
                        return 2;
                }

        if (atkbd_set == 4) {
                param[0] = 0x71;
                if (!atkbd_command(atkbd, param, ATKBD_CMD_EX_ENABLE))
                        return 4;
        }

        if (atkbd_set != 3)
                return 2;

        param[0] = 3;
        if (atkbd_command(atkbd, param, ATKBD_CMD_SSCANSET))
                return 2;

        param[0] = 0;
        if (atkbd_command(atkbd, param, ATKBD_CMD_GSCANSET))
                return 2;

        if (param[0] != 3) {
                param[0] = 2;
                if (atkbd_command(atkbd, param, ATKBD_CMD_SSCANSET))
                return 2;
        }

        atkbd_command(atkbd, param, ATKBD_CMD_SETALL_MBR);

        return 3;
}

static int atkbd_enable(struct atkbd *atkbd)
{
        unsigned char param[1];

/*
 * Set the LEDs to a defined state.
 */


        param[0] = 0;
        if (atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS))
                return -1;

/*
 * Set autorepeat to fastest possible.
 */


        param[0] = 0;
        if (atkbd_command(atkbd, param, ATKBD_CMD_SETREP))
                return -1;

/*
 * Enable the keyboard to receive keystrokes.
 */


        /*if (atkbd_command(atkbd, NULL, ATKBD_CMD_ENABLE)) {
                printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n",
                        atkbd->serio->phys);
                return -1;
        }*/


        return 0;
}

/*
 * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
 * reboot.
 */


static void atkbd_cleanup(struct serio *serio)
{
        struct atkbd *atkbd = serio->private;
        atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT);
}

/*
 * atkbd_disconnect() closes and frees.
 */


static void atkbd_disconnect(struct serio *serio)
{
        struct atkbd *atkbd = serio->private;
        input_unregister_device(&atkbd->dev);
        serio_close(serio);
        kfree(atkbd);
}

/*
 * atkbd_connect() is called when the serio module finds and interface
 * that isn't handled yet by an appropriate device driver. We check if
 * there is an AT keyboard out there and if yes, we register ourselves
 * to the input module.
 */


static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
{
        struct atkbd *atkbd;
        int i;

        if (!(atkbd = kmalloc(sizeof(struct atkbd), GFP_KERNEL)))
                return;
        memset(atkbd, 0, sizeof(struct atkbd));

        switch (serio->type & SERIO_TYPE) {

                case SERIO_8042_XL:
                        atkbd->translated = 1;
                case SERIO_8042:
                        if (serio->write)
                                atkbd->write = 1;
                        break;
                case SERIO_RS232:
                        if ((serio->type & SERIO_PROTO) == SERIO_PS2SER)
                                break;
                default:
                        kfree(atkbd);
                        return;
        }
                       
        if (atkbd->write) {
                atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP) | BIT(EV_PWR);
                atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
        } else  atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);

        if (!atkbd_softrepeat) {
                atkbd->dev.rep[REP_DELAY] = 250;
                atkbd->dev.rep[REP_PERIOD] = 33;
        }

        atkbd->serio = serio;

        init_input_dev(&atkbd->dev);

        atkbd->dev.keycode = atkbd->keycode;
        atkbd->dev.keycodesize = sizeof(unsigned char);
        atkbd->dev.keycodemax = ARRAY_SIZE(atkbd_set2_keycode);
        atkbd->dev.event = atkbd_event;
        atkbd->dev.private = atkbd;

        serio->private = atkbd;

        if (serio_open(serio, dev)) {
                kfree(atkbd);
                return;
        }

        if (atkbd->write) {

                if (atkbd_probe(atkbd)) {
                        serio_close(serio);
                        kfree(atkbd);
                        return;
                }
               
                atkbd->set = atkbd_set_3(atkbd);
                atkbd_enable(atkbd);

        } else {
                atkbd->set = 2;
                atkbd->id = 0xab00;
        }

        if (atkbd->set == 4) {
                atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) | BIT(LED_SLEEP) | BIT(LED_MUTE) | BIT(LED_MISC);
                sprintf26(atkbd->name, "AT Set 2 Extended keyboard");
        } else
                sprintf26(atkbd->name, "AT %s Set %d keyboard",
                        atkbd->translated ? "Translated" : "Raw", atkbd->set);

        sprintf26(atkbd->phys, "%s/input0", serio->phys);

        if (atkbd->set == 3)
                memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode));
        else
                memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode));

        atkbd->dev.name = atkbd->name;
        atkbd->dev.phys = atkbd->phys;
        atkbd->dev.id.bustype = BUS_I8042;
        atkbd->dev.id.vendor = 0x0001;
        atkbd->dev.id.product = atkbd->set;
        atkbd->dev.id.version = atkbd->id;

        for (i = 0; i < 512; i++)
                if (atkbd->keycode[i] && atkbd->keycode[i] < 255)
                        set_bit(atkbd->keycode[i], atkbd->dev.keybit);

        input_register_device(&atkbd->dev);

        printk(KERN_INFO "input: %s on %s\n", atkbd->name, serio->phys);
}


static struct serio_dev atkbd_dev = {
        .interrupt =    atkbd_interrupt,
        .connect =      atkbd_connect,
        .disconnect =   atkbd_disconnect,
        .cleanup =      atkbd_cleanup,
};

#ifndef MODULE
static int __init atkbd_setup_set(char *str)
{
        int ints[4];
        str = get_options(str, ARRAY_SIZE(ints), ints);
        if (ints[0] > 0) atkbd_set = ints[1];
        return 1;
}
static int __init atkbd_setup_reset(char *str)
{
        int ints[4];
        str = get_options(str, ARRAY_SIZE(ints), ints);
        if (ints[0] > 0) atkbd_reset = ints[1];
        return 1;
}

__setup("atkbd_set=", atkbd_setup_set);
__setup("atkbd_reset", atkbd_setup_reset);
#endif

int __init atkbd_init(void)
{
        serio_register_device(&atkbd_dev);
        return 0;
}

void __exit atkbd_exit(void)
{
        serio_unregister_device(&atkbd_dev);
}

module_init(atkbd_init);
module_exit(atkbd_exit);