blob: 160ed93c2e33399c03a85425b499423403f7f88a [file] [log] [blame]
/* 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);