Análisis del marco V4L2

       V4L2 es la abreviatura de Video para linux2, que es el controlador del núcleo del equipo de video en linux. v4L2 es un marco de programación para dispositivos USB sin unidad uvc (USB Video Class), utilizado principalmente para recolectar cámaras USB, etc.

      La siguiente figura es el marco de trabajo de V4L2. Primero, la configuración de asignación de la capa central del sistema registra una variable denominada estructura cdev (la estructura cdev es parte de la estructura video_device) y establece cdev-> ops = v4l2_fops; en la capa de hardware, asignamos el registro de configuraciones Una variable denominada estructura vfd (estructura video_device) y establece vfd-> fops = & vivi_fops, vfd-> ioctl_ops = & vivi_ioctl_ops; cuando la aplicación (APP) llama a funciones como leer y abrir, se llamará en v4l2_fops Las funciones de lectura y apertura, y luego las funciones de lectura y apertura en v4l2_fops llamarán a las funciones de lectura y apertura en vfd-> fops relacionadas con la capa de hardware. La función ioctl es similar.

        Comencemos con el programa para analizar el marco V4L2. Este artículo utiliza el controlador de video virtual vivi.c en los controladores \ medio \ video en el directorio del kernel de Linux (este código usa la api v4l2 para simular un dispositivo de video real) para analizar el marco V4L2. . Su marco general es el siguiente:

vivi_init
    vivi_create_instance
        v4l2_device_register   // 不是主要, 只是用于初始化一些东西,比如自旋锁、引用计数
        video_device_alloc
        // 设置
          1. vfd:
            .fops           = &vivi_fops,
            .ioctl_ops 	= &vivi_ioctl_ops,
            .release	= video_device_release,
          2.
            vfd->v4l2_dev = &dev->v4l2_dev;
          3. 设置"ctrl属性"(用于APP的ioctl):
            	v4l2_ctrl_handler_init(hdl, 11);
            	dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
            			V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
            	dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
            			V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
            	dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
            			V4L2_CID_CONTRAST, 0, 255, 1, 16);                        
        video_register_device(video_device, type:VFL_TYPE_GRABBER, nr)
            __video_register_device
                vdev->cdev = cdev_alloc();
                vdev->cdev->ops = &v4l2_fops;
                cdev_add
                
                video_device[vdev->minor] = vdev;

        		if (vdev->ctrl_handler == NULL)
        			vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;

Started Comenzamos desde la función vivi_init en vivi.c y descubrimos que llamaba v4l2_device_register, que se usa para inicializar algunas cosas, como bloqueo de giro, conteo de referencias, que no es necesario; Realice la configuración adecuada, por ejemplo

.fops = & vivi_fops,
.ioctl_ops = & vivi_ioctl_ops,
.release = video_device_release,

Espere la configuración y luego registre la estructura con video_register_device;

③ La función video_register_device llama a __video_register_device para lograr las siguientes operaciones:

vdev-> cdev = cdev_alloc ();
vdev-> cdev-> ops = & v4l2_fops;
cdev_add
                
video_device [vdev-> minor] = vdev;

if (vdev-> ctrl_handler == NULL)
    vdev-> ctrl_handler = vdev-> v4l2_dev-> ctrl_handler;

 

 

La imagen de arriba es parte de la función vivi_create_instance. Primero asigne una variable vfd de la estructura video_device, y luego establezca * vfd = vivi_template; donde vivi_template es una variable de estructura del video_device, que establece cierta información como .fops ), Esta operación es equivalente a la configuración

 1. vfd:

.fops = & vivi_fops,
.ioctl_ops = & vivi_ioctl_ops,
.release = video_device_release,

static struct video_device vivi_template = {
	.name		= "vivi",
	.fops           = &vivi_fops,
	.ioctl_ops 	= &vivi_ioctl_ops,
	.release	= video_device_release,

	.tvnorms              = V4L2_STD_525_60,
	.current_norm         = V4L2_STD_NTSC_M,
};

Luego ingrese la función video_register_device, lo siguiente es parte del código fuente en video_register_device, primero asigne una estructura cdev

Luego establezca cdev-> ops = & v4l2_fops; v4l2_fops mismo apunta a algunas funciones (como se muestra a continuación), por lo que cdev también apunta a estas funciones, cuando la aplicación llama a la función de lectura, llamará a la función de lectura en cdev

static const struct file_operations v4l2_fops = {
	.owner = THIS_MODULE,
	.read = v4l2_read,
	.write = v4l2_write,
	.open = v4l2_open,
	.get_unmapped_area = v4l2_get_unmapped_area,
	.mmap = v4l2_mmap,
	.unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = v4l2_compat_ioctl32,
#endif
	.release = v4l2_release,
	.poll = v4l2_poll,
	.llseek = no_llseek,
};

La función de lectura en cdev se muestra en la siguiente figura: Primero, la estructura video_device se obtiene de acuerdo con filp, y luego se juzga la función de lectura en la estructura video_device. Si existe, se llama. función de lectura

 

 

 

 

En comparación con las funciones de abrir y leer, el proceso de llamada de ioctl es más complicado. Echemos un vistazo a continuación (tomamos VIDIOC_QUERYCAP como ejemplo). La siguiente figura es la función v4l2_ioctl señalada por .unlocked_ioctl en v4l2_fops.

Llama al ioctl en los fops del anterior vivi_template.

El ioctl en los fops de vivi_template se llama a la función __video_do_ioctl en la siguiente figura, que finalmente llama a la función en el miembro ioctl_ops en vfd, es decir, la función en vivi_ioctl_ops

Por ejemplo, si se llama a VIDIOC_QUERYCAP, eventualmente se llamará a la siguiente función.

/* ------------------------------------------------------------------
	IOCTL vidioc handling
   ------------------------------------------------------------------*/
static int vidioc_querycap(struct file *file, void  *priv,
					struct v4l2_capability *cap)
{
	struct vivi_dev *dev = video_drvdata(file);

	strcpy(cap->driver, "vivi");
	strcpy(cap->card, "vivi");
	strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
	cap->version = VIVI_VERSION;
	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \
			    V4L2_CAP_READWRITE;
	return 0;
}

 

 

Resumen: la llamada de ioctl es una capa más que abierta y leída . Cuando la aplicación llama a la función ioctl, llamará a la función ioctl en cdev, y luego llamará a la función ioctl en vfd.fops que configuramos anteriormente, es decir, leer, abrir V4l2_ioctl en la misma estructura de la función, y finalmente llama a y  

.fops = & vivi_fops, la
función correspondiente en .ioctl_ops = & vivi_ioctl_ops en la misma estructura .

 

 

Artículos relacionados:

https://blog.csdn.net/qingkongyeyue/article/details/53447331

https://blog.csdn.net/qingkongyeyue/article/details/52372960

42 artículos originales publicados · Me gusta 10 · Visitantes más de 10,000

Supongo que te gusta

Origin blog.csdn.net/qq_37659294/article/details/104139839
Recomendado
Clasificación