Video Ioctl Handling

As with the radio driver the major control interface is via the ioctl() function. Video capture devices support the same tuner calls as a radio device 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.



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 and which all video capture and radio devices are required to support is VIDIOCGCAP. This behaves exactly the same as with a radio device. This time, however, we report the extra capabilities we outlined earlier on 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 16x16 pixels, our largest is 640x480.

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 follows what is very much the standard way an ioctl handler looks in Linux. We copy the data into a kernel space variable and we check that the request is valid (in this case that the input is 0). Finally we copy the camera info back to the user.

The VIDIOCGCHAN ioctl allows a user to ask about video channels (that is inputs to the video card). Our example card has a single camera input. The fields in the structure are

Table 3-2. struct video_channel fields

channelThe channel number we are selecting
nameThe name for this channel. This is intended to describe the port to the user. Appropriate names are therefore things like "Camera" "SCART input"
flagsChannel properties
typeInput type
normThe current television encoding being used if relevant for this channel.

Table 3-3. struct video_channel flags

VIDEO_VC_TUNERChannel has a tuner.
VIDEO_VC_AUDIOChannel has audio.

Table 3-4. struct video_channel types

VIDEO_TYPE_TVTelevision input.
VIDEO_TYPE_CAMERAFixed camera input.
0Type is unknown.

Table 3-5. struct video_channel norms

VIDEO_MODE_PALPAL encoded Television
VIDEO_MODE_NTSCNTSC (US) encoded Television
VIDEO_MODE_SECAMSECAM (French) Televison
VIDEO_MODE_AUTOAutomatic switching, or format does not matter

The corresponding VIDIOCSCHAN ioctl allows a user to change channel and to request the norm is changed - for exaple to switch between a PAL or 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;
                }


  

The implementation of this call in our driver 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 VIDIOCSPICT ioctl calls provide this information.


                case VIDIOCGPICT
                {
                        struct video_picture v;
                        v.brightness = hardware_brightness();
                        v.hue = hardware_hue();
                        v.colour = hardware_saturation();
                        v.contrast = hardware_brightness();
                        /* Not settable */
                        v.whiteness = 32768;
                        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 greyscale images. All of these values are scaled between 0-65535 and have 32768 as the mid point 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, as this is in bits. We will be returing RGB24 format. This has one byte of red, then one of green, then one of blue. This then repeats for every other pixel in the image. The other common formats the interface defines are

Table 3-6. Framebuffer Encodings

GREYLinear greyscale. This is for simple cameras and the like
RGB565The top 5 bits hold 32 red levels, the next six bits hold green and the low 5 bits hold blue.
RGB555The top bit is clear. The red green and blue levels each occupy five bits.

Additional modes are support for YUV capture formats. These are common for TV and video conferencing applications.

The VIDIOCSPICT ioctl allows a user to set some of the picture parameters. Exactly which ones are supported depends heavily on the card itself. 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 even 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 only be usable by root.

We will assume our card is one of the old ISA devices with feature connector and only supports a couple of standard video modes. Very common for older cards although the PCI devices are way 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 a user has the required capability. The Linux operating system has a set of about 30 capabilities indicating privileged access to services. The default set up gives the superuser (uid 0) all of them and nobody else has any.

We check that the user has the SYS_ADMIN capability, that is 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 we reject it as not supported by our card. 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 some undescribed 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 also 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.

Table 3-7. struct video_window fields

widthThe width in pixels of the desired image. The card may use a smaller size if this size is not available
heightThe 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.
chromakeyThe colour (expressed in RGB32 format) for the chromakey colour if chroma keying is being used.
clipsAn array of rectangles that must not be drawn over.
clipcountThe number of clips in this array.

Each clip is a struct video_clip which has the following fields

Table 3-8. video_clip fields

x, yCo-ordinates relative to the display
width, heightWidth and height in pixels
nextA spare field for the application to use

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. This may well mean it has to leave alone. small areas the application wished to be drawn.

Our example card uses chromakey so does not have to address most of the clipping. We will add a video_window structure to our global variables to remember our parameters, as we did with the frame buffer.


                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;
                }


  

Because we are using Chromakey our setup is fairly simple. Mostly we have to check 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 now what the user is talking about.