Question

I have a hardware device and the vendor that supplied it gave a bit of C code to listen for button presses which uses ioctl. The device has an SSD1289 controller.

Push buttons require no additional pins, their status canbe read over SPI.

That's what I want, to read which push button was pressed.

I am trying to replicate this script in Python for my own application, but the _IOR and ioctl requirements are throwing me off.

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#define SSD1289_GET_KEYS _IOR('keys', 1, unsigned char *)

void get_keys(int fd)
{
    unsigned char keys;
    if (ioctl(fd, SSD1289_GET_KEYS, &keys) == -1)
    {
        perror("_apps ioctl get");
    }
    else
    {
        printf("Keys : %2x\n", keys);
    }
}

int main(int argc, char *argv[])
{
    char *file_name = "/dev/fb1";
    int fd;
    fd = open(file_name, O_RDWR);
    if (fd == -1)
    {
        perror("_apps open");
        return 2;
    }

    while(1)
    get_keys(fd);

    printf("Ioctl Number: (int)%d  (hex)%x\n", SSD1289_GET_KEYS, SSD1289_GET_KEYS);
    close (fd);
    return 0;
}

Now I know that Python has an ioctl module, and at some point I should be calling

file = open("/dev/fb1")
buf = array.array('h', [0])
fcntl.ioctl(file,  ????, buf, 1)

I can't figure out what the SSD1289_GET_KEYS is supposed to be. How do I get this and what is _IOR?

Also, if this is the wrong approach, knowing that would be a help too. There are libraries such as spidev which are supposedly for SPI, but I don't know what to read using it.


@alexis provided some useful steps below, which got me to this point:

import fcntl
import array
file = open("/dev/fb1")
buf = array.array('h', [0])
fcntl.ioctl(file, -444763391, buf, 1)

Now, pressing a button changes the value of buf if I keep the above in a loop.

Was it helpful?

Solution

You're on the right track, you just need to figure out the constant to use. Your vendor's program will actually print it out, in decimal and hex-- if you would just edit main() and move the printf line above the endless while loop:

printf("Ioctl Number: (int)%d  (hex)%x\n", SSD1289_GET_KEYS, SSD1289_GET_KEYS);

while(1)
    get_keys(fd);

Explanation:

_IOR is a macro defined in sys/ioctl.h. Its definition is as follows:

#define _IOC(inout,group,num,len) \
    (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num))
#define _IO(g,n)    _IOC(IOC_VOID,  (g), (n), 0)
#define _IOR(g,n,t) _IOC(IOC_OUT,   (g), (n), sizeof(t))
#define _IOW(g,n,t) _IOC(IOC_IN,    (g), (n), sizeof(t))

I have included the relevant context lines. You can see that this macro constructs a bit mask that (we can tell from the name) deals with read operations. But your goal is to figure out the bitmask you need, which you can do without too much trouble: Run your vendor's C program through cc -E, and you'll see the source after preprocessor commands have applied. Track down the definition of get_keys (there'll be a whole lot of header files first, so it'll be at the very end of the output), and pull out the second argument.

The result just might be system-dependent, so you should really try it yourself. On my box, it comes out as

((__uint32_t)0x40000000 | ((sizeof(unsigned char *) & 0x1fff) << 16) | ((('keys')) << 8) | ((1)))

Not eager to translate that into python, I added the following lines at the very start of main():

printf("%d", ((__uint32_t)0x40000000 | ((sizeof(unsigned char *) & 0x1fff) << 16) | 
               ((('keys')) << 8) | ((1))));
exit(0);

I ran the program and it gave me the output 1702458113, which may be the value you need. It should be the same as the decimal output from the printf command that was already there (but hidden below the endless while loop). But check it yourself and don't blame me if you blow out your hardware or something!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top