Maemo patchset 20101501+0m5
[h-e-n] / drivers / media / video / omap34xxcam-daemon.c
diff --git a/drivers/media/video/omap34xxcam-daemon.c b/drivers/media/video/omap34xxcam-daemon.c
new file mode 100644 (file)
index 0000000..fdec272
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * drivers/media/video/omap/omap34xcam-daemon.c
+ *
+ * OMAP 3 camera driver daemon support.
+ *
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-common.h>
+
+#include "isp/isp.h"
+
+#include "omap34xxcam.h"
+
+/* Kernel requests stuff from daemon. */
+int omap34xxcam_daemon_req(struct omap34xxcam_videodev *vdev,
+                          struct omap34xxcam_daemon_req *req,
+                          struct file *file)
+{
+       struct omap34xxcam_daemon *d = &vdev->daemon;
+       unsigned long flags;
+       static int missing = 10;
+       int rval;
+
+       if (!d->file) {
+               if (missing > 0) {
+                       missing--;
+                       dev_info(&vdev->vfd->dev, "%s: daemon is missing!\n",
+                                __func__);
+               }
+               return 0;
+       }
+
+       if (req->max_size > OMAP34XXCAM_DAEMON_REQ_MAX_SIZE
+           || req->size > req->max_size)
+               return -EFBIG;
+
+       if (d->file == file) {
+               dev_info(&vdev->vfd->dev, "%s: invalid ioctl for daemon!\n",
+                        __func__);
+               return -EINVAL;
+       }
+
+       mutex_lock(&d->request_mutex);
+       d->request_state = OMAP34XXCAM_DAEMON_REQUEST_USER_START;
+
+       spin_lock_irqsave(&d->event_lock, flags);
+       d->req_pending |= OMAP34XXCAM_DAEMON_SYNC;
+       spin_unlock_irqrestore(&d->event_lock, flags);
+
+       wake_up_all(&d->poll_wait);
+
+       d->req = req;
+       up(&d->begin);
+       down(&d->finish);
+       rval = d->req_rval;
+
+       d->request_state = OMAP34XXCAM_DAEMON_REQUEST_USER_FINISH;
+
+       mutex_unlock(&d->request_mutex);
+
+       return rval;
+}
+
+/*
+ * User space requests stuff from daemon. The same as above but
+ * expects user-space pointers.
+ */
+int omap34xxcam_daemon_req_user(struct omap34xxcam_videodev *vdev,
+                               struct omap34xxcam_daemon_req *req,
+                               struct file *file)
+{
+       struct omap34xxcam_daemon *d = &vdev->daemon;
+       void __user *blob_ptr;
+       int rval = 0;
+       size_t myblob_size;
+       size_t stack_alloc;
+
+       if (!d->file)
+               return -ENOIOCTLCMD;
+
+       if (req->max_size > OMAP34XXCAM_DAEMON_REQ_MAX_SIZE
+           || req->size > req->max_size)
+               return -EFBIG;
+
+       if (req->max_size > OMAP34XXCAM_DAEMON_REQ_STACK_ALLOC) {
+               myblob_size = 0;
+               stack_alloc = 0;
+       } else {
+               myblob_size = req->max_size;
+               stack_alloc = 1;
+       }
+
+       {
+               char myblob[myblob_size];
+               void *tmp;
+
+               if (stack_alloc)
+                       tmp = myblob;
+               else {
+                       tmp = vmalloc(req->size);
+                       if (tmp == NULL)
+                               return -ENOMEM;
+               }
+
+               blob_ptr = req->blob;
+               req->blob = tmp;
+
+/*             printk(KERN_INFO "%s: request size %d, blob %p\n", */
+/*                    __func__, req->size, req->blob); */
+               if (copy_from_user(tmp, blob_ptr, req->size)) {
+                       printk(KERN_INFO "%s: copy_from_user failed\n",
+                              __func__);
+                       rval = -EFAULT;
+                       goto out_free;
+               }
+
+               rval = omap34xxcam_daemon_req(vdev, req, file);
+               if (rval) {
+                       printk(KERN_INFO "%s: request failed, error %d\n",
+                              __func__, rval);
+                       goto out_free;
+               }
+
+               if (req->max_size > OMAP34XXCAM_DAEMON_REQ_MAX_SIZE
+                   || req->size > req->max_size) {
+                       rval = -EFBIG;
+                       goto out_free;
+               }
+
+               req->blob = blob_ptr;
+               if (copy_to_user(blob_ptr, tmp, req->size)) {
+                       printk(KERN_INFO "%s: copy_to_user failed\n", __func__);
+                       rval = -EFAULT;
+               }
+
+       out_free:
+               if (!stack_alloc)
+                       vfree(tmp);
+
+/*             printk(KERN_INFO "%s: request end\n", __func__); */
+       }
+       return rval;
+}
+
+/* Get an event. Only daemon calls this. */
+int omap34xxcam_daemon_daemon_req_get_user(
+       struct omap34xxcam_videodev *vdev,
+       struct omap34xxcam_daemon_daemon_req *get,
+       struct file *file)
+{
+       struct omap34xxcam_daemon *d = &vdev->daemon;
+       unsigned long flags;
+       u32 pending;
+       int rval;
+
+       mutex_lock(&d->mutex);
+       if (d->file != file) {
+               rval = -EBUSY;
+               goto out;
+       }
+
+       spin_lock_irqsave(&d->event_lock, flags);
+       pending = d->req_pending;
+       spin_unlock_irqrestore(&d->event_lock, flags);
+
+/*     printk(KERN_INFO "%s: pending %x\n", __func__, pending); */
+
+       if (pending & OMAP34XXCAM_DAEMON_SYNC) {
+               get->u.sync = 1;
+
+               rval = omap34xxcam_daemon_daemon_req_sync(vdev, get);
+               if (!rval) {
+                       spin_lock_irqsave(&d->event_lock, flags);
+                       d->req_pending &= ~OMAP34XXCAM_DAEMON_SYNC;
+                       spin_unlock_irqrestore(&d->event_lock, flags);
+               }
+       } else if (pending & OMAP34XXCAM_DAEMON_ASYNC) {
+               get->u.sync = 0;
+
+               rval = omap34xxcam_daemon_daemon_req_async(vdev, get);
+               if (!rval) {
+                       spin_lock_irqsave(&d->event_lock, flags);
+                       d->req_pending &= ~OMAP34XXCAM_DAEMON_ASYNC;
+                       spin_unlock_irqrestore(&d->event_lock, flags);
+               }
+       } else {
+               rval = -EINVAL;
+       }
+
+out:
+       mutex_unlock(&d->mutex);
+       return rval;
+}
+
+/* Complete an event. Only daemon calls this. */
+int omap34xxcam_daemon_daemon_req_complete_user(
+       struct omap34xxcam_videodev *vdev,
+       struct omap34xxcam_daemon_daemon_req *complete,
+       struct file *file)
+{
+       struct omap34xxcam_daemon *d = &vdev->daemon;
+       int rval = 0;
+
+       mutex_lock(&d->mutex);
+       if (d->file != file) {
+               rval = -EBUSY;
+               goto out;
+       }
+
+       complete->u.rval = d->req_rval;
+/*     printk(KERN_INFO "%s: reqest rval %d\n", __func__, d->req_rval); */
+
+       if (!d->req) {
+               rval = -EINVAL;
+               goto out;
+       }
+
+       if (d->req->max_size < complete->req.size) {
+               d->req_rval = -EFBIG;
+               rval = -EFBIG;
+               goto out_up;
+       }
+
+       d->req->size = complete->req.size;
+
+       if (copy_from_user(d->req->blob, complete->req.blob,
+                          d->req->size)) {
+               printk(KERN_INFO "%s: copy_from_user failed\n", __func__);
+               d->req_rval = -EINVAL;
+               rval = -EFAULT;
+               goto out_up;
+       }
+
+out_up:
+/*     d->req_rval = complete->u.rval; */
+       d->request_state = OMAP34XXCAM_DAEMON_REQUEST_DAEMON_FINISH;
+       up(&d->finish);
+
+out:
+       mutex_unlock(&d->mutex);
+       return 0;
+}
+
+void omap34xxcam_daemon_init(struct omap34xxcam_videodev *vdev)
+{
+       struct omap34xxcam_daemon *d = &vdev->daemon;
+
+       mutex_init(&d->mutex);
+       mutex_init(&d->request_mutex);
+       init_waitqueue_head(&d->poll_wait);
+       sema_init(&d->begin, 0);
+       sema_init(&d->finish, 0);
+       spin_lock_init(&d->event_lock);
+}
+
+int omap34xxcam_daemon_install(struct file *file)
+{
+       struct omap34xxcam_fh *fh = file->private_data;
+       struct omap34xxcam_videodev *vdev = fh->vdev;
+       struct omap34xxcam_daemon *d = &vdev->daemon;
+       int rval = 0;
+
+/*     if (!capable(CAP_SYS_ADMIN)) */
+/*             return -EPERM; */
+
+       mutex_lock(&vdev->mutex);
+       mutex_lock(&d->mutex);
+
+       if (d->file) {
+               mutex_unlock(&d->mutex);
+               mutex_unlock(&vdev->mutex);
+               return -EBUSY;
+       }
+
+       d->file = file;
+
+       mutex_unlock(&d->mutex);
+
+       /* Drop us from use count, except the modules. */
+       if (atomic_dec_return(&vdev->users) == 0) {
+               omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF,
+                                           OMAP34XXCAM_SLAVE_POWER_ALL);
+               isp_put();
+       }
+       mutex_unlock(&vdev->mutex);
+
+       return rval;
+}
+
+int omap34xxcam_daemon_release(struct omap34xxcam_videodev *vdev,
+                              struct file *file)
+{
+       struct omap34xxcam_daemon *d = &vdev->daemon;
+
+       if (d->file != file)
+               return 0;
+
+       mutex_lock(&d->mutex);
+
+       /*  printk(KERN_ALERT "%s: state %d\n", __func__,
+        *  d->request_state); */
+       switch (d->request_state) {
+       case OMAP34XXCAM_DAEMON_REQUEST_USER_START:
+               down(&d->begin);
+       case OMAP34XXCAM_DAEMON_REQUEST_DAEMON_START:
+               d->req_rval = -EBUSY;
+               d->request_state =
+                       OMAP34XXCAM_DAEMON_REQUEST_DAEMON_FINISH;
+               up(&d->finish);
+               d->request_state =
+                       OMAP34XXCAM_DAEMON_REQUEST_DAEMON_FINISH;
+               break;
+       case OMAP34XXCAM_DAEMON_REQUEST_DAEMON_FINISH:
+               break;
+       case OMAP34XXCAM_DAEMON_REQUEST_USER_FINISH:
+               break;
+       }
+       d->file = NULL;
+
+       mutex_unlock(&d->mutex);
+
+       return 1;
+}
+
+void omap34xxcam_daemon_event_cb(unsigned long status, int (*arg1)
+                                (struct videobuf_buffer *vb), void *arg2)
+{
+       struct omap34xxcam_videodev *vdev =
+               (struct omap34xxcam_videodev *)arg1;
+       struct omap34xxcam_daemon *d = &vdev->daemon;
+       struct timeval stamp;
+       unsigned long flags;
+       u32 event = 0;
+
+       if (status & HIST_DONE)
+               event |= OMAP34XXCAM_DAEMON_EVENT_HIST_DONE;
+       if (status & H3A_AWB_DONE)
+               event |= OMAP34XXCAM_DAEMON_EVENT_H3A_AWB_DONE;
+       if (status & H3A_AF_DONE)
+               event |= OMAP34XXCAM_DAEMON_EVENT_H3A_AF_DONE;
+       if (status & HS_VS)
+               event |= OMAP34XXCAM_DAEMON_EVENT_HS_VS;
+
+       spin_lock_irqsave(&d->event_lock, flags);
+
+       event &= d->event_mask;
+       if (!event) {
+               spin_unlock_irqrestore(&d->event_lock, flags);
+               return;
+       }
+
+       spin_unlock_irqrestore(&d->event_lock, flags);
+
+       /* Enable interrupts during do_gettimeofday */
+       do_gettimeofday(&stamp);
+
+       spin_lock_irqsave(&d->event_lock, flags);
+
+       if (event & OMAP34XXCAM_DAEMON_EVENT_HIST_DONE)
+               d->event.hist_done_stamp = stamp;
+       if (event & OMAP34XXCAM_DAEMON_EVENT_H3A_AWB_DONE)
+               d->event.h3a_awb_done_stamp = stamp;
+       if (event & OMAP34XXCAM_DAEMON_EVENT_H3A_AF_DONE)
+               d->event.h3a_af_done_stamp = stamp;
+       if (event & OMAP34XXCAM_DAEMON_EVENT_HS_VS)
+               d->event.hs_vs_stamp = stamp;
+
+       d->event.mask |= event;
+
+       if (d->event.mask) {
+               d->req_pending |= OMAP34XXCAM_DAEMON_ASYNC;
+               wake_up_all(&d->poll_wait);
+       }
+
+       spin_unlock_irqrestore(&d->event_lock, flags);
+}
+
+int omap34xxcam_daemon_set_events(struct omap34xxcam_videodev *vdev, u32 *mask,
+                                 struct file *file)
+{
+       struct omap34xxcam_daemon *d = &vdev->daemon;
+       unsigned long flags;
+       int rval = 0;
+
+       mutex_lock(&d->mutex);
+
+       if (d->file != file) {
+               rval = -EBUSY;
+               goto out;
+       }
+
+       spin_lock_irqsave(&d->event_lock, flags);
+       d->event_mask = *mask;
+       spin_unlock_irqrestore(&d->event_lock, flags);
+
+out:
+       mutex_unlock(&d->mutex);
+
+       return rval;
+}