• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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