V4L2 - Custom Subdev IOCTLs
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; }