64-bit argument for fcntl.ioctl()
Question: In my Python (2.7.3) code, I'm trying to use an ioctl call, accepting a long
int (64 bit) as an argument. I'm on a 64-bit system, so a 64-bit int is the
same size as a pointer.

**My problem is that Python doesn't seem to accept a 64-bit int as the
argument for a fcntl.ioctl() call.** It happily accepts a 32-bit int or a
64-bit pointer - **but what I need is to pass a 64-bit int.**

Here's my ioctl handler:

    
    
    static long trivial_driver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
        long err = 0;
    
        switch (cmd)
        {
            case 1234:
                printk("=== (%u) Driver got arg %lx; arg<<32 is %lx\n", cmd, arg, arg<<32);
                break;
            case 5678:
                printk("=== (%u) Driver got arg %lx\n", cmd, arg);
                break;
            default:
                printk("=== OH NOES!!! %u %lu\n", cmd, arg);
                err = -EINVAL;
        }
    
        return err;
    }
    

In existing C code, I use the call like this:

    
    
    static int trivial_ioctl_test(){
        int ret;
        int fd = open(DEV_NAME, O_RDWR);
    
        unsigned long arg = 0xffff;
    
        ret = ioctl(fd, 1234, arg); // ===(1234) Driver got arg ffff; arg<<32 is ffff00000000
        arg = arg<<32;
        ret = ioctl(fd, 5678, arg); // === (5678) Driver got arg ffff00000000
        close(fd);
    
    }
    

In python, I open the device file, and then I get the following results:

    
    
    >>> from fcntl import ioctl
    >>> import os
    >>> fd = os.open (DEV_NAME, os.O_RDWR, 0666)
    >>> ioctl(fd, 1234, 0xffff)
    0
    >>> arg = 0xffff<<32
    >>> # Kernel log: === (1234) Driver got arg ffff; arg<<32 is ffff00000000
    >>> # This demonstrates that ioctl() happily accepts a 32-bit int as an argument.
    >>> import struct
    >>> ioctl(fd, 5678, struct.pack("L",arg))
    '\x00\x00\x00\x00\xff\xff\x00\x00'
    >>> # Kernel log: === (5678) Driver got arg 7fff9eb1fcb0
    >>> # This demonstrates that ioctl() happily accepts a 64-bit pointer as an argument.
    >>> ioctl(fd, 5678, arg)
    
    Traceback (most recent call last):
      File "<pyshell#10>", line 1, in <module>
        ioctl(fd, 5678, arg)
    OverflowError: signed integer is greater than maximum
    >>> # Kernel log: (no change - OverflowError is within python)
    >>> # Oh no! Can't pass a 64-bit int!
    >>> 
    

**Is there any way Python can pass my 64-bit argument to ioctl()?**


Answer: Whether or not this is possible using Python's `fcntl.ioctl()` will be system-
dependent. Tracing through the source code, the error message is coming from
the following test on [line 658 of
`getargs.c`](http://hg.python.org/cpython/file/bc6d28e726d8/Python/getargs.c#l658)...

    
    
    else if (ival > INT_MAX) {
        PyErr_SetString(PyExc_OverflowError,
        "signed integer is greater than maximum");
        RETURN_ERR_OCCURRED;
    }
    

...and on my system, `/usr/include/limits.h` tells me...

    
    
    #  define INT_MAX   2147483647
    

...which is (presumably) `(2 ** ((sizeof(int) * 8) - 1)) - 1`.

So, unless you're working on a system where `sizeof(int)` is at least `8`,
you'll have to call the underlying C function directly using the
[`ctypes`](http://docs.python.org/2/library/ctypes.html) module, but it's
platform-specific.

Assuming Linux, something like this ought to work...

    
    
    from ctypes import *
    
    libc = CDLL('libc.so.6')
    
    fd = os.open (DEV_NAME, os.O_RDWR, 0666)
    value = c_uint64(0xffff<<32)
    libc.ioctl(fd, 5678, value)
    

