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