358 lines
9.1 KiB
C
358 lines
9.1 KiB
C
/* Copyright (c) 2009-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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* OEM RAPI CLIENT Driver source file
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/err.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/module.h>
|
|
#include <mach/msm_rpcrouter.h>
|
|
#include <mach/oem_rapi_client.h>
|
|
|
|
#define OEM_RAPI_PROG 0x3000006B
|
|
#define OEM_RAPI_VERS 0x00010001
|
|
|
|
#define OEM_RAPI_NULL_PROC 0
|
|
#define OEM_RAPI_RPC_GLUE_CODE_INFO_REMOTE_PROC 1
|
|
#define OEM_RAPI_STREAMING_FUNCTION_PROC 2
|
|
|
|
#define OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE 128
|
|
|
|
static struct msm_rpc_client *rpc_client;
|
|
static uint32_t open_count;
|
|
static DEFINE_MUTEX(oem_rapi_client_lock);
|
|
|
|
/* TODO: check where to allocate memory for return */
|
|
static int oem_rapi_client_cb(struct msm_rpc_client *client,
|
|
struct rpc_request_hdr *req,
|
|
struct msm_rpc_xdr *xdr)
|
|
{
|
|
uint32_t cb_id, accept_status;
|
|
int rc;
|
|
void *cb_func;
|
|
uint32_t temp;
|
|
|
|
struct oem_rapi_client_streaming_func_cb_arg arg;
|
|
struct oem_rapi_client_streaming_func_cb_ret ret;
|
|
|
|
arg.input = NULL;
|
|
ret.out_len = NULL;
|
|
ret.output = NULL;
|
|
|
|
xdr_recv_uint32(xdr, &cb_id); /* cb_id */
|
|
xdr_recv_uint32(xdr, &arg.event); /* enum */
|
|
xdr_recv_uint32(xdr, (uint32_t *)(&arg.handle)); /* handle */
|
|
xdr_recv_uint32(xdr, &arg.in_len); /* in_len */
|
|
xdr_recv_bytes(xdr, (void **)&arg.input, &temp); /* input */
|
|
xdr_recv_uint32(xdr, &arg.out_len_valid); /* out_len */
|
|
if (arg.out_len_valid) {
|
|
ret.out_len = kmalloc(sizeof(*ret.out_len), GFP_KERNEL);
|
|
if (!ret.out_len) {
|
|
accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
|
|
goto oem_rapi_send_ack;
|
|
}
|
|
}
|
|
|
|
xdr_recv_uint32(xdr, &arg.output_valid); /* out */
|
|
if (arg.output_valid) {
|
|
xdr_recv_uint32(xdr, &arg.output_size); /* ouput_size */
|
|
|
|
ret.output = kmalloc(arg.output_size, GFP_KERNEL);
|
|
if (!ret.output) {
|
|
accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
|
|
goto oem_rapi_send_ack;
|
|
}
|
|
}
|
|
|
|
cb_func = msm_rpc_get_cb_func(client, cb_id);
|
|
if (cb_func) {
|
|
rc = ((int (*)(struct oem_rapi_client_streaming_func_cb_arg *,
|
|
struct oem_rapi_client_streaming_func_cb_ret *))
|
|
cb_func)(&arg, &ret);
|
|
if (rc)
|
|
accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
|
|
else
|
|
accept_status = RPC_ACCEPTSTAT_SUCCESS;
|
|
} else
|
|
accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
|
|
|
|
oem_rapi_send_ack:
|
|
xdr_start_accepted_reply(xdr, accept_status);
|
|
|
|
if (accept_status == RPC_ACCEPTSTAT_SUCCESS) {
|
|
uint32_t temp = sizeof(uint32_t);
|
|
xdr_send_pointer(xdr, (void **)&(ret.out_len), temp,
|
|
xdr_send_uint32);
|
|
|
|
/* output */
|
|
if (ret.output && ret.out_len)
|
|
xdr_send_bytes(xdr, (const void **)&ret.output,
|
|
ret.out_len);
|
|
else {
|
|
temp = 0;
|
|
xdr_send_uint32(xdr, &temp);
|
|
}
|
|
}
|
|
rc = xdr_send_msg(xdr);
|
|
if (rc)
|
|
pr_err("%s: sending reply failed: %d\n", __func__, rc);
|
|
|
|
kfree(arg.input);
|
|
kfree(ret.out_len);
|
|
kfree(ret.output);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int oem_rapi_client_streaming_function_arg(struct msm_rpc_client *client,
|
|
struct msm_rpc_xdr *xdr,
|
|
void *data)
|
|
{
|
|
int cb_id;
|
|
struct oem_rapi_client_streaming_func_arg *arg = data;
|
|
|
|
cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func);
|
|
if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID))
|
|
return cb_id;
|
|
|
|
xdr_send_uint32(xdr, &arg->event); /* enum */
|
|
xdr_send_uint32(xdr, &cb_id); /* cb_id */
|
|
xdr_send_uint32(xdr, (uint32_t *)(&arg->handle)); /* handle */
|
|
xdr_send_uint32(xdr, &arg->in_len); /* in_len */
|
|
xdr_send_bytes(xdr, (const void **)&arg->input,
|
|
&arg->in_len); /* input */
|
|
xdr_send_uint32(xdr, &arg->out_len_valid); /* out_len */
|
|
xdr_send_uint32(xdr, &arg->output_valid); /* output */
|
|
|
|
/* output_size */
|
|
if (arg->output_valid)
|
|
xdr_send_uint32(xdr, &arg->output_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int oem_rapi_client_streaming_function_ret(struct msm_rpc_client *client,
|
|
struct msm_rpc_xdr *xdr,
|
|
void *data)
|
|
{
|
|
struct oem_rapi_client_streaming_func_ret *ret = data;
|
|
uint32_t temp;
|
|
|
|
/* out_len */
|
|
xdr_recv_pointer(xdr, (void **)&(ret->out_len), sizeof(uint32_t),
|
|
xdr_recv_uint32);
|
|
|
|
/* output */
|
|
if (ret->out_len && *ret->out_len)
|
|
xdr_recv_bytes(xdr, (void **)&ret->output, &temp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int oem_rapi_client_streaming_function(
|
|
struct msm_rpc_client *client,
|
|
struct oem_rapi_client_streaming_func_arg *arg,
|
|
struct oem_rapi_client_streaming_func_ret *ret)
|
|
{
|
|
return msm_rpc_client_req2(client,
|
|
OEM_RAPI_STREAMING_FUNCTION_PROC,
|
|
oem_rapi_client_streaming_function_arg, arg,
|
|
oem_rapi_client_streaming_function_ret,
|
|
ret, -1);
|
|
}
|
|
EXPORT_SYMBOL(oem_rapi_client_streaming_function);
|
|
|
|
int oem_rapi_client_close(void)
|
|
{
|
|
mutex_lock(&oem_rapi_client_lock);
|
|
if (--open_count == 0) {
|
|
msm_rpc_unregister_client(rpc_client);
|
|
pr_info("%s: disconnected from remote oem rapi server\n",
|
|
__func__);
|
|
}
|
|
mutex_unlock(&oem_rapi_client_lock);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(oem_rapi_client_close);
|
|
|
|
struct msm_rpc_client *oem_rapi_client_init(void)
|
|
{
|
|
mutex_lock(&oem_rapi_client_lock);
|
|
if (open_count == 0) {
|
|
rpc_client = msm_rpc_register_client2("oemrapiclient",
|
|
OEM_RAPI_PROG,
|
|
OEM_RAPI_VERS, 0,
|
|
oem_rapi_client_cb);
|
|
if (!IS_ERR(rpc_client))
|
|
open_count++;
|
|
}
|
|
mutex_unlock(&oem_rapi_client_lock);
|
|
return rpc_client;
|
|
}
|
|
EXPORT_SYMBOL(oem_rapi_client_init);
|
|
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
|
|
static struct dentry *dent;
|
|
static int oem_rapi_client_test_res;
|
|
|
|
static int oem_rapi_client_null(struct msm_rpc_client *client,
|
|
void *arg, void *ret)
|
|
{
|
|
return msm_rpc_client_req2(client, OEM_RAPI_NULL_PROC,
|
|
NULL, NULL, NULL, NULL, -1);
|
|
}
|
|
|
|
static int oem_rapi_client_test_streaming_cb_func(
|
|
struct oem_rapi_client_streaming_func_cb_arg *arg,
|
|
struct oem_rapi_client_streaming_func_cb_ret *ret)
|
|
{
|
|
uint32_t size;
|
|
|
|
size = (arg->in_len < OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE) ?
|
|
arg->in_len : OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE;
|
|
|
|
if (ret->out_len != 0)
|
|
*ret->out_len = size;
|
|
|
|
if (ret->output != 0)
|
|
memcpy(ret->output, arg->input, size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t debug_read(struct file *fp, char __user *buf,
|
|
size_t count, loff_t *pos)
|
|
{
|
|
char _buf[16];
|
|
|
|
snprintf(_buf, sizeof(_buf), "%i\n", oem_rapi_client_test_res);
|
|
|
|
return simple_read_from_buffer(buf, count, pos, _buf, strlen(_buf));
|
|
}
|
|
|
|
static ssize_t debug_write(struct file *fp, const char __user *buf,
|
|
size_t count, loff_t *pos)
|
|
{
|
|
char input[OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE];
|
|
struct oem_rapi_client_streaming_func_arg arg;
|
|
struct oem_rapi_client_streaming_func_ret ret;
|
|
|
|
unsigned char cmd[64];
|
|
int len;
|
|
|
|
if (count < 1)
|
|
return 0;
|
|
|
|
len = count > 63 ? 63 : count;
|
|
|
|
if (copy_from_user(cmd, buf, len))
|
|
return -EFAULT;
|
|
|
|
cmd[len] = 0;
|
|
|
|
if (cmd[len-1] == '\n') {
|
|
cmd[len-1] = 0;
|
|
len--;
|
|
}
|
|
|
|
if (!strncmp(cmd, "null", 64)) {
|
|
oem_rapi_client_test_res = oem_rapi_client_null(rpc_client,
|
|
NULL, NULL);
|
|
} else if (!strncmp(cmd, "streaming_func", 64)) {
|
|
memset(input, 5, 16);
|
|
arg.event = 0;
|
|
arg.cb_func = oem_rapi_client_test_streaming_cb_func;
|
|
arg.handle = (void *)20;
|
|
arg.in_len = 16;
|
|
arg.input = input;
|
|
arg.out_len_valid = 1;
|
|
arg.output_valid = 1;
|
|
arg.output_size = OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE;
|
|
ret.out_len = NULL;
|
|
ret.output = NULL;
|
|
|
|
oem_rapi_client_test_res = oem_rapi_client_streaming_function(
|
|
rpc_client, &arg, &ret);
|
|
|
|
kfree(ret.out_len);
|
|
kfree(ret.output);
|
|
|
|
} else
|
|
oem_rapi_client_test_res = -EINVAL;
|
|
|
|
if (oem_rapi_client_test_res)
|
|
pr_err("oem rapi client test fail %d\n",
|
|
oem_rapi_client_test_res);
|
|
else
|
|
pr_info("oem rapi client test passed\n");
|
|
|
|
return count;
|
|
}
|
|
|
|
static int debug_release(struct inode *ip, struct file *fp)
|
|
{
|
|
return oem_rapi_client_close();
|
|
}
|
|
|
|
static int debug_open(struct inode *ip, struct file *fp)
|
|
{
|
|
struct msm_rpc_client *client;
|
|
client = oem_rapi_client_init();
|
|
if (IS_ERR(client)) {
|
|
pr_err("%s: couldn't open oem rapi client\n", __func__);
|
|
return PTR_ERR(client);
|
|
} else
|
|
pr_info("%s: connected to remote oem rapi server\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations debug_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = debug_open,
|
|
.release = debug_release,
|
|
.read = debug_read,
|
|
.write = debug_write,
|
|
};
|
|
|
|
static void __exit oem_rapi_client_mod_exit(void)
|
|
{
|
|
debugfs_remove(dent);
|
|
}
|
|
|
|
static int __init oem_rapi_client_mod_init(void)
|
|
{
|
|
dent = debugfs_create_file("oem_rapi", 0444, 0, NULL, &debug_ops);
|
|
open_count = 0;
|
|
oem_rapi_client_test_res = -1;
|
|
return 0;
|
|
}
|
|
|
|
module_init(oem_rapi_client_mod_init);
|
|
module_exit(oem_rapi_client_mod_exit);
|
|
|
|
#endif
|
|
|
|
MODULE_DESCRIPTION("OEM RAPI CLIENT Driver");
|
|
MODULE_LICENSE("GPL v2");
|