1355 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1355 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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"
 | |
| 
 | |
| #if defined(CONFIG_MACH_HTCLEO)
 | |
| #include "board-htcleo.h"
 | |
| #endif
 | |
| 
 | |
| #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 int smd_wait_count; /* odd while waiting */
 | |
| 
 | |
| 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;
 | |
| 
 | |
| 	RR("send control message cmd=%d srv.cmd=%d prog=%08x:%x id=%d:%08x\n", msg->cmd, msg->srv.cmd, msg->srv.prog, msg->srv.vers,  msg->srv.pid, msg->srv.cid);
 | |
| 
 | |
| 	if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !initialized
 | |
| #if defined(CONFIG_MACH_HTCLEO)
 | |
| 	  && (!(msg->cmd == RPCROUTER_CTRL_CMD_BYE) && !htcleo_is_nand_boot())
 | |
| #endif
 | |
| 	  ) {
 | |
| 		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);
 | |
| 		ept->flags |= MSM_RPC_ENABLE_RECEIVE;
 | |
| 
 | |
| 		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));
 | |
| #if defined(CONFIG_MACH_HTCLEO)
 | |
| 		if (htcleo_is_nand_boot())
 | |
| 		{
 | |
| 			ctl.cmd = RPCROUTER_CTRL_CMD_HELLO;
 | |
| 			rpcrouter_send_control_msg(&ctl);
 | |
| 		}
 | |
| #else
 | |
| 		ctl.cmd = RPCROUTER_CTRL_CMD_HELLO;
 | |
| 		rpcrouter_send_control_msg(&ctl);
 | |
| #endif
 | |
| 		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);
 | |
| 		smd_wait_count++;
 | |
| 		wake_up(&smd_wait);
 | |
| 		wait_event(smd_wait, smd_read_avail(smd_channel) >= len);
 | |
| 		smd_wait_count++;
 | |
| 	}
 | |
| 	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);
 | |
| 	if (ept->flags & MSM_RPC_ENABLE_RECEIVE) {
 | |
| 		wake_lock(&ept->read_q_wake_lock);
 | |
| 		list_add_tail(&pkt->list, &ept->read_q);
 | |
| 		wake_up(&ept->wait_q);
 | |
| 	} else {
 | |
| 		pr_warning("smd_rpcrouter: Unexpected incoming data on %08x:%08x\n",
 | |
| 				be32_to_cpu(ept->dst_prog),
 | |
| 				be32_to_cpu(ept->dst_vers));
 | |
| 	}
 | |
| 	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 || defined(CONFIG_ARCH_QSD8X50)
 | |
| 		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);
 | |
| 
 | |
| 	/* Allow replys to be added to the queue */
 | |
| 	ept->flags |= MSM_RPC_ENABLE_RECEIVE;
 | |
| 
 | |
| 	rc = msm_rpc_write(ept, req, request_size);
 | |
| 	if (rc < 0)
 | |
| 		goto error;
 | |
| 
 | |
| 	for (;;) {
 | |
| 		rc = msm_rpc_read(ept, (void*) &reply, -1, timeout);
 | |
| 		if (rc < 0)
 | |
| 			goto error;
 | |
| 		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);
 | |
| error:
 | |
| 	ept->flags &= ~MSM_RPC_ENABLE_RECEIVE;
 | |
| 	wake_unlock(&ept->read_q_wake_lock);
 | |
| 
 | |
| 	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 || defined(CONFIG_ARCH_QSD8X50)
 | |
| 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 || defined(CONFIG_ARCH_QSD8X50)
 | |
| 	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;
 | |
| 
 | |
| 	ept->flags |= MSM_RPC_ENABLE_RECEIVE;
 | |
| 	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;
 | |
| 
 | |
| 	ept->flags &= ~MSM_RPC_ENABLE_RECEIVE;
 | |
| 	wake_unlock(&ept->read_q_wake_lock);
 | |
| 	rpcrouter_destroy_server(server);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int msm_rpcrouter_close(void)
 | |
| {
 | |
| 	return smd_close(smd_channel);
 | |
| }
 | |
| 
 | |
| static int msm_rpcrouter_probe(struct platform_device *pdev)
 | |
| {
 | |
| 	int rc;
 | |
| 	union rr_control_msg msg = { 0 };
 | |
| 	pr_info("RPC Probe\n");
 | |
| 	
 | |
| 	/* 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;
 | |
| 
 | |
| 	pr_info("RPC Init done\n");
 | |
| 
 | |
| 	/* 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);
 | |
| 	
 | |
| #if defined(CONFIG_MACH_HTCLEO)
 | |
| 	if (!htcleo_is_nand_boot())
 | |
| 	{
 | |
| 		msg.cmd = RPCROUTER_CTRL_CMD_BYE;
 | |
| 		rpcrouter_send_control_msg(&msg);
 | |
| 		msleep(50);
 | |
| 
 | |
| 		/* wince rpc init */
 | |
|         	msg.cmd = RPCROUTER_CTRL_CMD_HELLO;
 | |
| 		rpcrouter_send_control_msg(&msg);
 | |
| 		msleep(50);
 | |
| 	
 | |
| 	
 | |
| 	        process_control_msg(&msg, sizeof(msg));
 | |
| 		msleep(100);
 | |
| 	}
 | |
| #endif	
 | |
| 	
 | |
| 	return 0;
 | |
| 
 | |
|  fail_remove_devices:
 | |
| 	msm_rpcrouter_exit_devices();
 | |
|  fail_destroy_workqueue:
 | |
| 	destroy_workqueue(rpcrouter_workqueue);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int msm_rpcrouter_suspend(struct platform_device *pdev,
 | |
| 					pm_message_t state)
 | |
| {
 | |
| 	/* Wait until the worker thread has waited at least once so that it
 | |
| 	 * gets a chance to release its wakelock.
 | |
| 	 */
 | |
| 	int wait_count = smd_wait_count;
 | |
| 	if (!(smd_wait_count & 1))
 | |
| 		wait_event(smd_wait, smd_wait_count != wait_count);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct platform_driver msm_smd_channel2_driver = {
 | |
| 	.probe		= msm_rpcrouter_probe,
 | |
| 	.driver		= {
 | |
| 			.name	= "SMD_RPCCALL",
 | |
| 			.owner	= THIS_MODULE,
 | |
| 	},
 | |
| 	.suspend	= msm_rpcrouter_suspend,
 | |
| };
 | |
| 
 | |
| 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");
 |