Search code examples
clinux-device-driverdevice-driver

Modify file operations in registered character device in linux


For some hardware hacking (can provide details, if requested), I need to make a small change in a kernel module that is built-in (not loadable) in the distribution I am using. I want to avoid to use a custom kernel or even use custom configs for portability reasons with respect to future kernel upgrades.

I would rather use an additional kernel module. However, the module is loaded by another module (i.e. its init_function is called by the other module).

The thing I need to change is in the implementation of the .write function of the file_operations.

A strategy could be to unregister the chardev and reregister it with a modified .write function once a custom module is loaded into the kernel. Is this a legit strategy? Are there any code examples for this around?

I would like to change the following line in drivers/media/rc/lirc_dev.c:

#define LIRCBUF_SIZE    256

to

#define LIRCBUF_SIZE    1024

Basically I need a longer buffer and avoid the EINVAL return around line 330

if (count > LIRCBUF_SIZE || count % 2 == 0) {
  ret = -EINVAL;
  goto out_unlock;
}

The lirc_dev is registering character devices /dev/lirc0, /dev/lirc1 etc. and the lines above come from the implementation of .write of the file_operations struct.
The registration is triggered by rc_core modules with lirc_dev_init, it seems, and I want to avoid using a custom implementation of the rc_core, as well.

I already did a custom modification in the lirc user space tools, which also have this buffer limitation to 256 units(=int), but it breaks at the point when the lirc driver is making a "write" to the '/dev/lirc0' device with an input output error.

The strategy of writing 256 int chunks essentially would work, but it is delicate and I do not think I can get it to be functional with the hardware, because of timing issues. (when does the "write" to the kernel space actually trigger some hardware actions...)

It all seems too complicated if all I want to do is increase the buffer...


Solution

  • "Is this a legit strategy?" - no. It is an interesting one, but I am pretty sure it isn't legit. If I understand your desire is to intercept /dev/lirc?, that might be easier to do by re-writing the device nodes for /dev/lirc?, which I think is still a thing.

    Traditional UNIX device nodes contain two numbers, a major and minor. When you open("/dev/lirc2"...), the kernel retrieves the major number, and retrieves the device instance from a mapping table. It then invokes methods in this instance to operate on the device (open, close, read, write, ioctl,..).

    The utility mknod can be used to create device entries, so I believe that:

    mv /dev/lirc2 /dev/olicr2 && mknod c /dev/lirc2 ${major} 2
    

    would permit you to redirect /dev/lirc2 to a new device.

    I glossed over about 10 chapters of details; including how to get numbers for you ${major} and I am not sure how the myriad /sys filesystem will respond to this sort of abuse, but that leads to another question:

    Why do you need more buffer space in lirc? At first blush it seems there may be a simpler way to achieve your goal with a lot less plumbing...