V4L2 - Debugging through IOCTLs
Overview
For driver debugging purposes, there are IOCTLs available that can be used to access hardware registers directly. The corresponding driver has to support the IOCTLs in order enable the access from the user space.
Sensor Driver Support
The IOCTLs used to read and write hardware registers are:
- VIDIOC_DBG_G_REGISTER
- VIDIOC_DBG_S_REGISTER
In order to enable these IOCTLs on the subdevice you need assign Core-Ops callbacks for g_register and s_register, check the example below:
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int camera_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct camera *priv = to_camera(sd);
unsigned int val = 0;
int ret = 0;
dev_dbg(&client->dev, "%s: 0x%llx\n", __func__, reg->reg);
ret = regmap_read(priv->regmap, reg->reg & 0xffff, &val);
if (ret)
return ret;
reg->size = 2;
reg->val = (__u64)val;
return ret;
}
static int camera_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct camera *priv = to_camera(sd);
dev_dbg(&client->dev, "%s: 0x%llx, 0x%llx\n", __func__,
reg->reg, reg->val);
return regmap_write(priv->regmap, reg->reg & 0xffff,
reg->val & 0xffff);
}
#endif
static const struct v4l2_subdev_core_ops camera_core_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = camera_g_register,
.s_register = camera_s_register,
#endif
};
- Be aware that this support is only available for debugging purposes, that CONFIG_VIDEO_ADV_DEBUG flag has to be set.
You can enable this flag on Kernel configuration:
-> Device Drivers -> Multimedia support [*] Enable advanced debug functionality on V4L2 drivers
Testing IOCTLs
V4l2 Utils
In v4l-utils already provide an utility to call this IOCTLs directly, called v4l2-dbg.
This utility allow the user to read and write hardware registers easily, check below example:
- First check the available chips:
v4l2-dbg -d /dev/video0 -n bridge0: cal-000 (--) subdev0: camera 2-0010 (rw)
As you can see, only the subdevice support read/write.
- Read register 0x400c:
v4l2-dbg -d /dev/video0 -c subdev0 -g 0x400c ioctl: VIDIOC_DBG_G_REGISTER Register 0x0000400c = 1eh (30d 00011110b)
- Write register 0x400c
v4l2-dbg -d /dev/video0 -c subdev0 -s 0x400c 0xf Register 0x0000400c set to 0x1f
- Verify register writting
v4l2-dbg -d /dev/video0 -c subdev0 -g 0x400c Register 0x0000400c = fh (15d 00001111b)
- To check a range of registers:
v4l2-dbg -d /dev/video0 -c subdev0 --list-registers=min=0x4000,max=0x400c ioctl: VIDIOC_DBG_G_REGISTER 00 02 04 06 08 0A 0C 0E 00004000: 0031 0012 0500 0000 02d0 0000 000f
Custom Application
Check the following basic application to enable g_register and s_register callbacks:
#include <linux/ioctl.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/videodev2.h>
int main ( ) {
int fd;
int ret;
struct v4l2_dbg_register reg = {0};
printf("IOCTL Debug test\n");
/* Open Device */
fd = open("/dev/video0", O_RDWR);
if (fd == -1) {
printf("Error in opening file\n");
exit(-1);
}
/* Set Subdevice-0 */
reg.match.type = V4L2_CHIP_MATCH_SUBDEV;
reg.match.addr = 0;
/* Initialize register addr */
reg.reg = 0x400c;
reg.size = 2;
reg.val = 0;
/* Read register */
if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) {
perror("Read error");
return 1;
}
printf("Reg read: 0x%x = 0x%x\n", reg.reg, reg.val);
/* Write register */
reg.val = 0xf;
if (ioctl(fd, VIDIOC_DBG_S_REGISTER, ®) < 0) {
perror("Read error");
return 1;
}
printf("Reg write: 0x%x = 0x%x\n", reg.reg, reg.val);
/* Read register after change */
reg.val = 0;
if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) {
perror("Read error");
return 1;
}
printf("Reg read: 0x%x = 0x%x\n", reg.reg, reg.val);
close(fd);
return 0;
}
- Compile application:
${CC} ioctl_test.c -o ioctl_test ${CFLAGS}
- Test application:
./ioctl_test IOCTL Debug test Reg read: 0x400c = 0x1e Reg write: 0x400c = 0xf Reg read: 0x400c = 0xf