blob: eac0f38a0abff1df889c624d2c8ae3bb841c9cb6 [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
Ritu Sharma4d5a6222024-05-29 11:48:16 +053087 if (plugin->state == COMPRESS_PLUG_STATE_RUNNING)
88 return plugin->ops->set_params(plugin, params);
89 else if (plugin->state != COMPRESS_PLUG_STATE_OPEN)
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +010090 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
104static 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
112static 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
123static 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
141static 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
157static 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
172static 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
187static 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
198static 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
209static 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
220static 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
280static 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
293static 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
305static 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
323static 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
334static 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;
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100338 void *dl_hdl;
339 int rc = 0;
Eric Laurent86822e42021-01-13 19:41:43 +0100340 char *so_name, *open_fn, token[80], *name, *token_saveptr;
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100341
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);
Eric Laurent86822e42021-01-13 19:41:43 +0100365 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 }
Eric Laurent53336572021-01-13 20:58:24 +0100371 const size_t open_fn_size = strlen(name) + strlen("_open") + 1;
372 open_fn = calloc(1, open_fn_size);
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100373 if (!open_fn) {
374 rc = -ENOMEM;
375 goto err_open_fn;
376 }
377
Eric Laurent53336572021-01-13 20:58:24 +0100378 strlcpy(open_fn, name, open_fn_size);
379 strlcat(open_fn, "_open", open_fn_size);
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100380
381 plug_data->plugin_open_fn = dlsym(dl_hdl, open_fn);
Eric Laurent86822e42021-01-13 19:41:43 +0100382 if (!plug_data->plugin_open_fn) {
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100383 fprintf(stderr, "%s: dlsym to open fn failed, err = '%s'\n",
Eric Laurent86822e42021-01-13 19:41:43 +0100384 __func__, dlerror());
Vidyakumar Athota931e7cf2020-12-10 12:05:54 +0100385 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
410err_dlsym:
411 free(open_fn);
412err_open_fn:
413 dlclose(dl_hdl);
414err_get_lib:
415err_dl_open:
416 free(plug_data);
417
418 return rc;
419}
420
421struct 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};