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