📄 videobook.tmpl
字号:
hardware_set_volume(current_volume); return 0; } </programlisting> <para> In our case there is very little that the user can set. The volume is basically the limit. Note that we could pretend to have a mute feature by rewriting this to </para> <programlisting> case VIDIOCSAUDIO: { struct video_audio v; if(copy_from_user(&v, arg, sizeof(v))) return -EFAULT; if(v.audio) return -EINVAL; current_volume = v/16384; if(v.flags&VIDEO_AUDIO_MUTE) hardware_set_volume(0); else hardware_set_volume(current_volume); current_muted = v.flags & VIDEO_AUDIO_MUTE; return 0; } </programlisting> <para> This with the corresponding changes to the VIDIOCGAUDIO code to report the state of the mute flag we save and to report the card has a mute function, will allow applications to use a mute facility with this card. It is questionable whether this is a good idea however. User applications can already fake this themselves and kernel space is precious. </para> <para> We now have a working radio ioctl handler. So we just wrap up the function </para> <programlisting> } return -ENOIOCTLCMD;} </programlisting> <para> and pass the Video4Linux layer back an error so that it knows we did not understand the request we got passed. </para> </sect1> <sect1 id="modradio"> <title>Module Wrapper</title> <para> Finally we add in the usual module wrapping and the driver is done. </para> <programlisting>#ifndef MODULEstatic int io = 0x300;#elsestatic int io = -1;MODULE_AUTHOR("Alan Cox");MODULE_DESCRIPTION("A driver for an imaginary radio card.");MODULE_PARM(io, "i");MODULE_PARM_DESC(io, "I/O address of the card.");EXPORT_NO_SYMBOLS;int init_module(void){ if(io==-1) { printk(KERN_ERR "You must set an I/O address with io=0x???\n"); return -EINVAL; } return myradio_init(NULL);}void cleanup_module(void){ video_unregister_device(&my_radio); release_region(io, MY_IO_SIZE);}#endif </programlisting> <para> In this example we set the IO base by default if the driver is compiled into the kernel where you cannot pass a parameter. For the module we require the user sets the parameter. We set io to a nonsense port (-1) so that we can tell if the user supplied an io parameter or not. </para> <para> We use MODULE_ defines to give an author for the card driver and a description. We also use them to declare that io is an integer and it is the address of the card. </para> <para> The clean-up routine unregisters the video_device we registered, and frees up the I/O space. Note that the unregister takes the actual video_device structure as its argument. Unlike the file operations structure which can be shared by all instances of a device a video_device structure as an actual instance of the device. If you are registering multiple radio devices you need to fill in one structure per device (most likely by setting up a template and copying it to each of the actual device structures). </para> </sect1> </chapter> <chapter> <title>Video Capture Devices</title> <sect1 id="introvid"> <title>Video Capture Device Types</title> <para> The video capture devices share the same interfaces as radio devices. In order to explain the video capture interface I will use the example of a camera that has no tuners or audio input. This keeps the example relatively clean. To get both combine the two driver examples. </para> <para> Video capture devices divide into four categories. A little technology backgrounder. Full motion video even at television resolution (which is actually fairly low) is pretty resource-intensive. You are continually passing megabytes of data every second from the capture card to the display. several alternative approaches have emerged because copying this through the processor and the user program is a particularly bad idea . </para> <para> The first is to add the television image onto the video output directly. This is also how some 3D cards work. These basic cards can generally drop the video into any chosen rectangle of the display. Cards like this, which include most mpeg1 cards that used the feature connector, aren't very friendly in a windowing environment. They don't understand windows or clipping. The video window is always on the top of the display. </para> <para> Chroma keying is a technique used by cards to get around this. It is an old television mixing trick where you mark all the areas you wish to replace with a single clear colour that isn't used in the image - TV people use an incredibly bright blue while computing people often use a particularly virulent purple. Bright blue occurs on the desktop. Anyone with virulent purple windows has another problem besides their TV overlay. </para> <para> The third approach is to copy the data from the capture card to the video card, but to do it directly across the PCI bus. This relieves the processor from doing the work but does require some smartness on the part of the video capture chip, as well as a suitable video card. Programming this kind of card and more so debugging it can be extremely tricky. There are some quite complicated interactions with the display and you may also have to cope with various chipset bugs that show up when PCI cards start talking to each other. </para> <para> To keep our example fairly simple we will assume a card that supports overlaying a flat rectangular image onto the frame buffer output, and which can also capture stuff into processor memory. </para> </sect1> <sect1 id="regvid"> <title>Registering Video Capture Devices</title> <para> This time we need to add more functions for our camera device. </para> <programlisting>static struct video_device my_camera{ "My Camera", VID_TYPE_OVERLAY|VID_TYPE_SCALES|\ VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY, VID_HARDWARE_MYCAMERA, camera_open. camera_close, camera_read, /* no read */ NULL, /* no write */ camera_poll, /* no poll */ camera_ioctl, NULL, /* no special init function */ NULL /* no private data */}; </programlisting> <para> We need a read() function which is used for capturing data from the card, and we need a poll function so that a driver can wait for the next frame to be captured. </para> <para> We use the extra video capability flags that did not apply to the radio interface. The video related flags are </para> <table frame=all><title>Capture Capabilities</title> <tgroup cols=2 align=left> <tbody> <row><entry>VID_TYPE_CAPTURE</><entry>We support image capture</></row><row><entry>VID_TYPE_TELETEXT</><entry>A teletext capture device (vbi{n])</></row><row><entry>VID_TYPE_OVERLAY</><entry>The image can be directly overlaid onto the frame buffer</></row><row><entry>VID_TYPE_CHROMAKEY</><entry>Chromakey can be used to select which parts of the image to display</></row><row><entry>VID_TYPE_CLIPPING</><entry>It is possible to give the board a list of rectangles to draw around. </></row><row><entry>VID_TYPE_FRAMERAM</><entry>The video capture goes into the video memory and actually changes it. Applications need to know this so they can clean up after the card</></row><row><entry>VID_TYPE_SCALES</><entry>The image can be scaled to various sizes, rather than being a single fixed size.</></row><row><entry>VID_TYPE_MONOCHROME</><entry>The capture will be monochrome. This isn't a complete answer to the question since a mono camera on a colour capture card will still produce mono output.</></row><row><entry>VID_TYPE_SUBCAPTURE</><entry>The card allows only part of its field of view to be captured. This enables applications to avoid copying all of a large image into memory when only some section is relevant.</> </row> </tbody> </tgroup> </table> <para> We set VID_TYPE_CAPTURE so that we are seen as a capture card, VID_TYPE_CHROMAKEY so the application knows it is time to draw in virulent purple, and VID_TYPE_SCALES because we can be resized. </para> <para> Our setup is fairly similar. This time we also want an interrupt line for the 'frame captured' signal. Not all cards have this so some of them cannot handle poll(). </para> <programlisting>static int io = 0x320;static int irq = 11;int __init mycamera_init(struct video_init *v){ if(check_region(io, MY_IO_SIZE)) { printk(KERN_ERR "mycamera: port 0x%03X is in use.\n", io); return -EBUSY; } if(video_device_register(&my_camera, VFL_TYPE_GRABBER)==-1) return -EINVAL; request_region(io, MY_IO_SIZE, "mycamera"); return 0;} </programlisting> <para> This is little changed from the needs of the radio card. We specify VFL_TYPE_GRABBER this time as we want to be allocated a /dev/video name. </para> </sect1> <sect1 id="opvid"> <title>Opening And Closing The Capture Device</title> <programlisting>static int users = 0;static int camera_open(stuct video_device *dev, int flags){ if(users) return -EBUSY; if(request_irq(irq, camera_irq, 0, "camera", dev)<0) return -EBUSY; users++; MOD_INC_USE_COUNT; return 0;}static int camera_close(struct video_device *dev){ users--; free_irq(irq, dev); MOD_DEC_USE_COUNT;} </programlisting> <para> The open and close routines are also quite similar. The only real change is that we now request an interrupt for the camera device interrupt line. If we cannot get the interrupt we report EBUSY to the application and give up. </para> </sect1> <sect1 id="irqvid"> <title>Interrupt Handling</title> <para> Our example handler is for an ISA bus device. If it was PCI you would be able to share the interrupt and would have set SA_SHIRQ to indicate a shared IRQ. We pass the device pointer as the interrupt routine argument. We don't need to since we only support one card but doing this will make it easier to upgrade the driver for multiple devices in the future. </para> <para> Our interrupt routine needs to do little if we assume the card can simply queue one frame to be read after it captures it. </para> <programlisting>static struct wait_queue *capture_wait;static int capture_ready = 0;static void camera_irq(int irq, void *dev_id, struct pt_regs *regs){ capture_ready=1; wake_up_interruptible(&capture_wait);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -