| /* 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 <linux/module.h> |
| #include <linux/fs.h> |
| #include <linux/miscdevice.h> |
| #include <linux/uaccess.h> |
| #include <linux/kthread.h> |
| #include <linux/delay.h> |
| #include <mach/msm_rpcrouter.h> |
| #include <linux/debugfs.h> |
| #include <mach/qdsp5/snd_adie.h> |
| #include <mach/debug_mm.h> |
| |
| 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); |