struct mutex vchi_mutex;
struct bcm2835_alsa_stream *alsa_stream;
int result;
+ unsigned int max_packet;
short peer_version;
};
static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream,
unsigned int count, void *src);
-// Routine to send a message across a service
+static void bcm2835_audio_lock(struct bcm2835_audio_instance *instance)
+{
+ mutex_lock(&instance->vchi_mutex);
+ vchi_service_use(instance->vchi_handle);
+}
+
+static void bcm2835_audio_unlock(struct bcm2835_audio_instance *instance)
+{
+ vchi_service_release(instance->vchi_handle);
+ mutex_unlock(&instance->vchi_mutex);
+}
+
+static int bcm2835_audio_send_msg_locked(struct bcm2835_audio_instance *instance,
+ struct vc_audio_msg *m, bool wait)
+{
+ int status;
+
+ if (wait) {
+ instance->result = -1;
+ init_completion(&instance->msg_avail_comp);
+ }
+
+ status = vchi_queue_kernel_message(instance->vchi_handle,
+ m, sizeof(*m));
+ if (status) {
+ LOG_ERR("vchi message queue failed: %d, msg=%d\n",
+ status, m->type);
+ return -EIO;
+ }
+
+ if (wait) {
+ if (!wait_for_completion_timeout(&instance->msg_avail_comp,
+ msecs_to_jiffies(10 * 1000))) {
+ LOG_ERR("vchi message timeout, msg=%d\n", m->type);
+ return -ETIMEDOUT;
+ } else if (instance->result) {
+ LOG_ERR("vchi message response error:%d, msg=%d\n",
+ instance->result, m->type);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
-static int
-bcm2835_vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle,
- void *data,
- unsigned int size)
+static int bcm2835_audio_send_msg(struct bcm2835_audio_instance *instance,
+ struct vc_audio_msg *m, bool wait)
{
- return vchi_queue_kernel_message(handle,
- data,
- size);
+ int err;
+
+ bcm2835_audio_lock(instance);
+ err = bcm2835_audio_send_msg_locked(instance, m, wait);
+ bcm2835_audio_unlock(instance);
+ return err;
+}
+
+static int bcm2835_audio_send_simple(struct bcm2835_audio_instance *instance,
+ int type, bool wait)
+{
+ struct vc_audio_msg m = { .type = type };
+
+ return bcm2835_audio_send_msg(instance, &m, wait);
}
static const u32 BCM2835_AUDIO_WRITE_COOKIE1 = ('B' << 24 | 'C' << 16 |
int status;
mutex_lock(&instance->vchi_mutex);
-
- /* Close all VCHI service connections */
vchi_service_use(instance->vchi_handle);
+ /* Close all VCHI service connections */
status = vchi_service_close(instance->vchi_handle);
if (status) {
LOG_DBG("%s: failed to close VCHI service connection (status=%d)\n",
instance = vc_vchi_audio_init(vhci_ctx->vchi_instance,
vhci_ctx->vchi_connection);
- if (IS_ERR(instance)) {
- LOG_ERR("%s: failed to initialize audio service\n", __func__);
-
- /* vchi_instance is retained for use the next time. */
+ if (IS_ERR(instance))
return PTR_ERR(instance);
- }
instance->alsa_stream = alsa_stream;
alsa_stream->instance = instance;
int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream)
{
struct bcm2835_audio_instance *instance;
- struct vc_audio_msg m;
- int status;
- int ret;
+ int err;
alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1);
if (!alsa_stream->my_wq)
return -ENOMEM;
- ret = bcm2835_audio_open_connection(alsa_stream);
- if (ret)
+ err = bcm2835_audio_open_connection(alsa_stream);
+ if (err < 0)
goto free_wq;
instance = alsa_stream->instance;
- LOG_DBG(" instance (%p)\n", instance);
-
- mutex_lock(&instance->vchi_mutex);
- vchi_service_use(instance->vchi_handle);
-
- m.type = VC_AUDIO_MSG_TYPE_OPEN;
-
- /* Send the message to the videocore */
- status = bcm2835_vchi_msg_queue(instance->vchi_handle,
- &m, sizeof(m));
-
- if (status) {
- LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- __func__, status);
-
- ret = -1;
- goto unlock;
- }
- ret = 0;
+ err = bcm2835_audio_send_simple(instance, VC_AUDIO_MSG_TYPE_OPEN,
+ false);
+ if (err < 0)
+ goto deinit;
-unlock:
- vchi_service_release(instance->vchi_handle);
- mutex_unlock(&instance->vchi_mutex);
+ bcm2835_audio_lock(instance);
+ vchi_get_peer_version(instance->vchi_handle, &instance->peer_version);
+ bcm2835_audio_unlock(instance);
+ if (instance->peer_version < 2 || force_bulk)
+ instance->max_packet = 0; /* bulk transfer */
+ else
+ instance->max_packet = 4000;
-free_wq:
- if (ret)
- destroy_workqueue(alsa_stream->my_wq);
+ return 0;
- return ret;
+ deinit:
+ vc_vchi_audio_deinit(instance);
+ free_wq:
+ destroy_workqueue(alsa_stream->my_wq);
+ return err;
}
int bcm2835_audio_set_ctls(struct bcm2835_alsa_stream *alsa_stream)
{
- struct vc_audio_msg m;
- struct bcm2835_audio_instance *instance = alsa_stream->instance;
struct bcm2835_chip *chip = alsa_stream->chip;
- int status;
- int ret;
-
- LOG_INFO(" Setting ALSA dest(%d), volume(%d)\n",
- chip->dest, chip->volume);
-
- mutex_lock(&instance->vchi_mutex);
- vchi_service_use(instance->vchi_handle);
-
- instance->result = -1;
+ struct vc_audio_msg m = {};
m.type = VC_AUDIO_MSG_TYPE_CONTROL;
m.u.control.dest = chip->dest;
else
m.u.control.volume = alsa2chip(chip->volume);
- /* Create the message available completion */
- init_completion(&instance->msg_avail_comp);
-
- /* Send the message to the videocore */
- status = bcm2835_vchi_msg_queue(instance->vchi_handle,
- &m, sizeof(m));
-
- if (status) {
- LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- __func__, status);
-
- ret = -1;
- goto unlock;
- }
-
- /* We are expecting a reply from the videocore */
- wait_for_completion(&instance->msg_avail_comp);
-
- if (instance->result) {
- LOG_ERR("%s: result=%d\n", __func__, instance->result);
-
- ret = -1;
- goto unlock;
- }
-
- ret = 0;
-
-unlock:
- vchi_service_release(instance->vchi_handle);
- mutex_unlock(&instance->vchi_mutex);
-
- return ret;
+ return bcm2835_audio_send_msg(alsa_stream->instance, &m, true);
}
int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream,
unsigned int channels, unsigned int samplerate,
unsigned int bps)
{
- struct vc_audio_msg m;
- struct bcm2835_audio_instance *instance = alsa_stream->instance;
- int status;
- int ret;
-
- LOG_INFO(" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n",
- channels, samplerate, bps);
+ struct vc_audio_msg m = {
+ .type = VC_AUDIO_MSG_TYPE_CONFIG,
+ .u.config.channels = channels,
+ .u.config.samplerate = samplerate,
+ .u.config.bps = bps,
+ };
+ int err;
/* resend ctls - alsa_stream may not have been open when first send */
- ret = bcm2835_audio_set_ctls(alsa_stream);
- if (ret) {
- LOG_ERR(" Alsa controls not supported\n");
- return -EINVAL;
- }
-
- mutex_lock(&instance->vchi_mutex);
- vchi_service_use(instance->vchi_handle);
-
- instance->result = -1;
-
- m.type = VC_AUDIO_MSG_TYPE_CONFIG;
- m.u.config.channels = channels;
- m.u.config.samplerate = samplerate;
- m.u.config.bps = bps;
-
- /* Create the message available completion */
- init_completion(&instance->msg_avail_comp);
-
- /* Send the message to the videocore */
- status = bcm2835_vchi_msg_queue(instance->vchi_handle,
- &m, sizeof(m));
-
- if (status) {
- LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- __func__, status);
-
- ret = -1;
- goto unlock;
- }
-
- /* We are expecting a reply from the videocore */
- wait_for_completion(&instance->msg_avail_comp);
-
- if (instance->result) {
- LOG_ERR("%s: result=%d", __func__, instance->result);
-
- ret = -1;
- goto unlock;
- }
-
- ret = 0;
-
-unlock:
- vchi_service_release(instance->vchi_handle);
- mutex_unlock(&instance->vchi_mutex);
+ err = bcm2835_audio_set_ctls(alsa_stream);
+ if (err)
+ return err;
- return ret;
+ return bcm2835_audio_send_msg(alsa_stream->instance, &m, true);
}
static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream)
{
- struct vc_audio_msg m;
- struct bcm2835_audio_instance *instance = alsa_stream->instance;
- int status;
- int ret;
-
- mutex_lock(&instance->vchi_mutex);
- vchi_service_use(instance->vchi_handle);
-
- m.type = VC_AUDIO_MSG_TYPE_START;
-
- /* Send the message to the videocore */
- status = bcm2835_vchi_msg_queue(instance->vchi_handle,
- &m, sizeof(m));
-
- if (status) {
- LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- __func__, status);
-
- ret = -1;
- goto unlock;
- }
-
- ret = 0;
-
-unlock:
- vchi_service_release(instance->vchi_handle);
- mutex_unlock(&instance->vchi_mutex);
- return ret;
+ return bcm2835_audio_send_simple(alsa_stream->instance,
+ VC_AUDIO_MSG_TYPE_START, false);
}
static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream)
{
- struct vc_audio_msg m;
- struct bcm2835_audio_instance *instance = alsa_stream->instance;
- int status;
- int ret;
-
- mutex_lock(&instance->vchi_mutex);
- vchi_service_use(instance->vchi_handle);
-
- m.type = VC_AUDIO_MSG_TYPE_STOP;
- m.u.stop.draining = alsa_stream->draining;
-
- /* Send the message to the videocore */
- status = bcm2835_vchi_msg_queue(instance->vchi_handle,
- &m, sizeof(m));
-
- if (status) {
- LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- __func__, status);
-
- ret = -1;
- goto unlock;
- }
-
- ret = 0;
-
-unlock:
- vchi_service_release(instance->vchi_handle);
- mutex_unlock(&instance->vchi_mutex);
- return ret;
+ return bcm2835_audio_send_simple(alsa_stream->instance,
+ VC_AUDIO_MSG_TYPE_STOP, false);
}
int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream)
{
- struct vc_audio_msg m;
struct bcm2835_audio_instance *instance = alsa_stream->instance;
- int status;
- int ret;
+ int err;
my_workqueue_quit(alsa_stream);
- mutex_lock(&instance->vchi_mutex);
- vchi_service_use(instance->vchi_handle);
-
- m.type = VC_AUDIO_MSG_TYPE_CLOSE;
-
- /* Create the message available completion */
- init_completion(&instance->msg_avail_comp);
-
- /* Send the message to the videocore */
- status = bcm2835_vchi_msg_queue(instance->vchi_handle,
- &m, sizeof(m));
-
- if (status) {
- LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- __func__, status);
- ret = -1;
- goto unlock;
- }
-
- /* We are expecting a reply from the videocore */
- wait_for_completion(&instance->msg_avail_comp);
-
- if (instance->result) {
- LOG_ERR("%s: failed result (result=%d)\n",
- __func__, instance->result);
-
- ret = -1;
- goto unlock;
- }
-
- ret = 0;
-
-unlock:
- vchi_service_release(instance->vchi_handle);
- mutex_unlock(&instance->vchi_mutex);
+ err = bcm2835_audio_send_simple(alsa_stream->instance,
+ VC_AUDIO_MSG_TYPE_CLOSE, true);
/* Stop the audio service */
vc_vchi_audio_deinit(instance);
alsa_stream->instance = NULL;
- return ret;
+ return err;
}
static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream,
- unsigned int count, void *src)
+ unsigned int size, void *src)
{
- struct vc_audio_msg m;
struct bcm2835_audio_instance *instance = alsa_stream->instance;
- int status;
- int ret;
-
- LOG_INFO(" Writing %d bytes from %p\n", count, src);
-
- mutex_lock(&instance->vchi_mutex);
- vchi_service_use(instance->vchi_handle);
-
- if (instance->peer_version == 0 &&
- vchi_get_peer_version(instance->vchi_handle, &instance->peer_version) == 0)
- LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version);
+ struct vc_audio_msg m = {
+ .type = VC_AUDIO_MSG_TYPE_WRITE,
+ .u.write.count = size,
+ .u.write.max_packet = instance->max_packet,
+ .u.write.cookie1 = BCM2835_AUDIO_WRITE_COOKIE1,
+ .u.write.cookie2 = BCM2835_AUDIO_WRITE_COOKIE2,
+ };
+ unsigned int count;
+ int err, status;
- m.type = VC_AUDIO_MSG_TYPE_WRITE;
- m.u.write.count = count;
- // old version uses bulk, new version uses control
- m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0 : 4000;
- m.u.write.cookie1 = BCM2835_AUDIO_WRITE_COOKIE1;
- m.u.write.cookie2 = BCM2835_AUDIO_WRITE_COOKIE2;
- m.u.write.silence = src == NULL;
+ if (!size)
+ return 0;
- /* Send the message to the videocore */
- status = bcm2835_vchi_msg_queue(instance->vchi_handle,
- &m, sizeof(m));
+ bcm2835_audio_lock(instance);
+ err = bcm2835_audio_send_msg_locked(instance, &m, false);
+ if (err < 0)
+ goto unlock;
- if (status) {
- LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- __func__, status);
+ count = size;
+ if (!instance->max_packet) {
+ /* Send the message to the videocore */
+ status = vchi_bulk_queue_transmit(instance->vchi_handle,
+ src, count,
+ VCHI_FLAGS_BLOCK_UNTIL_DATA_READ,
+ NULL);
+ } else {
+ while (count > 0) {
+ int bytes = min(instance->max_packet, count);
- ret = -1;
- goto unlock;
- }
- if (!m.u.write.silence) {
- if (!m.u.write.max_packet) {
- /* Send the message to the videocore */
- status = vchi_bulk_queue_transmit(instance->vchi_handle,
- src, count,
- 0 * VCHI_FLAGS_BLOCK_UNTIL_QUEUED
- +
- 1 * VCHI_FLAGS_BLOCK_UNTIL_DATA_READ,
- NULL);
- } else {
- while (count > 0) {
- int bytes = min_t(int, m.u.write.max_packet, count);
-
- status = bcm2835_vchi_msg_queue(instance->vchi_handle,
- src, bytes);
- src = (char *)src + bytes;
- count -= bytes;
- }
+ status = vchi_queue_kernel_message(instance->vchi_handle,
+ src, bytes);
+ src += bytes;
+ count -= bytes;
}
- if (status) {
- LOG_ERR("%s: failed on vchi_bulk_queue_transmit (status=%d)\n",
- __func__, status);
+ }
- ret = -1;
- goto unlock;
- }
+ if (status) {
+ LOG_ERR("failed on %d bytes transfer (status=%d)\n",
+ size, status);
+ err = -EIO;
}
- ret = 0;
-unlock:
- vchi_service_release(instance->vchi_handle);
- mutex_unlock(&instance->vchi_mutex);
- return ret;
+ unlock:
+ bcm2835_audio_unlock(instance);
+ return err;
}
unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream)