Some of the specific information in this file may have become partially obsolete, due to evolution of the V4L (video for Linux) API. However, the principles remain valid.
Your goal in producing a video device driver should be for it to support existing and future applications written to the API. You should test how well you have achieved this using existing open source applications that are known to work with other Linux video device drivers. One example of such an application is xawtv.
If you want to be able to test your frame grabber driver using an existing application, you need to support (even if trivially) all of the system calls that this application makes to the device. For a starting point, I ran strace for an execution of the program xawtv using the bttv driver. The following group of calls seemed to correspond to the the opening and initialization of the video device.
open("/dev/video0", O_RDWR|O_LARGEFILE) = 4
ioctl(4, 0x80585600, 0x80bbf88) = -1 EINVAL (Invalid argument)
close(4) = 0
open("/dev/video0", O_RDWR|O_LARGEFILE) = 4
ioctl(4, EXT2_IOC_GETVERSION, 0x80b64d4) = 0
fcntl64(4, F_SETFD, FD_CLOEXEC) = 0
rt_sigaction(SIGALRM, {0x4002f6d0, [], SA_RESTORER, 0x42028558}, {SIG_DFL}, 8) = 0
ioctl(4, EXT2_IOC_SETVERSION, 0x80b6718) = 0
ioctl(4, EXT2_IOC_SETVERSION, 0x80b6748) = 0
ioctl(4, EXT2_IOC_SETVERSION, 0x80b6778) = 0
ioctl(4, VIDIOCGAUDIO, 0x80b6548) = 0
ioctl(4, VIDIOCGTUNER, 0x80b6514) = 0
ioctl(4, 0x800476c6, 0xbffff9cc) = 2315
ioctl(4, VIDIOCGFBUF, 0x80b6590) = 0
ioctl(4, VIDIOCGPICT, 0x80b6570) = 0
ioctl(4, VIDIOCGMBUF, 0x80b6628) = 0
mmap2(NULL, 17039360, PROT_READ|PROT_WRITE, MAP_SHARED, 4, 0) = 0x404ec000
The details shown above will be different for different applications, and are likely to also be different for different versions of xawtv and different versions of the Linux kernel. You should apply strace for yourself, using the application you have, and the kernel version you have. You will probably find that the program terminates early, because your driver fails to respond to some calls. If so, you will need to need to repeat this experiment after you have extended the driver. You will probably find more gaps, and need to repeat the process, but you should finally manage to get through a complete execution of the application program.
In the above example, the last operation apparently maps a range of device memory to the virtual memory of the xawtv process. From this point on, much of the I/O is done through the memory mapping.
The following ioctl calls are used later in the execution.
ioctl(4, VIDIOCCAPTURE, 0x404e1c44) = 0 ioctl(4, VIDIOCGFREQ, 0xbffffa94) = 0 ioctl(4, VIDIOCMCAPTURE, 0x80b67d8) = 0 ioctl(4, VIDIOCSAUDIO, 0x80b6548) = 0 ioctl(4, VIDIOCSCHAN, 0x80b6718) = 0 ioctl(4, VIDIOCSFREQ, 0xbffff7a4) = 0 ioctl(4, VIDIOCSPICT, 0x80b6570) = 0 ioctl(4, VIDIOCSWIN, 0x80b65a4) = 0 ioctl(4, VIDIOCSYNC, 0x80b67d8) = 0
Note: That seems to be most of the calls, for the version of xawtv and the driver I used to test it. It was a long trace, with many repeated calls, so I may have missed one or two in my editing.
With a frame grabber that does not have a tuner and does not support audio, you should generally be able to stub out the operations whose names include CHAN, AUDIO, and FREQ. I don't know about the others.
There is a substantial infrastructure in IOCTL implementation
provided by the Linux generic video support code. Take a look at video_ioctl2
for a start. You will see that it does call-backs to the specific
device driver for the details. One example is
For comparison, the following is the DOS/Windows Pixelsmart driver code for IOCTLs used in that driver. These do not correspond to V4L2 IOCTLs exactly, but they give some idea of what functionality the board can support.
// Process the IRPs sent to this device
NTSTATUS HRTDispatchIoctl(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp)
{
PIO_STACK_LOCATION pIrpStack;
NTSTATUS status = STATUS_SUCCESS;
ULONG ctlCode;
// Get a pointer to the current location in the Irp.
// This is where the function codes and parameters are located.
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
ctlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
switch(ctlCode) {
case IOCTL_HRT_GETPCIDATA:
status = getPciData(pIrp, pDeviceObject);
break;
case IOCTL_HRT_GETDIGIDATA:
status = getDigiData(pIrp, pDeviceObject);
break;
case IOCTL_HRT_PUTDIGIDATA:
status = putDigiData(pIrp, pDeviceObject);
break;
case IOCTL_HRT_INITCRTC:
status = initCRTC(pIrp, pDeviceObject);
break;
case IOCTL_HRT_INITYUVRGB:
status = initYUVRGBlut(pIrp, pDeviceObject);
break;
case IOCTL_HRT_SETLUT:
status = setLUT(pIrp, pDeviceObject);
break;
case IOCTL_HRT_CAMERAPORT:
status = cameraPort(pIrp, pDeviceObject);
break;
case IOCTL_HRT_HUE:
status = setBrtConSatHue(pIrp, pDeviceObject, HUE_REG);
break;
case IOCTL_HRT_SATURATION:
status = setBrtConSatHue(pIrp, pDeviceObject, SAT_REG);
break;
case IOCTL_HRT_CONTRAST:
status = setBrtConSatHue(pIrp, pDeviceObject, CON_REG);
break;
case IOCTL_HRT_BRIGHTNESS:
status = setBrtConSatHue(pIrp, pDeviceObject, BRT_REG);
break;
case IOCTL_HRT_SETCRTCREG:
status = setCRTCReg(pIrp, pDeviceObject);
break;
case IOCTL_HRT_GETREG:
status = getReg(pIrp, pDeviceObject);
break;
case IOCTL_HRT_SETREG:
status = setReg(pIrp, pDeviceObject);
break;
case IOCTL_HRT_DETECT:
status = detect(pIrp, pDeviceObject);
break;
case IOCTL_HRT_INITIALIZEI2C:
status = initializeI2C(pIrp, pDeviceObject);
break;
case IOCTL_HRT_ENDFRAME:
status = endFrame(pIrp, pDeviceObject);
break;
case IOCTL_HRT_GETBASEADDR:
case IOCTL_HRT_GETERROR:
case IOCTL_HRT_GETVERSION:
status = getLDIelement(pIrp, pDeviceObject, ctlCode);
break;
case IOCTL_HRT_GETBASEADDREX:
status = getBaseAddrEx(pIrp, pDeviceObject);
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return(status);
}