Staging: HTC Dream: add rpcrouter driver
authorBrian Swetland <swetland@google.com>
Fri, 17 Jul 2009 11:09:09 +0000 (13:09 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Sep 2009 19:01:43 +0000 (12:01 -0700)
rpcrouter code is neccessarry for communication with QDSP and thus
many hardware components on HTC Dream, including camera hardware.

Cc: Brian Swetland <swetland@google.com>
Cc: Iliyan Malchev <ibm@android.com>
Cc: San Mehat <san@android.com>
Signed-off-by: Pavel Machek <pavel@ucw.cz>
drivers/staging/dream/smd/Kconfig [new file with mode: 0644]
drivers/staging/dream/smd/Makefile [new file with mode: 0644]
drivers/staging/dream/smd/rpc_server_dog_keepalive.c [new file with mode: 0644]
drivers/staging/dream/smd/rpc_server_time_remote.c [new file with mode: 0644]
drivers/staging/dream/smd/smd_rpcrouter.c [new file with mode: 0644]
drivers/staging/dream/smd/smd_rpcrouter.h [new file with mode: 0644]
drivers/staging/dream/smd/smd_rpcrouter_device.c [new file with mode: 0644]
drivers/staging/dream/smd/smd_rpcrouter_servers.c [new file with mode: 0644]

diff --git a/drivers/staging/dream/smd/Kconfig b/drivers/staging/dream/smd/Kconfig
new file mode 100644 (file)
index 0000000..3c1db3c
--- /dev/null
@@ -0,0 +1,25 @@
+config MSM_SMD
+       default y
+       bool "MSM Shared Memory Driver (SMD)"
+       help
+         Support for the shared memory interface between the apps
+         processor and the baseband processor.  Provides access to
+         the "shared heap", as well as virtual serial channels
+         used to communicate with various services on the baseband
+         processor.
+
+config MSM_ONCRPCROUTER
+       depends on MSM_SMD
+       default y
+       bool "MSM ONCRPC router support"
+       help
+         Support for the MSM ONCRPC router for communication between
+         the ARM9 and ARM11
+
+config MSM_RPCSERVERS
+       depends on MSM_ONCRPCROUTER
+       default y
+       bool "Kernel side RPC server bundle"
+       help
+         none
+
diff --git a/drivers/staging/dream/smd/Makefile b/drivers/staging/dream/smd/Makefile
new file mode 100644 (file)
index 0000000..892c741
--- /dev/null
@@ -0,0 +1,6 @@
+obj-$(CONFIG_MSM_SMD) += smd.o smd_tty.o smd_qmi.o
+obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o
+obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o
+obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o
+obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o
+obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o
diff --git a/drivers/staging/dream/smd/rpc_server_dog_keepalive.c b/drivers/staging/dream/smd/rpc_server_dog_keepalive.c
new file mode 100644 (file)
index 0000000..b23fccf
--- /dev/null
@@ -0,0 +1,68 @@
+/* arch/arm/mach-msm/rpc_server_dog_keepalive.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@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/module.h>
+#include <linux/kernel.h>
+#include <mach/msm_rpcrouter.h>
+
+/* dog_keepalive server definitions */
+
+#define DOG_KEEPALIVE_PROG 0x30000015
+#if CONFIG_MSM_AMSS_VERSION==6210
+#define DOG_KEEPALIVE_VERS 0
+#define RPC_DOG_KEEPALIVE_BEACON 1
+#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225)
+#define DOG_KEEPALIVE_VERS 0x731fa727
+#define RPC_DOG_KEEPALIVE_BEACON 2
+#elif CONFIG_MSM_AMSS_VERSION==6350
+#define DOG_KEEPALIVE_VERS 0x00010000
+#define RPC_DOG_KEEPALIVE_BEACON 2
+#else
+#error "Unsupported AMSS version"
+#endif
+#define RPC_DOG_KEEPALIVE_NULL 0
+
+
+/* TODO: Remove server registration with _VERS when modem is upated with _COMP*/
+
+static int handle_rpc_call(struct msm_rpc_server *server,
+                          struct rpc_request_hdr *req, unsigned len)
+{
+       switch (req->procedure) {
+       case RPC_DOG_KEEPALIVE_NULL:
+               return 0;
+       case RPC_DOG_KEEPALIVE_BEACON:
+               printk(KERN_INFO "DOG KEEPALIVE PING\n");
+               return 0;
+       default:
+               return -ENODEV;
+       }
+}
+
+static struct msm_rpc_server rpc_server = {
+       .prog = DOG_KEEPALIVE_PROG,
+       .vers = DOG_KEEPALIVE_VERS,
+       .rpc_call = handle_rpc_call,
+};
+
+static int __init rpc_server_init(void)
+{
+       /* Dual server registration to support backwards compatibility vers */
+       return msm_rpc_create_server(&rpc_server);
+}
+
+
+module_init(rpc_server_init);
diff --git a/drivers/staging/dream/smd/rpc_server_time_remote.c b/drivers/staging/dream/smd/rpc_server_time_remote.c
new file mode 100644 (file)
index 0000000..2f90fc8
--- /dev/null
@@ -0,0 +1,77 @@
+/* arch/arm/mach-msm/rpc_server_time_remote.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@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/module.h>
+#include <linux/kernel.h>
+#include <mach/msm_rpcrouter.h>
+
+/* time_remote_mtoa server definitions. */
+
+#define TIME_REMOTE_MTOA_PROG 0x3000005d
+#if CONFIG_MSM_AMSS_VERSION==6210
+#define TIME_REMOTE_MTOA_VERS 0
+#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225)
+#define TIME_REMOTE_MTOA_VERS 0x9202a8e4
+#elif CONFIG_MSM_AMSS_VERSION==6350
+#define TIME_REMOTE_MTOA_VERS 0x00010000
+#else
+#error "Unknown AMSS version"
+#endif
+#define RPC_TIME_REMOTE_MTOA_NULL   0
+#define RPC_TIME_TOD_SET_APPS_BASES 2
+
+struct rpc_time_tod_set_apps_bases_args {
+       uint32_t tick;
+       uint64_t stamp;
+};
+
+static int handle_rpc_call(struct msm_rpc_server *server,
+                          struct rpc_request_hdr *req, unsigned len)
+{
+       switch (req->procedure) {
+       case RPC_TIME_REMOTE_MTOA_NULL:
+               return 0;
+
+       case RPC_TIME_TOD_SET_APPS_BASES: {
+               struct rpc_time_tod_set_apps_bases_args *args;
+               args = (struct rpc_time_tod_set_apps_bases_args *)(req + 1);
+               args->tick = be32_to_cpu(args->tick);
+               args->stamp = be64_to_cpu(args->stamp);
+               printk(KERN_INFO "RPC_TIME_TOD_SET_APPS_BASES:\n"
+                      "\ttick = %d\n"
+                      "\tstamp = %lld\n",
+                      args->tick, args->stamp);
+               return 0;
+       }
+       default:
+               return -ENODEV;
+       }
+}
+
+static struct msm_rpc_server rpc_server = {
+       .prog = TIME_REMOTE_MTOA_PROG,
+       .vers = TIME_REMOTE_MTOA_VERS,
+       .rpc_call = handle_rpc_call,
+};
+
+static int __init rpc_server_init(void)
+{
+       /* Dual server registration to support backwards compatibility vers */
+       return msm_rpc_create_server(&rpc_server);
+}
+
+
+module_init(rpc_server_init);
diff --git a/drivers/staging/dream/smd/smd_rpcrouter.c b/drivers/staging/dream/smd/smd_rpcrouter.c
new file mode 100644 (file)
index 0000000..56557b8
--- /dev/null
@@ -0,0 +1,1274 @@
+/* arch/arm/mach-msm/smd_rpcrouter.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated.
+ * Author: San Mehat <san@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.
+ *
+ */
+
+/* TODO: handle cases where smd_write() will tempfail due to full fifo */
+/* TODO: thread priority? schedule a work to bump it? */
+/* TODO: maybe make server_list_lock a mutex */
+/* TODO: pool fragments to avoid kmalloc/kfree churn */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/wakelock.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include <asm/byteorder.h>
+
+#include <mach/msm_smd.h>
+#include "smd_rpcrouter.h"
+
+#define TRACE_R2R_MSG 0
+#define TRACE_R2R_RAW 0
+#define TRACE_RPC_MSG 0
+#define TRACE_NOTIFY_MSG 0
+
+#define MSM_RPCROUTER_DEBUG 0
+#define MSM_RPCROUTER_DEBUG_PKT 0
+#define MSM_RPCROUTER_R2R_DEBUG 0
+#define DUMP_ALL_RECEIVED_HEADERS 0
+
+#define DIAG(x...) printk("[RR] ERROR " x)
+
+#if MSM_RPCROUTER_DEBUG
+#define D(x...) printk(x)
+#else
+#define D(x...) do {} while (0)
+#endif
+
+#if TRACE_R2R_MSG
+#define RR(x...) printk("[RR] "x)
+#else
+#define RR(x...) do {} while (0)
+#endif
+
+#if TRACE_RPC_MSG
+#define IO(x...) printk("[RPC] "x)
+#else
+#define IO(x...) do {} while (0)
+#endif
+
+#if TRACE_NOTIFY_MSG
+#define NTFY(x...) printk(KERN_ERR "[NOTIFY] "x)
+#else
+#define NTFY(x...) do {} while (0)
+#endif
+
+static LIST_HEAD(local_endpoints);
+static LIST_HEAD(remote_endpoints);
+
+static LIST_HEAD(server_list);
+
+static smd_channel_t *smd_channel;
+static int initialized;
+static wait_queue_head_t newserver_wait;
+static wait_queue_head_t smd_wait;
+
+static DEFINE_SPINLOCK(local_endpoints_lock);
+static DEFINE_SPINLOCK(remote_endpoints_lock);
+static DEFINE_SPINLOCK(server_list_lock);
+static DEFINE_SPINLOCK(smd_lock);
+
+static struct workqueue_struct *rpcrouter_workqueue;
+static struct wake_lock rpcrouter_wake_lock;
+static int rpcrouter_need_len;
+
+static atomic_t next_xid = ATOMIC_INIT(1);
+static uint8_t next_pacmarkid;
+
+static void do_read_data(struct work_struct *work);
+static void do_create_pdevs(struct work_struct *work);
+static void do_create_rpcrouter_pdev(struct work_struct *work);
+
+static DECLARE_WORK(work_read_data, do_read_data);
+static DECLARE_WORK(work_create_pdevs, do_create_pdevs);
+static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev);
+
+#define RR_STATE_IDLE    0
+#define RR_STATE_HEADER  1
+#define RR_STATE_BODY    2
+#define RR_STATE_ERROR   3
+
+struct rr_context {
+       struct rr_packet *pkt;
+       uint8_t *ptr;
+       uint32_t state; /* current assembly state */
+       uint32_t count; /* bytes needed in this state */
+};
+
+struct rr_context the_rr_context;
+
+static struct platform_device rpcrouter_pdev = {
+       .name           = "oncrpc_router",
+       .id             = -1,
+};
+
+
+static int rpcrouter_send_control_msg(union rr_control_msg *msg)
+{
+       struct rr_header hdr;
+       unsigned long flags;
+       int need;
+
+       if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !initialized) {
+               printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, "
+                      "router not initialized\n");
+               return -EINVAL;
+       }
+
+       hdr.version = RPCROUTER_VERSION;
+       hdr.type = msg->cmd;
+       hdr.src_pid = RPCROUTER_PID_LOCAL;
+       hdr.src_cid = RPCROUTER_ROUTER_ADDRESS;
+       hdr.confirm_rx = 0;
+       hdr.size = sizeof(*msg);
+       hdr.dst_pid = 0;
+       hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS;
+
+       /* TODO: what if channel is full? */
+
+       need = sizeof(hdr) + hdr.size;
+       spin_lock_irqsave(&smd_lock, flags);
+       while (smd_write_avail(smd_channel) < need) {
+               spin_unlock_irqrestore(&smd_lock, flags);
+               msleep(250);
+               spin_lock_irqsave(&smd_lock, flags);
+       }
+       smd_write(smd_channel, &hdr, sizeof(hdr));
+       smd_write(smd_channel, msg, hdr.size);
+       spin_unlock_irqrestore(&smd_lock, flags);
+       return 0;
+}
+
+static struct rr_server *rpcrouter_create_server(uint32_t pid,
+                                                       uint32_t cid,
+                                                       uint32_t prog,
+                                                       uint32_t ver)
+{
+       struct rr_server *server;
+       unsigned long flags;
+       int rc;
+
+       server = kmalloc(sizeof(struct rr_server), GFP_KERNEL);
+       if (!server)
+               return ERR_PTR(-ENOMEM);
+
+       memset(server, 0, sizeof(struct rr_server));
+       server->pid = pid;
+       server->cid = cid;
+       server->prog = prog;
+       server->vers = ver;
+
+       spin_lock_irqsave(&server_list_lock, flags);
+       list_add_tail(&server->list, &server_list);
+       spin_unlock_irqrestore(&server_list_lock, flags);
+
+       if (pid == RPCROUTER_PID_REMOTE) {
+               rc = msm_rpcrouter_create_server_cdev(server);
+               if (rc < 0)
+                       goto out_fail;
+       }
+       return server;
+out_fail:
+       spin_lock_irqsave(&server_list_lock, flags);
+       list_del(&server->list);
+       spin_unlock_irqrestore(&server_list_lock, flags);
+       kfree(server);
+       return ERR_PTR(rc);
+}
+
+static void rpcrouter_destroy_server(struct rr_server *server)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&server_list_lock, flags);
+       list_del(&server->list);
+       spin_unlock_irqrestore(&server_list_lock, flags);
+       device_destroy(msm_rpcrouter_class, server->device_number);
+       kfree(server);
+}
+
+static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver)
+{
+       struct rr_server *server;
+       unsigned long flags;
+
+       spin_lock_irqsave(&server_list_lock, flags);
+       list_for_each_entry(server, &server_list, list) {
+               if (server->prog == prog
+                && server->vers == ver) {
+                       spin_unlock_irqrestore(&server_list_lock, flags);
+                       return server;
+               }
+       }
+       spin_unlock_irqrestore(&server_list_lock, flags);
+       return NULL;
+}
+
+static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev)
+{
+       struct rr_server *server;
+       unsigned long flags;
+
+       spin_lock_irqsave(&server_list_lock, flags);
+       list_for_each_entry(server, &server_list, list) {
+               if (server->device_number == dev) {
+                       spin_unlock_irqrestore(&server_list_lock, flags);
+                       return server;
+               }
+       }
+       spin_unlock_irqrestore(&server_list_lock, flags);
+       return NULL;
+}
+
+struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev)
+{
+       struct msm_rpc_endpoint *ept;
+       unsigned long flags;
+
+       ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL);
+       if (!ept)
+               return NULL;
+       memset(ept, 0, sizeof(struct msm_rpc_endpoint));
+
+       /* mark no reply outstanding */
+       ept->reply_pid = 0xffffffff;
+
+       ept->cid = (uint32_t) ept;
+       ept->pid = RPCROUTER_PID_LOCAL;
+       ept->dev = dev;
+
+       if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) {
+               struct rr_server *srv;
+               /*
+                * This is a userspace client which opened
+                * a program/ver devicenode. Bind the client
+                * to that destination
+                */
+               srv = rpcrouter_lookup_server_by_dev(dev);
+               /* TODO: bug? really? */
+               BUG_ON(!srv);
+
+               ept->dst_pid = srv->pid;
+               ept->dst_cid = srv->cid;
+               ept->dst_prog = cpu_to_be32(srv->prog);
+               ept->dst_vers = cpu_to_be32(srv->vers);
+
+               D("Creating local ept %p @ %08x:%08x\n", ept, srv->prog, srv->vers);
+       } else {
+               /* mark not connected */
+               ept->dst_pid = 0xffffffff;
+               D("Creating a master local ept %p\n", ept);
+       }
+
+       init_waitqueue_head(&ept->wait_q);
+       INIT_LIST_HEAD(&ept->read_q);
+       spin_lock_init(&ept->read_q_lock);
+       wake_lock_init(&ept->read_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_read");
+       INIT_LIST_HEAD(&ept->incomplete);
+
+       spin_lock_irqsave(&local_endpoints_lock, flags);
+       list_add_tail(&ept->list, &local_endpoints);
+       spin_unlock_irqrestore(&local_endpoints_lock, flags);
+       return ept;
+}
+
+int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept)
+{
+       int rc;
+       union rr_control_msg msg;
+
+       msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT;
+       msg.cli.pid = ept->pid;
+       msg.cli.cid = ept->cid;
+
+       RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid);
+       rc = rpcrouter_send_control_msg(&msg);
+       if (rc < 0)
+               return rc;
+
+       wake_lock_destroy(&ept->read_q_wake_lock);
+       list_del(&ept->list);
+       kfree(ept);
+       return 0;
+}
+
+static int rpcrouter_create_remote_endpoint(uint32_t cid)
+{
+       struct rr_remote_endpoint *new_c;
+       unsigned long flags;
+
+       new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL);
+       if (!new_c)
+               return -ENOMEM;
+       memset(new_c, 0, sizeof(struct rr_remote_endpoint));
+
+       new_c->cid = cid;
+       new_c->pid = RPCROUTER_PID_REMOTE;
+       init_waitqueue_head(&new_c->quota_wait);
+       spin_lock_init(&new_c->quota_lock);
+
+       spin_lock_irqsave(&remote_endpoints_lock, flags);
+       list_add_tail(&new_c->list, &remote_endpoints);
+       spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+       return 0;
+}
+
+static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid)
+{
+       struct msm_rpc_endpoint *ept;
+       unsigned long flags;
+
+       spin_lock_irqsave(&local_endpoints_lock, flags);
+       list_for_each_entry(ept, &local_endpoints, list) {
+               if (ept->cid == cid) {
+                       spin_unlock_irqrestore(&local_endpoints_lock, flags);
+                       return ept;
+               }
+       }
+       spin_unlock_irqrestore(&local_endpoints_lock, flags);
+       return NULL;
+}
+
+static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t cid)
+{
+       struct rr_remote_endpoint *ept;
+       unsigned long flags;
+
+       spin_lock_irqsave(&remote_endpoints_lock, flags);
+       list_for_each_entry(ept, &remote_endpoints, list) {
+               if (ept->cid == cid) {
+                       spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+                       return ept;
+               }
+       }
+       spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+       return NULL;
+}
+
+static int process_control_msg(union rr_control_msg *msg, int len)
+{
+       union rr_control_msg ctl;
+       struct rr_server *server;
+       struct rr_remote_endpoint *r_ept;
+       int rc = 0;
+       unsigned long flags;
+
+       if (len != sizeof(*msg)) {
+               printk(KERN_ERR "rpcrouter: r2r msg size %d != %d\n",
+                      len, sizeof(*msg));
+               return -EINVAL;
+       }
+
+       switch (msg->cmd) {
+       case RPCROUTER_CTRL_CMD_HELLO:
+               RR("o HELLO\n");
+
+               RR("x HELLO\n");
+               memset(&ctl, 0, sizeof(ctl));
+               ctl.cmd = RPCROUTER_CTRL_CMD_HELLO;
+               rpcrouter_send_control_msg(&ctl);
+
+               initialized = 1;
+
+               /* Send list of servers one at a time */
+               ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
+
+               /* TODO: long time to hold a spinlock... */
+               spin_lock_irqsave(&server_list_lock, flags);
+               list_for_each_entry(server, &server_list, list) {
+                       ctl.srv.pid = server->pid;
+                       ctl.srv.cid = server->cid;
+                       ctl.srv.prog = server->prog;
+                       ctl.srv.vers = server->vers;
+
+                       RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+                          server->pid, server->cid,
+                          server->prog, server->vers);
+
+                       rpcrouter_send_control_msg(&ctl);
+               }
+               spin_unlock_irqrestore(&server_list_lock, flags);
+
+               queue_work(rpcrouter_workqueue, &work_create_rpcrouter_pdev);
+               break;
+
+       case RPCROUTER_CTRL_CMD_RESUME_TX:
+               RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
+
+               r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
+               if (!r_ept) {
+                       printk(KERN_ERR
+                              "rpcrouter: Unable to resume client\n");
+                       break;
+               }
+               spin_lock_irqsave(&r_ept->quota_lock, flags);
+               r_ept->tx_quota_cntr = 0;
+               spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+               wake_up(&r_ept->quota_wait);
+               break;
+
+       case RPCROUTER_CTRL_CMD_NEW_SERVER:
+               RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+                  msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers);
+
+               server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
+
+               if (!server) {
+                       server = rpcrouter_create_server(
+                               msg->srv.pid, msg->srv.cid,
+                               msg->srv.prog, msg->srv.vers);
+                       if (!server)
+                               return -ENOMEM;
+                       /*
+                        * XXX: Verify that its okay to add the
+                        * client to our remote client list
+                        * if we get a NEW_SERVER notification
+                        */
+                       if (!rpcrouter_lookup_remote_endpoint(msg->srv.cid)) {
+                               rc = rpcrouter_create_remote_endpoint(
+                                       msg->srv.cid);
+                               if (rc < 0)
+                                       printk(KERN_ERR
+                                               "rpcrouter:Client create"
+                                               "error (%d)\n", rc);
+                       }
+                       schedule_work(&work_create_pdevs);
+                       wake_up(&newserver_wait);
+               } else {
+                       if ((server->pid == msg->srv.pid) &&
+                           (server->cid == msg->srv.cid)) {
+                               printk(KERN_ERR "rpcrouter: Duplicate svr\n");
+                       } else {
+                               server->pid = msg->srv.pid;
+                               server->cid = msg->srv.cid;
+                       }
+               }
+               break;
+
+       case RPCROUTER_CTRL_CMD_REMOVE_SERVER:
+               RR("o REMOVE_SERVER prog=%08x:%d\n",
+                  msg->srv.prog, msg->srv.vers);
+               server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
+               if (server)
+                       rpcrouter_destroy_server(server);
+               break;
+
+       case RPCROUTER_CTRL_CMD_REMOVE_CLIENT:
+               RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
+               if (msg->cli.pid != RPCROUTER_PID_REMOTE) {
+                       printk(KERN_ERR
+                              "rpcrouter: Denying remote removal of "
+                              "local client\n");
+                       break;
+               }
+               r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
+               if (r_ept) {
+                       spin_lock_irqsave(&remote_endpoints_lock, flags);
+                       list_del(&r_ept->list);
+                       spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+                       kfree(r_ept);
+               }
+
+               /* Notify local clients of this event */
+               printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n");
+               rc = -ENOSYS;
+
+               break;
+       default:
+               RR("o UNKNOWN(%08x)\n", msg->cmd);
+               rc = -ENOSYS;
+       }
+
+       return rc;
+}
+
+static void do_create_rpcrouter_pdev(struct work_struct *work)
+{
+       platform_device_register(&rpcrouter_pdev);
+}
+
+static void do_create_pdevs(struct work_struct *work)
+{
+       unsigned long flags;
+       struct rr_server *server;
+
+       /* TODO: race if destroyed while being registered */
+       spin_lock_irqsave(&server_list_lock, flags);
+       list_for_each_entry(server, &server_list, list) {
+               if (server->pid == RPCROUTER_PID_REMOTE) {
+                       if (server->pdev_name[0] == 0) {
+                               spin_unlock_irqrestore(&server_list_lock,
+                                                      flags);
+                               msm_rpcrouter_create_server_pdev(server);
+                               schedule_work(&work_create_pdevs);
+                               return;
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&server_list_lock, flags);
+}
+
+static void rpcrouter_smdnotify(void *_dev, unsigned event)
+{
+       if (event != SMD_EVENT_DATA)
+               return;
+
+       if (smd_read_avail(smd_channel) >= rpcrouter_need_len)
+               wake_lock(&rpcrouter_wake_lock);
+       wake_up(&smd_wait);
+}
+
+static void *rr_malloc(unsigned sz)
+{
+       void *ptr = kmalloc(sz, GFP_KERNEL);
+       if (ptr)
+               return ptr;
+
+       printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz);
+       do {
+               ptr = kmalloc(sz, GFP_KERNEL);
+       } while (!ptr);
+
+       return ptr;
+}
+
+/* TODO: deal with channel teardown / restore */
+static int rr_read(void *data, int len)
+{
+       int rc;
+       unsigned long flags;
+//     printk("rr_read() %d\n", len);
+       for(;;) {
+               spin_lock_irqsave(&smd_lock, flags);
+               if (smd_read_avail(smd_channel) >= len) {
+                       rc = smd_read(smd_channel, data, len);
+                       spin_unlock_irqrestore(&smd_lock, flags);
+                       if (rc == len)
+                               return 0;
+                       else
+                               return -EIO;
+               }
+               rpcrouter_need_len = len;
+               wake_unlock(&rpcrouter_wake_lock);
+               spin_unlock_irqrestore(&smd_lock, flags);
+
+//             printk("rr_read: waiting (%d)\n", len);
+               wait_event(smd_wait, smd_read_avail(smd_channel) >= len);
+       }
+       return 0;
+}
+
+static uint32_t r2r_buf[RPCROUTER_MSGSIZE_MAX];
+
+static void do_read_data(struct work_struct *work)
+{
+       struct rr_header hdr;
+       struct rr_packet *pkt;
+       struct rr_fragment *frag;
+       struct msm_rpc_endpoint *ept;
+       uint32_t pm, mid;
+       unsigned long flags;
+
+       if (rr_read(&hdr, sizeof(hdr)))
+               goto fail_io;
+
+#if TRACE_R2R_RAW
+       RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
+          hdr.version, hdr.type, hdr.src_pid, hdr.src_cid,
+          hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid);
+#endif
+
+       if (hdr.version != RPCROUTER_VERSION) {
+               DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION);
+               goto fail_data;
+       }
+       if (hdr.size > RPCROUTER_MSGSIZE_MAX) {
+               DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX);
+               goto fail_data;
+       }
+
+       if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) {
+               if (rr_read(r2r_buf, hdr.size))
+                       goto fail_io;
+               process_control_msg((void*) r2r_buf, hdr.size);
+               goto done;
+       }
+
+       if (hdr.size < sizeof(pm)) {
+               DIAG("runt packet (no pacmark)\n");
+               goto fail_data;
+       }
+       if (rr_read(&pm, sizeof(pm)))
+               goto fail_io;
+
+       hdr.size -= sizeof(pm);
+
+       frag = rr_malloc(hdr.size + sizeof(*frag));
+       frag->next = NULL;
+       frag->length = hdr.size;
+       if (rr_read(frag->data, hdr.size))
+               goto fail_io;
+
+       ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid);
+       if (!ept) {
+               DIAG("no local ept for cid %08x\n", hdr.dst_cid);
+               kfree(frag);
+               goto done;
+       }
+
+       /* See if there is already a partial packet that matches our mid
+        * and if so, append this fragment to that packet.
+        */
+       mid = PACMARK_MID(pm);
+       list_for_each_entry(pkt, &ept->incomplete, list) {
+               if (pkt->mid == mid) {
+                       pkt->last->next = frag;
+                       pkt->last = frag;
+                       pkt->length += frag->length;
+                       if (PACMARK_LAST(pm)) {
+                               list_del(&pkt->list);
+                               goto packet_complete;
+                       }
+                       goto done;
+               }
+       }
+       /* This mid is new -- create a packet for it, and put it on
+        * the incomplete list if this fragment is not a last fragment,
+        * otherwise put it on the read queue.
+        */
+       pkt = rr_malloc(sizeof(struct rr_packet));
+       pkt->first = frag;
+       pkt->last = frag;
+       memcpy(&pkt->hdr, &hdr, sizeof(hdr));
+       pkt->mid = mid;
+       pkt->length = frag->length;
+       if (!PACMARK_LAST(pm)) {
+               list_add_tail(&pkt->list, &ept->incomplete);
+               goto done;
+       }
+
+packet_complete:
+       spin_lock_irqsave(&ept->read_q_lock, flags);
+       wake_lock(&ept->read_q_wake_lock);
+       list_add_tail(&pkt->list, &ept->read_q);
+       wake_up(&ept->wait_q);
+       spin_unlock_irqrestore(&ept->read_q_lock, flags);
+done:
+
+       if (hdr.confirm_rx) {
+               union rr_control_msg msg;
+
+               msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX;
+               msg.cli.pid = hdr.dst_pid;
+               msg.cli.cid = hdr.dst_cid;
+
+               RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid);
+               rpcrouter_send_control_msg(&msg);
+       }
+
+       queue_work(rpcrouter_workqueue, &work_read_data);
+       return;
+
+fail_io:
+fail_data:
+       printk(KERN_ERR "rpc_router has died\n");
+       wake_unlock(&rpcrouter_wake_lock);
+}
+
+void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog,
+                      uint32_t vers, uint32_t proc)
+{
+       memset(hdr, 0, sizeof(struct rpc_request_hdr));
+       hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
+       hdr->rpc_vers = cpu_to_be32(2);
+       hdr->prog = cpu_to_be32(prog);
+       hdr->vers = cpu_to_be32(vers);
+       hdr->procedure = cpu_to_be32(proc);
+}
+
+struct msm_rpc_endpoint *msm_rpc_open(void)
+{
+       struct msm_rpc_endpoint *ept;
+
+       ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0));
+       if (ept == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       return ept;
+}
+
+int msm_rpc_close(struct msm_rpc_endpoint *ept)
+{
+       return msm_rpcrouter_destroy_local_endpoint(ept);
+}
+EXPORT_SYMBOL(msm_rpc_close);
+
+int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count)
+{
+       struct rr_header hdr;
+       uint32_t pacmark;
+       struct rpc_request_hdr *rq = buffer;
+       struct rr_remote_endpoint *r_ept;
+       unsigned long flags;
+       int needed;
+       DEFINE_WAIT(__wait);
+
+       /* TODO: fragmentation for large outbound packets */
+       if (count > (RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t)) || !count)
+               return -EINVAL;
+
+       /* snoop the RPC packet and enforce permissions */
+
+       /* has to have at least the xid and type fields */
+       if (count < (sizeof(uint32_t) * 2)) {
+               printk(KERN_ERR "rr_write: rejecting runt packet\n");
+               return -EINVAL;
+       }
+
+       if (rq->type == 0) {
+               /* RPC CALL */
+               if (count < (sizeof(uint32_t) * 6)) {
+                       printk(KERN_ERR
+                              "rr_write: rejecting runt call packet\n");
+                       return -EINVAL;
+               }
+               if (ept->dst_pid == 0xffffffff) {
+                       printk(KERN_ERR "rr_write: not connected\n");
+                       return -ENOTCONN;
+               }
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+               if ((ept->dst_prog != rq->prog) ||
+                       !msm_rpc_is_compatible_version(
+                                       be32_to_cpu(ept->dst_vers),
+                                       be32_to_cpu(rq->vers))) {
+#else
+               if (ept->dst_prog != rq->prog || ept->dst_vers != rq->vers) {
+#endif
+                       printk(KERN_ERR
+                              "rr_write: cannot write to %08x:%d "
+                              "(bound to %08x:%d)\n",
+                              be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+                              be32_to_cpu(ept->dst_prog),
+                              be32_to_cpu(ept->dst_vers));
+                       return -EINVAL;
+               }
+               hdr.dst_pid = ept->dst_pid;
+               hdr.dst_cid = ept->dst_cid;
+               IO("CALL on ept %p to %08x:%08x @ %d:%08x (%d bytes) (xid %x proc %x)\n",
+                  ept,
+                  be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+                  ept->dst_pid, ept->dst_cid, count,
+                  be32_to_cpu(rq->xid), be32_to_cpu(rq->procedure));
+       } else {
+               /* RPC REPLY */
+               /* TODO: locking */
+               if (ept->reply_pid == 0xffffffff) {
+                       printk(KERN_ERR
+                              "rr_write: rejecting unexpected reply\n");
+                       return -EINVAL;
+               }
+               if (ept->reply_xid != rq->xid) {
+                       printk(KERN_ERR
+                              "rr_write: rejecting packet w/ bad xid\n");
+                       return -EINVAL;
+               }
+
+               hdr.dst_pid = ept->reply_pid;
+               hdr.dst_cid = ept->reply_cid;
+
+               /* consume this reply */
+               ept->reply_pid = 0xffffffff;
+
+               IO("REPLY on ept %p to xid=%d @ %d:%08x (%d bytes)\n",
+                  ept,
+                  be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count);
+       }
+
+       r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_cid);
+
+       if (!r_ept) {
+               printk(KERN_ERR
+                       "msm_rpc_write(): No route to ept "
+                       "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid);
+               return -EHOSTUNREACH;
+       }
+
+       /* Create routing header */
+       hdr.type = RPCROUTER_CTRL_CMD_DATA;
+       hdr.version = RPCROUTER_VERSION;
+       hdr.src_pid = ept->pid;
+       hdr.src_cid = ept->cid;
+       hdr.confirm_rx = 0;
+       hdr.size = count + sizeof(uint32_t);
+
+       for (;;) {
+               prepare_to_wait(&r_ept->quota_wait, &__wait,
+                               TASK_INTERRUPTIBLE);
+               spin_lock_irqsave(&r_ept->quota_lock, flags);
+               if (r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA)
+                       break;
+               if (signal_pending(current) &&
+                   (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))
+                       break;
+               spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+               schedule();
+       }
+       finish_wait(&r_ept->quota_wait, &__wait);
+
+       if (signal_pending(current) &&
+           (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) {
+               spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+               return -ERESTARTSYS;
+       }
+       r_ept->tx_quota_cntr++;
+       if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA)
+               hdr.confirm_rx = 1;
+
+       /* bump pacmark while interrupts disabled to avoid race
+        * probably should be atomic op instead
+        */
+       pacmark = PACMARK(count, ++next_pacmarkid, 0, 1);
+
+       spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+
+       spin_lock_irqsave(&smd_lock, flags);
+
+       needed = sizeof(hdr) + hdr.size;
+       while (smd_write_avail(smd_channel) < needed) {
+               spin_unlock_irqrestore(&smd_lock, flags);
+               msleep(250);
+               spin_lock_irqsave(&smd_lock, flags);
+       }
+
+       /* TODO: deal with full fifo */
+       smd_write(smd_channel, &hdr, sizeof(hdr));
+       smd_write(smd_channel, &pacmark, sizeof(pacmark));
+       smd_write(smd_channel, buffer, count);
+
+       spin_unlock_irqrestore(&smd_lock, flags);
+
+       return count;
+}
+EXPORT_SYMBOL(msm_rpc_write);
+
+/*
+ * NOTE: It is the responsibility of the caller to kfree buffer
+ */
+int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer,
+                unsigned user_len, long timeout)
+{
+       struct rr_fragment *frag, *next;
+       char *buf;
+       int rc;
+
+       rc = __msm_rpc_read(ept, &frag, user_len, timeout);
+       if (rc <= 0)
+               return rc;
+
+       /* single-fragment messages conveniently can be
+        * returned as-is (the buffer is at the front)
+        */
+       if (frag->next == 0) {
+               *buffer = (void*) frag;
+               return rc;
+       }
+
+       /* multi-fragment messages, we have to do it the
+        * hard way, which is rather disgusting right now
+        */
+       buf = rr_malloc(rc);
+       *buffer = buf;
+
+       while (frag != NULL) {
+               memcpy(buf, frag->data, frag->length);
+               next = frag->next;
+               buf += frag->length;
+               kfree(frag);
+               frag = next;
+       }
+
+       return rc;
+}
+
+int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc,
+                void *_request, int request_size,
+                long timeout)
+{
+       return msm_rpc_call_reply(ept, proc,
+                                 _request, request_size,
+                                 NULL, 0, timeout);
+}
+EXPORT_SYMBOL(msm_rpc_call);
+
+int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc,
+                      void *_request, int request_size,
+                      void *_reply, int reply_size,
+                      long timeout)
+{
+       struct rpc_request_hdr *req = _request;
+       struct rpc_reply_hdr *reply;
+       int rc;
+
+       if (request_size < sizeof(*req))
+               return -ETOOSMALL;
+
+       if (ept->dst_pid == 0xffffffff)
+               return -ENOTCONN;
+
+       /* We can't use msm_rpc_setup_req() here, because dst_prog and
+        * dst_vers here are already in BE.
+        */
+       memset(req, 0, sizeof(*req));
+       req->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
+       req->rpc_vers = cpu_to_be32(2);
+       req->prog = ept->dst_prog;
+       req->vers = ept->dst_vers;
+       req->procedure = cpu_to_be32(proc);
+
+       rc = msm_rpc_write(ept, req, request_size);
+       if (rc < 0)
+               return rc;
+
+       for (;;) {
+               rc = msm_rpc_read(ept, (void*) &reply, -1, timeout);
+               if (rc < 0)
+                       return rc;
+               if (rc < (3 * sizeof(uint32_t))) {
+                       rc = -EIO;
+                       break;
+               }
+               /* we should not get CALL packets -- ignore them */
+               if (reply->type == 0) {
+                       kfree(reply);
+                       continue;
+               }
+               /* If an earlier call timed out, we could get the (no
+                * longer wanted) reply for it.  Ignore replies that
+                * we don't expect.
+                */
+               if (reply->xid != req->xid) {
+                       kfree(reply);
+                       continue;
+               }
+               if (reply->reply_stat != 0) {
+                       rc = -EPERM;
+                       break;
+               }
+               if (reply->data.acc_hdr.accept_stat != 0) {
+                       rc = -EINVAL;
+                       break;
+               }
+               if (_reply == NULL) {
+                       rc = 0;
+                       break;
+               }
+               if (rc > reply_size) {
+                       rc = -ENOMEM;
+               } else {
+                       memcpy(_reply, reply, rc);
+               }
+               break;
+       }
+       kfree(reply);
+       return rc;
+}
+EXPORT_SYMBOL(msm_rpc_call_reply);
+
+
+static inline int ept_packet_available(struct msm_rpc_endpoint *ept)
+{
+       unsigned long flags;
+       int ret;
+       spin_lock_irqsave(&ept->read_q_lock, flags);
+       ret = !list_empty(&ept->read_q);
+       spin_unlock_irqrestore(&ept->read_q_lock, flags);
+       return ret;
+}
+
+int __msm_rpc_read(struct msm_rpc_endpoint *ept,
+                  struct rr_fragment **frag_ret,
+                  unsigned len, long timeout)
+{
+       struct rr_packet *pkt;
+       struct rpc_request_hdr *rq;
+       DEFINE_WAIT(__wait);
+       unsigned long flags;
+       int rc;
+
+       IO("READ on ept %p\n", ept);
+
+       if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) {
+               if (timeout < 0) {
+                       wait_event(ept->wait_q, ept_packet_available(ept));
+               } else {
+                       rc = wait_event_timeout(
+                               ept->wait_q, ept_packet_available(ept),
+                               timeout);
+                       if (rc == 0)
+                               return -ETIMEDOUT;
+               }
+       } else {
+               if (timeout < 0) {
+                       rc = wait_event_interruptible(
+                               ept->wait_q, ept_packet_available(ept));
+                       if (rc < 0)
+                               return rc;
+               } else {
+                       rc = wait_event_interruptible_timeout(
+                               ept->wait_q, ept_packet_available(ept),
+                               timeout);
+                       if (rc == 0)
+                               return -ETIMEDOUT;
+               }
+       }
+
+       spin_lock_irqsave(&ept->read_q_lock, flags);
+       if (list_empty(&ept->read_q)) {
+               spin_unlock_irqrestore(&ept->read_q_lock, flags);
+               return -EAGAIN;
+       }
+       pkt = list_first_entry(&ept->read_q, struct rr_packet, list);
+       if (pkt->length > len) {
+               spin_unlock_irqrestore(&ept->read_q_lock, flags);
+               return -ETOOSMALL;
+       }
+       list_del(&pkt->list);
+       if (list_empty(&ept->read_q))
+               wake_unlock(&ept->read_q_wake_lock);
+       spin_unlock_irqrestore(&ept->read_q_lock, flags);
+
+       rc = pkt->length;
+
+       *frag_ret = pkt->first;
+       rq = (void*) pkt->first->data;
+       if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) {
+               IO("READ on ept %p is a CALL on %08x:%08x proc %d xid %d\n",
+                       ept, be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+                       be32_to_cpu(rq->procedure),
+                       be32_to_cpu(rq->xid));
+               /* RPC CALL */
+               if (ept->reply_pid != 0xffffffff) {
+                       printk(KERN_WARNING
+                              "rr_read: lost previous reply xid...\n");
+               }
+               /* TODO: locking? */
+               ept->reply_pid = pkt->hdr.src_pid;
+               ept->reply_cid = pkt->hdr.src_cid;
+               ept->reply_xid = rq->xid;
+       }
+#if TRACE_RPC_MSG
+       else if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 1))
+               IO("READ on ept %p is a REPLY\n", ept);
+       else IO("READ on ept %p (%d bytes)\n", ept, rc);
+#endif
+
+       kfree(pkt);
+       return rc;
+}
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+int msm_rpc_is_compatible_version(uint32_t server_version,
+                                 uint32_t client_version)
+{
+       if ((server_version & RPC_VERSION_MODE_MASK) !=
+           (client_version & RPC_VERSION_MODE_MASK))
+               return 0;
+
+       if (server_version & RPC_VERSION_MODE_MASK)
+               return server_version == client_version;
+
+       return ((server_version & RPC_VERSION_MAJOR_MASK) ==
+               (client_version & RPC_VERSION_MAJOR_MASK)) &&
+               ((server_version & RPC_VERSION_MINOR_MASK) >=
+               (client_version & RPC_VERSION_MINOR_MASK));
+}
+EXPORT_SYMBOL(msm_rpc_is_compatible_version);
+
+static int msm_rpc_get_compatible_server(uint32_t prog,
+                                       uint32_t ver,
+                                       uint32_t *found_vers)
+{
+       struct rr_server *server;
+       unsigned long     flags;
+       if (found_vers == NULL)
+               return 0;
+
+       spin_lock_irqsave(&server_list_lock, flags);
+       list_for_each_entry(server, &server_list, list) {
+               if ((server->prog == prog) &&
+                   msm_rpc_is_compatible_version(server->vers, ver)) {
+                       *found_vers = server->vers;
+                       spin_unlock_irqrestore(&server_list_lock, flags);
+                       return 0;
+               }
+       }
+       spin_unlock_irqrestore(&server_list_lock, flags);
+       return -1;
+}
+#endif
+
+struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags)
+{
+       struct msm_rpc_endpoint *ept;
+       struct rr_server *server;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       if (!(vers & RPC_VERSION_MODE_MASK)) {
+               uint32_t found_vers;
+               if (msm_rpc_get_compatible_server(prog, vers, &found_vers) < 0)
+                       return ERR_PTR(-EHOSTUNREACH);
+               if (found_vers != vers) {
+                       D("RPC using new version %08x:{%08x --> %08x}\n",
+                               prog, vers, found_vers);
+                       vers = found_vers;
+               }
+       }
+#endif
+
+       server = rpcrouter_lookup_server(prog, vers);
+       if (!server)
+               return ERR_PTR(-EHOSTUNREACH);
+
+       ept = msm_rpc_open();
+       if (IS_ERR(ept))
+               return ept;
+
+       ept->flags = flags;
+       ept->dst_pid = server->pid;
+       ept->dst_cid = server->cid;
+       ept->dst_prog = cpu_to_be32(prog);
+       ept->dst_vers = cpu_to_be32(vers);
+
+       return ept;
+}
+EXPORT_SYMBOL(msm_rpc_connect);
+
+uint32_t msm_rpc_get_vers(struct msm_rpc_endpoint *ept)
+{
+       return be32_to_cpu(ept->dst_vers);
+}
+EXPORT_SYMBOL(msm_rpc_get_vers);
+
+/* TODO: permission check? */
+int msm_rpc_register_server(struct msm_rpc_endpoint *ept,
+                           uint32_t prog, uint32_t vers)
+{
+       int rc;
+       union rr_control_msg msg;
+       struct rr_server *server;
+
+       server = rpcrouter_create_server(ept->pid, ept->cid,
+                                        prog, vers);
+       if (!server)
+               return -ENODEV;
+
+       msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
+       msg.srv.pid = ept->pid;
+       msg.srv.cid = ept->cid;
+       msg.srv.prog = prog;
+       msg.srv.vers = vers;
+
+       RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+          ept->pid, ept->cid, prog, vers);
+
+       rc = rpcrouter_send_control_msg(&msg);
+       if (rc < 0)
+               return rc;
+
+       return 0;
+}
+
+/* TODO: permission check -- disallow unreg of somebody else's server */
+int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept,
+                             uint32_t prog, uint32_t vers)
+{
+       struct rr_server *server;
+       server = rpcrouter_lookup_server(prog, vers);
+
+       if (!server)
+               return -ENOENT;
+       rpcrouter_destroy_server(server);
+       return 0;
+}
+
+static int msm_rpcrouter_probe(struct platform_device *pdev)
+{
+       int rc;
+
+       /* Initialize what we need to start processing */
+       INIT_LIST_HEAD(&local_endpoints);
+       INIT_LIST_HEAD(&remote_endpoints);
+
+       init_waitqueue_head(&newserver_wait);
+       init_waitqueue_head(&smd_wait);
+       wake_lock_init(&rpcrouter_wake_lock, WAKE_LOCK_SUSPEND, "SMD_RPCCALL");
+
+       rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter");
+       if (!rpcrouter_workqueue)
+               return -ENOMEM;
+
+       rc = msm_rpcrouter_init_devices();
+       if (rc < 0)
+               goto fail_destroy_workqueue;
+
+       /* Open up SMD channel 2 */
+       initialized = 0;
+       rc = smd_open("SMD_RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify);
+       if (rc < 0)
+               goto fail_remove_devices;
+
+       queue_work(rpcrouter_workqueue, &work_read_data);
+       return 0;
+
+ fail_remove_devices:
+       msm_rpcrouter_exit_devices();
+ fail_destroy_workqueue:
+       destroy_workqueue(rpcrouter_workqueue);
+       return rc;
+}
+
+static struct platform_driver msm_smd_channel2_driver = {
+       .probe          = msm_rpcrouter_probe,
+       .driver         = {
+                       .name   = "SMD_RPCCALL",
+                       .owner  = THIS_MODULE,
+       },
+};
+
+static int __init rpcrouter_init(void)
+{
+       return platform_driver_register(&msm_smd_channel2_driver);
+}
+
+module_init(rpcrouter_init);
+MODULE_DESCRIPTION("MSM RPC Router");
+MODULE_AUTHOR("San Mehat <san@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/dream/smd/smd_rpcrouter.h b/drivers/staging/dream/smd/smd_rpcrouter.h
new file mode 100644 (file)
index 0000000..a7416a2
--- /dev/null
@@ -0,0 +1,195 @@
+/** arch/arm/mach-msm/smd_rpcrouter.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2008 QUALCOMM Incorporated.
+ * Author: San Mehat <san@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 _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H
+#define _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+
+#include <mach/msm_smd.h>
+#include <mach/msm_rpcrouter.h>
+
+/* definitions for the R2R wire protcol */
+
+#define RPCROUTER_VERSION                      1
+#define RPCROUTER_PROCESSORS_MAX               4
+#define RPCROUTER_MSGSIZE_MAX                  512
+
+#define RPCROUTER_CLIENT_BCAST_ID              0xffffffff
+#define RPCROUTER_ROUTER_ADDRESS               0xfffffffe
+
+#define RPCROUTER_PID_LOCAL                    1
+#define RPCROUTER_PID_REMOTE                   0
+
+#define RPCROUTER_CTRL_CMD_DATA                        1
+#define RPCROUTER_CTRL_CMD_HELLO               2
+#define RPCROUTER_CTRL_CMD_BYE                 3
+#define RPCROUTER_CTRL_CMD_NEW_SERVER          4
+#define RPCROUTER_CTRL_CMD_REMOVE_SERVER       5
+#define RPCROUTER_CTRL_CMD_REMOVE_CLIENT       6
+#define RPCROUTER_CTRL_CMD_RESUME_TX           7
+#define RPCROUTER_CTRL_CMD_EXIT                        8
+
+#define RPCROUTER_DEFAULT_RX_QUOTA     5
+
+union rr_control_msg {
+       uint32_t cmd;
+       struct {
+               uint32_t cmd;
+               uint32_t prog;
+               uint32_t vers;
+               uint32_t pid;
+               uint32_t cid;
+       } srv;
+       struct {
+               uint32_t cmd;
+               uint32_t pid;
+               uint32_t cid;
+       } cli;
+};
+
+struct rr_header {
+       uint32_t version;
+       uint32_t type;
+       uint32_t src_pid;
+       uint32_t src_cid;
+       uint32_t confirm_rx;
+       uint32_t size;
+       uint32_t dst_pid;
+       uint32_t dst_cid;
+};
+
+/* internals */
+
+#define RPCROUTER_MAX_REMOTE_SERVERS           100
+
+struct rr_fragment {
+       unsigned char data[RPCROUTER_MSGSIZE_MAX];
+       uint32_t length;
+       struct rr_fragment *next;
+};
+
+struct rr_packet {
+       struct list_head list;
+       struct rr_fragment *first;
+       struct rr_fragment *last;
+       struct rr_header hdr;
+       uint32_t mid;
+       uint32_t length;
+};
+
+#define PACMARK_LAST(n) ((n) & 0x80000000)
+#define PACMARK_MID(n)  (((n) >> 16) & 0xFF)
+#define PACMARK_LEN(n)  ((n) & 0xFFFF)
+
+static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t first,
+                              uint32_t last)
+{
+       return (len & 0xFFFF) |
+         ((mid & 0xFF) << 16) |
+         ((!!first) << 30) |
+         ((!!last) << 31);
+}
+
+struct rr_server {
+       struct list_head list;
+
+       uint32_t pid;
+       uint32_t cid;
+       uint32_t prog;
+       uint32_t vers;
+
+       dev_t device_number;
+       struct cdev cdev;
+       struct device *device;
+       struct rpcsvr_platform_device p_device;
+       char pdev_name[32];
+};
+
+struct rr_remote_endpoint {
+       uint32_t pid;
+       uint32_t cid;
+
+       int tx_quota_cntr;
+       spinlock_t quota_lock;
+       wait_queue_head_t quota_wait;
+
+       struct list_head list;
+};
+
+struct msm_rpc_endpoint {
+       struct list_head list;
+
+       /* incomplete packets waiting for assembly */
+       struct list_head incomplete;
+
+       /* complete packets waiting to be read */
+       struct list_head read_q;
+       spinlock_t read_q_lock;
+       struct wake_lock read_q_wake_lock;
+       wait_queue_head_t wait_q;
+       unsigned flags;
+
+       /* endpoint address */
+       uint32_t pid;
+       uint32_t cid;
+
+       /* bound remote address
+        * if not connected (dst_pid == 0xffffffff) RPC_CALL writes fail
+        * RPC_CALLs must be to the prog/vers below or they will fail
+        */
+       uint32_t dst_pid;
+       uint32_t dst_cid;
+       uint32_t dst_prog; /* be32 */
+       uint32_t dst_vers; /* be32 */
+
+       /* reply remote address
+        * if reply_pid == 0xffffffff, none available
+        * RPC_REPLY writes may only go to the pid/cid/xid of the
+        * last RPC_CALL we received.
+        */
+       uint32_t reply_pid;
+       uint32_t reply_cid;
+       uint32_t reply_xid; /* be32 */
+       uint32_t next_pm;   /* Pacmark sequence */
+
+       /* device node if this endpoint is accessed via userspace */
+       dev_t dev;
+};
+
+/* shared between smd_rpcrouter*.c */
+
+int __msm_rpc_read(struct msm_rpc_endpoint *ept,
+                  struct rr_fragment **frag,
+                  unsigned len, long timeout);
+
+struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev);
+int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept);
+
+int msm_rpcrouter_create_server_cdev(struct rr_server *server);
+int msm_rpcrouter_create_server_pdev(struct rr_server *server);
+
+int msm_rpcrouter_init_devices(void);
+void msm_rpcrouter_exit_devices(void);
+
+extern dev_t msm_rpcrouter_devno;
+extern struct class *msm_rpcrouter_class;
+#endif
diff --git a/drivers/staging/dream/smd/smd_rpcrouter_device.c b/drivers/staging/dream/smd/smd_rpcrouter_device.c
new file mode 100644 (file)
index 0000000..cd3910b
--- /dev/null
@@ -0,0 +1,376 @@
+/* arch/arm/mach-msm/smd_rpcrouter_device.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated.
+ * Author: San Mehat <san@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/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+#include <linux/msm_rpcrouter.h>
+
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include "smd_rpcrouter.h"
+
+#define SAFETY_MEM_SIZE 65536
+
+/* Next minor # available for a remote server */
+static int next_minor = 1;
+
+struct class *msm_rpcrouter_class;
+dev_t msm_rpcrouter_devno;
+
+static struct cdev rpcrouter_cdev;
+static struct device *rpcrouter_device;
+
+static int rpcrouter_open(struct inode *inode, struct file *filp)
+{
+       int rc;
+       struct msm_rpc_endpoint *ept;
+
+       rc = nonseekable_open(inode, filp);
+       if (rc < 0)
+               return rc;
+
+       ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev);
+       if (!ept)
+               return -ENOMEM;
+
+       filp->private_data = ept;
+       return 0;
+}
+
+static int rpcrouter_release(struct inode *inode, struct file *filp)
+{
+       struct msm_rpc_endpoint *ept;
+       ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+       return msm_rpcrouter_destroy_local_endpoint(ept);
+}
+
+static ssize_t rpcrouter_read(struct file *filp, char __user *buf,
+                             size_t count, loff_t *ppos)
+{
+       struct msm_rpc_endpoint *ept;
+       struct rr_fragment *frag, *next;
+       int rc;
+
+       ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+       rc = __msm_rpc_read(ept, &frag, count, -1);
+       if (rc < 0)
+               return rc;
+
+       count = rc;
+
+       while (frag != NULL) {
+               if (copy_to_user(buf, frag->data, frag->length)) {
+                       printk(KERN_ERR
+                              "rpcrouter: could not copy all read data to user!\n");
+                       rc = -EFAULT;
+               }
+               buf += frag->length;
+               next = frag->next;
+               kfree(frag);
+               frag = next;
+       }
+
+       return rc;
+}
+
+static ssize_t rpcrouter_write(struct file *filp, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       struct msm_rpc_endpoint *ept;
+       int rc = 0;
+       void *k_buffer;
+
+       ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+       /* A check for safety, this seems non-standard */
+       if (count > SAFETY_MEM_SIZE)
+               return -EINVAL;
+
+       k_buffer = kmalloc(count, GFP_KERNEL);
+       if (!k_buffer)
+               return -ENOMEM;
+
+       if (copy_from_user(k_buffer, buf, count)) {
+               rc = -EFAULT;
+               goto write_out_free;
+       }
+
+       rc = msm_rpc_write(ept, k_buffer, count);
+       if (rc < 0)
+               goto write_out_free;
+
+       rc = count;
+write_out_free:
+       kfree(k_buffer);
+       return rc;
+}
+
+static unsigned int rpcrouter_poll(struct file *filp,
+                                  struct poll_table_struct *wait)
+{
+       struct msm_rpc_endpoint *ept;
+       unsigned mask = 0;
+       ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+       /* If there's data already in the read queue, return POLLIN.
+        * Else, wait for the requested amount of time, and check again.
+        */
+
+       if (!list_empty(&ept->read_q))
+               mask |= POLLIN;
+
+       if (!mask) {
+               poll_wait(filp, &ept->wait_q, wait);
+               if (!list_empty(&ept->read_q))
+                       mask |= POLLIN;
+       }
+
+       return mask;
+}
+
+static long rpcrouter_ioctl(struct file *filp, unsigned int cmd,
+                           unsigned long arg)
+{
+       struct msm_rpc_endpoint *ept;
+       struct rpcrouter_ioctl_server_args server_args;
+       int rc = 0;
+       uint32_t n;
+
+       ept = (struct msm_rpc_endpoint *) filp->private_data;
+       switch (cmd) {
+
+       case RPC_ROUTER_IOCTL_GET_VERSION:
+               n = RPC_ROUTER_VERSION_V1;
+               rc = put_user(n, (unsigned int *) arg);
+               break;
+
+       case RPC_ROUTER_IOCTL_GET_MTU:
+               /* the pacmark word reduces the actual payload
+                * possible per message
+                */
+               n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t);
+               rc = put_user(n, (unsigned int *) arg);
+               break;
+
+       case RPC_ROUTER_IOCTL_REGISTER_SERVER:
+               rc = copy_from_user(&server_args, (void *) arg,
+                                   sizeof(server_args));
+               if (rc < 0)
+                       break;
+               msm_rpc_register_server(ept,
+                                       server_args.prog,
+                                       server_args.vers);
+               break;
+
+       case RPC_ROUTER_IOCTL_UNREGISTER_SERVER:
+               rc = copy_from_user(&server_args, (void *) arg,
+                                   sizeof(server_args));
+               if (rc < 0)
+                       break;
+
+               msm_rpc_unregister_server(ept,
+                                         server_args.prog,
+                                         server_args.vers);
+               break;
+
+       case RPC_ROUTER_IOCTL_GET_MINOR_VERSION:
+               n = MSM_RPC_GET_MINOR(msm_rpc_get_vers(ept));
+               rc = put_user(n, (unsigned int *)arg);
+               break;
+
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static struct file_operations rpcrouter_server_fops = {
+       .owner   = THIS_MODULE,
+       .open    = rpcrouter_open,
+       .release = rpcrouter_release,
+       .read    = rpcrouter_read,
+       .write   = rpcrouter_write,
+       .poll    = rpcrouter_poll,
+       .unlocked_ioctl  = rpcrouter_ioctl,
+};
+
+static struct file_operations rpcrouter_router_fops = {
+       .owner   = THIS_MODULE,
+       .open    = rpcrouter_open,
+       .release = rpcrouter_release,
+       .read    = rpcrouter_read,
+       .write   = rpcrouter_write,
+       .poll    = rpcrouter_poll,
+       .unlocked_ioctl = rpcrouter_ioctl,
+};
+
+int msm_rpcrouter_create_server_cdev(struct rr_server *server)
+{
+       int rc;
+       uint32_t dev_vers;
+
+       if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) {
+               printk(KERN_ERR
+                      "rpcrouter: Minor numbers exhausted - Increase "
+                      "RPCROUTER_MAX_REMOTE_SERVERS\n");
+               return -ENOBUFS;
+       }
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       /* Servers with bit 31 set are remote msm servers with hashkey version.
+        * Servers with bit 31 not set are remote msm servers with
+        * backwards compatible version type in which case the minor number
+        * (lower 16 bits) is set to zero.
+        *
+        */
+       if ((server->vers & RPC_VERSION_MODE_MASK))
+               dev_vers = server->vers;
+       else
+               dev_vers = server->vers & RPC_VERSION_MAJOR_MASK;
+#else
+       dev_vers = server->vers;
+#endif
+
+       server->device_number =
+               MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++);
+
+       server->device =
+               device_create(msm_rpcrouter_class, rpcrouter_device,
+                             server->device_number, NULL, "%.8x:%.8x",
+                             server->prog, dev_vers);
+       if (IS_ERR(server->device)) {
+               printk(KERN_ERR
+                      "rpcrouter: Unable to create device (%ld)\n",
+                      PTR_ERR(server->device));
+               return PTR_ERR(server->device);;
+       }
+
+       cdev_init(&server->cdev, &rpcrouter_server_fops);
+       server->cdev.owner = THIS_MODULE;
+
+       rc = cdev_add(&server->cdev, server->device_number, 1);
+       if (rc < 0) {
+               printk(KERN_ERR
+                      "rpcrouter: Unable to add chrdev (%d)\n", rc);
+               device_destroy(msm_rpcrouter_class, server->device_number);
+               return rc;
+       }
+       return 0;
+}
+
+/* for backward compatible version type (31st bit cleared)
+ * clearing minor number (lower 16 bits) in device name
+ * is neccessary for driver binding
+ */
+int msm_rpcrouter_create_server_pdev(struct rr_server *server)
+{
+       sprintf(server->pdev_name, "rs%.8x:%.8x",
+               server->prog,
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+               (server->vers & RPC_VERSION_MODE_MASK) ? server->vers :
+               (server->vers & RPC_VERSION_MAJOR_MASK));
+#else
+               server->vers);
+#endif
+
+       server->p_device.base.id = -1;
+       server->p_device.base.name = server->pdev_name;
+
+       server->p_device.prog = server->prog;
+       server->p_device.vers = server->vers;
+
+       platform_device_register(&server->p_device.base);
+       return 0;
+}
+
+int msm_rpcrouter_init_devices(void)
+{
+       int rc;
+       int major;
+
+       /* Create the device nodes */
+       msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc");
+       if (IS_ERR(msm_rpcrouter_class)) {
+               rc = -ENOMEM;
+               printk(KERN_ERR
+                      "rpcrouter: failed to create oncrpc class\n");
+               goto fail;
+       }
+
+       rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0,
+                                RPCROUTER_MAX_REMOTE_SERVERS + 1,
+                                "oncrpc");
+       if (rc < 0) {
+               printk(KERN_ERR
+                      "rpcrouter: Failed to alloc chardev region (%d)\n", rc);
+               goto fail_destroy_class;
+       }
+
+       major = MAJOR(msm_rpcrouter_devno);
+       rpcrouter_device = device_create(msm_rpcrouter_class, NULL,
+                                        msm_rpcrouter_devno, NULL, "%.8x:%d",
+                                        0, 0);
+       if (IS_ERR(rpcrouter_device)) {
+               rc = -ENOMEM;
+               goto fail_unregister_cdev_region;
+       }
+
+       cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops);
+       rpcrouter_cdev.owner = THIS_MODULE;
+
+       rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1);
+       if (rc < 0)
+               goto fail_destroy_device;
+
+       return 0;
+
+fail_destroy_device:
+       device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
+fail_unregister_cdev_region:
+       unregister_chrdev_region(msm_rpcrouter_devno,
+                                RPCROUTER_MAX_REMOTE_SERVERS + 1);
+fail_destroy_class:
+       class_destroy(msm_rpcrouter_class);
+fail:
+       return rc;
+}
+
+void msm_rpcrouter_exit_devices(void)
+{
+       cdev_del(&rpcrouter_cdev);
+       device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
+       unregister_chrdev_region(msm_rpcrouter_devno,
+                                RPCROUTER_MAX_REMOTE_SERVERS + 1);
+       class_destroy(msm_rpcrouter_class);
+}
+
diff --git a/drivers/staging/dream/smd/smd_rpcrouter_servers.c b/drivers/staging/dream/smd/smd_rpcrouter_servers.c
new file mode 100644 (file)
index 0000000..2597bbb
--- /dev/null
@@ -0,0 +1,229 @@
+/* arch/arm/mach-msm/rpc_servers.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@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/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+
+#include <linux/msm_rpcrouter.h>
+#include <linux/uaccess.h>
+
+#include <mach/msm_rpcrouter.h>
+#include "smd_rpcrouter.h"
+
+static struct msm_rpc_endpoint *endpoint;
+
+#define FLAG_REGISTERED 0x0001
+
+static LIST_HEAD(rpc_server_list);
+static DEFINE_MUTEX(rpc_server_list_lock);
+static int rpc_servers_active;
+static struct wake_lock rpc_servers_wake_lock;
+
+static void rpc_server_register(struct msm_rpc_server *server)
+{
+       int rc;
+       rc = msm_rpc_register_server(endpoint, server->prog, server->vers);
+       if (rc < 0)
+               printk(KERN_ERR "[rpcserver] error registering %p @ %08x:%d\n",
+                      server, server->prog, server->vers);
+}
+
+static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers)
+{
+       struct msm_rpc_server *server;
+
+       mutex_lock(&rpc_server_list_lock);
+       list_for_each_entry(server, &rpc_server_list, list) {
+               if ((server->prog == prog) &&
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+                   msm_rpc_is_compatible_version(server->vers, vers)) {
+#else
+                   server->vers == vers) {
+#endif
+                       mutex_unlock(&rpc_server_list_lock);
+                       return server;
+               }
+       }
+       mutex_unlock(&rpc_server_list_lock);
+       return NULL;
+}
+
+static void rpc_server_register_all(void)
+{
+       struct msm_rpc_server *server;
+
+       mutex_lock(&rpc_server_list_lock);
+       list_for_each_entry(server, &rpc_server_list, list) {
+               if (!(server->flags & FLAG_REGISTERED)) {
+                       rpc_server_register(server);
+                       server->flags |= FLAG_REGISTERED;
+               }
+       }
+       mutex_unlock(&rpc_server_list_lock);
+}
+
+int msm_rpc_create_server(struct msm_rpc_server *server)
+{
+       /* make sure we're in a sane state first */
+       server->flags = 0;
+       INIT_LIST_HEAD(&server->list);
+
+       mutex_lock(&rpc_server_list_lock);
+       list_add(&server->list, &rpc_server_list);
+       if (rpc_servers_active) {
+               rpc_server_register(server);
+               server->flags |= FLAG_REGISTERED;
+       }
+       mutex_unlock(&rpc_server_list_lock);
+
+       return 0;
+}
+
+static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client,
+                                       uint32_t xid, uint32_t accept_status)
+{
+       int rc = 0;
+       uint8_t reply_buf[sizeof(struct rpc_reply_hdr)];
+       struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf;
+
+       reply->xid = cpu_to_be32(xid);
+       reply->type = cpu_to_be32(1); /* reply */
+       reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+       reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status);
+       reply->data.acc_hdr.verf_flavor = 0;
+       reply->data.acc_hdr.verf_length = 0;
+
+       rc = msm_rpc_write(client, reply_buf, sizeof(reply_buf));
+       if (rc < 0)
+               printk(KERN_ERR
+                      "%s: could not write response: %d\n",
+                      __FUNCTION__, rc);
+
+       return rc;
+}
+
+static int rpc_servers_thread(void *data)
+{
+       void *buffer;
+       struct rpc_request_hdr *req;
+       struct msm_rpc_server *server;
+       int rc;
+
+       for (;;) {
+               wake_unlock(&rpc_servers_wake_lock);
+               rc = wait_event_interruptible(endpoint->wait_q,
+                                               !list_empty(&endpoint->read_q));
+               wake_lock(&rpc_servers_wake_lock);
+               rc = msm_rpc_read(endpoint, &buffer, -1, -1);
+               if (rc < 0) {
+                       printk(KERN_ERR "%s: could not read: %d\n",
+                              __FUNCTION__, rc);
+                       break;
+               }
+               req = (struct rpc_request_hdr *)buffer;
+
+               req->type = be32_to_cpu(req->type);
+               req->xid = be32_to_cpu(req->xid);
+               req->rpc_vers = be32_to_cpu(req->rpc_vers);
+               req->prog = be32_to_cpu(req->prog);
+               req->vers = be32_to_cpu(req->vers);
+               req->procedure = be32_to_cpu(req->procedure);
+
+               server = rpc_server_find(req->prog, req->vers);
+
+               if (req->rpc_vers != 2)
+                       continue;
+               if (req->type != 0)
+                       continue;
+               if (!server) {
+                       rpc_send_accepted_void_reply(
+                               endpoint, req->xid,
+                               RPC_ACCEPTSTAT_PROG_UNAVAIL);
+                       continue;
+               }
+
+               rc = server->rpc_call(server, req, rc);
+
+               switch (rc) {
+               case 0:
+                       rpc_send_accepted_void_reply(
+                               endpoint, req->xid,
+                               RPC_ACCEPTSTAT_SUCCESS);
+                       break;
+               default:
+                       rpc_send_accepted_void_reply(
+                               endpoint, req->xid,
+                               RPC_ACCEPTSTAT_PROG_UNAVAIL);
+                       break;
+               }
+
+               kfree(buffer);
+       }
+
+       do_exit(0);
+}
+
+static int rpcservers_probe(struct platform_device *pdev)
+{
+       struct task_struct *server_thread;
+
+       endpoint = msm_rpc_open();
+       if (IS_ERR(endpoint))
+               return PTR_ERR(endpoint);
+
+       /* we're online -- register any servers installed beforehand */
+       rpc_servers_active = 1;
+       rpc_server_register_all();
+
+       /* start the kernel thread */
+       server_thread = kthread_run(rpc_servers_thread, NULL, "krpcserversd");
+       if (IS_ERR(server_thread))
+               return PTR_ERR(server_thread);
+
+       return 0;
+}
+
+static struct platform_driver rpcservers_driver = {
+       .probe  = rpcservers_probe,
+       .driver = {
+               .name   = "oncrpc_router",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init rpc_servers_init(void)
+{
+       wake_lock_init(&rpc_servers_wake_lock, WAKE_LOCK_SUSPEND, "rpc_server");
+       return platform_driver_register(&rpcservers_driver);
+}
+
+module_init(rpc_servers_init);
+
+MODULE_DESCRIPTION("MSM RPC Servers");
+MODULE_AUTHOR("Iliyan Malchev <ibm@android.com>");
+MODULE_LICENSE("GPL");