nuttx/drivers/video/goldfish_camera.c
yangyalei fd776e8cee fix wait after vfork return error
vfork use waitpid hang father process,
but waitpid release child processs information by default.
So when user call wait, it return errno 10.

Signed-off-by: yangyalei <yangyalei@xiaomi.com>
2023-09-08 00:53:56 +03:00

781 lines
20 KiB
C

/****************************************************************************
* drivers/video/goldfish_camera.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 <debug.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nuttx/nuttx.h>
#include <nuttx/kthread.h>
#include <nuttx/video/imgsensor.h>
#include <nuttx/video/imgdata.h>
#include <nuttx/video/video.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define GOLDFISH_CAMERA_MAX_NUMBER 8
#define GOLDFISH_CAMERA_SIZE_MAX_NUMBER 32
/****************************************************************************
* Private Types
****************************************************************************/
typedef struct
{
uint16_t width;
uint16_t height;
} goldfish_camera_size_t;
typedef struct
{
char name[32];
uint32_t channel;
uint32_t pix;
char dir[32];
goldfish_camera_size_t size[GOLDFISH_CAMERA_SIZE_MAX_NUMBER];
} goldfish_camera_info_t;
typedef struct
{
struct imgdata_s data;
struct imgsensor_s sensor;
imgdata_capture_t capture_cb;
FAR void *capture_arg;
uint32_t buf_size;
FAR uint8_t *next_buf;
struct file file;
sem_t run;
goldfish_camera_info_t info;
bool streaming;
pid_t pid;
} goldfish_camera_priv_t;
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Video image sensor operations */
static bool goldfish_camera_is_available(FAR struct imgsensor_s *sensor);
static int goldfish_camera_init(FAR struct imgsensor_s *sensor);
static int goldfish_camera_uninit(FAR struct imgsensor_s *sensor);
static FAR const char *
goldfish_camera_get_driver_name(FAR struct imgsensor_s *sensor);
static int
goldfish_camera_validate_frame_setting(FAR struct imgsensor_s *sensor,
imgsensor_stream_type_t type,
uint8_t nr_datafmt,
FAR imgsensor_format_t *datafmts,
FAR imgsensor_interval_t *interval);
static int goldfish_camera_start_capture(FAR struct imgsensor_s *sensor,
imgsensor_stream_type_t type,
uint8_t nr_datafmt,
FAR imgsensor_format_t *datafmts,
FAR imgsensor_interval_t *interval);
static int goldfish_camera_stop_capture(FAR struct imgsensor_s *sensor,
imgsensor_stream_type_t type);
/* Video image data operations */
static int goldfish_camera_data_init(FAR struct imgdata_s *data);
static int goldfish_camera_data_uninit(FAR struct imgdata_s *data);
static int
goldfish_camera_data_validate_frame_setting(FAR struct imgdata_s *data,
uint8_t nr_datafmt,
FAR imgdata_format_t *datafmt,
FAR imgdata_interval_t *interval);
static int
goldfish_camera_data_start_capture(FAR struct imgdata_s *data,
uint8_t nr_datafmt,
FAR imgdata_format_t *datafmt,
FAR imgdata_interval_t *interval,
imgdata_capture_t callback,
FAR void *arg);
static int goldfish_camera_data_stop_capture(FAR struct imgdata_s *data);
static int goldfish_camera_data_set_buf(FAR struct imgdata_s *data,
FAR uint8_t *addr,
uint32_t size);
/****************************************************************************
* Private Data
****************************************************************************/
/* Communication service for qemu-pipe */
static const char g_qemudservice[] = "qemud";
/* Camera service. */
static const char g_cameraservice[] = "camera";
static const struct imgsensor_ops_s g_goldfish_camera_ops =
{
.is_available = goldfish_camera_is_available,
.init = goldfish_camera_init,
.uninit = goldfish_camera_uninit,
.get_driver_name = goldfish_camera_get_driver_name,
.validate_frame_setting = goldfish_camera_validate_frame_setting,
.start_capture = goldfish_camera_start_capture,
.stop_capture = goldfish_camera_stop_capture,
};
static const struct imgdata_ops_s g_goldfish_camera_data_ops =
{
.init = goldfish_camera_data_init,
.uninit = goldfish_camera_data_uninit,
.set_buf = goldfish_camera_data_set_buf,
.validate_frame_setting = goldfish_camera_data_validate_frame_setting,
.start_capture = goldfish_camera_data_start_capture,
.stop_capture = goldfish_camera_data_stop_capture,
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int goldfish_camera_read_fully(FAR struct file *file,
FAR void *buffer,
size_t size)
{
FAR char *p = (FAR char *)buffer;
while (size > 0)
{
ssize_t n = file_read(file, p, size);
if (n < 0)
{
return n;
}
p += n;
size -= n;
}
return 0;
}
static int goldfish_camera_write_fully(FAR struct file *file,
FAR const void *buffer,
size_t size)
{
FAR const char *p = (FAR const char *)buffer;
while (size > 0)
{
ssize_t n = file_write(file, p, size);
if (n < 0)
{
return n;
}
p += n;
size -= n;
}
return 0;
}
static int goldfish_camera_send(FAR struct file *file,
FAR const char *format,
...)
{
char buf[256];
va_list ap;
int len;
va_start(ap, format);
len = vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
if (len < 0)
{
return len;
}
return goldfish_camera_write_fully(file, buf, len + 1);
}
static ssize_t goldfish_camera_recv(FAR struct file *file,
FAR void **data)
{
char buf[9];
size_t payload_size;
bool empty = !*data;
int ret;
ret = goldfish_camera_read_fully(file, buf, 8);
if (ret < 0)
{
return ret;
}
buf[8] = '\0';
payload_size = strtoul(buf, NULL, 16);
if (payload_size < 3)
{
return -EINVAL;
}
ret = goldfish_camera_read_fully(file, buf, 3);
if (ret < 0)
{
return ret;
}
if (memcmp(buf, "ok", 2) != 0)
{
return -EINVAL;
}
if (buf[2] == '\0')
{
return 0;
}
payload_size -= 3;
if (payload_size == 0)
{
return 0;
}
if (empty)
{
*data = kmm_malloc(payload_size);
if (*data == NULL)
{
return -ENOMEM;
}
}
ret = goldfish_camera_read_fully(file, *data, payload_size);
if (ret < 0)
{
if (empty)
{
kmm_free(*data);
}
return ret;
}
return payload_size;
}
static ssize_t goldfish_camera_get_list(FAR goldfish_camera_priv_t **priv,
size_t size)
{
FAR void *cameradata = NULL;
struct file file;
FAR char *data;
size_t count = 0;
int ret;
/* Queries list of cameras connected to the host. */
ret = file_open(&file,
CONFIG_GOLDFISH_CAMERA_PIPE_PATH,
O_RDWR);
if (ret < 0)
{
verr("Failed to open: %s: %d\n",
CONFIG_GOLDFISH_CAMERA_PIPE_PATH, ret);
return ret;
}
ret = goldfish_camera_send(&file,
"pipe:%s:%s",
g_qemudservice,
g_cameraservice);
if (ret < 0)
{
goto out;
}
ret = goldfish_camera_send(&file, "list");
if (ret < 0)
{
goto out;
}
ret = goldfish_camera_recv(&file, &cameradata);
if (ret < 0)
{
goto out;
}
/* Parse string
*
* For example:
* 'name=virtualscene channel=0 pix=876758866 dir=back framedims=640x480'
*
* Use the strstr function to parse name/channel/pix/dir/framedims
* of multiple devices by "name";
*
* Use the strchr function to parse multiple size by ',';
*/
data = (FAR char *)cameradata;
while ((data = strstr(data, "name")) != NULL)
{
char sizelist[256];
FAR char *sizedata = sizelist;
size_t sizecount = 0;
priv[count] = kmm_zalloc(sizeof(goldfish_camera_priv_t));
if (priv[count] == NULL)
{
verr("Failed to allocate instance\n");
ret = -ENOMEM;
goto out;
}
sscanf(data,
"name=%s channel=%"SCNu32" pix=%"SCNu32" dir=%s framedims=%s",
priv[count]->info.name,
&priv[count]->info.channel,
&priv[count]->info.pix,
priv[count]->info.dir,
sizelist);
sscanf(sizedata,
"%"SCNu16"x%"SCNu16"",
&priv[count]->info.size[sizecount].width,
&priv[count]->info.size[sizecount].height);
while ((sizedata = strchr(sizedata, ',')) != NULL)
{
if (++sizecount >= GOLDFISH_CAMERA_SIZE_MAX_NUMBER)
{
ret = -E2BIG;
goto err;
}
sizedata++;
sscanf(sizedata,
"%"SCNu16"x%"SCNu16"",
&priv[count]->info.size[sizecount].width,
&priv[count]->info.size[sizecount].height);
}
if (++count >= size)
{
ret = -E2BIG;
goto err;
}
data++;
}
ret = count;
goto out;
err:
do
{
if (priv[count])
{
kmm_free(priv[count]);
}
}
while (count--);
out:
file_close(&file);
if (cameradata)
{
kmm_free(cameradata);
}
return ret;
}
static int goldfish_camera_thread(int argc, FAR char *argv[])
{
FAR goldfish_camera_priv_t *priv = (FAR goldfish_camera_priv_t *)
((uintptr_t)strtoul(argv[1], NULL, 16));
int ret;
while (1)
{
if (nxsem_wait(&priv->run) < 0)
{
continue;
}
if (!priv->streaming)
{
return 0;
}
ret = goldfish_camera_send(&priv->file,
"frame video=%"PRIu32" preview=0 "
"whiteb=1,1,1 expcomp=1 time=0",
priv->buf_size);
if (ret < 0)
{
return ret;
}
reload:
ret = goldfish_camera_recv(&priv->file,
(FAR void **)&priv->next_buf);
if (ret < 0)
{
return ret;
}
else if (ret == 0)
{
goto reload;
}
else
{
struct timespec ts;
struct timeval tv;
DEBUGASSERT(ret == priv->buf_size);
clock_systime_timespec(&ts);
TIMESPEC_TO_TIMEVAL(&tv, &ts);
priv->capture_cb(0, priv->buf_size, &tv, priv->capture_arg);
}
}
}
/* Helper functions */
static uint32_t imgdata_fmt_to_v4l2(uint32_t pixelformat)
{
switch (pixelformat)
{
case IMGDATA_PIX_FMT_YUV420P:
return V4L2_PIX_FMT_YUV420;
case IMGDATA_PIX_FMT_YUYV:
return V4L2_PIX_FMT_YUYV;
case IMGDATA_PIX_FMT_JPEG_WITH_SUBIMG:
return V4L2_PIX_FMT_JPEG;
case IMGDATA_PIX_FMT_JPEG:
return V4L2_PIX_FMT_JPEG;
case IMGDATA_PIX_FMT_RGB565:
return V4L2_PIX_FMT_RGB565;
case IMGDATA_PIX_FMT_UYVY:
return V4L2_PIX_FMT_UYVY;
default:
/* Unsupported format */
return 0;
}
}
/* Sensor op functions are mostly dummy */
static bool goldfish_camera_is_available(FAR struct imgsensor_s *sensor)
{
return access(CONFIG_GOLDFISH_CAMERA_PIPE_PATH, F_OK) == 0;
}
static int goldfish_camera_init(FAR struct imgsensor_s *sensor)
{
return 0;
}
static int goldfish_camera_uninit(FAR struct imgsensor_s *sensor)
{
return 0;
}
static FAR const char *
goldfish_camera_get_driver_name(FAR struct imgsensor_s *sensor)
{
FAR goldfish_camera_priv_t *priv = container_of(sensor,
goldfish_camera_priv_t,
sensor);
return priv->info.name;
}
static int
goldfish_camera_validate_frame_setting(FAR struct imgsensor_s *sensor,
imgsensor_stream_type_t type,
uint8_t nr_fmt,
FAR imgsensor_format_t *fmt,
FAR imgsensor_interval_t *interval)
{
return 0;
}
static int goldfish_camera_start_capture(FAR struct imgsensor_s *sensor,
imgsensor_stream_type_t type,
uint8_t nr_fmt,
FAR imgsensor_format_t *fmt,
FAR imgsensor_interval_t *interval)
{
return 0;
}
static int goldfish_camera_stop_capture(FAR struct imgsensor_s *sensor,
imgsensor_stream_type_t type)
{
return 0;
}
/* Data op functions do all the real work */
static int goldfish_camera_data_init(FAR struct imgdata_s *data)
{
FAR goldfish_camera_priv_t *priv = (FAR goldfish_camera_priv_t *)data;
FAR char *argv[2];
char arg1[32];
int ret;
ret = file_open(&priv->file,
CONFIG_GOLDFISH_CAMERA_PIPE_PATH,
O_RDWR);
if (ret < 0)
{
verr("Failed to open: %s: %d\n",
CONFIG_GOLDFISH_CAMERA_PIPE_PATH, ret);
return ret;
}
nxsem_init(&priv->run, 0, 0);
priv->streaming = true;
ret = goldfish_camera_send(&priv->file,
"pipe:%s:%s:name=%s",
g_qemudservice,
g_cameraservice,
priv->info.name);
if (ret < 0)
{
goto err;
}
ret = goldfish_camera_send(&priv->file, "connect");
if (ret < 0)
{
goto err;
}
snprintf(arg1, sizeof(arg1), "%p", priv);
argv[0] = arg1;
argv[1] = NULL;
ret = kthread_create("goldfish_camera_thread",
CONFIG_GOLDFISH_CAMERA_PRIORITY,
CONFIG_GOLDFISH_CAMERA_STACKSIZE,
goldfish_camera_thread, argv);
if (ret < 0)
{
goto err;
}
priv->pid = ret;
return 0;
err:
nxsem_destroy(&priv->run);
priv->streaming = false;
file_close(&priv->file);
return ret;
}
static int goldfish_camera_data_uninit(FAR struct imgdata_s *data)
{
FAR goldfish_camera_priv_t *priv = (FAR goldfish_camera_priv_t *)data;
int ret;
ret = goldfish_camera_send(&priv->file, "disconnect");
if (ret < 0)
{
return ret;
}
priv->streaming = false;
nxsem_post(&priv->run);
nxsched_waitpid(priv->pid, NULL, 0);
nxsem_destroy(&priv->run);
file_close(&priv->file);
return 0;
}
static int goldfish_camera_data_validate_buf(FAR uint8_t *addr,
uint32_t size)
{
if (!addr || ((uintptr_t)addr & 0x1f))
{
return -EINVAL;
}
return 0;
}
static int goldfish_camera_data_set_buf(FAR struct imgdata_s *data,
FAR uint8_t *addr,
uint32_t size)
{
FAR goldfish_camera_priv_t *priv = (FAR goldfish_camera_priv_t *)data;
int ret;
ret = goldfish_camera_data_validate_buf(addr, size);
if (ret < 0)
{
return ret;
}
priv->next_buf = addr;
priv->buf_size = size;
nxsem_post(&priv->run);
return 0;
}
static int
goldfish_camera_data_validate_frame_setting(FAR struct imgdata_s *data,
uint8_t nr_datafmt,
FAR imgdata_format_t *datafmt,
FAR imgdata_interval_t *interval)
{
FAR goldfish_camera_priv_t *priv = (FAR goldfish_camera_priv_t *)data;
int i;
for (i = 0; i < GOLDFISH_CAMERA_SIZE_MAX_NUMBER; i++)
{
if (datafmt[IMGDATA_FMT_MAIN].width == priv->info.size[i].width &&
datafmt[IMGDATA_FMT_MAIN].height == priv->info.size[i].height &&
imgdata_fmt_to_v4l2(datafmt[IMGDATA_FMT_MAIN].pixelformat)
== priv->info.pix)
{
return 0;
}
}
return -ENOTSUP;
}
static int
goldfish_camera_data_start_capture(FAR struct imgdata_s *data,
uint8_t nr_datafmt,
FAR imgdata_format_t *datafmt,
FAR imgdata_interval_t *interval,
imgdata_capture_t callback,
FAR void *arg)
{
FAR goldfish_camera_priv_t *priv = (FAR goldfish_camera_priv_t *)data;
int ret;
ret = goldfish_camera_send(&priv->file,
"start dim=%dx%d pix=%d",
datafmt[IMGDATA_FMT_MAIN].width,
datafmt[IMGDATA_FMT_MAIN].height,
imgdata_fmt_to_v4l2(
datafmt[IMGDATA_FMT_MAIN].pixelformat));
if (ret < 0)
{
return ret;
}
priv->capture_cb = callback;
priv->capture_arg = arg;
return 0;
}
static int goldfish_camera_data_stop_capture(FAR struct imgdata_s *data)
{
FAR goldfish_camera_priv_t *priv = (FAR goldfish_camera_priv_t *)data;
int ret;
ret = goldfish_camera_send(&priv->file, "stop");
if (ret < 0)
{
return ret;
}
priv->capture_cb = NULL;
priv->capture_arg = NULL;
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int goldfish_camera_initialize(void)
{
FAR goldfish_camera_priv_t *priv[GOLDFISH_CAMERA_MAX_NUMBER];
FAR struct imgsensor_s *sensor;
char devpath[32];
ssize_t number;
ssize_t i;
number = goldfish_camera_get_list(priv, GOLDFISH_CAMERA_MAX_NUMBER);
if (number < 0)
{
return number;
}
for (i = 0; i < number; i++)
{
priv[i]->sensor.ops = &g_goldfish_camera_ops;
priv[i]->data.ops = &g_goldfish_camera_data_ops;
sensor = &priv[i]->sensor;
if (i == 0)
{
snprintf(devpath, sizeof(devpath), "/dev/video");
}
else
{
snprintf(devpath, sizeof(devpath), "/dev/video%zd", i);
}
video_register(devpath,
&priv[i]->data,
&sensor,
1);
}
return 0;
}