blob: 34a24763c8c788628e17c7ab29fad1bfcab7b7e0 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed 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.
*/
/**
* Simple fake HAL that supports ALSA MMAP/NOIRQ mode.
*/
#include <iostream>
#include <math.h>
#include <limits>
#include <string.h>
#include <unistd.h>
#define __force
#define __bitwise
#define __user
#include <sound/asound.h>
#include "tinyalsa/asoundlib.h"
#include "FakeAudioHal.h"
//using namespace aaudio;
using sample_t = int16_t;
using std::cout;
using std::endl;
#undef SNDRV_PCM_IOCTL_SYNC_PTR
#define SNDRV_PCM_IOCTL_SYNC_PTR 0xc0884123
#define PCM_ERROR_MAX 128
const int SAMPLE_RATE = 48000; // Hz
const int CHANNEL_COUNT = 2;
struct pcm {
int fd;
unsigned int flags;
int running:1;
int prepared:1;
int underruns;
unsigned int buffer_size;
unsigned int boundary;
char error[PCM_ERROR_MAX];
struct pcm_config config;
struct snd_pcm_mmap_status *mmap_status;
struct snd_pcm_mmap_control *mmap_control;
struct snd_pcm_sync_ptr *sync_ptr;
void *mmap_buffer;
unsigned int noirq_frames_per_msec;
int wait_for_avail_min;
};
static int pcm_sync_ptr(struct pcm *pcm, int flags) {
if (pcm->sync_ptr) {
pcm->sync_ptr->flags = flags;
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
return -1;
}
return 0;
}
int pcm_get_hw_ptr(struct pcm* pcm, unsigned int* hw_ptr) {
if (!hw_ptr || !pcm) return -EINVAL;
int result = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
if (!result) {
*hw_ptr = pcm->sync_ptr->s.status.hw_ptr;
}
return result;
}
typedef struct stream_tracker {
struct pcm * pcm;
int framesPerBurst;
sample_t * hwBuffer;
int32_t capacityInFrames;
int32_t capacityInBytes;
} stream_tracker_t;
#define FRAMES_PER_BURST_QUALCOMM 192
#define FRAMES_PER_BURST_NVIDIA 128
int fake_hal_open(int card_id, int device_id,
int frameCapacity,
fake_hal_stream_ptr *streamPP) {
int framesPerBurst = FRAMES_PER_BURST_QUALCOMM; // TODO update as needed
int periodCountRequested = frameCapacity / framesPerBurst;
int periodCount = 32;
unsigned int offset1;
unsigned int frames1;
void *area = nullptr;
int mmapAvail = 0;
// Try to match requested size with a power of 2.
while (periodCount < periodCountRequested && periodCount < 1024) {
periodCount *= 2;
}
std::cout << "fake_hal_open() requested frameCapacity = " << frameCapacity << std::endl;
std::cout << "fake_hal_open() periodCountRequested = " << periodCountRequested << std::endl;
std::cout << "fake_hal_open() periodCount = " << periodCount << std::endl;
// Configuration for an ALSA stream.
pcm_config cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.channels = CHANNEL_COUNT;
cfg.format = PCM_FORMAT_S16_LE;
cfg.rate = SAMPLE_RATE;
cfg.period_count = periodCount;
cfg.period_size = framesPerBurst;
cfg.start_threshold = 0; // for NOIRQ, should just start, was framesPerBurst;
cfg.stop_threshold = INT32_MAX;
cfg.silence_size = 0;
cfg.silence_threshold = 0;
cfg.avail_min = framesPerBurst;
stream_tracker_t *streamTracker = (stream_tracker_t *) malloc(sizeof(stream_tracker_t));
if (streamTracker == nullptr) {
return -1;
}
memset(streamTracker, 0, sizeof(stream_tracker_t));
streamTracker->pcm = pcm_open(card_id, device_id, PCM_OUT | PCM_MMAP | PCM_NOIRQ, &cfg);
if (streamTracker->pcm == nullptr) {
cout << "Could not open device." << endl;
free(streamTracker);
return -1;
}
streamTracker->framesPerBurst = cfg.period_size; // Get from ALSA
streamTracker->capacityInFrames = pcm_get_buffer_size(streamTracker->pcm);
streamTracker->capacityInBytes = pcm_frames_to_bytes(streamTracker->pcm, streamTracker->capacityInFrames);
std::cout << "fake_hal_open() streamTracker->framesPerBurst = " << streamTracker->framesPerBurst << std::endl;
std::cout << "fake_hal_open() streamTracker->capacityInFrames = " << streamTracker->capacityInFrames << std::endl;
if (pcm_is_ready(streamTracker->pcm) < 0) {
cout << "Device is not ready." << endl;
goto error;
}
if (pcm_prepare(streamTracker->pcm) < 0) {
cout << "Device could not be prepared." << endl;
cout << "For Marlin, please enter:" << endl;
cout << " adb shell" << endl;
cout << " tinymix \"QUAT_MI2S_RX Audio Mixer MultiMedia8\" 1" << endl;
goto error;
}
mmapAvail = pcm_mmap_avail(streamTracker->pcm);
if (mmapAvail <= 0) {
cout << "fake_hal_open() mmap_avail is <=0" << endl;
goto error;
}
cout << "fake_hal_open() mmap_avail = " << mmapAvail << endl;
// Where is the memory mapped area?
if (pcm_mmap_begin(streamTracker->pcm, &area, &offset1, &frames1) < 0) {
cout << "fake_hal_open() pcm_mmap_begin failed" << endl;
goto error;
}
// Clear the buffer.
memset((sample_t*) area, 0, streamTracker->capacityInBytes);
streamTracker->hwBuffer = (sample_t*) area;
streamTracker->hwBuffer[0] = 32000; // impulse
// Prime the buffer so it can start.
if (pcm_mmap_commit(streamTracker->pcm, 0, framesPerBurst) < 0) {
cout << "fake_hal_open() pcm_mmap_commit failed" << endl;
goto error;
}
*streamPP = streamTracker;
return 1;
error:
fake_hal_close(streamTracker);
return -1;
}
int fake_hal_get_mmap_info(fake_hal_stream_ptr stream, mmap_buffer_info *info) {
stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
info->fd = streamTracker->pcm->fd; // TODO use tinyalsa function
info->hw_buffer = streamTracker->hwBuffer;
info->burst_size_in_frames = streamTracker->framesPerBurst;
info->buffer_capacity_in_frames = streamTracker->capacityInFrames;
info->buffer_capacity_in_bytes = streamTracker->capacityInBytes;
info->sample_rate = SAMPLE_RATE;
info->channel_count = CHANNEL_COUNT;
return 0;
}
int fake_hal_start(fake_hal_stream_ptr stream) {
stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
if (pcm_start(streamTracker->pcm) < 0) {
cout << "fake_hal_start failed" << endl;
return -1;
}
return 0;
}
int fake_hal_pause(fake_hal_stream_ptr stream) {
stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
if (pcm_stop(streamTracker->pcm) < 0) {
cout << "fake_hal_stop failed" << endl;
return -1;
}
return 0;
}
int fake_hal_get_frame_counter(fake_hal_stream_ptr stream, int *frame_counter) {
stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
if (pcm_get_hw_ptr(streamTracker->pcm, (unsigned int *)frame_counter) < 0) {
cout << "fake_hal_get_frame_counter failed" << endl;
return -1;
}
return 0;
}
int fake_hal_close(fake_hal_stream_ptr stream) {
stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
pcm_close(streamTracker->pcm);
free(streamTracker);
return 0;
}