maru-camera: Implemented a virtual camera
authorjinhyung.jo <jinhyung.jo@samsung.com>
Wed, 8 Jul 2015 13:03:19 +0000 (22:03 +0900)
committerjinhyung.jo <jinhyung.jo@samsung.com>
Thu, 9 Jul 2015 04:41:12 +0000 (13:41 +0900)
It implemented using a Qt library.
And it supports image files that QImage can support by qt plugin.
It displays each image every a 100ms.
Also implemented simple feature that displays solid color image for SWT UI without Qt.

Change-Id: Ida76b5c5ab6bda17ee2bfc2414c24d1e308ef4c5
Signed-off-by: Jinhyung Jo <jinhyung.jo@samsung.com>
tizen/src/hw/pci/Makefile.objs
tizen/src/hw/pci/maru_camera.c
tizen/src/hw/pci/maru_camera.h
tizen/src/hw/pci/maru_camera_darwin.m
tizen/src/hw/pci/maru_camera_image.h [new file with mode: 0644]
tizen/src/hw/pci/maru_camera_linux.c
tizen/src/hw/pci/maru_camera_qtimage.cpp [new file with mode: 0644]
tizen/src/hw/pci/maru_camera_swimage.c [new file with mode: 0644]
tizen/src/hw/pci/maru_camera_virtual.c [new file with mode: 0644]
tizen/src/hw/pci/maru_camera_win32.c

index 7b58c97..8318d52 100644 (file)
@@ -10,17 +10,24 @@ endif
 
 obj-y += maru_brightness.o
 
-obj-y += maru_camera.o
+obj-y += maru_camera.o maru_camera_convert.o
+obj-y += maru_camera_virtual.o
+ifdef CONFIG_QT
+obj-y += maru_camera_qtimage.o
+$(obj)/maru_camera_qtimage.o: QEMU_CFLAGS += $(QT_CFLAGS)
+else
+obj-y += maru_camera_swimage.o
+endif
 ifdef CONFIG_LINUX
 obj-y += maru_camera_linux.o
 LIBS += -lv4l2 -lv4lconvert
 endif
 ifdef CONFIG_WIN32
-obj-y += maru_camera_win32.o maru_camera_convert.o
+obj-y += maru_camera_win32.o
 LIBS += -lole32 -loleaut32 -luuid -lstrmiids
 endif
 ifdef CONFIG_DARWIN
-obj-y += maru_camera_darwin.o maru_camera_convert.o
+obj-y += maru_camera_darwin.o
 LIBS += -framework Foundation -framework SystemConfiguration
 LIBS += -framework Cocoa -framework QTKit -framework CoreVideo
 LIBS += -framework AppKit
index d0fa1a4..72e6f4f 100644 (file)
@@ -48,7 +48,7 @@ MULTI_DEBUG_CHANNEL(tizen, camera);
 #define MARU_PCI_CAMERA_DEVICE_NAME     "maru-camera"
 
 /* PCI region size must be pow2.               */
-/* set to 1048(2^10) * 1048(2^10) * 4(2^2) = 4,194,304(2^22) */
+/* set to 1024(2^10) * 1024(2^10) * 4(2^2) = 4,194,304(2^22) */
 #define MARUCAM_MEM_SIZE      (1 << 22)
 #define MARUCAM_REG_SIZE      (1 << 8)             /* 64 * 4Byte = 256(2^8) */
 /* RAM type region must be at least 4KB */
@@ -64,6 +64,11 @@ marucam_mmio_read(void *opaque, hwaddr offset)
     MaruCamState *state = (MaruCamState *)opaque;
 
     switch (offset & 0xFF) {
+    /* Passes the index into the kernel driver */
+    case MARUCAM_CMD_INIT:
+        ret = state->index;
+        break;
+    /* return isr value and clear the interrupt signal */
     case MARUCAM_CMD_ISR:
         qemu_mutex_lock(&state->thread_mutex);
         ret = state->isr;
@@ -211,18 +216,38 @@ static int marucam_initfn(PCIDevice *dev)
 {
     MaruCamState *s = DO_UPCAST(MaruCamState, dev, dev);
     uint8_t *pci_conf = s->dev.config;
-
-    /* Check available webcam
-     * If there is not one, you can't use the camera.
-     */
-    if (!marucam_device_check()) {
-        s->initialized = false;
-        INFO("Failed to check the camera device, "
-            "You can *not* use the camera\n");
+    static bool is_used_index[2];
+    static bool has_native;
+
+    /* If there is no index property in 'maru-camera' option,
+       it gives an index by the following routine. */
+    TRACE("[%s] Input index(%d), path(%s)\n", __func__,
+          s->index, s->path ? s->path : NULL);
+    if (s->index == 0xFF) {
+        if (!is_used_index[0]) {
+            s->index = 0;
+            is_used_index[s->index] = true;
+        } else if (!is_used_index[1]) {
+            s->index = 1;
+            is_used_index[s->index] = true;
+        } else {
+            WARN("No more available, there are already 2 instances\n");
+            return 0;
+        }
+    } else if (s->index == 0 || s->index == 1) {
+        if (is_used_index[s->index]) {
+            WARN("Input index(%u) is already in use\n", s->index);
+            return 0;
+        } else {
+            is_used_index[s->index] = true;
+        }
+    } else {
+        WARN("Input index(%u) is out of range[0~1]\n", s->index);
         return 0;
     }
 
-    pci_config_set_interrupt_pin(pci_conf, 0x03);
+    /* Modify the pin number to avoid interrupt conflicts */
+    pci_config_set_interrupt_pin(pci_conf, (0x03 - s->index));
 
     memory_region_init_ram(&s->fbmem, OBJECT(s), "marucamera.fbmem",
                            MARUCAM_MEM_SIZE, &error_abort);
@@ -244,15 +269,31 @@ static int marucam_initfn(PCIDevice *dev)
     pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->iomem);
     pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ioreg);
 
-    /* for worker thread */
-    qemu_cond_init(&s->thread_cond);
-    qemu_mutex_init(&s->thread_mutex);
+    /* Check available webcam
+     * If there is not one, you can use the virtual camera.
+     */
+    if (s->path || has_native || !marucam_device_check()) {
+        INFO("[cam%d] Virtual Camera mode\n", s->index);
+        s->backend = marucam_virtual_backend_create(s);
+    } else {
+        INFO("[cam%d] Native Camera mode\n", s->index);
+        s->backend = marucam_native_backend_create(s);
+        has_native = true;
+    }
 
-    s->backend = marucam_backend_create(s);
-    s->backend->init(s);
+    if (s->backend) {
+        /* for worker thread */
+        qemu_cond_init(&s->thread_cond);
+        qemu_mutex_init(&s->thread_mutex);
 
-    s->tx_bh = qemu_bh_new(marucam_tx_bh, s);
-    INFO("initialize maru-camera device\n");
+        s->backend->init(s);
+
+        s->tx_bh = qemu_bh_new(marucam_tx_bh, s);
+        INFO("initialize the maru-camera%d device\n", s->index);
+    } else {
+        s->initialized = false;
+        ERR("failed to initialize the maru-camera%d device\n", s->index);
+    }
 
     return 0;
 }
@@ -271,7 +312,7 @@ static void marucam_exitfn(PCIDevice *pci_dev)
         qemu_mutex_destroy(&s->thread_mutex);
     }
 
-    INFO("finalize maru-camera device\n");
+    INFO("finalize maru-camera%d device\n", s->index);
 }
 
 static void marucam_resetfn(DeviceState *d)
@@ -285,7 +326,7 @@ static void marucam_resetfn(DeviceState *d)
         qemu_mutex_unlock(&s->thread_mutex);
         memset(s->fb_ptr, 0, MARUCAM_MEM_SIZE);
         memset(s->io_ptr, 0, MARUCAM_IOMEM_SIZE);
-        INFO("reset maru-camera device\n");
+        INFO("reset maru-camera%d device\n", s->index);
     }
 }
 
@@ -295,6 +336,12 @@ int maru_camera_pci_init(PCIBus *bus)
     return 0;
 }
 
+static Property marucam_props[] = {
+    DEFINE_PROP_UINT8("index", MaruCamState, index, 0xFF),
+    DEFINE_PROP_STRING("path", MaruCamState, path),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void maru_camera_pci_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -306,6 +353,7 @@ static void maru_camera_pci_class_init(ObjectClass *klass, void *data)
     k->device_id = PCI_DEVICE_ID_VIRTUAL_CAMERA;
     k->class_id = PCI_CLASS_OTHERS;
     dc->reset = marucam_resetfn;
+    dc->props = marucam_props;
     dc->desc = "MARU Virtual Camera device for Tizen emulator";
 }
 
index 0603522..4266b14 100644 (file)
@@ -113,6 +113,10 @@ struct MaruCamState {
     uint32_t            req_frame;
     uint32_t            ret_val;
 
+    /* Properties */
+    uint8_t             index;  /* device index (0 or 1)*/
+    char                *path;  /* image file path */
+
     MemoryRegion        fbmem;
     MemoryRegion        iomem;
     MemoryRegion        ioreg;
@@ -120,7 +124,8 @@ struct MaruCamState {
     MaruCamBackend      *backend;
 };
 
-MaruCamBackend *marucam_backend_create(MaruCamState *state);
+MaruCamBackend *marucam_native_backend_create(MaruCamState *state);
+MaruCamBackend *marucam_virtual_backend_create(MaruCamState *state);
 
 int marucam_device_check(void);
 
index e3031a2..39cdba1 100644 (file)
@@ -541,9 +541,8 @@ static void backend_mac_close(MaruCamState *state)
     if (backend->driver != nil) {
         [backend->driver dealloc];
         backend->driver = nil;
+        INFO("Closed\n");
     }
-
-    INFO("Closed\n");
 }
 
 static void backend_mac_stream_on(MaruCamState *state)
@@ -880,7 +879,7 @@ static void backend_mac_enum_fintv(MaruCamState *state)
     }
 }
 
-MaruCamBackend *marucam_backend_create(MaruCamState *state)
+MaruCamBackend *marucam_native_backend_create(MaruCamState *state)
 {
     MCBackendMac *backend_mac;
 
diff --git a/tizen/src/hw/pci/maru_camera_image.h b/tizen/src/hw/pci/maru_camera_image.h
new file mode 100644 (file)
index 0000000..5e3c1f9
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Qt5 module for the virtual camera
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * with Qt5 QImage class
+ */
+
+#ifndef __MARU_CAMERA_IMAGE_H__
+#define __MARU_CAMERA_IMAGE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *imagemgr_create(void *parent);
+int imagemgr_init(void *mgr, const char *path);
+void imagemgr_reset(void *mgr);
+void imagemgr_release(void *mgr);
+void imagemgr_prepare(void *mgr);
+bool imagemgr_is_init(void *mgr);
+
+void imagemgr_set_resolution(void *mgr, int w, int h);
+void *imagemgr_get_data(void *mgr);
+void imagemgr_adjust_ctrl(void *mgr,
+                          unsigned int ctrl_id,
+                          int ctrl_value);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MARU_CAMERA_IMAGE_H__ */
+
index 51f2a2e..d8677f3 100644 (file)
@@ -1367,7 +1367,7 @@ static void backend_v4l2_enum_fintv(MaruCamState *state)
     }
 }
 
-MaruCamBackend *marucam_backend_create(MaruCamState *state)
+MaruCamBackend *marucam_native_backend_create(MaruCamState *state)
 {
     MCBackendV4l2 *backend_v4l2;
 
diff --git a/tizen/src/hw/pci/maru_camera_qtimage.cpp b/tizen/src/hw/pci/maru_camera_qtimage.cpp
new file mode 100644 (file)
index 0000000..546bee7
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * Qt5 module for the virtual camera
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * with Qt5 QImage class
+ */
+
+#include <QFileInfo>
+#include <QDirIterator>
+#include <QImageReader>
+#include "maru_camera_image.h"
+#include "ui/qemu-pixman.h"
+
+// Internal classes
+// TODO: Add debug info and logs
+
+// video test colors
+enum pim_color {
+    COLOR_WHITE = 0,
+    COLOR_YELLOW,
+    COLOR_CYAN,
+    COLOR_GREEN,
+    COLOR_MAGENTA,
+    COLOR_RED,
+    COLOR_BLUE,
+    COLOR_BLACK,
+    COLOR_GRAY19,
+    COLOR_GRAY7,
+    COLOR_MAX
+};
+
+static const pixman_color_t colors[] = {
+    // WHITE
+    { .red = 0xFF << 8,
+      .green = 0xFF << 8,
+      .blue = 0xFF << 8,
+      .alpha = 0 },
+    // YELLOW
+    { .red = 0xFF << 8,
+      .green = 0xFF << 8,
+      .blue = 0,
+      .alpha = 0 },
+    // CYAN
+    { .red = 0,
+      .green = 0xFF << 8,
+      .blue = 0xFF << 8,
+      .alpha = 0 },
+    // GREEN
+    { .red = 0,
+      .green = 0xFF << 8,
+      .blue = 0,
+      .alpha = 0 },
+    // MAGENTA
+    { .red = 0xFF << 8,
+      .green = 0,
+      .blue = 0xFF << 8,
+      .alpha = 0 },
+    // RED
+    { .red = 0xFF << 8,
+      .green = 0,
+      .blue = 0,
+      .alpha = 0 },
+    // BLUE
+    { .red = 0,
+      .green = 0,
+      .blue = 0xFF << 8,
+      .alpha = 0 },
+    // BLACK
+    { .red = 0,
+      .green = 0,
+      .blue = 0,
+      .alpha = 0 },
+    // GRAY (#202020)
+    { .red = 0x20 << 8,
+      .green = 0x20 << 8,
+      .blue = 0x20 << 8,
+      .alpha = 0 },
+    // GREY (#505050)
+    { .red = 0x50 << 8,
+      .green = 0x50 << 8,
+      .blue = 0x50 << 8,
+      .alpha = 0 },
+};
+
+class PixmanImageManager
+{
+public:
+    explicit PixmanImageManager();
+    ~PixmanImageManager();
+
+    void makeImages(int w, int h);
+    void *getImageData();
+
+private:
+    int width;
+    int height;
+    int idx;
+    static const int MAX_DST_IMAGES = 3;
+    pixman_image_t *dstImages[MAX_DST_IMAGES];
+    pixman_image_t *srcImages[COLOR_MAX];
+};
+
+PixmanImageManager::PixmanImageManager()
+{
+    this->width = 0;
+    this->height = 0;
+    this->idx = 0;
+
+    for (int i = 0; i < COLOR_MAX; i++) {
+        srcImages[i] = pixman_image_create_solid_fill(&colors[i]);
+    }
+    for (int i = 0; i < MAX_DST_IMAGES; i++) {
+        dstImages[i] = NULL;
+    }
+}
+
+PixmanImageManager::~PixmanImageManager()
+{
+    for (int i = 0; i < COLOR_MAX; i++) {
+        pixman_image_unref(srcImages[i]);
+        srcImages[i] = NULL;
+    }
+    for (int i = 0; i < MAX_DST_IMAGES; i++) {
+        pixman_image_unref(dstImages[i]);
+        dstImages[i] = NULL;
+    }
+}
+
+void PixmanImageManager::makeImages(int w, int h)
+{
+    if (w == this->width && h == this->height) {
+        return;
+    } else {
+        this->width = w;
+        this->height = h;
+    }
+
+    for (int i = 0; i < MAX_DST_IMAGES; i++) {
+        if (this->dstImages[i]) {
+            pixman_image_unref(this->dstImages[i]);
+        }
+        this->dstImages[i] = pixman_image_create_bits(PIXMAN_r8g8b8,
+                                                      w, h, NULL, w * 3);
+        int h1 = 3 * h / 4;
+        for (int j = 0; j < COLOR_MAX; j++) {
+            int x1 = j * w / 7;
+            int x2 = (j + 1) * w / 7;
+            pixman_image_composite(PIXMAN_OP_SRC,
+                                   this->srcImages[j],
+                                   NULL, this->dstImages[i],
+                                   0, 0, 0, 0, x1, 0, (x2 - x1), h1);
+        }
+        for (int j = 0; j < COLOR_MAX - COLOR_BLACK; j++) {
+            int x1 = j * w / 3;
+            int x2 = (j + 1) * w / 3;
+            int k = (i + j) % 3;
+            pixman_image_composite(PIXMAN_OP_SRC,
+                                   this->srcImages[COLOR_BLACK + k],
+                                   NULL, this->dstImages[i],
+                                   0, 0, 0, 0, x1, h1, (x2 - x1), (h - h1));
+        }
+    }
+}
+
+void *PixmanImageManager::getImageData()
+{
+    void *buf = pixman_image_get_data(this->dstImages[this->idx++]);
+    if (this->idx == MAX_DST_IMAGES) {
+        this->idx = 0;
+    }
+
+    return buf;
+}
+
+class VCImageManager
+{
+public:
+    explicit VCImageManager(void *parent);
+    ~VCImageManager();
+
+    int init(const char *inPath);
+    void loadImages();
+    int findAnimatedImage();
+    void loadAnimatedImage(int idx);
+    void makeConvertedData(int w, int h);
+    void *getImageData();
+    bool isAvail();
+
+private:
+    void *parent;
+
+    int width;
+    int height;
+    int idxOfCurData;
+
+    bool isInit;
+    bool usePixman;
+    bool loadDone;
+
+    QList<QString> *fileList;
+    QList<QImage> *imageList;
+    QList<QByteArray *> *dataList;
+
+    void createPixmanImageManager();
+    PixmanImageManager *pim;
+};
+
+VCImageManager::VCImageManager(void *parent)
+{
+    this->parent = parent;
+    this->width = 0;
+    this->height = 0;
+    this->idxOfCurData = 0;
+
+    this->isInit = false;
+    this->usePixman = false;
+    this->loadDone = false;
+
+    this->fileList = new QList<QString>;
+    this->imageList = new QList<QImage>;
+    this->dataList = new QList<QByteArray *>;
+    this->pim = NULL;
+}
+
+VCImageManager::~VCImageManager()
+{
+    if (this->imageList) {
+        this->imageList->clear();
+        delete this->imageList;
+        this->imageList = NULL;
+    }
+    if (this->fileList) {
+        this->fileList->clear();
+        delete this->fileList;
+        this->fileList = NULL;
+    }
+    if (this->dataList) {
+        for (int i = 0; i < this->dataList->count(); i++) {
+            delete this->dataList->at(i);
+        }
+        this->dataList->clear();
+        delete this->dataList;
+        this->dataList = NULL;
+    }
+}
+
+int VCImageManager::init(const char *inPath)
+{
+    if (inPath) {
+        // If the inPath is enclosed with double quotes, remove them.
+        QFileInfo fi(QString(inPath).remove(QChar('\"')));
+        QString canonFilePath = fi.canonicalFilePath();
+        // single image file
+        if (fi.exists() && fi.isFile()) {
+            QImageReader ir(canonFilePath);
+            if (ir.canRead() && ir.imageCount() > 1) {
+                this->fileList->append(QDir::cleanPath(canonFilePath));
+            } else { // not animated, use generator
+                createPixmanImageManager();
+            }
+        // directory
+        } else if (fi.exists() && fi.isDir()) {
+            QDirIterator it(QDir::cleanPath(canonFilePath), QDir::Files);
+            if (!it.hasNext()) { // there is no file
+                createPixmanImageManager();
+            } else {
+                do {
+                    it.next();
+                    if (!it.fileName().isEmpty()) {
+                        QByteArray format =
+                                    QImageReader::imageFormat(it.filePath());
+                        // QImageReader wrong recognizing the file format
+                        // some file what is compressed by the gzip
+                        // so ignore it
+                        if (!format.isEmpty() && !format.startsWith("svgz")) {
+                            this->fileList->append(it.filePath());
+                        }
+                    }
+                } while (it.hasNext());
+                if (this->fileList->isEmpty()) {
+                    // there is no image file
+                    // use generator
+                    createPixmanImageManager();
+                }
+            }
+        } else {
+            createPixmanImageManager();
+        }
+    } else {
+        createPixmanImageManager();
+    }
+
+    this->isInit = true;
+
+    return 1;
+}
+
+int VCImageManager::findAnimatedImage()
+{
+    if (this->usePixman) {
+        return -1;
+    }
+
+    for (int i = 0; i < this->fileList->count(); i++) {
+        QImageReader ir(this->fileList->at(i));
+        if (ir.canRead() && ir.imageCount() > 1) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+void VCImageManager::loadAnimatedImage(int idx)
+{
+    if (this->loadDone || this->usePixman) {
+        return;
+    }
+
+    QImageReader ir(this->fileList->at(idx));
+    while (ir.canRead()) {
+        this->imageList->append(ir.read());
+    }
+    this->loadDone = true;
+}
+
+void VCImageManager::loadImages()
+{
+    if (this->loadDone) {
+        return;
+    }
+
+    if (this->usePixman) {
+        this->loadDone = true;
+        return;
+    }
+
+    for (int i = 0; i < this->fileList->count(); i++) {
+        QImage loadedImage = QImage(this->fileList->at(i));
+        if (!loadedImage.isNull()) {
+            this->imageList->append(loadedImage);
+        }
+    }
+    this->loadDone = true;
+}
+
+void VCImageManager::makeConvertedData(int w, int h)
+{
+    if (!isAvail()) {
+        return;
+    }
+
+    if (w == this->width && h == this->height) {
+        return;
+    } else {
+        this->width = w;
+        this->height = h;
+    }
+    if (this->usePixman) {
+        this->pim->makeImages(w, h);
+    } else {
+        bool dstListIsEmpty = this->dataList->empty();
+        for (int i = 0; i < this->imageList->count(); i++) {
+            QImage img = this->imageList->at(i).rgbSwapped();
+            img = img.scaled(this->width,
+                             this->height,
+                             Qt::IgnoreAspectRatio,
+                             Qt::SmoothTransformation);
+            img = img.convertToFormat(QImage::Format_RGB888);
+
+            if (dstListIsEmpty) {
+                this->dataList->append(new QByteArray((char *)img.bits(),
+                                       img.byteCount()));
+            } else {
+                delete this->dataList->at(i);
+                this->dataList->replace(i, new QByteArray((char *)img.bits(),
+                                        img.byteCount()));
+            }
+        }
+    }
+}
+
+void *VCImageManager::getImageData()
+{
+    if (!isAvail()) {
+        return NULL;
+    }
+    if (usePixman) {
+        return this->pim->getImageData();
+    } else {
+        QByteArray *ba = this->dataList->at(this->idxOfCurData++);
+        if (this->idxOfCurData == this->dataList->count()) {
+            this->idxOfCurData = 0;
+        }
+        return reinterpret_cast<void *>(ba->data());
+    }
+}
+
+bool VCImageManager::isAvail()
+{
+    return this->isInit;
+}
+
+void VCImageManager::createPixmanImageManager()
+{
+    if (this->pim) {
+        return;
+    }
+    pim = new PixmanImageManager();
+    this->usePixman = true;
+}
+
+// Extern Functions
+void *imagemgr_create(void *parent)
+{
+    VCImageManager *mgr = new VCImageManager(parent);
+
+    return static_cast<void *>(mgr);
+}
+
+int imagemgr_init(void *mgr, const char *inPath)
+{
+    VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+    return imagemgr->init(inPath);
+}
+
+void imagemgr_reset(void *mgr)
+{
+}
+
+void imagemgr_release(void *mgr)
+{
+    VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+    delete imagemgr;
+}
+
+void imagemgr_prepare(void *mgr)
+{
+
+    VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+    if (!imagemgr->isAvail()) {
+        return;
+    }
+
+    int aniIdx = imagemgr->findAnimatedImage();
+    if (aniIdx < 0) {
+        imagemgr->loadImages();
+    } else {
+        imagemgr->loadAnimatedImage(aniIdx);
+    }
+}
+
+bool imagemgr_is_init(void *mgr)
+{
+    VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+    return imagemgr->isAvail();
+}
+
+void imagemgr_set_resolution(void *mgr, int w, int h)
+{
+    VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+    imagemgr->makeConvertedData(w, h);
+}
+
+void *imagemgr_get_data(void *mgr)
+{
+    VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+    return imagemgr->getImageData();
+}
+
+void imagemgr_adjust_ctrl(void *mgr,
+                          unsigned int ctrl_id,
+                          int ctrl_value)
+{
+}
diff --git a/tizen/src/hw/pci/maru_camera_swimage.c b/tizen/src/hw/pci/maru_camera_swimage.c
new file mode 100644 (file)
index 0000000..0f87040
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Qt5 module for the virtual camera
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * with Qt5 QImage class
+ */
+
+#include "qemu-common.h"
+#include "ui/qemu-pixman.h"
+#include "maru_camera_image.h"
+
+#define VCSWIMAGE_MAX_BUF 20
+
+typedef struct VCSWImage {
+    void *parent;
+    bool is_init;
+    uint32_t width;
+    uint32_t height;
+    uint32_t idx;
+
+    pixman_image_t *srcs[VCSWIMAGE_MAX_BUF];
+    pixman_image_t *dsts[VCSWIMAGE_MAX_BUF];
+} VCSWImage;
+
+void *imagemgr_create(void *parent)
+{
+    VCSWImage *swimage = g_new0(VCSWImage, 1);
+    swimage->parent = parent;
+
+    return (void *)swimage;
+}
+
+int imagemgr_init(void *mgr, const char *inPath)
+{
+    return 1;
+}
+
+void imagemgr_reset(void *mgr)
+{
+}
+
+void imagemgr_release(void *mgr)
+{
+    int i;
+    VCSWImage *swimage = (VCSWImage *)mgr;
+
+    for (i = 0; i < VCSWIMAGE_MAX_BUF; i++) {
+        if (swimage->srcs[i]) {
+            pixman_image_unref(swimage->srcs[i]);
+        }
+        if (swimage->dsts[i]) {
+            pixman_image_unref(swimage->dsts[i]);
+        }
+    }
+    g_free(swimage);
+    swimage = NULL;
+}
+
+void imagemgr_prepare(void *mgr)
+{
+    int i, val;
+    time_t t;
+    pixman_color_t color;
+    VCSWImage *swimage = (VCSWImage *)mgr;
+
+    srand((unsigned) time(&t));
+
+    for (i = 0; i < VCSWIMAGE_MAX_BUF; i++) {
+        val = rand();
+
+        switch (val % 3) {
+        case 0:
+            color.alpha = 0x0000;
+            color.red = (val % 256) << 8;
+            color.green = 0x0000;
+            color.blue = 0x0000;
+            break;
+        case 1:
+            color.alpha = 0x0000;
+            color.red = 0x0000;
+            color.green = (val % 256) << 8;
+            color.blue = 0x0000;
+            break;
+        case 2:
+            color.alpha = 0x0000;
+            color.red = 0x0000;
+            color.green = 0x0000;
+            color.blue = (val % 256) << 8;
+            break;
+        default:
+            /* error */
+            break;
+        }
+
+        if (swimage->srcs[i]) {
+            pixman_image_unref(swimage->srcs[i]);
+        }
+        swimage->srcs[i] = pixman_image_create_solid_fill(&color);
+    }
+
+}
+
+bool imagemgr_is_init(void *mgr)
+{
+    VCSWImage *swimage = (VCSWImage *)mgr;
+    return swimage->is_init;
+}
+
+void imagemgr_set_resolution(void *mgr, int w, int h)
+{
+    int i;
+    VCSWImage *swimage = (VCSWImage *)mgr;
+
+    if (w == swimage->width && h == swimage->height) {
+        return;
+    } else {
+        swimage->width = w;
+        swimage->height = h;
+    }
+
+    for (i = 0; i < VCSWIMAGE_MAX_BUF; i++) {
+        if (swimage->dsts[i]) {
+            pixman_image_unref(swimage->dsts[i]);
+        }
+        swimage->dsts[i] = pixman_image_create_bits(PIXMAN_r8g8b8,
+                                                    w, h, NULL, w * 3);
+        pixman_image_composite(PIXMAN_OP_SRC,
+                               swimage->srcs[i], NULL, swimage->dsts[i],
+                               0, 0, 0, 0, 0, 0, w, h);
+    }
+}
+
+void *imagemgr_get_data(void *mgr)
+{
+    void *buf = NULL;
+    VCSWImage *swimage = (VCSWImage *)mgr;
+
+    buf = pixman_image_get_data(swimage->dsts[swimage->idx++]);
+    if (swimage->idx == VCSWIMAGE_MAX_BUF) {
+        swimage->idx = 0;
+    }
+
+    return buf;
+}
+
+void imagemgr_adjust_ctrl(void *mgr,
+                          unsigned int ctrl_id,
+                          int ctrl_value)
+{
+}
diff --git a/tizen/src/hw/pci/maru_camera_virtual.c b/tizen/src/hw/pci/maru_camera_virtual.c
new file mode 100644 (file)
index 0000000..9894eec
--- /dev/null
@@ -0,0 +1,650 @@
+/*
+ * Implementation of MARU Virtual Camera device by PCI bus
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * with Qt5 QImage class
+ */
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "sysemu/kvm.h"
+#include "emul_state.h"
+#include "maru_camera.h"
+#include "videodev2_min.h"
+#include "maru_camera_convert.h"
+#include "maru_camera_image.h"
+#include "debug_ch.h"
+
+MULTI_DEBUG_CHANNEL(tizen, camera);
+
+/* Controls the frame rate
+   Delay time for the next image display */
+#define MC_DEFAULT_MS 100
+
+static uint32_t support_fmts[] = {
+    V4L2_PIX_FMT_YUYV,
+    V4L2_PIX_FMT_YUV420,
+    V4L2_PIX_FMT_YVU420,
+};
+
+struct MCFrame {
+    uint32_t width;
+    uint32_t height;
+};
+
+static struct MCFrame support_frames[] = {
+        { 640, 480 },
+        { 352, 288 },
+        { 320, 240 },
+        { 176, 144 },
+        { 160, 120 },
+};
+
+struct MCControls {
+    uint32_t id;
+    int32_t min;
+    int32_t max;
+    int32_t step;
+    int32_t init_val;
+    int32_t cur_val;
+};
+
+enum {
+    VC_CID_BRIGHTNESS, /* V4L2_CID_BRIGHTNESS */
+    VC_CID_CONTRAST,   /* V4L2_CID_CONTRAST */
+    VC_CID_SATURATION, /* V4L2_CID_SATURATION */
+    VC_CID_SHARPNESS,  /* V4L2_CID_SHARPNESS */
+    VC_CID_MAX,
+};
+
+static uint32_t idx_to_cid[VC_CID_MAX] = {
+    V4L2_CID_BRIGHTNESS,
+    V4L2_CID_CONTRAST,
+    V4L2_CID_SATURATION,
+    V4L2_CID_SHARPNESS,
+};
+
+typedef struct MCBuffer {
+    void *data;
+    size_t size;
+} MCBuffer;
+
+struct MCStoredFrame {
+    void *data;
+    uint32_t pixelformat;
+    uint32_t width;
+    uint32_t height;
+    uint32_t size;
+};
+
+typedef struct MCBackendVirt {
+    MaruCamBackend base;
+
+    QEMUTimer *timer;
+    struct MCControls ctrl_tbl[VC_CID_MAX];
+    uint32_t src_fmt;
+    uint32_t dst_fmt;
+    uint32_t width;
+    uint32_t height;
+    void *imagemgr;
+} MCBackendVirt;
+
+static void backend_virt_reset_ctrls(MCBackendVirt *backend)
+{
+    uint32_t i;
+    for (i = 0; i < VC_CID_MAX; i++) {
+        backend->ctrl_tbl[i].id = idx_to_cid[i];
+        backend->ctrl_tbl[i].min = MARUCAM_CTRL_VALUE_MIN;
+        backend->ctrl_tbl[i].max = MARUCAM_CTRL_VALUE_MAX;
+        backend->ctrl_tbl[i].step = MARUCAM_CTRL_VALUE_STEP;
+        backend->ctrl_tbl[i].init_val = MARUCAM_CTRL_VALUE_MID;
+        backend->ctrl_tbl[i].cur_val = MARUCAM_CTRL_VALUE_MID;
+    }
+}
+
+static int is_streamon(MaruCamState *state)
+{
+    int st;
+    qemu_mutex_lock(&state->thread_mutex);
+    st = state->streamon;
+    qemu_mutex_unlock(&state->thread_mutex);
+    return (st == _MC_THREAD_STREAMON);
+}
+
+static void backend_virt_update(void *opaque)
+{
+    void *src_buf, *dst_buf;
+    MaruCamState *state = (MaruCamState *)opaque;
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    qemu_mutex_lock(&state->thread_mutex);
+    if (state->streamon) {
+        if (state->req_frame == 0) {
+            TRACE("there is no request\n");
+            goto update_out;
+        }
+        src_buf = imagemgr_get_data(backend->imagemgr);
+        if (src_buf == NULL) {
+            ERR("[%s:%d] Failed to get buffer\n", __func__, __LINE__);
+            goto update_out;
+        }
+
+        dst_buf = state->fb_ptr + state->buf_size * (state->req_frame - 1);
+        if (convert_frame(backend->src_fmt, backend->dst_fmt,
+                      backend->width, backend->height,
+                      (size_t)state->buf_size, src_buf, dst_buf, false) > 0) {
+            ERR("[%s:%d] Failed to convert buffer\n", __func__, __LINE__);
+            goto update_out;
+        }
+        state->req_frame = 0; /* clear request */
+        state->isr |= 0x01;   /* set a flag of rasing a interrupt */
+        qemu_bh_schedule(state->tx_bh);
+    }
+update_out:
+    qemu_mutex_unlock(&state->thread_mutex);
+
+    timer_mod(backend->timer,
+              qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + MC_DEFAULT_MS);
+}
+
+static void *manager_init_func(void *thread_param)
+{
+    MaruCamState *state = (MaruCamState *)thread_param;
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    backend->imagemgr = imagemgr_create(backend);
+
+    if (state->path && state->path[0] != '\0') {
+        /* using the image data from input files including default images */
+        imagemgr_init(backend->imagemgr, state->path);
+    } else {
+        /* using the image data to be generated internally */
+        imagemgr_init(backend->imagemgr, NULL);
+    }
+
+    backend->height = support_frames[0].height;
+    backend->width = support_frames[0].width;
+    backend->dst_fmt = support_fmts[0];
+    backend->src_fmt = V4L2_PIX_FMT_RGB24;
+
+    imagemgr_prepare(backend->imagemgr);
+    imagemgr_set_resolution(backend->imagemgr,
+                            backend->width,
+                            backend->height);
+
+    return NULL;
+}
+
+static void backend_virt_init(MaruCamState *state)
+{
+    qemu_thread_create(&state->thread_id,
+                       "marucam-init-virtual",
+                       manager_init_func,
+                       (void *)state,
+                       QEMU_THREAD_DETACHED);
+}
+
+static void backend_virt_reset(MaruCamState *state)
+{
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    imagemgr_reset(backend->imagemgr);
+}
+
+static void backend_virt_release(MaruCamState *state)
+{
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    imagemgr_release(backend->imagemgr);
+    g_free(backend);
+    backend = NULL;
+}
+
+static void backend_virt_open(MaruCamState *state)
+{
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    if (backend->imagemgr == NULL) {
+        ERR("There is no camera engine\n");
+        state->ret_val = ENODEV;
+        return;
+    }
+
+    INFO("Opened\n");
+    backend->timer = timer_new_ms(QEMU_CLOCK_REALTIME,
+                                  backend_virt_update,
+                                  state);
+
+    /* Set default values */
+    backend->height = support_frames[0].height;
+    backend->width = support_frames[0].width;
+    backend->dst_fmt = support_fmts[0];
+
+    backend_virt_reset_ctrls(backend);
+}
+
+static void backend_virt_close(MaruCamState *state)
+{
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    if (is_streamon(state)) {
+        state->backend->stream_off(state);
+    }
+
+    timer_free(backend->timer);
+    backend->timer = NULL;
+
+    INFO("Closed\n");
+}
+
+static void backend_virt_stream_on(MaruCamState *state)
+{
+    uint32_t width, height, pixfmt;
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    width = backend->width;
+    height = backend->height;
+    pixfmt = backend->dst_fmt;
+    state->buf_size = get_sizeimage(pixfmt, width, height);
+
+    INFO("Pixfmt(%c%c%c%c), W:H(%d:%d), buf size(%u)\n",
+         (char)(pixfmt), (char)(pixfmt >> 8),
+         (char)(pixfmt >> 16), (char)(pixfmt >> 24),
+         width, height, state->buf_size);
+    INFO("Starting preview\n");
+
+    timer_mod(backend->timer,
+              qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + MC_DEFAULT_MS);
+
+    qemu_mutex_lock(&state->thread_mutex);
+    state->streamon = _MC_THREAD_STREAMON;
+    qemu_mutex_unlock(&state->thread_mutex);
+
+    INFO("Streaming on ......\n");
+}
+
+static void backend_virt_stream_off(MaruCamState *state)
+{
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    INFO("...... Streaming off\n");
+    qemu_mutex_lock(&state->thread_mutex);
+    state->streamon = _MC_THREAD_STREAMOFF;
+    qemu_mutex_unlock(&state->thread_mutex);
+
+    state->buf_size = 0;
+
+    INFO("Stopping preview\n");
+    timer_del(backend->timer);
+}
+
+
+static void backend_virt_s_parm(MaruCamState *state)
+{
+}
+
+static void backend_virt_g_parm(MaruCamState *state)
+{
+    struct v4l2_captureparm *cp =
+                            (struct v4l2_captureparm *)state->io_ptr;
+
+    /* We use default FPS of the webcam
+     * return a fixed value on guest ini file (1/30).
+     */
+    cp->capability = V4L2_CAP_TIMEPERFRAME;
+    cp->timeperframe.numerator = 1;
+    cp->timeperframe.denominator = 30;
+}
+
+static void backend_virt_s_fmt(MaruCamState *state)
+{
+    uint32_t pidx, fidx;
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+    struct v4l2_pix_format *f = (struct v4l2_pix_format *)state->io_ptr;
+
+    for (fidx = 0; fidx < ARRAY_SIZE(support_frames); fidx++) {
+        if ((support_frames[fidx].width == f->width) &&
+            (support_frames[fidx].height == f->height)) {
+            break;
+        }
+    }
+    if (fidx == ARRAY_SIZE(support_frames)) {
+        state->ret_val = EINVAL;
+        return;
+    }
+    for (pidx = 0; pidx < ARRAY_SIZE(support_fmts); pidx++) {
+        if (support_fmts[pidx] == f->pixelformat) {
+            break;
+        }
+    }
+    if (pidx == ARRAY_SIZE(support_fmts)) {
+        state->ret_val = EINVAL;
+        return;
+    }
+
+    f->width = backend->width = support_frames[fidx].width;
+    f->height = backend->height = support_frames[fidx].height;
+    f->pixelformat = backend->dst_fmt = support_fmts[pidx];
+
+    f->field = V4L2_FIELD_NONE;
+    f->bytesperline = get_bytesperline(backend->dst_fmt,
+                                       backend->width);
+    f->sizeimage = get_sizeimage(backend->dst_fmt,
+                                 backend->width,
+                                 backend->height);
+    f->colorspace = 0;
+    f->priv = 0;
+
+    imagemgr_set_resolution(backend->imagemgr,
+                            backend->width,
+                            backend->height);
+
+    TRACE("Set the format: w:h(%dx%d), fmt(0x%x), "
+          "bytesperline(%d), sizeimage(%d), colorspace(%d)\n",
+          f->width, f->height, f->pixelformat, f->bytesperline,
+          f->sizeimage, f->colorspace);
+}
+
+static void backend_virt_g_fmt(MaruCamState *state)
+{
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+    struct v4l2_pix_format *f = (struct v4l2_pix_format *)state->io_ptr;
+
+    f->width = backend->width;
+    f->height = backend->height;
+    f->pixelformat = backend->dst_fmt;
+    f->field = V4L2_FIELD_NONE;
+    f->bytesperline = get_bytesperline(backend->dst_fmt,
+                                       backend->width);
+    f->sizeimage = get_sizeimage(backend->dst_fmt,
+                                 backend->width,
+                                 backend->height);
+    f->colorspace = 0;
+    f->priv = 0;
+
+    TRACE("Get the format: w:h(%dx%d), fmt(0x%x), "
+          "bytesperline(%d), sizeimage(%d), colorspace(%d)\n",
+          f->width, f->height, f->pixelformat, f->bytesperline,
+          f->sizeimage, f->colorspace);
+}
+
+static void backend_virt_try_fmt(MaruCamState *state)
+{
+    uint32_t i;
+    struct v4l2_pix_format *f = (struct v4l2_pix_format *)state->io_ptr;
+
+    for (i = 0; i < ARRAY_SIZE(support_frames); i++) {
+        if ((support_frames[i].width == f->width) &&
+                (support_frames[i].height == f->height)) {
+            break;
+        }
+    }
+    if (i == ARRAY_SIZE(support_frames)) {
+        state->ret_val = EINVAL;
+        return;
+    }
+    for (i = 0; i < ARRAY_SIZE(support_fmts); i++) {
+        if (support_fmts[i] == f->pixelformat) {
+            break;
+        }
+    }
+    if (i == ARRAY_SIZE(support_fmts)) {
+        state->ret_val = EINVAL;
+        return;
+    }
+
+    f->field = V4L2_FIELD_NONE;
+    f->bytesperline = get_bytesperline(f->pixelformat,
+                                       f->width);
+    f->sizeimage = get_sizeimage(f->pixelformat,
+                                 f->width,
+                                 f->height);
+    f->colorspace = 0;
+    f->priv = 0;
+
+
+    TRACE("Check the format: w:h(%dx%d), pix_fmt(0x%x), "
+          "bytesperline(%d), sizeimage(%d), colorspace(%d)\n",
+          f->width, f->height, f->pixelformat, f->bytesperline,
+          f->sizeimage, f->colorspace);
+}
+
+static void backend_virt_enum_fmt(MaruCamState *state)
+{
+    struct v4l2_fmtdesc *f = (struct v4l2_fmtdesc *)state->io_ptr;
+
+    if (f->index >= ARRAY_SIZE(support_fmts)) {
+        state->ret_val = EINVAL;
+        return;
+    }
+
+    f->flags = 0; /* flags = NONE */
+    f->pixelformat = support_fmts[f->index]; /* pixelformat */
+
+    /* set description */
+    switch (support_fmts[f->index]) {
+    case V4L2_PIX_FMT_YUYV:
+        pstrcpy((char *)f->description, sizeof(f->description), "YUYV");
+        break;
+    case V4L2_PIX_FMT_YUV420:
+        pstrcpy((char *)f->description, sizeof(f->description), "YU12");
+        break;
+    case V4L2_PIX_FMT_YVU420:
+        pstrcpy((char *)f->description, sizeof(f->description), "YV12");
+        break;
+    default:
+        ERR("Invalid fixel format\n");
+        state->ret_val = EINVAL;
+        break;
+    }
+}
+
+static void backend_virt_query_ctrl(MaruCamState *state)
+{
+    struct v4l2_queryctrl *qc = (struct v4l2_queryctrl *)state->io_ptr;
+
+    /* NOTICE: Tizen MMFW hardcoded for control name
+               Do Not Modified the name
+    */
+    switch (qc->id) {
+    case V4L2_CID_BRIGHTNESS:
+        TRACE("Query : BRIGHTNESS\n");
+        pstrcpy((char *)qc->name, sizeof(qc->name), "brightness");
+        break;
+    case V4L2_CID_CONTRAST:
+        TRACE("Query : CONTRAST\n");
+        pstrcpy((char *)qc->name, sizeof(qc->name), "contrast");
+        break;
+    case V4L2_CID_SATURATION:
+        TRACE("Query : SATURATION\n");
+        pstrcpy((char *)qc->name, sizeof(qc->name), "saturation");
+        break;
+    case V4L2_CID_SHARPNESS:
+        TRACE("Query : SHARPNESS\n");
+        pstrcpy((char *)qc->name, sizeof(qc->name), "sharpness");
+        break;
+    default:
+        ERR("[%s] Invalid control ID: 0x%x\n", __func__, qc->id);
+        state->ret_val = EINVAL;
+        return;
+    }
+
+    /* set fixed values by FW configuration file */
+    qc->minimum = MARUCAM_CTRL_VALUE_MIN;    /* minimum */
+    qc->maximum = MARUCAM_CTRL_VALUE_MAX;    /* maximum */
+    qc->step = MARUCAM_CTRL_VALUE_STEP;   /* step */
+    qc->default_value = MARUCAM_CTRL_VALUE_MID;    /* default_value */
+    qc->flags = V4L2_CTRL_FLAG_SLIDER;
+
+    TRACE("Query Control: id(0x%x), name(%s), min(%d), max(%d), step(%d), "
+          "def_value(%d), flags(0x%x)\n",
+          qc->id, qc->name, qc->minimum, qc->maximum,
+          qc->step, qc->default_value, qc->flags);
+}
+
+static void backend_virt_s_ctrl(MaruCamState *state)
+{
+    uint32_t i;
+    struct v4l2_control *ctrl = (struct v4l2_control *)state->io_ptr;
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    switch (ctrl->id) {
+    case V4L2_CID_BRIGHTNESS:
+        i = VC_CID_BRIGHTNESS;
+        TRACE("%d is set to the value of the BRIGHTNESS\n", ctrl->value);
+        break;
+    case V4L2_CID_CONTRAST:
+        i = VC_CID_CONTRAST;
+        TRACE("%d is set to the value of the CONTRAST\n", ctrl->value);
+        break;
+    case V4L2_CID_SATURATION:
+        i = VC_CID_SATURATION;
+        TRACE("%d is set to the value of the SATURATION\n", ctrl->value);
+        break;
+    case V4L2_CID_SHARPNESS:
+        i = VC_CID_SHARPNESS;
+        TRACE("%d is set to the value of the SHARPNESS\n", ctrl->value);
+        break;
+    default:
+        ERR("[%s] Not supported control: 0x%x\n", __func__, ctrl->id);
+        state->ret_val = EINVAL;
+        return;
+    }
+    backend->ctrl_tbl[i].cur_val = ctrl->value;
+    imagemgr_adjust_ctrl(backend->imagemgr, ctrl->id, ctrl->value);
+}
+
+static void backend_virt_g_ctrl(MaruCamState *state)
+{
+    uint32_t i;
+    struct v4l2_control *ctrl = (struct v4l2_control *)state->io_ptr;
+    MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+    switch (ctrl->id) {
+    case V4L2_CID_BRIGHTNESS:
+        i = VC_CID_BRIGHTNESS;
+        TRACE("Gets the value of the BRIGHTNESS\n");
+        break;
+    case V4L2_CID_CONTRAST:
+        i = VC_CID_CONTRAST;
+        TRACE("Gets the value of the CONTRAST\n");
+        break;
+    case V4L2_CID_SATURATION:
+        i = VC_CID_SATURATION;
+        TRACE("Gets the value of the SATURATION\n");
+        break;
+    case V4L2_CID_SHARPNESS:
+        i = VC_CID_SHARPNESS;
+        TRACE("Gets the value of the SHARPNESS\n");
+        break;
+    default:
+        ERR("[%s] Not supported control: 0x%x\n", __func__, ctrl->id);
+        state->ret_val = EINVAL;
+        return;
+    }
+
+    ctrl->value = backend->ctrl_tbl[i].cur_val;
+    TRACE("Value: %d\n", ctrl->value);
+}
+
+static void backend_virt_enum_fsizes(MaruCamState *state)
+{
+    uint32_t i;
+    struct v4l2_frmsizeenum *fsize =
+                        (struct v4l2_frmsizeenum *)state->io_ptr;
+
+    if (fsize->index >= ARRAY_SIZE(support_frames)) {
+        state->ret_val = EINVAL;
+        return;
+    }
+    for (i = 0; i < ARRAY_SIZE(support_fmts); i++) {
+        if (support_fmts[i] == fsize->pixel_format) {
+            break;
+        }
+    }
+
+    if (i == ARRAY_SIZE(support_fmts)) {
+        state->ret_val = EINVAL;
+        return;
+    }
+
+    fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+    fsize->discrete.width = support_frames[fsize->index].width;
+    fsize->discrete.height = support_frames[fsize->index].height;
+}
+
+static void backend_virt_enum_fintv(MaruCamState *state)
+{
+    struct v4l2_frmivalenum *fival =
+                        (struct v4l2_frmivalenum *)state->io_ptr;
+
+    /* switch by index) */
+    switch (fival->index) {
+    case 0:
+        /* we only use 1/30 frame interval */
+        fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+        fival->discrete.numerator = 1;
+        fival->discrete.denominator = 30;
+        break;
+    default:
+        state->ret_val = EINVAL;
+        return;
+    }
+}
+
+MaruCamBackend *marucam_virtual_backend_create(MaruCamState *state)
+{
+    MCBackendVirt *backend_virt;
+
+    backend_virt = g_new0(MCBackendVirt, 1);
+    if (backend_virt == NULL) {
+        ERR("Failed to allocate memory for the virtual backend\n");
+        return NULL;
+    }
+
+
+    backend_virt->base.state               = state;
+    backend_virt->base.init                = backend_virt_init;
+    backend_virt->base.reset               = backend_virt_reset;
+    backend_virt->base.release             = backend_virt_release;
+    backend_virt->base.open                = backend_virt_open;
+    backend_virt->base.close               = backend_virt_close;
+    backend_virt->base.stream_on           = backend_virt_stream_on;
+    backend_virt->base.stream_off          = backend_virt_stream_off;
+    backend_virt->base.enum_fmt            = backend_virt_enum_fmt;
+    backend_virt->base.try_fmt             = backend_virt_try_fmt;
+    backend_virt->base.s_fmt               = backend_virt_s_fmt;
+    backend_virt->base.g_fmt               = backend_virt_g_fmt;
+    backend_virt->base.s_parm              = backend_virt_s_parm;
+    backend_virt->base.g_parm              = backend_virt_g_parm;
+    backend_virt->base.query_ctrl          = backend_virt_query_ctrl;
+    backend_virt->base.s_ctrl              = backend_virt_s_ctrl;
+    backend_virt->base.g_ctrl              = backend_virt_g_ctrl;
+    backend_virt->base.enum_framesizes     = backend_virt_enum_fsizes;
+    backend_virt->base.enum_frameintervals = backend_virt_enum_fintv;
+
+    return &backend_virt->base;
+}
index 3ad02cc..038d2ed 100644 (file)
@@ -2495,7 +2495,7 @@ static void backend_win_enum_fintv(MaruCamState *state)
     }
 }
 
-MaruCamBackend *marucam_backend_create(MaruCamState *state)
+MaruCamBackend *marucam_native_backend_create(MaruCamState *state)
 {
     MCBackendWin *backend_win;