Coverage Report

Created: 2026-05-16 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/adhd/cras/src/server/cras_dsp.c
Line
Count
Source
1
/* Copyright 2012 The ChromiumOS Authors
2
 * Use of this source code is governed by a BSD-style license that can be
3
 * found in the LICENSE file.
4
 */
5
6
#include "cras/src/server/cras_dsp.h"
7
8
#include <pthread.h>
9
#include <stdlib.h>
10
#include <string.h>
11
#include <syslog.h>
12
13
#include "cras/server/main_message.h"
14
#include "cras/server/s2/s2.h"
15
#include "cras/src/common/cras_string.h"
16
#include "cras/src/common/dumper.h"
17
#include "cras/src/dsp/dsp_util.h"
18
#include "cras/src/server/cras_dsp_ini.h"
19
#include "cras/src/server/cras_dsp_pipeline.h"
20
#include "cras/src/server/cras_expr.h"
21
#include "cras/src/server/cras_iodev.h"
22
#include "cras/src/server/cras_main_thread_log.h"
23
#include "cras/src/server/cras_server_metrics.h"
24
#include "cras_audio_format.h"
25
#include "cras_iodev_info.h"
26
#include "cras_types.h"
27
#include "third_party/utlist/utlist.h"
28
29
/* We have a dsp_context for each pipeline. The context records the
30
 * parameters used to create a pipeline, so the pipeline can be
31
 * (re-)loaded later. The pipeline is (re-)loaded in the following
32
 * cases:
33
 *
34
 * (1) The client asks to (re-)load it with cras_load_pipeline().
35
 * (2) The client asks to reload the ini with cras_reload_ini().
36
 *
37
 * The pipeline is (re-)loaded asynchronously in an internal thread,
38
 * so the client needs to use cras_dsp_get_pipeline() and
39
 * cras_dsp_put_pipeline() to safely access the pipeline.
40
 */
41
struct cras_dsp_context {
42
  pthread_mutex_t mutex;
43
  struct pipeline* pipeline;
44
45
  struct cras_expr_env env;
46
  int sample_rate;
47
  const char* purpose;
48
  struct dsp_offload_map* offload_map;
49
  struct cras_dsp_context *prev, *next;
50
};
51
52
static struct dumper* syslog_dumper;
53
static const char* ini_filename;
54
static struct ini* global_ini;
55
static struct cras_dsp_context* context_list;
56
57
91
static void initialize_environment(struct cras_expr_env* env) {
58
91
  cras_expr_env_install_builtins(env);
59
91
  cras_expr_env_set_variable_boolean(env, "disable_eq", 0);
60
91
  cras_expr_env_set_variable_boolean(env, "disable_drc", 0);
61
91
  cras_expr_env_set_variable_string(env, "dsp_name", "");
62
91
  cras_expr_env_set_variable_boolean(env, "swap_lr_disabled", 1);
63
91
  cras_expr_env_set_variable_integer(env, "display_rotation", ROTATE_0);
64
91
  cras_expr_env_set_variable_integer(env, "FL", CRAS_CH_FL);
65
91
  cras_expr_env_set_variable_integer(env, "FR", CRAS_CH_FR);
66
91
  cras_expr_env_set_variable_integer(env, "RL", CRAS_CH_RL);
67
91
  cras_expr_env_set_variable_integer(env, "RR", CRAS_CH_RR);
68
91
}
69
70
69
static void destroy_pipeline(struct pipeline* pipeline) {
71
69
  struct ini* private_ini;
72
73
69
  private_ini = cras_dsp_pipeline_get_ini(pipeline);
74
69
  cras_dsp_pipeline_free(pipeline);
75
76
  /*
77
   * If pipeline is using an dsp ini other than the global one, free
78
   * this ini so its life cycle is aligned with the associated dsp
79
   * pipeline.
80
   */
81
69
  if (private_ini && (private_ini != global_ini)) {
82
69
    cras_dsp_ini_free(private_ini);
83
69
  }
84
69
}
85
86
static struct pipeline* prepare_pipeline(struct cras_dsp_context* ctx,
87
69
                                         struct ini* target_ini) {
88
69
  struct pipeline* pipeline;
89
69
  const char* purpose = ctx->purpose;
90
69
  int ret;
91
92
69
  pipeline = cras_dsp_pipeline_create(target_ini, &ctx->env, purpose);
93
94
69
  if (pipeline) {
95
69
    syslog(LOG_DEBUG, "pipeline created");
96
69
  } else {
97
0
    syslog(LOG_DEBUG, "pipeline not created");
98
0
    goto bail;
99
0
  }
100
101
69
  ret = cras_dsp_pipeline_load(pipeline);
102
69
  if (ret < 0) {
103
0
    syslog(LOG_ERR, "cannot load pipeline: %d", ret);
104
0
    goto bail;
105
0
  }
106
107
69
  ret = cras_dsp_pipeline_instantiate(pipeline, ctx->sample_rate, &ctx->env);
108
69
  if (ret < 0) {
109
0
    syslog(LOG_ERR, "cannot instantiate pipeline: %d", ret);
110
0
    goto bail;
111
0
  }
112
113
69
  if (cras_dsp_pipeline_get_sample_rate(pipeline) != ctx->sample_rate) {
114
0
    syslog(LOG_ERR, "pipeline sample rate mismatch (%d vs %d)",
115
0
           cras_dsp_pipeline_get_sample_rate(pipeline), ctx->sample_rate);
116
0
    goto bail;
117
0
  }
118
119
69
  return pipeline;
120
121
0
bail:
122
0
  if (pipeline) {
123
0
    destroy_pipeline(pipeline);
124
0
  }
125
0
  return NULL;
126
69
}
127
128
/* The strategy is to offload the given CRAS pipeline to DSP if applicable. If
129
 * that is the case, the following steps will be done to offload post-processing
130
 * effects from CRAS to DSP firmware:
131
 *   1. Enable the associated components on DSP and set the config blob to them
132
 *      each correspondent to CRAS pipeline modules.
133
 *   2. Set offload_applied flag true in CRAS pipeline, which makes the pipeline
134
 *      run through audio streams while bypassing post-processing modules.
135
 *
136
 * On the other hand if the pipeline is not applicable, disabling the associated
137
 * components on DSP is needed to assure no post-processing effect is on DSP.
138
 */
139
static void possibly_offload_pipeline(struct dsp_offload_map* offload_map,
140
91
                                      struct pipeline* pipe) {
141
91
  bool fallback = false;
142
91
  int rc;
143
144
91
  if (!offload_map) {
145
    // The DSP offload doesn't support for the device running this pipeline.
146
91
    return;
147
91
  }
148
149
0
  if (!offload_map->parent_dev) {
150
0
    syslog(LOG_ERR, "cras_dsp: invalid parent_dev in offload_map");
151
0
    return;
152
0
  }
153
154
  // Disable offload when disallow_bits is non-zero (at least one condition is
155
  // met that disallows applying DSP offload).
156
0
  if (offload_map->disallow_bits) {
157
0
    syslog(LOG_DEBUG, "cras_dsp: disallow offload (disallow_bits=%d)",
158
0
           offload_map->disallow_bits);
159
0
    goto disable_offload;
160
0
  }
161
162
  // If supports, check if the DSP offload is applicable, i.e. the pattern for
163
  // the CRAS pipeline is matched with the offload map. The pipeline can be NULL
164
  // when the current active node has no DSP config specified, which will be
165
  // regarded as not applicable.
166
0
  bool is_applicable = false;
167
0
  if (pipe) {
168
0
    char* pattern = cras_dsp_pipeline_get_pattern(pipe);
169
0
    syslog(LOG_DEBUG, "cras_dsp: trying to offload pipeline (%s)...", pattern);
170
0
    is_applicable = str_equals_bounded(offload_map->dsp_pattern, pattern,
171
0
                                       DSP_PATTERN_MAX_SIZE);
172
0
    free(pattern);
173
0
  }
174
0
  syslog(LOG_DEBUG, "cras_dsp: offload is %sapplicable",
175
0
         is_applicable ? "" : "non-");
176
177
  // If not applicable, disable offload.
178
0
  if (!is_applicable) {
179
0
    cras_dsp_offload_set_disallow_bit(offload_map, DISALLOW_OFFLOAD_BY_PATTERN);
180
0
    goto disable_offload;
181
0
  }
182
183
  // is_applicable == true
184
0
  cras_dsp_offload_clear_disallow_bit(offload_map, DISALLOW_OFFLOAD_BY_PATTERN);
185
  // If the DSP offload is already applied for the same pipeline/node, there
186
  // is no longer needed for setting configs to components on DSP.
187
0
  if (cras_dsp_offload_is_already_applied(offload_map)) {
188
0
    syslog(LOG_DEBUG, "cras_dsp: offload is already applied");
189
0
    cras_dsp_pipeline_apply_offload(pipe, true);
190
0
    return;
191
0
  }
192
193
0
  rc = cras_dsp_pipeline_config_offload(offload_map, pipe);
194
0
  if (rc) {
195
0
    syslog(LOG_ERR, "cras_dsp: Failed to config offload blobs, rc: %d", rc);
196
0
    MAINLOG(main_log, MAIN_THREAD_DEV_DSP_OFFLOAD,
197
0
            offload_map->parent_dev->info.idx, 1 /* enable */, 1 /* error */);
198
0
    fallback = true;
199
0
    goto disable_offload;  // fallback to process on CRAS
200
0
  }
201
202
0
  rc = cras_dsp_offload_set_state(offload_map, true);
203
0
  if (rc) {
204
0
    syslog(LOG_ERR, "cras_dsp: Failed to enable offload, rc: %d", rc);
205
0
    MAINLOG(main_log, MAIN_THREAD_DEV_DSP_OFFLOAD,
206
0
            offload_map->parent_dev->info.idx, 1 /* enable */, 1 /* error */);
207
0
    fallback = true;
208
0
    goto disable_offload;  // fallback to process on CRAS
209
0
  }
210
211
0
  syslog(LOG_DEBUG, "cras_dsp: offload is applied on success.");
212
0
  MAINLOG(main_log, MAIN_THREAD_DEV_DSP_OFFLOAD,
213
0
          offload_map->parent_dev->info.idx, 1 /* enable */, 0 /* ok */);
214
0
  cras_server_metrics_device_dsp_offload_status(
215
0
      offload_map->parent_dev, CRAS_DEVICE_DSP_OFFLOAD_SUCCESS);
216
217
  // Set offload_applied flag true
218
0
  cras_dsp_pipeline_apply_offload(pipe, true);
219
0
  return;
220
221
0
disable_offload:
222
  // Take actions to disable components on DSP if not applicable.
223
0
  rc = cras_dsp_offload_set_state(offload_map, false);
224
0
  if (rc) {
225
    // TODO(b/188647460): Consider better error handlings e.g. N-time retries,
226
    //                    report up to CRAS server, and etc.
227
0
    syslog(LOG_ERR, "cras_dsp: Failed to disable offload, rc: %d", rc);
228
0
    MAINLOG(main_log, MAIN_THREAD_DEV_DSP_OFFLOAD,
229
0
            offload_map->parent_dev->info.idx, 0 /* disable */, 1 /* error */);
230
0
    if (fallback) {
231
0
      cras_server_metrics_device_dsp_offload_status(
232
0
          offload_map->parent_dev, CRAS_DEVICE_DSP_OFFLOAD_FALLBACK_ERROR);
233
0
    } else {
234
0
      cras_server_metrics_device_dsp_offload_status(
235
0
          offload_map->parent_dev, CRAS_DEVICE_DSP_OFFLOAD_ERROR);
236
0
    }
237
0
  } else {
238
0
    MAINLOG(main_log, MAIN_THREAD_DEV_DSP_OFFLOAD,
239
0
            offload_map->parent_dev->info.idx, 0 /* disable */, 0 /* ok */);
240
0
    if (fallback) {
241
0
      cras_server_metrics_device_dsp_offload_status(
242
0
          offload_map->parent_dev, CRAS_DEVICE_DSP_OFFLOAD_FALLBACK_SUCCESS);
243
0
    }
244
0
  }
245
246
  // Set offload_applied flag false
247
0
  cras_dsp_pipeline_apply_offload(pipe, false);
248
0
}
249
250
static void cmd_load_pipeline(struct cras_dsp_context* ctx,
251
91
                              struct ini* target_ini) {
252
91
  struct pipeline *pipeline, *old_pipeline;
253
254
91
  pipeline = target_ini ? prepare_pipeline(ctx, target_ini) : NULL;
255
256
91
  possibly_offload_pipeline(ctx->offload_map, pipeline);
257
258
  // This locking is short to avoild blocking audio thread.
259
91
  pthread_mutex_lock(&ctx->mutex);
260
91
  old_pipeline = ctx->pipeline;
261
91
  ctx->pipeline = pipeline;
262
91
  pthread_mutex_unlock(&ctx->mutex);
263
264
91
  if (old_pipeline) {
265
0
    destroy_pipeline(old_pipeline);
266
0
  }
267
91
}
268
269
6
static void cmd_reload_ini() {
270
6
  struct ini* old_ini = global_ini;
271
6
  struct cras_dsp_context* ctx;
272
273
6
  struct ini* new_ini = cras_dsp_ini_create(ini_filename);
274
6
  if (!new_ini) {
275
6
    syslog(LOG_DEBUG, "cannot create dsp ini");
276
6
    return;
277
6
  }
278
279
0
  DL_FOREACH (context_list, ctx) {
280
    // Reset the offload state to force the offload blob re-configuring.
281
0
    cras_dsp_offload_reset_map(ctx->offload_map);
282
0
    cmd_load_pipeline(ctx, new_ini);
283
0
  }
284
285
0
  global_ini = new_ini;
286
287
0
  if (old_ini) {
288
0
    cras_dsp_ini_free(old_ini);
289
0
  }
290
0
}
291
292
0
static void cmd_reload_ini_cb(struct cras_main_message* msg, void* arg) {
293
0
  cmd_reload_ini();
294
0
}
295
296
0
void notify_reload_cras_dsp() {
297
0
  struct cras_main_message msg = {
298
0
      .length = sizeof(msg),
299
0
      .type = CRAS_MAIN_RELOAD_DSP,
300
0
  };
301
0
  cras_main_message_send(&msg);
302
0
}
303
304
// Exported functions
305
306
4
void cras_dsp_init(const char* filename) {
307
4
  dsp_enable_flush_denormal_to_zero();
308
4
  ini_filename = strdup(filename);
309
4
  syslog_dumper = syslog_dumper_create(LOG_WARNING);
310
4
  cras_main_message_add_handler(CRAS_MAIN_RELOAD_DSP, cmd_reload_ini_cb, NULL);
311
4
  cras_s2_set_reload_output_plugin_processor(notify_reload_cras_dsp);
312
4
  cmd_reload_ini();
313
4
}
314
315
0
void cras_dsp_stop() {
316
0
  syslog_dumper_free(syslog_dumper);
317
0
  if (ini_filename) {
318
0
    free((char*)ini_filename);
319
0
  }
320
0
  if (global_ini) {
321
0
    cras_dsp_ini_free(global_ini);
322
0
    global_ini = NULL;
323
0
  }
324
0
}
325
326
struct cras_dsp_context* cras_dsp_context_new(int sample_rate,
327
91
                                              const char* purpose) {
328
91
  struct cras_dsp_context* ctx = calloc(1, sizeof(*ctx));
329
330
91
  pthread_mutex_init(&ctx->mutex, NULL);
331
91
  initialize_environment(&ctx->env);
332
91
  ctx->sample_rate = sample_rate;
333
91
  ctx->purpose = strdup(purpose);
334
335
91
  DL_APPEND(context_list, ctx);
336
91
  return ctx;
337
91
}
338
339
void cras_dsp_context_set_offload_map(struct cras_dsp_context* ctx,
340
0
                                      struct dsp_offload_map* offload_map) {
341
0
  if (ctx) {
342
0
    ctx->offload_map = offload_map;
343
0
  }
344
0
}
345
346
91
void cras_dsp_context_free(struct cras_dsp_context* ctx) {
347
91
  DL_DELETE(context_list, ctx);
348
349
91
  pthread_mutex_destroy(&ctx->mutex);
350
91
  if (ctx->pipeline) {
351
69
    destroy_pipeline(ctx->pipeline);
352
69
    ctx->pipeline = NULL;
353
69
  }
354
91
  cras_expr_env_free(&ctx->env);
355
91
  free((char*)ctx->purpose);
356
91
  free(ctx);
357
91
}
358
359
void cras_dsp_set_variable_string(struct cras_dsp_context* ctx,
360
                                  const char* key,
361
22
                                  const char* value) {
362
22
  cras_expr_env_set_variable_string(&ctx->env, key, value);
363
22
}
364
365
void cras_dsp_set_variable_boolean(struct cras_dsp_context* ctx,
366
                                   const char* key,
367
0
                                   char value) {
368
0
  cras_expr_env_set_variable_boolean(&ctx->env, key, value);
369
0
}
370
371
void cras_dsp_set_variable_integer(struct cras_dsp_context* ctx,
372
                                   const char* key,
373
22
                                   int value) {
374
22
  cras_expr_env_set_variable_integer(&ctx->env, key, value);
375
22
}
376
377
22
void cras_dsp_load_pipeline(struct cras_dsp_context* ctx) {
378
22
  cmd_load_pipeline(ctx, global_ini);
379
22
}
380
381
void cras_dsp_load_mock_pipeline(struct cras_dsp_context* ctx,
382
69
                                 unsigned int num_channels) {
383
69
  struct ini* mock_ini;
384
69
  mock_ini = create_mock_ini(ctx->purpose, num_channels);
385
69
  if (mock_ini == NULL) {
386
0
    syslog(LOG_ERR, "Failed to create mock ini");
387
69
  } else {
388
69
    cmd_load_pipeline(ctx, mock_ini);
389
69
  }
390
69
}
391
392
2.46k
struct pipeline* cras_dsp_get_pipeline(struct cras_dsp_context* ctx) {
393
2.46k
  pthread_mutex_lock(&ctx->mutex);
394
2.46k
  if (!ctx->pipeline) {
395
44
    pthread_mutex_unlock(&ctx->mutex);
396
44
    return NULL;
397
44
  }
398
2.41k
  return ctx->pipeline;
399
2.46k
}
400
401
2.41k
void cras_dsp_put_pipeline(struct cras_dsp_context* ctx) {
402
2.41k
  pthread_mutex_unlock(&ctx->mutex);
403
2.41k
}
404
405
2
void cras_dsp_reload_ini() {
406
2
  cmd_reload_ini();
407
2
}
408
409
0
void cras_dsp_readapt_pipeline(struct cras_dsp_context* ctx) {
410
0
  struct pipeline* pipeline = cras_dsp_get_pipeline(ctx);
411
0
  if (!pipeline) {
412
0
    syslog(LOG_WARNING, "Bad attempt to readapt pipeline while not loaded.");
413
0
    return;
414
0
  }
415
  /* dsp_context mutex locked. Now it's safe to modify dsp
416
   * pipeline resources. */
417
418
0
  possibly_offload_pipeline(ctx->offload_map, pipeline);
419
0
  cras_dsp_put_pipeline(ctx);
420
0
}
421
422
18
void cras_dsp_dump_info() {
423
18
  struct pipeline* pipeline;
424
18
  struct cras_dsp_context* ctx;
425
426
18
  if (global_ini) {
427
0
    cras_dsp_ini_dump(syslog_dumper, global_ini);
428
0
  }
429
18
  DL_FOREACH (context_list, ctx) {
430
0
    cras_expr_env_dump(syslog_dumper, &ctx->env);
431
0
    pipeline = ctx->pipeline;
432
0
    if (pipeline) {
433
0
      cras_dsp_pipeline_dump(syslog_dumper, pipeline);
434
0
    }
435
0
  }
436
18
}
437
438
CRAS_STREAM_ACTIVE_AP_EFFECT cras_dsp_get_active_ap_effects(
439
0
    struct cras_dsp_context* ctx) {
440
0
  return ctx ? cras_dsp_pipeline_get_active_ap_effects(ctx->pipeline) : 0;
441
0
}
442
443
2
unsigned int cras_dsp_num_output_channels(const struct cras_dsp_context* ctx) {
444
2
  return cras_dsp_pipeline_get_num_output_channels(ctx->pipeline);
445
2
}
446
447
20
unsigned int cras_dsp_num_input_channels(const struct cras_dsp_context* ctx) {
448
20
  return cras_dsp_pipeline_get_num_input_channels(ctx->pipeline);
449
20
}