Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c
new file mode 100644
index 0000000..b45b182
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter.c
@@ -0,0 +1,2508 @@
+/* arch/arm/mach-msm/smd_rpcrouter.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * 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/slab.h>
+#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 <linux/debugfs.h>
+
+#include <asm/byteorder.h>
+
+#include <mach/msm_smd.h>
+#include <mach/smem_log.h>
+#include <mach/subsystem_notif.h>
+
+#include "smd_rpcrouter.h"
+#include "modem_notifier.h"
+#include "smd_rpc_sym.h"
+#include "smd_private.h"
+
+enum {
+	SMEM_LOG = 1U << 0,
+	RTR_DBG = 1U << 1,
+	R2R_MSG = 1U << 2,
+	R2R_RAW = 1U << 3,
+	RPC_MSG = 1U << 4,
+	NTFY_MSG = 1U << 5,
+	RAW_PMR = 1U << 6,
+	RAW_PMW = 1U << 7,
+	R2R_RAW_HDR = 1U << 8,
+};
+static int msm_rpc_connect_timeout_ms;
+module_param_named(connect_timeout, msm_rpc_connect_timeout_ms,
+		   int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static int smd_rpcrouter_debug_mask;
+module_param_named(debug_mask, smd_rpcrouter_debug_mask,
+		   int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define DIAG(x...) printk(KERN_ERR "[RR] ERROR " x)
+
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+#define D(x...) do { \
+if (smd_rpcrouter_debug_mask & RTR_DBG) \
+	printk(KERN_ERR x); \
+} while (0)
+
+#define RR(x...) do { \
+if (smd_rpcrouter_debug_mask & R2R_MSG) \
+	printk(KERN_ERR "[RR] "x); \
+} while (0)
+
+#define RAW(x...) do { \
+if (smd_rpcrouter_debug_mask & R2R_RAW) \
+	printk(KERN_ERR "[RAW] "x); \
+} while (0)
+
+#define RAW_HDR(x...) do { \
+if (smd_rpcrouter_debug_mask & R2R_RAW_HDR) \
+	printk(KERN_ERR "[HDR] "x); \
+} while (0)
+
+#define RAW_PMR(x...) do { \
+if (smd_rpcrouter_debug_mask & RAW_PMR) \
+	printk(KERN_ERR "[PMR] "x); \
+} while (0)
+
+#define RAW_PMR_NOMASK(x...) do { \
+	printk(KERN_ERR "[PMR] "x); \
+} while (0)
+
+#define RAW_PMW(x...) do { \
+if (smd_rpcrouter_debug_mask & RAW_PMW) \
+	printk(KERN_ERR "[PMW] "x); \
+} while (0)
+
+#define RAW_PMW_NOMASK(x...) do { \
+	printk(KERN_ERR "[PMW] "x); \
+} while (0)
+
+#define IO(x...) do { \
+if (smd_rpcrouter_debug_mask & RPC_MSG) \
+	printk(KERN_ERR "[RPC] "x); \
+} while (0)
+
+#define NTFY(x...) do { \
+if (smd_rpcrouter_debug_mask & NTFY_MSG) \
+	printk(KERN_ERR "[NOTIFY] "x); \
+} while (0)
+#else
+#define D(x...) do { } while (0)
+#define RR(x...) do { } while (0)
+#define RAW(x...) do { } while (0)
+#define RAW_HDR(x...) do { } while (0)
+#define RAW_PMR(x...) do { } while (0)
+#define RAW_PMR_NO_MASK(x...) do { } while (0)
+#define RAW_PMW(x...) do { } while (0)
+#define RAW_PMW_NO_MASK(x...) do { } while (0)
+#define IO(x...) do { } while (0)
+#define NTFY(x...) do { } while (0)
+#endif
+
+
+static LIST_HEAD(local_endpoints);
+static LIST_HEAD(remote_endpoints);
+
+static LIST_HEAD(server_list);
+
+static wait_queue_head_t newserver_wait;
+static wait_queue_head_t subsystem_restart_wait;
+
+static DEFINE_SPINLOCK(local_endpoints_lock);
+static DEFINE_SPINLOCK(remote_endpoints_lock);
+static DEFINE_SPINLOCK(server_list_lock);
+
+static LIST_HEAD(rpc_board_dev_list);
+static DEFINE_SPINLOCK(rpc_board_dev_list_lock);
+
+static struct workqueue_struct *rpcrouter_workqueue;
+
+static atomic_t next_xid = ATOMIC_INIT(1);
+static atomic_t pm_mid = ATOMIC_INIT(1);
+
+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_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
+
+/* State for remote ep following restart */
+#define RESTART_QUOTA_ABORT  1
+
+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;
+
+struct rpc_board_dev_info {
+	struct list_head list;
+
+	struct rpc_board_dev *dev;
+};
+
+static struct platform_device rpcrouter_pdev = {
+	.name		= "oncrpc_router",
+	.id		= -1,
+};
+
+struct rpcrouter_xprt_info {
+	struct list_head list;
+
+	struct rpcrouter_xprt *xprt;
+
+	int remote_pid;
+	uint32_t initialized;
+	wait_queue_head_t read_wait;
+	struct wake_lock wakelock;
+	spinlock_t lock;
+	uint32_t need_len;
+	struct work_struct read_data;
+	struct workqueue_struct *workqueue;
+	int abort_data_read;
+	unsigned char r2r_buf[RPCROUTER_MSGSIZE_MAX];
+};
+
+static LIST_HEAD(xprt_info_list);
+static DEFINE_MUTEX(xprt_info_list_lock);
+
+DECLARE_COMPLETION(rpc_remote_router_up);
+static atomic_t pending_close_count = ATOMIC_INIT(0);
+
+/*
+ * Search for transport (xprt) that matches the provided PID.
+ *
+ * Note: The calling function must ensure that the mutex
+ *       xprt_info_list_lock is locked when this function
+ *       is called.
+ *
+ * @remote_pid	Remote PID for the transport
+ *
+ * @returns Pointer to transport or NULL if not found
+ */
+static struct rpcrouter_xprt_info *rpcrouter_get_xprt_info(uint32_t remote_pid)
+{
+	struct rpcrouter_xprt_info *xprt_info;
+
+	list_for_each_entry(xprt_info, &xprt_info_list, list) {
+		if (xprt_info->remote_pid == remote_pid)
+			return xprt_info;
+	}
+	return NULL;
+}
+
+static int rpcrouter_send_control_msg(struct rpcrouter_xprt_info *xprt_info,
+				      union rr_control_msg *msg)
+{
+	struct rr_header hdr;
+	unsigned long flags = 0;
+	int need;
+
+	if (xprt_info->remote_pid == RPCROUTER_PID_LOCAL)
+		return 0;
+
+	if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) &&
+	    !xprt_info->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 = xprt_info->remote_pid;
+	hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS;
+
+	/* TODO: what if channel is full? */
+
+	need = sizeof(hdr) + hdr.size;
+	spin_lock_irqsave(&xprt_info->lock, flags);
+	while (xprt_info->xprt->write_avail() < need) {
+		spin_unlock_irqrestore(&xprt_info->lock, flags);
+		msleep(250);
+		spin_lock_irqsave(&xprt_info->lock, flags);
+	}
+	xprt_info->xprt->write(&hdr, sizeof(hdr), HEADER);
+	xprt_info->xprt->write(msg, hdr.size, PAYLOAD);
+	spin_unlock_irqrestore(&xprt_info->lock, flags);
+
+	return 0;
+}
+
+static void modem_reset_cleanup(struct rpcrouter_xprt_info *xprt_info)
+{
+	struct msm_rpc_endpoint *ept;
+	struct rr_remote_endpoint *r_ept;
+	struct rr_packet *pkt, *tmp_pkt;
+	struct rr_fragment *frag, *next;
+	struct msm_rpc_reply *reply, *reply_tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&local_endpoints_lock, flags);
+	/* remove all partial packets received */
+	list_for_each_entry(ept, &local_endpoints, list) {
+		RR("%s EPT DST PID %x, remote_pid:%d\n", __func__,
+			ept->dst_pid, xprt_info->remote_pid);
+
+		if (xprt_info->remote_pid != ept->dst_pid)
+			continue;
+
+		D("calling teardown cb %p\n", ept->cb_restart_teardown);
+		if (ept->cb_restart_teardown)
+			ept->cb_restart_teardown(ept->client_data);
+		ept->do_setup_notif = 1;
+
+		/* remove replies */
+		spin_lock(&ept->reply_q_lock);
+		list_for_each_entry_safe(reply, reply_tmp,
+					 &ept->reply_pend_q, list) {
+			list_del(&reply->list);
+			kfree(reply);
+		}
+		list_for_each_entry_safe(reply, reply_tmp,
+					 &ept->reply_avail_q, list) {
+			list_del(&reply->list);
+			kfree(reply);
+		}
+		spin_unlock(&ept->reply_q_lock);
+
+		/* Set restart state for local ep */
+		RR("EPT:0x%p, State %d  RESTART_PEND_NTFY_SVR "
+			"PROG:0x%08x VERS:0x%08x\n",
+			ept, ept->restart_state,
+			be32_to_cpu(ept->dst_prog),
+			be32_to_cpu(ept->dst_vers));
+		spin_lock(&ept->restart_lock);
+		ept->restart_state = RESTART_PEND_NTFY_SVR;
+
+		/* remove incomplete packets */
+		spin_lock(&ept->incomplete_lock);
+		list_for_each_entry_safe(pkt, tmp_pkt,
+					 &ept->incomplete, list) {
+			list_del(&pkt->list);
+			frag = pkt->first;
+			while (frag != NULL) {
+				next = frag->next;
+				kfree(frag);
+				frag = next;
+			}
+			kfree(pkt);
+		}
+		spin_unlock(&ept->incomplete_lock);
+
+		/* remove all completed packets waiting to be read */
+		spin_lock(&ept->read_q_lock);
+		list_for_each_entry_safe(pkt, tmp_pkt, &ept->read_q,
+					 list) {
+			list_del(&pkt->list);
+			frag = pkt->first;
+			while (frag != NULL) {
+				next = frag->next;
+				kfree(frag);
+				frag = next;
+			}
+			kfree(pkt);
+		}
+		spin_unlock(&ept->read_q_lock);
+
+		spin_unlock(&ept->restart_lock);
+		wake_up(&ept->wait_q);
+	}
+
+	spin_unlock_irqrestore(&local_endpoints_lock, flags);
+
+    /* Unblock endpoints waiting for quota ack*/
+	spin_lock_irqsave(&remote_endpoints_lock, flags);
+	list_for_each_entry(r_ept, &remote_endpoints, list) {
+		spin_lock(&r_ept->quota_lock);
+		r_ept->quota_restart_state = RESTART_QUOTA_ABORT;
+		RR("Set STATE_PENDING PID:0x%08x CID:0x%08x \n", r_ept->pid,
+		   r_ept->cid);
+		spin_unlock(&r_ept->quota_lock);
+		wake_up(&r_ept->quota_wait);
+	}
+	spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+}
+
+static void modem_reset_startup(struct rpcrouter_xprt_info *xprt_info)
+{
+	struct msm_rpc_endpoint *ept;
+	unsigned long flags;
+
+	spin_lock_irqsave(&local_endpoints_lock, flags);
+
+	/* notify all endpoints that we are coming back up */
+	list_for_each_entry(ept, &local_endpoints, list) {
+		RR("%s EPT DST PID %x, remote_pid:%d\n", __func__,
+			ept->dst_pid, xprt_info->remote_pid);
+
+		if (xprt_info->remote_pid != ept->dst_pid)
+			continue;
+
+		D("calling setup cb %d:%p\n", ept->do_setup_notif,
+					ept->cb_restart_setup);
+		if (ept->do_setup_notif && ept->cb_restart_setup)
+			ept->cb_restart_setup(ept->client_data);
+		ept->do_setup_notif = 0;
+	}
+
+	spin_unlock_irqrestore(&local_endpoints_lock, flags);
+}
+
+/*
+ * Blocks and waits for endpoint if a reset is in progress.
+ *
+ * @returns
+ *    ENETRESET     Reset is in progress and a notification needed
+ *    ERESTARTSYS   Signal occurred
+ *    0             Reset is not in progress
+ */
+static int wait_for_restart_and_notify(struct msm_rpc_endpoint *ept)
+{
+	unsigned long flags;
+	int ret = 0;
+	DEFINE_WAIT(__wait);
+
+	for (;;) {
+		prepare_to_wait(&ept->restart_wait, &__wait,
+				TASK_INTERRUPTIBLE);
+
+		spin_lock_irqsave(&ept->restart_lock, flags);
+		if (ept->restart_state == RESTART_NORMAL) {
+			spin_unlock_irqrestore(&ept->restart_lock, flags);
+			break;
+		} else if (ept->restart_state & RESTART_PEND_NTFY) {
+			ept->restart_state &= ~RESTART_PEND_NTFY;
+			spin_unlock_irqrestore(&ept->restart_lock, flags);
+			ret = -ENETRESET;
+			break;
+		}
+		if (signal_pending(current) &&
+		   ((!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))) {
+			spin_unlock_irqrestore(&ept->restart_lock, flags);
+			ret = -ERESTARTSYS;
+			break;
+		}
+		spin_unlock_irqrestore(&ept->restart_lock, flags);
+		schedule();
+	}
+	finish_wait(&ept->restart_wait, &__wait);
+	return ret;
+}
+
+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);
+
+	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);
+}
+
+int msm_rpc_add_board_dev(struct rpc_board_dev *devices, int num)
+{
+	unsigned long flags;
+	struct rpc_board_dev_info *board_info;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		board_info = kzalloc(sizeof(struct rpc_board_dev_info),
+				     GFP_KERNEL);
+		if (!board_info)
+			return -ENOMEM;
+
+		board_info->dev = &devices[i];
+		D("%s: adding program %x\n", __func__, board_info->dev->prog);
+		spin_lock_irqsave(&rpc_board_dev_list_lock, flags);
+		list_add_tail(&board_info->list, &rpc_board_dev_list);
+		spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_rpc_add_board_dev);
+
+static void rpcrouter_register_board_dev(struct rr_server *server)
+{
+	struct rpc_board_dev_info *board_info;
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(&rpc_board_dev_list_lock, flags);
+	list_for_each_entry(board_info, &rpc_board_dev_list, list) {
+		if (server->prog == board_info->dev->prog) {
+			D("%s: registering device %x\n",
+			  __func__, board_info->dev->prog);
+			list_del(&board_info->list);
+			rc = platform_device_register(&board_info->dev->pdev);
+			if (rc)
+				pr_err("%s: board dev register failed %d\n",
+				       __func__, rc);
+			kfree(board_info);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags);
+}
+
+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));
+	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);
+	} else {
+		/* mark not connected */
+		ept->dst_pid = 0xffffffff;
+	}
+
+	init_waitqueue_head(&ept->wait_q);
+	INIT_LIST_HEAD(&ept->read_q);
+	spin_lock_init(&ept->read_q_lock);
+	INIT_LIST_HEAD(&ept->reply_avail_q);
+	INIT_LIST_HEAD(&ept->reply_pend_q);
+	spin_lock_init(&ept->reply_q_lock);
+	spin_lock_init(&ept->restart_lock);
+	init_waitqueue_head(&ept->restart_wait);
+	ept->restart_state = RESTART_NORMAL;
+	wake_lock_init(&ept->read_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_read");
+	wake_lock_init(&ept->reply_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_reply");
+	INIT_LIST_HEAD(&ept->incomplete);
+	spin_lock_init(&ept->incomplete_lock);
+
+	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;
+	struct msm_rpc_reply *reply, *reply_tmp;
+	unsigned long flags;
+	struct rpcrouter_xprt_info *xprt_info;
+
+	/* Endpoint with dst_pid = 0xffffffff corresponds to that of
+	** router port. So don't send a REMOVE CLIENT message while
+	** destroying it.*/
+	if (ept->dst_pid != 0xffffffff) {
+		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);
+		mutex_lock(&xprt_info_list_lock);
+		list_for_each_entry(xprt_info, &xprt_info_list, list) {
+			rc = rpcrouter_send_control_msg(xprt_info, &msg);
+			if (rc < 0) {
+				mutex_unlock(&xprt_info_list_lock);
+				return rc;
+			}
+		}
+		mutex_unlock(&xprt_info_list_lock);
+	}
+
+	/* Free replies */
+	spin_lock_irqsave(&ept->reply_q_lock, flags);
+	list_for_each_entry_safe(reply, reply_tmp, &ept->reply_pend_q, list) {
+		list_del(&reply->list);
+		kfree(reply);
+	}
+	list_for_each_entry_safe(reply, reply_tmp, &ept->reply_avail_q, list) {
+		list_del(&reply->list);
+		kfree(reply);
+	}
+	spin_unlock_irqrestore(&ept->reply_q_lock, flags);
+
+	wake_lock_destroy(&ept->read_q_wake_lock);
+	wake_lock_destroy(&ept->reply_q_wake_lock);
+	spin_lock_irqsave(&local_endpoints_lock, flags);
+	list_del(&ept->list);
+	spin_unlock_irqrestore(&local_endpoints_lock, flags);
+	kfree(ept);
+	return 0;
+}
+
+static int rpcrouter_create_remote_endpoint(uint32_t pid, 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 = pid;
+	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);
+	new_c->quota_restart_state = RESTART_NORMAL;
+	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 pid,
+								   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->pid == pid) && (ept->cid == cid)) {
+			spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+			return ept;
+		}
+	}
+	spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+	return NULL;
+}
+
+static void handle_server_restart(struct rr_server *server,
+				  uint32_t pid, uint32_t cid,
+				  uint32_t prog, uint32_t vers)
+{
+	struct rr_remote_endpoint *r_ept;
+	struct msm_rpc_endpoint *ept;
+	unsigned long flags;
+	r_ept = rpcrouter_lookup_remote_endpoint(pid, cid);
+	if (r_ept && (r_ept->quota_restart_state !=
+		      RESTART_NORMAL)) {
+		spin_lock_irqsave(&r_ept->quota_lock, flags);
+		r_ept->tx_quota_cntr = 0;
+		r_ept->quota_restart_state =
+		RESTART_NORMAL;
+		spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+		D(KERN_INFO "rpcrouter: Remote EPT Reset %0x\n",
+			   (unsigned int)r_ept);
+		wake_up(&r_ept->quota_wait);
+	}
+	spin_lock_irqsave(&local_endpoints_lock, flags);
+	list_for_each_entry(ept, &local_endpoints, list) {
+		if ((be32_to_cpu(ept->dst_prog) == prog) &&
+		    (be32_to_cpu(ept->dst_vers) == vers) &&
+		    (ept->restart_state & RESTART_PEND_SVR)) {
+			spin_lock(&ept->restart_lock);
+			ept->restart_state &= ~RESTART_PEND_SVR;
+			spin_unlock(&ept->restart_lock);
+			D("rpcrouter: Local EPT Reset %08x:%08x \n",
+			  prog, vers);
+			wake_up(&ept->restart_wait);
+			wake_up(&ept->wait_q);
+		}
+	}
+	spin_unlock_irqrestore(&local_endpoints_lock, flags);
+}
+
+static int process_control_msg(struct rpcrouter_xprt_info *xprt_info,
+			       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;
+	static int first = 1;
+
+	if (len != sizeof(*msg)) {
+		RR(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 PID %d\n", xprt_info->remote_pid);
+		memset(&ctl, 0, sizeof(ctl));
+		ctl.cmd = RPCROUTER_CTRL_CMD_HELLO;
+		rpcrouter_send_control_msg(xprt_info, &ctl);
+
+		xprt_info->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) {
+			if (server->pid != RPCROUTER_PID_LOCAL)
+				continue;
+			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(xprt_info, &ctl);
+		}
+		spin_unlock_irqrestore(&server_list_lock, flags);
+
+		if (first) {
+			first = 0;
+			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.pid,
+							 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:
+		if (msg->srv.vers == 0) {
+			pr_err(
+			"rpcrouter: Server create rejected, version = 0, "
+			"program = %08x\n", msg->srv.prog);
+			break;
+		}
+
+		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.pid,
+							      msg->srv.cid)) {
+				rc = rpcrouter_create_remote_endpoint(
+					msg->srv.pid, msg->srv.cid);
+				if (rc < 0)
+					printk(KERN_ERR
+						"rpcrouter:Client create"
+						"error (%d)\n", rc);
+			}
+			rpcrouter_register_board_dev(server);
+			schedule_work(&work_create_pdevs);
+			wake_up(&newserver_wait);
+		} else {
+			if ((server->pid == msg->srv.pid) &&
+			    (server->cid == msg->srv.cid)) {
+				handle_server_restart(server,
+						      msg->srv.pid,
+						      msg->srv.cid,
+						      msg->srv.prog,
+						      msg->srv.vers);
+			} 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_LOCAL) {
+			printk(KERN_ERR
+			       "rpcrouter: Denying remote removal of "
+			       "local client\n");
+			break;
+		}
+		r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.pid,
+							 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;
+	case RPCROUTER_CTRL_CMD_PING:
+		/* No action needed for ping messages received */
+		RR("o PING\n");
+		break;
+	default:
+		RR("o UNKNOWN(%08x)\n", msg->cmd);
+		rc = -ENOSYS;
+	}
+
+	return rc;
+}
+
+static void do_create_rpcrouter_pdev(struct work_struct *work)
+{
+	D("%s: modem rpc router up\n", __func__);
+	platform_device_register(&rpcrouter_pdev);
+	complete_all(&rpc_remote_router_up);
+}
+
+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_LOCAL) {
+			if (server->pdev_name[0] == 0) {
+				sprintf(server->pdev_name, "rs%.8x",
+					server->prog);
+				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 *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;
+}
+
+static int rr_read(struct rpcrouter_xprt_info *xprt_info,
+		   void *data, uint32_t len)
+{
+	int rc;
+	unsigned long flags;
+
+	while (!xprt_info->abort_data_read) {
+		spin_lock_irqsave(&xprt_info->lock, flags);
+		if (xprt_info->xprt->read_avail() >= len) {
+			rc = xprt_info->xprt->read(data, len);
+			spin_unlock_irqrestore(&xprt_info->lock, flags);
+			if (rc == len && !xprt_info->abort_data_read)
+				return 0;
+			else
+				return -EIO;
+		}
+		xprt_info->need_len = len;
+		wake_unlock(&xprt_info->wakelock);
+		spin_unlock_irqrestore(&xprt_info->lock, flags);
+
+		wait_event(xprt_info->read_wait,
+			xprt_info->xprt->read_avail() >= len
+			|| xprt_info->abort_data_read);
+	}
+	return -EIO;
+}
+
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+static char *type_to_str(int i)
+{
+	switch (i) {
+	case RPCROUTER_CTRL_CMD_DATA:
+		return "data    ";
+	case RPCROUTER_CTRL_CMD_HELLO:
+		return "hello   ";
+	case RPCROUTER_CTRL_CMD_BYE:
+		return "bye     ";
+	case RPCROUTER_CTRL_CMD_NEW_SERVER:
+		return "new_srvr";
+	case RPCROUTER_CTRL_CMD_REMOVE_SERVER:
+		return "rmv_srvr";
+	case RPCROUTER_CTRL_CMD_REMOVE_CLIENT:
+		return "rmv_clnt";
+	case RPCROUTER_CTRL_CMD_RESUME_TX:
+		return "resum_tx";
+	case RPCROUTER_CTRL_CMD_EXIT:
+		return "cmd_exit";
+	default:
+		return "invalid";
+	}
+}
+#endif
+
+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;
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+	struct rpc_request_hdr *rq;
+#endif
+	uint32_t pm, mid;
+	unsigned long flags;
+
+	struct rpcrouter_xprt_info *xprt_info =
+		container_of(work,
+			     struct rpcrouter_xprt_info,
+			     read_data);
+
+	if (rr_read(xprt_info, &hdr, sizeof(hdr)))
+		goto fail_io;
+
+	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);
+	RAW_HDR("[r rr_h] "
+	    "ver=%i,type=%s,src_pid=%08x,src_cid=%08x,"
+	    "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n",
+	    hdr.version, type_to_str(hdr.type), hdr.src_pid, hdr.src_cid,
+	    hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid);
+
+	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 (xprt_info->remote_pid == -1) {
+			xprt_info->remote_pid = hdr.src_pid;
+
+			/* do restart notification */
+			modem_reset_startup(xprt_info);
+		}
+
+		if (rr_read(xprt_info, xprt_info->r2r_buf, hdr.size))
+			goto fail_io;
+		process_control_msg(xprt_info,
+				    (void *) xprt_info->r2r_buf, hdr.size);
+		goto done;
+	}
+
+	if (hdr.size < sizeof(pm)) {
+		DIAG("runt packet (no pacmark)\n");
+		goto fail_data;
+	}
+	if (rr_read(xprt_info, &pm, sizeof(pm)))
+		goto fail_io;
+
+	hdr.size -= sizeof(pm);
+
+	frag = rr_malloc(sizeof(*frag));
+	frag->next = NULL;
+	frag->length = hdr.size;
+	if (rr_read(xprt_info, frag->data, hdr.size)) {
+		kfree(frag);
+		goto fail_io;
+	}
+
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+	if ((smd_rpcrouter_debug_mask & RAW_PMR) &&
+	    ((pm >> 30 & 0x1) || (pm >> 31 & 0x1))) {
+		uint32_t xid = 0;
+		if (pm >> 30 & 0x1) {
+			rq = (struct rpc_request_hdr *) frag->data;
+			xid = ntohl(rq->xid);
+		}
+		if ((pm >> 31 & 0x1) || (pm >> 30 & 0x1))
+			RAW_PMR_NOMASK("xid:0x%03x first=%i,last=%i,mid=%3i,"
+				       "len=%3i,dst_cid=%08x\n",
+				       xid,
+				       pm >> 30 & 0x1,
+				       pm >> 31 & 0x1,
+				       pm >> 16 & 0xFF,
+				       pm & 0xFFFF, hdr.dst_cid);
+	}
+
+	if (smd_rpcrouter_debug_mask & SMEM_LOG) {
+		rq = (struct rpc_request_hdr *) frag->data;
+		if (rq->xid == 0)
+			smem_log_event(SMEM_LOG_PROC_ID_APPS |
+				       RPC_ROUTER_LOG_EVENT_MID_READ,
+				       PACMARK_MID(pm),
+				       hdr.dst_cid,
+				       hdr.src_cid);
+		else
+			smem_log_event(SMEM_LOG_PROC_ID_APPS |
+				       RPC_ROUTER_LOG_EVENT_MSG_READ,
+				       ntohl(rq->xid),
+				       hdr.dst_cid,
+				       hdr.src_cid);
+	}
+#endif
+
+	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);
+	spin_lock_irqsave(&ept->incomplete_lock, flags);
+	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);
+				spin_unlock_irqrestore(&ept->incomplete_lock,
+						       flags);
+				goto packet_complete;
+			}
+			spin_unlock_irqrestore(&ept->incomplete_lock, flags);
+			goto done;
+		}
+	}
+	spin_unlock_irqrestore(&ept->incomplete_lock, flags);
+	/* 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);
+	D("%s: take read lock on ept %p\n", __func__, ept);
+	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(xprt_info, &msg);
+
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+		if (smd_rpcrouter_debug_mask & SMEM_LOG)
+			smem_log_event(SMEM_LOG_PROC_ID_APPS |
+				       RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT,
+				       RPCROUTER_PID_LOCAL,
+				       hdr.dst_cid,
+				       hdr.src_cid);
+#endif
+
+	}
+
+	/* don't requeue if we should be shutting down */
+	if (!xprt_info->abort_data_read) {
+		queue_work(xprt_info->workqueue, &xprt_info->read_data);
+		return;
+	}
+
+	D("rpc_router terminating for '%s'\n",
+		xprt_info->xprt->name);
+
+fail_io:
+fail_data:
+	D(KERN_ERR "rpc_router has died for '%s'\n",
+			xprt_info->xprt->name);
+}
+
+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);
+}
+EXPORT_SYMBOL(msm_rpc_setup_req);
+
+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;
+}
+
+void msm_rpc_read_wakeup(struct msm_rpc_endpoint *ept)
+{
+	ept->forced_wakeup = 1;
+	wake_up(&ept->wait_q);
+}
+
+int msm_rpc_close(struct msm_rpc_endpoint *ept)
+{
+	if (!ept)
+		return -EINVAL;
+	return msm_rpcrouter_destroy_local_endpoint(ept);
+}
+EXPORT_SYMBOL(msm_rpc_close);
+
+static int msm_rpc_write_pkt(
+	struct rr_header *hdr,
+	struct msm_rpc_endpoint *ept,
+	struct rr_remote_endpoint *r_ept,
+	void *buffer,
+	int count,
+	int first,
+	int last,
+	uint32_t mid
+	)
+{
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+	struct rpc_request_hdr *rq = buffer;
+	uint32_t event_id;
+#endif
+	uint32_t pacmark;
+	unsigned long flags = 0;
+	int rc;
+	struct rpcrouter_xprt_info *xprt_info;
+	int needed;
+
+	DEFINE_WAIT(__wait);
+
+	/* 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);
+
+	rc = wait_for_restart_and_notify(ept);
+	if (rc)
+		return rc;
+
+	if (r_ept) {
+		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) ||
+			    (r_ept->quota_restart_state != RESTART_NORMAL))
+				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 (r_ept->quota_restart_state != RESTART_NORMAL) {
+			spin_lock(&ept->restart_lock);
+			ept->restart_state &= ~RESTART_PEND_NTFY;
+			spin_unlock(&ept->restart_lock);
+			spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+			return -ENETRESET;
+		}
+
+		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;
+
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+			if (smd_rpcrouter_debug_mask & SMEM_LOG) {
+				event_id = (rq->xid == 0) ?
+					RPC_ROUTER_LOG_EVENT_MID_CFM_REQ :
+					RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ;
+
+				smem_log_event(SMEM_LOG_PROC_ID_APPS | event_id,
+					       hdr->dst_pid,
+					       hdr->dst_cid,
+					       hdr->src_cid);
+			}
+#endif
+
+		}
+	}
+	pacmark = PACMARK(count, mid, first, last);
+
+	if (r_ept)
+		spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+
+	mutex_lock(&xprt_info_list_lock);
+	xprt_info = rpcrouter_get_xprt_info(hdr->dst_pid);
+	if (!xprt_info) {
+		mutex_unlock(&xprt_info_list_lock);
+		return -ENETRESET;
+	}
+	spin_lock_irqsave(&xprt_info->lock, flags);
+	mutex_unlock(&xprt_info_list_lock);
+	spin_lock(&ept->restart_lock);
+	if (ept->restart_state != RESTART_NORMAL) {
+		ept->restart_state &= ~RESTART_PEND_NTFY;
+		spin_unlock(&ept->restart_lock);
+		spin_unlock_irqrestore(&xprt_info->lock, flags);
+		return -ENETRESET;
+	}
+
+	needed = sizeof(*hdr) + hdr->size;
+	while ((ept->restart_state == RESTART_NORMAL) &&
+	       (xprt_info->xprt->write_avail() < needed)) {
+		spin_unlock(&ept->restart_lock);
+		spin_unlock_irqrestore(&xprt_info->lock, flags);
+		msleep(250);
+
+		/* refresh xprt pointer to ensure that it hasn't
+		 * been deleted since our last retrieval */
+		mutex_lock(&xprt_info_list_lock);
+		xprt_info = rpcrouter_get_xprt_info(hdr->dst_pid);
+		if (!xprt_info) {
+			mutex_unlock(&xprt_info_list_lock);
+			return -ENETRESET;
+		}
+		spin_lock_irqsave(&xprt_info->lock, flags);
+		mutex_unlock(&xprt_info_list_lock);
+		spin_lock(&ept->restart_lock);
+	}
+	if (ept->restart_state != RESTART_NORMAL) {
+		ept->restart_state &= ~RESTART_PEND_NTFY;
+		spin_unlock(&ept->restart_lock);
+		spin_unlock_irqrestore(&xprt_info->lock, flags);
+		return -ENETRESET;
+	}
+
+	/* TODO: deal with full fifo */
+	xprt_info->xprt->write(hdr, sizeof(*hdr), HEADER);
+	RAW_HDR("[w rr_h] "
+		    "ver=%i,type=%s,src_pid=%08x,src_cid=%08x,"
+		"confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n",
+		hdr->version, type_to_str(hdr->type),
+		hdr->src_pid, hdr->src_cid,
+		hdr->confirm_rx, hdr->size, hdr->dst_pid, hdr->dst_cid);
+	xprt_info->xprt->write(&pacmark, sizeof(pacmark), PACKMARK);
+
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+	if ((smd_rpcrouter_debug_mask & RAW_PMW) &&
+	    ((pacmark >> 30 & 0x1) || (pacmark >> 31 & 0x1))) {
+		uint32_t xid = 0;
+		if (pacmark >> 30 & 0x1)
+			xid = ntohl(rq->xid);
+		if ((pacmark >> 31 & 0x1) || (pacmark >> 30 & 0x1))
+			RAW_PMW_NOMASK("xid:0x%03x first=%i,last=%i,mid=%3i,"
+				       "len=%3i,src_cid=%x\n",
+				       xid,
+				       pacmark >> 30 & 0x1,
+				       pacmark >> 31 & 0x1,
+				       pacmark >> 16 & 0xFF,
+				       pacmark & 0xFFFF, hdr->src_cid);
+	}
+#endif
+
+	xprt_info->xprt->write(buffer, count, PAYLOAD);
+	spin_unlock(&ept->restart_lock);
+	spin_unlock_irqrestore(&xprt_info->lock, flags);
+
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+	if (smd_rpcrouter_debug_mask & SMEM_LOG) {
+		if (rq->xid == 0)
+			smem_log_event(SMEM_LOG_PROC_ID_APPS |
+				       RPC_ROUTER_LOG_EVENT_MID_WRITTEN,
+				       PACMARK_MID(pacmark),
+				       hdr->dst_cid,
+				       hdr->src_cid);
+		else
+			smem_log_event(SMEM_LOG_PROC_ID_APPS |
+				       RPC_ROUTER_LOG_EVENT_MSG_WRITTEN,
+				       ntohl(rq->xid),
+				       hdr->dst_cid,
+				       hdr->src_cid);
+	}
+#endif
+
+	return needed;
+}
+
+static struct msm_rpc_reply *get_pend_reply(struct msm_rpc_endpoint *ept,
+					    uint32_t xid)
+{
+	unsigned long flags;
+	struct msm_rpc_reply *reply;
+	spin_lock_irqsave(&ept->reply_q_lock, flags);
+	list_for_each_entry(reply, &ept->reply_pend_q, list) {
+		if (reply->xid == xid) {
+			list_del(&reply->list);
+			spin_unlock_irqrestore(&ept->reply_q_lock, flags);
+			return reply;
+		}
+	}
+	spin_unlock_irqrestore(&ept->reply_q_lock, flags);
+	return NULL;
+}
+
+void get_requesting_client(struct msm_rpc_endpoint *ept, uint32_t xid,
+			   struct msm_rpc_client_info *clnt_info)
+{
+	unsigned long flags;
+	struct msm_rpc_reply *reply;
+
+	if (!clnt_info)
+		return;
+
+	spin_lock_irqsave(&ept->reply_q_lock, flags);
+	list_for_each_entry(reply, &ept->reply_pend_q, list) {
+		if (reply->xid == xid) {
+			clnt_info->pid = reply->pid;
+			clnt_info->cid = reply->cid;
+			clnt_info->prog = reply->prog;
+			clnt_info->vers = reply->vers;
+			spin_unlock_irqrestore(&ept->reply_q_lock, flags);
+			return;
+		}
+	}
+	spin_unlock_irqrestore(&ept->reply_q_lock, flags);
+	return;
+}
+
+static void set_avail_reply(struct msm_rpc_endpoint *ept,
+			    struct msm_rpc_reply *reply)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&ept->reply_q_lock, flags);
+	list_add_tail(&reply->list, &ept->reply_avail_q);
+	spin_unlock_irqrestore(&ept->reply_q_lock, flags);
+}
+
+static struct msm_rpc_reply *get_avail_reply(struct msm_rpc_endpoint *ept)
+{
+	struct msm_rpc_reply *reply;
+	unsigned long flags;
+	if (list_empty(&ept->reply_avail_q)) {
+		if (ept->reply_cnt >= RPCROUTER_PEND_REPLIES_MAX) {
+			printk(KERN_ERR
+			       "exceeding max replies of %d \n",
+			       RPCROUTER_PEND_REPLIES_MAX);
+			return 0;
+		}
+		reply = kmalloc(sizeof(struct msm_rpc_reply), GFP_KERNEL);
+		if (!reply)
+			return 0;
+		D("Adding reply 0x%08x \n", (unsigned int)reply);
+		memset(reply, 0, sizeof(struct msm_rpc_reply));
+		spin_lock_irqsave(&ept->reply_q_lock, flags);
+		ept->reply_cnt++;
+		spin_unlock_irqrestore(&ept->reply_q_lock, flags);
+	} else {
+		spin_lock_irqsave(&ept->reply_q_lock, flags);
+		reply = list_first_entry(&ept->reply_avail_q,
+					 struct msm_rpc_reply,
+					 list);
+		list_del(&reply->list);
+		spin_unlock_irqrestore(&ept->reply_q_lock, flags);
+	}
+	return reply;
+}
+
+static void set_pend_reply(struct msm_rpc_endpoint *ept,
+			   struct msm_rpc_reply *reply)
+{
+		unsigned long flags;
+		spin_lock_irqsave(&ept->reply_q_lock, flags);
+		D("%s: take reply lock on ept %p\n", __func__, ept);
+		wake_lock(&ept->reply_q_wake_lock);
+		list_add_tail(&reply->list, &ept->reply_pend_q);
+		spin_unlock_irqrestore(&ept->reply_q_lock, flags);
+}
+
+int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count)
+{
+	struct rr_header hdr;
+	struct rpc_request_hdr *rq = buffer;
+	struct rr_remote_endpoint *r_ept;
+	struct msm_rpc_reply *reply = NULL;
+	int max_tx;
+	int tx_cnt;
+	char *tx_buf;
+	int rc;
+	int first_pkt = 1;
+	uint32_t mid;
+	unsigned long flags;
+
+	/* 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 ((ept->dst_prog != rq->prog) ||
+		    ((be32_to_cpu(ept->dst_vers) & 0x0fff0000) !=
+		     (be32_to_cpu(rq->vers) & 0x0fff0000))) {
+			printk(KERN_ERR
+			       "rr_write: cannot write to %08x:%08x "
+			       "(bound to %08x:%08x)\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 to %08x:%d @ %d:%08x (%d bytes)\n",
+		   be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+		   ept->dst_pid, ept->dst_cid, count);
+	} else {
+		/* RPC REPLY */
+		reply = get_pend_reply(ept, rq->xid);
+		if (!reply) {
+			printk(KERN_ERR
+			       "rr_write: rejecting, reply not found \n");
+			return -EINVAL;
+		}
+		hdr.dst_pid = reply->pid;
+		hdr.dst_cid = reply->cid;
+		IO("REPLY to xid=%d @ %d:%08x (%d bytes)\n",
+		   be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count);
+	}
+
+	r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_pid, hdr.dst_cid);
+
+	if ((!r_ept) && (hdr.dst_pid != RPCROUTER_PID_LOCAL)) {
+		printk(KERN_ERR
+			"msm_rpc_write(): No route to ept "
+			"[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid);
+		count = -EHOSTUNREACH;
+		goto write_release_lock;
+	}
+
+	tx_cnt = count;
+	tx_buf = buffer;
+	mid = atomic_add_return(1, &pm_mid) & 0xFF;
+	/* The modem's router can only take 500 bytes of data. The
+	   first 8 bytes it uses on the modem side for addressing,
+	   the next 4 bytes are for the pacmark header. */
+	max_tx = RPCROUTER_MSGSIZE_MAX - 8 - sizeof(uint32_t);
+	IO("Writing %d bytes, max pkt size is %d\n",
+	   tx_cnt, max_tx);
+	while (tx_cnt > 0) {
+		if (tx_cnt > max_tx) {
+			rc = msm_rpc_write_pkt(&hdr, ept, r_ept,
+					       tx_buf, max_tx,
+					       first_pkt, 0, mid);
+			if (rc < 0) {
+				count = rc;
+				goto write_release_lock;
+			}
+			IO("Wrote %d bytes First %d, Last 0 mid %d\n",
+			   rc, first_pkt, mid);
+			tx_cnt -= max_tx;
+			tx_buf += max_tx;
+		} else {
+			rc = msm_rpc_write_pkt(&hdr, ept, r_ept,
+					       tx_buf, tx_cnt,
+					       first_pkt, 1, mid);
+			if (rc < 0) {
+				count = rc;
+				goto write_release_lock;
+			}
+			IO("Wrote %d bytes First %d Last 1 mid %d\n",
+			   rc, first_pkt, mid);
+			break;
+		}
+		first_pkt = 0;
+	}
+
+ write_release_lock:
+	/* if reply, release wakelock after writing to the transport */
+	if (rq->type != 0) {
+		/* Upon failure, add reply tag to the pending list.
+		** Else add reply tag to the avail/free list. */
+		if (count < 0)
+			set_pend_reply(ept, reply);
+		else
+			set_avail_reply(ept, reply);
+
+		spin_lock_irqsave(&ept->reply_q_lock, flags);
+		if (list_empty(&ept->reply_pend_q)) {
+			D("%s: release reply lock on ept %p\n", __func__, ept);
+			wake_unlock(&ept->reply_q_wake_lock);
+		}
+		spin_unlock_irqrestore(&ept->reply_q_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;
+}
+EXPORT_SYMBOL(msm_rpc_read);
+
+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;
+
+	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;
+	struct msm_rpc_reply *reply;
+	unsigned long flags;
+	int rc;
+
+	rc = wait_for_restart_and_notify(ept);
+	if (rc)
+		return 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) ||
+						   ept->forced_wakeup ||
+						   ept->restart_state));
+			if (!msm_rpc_clear_netreset(ept))
+				return -ENETRESET;
+		} else {
+			rc = wait_event_timeout(
+				ept->wait_q,
+				(ept_packet_available(ept) ||
+				 ept->forced_wakeup ||
+				 ept->restart_state),
+				timeout);
+			if (!msm_rpc_clear_netreset(ept))
+				return -ENETRESET;
+			if (rc == 0)
+				return -ETIMEDOUT;
+		}
+	} else {
+		if (timeout < 0) {
+			rc = wait_event_interruptible(
+				ept->wait_q, (ept_packet_available(ept) ||
+					ept->forced_wakeup ||
+					ept->restart_state));
+			if (!msm_rpc_clear_netreset(ept))
+				return -ENETRESET;
+			if (rc < 0)
+				return rc;
+		} else {
+			rc = wait_event_interruptible_timeout(
+				ept->wait_q,
+				(ept_packet_available(ept) ||
+				 ept->forced_wakeup ||
+				 ept->restart_state),
+				timeout);
+			if (!msm_rpc_clear_netreset(ept))
+				return -ENETRESET;
+			if (rc == 0)
+				return -ETIMEDOUT;
+		}
+	}
+
+	if (ept->forced_wakeup) {
+		ept->forced_wakeup = 0;
+		return 0;
+	}
+
+	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);
+	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)) {
+		/* RPC CALL */
+		reply = get_avail_reply(ept);
+		if (!reply) {
+			rc = -ENOMEM;
+			goto read_release_lock;
+		}
+		reply->cid = pkt->hdr.src_cid;
+		reply->pid = pkt->hdr.src_pid;
+		reply->xid = rq->xid;
+		reply->prog = rq->prog;
+		reply->vers = rq->vers;
+		set_pend_reply(ept, reply);
+	}
+
+	kfree(pkt);
+
+	IO("READ on ept %p (%d bytes)\n", ept, rc);
+
+ read_release_lock:
+
+	/* release read wakelock after taking reply wakelock */
+	spin_lock_irqsave(&ept->read_q_lock, flags);
+	if (list_empty(&ept->read_q)) {
+		D("%s: release read lock on ept %p\n", __func__, ept);
+		wake_unlock(&ept->read_q_wake_lock);
+	}
+	spin_unlock_irqrestore(&ept->read_q_lock, flags);
+
+	return rc;
+}
+
+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 struct rr_server *msm_rpc_get_server(uint32_t prog, uint32_t vers,
+					    uint32_t accept_compatible,
+					    uint32_t *found_prog)
+{
+	struct rr_server *server;
+	unsigned long     flags;
+
+	if (found_prog == NULL)
+		return NULL;
+
+	*found_prog = 0;
+	spin_lock_irqsave(&server_list_lock, flags);
+	list_for_each_entry(server, &server_list, list) {
+		if (server->prog == prog) {
+			*found_prog = 1;
+			spin_unlock_irqrestore(&server_list_lock, flags);
+			if (accept_compatible) {
+				if (msm_rpc_is_compatible_version(server->vers,
+								  vers)) {
+					return server;
+				} else {
+					return NULL;
+				}
+			} else if (server->vers == vers) {
+				return server;
+			} else
+				return NULL;
+		}
+	}
+	spin_unlock_irqrestore(&server_list_lock, flags);
+	return NULL;
+}
+
+static struct msm_rpc_endpoint *__msm_rpc_connect(uint32_t prog, uint32_t vers,
+						  uint32_t accept_compatible,
+						  unsigned flags)
+{
+	struct msm_rpc_endpoint *ept;
+	struct rr_server *server;
+	uint32_t found_prog;
+	int rc = 0;
+
+	DEFINE_WAIT(__wait);
+
+	for (;;) {
+		prepare_to_wait(&newserver_wait, &__wait,
+				TASK_INTERRUPTIBLE);
+
+		server = msm_rpc_get_server(prog, vers, accept_compatible,
+					    &found_prog);
+		if (server)
+			break;
+
+		if (found_prog) {
+			pr_info("%s: server not found %x:%x\n",
+				__func__, prog, vers);
+			rc = -EHOSTUNREACH;
+			break;
+		}
+
+		if (msm_rpc_connect_timeout_ms == 0) {
+			rc = -EHOSTUNREACH;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			rc = -ERESTARTSYS;
+			break;
+		}
+
+		rc = schedule_timeout(
+			msecs_to_jiffies(msm_rpc_connect_timeout_ms));
+		if (!rc) {
+			rc = -ETIMEDOUT;
+			break;
+		}
+	}
+	finish_wait(&newserver_wait, &__wait);
+
+	if (!server)
+		return ERR_PTR(rc);
+
+	if (accept_compatible && (server->vers != vers)) {
+		D("RPC Using new version 0x%08x(0x%08x) prog 0x%08x",
+			vers, server->vers, prog);
+		D(" ... Continuing\n");
+	}
+
+	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(server->vers);
+
+	return ept;
+}
+
+struct msm_rpc_endpoint *msm_rpc_connect_compatible(uint32_t prog,
+			uint32_t vers, unsigned flags)
+{
+	return __msm_rpc_connect(prog, vers, 1, flags);
+}
+EXPORT_SYMBOL(msm_rpc_connect_compatible);
+
+struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog,
+			 uint32_t vers, unsigned flags)
+{
+	return __msm_rpc_connect(prog, vers, 0, flags);
+}
+EXPORT_SYMBOL(msm_rpc_connect);
+
+/* 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;
+	struct rpcrouter_xprt_info *xprt_info;
+
+	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);
+
+	mutex_lock(&xprt_info_list_lock);
+	list_for_each_entry(xprt_info, &xprt_info_list, list) {
+		rc = rpcrouter_send_control_msg(xprt_info, &msg);
+		if (rc < 0) {
+			mutex_unlock(&xprt_info_list_lock);
+			return rc;
+		}
+	}
+	mutex_unlock(&xprt_info_list_lock);
+	return 0;
+}
+
+int msm_rpc_clear_netreset(struct msm_rpc_endpoint *ept)
+{
+	unsigned long flags;
+	int rc = 1;
+	spin_lock_irqsave(&ept->restart_lock, flags);
+	if (ept->restart_state !=  RESTART_NORMAL) {
+		ept->restart_state &= ~RESTART_PEND_NTFY;
+		rc = 0;
+	}
+	spin_unlock_irqrestore(&ept->restart_lock, flags);
+	return rc;
+}
+
+/* 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;
+}
+
+int msm_rpc_get_curr_pkt_size(struct msm_rpc_endpoint *ept)
+{
+	unsigned long flags;
+	struct rr_packet *pkt;
+	int rc = 0;
+
+	if (!ept)
+		return -EINVAL;
+
+	if (!msm_rpc_clear_netreset(ept))
+		return -ENETRESET;
+
+	spin_lock_irqsave(&ept->read_q_lock, flags);
+	if (!list_empty(&ept->read_q)) {
+		pkt = list_first_entry(&ept->read_q, struct rr_packet, list);
+		rc = pkt->length;
+	}
+	spin_unlock_irqrestore(&ept->read_q_lock, flags);
+
+	return rc;
+}
+
+int msm_rpcrouter_close(void)
+{
+	struct rpcrouter_xprt_info *xprt_info, *tmp_xprt_info;
+	union rr_control_msg ctl;
+
+	ctl.cmd = RPCROUTER_CTRL_CMD_BYE;
+	mutex_lock(&xprt_info_list_lock);
+	list_for_each_entry_safe(xprt_info, tmp_xprt_info,
+				 &xprt_info_list, list) {
+		rpcrouter_send_control_msg(xprt_info, &ctl);
+		xprt_info->xprt->close();
+		list_del(&xprt_info->list);
+		kfree(xprt_info);
+	}
+	mutex_unlock(&xprt_info_list_lock);
+	return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static int dump_servers(char *buf, int max)
+{
+	int i = 0;
+	unsigned long flags;
+	struct rr_server *svr;
+	const char *sym;
+
+	spin_lock_irqsave(&server_list_lock, flags);
+	list_for_each_entry(svr, &server_list, list) {
+		i += scnprintf(buf + i, max - i, "pdev_name: %s\n",
+			       svr->pdev_name);
+		i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", svr->pid);
+		i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", svr->cid);
+		i += scnprintf(buf + i, max - i, "prog: 0x%08x", svr->prog);
+		sym = smd_rpc_get_sym(svr->prog);
+		if (sym)
+			i += scnprintf(buf + i, max - i, " (%s)\n", sym);
+		else
+			i += scnprintf(buf + i, max - i, "\n");
+		i += scnprintf(buf + i, max - i, "vers: 0x%08x\n", svr->vers);
+		i += scnprintf(buf + i, max - i, "\n");
+	}
+	spin_unlock_irqrestore(&server_list_lock, flags);
+
+	return i;
+}
+
+static int dump_remote_endpoints(char *buf, int max)
+{
+	int i = 0;
+	unsigned long flags;
+	struct rr_remote_endpoint *ept;
+
+	spin_lock_irqsave(&remote_endpoints_lock, flags);
+	list_for_each_entry(ept, &remote_endpoints, list) {
+		i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", ept->pid);
+		i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", ept->cid);
+		i += scnprintf(buf + i, max - i, "tx_quota_cntr: %i\n",
+			       ept->tx_quota_cntr);
+		i += scnprintf(buf + i, max - i, "quota_restart_state: %i\n",
+			       ept->quota_restart_state);
+		i += scnprintf(buf + i, max - i, "\n");
+	}
+	spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+
+	return i;
+}
+
+static int dump_msm_rpc_endpoint(char *buf, int max)
+{
+	int i = 0;
+	unsigned long flags;
+	struct msm_rpc_reply *reply;
+	struct msm_rpc_endpoint *ept;
+	struct rr_packet *pkt;
+	const char *sym;
+
+	spin_lock_irqsave(&local_endpoints_lock, flags);
+	list_for_each_entry(ept, &local_endpoints, list) {
+		i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", ept->pid);
+		i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", ept->cid);
+		i += scnprintf(buf + i, max - i, "dst_pid: 0x%08x\n",
+			       ept->dst_pid);
+		i += scnprintf(buf + i, max - i, "dst_cid: 0x%08x\n",
+			       ept->dst_cid);
+		i += scnprintf(buf + i, max - i, "dst_prog: 0x%08x",
+			       be32_to_cpu(ept->dst_prog));
+		sym = smd_rpc_get_sym(be32_to_cpu(ept->dst_prog));
+		if (sym)
+			i += scnprintf(buf + i, max - i, " (%s)\n", sym);
+		else
+			i += scnprintf(buf + i, max - i, "\n");
+		i += scnprintf(buf + i, max - i, "dst_vers: 0x%08x\n",
+			       be32_to_cpu(ept->dst_vers));
+		i += scnprintf(buf + i, max - i, "reply_cnt: %i\n",
+			       ept->reply_cnt);
+		i += scnprintf(buf + i, max - i, "restart_state: %i\n",
+			       ept->restart_state);
+
+		i += scnprintf(buf + i, max - i, "outstanding xids:\n");
+		spin_lock(&ept->reply_q_lock);
+		list_for_each_entry(reply, &ept->reply_pend_q, list)
+			i += scnprintf(buf + i, max - i, "    xid = %u\n",
+				       ntohl(reply->xid));
+		spin_unlock(&ept->reply_q_lock);
+
+		i += scnprintf(buf + i, max - i, "complete unread packets:\n");
+		spin_lock(&ept->read_q_lock);
+		list_for_each_entry(pkt, &ept->read_q, list) {
+			i += scnprintf(buf + i, max - i, "    mid = %i\n",
+				       pkt->mid);
+			i += scnprintf(buf + i, max - i, "    length = %i\n",
+				       pkt->length);
+		}
+		spin_unlock(&ept->read_q_lock);
+		i += scnprintf(buf + i, max - i, "\n");
+	}
+	spin_unlock_irqrestore(&local_endpoints_lock, flags);
+
+	return i;
+}
+
+#define DEBUG_BUFMAX 4096
+static char debug_buffer[DEBUG_BUFMAX];
+
+static ssize_t debug_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	int (*fill)(char *buf, int max) = file->private_data;
+	int bsize = fill(debug_buffer, DEBUG_BUFMAX);
+	return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations debug_ops = {
+	.read = debug_read,
+	.open = debug_open,
+};
+
+static void debug_create(const char *name, mode_t mode,
+			 struct dentry *dent,
+			 int (*fill)(char *buf, int max))
+{
+	debugfs_create_file(name, mode, dent, fill, &debug_ops);
+}
+
+static void debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("smd_rpcrouter", 0);
+	if (IS_ERR(dent))
+		return;
+
+	debug_create("dump_msm_rpc_endpoints", 0444, dent,
+		     dump_msm_rpc_endpoint);
+	debug_create("dump_remote_endpoints", 0444, dent,
+		     dump_remote_endpoints);
+	debug_create("dump_servers", 0444, dent,
+		     dump_servers);
+
+}
+
+#else
+static void debugfs_init(void) {}
+#endif
+
+static int msm_rpcrouter_add_xprt(struct rpcrouter_xprt *xprt)
+{
+	struct rpcrouter_xprt_info *xprt_info;
+
+	D("Registering xprt %s to RPC Router\n", xprt->name);
+
+	xprt_info = kmalloc(sizeof(struct rpcrouter_xprt_info), GFP_KERNEL);
+	if (!xprt_info)
+		return -ENOMEM;
+
+	xprt_info->xprt = xprt;
+	xprt_info->initialized = 0;
+	xprt_info->remote_pid = -1;
+	init_waitqueue_head(&xprt_info->read_wait);
+	spin_lock_init(&xprt_info->lock);
+	wake_lock_init(&xprt_info->wakelock,
+		       WAKE_LOCK_SUSPEND, xprt->name);
+	xprt_info->need_len = 0;
+	xprt_info->abort_data_read = 0;
+	INIT_WORK(&xprt_info->read_data, do_read_data);
+	INIT_LIST_HEAD(&xprt_info->list);
+
+	xprt_info->workqueue = create_singlethread_workqueue(xprt->name);
+	if (!xprt_info->workqueue) {
+		kfree(xprt_info);
+		return -ENOMEM;
+	}
+
+	if (!strcmp(xprt->name, "rpcrouter_loopback_xprt")) {
+		xprt_info->remote_pid = RPCROUTER_PID_LOCAL;
+		xprt_info->initialized = 1;
+	} else {
+		smsm_change_state(SMSM_APPS_STATE, 0, SMSM_RPCINIT);
+	}
+
+	mutex_lock(&xprt_info_list_lock);
+	list_add_tail(&xprt_info->list, &xprt_info_list);
+	mutex_unlock(&xprt_info_list_lock);
+
+	queue_work(xprt_info->workqueue, &xprt_info->read_data);
+
+	xprt->priv = xprt_info;
+
+	return 0;
+}
+
+static void msm_rpcrouter_remove_xprt(struct rpcrouter_xprt *xprt)
+{
+	struct rpcrouter_xprt_info *xprt_info;
+	unsigned long flags;
+
+	if (xprt && xprt->priv) {
+		xprt_info = xprt->priv;
+
+		/* abort rr_read thread */
+		xprt_info->abort_data_read = 1;
+		wake_up(&xprt_info->read_wait);
+
+		/* remove xprt from available xprts */
+		mutex_lock(&xprt_info_list_lock);
+		spin_lock_irqsave(&xprt_info->lock, flags);
+		list_del(&xprt_info->list);
+
+		/* unlock the spinlock last to avoid a race
+		 * condition with rpcrouter_get_xprt_info
+		 * in msm_rpc_write_pkt in which the
+		 * xprt is returned from rpcrouter_get_xprt_info
+		 * and then deleted here. */
+		mutex_unlock(&xprt_info_list_lock);
+		spin_unlock_irqrestore(&xprt_info->lock, flags);
+
+		/* cleanup workqueues and wakelocks */
+		flush_workqueue(xprt_info->workqueue);
+		destroy_workqueue(xprt_info->workqueue);
+		wake_lock_destroy(&xprt_info->wakelock);
+
+
+		/* free memory */
+		xprt->priv = 0;
+		kfree(xprt_info);
+	}
+}
+
+struct rpcrouter_xprt_work {
+	struct rpcrouter_xprt *xprt;
+	struct work_struct work;
+};
+
+static void xprt_open_worker(struct work_struct *work)
+{
+	struct rpcrouter_xprt_work *xprt_work =
+		container_of(work, struct rpcrouter_xprt_work, work);
+
+	msm_rpcrouter_add_xprt(xprt_work->xprt);
+
+	kfree(xprt_work);
+}
+
+static void xprt_close_worker(struct work_struct *work)
+{
+	struct rpcrouter_xprt_work *xprt_work =
+		container_of(work, struct rpcrouter_xprt_work, work);
+
+	modem_reset_cleanup(xprt_work->xprt->priv);
+	msm_rpcrouter_remove_xprt(xprt_work->xprt);
+
+	if (atomic_dec_return(&pending_close_count) == 0)
+		wake_up(&subsystem_restart_wait);
+
+	kfree(xprt_work);
+}
+
+void msm_rpcrouter_xprt_notify(struct rpcrouter_xprt *xprt, unsigned event)
+{
+	struct rpcrouter_xprt_info *xprt_info;
+	struct rpcrouter_xprt_work *xprt_work;
+
+	/* Workqueue is created in init function which works for all existing
+	 * clients.  If this fails in the future, then it will need to be
+	 * created earlier. */
+	BUG_ON(!rpcrouter_workqueue);
+
+	switch (event) {
+	case RPCROUTER_XPRT_EVENT_OPEN:
+		D("open event for '%s'\n", xprt->name);
+		xprt_work = kmalloc(sizeof(struct rpcrouter_xprt_work),
+				GFP_ATOMIC);
+		xprt_work->xprt = xprt;
+		INIT_WORK(&xprt_work->work, xprt_open_worker);
+		queue_work(rpcrouter_workqueue, &xprt_work->work);
+		break;
+
+	case RPCROUTER_XPRT_EVENT_CLOSE:
+		D("close event for '%s'\n", xprt->name);
+
+		atomic_inc(&pending_close_count);
+
+		xprt_work = kmalloc(sizeof(struct rpcrouter_xprt_work),
+				GFP_ATOMIC);
+		xprt_work->xprt = xprt;
+		INIT_WORK(&xprt_work->work, xprt_close_worker);
+		queue_work(rpcrouter_workqueue, &xprt_work->work);
+		break;
+	}
+
+	xprt_info = xprt->priv;
+	if (xprt_info) {
+		/* Check read_avail even for OPEN event to handle missed
+		   DATA events while processing the OPEN event*/
+		if (xprt->read_avail() >= xprt_info->need_len)
+			wake_lock(&xprt_info->wakelock);
+		wake_up(&xprt_info->read_wait);
+	}
+}
+
+static int modem_restart_notifier_cb(struct notifier_block *this,
+				  unsigned long code,
+				  void *data);
+static struct notifier_block nb = {
+	.notifier_call = modem_restart_notifier_cb,
+};
+
+static int modem_restart_notifier_cb(struct notifier_block *this,
+				  unsigned long code,
+				  void *data)
+{
+	switch (code) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+		D("%s: SUBSYS_BEFORE_SHUTDOWN\n", __func__);
+		break;
+
+	case SUBSYS_BEFORE_POWERUP:
+		D("%s: waiting for RPC restart to complete\n", __func__);
+		wait_event(subsystem_restart_wait,
+			atomic_read(&pending_close_count) == 0);
+		D("%s: finished restart wait\n", __func__);
+		break;
+
+	default:
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static void *restart_notifier_handle;
+static __init int modem_restart_late_init(void)
+{
+	restart_notifier_handle = subsys_notif_register_notifier("modem", &nb);
+	return 0;
+}
+late_initcall(modem_restart_late_init);
+
+static int __init rpcrouter_init(void)
+{
+	int ret;
+
+	msm_rpc_connect_timeout_ms = 0;
+	smd_rpcrouter_debug_mask |= SMEM_LOG;
+	debugfs_init();
+
+
+	/* Initialize what we need to start processing */
+	rpcrouter_workqueue =
+		create_singlethread_workqueue("rpcrouter");
+	if (!rpcrouter_workqueue) {
+		msm_rpcrouter_exit_devices();
+		return -ENOMEM;
+	}
+
+	init_waitqueue_head(&newserver_wait);
+	init_waitqueue_head(&subsystem_restart_wait);
+
+	ret = msm_rpcrouter_init_devices();
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+
+module_init(rpcrouter_init);
+MODULE_DESCRIPTION("MSM RPC Router");
+MODULE_AUTHOR("San Mehat <san@android.com>");
+MODULE_LICENSE("GPL");