* drivers/audio/audio_rpmsg.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <nuttx/kmalloc.h>
#include <nuttx/rpmsg/rpmsg.h>
#include <sys/param.h>
#include <nuttx/audio/audio.h>
#include <nuttx/audio/audio_rpmsg.h>
* Private Types
****************************************************************************/
#define AUDIO_RPMSG_CONFIGURE 0
#define AUDIO_RPMSG_IOCTL 1
#define AUDIO_RPMSG_START 2
#define AUDIO_RPMSG_STOP 3
#define AUDIO_RPMSG_PAUSE 4
#define AUDIO_RPMSG_RESUME 5
#define AUDIO_RPMSG_NOTIFY_AVAIL 6
#define AUDIO_RPMSG_SEND_DATA 7
#define AUDIO_RPMSG_RESPONSE (1u << 31)
#define AUDIO_RPMSG_IS_RESPONSE(cmd) (!!((cmd) & AUDIO_RPMSG_RESPONSE))
#define AUDIO_RPMSG_GET_COMMAND(cmd) ((cmd) & ~AUDIO_RPMSG_RESPONSE)
#define AUDIO_RPMSG_EPT_PREFIX "rpmsg-audio-"
struct audio_rpmsg_cookie_s
{
sem_t sem;
int result;
FAR void *data;
};
struct audio_rpmsg_header_s
{
uint32_t command;
int32_t result;
uint64_t cookie;
};
struct audio_rpmsg_configure_s
{
struct audio_rpmsg_header_s header;
struct audio_caps_s caps;
};
struct audio_rpmsg_ioctl_s
{
struct audio_rpmsg_header_s header;
int32_t request;
uint32_t arglen;
uint64_t arg;
uint8_t data[0];
};
struct audio_rpmsg_msg_s
{
struct audio_rpmsg_header_s header;
uint16_t msgid;
};
struct audio_rpmsg_send_data_s
{
struct audio_rpmsg_header_s header;
uint32_t flags;
uint32_t length;
uint8_t data[0];
};
struct audio_rpmsg_notify_avail_s
{
struct audio_rpmsg_header_s header;
};
struct audio_rpmsg_s
{
struct audio_lowerhalf_s dev;
struct rpmsg_endpoint ept;
FAR const char *remotecpu;
FAR const char *devname;
bool consumer;
sem_t wait;
* opreation until the connection
* between two cpu established.
*/
mutex_t mutex;
struct audio_info_s info;
struct ap_buffer_info_s binfo;
size_t navail;
struct dq_queue_s pendq;
};
* Private Function Prototypes
****************************************************************************/
static int audio_rpmsg_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
FAR struct audio_caps_s *caps);
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_configure(FAR struct audio_lowerhalf_s *dev,
FAR void *session,
FAR const struct audio_caps_s *caps);
#else
static int audio_rpmsg_configure(FAR struct audio_lowerhalf_s *dev,
FAR const struct audio_caps_s *caps);
#endif
static int audio_rpmsg_shutdown(FAR struct audio_lowerhalf_s *dev);
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_start(FAR struct audio_lowerhalf_s *dev,
FAR void *session);
#else
static int audio_rpmsg_start(FAR struct audio_lowerhalf_s *dev);
#endif
#ifndef CONFIG_AUDIO_EXCLUDE_STOP
# ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_stop(FAR struct audio_lowerhalf_s *dev,
FAR void *session);
# else
static int audio_rpmsg_stop(FAR struct audio_lowerhalf_s *dev);
# endif
#endif
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
# ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_pause(FAR struct audio_lowerhalf_s *dev,
FAR void *session);
static int audio_rpmsg_resume(FAR struct audio_lowerhalf_s *dev,
FAR void *session);
# else
static int audio_rpmsg_pause(FAR struct audio_lowerhalf_s *dev);
static int audio_rpmsg_resume(FAR struct audio_lowerhalf_s *dev);
# endif
#endif
static int audio_rpmsg_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
FAR struct ap_buffer_s *apb);
static int audio_rpmsg_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
unsigned long arg);
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_reserve(FAR struct audio_lowerhalf_s *dev,
FAR void **session);
#else
static int audio_rpmsg_reserve(FAR struct audio_lowerhalf_s *dev);
#endif
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_release(FAR struct audio_lowerhalf_s *dev,
FAR void *session);
#else
static int audio_rpmsg_release(FAR struct audio_lowerhalf_s *dev);
#endif
static int audio_rpmsg_message_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int audio_rpmsg_send_data_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int audio_rpmsg_notify_avail_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int audio_rpmsg_configure_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int audio_rpmsg_ioctl_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int audio_rpmsg_default_response(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int audio_rpmsg_ioctl_response(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
* Private Data
****************************************************************************/
#ifdef CONFIG_AUDIO_RPMSG_DEVICE_PARAMS
static const audio_rpmsg_param_t g_audio_rpmsg_params[] =
{
CONFIG_AUDIO_RPMSG_DEVICE_PARAMS
};
#endif
static const rpmsg_ept_cb g_audio_rpmsg_handler[] =
{
[AUDIO_RPMSG_CONFIGURE] = audio_rpmsg_configure_handler,
[AUDIO_RPMSG_IOCTL] = audio_rpmsg_ioctl_handler,
[AUDIO_RPMSG_START] = audio_rpmsg_message_handler,
[AUDIO_RPMSG_STOP] = audio_rpmsg_message_handler,
[AUDIO_RPMSG_PAUSE] = audio_rpmsg_message_handler,
[AUDIO_RPMSG_RESUME] = audio_rpmsg_message_handler,
[AUDIO_RPMSG_SEND_DATA] = audio_rpmsg_send_data_handler,
[AUDIO_RPMSG_NOTIFY_AVAIL] = audio_rpmsg_notify_avail_handler,
};
static const rpmsg_ept_cb g_audio_rpmsg_response[] =
{
[AUDIO_RPMSG_CONFIGURE] = audio_rpmsg_default_response,
[AUDIO_RPMSG_IOCTL] = audio_rpmsg_ioctl_response,
[AUDIO_RPMSG_START] = audio_rpmsg_default_response,
[AUDIO_RPMSG_STOP] = audio_rpmsg_default_response,
[AUDIO_RPMSG_PAUSE] = audio_rpmsg_default_response,
[AUDIO_RPMSG_RESUME] = audio_rpmsg_default_response,
[AUDIO_RPMSG_SEND_DATA] = audio_rpmsg_default_response,
[AUDIO_RPMSG_NOTIFY_AVAIL] = audio_rpmsg_default_response,
};
static const struct audio_ops_s g_audio_rpmsg_ops =
{
audio_rpmsg_getcaps,
audio_rpmsg_configure,
audio_rpmsg_shutdown,
audio_rpmsg_start,
#ifndef CONFIG_AUDIO_EXCLUDE_STOP
audio_rpmsg_stop,
#endif
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
audio_rpmsg_pause,
audio_rpmsg_resume,
#endif
NULL,
NULL,
audio_rpmsg_enqueuebuffer,
NULL,
audio_rpmsg_ioctl,
NULL,
NULL,
audio_rpmsg_reserve,
audio_rpmsg_release
};
* Private Functions
****************************************************************************/
static int audio_rpmsg_local_configure(FAR struct audio_rpmsg_s *aud,
FAR const struct audio_caps_s *caps)
{
int ret = OK;
nxmutex_lock(&aud->mutex);
switch (caps->ac_type)
{
case AUDIO_TYPE_OUTPUT:
case AUDIO_TYPE_INPUT:
aud->info.format = caps->ac_subtype;
aud->info.samplerate = caps->ac_controls.hw[0] |
(caps->ac_controls.b[3] << 16);
aud->info.channels = caps->ac_channels;
aud->info.subformat = caps->ac_format.b[0];
memcpy(&aud->info.codec, &caps->ac_codec,
sizeof(caps->ac_codec));
audinfo("Codec type %" PRIu8 " %" PRIu32 " %" PRIu8 " %" PRIu8 "\n",
aud->info.format,
aud->info.samplerate,
aud->info.channels,
aud->info.subformat);
break;
default:
audinfo("default case: %d\n", caps->ac_type);
ret = -ENOTTY;
break;
}
nxmutex_unlock(&aud->mutex);
return ret;
}
static int audio_rpmsg_local_ioctl(FAR struct audio_rpmsg_s *aud, int cmd,
unsigned long arg)
{
int ret = OK;
nxmutex_lock(&aud->mutex);
switch (cmd)
{
case AUDIOIOC_GETBUFFERINFO:
memcpy((FAR void *)(uintptr_t)arg, &aud->binfo,
sizeof(aud->binfo));
break;
case AUDIOIOC_SETBUFFERINFO:
memcpy(&aud->binfo, (FAR void *)(uintptr_t)arg,
sizeof(aud->binfo));
ret = -ENOTTY;
break;
case AUDIOIOC_GETAUDIOINFO:
memcpy((FAR void *)(uintptr_t)arg, &aud->info, sizeof(aud->info));
break;
default:
ret = -ENOTTY;
break;
}
nxmutex_unlock(&aud->mutex);
return ret;
}
static void audio_rpmsg_callback(FAR struct audio_rpmsg_s *aud,
int reason, FAR void *data)
{
#ifdef CONFIG_AUDIO_MULTI_SESSION
aud->dev.upper(aud->dev.priv, reason, data, OK, NULL);
#else
aud->dev.upper(aud->dev.priv, reason, data, OK);
#endif
}
static int audio_rpmsg_send_response(FAR struct audio_rpmsg_s *aud,
FAR struct audio_rpmsg_header_s *header,
size_t len, int result)
{
if (header->cookie)
{
header->command |= AUDIO_RPMSG_RESPONSE;
header->result = result;
return rpmsg_send(&aud->ept, header, len);
}
return OK;
}
* Name: audio_rpmsg_get_tx_buffer
*
* Description:
* Get the rpmsg device tx payload, the buffer is from the rpmsg
* share memory that can be accessed by local and remote cpu.
*
* Parameters:
* priv - The rpmsg-device handle
* len - The got memroy size
*
* Returned Values:
* NULL - failure
* not NULL - success
*
****************************************************************************/
static FAR void *audio_rpmsg_get_tx_buffer(FAR struct audio_rpmsg_s *aud,
FAR uint32_t *len)
{
int sval;
nxsem_get_value(&aud->wait, &sval);
if (sval <= 0)
{
rpmsg_wait(&aud->ept, &aud->wait);
rpmsg_post(&aud->ept, &aud->wait);
}
return rpmsg_get_tx_payload_buffer(&aud->ept, len, true);
}
static int audio_rpmsg_send_data(FAR struct audio_rpmsg_s *aud)
{
FAR struct audio_rpmsg_send_data_s *req;
FAR struct ap_buffer_s *apb;
uint32_t space;
int ret = 0;
nxmutex_lock(&aud->mutex);
if (aud->navail > 0 && dq_empty(&aud->pendq))
{
audwarn("%s no data to send.\n", aud->devname);
}
while (!dq_empty(&aud->pendq) && aud->navail > 0)
{
nxmutex_unlock(&aud->mutex);
req = audio_rpmsg_get_tx_buffer(aud, &space);
if (req == NULL)
{
auderr("%s failed to get tx buffer\n", aud->devname);
return -ENOMEM;
}
nxmutex_lock(&aud->mutex);
if (dq_empty(&aud->pendq) || aud->navail == 0)
{
rpmsg_release_tx_buffer(&aud->ept, req);
break;
}
apb = (FAR struct ap_buffer_s *)dq_peek(&aud->pendq);
space -= sizeof(*req);
if (space >= apb->nbytes - apb->curbyte)
{
space = apb->nbytes - apb->curbyte;
}
req->flags = 0;
req->length = space;
req->header.result = 0;
req->header.command = AUDIO_RPMSG_SEND_DATA;
memcpy(req->data, apb->samp + apb->curbyte, space);
if ((apb->curbyte + space) == apb->nbytes)
{
req->flags = apb->flags;
}
ret = rpmsg_send_nocopy(&aud->ept, req, sizeof(*req) + space);
if (ret < 0)
{
auderr("%s failed to send\n", aud->devname);
rpmsg_release_tx_buffer(&aud->ept, req);
break;
}
apb->curbyte += space;
if (apb->curbyte == apb->nbytes)
{
dq_remfirst(&aud->pendq);
aud->navail--;
audio_rpmsg_callback(aud, AUDIO_CALLBACK_DEQUEUE, apb);
if (apb->flags & AUDIO_APB_FINAL)
{
audio_rpmsg_callback(aud, AUDIO_CALLBACK_COMPLETE, apb);
}
}
}
nxmutex_unlock(&aud->mutex);
return ret;
}
static int audio_rpmsg_notify_avail(FAR struct audio_rpmsg_s *aud)
{
FAR struct audio_rpmsg_notify_avail_s req;
req.header.result = 0;
req.header.command = AUDIO_RPMSG_NOTIFY_AVAIL;
return rpmsg_send(&aud->ept, &req, sizeof(req));
}
static int audio_rpmsg_deliver_buffer(FAR struct audio_rpmsg_s *aud)
{
if (aud->consumer)
{
return audio_rpmsg_notify_avail(aud);
}
else
{
return audio_rpmsg_send_data(aud);
}
}
static int audio_rpmsg_deliver_message(FAR struct audio_rpmsg_s *aud,
int cmd, int msgid)
{
struct audio_rpmsg_msg_s req =
{
0
};
int ret;
req.header.command = cmd;
req.msgid = msgid;
ret = rpmsg_send(&aud->ept, &req, sizeof(req));
if (ret < 0)
{
return ret;
}
return OK;
}
* Name: audio_rpmsg_default_response
*
* Description:
* This function is used to handle the response from the RPMSG device.
* It is used to copy the rpc result to the cookie and post the semaphore.
*
****************************************************************************/
static int audio_rpmsg_default_response(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct audio_rpmsg_header_s *header = data;
FAR struct audio_rpmsg_cookie_s *cookie =
(struct audio_rpmsg_cookie_s *)(uintptr_t)header->cookie;
cookie->result = header->result;
rpmsg_post(ept, &cookie->sem);
return 0;
}
static int audio_rpmsg_ioctl_response(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct audio_rpmsg_header_s *header = data;
FAR struct audio_rpmsg_cookie_s *cookie =
(struct audio_rpmsg_cookie_s *)(uintptr_t)header->cookie;
FAR struct audio_rpmsg_ioctl_s *req = data;
cookie->result = header->result;
if (cookie->result >= 0 && req->arglen > 0)
{
memcpy(cookie->data, (FAR void *)(uintptr_t)req->data,
req->arglen);
}
rpmsg_post(ept, &cookie->sem);
return 0;
}
static int audio_rpmsg_configure_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct audio_rpmsg_s *aud = ept->priv;
FAR struct audio_rpmsg_header_s *header = data;
FAR struct audio_rpmsg_configure_s *req = data;
int ret;
audinfo("ac_type: %d\n", req->caps.ac_type);
ret = audio_rpmsg_local_configure(aud, &req->caps);
return audio_rpmsg_send_response(aud, header, sizeof(*req), ret);
}
static int audio_rpmsg_message_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct audio_rpmsg_s *aud = ept->priv;
FAR struct audio_rpmsg_msg_s *req = data;
struct audio_msg_s msg =
{
0
};
audinfo("%s handle message: %d.\n", aud->devname, req->msgid);
nxmutex_lock(&aud->mutex);
if (req->msgid == AUDIO_MSG_STOP)
{
aud->navail = 0;
}
nxmutex_unlock(&aud->mutex);
msg.msg_id = req->msgid;
audio_rpmsg_callback(aud, AUDIO_CALLBACK_MESSAGE, &msg);
return OK;
}
static int audio_rpmsg_ioctl_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct audio_rpmsg_s *aud = ept->priv;
FAR struct audio_rpmsg_ioctl_s *req = data;
int ret = 0;
audinfo("cmd=%" PRIu32 " arg=%" PRIu64 "\n", req->request, req->arg);
if (req->request == AUDIOIOC_HWRESET)
{
struct audio_msg_s msg =
{
0
};
msg.msg_id = AUDIO_MSG_IOERR;
audio_rpmsg_callback(aud, AUDIO_CALLBACK_MESSAGE, &msg);
ret = 0;
}
else
{
ret = audio_rpmsg_local_ioctl(aud, req->request,
req->arglen > 0 ?
(unsigned long)req->data : req->arg);
}
return audio_rpmsg_send_response(aud, &req->header, len,
ret >= 0 ? (int)req->arglen : ret);
}
static int audio_rpmsg_send_data_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct audio_rpmsg_s *aud = ept->priv;
FAR struct audio_rpmsg_send_data_s *req = data;
struct ap_buffer_s *apb;
nxmutex_lock(&aud->mutex);
if (dq_empty(&aud->pendq))
{
nxmutex_unlock(&aud->mutex);
audwarn("%s no buffer available\n", aud->devname);
return 0;
}
apb = (FAR struct ap_buffer_s *)dq_peek(&aud->pendq);
DEBUGASSERT((apb->nmaxbytes - apb->curbyte) >= req->length);
memcpy(apb->samp + apb->curbyte, req->data, req->length);
apb->curbyte += req->length;
if (apb->curbyte == apb->nmaxbytes || req->flags & AUDIO_APB_FINAL)
{
dq_remfirst(&aud->pendq);
apb->flags = req->flags;
apb->nbytes = apb->curbyte;
audio_rpmsg_callback(aud, AUDIO_CALLBACK_DEQUEUE, apb);
if (apb->flags & AUDIO_APB_FINAL)
{
audio_rpmsg_callback(aud, AUDIO_CALLBACK_COMPLETE, NULL);
}
}
nxmutex_unlock(&aud->mutex);
return 0;
}
static int audio_rpmsg_notify_avail_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct audio_rpmsg_s *aud = ept->priv;
nxmutex_lock(&aud->mutex);
aud->navail++;
nxmutex_unlock(&aud->mutex);
return audio_rpmsg_deliver_buffer(aud);
}
static int audio_rpmsg_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data,
size_t len, uint32_t src, FAR void *priv)
{
FAR struct audio_rpmsg_header_s *header = data;
uint32_t cmd = AUDIO_RPMSG_GET_COMMAND(header->command);
int ret = 0;
if (cmd < nitems(g_audio_rpmsg_handler))
{
if (AUDIO_RPMSG_IS_RESPONSE(header->command))
{
ret = g_audio_rpmsg_response[cmd](ept, data, len, src, priv);
}
else
{
ret = g_audio_rpmsg_handler[cmd](ept, data, len, src, priv);
}
}
else
{
auderr("cmd=%" PRIu32 " callback handler is null\n", cmd);
ret = -EINVAL;
}
if (ret < 0)
{
auderr("response:%d cmd=%" PRIu32 " fail\n",
AUDIO_RPMSG_IS_RESPONSE(header->command), cmd);
}
return ret;
}
* Name: audio_rpmsg_ns_bound
*
* Description:
* Rpmsg device end point service bound callback function , called when
* remote end point address is received.
*
* Parameters:
* ept - The rpmsg-device end point
*
* Returned Values:
* None
*
****************************************************************************/
static void audio_rpmsg_ns_bound(FAR struct rpmsg_endpoint *ept)
{
FAR struct audio_rpmsg_s *aud = ept->priv;
rpmsg_post(&aud->ept, &aud->wait);
}
* Name: audio_rpmsg_ns_unbind
*
* Description:
* This is the unbind callback function.
*
* Parameters:
* ept - rpmsg_endpoint for communicating with audio rpmsg driver.
*
****************************************************************************/
static void audio_rpmsg_ns_unbind(FAR struct rpmsg_endpoint *ept)
{
FAR struct audio_rpmsg_s *aud = ept->priv;
rpmsg_wait(&aud->ept, &aud->wait);
}
* Name: audio_rpmsg_device_created
*
* Description:
* Rpmsg device create function, this function will be called by rptun to
* create a rpmsg-audio end point.
*
* Parameters:
* rdev - The rpmsg-audio end point
* priv - Rpmsg-audio handle
*
* Returned Values:
* None
*
****************************************************************************/
static void audio_rpmsg_device_created(FAR struct rpmsg_device *rdev,
FAR void *priv)
{
FAR struct audio_rpmsg_s *aud = (FAR struct audio_rpmsg_s *)priv;
char eptname[RPMSG_NAME_SIZE];
if (strcmp(aud->remotecpu, rpmsg_get_cpuname(rdev)) == 0)
{
aud->ept.priv = aud;
aud->ept.ns_bound_cb = audio_rpmsg_ns_bound;
snprintf(eptname, sizeof(eptname), "%s%s",
AUDIO_RPMSG_EPT_PREFIX, aud->devname);
rpmsg_create_ept(&aud->ept, rdev, eptname, RPMSG_ADDR_ANY,
RPMSG_ADDR_ANY, audio_rpmsg_ept_cb,
audio_rpmsg_ns_unbind);
}
}
* Name: audio_rpmsg_device_destroy
*
* Description:
* Rpmsg device destroy function, this function will be called by rptun to
* destroy rpmsg-audio end point.
*
* Parameters:
* rdev - The rpmsg-device end point
* priv_ - Rpmsg-device handle
*
* Returned Values:
* None
*
****************************************************************************/
static void audio_rpmsg_device_destroy(FAR struct rpmsg_device *rdev,
FAR void *priv)
{
FAR struct audio_rpmsg_s *aud = (FAR struct audio_rpmsg_s *)priv;
if (strcmp(aud->remotecpu, rpmsg_get_cpuname(rdev)) == 0)
{
rpmsg_destroy_ept(&aud->ept);
}
}
* Name: audio_rpmsg_send_recv
*
* Description:
* Send and receive the rpmsg data.
*
* Parameters:
* priv - audio rpmsg handle
* command - the command, details see AUDIO_RPMSG_* in audio_rpmsg.h
* copy - true, send a message across to the remote processor, and the
* tx buffer will be alloced inside function rpmsg_send()
* false, send a message in tx buffer reserved by
* rpmsg_get_tx_payload_buffer() across to the remote
* processor.
* msg - the message header
* len - length of the payload
* data - the data
*
* Returned Values:
* OK on success; A negated errno value is returned on any failure.
*
****************************************************************************/
static int audio_rpmsg_send_recv(FAR struct audio_rpmsg_s *aud,
uint32_t command, bool copy,
FAR struct audio_rpmsg_header_s *req,
size_t len, FAR void *data)
{
struct audio_rpmsg_cookie_s cookie;
int ret;
memset(&cookie, 0, sizeof(cookie));
nxsem_init(&cookie.sem, 0, 0);
if (data)
{
cookie.data = data;
}
else if (copy)
{
cookie.data = req;
}
req->command = command;
req->result = -ENXIO;
req->cookie = (uintptr_t)&cookie;
if (copy)
{
ret = rpmsg_send(&aud->ept, req, len);
}
else
{
ret = rpmsg_send_nocopy(&aud->ept, req, len);
if (ret < 0)
{
rpmsg_release_tx_buffer(&aud->ept, req);
}
}
if (ret >= 0)
{
ret = rpmsg_wait(&aud->ept, &cookie.sem);
if (ret >= 0)
{
ret = cookie.result;
}
}
nxsem_destroy(&cookie.sem);
return ret;
}
* Name: audio_rpmsg_getcaps
*
* Description:
* Get the audio capabilities from device.
*
****************************************************************************/
static int audio_rpmsg_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
FAR struct audio_caps_s *caps)
{
FAR struct audio_rpmsg_s *aud = (FAR struct audio_rpmsg_s *)dev;
memset(&caps->ac_format, 0, sizeof(caps->ac_format));
memset(&caps->ac_controls, 0, sizeof(caps->ac_controls));
memset(&caps->ac_codec, 0, sizeof(caps->ac_codec));
nxmutex_lock(&aud->mutex);
switch (caps->ac_type)
{
case AUDIO_TYPE_QUERY:
caps->ac_channels = aud->info.channels;
switch (caps->ac_subtype)
{
case AUDIO_TYPE_QUERY:
caps->ac_controls.b[0] =
aud->consumer ? AUDIO_TYPE_INPUT : AUDIO_TYPE_OUTPUT;
if (aud->info.subformat == 0)
{
nxmutex_unlock(&aud->mutex);
return -ENOSYS;
}
caps->ac_format.hw = 1 << (aud->info.format - 1);
break;
case AUDIO_FMT_MP3:
case AUDIO_FMT_PCM:
case AUDIO_FMT_AAC:
case AUDIO_FMT_AC3:
case AUDIO_FMT_AMR:
case AUDIO_FMT_DTS:
case AUDIO_FMT_SBC:
case AUDIO_FMT_WMA:
case AUDIO_FMT_WAV:
case AUDIO_FMT_FLAC:
case AUDIO_FMT_MIDI:
case AUDIO_FMT_MPEG:
case AUDIO_FMT_MSBC:
case AUDIO_FMT_CVSD:
case AUDIO_FMT_OPUS:
case AUDIO_FMT_OGG_VORBIS:
caps->ac_controls.b[0] = aud->info.subformat;
caps->ac_controls.b[1] = AUDIO_SUBFMT_END;
memcpy(&caps->ac_codec, &aud->info.codec,
sizeof(aud->info.codec));
break;
default:
caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
break;
}
break;
case AUDIO_TYPE_OUTPUT:
case AUDIO_TYPE_INPUT:
caps->ac_channels = (aud->info.channels << 4) |
(aud->info.channels & 0x0f);
switch (caps->ac_subtype)
{
case AUDIO_TYPE_QUERY:
caps->ac_controls.hw[0] =
aud->info.samplerate == 8000 ? AUDIO_SAMP_RATE_8K :
aud->info.samplerate == 12000 ? AUDIO_SAMP_RATE_12K :
aud->info.samplerate == 16000 ? AUDIO_SAMP_RATE_16K :
aud->info.samplerate == 22050 ? AUDIO_SAMP_RATE_22K :
aud->info.samplerate == 24000 ? AUDIO_SAMP_RATE_24K :
aud->info.samplerate == 32000 ? AUDIO_SAMP_RATE_32K :
aud->info.samplerate == 44100 ? AUDIO_SAMP_RATE_44K :
aud->info.samplerate == 48000 ? AUDIO_SAMP_RATE_48K :
aud->info.samplerate == 88200 ? AUDIO_SAMP_RATE_88K :
aud->info.samplerate == 96000 ? AUDIO_SAMP_RATE_96K :
aud->info.samplerate == 192000 ? AUDIO_SAMP_RATE_192K :
AUDIO_SAMP_RATE_DEF_ALL;
break;
default:
break;
}
break;
}
nxmutex_unlock(&aud->mutex);
* proper Audio device type.
*/
audinfo("Return %d\n", caps->ac_len);
return caps->ac_len;
}
* Name: audio_rpmsg_configure
*
* Description:
* Configure the driver. Return directly at proxy peer.
*
****************************************************************************/
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_configure(FAR struct audio_lowerhalf_s *dev,
FAR void *session,
FAR const struct audio_caps_s *caps)
#else
static int audio_rpmsg_configure(FAR struct audio_lowerhalf_s *dev,
FAR const struct audio_caps_s *caps)
#endif
{
FAR struct audio_rpmsg_s *aud = (struct audio_rpmsg_s *)dev;
FAR struct audio_rpmsg_configure_s *req;
uint32_t space;
int ret;
audinfo("ac_type: %d\n", caps->ac_type);
ret = audio_rpmsg_local_configure(aud, caps);
if (ret < 0)
{
return ret;
}
req = audio_rpmsg_get_tx_buffer(aud, &space);
if (req == NULL)
{
return -ENOMEM;
}
DEBUGASSERT(sizeof(*req) <= space);
memcpy(&req->caps, caps, sizeof(struct audio_caps_s));
ret = audio_rpmsg_send_recv(aud, AUDIO_RPMSG_CONFIGURE, false,
&req->header, sizeof(*req), NULL);
if (ret < 0)
{
auderr("configure failed, ret=%d\n", ret);
return ret;
}
return OK;
}
static int audio_rpmsg_shutdown(FAR struct audio_lowerhalf_s *dev)
{
return OK;
}
* Name: audio_rpmsg_start
*
* Description:
* Start the driver and put it in the lowest power state possible.
*
****************************************************************************/
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_start(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
#else
static int audio_rpmsg_start(FAR struct audio_lowerhalf_s *dev)
#endif
{
FAR struct audio_rpmsg_s *aud = (FAR struct audio_rpmsg_s *)dev;
audinfo("audio %s start.\n", aud->devname);
return audio_rpmsg_deliver_message(aud, AUDIO_RPMSG_START,
AUDIO_MSG_START);
}
* Name: audio_rpmsg_stop
*
* Description:
* Stop the configured operation (audio streaming, volume
* disabled, etc.).
*
****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_STOP
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_stop(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
#else
static int audio_rpmsg_stop(FAR struct audio_lowerhalf_s *dev)
#endif
{
FAR struct audio_rpmsg_s *aud = (FAR struct audio_rpmsg_s *)dev;
audinfo("audio %s stop.\n", aud->devname);
nxmutex_lock(&aud->mutex);
while (!dq_empty(&aud->pendq))
{
FAR struct ap_buffer_s *apb =
(FAR struct ap_buffer_s *)dq_remfirst(&aud->pendq);
audio_rpmsg_callback(aud, AUDIO_CALLBACK_DEQUEUE, apb);
}
aud->navail = 0;
audio_rpmsg_callback(aud, AUDIO_CALLBACK_COMPLETE, NULL);
nxmutex_unlock(&aud->mutex);
return audio_rpmsg_deliver_message(aud, AUDIO_RPMSG_STOP,
AUDIO_MSG_STOP);
}
#endif
* Name: audio_rpmsg_pause
*
* Description: Pauses the playback.
*
****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_pause(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
#else
static int audio_rpmsg_pause(FAR struct audio_lowerhalf_s *dev)
#endif
{
FAR struct audio_rpmsg_s *aud = (FAR struct audio_rpmsg_s *)dev;
audinfo("audio %s pause.\n", aud->devname);
return audio_rpmsg_deliver_message(aud, AUDIO_RPMSG_PAUSE,
AUDIO_MSG_PAUSE);
}
#endif
* Name: audio_rpmsg_resume
*
* Description: Resumes the playback.
*
****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_resume(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
#else
static int audio_rpmsg_resume(FAR struct audio_lowerhalf_s *dev)
#endif
{
FAR struct audio_rpmsg_s *aud = (FAR struct audio_rpmsg_s *)dev;
audinfo("audio %s resume.\n", aud->devname);
return audio_rpmsg_deliver_message(aud, AUDIO_RPMSG_RESUME,
AUDIO_MSG_RESUME);
}
#endif
static int audio_rpmsg_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
FAR struct ap_buffer_s *apb)
{
FAR struct audio_rpmsg_s *aud = (FAR struct audio_rpmsg_s *)dev;
nxmutex_lock(&aud->mutex);
dq_addlast(&apb->dq_entry, &aud->pendq);
apb->curbyte = 0;
nxmutex_unlock(&aud->mutex);
return audio_rpmsg_deliver_buffer(aud);
}
* Name: audio_rpmsg_ioctl_arglen
*
* Description:
* Get audio rpmsg device ioctl argument length according to the command
*
* Parameters:
* cmd - the ioctl command
* arg - the ioctl arguments
*
* Returned Values:
* negativate - ioctl command not support
* positive - the argument length
*
****************************************************************************/
static ssize_t audio_rpmsg_ioctl_arglen(int cmd, unsigned long arg)
{
switch (cmd)
{
case AUDIOIOC_HWRESET:
return 0;
case AUDIOIOC_GETBUFFERINFO:
case AUDIOIOC_SETBUFFERINFO:
return sizeof(struct ap_buffer_info_s);
case AUDIOIOC_GETAUDIOINFO:
return sizeof(struct audio_info_s);
default:
return -ENOTTY;
}
}
* Name: audio_rpmsg_ioctl
*
* Description: Perform a device ioctl
*
****************************************************************************/
static int audio_rpmsg_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
unsigned long arg)
{
FAR struct audio_rpmsg_s *aud = (FAR struct audio_rpmsg_s *)dev;
struct audio_rpmsg_ioctl_s *req;
uint32_t space;
ssize_t arglen;
size_t reqlen;
int ret;
audinfo("cmd=%d arg=%lu\n", cmd, arg);
ret = audio_rpmsg_local_ioctl(aud, cmd, arg);
if (ret != -ENOTTY)
{
return ret;
}
arglen = audio_rpmsg_ioctl_arglen(cmd, arg);
if (arglen < 0)
{
return arglen;
}
reqlen = sizeof(*req) + arglen;
req = audio_rpmsg_get_tx_buffer(aud, &space);
if (req == NULL)
{
return -ENOMEM;
}
DEBUGASSERT(reqlen <= space);
req->request = cmd;
req->arg = arg;
req->arglen = arglen;
if (arglen > 0)
{
memcpy(req->data, (FAR void *)(uintptr_t)arg, arglen);
}
ret = audio_rpmsg_send_recv(aud, AUDIO_RPMSG_IOCTL,
false, &req->header, reqlen,
arglen > 0 ? (FAR void *)arg : NULL);
if (ret < 0)
{
auderr("configure failed, ret=%d\n", ret);
return ret;
}
audinfo("Return OK\n");
return ret;
}
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_reserve(FAR struct audio_lowerhalf_s *dev,
FAR void **session)
#else
static int audio_rpmsg_reserve(FAR struct audio_lowerhalf_s *dev)
#endif
{
return OK;
}
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int audio_rpmsg_release(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
#else
static int audio_rpmsg_release(FAR struct audio_lowerhalf_s *dev)
#endif
{
return OK;
}
* Name: audio_rpmsg_register
*
* Description:
* register the rpmsg audio driver.
*
* Input Parameters:
* remotecpu - The name of the cpu to connect with
* devname - The name of the audio device, eg: A2DP
* consumer - true for consumer, false for producer.
*
* Returned Value:
* 0 for Success; a negated value on failure.
*
****************************************************************************/
int audio_rpmsg_register(FAR const char *remotecpu, FAR const char *devname,
bool consumer)
{
FAR struct audio_rpmsg_s *aud;
int ret;
aud = (FAR struct audio_rpmsg_s *)kmm_zalloc(sizeof(*aud));
if (!aud)
{
auderr("ERROR: Failed to allocate rpmsg audio device\n");
return -ENOMEM;
}
nxsem_init(&aud->wait, 0, 0);
nxmutex_init(&aud->mutex);
aud->dev.ops = &g_audio_rpmsg_ops;
dq_init(&aud->pendq);
aud->remotecpu = remotecpu;
aud->devname = devname;
aud->consumer = consumer;
ret = audio_register(aud->devname, &aud->dev);
if (ret < 0)
{
auderr("WARNING: Failed to register (%s) audio device(%d)\n",
aud->devname, ret);
goto error;
}
ret = rpmsg_register_callback(aud,
audio_rpmsg_device_created,
audio_rpmsg_device_destroy,
NULL,
NULL);
if (ret < 0)
{
audio_unregister(aud->devname, &aud->dev);
auderr("ERROR: Failed to register rpmsg device, ret=%d\n", ret);
goto error;
}
return OK;
error:
nxmutex_destroy(&aud->mutex);
kmm_free(aud);
return ret;
}
* Public Functions
****************************************************************************/
* Name: audio_rpmsg_initialize
*
* Description:
* Initialize and register rpmsg audio device.
*
****************************************************************************/
int audio_rpmsg_initialize(void)
{
size_t i;
int ret;
for (i = 0; i < nitems(g_audio_rpmsg_params); i++)
{
ret = audio_rpmsg_register(g_audio_rpmsg_params[i].remotecpu,
g_audio_rpmsg_params[i].devname,
g_audio_rpmsg_params[i].consumer);
if (ret < 0)
{
auderr("Failed to register rpmsg audio device %s\n",
g_audio_rpmsg_params[i].devname);
return ret;
}
}
return OK;
}