Development of USB camera driver in Linux

Source: Internet
Author: User

In the first half of this year, I transplanted a Linux driver to a common vimicro camera on the market to an Embedded Development Board. The development environment is:

OS: linux2.6.9

Compile: Cross 3.3.2

CPU: Intel PXA270

Camera type: vimicro zc301p + hv7131r

The driver source code used is based on the free software spca5xx. Some minor changes were made based on the source code. Record it.

USB driver architecture in Linux
The complete USB driver in Linux must include three modules: USB core, USB master controller driver, and USB device driver. The relationship between the three is as follows:

 

Where:

Linux core encapsulates APIs that support USB master controllers and USB device drivers. It abstracts hardware and masks hardware details through a series of data structures, macros, and functions, in this way, the Linux system uses an interface function like a common file handler to access USB devices, thus implementing hardware independence. "The device is a file ".

The usb hc (host controller) is fully responsible for the division of information transmission between the host and the USB, and provides a bridge between the device driver and USB core data transmission. The Real-Time Streaming Data Transmission on USB is usually implemented through the data structure of urb (USB request block). During each data transmission, the device driver establishes urb and submits it to usb hc, usb hc is responsible for completing the urb interaction with the device.

USB driver structure
Struct usb_driver spca5xx_driver

Struct video_device spca50x_template

Struct file_operations spca5xx_fops

Module initialization and uninstallation
Initialize the spca5xx_init () module; uninstall the spca5xx_disconnect () module; search, match, and configure the spca5xx_probe () physical device, and register the video device with the kernel.

System Call Interface
Spca5xx_open () is used to open and initialize the device and initialize the decoder module. spca5xx_close () is used to close the device;

Spca5xx_read () completes data reading. The main task is to transmit data from the kernel space to the process user space;

Spca5xx_mmap () is used to map the device memory to the address space of the user process;

Spca5xx_ioctl () is used to obtain file information.

Data Transmission
Spca50x_reg_write

Camera data transmission process
Information Transmission types on the USB bus can be divided into four types: control, interrupt, timing, and block. The control type is mainly used for device configuration and control; the interrupt type is mainly used for the host's periodic query of USB devices; real-time use and real-time audio and video signal transmission; block type is used for applications that require reliable transmission without strong time requirements. For cameras, real-time transmission is usually used. The USB device provides several ports for data transmission. A logical communication channel is established between the host and the port. During camera device initialization, the host communicates with port 0, and the driver detects the camera model and all port information, establish a communication pipeline between the host and the detected real-time input port, and return the image data captured by the camera in real time.

The USB Host Controller divides the bus time per second into 1024 fixed-size frames. Each frame occupies 1 ms of time bandwidth and increments accordingly, corresponding to an interactive queue at the same time. The host is responsible for allocating the image data traffic to each frame when the USB Bus is passed back. The USB Host Controller scans all frames every second, read the data packets carried by each frame in sequence to the kernel buffer. Through memory ing, the application can directly read the content of the kernel buffer in the user space and store the content in the Linux video buffer (framebuffer), so that image data is displayed on the display screen.

My experimental camera is vimicro zc301p and sensor is hv7131r. The driver transmits urb twice every second, and each urb carries 16 1023-byte packets, in this way, the size of the image data obtained per second is 32 kbyte. For the image format of rgb565, 320x240, and 16-bit color depth, the test is performed on the embedded development board of PXA270 processor. The frame speed reaches 17 frames per second, fully meets the needs of Embedded Network real-time video conferencing.

 

Several Ways to Improve the camera Quality
Dual urb
Dual-Frame Buffer
V4l protocol Improvement

 

 

Note: I recently re-analyzed the spca5xx source code. I got a rare article on the Internet. I have analyzed spca5xx in detail. I have some experiences after reading it. I will post it here to express my gratitude.

The implementation of spac5xx is written according to the driver framework of the standard USB video device (the specific driver framework can be referred to/usr/src/Linux/Drivers/USB video. c file), the entire source program consists of four main parts: device module initialization module and uninstall module, upper-layer software interface module, data transmission module. The specific module analysis is as follows:

Initialize the device module:

The driver uses an explicit module initialization and elimination function, that is, it calls module_init to initialize a module, and call the moduel-exit function when uninstalling (these two functions are supported in the 2.3.13 kernel ). The specific implementation is as follows:

1. module initialization:

Module_init (usb_spca5xx_init );

Static int _ init

Usb_spca5xx_init (void)

{

# Ifdef config_proc_fs

Proc_spca50x_create (); // create a proc Device File

# Endif/* config_proc_fs */

If (usb_register (& spca5xx_driver) <0) // register the USB driver

Return-1;

Info ("spca5xx driver % s registered", version );

Return 0;

}

2. Uninstall the module:

Module_exit (usb_spca5xx_exit );

Tatic void _ exit

Usb_spca5xx_exit (void)

{

Usb_deregister (& spca5xx_driver); // disconnect the USB driver

Info ("Driver spca5xx deregistered ");

# Ifdef config_proc_fs

Proc_spca50x_destroy (); // undo the proc Device File

# Endif/* config_proc_fs */

}

Key Data Structure: // USB drive structure, plug-and-play function implementation

Static struct usb_driver spca5xx_driver = {

"Spca5xx ",

Spca5xx_probe, // registers the device's self-Detection Function

Spca5xx_disconnect, // register the device to disconnect itself

{Null, null}

};

Use two functions to call spca5xx_probe and spca5xx_disconnect to support the plug-and-play functions of USB devices:

The specific implementation of spca5xx_probe is as follows:

Static void *

Spca5xx_probe (struct usb_device * Dev, unsigned int ifnum, const struct usb_device_id * ID)

{

Struct usb_interface_descriptor * interface; // USB device interface Descriptor

Struct usb_spca50x * spca50x; // the data structure of the physical device.

Int err_probe;

Int I;

If (Dev-> descriptor. bnumconfigurations! = 1) // check whether the device is configurable.

Goto nodevice;

If (ifnum> 0)

Goto nodevice;

Interface = & Dev-> actconfig-> interface [ifnum]. altsetting [0];

Mod_inc_use_count;

Interface = & INTF-> altsetting [0]. DESC;

If (interface-> binterfacenumber> 0)

Goto nodevice;

If (spca50x = kmalloc (sizeof (struct usb_spca50x), gfp_kernel) = NULL)

// Allocate physical address space

{

Err ("couldn't kmalloc spca50x struct ");

Goto error;

}

Memset (spca50x, 0, sizeof (struct usb_spca50x ));

Spca50x-> Dev = dev;

Spca50x-> iface = interface-> binterfacenumber;

If (err_probe = spcadetectcamera (spca50x) <0)

// Find the specific physical device, match the vendor ID and device ID (in the subroutine)

{

Err ("devices not found !! ");

Goto error;

}

Pdebug (0, "camera type % s", plist [spca50x-> cameratype]. Name

For (I = 0; I <spca50x_numframes; I ++)

Init_waitqueue_head (& spca50x-> frame [I]. WQ); // initialize the frame wait queue

Init_waitqueue_head (& spca50x-> WQ); // initialize the driver wait queue

If (! Spca50x_configure (spca50x ))

// Physical device configuration (mainly for sensor detection and graphic parameter configuration). The main idea is to write values to the control register and read back the returned values to determine the specific sensor model.

{

Spca50x-> User = 0;

Init_mutex (& spca50x-> lock); // semaphore Initialization

Init_mutex (& spca50x-> buf_lock );

Spca50x-> v4l_lock = spin_lock_unlocked;

Spca50x-> buf_state = buf_not_allocated;

}

Else

{

Err ("failed to configure camera ");

Goto error;

}

/* Init video stuff */

Spca50x-> vdev = video_device_alloc (); // The device controls block memory allocation.

If (! Spca50x-> vdev)

Goto error;

Memcpy (spca50x-> vdev, & spca50x_template, sizeof (spca50x_template ));

// Mount the system call. The system call implemented by the driver is mounted to the kernel.

Video_set_drvdata (spca50x-> vdev, spca50x );

If (video_register_device (spca50x-> vdev, vfl_type_grabber, video_nr) <0)

{// Video device registration

Err ("video_register_device failed ");

Goto error;

}

Spca50x-> present = 1;

If (spca50x-> force_rgb)

Info ("data format set to RGB ");

Spca50x-> task. Sync = 0;

Spca50x-> task. Routine = auto_bh;

Spca50x-> task. Data = spca50x;

Spca50x-> bh_requested = 0;

Mod_dec_use_count; // increase the number of modules used.

Return spca50x; // return the Data Structure

Error: // handle the error

If (spca50x-> vdev)

{

If (spca50x-> vdev-> minor =-1)

Video_device_release (spca50x-> vdev );

Else

Video_unregister_device (spca50x-> vdev );

Spca50x-> vdev = NULL;

}

If (spca50x)

{

Kfree (spca50x );

Spca50x = NULL;

}

Mod_dec_use_count;

Return NULL;

Nodevice:

Return NULL;

}

The specific implementation of spca5xx_disconnect is as follows:

Static void

Spca5xx_disconnect (struct usb_device * Dev, void * PTR)

{

Struct usb_spca50x * spca50x = (struct usb_spca50x *) PTR;

Int N;

Mod_inc_use_count; // increase the number of modules used.

If (! Spca50x)

Return;

Down (& spca50x-> lock); // reduce semaphores

Spca50x-> present = 0; // The driver is uninstalled to 0.

For (n = 0; n <spca50x_numframes; n ++) // indicates the aborting status of all frames.

Spca50x-> frame [N]. grabstate = frame_aborting;

Spca50x-> curframe =-1;

For (n = 0; n <spca50x_numframes; n ++) // wake up all waiting Processes

If (waitqueue_active (& spca50x-> frame [N]. WQ ))

Wake_up_interruptible (& spca50x-> frame [N]. WQ );

If (waitqueue_active (& spca50x-> WQ ))

Wake_up_interruptible (& spca50x-> WQ );

Spca5xx_kill_isoc (spca50x); // The subfunction terminates the transmission of the urb package.

Pdebug (3, "Disconnect kill ISOC done ");

Up (& spca50x-> lock); // increase the semaphore

While (spca50x-> User) // if there are other processes in use, process switching

Schedule ();

Down (& spca50x-> lock );

If (spca50x-> vdev)

Video_unregister_device (spca50x-> vdev); // cancel the video device

Usb_driver_release_interface (& spca5xx_driver, // port release

& Spca50x-> Dev-> actconfig->

Interface [spca50x-> iface]);

Spca50x-> Dev = NULL;

Up (& spca50x-> lock );

# Ifdef config_proc_fs

Destroy_proc_spca50x_cam (spca50x); // deregister the proc file

# Endif/* config_proc_fs */

If (spca50x &&! Spca50x-> User) // release memory space

{

Spca5xx_dealloc (spca50x );

Kfree (spca50x );

Spca50x = NULL;

}

Mod_dec_use_count; // reduce the module count

Pdebug (3, "Disconnect complete ");

}

Upper Layer software interface module:

This module uses the file_operations data structure and implements key system calls for devices based on the v4l protocol specifications, implementing the documented UNIX System Design Features of devices. As the camera head driver, the function is data collection, but not output to the camera head. Therefore, the write system is not called in the source code. Its key data structure is as follows:

Static struct video_device spca50x_template = {

. Owner = this_module,

. Name = "spca5xx USB camera ",

. Type = vid_type_capture,

. Hardware = vid_hardware_spca5xx,

. Fops = & spca5xx_fops,

};

Static struct file_operations spca5xx_fops = {

. Owner = this_module,

. Open = spca5xx_open, // OPEN function

. Release = spca5xx_close, // close Function

. Read = spca5xx_read, // READ function

. MMAP = spca5xx_mmap, // memory ing Function

. IOCTL = spca5xx_ioctl, // obtain the File Information

. Llseek = no_llseek, // file location function not implemented

};

OPEN function:

Enable and initialize the device and initialize the decoder module. The specific implementation is as follows:

Static int

Spca5xx_open (struct video_device * vdev, int flags)

{

Struct usb_spca50x * spca50x = video_get_drvdata (vdev );

Int err;

Mod_inc_use_count; // increase the module count.

Down (& spca50x-> lock );

Err =-enodev;

If (! Spca50x-> present) // check whether the device exists, whether the driver exists, or not.

Goto out;

Err =-ebusy;

If (spca50x-> User)

Goto out;

Err =-enomem;

If (spca50x_alloc (spca50x ))

Goto out;

Err = spca50x_init_source (spca50x); // initialize the sensor and decoding module. In the implementation of this function, the initialization of each DSP chip is different, the DSP chip of star micro 301p is initialized in the sub-function zc3xx_init. Its implementation method is to fill in the register value.

If (Err! = 0 ){

Pdebug (0, "dealloc error on spca50x_init_source/N ");

Up (& spca50x-> lock );

Spca5xx_dealloc (spca50x );

Goto out2;

}

Spca5xx_initdecoder (spca50x); // the initialization of the decoding module. The specific implementation of the module adopts the Huffman algorithm.

Spca5xx_setframedecoder (spca50x );

Spca50x-> User ++;

Err = spca50x_init_isoc (spca50x); // initialize the urb (USB request block) package, start the camera header, and transmit data through synchronous transmission

If (ERR)

{

Pdebug (0, "dealloc error on init_isoc/N ");

Spca50x-> User --;

Spca5xx_kill_isoc (spca50x );

Up (& spca50x-> lock );

Spca5xx_dealloc (spca50x );

Goto out2;

}

Spca50x-> brightness = spca50x_get_brghtness (spca50x) <8;

Spca50x-> whiteness = 0;

Out:

Up (& spca50x-> lock );

Out2:

If (ERR)

Mod_dec_use_count;

If (ERR)

{

Pdebug (2, "Open failed ");

}

Else

{

Pdebug (2, "Open done ");

}

Return err;

}

2. Close function:

The specific process of closing a device is as follows:

Static void

Spca5xx_close (struct video_device * vdev)

{

Struct usb_spca50x * spca50x = vdev-> priv;

Int I;

Pdebug (2, "spca50x_close ");

Down (& spca50x-> lock); // parameter settings

Spca50x-> User --;

Spca50x-> curframe =-1;

If (spca50x-> present) // present: yes or has a driver to load.

{

Spca50x_stop_isoc (spca50x); // stop camera head and send data packets

Spcacamerashutdown (spca50x); // close the camera header, which is completed by the subfunction spca50x_stop_isoc.

For (I = 0; I <spca50x_numframes; I ++) // wake up all the waiting Processes

{

If (waitqueue_active (& spca50x-> frame [I]. WQ ))

Wake_up_interruptible (& spca50x-> frame [I]. WQ );

}

If (waitqueue_active (& spca50x-> WQ ))

Wake_up_interruptible (& spca50x-> WQ );

}

Up (& spca50x-> lock );

Spca5xx_dealloc (spca50x); // reclaim memory space

Pdebug (2, "Release ressources done ");

Mod_dec_use_count;

}

READ function:

To complete data reading, the main task is to transmit data from the kernel space to the process user space.

Static long

Spca5xx_read (struct video_device * Dev, char * Buf, unsigned long

Count, int Noblock)

{

Struct usb_spca50x * spca50x = video_get_drvdata (Dev );

Int I;

Int frmx =-1;

Int RC;

Volatile struct spca50x_frame * frame;

If (down_interruptible (& spca50x-> lock) // obtain the semaphore

Return-eintr;

If (! Dev |! Buf) {// determine the device status

Up (& spca50x-> lock );

Return-efault;

}

If (! Spca50x-> Dev ){

Up (& spca50x-> lock );

Return-EIO;

}

If (! Spca50x-> streaming ){

Up (& spca50x-> lock );

Return-EIO;

}

If (rc = wait_event_interruptible (spca50x-> WQ, // sleep on the specified queue until the condition 2 is true

Spca50x-> frame [0]. grabstate = frame_done |

Spca50x-> frame [1]. grabstate = frame_done |

Spca50x-> frame [2]. grabstate = frame_done |

Spca50x-> frame [3]. grabstate = frame_done ))){

Up (& spca50x-> lock );

Return RC;

}

For (I = 0; I <spca50x_numframes; I ++) // when data arrives

If (spca50x-> frame [I]. grabstate = frame_done) // identify that the data has been

Frmx = I;

If (frmx <0)

{

Pdebug (2, "couldnt find a frame ready to be read .");

Up (& spca50x-> lock );

Return-efault;

}

Frame = & spca50x-> frame [frmx];

Pdebug (2, "Count asked: % d available: % d", (INT) count,

(INT) frame-> scanlength );

If (count> frame-> scanlength)

Count = frame-> scanlength;

If (I = copy_to_user (BUF, frame-> data, count) // copies data in the user space and kernel space

{

Pdebug (2, "Copy failed! % D bytes not copied ", I );

Up (& spca50x-> lock );

Return-efault;

}

/* Release the frame */

Frame-> grabstate = frame_ready; // The ID data is empty.

Up (& spca50x-> lock );

Return count; // return the number of copied data

}

MMAP function:

To map the device memory to the address space of the user process, the key function is remap_page_range. The specific implementation is as follows:

Static int

Spca5xx_mmap (struct video_device * Dev, const char * ADR, unsigned Long SIZE)

{

Unsigned long start = (unsigned long) ADR;

Struct usb_spca50x * spca50x = Dev-> priv;

Unsigned long page, Pos;

 

If (spca50x-> Dev = NULL)

Return-EIO;

Pdebug (4, "MMAP: % LD (% lx) bytes", size, size );

If (size>

(Spca50x_numframes * max_data_size) + page_size-1 )&~ (Page_size-1 )))

Return-einval;

If (down_interruptible (& spca50x-> lock) // obtain the semaphore

Return-eintr;

Pos = (unsigned long) spca50x-> fbuf;

While (size> 0) // implement memory ing in a loop

{

Page = k1__to_pa (POS );

If (remap_page_range (START, page, page_size, page_shared) {// implement memory ing

Up (& spca50x-> lock );

Return-eagain ;}

Start + = page_size;

Pos + = page_size;

If (size> page_size)

Size-= page_size;

Else

Size = 0;

}

Up (& spca50x-> lock); // release the semaphore

Return 0;

}

Ioctl function:

To obtain file information,

Static int

Spca5xx_ioctl (struct inode * inode, struct file * file, unsigned int cmd, unsigned long Arg)

{

Struct video_device * vdev = file-> private_data;

Struct usb_spca50x * spca50x = vdev-> priv;

Int RC;

If (down_interruptible (& spca50x-> lock) // obtain the semaphore

Return-eintr;

Rc = video_usercopy (inode, file, CMD, ARG, spca5xx_do_ioctl); // transmits information to the user process, and the key function implements spca5xx_do_ioctl.

Up (& spca50x-> lock );

Return RC;

}

The implementation of the spca5xx_do_ioctl function depends on different hardware. In order to support multiple chips, the main idea of the Implementation Program is to use copy_to_user (ARG, B, sizeof (struct video_capability) the function transmits device information to the user process.

 

Data transmission module:

The source program uses tasklet to synchronize and quickly transmit data, and uses the software decoding module on spcadecode. C to decode the graphic information. The entry point of this module is attached to the spca_open function. The specific function is spca50x_init_isoc. When the device is turned on, the synchronous transmission of data has also started, and the data is passed to the driver through the spca50x_move_data function, the driver achieves data access through polling.

Void

Outpict_do_tasklet (unsigned long PTR)

{

Int err;

Struct spca50x_frame * taskletframe = (struct spca50x_frame *) PTR;

Taskletframe-> scanlength = taskletframe-> highwater-taskletframe-> data;

Pdebug (2, "tasklet ask spcadecoder hdrwidth % d hdrheight % d method % d ",

Taskletframe-> hdrwidth, taskletframe-> hdrheight,

Taskletframe-> method );

Err = spca50x_outpicture (taskletframe); // output processed image data

If (Err! = 0)

{

Pdebug (0, "frame decoder failed (% d)", err );

Taskletframe-> grabstate = frame_error;

}

Else

{

Taskletframe-> grabstate = frame_done;

}

If (waitqueue_active (& taskletframe-> WQ) // if a process waits, wake up the waiting process

Wake_up_interruptible (& taskletframe-> WQ );

}

It is worth mentioning that spcadecode. the decoding module on C decodes the original compressed graphics data streams yyuyv, yuvy, ipv411, and ipv422 as RGB Images. However, the implementation of this decompression algorithm also depends on the compression format, ultimately, it depends on the hardware Compression Algorithm in DSP (Digital Processing Chip.

 

Iv. USB core support:

In Linux, USB core relies on the System-implemented USB core layer for underlying hardware operations. USB core provides many function interfaces for upper-layer drivers, such as usb_control_msg and usb_sndctrlpipe, the most typical use is the Read and Write Functions spca50x_reg_write and spca50x_reg_read in the source code. The specific implementation is as follows: (the implementation of spca50x_reg_write is similar to other functions)

Static int spca50x_reg_write (struct usb_device * Dev, __2010reg, __2010index,

_ 2010value)

{

Int RC;

Rc = usb_control_msg (Dev, // set the register value through the interface function provided by USB core

Usb_sndctrlpipe (Dev, 0 ),

Reg,

Usb_type_vendor | usb_recip_device,

Value, index, null, 0, timeout );

Pdebug (5, "Reg write: 0x % 02x, 0x % 02x: 0x % 02x, 0x % x", Reg, index, value, RC );

If (RC <0)

Err ("Reg write: Error % d", RC );

Return RC;

}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.