mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
media: remove the old videobuf framework
The last driver that still used this old framework has been converted to the videobuf2 framework. So it is now time to delete the old videobuf code. Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
parent
785c4aa400
commit
2a2fffb488
@ -13,7 +13,6 @@ Video4Linux devices
|
||||
v4l2-subdev
|
||||
v4l2-event
|
||||
v4l2-controls
|
||||
v4l2-videobuf
|
||||
v4l2-videobuf2
|
||||
v4l2-dv-timings
|
||||
v4l2-flash-led-class
|
||||
|
@ -157,14 +157,6 @@ changing the e.g. exposure of the webcam.
|
||||
Of course, you can always do all the locking yourself by leaving both lock
|
||||
pointers at ``NULL``.
|
||||
|
||||
If you use the old :ref:`videobuf framework <vb_framework>` then you must
|
||||
pass the :c:type:`video_device`->lock to the videobuf queue initialize
|
||||
function: if videobuf has to wait for a frame to arrive, then it will
|
||||
temporarily unlock the lock and relock it afterwards. If your driver also
|
||||
waits in the code, then you should do the same to allow other
|
||||
processes to access the device node while the first process is waiting for
|
||||
something.
|
||||
|
||||
In the case of :ref:`videobuf2 <vb2_framework>` you will need to implement the
|
||||
``wait_prepare()`` and ``wait_finish()`` callbacks to unlock/lock if applicable.
|
||||
If you use the ``queue->lock`` pointer, then you can use the helper functions
|
||||
|
@ -1,403 +0,0 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. _vb_framework:
|
||||
|
||||
Videobuf Framework
|
||||
==================
|
||||
|
||||
Author: Jonathan Corbet <corbet@lwn.net>
|
||||
|
||||
Current as of 2.6.33
|
||||
|
||||
.. note::
|
||||
|
||||
The videobuf framework was deprecated in favor of videobuf2. Shouldn't
|
||||
be used on new drivers.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
- 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.
|
||||
|
||||
- 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.
|
||||
|
||||
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.
|
||||
|
||||
[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.]
|
||||
|
||||
Data structures, callbacks, and initialization
|
||||
----------------------------------------------
|
||||
|
||||
Depending on which type of buffers are being used, the driver should
|
||||
include one of the following files:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
<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.
|
||||
|
||||
The next step is to write four simple callbacks to help videobuf deal with
|
||||
the management of buffers:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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):
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
/* 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:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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).
|
||||
|
||||
The poll() function can usually be implemented with a direct call to:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
int videobuf_mmap_mapper(struct videobuf_queue *q,
|
||||
struct vm_area_struct *vma);
|
||||
|
||||
Everything else is handled by the videobuf code.
|
||||
|
||||
The release() function requires two separate videobuf calls:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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.
|
||||
|
||||
ioctl() operations
|
||||
------------------
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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);
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
- 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().
|
||||
|
||||
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.
|
||||
|
||||
For scatter/gather drivers, the needed memory pointers will be found in the
|
||||
scatterlist structure described above. Drivers using the vmalloc() method
|
||||
can get a memory pointer with:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
void *videobuf_to_vmalloc(struct videobuf_buffer *buf);
|
||||
|
||||
For contiguous DMA drivers, the function to use is:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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.
|
||||
|
||||
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. Note also that all of these calls are exported
|
||||
GPL-only, so they will not be available to non-GPL kernel modules.
|
@ -768,18 +768,6 @@ const char *video_device_node_name(struct video_device *vdev);
|
||||
此功能,而非访问 video_device::num 和 video_device::minor 域。
|
||||
|
||||
|
||||
视频缓冲辅助函数
|
||||
---------------
|
||||
|
||||
v4l2 核心 API 提供了一个处理视频缓冲的标准方法(称为“videobuf”)。
|
||||
这些方法使驱动可以通过统一的方式实现 read()、mmap() 和 overlay()。
|
||||
目前在设备上支持视频缓冲的方法有分散/聚集 DMA(videobuf-dma-sg)、
|
||||
线性 DMA(videobuf-dma-contig)以及大多用于 USB 设备的用 vmalloc
|
||||
分配的缓冲(videobuf-vmalloc)。
|
||||
|
||||
请参阅 Documentation/driver-api/media/v4l2-videobuf.rst,以获得更多关于 videobuf
|
||||
层的使用信息。
|
||||
|
||||
v4l2_fh 结构体
|
||||
-------------
|
||||
|
||||
|
@ -82,19 +82,3 @@ config V4L2_CCI_I2C
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select V4L2_CCI
|
||||
|
||||
# Used by drivers that need Videobuf modules
|
||||
config VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_DMA_SG
|
||||
tristate
|
||||
select VIDEOBUF_GEN
|
||||
|
||||
config VIDEOBUF_VMALLOC
|
||||
tristate
|
||||
select VIDEOBUF_GEN
|
||||
|
||||
config VIDEOBUF_DMA_CONTIG
|
||||
tristate
|
||||
select VIDEOBUF_GEN
|
||||
|
@ -33,10 +33,5 @@ obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o
|
||||
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
|
||||
obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o
|
||||
|
||||
obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
|
||||
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
|
||||
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
|
||||
obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_TUNER) += tuner.o
|
||||
obj-$(CONFIG_VIDEO_DEV) += v4l2-dv-timings.o videodev.o
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,402 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* helper functions for physically contiguous capture buffers
|
||||
*
|
||||
* The functions support hardware lacking scatter gather support
|
||||
* (i.e. the buffers must be linear in physical memory)
|
||||
*
|
||||
* Copyright (c) 2008 Magnus Damm
|
||||
*
|
||||
* Based on videobuf-vmalloc.c,
|
||||
* (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <media/videobuf-dma-contig.h>
|
||||
|
||||
struct videobuf_dma_contig_memory {
|
||||
u32 magic;
|
||||
void *vaddr;
|
||||
dma_addr_t dma_handle;
|
||||
unsigned long size;
|
||||
};
|
||||
|
||||
#define MAGIC_DC_MEM 0x0733ac61
|
||||
#define MAGIC_CHECK(is, should) \
|
||||
if (unlikely((is) != (should))) { \
|
||||
pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
|
||||
BUG(); \
|
||||
}
|
||||
|
||||
static int __videobuf_dc_alloc(struct device *dev,
|
||||
struct videobuf_dma_contig_memory *mem,
|
||||
unsigned long size)
|
||||
{
|
||||
mem->size = size;
|
||||
mem->vaddr = dma_alloc_coherent(dev, mem->size, &mem->dma_handle,
|
||||
GFP_KERNEL);
|
||||
if (!mem->vaddr) {
|
||||
dev_err(dev, "memory alloc size %ld failed\n", mem->size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __videobuf_dc_free(struct device *dev,
|
||||
struct videobuf_dma_contig_memory *mem)
|
||||
{
|
||||
dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle);
|
||||
|
||||
mem->vaddr = NULL;
|
||||
}
|
||||
|
||||
static void videobuf_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct videobuf_mapping *map = vma->vm_private_data;
|
||||
|
||||
dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
|
||||
map, map->count, vma->vm_start, vma->vm_end);
|
||||
|
||||
map->count++;
|
||||
}
|
||||
|
||||
static void videobuf_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct videobuf_mapping *map = vma->vm_private_data;
|
||||
struct videobuf_queue *q = map->q;
|
||||
int i;
|
||||
|
||||
dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
|
||||
map, map->count, vma->vm_start, vma->vm_end);
|
||||
|
||||
map->count--;
|
||||
if (0 == map->count) {
|
||||
struct videobuf_dma_contig_memory *mem;
|
||||
|
||||
dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
|
||||
videobuf_queue_lock(q);
|
||||
|
||||
/* We need first to cancel streams, before unmapping */
|
||||
if (q->streaming)
|
||||
videobuf_queue_cancel(q);
|
||||
|
||||
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
|
||||
if (NULL == q->bufs[i])
|
||||
continue;
|
||||
|
||||
if (q->bufs[i]->map != map)
|
||||
continue;
|
||||
|
||||
mem = q->bufs[i]->priv;
|
||||
if (mem) {
|
||||
/* This callback is called only if kernel has
|
||||
allocated memory and this memory is mmapped.
|
||||
In this case, memory should be freed,
|
||||
in order to do memory unmap.
|
||||
*/
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
|
||||
|
||||
/* vfree is not atomic - can't be
|
||||
called with IRQ's disabled
|
||||
*/
|
||||
dev_dbg(q->dev, "buf[%d] freeing %p\n",
|
||||
i, mem->vaddr);
|
||||
|
||||
__videobuf_dc_free(q->dev, mem);
|
||||
mem->vaddr = NULL;
|
||||
}
|
||||
|
||||
q->bufs[i]->map = NULL;
|
||||
q->bufs[i]->baddr = 0;
|
||||
}
|
||||
|
||||
kfree(map);
|
||||
|
||||
videobuf_queue_unlock(q);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct videobuf_vm_ops = {
|
||||
.open = videobuf_vm_open,
|
||||
.close = videobuf_vm_close,
|
||||
};
|
||||
|
||||
/**
|
||||
* videobuf_dma_contig_user_put() - reset pointer to user space buffer
|
||||
* @mem: per-buffer private videobuf-dma-contig data
|
||||
*
|
||||
* This function resets the user space pointer
|
||||
*/
|
||||
static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
|
||||
{
|
||||
mem->dma_handle = 0;
|
||||
mem->size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* videobuf_dma_contig_user_get() - setup user space memory pointer
|
||||
* @mem: per-buffer private videobuf-dma-contig data
|
||||
* @vb: video buffer to map
|
||||
*
|
||||
* This function validates and sets up a pointer to user space memory.
|
||||
* Only physically contiguous pfn-mapped memory is accepted.
|
||||
*
|
||||
* Returns 0 if successful.
|
||||
*/
|
||||
static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
|
||||
struct videobuf_buffer *vb)
|
||||
{
|
||||
unsigned long untagged_baddr = untagged_addr(vb->baddr);
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long prev_pfn, this_pfn;
|
||||
unsigned long pages_done, user_address;
|
||||
unsigned int offset;
|
||||
int ret;
|
||||
|
||||
offset = untagged_baddr & ~PAGE_MASK;
|
||||
mem->size = PAGE_ALIGN(vb->size + offset);
|
||||
ret = -EINVAL;
|
||||
|
||||
mmap_read_lock(mm);
|
||||
|
||||
vma = find_vma(mm, untagged_baddr);
|
||||
if (!vma)
|
||||
goto out_up;
|
||||
|
||||
if ((untagged_baddr + mem->size) > vma->vm_end)
|
||||
goto out_up;
|
||||
|
||||
pages_done = 0;
|
||||
prev_pfn = 0; /* kill warning */
|
||||
user_address = untagged_baddr;
|
||||
|
||||
while (pages_done < (mem->size >> PAGE_SHIFT)) {
|
||||
ret = follow_pfn(vma, user_address, &this_pfn);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (pages_done == 0)
|
||||
mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
|
||||
else if (this_pfn != (prev_pfn + 1))
|
||||
ret = -EFAULT;
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
prev_pfn = this_pfn;
|
||||
user_address += PAGE_SIZE;
|
||||
pages_done++;
|
||||
}
|
||||
|
||||
out_up:
|
||||
mmap_read_unlock(current->mm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct videobuf_buffer *__videobuf_alloc(size_t size)
|
||||
{
|
||||
struct videobuf_dma_contig_memory *mem;
|
||||
struct videobuf_buffer *vb;
|
||||
|
||||
vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
|
||||
if (vb) {
|
||||
vb->priv = ((char *)vb) + size;
|
||||
mem = vb->priv;
|
||||
mem->magic = MAGIC_DC_MEM;
|
||||
}
|
||||
|
||||
return vb;
|
||||
}
|
||||
|
||||
static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
|
||||
{
|
||||
struct videobuf_dma_contig_memory *mem = buf->priv;
|
||||
|
||||
BUG_ON(!mem);
|
||||
MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
|
||||
|
||||
return mem->vaddr;
|
||||
}
|
||||
|
||||
static int __videobuf_iolock(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb,
|
||||
struct v4l2_framebuffer *fbuf)
|
||||
{
|
||||
struct videobuf_dma_contig_memory *mem = vb->priv;
|
||||
|
||||
BUG_ON(!mem);
|
||||
MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
|
||||
|
||||
switch (vb->memory) {
|
||||
case V4L2_MEMORY_MMAP:
|
||||
dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
|
||||
|
||||
/* All handling should be done by __videobuf_mmap_mapper() */
|
||||
if (!mem->vaddr) {
|
||||
dev_err(q->dev, "memory is not allocated/mmapped.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case V4L2_MEMORY_USERPTR:
|
||||
dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
|
||||
|
||||
/* handle pointer from user space */
|
||||
if (vb->baddr)
|
||||
return videobuf_dma_contig_user_get(mem, vb);
|
||||
|
||||
/* allocate memory for the read() method */
|
||||
if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size)))
|
||||
return -ENOMEM;
|
||||
break;
|
||||
case V4L2_MEMORY_OVERLAY:
|
||||
default:
|
||||
dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __videobuf_mmap_mapper(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *buf,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct videobuf_dma_contig_memory *mem;
|
||||
struct videobuf_mapping *map;
|
||||
int retval;
|
||||
|
||||
dev_dbg(q->dev, "%s\n", __func__);
|
||||
|
||||
/* create mapping + update buffer list */
|
||||
map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->map = map;
|
||||
map->q = q;
|
||||
|
||||
buf->baddr = vma->vm_start;
|
||||
|
||||
mem = buf->priv;
|
||||
BUG_ON(!mem);
|
||||
MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
|
||||
|
||||
if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize)))
|
||||
goto error;
|
||||
|
||||
/* the "vm_pgoff" is just used in v4l2 to find the
|
||||
* corresponding buffer data structure which is allocated
|
||||
* earlier and it does not mean the offset from the physical
|
||||
* buffer start address as usual. So set it to 0 to pass
|
||||
* the sanity check in dma_mmap_coherent().
|
||||
*/
|
||||
vma->vm_pgoff = 0;
|
||||
retval = dma_mmap_coherent(q->dev, vma, mem->vaddr, mem->dma_handle,
|
||||
mem->size);
|
||||
if (retval) {
|
||||
dev_err(q->dev, "mmap: remap failed with error %d. ",
|
||||
retval);
|
||||
dma_free_coherent(q->dev, mem->size,
|
||||
mem->vaddr, mem->dma_handle);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vma->vm_ops = &videobuf_vm_ops;
|
||||
vm_flags_set(vma, VM_DONTEXPAND);
|
||||
vma->vm_private_data = map;
|
||||
|
||||
dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
|
||||
map, q, vma->vm_start, vma->vm_end,
|
||||
(long int)buf->bsize, vma->vm_pgoff, buf->i);
|
||||
|
||||
videobuf_vm_open(vma);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(map);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct videobuf_qtype_ops qops = {
|
||||
.magic = MAGIC_QTYPE_OPS,
|
||||
.alloc_vb = __videobuf_alloc,
|
||||
.iolock = __videobuf_iolock,
|
||||
.mmap_mapper = __videobuf_mmap_mapper,
|
||||
.vaddr = __videobuf_to_vaddr,
|
||||
};
|
||||
|
||||
void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
|
||||
const 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,
|
||||
struct mutex *ext_lock)
|
||||
{
|
||||
videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
|
||||
priv, &qops, ext_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
|
||||
|
||||
dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
|
||||
{
|
||||
struct videobuf_dma_contig_memory *mem = buf->priv;
|
||||
|
||||
BUG_ON(!mem);
|
||||
MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
|
||||
|
||||
return mem->dma_handle;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
|
||||
|
||||
void videobuf_dma_contig_free(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *buf)
|
||||
{
|
||||
struct videobuf_dma_contig_memory *mem = buf->priv;
|
||||
|
||||
/* mmapped memory can't be freed here, otherwise mmapped region
|
||||
would be released, while still needed. In this case, the memory
|
||||
release should happen inside videobuf_vm_close().
|
||||
So, it should free memory only if the memory were allocated for
|
||||
read() operation.
|
||||
*/
|
||||
if (buf->memory != V4L2_MEMORY_USERPTR)
|
||||
return;
|
||||
|
||||
if (!mem)
|
||||
return;
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
|
||||
|
||||
/* handle user space pointer case */
|
||||
if (buf->baddr) {
|
||||
videobuf_dma_contig_user_put(mem);
|
||||
return;
|
||||
}
|
||||
|
||||
/* read() method */
|
||||
if (mem->vaddr) {
|
||||
__videobuf_dc_free(q->dev, mem);
|
||||
mem->vaddr = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
|
||||
|
||||
MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
|
||||
MODULE_AUTHOR("Magnus Damm");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,681 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* helper functions for SG DMA video4linux capture buffers
|
||||
*
|
||||
* The functions expect the hardware being able to scatter gather
|
||||
* (i.e. the buffers are not linear in physical memory, but fragmented
|
||||
* into PAGE_SIZE chunks). They also assume the driver does not need
|
||||
* to touch the video data.
|
||||
*
|
||||
* (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org>
|
||||
*
|
||||
* Highly based on video-buf written originally by:
|
||||
* (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
|
||||
* (c) 2006 Mauro Carvalho Chehab, <mchehab@kernel.org>
|
||||
* (c) 2006 Ted Walther and John Sokol
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pgtable.h>
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#include <media/videobuf-dma-sg.h>
|
||||
|
||||
#define MAGIC_DMABUF 0x19721112
|
||||
#define MAGIC_SG_MEM 0x17890714
|
||||
|
||||
#define MAGIC_CHECK(is, should) \
|
||||
if (unlikely((is) != (should))) { \
|
||||
printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \
|
||||
is, should); \
|
||||
BUG(); \
|
||||
}
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
|
||||
MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers");
|
||||
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define dprintk(level, fmt, arg...) \
|
||||
if (debug >= level) \
|
||||
printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg)
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Return a scatterlist for some page-aligned vmalloc()'ed memory
|
||||
* block (NULL on errors). Memory for the scatterlist is allocated
|
||||
* using kmalloc. The caller must free the memory.
|
||||
*/
|
||||
static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt,
|
||||
int nr_pages)
|
||||
{
|
||||
struct scatterlist *sglist;
|
||||
struct page *pg;
|
||||
int i;
|
||||
|
||||
sglist = vzalloc(array_size(nr_pages, sizeof(*sglist)));
|
||||
if (NULL == sglist)
|
||||
return NULL;
|
||||
sg_init_table(sglist, nr_pages);
|
||||
for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
|
||||
pg = vmalloc_to_page(virt);
|
||||
if (NULL == pg)
|
||||
goto err;
|
||||
BUG_ON(PageHighMem(pg));
|
||||
sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
|
||||
}
|
||||
return sglist;
|
||||
|
||||
err:
|
||||
vfree(sglist);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a scatterlist for a an array of userpages (NULL on errors).
|
||||
* Memory for the scatterlist is allocated using kmalloc. The caller
|
||||
* must free the memory.
|
||||
*/
|
||||
static struct scatterlist *videobuf_pages_to_sg(struct page **pages,
|
||||
int nr_pages, int offset, size_t size)
|
||||
{
|
||||
struct scatterlist *sglist;
|
||||
int i;
|
||||
|
||||
if (NULL == pages[0])
|
||||
return NULL;
|
||||
sglist = vmalloc(array_size(nr_pages, sizeof(*sglist)));
|
||||
if (NULL == sglist)
|
||||
return NULL;
|
||||
sg_init_table(sglist, nr_pages);
|
||||
|
||||
if (PageHighMem(pages[0]))
|
||||
/* DMA to highmem pages might not work */
|
||||
goto highmem;
|
||||
sg_set_page(&sglist[0], pages[0],
|
||||
min_t(size_t, PAGE_SIZE - offset, size), offset);
|
||||
size -= min_t(size_t, PAGE_SIZE - offset, size);
|
||||
for (i = 1; i < nr_pages; i++) {
|
||||
if (NULL == pages[i])
|
||||
goto nopage;
|
||||
if (PageHighMem(pages[i]))
|
||||
goto highmem;
|
||||
sg_set_page(&sglist[i], pages[i], min_t(size_t, PAGE_SIZE, size), 0);
|
||||
size -= min_t(size_t, PAGE_SIZE, size);
|
||||
}
|
||||
return sglist;
|
||||
|
||||
nopage:
|
||||
dprintk(2, "sgl: oops - no page\n");
|
||||
vfree(sglist);
|
||||
return NULL;
|
||||
|
||||
highmem:
|
||||
dprintk(2, "sgl: oops - highmem page\n");
|
||||
vfree(sglist);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf)
|
||||
{
|
||||
struct videobuf_dma_sg_memory *mem = buf->priv;
|
||||
BUG_ON(!mem);
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
|
||||
|
||||
return &mem->dma;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_to_dma);
|
||||
|
||||
static void videobuf_dma_init(struct videobuf_dmabuf *dma)
|
||||
{
|
||||
memset(dma, 0, sizeof(*dma));
|
||||
dma->magic = MAGIC_DMABUF;
|
||||
}
|
||||
|
||||
static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
|
||||
int direction, unsigned long data, unsigned long size)
|
||||
{
|
||||
unsigned int gup_flags = FOLL_LONGTERM;
|
||||
unsigned long first, last;
|
||||
int err;
|
||||
|
||||
dma->direction = direction;
|
||||
switch (dma->direction) {
|
||||
case DMA_FROM_DEVICE:
|
||||
gup_flags |= FOLL_WRITE;
|
||||
break;
|
||||
case DMA_TO_DEVICE:
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
first = (data & PAGE_MASK) >> PAGE_SHIFT;
|
||||
last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT;
|
||||
dma->offset = data & ~PAGE_MASK;
|
||||
dma->size = size;
|
||||
dma->nr_pages = last-first+1;
|
||||
dma->pages = kmalloc_array(dma->nr_pages, sizeof(struct page *),
|
||||
GFP_KERNEL);
|
||||
if (NULL == dma->pages)
|
||||
return -ENOMEM;
|
||||
|
||||
dprintk(1, "init user [0x%lx+0x%lx => %lu pages]\n",
|
||||
data, size, dma->nr_pages);
|
||||
|
||||
err = pin_user_pages(data & PAGE_MASK, dma->nr_pages, gup_flags,
|
||||
dma->pages);
|
||||
|
||||
if (err != dma->nr_pages) {
|
||||
dma->nr_pages = (err >= 0) ? err : 0;
|
||||
dprintk(1, "pin_user_pages: err=%d [%lu]\n", err,
|
||||
dma->nr_pages);
|
||||
return err < 0 ? err : -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
|
||||
unsigned long data, unsigned long size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mmap_read_lock(current->mm);
|
||||
ret = videobuf_dma_init_user_locked(dma, direction, data, size);
|
||||
mmap_read_unlock(current->mm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
|
||||
unsigned long nr_pages)
|
||||
{
|
||||
int i;
|
||||
|
||||
dprintk(1, "init kernel [%lu pages]\n", nr_pages);
|
||||
|
||||
dma->direction = direction;
|
||||
dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages),
|
||||
GFP_KERNEL);
|
||||
if (!dma->vaddr_pages)
|
||||
return -ENOMEM;
|
||||
|
||||
dma->dma_addr = kcalloc(nr_pages, sizeof(*dma->dma_addr), GFP_KERNEL);
|
||||
if (!dma->dma_addr) {
|
||||
kfree(dma->vaddr_pages);
|
||||
return -ENOMEM;
|
||||
}
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
void *addr;
|
||||
|
||||
addr = dma_alloc_coherent(dma->dev, PAGE_SIZE,
|
||||
&(dma->dma_addr[i]), GFP_KERNEL);
|
||||
if (addr == NULL)
|
||||
goto out_free_pages;
|
||||
|
||||
dma->vaddr_pages[i] = virt_to_page(addr);
|
||||
}
|
||||
dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP,
|
||||
PAGE_KERNEL);
|
||||
if (NULL == dma->vaddr) {
|
||||
dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages);
|
||||
goto out_free_pages;
|
||||
}
|
||||
|
||||
dprintk(1, "vmalloc is at addr %p, size=%lu\n",
|
||||
dma->vaddr, nr_pages << PAGE_SHIFT);
|
||||
|
||||
memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT);
|
||||
dma->nr_pages = nr_pages;
|
||||
|
||||
return 0;
|
||||
out_free_pages:
|
||||
while (i > 0) {
|
||||
void *addr;
|
||||
|
||||
i--;
|
||||
addr = page_address(dma->vaddr_pages[i]);
|
||||
dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]);
|
||||
}
|
||||
kfree(dma->dma_addr);
|
||||
dma->dma_addr = NULL;
|
||||
kfree(dma->vaddr_pages);
|
||||
dma->vaddr_pages = NULL;
|
||||
|
||||
return -ENOMEM;
|
||||
|
||||
}
|
||||
|
||||
static int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
|
||||
dma_addr_t addr, unsigned long nr_pages)
|
||||
{
|
||||
dprintk(1, "init overlay [%lu pages @ bus 0x%lx]\n",
|
||||
nr_pages, (unsigned long)addr);
|
||||
dma->direction = direction;
|
||||
|
||||
if (0 == addr)
|
||||
return -EINVAL;
|
||||
|
||||
dma->bus_addr = addr;
|
||||
dma->nr_pages = nr_pages;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma)
|
||||
{
|
||||
MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
|
||||
BUG_ON(0 == dma->nr_pages);
|
||||
|
||||
if (dma->pages) {
|
||||
dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages,
|
||||
dma->offset, dma->size);
|
||||
}
|
||||
if (dma->vaddr) {
|
||||
dma->sglist = videobuf_vmalloc_to_sg(dma->vaddr,
|
||||
dma->nr_pages);
|
||||
}
|
||||
if (dma->bus_addr) {
|
||||
dma->sglist = vmalloc(sizeof(*dma->sglist));
|
||||
if (NULL != dma->sglist) {
|
||||
dma->sglen = 1;
|
||||
sg_dma_address(&dma->sglist[0]) = dma->bus_addr
|
||||
& PAGE_MASK;
|
||||
dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK;
|
||||
sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
if (NULL == dma->sglist) {
|
||||
dprintk(1, "scatterlist is NULL\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!dma->bus_addr) {
|
||||
dma->sglen = dma_map_sg(dev, dma->sglist,
|
||||
dma->nr_pages, dma->direction);
|
||||
if (0 == dma->sglen) {
|
||||
printk(KERN_WARNING
|
||||
"%s: videobuf_map_sg failed\n", __func__);
|
||||
vfree(dma->sglist);
|
||||
dma->sglist = NULL;
|
||||
dma->sglen = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma)
|
||||
{
|
||||
MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
|
||||
|
||||
if (!dma->sglen)
|
||||
return 0;
|
||||
|
||||
dma_unmap_sg(dev, dma->sglist, dma->nr_pages, dma->direction);
|
||||
|
||||
vfree(dma->sglist);
|
||||
dma->sglist = NULL;
|
||||
dma->sglen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_dma_unmap);
|
||||
|
||||
int videobuf_dma_free(struct videobuf_dmabuf *dma)
|
||||
{
|
||||
int i;
|
||||
MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
|
||||
BUG_ON(dma->sglen);
|
||||
|
||||
if (dma->pages) {
|
||||
unpin_user_pages_dirty_lock(dma->pages, dma->nr_pages,
|
||||
dma->direction == DMA_FROM_DEVICE);
|
||||
kfree(dma->pages);
|
||||
dma->pages = NULL;
|
||||
}
|
||||
|
||||
if (dma->dma_addr) {
|
||||
for (i = 0; i < dma->nr_pages; i++) {
|
||||
void *addr;
|
||||
|
||||
addr = page_address(dma->vaddr_pages[i]);
|
||||
dma_free_coherent(dma->dev, PAGE_SIZE, addr,
|
||||
dma->dma_addr[i]);
|
||||
}
|
||||
kfree(dma->dma_addr);
|
||||
dma->dma_addr = NULL;
|
||||
kfree(dma->vaddr_pages);
|
||||
dma->vaddr_pages = NULL;
|
||||
vunmap(dma->vaddr);
|
||||
dma->vaddr = NULL;
|
||||
}
|
||||
|
||||
if (dma->bus_addr)
|
||||
dma->bus_addr = 0;
|
||||
dma->direction = DMA_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_dma_free);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void videobuf_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct videobuf_mapping *map = vma->vm_private_data;
|
||||
|
||||
dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map,
|
||||
map->count, vma->vm_start, vma->vm_end);
|
||||
|
||||
map->count++;
|
||||
}
|
||||
|
||||
static void videobuf_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct videobuf_mapping *map = vma->vm_private_data;
|
||||
struct videobuf_queue *q = map->q;
|
||||
struct videobuf_dma_sg_memory *mem;
|
||||
int i;
|
||||
|
||||
dprintk(2, "vm_close %p [count=%d,vma=%08lx-%08lx]\n", map,
|
||||
map->count, vma->vm_start, vma->vm_end);
|
||||
|
||||
map->count--;
|
||||
if (0 == map->count) {
|
||||
dprintk(1, "munmap %p q=%p\n", map, q);
|
||||
videobuf_queue_lock(q);
|
||||
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
|
||||
if (NULL == q->bufs[i])
|
||||
continue;
|
||||
mem = q->bufs[i]->priv;
|
||||
if (!mem)
|
||||
continue;
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
|
||||
|
||||
if (q->bufs[i]->map != map)
|
||||
continue;
|
||||
q->bufs[i]->map = NULL;
|
||||
q->bufs[i]->baddr = 0;
|
||||
q->ops->buf_release(q, q->bufs[i]);
|
||||
}
|
||||
videobuf_queue_unlock(q);
|
||||
kfree(map);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a anonymous page for the mapping. Make sure we can DMA to that
|
||||
* memory location with 32bit PCI devices (i.e. don't use highmem for
|
||||
* now ...). Bounce buffers don't work very well for the data rates
|
||||
* video capture has.
|
||||
*/
|
||||
static vm_fault_t videobuf_vm_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct page *page;
|
||||
|
||||
dprintk(3, "fault: fault @ %08lx [vma %08lx-%08lx]\n",
|
||||
vmf->address, vma->vm_start, vma->vm_end);
|
||||
|
||||
page = alloc_page(GFP_USER | __GFP_DMA32);
|
||||
if (!page)
|
||||
return VM_FAULT_OOM;
|
||||
clear_user_highpage(page, vmf->address);
|
||||
vmf->page = page;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct videobuf_vm_ops = {
|
||||
.open = videobuf_vm_open,
|
||||
.close = videobuf_vm_close,
|
||||
.fault = videobuf_vm_fault,
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* SG handlers for the generic methods
|
||||
*/
|
||||
|
||||
/* Allocated area consists on 3 parts:
|
||||
struct video_buffer
|
||||
struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
|
||||
struct videobuf_dma_sg_memory
|
||||
*/
|
||||
|
||||
static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
|
||||
{
|
||||
struct videobuf_dma_sg_memory *mem;
|
||||
struct videobuf_buffer *vb;
|
||||
|
||||
vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
|
||||
if (!vb)
|
||||
return vb;
|
||||
|
||||
mem = vb->priv = ((char *)vb) + size;
|
||||
mem->magic = MAGIC_SG_MEM;
|
||||
|
||||
videobuf_dma_init(&mem->dma);
|
||||
|
||||
dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
|
||||
__func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
|
||||
mem, (long)sizeof(*mem));
|
||||
|
||||
return vb;
|
||||
}
|
||||
|
||||
static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
|
||||
{
|
||||
struct videobuf_dma_sg_memory *mem = buf->priv;
|
||||
BUG_ON(!mem);
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
|
||||
|
||||
return mem->dma.vaddr;
|
||||
}
|
||||
|
||||
static int __videobuf_iolock(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb,
|
||||
struct v4l2_framebuffer *fbuf)
|
||||
{
|
||||
struct videobuf_dma_sg_memory *mem = vb->priv;
|
||||
unsigned long pages;
|
||||
dma_addr_t bus;
|
||||
int err;
|
||||
|
||||
BUG_ON(!mem);
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
|
||||
|
||||
if (!mem->dma.dev)
|
||||
mem->dma.dev = q->dev;
|
||||
else
|
||||
WARN_ON(mem->dma.dev != q->dev);
|
||||
|
||||
switch (vb->memory) {
|
||||
case V4L2_MEMORY_MMAP:
|
||||
case V4L2_MEMORY_USERPTR:
|
||||
if (0 == vb->baddr) {
|
||||
/* no userspace addr -- kernel bounce buffer */
|
||||
pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
|
||||
err = videobuf_dma_init_kernel(&mem->dma,
|
||||
DMA_FROM_DEVICE,
|
||||
pages);
|
||||
if (0 != err)
|
||||
return err;
|
||||
} else if (vb->memory == V4L2_MEMORY_USERPTR) {
|
||||
/* dma directly to userspace */
|
||||
err = videobuf_dma_init_user(&mem->dma,
|
||||
DMA_FROM_DEVICE,
|
||||
vb->baddr, vb->bsize);
|
||||
if (0 != err)
|
||||
return err;
|
||||
} else {
|
||||
/* NOTE: HACK: videobuf_iolock on V4L2_MEMORY_MMAP
|
||||
buffers can only be called from videobuf_qbuf
|
||||
we take current->mm->mmap_lock there, to prevent
|
||||
locking inversion, so don't take it here */
|
||||
|
||||
err = videobuf_dma_init_user_locked(&mem->dma,
|
||||
DMA_FROM_DEVICE,
|
||||
vb->baddr, vb->bsize);
|
||||
if (0 != err)
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
case V4L2_MEMORY_OVERLAY:
|
||||
if (NULL == fbuf)
|
||||
return -EINVAL;
|
||||
/* FIXME: need sanity checks for vb->boff */
|
||||
/*
|
||||
* Using a double cast to avoid compiler warnings when
|
||||
* building for PAE. Compiler doesn't like direct casting
|
||||
* of a 32 bit ptr to 64 bit integer.
|
||||
*/
|
||||
bus = (dma_addr_t)(unsigned long)fbuf->base + vb->boff;
|
||||
pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
|
||||
err = videobuf_dma_init_overlay(&mem->dma, DMA_FROM_DEVICE,
|
||||
bus, pages);
|
||||
if (0 != err)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
err = videobuf_dma_map(q->dev, &mem->dma);
|
||||
if (0 != err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __videobuf_sync(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *buf)
|
||||
{
|
||||
struct videobuf_dma_sg_memory *mem = buf->priv;
|
||||
BUG_ON(!mem || !mem->dma.sglen);
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
|
||||
MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF);
|
||||
|
||||
dma_sync_sg_for_cpu(q->dev, mem->dma.sglist,
|
||||
mem->dma.nr_pages, mem->dma.direction);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __videobuf_mmap_mapper(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *buf,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct videobuf_dma_sg_memory *mem = buf->priv;
|
||||
struct videobuf_mapping *map;
|
||||
unsigned int first, last, size = 0, i;
|
||||
int retval;
|
||||
|
||||
retval = -EINVAL;
|
||||
|
||||
BUG_ON(!mem);
|
||||
MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
|
||||
|
||||
/* look for first buffer to map */
|
||||
for (first = 0; first < VIDEO_MAX_FRAME; first++) {
|
||||
if (buf == q->bufs[first]) {
|
||||
size = PAGE_ALIGN(q->bufs[first]->bsize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* paranoia, should never happen since buf is always valid. */
|
||||
if (!size) {
|
||||
dprintk(1, "mmap app bug: offset invalid [offset=0x%lx]\n",
|
||||
(vma->vm_pgoff << PAGE_SHIFT));
|
||||
goto done;
|
||||
}
|
||||
|
||||
last = first;
|
||||
|
||||
/* create mapping + update buffer list */
|
||||
retval = -ENOMEM;
|
||||
map = kmalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
|
||||
if (NULL == map)
|
||||
goto done;
|
||||
|
||||
size = 0;
|
||||
for (i = first; i <= last; i++) {
|
||||
if (NULL == q->bufs[i])
|
||||
continue;
|
||||
q->bufs[i]->map = map;
|
||||
q->bufs[i]->baddr = vma->vm_start + size;
|
||||
size += PAGE_ALIGN(q->bufs[i]->bsize);
|
||||
}
|
||||
|
||||
map->count = 1;
|
||||
map->q = q;
|
||||
vma->vm_ops = &videobuf_vm_ops;
|
||||
/* using shared anonymous pages */
|
||||
vm_flags_mod(vma, VM_DONTEXPAND | VM_DONTDUMP, VM_IO);
|
||||
vma->vm_private_data = map;
|
||||
dprintk(1, "mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n",
|
||||
map, q, vma->vm_start, vma->vm_end, vma->vm_pgoff, first, last);
|
||||
retval = 0;
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct videobuf_qtype_ops sg_ops = {
|
||||
.magic = MAGIC_QTYPE_OPS,
|
||||
|
||||
.alloc_vb = __videobuf_alloc_vb,
|
||||
.iolock = __videobuf_iolock,
|
||||
.sync = __videobuf_sync,
|
||||
.mmap_mapper = __videobuf_mmap_mapper,
|
||||
.vaddr = __videobuf_to_vaddr,
|
||||
};
|
||||
|
||||
void *videobuf_sg_alloc(size_t size)
|
||||
{
|
||||
struct videobuf_queue q;
|
||||
|
||||
/* Required to make generic handler to call __videobuf_alloc */
|
||||
q.int_ops = &sg_ops;
|
||||
|
||||
q.msize = size;
|
||||
|
||||
return videobuf_alloc_vb(&q);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_sg_alloc);
|
||||
|
||||
void videobuf_queue_sg_init(struct videobuf_queue *q,
|
||||
const 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,
|
||||
struct mutex *ext_lock)
|
||||
{
|
||||
videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
|
||||
priv, &sg_ops, ext_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_queue_sg_init);
|
||||
|
@ -1,326 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* helper functions for vmalloc video4linux capture buffers
|
||||
*
|
||||
* The functions expect the hardware being able to scatter gather
|
||||
* (i.e. the buffers are not linear in physical memory, but fragmented
|
||||
* into PAGE_SIZE chunks). They also assume the driver does not need
|
||||
* to touch the video data.
|
||||
*
|
||||
* (c) 2007 Mauro Carvalho Chehab <mchehab@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pgtable.h>
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#include <media/videobuf-vmalloc.h>
|
||||
|
||||
#define MAGIC_DMABUF 0x17760309
|
||||
#define MAGIC_VMAL_MEM 0x18221223
|
||||
|
||||
#define MAGIC_CHECK(is, should) \
|
||||
if (unlikely((is) != (should))) { \
|
||||
printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \
|
||||
is, should); \
|
||||
BUG(); \
|
||||
}
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
|
||||
MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers");
|
||||
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define dprintk(level, fmt, arg...) \
|
||||
if (debug >= level) \
|
||||
printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
static void videobuf_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct videobuf_mapping *map = vma->vm_private_data;
|
||||
|
||||
dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map,
|
||||
map->count, vma->vm_start, vma->vm_end);
|
||||
|
||||
map->count++;
|
||||
}
|
||||
|
||||
static void videobuf_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct videobuf_mapping *map = vma->vm_private_data;
|
||||
struct videobuf_queue *q = map->q;
|
||||
int i;
|
||||
|
||||
dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
|
||||
map->count, vma->vm_start, vma->vm_end);
|
||||
|
||||
map->count--;
|
||||
if (0 == map->count) {
|
||||
struct videobuf_vmalloc_memory *mem;
|
||||
|
||||
dprintk(1, "munmap %p q=%p\n", map, q);
|
||||
videobuf_queue_lock(q);
|
||||
|
||||
/* We need first to cancel streams, before unmapping */
|
||||
if (q->streaming)
|
||||
videobuf_queue_cancel(q);
|
||||
|
||||
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
|
||||
if (NULL == q->bufs[i])
|
||||
continue;
|
||||
|
||||
if (q->bufs[i]->map != map)
|
||||
continue;
|
||||
|
||||
mem = q->bufs[i]->priv;
|
||||
if (mem) {
|
||||
/* This callback is called only if kernel has
|
||||
allocated memory and this memory is mmapped.
|
||||
In this case, memory should be freed,
|
||||
in order to do memory unmap.
|
||||
*/
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
|
||||
|
||||
/* vfree is not atomic - can't be
|
||||
called with IRQ's disabled
|
||||
*/
|
||||
dprintk(1, "%s: buf[%d] freeing (%p)\n",
|
||||
__func__, i, mem->vaddr);
|
||||
|
||||
vfree(mem->vaddr);
|
||||
mem->vaddr = NULL;
|
||||
}
|
||||
|
||||
q->bufs[i]->map = NULL;
|
||||
q->bufs[i]->baddr = 0;
|
||||
}
|
||||
|
||||
kfree(map);
|
||||
|
||||
videobuf_queue_unlock(q);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct videobuf_vm_ops = {
|
||||
.open = videobuf_vm_open,
|
||||
.close = videobuf_vm_close,
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* vmalloc handlers for the generic methods
|
||||
*/
|
||||
|
||||
/* Allocated area consists on 3 parts:
|
||||
struct video_buffer
|
||||
struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
|
||||
struct videobuf_dma_sg_memory
|
||||
*/
|
||||
|
||||
static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
|
||||
{
|
||||
struct videobuf_vmalloc_memory *mem;
|
||||
struct videobuf_buffer *vb;
|
||||
|
||||
vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
|
||||
if (!vb)
|
||||
return vb;
|
||||
|
||||
mem = vb->priv = ((char *)vb) + size;
|
||||
mem->magic = MAGIC_VMAL_MEM;
|
||||
|
||||
dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
|
||||
__func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
|
||||
mem, (long)sizeof(*mem));
|
||||
|
||||
return vb;
|
||||
}
|
||||
|
||||
static int __videobuf_iolock(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb,
|
||||
struct v4l2_framebuffer *fbuf)
|
||||
{
|
||||
struct videobuf_vmalloc_memory *mem = vb->priv;
|
||||
int pages;
|
||||
|
||||
BUG_ON(!mem);
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
|
||||
|
||||
switch (vb->memory) {
|
||||
case V4L2_MEMORY_MMAP:
|
||||
dprintk(1, "%s memory method MMAP\n", __func__);
|
||||
|
||||
/* All handling should be done by __videobuf_mmap_mapper() */
|
||||
if (!mem->vaddr) {
|
||||
printk(KERN_ERR "memory is not allocated/mmapped.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case V4L2_MEMORY_USERPTR:
|
||||
pages = PAGE_ALIGN(vb->size);
|
||||
|
||||
dprintk(1, "%s memory method USERPTR\n", __func__);
|
||||
|
||||
if (vb->baddr) {
|
||||
printk(KERN_ERR "USERPTR is currently not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The only USERPTR currently supported is the one needed for
|
||||
* read() method.
|
||||
*/
|
||||
|
||||
mem->vaddr = vmalloc_user(pages);
|
||||
if (!mem->vaddr) {
|
||||
printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
|
||||
return -ENOMEM;
|
||||
}
|
||||
dprintk(1, "vmalloc is at addr %p (%d pages)\n",
|
||||
mem->vaddr, pages);
|
||||
break;
|
||||
case V4L2_MEMORY_OVERLAY:
|
||||
default:
|
||||
dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);
|
||||
|
||||
/* Currently, doesn't support V4L2_MEMORY_OVERLAY */
|
||||
printk(KERN_ERR "Memory method currently unsupported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __videobuf_mmap_mapper(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *buf,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct videobuf_vmalloc_memory *mem;
|
||||
struct videobuf_mapping *map;
|
||||
int retval, pages;
|
||||
|
||||
dprintk(1, "%s\n", __func__);
|
||||
|
||||
/* create mapping + update buffer list */
|
||||
map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
|
||||
if (NULL == map)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->map = map;
|
||||
map->q = q;
|
||||
|
||||
buf->baddr = vma->vm_start;
|
||||
|
||||
mem = buf->priv;
|
||||
BUG_ON(!mem);
|
||||
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
|
||||
|
||||
pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
|
||||
mem->vaddr = vmalloc_user(pages);
|
||||
if (!mem->vaddr) {
|
||||
printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
|
||||
goto error;
|
||||
}
|
||||
dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages);
|
||||
|
||||
/* Try to remap memory */
|
||||
retval = remap_vmalloc_range(vma, mem->vaddr, 0);
|
||||
if (retval < 0) {
|
||||
printk(KERN_ERR "mmap: remap failed with error %d. ", retval);
|
||||
vfree(mem->vaddr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vma->vm_ops = &videobuf_vm_ops;
|
||||
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
|
||||
vma->vm_private_data = map;
|
||||
|
||||
dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
|
||||
map, q, vma->vm_start, vma->vm_end,
|
||||
(long int)buf->bsize,
|
||||
vma->vm_pgoff, buf->i);
|
||||
|
||||
videobuf_vm_open(vma);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
mem = NULL;
|
||||
kfree(map);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct videobuf_qtype_ops qops = {
|
||||
.magic = MAGIC_QTYPE_OPS,
|
||||
|
||||
.alloc_vb = __videobuf_alloc_vb,
|
||||
.iolock = __videobuf_iolock,
|
||||
.mmap_mapper = __videobuf_mmap_mapper,
|
||||
.vaddr = videobuf_to_vmalloc,
|
||||
};
|
||||
|
||||
void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
|
||||
const 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,
|
||||
struct mutex *ext_lock)
|
||||
{
|
||||
videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
|
||||
priv, &qops, ext_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);
|
||||
|
||||
void *videobuf_to_vmalloc(struct videobuf_buffer *buf)
|
||||
{
|
||||
struct videobuf_vmalloc_memory *mem = buf->priv;
|
||||
BUG_ON(!mem);
|
||||
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
|
||||
|
||||
return mem->vaddr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_to_vmalloc);
|
||||
|
||||
void videobuf_vmalloc_free(struct videobuf_buffer *buf)
|
||||
{
|
||||
struct videobuf_vmalloc_memory *mem = buf->priv;
|
||||
|
||||
/* mmapped memory can't be freed here, otherwise mmapped region
|
||||
would be released, while still needed. In this case, the memory
|
||||
release should happen inside videobuf_vm_close().
|
||||
So, it should free memory only if the memory were allocated for
|
||||
read() operation.
|
||||
*/
|
||||
if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
|
||||
return;
|
||||
|
||||
if (!mem)
|
||||
return;
|
||||
|
||||
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
|
||||
|
||||
vfree(mem->vaddr);
|
||||
mem->vaddr = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);
|
||||
|
@ -1,233 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* generic helper functions for handling video4linux capture buffers
|
||||
*
|
||||
* (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org>
|
||||
*
|
||||
* Highly based on video-buf written originally by:
|
||||
* (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
|
||||
* (c) 2006 Mauro Carvalho Chehab, <mchehab@kernel.org>
|
||||
* (c) 2006 Ted Walther and John Sokol
|
||||
*/
|
||||
|
||||
#ifndef _VIDEOBUF_CORE_H
|
||||
#define _VIDEOBUF_CORE_H
|
||||
|
||||
#include <linux/poll.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#define UNSET (-1U)
|
||||
|
||||
|
||||
struct videobuf_buffer;
|
||||
struct videobuf_queue;
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* A small set of helper functions to manage video4linux buffers.
|
||||
*
|
||||
* struct videobuf_buffer holds the data structures used by the helper
|
||||
* functions, additionally some commonly used fields for v4l buffers
|
||||
* (width, height, lists, waitqueue) are in there. That struct should
|
||||
* be used as first element in the drivers buffer struct.
|
||||
*
|
||||
* about the mmap helpers (videobuf_mmap_*):
|
||||
*
|
||||
* The mmaper function allows to map any subset of contiguous buffers.
|
||||
* This includes one mmap() call for all buffers (which the original
|
||||
* video4linux API uses) as well as one mmap() for every single buffer
|
||||
* (which v4l2 uses).
|
||||
*
|
||||
* If there is a valid mapping for a buffer, buffer->baddr/bsize holds
|
||||
* userspace address + size which can be fed into the
|
||||
* videobuf_dma_init_user function listed above.
|
||||
*
|
||||
*/
|
||||
|
||||
struct videobuf_mapping {
|
||||
unsigned int count;
|
||||
struct videobuf_queue *q;
|
||||
};
|
||||
|
||||
enum videobuf_state {
|
||||
VIDEOBUF_NEEDS_INIT = 0,
|
||||
VIDEOBUF_PREPARED = 1,
|
||||
VIDEOBUF_QUEUED = 2,
|
||||
VIDEOBUF_ACTIVE = 3,
|
||||
VIDEOBUF_DONE = 4,
|
||||
VIDEOBUF_ERROR = 5,
|
||||
VIDEOBUF_IDLE = 6,
|
||||
};
|
||||
|
||||
struct videobuf_buffer {
|
||||
unsigned int i;
|
||||
u32 magic;
|
||||
|
||||
/* info about the buffer */
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int bytesperline; /* use only if != 0 */
|
||||
unsigned long size;
|
||||
enum v4l2_field field;
|
||||
enum videobuf_state state;
|
||||
struct list_head stream; /* QBUF/DQBUF list */
|
||||
|
||||
/* touched by irq handler */
|
||||
struct list_head queue;
|
||||
wait_queue_head_t done;
|
||||
unsigned int field_count;
|
||||
u64 ts;
|
||||
|
||||
/* Memory type */
|
||||
enum v4l2_memory memory;
|
||||
|
||||
/* buffer size */
|
||||
size_t bsize;
|
||||
|
||||
/* buffer offset (mmap + overlay) */
|
||||
size_t boff;
|
||||
|
||||
/* buffer addr (userland ptr!) */
|
||||
unsigned long baddr;
|
||||
|
||||
/* for mmap'ed buffers */
|
||||
struct videobuf_mapping *map;
|
||||
|
||||
/* Private pointer to allow specific methods to store their data */
|
||||
int privsize;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
#define MAGIC_QTYPE_OPS 0x12261003
|
||||
|
||||
/* Helper operations - device type dependent */
|
||||
struct videobuf_qtype_ops {
|
||||
u32 magic;
|
||||
|
||||
struct videobuf_buffer *(*alloc_vb)(size_t size);
|
||||
void *(*vaddr) (struct videobuf_buffer *buf);
|
||||
int (*iolock) (struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb,
|
||||
struct v4l2_framebuffer *fbuf);
|
||||
int (*sync) (struct videobuf_queue *q,
|
||||
struct videobuf_buffer *buf);
|
||||
int (*mmap_mapper) (struct videobuf_queue *q,
|
||||
struct videobuf_buffer *buf,
|
||||
struct vm_area_struct *vma);
|
||||
};
|
||||
|
||||
struct videobuf_queue {
|
||||
struct mutex vb_lock;
|
||||
struct mutex *ext_lock;
|
||||
spinlock_t *irqlock;
|
||||
struct device *dev;
|
||||
|
||||
wait_queue_head_t wait; /* wait if queue is empty */
|
||||
|
||||
enum v4l2_buf_type type;
|
||||
unsigned int msize;
|
||||
enum v4l2_field field;
|
||||
enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */
|
||||
struct videobuf_buffer *bufs[VIDEO_MAX_FRAME];
|
||||
const struct videobuf_queue_ops *ops;
|
||||
struct videobuf_qtype_ops *int_ops;
|
||||
|
||||
unsigned int streaming:1;
|
||||
unsigned int reading:1;
|
||||
|
||||
/* capture via mmap() + ioctl(QBUF/DQBUF) */
|
||||
struct list_head stream;
|
||||
|
||||
/* capture via read() */
|
||||
unsigned int read_off;
|
||||
struct videobuf_buffer *read_buf;
|
||||
|
||||
/* driver private data */
|
||||
void *priv_data;
|
||||
};
|
||||
|
||||
static inline void videobuf_queue_lock(struct videobuf_queue *q)
|
||||
{
|
||||
if (!q->ext_lock)
|
||||
mutex_lock(&q->vb_lock);
|
||||
}
|
||||
|
||||
static inline void videobuf_queue_unlock(struct videobuf_queue *q)
|
||||
{
|
||||
if (!q->ext_lock)
|
||||
mutex_unlock(&q->vb_lock);
|
||||
}
|
||||
|
||||
int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb,
|
||||
int non_blocking, int intr);
|
||||
int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
|
||||
struct v4l2_framebuffer *fbuf);
|
||||
|
||||
struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q);
|
||||
|
||||
/* Used on videobuf-dvb */
|
||||
void *videobuf_queue_to_vaddr(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *buf);
|
||||
|
||||
void videobuf_queue_core_init(struct videobuf_queue *q,
|
||||
const 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,
|
||||
struct videobuf_qtype_ops *int_ops,
|
||||
struct mutex *ext_lock);
|
||||
int videobuf_queue_is_busy(struct videobuf_queue *q);
|
||||
void videobuf_queue_cancel(struct videobuf_queue *q);
|
||||
|
||||
enum v4l2_field videobuf_next_field(struct videobuf_queue *q);
|
||||
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);
|
||||
|
||||
void videobuf_stop(struct videobuf_queue *q);
|
||||
|
||||
int videobuf_read_start(struct videobuf_queue *q);
|
||||
void videobuf_read_stop(struct videobuf_queue *q);
|
||||
ssize_t videobuf_read_stream(struct videobuf_queue *q,
|
||||
char __user *data, size_t count, loff_t *ppos,
|
||||
int vbihack, int nonblocking);
|
||||
ssize_t videobuf_read_one(struct videobuf_queue *q,
|
||||
char __user *data, size_t count, loff_t *ppos,
|
||||
int nonblocking);
|
||||
__poll_t videobuf_poll_stream(struct file *file,
|
||||
struct videobuf_queue *q,
|
||||
poll_table *wait);
|
||||
|
||||
int videobuf_mmap_setup(struct videobuf_queue *q,
|
||||
unsigned int bcount, unsigned int bsize,
|
||||
enum v4l2_memory memory);
|
||||
int __videobuf_mmap_setup(struct videobuf_queue *q,
|
||||
unsigned int bcount, unsigned int bsize,
|
||||
enum v4l2_memory memory);
|
||||
int videobuf_mmap_free(struct videobuf_queue *q);
|
||||
int videobuf_mmap_mapper(struct videobuf_queue *q,
|
||||
struct vm_area_struct *vma);
|
||||
|
||||
#endif
|
@ -1,30 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* helper functions for physically contiguous capture buffers
|
||||
*
|
||||
* The functions support hardware lacking scatter gather support
|
||||
* (i.e. the buffers must be linear in physical memory)
|
||||
*
|
||||
* Copyright (c) 2008 Magnus Damm
|
||||
*/
|
||||
#ifndef _VIDEOBUF_DMA_CONTIG_H
|
||||
#define _VIDEOBUF_DMA_CONTIG_H
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <media/videobuf-core.h>
|
||||
|
||||
void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
|
||||
const 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,
|
||||
struct mutex *ext_lock);
|
||||
|
||||
dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf);
|
||||
void videobuf_dma_contig_free(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *buf);
|
||||
|
||||
#endif /* _VIDEOBUF_DMA_CONTIG_H */
|
@ -1,102 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* helper functions for SG DMA video4linux capture buffers
|
||||
*
|
||||
* The functions expect the hardware being able to scatter gather
|
||||
* (i.e. the buffers are not linear in physical memory, but fragmented
|
||||
* into PAGE_SIZE chunks). They also assume the driver does not need
|
||||
* to touch the video data.
|
||||
*
|
||||
* (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org>
|
||||
*
|
||||
* Highly based on video-buf written originally by:
|
||||
* (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
|
||||
* (c) 2006 Mauro Carvalho Chehab, <mchehab@kernel.org>
|
||||
* (c) 2006 Ted Walther and John Sokol
|
||||
*/
|
||||
#ifndef _VIDEOBUF_DMA_SG_H
|
||||
#define _VIDEOBUF_DMA_SG_H
|
||||
|
||||
#include <media/videobuf-core.h>
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* A small set of helper functions to manage buffers (both userland
|
||||
* and kernel) for DMA.
|
||||
*
|
||||
* videobuf_dma_init_*()
|
||||
* creates a buffer. The userland version takes a userspace
|
||||
* pointer + length. The kernel version just wants the size and
|
||||
* does memory allocation too using vmalloc_32().
|
||||
*
|
||||
* videobuf_dma_*()
|
||||
* see Documentation/core-api/dma-api-howto.rst, these functions to
|
||||
* basically the same. The map function does also build a
|
||||
* scatterlist for the buffer (and unmap frees it ...)
|
||||
*
|
||||
* videobuf_dma_free()
|
||||
* no comment ...
|
||||
*
|
||||
*/
|
||||
|
||||
struct videobuf_dmabuf {
|
||||
u32 magic;
|
||||
|
||||
/* for userland buffer */
|
||||
int offset;
|
||||
size_t size;
|
||||
struct page **pages;
|
||||
|
||||
/* for kernel buffers */
|
||||
void *vaddr;
|
||||
struct page **vaddr_pages;
|
||||
dma_addr_t *dma_addr;
|
||||
struct device *dev;
|
||||
|
||||
/* for overlay buffers (pci-pci dma) */
|
||||
dma_addr_t bus_addr;
|
||||
|
||||
/* common */
|
||||
struct scatterlist *sglist;
|
||||
int sglen;
|
||||
unsigned long nr_pages;
|
||||
int direction;
|
||||
};
|
||||
|
||||
struct videobuf_dma_sg_memory {
|
||||
u32 magic;
|
||||
|
||||
/* for mmap'ed buffers */
|
||||
struct videobuf_dmabuf dma;
|
||||
};
|
||||
|
||||
/*
|
||||
* Scatter-gather DMA buffer API.
|
||||
*
|
||||
* These functions provide a simple way to create a page list and a
|
||||
* scatter-gather list from a kernel, userspace of physical address and map the
|
||||
* memory for DMA operation.
|
||||
*
|
||||
* Despite the name, this is totally unrelated to videobuf, except that
|
||||
* videobuf-dma-sg uses the same API internally.
|
||||
*/
|
||||
int videobuf_dma_free(struct videobuf_dmabuf *dma);
|
||||
|
||||
int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma);
|
||||
struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf);
|
||||
|
||||
void *videobuf_sg_alloc(size_t size);
|
||||
|
||||
void videobuf_queue_sg_init(struct videobuf_queue *q,
|
||||
const 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,
|
||||
struct mutex *ext_lock);
|
||||
|
||||
#endif /* _VIDEOBUF_DMA_SG_H */
|
||||
|
@ -1,43 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* helper functions for vmalloc capture buffers
|
||||
*
|
||||
* The functions expect the hardware being able to scatter gather
|
||||
* (i.e. the buffers are not linear in physical memory, but fragmented
|
||||
* into PAGE_SIZE chunks). They also assume the driver does not need
|
||||
* to touch the video data.
|
||||
*
|
||||
* (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org>
|
||||
*/
|
||||
#ifndef _VIDEOBUF_VMALLOC_H
|
||||
#define _VIDEOBUF_VMALLOC_H
|
||||
|
||||
#include <media/videobuf-core.h>
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
struct videobuf_vmalloc_memory {
|
||||
u32 magic;
|
||||
|
||||
void *vaddr;
|
||||
|
||||
/* remap_vmalloc_range seems to need to run
|
||||
* after mmap() on some cases */
|
||||
struct vm_area_struct *vma;
|
||||
};
|
||||
|
||||
void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
|
||||
const 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,
|
||||
struct mutex *ext_lock);
|
||||
|
||||
void *videobuf_to_vmalloc(struct videobuf_buffer *buf);
|
||||
|
||||
void videobuf_vmalloc_free(struct videobuf_buffer *buf);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user