blob: 7a5538ddb112d5e90ad8ab66803e69e525b99c8f [file] [log] [blame]
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +01001/* compress_plugin.c
2**
Eric Laurent86822e42021-01-13 19:41:43 +01003** Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +01004**
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
52enum {
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
60struct 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
73static 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
81static 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
102static 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
110static 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
121static 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
139static 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
155static 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
170static 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
185static 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
196static 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
207static 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
218static 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
278static 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
291static 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
303static 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
321static 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
332static 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;
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100336 void *dl_hdl;
337 int rc = 0;
Eric Laurent86822e42021-01-13 19:41:43 +0100338 char *so_name, *open_fn, token[80], *name, *token_saveptr;
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100339
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);
Eric Laurent86822e42021-01-13 19:41:43 +0100363 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 }
Eric Laurent53336572021-01-13 20:58:24 +0100369 const size_t open_fn_size = strlen(name) + strlen("_open") + 1;
370 open_fn = calloc(1, open_fn_size);
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100371 if (!open_fn) {
372 rc = -ENOMEM;
373 goto err_open_fn;
374 }
375
Eric Laurent53336572021-01-13 20:58:24 +0100376 strlcpy(open_fn, name, open_fn_size);
377 strlcat(open_fn, "_open", open_fn_size);
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100378
379 plug_data->plugin_open_fn = dlsym(dl_hdl, open_fn);
Eric Laurent86822e42021-01-13 19:41:43 +0100380 if (!plug_data->plugin_open_fn) {
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100381 fprintf(stderr, "%s: dlsym to open fn failed, err = '%s'\n",
Eric Laurent86822e42021-01-13 19:41:43 +0100382 __func__, dlerror());
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100383 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
408err_dlsym:
409 free(open_fn);
410err_open_fn:
411 dlclose(dl_hdl);
412err_get_lib:
413err_dl_open:
414 free(plug_data);
415
416 return rc;
417}
418
419struct 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};