[Solved] Strange reboot when testing PS/2 mouse driver

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

[Solved] Strange reboot when testing PS/2 mouse driver

Post by mystran »

Ok, so I wrote a better keyboard/mouse driver..

The first problem was that if driver does proper init on keyboard, my computer's BIOS thinks it should disable USB legacy support..

Fine.

So I plug my keyboard (and mouse) into the PS/2 port, and try again... BANG. Reboot. Unplugging the PS/2 mouse, everything works fine (once the mouse driver timeouts, which takes a while), but ... like... why does it reboot?

Once again, this obvious works in Bochs. And I suspect it's not a tripple-fault, because there isn't much of anything that could cause CPU exceptions in the code.. so obviously my system thinks it's time to reboot...

Can anybody spot anything strange/invalid/not-good-idea in this code:

Code: Select all

static int console_mouse_init() {

    printk(" - PS/2 mouse: enable");

    // enable PS/2 mouse
    if(KBD_write_cmd(0xE8)) return -1;

    printk(" interface");
    // enable it's interrupt reporting
    {
        // fetch command word
        if(KBD_write_cmd(0x20)) return -1;
        int cb = KBD_read();
        if(cb < 0) return -1;

        // set bit1 (aux interrupt),
        // clear bit5 (aux clock inhibit)
        // store the resulting new command word
        if(KBD_write_cmd(0x60)) return -1;
        if(KBD_write((cb |= 0x2) & ~0x20)) return -1;
    }

    printk(" defaults");
    // Send set defaults
    if(KBD_write_aux(0xF6) || KBD_wait_ack()) return -1;

    printk(" data");
    // Enable data reporting
    if(KBD_write_aux(0xF4) || KBD_wait_ack()) return -1;

    // TODO: detect IMPS/2, with or without 5 buttons
    console_mouse_imps2 = 0;

    printk(".\n");
    return 0;
}
The macros relevant are KBD_write_cmd (write to controller), KBD_write_aux (write to aux by prefixing), KBD_read (read from 0x60) and KBD_wait_ack (poll for ACK).

All return -1 on error..

Code: Select all

int KBD_wait_write() {
    unsigned int timeout = 100000;
    unsigned char c;
    while(--timeout) {
        c = in8_p(0x64);
        if(!(c & 2)) {
            return 0;
        }
    }
    return -1;
}

int KBD_write(unsigned char byte) {
    if(!KBD_wait_write()) {
        out8_p(0x60, byte);
        return 0;
    }
    else return -1;
}

int KBD_write_cmd(unsigned char byte) {
    if(!KBD_wait_write()) {
        out8_p(0x64, byte);
        return 0;
    }
    else return -1;
}

int KBD_write_aux(unsigned char byte) {
    if(KBD_write_cmd(0xD4) || KBD_write(byte)) return -1;
    else return 0;
}

// return unsigned char if success, -1 if fails
int KBD_read() {
    unsigned int timeout = 100000;
    unsigned char c;
    while(--timeout) {
        c = in8_p(0x64);
        if(c & 1) {
            c = in8_p(0x60);
            return (unsigned) c;
        }
    }
    return -1;
}

int KBD_wait_ack() {
    unsigned int timeout = 100000;
    while(--timeout) {
        unsigned char c = KBD_read();
        if(c == 0xFA) return 0; // got ack, we're happy

        if(!c) {
            // if we get 0, spend some time and retry
            int i;
            for(i = 0; i < 1000; i++) iodelay();
            continue;
        } else {
            printk("console_input: 0x%2x while expecting ACK\n", c);
            return -1;
        }
    }
    return -1;
}
This is the first reboot problem I've had for a while (I get panic's all the time, but almost never any reboots) I'm fairly sure it's the driver code upsetting my mobo somehow..
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

Problem was that initialization has a typo in the hex-codes.. :)

Also modularized init, now I can detect keyboard and mouse independently, so you can have neither, both, or either one alone. :)

What I still wonder is how to handle x/y overflows for mouse. Are those even useful for streaming mode? I've been unable to move my mouse fast enough to see what kind of packet I'd get for overflow..

Anyway, works perfectly now. :)

Corrected code follows:

Code: Select all

// this is for handler to know what length of packet to expect
// and how to decode the IMPS/2 extra byte..
static int console_mouse_imps2;

static int console_mouse_init() {

    // check interface
    if(KBD_write_cmd(0xA9) || KBD_read() != 0x00) {
        printk("console_input: PS/2 mouse interface test failed.\n");
        return -1;
    }

    // enable PS/2 mouse
    if(KBD_write_cmd(0xA8)) return -1; // <-- problem was here <--

    // enable it's interrupt reporting
    {
        // fetch command word
        if(KBD_write_cmd(0x20)) return -1;
        int cb = KBD_read();
        if(cb < 0) return -1;

        // set bit1 (aux interrupt),
        // store the resulting new command word
        if(KBD_write_cmd(0x60)) return -1;
        if(KBD_write(cb |= 0x2)) return -1; // <-- only need to set aux-int bit <--
    }

    // Send set defaults
    if(KBD_write_aux(0xF6) || KBD_wait_ack()) return -1;

    // Enable data reporting
    if(KBD_write_aux(0xF4) || KBD_wait_ack()) return -1;

    // detect IMPS/2 wheel mouse

    console_mouse_imps2 = 0;
    // set sample rate 200, then 100, then 80, then get ID
    if(KBD_write_aux(0xF3)     || KBD_wait_ack()
        || KBD_write_aux(200)  || KBD_wait_ack()
        || KBD_write_aux(0xF3) || KBD_wait_ack()
        || KBD_write_aux(100)  || KBD_wait_ack()
        || KBD_write_aux(0xF3) || KBD_wait_ack()
        || KBD_write_aux(80)   || KBD_wait_ack()
        || KBD_write_aux(0xF2) || KBD_wait_ack()) return -1;

    {
        int id = KBD_read();
        if(id < 0) return -1;
        if(id == 3) {
            console_mouse_imps2 = 1;
        }
    }

    // detect IMPS/2 wheel and 5 buttons mouse

    // set sample rate 200, then 200, then 80, then get ID
    if(KBD_write_aux(0xF3)     || KBD_wait_ack()
        || KBD_write_aux(200)  || KBD_wait_ack()
        || KBD_write_aux(0xF3) || KBD_wait_ack()
        || KBD_write_aux(200)  || KBD_wait_ack()
        || KBD_write_aux(0xF3) || KBD_wait_ack()
        || KBD_write_aux(80)   || KBD_wait_ack()
        || KBD_write_aux(0xF2) || KBD_wait_ack()) return -1;

    {
        int id = KBD_read();
        if(id < 0) return -1;
        if(id == 4) {
            console_mouse_imps2 = 2;
        }
    }

    static const char * mouse_types[3] = {
        "normal", "wheel", "extended wheel"
    };
    printk(" - PS/2 mouse: %s mouse detected.\n",
            mouse_types[console_mouse_imps2]);

    return 0;
}
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Post by ~ »

Curiously I never seem to get a mouse overflow (I always get 4 packet bytes seamlessly -- since I have enabled Mouse Wheel).

No matter what kind of mechanical "overload" I perform to the mouse and keyboard, nor if I disconnect/reconnect them: they always work properly.

And, I can disconnect the mouse and it will reinitialize properly, and the keyboard, will reconfigure itself to recover its LED status and its high repeat speed.
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

What I mean is the overflow bits in the mouse packets. I'm interested in how to interpret x/y-movement fields if the mouse sends a packet with the x/y-overflow bits set.

I've been unable to get such a packet from any mouse I have here. I guess I could set a delay in my driver, and then spin the sensor of a mechanical mouse... but thought I'd ask in case somebody knew about those already.

Since I run the mouse in streaming mode, I guess it should be exceptional to get such a packet (some other driver / kernel code blocks interrupts for long enough while the user moves mouse enough to get an interrupt). Testing on real hardware, I typically get movement less than 20, even when moving mouse really fast. Most of the time for normal movement it's less than 5.

So I guess I could just drop such packets. But if (say) the sign bit is still valid, one could use the maximum movement to that direction, instead of dropping the whole packet... no idea if it makes much of a difference.
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

Oh and I don't bother reinitializing mouse/keyboard if they get unplugged. Not yet at least. You're not supposed to do that, because the standard doesn't require the ports to be designed to deal with that (at least in theory it's your problem if such action blows your mobo). Whether it works, depends on the mobo. I've seen computers where you need to hit reset to get it work again.

At least Windows tries to support replugging though, if it has the relevant drivers loaded (it won't have them if you're never plugged in one before). There are several report around the internet, that it works about 50% of the time, depending mostly (afaik) on your system.

But yeah, you could try detecting a replugged device, and just do normal init, and if your mobo/chipset supports it, then it's possible to get work.

Maybe I'll support it in the future. It's basicly just a redetection routine to add to my current driver.
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
Post Reply