V4L2用户空间和kernel层driver的交互过程

V4L2用户空间和kernel层driver的交互过程

这篇文章详细分析了V4L2用户空间和kernel层driver的交互过程,目的只有一个:

更清晰的理解V4L2视频驱动程序的系统结构,驱动编程方法,为以后开发视频驱动打好基础

既然从用户层出发探究驱动层,这里先贴出应用层code:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <assert.h>
  5. #include <getopt.h>
  6. #include <fcntl.h>
  7. #include <unistd.h>
  8. #include <errno.h>
  9. #include <sys/stat.h>
  10. #include <sys/types.h>
  11. #include <sys/time.h>
  12. #include <sys/mman.h>
  13. #include <sys/ioctl.h>
  14. #include <asm/types.h>
  15. #include <linux/videodev2.h>
  16. #include <linux/fb.h>
  17. #define CLEAR(x) memset
    (&(x), 0, sizeof
    (x))
  18. struct buffer {
  19. void * start;
  20. size_t length;
  21. };
  22. static char * dev_name
    = NULL;
  23. static int fd
    = -1;
  24. struct buffer * buffers
    = NULL;
  25. static unsigned int n_buffers
    = 0;
  26. static int time_in_sec_capture=5;
  27. static int fbfd
    = -1;
  28. static struct fb_var_screeninfo vinfo;
  29. static struct fb_fix_screeninfo finfo;
  30. static char *fbp=NULL;
  31. static long screensize=0;
  32. static void errno_exit (const char
    * s)
  33. {
  34. fprintf (stderr,
    "%s error %d, %s/n",s, errno, strerror
    (errno));
  35. exit (EXIT_FAILURE);
  36. }
  37. static int xioctl
    (int fd,int request,void
    * arg)
  38. {
  39. int r;
  40. /* Here use this method
    to make sure cmd success*/
  41. do r = ioctl
    (fd, request, arg);
  42. while (-1
    == r
    && EINTR == errno);
  43. return r;
  44. }
  45. inline int clip(int value,
    int min,
    int max) {
  46. return (value
    > max ? max
    : value < min
    ? min : value);
  47. }
  48. static void process_image (const void
    * p){
  49. //ConvertYUVToRGB321;
  50. unsigned char*
    in=(char*)p;
  51. int width=640;
  52. int height=480;
  53. int istride=1280;
  54. int x,y,j;
  55. int y0,u,y1,v,r,g,b;
  56. long location=0;
  57. for ( y
    = 100; y
    < height + 100;
    ++y)
    {
  58. for (j
    = 0, x=100; j
    < width * 2
    ; j += 4,x
    +=2)
    {
  59. location
    = (x+vinfo.xoffset)
    * (vinfo.bits_per_pixel/8)
    +
  60. (y+vinfo.yoffset)
    * finfo.line_length;
  61. y0 =
    in[j];
  62. u =
    in[j + 1]
    - 128;
  63. y1 =
    in[j + 2];
  64. v =
    in[j + 3]
    - 128;
  65. r =
    (298 * y0 + 409
    * v + 128)
    >> 8;
  66. g =
    (298 * y0 - 100
    * u - 208
    * v + 128)
    >> 8;
  67. b =
    (298 * y0 + 516
    * u + 128)
    >> 8;
  68. fbp[
    location + 0]
    = clip(b, 0, 255);
  69. fbp[
    location + 1]
    = clip(g, 0, 255);
  70. fbp[
    location + 2]
    = clip(r, 0, 255);
  71. fbp[
    location + 3]
    = 255;
  72. r =
    (298 * y1 + 409
    * v + 128)
    >> 8;
  73. g =
    (298 * y1 - 100
    * u - 208
    * v + 128)
    >> 8;
  74. b =
    (298 * y1 + 516
    * u + 128)
    >> 8;
  75. fbp[
    location + 4]
    = clip(b, 0, 255);
  76. fbp[
    location + 5]
    = clip(g, 0, 255);
  77. fbp[
    location + 6]
    = clip(r, 0, 255);
  78. fbp[
    location + 7]
    = 255;
  79. }
  80. in +=istride;
  81. }
  82. }
  83. static int read_frame
    (void)
  84. {
  85. struct v4l2_buffer buf;
  86. unsigned int i;
  87. CLEAR (buf);
  88. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  89. buf.memory
    = V4L2_MEMORY_MMAP;
  90. /* 11.
    VIDIOC_DQBUF把数据放回缓存队列*/
  91. if
    (-1
    == xioctl
    (fd, VIDIOC_DQBUF,
    &buf))
    {
  92. switch (errno)
    {
  93. case EAGAIN:
  94. return 0;
  95. case EIO:
  96. default:
  97. errno_exit ("VIDIOC_DQBUF");
  98. }
  99. }
  100. assert (buf.index
    < n_buffers);
  101. printf("v4l2_pix_format->field(%d)/n", buf.field);
  102. //assert
    (buf.field
    ==V4L2_FIELD_NONE);
  103. process_image (buffers[buf.index].start);
  104. /*12.
    VIDIOC_QBUF把数据从缓存中读取出来*/
  105. if
    (-1
    == xioctl
    (fd, VIDIOC_QBUF,
    &buf))
  106. errno_exit ("VIDIOC_QBUF");
  107. return 1;
  108. }
  109. static void run (void)
  110. {
  111. unsigned int count;
  112. int frames;
  113. frames = 30
    * time_in_sec_capture;
  114. while (frames--
    > 0)
    {
  115. for (;;)
    {
  116. fd_set fds;
  117. struct timeval tv;
  118. int r;
  119. FD_ZERO (&fds);
  120. FD_SET (fd,
    &fds);
  121. tv.tv_sec
    = 2;
  122. tv.tv_usec
    = 0;
  123. /* 10.
    poll method*/
  124. r =
    select
    (fd + 1,
    &fds,
    NULL,
    NULL,
    &tv);
  125. if
    (-1 == r)
    {
  126. if
    (EINTR == errno)
  127. continue;
  128. errno_exit ("select");
  129. }
  130. if
    (0 == r)
    {
  131. fprintf (stderr,
    "select timeout/n");
  132. exit
    (EXIT_FAILURE);
  133. }
  134. if
    (read_frame())
  135. break;
  136. }
  137. }
  138. }
  139. static void stop_capturing (void)
  140. {
  141. enum v4l2_buf_type type;
  142. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  143. /*13.
    VIDIOC_STREAMOFF结束视频显示函数*/
  144. if
    (-1
    == xioctl
    (fd, VIDIOC_STREAMOFF,
    &type))
  145. errno_exit ("VIDIOC_STREAMOFF");
  146. }
  147. static void start_capturing (void)
  148. {
  149. unsigned int i;
  150. enum v4l2_buf_type type;
  151. for (i
    = 0; i
    < n_buffers;
    ++i)
    {
  152. struct v4l2_buffer buf;
  153. CLEAR (buf);
  154. buf.type
    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  155. buf.memory
    = V4L2_MEMORY_MMAP;
  156. buf.index
    = i;
  157. /* 8.
    VIDIOC_QBUF把数据从缓存中读取出来*/
  158. if
    (-1
    == xioctl
    (fd, VIDIOC_QBUF,
    &buf))
  159. errno_exit ("VIDIOC_QBUF");
  160. }
  161. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  162. /* 9.
    VIDIOC_STREAMON开始视频显示函数*/
  163. if
    (-1
    == xioctl
    (fd, VIDIOC_STREAMON,
    &type))
  164. errno_exit ("VIDIOC_STREAMON");
  165. }
  166. static void uninit_device (void)
  167. {
  168. unsigned int i;
  169. for (i
    = 0; i
    < n_buffers;
    ++i)
  170. if (-1
    == munmap
    (buffers[i].start, buffers[i].length))
  171. errno_exit ("munmap");
  172. if (-1
    == munmap(fbp, screensize))
    {
  173. printf(" Error: framebuffer device munmap() failed./n");
  174. exit
    (EXIT_FAILURE)
    ;
  175. }
  176. free (buffers);
  177. }
  178. static void init_mmap (void)
  179. {
  180. struct v4l2_requestbuffers req;
  181. //mmap framebuffer
  182. fbp = (char
    *)mmap(NULL,screensize,PROT_READ
    | PROT_WRITE,MAP_SHARED
    ,fbfd, 0);
  183. if ((int)fbp
    ==
    -1) {
  184. printf("Error: failed to map framebuffer device to memory./n");
  185. exit (EXIT_FAILURE)
    ;
  186. }
  187. memset(fbp, 0, screensize);
  188. CLEAR (req);
  189. req.count
    = 4;
  190. req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  191. req.memory
    = V4L2_MEMORY_MMAP;
  192. /* 6.
    VIDIOC_REQBUFS分配内存*/
  193. if
    (-1
    == xioctl
    (fd, VIDIOC_REQBUFS,
    &req))
    {
  194. if (EINVAL
    == errno)
    {
  195. fprintf (stderr,
    "%s does not support memory mapping/n", dev_name);
  196. exit
    (EXIT_FAILURE);
  197. } else
    {
  198. errno_exit ("VIDIOC_REQBUFS");
  199. }
  200. }
  201. if (req.count
    < 4)
    {
  202. fprintf (stderr,
    "Insufficient buffer memory on %s/n",dev_name);
  203. exit (EXIT_FAILURE);
  204. }
  205. buffers = calloc
    (req.count, sizeof
    (*buffers));
  206. if (!buffers)
    {
  207. fprintf (stderr,
    "Out of memory/n");
  208. exit (EXIT_FAILURE);
  209. }
  210. for (n_buffers
    = 0; n_buffers
    < req.count;
    ++n_buffers)
    {
  211. struct v4l2_buffer buf;
  212. CLEAR (buf);
  213. buf.type
    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  214. buf.memory
    = V4L2_MEMORY_MMAP;
  215. buf.index
    = n_buffers;
  216. /* 7.
    VIDIOC_QUERYBUF把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址*/
  217. if
    (-1
    == xioctl
    (fd, VIDIOC_QUERYBUF,
    &buf))
  218. errno_exit ("VIDIOC_QUERYBUF");
  219. buffers[n_buffers].length
    = buf.length;
  220. buffers[n_buffers].start
    =mmap (NULL,buf.length,PROT_READ
    | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);
  221. if (MAP_FAILED
    == buffers[n_buffers].start)
  222. errno_exit ("mmap");
  223. }
  224. }
  225. static void init_device (void)
  226. {
  227. struct v4l2_capability cap;
  228. struct v4l2_cropcap cropcap;
  229. struct v4l2_crop crop;
  230. struct v4l2_format fmt;
  231. unsigned int min;
  232. //
    Get fixed screen information
  233. if (-1==xioctl(fbfd,
    FBIOGET_FSCREENINFO,
    &finfo))
    {
  234. printf("Error reading fixed information./n");
  235. exit (EXIT_FAILURE);
  236. }
  237. //
    Get variable screen information
  238. if (-1==xioctl(fbfd,
    FBIOGET_VSCREENINFO,
    &vinfo))
    {
  239. printf("Error reading variable information./n");
  240. exit (EXIT_FAILURE);
  241. }
  242. screensize = vinfo.xres
    * vinfo.yres
    * vinfo.bits_per_pixel
    / 8;
  243. /* 2.
    VIDIOC_QUERYCAP查询驱动功能*/
  244. if
    (-1
    == xioctl
    (fd, VIDIOC_QUERYCAP,
    &cap))
    {
  245. if (EINVAL
    == errno)
    {
  246. fprintf (stderr,
    "%s is no V4L2 device/n",dev_name);
  247. exit
    (EXIT_FAILURE);
  248. } else
    {
  249. errno_exit ("VIDIOC_QUERYCAP");
  250. }
  251. }
  252. /* Check
    if it is a video capture device*/
  253. if (!(cap.capabilities
    & V4L2_CAP_VIDEO_CAPTURE))
    {
  254. fprintf (stderr,
    "%s is no video capture device/n",dev_name);
  255. exit (EXIT_FAILURE);
  256. }
  257. /* Check
    if support streaming I/O ioctls*/
  258. if (!(cap.capabilities
    & V4L2_CAP_STREAMING))
    {
  259. fprintf (stderr,
    "%s does not support streaming i/o/n",dev_name);
  260. exit (EXIT_FAILURE);
  261. }
  262. CLEAR (cropcap);
  263. /*
    Set type*/
  264. cropcap.type
    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  265. /* 3.
    VIDIOC_CROPCAP查询驱动的修剪能力*/
  266. /* 这里在vivi驱动中我们没有实现此方法,即不支持此操作*/
  267. if
    (0
    == xioctl
    (fd, VIDIOC_CROPCAP,
    &cropcap))
    {
  268. crop.type
    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  269. crop.c
    = cropcap.defrect;
  270. /* 4.
    VIDIOC_S_CROP设置视频信号的边框*/
  271. /* 同样不支持这个操作*/
  272. if
    (-1
    == xioctl
    (fd, VIDIOC_S_CROP,
    &crop))
    {
  273. switch (errno)
    {
  274. case EINVAL:
  275. break;
  276. default:
  277. break;
  278. }
  279. }
  280. }else
    { }
  281. CLEAR (fmt);
  282. fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  283. fmt.fmt.pix.width
    = 640;
  284. fmt.fmt.pix.height
    = 480;
  285. fmt.fmt.pix.pixelformat
    = V4L2_PIX_FMT_YUYV;
  286. fmt.fmt.pix.field
    = V4L2_FIELD_INTERLACED;
  287. /* 5.
    VIDIOC_S_FMT设置当前驱动的频捕获格式*/
  288. if
    (-1
    == xioctl
    (fd, VIDIOC_S_FMT,
    &fmt))
  289. errno_exit ("VIDIOC_S_FMT");
  290. init_mmap ();
  291. }
  292. static void close_device (void)
  293. {
  294. if (-1
    == close
    (fd))
  295. errno_exit ("close");
  296. fd = -1;
  297. /*14.
    close method*/
  298. close(fbfd);
  299. }
  300. static void open_device (void)
  301. {
  302. struct stat st;
  303. if (-1
    == stat
    (dev_name,
    &st))
    {
  304. fprintf (stderr,
    "Cannot identify ‘%s‘: %d, %s/n",dev_name, errno, strerror
    (errno));
  305. exit (EXIT_FAILURE);
  306. }
  307. if (!S_ISCHR
    (st.st_mode))
    {
  308. fprintf (stderr,
    "%s is no device/n", dev_name);
  309. exit (EXIT_FAILURE);
  310. }
  311. fbfd = open("/dev/fb0", O_RDWR);
  312. if (fbfd==-1)
    {
  313. printf("Error: cannot open framebuffer device./n");
  314. exit (EXIT_FAILURE);
  315. }
  316. /* 1.
    open the char device */
  317. fd = open
    (dev_name, O_RDWR|
    O_NONBLOCK, 0);
  318. if (-1
    == fd)
    {
  319. fprintf (stderr,
    "Cannot open ‘%s‘: %d, %s/n",dev_name, errno, strerror
    (errno));
  320. exit (EXIT_FAILURE);
  321. }
  322. }
  323. static void usage (FILE
    * fp,int argc,char
    ** argv)
  324. {
  325. fprintf (fp,
  326. "Usage: %s [options]/n/n"
  327. "Options:/n"
  328. "-d | --device name Video device name [/dev/video]/n"
  329. "-h | --help Print this message/n"
  330. "-t | --how long will display in seconds/n"
  331. "",
  332. argv[0]);
  333. }
  334. static const char short_options
    [] =
    "d:ht:";
  335. static const struct
    option long_options []
    = {
  336. { "device", required_argument,
    NULL,
    ‘d‘ },
  337. { "help", no_argument,
    NULL,
    ‘h‘ },
  338. { "time", no_argument,
    NULL,
    ‘t‘ },
  339. { 0, 0, 0, 0
    }
  340. };
  341. int main (int argc,char
    ** argv)
  342. {
  343. dev_name =
    "/dev/video0";
  344. for (;;)
  345. {
  346. int index;
  347. int c;
  348. c = getopt_long
    (argc, argv,short_options, long_options,&index);
  349. if (-1
    == c)
  350. break;
  351. switch (c)
    {
  352. case 0:
  353. break;
  354. case ‘d‘:
  355. dev_name = optarg;
  356. break;
  357. case ‘h‘:
  358. usage (stdout, argc, argv);
  359. exit (EXIT_SUCCESS);
  360. case ‘t‘:
  361. time_in_sec_capture = atoi(optarg);
  362. break;
  363. default:
  364. usage (stderr, argc, argv);
  365. exit (EXIT_FAILURE);
  366. }
  367. }
  368. open_device();
  369. init_device();
  370. start_capturing();
  371. run();
  372. stop_capturing();
  373. uninit_device();
  374. close_device();
  375. exit(EXIT_SUCCESS);
  376. return 0;
  377. }

上面code中我已经标注出程序顺序指向的步骤1--14步,下面将一一说明应用从做这14步时驱动层是怎样响应,变化过程,驱动加载初始化部分上一篇文章已经说过了

正式开始取经之路哇。。。。。。。

STEP 1:

fd = open (dev_name, O_RDWR| O_NONBLOCK, 0);

打开字符设备,这个字符设备是video_device_register时创建的,code在v4l2_dev.c中,具体:

  1. static int v4l2_open(struct inode
    *inode, struct file
    *filp)
  2. {
  3. struct video_device *vdev;
  4. int ret = 0;
  5. /* Check
    if the video device
    is available */
  6. mutex_lock(&videodev_lock);
  7. vdev = video_devdata(filp);
  8. /* return ENODEV
    if the video device has already been removed.
    */
  9. if (vdev
    ==
    NULL ||
    !video_is_registered(vdev))
    {
  10. mutex_unlock(&videodev_lock);
  11. return -ENODEV;
  12. }
  13. /*
    and increase the device refcount */
  14. video_get(vdev);
  15. mutex_unlock(&videodev_lock);
  16. /*
  17. * Here using the API you
    get the method you get the open() method write
  18. * The other methods
    in fops use the same method to use you own code
  19. */
  20. if (vdev->fops->open)
    {
  21. if (vdev->lock
    && mutex_lock_interruptible(vdev->lock))
    {
  22. ret =
    -ERESTARTSYS;
  23. goto err;
  24. }
  25. if (video_is_registered(vdev))
  26. ret = vdev->fops->open(filp);
  27. else
  28. ret =
    -ENODEV;
  29. if (vdev->lock)
  30. mutex_unlock(vdev->lock);
  31. }
  32. err:
  33. /* decrease the refcount
    in case of an
    error */
  34. if (ret)
  35. video_put(vdev);
  36. return ret;
  37. }

重点在标注部分,通过这个V4L2的API调用我们自己驱动程序中定义的open方法,我们自己的open方法所属的fops是在vivi.c驱动程序的vivi_create_instance方法中video_device_register之前关联进来的

  1. int v4l2_fh_open(struct file
    *filp)
  2. {
  3. struct video_device *vdev
    = video_devdata(filp);
  4. struct v4l2_fh *fh
    = kzalloc(sizeof(*fh), GFP_KERNEL);
  5. /*
  6. * IN the open method,
    do only one job
  7. * set v4l2_fh into filp->private_data
    for later use,
    and initial v4l2_fh
  8. */
  9. filp->private_data
    = fh;
  10. if (fh
    ==
    NULL)
  11. return -ENOMEM;
  12. v4l2_fh_init(fh, vdev);
  13. v4l2_fh_add(fh);
  14. return 0;
  15. }
  16. EXPORT_SYMBOL_GPL(v4l2_fh_open);

这个open方法只是初始化了一个v4l2_fh,并关联到filp->private中,方便以后使用

这里设置V4L2_FL_USES_V4L2_FH这个标志位,设置优先级为UNSET,如果我们的自己驱动程序实现了,支持

VIDIOC_SUBSCRIBE_EVENT,那么v4l2_event_init,在events初始化中初始化链表,并设置sequence为-1,如果不支持,则设置fh->events为NULL

最后add list

STEP 2:

if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap))

这么调用完成下面过程,不行的从驱动层获取cap。直到成功拿到我们想要的数据

  1. static int xioctl
    (int fd,int request,void
    * arg)
  2. {
  3. int r;
  4. /* Here use this method
    to make sure cmd success*/
  5. do r = ioctl
    (fd, request, arg);
  6. while (-1
    == r
    && EINTR == errno);
  7. return r;
  8. }

也就是调用驱动层的ioctl方法,从v4l2 api中的ictol 调用我们自己定义的ioctl ,这中间的过程不在多做说明,我们自己的驱动的控制过程由v4l2_ioctl.c这个文件中的方法实现,一个很庞大的switch

值得一提的是,慢慢后面你会明白的,这里v4l2_ioctl.c这个文件中的方法实现其实只是会中转站,它接着就回调了我们自己驱动程序中定义的控制接口,后面再说吧

  1. long video_ioctl2(struct file
    *file,
  2. unsigned int cmd, unsigned long arg)
  3. {
  4. return video_usercopy(file,
    cmd, arg, __video_do_ioctl);
  5. }

这里这个__video_do_ioctl方法其实完全做了我们所有的控制过程,又为什么又要经过video_usercopy这个方法呢,不妨看一看这个方法

  1. long
  2. video_usercopy(struct file
    *file, unsigned
    int cmd, unsigned long arg,
  3. v4l2_kioctl func)
  4. {
  5. char    sbuf[128];
  6. void *mbuf
    = NULL;
  7. void    *parg
    = (void *)arg;
  8. long    err
    = -EINVAL;
  9. bool    has_array_args;
  10. size_t array_size = 0;
  11. void __user *user_ptr
    = NULL;
  12. void    **kernel_ptr
    = NULL;
  13. /* Copy arguments into temp kernel buffer
    */
  14. if (_IOC_DIR(cmd)
    != _IOC_NONE)
    {
  15. ........这里检查128个字节的大小是否够存放用户端发送来的数据,不够则需要重新申请一个新的内存用来存放,指向parg这个地址
  16. if (_IOC_SIZE(cmd)
    <= sizeof(sbuf))
    {
  17. parg = sbuf;
  18. } else
    {
  19. /* too big
    to allocate from stack
    */
  20. mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
  21. if
    (NULL == mbuf)
  22. return -ENOMEM;
  23. parg = mbuf;
  24. }
  25. err =
    -EFAULT;
  26. if (_IOC_DIR(cmd)
    & _IOC_WRITE)
    {
  27. unsigned long n = cmd_input_size(cmd);
  28. if
    (copy_from_user(parg,
    (void __user *)arg, n))
  29. goto out;
  30. /* zero out anything we don‘t copy from userspace
    */
  31. if
    (n < _IOC_SIZE(cmd))
  32. memset((u8
    *)parg
    + n, 0, _IOC_SIZE(cmd)
    - n);
  33. } else
    {
  34. /* read-only ioctl
    */
  35. memset(parg, 0, _IOC_SIZE(cmd));
  36. }
  37. }
  38. ....check
  39. err = check_array_args(cmd, parg,
    &array_size,
    &user_ptr,
    &kernel_ptr);
  40. if (err
    < 0)
  41. goto out;
  42. has_array_args =
    err;
  43. ....这里这块如果用户端有数据写到kernel,这里负责数据拷贝
  44. if (has_array_args)
    {
  45. /*
  46. * When adding new types of
    array args, make sure that the
  47. * parent argument
    to ioctl (which contains the pointer
    to the
  48. * array) fits into sbuf
    (so that mbuf will still remain
  49. * unused up
    to here).
  50. */
  51. mbuf = kmalloc(array_size, GFP_KERNEL);
  52. err =
    -ENOMEM;
  53. if (NULL
    == mbuf)
  54. goto out_array_args;
  55. err =
    -EFAULT;
  56. if (copy_from_user(mbuf, user_ptr, array_size))
  57. goto out_array_args;
  58. *kernel_ptr
    = mbuf;
  59. }
  60. /* Handles IOCTL
    */
  61. err
    = func(file,
    cmd, parg);
  62. if (err
    ==
    -ENOIOCTLCMD)
  63. err =
    -EINVAL;
  1. if (has_array_args)
    {
  2. *kernel_ptr
    = user_ptr;
  3. if (copy_to_user(user_ptr, mbuf, array_size))
  4. err
    = -EFAULT;
  5. goto out_array_args;
  6. }
  7. if (err
    < 0)
  8. goto out;
  9. out_array_args:
  10. /* Copy results into user buffer
    */
  11. switch (_IOC_DIR(cmd))
    {
  12. case _IOC_READ:
  13. case (_IOC_WRITE
    | _IOC_READ):
  14. if (copy_to_user((void __user
    *)arg, parg, _IOC_SIZE(cmd)))
  15. err
    = -EFAULT;
  16. break;
  17. }
  18. out:
  19. kfree(mbuf);
  20. return err;
  21. }
  22. EXPORT_SYMBOL(video_usercopy);

自我感觉这个方法还是有很多精妙之处的,主要的控制过程是在我标注的地方调用完成的,这个调用之前做check动作,检查用户端发来的命令是否合法,

最重要的是把用户端的数据copy到kernel 端;而这个调用之后,则是我们处理完我们的动作之后,我们在这里吧用户端请求的数据从kernel 端copy到用户端

这样做的好处是显而易见的,任务明确,控制只做控制,用户空间和kernel空间数据的copy在所有控制之前,控制之后进行

以上动作做完之后,进入庞大的控制中枢,这来开始至贴出具体到某一个控制的代码,否则code过大,不易分析:

  1. case VIDIOC_QUERYCAP://查询视频设备的功能
  2. {
  3. struct v4l2_capability *cap
    = (struct v4l2_capability
    *)arg;
  4. if (!ops->vidioc_querycap)
  5. break;
  6. ret = ops->vidioc_querycap(file,
    fh, cap);
  7. if (!ret)/* i don‘t
    think here need to check
    */
  8. dbgarg(cmd,
    "driver=%s, card=%s, bus=%s, "
  9. "version=0x%08x, "
  10. "capabilities=0x%08x\n",
  11. cap->driver, cap->card,
    cap->bus_info,
  12. cap->version,
  13. cap->capabilities);
  14. break;
  15. }

这来调用了我们自己驱动中填充的v4l2_ioctl_ops结构体,从这里开始,我上面说到的话得到了验证,这就是linux 中API 的强大之处

作为中间层的这个控制中枢又回调驱动自己定义编写的控制

  1. /*
    ------------------------------------------------------------------
  2. IOCTL vidioc handling
  3. ------------------------------------------------------------------*/
  4. static int vidioc_querycap(struct file
    *file, void
    *priv,
  5. struct v4l2_capability *cap)
  6. {
  7. struct vivi_dev *dev
    = video_drvdata(file);
  8. strcpy(cap->driver,
    "vivi");
  9. strcpy(cap->card,
    "vivi");
  10. strlcpy(cap->bus_info, dev->v4l2_dev.name,
    sizeof(cap->bus_info));
  11. cap->version
    = VIVI_VERSION;
  12. cap->capabilities
    = V4L2_CAP_VIDEO_CAPTURE
    | V4L2_CAP_STREAMING |
    \
  13. V4L2_CAP_READWRITE;
  14. return 0;
  15. }

这来做的事情很简单,只是将配置信息保存到cap这个变量中,之后上传给用户空间

STEP 3:

/* 3. VIDIOC_CROPCAP查询驱动的修剪能力*/

/* 这里在vivi 驱动中我们没有实现此方法,即不支持此操作*/

if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap))

这个判断在中间层控制中枢中进行的,check到我们自己的驱动中没有这个控制功能的支持

所以这里的STEP 4同样不会进行

STEP 5:

/* 5. VIDIOC_S_FMT设置当前驱动的频捕获格式*/

if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))

对应到控制中心是这样的

  1. case VIDIOC_S_FMT:
  2. {
  3. struct v4l2_format *f
    = (struct v4l2_format
    *)arg;
  4. /* FIXME: Should be one dump per type
    */
  5. dbgarg(cmd,
    "type=%s\n", prt_names(f->type, v4l2_type_names));
  6. switch (f->type)
    {
  7. case V4L2_BUF_TYPE_VIDEO_CAPTURE:
  8. CLEAR_AFTER_FIELD(f, fmt.pix);
  9. v4l_print_pix_fmt(vfd,
    &f->fmt.pix);
  10. if
    (ops->vidioc_s_fmt_vid_cap)
    {
  11. ret = ops->vidioc_s_fmt_vid_cap(file, fh,
    f);
  12. }
    else if (ops->vidioc_s_fmt_vid_cap_mplane)
    {
  13. if
    (fmt_sp_to_mp(f,
    &f_copy))
  14. break;
  15. ret = ops->vidioc_s_fmt_vid_cap_mplane(file, fh,
  16. &f_copy);
  17. if
    (ret)
  18. break;
  19. if
    (f_copy.fmt.pix_mp.num_planes
    > 1)
    {
  20. /* Drivers shouldn‘t adjust from 1-plane
  21. *
    to more than 1-plane formats
    */
  22. ret =
    -EBUSY;
  23. WARN_ON(1);
  24. break;
  25. }
  26. ret = fmt_mp_to_sp(&f_copy, f);
  27. }
  28. break;
  29. case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
  30. CLEAR_AFTER_FIELD(f, fmt.pix_mp);
  31. v4l_print_pix_fmt_mplane(vfd,
    &f->fmt.pix_mp);
  32. if
    (ops->vidioc_s_fmt_vid_cap_mplane)
    {
  33. ret = ops->vidioc_s_fmt_vid_cap_mplane(file,
  34. fh, f);
  35. }
    else if (ops->vidioc_s_fmt_vid_cap
    &&
  36. f->fmt.pix_mp.num_planes
    == 1)
    {
  37. if
    (fmt_mp_to_sp(f,
    &f_copy))
  38. break;
  39. ret = ops->vidioc_s_fmt_vid_cap(file,
  40. fh,
    &f_copy);
  41. if
    (ret)
  42. break;
  43. ret = fmt_sp_to_mp(&f_copy, f);
  44. }
  45. break;
  46. case V4L2_BUF_TYPE_VIDEO_OVERLAY:
  47. CLEAR_AFTER_FIELD(f, fmt.win);
  48. if
    (ops->vidioc_s_fmt_vid_overlay)
  49. ret = ops->vidioc_s_fmt_vid_overlay(file,
  50. fh, f);
  51. break;
  52. case V4L2_BUF_TYPE_VIDEO_OUTPUT:
  53. CLEAR_AFTER_FIELD(f, fmt.pix);
  54. v4l_print_pix_fmt(vfd,
    &f->fmt.pix);
  55. if
    (ops->vidioc_s_fmt_vid_out)
    {
  56. ret = ops->vidioc_s_fmt_vid_out(file, fh,
    f);
  57. }
    else if (ops->vidioc_s_fmt_vid_out_mplane)
    {
  58. if
    (fmt_sp_to_mp(f,
    &f_copy))
  59. break;
  60. ret = ops->vidioc_s_fmt_vid_out_mplane(file, fh,
  61. &f_copy);
  62. if
    (ret)
  63. break;
  64. if
    (f_copy.fmt.pix_mp.num_planes
    > 1)
    {
  65. /* Drivers shouldn‘t adjust from 1-plane
  66. *
    to more than 1-plane formats
    */
  67. ret =
    -EBUSY;
  68. WARN_ON(1);
  69. break;
  70. }
  71. ret = fmt_mp_to_sp(&f_copy, f);
  72. }
  73. break;
  74. case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
  75. CLEAR_AFTER_FIELD(f, fmt.pix_mp);
  76. v4l_print_pix_fmt_mplane(vfd,
    &f->fmt.pix_mp);
  77. if
    (ops->vidioc_s_fmt_vid_out_mplane)
    {
  78. ret = ops->vidioc_s_fmt_vid_out_mplane(file,
  79. fh, f);
  80. }
    else if (ops->vidioc_s_fmt_vid_out
    &&
  81. f->fmt.pix_mp.num_planes
    == 1)
    {
  82. if
    (fmt_mp_to_sp(f,
    &f_copy))
  83. break;
  84. ret = ops->vidioc_s_fmt_vid_out(file,
  85. fh,
    &f_copy);
  86. if
    (ret)
  87. break;
  88. ret = fmt_mp_to_sp(&f_copy, f);
  89. }
  90. break;
  91. case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
  92. CLEAR_AFTER_FIELD(f, fmt.win);
  93. if
    (ops->vidioc_s_fmt_vid_out_overlay)
  94. ret = ops->vidioc_s_fmt_vid_out_overlay(file,
  95. fh, f);
  96. break;
  97. case V4L2_BUF_TYPE_VBI_CAPTURE:
  98. CLEAR_AFTER_FIELD(f, fmt.vbi);
  99. if
    (ops->vidioc_s_fmt_vbi_cap)
  100. ret = ops->vidioc_s_fmt_vbi_cap(file, fh,
    f);
  101. break;
  102. case V4L2_BUF_TYPE_VBI_OUTPUT:
  103. CLEAR_AFTER_FIELD(f, fmt.vbi);
  104. if
    (ops->vidioc_s_fmt_vbi_out)
  105. ret = ops->vidioc_s_fmt_vbi_out(file, fh,
    f);
  106. break;
  107. case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
  108. CLEAR_AFTER_FIELD(f, fmt.sliced);
  109. if
    (ops->vidioc_s_fmt_sliced_vbi_cap)
  110. ret = ops->vidioc_s_fmt_sliced_vbi_cap(file,
  111. fh, f);
  112. break;
  113. case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
  114. CLEAR_AFTER_FIELD(f, fmt.sliced);
  115. if
    (ops->vidioc_s_fmt_sliced_vbi_out)
  116. ret = ops->vidioc_s_fmt_sliced_vbi_out(file,
  117. fh, f);
  118. break;
  119. case V4L2_BUF_TYPE_PRIVATE:
  120. /* CLEAR_AFTER_FIELD(f, fmt.raw_data);
    <- does
    nothing */
  121. if
    (ops->vidioc_s_fmt_type_private)
  122. ret = ops->vidioc_s_fmt_type_private(file,
  123. fh, f);
  124. break;
  125. }
  126. break;
  127. }

以后根据不同的type 决定了我们自己驱动程序中不同的控制实现,这个type是根据用户空间的设置而定的,还包括其他几个参数,如下:

  1. fmt.type
    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  2. fmt.fmt.pix.width
    = 640;
  3. fmt.fmt.pix.height
    = 480;
  4. fmt.fmt.pix.pixelformat
    = V4L2_PIX_FMT_YUYV;
  5. fmt.fmt.pix.field
    = V4L2_FIELD_INTERLACED;

这里根据设定的type,所以驱动程序的处理过程如下:

  1. static int vidioc_s_fmt_vid_cap(struct file
    *file, void
    *priv,
  2. struct v4l2_format *f)
  3. {
  4. struct vivi_dev *dev
    = video_drvdata(file);
  5. struct vb2_queue *q
    = &dev->vb_vidq;
  6. ....在下面这个函数中,做了一些试探性的动作,如果试探失败则下面不会赋值,试探通过则后续正常设置即可,在这个试探函数中同时做了一些设置动作
  7. int ret = vidioc_try_fmt_vid_cap(file, priv, f);
  8. if (ret
    < 0)
  9. return ret;
  10. if (vb2_is_streaming(q))
    {
  11. dprintk(dev, 1,
    "%s device busy\n", __func__);
  12. return -EBUSY;
  13. }
  14. ....按用户空间需求设置
  15. dev->fmt
    = get_format(f);
  16. dev->width
    = f->fmt.pix.width;
  17. dev->height
    = f->fmt.pix.height;
  18. dev->field
    = f->fmt.pix.field;
  19. return 0;
  20. }

STEP6 :

/* 6. VIDIOC_REQBUFS分配内存*/

if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req))

中间层控制中枢:

  1. case VIDIOC_REQBUFS:
  2. {
  3. struct v4l2_requestbuffers *p
    = arg;
  4. if (!ops->vidioc_reqbufs)
  5. break;
  6. ........这个方法check 驱动必须实现了fmt方法,看具体看代码
  7. ret = check_fmt(ops, p->type);
  8. if (ret)
  9. break;
  10. if (p->type
    < V4L2_BUF_TYPE_PRIVATE)
  11. CLEAR_AFTER_FIELD(p, memory);
  12. ret = ops->vidioc_reqbufs(file,
    fh, p);
  13. dbgarg(cmd,
    "count=%d, type=%s, memory=%s\n",
  14. p->count,
  15. prt_names(p->type, v4l2_type_names),
  16. prt_names(p->memory, v4l2_memory_names));
  17. break;
  18. }

驱动中实现:

  1. static int vidioc_reqbufs(struct file
    *file, void
    *priv,
  2. struct v4l2_requestbuffers *p)
  3. {
  4. struct vivi_dev *dev
    = video_drvdata(file);
  5. return vb2_reqbufs(&dev->vb_vidq,
    p);
  6. }

到了这里来到了这个全新的话题,实现

vb2_reqbufs(&dev->vb_vidq, p);

这里暂且不讨论这个方法,相对较复杂,待日后研究,先把注释部分放到这里,包括其他内存操作,之后深入研究补充,专门作为一篇整理

/**

* Should be called from vidioc_reqbufs ioctl handler of a driver.

* This function:

* 1) verifies streaming parameters passed from the userspace,

* 2) sets up the queue,

* 3) negotiates number of buffers and planes per buffer with the driver to be used during streaming,

* 4) allocates internal buffer structures (struct vb2_buffer), according to the agreed parameters,

* 5) for MMAP memory type, allocates actual video memory, using the memory handling/allocation routines provided during queue initialization

* If req->count is 0, all the memory will be freed instead.

* If the queue has been allocated previously (by a previous vb2_reqbufs) call

* and the queue is not busy, memory will be reallocated.

* The return values from this function are intended to be directly returned from vidioc_reqbufs handler in driver.

*/

STEP 7:

/* 7. VIDIOC_QUERYBUF把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址*/

if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))

中间层控制中枢:

  1. case VIDIOC_QUERYBUF:
  2. {
  3. struct v4l2_buffer *p
    = arg;
  4. if (!ops->vidioc_querybuf)
  5. break;
  6. ret = check_fmt(ops, p->type);
  7. if (ret)
  8. break;
  9. ret = ops->vidioc_querybuf(file, fh,
    p);
  10. if (!ret)
  11. dbgbuf(cmd, vfd, p);
  12. break;
  13. }

驱动中控制实现:

  1. static int vidioc_querybuf(struct file
    *file, void
    *priv, struct v4l2_buffer
    *p)
  2. {
  3. struct vivi_dev *dev
    = video_drvdata(file);
  4. return vb2_querybuf(&dev->vb_vidq,
    p);
  5. }

/**

* Should be called from vidioc_querybuf ioctl handler in driver.

* This function will verify the passed v4l2_buffer structure and fill the

* relevant information for the userspace.

* The return values from this function are intended to be directly returned from vidioc_querybuf handler in driver.

*/

STEP 8:

/* 8. VIDIOC_QBUF把数据从缓存中读取出来*/

if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))

中间层控制中枢:

  1. case VIDIOC_QBUF:
  2. {
  3. struct v4l2_buffer *p
    = arg;
  4. if (!ops->vidioc_qbuf)
  5. break;
  6. ret = check_fmt(ops, p->type);
  7. if (ret)
  8. break;
  9. ret = ops->vidioc_qbuf(file, fh, p);
  10. if (!ret)
  11. dbgbuf(cmd, vfd, p);
  12. break;
  13. }

驱动中控制实现:

  1. static int vidioc_qbuf(struct file
    *file, void
    *priv, struct v4l2_buffer
    *p)
  2. {
  3. struct vivi_dev *dev
    = video_drvdata(file);
  4. return vb2_qbuf(&dev->vb_vidq,
    p);
  5. }

/**

* Should be called from vidioc_qbuf ioctl handler of a driver.

* This function:

* 1) verifies the passed buffer,

* 2) calls buf_prepare callback in the driver (if provided), in which driver-specific buffer initialization can be performed,

* 3) if streaming is on, queues the buffer in driver by the means of buf_queue callback for processing.

* The return values from this function are intended to be directly returned from vidioc_qbuf handler in driver.

*/

STEP 9:

/* 9. VIDIOC_STREAMON开始视频显示函数*/

if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))

中间层控制中枢:

  1. case VIDIOC_STREAMON:
  2. {
  3. enum v4l2_buf_type i =
    *(int
    *)arg;
  4. if (!ops->vidioc_streamon)
  5. break;
  6. dbgarg(cmd,
    "type=%s\n", prt_names(i, v4l2_type_names));
  7. ret = ops->vidioc_streamon(file,
    fh, i);
  8. break;
  9. }

驱动控制实现;

  1. static int vidioc_streamon(struct file
    *file, void
    *priv, enum v4l2_buf_type i)
  2. {
  3. struct vivi_dev *dev
    = video_drvdata(file);
  4. return vb2_streamon(&dev->vb_vidq,
    i);
  5. }

/**

* Should be called from vidioc_streamon handler of a driver.

* This function:

* 1) verifies current state

* 2) starts streaming and passes any previously queued buffers to the driver

* The return values from this function are intended to be directly returned from vidioc_streamon handler in the driver.

*/

STEP 10:

/* 10. poll method*/

select (fd + 1, &fds, NULL, NULL, &tv);

从V4L2驱动API开始:

  1. static unsigned int v4l2_poll(struct file
    *filp, struct poll_table_struct
    *poll)
  2. {
  3. struct video_device *vdev
    = video_devdata(filp);
  4. int ret = POLLERR
    | POLLHUP;
  5. if (!vdev->fops->poll)
  6. return DEFAULT_POLLMASK;
  7. if (vdev->lock)
  8. mutex_lock(vdev->lock);
  9. if (video_is_registered(vdev))
  10. ret = vdev->fops->poll(filp,
    poll);
  11. if (vdev->lock)
  12. mutex_unlock(vdev->lock);
  13. return ret;
  14. }

驱动实现:

  1. static unsigned int
  2. vivi_poll(struct file
    *file, struct poll_table_struct
    *wait)
  3. {
  4. struct vivi_dev *dev
    = video_drvdata(file);
  5. struct vb2_queue *q
    = &dev->vb_vidq;
  6. dprintk(dev, 1,
    "%s\n", __func__);
  7. return vb2_poll(q,
    file, wait);
  8. }

/**

* This function implements poll file operation handler for a driver.

* For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will be informed that the file descriptor of a video device is available for reading.

* For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor will be reported as available for writing.

* The return values from this function are intended to be directly returned from poll handler in driver.

*/

STEP 11:

/* 11. VIDIOC_DQBUF把数据放回缓存队列*/

if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf))

中间层控制中枢:

  1. case VIDIOC_DQBUF:
  2. {
  3. struct v4l2_buffer *p
    = arg;
  4. if (!ops->vidioc_dqbuf)
  5. break;
  6. ret = check_fmt(ops, p->type);
  7. if (ret)
  8. break;
  9. ret = ops->vidioc_dqbuf(file,
    fh, p);
  10. if (!ret)
  11. dbgbuf(cmd, vfd, p);
  12. break;
  13. }

驱动控制实现:

  1. static int vidioc_dqbuf(struct file
    *file, void
    *priv, struct v4l2_buffer
    *p)
  2. {
  3. struct vivi_dev *dev
    = video_drvdata(file);
  4. return vb2_dqbuf(&dev->vb_vidq,
    p, file->f_flags
    & O_NONBLOCK);
  5. }

/**

* Should be called from vidioc_dqbuf ioctl handler of a driver.

* This function:

* 1) verifies the passed buffer,

* 2) calls buf_finish callback in the driver (if provided), in which driver can perform any additional operations that may be required before returning the buffer to userspace, such as cache sync,

* 3) the buffer struct members are filled with relevant information for the userspace.

* The return values from this function are intended to be directly returned from vidioc_dqbuf handler in driver.

*/

STEP 12:

/*12. VIDIOC_QBUF把数据从缓存中读取出来*/

if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))

中间层控制中枢:

  1. case VIDIOC_QBUF:
  2. {
  3. struct v4l2_buffer *p
    = arg;
  4. if (!ops->vidioc_qbuf)
  5. break;
  6. ret = check_fmt(ops, p->type);
  7. if (ret)
  8. break;
  9. ret = ops->vidioc_qbuf(file,
    fh, p);
  10. if (!ret)
  11. dbgbuf(cmd, vfd, p);
  12. break;
  13. }

驱动控制实现:

  1. static int vidioc_qbuf(struct file
    *file, void
    *priv, struct v4l2_buffer
    *p)
  2. {
  3. struct vivi_dev *dev
    = video_drvdata(file);
  4. return vb2_qbuf(&dev->vb_vidq,
    p);
  5. }

STEP 13:

/*13. VIDIOC_STREAMOFF结束视频显示函数*/

if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))

中间层控制中枢:

  1. case VIDIOC_STREAMOFF:
  2. {
  3. enum v4l2_buf_type i =
    *(int
    *)arg;
  4. if (!ops->vidioc_streamoff)
  5. break;
  6. dbgarg(cmd,
    "type=%s\n", prt_names(i, v4l2_type_names));
  7. ret = ops->vidioc_streamoff(file,
    fh, i);
  8. break;
  9. }

驱动控制实现:

  1. static int vidioc_streamoff(struct file
    *file, void
    *priv, enum v4l2_buf_type i)
  2. {
  3. struct vivi_dev *dev
    = video_drvdata(file);
  4. return vb2_streamoff(&dev->vb_vidq, i);
  5. }

STEP 13:

/*13. VIDIOC_STREAMOFF结束视频显示函数*/

if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))

中间层控制中枢:

  1. case VIDIOC_STREAMOFF:
  2. {
  3. enum v4l2_buf_type i =
    *(int
    *)arg;
  4. if (!ops->vidioc_streamoff)
  5. break;
  6. dbgarg(cmd,
    "type=%s\n", prt_names(i, v4l2_type_names));
  7. ret = ops->vidioc_streamoff(file, fh,
    i);
  8. break;
  9. }

驱动控制实现:

  1. static int vidioc_streamoff(struct file
    *file, void
    *priv, enum v4l2_buf_type i)
  2. {
  3. struct vivi_dev *dev
    = video_drvdata(file);
  4. return vb2_streamoff(&dev->vb_vidq, i);
  5. }

STEP 14:

/*14. close method*/

close(fbfd);

  1. static int v4l2_release(struct inode
    *inode, struct file
    *filp)
  2. {
  3. struct video_device *vdev
    = video_devdata(filp);
  4. int ret = 0;
  5. if (vdev->fops->release)
    {
  6. if (vdev->lock)
  7. mutex_lock(vdev->lock);
  8. vdev->fops->release(filp);
  9. if (vdev->lock)
  10. mutex_unlock(vdev->lock);
  11. }
  12. /* decrease the refcount unconditionally since the release()
  13. return value is ignored.
    */
  14. video_put(vdev);
  15. return ret;
  16. }
  1. static int vivi_close(struct file
    *file)
  2. {
  3. struct video_device *vdev
    = video_devdata(file);
  4. struct vivi_dev *dev
    = video_drvdata(file);
  5. dprintk(dev, 1,
    "close called (dev=%s), file %p\n",
  6. video_device_node_name(vdev), file);
  7. if (v4l2_fh_is_singular_file(file))
  8. vb2_queue_release(&dev->vb_vidq);
  9. return v4l2_fh_release(file);
  10. }

到此为止,整个过程算是基本完结了,不过其中videobuf2_core.c 在我看来自己必须专门钻研一下了

videobuf2_core.c 是视频数据传输的核心

也可以说是视频驱动的重中之重。。。。

时间: 2024-07-28 15:42:45

V4L2用户空间和kernel层driver的交互过程的相关文章

AndroidM 内核空间到用户空间接口类型

Android系统中, 驱动程序因商业需求分为运行在用户空间的hardware层以及运行在内核空间的驱动程序, 大多情况下内核驱动都需要提供用户空间访问的接口. Linux内核空间到用户空间的接口有主要有以下几种类型1.系统调用    系统调用是指系统实现的所有系统调用所构成的集合,即程序接口.    linux系统调用分为:进程控制,文件系统控制,系统控制,内存管理,网络管理,用户管理,进程管理等类型    linux操作系统中,系统调用的ID通常在arch/{体系结构}/include/as

Android Camera 通过V4L2与kernel driver的完整交互过程

http://blog.chinaunix.net/uid-26215986-id-3552456.html 原文地址:Android Camera 通过V4L2与kernel driver的完整交互过程 作者:xinyuwuxian 之前在 Android Camera 的执行流程 http://blog.chinaunix.net/uid-26765074-id-3499537.html 这篇文章中已经详细介绍了Android Camera app到调用open打开camera 设备的完成过

Linux内核工程导论——用户空间设备管理

用户空间设备管理 用户空间所能见到的所有设备都放在/dev目录下(当然,只是一个目录,是可以变化的),文件系统所在的分区被当成一个单独的设备也放在该目录下.以前的2.4版本的曾经出现过devfs,这个思路非常好,在内核态实现对磁盘设备的动态管理.可以做到当用户访问一个设备的设备的时候,devfs驱动才会去加载该设备的驱动.甚至每个节点的设备号都是动态获得的.但是该机制的作者不再维护他的代码,linux成员经过讨论,使用用户态的udev代替内核态的devfs,所以现在的devfs已经废弃了.用户态

Linux usb子系统(三):通过usbfs操作设备的用户空间驱动

内核中提供了USB设备文件系统(usbdevfs,Linux 2.6改为usbfs,即USB文件系统),它和/proc类似,都是动态产生的.通过在/etc/fstab文件中添加如下一行:none /proc/bus/usb usbfs defaults或者输入命令:mount -t usbfs none /proc/bus/usb可以实现USB设备文件系统的挂载. 一个典型的/proc/bus/usb/devices文件的结构如下(运行的arm Linux 2.6.37内核上的机器上插入了一个u

Operating System-Thread(3)用户空间和内核空间实现线程

本文主要内容: 操作系统用户空间和内核空间简介 在用户空间实现线程 在内核空间实现线程 用户空间和内核空间线程混合使用 一.用户空间和内核空间简介 用户空间:User space,内核空间:Kernel Space.这两个是操作系统的重要概念之一,今天为了线程做一下简单的介绍: 内核空间用于运行操作系统核心组件,比如内存管理组件,IO交互组件,文件管理.中断管理组件等,同时驱动程序(Driver)也运行在内核空间. 用户空间,用于运行普通应用程序. 示意图: 二,在用户空间实现线程 用户空间的线

ubuntu+systemtap进行Linux内核和用户空间开发测试

ubuntu+systemtap进行Linux内核和用户空间开发测试 Sailor_forever  sailing_9806#163.com (本原创文章发表于Sailor_forever 的个人blog,未经本人许可,不得用于商业用途.任何个人.媒体.其他网站不得私自抄袭:网络媒体转载请注明出处,增加原文链接,否则属于侵权行为.如有任何问题,请留言或者发邮件给sailing_9806#163.com) [摘要]本文主要介绍在ubuntu平台 + 自定义内核上如何安装systemtap工具包及

用户空间和内核空间通讯之【proc文件系统】

今天我们介绍另一种用户内核空间通信的方法:proc文件系统. proc文件系统作为linux提供的一种虚拟文件系统并不占用实际外围存储空间,它仅存在于内存中,系统断电即消失.proc文件系统最开始的设计主要是为满足内核向用户态进程报告其状态而设计,并没有为输入做规定和说明.随着发展,现在的proc文件系统已经演变成一个"用户-内核"空间半双工的通信方式了(虽然目前已经开始有点混乱了,但某些早期开发的软件代码中还在继续使用这个文件系统).用户不但可以从proc文件系统中读取内核的相关状态

用户空间和内核空间通讯之【Netlink 中】

原文地址:用户空间和内核空间通讯之[Netlink 中] 作者:wjlkoorey258 今天我们来动手演练一下Netlink的用法,看看它到底是如何实现用户-内核空间的数据通信的.我们依旧是在2.6.21的内核环境下进行开发. 在</usr/include/linux/netlink.h>文件里包含了Netlink协议簇已经定义好的一些预定义协议: 点击(此处)折叠或打开 #define NETLINK_ROUTE        0    /* Routing/device hook    

用户空间驱动

一个第一次涉及内核问题的 Unix 程序猿, 可能会紧张写一个模块. 编写一个用户程序来 直接读写设备port可能easy些. 确实, 有几个论据倾向于用户空间编程, 有时编写一个所谓的用户空间设备驱动对照钻研 内核是一个明智的选择. 在本节, 我们讨论几个理由, 为什么你可能在用户空间编写驱动. 本书是关于内核空间驱动的, 可是, 所以我们不超越这个介绍性的讨论. 用户空间驱动的优点在于: ? 完整的 C 库能够连接. 驱动能够进行很多奇怪的任务, 不用依靠外面的程序(实现 使用策略的工具程序