dcsimg

More on Writing Device Drivers for Video Capture Devices

Last month I began to develop a very simple camera driver as an illustration of Linux's video-capture interface. What we need to do now is to provide the functions to control the use of the device and to query its facilities. As with the radio driver, the major control interface is via the ioctl() function (Listing One). Video-capture devices support the same tuner calls as radio devices, and also support additional calls to control how the video functions are handled. In this simple example the card has no tuners, to avoid making the code complex.

Last month I began to develop a very simple camera driver as an illustration of Linux’s video-capture interface. What we need to do now is to provide the functions to control the use of the device and to query its facilities. As with the radio driver, the major control interface is via the ioctl() function (Listing One). Video-capture devices support the same tuner calls as radio devices, and also support additional calls to control how the video functions are handled. In this simple example the card has no tuners, to avoid making the code complex.




Listing One: The Control Interface


  static int camera_ioctl(struct video_device *dev,
unsigned int cmd, void *arg)
{

switch(cmd)
{
case VIDIOCGCAP:
{
struct video_capability v;
v.type = VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY|\
VID_TYPE_SCALES|VID_TYPE_OVERLAY;
v.channels = 1;
v.audios = 0;
v.maxwidth = 640;
v.minwidth = 16;
v.maxheight = 480;
v.minheight = 16;
strcpy(v.name, “My Camera”);
if(copy_to_user(arg, &v, sizeof(v)))
return -EFAULT;
return 0;
}

The first ioctl we must support (required by all video-capture and radio devices) is VIDIOCGCAP. This behaves exactly the same as with a radio device. This time, however, we report the extra capabilities we outlined earlier when defining our video_dev structure.

We now set the video flags saying that we support overlay, capture, scaling, and chromakey. We also report size limits — our smallest image is 16×16 pixels, our largest is 640×480.

To keep things simple we report no audio and no tuning capabilities at all.


case VIDIOCGCHAN:
{
struct video_channel v;
if(copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if(v.channel != 0)
return -EINVAL;
v.flags = 0;
v.tuners = 0;
v.type = VIDEO_TYPE_CAMERA;
v.norm = VIDEO_MODE_AUTO;
strcpy(v.name, “Camera Input”);break;
if(copy_to_user(&v, arg, sizeof(v)))
return -EFAULT;
return 0;
}

This code follows the standard way an ioctl handler looks in Linux. We copy the data into a kernel-space variable and check that the request is valid (that the input channel is 0). Finally, we copy the camera info back to the user.

The VIDIOCGCHANioctl allows a user to ask about video channels (i.e., inputs to the video card). Our example card has a single camera input. The fields in the structure are:

channel: The channel number we are selecting

name:The name for this channel. This is intended to describe the port to the user. Appropriate names are therefore things like “Camera” or “SCART input.”

flags:

* VIDEO_VC_TUNER: Channel has a tuner.

* VIDEO_VC_AUDIO: Channel has audio.

type:

* VIDEO_TYPE_TV: Television input

* VIDEO_TYPE_CAMERA: Fixed camera input.

norm: The current TV encoding if relevant for this channel:

* VIDEO_MODE_PAL: PAL-encoded television

* VIDEO_MODE_NTSC: NTSC (US) encoded television

* VIDEO_MODE_SECAM: SECAM (French) television

* VIDEO_MODE_AUTO: Automatic switching, or format does not matter

The corresponding VIDIOCSCHANioctl allows a user to change channel and to change the norm — for example, to switch between a PAL and an NTSC format camera.


case VIDIOCSCHAN:
{
struct video_channel v;
if(copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if(v.channel != 0)
return -EINVAL;
if(v.norm != VIDEO_MODE_AUTO)
return -EINVAL;
return 0;
}

Our implementation of this call is remarkably easy. Because we are assuming fixed-format hardware, we need only check that the user has not tried to change anything.

The user also needs to be able to configure and adjust the picture they are seeing. This is much like adjusting a television set. A user application also needs to know the palette being used so that it knows how to display the image that has been captured. The VIDIOCGPICT and VIDIOCGPICTioctl calls provide this capability.



case VIDIOCGPICT
{
struct video_picture v;
v.brightness = hardware_brightness();
v.hue = hardware_hue();
v.colour = hardware_saturation();
v.contrast = hardware_brightness();
v.whiteness = 32768;
/* Not settable */
v.depth = 24;
/* 24bit */
v.palette = VIDEO_PALETTE_RGB24;
if(copy_to_user(&v, arg,sizeof(v)))
return -EFAULT;
return 0;
}

The brightness, hue, color, and contrast provide the picture controls that are akin to a conventional television. Whiteness provides additional control for grayscale images. All of these values are scaled between 0 and 65535 and have 32768 as the midpoint setting. The scaling means that applications do not have to worry about the capability range of the hardware but can let it make a best-effort attempt.

Our depth is 24 bits, so we will be returning RGB24 format. Therefore, each pixel contains one byte of red, then one of green, then one of blue. The other common formats are GREY (linear grayscale), RGB565 (top five bits hold 32 red levels, next six bits hold green, low five bits hold blue), RGB555 (top bit clear, red, green, and blue levels occupy five bits).

Additional modes are supported for YUV capture formats, that are commonly used for TV and video-conferencing applications. The VIDIOCSPICT ioctl allows a user to set some of the picture parameters. Exactly which of the parameters the user will be able to set depends heavily on the card itelf. It is possible to support many modes and effects in software. In general, doing this in the kernel is a bad idea. Video capture is a performance-sensitive application, and the programs can often do better if they aren’t being “helped” by an overkeen driver writer. Thus for our device we will report RGB24 only and refuse to allow a change.


case VIDIOCSPICT:
{
struct video_picture v;
if(copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if(v.depth!=24 || v.palette != VIDEO_
PALETTE_RGB24)
return -EINVAL;
set_hardware_brightness(v.brightness);
set_hardware_hue(v.hue);
set_hardware_saturation(v.colour);
set_hardware_brightness(v.contrast);
return 0;
}

We check the user has not tried to change the palette or the depth. We do not want to carry out some of the changes and then return an error. This may confuse the application, which will be assuming no change occurred.

In much the same way as you need to be able to set the picture controls to get the right capture images, many cards need to know what they are displaying onto when generating overlay output.

In some cases, getting this wrong makes a nasty mess or may crash the computer. For that reason the VIDIOCSBUF ioctl used to set up the frame buffer information may well be usable only by root.

We will assume our card is one of the old ISA devices with a feature connector and only supports a couple of standard video modes. This is common for older cards, although the PCI devices are usually smarter than this.


static struct video_buffer capture_fb;

case VIDIOCGFBUF:
{
if(copy_to_user(arg, &capture_fb,
sizeof(capture_fb)))
return -EFAULT;
return 0;
}

We keep the frame buffer information in the format the ioctl uses. This makes it nice and easy to work with in the ioctl calls.


case VIDIOCSFBUF:
{
struct video_buffer v;

if(!capable(CAP_SYS_ADMIN))
return -EPERM;

if(copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if(v.width!=320 && v.width!=640)
return -EINVAL;
if(v.height!=200 && v.height!=240 &&
v.height!=400 && v.height!=480)
return -EINVAL;
memcpy(&capture_fb, &v, sizeof(v));
hardware_set_fb(&v);
return 0;
}

The capable() function checks whether a user has the capability to modify the buffer settings. The Linux operating system has a set of about 30 capabilities indicating privileged access to services. The default setup gives the superuser (uid 0) all of them, and regular users none.

We check that the user has the SYS_ADMIN capability, which means that they are allowed to operate as the machine administrator. We don’t want anyone but the administrator making a mess of the display.

Next we check for standard PC video modes (320 or 640 wide with either EGA or VGA depths). If the mode is not a standard video mode, then we reject it as not supported by our card. But if the mode is acceptable we save it so that VIDIOCFBUF will give the right answer next time it is called. The hardware_set_fb() function is a card-specific function to program the card for the desired mode.

Before the driver can display an overlay window it needs to know where the window should be placed and how large it should be. If the card supports clipping, it needs to know which rectangles to omit from the display. The video_ window structure is used to describe the way the image should be displayed, using the following parameters:

width: The width in pixels of the desired image. The card may use a smaller size if this size is not available.

height: The height of the image. The card may use a smaller size if this size is not available.

x:The X position of the top left of the window. This is in pixels relative to the left hand edge of the picture. Not all cards can display images aligned on any pixel boundary. If the position is unsuitable the card adjusts the image right and reduces the width.

y: The Y position of the top left of the window. This is counted in pixels relative to the top edge of the picture. As with the width, if the card cannot display starting on this line it will adjust the values.

chromakey: The color (expressed in RGB32 format) for the chromakey color if chromakeying is being used.

clips: An array of rectangles that must not be drawn over.

clipcount:The number of clips in this array.

Each clip is a struct video_clip with fields that coordinate relative to the display (x, y) and specify width and height in pixels (width, height). There is also a spare field for the application (next). The driver is required to ensure it always draws in the area requested or a smaller area, and that it never draws in any of the areas that are clipped.


case VIDIOCGWIN:
{
if(copy_to_user(arg,&capture_win,
sizeof(capture_win)))
return -EFAULT;
return 0;
}

case VIDIOCSWIN:
{
struct video_window v;
if(copy_from_user(&v,arg, sizeof(v)))
return -EFAULT;
if(v.width > 640 || v.height > 480)
return -EINVAL;
if(v.width < 16 || v.height < 16)
return -EINVAL;
hardware_set_key(v.chromakey);
hardware_set_window(v);
memcpy (&capture_win, &v, sizeof(v));
capture_w = v.width;
capture_h = v.height;
return 0;
}

Our example card uses chromakey, so we don’t have to address most of the clipping options. We add a video_ window structure to our global variables to remember our parameters, as we did with the frame buffer.

Because we are using chromakey, our setup is fairly simple. Mostly, we have to check that the values are sane and load them into the capture card.

With all the setup done we can now turn on the actual capture/overlay. This is done with the VIDIOCCAPTURE ioctl. This takes a single integer argument where 0 is on and 1 is off.


case VIDIOCCAPTURE:
{
int v;
if(get_user(v, (int*)arg))
return -EFAULT;
if(v==0)
hardware_capture_off();
else
{
if(capture_fb.width == 0 ||
capture_w == 0)
return -EINVAL;
hardware_capture_on();
}
return 0;
}

We grab the flag from user space and either enable or disable according to its value. There is one small corner case we have to consider here. Suppose that the capture was requested before the video window or the frame buffer had been set up. In those cases, there will be unconfigured fields in our card data, as well as unconfigured hardware settings. We check for this case and return an error if the frame buffer or the capture window width is zero.


default:
return -ENOIOCTLCMD;
}
}

We don’t need to support any other ioctls, so if we get this far, it is time to tell the video layer that we don’t know what the user is talking about.

The Video4Linux layer supports additional features, including a high-performance mmap()-based capture mode, as well as capturing partial images. You should now have enough example code to implement most simple Video4Linux devices for radio and TV cards.





Alan Cox is a well-known Linux hacker currently working on the development of drivers, Linux/SGI porting, and modular sound. He can be reached at alan@lxorguk.ukuu.org.uk

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62