arch/sim: add v4l2 driver for sim

communicate with Linux host v4l2 drivers

Signed-off-by: Peter Bee <bijunda1@xiaomi.com>
This commit is contained in:
Peter Bee
2022-11-22 16:13:36 +08:00
committed by Xiang Xiao
parent 77504aa1b5
commit a5a1a68a25
8 changed files with 800 additions and 10 deletions

View File

@@ -265,6 +265,33 @@ endchoice
endif
config SIM_VIDEO
bool "Simulated video support"
depends on VIDEO
default y
if SIM_VIDEO
choice
prompt "Simulated video device type"
default SIM_VIDEO_V4L2
config SIM_VIDEO_V4L2
bool "V4L2 camera support on sim"
depends on HOST_LINUX
endchoice
config HOST_VIDEO_DEV_PATH
string "Host video device path"
default "/dev/video0"
config SIM_VIDEO_DEV_PATH
string "NuttX video device path"
default "/dev/video"
endif
menu "Simulated Graphics/Input"
config SIM_X11FB

View File

@@ -204,6 +204,12 @@ ifeq ($(CONFIG_SIM_SOUND_ALSA),y)
STDLIBS += -lmad
endif
ifeq ($(CONFIG_SIM_VIDEO_V4L2),y)
HOSTSRCS += sim_host_v4l2.c
CSRCS += sim_video.c
STDLIBS += -lv4l2
endif
ifeq ($(CONFIG_SIM_HOSTFS),y)
HOSTSRCS += sim_hostfs.c

View File

@@ -0,0 +1,364 @@
/****************************************************************************
* arch/sim/src/sim/posix/sim_host_v4l2.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include "sim_hostvideo.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MAX_REQBUFS 3
#define WARN(fmt, ...) \
syslog(LOG_WARNING, "sim_host_video: " fmt "\n", ##__VA_ARGS__)
/****************************************************************************
* Private Types
****************************************************************************/
struct host_video_dev_s
{
int fd;
void *addrs[MAX_REQBUFS];
size_t buflen[MAX_REQBUFS];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
static int host_video_ioctl(int fd, int request, void *arg)
{
int r;
do
{
r = ioctl(fd, request, arg);
}
while (-1 == r && EINTR == errno);
return r;
}
/****************************************************************************
* Public Functions
****************************************************************************/
bool host_video_is_available(const char *host_video_dev_path)
{
return access(host_video_dev_path, F_OK) == 0;
}
struct host_video_dev_s *host_video_init(const char *host_video_dev_path)
{
int fd;
struct host_video_dev_s *vdev;
fd = open(host_video_dev_path, O_RDWR | O_NONBLOCK);
if (fd < 0)
{
perror(host_video_dev_path);
return NULL;
}
vdev = calloc(1, sizeof(*vdev));
if (vdev == NULL)
{
perror("host_video_init failed");
close(fd);
return NULL;
}
vdev->fd = fd;
return vdev;
}
int host_video_dq_buf(struct host_video_dev_s *vdev, uint8_t *addr,
uint32_t size)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
/* Dequeue a buffer */
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_DQBUF, &buf))
{
switch (errno)
{
case EAGAIN:
/* No buffer in the outgoing queue */
return 0;
default:
perror("VIDIOC_DQBUF");
return -errno;
}
}
if (size > buf.bytesused)
{
size = buf.bytesused;
}
memcpy(addr, vdev->addrs[buf.index], size);
if (-1 == ioctl(vdev->fd, VIDIOC_QBUF, &buf))
{
perror("VIDIOC_QBUF");
return -errno;
}
return size;
}
int host_video_uninit(struct host_video_dev_s *vdev)
{
if (vdev != NULL)
{
close(vdev->fd);
free(vdev);
}
return 0;
}
int host_video_start_capture(struct host_video_dev_s *vdev)
{
struct v4l2_buffer buf;
struct v4l2_requestbuffers reqbuf;
enum v4l2_buf_type type;
int i;
/* VIDIOC_REQBUFS initiate user pointer I/O */
memset(&reqbuf, 0, sizeof(reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = MAX_REQBUFS;
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_REQBUFS, &reqbuf))
{
perror("VIDIOC_REQBUFS");
return -errno;
}
if (reqbuf.count < 2)
{
errno = ENOMEM;
perror("Not enough buffers");
return -ENOMEM;
}
for (i = 0; i < reqbuf.count; i++)
{
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_QUERYBUF, &buf))
{
perror("VIDIOC_QUERYBUF");
goto err_out;
}
vdev->addrs[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
MAP_SHARED, vdev->fd, buf.m.offset);
if (vdev->addrs[i] == MAP_FAILED)
{
perror("Mmap failed");
goto err_out;
}
vdev->buflen[i] = buf.length;
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_QBUF, &buf))
{
perror("VIDIOC_QBUF");
goto err_out;
}
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_STREAMON, &type))
{
perror("VIDIOC_STREAMON");
goto err_out;
}
return 0;
err_out:
while (i--)
{
munmap(vdev->addrs[i], vdev->buflen[i]);
vdev->addrs[i] = NULL;
vdev->buflen[i] = 0;
}
return -errno;
}
int host_video_stop_capture(struct host_video_dev_s *vdev)
{
enum v4l2_buf_type type;
int i;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_STREAMOFF, &type))
{
perror("VIDIOC_STREAMOFF");
return -errno;
}
for (i = 0; i < MAX_REQBUFS; i++)
{
if (vdev->buflen[i] == 0)
{
break;
}
munmap(vdev->addrs[i], vdev->buflen[i]);
vdev->addrs[i] = NULL;
vdev->buflen[i] = 0;
}
return 0;
}
int host_video_set_fmt(struct host_video_dev_s *vdev,
uint16_t width, uint16_t height, uint32_t fmt,
uint32_t denom, uint32_t numer)
{
struct v4l2_format v4l2_fmt;
struct v4l2_streamparm streamparm;
memset(&v4l2_fmt, 0, sizeof(v4l2_fmt));
v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_fmt.fmt.pix.width = width;
v4l2_fmt.fmt.pix.height = height;
v4l2_fmt.fmt.pix.pixelformat = fmt;
v4l2_fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_S_FMT, &v4l2_fmt))
{
perror("VIDIOC_S_FMT");
return -errno;
}
memset(&streamparm, 0, sizeof(streamparm));
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_G_PARM, &streamparm))
{
perror("VIDIOC_G_PARM");
return -errno;
}
streamparm.parm.capture.capturemode |= V4L2_CAP_TIMEPERFRAME;
streamparm.parm.capture.timeperframe.numerator = numer;
streamparm.parm.capture.timeperframe.denominator = denom;
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_S_PARM, &streamparm))
{
perror("VIDIOC_S_PARM");
return -errno;
}
return 0;
}
int host_video_try_fmt(struct host_video_dev_s *vdev,
uint16_t width, uint16_t height, uint32_t fmt,
uint32_t denom, uint32_t numer)
{
struct v4l2_format v4l2_fmt;
struct v4l2_frmivalenum v4l2_frmival;
memset(&v4l2_fmt, 0, sizeof(v4l2_fmt));
v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_fmt.fmt.pix.width = width;
v4l2_fmt.fmt.pix.height = height;
v4l2_fmt.fmt.pix.pixelformat = fmt;
v4l2_fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (-1 == host_video_ioctl(vdev->fd, VIDIOC_TRY_FMT, &v4l2_fmt))
{
perror("VIDIOC_TRY_FMT");
return -errno;
}
if (v4l2_fmt.fmt.pix.pixelformat != fmt)
{
WARN("Pixel format not supported");
return -EINVAL;
}
/* Need not check frame interval for STILL type */
if (!denom)
{
memset(&v4l2_frmival, 0, sizeof(v4l2_frmival));
v4l2_frmival.width = width;
v4l2_frmival.height = height;
v4l2_frmival.pixel_format = fmt;
while (host_video_ioctl(vdev->fd, VIDIOC_ENUM_FRAMEINTERVALS,
&v4l2_frmival) == 0)
{
if (v4l2_frmival.type == V4L2_FRMSIZE_TYPE_DISCRETE &&
v4l2_frmival.discrete.denominator == denom &&
v4l2_frmival.discrete.numerator == numer)
{
return 0;
}
v4l2_frmival.index++;
}
WARN("Invalid frame interval, fallback to default");
}
return 0;
}

View File

@@ -0,0 +1,55 @@
/****************************************************************************
* arch/sim/src/sim/sim_hostvideo.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __ARCH_SIM_SRC_SIM_SIM_HOSTVIDEO_H
#define __ARCH_SIM_SRC_SIM_SIM_HOSTVIDEO_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdbool.h>
#include <stdint.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
struct host_video_dev_s;
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
bool host_video_is_available(const char *host_video_dev_path);
struct host_video_dev_s *host_video_init(const char *host_video_dev_path);
int host_video_uninit(struct host_video_dev_s *vdev);
int host_video_start_capture(struct host_video_dev_s *vdev);
int host_video_stop_capture(struct host_video_dev_s *vdev);
int host_video_dq_buf(struct host_video_dev_s *vdev, uint8_t *addr,
uint32_t size);
int host_video_set_fmt(struct host_video_dev_s *vdev,
uint16_t width, uint16_t height, uint32_t fmt,
uint32_t denom, uint32_t numer);
int host_video_try_fmt(struct host_video_dev_s *vdev,
uint16_t width, uint16_t height, uint32_t fmt,
uint32_t denom, uint32_t numer);
#endif /* __ARCH_SIM_SRC_SIM_SIM_HOSTVIDEO_H */

View File

@@ -195,6 +195,10 @@ static int sim_loop_task(int argc, char **argv)
sim_audio_loop();
#endif
#ifdef CONFIG_SIM_VIDEO
sim_video_loop();
#endif
#ifdef CONFIG_MOTOR_FOC_DUMMY
/* Update simulated FOC device */

View File

@@ -357,6 +357,13 @@ struct spi_dev_s *sim_spi_initialize(const char *filename);
int sim_spi_uninitialize(struct spi_dev_s *dev);
#endif
/* up_video.c ***************************************************************/
#ifdef CONFIG_SIM_VIDEO
int sim_video_initialize(void);
void sim_video_loop(void);
#endif
/* Debug ********************************************************************/
#ifdef CONFIG_STACK_COLORATION

View File

@@ -0,0 +1,314 @@
/****************************************************************************
* arch/sim/src/sim/sim_video.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <errno.h>
#include <string.h>
#include <nuttx/video/imgsensor.h>
#include <nuttx/video/imgdata.h>
#include <nuttx/video/video.h>
#include "sim_hostvideo.h"
#include "sim_internal.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
typedef struct
{
imgdata_capture_t capture_cb;
uint32_t buf_size;
uint8_t *next_buf;
struct host_video_dev_s *vdev;
} sim_video_priv_t;
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Video image sensor operations */
static bool sim_video_is_available(void);
static int sim_video_init(void);
static int sim_video_uninit(void);
static const char *sim_video_get_driver_name(void);
static int sim_video_validate_frame_setting(imgsensor_stream_type_t type,
uint8_t nr_datafmt,
imgsensor_format_t *datafmts,
imgsensor_interval_t *interval);
static int sim_video_start_capture(imgsensor_stream_type_t type,
uint8_t nr_datafmt,
imgsensor_format_t *datafmts,
imgsensor_interval_t *interval);
static int sim_video_stop_capture(imgsensor_stream_type_t type);
/* Video image data operations */
static int sim_video_data_init(void);
static int sim_video_data_uninit(void);
static int sim_video_data_validate_frame_setting(uint8_t nr_datafmt,
imgdata_format_t *datafmt,
imgdata_interval_t *interv);
static int sim_video_data_start_capture(uint8_t nr_datafmt,
imgdata_format_t *datafmt,
imgdata_interval_t *interval,
imgdata_capture_t callback);
static int sim_video_data_stop_capture(void);
static int sim_video_data_validate_buf(uint8_t *addr, uint32_t size);
static int sim_video_data_set_buf(uint8_t *addr, uint32_t size);
static uint32_t imgdata_fmt_to_v4l2(uint32_t pixelformat);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct imgsensor_ops_s g_sim_video_ops =
{
.is_available = sim_video_is_available,
.init = sim_video_init,
.uninit = sim_video_uninit,
.get_driver_name = sim_video_get_driver_name,
.validate_frame_setting = sim_video_validate_frame_setting,
.start_capture = sim_video_start_capture,
.stop_capture = sim_video_stop_capture,
};
static const struct imgdata_ops_s g_sim_video_data_ops =
{
.init = sim_video_data_init,
.uninit = sim_video_data_uninit,
.validate_buf = sim_video_data_validate_buf,
.set_buf = sim_video_data_set_buf,
.validate_frame_setting = sim_video_data_validate_frame_setting,
.start_capture = sim_video_data_start_capture,
.stop_capture = sim_video_data_stop_capture,
};
static sim_video_priv_t g_sim_video_priv;
/****************************************************************************
* Private Functions
****************************************************************************/
/* Sensor op functions are mostly dummy */
static bool sim_video_is_available(void)
{
return host_video_is_available(CONFIG_HOST_VIDEO_DEV_PATH);
}
static int sim_video_init(void)
{
return 0;
}
static int sim_video_uninit(void)
{
return 0;
}
static const char *sim_video_get_driver_name(void)
{
return "V4L2 NuttX Sim Driver";
}
static int sim_video_validate_frame_setting(imgsensor_stream_type_t type,
uint8_t nr_fmt,
imgsensor_format_t *fmt,
imgsensor_interval_t *interval)
{
return 0;
}
static int sim_video_start_capture(imgsensor_stream_type_t type,
uint8_t nr_fmt,
imgsensor_format_t *fmt,
imgsensor_interval_t *interval)
{
return 0;
}
static int sim_video_stop_capture(imgsensor_stream_type_t type)
{
return 0;
}
/* Data op functions do all the real work */
static int sim_video_data_init(void)
{
memset(&g_sim_video_priv, 0, sizeof(g_sim_video_priv));
g_sim_video_priv.vdev = host_video_init(CONFIG_HOST_VIDEO_DEV_PATH);
if (g_sim_video_priv.vdev == NULL)
{
return -ENODEV;
}
return 0;
}
static int sim_video_data_uninit(void)
{
return host_video_uninit(g_sim_video_priv.vdev);
}
static int sim_video_data_validate_buf(uint8_t *addr, uint32_t size)
{
if (!addr || ((uintptr_t)(addr) & 0x1f))
{
return -EINVAL;
}
return 0;
}
static int sim_video_data_set_buf(uint8_t *addr, uint32_t size)
{
g_sim_video_priv.next_buf = addr;
g_sim_video_priv.buf_size = size;
return 0;
}
static int sim_video_data_validate_frame_setting(uint8_t nr_datafmt,
imgdata_format_t *datafmt,
imgdata_interval_t *interv)
{
uint32_t v4l2_fmt;
if (nr_datafmt > 1)
{
return -ENOTSUP;
}
v4l2_fmt = imgdata_fmt_to_v4l2(datafmt->pixelformat);
return host_video_try_fmt(g_sim_video_priv.vdev, datafmt->width,
datafmt->height, v4l2_fmt, interv->denominator,
interv->numerator);
}
static int sim_video_data_start_capture(uint8_t nr_datafmt,
imgdata_format_t *datafmt,
imgdata_interval_t *interval,
imgdata_capture_t callback)
{
int ret;
ret = host_video_set_fmt(g_sim_video_priv.vdev,
datafmt[IMGDATA_FMT_MAIN].width,
datafmt[IMGDATA_FMT_MAIN].height,
imgdata_fmt_to_v4l2(
datafmt[IMGDATA_FMT_MAIN].pixelformat),
interval->denominator, interval->numerator);
if (ret < 0)
{
return ret;
}
g_sim_video_priv.capture_cb = callback;
return host_video_start_capture(g_sim_video_priv.vdev);
}
static int sim_video_data_stop_capture(void)
{
g_sim_video_priv.next_buf = NULL;
return host_video_stop_capture(g_sim_video_priv.vdev);
}
/* Helper functions */
static uint32_t imgdata_fmt_to_v4l2(uint32_t pixelformat)
{
uint32_t fourcc;
switch (pixelformat)
{
case IMGDATA_PIX_FMT_YUV420P:
fourcc = V4L2_PIX_FMT_YUV420;
break;
case IMGDATA_PIX_FMT_YUYV:
fourcc = V4L2_PIX_FMT_YUYV;
break;
case IMGDATA_PIX_FMT_JPEG_WITH_SUBIMG:
fourcc = V4L2_PIX_FMT_JPEG;
break;
case IMGDATA_PIX_FMT_JPEG:
fourcc = V4L2_PIX_FMT_JPEG;
break;
case IMGDATA_PIX_FMT_RGB565:
fourcc = V4L2_PIX_FMT_RGB565;
break;
case IMGDATA_PIX_FMT_UYVY:
fourcc = V4L2_PIX_FMT_UYVY;
break;
default:
/* Unsupported format */
fourcc = 0;
}
return fourcc;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int sim_video_initialize(void)
{
imgsensor_register(&g_sim_video_ops);
imgdata_register(&g_sim_video_data_ops);
return 0;
}
int sim_video_uninitialize(void)
{
return 0;
}
void sim_video_loop(void)
{
int ret;
if (g_sim_video_priv.next_buf)
{
ret = host_video_dq_buf(g_sim_video_priv.vdev,
g_sim_video_priv.next_buf,
g_sim_video_priv.buf_size);
if (ret > 0)
{
g_sim_video_priv.capture_cb(0, ret);
}
}
}

View File

@@ -47,6 +47,7 @@
#include <nuttx/serial/uart_rpmsg.h>
#include <nuttx/timers/oneshot.h>
#include <nuttx/video/fb.h>
#include <nuttx/video/video.h>
#include <nuttx/timers/oneshot.h>
#include <nuttx/wireless/pktradio.h>
#include <nuttx/wireless/bluetooth/bt_null.h>
@@ -291,6 +292,18 @@ int sim_bringup(void)
}
#endif
#ifdef CONFIG_SIM_VIDEO
/* Initialize and register the simulated video driver */
ret = video_initialize(CONFIG_SIM_VIDEO_DEV_PATH);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: video_initialize() failed: %d\n", ret);
}
sim_video_initialize();
#endif
#ifdef CONFIG_LCD
ret = board_lcd_initialize();