下面是Linux内核文档中的一篇---videobuf的翻译,只是大概意思的翻译,有不对的地方请指正。

欢迎转载,转载请注明出处。

An introduction to the videobuf layer

Jonathan Corbet <corbet@lwn.net>

Current as of 2.6.33

The videobuf layer functions as a sort of glue layer between a V4L2 driver

and user space.  It handles the allocation and management of buffers for

the storage of video frames.  There is a set of functions which can be used

to implement many of the standard POSIX I/O system calls, including read(),

poll(), and, happily, mmap().  Another set of functions can be used to

implement the bulk of the V4L2 ioctl() calls related to streaming I/O,

including buffer allocation, queueing and dequeueing, and streaming

control.  Using videobuf imposes a few design decisions on the driver

author, but the payback comes in the form of reduced code in the driver and

a consistent implementation of the V4L2 user-space API.

videobuf层位于V4L2驱动层和用户空间层之间,这个层分配和管理视频缓冲帧。

这个层包含了一组函数,这些函数可以用来实现标准POSIX I/O系统调用(包括read(),

poll()和另人喜爱的mmap()等)。同时这个层也包含一组可以用来实现V4L2 ioctl()调用的和

I/O相关的函数(包括缓冲区分配,入队列,出队列和流控制等)。虽然使用videobuf

让驱动作者增加一些设计,但是它会减少驱动的代码,而且可以提供一个统一的V4L2用户空间API

Buffer types

Not all video devices use the same kind of buffers.  In fact, there are (at

least) three common variations:

有三种类型的缓冲区:

 - Buffers which are scattered in both the physical and (kernel) virtual

   address spaces.  (Almost) all user-space buffers are like this, but it

   makes great sense to allocate kernel-space buffers this way as well when

   it is possible.  Unfortunately, it is not always possible; working with

   this kind of buffer normally requires hardware which can do

   scatter/gather DMA operations.

缓冲区一:在物理上和虚拟地址上都不连续的缓冲区。几乎所有用户空间

的缓冲区都是这种类型的,这对在内核中用同样的方式分配缓冲区(如果可能的话)

具有重要意义。不幸的是,这种分配方式需要硬件支持分散/聚集DMA操作。

 - Buffers which are physically scattered, but which are virtually

   contiguous; buffers allocated with vmalloc(), in other words.  These

   buffers are just as hard to use for DMA operations, but they can be

   useful in situations where DMA is not available but virtually-contiguous

   buffers are convenient.

缓冲区二:在物理上不连续,在虚拟地址上连续的缓冲区。这种缓冲区是

用vmalloc()函数分配的,这种缓冲区很难用于DMA操作,可以用在没有DMA

的场景中。

 - Buffers which are physically contiguous.  Allocation of this kind of

   buffer can be unreliable on fragmented systems, but simpler DMA

   controllers cannot deal with anything else.

缓冲区三:在物理上和虚拟地址上都连续的缓冲区。这种缓冲区的缺点是可能分配

不到内存,优点是使DMA操作很简单。

Videobuf can work with all three types of buffers, but the driver author

must pick one at the outset and design the driver around that decision.

videobuf对这三种缓冲区都支持,但是驱动作者需要在最开始的时候

选择使用某一种缓冲区,并且围绕这种缓冲区来设计驱动。

[It's worth noting that there's a fourth kind of buffer: "overlay" buffers

which are located within the system's video memory.  The overlay

functionality is considered to be deprecated for most use, but it still

shows up occasionally in system-on-chip drivers where the performance

benefits merit the use of this technique.  Overlay buffers can be handled

as a form of scattered buffer, but there are very few implementations in

the kernel and a description of this technique is currently beyond the

scope of this document.]

[这里还有一种不推荐使用的缓冲区:overlay buffer,这是在显存中的缓冲区]

Data structures, callbacks, and initialization

数据结构,回调函数和初始化

Depending on which type of buffers are being used, the driver should

include one of the following files:

根据所选择的缓冲区类型,驱动需要包含下面头文件的某一个:

    <media/videobuf-dma-sg.h> /* Physically scattered */

    <media/videobuf-vmalloc.h> /* vmalloc() buffers */

    <media/videobuf-dma-contig.h> /* Physically contiguous */

The driver's data structure describing a V4L2 device should include a

struct videobuf_queue instance for the management of the buffer queue,

along with a list_head for the queue of available buffers.  There will also

need to be an interrupt-safe spinlock which is used to protect (at least)

the queue.

描述V4L2设备的驱动数据结构体应该包含一个videobuf_queue实例,

用于管理buffer队列,同时也应该包含一个有效缓冲区的队列和一个

管理队列共享的spinlock.

The next step is to write four simple callbacks to help videobuf deal with

the management of buffers:

接下来的步骤是填写四个接口,帮助videobuf管理缓冲区。

    struct videobuf_queue_ops {

int (*buf_setup)(struct videobuf_queue *q,

 unsigned int *count, unsigned int *size);

int (*buf_prepare)(struct videobuf_queue *q,

   struct videobuf_buffer *vb,

   enum v4l2_field field);

void (*buf_queue)(struct videobuf_queue *q,

  struct videobuf_buffer *vb);

void (*buf_release)(struct videobuf_queue *q,

    struct videobuf_buffer *vb);

    };

其中:

REQBUF---->videobuf_reqbufs--->buf_setup

QBUF---->videobuf_qbuf--->buf_prepare

STREAMON---->videobuf_streamon--->buf_queue

STREAMOFF---->videobuf_streamoff---->__videobuf_streamoff---->videobuf_queue_cancel---->buf_release

buf_setup() is called early in the I/O process, when streaming is being

initiated; its purpose is to tell videobuf about the I/O stream.  The count

parameter will be a suggested number of buffers to use; the driver should

check it for rationality and adjust it if need be.  As a practical rule, a

minimum of two buffers are needed for proper streaming, and there is

usually a maximum (which cannot exceed 32) which makes sense for each

device.  The size parameter should be set to the expected (maximum) size

for each frame of data.

buf_setup()这个函数在初始化流的时候调用,目的是告诉videobuf关于

I/O流的一些事情。参数count是缓冲区的建议数量,驱动应该检查它并

且根据需要调整它。根据实际情况,最少应该设置2个缓冲区,最大不能

超过32个。参数size 是每个视频帧的数据的大小。

Each buffer (in the form of a struct videobuf_buffer pointer) will be

passed to buf_prepare(), which should set the buffer's size, width, height,

and field fields properly.  If the buffer's state field is

VIDEOBUF_NEEDS_INIT, the driver should pass it to:

每个buffer都会传给buf_prepare()函数处理,在这个函数中,驱动需要

设置好buffer的宽,高和其它参数,如果bufferstateVIDEOBUF_NEEDS_INIT,

驱动需要将buffer传给下面这个函数:

    int videobuf_iolock(struct videobuf_queue* q, struct videobuf_buffer *vb,

struct v4l2_framebuffer *fbuf);

Among other things, this call will usually allocate memory for the buffer.

Finally, the buf_prepare() function should set the buffer's state to

VIDEOBUF_PREPARED.

这个函数通常为buffer分配内存,在调用完这个函数后,buf_prepare()

需要设置stateVIDEOBUF_PREPARED。

When a buffer is queued for I/O, it is passed to buf_queue(), which should

put it onto the driver's list of available buffers and set its state to

VIDEOBUF_QUEUED.  Note that this function is called with the queue spinlock

held; if it tries to acquire it as well things will come to a screeching

halt.  Yes, this is the voice of experience.  Note also that videobuf may

wait on the first buffer in the queue; placing other buffers in front of it

could again gum up the works.  So use list_add_tail() to enqueue buffers.

当一个buffer进入队列(API发送QBUF命令),videobuf会调用buf_queue函数,

这个函数需要将buffer放到驱动的buffer列表中,并将这个buffer状态设置为

VIDEOBUF_QUEUED。 注意这个函数是在持有queue spinlock下被调用的,

所以不能在这个函数中再获取这个锁,如果再获取事情就简单地完了。另外注意

videobuf在第一个buffer等待,所以如果把其他buffer放到最前面会把事情弄糟,

所以在加入队列时,应当调用 list_add_tail() 把它放到最后。

Finally, buf_release() is called when a buffer is no longer intended to be

used.  The driver should ensure that there is no I/O active on the buffer,

then pass it to the appropriate free routine(s):

最后,当一个buffer不再使用的时候,videobuf会调用buf_release(),

驱动应当保证在这个buffer上没有进行IO动,然后调用下面函数中

的某一个释放掉它:

    /* Scatter/gather drivers */

    int videobuf_dma_unmap(struct videobuf_queue *q,

   struct videobuf_dmabuf *dma);

    int videobuf_dma_free(struct videobuf_dmabuf *dma);

    /* vmalloc drivers */

    void videobuf_vmalloc_free (struct videobuf_buffer *buf);

    /* Contiguous drivers */

    void videobuf_dma_contig_free(struct videobuf_queue *q,

  struct videobuf_buffer *buf);

One way to ensure that a buffer is no longer under I/O is to pass it to:

保证某个buffer没有IO在活动的方法是调用:

    int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr);

Here, vb is the buffer, non_blocking indicates whether non-blocking I/O

should be used (it should be zero in the buf_release() case), and intr

controls whether an interruptible wait is used.

这里,vbbuffernon_blocking是指是否使用non-blocking I/O(

在release中要被设置为0),intr控制是否使用中断等待。

File operations

At this point, much of the work is done; much of the rest is slipping

videobuf calls into the implementation of the other driver callbacks.  The

first step is in the open() function, which must initialize the

videobuf queue.  The function to use depends on the type of buffer used:

文件操作:

到现在,很多的工作已经做完了,剩下的只需要在驱动其他函数中调用

videobuf的函数。首先需要在open()函数中调用初始化videobuf queue

函数,根据选定的buffer类型,调用下面函数中的某一个:

    void videobuf_queue_sg_init(struct videobuf_queue *q,

struct videobuf_queue_ops *ops,

struct device *dev,

spinlock_t *irqlock,

enum v4l2_buf_type type,

enum v4l2_field field,

unsigned int msize,

void *priv);

    void videobuf_queue_vmalloc_init(struct videobuf_queue *q,

struct videobuf_queue_ops *ops,

struct device *dev,

spinlock_t *irqlock,

enum v4l2_buf_type type,

enum v4l2_field field,

unsigned int msize,

void *priv);

    void videobuf_queue_dma_contig_init(struct videobuf_queue *q,

       struct videobuf_queue_ops *ops,

       struct device *dev,

       spinlock_t *irqlock,

       enum v4l2_buf_type type,

       enum v4l2_field field,

       unsigned int msize,

       void *priv);

In each case, the parameters are the same: q is the queue structure for the

device, ops is the set of callbacks as described above, dev is the device

structure for this video device, irqlock is an interrupt-safe spinlock to

protect access to the data structures, type is the buffer type used by the

device (cameras will use V4L2_BUF_TYPE_VIDEO_CAPTURE, for example), field

describes which field is being captured (often V4L2_FIELD_NONE for

progressive devices), msize is the size of any containing structure used

around struct videobuf_buffer, and priv is a private data pointer which

shows up in the priv_data field of struct videobuf_queue.  Note that these

are void functions which, evidently, are immune to failure.

这几个函数的参数都是一样的:

q:队列结构体;

ops:是一组videobuf的回调函数(buf_setup等);

dev:是视频设备的device结构体(想想总线-设备-驱动模型);

irqlock: 是中断安全的访问数据结构体的自旋锁;

type: buffer类型(V4L2_BUF_TYPE_VIDEO_CAPTURE等);

field: 场模式(通常使用V4L2_FIELD_NONE);

msize:包含videobuf_buffer结构体的结构体的大小;

priv: 传给videobuf_queue->priv_data

这些函数都是void类型,是不会失败的。

V4L2 capture drivers can be written to support either of two APIs: the

read() system call and the rather more complicated streaming mechanism.  As

a general rule, it is necessary to support both to ensure that all

applications have a chance of working with the device.  Videobuf makes it

easy to do that with the same code.  To implement read(), the driver need

only make a call to one of:

V4L2 捕捉驱动能够被设计得支持两种API:使用read()系统调用或者

更加复杂的流机制。根据通用的规则两种方式都应该被驱动支持,videobuf

可以很容易地支持这一点。可以调用下面的函数中的某一个来支持read函数:

    ssize_t videobuf_read_one(struct videobuf_queue *q,

      char __user *data, size_t count,

      loff_t *ppos, int nonblocking);

    ssize_t videobuf_read_stream(struct videobuf_queue *q,

 char __user *data, size_t count,

 loff_t *ppos, int vbihack, int nonblocking);

Either one of these functions will read frame data into data, returning the

amount actually read; the difference is that videobuf_read_one() will only

read a single frame, while videobuf_read_stream() will read multiple frames

if they are needed to satisfy the count requested by the application.  A

typical driver read() implementation will start the capture engine, call

one of the above functions, then stop the engine before returning (though a

smarter implementation might leave the engine running for a little while in

anticipation of another read() call happening in the near future).

这些函数会读取帧数据到data指向的空间,videobuf_read_one只读取一帧,

videobuf_read_stream读取若干帧。一个典型的驱动read()实现是开启捕捉引擎,

然后调用上面这两个函数中的一个,然后在返回前停止捕捉引擎(但是一个更

智能的实现是继续让捕捉引擎运行一会,因为可能在不远处就有下一次read被调用)。

The poll() function can usually be implemented with a direct call to:

poll()函数可以直接使用下面的函数实现:

    unsigned int videobuf_poll_stream(struct file *file,

      struct videobuf_queue *q,

      poll_table *wait);

Note that the actual wait queue eventually used will be the one associated

with the first available buffer.

注意实际的等待队列最终会和第一个有效缓冲区联系起来。

When streaming I/O is done to kernel-space buffers, the driver must support

the mmap() system call to enable user space to access the data.  In many

V4L2 drivers, the often-complex mmap() implementation simplifies to a

single call to:

当流I/O使用的是内核分配缓冲区,那么驱动需要实现mmap()函数,

可以直接使用下面这个函数:

    int videobuf_mmap_mapper(struct videobuf_queue *q,

     struct vm_area_struct *vma);

Everything else is handled by the videobuf code.

自从使用videobuf的代码后,一切都变得简单了。

The release() function requires two separate videobuf calls:

release()函数需要调用下面两个函数:

    void videobuf_stop(struct videobuf_queue *q);

    int videobuf_mmap_free(struct videobuf_queue *q);

The call to videobuf_stop() terminates any I/O in progress - though it is

still up to the driver to stop the capture engine.  The call to

videobuf_mmap_free() will ensure that all buffers have been unmapped; if

so, they will all be passed to the buf_release() callback.  If buffers

remain mapped, videobuf_mmap_free() returns an error code instead.  The

purpose is clearly to cause the closing of the file descriptor to fail if

buffers are still mapped, but every driver in the 2.6.32 kernel cheerfully

ignores its return value.

videobuf_stop()结束所有的正在进行的IO操作(实际上依赖驱动停止

捕捉引擎),videobuf_mmap_free() 能够确保所有缓冲区都被unmap了,

如果有缓冲区没有被unmap,这个函数会返回错误,否则这个函数会

调用到buf_release()释放掉缓冲区。这样做是因为早期的想法是在关闭时

,如果缓冲区还在映射就会引发关闭文件时失败,但是真实的情况是,

2.6.32内核中驱动很开心地忽略了它的返回值 。

ioctl() operations

ioctl()操作

The V4L2 API includes a very long list of driver callbacks to respond to

the many ioctl() commands made available to user space.  A number of these

- those associated with streaming I/O - turn almost directly into videobuf

calls.  The relevant helper functions are:

V4L2 API包括一个很长的驱动函数列表,用来响应许多的来自用户空间的

ioctl命令,很多命令(关于流IO的)直接在videobuf中实现并返回了。

重要的函数如下:

    int videobuf_reqbufs(struct videobuf_queue *q,

 struct v4l2_requestbuffers *req);

    int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b);

    int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b);

    int videobuf_dqbuf(struct videobuf_queue *q, struct v4l2_buffer *b,

       int nonblocking);

    int videobuf_streamon(struct videobuf_queue *q);

    int videobuf_streamoff(struct videobuf_queue *q);

    int videobuf_cgmbuf(struct videobuf_queue *q, struct video_mbuf *mbuf,

int count);

So, for example, a VIDIOC_REQBUFS call turns into a call to the driver's

vidioc_reqbufs() callback which, in turn, usually only needs to locate the

proper struct videobuf_queue pointer and pass it to videobuf_reqbufs().

These support functions can replace a great deal of buffer management

boilerplate in a lot of V4L2 drivers.

举个例子,用户的一个VIDIOC_REQBUFS会调用到vidioc_reqbufs(),

通常驱动只需要简单地传递videobuf_queue的指针给videobuf_reqbufs()

这些videobuf的支持函数能够替大量的V4L2驱动处理好缓冲区管理。

The vidioc_streamon() and vidioc_streamoff() functions will be a bit more

complex, of course, since they will also need to deal with starting and

stopping the capture engine.  videobuf_cgmbuf(), called from the driver's

vidiocgmbuf() function, only exists if the V4L1 compatibility module has

been selected with CONFIG_VIDEO_V4L1_COMPAT, so its use must be surrounded

with #ifdef directives.

 vidioc_streamon()和vidioc_streamoff()稍微复杂点,因为它们需要处理开启

和关闭捕捉引擎。驱动的vidiocgmbuf()调用videobufvideobuf_cgmbuf(),

只有当V4L1的兼容层模块被加载时才可以。

Buffer allocation

缓冲区分配

Thus far, we have talked about buffers, but have not looked at how they are

allocated.  The scatter/gather case is the most complex on this front.  For

allocation, the driver can leave buffer allocation entirely up to the

videobuf layer; in this case, buffers will be allocated as anonymous

user-space pages and will be very scattered indeed.  If the application is

using user-space buffers, no allocation is needed; the videobuf layer will

take care of calling get_user_pages() and filling in the scatterlist array.

之前我们有讨论过缓冲区,但是没有看到它们是如何被申请的。分散/聚集

的缓冲区情况让事情变得复杂了。驱动可以让videobuf层分配缓冲区,

这种情况下,videobuf会从无名用户页中分配出缓冲区,是非常分散的。

如果应用使用用户空间缓冲区,这里就不需要分配了,videobuf层会

调用 get_user_pages()和填充scatterlist数组来照顾好这些用户空间的缓

冲区的。

If the driver needs to do its own memory allocation, it should be done in

the vidioc_reqbufs() function, *after* calling videobuf_reqbufs().  The

first step is a call to:

如果驱动需要自己分配内存,那么应当在vidioc_reqbufs()函数中实现,这个函数

应当调用videobuf_reqbufs()。第一步是调用:

    struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf);

The returned videobuf_dmabuf structure (defined in

<media/videobuf-dma-sg.h>) includes a couple of relevant fields:

返回的videobuf_dmabuf结构体(定义在<media/videobuf-dma-sg.h>中)包含

一对重要的变量:

    struct scatterlist  *sglist;

    int                 sglen;

The driver must allocate an appropriately-sized scatterlist array and

populate it with pointers to the pieces of the allocated buffer; sglen

should be set to the length of the array.

驱动必须分配一个大小合适的scatterlist数组,sglen是这个数组的长度

驱动应当使用缓冲区的块指针填充好scatterlist数组。

Drivers using the vmalloc() method need not (and cannot) concern themselves

with buffer allocation at all; videobuf will handle those details.  The

same is normally true of contiguous-DMA drivers as well; videobuf will

allocate the buffers (with dma_alloc_coherent()) when it sees fit.  That

means that these drivers may be trying to do high-order allocations at any

time, an operation which is not always guaranteed to work.  Some drivers

play tricks by allocating DMA space at system boot time; videobuf does not

currently play well with those drivers.

使用vmalloc()方法(物理上不连续虚拟地址上连续的缓冲区)的驱动不需要关心

buffer的分配,因为videobuf会处理它们。同样使用物理连续虚拟地址也连续

的缓冲区的驱动也不需要关心,videobuf会分配缓冲区(使用dma_alloc_coherent())

当合适的时候。这就是说这些驱动会在任何时候尝试做高阶内存分配,但是这

些操作不是总能成功的。一些驱动在系统启动时分配DMA空间来避免这种失败风险,

videobuf目前还不能很好地支持这种驱动。

As of 2.6.31, contiguous-DMA drivers can work with a user-supplied buffer,

as long as that buffer is physically contiguous.  Normal user-space

allocations will not meet that criterion, but buffers obtained from other

kernel drivers, or those contained within huge pages, will work with these

drivers.

在2.6.31中,使用连续DMA缓冲区的驱动可以使用用户提供的缓冲区,

只要这些缓冲区也是在物理上连续的。一般的用户空间申请出来的缓冲区

一般不能达到这个条件,但是从其他内核驱动获取的缓冲区或者被包含在

巨大页面的缓冲区能够支持驱动使用这个特性。

Filling the buffers

填充缓冲区

The final part of a videobuf implementation has no direct callback - it's

the portion of the code which actually puts frame data into the buffers,

usually in response to interrupts from the device.  For all types of

drivers, this process works approximately as follows:

这个文档的最后一部分没有直接的回调函数----只是一些驱动代码将帧数据

放入buffer中的事情,驱动一般在中断中干这种事情。对于所有的驱动而言,

大概的流程是这样的:

 - Obtain the next available buffer and make sure that somebody is actually

   waiting for it.

 - Get a pointer to the memory and put video data there.

 - Mark the buffer as done and wake up the process waiting for it.

获取下一个有效缓冲区,并且确保有人在上面等待它;

获取一个内存指针并且将视频数据放到那里;

标记缓冲区已经准备好,并唤醒在等待它的人。

Step (1) above is done by looking at the driver-managed list_head structure

- the one which is filled in the buf_queue() callback.  Because starting

the engine and enqueueing buffers are done in separate steps, it's possible

for the engine to be running without any buffers available - in the

vmalloc() case especially.  So the driver should be prepared for the list

to be empty.  It is equally possible that nobody is yet interested in the

buffer; the driver should not remove it from the list or fill it until a

process is waiting on it.  That test can be done by examining the buffer's

done field (a wait_queue_head_t structure) with waitqueue_active().

上面的第一步,在buf_queue()查找驱动管理的缓冲区链表实现。因为

启动引擎和缓冲区入队列是两个独立的步骤,所以引擎可能工作在没有

缓冲区的情况下,例如使用vmalloc的缓冲区。驱动应当准备好处理缓冲

区链表为空的情况。当前没有进程对缓冲区感兴趣是非常可能的,如果没

有进程在等待缓冲区,驱动不应该从链表移除它或填充它。驱动可以使用

waitqueue_active()函数测试缓冲区的donewait_queue_head_t结构体)来

确定有没有进程在等待这个缓冲区。

A buffer's state should be set to VIDEOBUF_ACTIVE before being mapped for

DMA; that ensures that the videobuf layer will not try to do anything with

it while the device is transferring data.

在缓冲区被进行映射前,它的状态必须被设置为VIDEOBUF_ACTIVE,

这个可确保在设备传输数据时,videobuf层不会尝试对这个缓冲区做任何事。

For scatter/gather drivers, the needed memory pointers will be found in the

scatterlist structure described above. 

对于使用分散/聚集DMA的驱动,获取内存指针的地方是从scatterlist结构体中。 

Drivers using the vmalloc() methodcan get a memory pointer with:

使用vmalloc()方法的驱动使用下面这个函数:

    void *videobuf_to_vmalloc(struct videobuf_buffer *buf);

For contiguous DMA drivers, the function to use is:

连续DMA驱动使用这个函数:

    dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf);

The contiguous DMA API goes out of its way to hide the kernel-space address

of the DMA buffer from drivers.

The final step is to set the size field of the relevant videobuf_buffer

structure to the actual size of the captured image, set state to

VIDEOBUF_DONE, then call wake_up() on the done queue.  At this point, the

buffer is owned by the videobuf layer and the driver should not touch it

again.

最后一步是设置相关videobuf_buffer结构体的size区域为捕捉的图像的真实

大小,设置缓冲区的状态为VIDEOBUF_DONE,在done队列上调用wake_up()

函数。从此以后,这个缓冲区会被videobuf层拿去处理,驱动不应该再碰它。

Developers who are interested in more information can go into the relevant

header files; there are a few low-level functions declared there which have

not been talked about here.  Also worthwhile is the vivi driver

(drivers/media/video/vivi.c), which is maintained as an example of how V4L2

drivers should be written.  Vivi only uses the vmalloc() API, but it's good

enough to get started with.  Note also that all of these calls are exported

GPL-only, so they will not be available to non-GPL kernel modules.

感兴趣的开发者可以查看相关头文件得到更多的信息,可以去看一下

vivi驱动(drivers/media/video/vivi.c),它使用vmalloc类型的缓冲区,可

以作为一个好的入门代码。videobuf是一个GPL模块,非GPL模块

使用不了它。


GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐