OSDN Git Service

Snap for 4807121 from 1a5092e9648b9e59b7e2d7fd92b9368cd5966af9 to pi-release
[android-x86/system-media.git] / audio_utils / fifo.cpp
1 /*
2  * Copyright (C) 2015 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "audio_utils_fifo"
19
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <audio_utils/clock_nanosleep.h>
26 #include <audio_utils/fifo.h>
27 #include <audio_utils/futex.h>
28 #include <audio_utils/roundup.h>
29 #include <cutils/log.h>
30 #include <utils/Errors.h>
31
32 audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount,
33         audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
34         __attribute__((no_sanitize("integer"))) :
35     mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)),
36     mFudgeFactor(mFrameCountP2 - mFrameCount),
37     // FIXME need an API to configure the sync types
38     mWriterRear(writerRear), mWriterRearSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
39     mThrottleFront(throttleFront), mThrottleFrontSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
40     mIsShutdown(false)
41 {
42     // actual upper bound on frameCount will depend on the frame size
43     LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT32_MAX));
44 }
45
46 audio_utils_fifo_base::~audio_utils_fifo_base()
47 {
48 }
49
50 uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment) const
51         __attribute__((no_sanitize("integer")))
52 {
53     if (mFudgeFactor > 0) {
54         uint32_t mask = mFrameCountP2 - 1;
55         ALOG_ASSERT((index & mask) < mFrameCount);
56         ALOG_ASSERT(increment <= mFrameCountP2);
57         if ((index & mask) + increment >= mFrameCount) {
58             increment += mFudgeFactor;
59         }
60         index += increment;
61         ALOG_ASSERT((index & mask) < mFrameCount);
62         return index;
63     } else {
64         return index + increment;
65     }
66 }
67
68 int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost, bool flush) const
69         __attribute__((no_sanitize("integer")))
70 {
71     // TODO replace multiple returns by a single return point so this isn't needed
72     if (lost != NULL) {
73         *lost = 0;
74     }
75     if (mIsShutdown) {
76         return -EIO;
77     }
78     uint32_t diff = rear - front;
79     if (mFudgeFactor > 0) {
80         uint32_t mask = mFrameCountP2 - 1;
81         uint32_t rearOffset = rear & mask;
82         uint32_t frontOffset = front & mask;
83         if (rearOffset >= mFrameCount || frontOffset >= mFrameCount) {
84             ALOGE("%s frontOffset=%u rearOffset=%u mFrameCount=%u",
85                     __func__, frontOffset, rearOffset, mFrameCount);
86             shutdown();
87             return -EIO;
88         }
89         // genDiff is the difference between the generation count fields of rear and front,
90         // and is always a multiple of mFrameCountP2.
91         uint32_t genDiff = (rear & ~mask) - (front & ~mask);
92         // It's OK for writer to be one generation beyond reader,
93         // but reader has lost frames if writer is further than one generation beyond.
94         if (genDiff > mFrameCountP2) {
95             if (lost != NULL) {
96                 // Calculate the number of lost frames as the raw difference,
97                 // less the mFrameCount frames that are still valid and can be read on retry,
98                 // less the wasted indices that don't count as true lost frames.
99                 *lost = diff - (flush ? 0 : mFrameCount) - mFudgeFactor * (genDiff/mFrameCountP2);
100             }
101             return -EOVERFLOW;
102         }
103         // If writer is one generation beyond reader, skip over the wasted indices.
104         if (genDiff > 0) {
105             diff -= mFudgeFactor;
106             // Note is still possible for diff > mFrameCount. BCD 16 - BCD 1 shows the problem.
107             // genDiff is 16, fudge is 6, decimal diff is 15 = (22 - 1 - 6).
108             // So we need to check diff for overflow one more time. See "if" a few lines below.
109         }
110     }
111     // FIFO should not be overfull
112     if (diff > mFrameCount) {
113         if (lost != NULL) {
114             *lost = diff - (flush ? 0 : mFrameCount);
115         }
116         return -EOVERFLOW;
117     }
118     return (int32_t) diff;
119 }
120
121 void audio_utils_fifo_base::shutdown() const
122 {
123     ALOGE("%s", __func__);
124     mIsShutdown = true;
125 }
126
127 ////////////////////////////////////////////////////////////////////////////////
128
129 audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
130         audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
131         __attribute__((no_sanitize("integer"))) :
132     audio_utils_fifo_base(frameCount, writerRear, throttleFront),
133     mFrameSize(frameSize), mBuffer(buffer)
134 {
135     // maximum value of frameCount * frameSize is INT32_MAX (2^31 - 1), not 2^31, because we need to
136     // be able to distinguish successful and error return values from read and write.
137     LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
138             frameCount > ((uint32_t) INT32_MAX) / frameSize);
139 }
140
141 audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
142         bool throttlesWriter) :
143     audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
144         throttlesWriter ?  &mSingleProcessSharedFront : NULL)
145 {
146 }
147
148 audio_utils_fifo::~audio_utils_fifo()
149 {
150 }
151
152 ////////////////////////////////////////////////////////////////////////////////
153
154 audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) :
155     mFifo(fifo), mObtained(0), mTotalReleased(0)
156 {
157 }
158
159 audio_utils_fifo_provider::~audio_utils_fifo_provider()
160 {
161 }
162
163 ////////////////////////////////////////////////////////////////////////////////
164
165 audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
166     audio_utils_fifo_provider(fifo), mLocalRear(0),
167     mArmLevel(fifo.mFrameCount), mTriggerLevel(0),
168     mIsArmed(true), // because initial fill level of zero is < mArmLevel
169     mEffectiveFrames(fifo.mFrameCount)
170 {
171 }
172
173 audio_utils_fifo_writer::~audio_utils_fifo_writer()
174 {
175 }
176
177 ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count,
178         const struct timespec *timeout)
179         __attribute__((no_sanitize("integer")))
180 {
181     audio_utils_iovec iovec[2];
182     ssize_t availToWrite = obtain(iovec, count, timeout);
183     if (availToWrite > 0) {
184         memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
185                 iovec[0].mLength * mFifo.mFrameSize);
186         if (iovec[1].mLength > 0) {
187             memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
188                     (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
189                     iovec[1].mLength * mFifo.mFrameSize);
190         }
191         release(availToWrite);
192     }
193     return availToWrite;
194 }
195
196 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
197 ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
198         const struct timespec *timeout)
199         __attribute__((no_sanitize("integer")))
200 {
201     int err = 0;
202     size_t availToWrite;
203     if (mFifo.mThrottleFront != NULL) {
204         int retries = kRetries;
205         uint32_t front;
206         for (;;) {
207             front = mFifo.mThrottleFront->loadAcquire();
208             // returns -EIO if mIsShutdown
209             int32_t filled = mFifo.diff(mLocalRear, front);
210             if (filled < 0) {
211                 // on error, return an empty slice
212                 err = filled;
213                 availToWrite = 0;
214                 break;
215             }
216             availToWrite = mEffectiveFrames > (uint32_t) filled ?
217                     mEffectiveFrames - (uint32_t) filled : 0;
218             // TODO pull out "count == 0"
219             if (count == 0 || availToWrite > 0 || timeout == NULL ||
220                     (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
221                 break;
222             }
223             // TODO add comments
224             // TODO abstract out switch and replace by general sync object
225             //      the high level code (synchronization, sleep, futex, iovec) should be completely
226             //      separate from the low level code (indexes, available, masking).
227             int op = FUTEX_WAIT;
228             switch (mFifo.mThrottleFrontSync) {
229             case AUDIO_UTILS_FIFO_SYNC_SLEEP:
230                 err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout,
231                         NULL /*remain*/);
232                 if (err < 0) {
233                     LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
234                     err = -errno;
235                 } else {
236                     err = -ETIMEDOUT;
237                 }
238                 break;
239             case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
240                 op = FUTEX_WAIT_PRIVATE;
241                 // fall through
242             case AUDIO_UTILS_FIFO_SYNC_SHARED:
243                 if (timeout->tv_sec == LONG_MAX) {
244                     timeout = NULL;
245                 }
246                 err = mFifo.mThrottleFront->wait(op, front, timeout);
247                 if (err < 0) {
248                     switch (errno) {
249                     case EWOULDBLOCK:
250                         // Benign race condition with partner: mFifo.mThrottleFront->mIndex
251                         // changed value between the earlier atomic_load_explicit() and sys_futex().
252                         // Try to load index again, but give up if we are unable to converge.
253                         if (retries-- > 0) {
254                             // bypass the "timeout = NULL;" below
255                             continue;
256                         }
257                         // fall through
258                     case EINTR:
259                     case ETIMEDOUT:
260                         err = -errno;
261                         break;
262                     default:
263                         LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
264                         break;
265                     }
266                 }
267                 break;
268             default:
269                 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
270                 break;
271             }
272             timeout = NULL;
273         }
274     } else {
275         if (mFifo.mIsShutdown) {
276             err = -EIO;
277             availToWrite = 0;
278         } else {
279             availToWrite = mEffectiveFrames;
280         }
281     }
282     if (availToWrite > count) {
283         availToWrite = count;
284     }
285     uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
286     size_t part1 = mFifo.mFrameCount - rearOffset;
287     if (part1 > availToWrite) {
288         part1 = availToWrite;
289     }
290     size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
291     // return slice
292     if (iovec != NULL) {
293         iovec[0].mOffset = rearOffset;
294         iovec[0].mLength = part1;
295         iovec[1].mOffset = 0;
296         iovec[1].mLength = part2;
297         mObtained = availToWrite;
298     }
299     return availToWrite > 0 ? availToWrite : err;
300 }
301
302 void audio_utils_fifo_writer::release(size_t count)
303         __attribute__((no_sanitize("integer")))
304 {
305     // no need to do an early check for mIsShutdown, because the extra code executed is harmless
306     if (count > 0) {
307         if (count > mObtained) {
308             ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
309             mFifo.shutdown();
310             return;
311         }
312         if (mFifo.mThrottleFront != NULL) {
313             uint32_t front = mFifo.mThrottleFront->loadAcquire();
314             // returns -EIO if mIsShutdown
315             int32_t filled = mFifo.diff(mLocalRear, front);
316             mLocalRear = mFifo.sum(mLocalRear, count);
317             mFifo.mWriterRear.storeRelease(mLocalRear);
318             // TODO add comments
319             int op = FUTEX_WAKE;
320             switch (mFifo.mWriterRearSync) {
321             case AUDIO_UTILS_FIFO_SYNC_SLEEP:
322                 break;
323             case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
324                 op = FUTEX_WAKE_PRIVATE;
325                 // fall through
326             case AUDIO_UTILS_FIFO_SYNC_SHARED:
327                 if (filled >= 0) {
328                     if ((uint32_t) filled < mArmLevel) {
329                         mIsArmed = true;
330                     }
331                     if (mIsArmed && filled + count > mTriggerLevel) {
332                         int err = mFifo.mWriterRear.wake(op, INT32_MAX /*waiters*/);
333                         // err is number of processes woken up
334                         if (err < 0) {
335                             LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
336                                     __func__, err, errno);
337                         }
338                         mIsArmed = false;
339                     }
340                 }
341                 break;
342             default:
343                 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
344                 break;
345             }
346         } else {
347             mLocalRear = mFifo.sum(mLocalRear, count);
348             mFifo.mWriterRear.storeRelease(mLocalRear);
349         }
350         mObtained -= count;
351         mTotalReleased += count;
352     }
353 }
354
355 ssize_t audio_utils_fifo_writer::available()
356 {
357     // iovec == NULL is not part of the public API, but internally it means don't set mObtained
358     return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
359 }
360
361 void audio_utils_fifo_writer::resize(uint32_t frameCount)
362 {
363     // cap to range [0, mFifo.mFrameCount]
364     if (frameCount > mFifo.mFrameCount) {
365         frameCount = mFifo.mFrameCount;
366     }
367     // if we reduce the effective frame count, update hysteresis points to be within the new range
368     if (frameCount < mEffectiveFrames) {
369         if (mArmLevel > frameCount) {
370             mArmLevel = frameCount;
371         }
372         if (mTriggerLevel > frameCount) {
373             mTriggerLevel = frameCount;
374         }
375     }
376     mEffectiveFrames = frameCount;
377 }
378
379 uint32_t audio_utils_fifo_writer::size() const
380 {
381     return mEffectiveFrames;
382 }
383
384 void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
385 {
386     // cap to range [0, mEffectiveFrames]
387     if (lowLevelArm > mEffectiveFrames) {
388         lowLevelArm = mEffectiveFrames;
389     }
390     if (highLevelTrigger > mEffectiveFrames) {
391         highLevelTrigger = mEffectiveFrames;
392     }
393     // TODO this is overly conservative; it would be better to arm based on actual fill level
394     if (lowLevelArm > mArmLevel) {
395         mIsArmed = true;
396     }
397     mArmLevel = lowLevelArm;
398     mTriggerLevel = highLevelTrigger;
399 }
400
401 void audio_utils_fifo_writer::getHysteresis(uint32_t *armLevel, uint32_t *triggerLevel) const
402 {
403     *armLevel = mArmLevel;
404     *triggerLevel = mTriggerLevel;
405 }
406
407 ////////////////////////////////////////////////////////////////////////////////
408
409 audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter,
410         bool flush) :
411     audio_utils_fifo_provider(fifo),
412
413     // If we throttle the writer, then initialize our front index to zero so that we see all data
414     // currently in the buffer.
415     // Otherwise, ignore everything currently in the buffer by initializing our front index to the
416     // current value of writer's rear.  This avoids an immediate -EOVERFLOW (overrun) in the case
417     // where reader starts out more than one buffer behind writer.  The initial catch-up does not
418     // contribute towards the totalLost, totalFlushed, or totalReleased counters.
419     mLocalFront(throttlesWriter ? 0 : mFifo.mWriterRear.loadConsume()),
420
421     mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
422     mFlush(flush),
423     mArmLevel(-1), mTriggerLevel(mFifo.mFrameCount),
424     mIsArmed(true), // because initial fill level of zero is > mArmLevel
425     mTotalLost(0), mTotalFlushed(0)
426 {
427 }
428
429 audio_utils_fifo_reader::~audio_utils_fifo_reader()
430 {
431     // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
432 }
433
434 ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
435         size_t *lost)
436         __attribute__((no_sanitize("integer")))
437 {
438     audio_utils_iovec iovec[2];
439     ssize_t availToRead = obtain(iovec, count, timeout, lost);
440     if (availToRead > 0) {
441         memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
442                 iovec[0].mLength * mFifo.mFrameSize);
443         if (iovec[1].mLength > 0) {
444             memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
445                     (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
446                     iovec[1].mLength * mFifo.mFrameSize);
447         }
448         release(availToRead);
449     }
450     return availToRead;
451 }
452
453 ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
454         const struct timespec *timeout)
455         __attribute__((no_sanitize("integer")))
456 {
457     return obtain(iovec, count, timeout, NULL /*lost*/);
458 }
459
460 void audio_utils_fifo_reader::release(size_t count)
461         __attribute__((no_sanitize("integer")))
462 {
463     // no need to do an early check for mIsShutdown, because the extra code executed is harmless
464     if (count > 0) {
465         if (count > mObtained) {
466             ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
467             mFifo.shutdown();
468             return;
469         }
470         if (mThrottleFront != NULL) {
471             uint32_t rear = mFifo.mWriterRear.loadAcquire();
472             // returns -EIO if mIsShutdown
473             int32_t filled = mFifo.diff(rear, mLocalFront);
474             mLocalFront = mFifo.sum(mLocalFront, count);
475             mThrottleFront->storeRelease(mLocalFront);
476             // TODO add comments
477             int op = FUTEX_WAKE;
478             switch (mFifo.mThrottleFrontSync) {
479             case AUDIO_UTILS_FIFO_SYNC_SLEEP:
480                 break;
481             case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
482                 op = FUTEX_WAKE_PRIVATE;
483                 // fall through
484             case AUDIO_UTILS_FIFO_SYNC_SHARED:
485                 if (filled >= 0) {
486                     if (filled > mArmLevel) {
487                         mIsArmed = true;
488                     }
489                     if (mIsArmed && filled - count < mTriggerLevel) {
490                         int err = mThrottleFront->wake(op, 1 /*waiters*/);
491                         // err is number of processes woken up
492                         if (err < 0 || err > 1) {
493                             LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
494                                     __func__, err, errno);
495                         }
496                         mIsArmed = false;
497                     }
498                 }
499                 break;
500             default:
501                 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
502                 break;
503             }
504         } else {
505             mLocalFront = mFifo.sum(mLocalFront, count);
506         }
507         mObtained -= count;
508         mTotalReleased += count;
509     }
510 }
511
512 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
513 ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
514         const struct timespec *timeout, size_t *lost)
515         __attribute__((no_sanitize("integer")))
516 {
517     int err = 0;
518     int retries = kRetries;
519     uint32_t rear;
520     for (;;) {
521         rear = mFifo.mWriterRear.loadAcquire();
522         // TODO pull out "count == 0"
523         if (count == 0 || rear != mLocalFront || timeout == NULL ||
524                 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
525             break;
526         }
527         // TODO add comments
528         int op = FUTEX_WAIT;
529         switch (mFifo.mWriterRearSync) {
530         case AUDIO_UTILS_FIFO_SYNC_SLEEP:
531             err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout,
532                     NULL /*remain*/);
533             if (err < 0) {
534                 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
535                 err = -errno;
536             } else {
537                 err = -ETIMEDOUT;
538             }
539             break;
540         case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
541             op = FUTEX_WAIT_PRIVATE;
542             // fall through
543         case AUDIO_UTILS_FIFO_SYNC_SHARED:
544             if (timeout->tv_sec == LONG_MAX) {
545                 timeout = NULL;
546             }
547             err = mFifo.mWriterRear.wait(op, rear, timeout);
548             if (err < 0) {
549                 switch (errno) {
550                 case EWOULDBLOCK:
551                     // Benign race condition with partner: mFifo.mWriterRear->mIndex
552                     // changed value between the earlier atomic_load_explicit() and sys_futex().
553                     // Try to load index again, but give up if we are unable to converge.
554                     if (retries-- > 0) {
555                         // bypass the "timeout = NULL;" below
556                         continue;
557                     }
558                     // fall through
559                 case EINTR:
560                 case ETIMEDOUT:
561                     err = -errno;
562                     break;
563                 default:
564                     LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
565                     break;
566                 }
567             }
568             break;
569         default:
570             LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
571             break;
572         }
573         timeout = NULL;
574     }
575     size_t ourLost;
576     if (lost == NULL) {
577         lost = &ourLost;
578     }
579     // returns -EIO if mIsShutdown
580     int32_t filled = mFifo.diff(rear, mLocalFront, lost, mFlush);
581     mTotalLost += *lost;
582     mTotalReleased += *lost;
583     if (filled < 0) {
584         if (filled == -EOVERFLOW) {
585             // catch up with writer, but preserve the still valid frames in buffer
586             mLocalFront = rear - (mFlush ? 0 : mFifo.mFrameCountP2 /*sic*/);
587         }
588         // on error, return an empty slice
589         err = filled;
590         filled = 0;
591     }
592     size_t availToRead = (size_t) filled;
593     if (availToRead > count) {
594         availToRead = count;
595     }
596     uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
597     size_t part1 = mFifo.mFrameCount - frontOffset;
598     if (part1 > availToRead) {
599         part1 = availToRead;
600     }
601     size_t part2 = part1 > 0 ? availToRead - part1 : 0;
602     // return slice
603     if (iovec != NULL) {
604         iovec[0].mOffset = frontOffset;
605         iovec[0].mLength = part1;
606         iovec[1].mOffset = 0;
607         iovec[1].mLength = part2;
608         mObtained = availToRead;
609     }
610     return availToRead > 0 ? availToRead : err;
611 }
612
613 ssize_t audio_utils_fifo_reader::available()
614 {
615     return available(NULL /*lost*/);
616 }
617
618 ssize_t audio_utils_fifo_reader::available(size_t *lost)
619 {
620     // iovec == NULL is not part of the public API, but internally it means don't set mObtained
621     return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
622 }
623
624 ssize_t audio_utils_fifo_reader::flush(size_t *lost)
625 {
626     audio_utils_iovec iovec[2];
627     ssize_t ret = obtain(iovec, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
628     if (ret > 0) {
629         size_t flushed = (size_t) ret;
630         release(flushed);
631         mTotalFlushed += flushed;
632         ret = flushed;
633     }
634     return ret;
635 }
636
637 void audio_utils_fifo_reader::setHysteresis(int32_t armLevel, uint32_t triggerLevel)
638 {
639     // cap to range [0, mFifo.mFrameCount]
640     if (armLevel < 0) {
641         armLevel = -1;
642     } else if ((uint32_t) armLevel > mFifo.mFrameCount) {
643         armLevel = mFifo.mFrameCount;
644     }
645     if (triggerLevel > mFifo.mFrameCount) {
646         triggerLevel = mFifo.mFrameCount;
647     }
648     // TODO this is overly conservative; it would be better to arm based on actual fill level
649     if (armLevel < mArmLevel) {
650         mIsArmed = true;
651     }
652     mArmLevel = armLevel;
653     mTriggerLevel = triggerLevel;
654 }
655
656 void audio_utils_fifo_reader::getHysteresis(int32_t *armLevel, uint32_t *triggerLevel) const
657 {
658     *armLevel = mArmLevel;
659     *triggerLevel = mTriggerLevel;
660 }