473 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			473 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  *
 | |
|  *     * Redistributions of source code must retain the above copyright
 | |
|  *       notice, this list of conditions and the following disclaimer.
 | |
|  *     * Redistributions in binary form must reproduce the above
 | |
|  *       copyright notice, this list of conditions and the following
 | |
|  *       disclaimer in the documentation and/or other materials provided
 | |
|  *       with the distribution.
 | |
|  *     * Neither the name of Code Aurora Forum, Inc. nor the names of its
 | |
|  *       contributors may be used to endorse or promote products derived
 | |
|  *       from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 | |
|  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | |
|  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | |
|  * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 | |
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 | |
|  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 | |
|  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 | |
|  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 | |
|  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  * Alternatively, and instead of the terms immediately above, this
 | |
|  * software may be relicensed by the recipient at their option under the
 | |
|  * terms of the GNU General Public License version 2 ("GPL") and only
 | |
|  * version 2.  If the recipient chooses to relicense the software under
 | |
|  * the GPL, then the recipient shall replace all of the text immediately
 | |
|  * above and including this paragraph with the text immediately below
 | |
|  * and between the words START OF ALTERNATE GPL TERMS and END OF
 | |
|  * ALTERNATE GPL TERMS and such notices and license terms shall apply
 | |
|  * INSTEAD OF the notices and licensing terms given above.
 | |
|  *
 | |
|  * START OF ALTERNATE GPL TERMS
 | |
|  *
 | |
|  * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
 | |
|  *
 | |
|  * This software was originally licensed under the Code Aurora Forum
 | |
|  * Inc. Dual BSD/GPL License version 1.1 and relicensed as permitted
 | |
|  * under the terms thereof by a recipient under the General Public
 | |
|  * License Version 2.
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License version 2 and
 | |
|  * only version 2 as published by the Free Software Foundation.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | |
|  * 02110-1301, USA.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 | |
|  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | |
|  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | |
|  * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 | |
|  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 | |
|  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 | |
|  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 | |
|  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 | |
|  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  * END OF ALTERNATE GPL TERMS
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * SMD RPCROUTER XDR module.
 | |
|  */
 | |
| 
 | |
| #include <linux/types.h>
 | |
| #include <linux/string.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/delay.h>
 | |
| 
 | |
| #include <mach/msm_rpcrouter.h>
 | |
| 
 | |
| int xdr_send_uint32(struct msm_rpc_xdr *xdr, const uint32_t *value)
 | |
| {
 | |
| 	if ((xdr->out_index + sizeof(uint32_t)) > xdr->out_size) {
 | |
| 		pr_err("%s: xdr out buffer full\n", __func__);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	*(uint32_t *)(xdr->out_buf + xdr->out_index) = cpu_to_be32(*value);
 | |
| 	xdr->out_index += sizeof(uint32_t);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int xdr_send_int8(struct msm_rpc_xdr *xdr, const int8_t *value)
 | |
| {
 | |
| 	return xdr_send_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_send_uint8(struct msm_rpc_xdr *xdr, const uint8_t *value)
 | |
| {
 | |
| 	return xdr_send_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_send_int16(struct msm_rpc_xdr *xdr, const int16_t *value)
 | |
| {
 | |
| 	return xdr_send_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_send_uint16(struct msm_rpc_xdr *xdr, const uint16_t *value)
 | |
| {
 | |
| 	return xdr_send_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_send_int32(struct msm_rpc_xdr *xdr, const int32_t *value)
 | |
| {
 | |
| 	return xdr_send_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_send_bytes(struct msm_rpc_xdr *xdr, const void **data,
 | |
| 		   uint32_t *size)
 | |
| {
 | |
| 	void *buf = xdr->out_buf + xdr->out_index;
 | |
| 	uint32_t temp;
 | |
| 
 | |
| 	if (!size || !data || !*data)
 | |
| 		return -1;
 | |
| 
 | |
| 	temp = *size;
 | |
| 	if (temp & 0x3)
 | |
| 		temp += 4 - (temp & 0x3);
 | |
| 
 | |
| 	temp += sizeof(uint32_t);
 | |
| 	if ((xdr->out_index + temp) > xdr->out_size) {
 | |
| 		pr_err("%s: xdr out buffer full\n", __func__);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	*((uint32_t *)buf) = cpu_to_be32(*size);
 | |
| 	buf += sizeof(uint32_t);
 | |
| 	memcpy(buf, *data, *size);
 | |
| 	buf += *size;
 | |
| 	if (*size & 0x3) {
 | |
| 		memset(buf, 0, 4 - (*size & 0x3));
 | |
| 		buf += 4 - (*size & 0x3);
 | |
| 	}
 | |
| 
 | |
| 	xdr->out_index = buf - xdr->out_buf;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int xdr_recv_uint32(struct msm_rpc_xdr *xdr, uint32_t *value)
 | |
| {
 | |
| 	if ((xdr->in_index + sizeof(uint32_t)) > xdr->in_size) {
 | |
| 		pr_err("%s: xdr in buffer full\n", __func__);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	*value = be32_to_cpu(*(uint32_t *)(xdr->in_buf + xdr->in_index));
 | |
| 	xdr->in_index += sizeof(uint32_t);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int xdr_recv_int8(struct msm_rpc_xdr *xdr, int8_t *value)
 | |
| {
 | |
| 	return xdr_recv_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_recv_uint8(struct msm_rpc_xdr *xdr, uint8_t *value)
 | |
| {
 | |
| 	return xdr_recv_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_recv_int16(struct msm_rpc_xdr *xdr, int16_t *value)
 | |
| {
 | |
| 	return xdr_recv_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_recv_uint16(struct msm_rpc_xdr *xdr, uint16_t *value)
 | |
| {
 | |
| 	return xdr_recv_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_recv_int32(struct msm_rpc_xdr *xdr, int32_t *value)
 | |
| {
 | |
| 	return xdr_recv_uint32(xdr, (uint32_t *)value);
 | |
| }
 | |
| 
 | |
| int xdr_recv_bytes(struct msm_rpc_xdr *xdr, void **data,
 | |
| 		   uint32_t *size)
 | |
| {
 | |
| 	void *buf = xdr->in_buf + xdr->in_index;
 | |
| 	uint32_t temp;
 | |
| 
 | |
| 	if (!size || !data)
 | |
| 		return -1;
 | |
| 
 | |
| 	*size = be32_to_cpu(*(uint32_t *)buf);
 | |
| 	buf += sizeof(uint32_t);
 | |
| 
 | |
| 	temp = *size;
 | |
| 	if (temp & 0x3)
 | |
| 		temp += 4 - (temp & 0x3);
 | |
| 
 | |
| 	temp += sizeof(uint32_t);
 | |
| 	if ((xdr->in_index + temp) > xdr->in_size) {
 | |
| 		pr_err("%s: xdr in buffer full\n", __func__);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (*size) {
 | |
| 		*data = kmalloc(*size, GFP_KERNEL);
 | |
| 		if (!*data)
 | |
| 			return -1;
 | |
| 
 | |
| 		memcpy(*data, buf, *size);
 | |
| 
 | |
| 		buf += *size;
 | |
| 		if (*size & 0x3)
 | |
| 			buf += 4 - (*size & 0x3);
 | |
| 	} else
 | |
| 		*data = NULL;
 | |
| 
 | |
| 	xdr->in_index = buf - xdr->in_buf;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int xdr_send_pointer(struct msm_rpc_xdr *xdr, void **obj,
 | |
| 		     uint32_t obj_size, void *xdr_op)
 | |
| {
 | |
| 	uint32_t ptr_valid, rc;
 | |
| 
 | |
| 	ptr_valid = (*obj != NULL);
 | |
| 
 | |
| 	rc = xdr_send_uint32(xdr, &ptr_valid);
 | |
| 	if (rc)
 | |
| 		return rc;
 | |
| 
 | |
| 	if (!ptr_valid)
 | |
| 		return 0;
 | |
| 
 | |
| 	return ((int (*) (struct msm_rpc_xdr *, void *))xdr_op)(xdr, *obj);
 | |
| }
 | |
| 
 | |
| int xdr_recv_pointer(struct msm_rpc_xdr *xdr, void **obj,
 | |
| 		     uint32_t obj_size, void *xdr_op)
 | |
| {
 | |
| 	uint32_t rc, ptr_valid = 0;
 | |
| 
 | |
| 	rc = xdr_recv_uint32(xdr, &ptr_valid);
 | |
| 	if (rc)
 | |
| 		return rc;
 | |
| 
 | |
| 	if (!ptr_valid) {
 | |
| 		*obj = NULL;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	*obj = kmalloc(obj_size, GFP_KERNEL);
 | |
| 	if (!*obj)
 | |
| 		return -1;
 | |
| 
 | |
| 	rc = ((int (*) (struct msm_rpc_xdr *, void *))xdr_op)(xdr, *obj);
 | |
| 	if (rc)
 | |
| 		kfree(*obj);
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| int xdr_send_array(struct msm_rpc_xdr *xdr, void **addr, uint32_t *size,
 | |
| 		   uint32_t maxsize, uint32_t elm_size, void *xdr_op)
 | |
| {
 | |
| 	int i, rc;
 | |
| 	void *tmp_addr = *addr;
 | |
| 
 | |
| 	if (!size || !tmp_addr || (*size > maxsize) || !xdr_op)
 | |
| 		return -1;
 | |
| 
 | |
| 	rc = xdr_send_uint32(xdr, size);
 | |
| 	if (rc)
 | |
| 		return rc;
 | |
| 
 | |
| 	for (i = 0; i < *size; i++) {
 | |
| 		rc = ((int (*) (struct msm_rpc_xdr *, void *))xdr_op)
 | |
| 			(xdr, tmp_addr);
 | |
| 		if (rc)
 | |
| 			return rc;
 | |
| 
 | |
| 		tmp_addr += elm_size;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int xdr_recv_array(struct msm_rpc_xdr *xdr, void **addr, uint32_t *size,
 | |
| 		   uint32_t maxsize, uint32_t elm_size, void *xdr_op)
 | |
| {
 | |
| 	int i, rc;
 | |
| 	void *tmp_addr;
 | |
| 
 | |
| 	if (!size || !xdr_op)
 | |
| 		return -1;
 | |
| 
 | |
| 	rc = xdr_recv_uint32(xdr, size);
 | |
| 	if (rc)
 | |
| 		return rc;
 | |
| 
 | |
| 	if (*size > maxsize)
 | |
| 		return -1;
 | |
| 
 | |
| 	tmp_addr = kmalloc((*size * elm_size), GFP_KERNEL);
 | |
| 	if (!tmp_addr)
 | |
| 		return -1;
 | |
| 
 | |
| 	*addr = tmp_addr;
 | |
| 	for (i = 0; i < *size; i++) {
 | |
| 		rc = ((int (*) (struct msm_rpc_xdr *, void *))xdr_op)
 | |
| 			(xdr, tmp_addr);
 | |
| 		if (rc) {
 | |
| 			kfree(*addr);
 | |
| 			*addr = NULL;
 | |
| 			return rc;
 | |
| 		}
 | |
| 
 | |
| 		tmp_addr += elm_size;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int xdr_recv_req(struct msm_rpc_xdr *xdr, struct rpc_request_hdr *req)
 | |
| {
 | |
| 	int rc = 0;
 | |
| 	if (!req)
 | |
| 		return -1;
 | |
| 
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->xid);           /* xid */
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->type);          /* type */
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->rpc_vers);      /* rpc_vers */
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->prog);          /* prog */
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->vers);          /* vers */
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->procedure);     /* procedure */
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->cred_flavor);   /* cred_flavor */
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->cred_length);   /* cred_length */
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->verf_flavor);   /* verf_flavor */
 | |
| 	rc |= xdr_recv_uint32(xdr, &req->verf_length);   /* verf_length */
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| int xdr_recv_reply(struct msm_rpc_xdr *xdr, struct rpc_reply_hdr *reply)
 | |
| {
 | |
| 	int rc = 0;
 | |
| 
 | |
| 	if (!reply)
 | |
| 		return -1;
 | |
| 
 | |
| 	rc |= xdr_recv_uint32(xdr, &reply->xid);           /* xid */
 | |
| 	rc |= xdr_recv_uint32(xdr, &reply->type);          /* type */
 | |
| 	rc |= xdr_recv_uint32(xdr, &reply->reply_stat);    /* reply_stat */
 | |
| 
 | |
| 	/* acc_hdr */
 | |
| 	if (reply->reply_stat == RPCMSG_REPLYSTAT_ACCEPTED) {
 | |
| 		rc |= xdr_recv_uint32(xdr, &reply->data.acc_hdr.verf_flavor);
 | |
| 		rc |= xdr_recv_uint32(xdr, &reply->data.acc_hdr.verf_length);
 | |
| 		rc |= xdr_recv_uint32(xdr, &reply->data.acc_hdr.accept_stat);
 | |
| 	}
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| int xdr_start_request(struct msm_rpc_xdr *xdr, uint32_t prog,
 | |
| 		      uint32_t ver, uint32_t proc)
 | |
| {
 | |
| 	mutex_lock(&xdr->out_lock);
 | |
| 
 | |
| 	/* TODO: replace below function with its implementation */
 | |
| 	msm_rpc_setup_req((struct rpc_request_hdr *)xdr->out_buf,
 | |
| 			  prog, ver, proc);
 | |
| 
 | |
| 	xdr->out_index = sizeof(struct rpc_request_hdr);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int xdr_start_accepted_reply(struct msm_rpc_xdr *xdr, uint32_t accept_status)
 | |
| {
 | |
| 	struct rpc_reply_hdr *reply;
 | |
| 
 | |
| 	mutex_lock(&xdr->out_lock);
 | |
| 
 | |
| 	/* TODO: err if xdr is not cb xdr */
 | |
| 	reply = (struct rpc_reply_hdr *)xdr->out_buf;
 | |
| 
 | |
| 	/* TODO: use xdr functions instead */
 | |
| 	reply->xid = ((struct rpc_request_hdr *)(xdr->in_buf))->xid;
 | |
| 	reply->type = cpu_to_be32(1); /* reply */
 | |
| 	reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
 | |
| 
 | |
| 	reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status);
 | |
| 	reply->data.acc_hdr.verf_flavor = 0;
 | |
| 	reply->data.acc_hdr.verf_length = 0;
 | |
| 
 | |
| 	xdr->out_index = sizeof(*reply);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int xdr_send_msg(struct msm_rpc_xdr *xdr)
 | |
| {
 | |
| 	int rc = 0;
 | |
| 
 | |
| 	rc = msm_rpc_write(xdr->ept, xdr->out_buf,
 | |
| 			   xdr->out_index);
 | |
| 	if (rc > 0)
 | |
| 		rc = 0;
 | |
| 
 | |
| 	mutex_unlock(&xdr->out_lock);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| void xdr_init(struct msm_rpc_xdr *xdr)
 | |
| {
 | |
| 	mutex_init(&xdr->out_lock);
 | |
| 	mutex_init(&xdr->in_lock);
 | |
| 
 | |
| 	xdr->in_buf = NULL;
 | |
| 	xdr->in_size = 0;
 | |
| 	xdr->in_index = 0;
 | |
| 
 | |
| 	xdr->out_buf = NULL;
 | |
| 	xdr->out_size = 0;
 | |
| 	xdr->out_index = 0;
 | |
| }
 | |
| 
 | |
| void xdr_init_input(struct msm_rpc_xdr *xdr, void *buf, uint32_t size)
 | |
| {
 | |
| 	mutex_lock(&xdr->in_lock);
 | |
| 
 | |
| 	xdr->in_buf = buf;
 | |
| 	xdr->in_size = size;
 | |
| 	xdr->in_index = 0;
 | |
| }
 | |
| 
 | |
| void xdr_init_output(struct msm_rpc_xdr *xdr, void *buf, uint32_t size)
 | |
| {
 | |
| 	xdr->out_buf = buf;
 | |
| 	xdr->out_size = size;
 | |
| 	xdr->out_index = 0;
 | |
| }
 | |
| 
 | |
| void xdr_clean_input(struct msm_rpc_xdr *xdr)
 | |
| {
 | |
| 	kfree(xdr->in_buf);
 | |
| 	xdr->in_buf = NULL;
 | |
| 	xdr->in_size = 0;
 | |
| 	xdr->in_index = 0;
 | |
| 
 | |
| 	mutex_unlock(&xdr->in_lock);
 | |
| }
 | |
| 
 | |
| void xdr_clean_output(struct msm_rpc_xdr *xdr)
 | |
| {
 | |
| 	kfree(xdr->out_buf);
 | |
| 	xdr->out_buf = NULL;
 | |
| 	xdr->out_size = 0;
 | |
| 	xdr->out_index = 0;
 | |
| }
 | |
| 
 | |
| uint32_t xdr_read_avail(struct msm_rpc_xdr *xdr)
 | |
| {
 | |
| 	return xdr->in_size;
 | |
| }
 |