| /* Copyright (c) 2010, The Linux Foundation. All rights reserved. |
| * |
| * 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. |
| * |
| * 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. |
| * |
| */ |
| |
| /* |
| * SMD RPCROUTER XDR module. |
| */ |
| |
| #include <linux/slab.h> |
| #include <linux/types.h> |
| #include <linux/string.h> |
| #include <linux/kernel.h> |
| #include <linux/delay.h> |
| #include <linux/sched.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); |
| init_waitqueue_head(&xdr->in_buf_wait_q); |
| |
| 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) |
| { |
| wait_event(xdr->in_buf_wait_q, !(xdr->in_buf)); |
| |
| 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_size = 0; |
| xdr->in_index = 0; |
| xdr->in_buf = NULL; |
| |
| wake_up(&xdr->in_buf_wait_q); |
| } |
| |
| 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; |
| } |