Module Name: src Committed By: isaki Date: Sat Apr 23 07:47:42 UTC 2022
Modified Files: src/tests/dev/audio: audiotest.c Log Message: tests: Add tests for AUDIO_GET[IO]OFFS ioctls. - AUDIO_GETIOFFS_one_{RDONLY,RDWR,WRONLY} - AUDIO_GETOOFFS_one_{RDONLY,RDWR,WRONLY} - AUDIO_GETOOFFS_wrap_{RDONLY,RDWR,WRONLY} - AUDIO_GETOOFFS_flush_{RDONLY,RDWR,WRONLY} - AUDIO_GETOOFFS_set_{RDONLY,RDWR,WRONLY} To generate a diff of this commit: cvs rdiff -u -r1.18 -r1.19 src/tests/dev/audio/audiotest.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/tests/dev/audio/audiotest.c diff -u src/tests/dev/audio/audiotest.c:1.18 src/tests/dev/audio/audiotest.c:1.19 --- src/tests/dev/audio/audiotest.c:1.18 Fri Dec 10 20:36:05 2021 +++ src/tests/dev/audio/audiotest.c Sat Apr 23 07:47:42 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: audiotest.c,v 1.18 2021/12/10 20:36:05 andvar Exp $ */ +/* $NetBSD: audiotest.c,v 1.19 2022/04/23 07:47:42 isaki Exp $ */ /* * Copyright (C) 2019 Tetsuya Isaki. All rights reserved. @@ -26,7 +26,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: audiotest.c,v 1.18 2021/12/10 20:36:05 andvar Exp $"); +__RCSID("$NetBSD: audiotest.c,v 1.19 2022/04/23 07:47:42 isaki Exp $"); #include <errno.h> #include <fcntl.h> @@ -1395,6 +1395,11 @@ int getenc_make_table(int, int[][5]); void xp_getenc(int[][5], int, int, int, struct audio_prinfo *); void getenc_check_encodings(int, int[][5]); void test_AUDIO_ERROR(int); +void test_AUDIO_GETIOFFS_one(int); +void test_AUDIO_GETOOFFS_one(int); +void test_AUDIO_GETOOFFS_wrap(int); +void test_AUDIO_GETOOFFS_flush(int); +void test_AUDIO_GETOOFFS_set(int); void test_audioctl_open_1(int, int); void test_audioctl_open_2(int, int); void try_audioctl_open_multiuser(const char *, const char *); @@ -6175,6 +6180,553 @@ DEF(AUDIO_ERROR_WRONLY) { test_AUDIO_ERR DEF(AUDIO_ERROR_RDWR) { test_AUDIO_ERROR(O_RDWR); } /* + * AUDIO_GETIOFFS at least one block. + */ +void +test_AUDIO_GETIOFFS_one(int openmode) +{ + struct audio_info ai; + audio_offset_t o; + int fd; + int r; + u_int blocksize; + u_int blk_ms; + + TEST("AUDIO_GETIOFFS_one_%s", openmode_str[openmode] + 2); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + +#if 0 + /* + * On NetBSD7/8, native encodings and emulated encodings behave + * differently. But it's hard to identify which encoding is native. + * If you try other encodings, edit these parameters manually. + */ + AUDIO_INITINFO(&ai); + ai.record.encoding = AUDIO_ENCODING_SLINEAR_NE; + ai.record.precision = 16; + ai.record.channels = 2; + ai.record.sample_rate = 48000; + /* ai.blocksize is shared by play and record, so set both the same. */ + *ai.play = *ai.record; + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); +#endif + + /* Get blocksize to calc blk_ms. */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + blocksize = ai.blocksize; + if (netbsd < 9) { + blk_ms = 0; + } else { + /* On NetBSD9, blocktime can always be calculated. */ + blk_ms = blocksize * 1000 / + (ai.play.precision / 8 * ai.play.channels * + ai.play.sample_rate); + } + if (blk_ms == 0) + blk_ms = 50; + DPRINTF(" > blocksize=%u, estimated blk_ms=%u\n", blocksize, blk_ms); + + /* + * Even when just opened, recording counters will start. + * Wait a moment, about one block time. + */ + usleep(blk_ms * 1000); + + r = IOCTL(fd, AUDIO_GETIOFFS, &o, ""); + XP_SYS_EQ(0, r); + if (mode2rec(openmode)) { + /* + * It's difficult to know exact values. + * But at least these should not be zero. + */ + DPRINTF(" > %d: samples=%u deltablks=%u offset=%u\n", + __LINE__, o.samples, o.deltablks, o.offset); + XP_NE(0, o.samples); + XP_NE(0, o.deltablks); + XP_NE(0, o.offset); + } else { + /* All are zero on playback track. */ + XP_EQ(0, o.samples); + XP_EQ(0, o.deltablks); + XP_EQ(0, o.offset); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +DEF(AUDIO_GETIOFFS_one_RDONLY) { test_AUDIO_GETIOFFS_one(O_RDONLY); } +DEF(AUDIO_GETIOFFS_one_WRONLY) { test_AUDIO_GETIOFFS_one(O_WRONLY); } +DEF(AUDIO_GETIOFFS_one_RDWR) { test_AUDIO_GETIOFFS_one(O_RDWR); } + +/* + * AUDIO_GETOOFFS for one block. + */ +void +test_AUDIO_GETOOFFS_one(int openmode) +{ + struct audio_info ai; + audio_offset_t o; + char *buf; + int fd; + int r; + u_int blocksize; + u_int initial_offset; + u_int blk_ms; + + TEST("AUDIO_GETOOFFS_one_%s", openmode_str[openmode] + 2); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + +#if 0 + /* + * On NetBSD7/8, native encodings and emulated encodings behave + * differently. But it's hard to identify which encoding is native. + * If you try other encodings, edit these parameters manually. + */ + AUDIO_INITINFO(&ai); + ai.play.encoding = AUDIO_ENCODING_SLINEAR_NE; + ai.play.precision = 16; + ai.play.channels = 2; + ai.play.sample_rate = 48000; + /* ai.blocksize is shared by play and record, so set both the same. */ + *ai.record = *ai.play; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "slinear16/2ch/48000"); + REQUIRED_SYS_EQ(0, r); +#endif + + /* Get blocksize to calc blk_ms. */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + blocksize = ai.blocksize; + if (netbsd < 9) { + blk_ms = 0; + } else { + /* On NetBSD9, blocktime can always be calculated. */ + blk_ms = blocksize * 1000 / + (ai.play.precision / 8 * ai.play.channels * + ai.play.sample_rate); + } + if (blk_ms == 0) + blk_ms = 50; + DPRINTF(" > blocksize=%u, estimated blk_ms=%u\n", blocksize, blk_ms); + + buf = (char *)malloc(blocksize); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, blocksize); + + /* + * On NetBSD7, .offset starts from one block. What is the block?? + * On NetBSD9, .offset starts from zero. + */ + if (netbsd < 9) { + initial_offset = blocksize; + } else { + initial_offset = 0; + } + + /* When just opened, all are zero. */ + r = IOCTL(fd, AUDIO_GETOOFFS, &o, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, o.samples); + XP_EQ(0, o.deltablks); + XP_EQ(initial_offset, o.offset); + + /* Even if wait (at least) one block, these remain unchanged. */ + usleep(blk_ms * 1000); + r = IOCTL(fd, AUDIO_GETOOFFS, &o, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, o.samples); + XP_EQ(0, o.deltablks); + XP_EQ(initial_offset, o.offset); + + /* Write one block. */ + r = WRITE(fd, buf, blocksize); + if (mode2play(openmode)) { + XP_SYS_EQ(blocksize, r); + } else { + XP_SYS_NG(EBADF, r); + } + r = IOCTL(fd, AUDIO_DRAIN, NULL, ""); + REQUIRED_SYS_EQ(0, r); + + r = IOCTL(fd, AUDIO_GETOOFFS, &o, ""); + XP_SYS_EQ(0, r); + if (mode2play(openmode)) { + /* All advance one block. */ + XP_EQ(blocksize, o.samples); + XP_EQ(1, o.deltablks); + XP_EQ(initial_offset + blocksize, o.offset); + } else { + /* + * All are zero on non-play track. + * On NetBSD7, the rec track has play buffer, too. + */ + XP_EQ(0, o.samples); + XP_EQ(0, o.deltablks); + XP_EQ(initial_offset, o.offset); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + free(buf); +} +DEF(AUDIO_GETOOFFS_one_RDONLY) { test_AUDIO_GETOOFFS_one(O_RDONLY); } +DEF(AUDIO_GETOOFFS_one_WRONLY) { test_AUDIO_GETOOFFS_one(O_WRONLY); } +DEF(AUDIO_GETOOFFS_one_RDWR) { test_AUDIO_GETOOFFS_one(O_RDWR); } + +/* + * AUDIO_GETOOFFS when wrap around buffer. + */ +void +test_AUDIO_GETOOFFS_wrap(int openmode) +{ + struct audio_info ai; + audio_offset_t o; + char *buf; + int fd; + int r; + u_int blocksize; + u_int buffer_size; + u_int initial_offset; + u_int nblks; + + TEST("AUDIO_GETOOFFS_wrap_%s", openmode_str[openmode] + 2); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + +#if 1 + /* To save test time, use larger format if possible. */ + AUDIO_INITINFO(&ai); + ai.play.encoding = AUDIO_ENCODING_SLINEAR_NE; + ai.play.precision = 16; + ai.play.channels = 2; + ai.play.sample_rate = 48000; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "slinear16/2/48000"); + if (r != 0) +#endif + { + /* + * If it cannot be set, use common format instead. + * May be happened on NetBSD7/8. + */ + AUDIO_INITINFO(&ai); + ai.play.encoding = AUDIO_ENCODING_ULAW; + ai.play.precision = 8; + ai.play.channels = 1; + ai.play.sample_rate = 8000; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "ulaw/1/8000"); + } + REQUIRED_SYS_EQ(0, r); + + /* Get buffer_size and blocksize. */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + buffer_size = ai.play.buffer_size; + blocksize = ai.blocksize; + nblks = buffer_size / blocksize; + DPRINTF(" > buffer_size=%u blocksize=%u nblks=%u\n", + buffer_size, blocksize, nblks); + + buf = (char *)malloc(buffer_size); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, buffer_size); + + /* + * On NetBSD7, .offset starts from one block. What is the block?? + * On NetBSD9, .offset starts from zero. + */ + if (netbsd < 9) { + initial_offset = blocksize; + } else { + initial_offset = 0; + } + + /* Write full buffer. */ + r = WRITE(fd, buf, buffer_size); + if (mode2play(openmode)) { + XP_SYS_EQ(buffer_size, r); + + /* Then, wait. */ + r = IOCTL(fd, AUDIO_DRAIN, NULL, ""); + REQUIRED_SYS_EQ(0, r); + } else { + XP_SYS_NG(EBADF, r); + } + + /* + * .deltablks is number of blocks since last checked. + * .offset is wrapped around to zero. + */ + r = IOCTL(fd, AUDIO_GETOOFFS, &o, ""); + XP_SYS_EQ(0, r); + if (mode2play(openmode)) { + /* + * On NetBSD7, samples may be blocksize * nblks or buffer_size + * depending on native/emulated encoding. + * On NetBSD9, samples is always equal to buffer_size. + */ + if (buffer_size != blocksize * nblks && + o.samples == blocksize * nblks) { + DPRINTF(" > %d: samples(%u) == blocksize * nblks\n", + __LINE__, o.samples); + } else { + XP_EQ(buffer_size, o.samples); + } + XP_EQ(nblks, o.deltablks); + XP_EQ(initial_offset, o.offset); + } else { + /* + * On non-play track, it silently succeeds with zero. + * But on NetBSD7, RDONLY descriptor also has play buffer. + */ + XP_EQ(0, o.samples); + XP_EQ(0, o.deltablks); + XP_EQ(initial_offset, o.offset); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + free(buf); +} +DEF(AUDIO_GETOOFFS_wrap_RDONLY) { test_AUDIO_GETOOFFS_wrap(O_RDONLY); } +DEF(AUDIO_GETOOFFS_wrap_WRONLY) { test_AUDIO_GETOOFFS_wrap(O_WRONLY); } +DEF(AUDIO_GETOOFFS_wrap_RDWR) { test_AUDIO_GETOOFFS_wrap(O_RDWR); } + +/* + * Check whether AUDIO_FLUSH clears AUDIO_GETOOFFS. + */ +void +test_AUDIO_GETOOFFS_flush(int openmode) +{ + struct audio_info ai; + audio_offset_t o; + char *buf; + int fd; + int r; + u_int initial_offset; + u_int last_offset; + + TEST("AUDIO_GETOOFFS_flush_%s", openmode_str[openmode] + 2); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + +#if 0 + /* On NetBSD7/8, native encoding changes buffer behavior. */ + AUDIO_INITINFO(&ai); + ai.play.encoding = AUDIO_ENCODING_SLINEAR_NE; + ai.play.precision = 16; + ai.play.channels = 2; + ai.play.sample_rate = 48000; + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); +#endif + + /* Get blocksize. */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + + buf = (char *)malloc(ai.blocksize); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, ai.blocksize); + + /* + * On NetBSD7, .offset starts from one block. What is the block?? + * On NetBSD9, .offset starts from zero. + */ + if (netbsd < 9) { + initial_offset = ai.blocksize; + } else { + initial_offset = 0; + } + + /* Write one block. */ + r = WRITE(fd, buf, ai.blocksize); + if (mode2play(openmode)) { + XP_SYS_EQ(ai.blocksize, r); + } else { + XP_SYS_NG(EBADF, r); + } + r = IOCTL(fd, AUDIO_DRAIN, NULL, ""); + XP_SYS_EQ(0, r); + + /* Obtain once. */ + r = IOCTL(fd, AUDIO_GETOOFFS, &o, ""); + XP_SYS_EQ(0, r); + if (mode2play(openmode)) { + XP_EQ(ai.blocksize, o.samples); + XP_EQ(1, o.deltablks); + XP_EQ(initial_offset + ai.blocksize, o.offset); + } else { + /* + * On non-play track, it silently succeeds with zero. + * But on NetBSD7, RDONLY descriptor also has play buffer. + */ + XP_EQ(0, o.samples); + XP_EQ(0, o.deltablks); + XP_EQ(initial_offset, o.offset); + } + + /* Write one more block to advance .offset. */ + r = WRITE(fd, buf, ai.blocksize); + if (mode2play(openmode)) { + XP_SYS_EQ(ai.blocksize, r); + } else { + XP_SYS_NG(EBADF, r); + } + r = IOCTL(fd, AUDIO_DRAIN, NULL, ""); + XP_SYS_EQ(0, r); + + /* If offset remains unchanged, this is expected offset. */ + last_offset = initial_offset + ai.blocksize * 2; + + /* Then, flush. */ + r = IOCTL(fd, AUDIO_FLUSH, NULL, ""); + REQUIRED_SYS_EQ(0, r); + + /* All should be cleared. */ + r = IOCTL(fd, AUDIO_GETOOFFS, &o, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, o.samples); + XP_EQ(0, o.deltablks); + if (mode2play(openmode)) { + /* + * On NetBSD7, + * offset is cleared if native encodings(?), but remains + * unchanged if emulated encodings(?). Looks a bug. + * On NetBSD9, it should always be cleared. + */ + if (netbsd < 9 && o.offset == last_offset) { + DPRINTF(" > %d: offset(%u) == last_offset\n", + __LINE__, o.offset); + } else { + XP_EQ(initial_offset, o.offset); + } + } else { + XP_EQ(initial_offset, o.offset); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + free(buf); +} +DEF(AUDIO_GETOOFFS_flush_RDONLY) { test_AUDIO_GETOOFFS_flush(O_RDONLY); } +DEF(AUDIO_GETOOFFS_flush_WRONLY) { test_AUDIO_GETOOFFS_flush(O_WRONLY); } +DEF(AUDIO_GETOOFFS_flush_RDWR) { test_AUDIO_GETOOFFS_flush(O_RDWR); } + +/* + * Check whether AUDIO_SETINFO(encoding) clears AUDIO_GETOOFFS. + */ +void +test_AUDIO_GETOOFFS_set(int openmode) +{ + struct audio_info ai; + audio_offset_t o; + char *buf; + int fd; + int r; + u_int initial_offset; + + TEST("AUDIO_GETOOFFS_set_%s", openmode_str[openmode] + 2); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + + /* Get blocksize. */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + + buf = (char *)malloc(ai.blocksize); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, ai.blocksize); + + /* + * On NetBSD7, .offset starts from one block. What is the block?? + * On NetBSD9, .offset starts from zero. + */ + if (netbsd < 9) { + initial_offset = ai.blocksize; + } else { + initial_offset = 0; + } + + /* Write one block. */ + r = WRITE(fd, buf, ai.blocksize); + if (mode2play(openmode)) { + XP_SYS_EQ(ai.blocksize, r); + } else { + XP_SYS_NG(EBADF, r); + } + r = IOCTL(fd, AUDIO_DRAIN, NULL, ""); + XP_SYS_EQ(0, r); + + /* + * Then, change encoding. + * If we fail to change it, we cannot continue. This may happen + * on NetBSD7/8. + */ + AUDIO_INITINFO(&ai); + ai.play.encoding = AUDIO_ENCODING_SLINEAR_NE; + ai.play.precision = 16; + ai.play.channels = 2; + ai.play.sample_rate = 48000; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "slinear16/2ch/48000"); + REQUIRED_SYS_EQ(0, r); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + if (netbsd < 9) { + initial_offset = ai.blocksize; + } else { + initial_offset = 0; + } + + /* Clear counters? */ + r = IOCTL(fd, AUDIO_GETOOFFS, &o, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, o.samples); + XP_EQ(0, o.deltablks); + XP_EQ(initial_offset, o.offset); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + free(buf); +} +DEF(AUDIO_GETOOFFS_set_RDONLY) { test_AUDIO_GETOOFFS_set(O_RDONLY); } +DEF(AUDIO_GETOOFFS_set_WRONLY) { test_AUDIO_GETOOFFS_set(O_WRONLY); } +DEF(AUDIO_GETOOFFS_set_RDWR) { test_AUDIO_GETOOFFS_set(O_RDWR); } + +/* * /dev/audioctl can always be opened while /dev/audio is open. */ void @@ -6662,6 +7214,21 @@ struct testentry testtable[] = { ENT(AUDIO_ERROR_RDONLY), ENT(AUDIO_ERROR_WRONLY), ENT(AUDIO_ERROR_RDWR), + ENT(AUDIO_GETIOFFS_one_RDONLY), + ENT(AUDIO_GETIOFFS_one_WRONLY), + ENT(AUDIO_GETIOFFS_one_RDWR), + ENT(AUDIO_GETOOFFS_one_RDONLY), + ENT(AUDIO_GETOOFFS_one_WRONLY), + ENT(AUDIO_GETOOFFS_one_RDWR), + ENT(AUDIO_GETOOFFS_wrap_RDONLY), + ENT(AUDIO_GETOOFFS_wrap_WRONLY), + ENT(AUDIO_GETOOFFS_wrap_RDWR), + ENT(AUDIO_GETOOFFS_flush_RDONLY), + ENT(AUDIO_GETOOFFS_flush_WRONLY), + ENT(AUDIO_GETOOFFS_flush_RDWR), + ENT(AUDIO_GETOOFFS_set_RDONLY), + ENT(AUDIO_GETOOFFS_set_WRONLY), + ENT(AUDIO_GETOOFFS_set_RDWR), ENT(audioctl_open_1_RDONLY_RDONLY), ENT(audioctl_open_1_RDONLY_RWONLY), ENT(audioctl_open_1_RDONLY_RDWR),