media: video: tegra: Add Tegra RPC support for tegra multimedia framework
authorDima Zavin <dima@android.com>
Mon, 1 Nov 2010 21:20:17 +0000 (14:20 -0700)
committerDima Zavin <dima@android.com>
Mon, 8 Nov 2010 06:37:40 +0000 (22:37 -0800)
Change-Id: I9233c5d7c678f6a9ba1c23af686137bf4d6a4291
Signed-off-by: Dima Zavin <dima@android.com>
drivers/media/video/tegra/Kconfig
drivers/media/video/tegra/Makefile
drivers/media/video/tegra/avp/Kconfig [new file with mode: 0644]
drivers/media/video/tegra/avp/Makefile [new file with mode: 0644]
drivers/media/video/tegra/avp/tegra_rpc.c [new file with mode: 0644]
drivers/media/video/tegra/avp/trpc.h [new file with mode: 0644]
drivers/media/video/tegra/avp/trpc_local.c [new file with mode: 0644]
drivers/media/video/tegra/avp/trpc_sema.c [new file with mode: 0644]
drivers/media/video/tegra/avp/trpc_sema.h [new file with mode: 0644]
include/linux/tegra_rpc.h [new file with mode: 0644]
include/linux/tegra_sema.h [new file with mode: 0644]

index 86c6aa2c466b624e2eb18a7d8fc571e951f2b049..ce31c4ecbe978c32203edcaf3ee3ac7b08c6418f 100644 (file)
@@ -6,3 +6,6 @@ config TEGRA_CAMERA
           Enables support for the Tegra camera interface
 
           If unsure, say Y
+
+
+source "drivers/media/video/tegra/avp/Kconfig"
index e5a074ea6608a4551aebd01f135ffd9e3aa72bfb..4cb8e3c7848598bf4e7f98f82d712d9ae8867e93 100644 (file)
@@ -1 +1,2 @@
 obj-$(CONFIG_TEGRA_CAMERA)             += tegra_camera.o
+obj-y                          += avp/
diff --git a/drivers/media/video/tegra/avp/Kconfig b/drivers/media/video/tegra/avp/Kconfig
new file mode 100644 (file)
index 0000000..dca58da
--- /dev/null
@@ -0,0 +1,15 @@
+config TEGRA_RPC
+       bool "Enable support for Tegra RPC"
+       depends on ARCH_TEGRA
+       default y
+       help
+         Enables support for the RPC mechanism necessary for the Tegra
+         multimedia framework. It is both used to communicate locally on the
+         CPU between multiple multimedia components as well as to communicate
+         with the AVP for offloading media decode.
+
+         Exports the local tegra RPC interface on device node
+         /dev/tegra_rpc. Also provides tegra fd based semaphores needed by
+         the tegra multimedia framework.
+
+         If unsure, say Y
diff --git a/drivers/media/video/tegra/avp/Makefile b/drivers/media/video/tegra/avp/Makefile
new file mode 100644 (file)
index 0000000..438cbb7
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_TEGRA_RPC)                += tegra_rpc.o
+obj-$(CONFIG_TEGRA_RPC)                += trpc_local.o
+obj-$(CONFIG_TEGRA_RPC)                += trpc_sema.o
diff --git a/drivers/media/video/tegra/avp/tegra_rpc.c b/drivers/media/video/tegra/avp/tegra_rpc.c
new file mode 100644 (file)
index 0000000..c4e707e
--- /dev/null
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *   Dima Zavin <dima@android.com>
+ *
+ * Based on original NVRM code from NVIDIA, and a partial rewrite by:
+ *   Gary King <gking@nvidia.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/tegra_rpc.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include "trpc.h"
+
+struct trpc_port;
+struct trpc_endpoint {
+       struct list_head        msg_list;
+       wait_queue_head_t       msg_waitq;
+
+       struct trpc_endpoint    *out;
+       struct trpc_port        *port;
+
+       struct trpc_node        *owner;
+
+       struct completion       *connect_done;
+       bool                    ready;
+       struct trpc_ep_ops      *ops;
+       void                    *priv;
+};
+
+struct trpc_port {
+       char                    name[TEGRA_RPC_MAX_NAME_LEN];
+
+       /* protects peer and closed state */
+       spinlock_t              lock;
+       struct trpc_endpoint    peers[2];
+       bool                    closed;
+
+       /* private */
+       struct kref             ref;
+       struct rb_node          rb_node;
+};
+
+enum {
+       TRPC_TRACE_MSG  = 1U << 0,
+       TRPC_TRACE_CONN = 1U << 1,
+       TRPC_TRACE_PORT = 1U << 2,
+};
+
+static u32 trpc_debug_mask = TRPC_TRACE_CONN | TRPC_TRACE_PORT;
+module_param_named(debug_mask, trpc_debug_mask, uint, S_IWUSR | S_IRUGO);
+
+#define DBG(flag, args...) \
+       do { if (trpc_debug_mask & (flag)) pr_info(args); } while (0)
+
+struct tegra_rpc_info {
+       struct kmem_cache               *msg_cache;
+
+       spinlock_t                      ports_lock;
+       struct rb_root                  ports;
+
+       struct list_head                node_list;
+       struct mutex                    node_lock;
+};
+
+struct trpc_msg {
+       struct list_head                list;
+
+       size_t                          len;
+       u8                              payload[TEGRA_RPC_MAX_MSG_LEN];
+};
+
+static struct tegra_rpc_info *tegra_rpc;
+
+static struct trpc_msg *dequeue_msg_locked(struct trpc_endpoint *ep);
+
+/* a few accessors for the outside world to keep the trpc_endpoint struct
+ * definition private to this module */
+void *trpc_priv(struct trpc_endpoint *ep)
+{
+       return ep->priv;
+}
+
+struct trpc_endpoint *trpc_peer(struct trpc_endpoint *ep)
+{
+       return ep->out;
+}
+
+const char *trpc_name(struct trpc_endpoint *ep)
+{
+       return ep->port->name;
+}
+
+static inline bool is_connected(struct trpc_port *port)
+{
+       return port->peers[0].ready && port->peers[1].ready;
+}
+
+static inline bool is_closed(struct trpc_port *port)
+{
+       return port->closed;
+}
+
+static void rpc_port_free(struct tegra_rpc_info *info, struct trpc_port *port)
+{
+       struct trpc_msg *msg;
+       int i;
+
+       for (i = 0; i < 2; ++i) {
+               struct list_head *list = &port->peers[i].msg_list;
+               while (!list_empty(list)) {
+                       msg = list_first_entry(list, struct trpc_msg, list);
+                       list_del(&msg->list);
+                       kmem_cache_free(info->msg_cache, msg);
+               }
+       }
+       kfree(port);
+}
+
+static void _rpc_port_release(struct kref *kref)
+{
+       struct tegra_rpc_info *info = tegra_rpc;
+       struct trpc_port *port = container_of(kref, struct trpc_port, ref);
+       unsigned long flags;
+
+       DBG(TRPC_TRACE_PORT, "%s: releasing port '%s' (%p)\n", __func__,
+           port->name, port);
+       spin_lock_irqsave(&info->ports_lock, flags);
+       rb_erase(&port->rb_node, &info->ports);
+       spin_unlock_irqrestore(&info->ports_lock, flags);
+       rpc_port_free(info, port);
+}
+
+/* note that the refcount is actually on the port and not on the endpoint */
+void trpc_put(struct trpc_endpoint *ep)
+{
+       kref_put(&ep->port->ref, _rpc_port_release);
+}
+
+void trpc_get(struct trpc_endpoint *ep)
+{
+       kref_get(&ep->port->ref);
+}
+
+/* Searches the rb_tree for a port with the provided name. If one is not found,
+ * the new port in inserted. Otherwise, the existing port is returned.
+ * Must be called with the ports_lock held */
+static struct trpc_port *rpc_port_find_insert(struct tegra_rpc_info *info,
+                                             struct trpc_port *port)
+{
+       struct rb_node **p;
+       struct rb_node *parent;
+       struct trpc_port *tmp;
+       int ret = 0;
+
+       p = &info->ports.rb_node;
+       parent = NULL;
+       while (*p) {
+               parent = *p;
+               tmp = rb_entry(parent, struct trpc_port, rb_node);
+
+               ret = strncmp(port->name, tmp->name, TEGRA_RPC_MAX_NAME_LEN);
+               if (ret < 0)
+                       p = &(*p)->rb_left;
+               else if (ret > 0)
+                       p = &(*p)->rb_right;
+               else
+                       return tmp;
+       }
+       rb_link_node(&port->rb_node, parent, p);
+       rb_insert_color(&port->rb_node, &info->ports);
+       DBG(TRPC_TRACE_PORT, "%s: inserted port '%s' (%p)\n", __func__,
+           port->name, port);
+       return port;
+}
+
+static int nodes_try_connect(struct tegra_rpc_info *info,
+                            struct trpc_node *src,
+                            struct trpc_endpoint *from)
+{
+       struct trpc_node *node;
+       int ret;
+
+       mutex_lock(&info->node_lock);
+       list_for_each_entry(node, &info->node_list, list) {
+               if (!node->try_connect)
+                       continue;
+               ret = node->try_connect(node, src, from);
+               if (!ret) {
+                       mutex_unlock(&info->node_lock);
+                       return 0;
+               }
+       }
+       mutex_unlock(&info->node_lock);
+       return -ECONNREFUSED;
+}
+
+static struct trpc_port *rpc_port_alloc(const char *name)
+{
+       struct trpc_port *port;
+       int i;
+
+       port = kzalloc(sizeof(struct trpc_port), GFP_KERNEL);
+       if (!port) {
+               pr_err("%s: can't alloc rpc_port\n", __func__);
+               return NULL;
+       }
+       BUILD_BUG_ON(2 != ARRAY_SIZE(port->peers));
+
+       spin_lock_init(&port->lock);
+       kref_init(&port->ref);
+       strlcpy(port->name, name, TEGRA_RPC_MAX_NAME_LEN);
+       for (i = 0; i < 2; i++) {
+               struct trpc_endpoint *ep = port->peers + i;
+               INIT_LIST_HEAD(&ep->msg_list);
+               init_waitqueue_head(&ep->msg_waitq);
+               ep->port = port;
+       }
+       port->peers[0].out = &port->peers[1];
+       port->peers[1].out = &port->peers[0];
+
+       return port;
+}
+
+/* must be holding the ports lock */
+static inline void handle_port_connected(struct trpc_port *port)
+{
+       int i;
+
+       DBG(TRPC_TRACE_CONN, "tegra_rpc: port '%s' connected\n", port->name);
+
+       for (i = 0; i < 2; i++)
+               if (port->peers[i].connect_done)
+                       complete(port->peers[i].connect_done);
+}
+
+static inline void _ready_ep(struct trpc_endpoint *ep,
+                            struct trpc_node *owner,
+                            struct trpc_ep_ops *ops,
+                            void *priv)
+{
+       ep->ready = true;
+       ep->owner = owner;
+       ep->ops = ops;
+       ep->priv = priv;
+}
+
+/* this keeps a reference on the port */
+static struct trpc_endpoint *_create_peer(struct tegra_rpc_info *info,
+                                         struct trpc_node *owner,
+                                         struct trpc_endpoint *ep,
+                                         struct trpc_ep_ops *ops,
+                                         void *priv)
+{
+       struct trpc_port *port = ep->port;
+       struct trpc_endpoint *peer = ep->out;
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       BUG_ON(port->closed);
+       if (peer->ready || !ep->ready) {
+               peer = NULL;
+               goto out;
+       }
+       _ready_ep(peer, owner, ops, priv);
+       if (WARN_ON(!is_connected(port)))
+               pr_warning("%s: created peer but no connection established?!\n",
+                          __func__);
+       else
+               handle_port_connected(port);
+       trpc_get(peer);
+out:
+       spin_unlock_irqrestore(&port->lock, flags);
+       return peer;
+}
+
+/* Exported code. This is out interface to the outside world */
+struct trpc_endpoint *trpc_create(struct trpc_node *owner, const char *name,
+                                 struct trpc_ep_ops *ops, void *priv)
+{
+       struct tegra_rpc_info *info = tegra_rpc;
+       struct trpc_endpoint *ep;
+       struct trpc_port *new_port;
+       struct trpc_port *port;
+       unsigned long flags;
+
+       BUG_ON(!owner);
+
+       /* we always allocate a new port even if one already might exist. This
+        * is slightly inefficient, but it allows us to do the allocation
+        * without holding our ports_lock spinlock. */
+       new_port = rpc_port_alloc(name);
+       if (!new_port) {
+               pr_err("%s: can't allocate memory for '%s'\n", __func__, name);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       spin_lock_irqsave(&info->ports_lock, flags);
+       port = rpc_port_find_insert(info, new_port);
+       if (port != new_port) {
+               rpc_port_free(info, new_port);
+               /* There was already a port by that name in the rb_tree,
+                * so just try to create its peer[1], i.e. peer for peer[0]
+                */
+               ep = _create_peer(info, owner, &port->peers[0], ops, priv);
+               if (!ep) {
+                       pr_err("%s: port '%s' is not in a connectable state\n",
+                              __func__, port->name);
+                       ep = ERR_PTR(-EINVAL);
+               }
+               goto out;
+       }
+       /* don't need to grab the individual port lock here since we must be
+        * holding the ports_lock to add the new element, and never dropped
+        * it, and thus noone could have gotten a reference to this port
+        * and thus the state couldn't have been touched */
+       ep = &port->peers[0];
+       _ready_ep(ep, owner, ops, priv);
+out:
+       spin_unlock_irqrestore(&info->ports_lock, flags);
+       return ep;
+}
+
+struct trpc_endpoint *trpc_create_peer(struct trpc_node *owner,
+                                      struct trpc_endpoint *ep,
+                                      struct trpc_ep_ops *ops,
+                                      void *priv)
+{
+       struct tegra_rpc_info *info = tegra_rpc;
+       struct trpc_endpoint *peer;
+       unsigned long flags;
+
+       BUG_ON(!owner);
+
+       spin_lock_irqsave(&info->ports_lock, flags);
+       peer = _create_peer(info, owner, ep, ops, priv);
+       spin_unlock_irqrestore(&info->ports_lock, flags);
+       return peer;
+}
+
+/* timeout == -1, waits forever
+ * timeout == 0, return immediately
+ */
+int trpc_connect(struct trpc_endpoint *from, long timeout)
+{
+       struct tegra_rpc_info *info = tegra_rpc;
+       struct trpc_port *port = from->port;
+       struct trpc_node *src = from->owner;
+       int ret;
+       bool no_retry = !timeout;
+       unsigned long endtime = jiffies + msecs_to_jiffies(timeout);
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       /* XXX: add state for connections and ports to prevent invalid
+        * states like multiple connections, etc. ? */
+       if (unlikely(is_closed(port))) {
+               ret = -ECONNRESET;
+               pr_err("%s: can't connect to %s, closed\n", __func__,
+                      port->name);
+               goto out;
+       } else if (is_connected(port)) {
+               ret = 0;
+               goto out;
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       do {
+               ret = nodes_try_connect(info, src, from);
+
+               spin_lock_irqsave(&port->lock, flags);
+               if (is_connected(port)) {
+                       ret = 0;
+                       goto out;
+               } else if (no_retry) {
+                       goto out;
+               } else if (signal_pending(current)) {
+                       ret = -EINTR;
+                       goto out;
+               }
+               spin_unlock_irqrestore(&port->lock, flags);
+               usleep_range(5000, 20000);
+       } while (timeout < 0 || time_before(jiffies, endtime));
+
+       return -ETIMEDOUT;
+
+out:
+       spin_unlock_irqrestore(&port->lock, flags);
+       return ret;
+}
+
+/* convenience function for doing this common pattern in a single call */
+struct trpc_endpoint *trpc_create_connect(struct trpc_node *src,
+                                         char *name,
+                                         struct trpc_ep_ops *ops,
+                                         void *priv,
+                                         long timeout)
+{
+       struct trpc_endpoint *ep;
+       int ret;
+
+       ep = trpc_create(src, name, ops, priv);
+       if (IS_ERR(ep))
+               return ep;
+
+       ret = trpc_connect(ep, timeout);
+       if (ret) {
+               trpc_close(ep);
+               return ERR_PTR(ret);
+       }
+
+       return ep;
+}
+
+void trpc_close(struct trpc_endpoint *ep)
+{
+       struct trpc_port *port = ep->port;
+       struct trpc_endpoint *peer = ep->out;
+       bool need_close_op = false;
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       BUG_ON(!ep->ready);
+       ep->ready = false;
+       port->closed = true;
+       if (peer->ready) {
+               need_close_op = true;
+               /* the peer may be waiting for a message */
+               wake_up_all(&peer->msg_waitq);
+               if (peer->connect_done)
+                       complete(peer->connect_done);
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+       if (need_close_op && peer->ops && peer->ops->close)
+               peer->ops->close(peer);
+       trpc_put(ep);
+}
+
+int trpc_wait_peer(struct trpc_endpoint *ep, long timeout)
+{
+       struct trpc_port *port = ep->port;
+       DECLARE_COMPLETION_ONSTACK(event);
+       int ret;
+       unsigned long flags;
+
+       if (timeout < 0)
+               timeout = MAX_SCHEDULE_TIMEOUT;
+       else if (timeout > 0)
+               timeout = msecs_to_jiffies(timeout);
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (ep->connect_done) {
+               ret = -EBUSY;
+               goto done;
+       } else if (is_connected(port)) {
+               ret = 0;
+               goto done;
+       } else if (is_closed(port)) {
+               ret = -ECONNRESET;
+               goto done;
+       } else if (!timeout) {
+               ret = -EAGAIN;
+               goto done;
+       }
+       ep->connect_done = &event;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       ret = wait_for_completion_interruptible_timeout(&event, timeout);
+
+       spin_lock_irqsave(&port->lock, flags);
+       ep->connect_done = NULL;
+
+       if (is_connected(port)) {
+               ret = 0;
+       } else {
+               if (is_closed(port))
+                       ret = -ECONNRESET;
+               else if (ret == -ERESTARTSYS)
+                       ret = -EINTR;
+               else if (!ret)
+                       ret = -ETIMEDOUT;
+       }
+
+done:
+       spin_unlock_irqrestore(&port->lock, flags);
+       return ret;
+}
+
+static inline int _ep_id(struct trpc_endpoint *ep)
+{
+       return ep - ep->port->peers;
+}
+
+static int queue_msg(struct trpc_node *src, struct trpc_endpoint *from,
+                    void *buf, size_t len, gfp_t gfp_flags)
+{
+       struct tegra_rpc_info *info = tegra_rpc;
+       struct trpc_endpoint *peer = from->out;
+       struct trpc_port *port = from->port;
+       struct trpc_msg *msg;
+       unsigned long flags;
+       int ret;
+
+       BUG_ON(len > TEGRA_RPC_MAX_MSG_LEN);
+       /* shouldn't be enqueueing to the endpoint */
+       BUG_ON(peer->ops && peer->ops->send);
+
+       DBG(TRPC_TRACE_MSG, "%s: queueing message for %s.%d\n", __func__,
+           port->name, _ep_id(peer));
+
+       msg = kmem_cache_alloc(info->msg_cache, gfp_flags);
+       if (!msg) {
+               pr_err("%s: can't alloc memory for msg\n", __func__);
+               return -ENOMEM;
+       }
+
+       memcpy(msg->payload, buf, len);
+       msg->len = len;
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (is_closed(port)) {
+               pr_err("%s: cannot send message for closed port %s.%d\n",
+                      __func__, port->name, _ep_id(peer));
+               ret = -ECONNRESET;
+               goto err;
+       } else if (!is_connected(port)) {
+               pr_err("%s: cannot send message for unconnected port %s.%d\n",
+                      __func__, port->name, _ep_id(peer));
+               ret = -ENOTCONN;
+               goto err;
+       }
+
+       list_add_tail(&msg->list, &peer->msg_list);
+       if (peer->ops && peer->ops->notify_recv)
+               peer->ops->notify_recv(peer);
+       wake_up_all(&peer->msg_waitq);
+       spin_unlock_irqrestore(&port->lock, flags);
+       return 0;
+
+err:
+       spin_unlock_irqrestore(&port->lock, flags);
+       kmem_cache_free(info->msg_cache, msg);
+       return ret;
+}
+
+/* Returns -ENOMEM if failed to allocate memory for the message. */
+int trpc_send_msg(struct trpc_node *src, struct trpc_endpoint *from,
+                 void *buf, size_t len, gfp_t gfp_flags)
+{
+       struct trpc_endpoint *peer = from->out;
+       struct trpc_port *port = from->port;
+
+       BUG_ON(len > TEGRA_RPC_MAX_MSG_LEN);
+
+       DBG(TRPC_TRACE_MSG, "%s: sending message from %s.%d to %s.%d\n",
+           __func__, port->name, _ep_id(from), port->name, _ep_id(peer));
+
+       if (peer->ops && peer->ops->send) {
+               might_sleep();
+               return peer->ops->send(peer, buf, len);
+       } else {
+               might_sleep_if(gfp_flags & __GFP_WAIT);
+               return queue_msg(src, from, buf, len, gfp_flags);
+       }
+}
+
+static inline struct trpc_msg *dequeue_msg_locked(struct trpc_endpoint *ep)
+{
+       struct trpc_msg *msg = NULL;
+
+       if (!list_empty(&ep->msg_list)) {
+               msg = list_first_entry(&ep->msg_list, struct trpc_msg, list);
+               list_del_init(&msg->list);
+       }
+
+       return msg;
+}
+
+static bool __should_wake(struct trpc_endpoint *ep)
+{
+       struct trpc_port *port = ep->port;
+       unsigned long flags;
+       bool ret;
+
+       spin_lock_irqsave(&port->lock, flags);
+       ret = !list_empty(&ep->msg_list) || is_closed(port);
+       spin_unlock_irqrestore(&port->lock, flags);
+       return ret;
+}
+
+int trpc_recv_msg(struct trpc_node *src, struct trpc_endpoint *ep,
+                 void *buf, size_t buf_len, long timeout)
+{
+       struct tegra_rpc_info *info = tegra_rpc;
+       struct trpc_port *port = ep->port;
+       struct trpc_msg *msg;
+       size_t len;
+       long ret;
+       unsigned long flags;
+
+       BUG_ON(buf_len > TEGRA_RPC_MAX_MSG_LEN);
+
+       spin_lock_irqsave(&port->lock, flags);
+       /* we allow closed ports to finish receiving already-queued messages */
+       msg = dequeue_msg_locked(ep);
+       if (msg) {
+               goto got_msg;
+       } else if (is_closed(port)) {
+               ret = -ECONNRESET;
+               goto out;
+       } else if (!is_connected(port)) {
+               ret = -ENOTCONN;
+               goto out;
+       }
+
+       if (timeout == 0) {
+               ret = 0;
+               goto out;
+       } else if (timeout < 0) {
+               timeout = MAX_SCHEDULE_TIMEOUT;
+       } else {
+               timeout = msecs_to_jiffies(timeout);
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+       DBG(TRPC_TRACE_MSG, "%s: waiting for message for %s.%d\n", __func__,
+           port->name, _ep_id(ep));
+
+       ret = wait_event_interruptible_timeout(ep->msg_waitq, __should_wake(ep),
+                                              timeout);
+
+       DBG(TRPC_TRACE_MSG, "%s: woke up for %s\n", __func__, port->name);
+       spin_lock_irqsave(&port->lock, flags);
+       msg = dequeue_msg_locked(ep);
+       if (!msg) {
+               if (is_closed(port))
+                       ret = -ECONNRESET;
+               else if (!ret)
+                       ret = -ETIMEDOUT;
+               else if (ret == -ERESTARTSYS)
+                       ret = -EINTR;
+               else
+                       pr_err("%s: error (%d) while receiving msg for '%s'\n",
+                              __func__, (int)ret, port->name);
+               goto out;
+       }
+
+got_msg:
+       spin_unlock_irqrestore(&port->lock, flags);
+       len = min(buf_len, msg->len);
+       memcpy(buf, msg->payload, len);
+       kmem_cache_free(info->msg_cache, msg);
+       return len;
+
+out:
+       spin_unlock_irqrestore(&port->lock, flags);
+       return ret;
+}
+
+int trpc_node_register(struct trpc_node *node)
+{
+       struct tegra_rpc_info *info = tegra_rpc;
+
+       if (!info)
+               return -ENOMEM;
+
+       pr_info("%s: Adding '%s' to node list\n", __func__, node->name);
+
+       mutex_lock(&info->node_lock);
+       if (node->type == TRPC_NODE_LOCAL)
+               list_add(&node->list, &info->node_list);
+       else
+               list_add_tail(&node->list, &info->node_list);
+       mutex_unlock(&info->node_lock);
+       return 0;
+}
+
+void trpc_node_unregister(struct trpc_node *node)
+{
+       struct tegra_rpc_info *info = tegra_rpc;
+
+       mutex_lock(&info->node_lock);
+       list_del(&node->list);
+       mutex_unlock(&info->node_lock);
+}
+
+static int __init tegra_rpc_init(void)
+{
+       struct tegra_rpc_info *rpc_info;
+       int ret;
+
+       rpc_info = kzalloc(sizeof(struct tegra_rpc_info), GFP_KERNEL);
+       if (!rpc_info) {
+               pr_err("%s: error allocating rpc_info\n", __func__);
+               return -ENOMEM;
+       }
+
+       rpc_info->ports = RB_ROOT;
+       spin_lock_init(&rpc_info->ports_lock);
+       INIT_LIST_HEAD(&rpc_info->node_list);
+       mutex_init(&rpc_info->node_lock);
+
+       rpc_info->msg_cache = KMEM_CACHE(trpc_msg, 0);
+       if (!rpc_info->msg_cache) {
+               pr_err("%s: unable to create message cache\n", __func__);
+               ret = -ENOMEM;
+               goto err_kmem_cache;
+       }
+       tegra_rpc = rpc_info;
+
+       return 0;
+
+err_kmem_cache:
+       kfree(rpc_info);
+       return ret;
+}
+
+subsys_initcall(tegra_rpc_init);
diff --git a/drivers/media/video/tegra/avp/trpc.h b/drivers/media/video/tegra/avp/trpc.h
new file mode 100644 (file)
index 0000000..859c94e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *   Dima Zavin <dima@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARM_MACH_TEGRA_RPC_H
+#define __ARM_MACH_TEGRA_RPC_H
+
+#include <linux/list.h>
+#include <linux/tegra_rpc.h>
+
+struct trpc_endpoint;
+struct trpc_ep_ops {
+       /* send is allowed to sleep */
+       int     (*send)(struct trpc_endpoint *ep, void *buf, size_t len);
+       /* notify_recv is NOT allowed to sleep */
+       void    (*notify_recv)(struct trpc_endpoint *ep);
+       /* close is allowed to sleep */
+       void    (*close)(struct trpc_endpoint *ep);
+};
+
+enum {
+       TRPC_NODE_LOCAL,
+       TRPC_NODE_REMOTE,
+};
+
+struct trpc_node {
+       struct list_head        list;
+       const char              *name;
+       int                     type;
+       void                    *priv;
+
+       int                     (*try_connect)(struct trpc_node *node,
+                                              struct trpc_node *src,
+                                              struct trpc_endpoint *from);
+};
+
+struct trpc_endpoint *trpc_peer(struct trpc_endpoint *ep);
+void *trpc_priv(struct trpc_endpoint *ep);
+const char *trpc_name(struct trpc_endpoint *ep);
+
+void trpc_put(struct trpc_endpoint *ep);
+void trpc_get(struct trpc_endpoint *ep);
+
+int trpc_send_msg(struct trpc_node *src, struct trpc_endpoint *ep, void *buf,
+                 size_t len, gfp_t gfp_flags);
+int trpc_recv_msg(struct trpc_node *src, struct trpc_endpoint *ep,
+                 void *buf, size_t len, long timeout);
+struct trpc_endpoint *trpc_create(struct trpc_node *owner, const char *name,
+                                 struct trpc_ep_ops *ops, void *priv);
+struct trpc_endpoint *trpc_create_connect(struct trpc_node *src, char *name,
+                                         struct trpc_ep_ops *ops, void *priv,
+                                         long timeout);
+int trpc_connect(struct trpc_endpoint *from, long timeout);
+struct trpc_endpoint *trpc_create_peer(struct trpc_node *owner,
+                                      struct trpc_endpoint *ep,
+                                      struct trpc_ep_ops *ops,
+                                      void *priv);
+void trpc_close(struct trpc_endpoint *ep);
+int trpc_wait_peer(struct trpc_endpoint *ep, long timeout);
+
+int trpc_node_register(struct trpc_node *node);
+void trpc_node_unregister(struct trpc_node *node);
+
+#endif
diff --git a/drivers/media/video/tegra/avp/trpc_local.c b/drivers/media/video/tegra/avp/trpc_local.c
new file mode 100644 (file)
index 0000000..5a941a7
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *   Dima Zavin <dima@android.com>
+ *
+ * Based on original NVRM code from NVIDIA, and a partial rewrite by
+ *   Gary King <gking@nvidia.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tegra_rpc.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include "trpc.h"
+#include "trpc_sema.h"
+
+struct rpc_info {
+       struct trpc_endpoint    *rpc_ep;
+       struct file             *sema_file;
+};
+
+/* ports names reserved for system functions, i.e. communicating with the
+ * AVP */
+static const char reserved_ports[][TEGRA_RPC_MAX_NAME_LEN] = {
+       "RPC_AVP_PORT",
+       "RPC_CPU_PORT",
+};
+static int num_reserved_ports = ARRAY_SIZE(reserved_ports);
+
+static void rpc_notify_recv(struct trpc_endpoint *ep);
+
+/* TODO: do we need to do anything when port is closed from the other side? */
+static struct trpc_ep_ops ep_ops = {
+       .notify_recv    = rpc_notify_recv,
+};
+
+static struct trpc_node rpc_node = {
+       .name   = "local",
+       .type   = TRPC_NODE_LOCAL,
+};
+
+static void rpc_notify_recv(struct trpc_endpoint *ep)
+{
+       struct rpc_info *info = trpc_priv(ep);
+
+       if (WARN_ON(!info))
+               return;
+       if (info->sema_file)
+               trpc_sema_signal(info->sema_file);
+}
+
+static int local_rpc_open(struct inode *inode, struct file *file)
+{
+       struct rpc_info *info;
+
+       info = kzalloc(sizeof(struct rpc_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       nonseekable_open(inode, file);
+       file->private_data = info;
+       return 0;
+}
+
+static int local_rpc_release(struct inode *inode, struct file *file)
+{
+       struct rpc_info *info = file->private_data;
+
+       if (info->rpc_ep)
+               trpc_close(info->rpc_ep);
+       if (info->sema_file)
+               fput(info->sema_file);
+       kfree(info);
+       file->private_data = NULL;
+       return 0;
+}
+
+static int __get_port_desc(struct tegra_rpc_port_desc *desc,
+                          unsigned int cmd, unsigned long arg)
+{
+       unsigned int size = _IOC_SIZE(cmd);
+
+       if (size != sizeof(struct tegra_rpc_port_desc))
+               return -EINVAL;
+       if (copy_from_user(desc, (void __user *)arg, sizeof(*desc)))
+               return -EFAULT;
+
+       desc->name[TEGRA_RPC_MAX_NAME_LEN - 1] = '\0';
+       return 0;
+}
+
+static char uniq_name[] = "aaaaaaaa+";
+static const int uniq_len = sizeof(uniq_name) - 1;
+static DEFINE_MUTEX(uniq_lock);
+
+static void _gen_port_name(char *new_name)
+{
+       int i;
+
+       mutex_lock(&uniq_lock);
+       for (i = 0; i < uniq_len - 1; i++) {
+               ++uniq_name[i];
+               if (uniq_name[i] != 'z')
+                       break;
+               uniq_name[i] = 'a';
+       }
+       strlcpy(new_name, uniq_name, TEGRA_RPC_MAX_NAME_LEN);
+       mutex_unlock(&uniq_lock);
+}
+
+static int _validate_port_name(const char *name)
+{
+       int i;
+
+       for (i = 0; i < num_reserved_ports; i++)
+               if (!strncmp(name, reserved_ports[i], TEGRA_RPC_MAX_NAME_LEN))
+                       return -EINVAL;
+       return 0;
+}
+
+static long local_rpc_ioctl(struct file *file, unsigned int cmd,
+                           unsigned long arg)
+{
+       struct rpc_info *info = file->private_data;
+       struct tegra_rpc_port_desc desc;
+       struct trpc_endpoint *ep;
+       int ret = 0;
+
+       if (_IOC_TYPE(cmd) != TEGRA_RPC_IOCTL_MAGIC ||
+           _IOC_NR(cmd) < TEGRA_RPC_IOCTL_MIN_NR ||
+           _IOC_NR(cmd) > TEGRA_RPC_IOCTL_MAX_NR) {
+               ret = -ENOTTY;
+               goto err;
+       }
+
+       switch (cmd) {
+       case TEGRA_RPC_IOCTL_PORT_CREATE:
+               if (info->rpc_ep) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               ret = __get_port_desc(&desc, cmd, arg);
+               if (ret)
+                       goto err;
+               if (desc.name[0]) {
+                       ret = _validate_port_name(desc.name);
+                       if (ret)
+                               goto err;
+               } else {
+                       _gen_port_name(desc.name);
+               }
+               if (desc.notify_fd != -1) {
+                       /* grab a reference to the trpc_sema fd */
+                       info->sema_file = trpc_sema_get_from_fd(desc.notify_fd);
+                       if (IS_ERR(info->sema_file)) {
+                               ret = PTR_ERR(info->sema_file);
+                               info->sema_file = NULL;
+                               goto err;
+                       }
+               }
+               ep = trpc_create(&rpc_node, desc.name, &ep_ops, info);
+               if (IS_ERR(ep)) {
+                       ret = PTR_ERR(ep);
+                       if (info->sema_file)
+                               fput(info->sema_file);
+                       info->sema_file = NULL;
+                       goto err;
+               }
+               info->rpc_ep = ep;
+               break;
+       case TEGRA_RPC_IOCTL_PORT_GET_NAME:
+               if (!info->rpc_ep) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if (copy_to_user((void __user *)arg,
+                                trpc_name(info->rpc_ep),
+                                TEGRA_RPC_MAX_NAME_LEN)) {
+                       ret = -EFAULT;
+                       goto err;
+               }
+               break;
+       case TEGRA_RPC_IOCTL_PORT_CONNECT:
+               if (!info->rpc_ep) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               ret = trpc_connect(info->rpc_ep, (long)arg);
+               if (ret) {
+                       pr_err("%s: can't connect to '%s' (%d)\n", __func__,
+                              trpc_name(info->rpc_ep), ret);
+                       goto err;
+               }
+               break;
+       case TEGRA_RPC_IOCTL_PORT_LISTEN:
+               if (!info->rpc_ep) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               ret = trpc_wait_peer(info->rpc_ep, (long)arg);
+               if (ret) {
+                       pr_err("%s: error waiting for peer for '%s' (%d)\n",
+                              __func__, trpc_name(info->rpc_ep), ret);
+                       goto err;
+               }
+               break;
+       default:
+               pr_err("%s: unknown cmd %d\n", __func__, _IOC_NR(cmd));
+               ret = -EINVAL;
+               goto err;
+       }
+
+       return 0;
+
+err:
+       if (ret && ret != -ERESTARTSYS)
+               pr_err("tegra_rpc: pid=%d ioctl=%x/%lx (%x) ret=%d\n",
+                      current->pid, cmd, arg, _IOC_NR(cmd), ret);
+       return (long)ret;
+}
+
+static ssize_t local_rpc_write(struct file *file, const char __user *buf,
+                              size_t count, loff_t *ppos)
+{
+       struct rpc_info *info = file->private_data;
+       u8 data[TEGRA_RPC_MAX_MSG_LEN];
+       int ret;
+
+       if (!info)
+               return -EINVAL;
+       else if (count > TEGRA_RPC_MAX_MSG_LEN)
+               return -EINVAL;
+
+       if (copy_from_user(data, buf, count))
+               return -EFAULT;
+
+       ret = trpc_send_msg(&rpc_node, info->rpc_ep, data, count,
+                           GFP_KERNEL);
+       if (ret)
+               return ret;
+       return count;
+}
+
+static ssize_t local_rpc_read(struct file *file, char __user *buf, size_t max,
+                             loff_t *ppos)
+{
+       struct rpc_info *info = file->private_data;
+       int ret;
+       u8 data[TEGRA_RPC_MAX_MSG_LEN];
+
+       if (max > TEGRA_RPC_MAX_MSG_LEN)
+               return -EINVAL;
+
+       ret = trpc_recv_msg(&rpc_node, info->rpc_ep, data,
+                           TEGRA_RPC_MAX_MSG_LEN, 0);
+       if (ret == 0)
+               return 0;
+       else if (ret < 0)
+               return ret;
+       else if (ret > max)
+               return -ENOSPC;
+       else if (copy_to_user(buf, data, ret))
+               return -EFAULT;
+
+       return ret;
+}
+
+static const struct file_operations local_rpc_misc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = local_rpc_open,
+       .release        = local_rpc_release,
+       .unlocked_ioctl = local_rpc_ioctl,
+       .write          = local_rpc_write,
+       .read           = local_rpc_read,
+};
+
+static struct miscdevice local_rpc_misc_device = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "tegra_rpc",
+       .fops   = &local_rpc_misc_fops,
+};
+
+int __init rpc_local_init(void)
+{
+       int ret;
+
+       ret = trpc_sema_init();
+       if (ret) {
+               pr_err("%s: error in trpc_sema_init\n", __func__);
+               goto err_sema_init;
+       }
+
+       ret = misc_register(&local_rpc_misc_device);
+       if (ret) {
+               pr_err("%s: can't register misc device\n", __func__);
+               goto err_misc;
+       }
+
+       ret = trpc_node_register(&rpc_node);
+       if (ret) {
+               pr_err("%s: can't register rpc node\n", __func__);
+               goto err_node_reg;
+       }
+       return 0;
+
+err_node_reg:
+       misc_deregister(&local_rpc_misc_device);
+err_misc:
+err_sema_init:
+       return ret;
+}
+
+module_init(rpc_local_init);
diff --git a/drivers/media/video/tegra/avp/trpc_sema.c b/drivers/media/video/tegra/avp/trpc_sema.c
new file mode 100644 (file)
index 0000000..b877257
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *   Dima Zavin <dima@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tegra_sema.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include "trpc_sema.h"
+
+struct trpc_sema {
+       wait_queue_head_t       wq;
+       spinlock_t              lock;
+       int                     count;
+};
+
+static int rpc_sema_minor = -1;
+
+static inline bool is_trpc_sema_file(struct file *file)
+{
+       dev_t rdev = file->f_dentry->d_inode->i_rdev;
+
+       if (MAJOR(rdev) == MISC_MAJOR && MINOR(rdev) == rpc_sema_minor)
+               return true;
+       return false;
+}
+
+struct file *trpc_sema_get_from_fd(int fd)
+{
+       struct file *file;
+
+       file = fget(fd);
+       if (unlikely(file == NULL)) {
+               pr_err("%s: fd %d is invalid\n", __func__, fd);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (!is_trpc_sema_file(file)) {
+               pr_err("%s: fd (%d) is not a trpc_sema file\n", __func__, fd);
+               fput(file);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return file;
+}
+
+int trpc_sema_signal(struct file *file)
+{
+       struct trpc_sema *info = file->private_data;
+       unsigned long flags;
+
+       if (!info)
+               return -EINVAL;
+
+       spin_lock_irqsave(&info->lock, flags);
+       info->count++;
+       wake_up_interruptible_all(&info->wq);
+       spin_unlock_irqrestore(&info->lock, flags);
+       return 0;
+}
+
+static int trpc_sema_wait(struct trpc_sema *info, long *timeleft)
+{
+       unsigned long flags;
+       int ret = 0;
+       unsigned long endtime;
+       long timeout = *timeleft;
+
+       *timeleft = 0;
+       if (timeout < 0) {
+               timeout = MAX_SCHEDULE_TIMEOUT;
+       } else if (timeout > 0) {
+               timeout = msecs_to_jiffies(timeout);
+               endtime = jiffies + timeout;
+       }
+
+again:
+       if (timeout)
+               ret = wait_event_interruptible_timeout(info->wq,
+                                                      info->count > 0,
+                                                      timeout);
+       spin_lock_irqsave(&info->lock, flags);
+       if (info->count > 0) {
+               info->count--;
+               ret = 0;
+       } else if (ret == 0 || timeout == 0) {
+               ret = -ETIMEDOUT;
+       } else if (ret < 0) {
+               ret = -EINTR;
+               if (timeout != MAX_SCHEDULE_TIMEOUT &&
+                   time_before(jiffies, endtime))
+                       *timeleft = jiffies_to_msecs(endtime - jiffies);
+               else
+                       *timeleft = 0;
+       } else {
+               /* we woke up but someone else got the semaphore and we have
+                * time left, try again */
+               timeout = ret;
+               spin_unlock_irqrestore(&info->lock, flags);
+               goto again;
+       }
+       spin_unlock_irqrestore(&info->lock, flags);
+       return ret;
+}
+
+static int trpc_sema_open(struct inode *inode, struct file *file)
+{
+       struct trpc_sema *info;
+
+       info = kzalloc(sizeof(struct trpc_sema), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       nonseekable_open(inode, file);
+       init_waitqueue_head(&info->wq);
+       spin_lock_init(&info->lock);
+       file->private_data = info;
+       return 0;
+}
+
+static int trpc_sema_release(struct inode *inode, struct file *file)
+{
+       struct trpc_sema *info = file->private_data;
+
+       file->private_data = NULL;
+       kfree(info);
+       return 0;
+}
+
+static long trpc_sema_ioctl(struct file *file, unsigned int cmd,
+                           unsigned long arg)
+{
+       struct trpc_sema *info = file->private_data;
+       int ret;
+       long timeout;
+
+       if (_IOC_TYPE(cmd) != TEGRA_SEMA_IOCTL_MAGIC ||
+           _IOC_NR(cmd) < TEGRA_SEMA_IOCTL_MIN_NR ||
+           _IOC_NR(cmd) > TEGRA_SEMA_IOCTL_MAX_NR)
+               return -ENOTTY;
+       else if (!info)
+               return -EINVAL;
+
+       switch (cmd) {
+       case TEGRA_SEMA_IOCTL_WAIT:
+               if (copy_from_user(&timeout, (void __user *)arg, sizeof(long)))
+                       return -EFAULT;
+               ret = trpc_sema_wait(info, &timeout);
+               if (ret != -EINTR)
+                       break;
+               if (copy_to_user((void __user *)arg, &timeout, sizeof(long)))
+                       ret = -EFAULT;
+               break;
+       case TEGRA_SEMA_IOCTL_SIGNAL:
+               ret = trpc_sema_signal(file);
+               break;
+       default:
+               pr_err("%s: Unknown tegra_sema ioctl 0x%x\n", __func__,
+                      _IOC_NR(cmd));
+               ret = -ENOTTY;
+               break;
+       }
+       return ret;
+}
+
+static const struct file_operations trpc_sema_misc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = trpc_sema_open,
+       .release        = trpc_sema_release,
+       .unlocked_ioctl = trpc_sema_ioctl,
+};
+
+static struct miscdevice trpc_sema_misc_device = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "tegra_sema",
+       .fops   = &trpc_sema_misc_fops,
+};
+
+int __init trpc_sema_init(void)
+{
+       int ret;
+
+       if (rpc_sema_minor >= 0) {
+               pr_err("%s: trpc_sema already registered\n", __func__);
+               return -EBUSY;
+       }
+
+       ret = misc_register(&trpc_sema_misc_device);
+       if (ret) {
+               pr_err("%s: can't register misc device\n", __func__);
+               return ret;
+       }
+
+       rpc_sema_minor = trpc_sema_misc_device.minor;
+       pr_info("%s: registered misc dev %d:%d\n", __func__, MISC_MAJOR,
+               rpc_sema_minor);
+
+       return 0;
+}
diff --git a/drivers/media/video/tegra/avp/trpc_sema.h b/drivers/media/video/tegra/avp/trpc_sema.h
new file mode 100644 (file)
index 0000000..566bbdb
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *   Dima Zavin <dima@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARM_MACH_TEGRA_RPC_SEMA_H
+#define __ARM_MACH_TEGRA_RPC_SEMA_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+struct file *trpc_sema_get_from_fd(int fd);
+int trpc_sema_signal(struct file *file);
+int __init trpc_sema_init(void);
+
+#endif
diff --git a/include/linux/tegra_rpc.h b/include/linux/tegra_rpc.h
new file mode 100644 (file)
index 0000000..16e6367
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *   Dima Zavin <dima@android.com>
+ *
+ * Based on original code from NVIDIA, and a partial rewrite by:
+ *   Gary King <gking@nvidia.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_TEGRA_RPC_H
+#define __LINUX_TEGRA_RPC_H
+
+#define TEGRA_RPC_MAX_MSG_LEN          256
+
+/* Note: the actual size of the name in the protocol message is 16 bytes,
+ * but that is because the name there is not NUL terminated, only NUL
+ * padded. */
+#define TEGRA_RPC_MAX_NAME_LEN         17
+
+struct tegra_rpc_port_desc {
+       char name[TEGRA_RPC_MAX_NAME_LEN];
+       int notify_fd; /* fd representing a trpc_sema to signal when a
+                       * message has been received */
+};
+
+#define TEGRA_RPC_IOCTL_MAGIC          'r'
+
+#define TEGRA_RPC_IOCTL_PORT_CREATE    _IOW(TEGRA_RPC_IOCTL_MAGIC, 0x20, struct tegra_rpc_port_desc)
+#define TEGRA_RPC_IOCTL_PORT_GET_NAME  _IOR(TEGRA_RPC_IOCTL_MAGIC, 0x21, char *)
+#define TEGRA_RPC_IOCTL_PORT_CONNECT   _IOR(TEGRA_RPC_IOCTL_MAGIC, 0x22, long)
+#define TEGRA_RPC_IOCTL_PORT_LISTEN    _IOR(TEGRA_RPC_IOCTL_MAGIC, 0x23, long)
+
+#define TEGRA_RPC_IOCTL_MIN_NR         _IOC_NR(TEGRA_RPC_IOCTL_PORT_CREATE)
+#define TEGRA_RPC_IOCTL_MAX_NR         _IOC_NR(TEGRA_RPC_IOCTL_PORT_LISTEN)
+
+#endif
diff --git a/include/linux/tegra_sema.h b/include/linux/tegra_sema.h
new file mode 100644 (file)
index 0000000..7b423b6
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *   Dima Zavin <dima@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_TEGRA_SEMA_H
+#define __LINUX_TEGRA_SEMA_H
+
+/* this shares the magic with the tegra RPC and AVP drivers.
+ *  See include/linux/tegra_avp.h and include/linux/tegra_rpc.h */
+#define TEGRA_SEMA_IOCTL_MAGIC         'r'
+
+/* If IOCTL_WAIT is interrupted by a signal and the timeout was not -1,
+ * then the value pointed to by the argument will be updated with the amount
+ * of time remaining for the wait. */
+#define TEGRA_SEMA_IOCTL_WAIT          _IOW(TEGRA_SEMA_IOCTL_MAGIC, 0x30, long *)
+#define TEGRA_SEMA_IOCTL_SIGNAL                _IO(TEGRA_SEMA_IOCTL_MAGIC, 0x31)
+
+#define TEGRA_SEMA_IOCTL_MIN_NR                _IOC_NR(TEGRA_SEMA_IOCTL_WAIT)
+#define TEGRA_SEMA_IOCTL_MAX_NR                _IOC_NR(TEGRA_SEMA_IOCTL_SIGNAL)
+
+#endif