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_RUNNING)
88 return plugin->ops->set_params(plugin, params);
89 else if (plugin->state != COMPRESS_PLUG_STATE_OPEN)
90 return -EBADFD;
91
92 if (params->buffer.fragment_size == 0 ||
93 params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||
94 params->buffer.fragments == 0)
95 return -EINVAL;
96
97 rc = plugin->ops->set_params(plugin, params);
98 if (!rc)
99 plugin->state = COMPRESS_PLUG_STATE_SETUP;
100
101 return rc;
102 }
103
compress_plug_avail(struct compress_plug_data * plug_data,struct snd_compr_avail * avail)104 static int compress_plug_avail(struct compress_plug_data *plug_data,
105 struct snd_compr_avail *avail)
106 {
107 struct compress_plugin *plugin = plug_data->plugin;
108
109 return plugin->ops->avail(plugin, avail);
110 }
111
compress_plug_tstamp(struct compress_plug_data * plug_data,struct snd_compr_tstamp * tstamp)112 static int compress_plug_tstamp(struct compress_plug_data *plug_data,
113 struct snd_compr_tstamp *tstamp)
114 {
115 struct compress_plugin *plugin = plug_data->plugin;
116
117 if (plugin->state != COMPRESS_PLUG_STATE_SETUP)
118 return -EBADFD;
119
120 return plugin->ops->tstamp(plugin, tstamp);
121 }
122
compress_plug_start(struct compress_plug_data * plug_data)123 static int compress_plug_start(struct compress_plug_data *plug_data)
124 {
125 struct compress_plugin *plugin = plug_data->plugin;
126 int rc;
127
128 /* for playback moved to prepare in first write */
129 /* for capture: move to prepare state set params */
130 /* TODO: add direction in set params */
131 if (plugin->state != COMPRESS_PLUG_STATE_PREPARED)
132 return -EBADFD;
133
134 rc = plugin->ops->start(plugin);
135 if (!rc)
136 plugin->state = COMPRESS_PLUG_STATE_RUNNING;
137
138 return rc;
139 }
140
compress_plug_stop(struct compress_plug_data * plug_data)141 static int compress_plug_stop(struct compress_plug_data *plug_data)
142 {
143 struct compress_plugin *plugin = plug_data->plugin;
144 int rc;
145
146 if (plugin->state == COMPRESS_PLUG_STATE_PREPARED ||
147 plugin->state == COMPRESS_PLUG_STATE_SETUP)
148 return -EBADFD;
149
150 rc = plugin->ops->stop(plugin);
151 if (!rc)
152 plugin->state = COMPRESS_PLUG_STATE_SETUP;
153
154 return rc;
155 }
156
compress_plug_pause(struct compress_plug_data * plug_data)157 static int compress_plug_pause(struct compress_plug_data *plug_data)
158 {
159 struct compress_plugin *plugin = plug_data->plugin;
160 int rc;
161
162 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
163 return -EBADFD;
164
165 rc = plugin->ops->pause(plugin);
166 if (!rc)
167 plugin->state = COMPRESS_PLUG_STATE_PAUSE;
168
169 return rc;
170 }
171
compress_plug_resume(struct compress_plug_data * plug_data)172 static int compress_plug_resume(struct compress_plug_data *plug_data)
173 {
174 struct compress_plugin *plugin = plug_data->plugin;
175 int rc;
176
177 if (plugin->state != COMPRESS_PLUG_STATE_PAUSE)
178 return -EBADFD;
179
180 rc = plugin->ops->resume(plugin);
181 if (!rc)
182 plugin->state = COMPRESS_PLUG_STATE_RUNNING;
183
184 return rc;
185 }
186
compress_plug_drain(struct compress_plug_data * plug_data)187 static int compress_plug_drain(struct compress_plug_data *plug_data)
188 {
189 struct compress_plugin *plugin = plug_data->plugin;
190
191 /* check if we will allow in pause */
192 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
193 return -EBADFD;
194
195 return plugin->ops->drain(plugin);
196 }
197
compress_plug_partial_drain(struct compress_plug_data * plug_data)198 static int compress_plug_partial_drain(struct compress_plug_data *plug_data)
199 {
200 struct compress_plugin *plugin = plug_data->plugin;
201
202 /* check if we will allow in pause */
203 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
204 return -EBADFD;
205
206 return plugin->ops->partial_drain(plugin);
207 }
208
compress_plug_next_track(struct compress_plug_data * plug_data)209 static int compress_plug_next_track(struct compress_plug_data *plug_data)
210 {
211 struct compress_plugin *plugin = plug_data->plugin;
212
213 /* transion to next track applied to running stream only */
214 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
215 return -EBADFD;
216
217 return plugin->ops->next_track(plugin);
218 }
219
compress_plug_ioctl(void * data,unsigned int cmd,...)220 static int compress_plug_ioctl(void *data, unsigned int cmd, ...)
221 {
222 struct compress_plug_data *plug_data = data;
223 struct compress_plugin *plugin = plug_data->plugin;
224 int ret = 0;
225 va_list ap;
226 void *arg;
227
228 va_start(ap, cmd);
229 arg = va_arg(ap, void *);
230 va_end(ap);
231
232 switch (cmd) {
233 case SNDRV_COMPRESS_IOCTL_VERSION:
234 *((int*)arg) = SNDRV_COMPRESS_VERSION;
235 break;
236 case SNDRV_COMPRESS_GET_CAPS:
237 ret = compress_plug_get_caps(plug_data, arg);
238 break;
239 case SNDRV_COMPRESS_SET_PARAMS:
240 ret = compress_plug_set_params(plug_data, arg);
241 break;
242 case SNDRV_COMPRESS_AVAIL:
243 ret = compress_plug_avail(plug_data, arg);
244 break;
245 case SNDRV_COMPRESS_TSTAMP:
246 ret = compress_plug_tstamp(plug_data, arg);
247 break;
248 case SNDRV_COMPRESS_START:
249 ret = compress_plug_start(plug_data);
250 break;
251 case SNDRV_COMPRESS_STOP:
252 ret = compress_plug_stop(plug_data);
253 break;
254 case SNDRV_COMPRESS_PAUSE:
255 ret = compress_plug_pause(plug_data);
256 break;
257 case SNDRV_COMPRESS_RESUME:
258 ret = compress_plug_resume(plug_data);
259 break;
260 case SNDRV_COMPRESS_DRAIN:
261 ret = compress_plug_drain(plug_data);
262 break;
263 case SNDRV_COMPRESS_PARTIAL_DRAIN:
264 ret = compress_plug_partial_drain(plug_data);
265 break;
266 case SNDRV_COMPRESS_NEXT_TRACK:
267 ret = compress_plug_next_track(plug_data);
268 break;
269 default:
270 if (plugin->ops->ioctl)
271 ret = plugin->ops->ioctl(plugin, cmd, arg);
272 else
273 ret = -EINVAL;
274 break;
275 }
276
277 return ret;
278 }
279
compress_plug_poll(void * data,struct pollfd * fds,nfds_t nfds,int timeout)280 static int compress_plug_poll(void *data, struct pollfd *fds,
281 nfds_t nfds, int timeout)
282 {
283 struct compress_plug_data *plug_data = data;
284 struct compress_plugin *plugin = plug_data->plugin;
285
286 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING)
287 return -EBADFD;
288
289 return plugin->ops->poll(plugin, fds, nfds, timeout);
290 }
291
292
compress_plug_read(void * data,void * buf,size_t size)293 static int compress_plug_read(void *data, void *buf, size_t size)
294 {
295 struct compress_plug_data *plug_data = data;
296 struct compress_plugin *plugin = plug_data->plugin;
297
298 if (plugin->state != COMPRESS_PLUG_STATE_RUNNING &&
299 plugin->state != COMPRESS_PLUG_STATE_SETUP)
300 return -EBADFD;
301
302 return plugin->ops->read(plugin, buf, size);
303 }
304
compress_plug_write(void * data,const void * buf,size_t size)305 static int compress_plug_write(void *data, const void *buf, size_t size)
306 {
307 struct compress_plug_data *plug_data = data;
308 struct compress_plugin *plugin = plug_data->plugin;
309 int rc;
310
311 if (plugin->state != COMPRESS_PLUG_STATE_SETUP &&
312 plugin->state != COMPRESS_PLUG_STATE_PREPARED &&
313 plugin->state != COMPRESS_PLUG_STATE_RUNNING)
314 return -EBADFD;
315
316 rc = plugin->ops->write(plugin, buf, size);
317 if ((rc > 0) && (plugin->state == COMPRESS_PLUG_STATE_SETUP))
318 plugin->state = COMPRESS_PLUG_STATE_PREPARED;
319
320 return rc;
321 }
322
compress_plug_close(void * data)323 static void compress_plug_close(void *data)
324 {
325 struct compress_plug_data *plug_data = data;
326 struct compress_plugin *plugin = plug_data->plugin;
327
328 plugin->ops->close(plugin);
329 dlclose(plug_data->dl_hdl);
330
331 free(plug_data);
332 }
333
compress_plug_open(unsigned int card,unsigned int device,unsigned int flags,void ** data,void * node)334 static int compress_plug_open(unsigned int card, unsigned int device,
335 unsigned int flags, void **data, void *node)
336 {
337 struct compress_plug_data *plug_data;
338 void *dl_hdl;
339 int rc = 0;
340 char *so_name, *open_fn, token[80], *name, *token_saveptr;
341
342 plug_data = calloc(1, sizeof(*plug_data));
343 if (!plug_data) {
344 return -ENOMEM;
345 }
346
347 rc = snd_utils_get_str(node, "so-name", &so_name);
348 if (rc) {
349 fprintf(stderr, "%s: failed to get plugin lib name\n",
350 __func__);
351 goto err_get_lib;
352 }
353
354 dl_hdl = dlopen(so_name, RTLD_NOW);
355 if (!dl_hdl) {
356 fprintf(stderr, "%s: unable to open %s, error: %s\n",
357 __func__, so_name, dlerror());
358 goto err_dl_open;
359 } else {
360 fprintf(stderr, "%s: dlopen successful for %s\n",
361 __func__, so_name);
362 }
363
364 sscanf(so_name, "lib%s", token);
365 token_saveptr = token;
366 name = strtok_r(token, ".", &token_saveptr);
367 if (!name) {
368 fprintf(stderr, "%s: invalid library name\n", __func__);
369 goto err_open_fn;
370 }
371 const size_t open_fn_size = strlen(name) + strlen("_open") + 1;
372 open_fn = calloc(1, open_fn_size);
373 if (!open_fn) {
374 rc = -ENOMEM;
375 goto err_open_fn;
376 }
377
378 strlcpy(open_fn, name, open_fn_size);
379 strlcat(open_fn, "_open", open_fn_size);
380
381 plug_data->plugin_open_fn = dlsym(dl_hdl, open_fn);
382 if (!plug_data->plugin_open_fn) {
383 fprintf(stderr, "%s: dlsym to open fn failed, err = '%s'\n",
384 __func__, dlerror());
385 goto err_dlsym;
386 }
387
388 rc = plug_data->plugin_open_fn(&plug_data->plugin,
389 card, device, flags);
390 if (rc) {
391 fprintf(stderr, "%s: failed to open plugin\n", __func__);
392 goto err_dlsym;
393 }
394
395 /* Call snd-card-def to get card and compress nodes */
396 /* Check how to manage fd for plugin */
397
398 plug_data->dl_hdl = dl_hdl;
399 plug_data->card = card;
400 plug_data->device = device;
401 plug_data->dev_node = node;
402 plug_data->flags = flags;
403
404 *data = plug_data;
405
406 plug_data->plugin->state = COMPRESS_PLUG_STATE_OPEN;
407
408 return 0;
409
410 err_dlsym:
411 free(open_fn);
412 err_open_fn:
413 dlclose(dl_hdl);
414 err_get_lib:
415 err_dl_open:
416 free(plug_data);
417
418 return rc;
419 }
420
421 struct compress_ops compr_plug_ops = {
422 .open = compress_plug_open,
423 .close = compress_plug_close,
424 .ioctl = compress_plug_ioctl,
425 .read = compress_plug_read,
426 .write = compress_plug_write,
427 .poll = compress_plug_poll,
428 };
429