asoc: msm-lsm-client: add support for ADSP Generic Detection Event

ADSP detection engine module can raise Generic Detection Event to
provide variable payload data based on the information requested by
userspace. Adding new API/interface to provide support for Generic
Detection Event in LSM drivers.

Change-Id: Id5541f959ecdcf862dcfa1de1c1a1b3a796f260c
Signed-off-by: Xiaoyu Ye <benyxy@codeaurora.org>
This commit is contained in:
Xiaoyu Ye 2018-03-21 17:43:38 -07:00 committed by Gerrit - the friendly Code Review server
parent 65049fbf9d
commit f0ec686d3b
4 changed files with 344 additions and 74 deletions

View file

@ -79,6 +79,7 @@ struct lsm_priv {
struct snd_pcm_substream *substream;
struct lsm_client *lsm_client;
struct snd_lsm_event_status_v3 *event_status;
struct snd_lsm_event_status *det_event;
spinlock_t event_lock;
wait_queue_head_t event_wait;
unsigned long event_avail;
@ -294,6 +295,46 @@ static void lsm_event_handler(uint32_t opcode, uint32_t token,
payload_size);
break;
case LSM_SESSION_DETECTION_ENGINE_GENERIC_EVENT: {
struct snd_lsm_event_status *tmp;
status = ((uint16_t *)payload)[0];
payload_size = ((uint16_t *)payload)[1];
spin_lock_irqsave(&prtd->event_lock, flags);
tmp = krealloc(prtd->det_event,
sizeof(struct snd_lsm_event_status) +
payload_size, GFP_ATOMIC);
if (!tmp) {
spin_unlock_irqrestore(&prtd->event_lock, flags);
dev_err(rtd->dev,
"%s: Failed to allocate memory for %s, size = %lu\n",
__func__,
"LSM_SESSION_DETECTION_ENGINE_GENERIC_EVENT",
sizeof(struct snd_lsm_event_status) +
payload_size);
return;
}
prtd->det_event = tmp;
prtd->det_event->status = status;
prtd->det_event->payload_size = payload_size;
memcpy(prtd->det_event->payload, &((uint8_t *)payload)[4],
payload_size);
prtd->event_avail = 1;
spin_unlock_irqrestore(&prtd->event_lock, flags);
wake_up(&prtd->event_wait);
if (substream->timer_running)
snd_timer_interrupt(substream->timer, 1);
dev_dbg(rtd->dev,
"%s: Generic det event status = %d payload size = %d\n",
__func__, prtd->det_event->status,
prtd->det_event->payload_size);
break;
}
default:
break;
}
@ -719,6 +760,46 @@ static int msm_lsm_set_poll_enable(struct snd_pcm_substream *substream,
return rc;
}
static int msm_lsm_set_det_event_type(struct snd_pcm_substream *substream,
struct lsm_params_info *p_info)
{
struct snd_lsm_det_event_type det_event_type;
struct snd_pcm_runtime *runtime = substream->runtime;
struct lsm_priv *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int rc = 0;
if (p_info->param_size != sizeof(det_event_type)) {
dev_err(rtd->dev,
"%s: Invalid param_size %d\n",
__func__, p_info->param_size);
rc = -EINVAL;
goto done;
}
if (copy_from_user(&det_event_type, p_info->param_data,
sizeof(det_event_type))) {
dev_err(rtd->dev,
"%s: copy_from_user failed, size = %zd\n",
__func__, sizeof(det_event_type));
rc = -EFAULT;
goto done;
}
rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
&det_event_type, LSM_DET_EVENT_TYPE);
if (!rc)
prtd->lsm_client->event_type = det_event_type.event_type;
else
dev_err(rtd->dev,
"%s: Failed to set detection event type %s, err = %d\n",
__func__, (det_event_type.event_type ?
"LSM_DET_EVENT_TYPE_GENERIC" :
"LSM_DET_EVENT_TYPE_LEGACY"), rc);
done:
return rc;
}
static int msm_lsm_process_params(struct snd_pcm_substream *substream,
struct snd_lsm_module_params *p_data,
void *params)
@ -762,6 +843,9 @@ static int msm_lsm_process_params(struct snd_pcm_substream *substream,
case LSM_POLLING_ENABLE:
rc = msm_lsm_set_poll_enable(substream, p_info);
break;
case LSM_DET_EVENT_TYPE:
rc = msm_lsm_set_det_event_type(substream, p_info);
break;
default:
dev_err(rtd->dev,
"%s: Invalid param_type %d\n",
@ -781,6 +865,30 @@ static int msm_lsm_process_params(struct snd_pcm_substream *substream,
return rc;
}
static int msm_lsm_start_lab_buffer(struct lsm_priv *prtd, uint16_t status)
{
struct lsm_client *lsm_client = prtd->lsm_client;
int rc = 0;
if (lsm_client && lsm_client->lab_enable &&
!lsm_client->lab_started &&
status == LSM_VOICE_WAKEUP_STATUS_DETECTED) {
atomic_set(&prtd->read_abort, 0);
atomic_set(&prtd->buf_count, 0);
prtd->appl_cnt = 0;
prtd->dma_write = 0;
rc = msm_lsm_queue_lab_buffer(prtd, 0);
if (rc)
pr_err("%s: Queue buffer failed for lab rc = %d\n",
__func__, rc);
else
prtd->lsm_client->lab_started = true;
}
return rc;
}
static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
@ -1026,26 +1134,9 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
payload_size);
}
}
if (!rc) {
if (prtd->lsm_client->lab_enable
&& !prtd->lsm_client->lab_started
&& prtd->event_status->status ==
LSM_VOICE_WAKEUP_STATUS_DETECTED) {
atomic_set(&prtd->read_abort, 0);
atomic_set(&prtd->buf_count, 0);
prtd->appl_cnt = 0;
prtd->dma_write = 0;
rc = msm_lsm_queue_lab_buffer(prtd,
0);
if (rc)
dev_err(rtd->dev,
"%s: Queue buffer failed for lab rc = %d\n",
__func__, rc);
else
prtd->lsm_client->lab_started
= true;
}
}
if (!rc)
rc = msm_lsm_start_lab_buffer(prtd, status);
} else if (xchg) {
dev_dbg(rtd->dev, "%s: Wait aborted\n", __func__);
rc = 0;
@ -1053,6 +1144,76 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
break;
}
case SNDRV_LSM_GENERIC_DET_EVENT: {
struct snd_lsm_event_status *user = arg;
uint16_t status = 0;
uint16_t payload_size = 0;
dev_dbg(rtd->dev,
"%s: SNDRV_LSM_GENERIC_DET_EVENT\n", __func__);
atomic_set(&prtd->event_wait_stop, 0);
/*
* Release the api lock before wait to allow
* other IOCTLs to be invoked while waiting
* for event
*/
mutex_unlock(&prtd->lsm_api_lock);
rc = wait_event_freezable(prtd->event_wait,
(cmpxchg(&prtd->event_avail, 1, 0) ||
(xchg = atomic_cmpxchg(&prtd->event_wait_stop,
1, 0))));
mutex_lock(&prtd->lsm_api_lock);
dev_dbg(rtd->dev, "%s: wait_event_freezable %d event_wait_stop %d\n",
__func__, rc, xchg);
if (!rc && !xchg) {
dev_dbg(rtd->dev, "%s: %s: New event available %ld\n",
__func__, "SNDRV_LSM_GENERIC_DET_EVENT",
prtd->event_avail);
spin_lock_irqsave(&prtd->event_lock, flags);
if (prtd->det_event) {
payload_size = prtd->det_event->payload_size;
status = prtd->det_event->status;
spin_unlock_irqrestore(&prtd->event_lock,
flags);
} else {
spin_unlock_irqrestore(&prtd->event_lock,
flags);
dev_err(rtd->dev,
"%s: %s: prtd->event_status is NULL\n",
__func__,
"SNDRV_LSM_GENERIC_DET_EVENT");
rc = -EINVAL;
break;
}
if (user->payload_size < payload_size) {
dev_err(rtd->dev,
"%s: provided %d bytes isn't enough, needs %d bytes\n",
__func__, user->payload_size,
payload_size);
rc = -ENOMEM;
break;
}
user->status = status;
user->payload_size = payload_size;
memcpy(user->payload, prtd->det_event->payload,
payload_size);
rc = msm_lsm_start_lab_buffer(prtd, status);
} else if (xchg) {
dev_dbg(rtd->dev, "%s: %s: Wait aborted\n",
__func__, "SNDRV_LSM_GENERIC_DET_EVENT");
rc = 0;
}
break;
}
case SNDRV_LSM_ABORT_EVENT:
dev_dbg(rtd->dev, "%s: Aborting event status wait\n",
__func__);
@ -1211,6 +1372,28 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
return rc;
}
static int msm_lsm_check_event_type(struct lsm_client *lsm_client,
unsigned int cmd)
{
int err = 0;
uint32_t event_type = lsm_client->event_type;
if (cmd == SNDRV_LSM_EVENT_STATUS &&
event_type != LSM_DET_EVENT_TYPE_LEGACY) {
pr_err("%s: %s: Invalid event request\n",
__func__, "SNDRV_LSM_EVENT_STATUS");
err = -EINVAL;
} else if (cmd == SNDRV_LSM_GENERIC_DET_EVENT &&
event_type != LSM_DET_EVENT_TYPE_GENERIC) {
pr_err("%s: %s: Invalid event request\n",
__func__, "SNDRV_LSM_GENERIC_DET_EVENT");
err = -EINVAL;
}
return err;
}
#ifdef CONFIG_COMPAT
struct snd_lsm_event_status32 {
@ -1292,20 +1475,34 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream,
mutex_lock(&prtd->lsm_api_lock);
switch (cmd) {
case SNDRV_LSM_EVENT_STATUS: {
struct snd_lsm_event_status *user = NULL, userarg32;
struct snd_lsm_event_status *user32 = NULL;
case SNDRV_LSM_EVENT_STATUS:
case SNDRV_LSM_GENERIC_DET_EVENT: {
struct snd_lsm_event_status userarg32, *user32 = NULL;
struct snd_lsm_event_status *user = NULL;
dev_dbg(rtd->dev,
"%s: %s\n", __func__,
(cmd == SNDRV_LSM_EVENT_STATUS) ?
"SNDRV_LSM_EVENT_STATUS" :
"SNDRV_LSM_GENERIC_DET_EVENT");
err = msm_lsm_check_event_type(prtd->lsm_client, cmd);
if (err)
goto done;
if (copy_from_user(&userarg32, arg, sizeof(userarg32))) {
dev_err(rtd->dev, "%s: err copyuser ioctl %s\n",
__func__, "SNDRV_LSM_EVENT_STATUS");
dev_err(rtd->dev, "%s: %s: Failed to copy from user\n",
__func__, (cmd == SNDRV_LSM_EVENT_STATUS) ?
"SNDRV_LSM_EVENT_STATUS" :
"SNDRV_LSM_GENERIC_DET_EVENT");
err = -EFAULT;
goto done;
}
if (userarg32.payload_size >
LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
dev_err(rtd->dev,
"%s: payload_size %d is invalid, max allowed = %d\n",
__func__, userarg32.payload_size,
LISTEN_MAX_STATUS_PAYLOAD_SIZE);
err = -EINVAL;
@ -1315,49 +1512,50 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream,
size = sizeof(*user) + userarg32.payload_size;
user = kzalloc(size, GFP_KERNEL);
if (!user) {
dev_err(rtd->dev,
"%s: Allocation failed event status size %d\n",
__func__, size);
err = -EFAULT;
err = -ENOMEM;
goto done;
}
user->payload_size = userarg32.payload_size;
err = msm_lsm_ioctl_shared(substream, cmd, user);
if (err) {
dev_err(rtd->dev,
"%s: msm_lsm_ioctl_shared() failed, err = %d",
__func__, err);
kfree(user);
goto done;
} else {
cmd = SNDRV_LSM_EVENT_STATUS;
user->payload_size = userarg32.payload_size;
err = msm_lsm_ioctl_shared(substream, cmd, user);
}
/* Update size with actual payload size */
size = sizeof(userarg32) + user->payload_size;
if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
if (!access_ok(VERIFY_WRITE, arg, size)) {
dev_err(rtd->dev,
"%s: write verify failed size %d\n",
"%s: Failed to verify write, size = %d\n",
__func__, size);
err = -EFAULT;
kfree(user);
goto done;
}
if (!err) {
user32 = kzalloc(size, GFP_KERNEL);
if (!user32) {
dev_err(rtd->dev,
"%s: Allocation event user status size %d\n",
__func__, size);
err = -EFAULT;
} else {
user32->status = user->status;
user32->payload_size = user->payload_size;
memcpy(user32->payload,
user->payload, user32->payload_size);
}
user32 = kzalloc(size, GFP_KERNEL);
if (!user32) {
err = -ENOMEM;
kfree(user);
goto done;
}
if (!err && (copy_to_user(arg, user32, size))) {
dev_err(rtd->dev, "%s: failed to copy payload %d",
user32->status = user->status;
user32->payload_size = user->payload_size;
memcpy(user32->payload, user->payload,
user32->payload_size);
if (copy_to_user(arg, user32, size)) {
dev_err(rtd->dev,
"%s: Failed to copy payload to user, size = %d",
__func__, size);
err = -EFAULT;
}
kfree(user);
kfree(user32);
if (err)
dev_err(rtd->dev, "%s: lsmevent failed %d",
__func__, err);
break;
}
@ -1365,10 +1563,19 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream,
struct snd_lsm_event_status_v3_32 userarg32, *user32 = NULL;
struct snd_lsm_event_status_v3 *user = NULL;
if (prtd->lsm_client->event_type !=
LSM_DET_EVENT_TYPE_LEGACY) {
dev_err(rtd->dev,
"%s: %s: Invalid event request\n",
__func__, "SNDRV_LSM_EVENT_STATUS_V3_32");
err = -EINVAL;
goto done;
}
if (copy_from_user(&userarg32, arg, sizeof(userarg32))) {
dev_err(rtd->dev, "%s: err copyuser ioctl %s\n",
__func__, "SNDRV_LSM_EVENT_STATUS_V3_32");
err = -EINVAL;
err = -EFAULT;
goto done;
}
@ -1789,22 +1996,35 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
break;
}
case SNDRV_LSM_EVENT_STATUS: {
struct snd_lsm_event_status *user = NULL, userarg;
case SNDRV_LSM_EVENT_STATUS:
case SNDRV_LSM_GENERIC_DET_EVENT: {
struct snd_lsm_event_status *user = NULL;
struct snd_lsm_event_status userarg;
dev_dbg(rtd->dev,
"%s: SNDRV_LSM_EVENT_STATUS\n", __func__);
"%s: %s\n", __func__,
(cmd == SNDRV_LSM_EVENT_STATUS) ?
"SNDRV_LSM_EVENT_STATUS" :
"SNDRV_LSM_GENERIC_DET_EVENT");
err = msm_lsm_check_event_type(prtd->lsm_client, cmd);
if (err)
goto done;
if (copy_from_user(&userarg, arg, sizeof(userarg))) {
dev_err(rtd->dev,
"%s: err copyuser event_status\n",
__func__);
"%s: %s: Copy from user failed\n", __func__,
(cmd == SNDRV_LSM_EVENT_STATUS) ?
"SNDRV_LSM_EVENT_STATUS" :
"SNDRV_LSM_GENERIC_DET_EVENT");
err = -EFAULT;
goto done;
}
if (userarg.payload_size >
LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
dev_err(rtd->dev,
"%s: payload_size %d is invalid, max allowed = %d\n",
__func__, userarg.payload_size,
LISTEN_MAX_STATUS_PAYLOAD_SIZE);
err = -EINVAL;
@ -1812,37 +2032,40 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
}
size = sizeof(struct snd_lsm_event_status) +
userarg.payload_size;
userarg.payload_size;
user = kzalloc(size, GFP_KERNEL);
if (!user) {
dev_err(rtd->dev,
"%s: Allocation failed event status size %d\n",
__func__, size);
err = -EFAULT;
err = -ENOMEM;
goto done;
}
user->payload_size = userarg.payload_size;
err = msm_lsm_ioctl_shared(substream, cmd, user);
if (err) {
dev_err(rtd->dev,
"%s: msm_lsm_ioctl_shared() failed, err = %d",
__func__, err);
kfree(user);
goto done;
}
/* Update size with actual payload size */
size = sizeof(*user) + user->payload_size;
if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
if (!access_ok(VERIFY_WRITE, arg, size)) {
dev_err(rtd->dev,
"%s: write verify failed size %d\n",
"%s: Failed to verify write, size = %d\n",
__func__, size);
err = -EFAULT;
}
if (!err && (copy_to_user(arg, user, size))) {
if (!err && copy_to_user(arg, user, size)) {
dev_err(rtd->dev,
"%s: failed to copy payload %d",
"%s: Failed to copy payload to user, size = %d\n",
__func__, size);
err = -EFAULT;
}
kfree(user);
if (err)
dev_err(rtd->dev,
"%s: lsmevent failed %d", __func__, err);
goto done;
break;
}
case SNDRV_LSM_EVENT_STATUS_V3: {
@ -1851,6 +2074,16 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
dev_dbg(rtd->dev,
"%s: SNDRV_LSM_EVENT_STATUS_V3\n", __func__);
if (prtd->lsm_client->event_type !=
LSM_DET_EVENT_TYPE_LEGACY) {
dev_err(rtd->dev,
"%s: %s: Invalid event request\n",
__func__, "SNDRV_LSM_EVENT_STATUS_V3");
err = -EINVAL;
goto done;
}
if (!arg) {
dev_err(rtd->dev,
"%s: Invalid params event_status_v3\n",
@ -1983,6 +2216,7 @@ static int msm_lsm_open(struct snd_pcm_substream *substream)
prtd->lsm_client->poll_enable = true;
prtd->lsm_client->perf_mode = 0;
prtd->lsm_client->event_mode = LSM_EVENT_NON_TIME_STAMP_MODE;
prtd->lsm_client->event_type = LSM_DET_EVENT_TYPE_LEGACY;
return 0;
}
@ -2090,6 +2324,8 @@ static int msm_lsm_close(struct snd_pcm_substream *substream)
spin_lock_irqsave(&prtd->event_lock, flags);
kfree(prtd->event_status);
prtd->event_status = NULL;
kfree(prtd->det_event);
prtd->det_event = NULL;
spin_unlock_irqrestore(&prtd->event_lock, flags);
mutex_destroy(&prtd->lsm_api_lock);
kfree(prtd);

View file

@ -1905,6 +1905,32 @@ int q6lsm_set_one_param(struct lsm_client *client,
__func__, rc);
break;
}
case LSM_DET_EVENT_TYPE: {
struct lsm_param_det_event_type det_event_type;
struct snd_lsm_det_event_type *det_event_data =
(struct snd_lsm_det_event_type *)data;
param_info.module_id = p_info->module_id;
param_info.instance_id = INSTANCE_ID_0;
param_info.param_id = p_info->param_id;
param_info.param_size = sizeof(det_event_type);
memset(&det_event_type, 0, sizeof(det_event_type));
det_event_type.minor_version = QLSM_PARAM_ID_MINOR_VERSION;
det_event_type.event_type = det_event_data->event_type;
det_event_type.mode = det_event_data->mode;
rc = q6lsm_pack_and_set_params(client, &param_info,
(uint8_t *)&det_event_type,
LSM_SESSION_CMD_SET_PARAMS_V2);
if (rc)
pr_err("%s: DET_EVENT_TYPE cmd failed, rc %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: wrong param_type 0x%x\n",
__func__, p_info->param_type);

View file

@ -9674,6 +9674,7 @@ struct avcs_fwk_ver_info {
#define LSM_DATA_EVENT_READ_DONE (0x00012B02)
#define LSM_DATA_EVENT_STATUS (0x00012B03)
#define LSM_SESSION_EVENT_DETECTION_STATUS_V3 (0x00012B04)
#define LSM_SESSION_DETECTION_ENGINE_GENERIC_EVENT (0x00012B06)
#define LSM_MODULE_ID_VOICE_WAKEUP (0x00012C00)
#define LSM_PARAM_ID_ENDPOINT_DETECT_THRESHOLD (0x00012C01)

View file

@ -88,6 +88,7 @@ struct lsm_client {
bool poll_enable;
int perf_mode;
uint32_t event_mode;
uint32_t event_type;
};
struct lsm_stream_cmd_open_tx {
@ -212,6 +213,12 @@ struct lsm_cmd_read_done {
uint32_t flags;
} __packed;
struct lsm_param_det_event_type {
uint32_t minor_version;
uint32_t event_type;
uint32_t mode;
} __packed;
struct lsm_client *q6lsm_client_alloc(lsm_app_cb cb, void *priv);
void q6lsm_client_free(struct lsm_client *client);
int q6lsm_open(struct lsm_client *client, uint16_t app_id);