1 /* compress_plugin.c
2 **
3 ** Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
4 **
5 ** Redistribution and use in source and binary forms, with or without
6 ** modification, are permitted provided that the following conditions are
7 ** met:
8 ** * Redistributions of source code must retain the above copyright
9 ** notice, this list of conditions and the following disclaimer.
10 ** * Redistributions in binary form must reproduce the above
11 ** copyright notice, this list of conditions and the following
12 ** disclaimer in the documentation and/or other materials provided
13 ** with the distribution.
14 ** * Neither the name of The Linux Foundation nor the names of its
15 ** contributors may be used to endorse or promote products derived
16 ** from this software without specific prior written permission.
17 **
18 ** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
19 ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
21 ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22 ** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 ** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 ** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
28 ** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 **/
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <fcntl.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <poll.h>
40 #include <dlfcn.h>
41
42 #include <sys/ioctl.h>
43 #include <linux/ioctl.h>
44 #include <sound/asound.h>
45 #include "tinycompress/compress_plugin.h"
46 #include "sound/compress_offload.h"
47 #include "compress_ops.h"
48 #include "snd_utils.h"
49
50 #define U32_MAX ((uint32_t)~0U)
51
52 enum {
53 COMPRESS_PLUG_STATE_OPEN,
54 COMPRESS_PLUG_STATE_SETUP,
55 COMPRESS_PLUG_STATE_PREPARED,
56 COMPRESS_PLUG_STATE_PAUSE,
57 COMPRESS_PLUG_STATE_RUNNING,
58 };
59
60 struct compress_plug_data {
61 unsigned int card;
62 unsigned int device;
63 unsigned int fd;
64 unsigned int flags;
65
66 void *dl_hdl;
67 COMPRESS_PLUGIN_OPEN_FN_PTR();
68
69 struct compress_plugin *plugin;
70 void *dev_node;
71 };
72
compress_plug_get_caps(struct compress_plug_data * plug_data,struct snd_compr_caps * caps)73 static int compress_plug_get_caps(struct compress_plug_data *plug_data,
74 struct snd_compr_caps *caps)
75 {
76 struct compress_plugin *plugin = plug_data->plugin;
77
78 return plugin->ops->get_caps(plugin, caps);
79 }
80
compress_plug_set_params(struct compress_plug_data * plug_data,struct snd_compr_params * params)81 static int compress_plug_set_params(struct compress_plug_data *plug_data,
82 struct snd_compr_params *params)
83 {
84 struct compress_plugin *plugin = plug_data->plugin;
85 int rc;
86
87 if (plugin->state != COMPRESS_PLUG_STATE_OPEN)
88 return -EBADFD;
89
90 if (params->buffer.fragment_size == 0 ||
91 params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||
92 params->buffer.fragments == 0)
93 return -EINVAL;
94
95 rc = plugin->ops->set_params(plugin, params);
96 if (!rc)
97 plugin->state = COMPRESS_PLUG_STATE_SETUP;
98
99 return rc;
100 }
101
compress_plug_avail(struct compress_plug_data * plug_data,struct snd_compr_avail * avail)102 static int compress_plug_avail(struct compress_plug_data *plug_data,
103 struct snd_compr_avail *avail)
104 {
105 struct compress_plugin *plugin = plug_data->plugin;
106
107 return plugin->ops->avail(plugin, avail);
108 }
109
compress_plug_tstamp(struct compress_plug_data * plug_data,struct snd_compr_tstamp * tstamp)110 static int compress_plug_tstamp(struct compress_plug_data *plug_data,
111 struct snd_compr_tstamp *tstamp)
112 {
113 struct compress_plugin *plugin = plug_data->plugin;
114
115 if (plugin->state != COMPRESS_PLUG_STATE_SETUP)
116 return -EBADFD;
117
118 return plugin->ops->tstamp(plugin, tstamp);
119 }
120
compress_plug_start(struct compress_plug_data * plug_data)121 static int compress_plug_start(struct compress_plug_data *plug_data)
122 {
123 struct compress_plugin *plugin = plug_data->plugin;
124 int rc;
125
126 /* for playback moved to prepare in first write */
127 /* for capture: move to prepare state set params */
128 /* TODO: add direction in set params */
129 if (plugin->state != COMPRESS_PLUG_STATE_PREPARED)
130 return -EBADFD;
131
132 rc = plugin->ops->start(plugin);
133 if (!rc)
134 plugin->state = COMPRESS_PLUG_STATE_RUNNING;
135
136 return rc;
137 }
138
compress_plug_stop(struct compress_plug_data * plug_data)139 static int compress_plug_stop(struct compress_plug_data *plug_data)
140 {
141 struct compress_plugin *plugin = plug_data->plugin;
142 int rc;
143
144 if (plugin->state == COMPRESS_PLUG_STATE_PREPARED ||
145 plugin->state == COMPRESS_PLUG_STATE_SETUP)
146 return -EBADFD;
147
148 rc = plugin->ops->stop(plugin);
149 if (!rc)
150 plugin->state = COMPRESS_PLUG_STATE_SETUP;
151
152 return rc;
153 }
154
compress_plug_pause(struct compress_plug_data * plug_data)155 static int compress_plug_pause(struct compress_plug_data *plug_data)
156 {
157 struct compress_plugin *plugin = plug_data->plugin;
158 int rc;
159
160 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
161 return -EBADFD;
162
163 rc = plugin->ops->pause(plugin);
164 if (!rc)
165 plugin->state = COMPRESS_PLUG_STATE_PAUSE;
166
167 return rc;
168 }
169
compress_plug_resume(struct compress_plug_data * plug_data)170 static int compress_plug_resume(struct compress_plug_data *plug_data)
171 {
172 struct compress_plugin *plugin = plug_data->plugin;
173 int rc;
174
175 if (plugin->state != COMPRESS_PLUG_STATE_PAUSE)
176 return -EBADFD;
177
178 rc = plugin->ops->resume(plugin);
179 if (!rc)
180 plugin->state = COMPRESS_PLUG_STATE_RUNNING;
181
182 return rc;
183 }
184
compress_plug_drain(struct compress_plug_data * plug_data)185 static int compress_plug_drain(struct compress_plug_data *plug_data)
186 {
187 struct compress_plugin *plugin = plug_data->plugin;
188
189 /* check if we will allow in pause */
190 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
191 return -EBADFD;
192
193 return plugin->ops->drain(plugin);
194 }
195
compress_plug_partial_drain(struct compress_plug_data * plug_data)196 static int compress_plug_partial_drain(struct compress_plug_data *plug_data)
197 {
198 struct compress_plugin *plugin = plug_data->plugin;
199
200 /* check if we will allow in pause */
201 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
202 return -EBADFD;
203
204 return plugin->ops->partial_drain(plugin);
205 }
206
compress_plug_next_track(struct compress_plug_data * plug_data)207 static int compress_plug_next_track(struct compress_plug_data *plug_data)
208 {
209 struct compress_plugin *plugin = plug_data->plugin;
210
211 /* transion to next track applied to running stream only */
212 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
213 return -EBADFD;
214
215 return plugin->ops->next_track(plugin);
216 }
217
compress_plug_ioctl(void * data,unsigned int cmd,...)218 static int compress_plug_ioctl(void *data, unsigned int cmd, ...)
219 {
220 struct compress_plug_data *plug_data = data;
221 struct compress_plugin *plugin = plug_data->plugin;
222 int ret = 0;
223 va_list ap;
224 void *arg;
225
226 va_start(ap, cmd);
227 arg = va_arg(ap, void *);
228 va_end(ap);
229
230 switch (cmd) {
231 case SNDRV_COMPRESS_IOCTL_VERSION:
232 *((int*)arg) = SNDRV_COMPRESS_VERSION;
233 break;
234 case SNDRV_COMPRESS_GET_CAPS:
235 ret = compress_plug_get_caps(plug_data, arg);
236 break;
237 case SNDRV_COMPRESS_SET_PARAMS:
238 ret = compress_plug_set_params(plug_data, arg);
239 break;
240 case SNDRV_COMPRESS_AVAIL:
241 ret = compress_plug_avail(plug_data, arg);
242 break;
243 case SNDRV_COMPRESS_TSTAMP:
244 ret = compress_plug_tstamp(plug_data, arg);
245 break;
246 case SNDRV_COMPRESS_START:
247 ret = compress_plug_start(plug_data);
248 break;
249 case SNDRV_COMPRESS_STOP:
250 ret = compress_plug_stop(plug_data);
251 break;
252 case SNDRV_COMPRESS_PAUSE:
253 ret = compress_plug_pause(plug_data);
254 break;
255 case SNDRV_COMPRESS_RESUME:
256 ret = compress_plug_resume(plug_data);
257 break;
258 case SNDRV_COMPRESS_DRAIN:
259 ret = compress_plug_drain(plug_data);
260 break;
261 case SNDRV_COMPRESS_PARTIAL_DRAIN:
262 ret = compress_plug_partial_drain(plug_data);
263 break;
264 case SNDRV_COMPRESS_NEXT_TRACK:
265 ret = compress_plug_next_track(plug_data);
266 break;
267 default:
268 if (plugin->ops->ioctl)
269 ret = plugin->ops->ioctl(plugin, cmd, arg);
270 else
271 ret = -EINVAL;
272 break;
273 }
274
275 return ret;
276 }
277
compress_plug_poll(void * data,struct pollfd * fds,nfds_t nfds,int timeout)278 static int compress_plug_poll(void *data, struct pollfd *fds,
279 nfds_t nfds, int timeout)
280 {
281 struct compress_plug_data *plug_data = data;
282 struct compress_plugin *plugin = plug_data->plugin;
283
284 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
285 return -EBADFD;
286
287 return plugin->ops->poll(plugin, fds, nfds, timeout);
288 }
289
290
compress_plug_read(void * data,void * buf,size_t size)291 static int compress_plug_read(void *data, void *buf, size_t size)
292 {
293 struct compress_plug_data *plug_data = data;
294 struct compress_plugin *plugin = plug_data->plugin;
295
296 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING &&
297 plugin->state != COMPRESS_PLUG_STATE_SETUP)
298 return -EBADFD;
299
300 return plugin->ops->read(plugin, buf, size);
301 }
302
compress_plug_write(void * data,const void * buf,size_t size)303 static int compress_plug_write(void *data, const void *buf, size_t size)
304 {
305 struct compress_plug_data *plug_data = data;
306 struct compress_plugin *plugin = plug_data->plugin;
307 int rc;
308
309 if (plugin->state != COMPRESS_PLUG_STATE_SETUP &&
310 plugin->state != COMPRESS_PLUG_STATE_PREPARED &&
311 plugin->state != COMPRESS_PLUG_STATE_RUNNING)
312 return -EBADFD;
313
314 rc = plugin->ops->write(plugin, buf, size);
315 if ((rc > 0) && (plugin->state == COMPRESS_PLUG_STATE_SETUP))
316 plugin->state = COMPRESS_PLUG_STATE_PREPARED;
317
318 return rc;
319 }
320
compress_plug_close(void * data)321 static void compress_plug_close(void *data)
322 {
323 struct compress_plug_data *plug_data = data;
324 struct compress_plugin *plugin = plug_data->plugin;
325
326 plugin->ops->close(plugin);
327 dlclose(plug_data->dl_hdl);
328
329 free(plug_data);
330 }
331
compress_plug_open(unsigned int card,unsigned int device,unsigned int flags,void ** data,void * node)332 static int compress_plug_open(unsigned int card, unsigned int device,
333 unsigned int flags, void **data, void *node)
334 {
335 struct compress_plug_data *plug_data;
336 void *dl_hdl;
337 int rc = 0;
338 char *so_name, *open_fn, token[80], *name, *token_saveptr;
339
340 plug_data = calloc(1, sizeof(*plug_data));
341 if (!plug_data) {
342 return -ENOMEM;
343 }
344
345 rc = snd_utils_get_str(node, "so-name", &so_name);
346 if (rc) {
347 fprintf(stderr, "%s: failed to get plugin lib name\n",
348 __func__);
349 goto err_get_lib;
350 }
351
352 dl_hdl = dlopen(so_name, RTLD_NOW);
353 if (!dl_hdl) {
354 fprintf(stderr, "%s: unable to open %s, error: %s\n",
355 __func__, so_name, dlerror());
356 goto err_dl_open;
357 } else {
358 fprintf(stderr, "%s: dlopen successful for %s\n",
359 __func__, so_name);
360 }
361
362 sscanf(so_name, "lib%s", token);
363 token_saveptr = token;
364 name = strtok_r(token, ".", &token_saveptr);
365 if (!name) {
366 fprintf(stderr, "%s: invalid library name\n", __func__);
367 goto err_open_fn;
368 }
369 const size_t open_fn_size = strlen(name) + strlen("_open") + 1;
370 open_fn = calloc(1, open_fn_size);
371 if (!open_fn) {
372 rc = -ENOMEM;
373 goto err_open_fn;
374 }
375
376 strlcpy(open_fn, name, open_fn_size);
377 strlcat(open_fn, "_open", open_fn_size);
378
379 plug_data->plugin_open_fn = dlsym(dl_hdl, open_fn);
380 if (!plug_data->plugin_open_fn) {
381 fprintf(stderr, "%s: dlsym to open fn failed, err = '%s'\n",
382 __func__, dlerror());
383 goto err_dlsym;
384 }
385
386 rc = plug_data->plugin_open_fn(&plug_data->plugin,
387 card, device, flags);
388 if (rc) {
389 fprintf(stderr, "%s: failed to open plugin\n", __func__);
390 goto err_dlsym;
391 }
392
393 /* Call snd-card-def to get card and compress nodes */
394 /* Check how to manage fd for plugin */
395
396 plug_data->dl_hdl = dl_hdl;
397 plug_data->card = card;
398 plug_data->device = device;
399 plug_data->dev_node = node;
400 plug_data->flags = flags;
401
402 *data = plug_data;
403
404 plug_data->plugin->state = COMPRESS_PLUG_STATE_OPEN;
405
406 return 0;
407
408 err_dlsym:
409 free(open_fn);
410 err_open_fn:
411 dlclose(dl_hdl);
412 err_get_lib:
413 err_dl_open:
414 free(plug_data);
415
416 return rc;
417 }
418
419 struct compress_ops compr_plug_ops = {
420 .open = compress_plug_open,
421 .close = compress_plug_close,
422 .ioctl = compress_plug_ioctl,
423 .read = compress_plug_read,
424 .write = compress_plug_write,
425 .poll = compress_plug_poll,
426 };
427