1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
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 <fcntl.h>
7 #include <pthread.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <sys/mman.h>
11 #include <sys/param.h>
12 #include <sys/stat.h>
13 #include <syslog.h>
14
15 #include "cras_alsa_card.h"
16 #include "cras_config.h"
17 #include "cras_device_blacklist.h"
18 #include "cras_observer.h"
19 #include "cras_shm.h"
20 #include "cras_system_state.h"
21 #include "cras_tm.h"
22 #include "cras_types.h"
23 #include "cras_util.h"
24 #include "utlist.h"
25
26 struct card_list {
27 struct cras_alsa_card *card;
28 struct card_list *prev, *next;
29 };
30
31 /* The system state.
32 * Members:
33 * exp_state - The exported system state shared with clients.
34 * shm_name - Name of posix shm region for exported state.
35 * shm_fd - fd for shm area of system_state struct.
36 * shm_fd_ro - fd for shm area of system_state struct, opened read-only.
37 * shm_size - Size of the shm area.
38 * device_config_dir - Directory of device configs where volume curves live.
39 * internal_ucm_suffix - The suffix to append to internal card name to
40 * control which ucm config file to load.
41 * device_blacklist - Blacklist of device the server will ignore.
42 * cards - A list of active sound cards in the system.
43 * update_lock - Protects the update_count, as audio threads can update the
44 * stream count.
45 * tm - The system-wide timer manager.
46 */
47 static struct {
48 struct cras_server_state *exp_state;
49 char shm_name[NAME_MAX];
50 int shm_fd;
51 int shm_fd_ro;
52 size_t shm_size;
53 const char *device_config_dir;
54 const char *internal_ucm_suffix;
55 struct cras_device_blacklist *device_blacklist;
56 struct card_list *cards;
57 pthread_mutex_t update_lock;
58 struct cras_tm *tm;
59 /* Select loop callback registration. */
60 int (*fd_add)(int fd, void (*cb)(void *data),
61 void *cb_data, void *select_data);
62 void (*fd_rm)(int fd, void *select_data);
63 void *select_data;
64 } state;
65
66 /*
67 * Exported Interface.
68 */
69
cras_system_state_init(const char * device_config_dir)70 void cras_system_state_init(const char *device_config_dir)
71 {
72 struct cras_server_state *exp_state;
73 int rc;
74
75 state.shm_size = sizeof(*exp_state);
76
77 snprintf(state.shm_name, sizeof(state.shm_name), "/cras-%d", getpid());
78 state.shm_fd = cras_shm_open_rw(state.shm_name, state.shm_size);
79 if (state.shm_fd < 0)
80 exit(state.shm_fd);
81
82 /* mmap shm. */
83 exp_state = mmap(NULL, state.shm_size,
84 PROT_READ | PROT_WRITE, MAP_SHARED,
85 state.shm_fd, 0);
86 if (exp_state == (struct cras_server_state *)-1)
87 exit(-ENOMEM);
88
89 /* Open a read-only copy to dup and pass to clients. */
90 state.shm_fd_ro = cras_shm_reopen_ro(state.shm_name, state.shm_fd);
91 if (state.shm_fd_ro < 0)
92 exit(state.shm_fd_ro);
93
94 /* Initial system state. */
95 exp_state->state_version = CRAS_SERVER_STATE_VERSION;
96 exp_state->volume = CRAS_MAX_SYSTEM_VOLUME;
97 exp_state->mute = 0;
98 exp_state->mute_locked = 0;
99 exp_state->suspended = 0;
100 exp_state->capture_gain = DEFAULT_CAPTURE_GAIN;
101 exp_state->capture_gain_target = DEFAULT_CAPTURE_GAIN;
102 exp_state->capture_mute = 0;
103 exp_state->capture_mute_locked = 0;
104 exp_state->min_volume_dBFS = DEFAULT_MIN_VOLUME_DBFS;
105 exp_state->max_volume_dBFS = DEFAULT_MAX_VOLUME_DBFS;
106 exp_state->min_capture_gain = DEFAULT_MIN_CAPTURE_GAIN;
107 exp_state->max_capture_gain = DEFAULT_MAX_CAPTURE_GAIN;
108 exp_state->num_streams_attached = 0;
109
110 if ((rc = pthread_mutex_init(&state.update_lock, 0) != 0)) {
111 syslog(LOG_ERR, "Fatal: system state mutex init");
112 exit(rc);
113 }
114
115 state.exp_state = exp_state;
116
117 /* Directory for volume curve configs.
118 * Note that device_config_dir does not affect device blacklist.
119 * Device blacklist is common to all boards so we do not need
120 * to change device blacklist at run time. */
121 state.device_config_dir = device_config_dir;
122 state.internal_ucm_suffix = NULL;
123
124 state.tm = cras_tm_init();
125 if (!state.tm) {
126 syslog(LOG_ERR, "Fatal: system state timer init");
127 exit(-ENOMEM);
128 }
129
130 /* Read config file for blacklisted devices. */
131 state.device_blacklist =
132 cras_device_blacklist_create(CRAS_CONFIG_FILE_DIR);
133 }
134
cras_system_state_set_internal_ucm_suffix(const char * internal_ucm_suffix)135 void cras_system_state_set_internal_ucm_suffix(const char *internal_ucm_suffix)
136 {
137 state.internal_ucm_suffix = internal_ucm_suffix;
138 }
139
cras_system_state_deinit()140 void cras_system_state_deinit()
141 {
142 /* Free any resources used. This prevents unit tests from leaking. */
143
144 cras_device_blacklist_destroy(state.device_blacklist);
145
146 cras_tm_deinit(state.tm);
147
148 if (state.exp_state) {
149 munmap(state.exp_state, state.shm_size);
150 cras_shm_close_unlink(state.shm_name, state.shm_fd);
151 if (state.shm_fd_ro != state.shm_fd)
152 close(state.shm_fd_ro);
153 }
154
155 pthread_mutex_destroy(&state.update_lock);
156 }
157
cras_system_set_volume(size_t volume)158 void cras_system_set_volume(size_t volume)
159 {
160 if (volume > CRAS_MAX_SYSTEM_VOLUME)
161 syslog(LOG_DEBUG, "system volume set out of range %zu", volume);
162
163 state.exp_state->volume = MIN(volume, CRAS_MAX_SYSTEM_VOLUME);
164 cras_observer_notify_output_volume(state.exp_state->volume);
165 }
166
cras_system_get_volume()167 size_t cras_system_get_volume()
168 {
169 return state.exp_state->volume;
170 }
171
cras_system_set_capture_gain(long gain)172 void cras_system_set_capture_gain(long gain)
173 {
174 /* Adjust targeted gain to be in supported range. */
175 state.exp_state->capture_gain_target = gain;
176 gain = MAX(gain, state.exp_state->min_capture_gain);
177 gain = MIN(gain, state.exp_state->max_capture_gain);
178 state.exp_state->capture_gain = gain;
179 cras_observer_notify_capture_gain(state.exp_state->capture_gain);
180 }
181
cras_system_get_capture_gain()182 long cras_system_get_capture_gain()
183 {
184 return state.exp_state->capture_gain;
185 }
186
cras_system_notify_mute(void)187 void cras_system_notify_mute(void)
188 {
189 cras_observer_notify_output_mute(state.exp_state->mute,
190 state.exp_state->user_mute,
191 state.exp_state->mute_locked);
192 }
193
cras_system_set_user_mute(int mute)194 void cras_system_set_user_mute(int mute)
195 {
196 if (state.exp_state->user_mute == !!mute)
197 return;
198
199 state.exp_state->user_mute = !!mute;
200 cras_system_notify_mute();
201 }
202
cras_system_set_mute(int mute)203 void cras_system_set_mute(int mute)
204 {
205 if (state.exp_state->mute_locked)
206 return;
207
208 if (state.exp_state->mute == !!mute)
209 return;
210
211 state.exp_state->mute = !!mute;
212 cras_system_notify_mute();
213 }
214
cras_system_set_mute_locked(int locked)215 void cras_system_set_mute_locked(int locked)
216 {
217 if (state.exp_state->mute_locked == !!locked)
218 return;
219
220 state.exp_state->mute_locked = !!locked;
221 cras_system_notify_mute();
222 }
223
cras_system_get_mute()224 int cras_system_get_mute()
225 {
226 return state.exp_state->mute || state.exp_state->user_mute;
227 }
228
cras_system_get_user_mute()229 int cras_system_get_user_mute()
230 {
231 return state.exp_state->user_mute;
232 }
233
cras_system_get_system_mute()234 int cras_system_get_system_mute()
235 {
236 return state.exp_state->mute;
237 }
238
cras_system_get_mute_locked()239 int cras_system_get_mute_locked()
240 {
241 return state.exp_state->mute_locked;
242 }
243
cras_system_notify_capture_mute(void)244 void cras_system_notify_capture_mute(void)
245 {
246 cras_observer_notify_capture_mute(state.exp_state->capture_mute,
247 state.exp_state->capture_mute_locked);
248 }
249
cras_system_set_capture_mute(int mute)250 void cras_system_set_capture_mute(int mute)
251 {
252 if (state.exp_state->capture_mute_locked)
253 return;
254
255 state.exp_state->capture_mute = !!mute;
256 cras_system_notify_capture_mute();
257 }
258
cras_system_set_capture_mute_locked(int locked)259 void cras_system_set_capture_mute_locked(int locked)
260 {
261 state.exp_state->capture_mute_locked = !!locked;
262 cras_system_notify_capture_mute();
263 }
264
cras_system_get_capture_mute()265 int cras_system_get_capture_mute()
266 {
267 return state.exp_state->capture_mute;
268 }
269
cras_system_get_capture_mute_locked()270 int cras_system_get_capture_mute_locked()
271 {
272 return state.exp_state->capture_mute_locked;
273 }
274
cras_system_get_suspended()275 int cras_system_get_suspended()
276 {
277 return state.exp_state->suspended;
278 }
279
cras_system_set_suspended(int suspended)280 void cras_system_set_suspended(int suspended)
281 {
282 state.exp_state->suspended = suspended;
283 cras_observer_notify_suspend_changed(suspended);
284 }
285
cras_system_set_volume_limits(long min,long max)286 void cras_system_set_volume_limits(long min, long max)
287 {
288 state.exp_state->min_volume_dBFS = min;
289 state.exp_state->max_volume_dBFS = max;
290 }
291
cras_system_get_min_volume()292 long cras_system_get_min_volume()
293 {
294 return state.exp_state->min_volume_dBFS;
295 }
296
cras_system_get_max_volume()297 long cras_system_get_max_volume()
298 {
299 return state.exp_state->max_volume_dBFS;
300 }
301
cras_system_set_capture_gain_limits(long min,long max)302 void cras_system_set_capture_gain_limits(long min, long max)
303 {
304 state.exp_state->min_capture_gain = MAX(min, DEFAULT_MIN_CAPTURE_GAIN);
305 state.exp_state->max_capture_gain = max;
306 /* Re-apply target gain subjected to the new supported range. */
307 cras_system_set_capture_gain(state.exp_state->capture_gain_target);
308 }
309
cras_system_get_min_capture_gain()310 long cras_system_get_min_capture_gain()
311 {
312 return state.exp_state->min_capture_gain;
313 }
314
cras_system_get_max_capture_gain()315 long cras_system_get_max_capture_gain()
316 {
317 return state.exp_state->max_capture_gain;
318 }
319
cras_system_add_alsa_card(struct cras_alsa_card_info * alsa_card_info)320 int cras_system_add_alsa_card(struct cras_alsa_card_info *alsa_card_info)
321 {
322 struct card_list *card;
323 struct cras_alsa_card *alsa_card;
324 unsigned card_index;
325
326 if (alsa_card_info == NULL)
327 return -EINVAL;
328
329 card_index = alsa_card_info->card_index;
330
331 DL_FOREACH(state.cards, card) {
332 if (card_index == cras_alsa_card_get_index(card->card))
333 return -EINVAL;
334 }
335 alsa_card = cras_alsa_card_create(
336 alsa_card_info,
337 state.device_config_dir,
338 state.device_blacklist,
339 (alsa_card_info->card_type == ALSA_CARD_TYPE_INTERNAL)
340 ? state.internal_ucm_suffix
341 : NULL);
342 if (alsa_card == NULL)
343 return -ENOMEM;
344 card = calloc(1, sizeof(*card));
345 if (card == NULL)
346 return -ENOMEM;
347 card->card = alsa_card;
348 DL_APPEND(state.cards, card);
349 return 0;
350 }
351
cras_system_remove_alsa_card(size_t alsa_card_index)352 int cras_system_remove_alsa_card(size_t alsa_card_index)
353 {
354 struct card_list *card;
355
356 DL_FOREACH(state.cards, card) {
357 if (alsa_card_index == cras_alsa_card_get_index(card->card))
358 break;
359 }
360 if (card == NULL)
361 return -EINVAL;
362 DL_DELETE(state.cards, card);
363 cras_alsa_card_destroy(card->card);
364 free(card);
365 return 0;
366 }
367
cras_system_alsa_card_exists(unsigned alsa_card_index)368 int cras_system_alsa_card_exists(unsigned alsa_card_index)
369 {
370 struct card_list *card;
371
372 DL_FOREACH(state.cards, card)
373 if (alsa_card_index == cras_alsa_card_get_index(card->card))
374 return 1;
375 return 0;
376 }
377
cras_system_set_select_handler(int (* add)(int fd,void (* callback)(void * data),void * callback_data,void * select_data),void (* rm)(int fd,void * select_data),void * select_data)378 int cras_system_set_select_handler(int (*add)(int fd,
379 void (*callback)(void *data),
380 void *callback_data,
381 void *select_data),
382 void (*rm)(int fd, void *select_data),
383 void *select_data)
384 {
385 if (state.fd_add != NULL || state.fd_rm != NULL)
386 return -EEXIST;
387 state.fd_add = add;
388 state.fd_rm = rm;
389 state.select_data = select_data;
390 return 0;
391 }
392
cras_system_add_select_fd(int fd,void (* callback)(void * data),void * callback_data)393 int cras_system_add_select_fd(int fd,
394 void (*callback)(void *data),
395 void *callback_data)
396 {
397 if (state.fd_add == NULL)
398 return -EINVAL;
399 return state.fd_add(fd, callback, callback_data,
400 state.select_data);
401 }
402
cras_system_rm_select_fd(int fd)403 void cras_system_rm_select_fd(int fd)
404 {
405 if (state.fd_rm != NULL)
406 state.fd_rm(fd, state.select_data);
407 }
408
cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction)409 void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction)
410 {
411 struct cras_server_state *s;
412
413 s = cras_system_state_update_begin();
414 if (!s)
415 return;
416
417 s->num_active_streams[direction]++;
418 s->num_streams_attached++;
419
420 cras_system_state_update_complete();
421 cras_observer_notify_num_active_streams(
422 direction, s->num_active_streams[direction]);
423 }
424
cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction)425 void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction)
426 {
427 struct cras_server_state *s;
428 unsigned i, sum;
429
430
431 s = cras_system_state_update_begin();
432 if (!s)
433 return;
434
435 sum = 0;
436 for (i=0; i < CRAS_NUM_DIRECTIONS; i++)
437 sum += s->num_active_streams[i];
438
439 /* Set the last active time when removing the final stream. */
440 if (sum == 1)
441 cras_clock_gettime(CLOCK_MONOTONIC_RAW,
442 &s->last_active_stream_time);
443 s->num_active_streams[direction]--;
444
445 cras_system_state_update_complete();
446 cras_observer_notify_num_active_streams(
447 direction, s->num_active_streams[direction]);
448 }
449
cras_system_state_get_active_streams()450 unsigned cras_system_state_get_active_streams()
451 {
452 unsigned i, sum;
453 sum = 0;
454 for (i=0; i < CRAS_NUM_DIRECTIONS; i++)
455 sum += state.exp_state->num_active_streams[i];
456 return sum;
457 }
458
cras_system_state_get_active_streams_by_direction(enum CRAS_STREAM_DIRECTION direction)459 unsigned cras_system_state_get_active_streams_by_direction(
460 enum CRAS_STREAM_DIRECTION direction)
461 {
462 return state.exp_state->num_active_streams[direction];
463 }
464
cras_system_state_get_last_stream_active_time(struct cras_timespec * ts)465 void cras_system_state_get_last_stream_active_time(struct cras_timespec *ts)
466 {
467 *ts = state.exp_state->last_active_stream_time;
468 }
469
cras_system_state_get_output_devs(const struct cras_iodev_info ** devs)470 int cras_system_state_get_output_devs(const struct cras_iodev_info **devs)
471 {
472 *devs = state.exp_state->output_devs;
473 return state.exp_state->num_output_devs;
474 }
475
cras_system_state_get_input_devs(const struct cras_iodev_info ** devs)476 int cras_system_state_get_input_devs(const struct cras_iodev_info **devs)
477 {
478 *devs = state.exp_state->input_devs;
479 return state.exp_state->num_input_devs;
480 }
481
cras_system_state_get_output_nodes(const struct cras_ionode_info ** nodes)482 int cras_system_state_get_output_nodes(const struct cras_ionode_info **nodes)
483 {
484 *nodes = state.exp_state->output_nodes;
485 return state.exp_state->num_output_nodes;
486 }
487
cras_system_state_get_input_nodes(const struct cras_ionode_info ** nodes)488 int cras_system_state_get_input_nodes(const struct cras_ionode_info **nodes)
489 {
490 *nodes = state.exp_state->input_nodes;
491 return state.exp_state->num_input_nodes;
492 }
493
cras_system_state_update_begin()494 struct cras_server_state *cras_system_state_update_begin()
495 {
496 if (pthread_mutex_lock(&state.update_lock)) {
497 syslog(LOG_ERR, "Failed to lock stream mutex");
498 return NULL;
499 }
500
501 __sync_fetch_and_add(&state.exp_state->update_count, 1);
502 return state.exp_state;
503 }
504
cras_system_state_update_complete()505 void cras_system_state_update_complete()
506 {
507 __sync_fetch_and_add(&state.exp_state->update_count, 1);
508 pthread_mutex_unlock(&state.update_lock);
509 }
510
cras_system_state_get_no_lock()511 struct cras_server_state *cras_system_state_get_no_lock()
512 {
513 return state.exp_state;
514 }
515
cras_sys_state_shm_fd()516 key_t cras_sys_state_shm_fd()
517 {
518 return state.shm_fd_ro;
519 }
520
cras_system_state_get_tm()521 struct cras_tm *cras_system_state_get_tm()
522 {
523 return state.tm;
524 }
525