| /* 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. |
| * |
| */ |
| |
| /* |
| * PING APPS SERVER Driver |
| */ |
| |
| #include <linux/slab.h> |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| #include <linux/kthread.h> |
| #include <linux/workqueue.h> |
| #include <mach/msm_rpcrouter.h> |
| |
| /* ping server definitions */ |
| |
| #define PING_APPS_PROG 0x30000082 |
| #define PING_APPS_VERS 0x00010001 |
| |
| #define PING_APPS_NULL 0 |
| #define PING_APPS_DATA 4 |
| #define PING_APPS_REG 2 |
| #define PING_APPS_UNREG 3 |
| #define PING_APPS_DATA_CB_REG 6 |
| #define PING_APPS_DATA_CB_UNREG 5 |
| |
| #define PING_APPS_REG_CB 2 |
| #define PING_APPS_DATA_CB 1 |
| |
| static LIST_HEAD(cb_entry_list); |
| static DEFINE_MUTEX(cb_entry_list_lock); |
| |
| static struct task_struct *server_thread; |
| |
| struct ping_apps_register_arg { |
| uint32_t cb_id; |
| int32_t num; |
| }; |
| |
| struct ping_apps_unregister_arg { |
| uint32_t cb_id; |
| }; |
| |
| struct ping_apps_register_cb_arg { |
| uint32_t cb_id; |
| int32_t num; |
| }; |
| |
| struct ping_apps_register_ret { |
| uint32_t result; |
| }; |
| |
| struct ping_apps_unregister_ret { |
| uint32_t result; |
| }; |
| |
| struct ping_apps_data_cb_reg_arg { |
| uint32_t cb_id; |
| uint32_t num; |
| uint32_t size; |
| uint32_t interval_ms; |
| uint32_t num_tasks; |
| }; |
| |
| struct ping_apps_data_cb_unreg_arg { |
| uint32_t cb_id; |
| }; |
| |
| struct ping_apps_data_cb_arg { |
| uint32_t cb_id; |
| uint32_t *data; |
| uint32_t size; |
| uint32_t sum; |
| }; |
| |
| struct ping_apps_data_cb_reg_ret { |
| uint32_t result; |
| }; |
| |
| struct ping_apps_data_cb_unreg_ret { |
| uint32_t result; |
| }; |
| |
| struct ping_apps_data_cb_ret { |
| uint32_t result; |
| }; |
| |
| struct ping_apps_data_arg { |
| uint32_t *data; |
| uint32_t size; |
| }; |
| |
| struct ping_apps_data_ret { |
| uint32_t result; |
| }; |
| |
| struct ping_apps_data_cb_info { |
| void *cb_func; |
| uint32_t size; |
| uint32_t num_tasks; |
| }; |
| |
| struct ping_apps_cb_entry { |
| struct list_head list; |
| |
| struct msm_rpc_client_info clnt_info; |
| void *cb_info; |
| uint32_t cb_id; |
| int32_t num; |
| uint32_t interval_ms; |
| uint32_t time_to_next_cb; |
| void (*cb_func)(struct ping_apps_cb_entry *); |
| }; |
| |
| static int handle_rpc_call(struct msm_rpc_server *server, |
| struct rpc_request_hdr *req, |
| struct msm_rpc_xdr *xdr); |
| |
| static int ping_apps_data_cb(struct msm_rpc_server *server, |
| struct msm_rpc_client_info *clnt_info, |
| struct ping_apps_data_cb_arg *arg, |
| struct ping_apps_data_cb_ret *ret); |
| |
| static int ping_apps_register_cb(struct msm_rpc_server *server, |
| struct msm_rpc_client_info *clnt_info, |
| struct ping_apps_register_cb_arg *arg, |
| void *ret); |
| |
| static struct msm_rpc_server rpc_server = { |
| .prog = PING_APPS_PROG, |
| .vers = PING_APPS_VERS, |
| .rpc_call2 = handle_rpc_call, |
| }; |
| |
| static void handle_ping_apps_data_cb(struct ping_apps_cb_entry *cb_entry) |
| { |
| struct ping_apps_data_cb_arg arg; |
| struct ping_apps_data_cb_ret ret; |
| uint32_t my_sum = 0; |
| uint32_t *my_data; |
| int i; |
| |
| if (cb_entry->num > 0) { |
| cb_entry->num--; |
| arg.cb_id = cb_entry->cb_id; |
| arg.size = ((struct ping_apps_data_cb_info *) |
| (cb_entry->cb_info))->size; |
| |
| my_data = kmalloc((arg.size * sizeof(uint32_t)), GFP_KERNEL); |
| if (!my_data) |
| return; |
| |
| for (i = 0; i < arg.size; i++) { |
| my_data[i] = (42 + i); |
| my_sum ^= (42 + i); |
| } |
| arg.data = my_data; |
| arg.sum = my_sum; |
| |
| ((int (*)(struct msm_rpc_server *, |
| struct msm_rpc_client_info *, |
| struct ping_apps_data_cb_arg *, |
| struct ping_apps_data_cb_ret *)) |
| ((struct ping_apps_data_cb_info *) |
| (cb_entry->cb_info))->cb_func)(&rpc_server, |
| &cb_entry->clnt_info, |
| &arg, &ret); |
| pr_info("%s: cb_id = %d, ret = %d\n", |
| __func__, arg.cb_id, ret.result); |
| kfree(my_data); |
| } |
| } |
| |
| static void handle_ping_apps_register_cb(struct ping_apps_cb_entry *cb_entry) |
| { |
| struct ping_apps_register_cb_arg arg; |
| |
| if (cb_entry->num > 0) { |
| cb_entry->num--; |
| arg.cb_id = cb_entry->cb_id; |
| arg.num = cb_entry->num; |
| |
| pr_info("%s: cb_id = %d, num = %d\n", |
| __func__, arg.cb_id, arg.num); |
| ((int (*)(struct msm_rpc_server *, |
| struct msm_rpc_client_info *, |
| struct ping_apps_register_cb_arg *, |
| void *))cb_entry->cb_info)(&rpc_server, |
| &cb_entry->clnt_info, |
| &arg, NULL); |
| } |
| |
| } |
| |
| static int ping_apps_cb_process_thread(void *data) |
| { |
| struct ping_apps_cb_entry *cb_entry; |
| uint32_t sleep_time; |
| uint32_t time_slept = 0; |
| |
| pr_info("%s: thread started\n", __func__); |
| for (;;) { |
| sleep_time = 1000; |
| mutex_lock(&cb_entry_list_lock); |
| list_for_each_entry(cb_entry, &cb_entry_list, list) { |
| if (cb_entry->time_to_next_cb <= time_slept) { |
| cb_entry->cb_func(cb_entry); |
| cb_entry->time_to_next_cb = |
| cb_entry->interval_ms; |
| } else |
| cb_entry->time_to_next_cb -= time_slept; |
| |
| if (cb_entry->time_to_next_cb < sleep_time) |
| sleep_time = cb_entry->time_to_next_cb; |
| } |
| mutex_unlock(&cb_entry_list_lock); |
| |
| msleep(sleep_time); |
| time_slept = sleep_time; |
| } |
| |
| do_exit(0); |
| } |
| |
| static int ping_apps_data_register(struct ping_apps_data_arg *arg, |
| struct ping_apps_data_ret *ret) |
| { |
| int i; |
| |
| ret->result = 0; |
| for (i = 0; i < arg->size; i++) |
| ret->result ^= arg->data[i]; |
| |
| return 0; |
| } |
| |
| static int ping_apps_data_cb_reg(struct ping_apps_data_cb_reg_arg *arg, |
| struct ping_apps_data_cb_reg_ret *ret) |
| { |
| struct ping_apps_cb_entry *cb_entry; |
| struct ping_apps_data_cb_info *cb_info; |
| |
| cb_entry = kmalloc(sizeof(*cb_entry), GFP_KERNEL); |
| if (!cb_entry) |
| return -ENOMEM; |
| |
| cb_entry->cb_info = kmalloc(sizeof(struct ping_apps_data_cb_info), |
| GFP_KERNEL); |
| if (!cb_entry->cb_info) { |
| kfree(cb_entry); |
| return -ENOMEM; |
| } |
| cb_info = (struct ping_apps_data_cb_info *)cb_entry->cb_info; |
| |
| INIT_LIST_HEAD(&cb_entry->list); |
| cb_entry->cb_func = handle_ping_apps_data_cb; |
| cb_entry->cb_id = arg->cb_id; |
| cb_entry->num = arg->num; |
| cb_entry->interval_ms = arg->interval_ms; |
| cb_entry->time_to_next_cb = arg->interval_ms; |
| cb_info->cb_func = ping_apps_data_cb; |
| cb_info->size = arg->size; |
| cb_info->num_tasks = arg->num_tasks; |
| |
| mutex_lock(&cb_entry_list_lock); |
| list_add_tail(&cb_entry->list, &cb_entry_list); |
| mutex_unlock(&cb_entry_list_lock); |
| |
| msm_rpc_server_get_requesting_client(&cb_entry->clnt_info); |
| |
| if (IS_ERR(server_thread)) |
| server_thread = kthread_run(ping_apps_cb_process_thread, |
| NULL, "kpingrpccbprocessd"); |
| if (IS_ERR(server_thread)) { |
| kfree(cb_entry); |
| return PTR_ERR(server_thread); |
| } |
| |
| ret->result = 1; |
| return 0; |
| } |
| |
| static int ping_apps_data_cb_unreg(struct ping_apps_data_cb_unreg_arg *arg, |
| struct ping_apps_data_cb_unreg_ret *ret) |
| { |
| struct ping_apps_cb_entry *cb_entry, *tmp_cb_entry; |
| |
| mutex_lock(&cb_entry_list_lock); |
| list_for_each_entry_safe(cb_entry, tmp_cb_entry, |
| &cb_entry_list, list) { |
| if (cb_entry->cb_id == arg->cb_id) { |
| list_del(&cb_entry->list); |
| kfree(cb_entry->cb_info); |
| kfree(cb_entry); |
| break; |
| } |
| } |
| mutex_unlock(&cb_entry_list_lock); |
| |
| ret->result = 1; |
| return 0; |
| } |
| |
| static int ping_apps_register(struct ping_apps_register_arg *arg, |
| struct ping_apps_register_ret *ret) |
| { |
| struct ping_apps_cb_entry *cb_entry; |
| |
| cb_entry = kmalloc(sizeof(*cb_entry), GFP_KERNEL); |
| if (!cb_entry) |
| return -ENOMEM; |
| |
| INIT_LIST_HEAD(&cb_entry->list); |
| cb_entry->cb_func = handle_ping_apps_register_cb; |
| cb_entry->cb_info = ping_apps_register_cb; |
| cb_entry->cb_id = arg->cb_id; |
| cb_entry->num = arg->num; |
| cb_entry->interval_ms = 100; |
| cb_entry->time_to_next_cb = 100; |
| |
| mutex_lock(&cb_entry_list_lock); |
| list_add_tail(&cb_entry->list, &cb_entry_list); |
| mutex_unlock(&cb_entry_list_lock); |
| |
| msm_rpc_server_get_requesting_client(&cb_entry->clnt_info); |
| |
| if (IS_ERR(server_thread)) |
| server_thread = kthread_run(ping_apps_cb_process_thread, |
| NULL, "kpingrpccbprocessd"); |
| if (IS_ERR(server_thread)) { |
| kfree(cb_entry); |
| return PTR_ERR(server_thread); |
| } |
| |
| ret->result = 1; |
| return 0; |
| } |
| |
| static int ping_apps_unregister(struct ping_apps_unregister_arg *arg, |
| struct ping_apps_unregister_ret *ret) |
| { |
| struct ping_apps_cb_entry *cb_entry, *tmp_cb_entry; |
| |
| mutex_lock(&cb_entry_list_lock); |
| list_for_each_entry_safe(cb_entry, tmp_cb_entry, |
| &cb_entry_list, list) { |
| if (cb_entry->cb_id == arg->cb_id) { |
| list_del(&cb_entry->list); |
| kfree(cb_entry); |
| break; |
| } |
| } |
| mutex_unlock(&cb_entry_list_lock); |
| |
| ret->result = 1; |
| return 0; |
| } |
| |
| static int ping_apps_data_cb_arg_func(struct msm_rpc_server *server, |
| struct msm_rpc_xdr *xdr, void *data) |
| { |
| struct ping_apps_data_cb_arg *arg = data; |
| |
| xdr_send_uint32(xdr, &arg->cb_id); |
| xdr_send_array(xdr, (void **)&arg->data, &arg->size, 64, |
| sizeof(uint32_t), (void *)xdr_send_uint32); |
| xdr_send_uint32(xdr, &arg->size); |
| xdr_send_uint32(xdr, &arg->sum); |
| |
| return 0; |
| } |
| |
| static int ping_apps_data_cb_ret_func(struct msm_rpc_server *server, |
| struct msm_rpc_xdr *xdr, void *data) |
| { |
| struct ping_apps_data_cb_ret *ret = data; |
| |
| xdr_recv_uint32(xdr, &ret->result); |
| |
| return 0; |
| } |
| |
| static int ping_apps_data_cb(struct msm_rpc_server *server, |
| struct msm_rpc_client_info *clnt_info, |
| struct ping_apps_data_cb_arg *arg, |
| struct ping_apps_data_cb_ret *ret) |
| { |
| return msm_rpc_server_cb_req2(server, clnt_info, |
| PING_APPS_DATA_CB, |
| ping_apps_data_cb_arg_func, arg, |
| ping_apps_data_cb_ret_func, ret, -1); |
| } |
| |
| static int ping_apps_register_cb_arg(struct msm_rpc_server *server, |
| struct msm_rpc_xdr *xdr, void *data) |
| { |
| struct ping_apps_register_cb_arg *arg = data; |
| |
| xdr_send_uint32(xdr, &arg->cb_id); |
| xdr_send_int32(xdr, &arg->num); |
| |
| return 0; |
| } |
| |
| static int ping_apps_register_cb(struct msm_rpc_server *server, |
| struct msm_rpc_client_info *clnt_info, |
| struct ping_apps_register_cb_arg *arg, |
| void *ret) |
| { |
| return msm_rpc_server_cb_req2(server, clnt_info, |
| PING_APPS_REG_CB, |
| ping_apps_register_cb_arg, |
| arg, NULL, NULL, -1); |
| } |
| |
| static int handle_ping_apps_data_register(struct msm_rpc_server *server, |
| struct rpc_request_hdr *req, |
| struct msm_rpc_xdr *xdr) |
| { |
| uint32_t rc; |
| struct ping_apps_data_arg arg; |
| struct ping_apps_data_ret ret; |
| |
| pr_info("%s: request received\n", __func__); |
| |
| xdr_recv_array(xdr, (void **)&arg.data, &arg.size, 64, |
| sizeof(uint32_t), (void *)xdr_recv_uint32); |
| xdr_recv_uint32(xdr, &arg.size); |
| |
| rc = ping_apps_data_register(&arg, &ret); |
| if (rc < 0) |
| goto free_and_return; |
| |
| xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); |
| xdr_send_uint32(xdr, &ret.result); |
| rc = xdr_send_msg(xdr); |
| if (rc < 0) |
| pr_info("%s: sending reply failed\n", __func__); |
| else |
| rc = 1; |
| |
| free_and_return: |
| kfree(arg.data); |
| return rc; |
| } |
| |
| static int handle_ping_apps_data_cb_reg(struct msm_rpc_server *server, |
| struct rpc_request_hdr *req, |
| struct msm_rpc_xdr *xdr) |
| { |
| uint32_t rc; |
| struct ping_apps_data_cb_reg_arg arg; |
| struct ping_apps_data_cb_reg_ret ret; |
| |
| pr_info("%s: request received\n", __func__); |
| |
| xdr_recv_uint32(xdr, &arg.cb_id); |
| xdr_recv_uint32(xdr, &arg.num); |
| xdr_recv_uint32(xdr, &arg.size); |
| xdr_recv_uint32(xdr, &arg.interval_ms); |
| xdr_recv_uint32(xdr, &arg.num_tasks); |
| |
| rc = ping_apps_data_cb_reg(&arg, &ret); |
| if (rc < 0) |
| return rc; |
| |
| xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); |
| xdr_send_uint32(xdr, &ret.result); |
| rc = xdr_send_msg(xdr); |
| if (rc < 0) |
| pr_info("%s: sending reply failed\n", __func__); |
| else |
| rc = 1; |
| |
| return rc; |
| } |
| |
| static int handle_ping_apps_data_cb_unreg(struct msm_rpc_server *server, |
| struct rpc_request_hdr *req, |
| struct msm_rpc_xdr *xdr) |
| { |
| uint32_t rc; |
| struct ping_apps_data_cb_unreg_arg arg; |
| struct ping_apps_data_cb_unreg_ret ret; |
| |
| pr_info("%s: request received\n", __func__); |
| |
| xdr_recv_uint32(xdr, &arg.cb_id); |
| |
| rc = ping_apps_data_cb_unreg(&arg, &ret); |
| if (rc < 0) |
| return rc; |
| |
| xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); |
| xdr_send_uint32(xdr, &ret.result); |
| rc = xdr_send_msg(xdr); |
| if (rc < 0) |
| pr_info("%s: sending reply failed\n", __func__); |
| else |
| rc = 1; |
| |
| return rc; |
| } |
| |
| static int handle_ping_apps_register(struct msm_rpc_server *server, |
| struct rpc_request_hdr *req, |
| struct msm_rpc_xdr *xdr) |
| { |
| uint32_t rc; |
| struct ping_apps_register_arg arg; |
| struct ping_apps_register_ret ret; |
| |
| pr_info("%s: request received\n", __func__); |
| |
| xdr_recv_uint32(xdr, &arg.cb_id); |
| xdr_recv_int32(xdr, &arg.num); |
| |
| rc = ping_apps_register(&arg, &ret); |
| if (rc < 0) |
| return rc; |
| |
| xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); |
| xdr_send_uint32(xdr, &ret.result); |
| rc = xdr_send_msg(xdr); |
| if (rc < 0) |
| pr_info("%s: sending reply failed\n", __func__); |
| else |
| rc = 1; |
| |
| return rc; |
| } |
| |
| static int handle_ping_apps_unregister(struct msm_rpc_server *server, |
| struct rpc_request_hdr *req, |
| struct msm_rpc_xdr *xdr) |
| { |
| uint32_t rc; |
| struct ping_apps_unregister_arg arg; |
| struct ping_apps_unregister_ret ret; |
| |
| pr_info("%s: request received\n", __func__); |
| |
| xdr_recv_uint32(xdr, &arg.cb_id); |
| |
| rc = ping_apps_unregister(&arg, &ret); |
| if (rc < 0) |
| return rc; |
| |
| xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); |
| xdr_send_uint32(xdr, &ret.result); |
| rc = xdr_send_msg(xdr); |
| if (rc < 0) |
| pr_info("%s: sending reply failed\n", __func__); |
| else |
| rc = 1; |
| |
| return rc; |
| } |
| |
| static int handle_rpc_call(struct msm_rpc_server *server, |
| struct rpc_request_hdr *req, |
| struct msm_rpc_xdr *xdr) |
| { |
| switch (req->procedure) { |
| case PING_APPS_NULL: |
| pr_info("%s: null procedure request received\n", __func__); |
| return 0; |
| case PING_APPS_DATA: |
| return handle_ping_apps_data_register(server, req, xdr); |
| case PING_APPS_REG: |
| return handle_ping_apps_register(server, req, xdr); |
| case PING_APPS_UNREG: |
| return handle_ping_apps_unregister(server, req, xdr); |
| case PING_APPS_DATA_CB_REG: |
| return handle_ping_apps_data_cb_reg(server, req, xdr); |
| case PING_APPS_DATA_CB_UNREG: |
| return handle_ping_apps_data_cb_unreg(server, req, xdr); |
| default: |
| return -ENODEV; |
| } |
| } |
| |
| static int __init ping_apps_server_init(void) |
| { |
| INIT_LIST_HEAD(&cb_entry_list); |
| server_thread = ERR_PTR(-1); |
| return msm_rpc_create_server2(&rpc_server); |
| } |
| |
| module_init(ping_apps_server_init); |
| |
| MODULE_DESCRIPTION("PING APPS SERVER Driver"); |
| MODULE_LICENSE("GPL v2"); |