V4L2 - Custom Subdev IOCTLs

From RidgeRun Developer Wiki

Overview

Custom IOCTLs can be added to a subdevice, this document show you how.

Create IOCTLs

In order to add a custom IOCTL on the subdevice you need the following:

  • Define your custom ioctls
  • Create you ioctl method handling, it be called when you call those defined IOCTLs
  • Assign ioctl on Subdev-Core-Ops callbacks.

Check the example below:

#define D43X_VIDIOC_SET_VAL _IOWR('V', BASE_VIDIOC_PRIVATE, unsigned int)
#define D43X_VIDIOC_SHIFT_VAL _IOWR('V', BASE_VIDIOC_PRIVATE + 1, unsigned int)

static long d43x_ioctl(struct v4l2_subdev *sd,
			unsigned int cmd, void *arg)
{
    struct i2c_client *client = v4l2_get_subdevdata(sd);
    int *val;

    switch (cmd) {
    case D43X_VIDIOC_SET_VAL:
        val = arg;
        dev_info(&client->dev, "%s: custom ioctl 1, value=%d\n", __func__, *val);
	break;
    case D43X_VIDIOC_SHIFT_VAL:
        val = arg;
        dev_info(&client->dev, "%s: custom ioctl 2, value=%d\n", __func__, *val);
        *val = *val << 1;
        arg = val;
	break;
    default:
	  dev_err(&client->dev, "Wrong IOCTL cmd\n");
	  break;
    }
    
    return 0;
}

static const struct v4l2_subdev_core_ops d43x_core_ops = {
    .ioctl = d43x_ioctl,
};

Test IOCTLs

IOCTLs can be called either from KERNEL or user spaces.

KERNEL

IOCTL can be trigger using v4l2_subdev_call(), check example below:

    unsigned int val = 2;

    ret = v4l2_subdev_call(sd, core, ioctl,
			   D43X_VIDIOC_SET_VALUE,
			   &val);
    if (ret)
        return ret;
    pr_info ("Value: %d\n", val)

    ret = v4l2_subdev_call(sd, core, ioctl,
			   D43X_VIDIOC_SHIFT_VALUE,
			   &val);
    if (ret)
        return ret;
    pr_info ("Value: %d\n", val)

Userspace

From the user application side, those IOCTLs can be triggered by calling ioctl() after opening the specific subdevice. Check example below:

#include <linux/ioctl.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/videodev2.h>

#define D43X_VIDIOC_SET_VALUE _IOWR('V', BASE_VIDIOC_PRIVATE, unsigned int)
#define D43X_VIDIOC_SHIFT_VALUE _IOWR('V', BASE_VIDIOC_PRIVATE + 1, unsigned int)

int main ( ) {
  int fd;
  int ret;
  unsigned int val = 2;

  printf("IOCTL Debug test\n");

  /* Open Device */
  fd = open("/dev/v4l-subdev0", O_RDWR);
  if (fd == -1) {
      printf("Error in opening file\n");
      exit(-1);
  }
  
  /* Test custom IOCTL set value */
  if (ioctl(fd, D43X_VIDIOC_SET_VALUE, (unsigned int *) &val) < 0) {
      perror("custom ioctl error");
      return 1;
  }
  printf("Value = %d\n", val);

  /* Test custom IOCTL shift value */
  if (ioctl(fd, D43X_VIDIOC_SHIFT_VALUE, (unsigned int *) &val) < 0) {
      perror("custom ioctl error");
      return 1;
  }
  printf("Value = %d\n", val);
  printf("---> End of test\n");

  close(fd);

  return 0;
}