/* 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 #include #include #include #include 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; }