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