Driver accessing ports with inb() and outb()

1.3k views Asked by At

I am making a device driver that turns on and off the keyboard leds by receiving any combination of three, two, one or none digit which should be 1, 2 or 3, if I make:

 echo 12 > /dev/ledDevice

The program should turn on Num lock, Caps lock and turn off scroll lock, if I write:

 echo "" > /dev/ledDevice

Every led should be turned off, or turn on if it would be echo 123 but this does not happen, they always turn off. They are ubicated (in debian 6) in a port represented with an integer, in the positions o, 1 and 2. Additionally but I don't know if it's related, outb produce this exit on system log

atkbd.c: Spurious ACK on isa0060/serio0. Some program might be trying access hardware directly.

This is my source

static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
    char aux[BUF_LEN];
    int state = 0x00;
    int stateInitial = 0x00;
    int i =0;

    int timeout = 0;
    int retries = 7;
    printk(KERN_ALERT "Entering device_write");

    if(copy_from_user(aux,buff,len)){
        printk(KERN_ALERT "Problems in copy from user");
        return -EINVAL;
    }
    if (len  <= 4){
        for (i=0; i<len;i++){
            if(aux[i] == '3'){
                state = state | 0x01; //Scroll lock
            }else if(aux[i] == '1'){
                state = state | 0x02; //Caps lock
            }else if(aux[i]== '2'){
                state= state | 0x04; //Num lock
            }else if (aux[i] != '\n'){
                printk(KERN_ALERT "Error, wrong input.");
                return -EINVAL;
            }
        }
    }else   return -EINVAL;

    if (down_interruptible(&mtx)) /*SEMAPHORE LOCK*/
        return -EINTR;

    stateInitial = inb(0xed);

    stateInitial = stateInitial & 0xF8; //248 mask that deletes the 1, 2 and 3 bits (the led ones)
    state = stateInitial | state;

    /*
     AquĆ­ se modifican los leds
    */
    timeout = 1000;
    outb(0xed,0x60); // Telling the keyboard that we want to modify the leds
    udelay(timeout);

    while (retries!=0 && inb(0x60)!=0xfa) { // Waiting for the controller
        retries--;
        udelay(timeout);
    }
    if (retries!=0) { // prooving the keyboard is ready

        outb(state,0x60);
    }else{  
        up(&mtx);  
        return -EINVAL;
    }
    up(&mtx);

    printk(KERN_ALERT "getting out from device_write, %d bytes read",len);

    return len;
}
1

There are 1 answers

0
Gil On

This can be triggered in a myriad of situations. Quite a few key switchers and other tools trigger the ATKBD_RET_NAK and in some cases we sure are helpless.

Considering your code to be just, let us try to break the error code. From the looks of it, the error seems to arise from the atkbd_interrupt call.

atkbd_interrupt() deals with the processing of data received from the keyboard to events.

static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char
data, unsigned int flags)
{----}

The specific error message occurs due to the triggering of case ATKBD_RET_NAK coupled to unsigned char data argument.

case ATKBD_RET_NAK:
    if (printk_ratelimit())
        dev_warn(&serio->dev,
             "Spurious %s on %s. "
             "Some program might be trying access hardware directly.\n",
             data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);

The atkbd provides access to AT enhanced keyboard connected to the AT keyboard controller. Try bypassing KVM .