1 /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include <sys/ioctl.h>
7 #include <pthread.h>
8 #include <sys/param.h>
9 #include <syslog.h>
10
11 #include "audio_thread.h"
12 #include "byte_buffer.h"
13 #include "cras_audio_area.h"
14 #include "cras_config.h"
15 #include "cras_iodev.h"
16 #include "cras_iodev_list.h"
17 #include "cras_types.h"
18 #include "cras_util.h"
19 #include "test_iodev.h"
20 #include "utlist.h"
21
22 #define TEST_BUFFER_SIZE (16 * 1024)
23
24 static size_t test_supported_rates[] = {
25 16000, 0
26 };
27
28 static size_t test_supported_channel_counts[] = {
29 1, 0
30 };
31
32 static snd_pcm_format_t test_supported_formats[] = {
33 SND_PCM_FORMAT_S16_LE,
34 0
35 };
36
37 struct test_iodev {
38 struct cras_iodev base;
39 int fd;
40 struct byte_buffer *audbuff;
41 unsigned int fmt_bytes;
42 };
43
44 /*
45 * iodev callbacks.
46 */
47
frames_queued(const struct cras_iodev * iodev,struct timespec * tstamp)48 static int frames_queued(const struct cras_iodev *iodev,
49 struct timespec *tstamp)
50 {
51 struct test_iodev *testio = (struct test_iodev *)iodev;
52 int available;
53
54 if (testio->fd < 0)
55 return 0;
56 ioctl(testio->fd, FIONREAD, &available);
57 clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
58 return available / testio->fmt_bytes;
59 }
60
delay_frames(const struct cras_iodev * iodev)61 static int delay_frames(const struct cras_iodev *iodev)
62 {
63 return 0;
64 }
65
close_dev(struct cras_iodev * iodev)66 static int close_dev(struct cras_iodev *iodev)
67 {
68 struct test_iodev *testio = (struct test_iodev *)iodev;
69
70 byte_buffer_destroy(&testio->audbuff);
71 testio->audbuff = NULL;
72 cras_iodev_free_audio_area(iodev);
73 return 0;
74 }
75
configure_dev(struct cras_iodev * iodev)76 static int configure_dev(struct cras_iodev *iodev)
77 {
78 struct test_iodev *testio = (struct test_iodev *)iodev;
79
80 if (iodev->format == NULL)
81 return -EINVAL;
82
83 cras_iodev_init_audio_area(iodev, iodev->format->num_channels);
84 testio->fmt_bytes = cras_get_format_bytes(iodev->format);
85 testio->audbuff = byte_buffer_create(TEST_BUFFER_SIZE *
86 testio->fmt_bytes);
87
88 return 0;
89 }
90
get_buffer(struct cras_iodev * iodev,struct cras_audio_area ** area,unsigned * frames)91 static int get_buffer(struct cras_iodev *iodev,
92 struct cras_audio_area **area,
93 unsigned *frames)
94 {
95 struct test_iodev *testio = (struct test_iodev *)iodev;
96 unsigned int readable;
97 uint8_t *buff;
98
99 buff = buf_read_pointer_size(testio->audbuff, &readable);
100 *frames = MIN(*frames, readable);
101
102 iodev->area->frames = *frames;
103 cras_audio_area_config_buf_pointers(iodev->area, iodev->format, buff);
104 *area = iodev->area;
105 return 0;
106 }
107
put_buffer(struct cras_iodev * iodev,unsigned frames)108 static int put_buffer(struct cras_iodev *iodev, unsigned frames)
109 {
110 struct test_iodev *testio = (struct test_iodev *)iodev;
111
112 /* Input */
113 buf_increment_read(testio->audbuff, frames * testio->fmt_bytes);
114
115 return 0;
116 }
117
get_buffer_fd_read(struct cras_iodev * iodev,struct cras_audio_area ** area,unsigned * frames)118 static int get_buffer_fd_read(struct cras_iodev *iodev,
119 struct cras_audio_area **area,
120 unsigned *frames)
121 {
122 struct test_iodev *testio = (struct test_iodev *)iodev;
123 int nread;
124 uint8_t *write_ptr;
125 unsigned int avail;
126
127 if (testio->fd < 0) {
128 *frames = 0;
129 return 0;
130 }
131
132 write_ptr = buf_write_pointer_size(testio->audbuff, &avail);
133 avail = MIN(avail, *frames * testio->fmt_bytes);
134 nread = read(testio->fd, write_ptr, avail);
135 if (nread <= 0) {
136 *frames = 0;
137 audio_thread_rm_callback(testio->fd);
138 close(testio->fd);
139 testio->fd = -1;
140 return 0;
141 }
142 buf_increment_write(testio->audbuff, nread);
143 *frames = nread / testio->fmt_bytes;
144 iodev->area->frames = *frames;
145 cras_audio_area_config_buf_pointers(iodev->area, iodev->format,
146 write_ptr);
147 *area = iodev->area;
148 return nread;
149 }
150
update_active_node(struct cras_iodev * iodev,unsigned node_idx,unsigned dev_enabled)151 static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
152 unsigned dev_enabled)
153 {
154 }
155
play_file_as_hotword(struct test_iodev * testio,const char * path)156 static void play_file_as_hotword(struct test_iodev *testio, const char *path)
157 {
158 if (testio->fd >= 0) {
159 /* Remove audio thread callback from main thread. */
160 audio_thread_rm_callback_sync(
161 cras_iodev_list_get_audio_thread(),
162 testio->fd);
163 close(testio->fd);
164 }
165
166 testio->fd = open(path, O_RDONLY);
167 buf_reset(testio->audbuff);
168 }
169
170 /*
171 * Exported Interface.
172 */
173
test_iodev_create(enum CRAS_STREAM_DIRECTION direction,enum TEST_IODEV_TYPE type)174 struct cras_iodev *test_iodev_create(enum CRAS_STREAM_DIRECTION direction,
175 enum TEST_IODEV_TYPE type)
176 {
177 struct test_iodev *testio;
178 struct cras_iodev *iodev;
179 struct cras_ionode *node;
180
181 if (direction != CRAS_STREAM_INPUT || type != TEST_IODEV_HOTWORD)
182 return NULL;
183
184 testio = calloc(1, sizeof(*testio));
185 if (testio == NULL)
186 return NULL;
187 iodev = &testio->base;
188 iodev->direction = direction;
189 testio->fd = -1;
190
191 iodev->supported_rates = test_supported_rates;
192 iodev->supported_channel_counts = test_supported_channel_counts;
193 iodev->supported_formats = test_supported_formats;
194 iodev->buffer_size = TEST_BUFFER_SIZE;
195
196 iodev->configure_dev = configure_dev;
197 iodev->close_dev = close_dev;
198 iodev->frames_queued = frames_queued;
199 iodev->delay_frames = delay_frames;
200 if (type == TEST_IODEV_HOTWORD)
201 iodev->get_buffer = get_buffer_fd_read;
202 else
203 iodev->get_buffer = get_buffer;
204 iodev->put_buffer = put_buffer;
205 iodev->update_active_node = update_active_node;
206
207 /* Create a dummy ionode */
208 node = (struct cras_ionode *)calloc(1, sizeof(*node));
209 node->dev = iodev;
210 node->plugged = 1;
211 if (type == TEST_IODEV_HOTWORD)
212 node->type = CRAS_NODE_TYPE_HOTWORD;
213 else
214 node->type = CRAS_NODE_TYPE_UNKNOWN;
215 node->volume = 100;
216 node->software_volume_needed = 0;
217 node->max_software_gain = 0;
218 strcpy(node->name, "(default)");
219 cras_iodev_add_node(iodev, node);
220 cras_iodev_set_active_node(iodev, node);
221
222 /* Finally add it to the appropriate iodev list. */
223 snprintf(iodev->info.name, ARRAY_SIZE(iodev->info.name), "Tester");
224 iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = '\0';
225 cras_iodev_list_add_input(iodev);
226
227 return iodev;
228 }
229
test_iodev_destroy(struct cras_iodev * iodev)230 void test_iodev_destroy(struct cras_iodev *iodev)
231 {
232 struct test_iodev *testio = (struct test_iodev *)iodev;
233
234 cras_iodev_list_rm_input(iodev);
235 free(iodev->active_node);
236 cras_iodev_free_resources(iodev);
237 free(testio);
238 }
239
test_iodev_add_samples(struct test_iodev * testio,uint8_t * samples,unsigned int count)240 unsigned int test_iodev_add_samples(struct test_iodev *testio,
241 uint8_t *samples,
242 unsigned int count)
243 {
244 unsigned int avail;
245 uint8_t *write_ptr;
246
247 write_ptr = buf_write_pointer_size(testio->audbuff, &avail);
248 count = MIN(count, avail);
249 memcpy(write_ptr, samples, count * testio->fmt_bytes);
250 buf_increment_write(testio->audbuff, count * testio->fmt_bytes);
251 return count;
252 }
253
test_iodev_command(struct cras_iodev * iodev,enum CRAS_TEST_IODEV_CMD command,unsigned int data_len,const uint8_t * data)254 void test_iodev_command(struct cras_iodev *iodev,
255 enum CRAS_TEST_IODEV_CMD command,
256 unsigned int data_len,
257 const uint8_t *data)
258 {
259 struct test_iodev *testio = (struct test_iodev *)iodev;
260
261 if (!cras_iodev_is_open(iodev))
262 return;
263
264 switch (command) {
265 case TEST_IODEV_CMD_HOTWORD_TRIGGER:
266 play_file_as_hotword(testio, (char *)data);
267 break;
268 default:
269 break;
270 }
271 }
272