blob: 122671e10152d681d8196e7ae9509cbe8eee5252 [file] [log] [blame]
Phil Burk2355edb2016-12-26 13:54:02 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16/**
17 * Simple fake HAL that supports ALSA MMAP/NOIRQ mode.
18 */
19
20#include <iostream>
21#include <math.h>
22#include <limits>
23#include <string.h>
24#include <unistd.h>
25
Phil Burk2355edb2016-12-26 13:54:02 -080026#include <sound/asound.h>
27
28#include "tinyalsa/asoundlib.h"
29
30#include "FakeAudioHal.h"
31
Phil Burk5ed503c2017-02-01 09:38:15 -080032//using namespace aaudio;
Phil Burk2355edb2016-12-26 13:54:02 -080033
34using sample_t = int16_t;
35using std::cout;
36using std::endl;
37
38#undef SNDRV_PCM_IOCTL_SYNC_PTR
39#define SNDRV_PCM_IOCTL_SYNC_PTR 0xc0884123
40#define PCM_ERROR_MAX 128
41
42const int SAMPLE_RATE = 48000; // Hz
43const int CHANNEL_COUNT = 2;
44
45struct pcm {
46 int fd;
47 unsigned int flags;
48 int running:1;
49 int prepared:1;
50 int underruns;
51 unsigned int buffer_size;
52 unsigned int boundary;
53 char error[PCM_ERROR_MAX];
54 struct pcm_config config;
55 struct snd_pcm_mmap_status *mmap_status;
56 struct snd_pcm_mmap_control *mmap_control;
57 struct snd_pcm_sync_ptr *sync_ptr;
58 void *mmap_buffer;
59 unsigned int noirq_frames_per_msec;
60 int wait_for_avail_min;
61};
62
63static int pcm_sync_ptr(struct pcm *pcm, int flags) {
64 if (pcm->sync_ptr) {
65 pcm->sync_ptr->flags = flags;
66 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
67 return -1;
68 }
69 return 0;
70}
71
72int pcm_get_hw_ptr(struct pcm* pcm, unsigned int* hw_ptr) {
73 if (!hw_ptr || !pcm) return -EINVAL;
74
75 int result = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
76 if (!result) {
77 *hw_ptr = pcm->sync_ptr->s.status.hw_ptr;
78 }
79
80 return result;
81}
82
83typedef struct stream_tracker {
84 struct pcm * pcm;
85 int framesPerBurst;
86 sample_t * hwBuffer;
87 int32_t capacityInFrames;
88 int32_t capacityInBytes;
89} stream_tracker_t;
90
91#define FRAMES_PER_BURST_QUALCOMM 192
92#define FRAMES_PER_BURST_NVIDIA 128
93
Phil Burk3df348f2017-02-08 11:41:55 -080094int fake_hal_open(int card_id, int device_id,
95 int frameCapacity,
96 fake_hal_stream_ptr *streamPP) {
Phil Burk2355edb2016-12-26 13:54:02 -080097 int framesPerBurst = FRAMES_PER_BURST_QUALCOMM; // TODO update as needed
Phil Burk3df348f2017-02-08 11:41:55 -080098 int periodCountRequested = frameCapacity / framesPerBurst;
Phil Burk2355edb2016-12-26 13:54:02 -080099 int periodCount = 32;
100 unsigned int offset1;
101 unsigned int frames1;
102 void *area = nullptr;
103 int mmapAvail = 0;
104
Phil Burk3df348f2017-02-08 11:41:55 -0800105 // Try to match requested size with a power of 2.
106 while (periodCount < periodCountRequested && periodCount < 1024) {
107 periodCount *= 2;
108 }
109 std::cout << "fake_hal_open() requested frameCapacity = " << frameCapacity << std::endl;
110 std::cout << "fake_hal_open() periodCountRequested = " << periodCountRequested << std::endl;
111 std::cout << "fake_hal_open() periodCount = " << periodCount << std::endl;
112
Phil Burk2355edb2016-12-26 13:54:02 -0800113 // Configuration for an ALSA stream.
114 pcm_config cfg;
115 memset(&cfg, 0, sizeof(cfg));
116 cfg.channels = CHANNEL_COUNT;
117 cfg.format = PCM_FORMAT_S16_LE;
118 cfg.rate = SAMPLE_RATE;
119 cfg.period_count = periodCount;
120 cfg.period_size = framesPerBurst;
121 cfg.start_threshold = 0; // for NOIRQ, should just start, was framesPerBurst;
122 cfg.stop_threshold = INT32_MAX;
123 cfg.silence_size = 0;
124 cfg.silence_threshold = 0;
125 cfg.avail_min = framesPerBurst;
126
127 stream_tracker_t *streamTracker = (stream_tracker_t *) malloc(sizeof(stream_tracker_t));
128 if (streamTracker == nullptr) {
129 return -1;
130 }
131 memset(streamTracker, 0, sizeof(stream_tracker_t));
132
133 streamTracker->pcm = pcm_open(card_id, device_id, PCM_OUT | PCM_MMAP | PCM_NOIRQ, &cfg);
134 if (streamTracker->pcm == nullptr) {
135 cout << "Could not open device." << endl;
136 free(streamTracker);
137 return -1;
138 }
139
140 streamTracker->framesPerBurst = cfg.period_size; // Get from ALSA
141 streamTracker->capacityInFrames = pcm_get_buffer_size(streamTracker->pcm);
142 streamTracker->capacityInBytes = pcm_frames_to_bytes(streamTracker->pcm, streamTracker->capacityInFrames);
143 std::cout << "fake_hal_open() streamTracker->framesPerBurst = " << streamTracker->framesPerBurst << std::endl;
144 std::cout << "fake_hal_open() streamTracker->capacityInFrames = " << streamTracker->capacityInFrames << std::endl;
145
146 if (pcm_is_ready(streamTracker->pcm) < 0) {
147 cout << "Device is not ready." << endl;
148 goto error;
149 }
150
151 if (pcm_prepare(streamTracker->pcm) < 0) {
152 cout << "Device could not be prepared." << endl;
153 cout << "For Marlin, please enter:" << endl;
154 cout << " adb shell" << endl;
155 cout << " tinymix \"QUAT_MI2S_RX Audio Mixer MultiMedia8\" 1" << endl;
156 goto error;
157 }
158 mmapAvail = pcm_mmap_avail(streamTracker->pcm);
159 if (mmapAvail <= 0) {
160 cout << "fake_hal_open() mmap_avail is <=0" << endl;
161 goto error;
162 }
163 cout << "fake_hal_open() mmap_avail = " << mmapAvail << endl;
164
165 // Where is the memory mapped area?
166 if (pcm_mmap_begin(streamTracker->pcm, &area, &offset1, &frames1) < 0) {
167 cout << "fake_hal_open() pcm_mmap_begin failed" << endl;
168 goto error;
169 }
170
171 // Clear the buffer.
172 memset((sample_t*) area, 0, streamTracker->capacityInBytes);
173 streamTracker->hwBuffer = (sample_t*) area;
174 streamTracker->hwBuffer[0] = 32000; // impulse
175
176 // Prime the buffer so it can start.
177 if (pcm_mmap_commit(streamTracker->pcm, 0, framesPerBurst) < 0) {
178 cout << "fake_hal_open() pcm_mmap_commit failed" << endl;
179 goto error;
180 }
181
182 *streamPP = streamTracker;
183 return 1;
184
185error:
186 fake_hal_close(streamTracker);
187 return -1;
188}
189
190int fake_hal_get_mmap_info(fake_hal_stream_ptr stream, mmap_buffer_info *info) {
191 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
192 info->fd = streamTracker->pcm->fd; // TODO use tinyalsa function
193 info->hw_buffer = streamTracker->hwBuffer;
194 info->burst_size_in_frames = streamTracker->framesPerBurst;
195 info->buffer_capacity_in_frames = streamTracker->capacityInFrames;
196 info->buffer_capacity_in_bytes = streamTracker->capacityInBytes;
197 info->sample_rate = SAMPLE_RATE;
198 info->channel_count = CHANNEL_COUNT;
199 return 0;
200}
201
202int fake_hal_start(fake_hal_stream_ptr stream) {
203 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
204 if (pcm_start(streamTracker->pcm) < 0) {
205 cout << "fake_hal_start failed" << endl;
206 return -1;
207 }
208 return 0;
209}
210
211int fake_hal_pause(fake_hal_stream_ptr stream) {
212 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
213 if (pcm_stop(streamTracker->pcm) < 0) {
214 cout << "fake_hal_stop failed" << endl;
215 return -1;
216 }
217 return 0;
218}
219
220int fake_hal_get_frame_counter(fake_hal_stream_ptr stream, int *frame_counter) {
221 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
222 if (pcm_get_hw_ptr(streamTracker->pcm, (unsigned int *)frame_counter) < 0) {
223 cout << "fake_hal_get_frame_counter failed" << endl;
224 return -1;
225 }
226 return 0;
227}
228
229int fake_hal_close(fake_hal_stream_ptr stream) {
230 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
231 pcm_close(streamTracker->pcm);
232 free(streamTracker);
233 return 0;
234}
235