pa_fdsem *pa_fdsem_new(void) { pa_fdsem *f; f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data))); #ifdef HAVE_SYS_EVENTFD_H if ((f->efd = eventfd(0, EFD_CLOEXEC)) >= 0) f->fds[0] = f->fds[1] = -1; else #endif { if (pa_pipe_cloexec(f->fds) < 0) { pa_xfree(f); return NULL; } } f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem))); pa_atomic_store(&f->data->waiting, 0); pa_atomic_store(&f->data->signalled, 0); pa_atomic_store(&f->data->in_pipe, 0); return f; }
pa_flist *pa_flist_new_with_name(unsigned size, const char *name) { pa_flist *l; unsigned i; pa_assert(name); if (!size) size = FLIST_SIZE; l = pa_xmalloc0(sizeof(pa_flist) + sizeof(pa_flist_elem) * size); l->name = pa_xstrdup(name); l->size = size; while (1 << l->tag_shift < (int) size) l->tag_shift++; l->index_mask = (1 << l->tag_shift) - 1; l->tag_mask = INT_MAX - l->index_mask; pa_atomic_store(&l->stored, -1); pa_atomic_store(&l->empty, -1); for (i=0; i < size; i++) { stack_push(l, &l->empty, &l->table[i]); } return l; }
pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) { pa_fdsem *f = NULL; pa_assert(data); pa_assert(event_fd); #ifdef HAVE_SYS_EVENTFD_H f = pa_xnew(pa_fdsem, 1); if ((f->efd = eventfd(0, EFD_CLOEXEC)) < 0) { pa_xfree(f); return NULL; } f->fds[0] = f->fds[1] = -1; f->data = data; pa_atomic_store(&f->data->waiting, 0); pa_atomic_store(&f->data->signalled, 0); pa_atomic_store(&f->data->in_pipe, 0); #endif return f; }
/* No lock necessary */ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free_cb_t free_cb, bool read_only) { pa_memblock *b; pa_assert(p); pa_assert(d); pa_assert(length); pa_assert(length != (size_t) -1); pa_assert(free_cb); if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) b = pa_xnew(pa_memblock, 1); PA_REFCNT_INIT(b); b->pool = p; b->type = PA_MEMBLOCK_USER; b->read_only = read_only; b->is_silence = false; pa_atomic_ptr_store(&b->data, d); b->length = length; pa_atomic_store(&b->n_acquired, 0); pa_atomic_store(&b->please_signal, 0); b->per_type.user.free_cb = free_cb; stat_add(b); return b; }
/* No lock necessary */ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { pa_memblock *b = NULL; struct mempool_slot *slot; static int mempool_disable = 0; pa_assert(p); pa_assert(length); if (mempool_disable == 0) mempool_disable = getenv("PULSE_MEMPOOL_DISABLE") ? 1 : -1; if (mempool_disable > 0) return NULL; /* If -1 is passed as length we choose the size for the caller: we * take the largest size that fits in one of our slots. */ if (length == (size_t) -1) length = pa_mempool_block_size_max(p); if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) { if (!(slot = mempool_allocate_slot(p))) return NULL; b = mempool_slot_data(slot); b->type = PA_MEMBLOCK_POOL; pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock))); } else if (p->block_size >= length) { if (!(slot = mempool_allocate_slot(p))) return NULL; if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) b = pa_xnew(pa_memblock, 1); b->type = PA_MEMBLOCK_POOL_EXTERNAL; pa_atomic_ptr_store(&b->data, mempool_slot_data(slot)); } else { pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size); pa_atomic_inc(&p->stat.n_too_large_for_pool); return NULL; } PA_REFCNT_INIT(b); b->pool = p; b->read_only = b->is_silence = false; b->length = length; pa_atomic_store(&b->n_acquired, 0); pa_atomic_store(&b->please_signal, 0); stat_add(b); return b; }
/* Self-locked */ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size) { pa_memblock *b = NULL; pa_memimport_segment *seg; pa_assert(i); pa_mutex_lock(i->mutex); if ((b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(block_id)))) { pa_memblock_ref(b); goto finish; } if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX) goto finish; if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id)))) if (!(seg = segment_attach(i, shm_id))) goto finish; if (offset+size > seg->memory.size) goto finish; if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) b = pa_xnew(pa_memblock, 1); PA_REFCNT_INIT(b); b->pool = i->pool; b->type = PA_MEMBLOCK_IMPORTED; b->read_only = true; b->is_silence = false; pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset); b->length = size; pa_atomic_store(&b->n_acquired, 0); pa_atomic_store(&b->please_signal, 0); b->per_type.imported.id = block_id; b->per_type.imported.segment = seg; pa_hashmap_put(i->blocks, PA_UINT32_TO_PTR(block_id), b); seg->n_blocks++; stat_add(b); finish: pa_mutex_unlock(i->mutex); return b; }
pa_mempool* pa_mempool_new(int shared) { pa_mempool *p; p = pa_xnew(pa_mempool, 1); p->mutex = pa_mutex_new(TRUE, TRUE); p->semaphore = pa_semaphore_new(0); p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE); if (p->block_size < PA_PAGE_SIZE) p->block_size = PA_PAGE_SIZE; p->n_blocks = PA_MEMPOOL_SLOTS_MAX; pa_assert(p->block_size > PA_ALIGN(sizeof(struct mempool_slot))); if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) { pa_xfree(p); return NULL; } memset(&p->stat, 0, sizeof(p->stat)); pa_atomic_store(&p->n_init, 0); PA_LLIST_HEAD_INIT(pa_memimport, p->imports); PA_LLIST_HEAD_INIT(pa_memexport, p->exports); p->free_slots = pa_flist_new(p->n_blocks*2); return p; }
pa_flist *pa_flist_new(unsigned size) { pa_flist *l; if (!size) size = FLIST_SIZE; pa_assert(pa_is_power_of_two(size)); l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(pa_atomic_ptr_t) * size)); l->size = size; pa_atomic_store(&l->read_idx, 0); pa_atomic_store(&l->write_idx, 0); pa_atomic_store(&l->length, 0); return l; }
pa_aupdate *pa_aupdate_new(void) { pa_aupdate *a; a = pa_xnew(pa_aupdate, 1); pa_atomic_store(&a->read_lock, 0); a->write_lock = pa_mutex_new(false, false); a->semaphore = pa_semaphore_new(0); return a; }
/* No lock necessary */ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) { pa_memblock *b; pa_assert(p); pa_assert(d); pa_assert(length != (size_t) -1); pa_assert(length > 0); if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) b = pa_xnew(pa_memblock, 1); PA_REFCNT_INIT(b); b->pool = p; b->type = PA_MEMBLOCK_FIXED; b->read_only = read_only; pa_atomic_ptr_store(&b->data, d); b->length = length; pa_atomic_store(&b->n_acquired, 0); pa_atomic_store(&b->please_signal, 0); stat_add(b); return b; }
/* Lock free push to linked list stack */ static void stack_push(pa_flist *flist, pa_atomic_t *list, pa_flist_elem *new_elem) { int tag, newindex, next; pa_assert(list); tag = pa_atomic_inc(&flist->current_tag); newindex = new_elem - flist->table; pa_assert(newindex >= 0 && newindex < (int) flist->size); newindex |= (tag << flist->tag_shift) & flist->tag_mask; do { next = pa_atomic_load(list); pa_atomic_store(&new_elem->next, next); } while (!pa_atomic_cmpxchg(list, next, newindex)); }
/* No lock necessary */ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) { pa_memblock *b; pa_assert(p); pa_assert(length); /* If -1 is passed as length we choose the size for the caller. */ if (length == (size_t) -1) length = pa_mempool_block_size_max(p); b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length); PA_REFCNT_INIT(b); b->pool = p; b->type = PA_MEMBLOCK_APPENDED; b->read_only = b->is_silence = false; pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock))); b->length = length; pa_atomic_store(&b->n_acquired, 0); pa_atomic_store(&b->please_signal, 0); stat_add(b); return b; }
pa_mempool* pa_mempool_new(bool shared, size_t size) { pa_mempool *p; char t1[PA_BYTES_SNPRINT_MAX], t2[PA_BYTES_SNPRINT_MAX]; p = pa_xnew(pa_mempool, 1); p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE); if (p->block_size < PA_PAGE_SIZE) p->block_size = PA_PAGE_SIZE; if (size <= 0) p->n_blocks = PA_MEMPOOL_SLOTS_MAX; else { p->n_blocks = (unsigned) (size / p->block_size); if (p->n_blocks < 2) p->n_blocks = 2; } if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) { pa_xfree(p); return NULL; } pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is %s, maximum usable slot size is %lu", p->memory.shared ? "shared" : "private", p->n_blocks, pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size), pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size)), (unsigned long) pa_mempool_block_size_max(p)); memset(&p->stat, 0, sizeof(p->stat)); pa_atomic_store(&p->n_init, 0); PA_LLIST_HEAD_INIT(pa_memimport, p->imports); PA_LLIST_HEAD_INIT(pa_memexport, p->exports); p->mutex = pa_mutex_new(true, true); p->semaphore = pa_semaphore_new(0); p->free_slots = pa_flist_new(p->n_blocks); return p; }
int pa__init(pa_module *m) { pa_modargs *ma; const char *master_sink_name; const char *master_source_name; const char *max_hw_frag_size_str; const char *aep_runtime; pa_source *master_source; struct userdata *u; pa_proplist *p; pa_sink *master_sink; const char *raw_sink_name; const char *raw_source_name; const char *voice_sink_name; const char *voice_source_name; const char *dbus_type; int max_hw_frag_size = 3840; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log_error("Failed to parse module arguments"); goto fail; } voice_turn_sidetone_down(); master_sink_name = pa_modargs_get_value(ma, "master_sink", NULL); master_source_name = pa_modargs_get_value(ma, "master_source", NULL); raw_sink_name = pa_modargs_get_value(ma, "raw_sink_name", "sink.voice.raw"); raw_source_name = pa_modargs_get_value(ma, "raw_source_name", "source.voice.raw"); voice_sink_name = pa_modargs_get_value(ma, "voice_sink_name", "sink.voice"); voice_source_name = pa_modargs_get_value(ma, "voice_source_name", "source.voice"); dbus_type = pa_modargs_get_value(ma, "dbus_type", "session"); max_hw_frag_size_str = pa_modargs_get_value(ma, "max_hw_frag_size", "3840"); aep_runtime = pa_modargs_get_value(ma, "aep_runtime", "bbaid1n-wr0-h9a22b--dbxpb--"); voice_set_aep_runtime_switch(aep_runtime); pa_log_debug("Got arguments: master_sink=\"%s\" master_source=\"%s\" raw_sink_name=\"%s\" raw_source_name=\"%s\" dbus_type=\"%s\" max_hw_frag_size=\"%s\". ", master_sink_name, master_source_name, raw_sink_name, raw_source_name, dbus_type, max_hw_frag_size_str); if (!(master_sink = pa_namereg_get(m->core, master_sink_name, PA_NAMEREG_SINK))) { pa_log("Master sink \"%s\" not found", master_sink_name); goto fail; } if (!(master_source = pa_namereg_get(m->core, master_source_name, PA_NAMEREG_SOURCE))) { pa_log( "Master source \"%s\" not found", master_source_name); goto fail; } if (master_sink->sample_spec.format != master_source->sample_spec.format && master_sink->sample_spec.rate != master_source->sample_spec.rate && master_sink->sample_spec.channels != master_source->sample_spec.channels) { pa_log("Master source and sink must have same sample spec"); goto fail; } if (pa_atoi(max_hw_frag_size_str, &max_hw_frag_size) < 0 || max_hw_frag_size < 960 || max_hw_frag_size > 128*960) { pa_log("Bad value for max_hw_frag_size: %s", max_hw_frag_size_str); goto fail; } m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->modargs = ma; u->master_sink = master_sink; u->master_source = master_source; u->mainloop_handler = voice_mainloop_handler_new(u);; u->ul_timing_advance = 500; // = 500 micro seconds, seems to be a good default value pa_channel_map_init_mono(&u->mono_map); pa_channel_map_init_stereo(&u->stereo_map); u->hw_sample_spec.format = PA_SAMPLE_S16NE; u->hw_sample_spec.rate = SAMPLE_RATE_HW_HZ; u->hw_sample_spec.channels = 2; u->hw_mono_sample_spec.format = PA_SAMPLE_S16NE; u->hw_mono_sample_spec.rate = SAMPLE_RATE_HW_HZ; u->hw_mono_sample_spec.channels = 1; u->aep_sample_spec.format = PA_SAMPLE_S16NE; u->aep_sample_spec.rate = SAMPLE_RATE_AEP_HZ; u->aep_sample_spec.channels = 1; pa_channel_map_init_mono(&u->aep_channel_map); // The result is rounded down incorrectly thus +1 u->aep_fragment_size = pa_usec_to_bytes(PERIOD_AEP_USECS+1, &u->aep_sample_spec); u->aep_hw_fragment_size = pa_usec_to_bytes(PERIOD_AEP_USECS+1, &u->hw_sample_spec); u->hw_fragment_size = pa_usec_to_bytes(PERIOD_MASTER_USECS+1, &u->hw_sample_spec); u->hw_fragment_size_max = max_hw_frag_size; if (0 != (u->hw_fragment_size_max % u->hw_fragment_size)) u->hw_fragment_size_max += u->hw_fragment_size - (u->hw_fragment_size_max % u->hw_fragment_size); u->aep_hw_mono_fragment_size = pa_usec_to_bytes(PERIOD_AEP_USECS+1, &u->hw_mono_sample_spec); u->hw_mono_fragment_size = pa_usec_to_bytes(PERIOD_MASTER_USECS+1, &u->hw_mono_sample_spec); u->voice_ul_fragment_size = pa_usec_to_bytes(PERIOD_CMT_USECS+1, &u->aep_sample_spec); pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->aep_silence_memchunk, &u->aep_sample_spec, u->aep_fragment_size); voice_memchunk_pool_load(u); if (voice_init_raw_sink(u, raw_sink_name)) goto fail; pa_sink_put(u->raw_sink); if (voice_init_voip_sink(u, voice_sink_name)) goto fail; pa_sink_put(u->voip_sink); if (voice_init_aep_sink_input(u)) goto fail; pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); u->alt_mixer_compensation = PA_VOLUME_NORM; if (voice_init_hw_sink_input(u)) goto fail; u->sink_temp_buff = pa_xmalloc(2 * u->hw_fragment_size_max); u->sink_temp_buff_len = 2 * u->hw_fragment_size_max; u->dl_memblockq = pa_memblockq_new(0, 2 * u->voice_ul_fragment_size, 0, pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL); if (voice_init_raw_source(u, raw_source_name)) goto fail; pa_source_put(u->raw_source); if (voice_init_voip_source(u, voice_source_name)) goto fail; pa_source_put(u->voip_source); if (voice_init_hw_source_output(u)) goto fail; u->hw_source_memblockq = pa_memblockq_new(0, 2 * u->hw_fragment_size_max, 0, pa_frame_size(&u->hw_sample_spec), 0, 0, 0, NULL); u->ul_memblockq = pa_memblockq_new(0, 2 * u->voice_ul_fragment_size, 0, pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL); u->cs_call_sink_input = 0; u->dl_sideinfo_queue = pa_queue_new(); u->linear_q15_master_volume_L = INT16_MAX; u->linear_q15_master_volume_R = INT16_MAX; u->field_2CC = 0; voice_aep_ear_ref_init(u); if (voice_convert_init(u)) goto fail; if (voice_init_event_forwarder(u, dbus_type) || voice_init_cmtspeech(u)) goto fail; if (!(u->wb_mic_iir_eq = iir_eq_new(u->hw_fragment_size / 2, master_source->sample_spec.channels))) goto fail; if (!(u->nb_mic_iir_eq = iir_eq_new( u->aep_fragment_size / 2, 1))) goto fail; if (!(u->wb_ear_iir_eq = fir_eq_new(master_sink->sample_spec.rate, master_sink->sample_spec.channels))) goto fail; if (!(u->nb_ear_iir_eq = iir_eq_new(u->aep_fragment_size / 2, 1))) goto fail; u->input_task_active = FALSE; u->xprot_watchdog = TRUE; u->ambient_temp = 30; if (!(u->xprot = xprot_new())) goto fail; u->aep_enable = FALSE; u->wb_meq_enable = FALSE; u->wb_eeq_enable = FALSE; u->nb_meq_enable = FALSE; u->nb_eeq_enable = FALSE; u->xprot_enable = FALSE; u->updating_parameters = FALSE; u->sink_proplist_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], 0, (pa_hook_cb_t)sink_proplist_changed_cb, u);; u->source_proplist_changed_slot = pa_hook_connect( &m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], 0, (pa_hook_cb_t)source_proplist_changed_cb, u); u->mode_accessory_hwid_hash = 0; p = pa_proplist_new(); pa_proplist_sets(p, PA_NOKIA_PROP_AUDIO_MODE, "ihf"); pa_proplist_sets(p, PA_NOKIA_PROP_AUDIO_ACCESSORY_HWID, ""); pa_sink_update_proplist( master_sink, PA_UPDATE_REPLACE, p); pa_proplist_free(p); pa_source_output_put(u->hw_source_output); pa_sink_input_put(u->hw_sink_input); pa_sink_input_put(u->aep_sink_input); u->sink_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK, sink_subscribe_cb, u); return 0; fail: if (ma) pa_modargs_free(ma); pa__done(m); return -1; }
/* Generic sink state change logic. Used by raw_sink and voip_sink */ int voice_sink_set_state(pa_sink *s, pa_sink *other, pa_sink_state_t state) { struct userdata *u; pa_sink *om_sink; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!other) { pa_log_debug("other sink not initialized or freed"); return 0; } pa_sink_assert_ref(other); om_sink = u->master_sink; if (u->hw_sink_input && PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->hw_sink_input))) { if (pa_sink_input_get_state(u->hw_sink_input) == PA_SINK_INPUT_CORKED) { if (PA_SINK_IS_OPENED(state) || PA_SINK_IS_OPENED(pa_sink_get_state(other)) || pa_atomic_load(&u->cmt_connection.dl_state) == CMT_DL_ACTIVE) { pa_sink_input_cork(u->hw_sink_input, FALSE); pa_log_debug("hw_sink_input uncorked"); } } else { if (state == PA_SINK_SUSPENDED && pa_sink_get_state(other) == PA_SINK_SUSPENDED && pa_atomic_load(&u->cmt_connection.dl_state) != CMT_DL_ACTIVE) { pa_sink_input_cork(u->hw_sink_input, TRUE); pa_log_debug("hw_sink_input corked"); } } } if (om_sink == NULL) { pa_log_info("No master sink, assuming primary mixer tuning.\n"); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); } else if (pa_atomic_load(&u->cmt_connection.dl_state) == CMT_DL_ACTIVE || (pa_sink_get_state(u->voip_sink) <= PA_SINK_SUSPENDED && voice_voip_sink_used_by(u))) { if (pa_atomic_load(&u->mixer_state) == PROP_MIXER_TUNING_PRI) { pa_proplist *p = pa_proplist_new(); pa_assert(p); pa_proplist_sets(p, PROP_MIXER_TUNING_MODE, PROP_MIXER_TUNING_ALT_S); pa_sink_update_proplist(om_sink, PA_UPDATE_REPLACE, p); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_ALT); pa_proplist_free(p); if (u->sidetone_enable) voice_enable_sidetone(u,1); } } else { if (pa_atomic_load(&u->mixer_state) == PROP_MIXER_TUNING_ALT) { pa_proplist *p = pa_proplist_new(); pa_assert(p); pa_proplist_sets(p, PROP_MIXER_TUNING_MODE, PROP_MIXER_TUNING_PRI_S); pa_sink_update_proplist(om_sink, PA_UPDATE_REPLACE, p); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); pa_proplist_free(p); voice_enable_sidetone(u,0); } } return 0; }
static int sharedmem_create(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) { #if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) char fn[32]; int fd = -1; struct shm_marker *marker; bool do_unlink = false; /* Each time we create a new SHM area, let's first drop all stale * ones */ pa_shm_cleanup(); pa_random(&m->id, sizeof(m->id)); switch (type) { #ifdef HAVE_SHM_OPEN case PA_MEM_TYPE_SHARED_POSIX: segment_name(fn, sizeof(fn), m->id); fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode); do_unlink = true; break; #endif #ifdef HAVE_MEMFD case PA_MEM_TYPE_SHARED_MEMFD: fd = memfd_create("pulseaudio", MFD_ALLOW_SEALING); break; #endif default: goto fail; } if (fd < 0) { pa_log("%s open() failed: %s", pa_mem_type_to_string(type), pa_cstrerror(errno)); goto fail; } m->type = type; m->size = size + shm_marker_size(type); m->do_unlink = do_unlink; if (ftruncate(fd, (off_t) m->size) < 0) { pa_log("ftruncate() failed: %s", pa_cstrerror(errno)); goto fail; } #ifndef MAP_NORESERVE #define MAP_NORESERVE 0 #endif if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) { pa_log("mmap() failed: %s", pa_cstrerror(errno)); goto fail; } if (type == PA_MEM_TYPE_SHARED_POSIX) { /* We store our PID at the end of the shm block, so that we * can check for dead shm segments later */ marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - shm_marker_size(type)); pa_atomic_store(&marker->pid, (int) getpid()); pa_atomic_store(&marker->marker, SHM_MARKER); } /* For memfds, we keep the fd open until we pass it * to the other PA endpoint over unix domain socket. */ if (type == PA_MEM_TYPE_SHARED_MEMFD) m->fd = fd; else { pa_assert_se(pa_close(fd) == 0); m->fd = -1; } return 0; fail: if (fd >= 0) { #ifdef HAVE_SHM_OPEN if (type == PA_MEM_TYPE_SHARED_POSIX) shm_unlink(fn); #endif pa_close(fd); } #endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */ return -1; }
int pa_shm_create_rw(pa_shm *m, size_t size, bool shared, mode_t mode) { #ifdef HAVE_SHM_OPEN char fn[32]; int fd = -1; #endif pa_assert(m); pa_assert(size > 0); pa_assert(size <= MAX_SHM_SIZE); pa_assert(!(mode & ~0777)); pa_assert(mode >= 0600); /* Each time we create a new SHM area, let's first drop all stale * ones */ pa_shm_cleanup(); /* Round up to make it page aligned */ size = PA_PAGE_ALIGN(size); if (!shared) { m->id = 0; m->size = size; #ifdef MAP_ANONYMOUS if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) { pa_log("mmap() failed: %s", pa_cstrerror(errno)); goto fail; } #elif defined(HAVE_POSIX_MEMALIGN) { int r; if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) { pa_log("posix_memalign() failed: %s", pa_cstrerror(r)); goto fail; } } #else m->ptr = pa_xmalloc(m->size); #endif m->do_unlink = false; } else { #ifdef HAVE_SHM_OPEN struct shm_marker *marker; pa_random(&m->id, sizeof(m->id)); segment_name(fn, sizeof(fn), m->id); if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode)) < 0) { pa_log("shm_open() failed: %s", pa_cstrerror(errno)); goto fail; } m->size = size + SHM_MARKER_SIZE; if (ftruncate(fd, (off_t) m->size) < 0) { pa_log("ftruncate() failed: %s", pa_cstrerror(errno)); goto fail; } #ifndef MAP_NORESERVE #define MAP_NORESERVE 0 #endif if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) { pa_log("mmap() failed: %s", pa_cstrerror(errno)); goto fail; } /* We store our PID at the end of the shm block, so that we * can check for dead shm segments later */ marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - SHM_MARKER_SIZE); pa_atomic_store(&marker->pid, (int) getpid()); pa_atomic_store(&marker->marker, SHM_MARKER); pa_assert_se(pa_close(fd) == 0); m->do_unlink = true; #else goto fail; #endif } m->shared = shared; return 0; fail: #ifdef HAVE_SHM_OPEN if (fd >= 0) { shm_unlink(fn); pa_close(fd); } #endif return -1; }
void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simple_options *o) { connection *c = NULL; char pname[128]; pa_client_new_data client_data; pa_assert(p); pa_assert(io); pa_assert(o); if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS); pa_iochannel_free(io); return; } c = pa_msgobject_new(connection); c->parent.parent.free = connection_free; c->parent.process_msg = connection_process_msg; c->io = io; pa_iochannel_set_callback(c->io, io_callback, c); c->sink_input = NULL; c->source_output = NULL; c->input_memblockq = c->output_memblockq = NULL; c->protocol = p; c->options = pa_simple_options_ref(o); c->playback.current_memblock = NULL; c->playback.memblock_index = 0; c->dead = false; c->playback.underrun = true; pa_atomic_store(&c->playback.missing, 0); pa_client_new_data_init(&client_data); client_data.module = o->module; client_data.driver = __FILE__; pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "Simple client (%s)", pname); pa_proplist_sets(client_data.proplist, "simple-protocol.peer", pname); c->client = pa_client_new(p->core, &client_data); pa_client_new_data_done(&client_data); if (!c->client) goto fail; c->client->kill = client_kill_cb; c->client->userdata = c; if (o->playback) { pa_sink_input_new_data data; pa_memchunk silence; size_t l; pa_sink *sink; if (!(sink = pa_namereg_get(c->protocol->core, o->default_sink, PA_NAMEREG_SINK))) { pa_log("Failed to get sink."); goto fail; } pa_sink_input_new_data_init(&data); data.driver = __FILE__; data.module = o->module; data.client = c->client; pa_sink_input_new_data_set_sink(&data, sink, false); pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec); pa_sink_input_new(&c->sink_input, p->core, &data); pa_sink_input_new_data_done(&data); if (!c->sink_input) { pa_log("Failed to create sink input."); goto fail; } c->sink_input->parent.process_msg = sink_input_process_msg; c->sink_input->pop = sink_input_pop_cb; c->sink_input->process_rewind = sink_input_process_rewind_cb; c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; c->sink_input->kill = sink_input_kill_cb; c->sink_input->userdata = c; pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY); l = (size_t) ((double) pa_bytes_per_second(&o->sample_spec)*PLAYBACK_BUFFER_SECONDS); pa_sink_input_get_silence(c->sink_input, &silence); c->input_memblockq = pa_memblockq_new( "simple protocol connection input_memblockq", 0, l, l, &o->sample_spec, (size_t) -1, l/PLAYBACK_BUFFER_FRAGMENTS, 0, &silence); pa_memblock_unref(silence.memblock); pa_iochannel_socket_set_rcvbuf(io, l); pa_atomic_store(&c->playback.missing, (int) pa_memblockq_pop_missing(c->input_memblockq)); pa_sink_input_put(c->sink_input); } if (o->record) { pa_source_output_new_data data; size_t l; pa_source *source; if (!(source = pa_namereg_get(c->protocol->core, o->default_source, PA_NAMEREG_SOURCE))) { pa_log("Failed to get source."); goto fail; } pa_source_output_new_data_init(&data); data.driver = __FILE__; data.module = o->module; data.client = c->client; pa_source_output_new_data_set_source(&data, source, false); pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); pa_source_output_new_data_set_sample_spec(&data, &o->sample_spec); pa_source_output_new(&c->source_output, p->core, &data); pa_source_output_new_data_done(&data); if (!c->source_output) { pa_log("Failed to create source output."); goto fail; } c->source_output->push = source_output_push_cb; c->source_output->kill = source_output_kill_cb; c->source_output->get_latency = source_output_get_latency_cb; c->source_output->userdata = c; pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY); l = (size_t) (pa_bytes_per_second(&o->sample_spec)*RECORD_BUFFER_SECONDS); c->output_memblockq = pa_memblockq_new( "simple protocol connection output_memblockq", 0, l, 0, &o->sample_spec, 1, 0, 0, NULL); pa_iochannel_socket_set_sndbuf(io, l); pa_source_output_put(c->source_output); } pa_idxset_put(p->connections, c, NULL); return; fail: connection_unlink(c); }
int pa__init(pa_module*m) { pa_modargs *ma = NULL; struct userdata *u; const char *master_sink_name; const char *master_source_name; const char *raw_sink_name; const char *raw_source_name; const char *voice_sink_name; const char *voice_source_name; const char *max_hw_frag_size_str; int max_hw_frag_size = 3840; pa_sink *master_sink; pa_source *master_source; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; } master_sink_name = pa_modargs_get_value(ma, "master_sink", NULL); master_source_name = pa_modargs_get_value(ma, "master_source", NULL); raw_sink_name = pa_modargs_get_value(ma, "raw_sink_name", "sink.voice.raw"); raw_source_name = pa_modargs_get_value(ma, "raw_source_name", "source.voice.raw"); voice_sink_name = pa_modargs_get_value(ma, "voice_sink_name", "sink.voice"); voice_source_name = pa_modargs_get_value(ma, "voice_source_name", "source.voice"); max_hw_frag_size_str = pa_modargs_get_value(ma, "max_hw_frag_size", "3840"); pa_log_debug("Got arguments: master_sink=\"%s\" master_source=\"%s\" " "raw_sink_name=\"%s\" raw_source_name=\"%s\" max_hw_frag_size=\"%s\".", master_sink_name, master_source_name, raw_sink_name, raw_source_name, max_hw_frag_size_str); if (!(master_sink = pa_namereg_get(m->core, master_sink_name, PA_NAMEREG_SINK))) { pa_log("Master sink \"%s\" not found", master_sink_name); goto fail; } if (!(master_source = pa_namereg_get(m->core, master_source_name, PA_NAMEREG_SOURCE))) { pa_log("Master source \"%s\" not found", master_source_name); goto fail; } if (master_sink->sample_spec.format != master_source->sample_spec.format && master_sink->sample_spec.rate != master_source->sample_spec.rate && master_sink->sample_spec.channels != master_source->sample_spec.channels) { pa_log("Master source and sink must have same sample spec"); goto fail; } if (pa_atoi(max_hw_frag_size_str, &max_hw_frag_size) < 0 || max_hw_frag_size < 960 || max_hw_frag_size > 128*960) { pa_log("Bad value for max_hw_frag_size: %s", max_hw_frag_size_str); goto fail; } m->userdata = u = pa_xnew0(struct userdata, 1); u->modargs = ma; u->core = m->core; u->module = m; u->master_sink = master_sink; u->master_source = master_source; set_hooks(u); u->mainloop_handler = voice_mainloop_handler_new(u); u->ul_timing_advance = 500; // = 500 micro seconds, seems to be a good default value pa_channel_map_init_mono(&u->mono_map); pa_channel_map_init_stereo(&u->stereo_map); u->hw_sample_spec.format = PA_SAMPLE_S16NE; u->hw_sample_spec.rate = VOICE_SAMPLE_RATE_HW_HZ; u->hw_sample_spec.channels = 2; u->hw_mono_sample_spec.format = PA_SAMPLE_S16NE; u->hw_mono_sample_spec.rate = VOICE_SAMPLE_RATE_HW_HZ; u->hw_mono_sample_spec.channels = 1; u->aep_sample_spec.format = PA_SAMPLE_S16NE; u->aep_sample_spec.rate = VOICE_SAMPLE_RATE_AEP_HZ; u->aep_sample_spec.channels = 1; pa_channel_map_init_mono(&u->aep_channel_map); // The result is rounded down incorrectly thus +1 u->aep_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->aep_sample_spec); u->aep_hw_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->hw_sample_spec); u->hw_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_MASTER_USECS+1, &u->hw_sample_spec); u->hw_fragment_size_max = max_hw_frag_size; if (0 != (u->hw_fragment_size_max % u->hw_fragment_size)) u->hw_fragment_size_max += u->hw_fragment_size - (u->hw_fragment_size_max % u->hw_fragment_size); u->aep_hw_mono_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->hw_mono_sample_spec); u->hw_mono_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_MASTER_USECS+1, &u->hw_mono_sample_spec); u->voice_ul_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_CMT_USECS+1, &u->aep_sample_spec); pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->aep_silence_memchunk, & u->aep_sample_spec, u->aep_fragment_size); voice_memchunk_pool_load(u); if (voice_init_raw_sink(u, raw_sink_name)) goto fail; u->call_state_tracker = pa_call_state_tracker_get(m->core); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); pa_call_state_tracker_set_active(u->call_state_tracker, FALSE); u->alt_mixer_compensation = PA_VOLUME_NORM; if (voice_init_hw_sink_input(u)) goto fail; /* This must be set before calling pa_sink_put(), because pa_sink_put() has * assertion * "!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->flat_sink_input". */ u->raw_sink->flat_sink_input = u->hw_sink_input; /* This must be called before calling voice_init_voip_sink(), because * pa_sink_input_new() has assertion * "PA_SINK_IS_LINKED(pa_sink_get_state(data->sink))". */ pa_sink_put(u->raw_sink); /* This must be called before calling voice_init_aep_sink_input(), because * the flat volume logic will otherwise mess up the aep sink input's volume * when pa_sink_input_put(u->hw_sink_input) is called. */ pa_sink_input_put(u->hw_sink_input); if (voice_init_voip_sink(u, voice_sink_name)) goto fail; if (voice_init_aep_sink_input(u)) goto fail; u->sink_temp_buff = pa_xmalloc(2*u->hw_fragment_size_max); u->sink_temp_buff_len = 2*u->hw_fragment_size_max; if (voice_init_raw_source(u, raw_source_name)) goto fail; pa_source_put(u->raw_source); if (voice_init_voip_source(u, voice_source_name)) goto fail; pa_source_put(u->voip_source); if (voice_init_hw_source_output(u)) goto fail; /* TODO: Guess we should use max_hw_frag_size here */ u->hw_source_memblockq = // 8 * 5ms = 40ms pa_memblockq_new(0, 2*u->hw_fragment_size_max, 0, pa_frame_size(&u->hw_sample_spec), 0, 0, 0, NULL); u->ul_memblockq = pa_memblockq_new(0, 2*u->voice_ul_fragment_size, 0, pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL); u->dl_sideinfo_queue = pa_queue_new(); u->ul_deadline = 0; u->linear_q15_master_volume_L = INT16_MAX; u->linear_q15_master_volume_R = INT16_MAX; voice_aep_ear_ref_init(u); if (voice_convert_init(u)) goto fail; /* IHF mode is the default and this initialization is consistent with it. */ u->active_mic_channel = MIC_CH0; meego_parameter_request_updates("voice", (pa_hook_cb_t)voice_parameter_cb, PA_HOOK_NORMAL, FALSE, u); meego_parameter_request_updates("alsa", (pa_hook_cb_t)alsa_parameter_cb, PA_HOOK_NORMAL, FALSE, u); meego_parameter_request_updates("aep", (pa_hook_cb_t)aep_parameter_cb, PA_HOOK_LATE, FALSE, u); /* aep-s-i */ /* voip-sink ---\ hw-sink-input */ /* > optimized mix -------------> master-sink */ /* | */ /* raw-sink */ /* */ /* voip-src <--- hw-source-output */ /* < mux <------------- master-src */ /* raw-src <--- */ u->voip_sink->flat_sink_input = u->aep_sink_input; pa_sink_put(u->voip_sink); pa_source_output_put(u->hw_source_output); pa_sink_input_put(u->aep_sink_input); u->sink_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SINK_INPUT, master_sink_volume_subscribe_cb, u); u->previous_master_source_state = pa_source_get_state(u->master_source); u->source_change_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SOURCE, master_source_state_subscribe_cb, u); return 0; fail: pa__done(m); return -1; }