From b563ad25dc4f4bac85a1085ab635078cd80a03e0 Mon Sep 17 00:00:00 2001 From: "jinhyung.jo" Date: Tue, 25 Oct 2011 16:26:53 +0900 Subject: [PATCH] [Title] add Virtual Camera Device [Type] Feature [Module] Emualtor / Camera [Priority] [CQ#] [Redmine#] [Problem] [Cause] [Solution] [TestCase] --- Makefile.target | 2 +- hw/pc.h | 2 +- hw/pc_piix.c | 4 + hw/svcamera.h | 148 +++++++++++++++ hw/svcamera_linux.c | 530 ++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/svcamera_pci.c | 234 +++++++++++++++++++++++ qemu_configure.sh | 3 +- 7 files changed, 920 insertions(+), 3 deletions(-) create mode 100644 hw/svcamera.h create mode 100644 hw/svcamera_linux.c create mode 100644 hw/svcamera_pci.c diff --git a/Makefile.target b/Makefile.target index b7fe9b4..0a4e9a6 100644 --- a/Makefile.target +++ b/Makefile.target @@ -234,7 +234,7 @@ obj-i386-y += helper_opengl.o opengl_exec.o opengl_server.o #obj-i386-y += svibe.o 3D.o smbus_smb380.o obj-i386-y += svibe.o ########################################################## - +obj-i386-y += svcamera_pci.o svcamera_linux.o diff --git a/hw/pc.h b/hw/pc.h index 478229e..89f5cb8 100755 --- a/hw/pc.h +++ b/hw/pc.h @@ -175,5 +175,5 @@ void isa_ne2000_init(int base, int irq, NICInfo *nd); #define E820_UNUSABLE 5 int e820_add_entry(uint64_t, uint64_t, uint32_t); - +int svcamera_pci_init(PCIBus *bus); #endif diff --git a/hw/pc_piix.c b/hw/pc_piix.c index 3fa1e9d..dcb3936 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -184,6 +184,10 @@ static void pc_init1(ram_addr_t ram_size, if (pci_enabled) { pc_pci_device_init(pci_bus); } + + if (pci_enabled) { + svcamera_pci_init(pci_bus); + } } static void pc_init_pci(ram_addr_t ram_size, diff --git a/hw/svcamera.h b/hw/svcamera.h new file mode 100644 index 0000000..b8af964 --- /dev/null +++ b/hw/svcamera.h @@ -0,0 +1,148 @@ +/* + * Samsung Virtual Camera device. + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef _SVCAMERA_H +#define _SVCAMERA_H + +#include "pci.h" +#ifndef _WIN32 +#include +#include +#endif + +#define DEBUG_SVCAM + +#if defined (DEBUG_SVCAM) +# define DEBUG_PRINT(x, arg...) do { printf("[%s] " x "\n", __func__, ##arg); } while (0) +#else +# define DEBUG_PRINT(x) +#endif + +#define SVCAM_MAX_PARAM 20 + +/* must sync with GUEST camera_driver */ +#define SVCAM_CMD_OPEN 0x00 +#define SVCAM_CMD_CLOSE 0x04 +#define SVCAM_CMD_IS_WEBCAM 0x08 +#define SVCAM_CMD_START_PREVIEW 0x0C +#define SVCAM_CMD_STOP_PREVIEW 0x10 +#define SVCAM_CMD_START_CAPTURE 0x14 +#define SVCAM_CMD_STOP_CAPTURE 0x18 +#define SVCAM_CMD_S_PARAM 0x1C +#define SVCAM_CMD_G_PARAM 0x20 +#define SVCAM_CMD_ENUM_FMT 0x24 +#define SVCAM_CMD_TRY_FMT 0x28 +#define SVCAM_CMD_S_FMT 0x2C +#define SVCAM_CMD_G_FMT 0x30 +#define SVCAM_CMD_QCTRL 0x34 +#define SVCAM_CMD_S_CTRL 0x38 +#define SVCAM_CMD_G_CTRL 0x3C +#define SVCAM_CMD_ENUM_FSIZES 0x40 +#define SVCAM_CMD_ENUM_FINTV 0x44 +#define SVCAM_CMD_S_DATA 0x48 +#define SVCAM_CMD_G_DATA 0x4C +#define SVCAM_CMD_DTC 0xFF + +typedef struct SVCamState SVCamState; +typedef struct SVCamThreadInfo SVCamThreadInfo; +typedef struct SVCamDevInfo SVCamDevInfo; +typedef struct SVCamParam SVCamParam; + +struct SVCamParam { + uint32_t devCmd; + uint32_t stack[SVCAM_MAX_PARAM]; + uint32_t top; + uint32_t retVal; + uint32_t errCode; +}; + +struct SVCamDevInfo { + uint32_t width; + uint32_t height; + uint32_t pixelformat; + uint32_t framerate; + uint32_t bytesperline; + uint32_t sizeimage; + uint32_t field; +}; + + +struct SVCamThreadInfo { + SVCamState* state; + SVCamParam* param; + +#if defined(_WIN32) + HANDLE tid; + HANDLE hReady; + HANDLE hFinish; +#else + pthread_t thread_id; + pthread_cond_t thread_cond; + pthread_mutex_t mutex_lock; + sem_t sem; +#endif +}; + +struct SVCamState { + PCIDevice dev; + + int is_webcam; + int streamon; + + ram_addr_t mem_offset; + void *vaddr; + + int cam_mmio; + + uint32_t mem_addr; + uint32_t mmio_addr; + + SVCamThreadInfo *thread; + SVCamDevInfo cam_info; +}; + +/* ----------------------------------------------------------------------------- */ +/* Fucntion prototype */ +/* ----------------------------------------------------------------------------- */ +int svcam_fake_grab(SVCamState *state); +void svcam_fake_start_preview(SVCamState *state, SVCamParam *param); +void svcam_fake_stop_preview(SVCamState *state, SVCamParam *param); +void svcam_fake_s_param(SVCamState *state, SVCamParam *param); +void svcam_fake_g_param(SVCamState *state, SVCamParam *param); +void svcam_fake_s_fmt(SVCamState *state, SVCamParam *param); +void svcam_fake_g_fmt(SVCamState *state, SVCamParam *param); +void svcam_fake_try_fmt(SVCamState *state, SVCamParam *param); +void svcam_fake_enum_fmt(SVCamState *state, SVCamParam *param); +void svcam_fake_qctrl(SVCamState *state, SVCamParam *param); +void svcam_fake_s_ctrl(SVCamState *state, SVCamParam *param); +void svcam_fake_g_ctrl(SVCamState *state, SVCamParam *param); +void svcam_fake_enum_fsizes(SVCamState *state, SVCamParam *param); +void svcam_fake_enum_fintv(SVCamState *state, SVCamParam *param); + +void *svcam_worker_thread(void *thread_param); + +void svcam_device_init(SVCamState *state, SVCamParam *param); +void svcam_device_open(SVCamState *state, SVCamParam *param); +void svcam_device_start_preview(SVCamState *state, SVCamParam *param); +void svcam_device_stop_preview(SVCamState *state, SVCamParam *param); +void svcam_device_close(SVCamState *state, SVCamParam *param); +void svcam_device_s_param(SVCamState *state, SVCamParam *param); +void svcam_device_g_param(SVCamState *state, SVCamParam *param); +void svcam_device_s_fmt(SVCamState *state, SVCamParam *param); +void svcam_device_g_fmt(SVCamState *state, SVCamParam *param); +void svcam_device_try_fmt(SVCamState *state, SVCamParam *param); +void svcam_device_enum_fmt(SVCamState *state, SVCamParam *param); +void svcam_device_qctrl(SVCamState *state, SVCamParam *param); +void svcam_device_s_ctrl(SVCamState *state, SVCamParam *param); +void svcam_device_g_ctrl(SVCamState *state, SVCamParam *param); +void svcam_device_enum_fsizes(SVCamState *state, SVCamParam *param); +void svcam_device_enum_fintv(SVCamState *state, SVCamParam *param); + + +#endif /* _CAMERA_H */ diff --git a/hw/svcamera_linux.c b/hw/svcamera_linux.c new file mode 100644 index 0000000..41c7535 --- /dev/null +++ b/hw/svcamera_linux.c @@ -0,0 +1,530 @@ +/* + * Samsung Virtual Camera device for Linux on X86. + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu-common.h" +#include "svcamera.h" +#include "pci.h" + +#include +#include +#include + +#include +#include + +#if defined (DEBUG_SVCAM) +#define PIX_TO_FOURCC(pix) \ + ((char) ((pix) & 0xff)), \ + ((char) (((pix) >> 8) & 0xff)), \ + ((char) (((pix) >> 16) & 0xff)), \ + ((char) (((pix) >> 24) & 0xff)) +#endif + +static int v4l2_fd; + +static struct v4lconvert_data *v4lcvtdata; +static struct v4l2_format src_fmt; +static struct v4l2_format dst_fmt; +static void *dst_buf; + +static int xioctl(int fd, int req, void *arg) +{ + int r; + + do { + r = v4l2_ioctl(fd, req, arg); + } while ( r < 0 && errno == EINTR); + + return r; +} + +static int __v4l_convert(SVCamState *state, void *src_ptr, uint32_t src_size) +{ + int ret; + + ret = v4lconvert_convert(v4lcvtdata, + &src_fmt, &dst_fmt, + (unsigned char *)src_ptr, src_size, (unsigned char *)dst_buf, + dst_fmt.fmt.pix.sizeimage); + if (ret < 0) { + if (errno == EAGAIN) + return 0; + DEBUG_PRINT("v4lconvert_convert failed!!!"); + DEBUG_PRINT("%s", v4lconvert_get_error_message(v4lcvtdata)); + return -1; + } + + memcpy(src_ptr, dst_buf, dst_fmt.fmt.pix.sizeimage); + + qemu_set_irq(state->dev.irq[0], 1); + + return 1; +} + +static int __v4l2_get_frame(SVCamState *state){ + int r; + + r = v4l2_read(v4l2_fd, state->vaddr, dst_fmt.fmt.pix.sizeimage); + if ( r < 0) { + if (errno == EAGAIN) + return 0; + DEBUG_PRINT("READ : %s, %d", strerror(errno), errno); + return -1; + } + + if (v4lcvtdata) { + r = __v4l_convert(state, state->vaddr, (uint32_t)r); + return r; + } + + qemu_set_irq(state->dev.irq[0], 1); + + return 1; +} + +static int __v4l2_grab(SVCamState *state) +{ + fd_set fds; + struct timeval tv; + int r; + + FD_ZERO(&fds); + FD_SET(v4l2_fd, &fds); + + tv.tv_sec = 1; + tv.tv_usec = 0; + + r = select(v4l2_fd + 1, &fds, NULL, NULL, &tv); + + if ( r < 0) { + if (errno == EINTR) + return 0; + DEBUG_PRINT("select : %s", strerror(errno)); + return -1; + } + if (!r) { + DEBUG_PRINT("Timed out"); + return -1; + } + + return __v4l2_get_frame(state); +} + +int svcam_fake_grab(SVCamState *state) +{ + return 0; +} + +// Worker thread +void *svcam_worker_thread(void *thread_param) +{ + int cond; + SVCamThreadInfo* thread = (SVCamThreadInfo*)thread_param; + +wait_worker_thread: + pthread_mutex_lock(&thread->mutex_lock); + pthread_cond_wait(&thread->thread_cond, &thread->mutex_lock); + pthread_mutex_unlock(&thread->mutex_lock); + + if (thread->state->is_webcam) { + __v4l2_grab(thread->state); + __v4l2_grab(thread->state); + while(1) + { + pthread_mutex_lock(&thread->mutex_lock); + cond = thread->state->streamon; + pthread_mutex_unlock(&thread->mutex_lock); + if (!cond) + goto wait_worker_thread; + if (__v4l2_grab(thread->state) < 0) + goto wait_worker_thread; + } + } else { + while(1) + { + pthread_mutex_lock(&thread->mutex_lock); + cond = thread->state->streamon; + pthread_mutex_unlock(&thread->mutex_lock); + if (!cond) + goto wait_worker_thread; + if (svcam_fake_grab(thread->state) < 0) + goto wait_worker_thread; + } + } + + qemu_free(thread_param); + pthread_exit(NULL); +} + +void svcam_device_init(SVCamState* state, SVCamParam* param) +{ + int err = 0; + SVCamThreadInfo *thread = state->thread; + + pthread_cond_init(&thread->thread_cond, NULL); + pthread_mutex_init(&thread->mutex_lock, NULL); + + err = pthread_create(&thread->thread_id, NULL, svcam_worker_thread, (void*)thread); + if (err != 0) { + DEBUG_PRINT("pthread_create() is failed"); + } +} + +// SVCAM_CMD_OPEN +void svcam_device_open(SVCamState* state, SVCamParam* param) +{ + int fd; + struct v4l2_capability cap; + + DEBUG_PRINT(""); + v4l2_fd = v4l2_open("/dev/video0", O_RDWR | O_NONBLOCK); + if (v4l2_fd < 0) { + DEBUG_PRINT("v4l2 device open failed. fake webcam mode ON."); + return; + } + if (xioctl(v4l2_fd, VIDIOC_QUERYCAP, &cap) < 0) { + DEBUG_PRINT("VIDIOC_QUERYCAP failed. fake webcam mode ON."); + v4l2_close(v4l2_fd); + return; + } + + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) || + !(cap.capabilities & V4L2_CAP_STREAMING)) { + DEBUG_PRINT("Not supported video driver. fake webcam mode ON."); + v4l2_close(v4l2_fd); + return; + } + + /* we will use host webcam */ + state->is_webcam = 1; + fd = v4l2_fd_open(v4l2_fd, V4L2_ENABLE_ENUM_FMT_EMULATION); + if (fd != -1) { + v4l2_fd = fd; + } + v4lcvtdata = v4lconvert_create(v4l2_fd); + if (!v4lcvtdata) { + DEBUG_PRINT("v4lconvert_create failed!!"); + } + + /* this is necessary? */ + memset(&src_fmt, 0, sizeof(src_fmt)); + memset(&dst_fmt, 0, sizeof(dst_fmt)); +} + +// SVCAM_CMD_START_PREVIEW +void svcam_device_start_preview(SVCamState* state, SVCamParam* param) +{ + DEBUG_PRINT(""); + + pthread_mutex_lock(&state->thread->mutex_lock); + state->streamon = 1; + pthread_cond_signal(&state->thread->thread_cond); + pthread_mutex_unlock(&state->thread->mutex_lock); +} + +// SVCAM_CMD_STOP_PREVIEW +void svcam_device_stop_preview(SVCamState* state, SVCamParam* param) +{ + DEBUG_PRINT(""); + pthread_mutex_lock(&state->thread->mutex_lock); + state->streamon = 0; + pthread_mutex_unlock(&state->thread->mutex_lock); + sleep(1); +} + +void svcam_device_s_param(SVCamState* state, SVCamParam* param) +{ + struct v4l2_streamparm sp; + + DEBUG_PRINT(""); + param->top = 0; + memset(&sp, 0, sizeof(struct v4l2_streamparm)); + sp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + sp.parm.capture.timeperframe.numerator = param->stack[0]; + sp.parm.capture.timeperframe.denominator = param->stack[1]; + + if (xioctl(v4l2_fd, VIDIOC_S_PARM, &sp) < 0) { + param->errCode = errno; + } + DEBUG_PRINT("numerator = %d", param->stack[0]); + DEBUG_PRINT("demonimator = %d", param->stack[1]); +} + +void svcam_device_g_param(SVCamState* state, SVCamParam* param) +{ + struct v4l2_streamparm sp; + + DEBUG_PRINT(""); + param->top = 0; + memset(&sp, 0, sizeof(struct v4l2_streamparm)); + sp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (xioctl(v4l2_fd, VIDIOC_G_PARM, &sp) < 0) { + param->errCode = errno; + return; + } + param->stack[0] = sp.parm.capture.timeperframe.numerator; + param->stack[1] = sp.parm.capture.timeperframe.denominator; + DEBUG_PRINT("numerator = %d", param->stack[0]); + DEBUG_PRINT("demonimator = %d", param->stack[1]); +} + +void svcam_device_s_fmt(SVCamState* state, SVCamParam* param) +{ + DEBUG_PRINT(""); + param->top = 0; + memset(&dst_fmt, 0, sizeof(struct v4l2_format)); + dst_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_fmt.fmt.pix.width = param->stack[0]; + dst_fmt.fmt.pix.height = param->stack[1]; + dst_fmt.fmt.pix.pixelformat = param->stack[2]; + dst_fmt.fmt.pix.field = param->stack[3]; + DEBUG_PRINT("[IN] width : %d", dst_fmt.fmt.pix.width); + DEBUG_PRINT("[IN] height : %d", dst_fmt.fmt.pix.height); + DEBUG_PRINT("[IN] pix : %c%c%c%c", PIX_TO_FOURCC(dst_fmt.fmt.pix.pixelformat)); + DEBUG_PRINT("[IN] field : %d", dst_fmt.fmt.pix.field); + + if (v4lcvtdata) { + if (v4lconvert_try_format(v4lcvtdata, &dst_fmt, &src_fmt) != 0) + DEBUG_PRINT("v4lconvert_try_format failed!!"); + if (xioctl(v4l2_fd, VIDIOC_S_FMT, &src_fmt) < 0) { + DEBUG_PRINT("VIDIOC_S_FMT failed!!"); + param->errCode = errno; + return; + } + if (dst_buf) { + qemu_free(dst_buf); + dst_buf = NULL; + } + dst_buf = qemu_malloc(dst_fmt.fmt.pix.sizeimage); + } else { + if (xioctl(v4l2_fd, VIDIOC_S_FMT, &dst_fmt) < 0) { + DEBUG_PRINT("VIDIOC_S_FMT failed!!"); + param->errCode = errno; + return; + } + } + + DEBUG_PRINT("[OUT] width : %d", dst_fmt.fmt.pix.width); + DEBUG_PRINT("[OUT] height : %d", dst_fmt.fmt.pix.height); + DEBUG_PRINT("[OUT] pix : %c%c%c%c", PIX_TO_FOURCC(dst_fmt.fmt.pix.pixelformat)); + DEBUG_PRINT("[OUT] field : %d", dst_fmt.fmt.pix.field); + DEBUG_PRINT("[OUT] bytesperline : %d", dst_fmt.fmt.pix.bytesperline); + DEBUG_PRINT("[OUT] sizeimage : %d", dst_fmt.fmt.pix.sizeimage); +} + +void svcam_device_g_fmt(SVCamState* state, SVCamParam* param) +{ + struct v4l2_format format; + + DEBUG_PRINT(""); + param->top = 0; + memset(&format, 0, sizeof(struct v4l2_format)); + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (xioctl(v4l2_fd, VIDIOC_G_FMT, &format) < 0) { + param->errCode = errno; + } else { + param->stack[0] = format.fmt.pix.width; + param->stack[1] = format.fmt.pix.height; + param->stack[2] = format.fmt.pix.pixelformat; + param->stack[3] = format.fmt.pix.field; + param->stack[4] = format.fmt.pix.bytesperline; + param->stack[5] = format.fmt.pix.sizeimage; + memcpy(&dst_fmt, &format, sizeof(format)); + if (v4lcvtdata) { + if (v4lconvert_try_format(v4lcvtdata, &dst_fmt, &src_fmt) != 0) + DEBUG_PRINT("v4lconvert_try_format failed!!"); + if (dst_buf) { + qemu_free(dst_buf); + dst_buf = NULL; + } + dst_buf = qemu_malloc(dst_fmt.fmt.pix.sizeimage); + } + + DEBUG_PRINT("[OUT] width : %d", format.fmt.pix.width); + DEBUG_PRINT("[OUT] height : %d", format.fmt.pix.height); + DEBUG_PRINT("[OUT] pix : %c%c%c%c", PIX_TO_FOURCC(format.fmt.pix.pixelformat)); + DEBUG_PRINT("[OUT] field : %d", format.fmt.pix.field); + DEBUG_PRINT("[OUT] bytesperline : %d", format.fmt.pix.bytesperline); + DEBUG_PRINT("[OUT] sizeimage : %d", format.fmt.pix.sizeimage); + } +} + +void svcam_device_try_fmt(SVCamState* state, SVCamParam* param) +{ + struct v4l2_format format; + + DEBUG_PRINT(""); + param->top = 0; + memset(&format, 0, sizeof(struct v4l2_format)); + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + format.fmt.pix.width = param->stack[0]; + format.fmt.pix.height = param->stack[1]; + format.fmt.pix.pixelformat = param->stack[2]; + format.fmt.pix.field = param->stack[3]; + DEBUG_PRINT("[IN] width : %d", format.fmt.pix.width); + DEBUG_PRINT("[IN] height : %d", format.fmt.pix.height); + DEBUG_PRINT("[IN] pix : %c%c%c%c", PIX_TO_FOURCC(format.fmt.pix.pixelformat)); + DEBUG_PRINT("[IN] field : %d", format.fmt.pix.field); + + if (xioctl(v4l2_fd, VIDIOC_TRY_FMT, &format) < 0) { + param->errCode = errno; + return; + } +} + +void svcam_device_enum_fmt(SVCamState* state, SVCamParam* param) +{ + struct v4l2_fmtdesc format; + + DEBUG_PRINT(""); + param->top = 0; + memset(&format, 0, sizeof(struct v4l2_fmtdesc)); + format.index = param->stack[0]; + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (xioctl(v4l2_fd, VIDIOC_ENUM_FMT, &format) < 0) { + param->errCode = errno; + return; + } + param->stack[0] = format.index; + param->stack[1] = format.flags; + param->stack[2] = format.pixelformat; + /* set description */ + memcpy(¶m->stack[3], format.description, sizeof(format.description)); + DEBUG_PRINT("index : %d", format.index); + DEBUG_PRINT("flags : %d", format.flags); + DEBUG_PRINT("pixelformat : %c%c%c%c", PIX_TO_FOURCC(format.pixelformat)); + DEBUG_PRINT("description : %s", format.description); +} + +void svcam_device_qctrl(SVCamState* state, SVCamParam* param) +{ + struct v4l2_queryctrl ctrl = {0, }; + + DEBUG_PRINT(""); + param->top = 0; + ctrl.id = param->stack[0]; + + if (xioctl(v4l2_fd, VIDIOC_QUERYCTRL, &ctrl) < 0) { + param->errCode = errno; + return; + } + param->stack[0] = ctrl.id; + param->stack[1] = ctrl.minimum; + param->stack[2] = ctrl.maximum; + param->stack[3] = ctrl.step; + param->stack[4] = ctrl.default_value; + param->stack[5] = ctrl.flags; + /* name field setting */ + memcpy(¶m->stack[6], ctrl.name, sizeof(ctrl.name)); +} + +void svcam_device_s_ctrl(SVCamState* state, SVCamParam* param) +{ + struct v4l2_control ctrl = {0, }; + + DEBUG_PRINT(""); + param->top = 0; + ctrl.id = param->stack[0]; + ctrl.value = param->stack[1]; + + if (xioctl(v4l2_fd, VIDIOC_S_CTRL, &ctrl) < 0) { + param->errCode = errno; + return; + } +} + +void svcam_device_g_ctrl(SVCamState* state, SVCamParam* param) +{ + struct v4l2_control ctrl = {0, }; + + DEBUG_PRINT(""); + param->top = 0; + ctrl.id = param->stack[0]; + + if (xioctl(v4l2_fd, VIDIOC_G_CTRL, &ctrl) < 0) { + param->errCode = errno; + return; + } + + param->stack[0] = ctrl.value; +} + +void svcam_device_enum_fsizes(SVCamState* state, SVCamParam* param) +{ + struct v4l2_frmsizeenum fsize; + + DEBUG_PRINT(""); + param->top = 0; + memset(&fsize, 0, sizeof(struct v4l2_frmsizeenum)); + fsize.index = param->stack[0]; + fsize.pixel_format = param->stack[1]; + + if (xioctl(v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &fsize) < 0) { + param->errCode = errno; + return; + } + + if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + param->stack[0] = fsize.discrete.width; + param->stack[1] = fsize.discrete.height; + DEBUG_PRINT("width = %d", fsize.discrete.width); + DEBUG_PRINT("height = %d", fsize.discrete.height); + } else { + param->errCode = EINVAL; + DEBUG_PRINT("Not Supported mode, we only support DISCRETE"); + } +} + +void svcam_device_enum_fintv(SVCamState* state, SVCamParam* param) +{ + struct v4l2_frmivalenum ival; + + DEBUG_PRINT(""); + param->top = 0; + memset(&ival, 0, sizeof(struct v4l2_frmivalenum)); + ival.index = param->stack[0]; + ival.pixel_format = param->stack[1]; + ival.width = param->stack[2]; + ival.height = param->stack[3]; + + if (xioctl(v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &ival) < 0) { + param->errCode = errno; + return; + } + + if (ival.type == V4L2_FRMIVAL_TYPE_DISCRETE) { + param->stack[0] = ival.discrete.numerator; + param->stack[1] = ival.discrete.denominator; + DEBUG_PRINT("numerator = %d", ival.discrete.numerator); + DEBUG_PRINT("denominator = %d", ival.discrete.denominator); + } else { + param->errCode = EINVAL; + DEBUG_PRINT("Not Supported mode, we only support DISCRETE"); + } +} + +// SVCAM_CMD_CLOSE +void svcam_device_close(SVCamState* state, SVCamParam* param) +{ + DEBUG_PRINT(""); + state->is_webcam = 0; + if (v4lcvtdata) { + v4lconvert_destroy(v4lcvtdata); + v4lcvtdata = NULL; + } + + if (dst_buf) { + qemu_free(dst_buf); + dst_buf = NULL; + } + v4l2_close(v4l2_fd); +} diff --git a/hw/svcamera_pci.c b/hw/svcamera_pci.c new file mode 100644 index 0000000..09a4c44 --- /dev/null +++ b/hw/svcamera_pci.c @@ -0,0 +1,234 @@ +/* + * Samsung Qemu webcam device emulation by PCI bus + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Authors: + * + * PROPRIETARY/CONFIDENTIAL + * This software is the confidential and proprietary information of SAMSUNG ELECTRONICS ("Confidential Information"). + * You shall not disclose such Confidential Information and shall use it only in accordance with the terms of the license agreement + * you entered into with SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the suitability + * of the software, either express or implied, including but not limited to the implied warranties of merchantability, fitness for + * a particular purpose, or non-infringement. SAMSUNG shall not be liable for any damages suffered by licensee as + * a result of using, modifying or distributing this software or its derivatives. + */ + +#include +#include +#include +#include +#include +#include + +#include "qemu-common.h" +#include "cpu-common.h" + +#include "pc.h" +#include "pci.h" +#include "pci_ids.h" + +#include "svcamera.h" + +#define PCI_CAMERA_DEVICE_NAME "svcamera_pci" + +#define PCI_VENDOR_ID_SAMSUNG 0x144d +#define PCI_DEVICE_ID_VIRTUAL_CAMERA 0x1018 + +#define SVCAM_MEM_SIZE (4 * 1024 * 1024) // 4MB +#define SVCAM_REG_SIZE (256) // 64 * 4 + +/* =================================================================================== + I/O +==================================================================================== */ + +static SVCamParam g_param; +static inline uint32_t svcam_reg_read(void *opaque, target_phys_addr_t offset) +{ + uint32_t ret; + + switch (offset & 0xFF) { + case SVCAM_CMD_G_DATA: + return g_param.stack[g_param.top++]; + case SVCAM_CMD_OPEN: + case SVCAM_CMD_CLOSE: + case SVCAM_CMD_START_PREVIEW: + case SVCAM_CMD_STOP_PREVIEW: + case SVCAM_CMD_S_PARAM: + case SVCAM_CMD_G_PARAM: + case SVCAM_CMD_ENUM_FMT: + case SVCAM_CMD_TRY_FMT: + case SVCAM_CMD_S_FMT: + case SVCAM_CMD_G_FMT: + case SVCAM_CMD_QCTRL: + case SVCAM_CMD_S_CTRL: + case SVCAM_CMD_G_CTRL: + case SVCAM_CMD_ENUM_FSIZES: + case SVCAM_CMD_ENUM_FINTV: + ret = g_param.errCode; + g_param.errCode = 0; + return ret; + default: + DEBUG_PRINT("Not supported command!!"); + break; + } + return 0; +} + +static inline void svcam_reg_write(void *opaque, target_phys_addr_t offset, uint32_t value) +{ + SVCamState *state = (SVCamState*)opaque; + uint32_t cmd = offset/sizeof(uint32_t); + + g_param.devCmd = cmd; + + switch(offset & 0xFF) { + case SVCAM_CMD_OPEN: + svcam_device_open(state, &g_param); + break; + case SVCAM_CMD_CLOSE: + svcam_device_close(state, &g_param); + break; + case SVCAM_CMD_START_PREVIEW: + svcam_device_start_preview(state, &g_param); + break; + case SVCAM_CMD_STOP_PREVIEW: + svcam_device_stop_preview(state, &g_param); + break; + case SVCAM_CMD_S_PARAM: + svcam_device_s_param(state, &g_param); + break; + case SVCAM_CMD_G_PARAM: + svcam_device_g_param(state, &g_param); + break; + case SVCAM_CMD_ENUM_FMT: + svcam_device_enum_fmt(state, &g_param); + break; + case SVCAM_CMD_TRY_FMT: + svcam_device_try_fmt(state, &g_param); + break; + case SVCAM_CMD_S_FMT: + svcam_device_s_fmt(state, &g_param); + break; + case SVCAM_CMD_G_FMT: + svcam_device_g_fmt(state, &g_param); + break; + case SVCAM_CMD_QCTRL: + svcam_device_qctrl(state, &g_param); + break; + case SVCAM_CMD_S_CTRL: + svcam_device_s_ctrl(state, &g_param); + break; + case SVCAM_CMD_G_CTRL: + svcam_device_g_ctrl(state, &g_param); + break; + case SVCAM_CMD_ENUM_FSIZES: + svcam_device_enum_fsizes(state, &g_param); + break; + case SVCAM_CMD_ENUM_FINTV: + svcam_device_enum_fintv(state, &g_param); + break; + case SVCAM_CMD_S_DATA: + g_param.stack[g_param.top++] = value; + break; + case SVCAM_CMD_DTC: + memset(&g_param, 0, sizeof(g_param)); + break; + default: + DEBUG_PRINT("Not supported command!!"); + break; + } +} + +static CPUReadMemoryFunc * const svcam_reg_readfn[3] = { + svcam_reg_read, + svcam_reg_read, + svcam_reg_read, +}; + +static CPUWriteMemoryFunc * const svcam_reg_writefn[3] = { + svcam_reg_write, + svcam_reg_write, + svcam_reg_write, +}; + +/* =================================================================================== + memory allocation +==================================================================================== */ +static void svcam_memory_map(PCIDevice *dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + SVCamState *s = DO_UPCAST(SVCamState, dev, dev); + + cpu_register_physical_memory(addr, size, s->mem_offset); + s->mem_addr = addr; +} + +static void svcam_mmio_map(PCIDevice *dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + SVCamState *s = DO_UPCAST(SVCamState, dev, dev); + + cpu_register_physical_memory(addr, size, s->cam_mmio); + s->mmio_addr = addr; +} + +// ========================================================================================== +static int svcam_initfn(PCIDevice *dev) +{ + SVCamState *s = DO_UPCAST(SVCamState, dev, dev); + uint8_t *pci_conf = s->dev.config; + + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_SAMSUNG); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIRTUAL_CAMERA); + pci_config_set_class(pci_conf, PCI_CLASS_OTHERS); + pci_config_set_interrupt_pin(pci_conf, 2); + + s->mem_offset = qemu_ram_alloc(NULL, "svcamera.ram", SVCAM_MEM_SIZE); + /* return a host pointer */ + s->vaddr = qemu_get_ram_ptr(s->mem_offset); + + s->cam_mmio = cpu_register_io_memory(svcam_reg_readfn, svcam_reg_writefn, + s, DEVICE_LITTLE_ENDIAN); + + /* setup memory space */ + /* memory #0 device memory (webcam buffer) */ + /* memory #1 memory-mapped I/O */ + pci_register_bar(&s->dev, 0, SVCAM_MEM_SIZE, + PCI_BASE_ADDRESS_MEM_PREFETCH, svcam_memory_map); + + pci_register_bar(&s->dev, 1, SVCAM_REG_SIZE, + PCI_BASE_ADDRESS_SPACE_MEMORY, svcam_mmio_map); + + /* for worker thread */ + SVCamThreadInfo *thread = qemu_malloc(sizeof(SVCamThreadInfo)); + + thread->state = s; + s->thread = thread; + + svcam_device_init(thread->state, NULL); + + memset(&g_param, 0, sizeof(g_param)); + + return 0; +} + +int svcamera_pci_init(PCIBus *bus) +{ + pci_create_simple(bus, -1, PCI_CAMERA_DEVICE_NAME); + return 0; +} + +static PCIDeviceInfo svcamera_info = { + .qdev.name = PCI_CAMERA_DEVICE_NAME, + .qdev.size = sizeof(SVCamState), + .no_hotplug = 1, + .init = svcam_initfn, +}; + +static void svcamera_pci_register(void) +{ + pci_qdev_register(&svcamera_info); +} + +device_init(svcamera_pci_register); diff --git a/qemu_configure.sh b/qemu_configure.sh index 66a4f2f..370e862 100755 --- a/qemu_configure.sh +++ b/qemu_configure.sh @@ -11,7 +11,8 @@ exec ./configure \ --audio-drv-list=pa \ --audio-card-list=es1370 \ --enable-mixemu \ - --disable-vnc-tls + --disable-vnc-tls \ + --extra-ldflags="-lv4l2 -lv4lconvert" #--enable-profiler \ # --enable-gles2 --gles2dir=/usr ;; -- 2.7.4