|
@@ -0,0 +1,1274 @@
|
|
|
+/* arch/arm/mach-msm/smd_rpcrouter.c
|
|
|
+ *
|
|
|
+ * Copyright (C) 2007 Google, Inc.
|
|
|
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated.
|
|
|
+ * Author: San Mehat <san@android.com>
|
|
|
+ *
|
|
|
+ * This software is licensed under the terms of the GNU General Public
|
|
|
+ * License version 2, as published by the Free Software Foundation, and
|
|
|
+ * may be copied, distributed, and modified under those terms.
|
|
|
+ *
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+/* TODO: handle cases where smd_write() will tempfail due to full fifo */
|
|
|
+/* TODO: thread priority? schedule a work to bump it? */
|
|
|
+/* TODO: maybe make server_list_lock a mutex */
|
|
|
+/* TODO: pool fragments to avoid kmalloc/kfree churn */
|
|
|
+
|
|
|
+#include <linux/module.h>
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/string.h>
|
|
|
+#include <linux/errno.h>
|
|
|
+#include <linux/cdev.h>
|
|
|
+#include <linux/init.h>
|
|
|
+#include <linux/device.h>
|
|
|
+#include <linux/types.h>
|
|
|
+#include <linux/delay.h>
|
|
|
+#include <linux/fs.h>
|
|
|
+#include <linux/err.h>
|
|
|
+#include <linux/sched.h>
|
|
|
+#include <linux/poll.h>
|
|
|
+#include <linux/wakelock.h>
|
|
|
+#include <asm/uaccess.h>
|
|
|
+#include <asm/byteorder.h>
|
|
|
+#include <linux/platform_device.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
+
|
|
|
+#include <asm/byteorder.h>
|
|
|
+
|
|
|
+#include <mach/msm_smd.h>
|
|
|
+#include "smd_rpcrouter.h"
|
|
|
+
|
|
|
+#define TRACE_R2R_MSG 0
|
|
|
+#define TRACE_R2R_RAW 0
|
|
|
+#define TRACE_RPC_MSG 0
|
|
|
+#define TRACE_NOTIFY_MSG 0
|
|
|
+
|
|
|
+#define MSM_RPCROUTER_DEBUG 0
|
|
|
+#define MSM_RPCROUTER_DEBUG_PKT 0
|
|
|
+#define MSM_RPCROUTER_R2R_DEBUG 0
|
|
|
+#define DUMP_ALL_RECEIVED_HEADERS 0
|
|
|
+
|
|
|
+#define DIAG(x...) printk("[RR] ERROR " x)
|
|
|
+
|
|
|
+#if MSM_RPCROUTER_DEBUG
|
|
|
+#define D(x...) printk(x)
|
|
|
+#else
|
|
|
+#define D(x...) do {} while (0)
|
|
|
+#endif
|
|
|
+
|
|
|
+#if TRACE_R2R_MSG
|
|
|
+#define RR(x...) printk("[RR] "x)
|
|
|
+#else
|
|
|
+#define RR(x...) do {} while (0)
|
|
|
+#endif
|
|
|
+
|
|
|
+#if TRACE_RPC_MSG
|
|
|
+#define IO(x...) printk("[RPC] "x)
|
|
|
+#else
|
|
|
+#define IO(x...) do {} while (0)
|
|
|
+#endif
|
|
|
+
|
|
|
+#if TRACE_NOTIFY_MSG
|
|
|
+#define NTFY(x...) printk(KERN_ERR "[NOTIFY] "x)
|
|
|
+#else
|
|
|
+#define NTFY(x...) do {} while (0)
|
|
|
+#endif
|
|
|
+
|
|
|
+static LIST_HEAD(local_endpoints);
|
|
|
+static LIST_HEAD(remote_endpoints);
|
|
|
+
|
|
|
+static LIST_HEAD(server_list);
|
|
|
+
|
|
|
+static smd_channel_t *smd_channel;
|
|
|
+static int initialized;
|
|
|
+static wait_queue_head_t newserver_wait;
|
|
|
+static wait_queue_head_t smd_wait;
|
|
|
+
|
|
|
+static DEFINE_SPINLOCK(local_endpoints_lock);
|
|
|
+static DEFINE_SPINLOCK(remote_endpoints_lock);
|
|
|
+static DEFINE_SPINLOCK(server_list_lock);
|
|
|
+static DEFINE_SPINLOCK(smd_lock);
|
|
|
+
|
|
|
+static struct workqueue_struct *rpcrouter_workqueue;
|
|
|
+static struct wake_lock rpcrouter_wake_lock;
|
|
|
+static int rpcrouter_need_len;
|
|
|
+
|
|
|
+static atomic_t next_xid = ATOMIC_INIT(1);
|
|
|
+static uint8_t next_pacmarkid;
|
|
|
+
|
|
|
+static void do_read_data(struct work_struct *work);
|
|
|
+static void do_create_pdevs(struct work_struct *work);
|
|
|
+static void do_create_rpcrouter_pdev(struct work_struct *work);
|
|
|
+
|
|
|
+static DECLARE_WORK(work_read_data, do_read_data);
|
|
|
+static DECLARE_WORK(work_create_pdevs, do_create_pdevs);
|
|
|
+static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev);
|
|
|
+
|
|
|
+#define RR_STATE_IDLE 0
|
|
|
+#define RR_STATE_HEADER 1
|
|
|
+#define RR_STATE_BODY 2
|
|
|
+#define RR_STATE_ERROR 3
|
|
|
+
|
|
|
+struct rr_context {
|
|
|
+ struct rr_packet *pkt;
|
|
|
+ uint8_t *ptr;
|
|
|
+ uint32_t state; /* current assembly state */
|
|
|
+ uint32_t count; /* bytes needed in this state */
|
|
|
+};
|
|
|
+
|
|
|
+struct rr_context the_rr_context;
|
|
|
+
|
|
|
+static struct platform_device rpcrouter_pdev = {
|
|
|
+ .name = "oncrpc_router",
|
|
|
+ .id = -1,
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+static int rpcrouter_send_control_msg(union rr_control_msg *msg)
|
|
|
+{
|
|
|
+ struct rr_header hdr;
|
|
|
+ unsigned long flags;
|
|
|
+ int need;
|
|
|
+
|
|
|
+ if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !initialized) {
|
|
|
+ printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, "
|
|
|
+ "router not initialized\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ hdr.version = RPCROUTER_VERSION;
|
|
|
+ hdr.type = msg->cmd;
|
|
|
+ hdr.src_pid = RPCROUTER_PID_LOCAL;
|
|
|
+ hdr.src_cid = RPCROUTER_ROUTER_ADDRESS;
|
|
|
+ hdr.confirm_rx = 0;
|
|
|
+ hdr.size = sizeof(*msg);
|
|
|
+ hdr.dst_pid = 0;
|
|
|
+ hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS;
|
|
|
+
|
|
|
+ /* TODO: what if channel is full? */
|
|
|
+
|
|
|
+ need = sizeof(hdr) + hdr.size;
|
|
|
+ spin_lock_irqsave(&smd_lock, flags);
|
|
|
+ while (smd_write_avail(smd_channel) < need) {
|
|
|
+ spin_unlock_irqrestore(&smd_lock, flags);
|
|
|
+ msleep(250);
|
|
|
+ spin_lock_irqsave(&smd_lock, flags);
|
|
|
+ }
|
|
|
+ smd_write(smd_channel, &hdr, sizeof(hdr));
|
|
|
+ smd_write(smd_channel, msg, hdr.size);
|
|
|
+ spin_unlock_irqrestore(&smd_lock, flags);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct rr_server *rpcrouter_create_server(uint32_t pid,
|
|
|
+ uint32_t cid,
|
|
|
+ uint32_t prog,
|
|
|
+ uint32_t ver)
|
|
|
+{
|
|
|
+ struct rr_server *server;
|
|
|
+ unsigned long flags;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ server = kmalloc(sizeof(struct rr_server), GFP_KERNEL);
|
|
|
+ if (!server)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ memset(server, 0, sizeof(struct rr_server));
|
|
|
+ server->pid = pid;
|
|
|
+ server->cid = cid;
|
|
|
+ server->prog = prog;
|
|
|
+ server->vers = ver;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&server_list_lock, flags);
|
|
|
+ list_add_tail(&server->list, &server_list);
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+
|
|
|
+ if (pid == RPCROUTER_PID_REMOTE) {
|
|
|
+ rc = msm_rpcrouter_create_server_cdev(server);
|
|
|
+ if (rc < 0)
|
|
|
+ goto out_fail;
|
|
|
+ }
|
|
|
+ return server;
|
|
|
+out_fail:
|
|
|
+ spin_lock_irqsave(&server_list_lock, flags);
|
|
|
+ list_del(&server->list);
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+ kfree(server);
|
|
|
+ return ERR_PTR(rc);
|
|
|
+}
|
|
|
+
|
|
|
+static void rpcrouter_destroy_server(struct rr_server *server)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&server_list_lock, flags);
|
|
|
+ list_del(&server->list);
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+ device_destroy(msm_rpcrouter_class, server->device_number);
|
|
|
+ kfree(server);
|
|
|
+}
|
|
|
+
|
|
|
+static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver)
|
|
|
+{
|
|
|
+ struct rr_server *server;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&server_list_lock, flags);
|
|
|
+ list_for_each_entry(server, &server_list, list) {
|
|
|
+ if (server->prog == prog
|
|
|
+ && server->vers == ver) {
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+ return server;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev)
|
|
|
+{
|
|
|
+ struct rr_server *server;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&server_list_lock, flags);
|
|
|
+ list_for_each_entry(server, &server_list, list) {
|
|
|
+ if (server->device_number == dev) {
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+ return server;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev)
|
|
|
+{
|
|
|
+ struct msm_rpc_endpoint *ept;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL);
|
|
|
+ if (!ept)
|
|
|
+ return NULL;
|
|
|
+ memset(ept, 0, sizeof(struct msm_rpc_endpoint));
|
|
|
+
|
|
|
+ /* mark no reply outstanding */
|
|
|
+ ept->reply_pid = 0xffffffff;
|
|
|
+
|
|
|
+ ept->cid = (uint32_t) ept;
|
|
|
+ ept->pid = RPCROUTER_PID_LOCAL;
|
|
|
+ ept->dev = dev;
|
|
|
+
|
|
|
+ if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) {
|
|
|
+ struct rr_server *srv;
|
|
|
+ /*
|
|
|
+ * This is a userspace client which opened
|
|
|
+ * a program/ver devicenode. Bind the client
|
|
|
+ * to that destination
|
|
|
+ */
|
|
|
+ srv = rpcrouter_lookup_server_by_dev(dev);
|
|
|
+ /* TODO: bug? really? */
|
|
|
+ BUG_ON(!srv);
|
|
|
+
|
|
|
+ ept->dst_pid = srv->pid;
|
|
|
+ ept->dst_cid = srv->cid;
|
|
|
+ ept->dst_prog = cpu_to_be32(srv->prog);
|
|
|
+ ept->dst_vers = cpu_to_be32(srv->vers);
|
|
|
+
|
|
|
+ D("Creating local ept %p @ %08x:%08x\n", ept, srv->prog, srv->vers);
|
|
|
+ } else {
|
|
|
+ /* mark not connected */
|
|
|
+ ept->dst_pid = 0xffffffff;
|
|
|
+ D("Creating a master local ept %p\n", ept);
|
|
|
+ }
|
|
|
+
|
|
|
+ init_waitqueue_head(&ept->wait_q);
|
|
|
+ INIT_LIST_HEAD(&ept->read_q);
|
|
|
+ spin_lock_init(&ept->read_q_lock);
|
|
|
+ wake_lock_init(&ept->read_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_read");
|
|
|
+ INIT_LIST_HEAD(&ept->incomplete);
|
|
|
+
|
|
|
+ spin_lock_irqsave(&local_endpoints_lock, flags);
|
|
|
+ list_add_tail(&ept->list, &local_endpoints);
|
|
|
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
|
|
|
+ return ept;
|
|
|
+}
|
|
|
+
|
|
|
+int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ union rr_control_msg msg;
|
|
|
+
|
|
|
+ msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT;
|
|
|
+ msg.cli.pid = ept->pid;
|
|
|
+ msg.cli.cid = ept->cid;
|
|
|
+
|
|
|
+ RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid);
|
|
|
+ rc = rpcrouter_send_control_msg(&msg);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ wake_lock_destroy(&ept->read_q_wake_lock);
|
|
|
+ list_del(&ept->list);
|
|
|
+ kfree(ept);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int rpcrouter_create_remote_endpoint(uint32_t cid)
|
|
|
+{
|
|
|
+ struct rr_remote_endpoint *new_c;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL);
|
|
|
+ if (!new_c)
|
|
|
+ return -ENOMEM;
|
|
|
+ memset(new_c, 0, sizeof(struct rr_remote_endpoint));
|
|
|
+
|
|
|
+ new_c->cid = cid;
|
|
|
+ new_c->pid = RPCROUTER_PID_REMOTE;
|
|
|
+ init_waitqueue_head(&new_c->quota_wait);
|
|
|
+ spin_lock_init(&new_c->quota_lock);
|
|
|
+
|
|
|
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
|
|
|
+ list_add_tail(&new_c->list, &remote_endpoints);
|
|
|
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid)
|
|
|
+{
|
|
|
+ struct msm_rpc_endpoint *ept;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&local_endpoints_lock, flags);
|
|
|
+ list_for_each_entry(ept, &local_endpoints, list) {
|
|
|
+ if (ept->cid == cid) {
|
|
|
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
|
|
|
+ return ept;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t cid)
|
|
|
+{
|
|
|
+ struct rr_remote_endpoint *ept;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
|
|
|
+ list_for_each_entry(ept, &remote_endpoints, list) {
|
|
|
+ if (ept->cid == cid) {
|
|
|
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
|
|
|
+ return ept;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static int process_control_msg(union rr_control_msg *msg, int len)
|
|
|
+{
|
|
|
+ union rr_control_msg ctl;
|
|
|
+ struct rr_server *server;
|
|
|
+ struct rr_remote_endpoint *r_ept;
|
|
|
+ int rc = 0;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ if (len != sizeof(*msg)) {
|
|
|
+ printk(KERN_ERR "rpcrouter: r2r msg size %d != %d\n",
|
|
|
+ len, sizeof(*msg));
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (msg->cmd) {
|
|
|
+ case RPCROUTER_CTRL_CMD_HELLO:
|
|
|
+ RR("o HELLO\n");
|
|
|
+
|
|
|
+ RR("x HELLO\n");
|
|
|
+ memset(&ctl, 0, sizeof(ctl));
|
|
|
+ ctl.cmd = RPCROUTER_CTRL_CMD_HELLO;
|
|
|
+ rpcrouter_send_control_msg(&ctl);
|
|
|
+
|
|
|
+ initialized = 1;
|
|
|
+
|
|
|
+ /* Send list of servers one at a time */
|
|
|
+ ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
|
|
|
+
|
|
|
+ /* TODO: long time to hold a spinlock... */
|
|
|
+ spin_lock_irqsave(&server_list_lock, flags);
|
|
|
+ list_for_each_entry(server, &server_list, list) {
|
|
|
+ ctl.srv.pid = server->pid;
|
|
|
+ ctl.srv.cid = server->cid;
|
|
|
+ ctl.srv.prog = server->prog;
|
|
|
+ ctl.srv.vers = server->vers;
|
|
|
+
|
|
|
+ RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
|
|
|
+ server->pid, server->cid,
|
|
|
+ server->prog, server->vers);
|
|
|
+
|
|
|
+ rpcrouter_send_control_msg(&ctl);
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+
|
|
|
+ queue_work(rpcrouter_workqueue, &work_create_rpcrouter_pdev);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case RPCROUTER_CTRL_CMD_RESUME_TX:
|
|
|
+ RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
|
|
|
+
|
|
|
+ r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
|
|
|
+ if (!r_ept) {
|
|
|
+ printk(KERN_ERR
|
|
|
+ "rpcrouter: Unable to resume client\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ spin_lock_irqsave(&r_ept->quota_lock, flags);
|
|
|
+ r_ept->tx_quota_cntr = 0;
|
|
|
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
|
|
|
+ wake_up(&r_ept->quota_wait);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case RPCROUTER_CTRL_CMD_NEW_SERVER:
|
|
|
+ RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
|
|
|
+ msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers);
|
|
|
+
|
|
|
+ server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
|
|
|
+
|
|
|
+ if (!server) {
|
|
|
+ server = rpcrouter_create_server(
|
|
|
+ msg->srv.pid, msg->srv.cid,
|
|
|
+ msg->srv.prog, msg->srv.vers);
|
|
|
+ if (!server)
|
|
|
+ return -ENOMEM;
|
|
|
+ /*
|
|
|
+ * XXX: Verify that its okay to add the
|
|
|
+ * client to our remote client list
|
|
|
+ * if we get a NEW_SERVER notification
|
|
|
+ */
|
|
|
+ if (!rpcrouter_lookup_remote_endpoint(msg->srv.cid)) {
|
|
|
+ rc = rpcrouter_create_remote_endpoint(
|
|
|
+ msg->srv.cid);
|
|
|
+ if (rc < 0)
|
|
|
+ printk(KERN_ERR
|
|
|
+ "rpcrouter:Client create"
|
|
|
+ "error (%d)\n", rc);
|
|
|
+ }
|
|
|
+ schedule_work(&work_create_pdevs);
|
|
|
+ wake_up(&newserver_wait);
|
|
|
+ } else {
|
|
|
+ if ((server->pid == msg->srv.pid) &&
|
|
|
+ (server->cid == msg->srv.cid)) {
|
|
|
+ printk(KERN_ERR "rpcrouter: Duplicate svr\n");
|
|
|
+ } else {
|
|
|
+ server->pid = msg->srv.pid;
|
|
|
+ server->cid = msg->srv.cid;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case RPCROUTER_CTRL_CMD_REMOVE_SERVER:
|
|
|
+ RR("o REMOVE_SERVER prog=%08x:%d\n",
|
|
|
+ msg->srv.prog, msg->srv.vers);
|
|
|
+ server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
|
|
|
+ if (server)
|
|
|
+ rpcrouter_destroy_server(server);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case RPCROUTER_CTRL_CMD_REMOVE_CLIENT:
|
|
|
+ RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
|
|
|
+ if (msg->cli.pid != RPCROUTER_PID_REMOTE) {
|
|
|
+ printk(KERN_ERR
|
|
|
+ "rpcrouter: Denying remote removal of "
|
|
|
+ "local client\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
|
|
|
+ if (r_ept) {
|
|
|
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
|
|
|
+ list_del(&r_ept->list);
|
|
|
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
|
|
|
+ kfree(r_ept);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Notify local clients of this event */
|
|
|
+ printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n");
|
|
|
+ rc = -ENOSYS;
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ RR("o UNKNOWN(%08x)\n", msg->cmd);
|
|
|
+ rc = -ENOSYS;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static void do_create_rpcrouter_pdev(struct work_struct *work)
|
|
|
+{
|
|
|
+ platform_device_register(&rpcrouter_pdev);
|
|
|
+}
|
|
|
+
|
|
|
+static void do_create_pdevs(struct work_struct *work)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ struct rr_server *server;
|
|
|
+
|
|
|
+ /* TODO: race if destroyed while being registered */
|
|
|
+ spin_lock_irqsave(&server_list_lock, flags);
|
|
|
+ list_for_each_entry(server, &server_list, list) {
|
|
|
+ if (server->pid == RPCROUTER_PID_REMOTE) {
|
|
|
+ if (server->pdev_name[0] == 0) {
|
|
|
+ spin_unlock_irqrestore(&server_list_lock,
|
|
|
+ flags);
|
|
|
+ msm_rpcrouter_create_server_pdev(server);
|
|
|
+ schedule_work(&work_create_pdevs);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+}
|
|
|
+
|
|
|
+static void rpcrouter_smdnotify(void *_dev, unsigned event)
|
|
|
+{
|
|
|
+ if (event != SMD_EVENT_DATA)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (smd_read_avail(smd_channel) >= rpcrouter_need_len)
|
|
|
+ wake_lock(&rpcrouter_wake_lock);
|
|
|
+ wake_up(&smd_wait);
|
|
|
+}
|
|
|
+
|
|
|
+static void *rr_malloc(unsigned sz)
|
|
|
+{
|
|
|
+ void *ptr = kmalloc(sz, GFP_KERNEL);
|
|
|
+ if (ptr)
|
|
|
+ return ptr;
|
|
|
+
|
|
|
+ printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz);
|
|
|
+ do {
|
|
|
+ ptr = kmalloc(sz, GFP_KERNEL);
|
|
|
+ } while (!ptr);
|
|
|
+
|
|
|
+ return ptr;
|
|
|
+}
|
|
|
+
|
|
|
+/* TODO: deal with channel teardown / restore */
|
|
|
+static int rr_read(void *data, int len)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ unsigned long flags;
|
|
|
+// printk("rr_read() %d\n", len);
|
|
|
+ for(;;) {
|
|
|
+ spin_lock_irqsave(&smd_lock, flags);
|
|
|
+ if (smd_read_avail(smd_channel) >= len) {
|
|
|
+ rc = smd_read(smd_channel, data, len);
|
|
|
+ spin_unlock_irqrestore(&smd_lock, flags);
|
|
|
+ if (rc == len)
|
|
|
+ return 0;
|
|
|
+ else
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+ rpcrouter_need_len = len;
|
|
|
+ wake_unlock(&rpcrouter_wake_lock);
|
|
|
+ spin_unlock_irqrestore(&smd_lock, flags);
|
|
|
+
|
|
|
+// printk("rr_read: waiting (%d)\n", len);
|
|
|
+ wait_event(smd_wait, smd_read_avail(smd_channel) >= len);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static uint32_t r2r_buf[RPCROUTER_MSGSIZE_MAX];
|
|
|
+
|
|
|
+static void do_read_data(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct rr_header hdr;
|
|
|
+ struct rr_packet *pkt;
|
|
|
+ struct rr_fragment *frag;
|
|
|
+ struct msm_rpc_endpoint *ept;
|
|
|
+ uint32_t pm, mid;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ if (rr_read(&hdr, sizeof(hdr)))
|
|
|
+ goto fail_io;
|
|
|
+
|
|
|
+#if TRACE_R2R_RAW
|
|
|
+ RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
|
|
|
+ hdr.version, hdr.type, hdr.src_pid, hdr.src_cid,
|
|
|
+ hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid);
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (hdr.version != RPCROUTER_VERSION) {
|
|
|
+ DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION);
|
|
|
+ goto fail_data;
|
|
|
+ }
|
|
|
+ if (hdr.size > RPCROUTER_MSGSIZE_MAX) {
|
|
|
+ DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX);
|
|
|
+ goto fail_data;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) {
|
|
|
+ if (rr_read(r2r_buf, hdr.size))
|
|
|
+ goto fail_io;
|
|
|
+ process_control_msg((void*) r2r_buf, hdr.size);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hdr.size < sizeof(pm)) {
|
|
|
+ DIAG("runt packet (no pacmark)\n");
|
|
|
+ goto fail_data;
|
|
|
+ }
|
|
|
+ if (rr_read(&pm, sizeof(pm)))
|
|
|
+ goto fail_io;
|
|
|
+
|
|
|
+ hdr.size -= sizeof(pm);
|
|
|
+
|
|
|
+ frag = rr_malloc(hdr.size + sizeof(*frag));
|
|
|
+ frag->next = NULL;
|
|
|
+ frag->length = hdr.size;
|
|
|
+ if (rr_read(frag->data, hdr.size))
|
|
|
+ goto fail_io;
|
|
|
+
|
|
|
+ ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid);
|
|
|
+ if (!ept) {
|
|
|
+ DIAG("no local ept for cid %08x\n", hdr.dst_cid);
|
|
|
+ kfree(frag);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* See if there is already a partial packet that matches our mid
|
|
|
+ * and if so, append this fragment to that packet.
|
|
|
+ */
|
|
|
+ mid = PACMARK_MID(pm);
|
|
|
+ list_for_each_entry(pkt, &ept->incomplete, list) {
|
|
|
+ if (pkt->mid == mid) {
|
|
|
+ pkt->last->next = frag;
|
|
|
+ pkt->last = frag;
|
|
|
+ pkt->length += frag->length;
|
|
|
+ if (PACMARK_LAST(pm)) {
|
|
|
+ list_del(&pkt->list);
|
|
|
+ goto packet_complete;
|
|
|
+ }
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* This mid is new -- create a packet for it, and put it on
|
|
|
+ * the incomplete list if this fragment is not a last fragment,
|
|
|
+ * otherwise put it on the read queue.
|
|
|
+ */
|
|
|
+ pkt = rr_malloc(sizeof(struct rr_packet));
|
|
|
+ pkt->first = frag;
|
|
|
+ pkt->last = frag;
|
|
|
+ memcpy(&pkt->hdr, &hdr, sizeof(hdr));
|
|
|
+ pkt->mid = mid;
|
|
|
+ pkt->length = frag->length;
|
|
|
+ if (!PACMARK_LAST(pm)) {
|
|
|
+ list_add_tail(&pkt->list, &ept->incomplete);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+packet_complete:
|
|
|
+ spin_lock_irqsave(&ept->read_q_lock, flags);
|
|
|
+ wake_lock(&ept->read_q_wake_lock);
|
|
|
+ list_add_tail(&pkt->list, &ept->read_q);
|
|
|
+ wake_up(&ept->wait_q);
|
|
|
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
|
|
|
+done:
|
|
|
+
|
|
|
+ if (hdr.confirm_rx) {
|
|
|
+ union rr_control_msg msg;
|
|
|
+
|
|
|
+ msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX;
|
|
|
+ msg.cli.pid = hdr.dst_pid;
|
|
|
+ msg.cli.cid = hdr.dst_cid;
|
|
|
+
|
|
|
+ RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid);
|
|
|
+ rpcrouter_send_control_msg(&msg);
|
|
|
+ }
|
|
|
+
|
|
|
+ queue_work(rpcrouter_workqueue, &work_read_data);
|
|
|
+ return;
|
|
|
+
|
|
|
+fail_io:
|
|
|
+fail_data:
|
|
|
+ printk(KERN_ERR "rpc_router has died\n");
|
|
|
+ wake_unlock(&rpcrouter_wake_lock);
|
|
|
+}
|
|
|
+
|
|
|
+void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog,
|
|
|
+ uint32_t vers, uint32_t proc)
|
|
|
+{
|
|
|
+ memset(hdr, 0, sizeof(struct rpc_request_hdr));
|
|
|
+ hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
|
|
|
+ hdr->rpc_vers = cpu_to_be32(2);
|
|
|
+ hdr->prog = cpu_to_be32(prog);
|
|
|
+ hdr->vers = cpu_to_be32(vers);
|
|
|
+ hdr->procedure = cpu_to_be32(proc);
|
|
|
+}
|
|
|
+
|
|
|
+struct msm_rpc_endpoint *msm_rpc_open(void)
|
|
|
+{
|
|
|
+ struct msm_rpc_endpoint *ept;
|
|
|
+
|
|
|
+ ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0));
|
|
|
+ if (ept == NULL)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ return ept;
|
|
|
+}
|
|
|
+
|
|
|
+int msm_rpc_close(struct msm_rpc_endpoint *ept)
|
|
|
+{
|
|
|
+ return msm_rpcrouter_destroy_local_endpoint(ept);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(msm_rpc_close);
|
|
|
+
|
|
|
+int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count)
|
|
|
+{
|
|
|
+ struct rr_header hdr;
|
|
|
+ uint32_t pacmark;
|
|
|
+ struct rpc_request_hdr *rq = buffer;
|
|
|
+ struct rr_remote_endpoint *r_ept;
|
|
|
+ unsigned long flags;
|
|
|
+ int needed;
|
|
|
+ DEFINE_WAIT(__wait);
|
|
|
+
|
|
|
+ /* TODO: fragmentation for large outbound packets */
|
|
|
+ if (count > (RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t)) || !count)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* snoop the RPC packet and enforce permissions */
|
|
|
+
|
|
|
+ /* has to have at least the xid and type fields */
|
|
|
+ if (count < (sizeof(uint32_t) * 2)) {
|
|
|
+ printk(KERN_ERR "rr_write: rejecting runt packet\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rq->type == 0) {
|
|
|
+ /* RPC CALL */
|
|
|
+ if (count < (sizeof(uint32_t) * 6)) {
|
|
|
+ printk(KERN_ERR
|
|
|
+ "rr_write: rejecting runt call packet\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ if (ept->dst_pid == 0xffffffff) {
|
|
|
+ printk(KERN_ERR "rr_write: not connected\n");
|
|
|
+ return -ENOTCONN;
|
|
|
+ }
|
|
|
+
|
|
|
+#if CONFIG_MSM_AMSS_VERSION >= 6350
|
|
|
+ if ((ept->dst_prog != rq->prog) ||
|
|
|
+ !msm_rpc_is_compatible_version(
|
|
|
+ be32_to_cpu(ept->dst_vers),
|
|
|
+ be32_to_cpu(rq->vers))) {
|
|
|
+#else
|
|
|
+ if (ept->dst_prog != rq->prog || ept->dst_vers != rq->vers) {
|
|
|
+#endif
|
|
|
+ printk(KERN_ERR
|
|
|
+ "rr_write: cannot write to %08x:%d "
|
|
|
+ "(bound to %08x:%d)\n",
|
|
|
+ be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
|
|
|
+ be32_to_cpu(ept->dst_prog),
|
|
|
+ be32_to_cpu(ept->dst_vers));
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ hdr.dst_pid = ept->dst_pid;
|
|
|
+ hdr.dst_cid = ept->dst_cid;
|
|
|
+ IO("CALL on ept %p to %08x:%08x @ %d:%08x (%d bytes) (xid %x proc %x)\n",
|
|
|
+ ept,
|
|
|
+ be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
|
|
|
+ ept->dst_pid, ept->dst_cid, count,
|
|
|
+ be32_to_cpu(rq->xid), be32_to_cpu(rq->procedure));
|
|
|
+ } else {
|
|
|
+ /* RPC REPLY */
|
|
|
+ /* TODO: locking */
|
|
|
+ if (ept->reply_pid == 0xffffffff) {
|
|
|
+ printk(KERN_ERR
|
|
|
+ "rr_write: rejecting unexpected reply\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ if (ept->reply_xid != rq->xid) {
|
|
|
+ printk(KERN_ERR
|
|
|
+ "rr_write: rejecting packet w/ bad xid\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ hdr.dst_pid = ept->reply_pid;
|
|
|
+ hdr.dst_cid = ept->reply_cid;
|
|
|
+
|
|
|
+ /* consume this reply */
|
|
|
+ ept->reply_pid = 0xffffffff;
|
|
|
+
|
|
|
+ IO("REPLY on ept %p to xid=%d @ %d:%08x (%d bytes)\n",
|
|
|
+ ept,
|
|
|
+ be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count);
|
|
|
+ }
|
|
|
+
|
|
|
+ r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_cid);
|
|
|
+
|
|
|
+ if (!r_ept) {
|
|
|
+ printk(KERN_ERR
|
|
|
+ "msm_rpc_write(): No route to ept "
|
|
|
+ "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid);
|
|
|
+ return -EHOSTUNREACH;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Create routing header */
|
|
|
+ hdr.type = RPCROUTER_CTRL_CMD_DATA;
|
|
|
+ hdr.version = RPCROUTER_VERSION;
|
|
|
+ hdr.src_pid = ept->pid;
|
|
|
+ hdr.src_cid = ept->cid;
|
|
|
+ hdr.confirm_rx = 0;
|
|
|
+ hdr.size = count + sizeof(uint32_t);
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ prepare_to_wait(&r_ept->quota_wait, &__wait,
|
|
|
+ TASK_INTERRUPTIBLE);
|
|
|
+ spin_lock_irqsave(&r_ept->quota_lock, flags);
|
|
|
+ if (r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA)
|
|
|
+ break;
|
|
|
+ if (signal_pending(current) &&
|
|
|
+ (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))
|
|
|
+ break;
|
|
|
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
|
|
|
+ schedule();
|
|
|
+ }
|
|
|
+ finish_wait(&r_ept->quota_wait, &__wait);
|
|
|
+
|
|
|
+ if (signal_pending(current) &&
|
|
|
+ (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) {
|
|
|
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
|
|
|
+ return -ERESTARTSYS;
|
|
|
+ }
|
|
|
+ r_ept->tx_quota_cntr++;
|
|
|
+ if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA)
|
|
|
+ hdr.confirm_rx = 1;
|
|
|
+
|
|
|
+ /* bump pacmark while interrupts disabled to avoid race
|
|
|
+ * probably should be atomic op instead
|
|
|
+ */
|
|
|
+ pacmark = PACMARK(count, ++next_pacmarkid, 0, 1);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
|
|
|
+
|
|
|
+ spin_lock_irqsave(&smd_lock, flags);
|
|
|
+
|
|
|
+ needed = sizeof(hdr) + hdr.size;
|
|
|
+ while (smd_write_avail(smd_channel) < needed) {
|
|
|
+ spin_unlock_irqrestore(&smd_lock, flags);
|
|
|
+ msleep(250);
|
|
|
+ spin_lock_irqsave(&smd_lock, flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* TODO: deal with full fifo */
|
|
|
+ smd_write(smd_channel, &hdr, sizeof(hdr));
|
|
|
+ smd_write(smd_channel, &pacmark, sizeof(pacmark));
|
|
|
+ smd_write(smd_channel, buffer, count);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&smd_lock, flags);
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(msm_rpc_write);
|
|
|
+
|
|
|
+/*
|
|
|
+ * NOTE: It is the responsibility of the caller to kfree buffer
|
|
|
+ */
|
|
|
+int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer,
|
|
|
+ unsigned user_len, long timeout)
|
|
|
+{
|
|
|
+ struct rr_fragment *frag, *next;
|
|
|
+ char *buf;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ rc = __msm_rpc_read(ept, &frag, user_len, timeout);
|
|
|
+ if (rc <= 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ /* single-fragment messages conveniently can be
|
|
|
+ * returned as-is (the buffer is at the front)
|
|
|
+ */
|
|
|
+ if (frag->next == 0) {
|
|
|
+ *buffer = (void*) frag;
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* multi-fragment messages, we have to do it the
|
|
|
+ * hard way, which is rather disgusting right now
|
|
|
+ */
|
|
|
+ buf = rr_malloc(rc);
|
|
|
+ *buffer = buf;
|
|
|
+
|
|
|
+ while (frag != NULL) {
|
|
|
+ memcpy(buf, frag->data, frag->length);
|
|
|
+ next = frag->next;
|
|
|
+ buf += frag->length;
|
|
|
+ kfree(frag);
|
|
|
+ frag = next;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc,
|
|
|
+ void *_request, int request_size,
|
|
|
+ long timeout)
|
|
|
+{
|
|
|
+ return msm_rpc_call_reply(ept, proc,
|
|
|
+ _request, request_size,
|
|
|
+ NULL, 0, timeout);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(msm_rpc_call);
|
|
|
+
|
|
|
+int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc,
|
|
|
+ void *_request, int request_size,
|
|
|
+ void *_reply, int reply_size,
|
|
|
+ long timeout)
|
|
|
+{
|
|
|
+ struct rpc_request_hdr *req = _request;
|
|
|
+ struct rpc_reply_hdr *reply;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ if (request_size < sizeof(*req))
|
|
|
+ return -ETOOSMALL;
|
|
|
+
|
|
|
+ if (ept->dst_pid == 0xffffffff)
|
|
|
+ return -ENOTCONN;
|
|
|
+
|
|
|
+ /* We can't use msm_rpc_setup_req() here, because dst_prog and
|
|
|
+ * dst_vers here are already in BE.
|
|
|
+ */
|
|
|
+ memset(req, 0, sizeof(*req));
|
|
|
+ req->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
|
|
|
+ req->rpc_vers = cpu_to_be32(2);
|
|
|
+ req->prog = ept->dst_prog;
|
|
|
+ req->vers = ept->dst_vers;
|
|
|
+ req->procedure = cpu_to_be32(proc);
|
|
|
+
|
|
|
+ rc = msm_rpc_write(ept, req, request_size);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ rc = msm_rpc_read(ept, (void*) &reply, -1, timeout);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+ if (rc < (3 * sizeof(uint32_t))) {
|
|
|
+ rc = -EIO;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /* we should not get CALL packets -- ignore them */
|
|
|
+ if (reply->type == 0) {
|
|
|
+ kfree(reply);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* If an earlier call timed out, we could get the (no
|
|
|
+ * longer wanted) reply for it. Ignore replies that
|
|
|
+ * we don't expect.
|
|
|
+ */
|
|
|
+ if (reply->xid != req->xid) {
|
|
|
+ kfree(reply);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (reply->reply_stat != 0) {
|
|
|
+ rc = -EPERM;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (reply->data.acc_hdr.accept_stat != 0) {
|
|
|
+ rc = -EINVAL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (_reply == NULL) {
|
|
|
+ rc = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (rc > reply_size) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ } else {
|
|
|
+ memcpy(_reply, reply, rc);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ kfree(reply);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(msm_rpc_call_reply);
|
|
|
+
|
|
|
+
|
|
|
+static inline int ept_packet_available(struct msm_rpc_endpoint *ept)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ int ret;
|
|
|
+ spin_lock_irqsave(&ept->read_q_lock, flags);
|
|
|
+ ret = !list_empty(&ept->read_q);
|
|
|
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+int __msm_rpc_read(struct msm_rpc_endpoint *ept,
|
|
|
+ struct rr_fragment **frag_ret,
|
|
|
+ unsigned len, long timeout)
|
|
|
+{
|
|
|
+ struct rr_packet *pkt;
|
|
|
+ struct rpc_request_hdr *rq;
|
|
|
+ DEFINE_WAIT(__wait);
|
|
|
+ unsigned long flags;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ IO("READ on ept %p\n", ept);
|
|
|
+
|
|
|
+ if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) {
|
|
|
+ if (timeout < 0) {
|
|
|
+ wait_event(ept->wait_q, ept_packet_available(ept));
|
|
|
+ } else {
|
|
|
+ rc = wait_event_timeout(
|
|
|
+ ept->wait_q, ept_packet_available(ept),
|
|
|
+ timeout);
|
|
|
+ if (rc == 0)
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (timeout < 0) {
|
|
|
+ rc = wait_event_interruptible(
|
|
|
+ ept->wait_q, ept_packet_available(ept));
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+ } else {
|
|
|
+ rc = wait_event_interruptible_timeout(
|
|
|
+ ept->wait_q, ept_packet_available(ept),
|
|
|
+ timeout);
|
|
|
+ if (rc == 0)
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock_irqsave(&ept->read_q_lock, flags);
|
|
|
+ if (list_empty(&ept->read_q)) {
|
|
|
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
|
|
|
+ return -EAGAIN;
|
|
|
+ }
|
|
|
+ pkt = list_first_entry(&ept->read_q, struct rr_packet, list);
|
|
|
+ if (pkt->length > len) {
|
|
|
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
|
|
|
+ return -ETOOSMALL;
|
|
|
+ }
|
|
|
+ list_del(&pkt->list);
|
|
|
+ if (list_empty(&ept->read_q))
|
|
|
+ wake_unlock(&ept->read_q_wake_lock);
|
|
|
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
|
|
|
+
|
|
|
+ rc = pkt->length;
|
|
|
+
|
|
|
+ *frag_ret = pkt->first;
|
|
|
+ rq = (void*) pkt->first->data;
|
|
|
+ if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) {
|
|
|
+ IO("READ on ept %p is a CALL on %08x:%08x proc %d xid %d\n",
|
|
|
+ ept, be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
|
|
|
+ be32_to_cpu(rq->procedure),
|
|
|
+ be32_to_cpu(rq->xid));
|
|
|
+ /* RPC CALL */
|
|
|
+ if (ept->reply_pid != 0xffffffff) {
|
|
|
+ printk(KERN_WARNING
|
|
|
+ "rr_read: lost previous reply xid...\n");
|
|
|
+ }
|
|
|
+ /* TODO: locking? */
|
|
|
+ ept->reply_pid = pkt->hdr.src_pid;
|
|
|
+ ept->reply_cid = pkt->hdr.src_cid;
|
|
|
+ ept->reply_xid = rq->xid;
|
|
|
+ }
|
|
|
+#if TRACE_RPC_MSG
|
|
|
+ else if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 1))
|
|
|
+ IO("READ on ept %p is a REPLY\n", ept);
|
|
|
+ else IO("READ on ept %p (%d bytes)\n", ept, rc);
|
|
|
+#endif
|
|
|
+
|
|
|
+ kfree(pkt);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+#if CONFIG_MSM_AMSS_VERSION >= 6350
|
|
|
+int msm_rpc_is_compatible_version(uint32_t server_version,
|
|
|
+ uint32_t client_version)
|
|
|
+{
|
|
|
+ if ((server_version & RPC_VERSION_MODE_MASK) !=
|
|
|
+ (client_version & RPC_VERSION_MODE_MASK))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (server_version & RPC_VERSION_MODE_MASK)
|
|
|
+ return server_version == client_version;
|
|
|
+
|
|
|
+ return ((server_version & RPC_VERSION_MAJOR_MASK) ==
|
|
|
+ (client_version & RPC_VERSION_MAJOR_MASK)) &&
|
|
|
+ ((server_version & RPC_VERSION_MINOR_MASK) >=
|
|
|
+ (client_version & RPC_VERSION_MINOR_MASK));
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(msm_rpc_is_compatible_version);
|
|
|
+
|
|
|
+static int msm_rpc_get_compatible_server(uint32_t prog,
|
|
|
+ uint32_t ver,
|
|
|
+ uint32_t *found_vers)
|
|
|
+{
|
|
|
+ struct rr_server *server;
|
|
|
+ unsigned long flags;
|
|
|
+ if (found_vers == NULL)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&server_list_lock, flags);
|
|
|
+ list_for_each_entry(server, &server_list, list) {
|
|
|
+ if ((server->prog == prog) &&
|
|
|
+ msm_rpc_is_compatible_version(server->vers, ver)) {
|
|
|
+ *found_vers = server->vers;
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&server_list_lock, flags);
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags)
|
|
|
+{
|
|
|
+ struct msm_rpc_endpoint *ept;
|
|
|
+ struct rr_server *server;
|
|
|
+
|
|
|
+#if CONFIG_MSM_AMSS_VERSION >= 6350
|
|
|
+ if (!(vers & RPC_VERSION_MODE_MASK)) {
|
|
|
+ uint32_t found_vers;
|
|
|
+ if (msm_rpc_get_compatible_server(prog, vers, &found_vers) < 0)
|
|
|
+ return ERR_PTR(-EHOSTUNREACH);
|
|
|
+ if (found_vers != vers) {
|
|
|
+ D("RPC using new version %08x:{%08x --> %08x}\n",
|
|
|
+ prog, vers, found_vers);
|
|
|
+ vers = found_vers;
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ server = rpcrouter_lookup_server(prog, vers);
|
|
|
+ if (!server)
|
|
|
+ return ERR_PTR(-EHOSTUNREACH);
|
|
|
+
|
|
|
+ ept = msm_rpc_open();
|
|
|
+ if (IS_ERR(ept))
|
|
|
+ return ept;
|
|
|
+
|
|
|
+ ept->flags = flags;
|
|
|
+ ept->dst_pid = server->pid;
|
|
|
+ ept->dst_cid = server->cid;
|
|
|
+ ept->dst_prog = cpu_to_be32(prog);
|
|
|
+ ept->dst_vers = cpu_to_be32(vers);
|
|
|
+
|
|
|
+ return ept;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(msm_rpc_connect);
|
|
|
+
|
|
|
+uint32_t msm_rpc_get_vers(struct msm_rpc_endpoint *ept)
|
|
|
+{
|
|
|
+ return be32_to_cpu(ept->dst_vers);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(msm_rpc_get_vers);
|
|
|
+
|
|
|
+/* TODO: permission check? */
|
|
|
+int msm_rpc_register_server(struct msm_rpc_endpoint *ept,
|
|
|
+ uint32_t prog, uint32_t vers)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ union rr_control_msg msg;
|
|
|
+ struct rr_server *server;
|
|
|
+
|
|
|
+ server = rpcrouter_create_server(ept->pid, ept->cid,
|
|
|
+ prog, vers);
|
|
|
+ if (!server)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
|
|
|
+ msg.srv.pid = ept->pid;
|
|
|
+ msg.srv.cid = ept->cid;
|
|
|
+ msg.srv.prog = prog;
|
|
|
+ msg.srv.vers = vers;
|
|
|
+
|
|
|
+ RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
|
|
|
+ ept->pid, ept->cid, prog, vers);
|
|
|
+
|
|
|
+ rc = rpcrouter_send_control_msg(&msg);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* TODO: permission check -- disallow unreg of somebody else's server */
|
|
|
+int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept,
|
|
|
+ uint32_t prog, uint32_t vers)
|
|
|
+{
|
|
|
+ struct rr_server *server;
|
|
|
+ server = rpcrouter_lookup_server(prog, vers);
|
|
|
+
|
|
|
+ if (!server)
|
|
|
+ return -ENOENT;
|
|
|
+ rpcrouter_destroy_server(server);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int msm_rpcrouter_probe(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ /* Initialize what we need to start processing */
|
|
|
+ INIT_LIST_HEAD(&local_endpoints);
|
|
|
+ INIT_LIST_HEAD(&remote_endpoints);
|
|
|
+
|
|
|
+ init_waitqueue_head(&newserver_wait);
|
|
|
+ init_waitqueue_head(&smd_wait);
|
|
|
+ wake_lock_init(&rpcrouter_wake_lock, WAKE_LOCK_SUSPEND, "SMD_RPCCALL");
|
|
|
+
|
|
|
+ rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter");
|
|
|
+ if (!rpcrouter_workqueue)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ rc = msm_rpcrouter_init_devices();
|
|
|
+ if (rc < 0)
|
|
|
+ goto fail_destroy_workqueue;
|
|
|
+
|
|
|
+ /* Open up SMD channel 2 */
|
|
|
+ initialized = 0;
|
|
|
+ rc = smd_open("SMD_RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify);
|
|
|
+ if (rc < 0)
|
|
|
+ goto fail_remove_devices;
|
|
|
+
|
|
|
+ queue_work(rpcrouter_workqueue, &work_read_data);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ fail_remove_devices:
|
|
|
+ msm_rpcrouter_exit_devices();
|
|
|
+ fail_destroy_workqueue:
|
|
|
+ destroy_workqueue(rpcrouter_workqueue);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static struct platform_driver msm_smd_channel2_driver = {
|
|
|
+ .probe = msm_rpcrouter_probe,
|
|
|
+ .driver = {
|
|
|
+ .name = "SMD_RPCCALL",
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+static int __init rpcrouter_init(void)
|
|
|
+{
|
|
|
+ return platform_driver_register(&msm_smd_channel2_driver);
|
|
|
+}
|
|
|
+
|
|
|
+module_init(rpcrouter_init);
|
|
|
+MODULE_DESCRIPTION("MSM RPC Router");
|
|
|
+MODULE_AUTHOR("San Mehat <san@android.com>");
|
|
|
+MODULE_LICENSE("GPL");
|