Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/qdsp5/audmgr.c b/arch/arm/mach-msm/qdsp5/audmgr.c
new file mode 100644
index 0000000..231a28c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audmgr.c
@@ -0,0 +1,365 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.c
+ *
+ * interface to "audmgr" service on the baseband cpu
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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 <linux/slab.h>
+
+#include <asm/atomic.h>
+#include <mach/msm_rpcrouter.h>
+
+#include "audmgr.h"
+#include <mach/debug_mm.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
+
+/* store information used across complete audmgr sessions */
+struct audmgr_global {
+ struct mutex *lock;
+ struct msm_rpc_endpoint *ept;
+ struct task_struct *task;
+ uint32_t rpc_version;
+};
+static DEFINE_MUTEX(audmgr_lock);
+
+static struct audmgr_global the_audmgr_state = {
+ .lock = &audmgr_lock,
+};
+
+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_global *amg,
+ struct rpc_audmgr_cb_func_ptr *args,
+ int len)
+{
+ struct audmgr *am;
+
+ /* Allow only if complete arguments recevied */
+ if (len < (sizeof(struct rpc_audmgr_cb_func_ptr)))
+ return;
+
+ /* Allow only if valid argument */
+ if (be32_to_cpu(args->set_to_one) != 1)
+ return;
+
+ am = (struct audmgr *) be32_to_cpu(args->client_data);
+
+ if (!am)
+ return;
+
+ switch (be32_to_cpu(args->status)) {
+ case RPC_AUDMGR_STATUS_READY:
+ am->handle = be32_to_cpu(args->u.handle);
+ MM_INFO("rpc READY handle=0x%08x\n", am->handle);
+ break;
+ case RPC_AUDMGR_STATUS_CODEC_CONFIG: {
+ uint32_t volume;
+ volume = be32_to_cpu(args->u.volume);
+ MM_INFO("rpc CODEC_CONFIG volume=0x%08x\n", volume);
+ am->state = STATE_ENABLED;
+ wake_up(&am->wait);
+ break;
+ }
+ case RPC_AUDMGR_STATUS_PENDING:
+ MM_ERR("PENDING?\n");
+ break;
+ case RPC_AUDMGR_STATUS_SUSPEND:
+ MM_ERR("SUSPEND?\n");
+ break;
+ case RPC_AUDMGR_STATUS_FAILURE:
+ MM_ERR("FAILURE\n");
+ break;
+ case RPC_AUDMGR_STATUS_VOLUME_CHANGE:
+ MM_ERR("VOLUME_CHANGE?\n");
+ break;
+ case RPC_AUDMGR_STATUS_DISABLED:
+ MM_ERR("DISABLED\n");
+ am->state = STATE_DISABLED;
+ wake_up(&am->wait);
+ break;
+ case RPC_AUDMGR_STATUS_ERROR:
+ MM_ERR("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_global *amg = private;
+
+ if (proc == AUDMGR_CB_FUNC_PTR)
+ process_audmgr_callback(amg, data, len);
+ else
+ MM_ERR("unknown rpc proc %d\n", proc);
+ rpc_ack(amg->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_global *amg = data;
+ struct rpc_request_hdr *hdr = NULL;
+ uint32_t type;
+ int len;
+
+ MM_INFO("start\n");
+
+ while (!kthread_should_stop()) {
+ if (hdr) {
+ kfree(hdr);
+ hdr = NULL;
+ }
+ len = msm_rpc_read(amg->ept, (void **) &hdr, -1, -1);
+ if (len < 0) {
+ MM_ERR("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);
+ MM_INFO("rpc_reply status %d\n", status);
+ } else {
+ MM_INFO("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);
+ }
+ MM_INFO("exit\n");
+ if (hdr) {
+ kfree(hdr);
+ hdr = NULL;
+ }
+ amg->task = NULL;
+ 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)
+{
+ struct audmgr_global *amg = &the_audmgr_state;
+ int rc;
+
+ if (am->state != STATE_CLOSED)
+ return 0;
+
+ mutex_lock(amg->lock);
+
+ /* connect to audmgr end point and polling thread only once */
+ if (amg->ept == NULL) {
+ amg->ept = msm_rpc_connect_compatible(AUDMGR_PROG,
+ AUDMGR_VERS_COMP_VER3,
+ MSM_RPC_UNINTERRUPTIBLE);
+ if (IS_ERR(amg->ept)) {
+ MM_ERR("connect failed with current VERS \
+ = %x, trying again with another API\n",
+ AUDMGR_VERS_COMP_VER3);
+ amg->ept = msm_rpc_connect_compatible(AUDMGR_PROG,
+ AUDMGR_VERS_COMP_VER2,
+ MSM_RPC_UNINTERRUPTIBLE);
+ if (IS_ERR(amg->ept)) {
+ MM_ERR("connect failed with current VERS \
+ = %x, trying again with another API\n",
+ AUDMGR_VERS_COMP_VER2);
+ amg->ept = msm_rpc_connect_compatible(
+ AUDMGR_PROG,
+ AUDMGR_VERS_COMP,
+ MSM_RPC_UNINTERRUPTIBLE);
+ if (IS_ERR(amg->ept)) {
+ MM_ERR("connect failed with current \
+ VERS=%x, trying again with another \
+ API\n", AUDMGR_VERS_COMP);
+ amg->ept = msm_rpc_connect(AUDMGR_PROG,
+ AUDMGR_VERS,
+ MSM_RPC_UNINTERRUPTIBLE);
+ amg->rpc_version = AUDMGR_VERS;
+ } else
+ amg->rpc_version = AUDMGR_VERS_COMP;
+ } else
+ amg->rpc_version = AUDMGR_VERS_COMP_VER2;
+ } else
+ amg->rpc_version = AUDMGR_VERS_COMP_VER3;
+
+ if (IS_ERR(amg->ept)) {
+ rc = PTR_ERR(amg->ept);
+ amg->ept = NULL;
+ MM_ERR("failed to connect to audmgr svc\n");
+ goto done;
+ }
+
+ amg->task = kthread_run(audmgr_rpc_thread, amg, "audmgr_rpc");
+ if (IS_ERR(amg->task)) {
+ rc = PTR_ERR(amg->task);
+ amg->task = NULL;
+ msm_rpc_close(amg->ept);
+ amg->ept = NULL;
+ goto done;
+ }
+ }
+
+ /* Initialize session parameters */
+ init_waitqueue_head(&am->wait);
+ am->state = STATE_DISABLED;
+ rc = 0;
+done:
+ mutex_unlock(amg->lock);
+ return rc;
+}
+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_global *amg = &the_audmgr_state;
+ struct audmgr_enable_msg msg;
+ int rc;
+
+ if (am->state == STATE_ENABLED)
+ return 0;
+
+ if (am->state == STATE_DISABLING)
+ MM_ERR("state is DISABLING in enable?\n");
+ am->state = STATE_ENABLING;
+
+ MM_INFO("session 0x%08x\n", (int) am);
+ 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((int)am);
+
+ msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, amg->rpc_version,
+ AUDMGR_ENABLE_CLIENT);
+
+ rc = msm_rpc_write(amg->ept, &msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ);
+ if (rc == 0) {
+ MM_ERR("ARM9 did not reply to RPC am->state = %d\n", am->state);
+ }
+ if (am->state == STATE_ENABLED)
+ return 0;
+
+ MM_ERR("unexpected state %d while enabling?!\n", am->state);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_enable);
+
+int audmgr_disable(struct audmgr *am)
+{
+ struct audmgr_global *amg = &the_audmgr_state;
+ struct audmgr_disable_msg msg;
+ int rc;
+
+ if (am->state == STATE_DISABLED)
+ return 0;
+
+ MM_INFO("session 0x%08x\n", (int) am);
+ msg.handle = cpu_to_be32(am->handle);
+ msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, amg->rpc_version,
+ AUDMGR_DISABLE_CLIENT);
+
+ am->state = STATE_DISABLING;
+
+ rc = msm_rpc_write(amg->ept, &msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ);
+ if (rc == 0) {
+ MM_ERR("ARM9 did not reply to RPC am->state = %d\n", am->state);
+ }
+
+ if (am->state == STATE_DISABLED)
+ return 0;
+
+ MM_ERR("unexpected state %d while disabling?!\n", am->state);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_disable);