#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <malloc.h>
#include <math.h>
#include <string.h>
#include <sys/mman.h>
#include <errno.h>
#include <assert.h>
#include <poll.h>
#include "ion.h"
#include "dma-buf.h"
#define CLEAR(x) memset(&(x), 0, sizeof(x))
#define BUF_COUNT 3
// 当前实验环境中,图像格式最多3个plane
#define MAX_PLANE 3
struct user_plane {
// 缓冲区内存起始虚拟地址
void *start;
// 缓冲区内存长度
int len;
// 缓冲区内存起始物理地址
unsigned long paddr;
// 根据缓冲区内存类型维护的元数据
union {
// mmap类型
unsigned int offset;
// userptr类型
unsigned long userptr;
// dmabuf类型
int fd;
} m;
};
struct user_buf {
// 缓冲区内存的plane个数
int planes_num;
// 缓冲区内存每个plane的信息
struct user_plane planes[MAX_PLANE];
};
struct user_buf user_bufs[BUF_COUNT];
static int alloc_ion_buffer(int fd, int len, int *dmabuf_fd,
unsigned long *paddr, unsigned long *vaddr)
{
struct ion_allocation_data alloc_data;
unsigned int heap_id = 4;
int ret = 0;
if (!dmabuf_fd || !paddr || !vaddr) {
printf("invalid argument\n");
goto err;
}
CLEAR(alloc_data);
alloc_data.len = len;
alloc_data.heap_id_mask = heap_id;
alloc_data.flags = ION_HEAP_TYPE_DMA;
ret = ioctl(fd, ION_IOC_ALLOC, &alloc_data);
if (ret < 0) {
perror("ION_IOC_ALLOC error");
goto err;
}
*dmabuf_fd = alloc_data.fd;
printf("============ after ION_IOC_ALLOC ============\n");
printf("alloc_data.len = 0x%llx\n", alloc_data.len);
printf("alloc_data.heap_id_mask = 0x%x\n", alloc_data.heap_id_mask);
printf("alloc_data.flags = %d\n", alloc_data.flags);
printf("alloc_data.fd = %d\n", alloc_data.fd);
printf("=============================================\n\n");
// 获取分配内存的物理地址
struct dma_buf_phys dma_phys;
CLEAR(dma_phys);
ret = ioctl(alloc_data.fd, DMA_BUF_IOCTL_PHYS, &dma_phys);
if (ret < 0) {
perror("DMA_BUF_IOCTL_PHYS error");
goto err;
}
*paddr = dma_phys.phys;
printf("============ after DMA_BUF_IOCTL_PHYS ============\n");
printf("sizeof(struct dma_buf_phys) = %ld\n", sizeof(struct dma_buf_phys));
printf("dma_phys.phys = 0x%lx\n", dma_phys.phys);
printf("==================================================\n\n");
// 将分配的内存映射到进程用户空间
char *virt_addr = NULL;
virt_addr = (char *)mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED,
alloc_data.fd, 0);
if (vaddr == MAP_FAILED) {
perror("mmap error\n");
goto err;
}
*vaddr = (unsigned long)virt_addr;
printf("============ after mmap ============\n");
printf("virt_addr = %p\n", virt_addr);
printf("====================================\n\n");
return 0;
err:
return -1;
}
/*
* buffer_export: 导出single-planar格式的mmap类型缓冲区内存
* v4lfd: V4L2设备文件描述符
* bt: 缓冲区类型
* index: 要导出的缓冲区序号
* dmafd[OUT]: 返回导出的dmabuf文件描述符
* 返回值: 成功返回0,失败返回-1
*/
int buffer_export(int v4lfd, enum v4l2_buf_type bt, int index, int *dmafd)
{
struct v4l2_exportbuffer expbuf;
memset(&expbuf, 0, sizeof(expbuf));
expbuf.type = bt;
expbuf.index = index;
if (ioctl(v4lfd, VIDIOC_EXPBUF, &expbuf) == -1) {
perror("VIDIOC_EXPBUF");
return -1;
}
*dmafd = expbuf.fd;
return 0;
}
/*
* buffer_export_mp: 导出single-planar格式的mmap类型缓冲区内存
* v4lfd: V4L2设备文件描述符
* bt: 缓冲区类型
* index: 要导出的缓冲区序号
* n_planes: 要导出缓冲区的plane个数
* dmafd[OUT]: 返回导出的dmabuf文件描述符数组
* 返回值: 成功返回0,失败返回-1
*/
int buffer_export_mp(int v4lfd, enum v4l2_buf_type bt, int index,
int dmafd[], int n_planes)
{
int i = 0;
// 逐个plane导出
for (i = 0; i < n_planes; ++i) {
struct v4l2_exportbuffer expbuf;
memset(&expbuf, 0, sizeof(expbuf));
expbuf.type = bt;
expbuf.index = index;
expbuf.plane = i;
if (ioctl(v4lfd, VIDIOC_EXPBUF, &expbuf) == -1) {
perror("VIDIOC_EXPBUF");
while (i)
close(dmafd[--i]);
return -1;
}
dmafd[i] = expbuf.fd;
}
return 0;
}
int main(void)
{
int fd = 0;
int ret = 0;
int i = 0;
// 打开设备节点
fd = open("/dev/video4", O_RDWR);
if (fd < 0) {
perror("opend device error");
return -1;
}
printf("fd = %d\n\n", fd);
// 获取设备能力
struct v4l2_capability cap;
CLEAR(cap);
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
if (ret < 0) {
perror("VIDIOC_QUERYCAP error");
goto err;
}
printf("======== uvc_camera capability =========\n");
printf("driver = %s\n", cap.driver);
printf("card = %s\n", cap.card);
printf("bus_info = %s\n", cap.bus_info);
printf("version = %u.%u.%u\n", (cap.version >> 16) & 0xFF,
(cap.version >> 8) & 0xFF, cap.version & 0xFF);
printf("capability = 0x%x\n", cap.capabilities);
printf("device_caps = 0x%x\n", cap.device_caps);
printf("========================================\n\n");
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
printf("%s is not a video capture device\n\n", cap.card);
goto err;
}
if (!(cap.capabilities & V4L2_CAP_READWRITE))
printf("%s does not support read/write io\n\n", cap.card);
if (!(cap.capabilities & V4L2_CAP_STREAMING))
printf("%s does not support stream io\n\n", cap.card);
// 枚举设备格式
struct v4l2_fmtdesc fmtdesc;
CLEAR(fmtdesc);
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
i = 0;
printf("======== uvc_camera V4L2_BUF_TYPE_VIDEO_CAPTURE format ========\n");
while (1) {
fmtdesc.index = i++;
ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
if (ret < 0) {
if (errno != EINVAL) {
perror("VIDIOC_ENUM_FMT error");
goto err;
} else {
printf("enumerate end\n");
break;
}
}
printf("index = %d\n", fmtdesc.index);
printf("type = %d\n", fmtdesc.type);
printf("flags = 0x%x\n", fmtdesc.flags);
printf("description = %s\n", fmtdesc.description);
printf("pixelformat = %c.%c.%c.%c\n\n", fmtdesc.pixelformat & 0xFF,
(fmtdesc.pixelformat >> 8) & 0xFF, (fmtdesc.pixelformat >> 16) & 0xFF,
(fmtdesc.pixelformat >> 24) & 0xFF);
}
printf("===============================================================\n\n");
// 枚举指定格式的分辨率
struct v4l2_frmsizeenum frmsize;
CLEAR(frmsize);
frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
i = 0;
printf("======== uvc_camera V4L2_PIX_FMT_MJPEG framesize ========\n");
while (1) {
frmsize.index = i++;
ret = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize);
if (ret < 0) {
if (errno != EINVAL) {
perror("VIDIOC_ENUM_FRAMESIZES error");
goto err;
} else {
printf("enumerate end\n");
break;
}
}
printf("index = %d\n", frmsize.index);
switch (frmsize.type) {
case V4L2_FRMSIZE_TYPE_DISCRETE:
printf("V4L2_FRMSIZE_TYPE_DISCRETE\n");
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
printf("V4L2_FRMSIZE_TYPE_CONTINUOUS\n");
break;
case V4L2_FRMSIZE_TYPE_STEPWISE:
printf("V4L2_FRMSIZE_TYPE_STEPWISE\n");
break;
default:
printf("invalid type\n");
break;
}
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
printf("width = %d\n", frmsize.discrete.width);
printf("height = %d\n\n", frmsize.discrete.height);
} else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE ||
frmsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
printf("min_width = %d\n", frmsize.stepwise.min_width);
pr