/* Copyright (c) 2009, 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. * */ #include #include #include #include #include #include #include #include #include #include static struct adie_svc_client adie_client[ADIE_SVC_MAX_CLIENTS]; static DEFINE_MUTEX(adie_client_lock); static int adie_svc_process_cb(struct msm_rpc_client *client, void *buffer, int in_size) { int rc, id; uint32_t accept_status; struct rpc_request_hdr *req; struct adie_svc_client_register_cb_cb_args arg, *buf_ptr; req = (struct rpc_request_hdr *)buffer; for (id = 0; id < ADIE_SVC_MAX_CLIENTS; id++) { if (adie_client[id].rpc_client == client) break; } if (id == ADIE_SVC_MAX_CLIENTS) { MM_ERR("RPC reply with invalid rpc client\n"); accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; goto err; } buf_ptr = (struct adie_svc_client_register_cb_cb_args *)(req + 1); arg.cb_id = be32_to_cpu(buf_ptr->cb_id); arg.size = be32_to_cpu(buf_ptr->size); arg.client_id = be32_to_cpu(buf_ptr->client_id); arg.adie_block = be32_to_cpu(buf_ptr->adie_block); arg.status = be32_to_cpu(buf_ptr->status); arg.client_operation = be32_to_cpu(buf_ptr->client_operation); if (arg.cb_id != adie_client[id].cb_id) { MM_ERR("RPC reply with invalid invalid cb_id\n"); accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; goto err; } mutex_lock(&adie_client[id].lock); switch (arg.client_operation) { case ADIE_SVC_REGISTER_CLIENT: MM_DBG("ADIE_SVC_REGISTER_CLIENT callback\n"); adie_client[id].client_id = arg.client_id; break; case ADIE_SVC_DEREGISTER_CLIENT: MM_DBG("ADIE_SVC_DEREGISTER_CLIENT callback\n"); break; case ADIE_SVC_CONFIG_ADIE_BLOCK: MM_DBG("ADIE_SVC_CONFIG_ADIE_BLOCK callback\n"); if (adie_client[id].client_id != arg.client_id) { mutex_unlock(&adie_client[id].lock); accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; goto err; } break; default: accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; goto err; } adie_client[id].status = arg.status; adie_client[id].adie_svc_cb_done = 1; mutex_unlock(&adie_client[id].lock); wake_up(&adie_client[id].wq); accept_status = RPC_ACCEPTSTAT_SUCCESS; err: msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), accept_status); rc = msm_rpc_send_accepted_reply(client, 0); if (rc) MM_ERR("%s: send accepted reply failed: %d\n", __func__, rc); return rc; } static int adie_svc_rpc_cb_func(struct msm_rpc_client *client, void *buffer, int in_size) { int rc = 0; struct rpc_request_hdr *req; req = (struct rpc_request_hdr *)buffer; MM_DBG("procedure received to rpc cb %d\n", be32_to_cpu(req->procedure)); switch (be32_to_cpu(req->procedure)) { case ADIE_SVC_CLIENT_STATUS_FUNC_PTR_TYPE_PROC: rc = adie_svc_process_cb(client, buffer, in_size); break; default: MM_ERR("%s: procedure not supported %d\n", __func__, be32_to_cpu(req->procedure)); msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), RPC_ACCEPTSTAT_PROC_UNAVAIL); rc = msm_rpc_send_accepted_reply(client, 0); if (rc) MM_ERR("%s: sending reply failed: %d\n", __func__, rc); break; } return rc; } static int adie_svc_client_register_arg(struct msm_rpc_client *client, void *buf, void *data) { struct adie_svc_client_register_cb_args *arg; arg = (struct adie_svc_client_register_cb_args *)data; *((int *)buf) = cpu_to_be32((int)arg->cb_id); return sizeof(int); } static int adie_svc_client_deregister_arg(struct msm_rpc_client *client, void *buf, void *data) { struct adie_svc_client_deregister_cb_args *arg; arg = (struct adie_svc_client_deregister_cb_args *)data; *((int *)buf) = cpu_to_be32(arg->client_id); return sizeof(int); } static int adie_svc_config_adie_block_arg(struct msm_rpc_client *client, void *buf, void *data) { struct adie_svc_config_adie_block_cb_args *arg; int size = 0; arg = (struct adie_svc_config_adie_block_cb_args *)data; *((int *)buf) = cpu_to_be32(arg->client_id); size += sizeof(int); buf += sizeof(int); *((int *)buf) = cpu_to_be32(arg->adie_block); size += sizeof(int); buf += sizeof(int); *((int *)buf) = cpu_to_be32(arg->config); size += sizeof(int); return size; } /* Returns : client id on success * and -1 on failure */ int adie_svc_get(void) { int id, rc = 0; struct adie_svc_client_register_cb_args arg; mutex_lock(&adie_client_lock); for (id = 0; id < ADIE_SVC_MAX_CLIENTS; id++) { if (adie_client[id].client_id == -1 && adie_client[id].rpc_client == NULL) break; } if (id == ADIE_SVC_MAX_CLIENTS) { mutex_unlock(&adie_client_lock); return -1; } mutex_lock(&adie_client[id].lock); adie_client[id].rpc_client = msm_rpc_register_client("adie_client", ADIE_SVC_PROG, ADIE_SVC_VERS, 1, adie_svc_rpc_cb_func); if (IS_ERR(adie_client[id].rpc_client)) { MM_ERR("Failed to register RPC client\n"); adie_client[id].rpc_client = NULL; mutex_unlock(&adie_client[id].lock); mutex_unlock(&adie_client_lock); return -1; } mutex_unlock(&adie_client_lock); adie_client[id].adie_svc_cb_done = 0; arg.cb_id = id; adie_client[id].cb_id = arg.cb_id; mutex_unlock(&adie_client[id].lock); rc = msm_rpc_client_req(adie_client[id].rpc_client, SND_ADIE_SVC_CLIENT_REGISTER_PROC, adie_svc_client_register_arg, &arg, NULL, NULL, -1); if (!rc) { rc = wait_event_interruptible(adie_client[id].wq, adie_client[id].adie_svc_cb_done); mutex_lock(&adie_client[id].lock); if (unlikely(rc < 0)) { if (rc == -ERESTARTSYS) MM_ERR("wait_event_interruptible " "returned -ERESTARTSYS\n"); else MM_ERR("wait_event_interruptible " "returned error\n"); rc = -1; goto err; } MM_DBG("Status %d received from CB function, id %d rc %d\n", adie_client[id].status, adie_client[id].client_id, rc); rc = id; if (adie_client[id].status == ADIE_SVC_STATUS_FAILURE) { MM_ERR("Received failed status for register request\n"); rc = -1; } else goto done; } else { MM_ERR("Failed to send register client request\n"); rc = -1; mutex_lock(&adie_client[id].lock); } err: msm_rpc_unregister_client(adie_client[id].rpc_client); adie_client[id].rpc_client = NULL; adie_client[id].client_id = -1; adie_client[id].cb_id = MSM_RPC_CLIENT_NULL_CB_ID; adie_client[id].adie_svc_cb_done = 0; done: mutex_unlock(&adie_client[id].lock); return rc; } EXPORT_SYMBOL(adie_svc_get); /* Returns: 0 on succes and * -1 on failure */ int adie_svc_put(int id) { int rc = 0; struct adie_svc_client_deregister_cb_args arg; if (id < 0 || id >= ADIE_SVC_MAX_CLIENTS) return -1; mutex_lock(&adie_client[id].lock); if (adie_client[id].client_id == -1 || adie_client[id].rpc_client == NULL) { mutex_unlock(&adie_client[id].lock); return -1; } arg.client_id = adie_client[id].client_id; adie_client[id].adie_svc_cb_done = 0; mutex_unlock(&adie_client[id].lock); rc = msm_rpc_client_req(adie_client[id].rpc_client, SND_ADIE_SVC_CLIENT_DEREGISTER_PROC, adie_svc_client_deregister_arg, &arg, NULL, NULL, -1); if (!rc) { rc = wait_event_interruptible(adie_client[id].wq, adie_client[id].adie_svc_cb_done); if (unlikely(rc < 0)) { if (rc == -ERESTARTSYS) MM_ERR("wait_event_interruptible " "returned -ERESTARTSYS\n"); else MM_ERR("wait_event_interruptible " "returned error\n"); rc = -1; goto err; } MM_DBG("Status received from CB function\n"); mutex_lock(&adie_client[id].lock); if (adie_client[id].status == ADIE_SVC_STATUS_FAILURE) { rc = -1; } else { msm_rpc_unregister_client(adie_client[id].rpc_client); adie_client[id].rpc_client = NULL; adie_client[id].client_id = -1; adie_client[id].cb_id = MSM_RPC_CLIENT_NULL_CB_ID; adie_client[id].adie_svc_cb_done = 0; } mutex_unlock(&adie_client[id].lock); } else { MM_ERR("Failed to send deregister client request\n"); rc = -1; } err: return rc; } EXPORT_SYMBOL(adie_svc_put); /* Returns: 0 on success * 2 already in use * -1 on failure */ int adie_svc_config_adie_block(int id, enum adie_block_enum_type adie_block_type, bool enable) { int rc = 0; struct adie_svc_config_adie_block_cb_args arg; if (id < 0 || id >= ADIE_SVC_MAX_CLIENTS) return -1; mutex_lock(&adie_client[id].lock); if (adie_client[id].client_id == -1 || adie_client[id].rpc_client == NULL) { mutex_unlock(&adie_client[id].lock); return -1; } arg.client_id = adie_client[id].client_id; arg.adie_block = adie_block_type; arg.config = (enum adie_config_enum_type)enable; adie_client[id].adie_svc_cb_done = 0; mutex_unlock(&adie_client[id].lock); rc = msm_rpc_client_req(adie_client[id].rpc_client, SND_ADIE_SVC_CONFIG_ADIE_BLOCK_PROC, adie_svc_config_adie_block_arg, &arg, NULL, NULL, -1); if (!rc) { rc = wait_event_interruptible(adie_client[id].wq, adie_client[id].adie_svc_cb_done); if (unlikely(rc < 0)) { if (rc == -ERESTARTSYS) MM_ERR("wait_event_interruptible " "returned -ERESTARTSYS\n"); else MM_ERR("wait_event_interruptible " "returned error\n"); rc = -1; goto err; } MM_DBG("Status received from CB function\n"); mutex_lock(&adie_client[id].lock); if (adie_client[id].status == ADIE_SVC_STATUS_FAILURE) rc = -1; else rc = adie_client[id].status; mutex_unlock(&adie_client[id].lock); } else { MM_ERR("Failed to send adie block config request\n"); rc = -1; } err: return rc; } EXPORT_SYMBOL(adie_svc_config_adie_block); #ifdef CONFIG_DEBUG_FS struct dentry *dentry; static ssize_t snd_adie_debug_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; return 0; } static ssize_t snd_adie_debug_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int rc = 0, op = 0; int id = 0, adie_block = 0, config = 1; sscanf(buf, "%d %d %d %d", &op, &id, &adie_block, &config); MM_INFO("\nUser input: op %d id %d block %d config %d\n", op, id, adie_block, config); switch (op) { case ADIE_SVC_REGISTER_CLIENT: MM_INFO("ADIE_SVC_REGISTER_CLIENT\n"); rc = adie_svc_get(); if (rc >= 0) MM_INFO("Client registered: %d\n", rc); else MM_ERR("Failed registering client\n"); break; case ADIE_SVC_DEREGISTER_CLIENT: MM_INFO("ADIE_SVC_DEREGISTER_CLIENT: %d\n", id); rc = adie_svc_put(id); if (!rc) MM_INFO("Client %d deregistered\n", id); else MM_ERR("Failed unregistering the client: %d\n", id); break; case ADIE_SVC_CONFIG_ADIE_BLOCK: MM_INFO("ADIE_SVC_CONFIG_ADIE_BLOCK: id %d adie_block %d \ config %d\n", id, adie_block, config); rc = adie_svc_config_adie_block(id, (enum adie_block_enum_type)adie_block, (bool)config); if (!rc) MM_INFO("ADIE block %d %s", adie_block, config ? "enabled\n" : "disabled\n"); else if (rc == 2) MM_INFO("ADIE block %d already in use\n", adie_block); else MM_ERR("ERROR configuring the ADIE block\n"); break; default: MM_INFO("Invalid operation\n"); } return count; } static ssize_t snd_adie_debug_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { static char buffer[1024]; const int debug_bufmax = sizeof(buffer); int id, n = 0; n += scnprintf(buffer + n, debug_bufmax - n, "LIST OF CLIENTS\n"); for (id = 0; id < ADIE_SVC_MAX_CLIENTS ; id++) { if (adie_client[id].client_id != -1 && adie_client[id].rpc_client != NULL) { n += scnprintf(buffer + n, debug_bufmax - n, "id %d rpc client 0x%08x\n", id, (uint32_t)adie_client[id].rpc_client); } } buffer[n] = 0; return simple_read_from_buffer(buf, count, ppos, buffer, n); } static const struct file_operations snd_adie_debug_fops = { .read = snd_adie_debug_read, .open = snd_adie_debug_open, .write = snd_adie_debug_write, }; #endif static void __exit snd_adie_exit(void) { #ifdef CONFIG_DEBUG_FS if (dentry) debugfs_remove(dentry); #endif } static int __init snd_adie_init(void) { int id; #ifdef CONFIG_DEBUG_FS char name[sizeof "msm_snd_adie"]; snprintf(name, sizeof name, "msm_snd_adie"); dentry = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUGO, NULL, NULL, &snd_adie_debug_fops); if (IS_ERR(dentry)) MM_DBG("debugfs_create_file failed\n"); #endif for (id = 0; id < ADIE_SVC_MAX_CLIENTS; id++) { adie_client[id].client_id = -1; adie_client[id].cb_id = MSM_RPC_CLIENT_NULL_CB_ID; adie_client[id].status = 0; adie_client[id].adie_svc_cb_done = 0; mutex_init(&adie_client[id].lock); init_waitqueue_head(&adie_client[id].wq); adie_client[id].rpc_client = NULL; } return 0; } module_init(snd_adie_init); module_exit(snd_adie_exit);