314 lines
7.2 KiB
C
314 lines
7.2 KiB
C
|
/* arch/arm/mach-msm/qdsp5/audmgr.c
|
||
|
*
|
||
|
* interface to "audmgr" service on the baseband cpu
|
||
|
*
|
||
|
* Copyright (C) 2008 Google, Inc.
|
||
|
*
|
||
|
* This software is licensed under the terms of the GNU General Public
|
||
|
* License version 2, as published by the Free Software Foundation, and
|
||
|
* may be copied, distributed, and modified under those terms.
|
||
|
*
|
||
|
* 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/uaccess.h>
|
||
|
#include <linux/kthread.h>
|
||
|
#include <linux/wait.h>
|
||
|
|
||
|
#include <asm/atomic.h>
|
||
|
#include <mach/msm_rpcrouter.h>
|
||
|
|
||
|
#include "audmgr.h"
|
||
|
|
||
|
#define STATE_CLOSED 0
|
||
|
#define STATE_DISABLED 1
|
||
|
#define STATE_ENABLING 2
|
||
|
#define STATE_ENABLED 3
|
||
|
#define STATE_DISABLING 4
|
||
|
#define STATE_ERROR 5
|
||
|
|
||
|
static void rpc_ack(struct msm_rpc_endpoint *ept, uint32_t xid)
|
||
|
{
|
||
|
uint32_t rep[6];
|
||
|
|
||
|
rep[0] = cpu_to_be32(xid);
|
||
|
rep[1] = cpu_to_be32(1);
|
||
|
rep[2] = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
|
||
|
rep[3] = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
|
||
|
rep[4] = 0;
|
||
|
rep[5] = 0;
|
||
|
|
||
|
msm_rpc_write(ept, rep, sizeof(rep));
|
||
|
}
|
||
|
|
||
|
static void process_audmgr_callback(struct audmgr *am,
|
||
|
struct rpc_audmgr_cb_func_ptr *args,
|
||
|
int len)
|
||
|
{
|
||
|
if (len < (sizeof(uint32_t) * 3))
|
||
|
return;
|
||
|
if (be32_to_cpu(args->set_to_one) != 1)
|
||
|
return;
|
||
|
|
||
|
switch (be32_to_cpu(args->status)) {
|
||
|
case RPC_AUDMGR_STATUS_READY:
|
||
|
if (len < sizeof(uint32_t) * 4)
|
||
|
break;
|
||
|
am->handle = be32_to_cpu(args->u.handle);
|
||
|
pr_info("audmgr: rpc READY handle=0x%08x\n", am->handle);
|
||
|
break;
|
||
|
case RPC_AUDMGR_STATUS_CODEC_CONFIG: {
|
||
|
uint32_t volume;
|
||
|
if (len < sizeof(uint32_t) * 4)
|
||
|
break;
|
||
|
volume = be32_to_cpu(args->u.volume);
|
||
|
pr_info("audmgr: rpc CODEC_CONFIG volume=0x%08x\n", volume);
|
||
|
am->state = STATE_ENABLED;
|
||
|
wake_up(&am->wait);
|
||
|
break;
|
||
|
}
|
||
|
case RPC_AUDMGR_STATUS_PENDING:
|
||
|
pr_err("audmgr: PENDING?\n");
|
||
|
break;
|
||
|
case RPC_AUDMGR_STATUS_SUSPEND:
|
||
|
pr_err("audmgr: SUSPEND?\n");
|
||
|
break;
|
||
|
case RPC_AUDMGR_STATUS_FAILURE:
|
||
|
pr_err("audmgr: FAILURE\n");
|
||
|
break;
|
||
|
case RPC_AUDMGR_STATUS_VOLUME_CHANGE:
|
||
|
pr_err("audmgr: VOLUME_CHANGE?\n");
|
||
|
break;
|
||
|
case RPC_AUDMGR_STATUS_DISABLED:
|
||
|
pr_err("audmgr: DISABLED\n");
|
||
|
am->state = STATE_DISABLED;
|
||
|
wake_up(&am->wait);
|
||
|
break;
|
||
|
case RPC_AUDMGR_STATUS_ERROR:
|
||
|
pr_err("audmgr: ERROR?\n");
|
||
|
am->state = STATE_ERROR;
|
||
|
wake_up(&am->wait);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void process_rpc_request(uint32_t proc, uint32_t xid,
|
||
|
void *data, int len, void *private)
|
||
|
{
|
||
|
struct audmgr *am = private;
|
||
|
uint32_t *x = data;
|
||
|
|
||
|
if (0) {
|
||
|
int n = len / 4;
|
||
|
pr_info("rpc_call proc %d:", proc);
|
||
|
while (n--)
|
||
|
printk(" %08x", be32_to_cpu(*x++));
|
||
|
printk("\n");
|
||
|
}
|
||
|
|
||
|
if (proc == AUDMGR_CB_FUNC_PTR)
|
||
|
process_audmgr_callback(am, data, len);
|
||
|
else
|
||
|
pr_err("audmgr: unknown rpc proc %d\n", proc);
|
||
|
rpc_ack(am->ept, xid);
|
||
|
}
|
||
|
|
||
|
#define RPC_TYPE_REQUEST 0
|
||
|
#define RPC_TYPE_REPLY 1
|
||
|
|
||
|
#define RPC_VERSION 2
|
||
|
|
||
|
#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
|
||
|
#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
|
||
|
#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
|
||
|
#define RPC_REPLY_SZ (sizeof(uint32_t) * 6)
|
||
|
|
||
|
static int audmgr_rpc_thread(void *data)
|
||
|
{
|
||
|
struct audmgr *am = data;
|
||
|
struct rpc_request_hdr *hdr = NULL;
|
||
|
uint32_t type;
|
||
|
int len;
|
||
|
|
||
|
pr_info("audmgr_rpc_thread() start\n");
|
||
|
|
||
|
while (!kthread_should_stop()) {
|
||
|
if (hdr) {
|
||
|
kfree(hdr);
|
||
|
hdr = NULL;
|
||
|
}
|
||
|
len = msm_rpc_read(am->ept, (void **) &hdr, -1, -1);
|
||
|
if (len < 0) {
|
||
|
pr_err("audmgr: rpc read failed (%d)\n", len);
|
||
|
break;
|
||
|
}
|
||
|
if (len < RPC_COMMON_HDR_SZ)
|
||
|
continue;
|
||
|
|
||
|
type = be32_to_cpu(hdr->type);
|
||
|
if (type == RPC_TYPE_REPLY) {
|
||
|
struct rpc_reply_hdr *rep = (void *) hdr;
|
||
|
uint32_t status;
|
||
|
if (len < RPC_REPLY_HDR_SZ)
|
||
|
continue;
|
||
|
status = be32_to_cpu(rep->reply_stat);
|
||
|
if (status == RPCMSG_REPLYSTAT_ACCEPTED) {
|
||
|
status = be32_to_cpu(rep->data.acc_hdr.accept_stat);
|
||
|
pr_info("audmgr: rpc_reply status %d\n", status);
|
||
|
} else {
|
||
|
pr_info("audmgr: rpc_reply denied!\n");
|
||
|
}
|
||
|
/* process reply */
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (len < RPC_REQUEST_HDR_SZ)
|
||
|
continue;
|
||
|
|
||
|
process_rpc_request(be32_to_cpu(hdr->procedure),
|
||
|
be32_to_cpu(hdr->xid),
|
||
|
(void *) (hdr + 1),
|
||
|
len - sizeof(*hdr),
|
||
|
data);
|
||
|
}
|
||
|
pr_info("audmgr_rpc_thread() exit\n");
|
||
|
if (hdr) {
|
||
|
kfree(hdr);
|
||
|
hdr = NULL;
|
||
|
}
|
||
|
am->task = NULL;
|
||
|
wake_up(&am->wait);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
struct audmgr_enable_msg {
|
||
|
struct rpc_request_hdr hdr;
|
||
|
struct rpc_audmgr_enable_client_args args;
|
||
|
};
|
||
|
|
||
|
struct audmgr_disable_msg {
|
||
|
struct rpc_request_hdr hdr;
|
||
|
uint32_t handle;
|
||
|
};
|
||
|
|
||
|
int audmgr_open(struct audmgr *am)
|
||
|
{
|
||
|
int rc;
|
||
|
|
||
|
if (am->state != STATE_CLOSED)
|
||
|
return 0;
|
||
|
|
||
|
am->ept = msm_rpc_connect(AUDMGR_PROG,
|
||
|
AUDMGR_VERS,
|
||
|
MSM_RPC_UNINTERRUPTIBLE | MSM_RPC_ENABLE_RECEIVE);
|
||
|
|
||
|
init_waitqueue_head(&am->wait);
|
||
|
|
||
|
if (IS_ERR(am->ept)) {
|
||
|
rc = PTR_ERR(am->ept);
|
||
|
am->ept = NULL;
|
||
|
pr_err("audmgr: failed to connect to audmgr svc\n");
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
am->task = kthread_run(audmgr_rpc_thread, am, "audmgr_rpc");
|
||
|
if (IS_ERR(am->task)) {
|
||
|
rc = PTR_ERR(am->task);
|
||
|
am->task = NULL;
|
||
|
msm_rpc_close(am->ept);
|
||
|
am->ept = NULL;
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
am->state = STATE_DISABLED;
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(audmgr_open);
|
||
|
|
||
|
int audmgr_close(struct audmgr *am)
|
||
|
{
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
EXPORT_SYMBOL(audmgr_close);
|
||
|
|
||
|
int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg)
|
||
|
{
|
||
|
struct audmgr_enable_msg msg;
|
||
|
int rc;
|
||
|
|
||
|
if (am->state == STATE_ENABLED)
|
||
|
return 0;
|
||
|
|
||
|
if (am->state == STATE_DISABLING)
|
||
|
pr_err("audmgr: state is DISABLING in enable?\n");
|
||
|
am->state = STATE_ENABLING;
|
||
|
|
||
|
msg.args.set_to_one = cpu_to_be32(1);
|
||
|
msg.args.tx_sample_rate = cpu_to_be32(cfg->tx_rate);
|
||
|
msg.args.rx_sample_rate = cpu_to_be32(cfg->rx_rate);
|
||
|
msg.args.def_method = cpu_to_be32(cfg->def_method);
|
||
|
msg.args.codec_type = cpu_to_be32(cfg->codec);
|
||
|
msg.args.snd_method = cpu_to_be32(cfg->snd_method);
|
||
|
msg.args.cb_func = cpu_to_be32(0x11111111);
|
||
|
msg.args.client_data = cpu_to_be32(0x11223344);
|
||
|
|
||
|
msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
|
||
|
AUDMGR_ENABLE_CLIENT);
|
||
|
|
||
|
rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
|
||
|
if (rc < 0)
|
||
|
return rc;
|
||
|
|
||
|
rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ);
|
||
|
if (rc == 0) {
|
||
|
pr_err("audmgr_enable: ARM9 did not reply to RPC am->state = %d\n", am->state);
|
||
|
BUG();
|
||
|
}
|
||
|
if (am->state == STATE_ENABLED)
|
||
|
return 0;
|
||
|
|
||
|
pr_err("audmgr: unexpected state %d while enabling?!\n", am->state);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
EXPORT_SYMBOL(audmgr_enable);
|
||
|
|
||
|
int audmgr_disable(struct audmgr *am)
|
||
|
{
|
||
|
struct audmgr_disable_msg msg;
|
||
|
int rc;
|
||
|
|
||
|
if (am->state == STATE_DISABLED)
|
||
|
return 0;
|
||
|
|
||
|
msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
|
||
|
AUDMGR_DISABLE_CLIENT);
|
||
|
msg.handle = cpu_to_be32(am->handle);
|
||
|
|
||
|
am->state = STATE_DISABLING;
|
||
|
|
||
|
rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
|
||
|
if (rc < 0)
|
||
|
return rc;
|
||
|
|
||
|
rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ);
|
||
|
if (rc == 0) {
|
||
|
pr_err("audmgr_disable: ARM9 did not reply to RPC am->state = %d\n", am->state);
|
||
|
BUG();
|
||
|
}
|
||
|
|
||
|
if (am->state == STATE_DISABLED)
|
||
|
return 0;
|
||
|
|
||
|
pr_err("audmgr: unexpected state %d while disabling?!\n", am->state);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
EXPORT_SYMBOL(audmgr_disable);
|