481 lines
13 KiB
C
481 lines
13 KiB
C
|
/* 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);
|