summaryrefslogtreecommitdiff
path: root/arch/arm/mach-msm/smd_rpcrouter.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-msm/smd_rpcrouter.c')
-rw-r--r--arch/arm/mach-msm/smd_rpcrouter.c2197
1 files changed, 2197 insertions, 0 deletions
diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c
new file mode 100644
index 000000000000..eb69670e2b24
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter.c
@@ -0,0 +1,2197 @@
+/* arch/arm/mach-msm/smd_rpcrouter.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009, 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/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 <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 "smd_rpcrouter.h"
+#include "modem_notifier.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 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 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 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_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
+
+/* After restart notification, local ep keep
+ * state for server restart and for ep notify.
+ * Server restart cleared by R-R new svr msg.
+ * NTFY cleared by calling msm_rpc_clear_netreset
+*/
+
+#define RESTART_NORMAL 0
+#define RESTART_PEND_SVR 1
+#define RESTART_PEND_NTFY 2
+#define RESTART_PEND_NTFY_SVR 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;
+
+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 void modem_reset_start_cleanup(void)
+{
+ 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("modem_reset_start_clenup PID %x, remotepid:%d \n",
+ ept->dst_pid, RPCROUTER_PID_REMOTE);
+ /* 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);
+ if (ept->dst_pid == RPCROUTER_PID_REMOTE) {
+ 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);
+ /* 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;
+ 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 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));
+ 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;
+ 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;
+
+ 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;
+
+ /* 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);
+
+ 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);
+ 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 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 void handle_server_restart(struct rr_server *server, 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(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);
+ printk(KERN_INFO "rpcrouter: Remote EP %0x Reset\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(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)) {
+ 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) {
+ 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(&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.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.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)) {
+ handle_server_restart(server, 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_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;
+ 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)
+{
+ 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;
+
+ 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;
+ }
+ 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;
+}
+
+#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 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;
+#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG)
+ struct rpc_request_hdr *rq;
+#endif
+ uint32_t pm, mid;
+ unsigned long flags;
+
+ if (rr_read(&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 (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;
+
+#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);
+ 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);
+
+#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
+
+ }
+
+ queue_work(rpcrouter_workqueue, &work_read_data);
+ return;
+
+fail_io:
+fail_data:
+ printk(KERN_ERR "rpc_router has died\n");
+}
+
+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;
+}
+
+int msm_rpc_close(struct msm_rpc_endpoint *ept)
+{
+ 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;
+#endif
+ uint32_t pacmark;
+ unsigned long flags;
+ 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);
+
+ 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;
+ }
+ if (signal_pending(current) &&
+ ((!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))) {
+ spin_unlock_irqrestore(&ept->restart_lock, flags);
+ break;
+ }
+ spin_unlock_irqrestore(&ept->restart_lock, flags);
+ schedule();
+ }
+ finish_wait(&ept->restart_wait, &__wait);
+
+ if (signal_pending(current) &&
+ (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) {
+ return -ERESTARTSYS;
+ }
+
+ 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) {
+ if (rq->xid == 0)
+ smem_log_event(SMEM_LOG_PROC_ID_APPS |
+ RPC_ROUTER_LOG_EVENT_MID_CFM_REQ,
+ hdr->dst_pid,
+ hdr->dst_cid,
+ hdr->src_cid);
+ else
+ smem_log_event(SMEM_LOG_PROC_ID_APPS |
+ RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ,
+ hdr->dst_pid,
+ hdr->dst_cid,
+ hdr->src_cid);
+ }
+#endif
+
+ }
+ pacmark = PACMARK(count, mid, first, last);
+
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+
+ spin_lock_irqsave(&smd_lock, flags);
+ 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(&smd_lock, flags);
+ return -ENETRESET;
+ }
+
+ needed = sizeof(*hdr) + hdr->size;
+ while ((ept->restart_state == RESTART_NORMAL) &&
+ (smd_write_avail(smd_channel) < needed)) {
+ spin_unlock(&ept->restart_lock);
+ spin_unlock_irqrestore(&smd_lock, flags);
+ msleep(250);
+ spin_lock_irqsave(&smd_lock, flags);
+ 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(&smd_lock, flags);
+ return -ENETRESET;
+ }
+
+ /* TODO: deal with full fifo */
+ smd_write(smd_channel, hdr, sizeof(*hdr));
+ 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);
+ smd_write(smd_channel, &pacmark, sizeof(pacmark));
+
+#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
+
+ smd_write(smd_channel, buffer, count);
+ spin_unlock(&ept->restart_lock);
+ spin_unlock_irqrestore(&smd_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);
+ 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;
+ int max_tx;
+ int tx_cnt;
+ char *tx_buf;
+ int rc;
+ int first_pkt = 1;
+ uint32_t mid;
+
+ /* 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;
+ set_avail_reply(ept, reply);
+ 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_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;
+ }
+
+ 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)
+ return rc;
+ 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)
+ return rc;
+ IO("Wrote %d bytes First %d Last 1 mid %d\n",
+ rc, first_pkt, mid);
+ break;
+ }
+ first_pkt = 0;
+ }
+
+ 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;
+ DEFINE_WAIT(__wait);
+ unsigned long flags;
+ int rc;
+
+ IO("READ on ept %p\n", ept);
+ spin_lock_irqsave(&ept->restart_lock, flags);
+ if (ept->restart_state != RESTART_NORMAL) {
+ ept->restart_state &= ~RESTART_PEND_NTFY;
+ spin_unlock_irqrestore(&ept->restart_lock, flags);
+ return -ENETRESET;
+ }
+ spin_unlock_irqrestore(&ept->restart_lock, flags);
+
+ if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) {
+ if (timeout < 0) {
+ wait_event(ept->wait_q, ept_packet_available(ept));
+ if (!msm_rpc_clear_netreset(ept))
+ return -ENETRESET;
+ } else {
+ rc = wait_event_timeout(
+ ept->wait_q, ept_packet_available(ept),
+ 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));
+ 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),
+ timeout);
+ if (!msm_rpc_clear_netreset(ept))
+ return -ENETRESET;
+ 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);
+ 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)
+ return -ENOMEM;
+ 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);
+ 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);
+
+int msm_rpc_get_compatible_server(uint32_t prog,
+ uint32_t ver,
+ uint32_t *found_vers)
+{
+ struct rr_server *server;
+ unsigned long flags;
+ uint32_t found = -1;
+ 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 found;
+}
+EXPORT_SYMBOL(msm_rpc_get_compatible_server);
+
+struct msm_rpc_endpoint *msm_rpc_connect_compatible(uint32_t prog,
+ uint32_t vers, unsigned flags)
+{
+ uint32_t found_vers;
+ int ret;
+ ret = msm_rpc_get_compatible_server(prog, vers, &found_vers);
+ if (ret < 0)
+ return ERR_PTR(-EHOSTUNREACH);
+ if (found_vers != vers) {
+ D("RPC Using new version 0x%08x(0x%08x) prog 0x%08x",
+ vers, found_vers, prog);
+ D(" ... Continuing\n");
+ }
+ return msm_rpc_connect(prog, found_vers, flags);
+}
+EXPORT_SYMBOL(msm_rpc_connect_compatible);
+
+struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rr_server *server;
+
+ 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);
+
+/* 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;
+}
+
+int msm_rpc_clear_netreset(struct msm_rpc_endpoint *ept)
+{
+ unsigned long flags;
+ int rc = 1;
+ RR("RESET RESTART FLAG for EPT:%08x \n", (unsigned int)ept);
+ 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;
+}
+
+static int msm_rpcrouter_modem_notify(struct notifier_block *this,
+ unsigned long code,
+ void *_cmd)
+{
+ switch (code) {
+ case MODEM_NOTIFIER_START_RESET:
+ NTFY("%s: MODEM_NOTIFIER_START_RESET", __func__);
+ modem_reset_start_cleanup();
+ break;
+ case MODEM_NOTIFIER_END_RESET:
+ NTFY("%s: MODEM_NOTIFIER_END_RESET", __func__);
+ break;
+ default:
+ NTFY("%s: default", __func__);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_rpcrouter_nb = {
+ .notifier_call = msm_rpcrouter_modem_notify,
+};
+
+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);
+
+ rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter");
+ if (!rpcrouter_workqueue)
+ return -ENOMEM;
+
+ rc = msm_rpcrouter_init_devices();
+ if (rc < 0)
+ goto fail_destroy_workqueue;
+
+ rc = modem_register_notifier(&msm_rpcrouter_nb);
+ if (rc < 0)
+ goto fail_remove_devices;
+
+ /* Open up SMD channel 2 */
+ initialized = 0;
+ rc = smd_open("RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify);
+ if (rc < 0)
+ goto fail_remove_reset_notifier;
+
+ queue_work(rpcrouter_workqueue, &work_read_data);
+ return 0;
+
+ fail_remove_reset_notifier:
+ modem_unregister_notifier(&msm_rpcrouter_nb);
+ 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 = "RPCCALL",
+ .owner = THIS_MODULE,
+ },
+};
+
+#if defined(CONFIG_DEBUG_FS)
+#define HSIZE 13
+
+struct sym {
+ uint32_t val;
+ char *str;
+ struct hlist_node node;
+};
+
+static struct sym oncrpc_syms[] = {
+ { 0x30000000, "CM" },
+ { 0x30000001, "DB" },
+ { 0x30000002, "SND" },
+ { 0x30000003, "WMS" },
+ { 0x30000004, "PDSM" },
+ { 0x30000005, "MISC_MODEM_APIS" },
+ { 0x30000006, "MISC_APPS_APIS" },
+ { 0x30000007, "JOYST" },
+ { 0x30000008, "VJOY" },
+ { 0x30000009, "JOYSTC" },
+ { 0x3000000a, "ADSPRTOSATOM" },
+ { 0x3000000b, "ADSPRTOSMTOA" },
+ { 0x3000000c, "I2C" },
+ { 0x3000000d, "TIME_REMOTE" },
+ { 0x3000000e, "NV" },
+ { 0x3000000f, "CLKRGM_SEC" },
+ { 0x30000010, "RDEVMAP" },
+ { 0x30000011, "FS_RAPI" },
+ { 0x30000012, "PBMLIB" },
+ { 0x30000013, "AUDMGR" },
+ { 0x30000014, "MVS" },
+ { 0x30000015, "DOG_KEEPALIVE" },
+ { 0x30000016, "GSDI_EXP" },
+ { 0x30000017, "AUTH" },
+ { 0x30000018, "NVRUIMI" },
+ { 0x30000019, "MMGSDILIB" },
+ { 0x3000001a, "CHARGER" },
+ { 0x3000001b, "UIM" },
+ { 0x3000001C, "ONCRPCTEST" },
+ { 0x3000001d, "PDSM_ATL" },
+ { 0x3000001e, "FS_XMOUNT" },
+ { 0x3000001f, "SECUTIL " },
+ { 0x30000020, "MCCMEID" },
+ { 0x30000021, "PM_STROBE_FLASH" },
+ { 0x30000022, "DS707_EXTIF" },
+ { 0x30000023, "SMD BRIDGE_MODEM" },
+ { 0x30000024, "SMD PORT_MGR" },
+ { 0x30000025, "BUS_PERF" },
+ { 0x30000026, "BUS_MON" },
+ { 0x30000027, "MC" },
+ { 0x30000028, "MCCAP" },
+ { 0x30000029, "MCCDMA" },
+ { 0x3000002a, "MCCDS" },
+ { 0x3000002b, "MCCSCH" },
+ { 0x3000002c, "MCCSRID" },
+ { 0x3000002d, "SNM" },
+ { 0x3000002e, "MCCSYOBJ" },
+ { 0x3000002f, "DS707_APIS" },
+ { 0x30000030, "DS_MP_SHIM_APPS_ASYNC" },
+ { 0x30000031, "DSRLP_APIS" },
+ { 0x30000032, "RLP_APIS" },
+ { 0x30000033, "DS_MP_SHIM_MODEM" },
+ { 0x30000034, "DSHDR_APIS" },
+ { 0x30000035, "DSHDR_MDM_APIS" },
+ { 0x30000036, "DS_MP_SHIM_APPS" },
+ { 0x30000037, "HDRMC_APIS" },
+ { 0x30000038, "SMD_BRIDGE_MTOA" },
+ { 0x30000039, "SMD_BRIDGE_ATOM" },
+ { 0x3000003a, "DPMAPP_OTG" },
+ { 0x3000003b, "DIAG" },
+ { 0x3000003c, "GSTK_EXP" },
+ { 0x3000003d, "DSBC_MDM_APIS" },
+ { 0x3000003e, "HDRMRLP_MDM_APIS" },
+ { 0x3000003f, "HDRMRLP_APPS_APIS" },
+ { 0x30000040, "HDRMC_MRLP_APIS" },
+ { 0x30000041, "PDCOMM_APP_API" },
+ { 0x30000042, "DSAT_APIS" },
+ { 0x30000043, "MISC_RF_APIS" },
+ { 0x30000044, "CMIPAPP" },
+ { 0x30000045, "DSMP_UMTS_MODEM_APIS" },
+ { 0x30000046, "DSMP_UMTS_APPS_APIS" },
+ { 0x30000047, "DSUCSDMPSHIM" },
+ { 0x30000048, "TIME_REMOTE_ATOM" },
+ { 0x3000004a, "SD" },
+ { 0x3000004b, "MMOC" },
+ { 0x3000004c, "WLAN_ADP_FTM" },
+ { 0x3000004d, "WLAN_CP_CM" },
+ { 0x3000004e, "FTM_WLAN" },
+ { 0x3000004f, "SDCC_CPRM" },
+ { 0x30000050, "CPRMINTERFACE" },
+ { 0x30000051, "DATA_ON_MODEM_MTOA_APIS" },
+ { 0x30000052, "DATA_ON_APPS_ATOM_APIS" },
+ { 0x30000053, "MISC_MODEM_APIS_NONWINMOB" },
+ { 0x30000054, "MISC_APPS_APIS_NONWINMOB" },
+ { 0x30000055, "PMEM_REMOTE" },
+ { 0x30000056, "TCXOMGR" },
+ { 0x30000057, "DSUCSDAPPIF_APIS" },
+ { 0x30000058, "BT" },
+ { 0x30000059, "PD_COMMS_API" },
+ { 0x3000005a, "PD_COMMS_CLIENT_API" },
+ { 0x3000005b, "PDAPI" },
+ { 0x3000005c, "LSA_SUPL_DSM" },
+ { 0x3000005d, "TIME_REMOTE_MTOA" },
+ { 0x3000005e, "FTM_BT" },
+ { 0X3000005f, "DSUCSDAPPIF_APIS" },
+ { 0X30000060, "PMAPP_GEN" },
+ { 0X30000061, "PM_LIB" },
+ { 0X30000062, "KEYPAD" },
+ { 0X30000063, "HSU_APP_APIS" },
+ { 0X30000064, "HSU_MDM_APIS" },
+ { 0X30000065, "ADIE_ADC_REMOTE_ATOM " },
+ { 0X30000066, "TLMM_REMOTE_ATOM" },
+ { 0X30000067, "UI_CALLCTRL" },
+ { 0X30000068, "UIUTILS" },
+ { 0X30000069, "PRL" },
+ { 0X3000006a, "HW" },
+ { 0X3000006b, "OEM_RAPI" },
+ { 0X3000006c, "WMSPM" },
+ { 0X3000006d, "BTPF" },
+ { 0X3000006e, "CLKRGM_SYNC_EVENT" },
+ { 0X3000006f, "USB_APPS_RPC" },
+ { 0X30000070, "USB_MODEM_RPC" },
+ { 0X30000071, "ADC" },
+ { 0X30000072, "CAMERAREMOTED" },
+ { 0X30000073, "SECAPIREMOTED" },
+ { 0X30000074, "DSATAPI" },
+ { 0X30000075, "CLKCTL_RPC" },
+ { 0X30000076, "BREWAPPCOORD" },
+ { 0X30000077, "ALTENVSHELL" },
+ { 0X30000078, "WLAN_TRP_UTILS" },
+ { 0X30000079, "GPIO_RPC" },
+ { 0X3000007a, "PING_RPC" },
+ { 0X3000007b, "DSC_DCM_API" },
+ { 0X3000007c, "L1_DS" },
+ { 0X3000007d, "QCHATPK_APIS" },
+ { 0X3000007e, "GPS_API" },
+ { 0X3000007f, "OSS_RRCASN_REMOTE" },
+ { 0X30000080, "PMAPP_OTG_REMOTE" },
+ { 0X30000081, "PING_MDM_RPC" },
+ { 0X30000082, "PING_KERNEL_RPC" },
+ { 0X30000083, "TIMETICK" },
+ { 0X30000084, "WM_BTHCI_FTM " },
+ { 0X30000085, "WM_BT_PF" },
+ { 0X30000086, "IPA_IPC_APIS" },
+ { 0X30000087, "UKCC_IPC_APIS" },
+ { 0X30000088, "CMIPSMS " },
+ { 0X30000089, "VBATT_REMOTE" },
+ { 0X3000008a, "MFPAL" },
+ { 0X3000008b, "DSUMTSPDPREG" },
+ { 0X3000fe00, "RESTART_DAEMON NUMBER 0" },
+ { 0X3000fe01, "RESTART_DAEMON NUMBER 1" },
+ { 0X3000feff, "RESTART_DAEMON NUMBER 255" },
+ { 0X3000fffe, "BACKWARDS_COMPATIBILITY_IN_RPC_CLNT_LOOKUP" },
+ { 0X3000ffff, "RPC_ROUTER_SERVER_PROGRAM" },
+};
+
+#define ONCRPC_SYM 0
+
+static struct sym_tbl {
+ struct sym *data;
+ int size;
+ struct hlist_head hlist[HSIZE];
+} tbl[] = {
+ { oncrpc_syms, ARRAY_SIZE(oncrpc_syms) },
+};
+
+#define hash(val) (val % HSIZE)
+
+static void init_syms(void)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < ARRAY_SIZE(tbl); ++i)
+ for (j = 0; j < HSIZE; ++j)
+ INIT_HLIST_HEAD(&tbl[i].hlist[j]);
+
+ for (i = 0; i < ARRAY_SIZE(tbl); ++i)
+ for (j = 0; j < tbl[i].size; ++j) {
+ INIT_HLIST_NODE(&tbl[i].data[j].node);
+ hlist_add_head(&tbl[i].data[j].node,
+ &tbl[i].hlist[hash(tbl[i].data[j].val)]);
+ }
+}
+
+static char *find_sym(uint32_t id, uint32_t val)
+{
+ struct hlist_node *n;
+ struct sym *s;
+
+ hlist_for_each(n, &tbl[id].hlist[hash(val)]) {
+ s = hlist_entry(n, struct sym, node);
+ if (s->val == val)
+ return s->str;
+ }
+
+ return 0;
+}
+
+static int dump_servers(char *buf, int max)
+{
+ int i = 0;
+ unsigned long flags;
+ struct rr_server *svr;
+ 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 = find_sym(ONCRPC_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;
+ 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 = find_sym(ONCRPC_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);
+
+ init_syms();
+}
+
+#else
+static void debugfs_init(void) {}
+#endif
+
+
+static int __init rpcrouter_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&msm_smd_channel2_driver);
+ if (ret)
+ return ret;
+
+ debugfs_init();
+
+ return ret;
+}
+
+module_init(rpcrouter_init);
+MODULE_DESCRIPTION("MSM RPC Router");
+MODULE_AUTHOR("San Mehat <san@android.com>");
+MODULE_LICENSE("GPL");