blob: 7fa2eeffa40c83122c18f42bab5c6a4f80b8dbcb [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
26#define __force
27#define __bitwise
28#define __user
29#include <sound/asound.h>
30
31#include "tinyalsa/asoundlib.h"
32
33#include "FakeAudioHal.h"
34
35//using namespace oboe;
36
37using sample_t = int16_t;
38using std::cout;
39using std::endl;
40
41#undef SNDRV_PCM_IOCTL_SYNC_PTR
42#define SNDRV_PCM_IOCTL_SYNC_PTR 0xc0884123
43#define PCM_ERROR_MAX 128
44
45const int SAMPLE_RATE = 48000; // Hz
46const int CHANNEL_COUNT = 2;
47
48struct pcm {
49 int fd;
50 unsigned int flags;
51 int running:1;
52 int prepared:1;
53 int underruns;
54 unsigned int buffer_size;
55 unsigned int boundary;
56 char error[PCM_ERROR_MAX];
57 struct pcm_config config;
58 struct snd_pcm_mmap_status *mmap_status;
59 struct snd_pcm_mmap_control *mmap_control;
60 struct snd_pcm_sync_ptr *sync_ptr;
61 void *mmap_buffer;
62 unsigned int noirq_frames_per_msec;
63 int wait_for_avail_min;
64};
65
66static int pcm_sync_ptr(struct pcm *pcm, int flags) {
67 if (pcm->sync_ptr) {
68 pcm->sync_ptr->flags = flags;
69 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
70 return -1;
71 }
72 return 0;
73}
74
75int pcm_get_hw_ptr(struct pcm* pcm, unsigned int* hw_ptr) {
76 if (!hw_ptr || !pcm) return -EINVAL;
77
78 int result = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
79 if (!result) {
80 *hw_ptr = pcm->sync_ptr->s.status.hw_ptr;
81 }
82
83 return result;
84}
85
86typedef struct stream_tracker {
87 struct pcm * pcm;
88 int framesPerBurst;
89 sample_t * hwBuffer;
90 int32_t capacityInFrames;
91 int32_t capacityInBytes;
92} stream_tracker_t;
93
94#define FRAMES_PER_BURST_QUALCOMM 192
95#define FRAMES_PER_BURST_NVIDIA 128
96
97int fake_hal_open(int card_id, int device_id, fake_hal_stream_ptr *streamPP) {
98 int framesPerBurst = FRAMES_PER_BURST_QUALCOMM; // TODO update as needed
99 int periodCount = 32;
100 unsigned int offset1;
101 unsigned int frames1;
102 void *area = nullptr;
103 int mmapAvail = 0;
104
105 // Configuration for an ALSA stream.
106 pcm_config cfg;
107 memset(&cfg, 0, sizeof(cfg));
108 cfg.channels = CHANNEL_COUNT;
109 cfg.format = PCM_FORMAT_S16_LE;
110 cfg.rate = SAMPLE_RATE;
111 cfg.period_count = periodCount;
112 cfg.period_size = framesPerBurst;
113 cfg.start_threshold = 0; // for NOIRQ, should just start, was framesPerBurst;
114 cfg.stop_threshold = INT32_MAX;
115 cfg.silence_size = 0;
116 cfg.silence_threshold = 0;
117 cfg.avail_min = framesPerBurst;
118
119 stream_tracker_t *streamTracker = (stream_tracker_t *) malloc(sizeof(stream_tracker_t));
120 if (streamTracker == nullptr) {
121 return -1;
122 }
123 memset(streamTracker, 0, sizeof(stream_tracker_t));
124
125 streamTracker->pcm = pcm_open(card_id, device_id, PCM_OUT | PCM_MMAP | PCM_NOIRQ, &cfg);
126 if (streamTracker->pcm == nullptr) {
127 cout << "Could not open device." << endl;
128 free(streamTracker);
129 return -1;
130 }
131
132 streamTracker->framesPerBurst = cfg.period_size; // Get from ALSA
133 streamTracker->capacityInFrames = pcm_get_buffer_size(streamTracker->pcm);
134 streamTracker->capacityInBytes = pcm_frames_to_bytes(streamTracker->pcm, streamTracker->capacityInFrames);
135 std::cout << "fake_hal_open() streamTracker->framesPerBurst = " << streamTracker->framesPerBurst << std::endl;
136 std::cout << "fake_hal_open() streamTracker->capacityInFrames = " << streamTracker->capacityInFrames << std::endl;
137
138 if (pcm_is_ready(streamTracker->pcm) < 0) {
139 cout << "Device is not ready." << endl;
140 goto error;
141 }
142
143 if (pcm_prepare(streamTracker->pcm) < 0) {
144 cout << "Device could not be prepared." << endl;
145 cout << "For Marlin, please enter:" << endl;
146 cout << " adb shell" << endl;
147 cout << " tinymix \"QUAT_MI2S_RX Audio Mixer MultiMedia8\" 1" << endl;
148 goto error;
149 }
150 mmapAvail = pcm_mmap_avail(streamTracker->pcm);
151 if (mmapAvail <= 0) {
152 cout << "fake_hal_open() mmap_avail is <=0" << endl;
153 goto error;
154 }
155 cout << "fake_hal_open() mmap_avail = " << mmapAvail << endl;
156
157 // Where is the memory mapped area?
158 if (pcm_mmap_begin(streamTracker->pcm, &area, &offset1, &frames1) < 0) {
159 cout << "fake_hal_open() pcm_mmap_begin failed" << endl;
160 goto error;
161 }
162
163 // Clear the buffer.
164 memset((sample_t*) area, 0, streamTracker->capacityInBytes);
165 streamTracker->hwBuffer = (sample_t*) area;
166 streamTracker->hwBuffer[0] = 32000; // impulse
167
168 // Prime the buffer so it can start.
169 if (pcm_mmap_commit(streamTracker->pcm, 0, framesPerBurst) < 0) {
170 cout << "fake_hal_open() pcm_mmap_commit failed" << endl;
171 goto error;
172 }
173
174 *streamPP = streamTracker;
175 return 1;
176
177error:
178 fake_hal_close(streamTracker);
179 return -1;
180}
181
182int fake_hal_get_mmap_info(fake_hal_stream_ptr stream, mmap_buffer_info *info) {
183 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
184 info->fd = streamTracker->pcm->fd; // TODO use tinyalsa function
185 info->hw_buffer = streamTracker->hwBuffer;
186 info->burst_size_in_frames = streamTracker->framesPerBurst;
187 info->buffer_capacity_in_frames = streamTracker->capacityInFrames;
188 info->buffer_capacity_in_bytes = streamTracker->capacityInBytes;
189 info->sample_rate = SAMPLE_RATE;
190 info->channel_count = CHANNEL_COUNT;
191 return 0;
192}
193
194int fake_hal_start(fake_hal_stream_ptr stream) {
195 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
196 if (pcm_start(streamTracker->pcm) < 0) {
197 cout << "fake_hal_start failed" << endl;
198 return -1;
199 }
200 return 0;
201}
202
203int fake_hal_pause(fake_hal_stream_ptr stream) {
204 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
205 if (pcm_stop(streamTracker->pcm) < 0) {
206 cout << "fake_hal_stop failed" << endl;
207 return -1;
208 }
209 return 0;
210}
211
212int fake_hal_get_frame_counter(fake_hal_stream_ptr stream, int *frame_counter) {
213 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
214 if (pcm_get_hw_ptr(streamTracker->pcm, (unsigned int *)frame_counter) < 0) {
215 cout << "fake_hal_get_frame_counter failed" << endl;
216 return -1;
217 }
218 return 0;
219}
220
221int fake_hal_close(fake_hal_stream_ptr stream) {
222 stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
223 pcm_close(streamTracker->pcm);
224 free(streamTracker);
225 return 0;
226}
227