/* Copyright 2017 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include <poll.h> #include <syslog.h> #include "audio_thread_log.h" #include "cras_audio_area.h" #include "cras_iodev.h" #include "cras_non_empty_audio_handler.h" #include "cras_rstream.h" #include "cras_server_metrics.h" #include "dev_stream.h" #include "input_data.h" #include "polled_interval_checker.h" #include "utlist.h" #include "dev_io.h" static const struct timespec playback_wake_fuzz_ts = { 0, 500 * 1000 /* 500 usec. */ }; /* The maximum time to wait before checking the device's non-empty status. */ static const int NON_EMPTY_UPDATE_INTERVAL_SEC = 5; /* * The minimum number of consecutive seconds of empty audio that must be * played before a device is considered to be playing empty audio. */ static const int MIN_EMPTY_PERIOD_SEC = 30; /* The number of devices playing/capturing non-empty stream(s). */ static int non_empty_device_count = 0; /* Gets the master device which the stream is attached to. */ static inline struct cras_iodev *get_master_dev(const struct dev_stream *stream) { return (struct cras_iodev *)stream->stream->master_dev.dev_ptr; } /* Updates the estimated sample rate of open device to all attached * streams. */ static void update_estimated_rate(struct open_dev *adev) { struct cras_iodev *master_dev; struct cras_iodev *dev = adev->dev; struct dev_stream *dev_stream; DL_FOREACH(dev->streams, dev_stream) { master_dev = get_master_dev(dev_stream); if (master_dev == NULL) { syslog(LOG_ERR, "Fail to find master open dev."); continue; } dev_stream_set_dev_rate(dev_stream, dev->ext_format->frame_rate, cras_iodev_get_est_rate_ratio(dev), cras_iodev_get_est_rate_ratio(master_dev), adev->coarse_rate_adjust); } } /* * Counts the number of devices which are currently playing/capturing non-empty * audio. */ static inline int count_non_empty_dev(struct open_dev *adevs) { int count = 0; struct open_dev *adev; DL_FOREACH(adevs, adev) { if (!adev->empty_pi || !pic_interval_elapsed(adev->empty_pi)) count++; } return count; } static void check_non_empty_state_transition(struct open_dev *adevs) { int new_non_empty_dev_count = count_non_empty_dev(adevs); // If we have transitioned to or from a state with 0 non-empty devices, // notify the main thread to update system state. if ((non_empty_device_count == 0) != (new_non_empty_dev_count == 0)) cras_non_empty_audio_send_msg( new_non_empty_dev_count > 0 ? 1 : 0); non_empty_device_count = new_non_empty_dev_count; } /* Asks any stream with room for more data. Sets the time stamp for all streams. * Args: * adev - The output device streams are attached to. * Returns: * 0 on success, negative error on failure. If failed, can assume that all * streams have been removed from the device. */ static int fetch_streams(struct open_dev *adev) { struct dev_stream *dev_stream; struct cras_iodev *odev = adev->dev; int rc; int delay; delay = cras_iodev_delay_frames(odev); if (delay < 0) return delay; DL_FOREACH(adev->dev->streams, dev_stream) { struct cras_rstream *rstream = dev_stream->stream; struct cras_audio_shm *shm = cras_rstream_output_shm(rstream); const struct timespec *next_cb_ts; struct timespec now; clock_gettime(CLOCK_MONOTONIC_RAW, &now); if (dev_stream_is_pending_reply(dev_stream)) { dev_stream_flush_old_audio_messages(dev_stream); cras_rstream_record_fetch_interval(dev_stream->stream, &now); } if (cras_shm_get_frames(shm) < 0) cras_rstream_set_is_draining(rstream, 1); if (cras_rstream_get_is_draining(dev_stream->stream)) continue; next_cb_ts = dev_stream_next_cb_ts(dev_stream); if (!next_cb_ts) continue; /* Check if it's time to get more data from this stream. * Allow for waking up a little early. */ add_timespecs(&now, &playback_wake_fuzz_ts); if (!timespec_after(&now, next_cb_ts)) continue; if (!dev_stream_can_fetch(dev_stream)) { ATLOG(atlog, AUDIO_THREAD_STREAM_SKIP_CB, cras_rstream_id(rstream), shm->area->write_offset[0], shm->area->write_offset[1]); continue; } dev_stream_set_delay(dev_stream, delay); ATLOG(atlog, AUDIO_THREAD_FETCH_STREAM, rstream->stream_id, cras_rstream_get_cb_threshold(rstream), delay); rc = dev_stream_request_playback_samples(dev_stream, &now); if (rc < 0) { syslog(LOG_ERR, "fetch err: %d for %x", rc, cras_rstream_id(rstream)); cras_rstream_set_is_draining(rstream, 1); } } return 0; } /* Gets the max delay frames of open input devices. */ static int input_delay_frames(struct open_dev *adevs) { struct open_dev *adev; int delay; int max_delay = 0; DL_FOREACH(adevs, adev) { if (!cras_iodev_is_open(adev->dev)) continue; delay = cras_iodev_delay_frames(adev->dev); if (delay < 0) return delay; if (delay > max_delay) max_delay = delay; } return max_delay; } /* Sets the stream delay. * Args: * adev[in] - The device to capture from. */ static unsigned int set_stream_delay(struct open_dev *adev) { struct dev_stream *stream; int delay; /* TODO(dgreid) - Setting delay from last dev only. */ delay = input_delay_frames(adev); DL_FOREACH(adev->dev->streams, stream) { if (stream->stream->flags & TRIGGER_ONLY) continue; dev_stream_set_delay(stream, delay); } return 0; } /* Gets the minimum amount of space available for writing across all streams. * Args: * adev[in] - The device to capture from. * write_limit[in] - Initial limit to number of frames to capture. * limit_stream[out] - The pointer to the pointer of stream which * causes capture limit. * Output NULL if there is no stream that causes * capture limit less than the initial limit. */ static unsigned int get_stream_limit( struct open_dev *adev, unsigned int write_limit, struct dev_stream **limit_stream) { struct cras_rstream *rstream; struct cras_audio_shm *shm; struct dev_stream *stream; unsigned int avail; *limit_stream = NULL; DL_FOREACH(adev->dev->streams, stream) { rstream = stream->stream; if (rstream->flags & TRIGGER_ONLY) continue; shm = cras_rstream_input_shm(rstream); if (cras_shm_check_write_overrun(shm)) ATLOG(atlog, AUDIO_THREAD_READ_OVERRUN, adev->dev->info.idx, rstream->stream_id, shm->area->num_overruns); avail = dev_stream_capture_avail(stream); if (avail < write_limit) { write_limit = avail; *limit_stream = stream; } } return write_limit; } /* * The minimum wake time for a input device, which is 5ms. It's only used by * function get_input_dev_max_wake_ts. */ static const struct timespec min_input_dev_wake_ts = { 0, 5 * 1000 * 1000 /* 5 ms. */ }; /* * Get input device maximum sleep time, which is the approximate time that the * device will have hw_level = buffer_size / 2 samples. Some devices have * capture period = 2 so the audio_thread should wake up and consume some * samples from hardware at that time. To prevent busy loop occurs, the returned * sleep time should be >= 5ms. * * Returns: 0 on success negative error on device failure. */ static int get_input_dev_max_wake_ts( struct open_dev *adev, unsigned int curr_level, struct timespec *res_ts) { struct timespec dev_wake_ts, now; unsigned int dev_rate, half_buffer_size, target_frames; if(!adev || !adev->dev || !adev->dev->format || !adev->dev->format->frame_rate || !adev->dev->buffer_size) return -EINVAL; *res_ts = min_input_dev_wake_ts; dev_rate = adev->dev->format->frame_rate; half_buffer_size = adev->dev->buffer_size / 2; if(curr_level < half_buffer_size) target_frames = half_buffer_size - curr_level; else target_frames = 0; cras_frames_to_time(target_frames, dev_rate, &dev_wake_ts); if (timespec_after(&dev_wake_ts, res_ts)) { *res_ts = dev_wake_ts; } clock_gettime(CLOCK_MONOTONIC_RAW, &now); add_timespecs(res_ts, &now); return 0; } /* * Set wake_ts for this device to be the earliest wake up time for * dev_streams. */ static int set_input_dev_wake_ts(struct open_dev *adev) { int rc; struct timespec level_tstamp, wake_time_out, min_ts, now, dev_wake_ts; unsigned int curr_level, cap_limit; struct dev_stream *stream; struct dev_stream *cap_limit_stream; /* Limit the sleep time to 20 seconds. */ min_ts.tv_sec = 20; min_ts.tv_nsec = 0; clock_gettime(CLOCK_MONOTONIC_RAW, &now); add_timespecs(&min_ts, &now); rc = cras_iodev_frames_queued(adev->dev, &level_tstamp); if (rc < 0) return rc; curr_level = rc; if (!timespec_is_nonzero(&level_tstamp)) clock_gettime(CLOCK_MONOTONIC_RAW, &level_tstamp); cap_limit = get_stream_limit(adev, UINT_MAX, &cap_limit_stream); /* * Loop through streams to find the earliest time audio thread * should wake up. */ DL_FOREACH(adev->dev->streams, stream) { wake_time_out = min_ts; rc = dev_stream_wake_time( stream, curr_level, &level_tstamp, cap_limit, cap_limit_stream == stream, &wake_time_out); /* * rc > 0 means there is no need to set wake up time for this * stream. */ if (rc > 0) continue; if (rc < 0) return rc; if (timespec_after(&min_ts, &wake_time_out)) { min_ts = wake_time_out; } } if(adev->dev->active_node && adev->dev->active_node->type != CRAS_NODE_TYPE_HOTWORD) { rc = get_input_dev_max_wake_ts(adev, curr_level, &dev_wake_ts); if(rc < 0) { syslog(LOG_ERR, "Failed to call get_input_dev_max_wake_ts." "rc = %d", rc); } else if(timespec_after(&min_ts, &dev_wake_ts)) { min_ts = dev_wake_ts; } } adev->wake_ts = min_ts; return rc; } /* Read samples from an input device to the specified stream. * Args: * adev - The device to capture samples from. * Returns 0 on success. */ static int capture_to_streams(struct open_dev *adev) { struct cras_iodev *idev = adev->dev; snd_pcm_uframes_t remainder, hw_level, cap_limit; struct timespec hw_tstamp; int rc; struct dev_stream *cap_limit_stream; struct dev_stream *stream; DL_FOREACH(adev->dev->streams, stream) dev_stream_flush_old_audio_messages(stream); rc = cras_iodev_frames_queued(idev, &hw_tstamp); if (rc < 0) return rc; hw_level = rc; cras_iodev_update_highest_hw_level(idev, hw_level); ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_TSTAMP, idev->info.idx, hw_tstamp.tv_sec, hw_tstamp.tv_nsec); if (timespec_is_nonzero(&hw_tstamp)) { if (hw_level < idev->min_cb_level / 2) adev->coarse_rate_adjust = 1; else if (hw_level > idev->max_cb_level * 2) adev->coarse_rate_adjust = -1; else adev->coarse_rate_adjust = 0; if (cras_iodev_update_rate(idev, hw_level, &hw_tstamp)) update_estimated_rate(adev); } cap_limit = get_stream_limit(adev, hw_level, &cap_limit_stream); set_stream_delay(adev); remainder = MIN(hw_level, cap_limit); ATLOG(atlog, AUDIO_THREAD_READ_AUDIO, idev->info.idx, hw_level, remainder); if (cras_iodev_state(idev) != CRAS_IODEV_STATE_NORMAL_RUN) return 0; while (remainder > 0) { struct cras_audio_area *area = NULL; unsigned int nread, total_read; nread = remainder; rc = cras_iodev_get_input_buffer(idev, &nread); if (rc < 0 || nread == 0) return rc; DL_FOREACH(adev->dev->streams, stream) { unsigned int this_read; unsigned int area_offset; float software_gain_scaler; if ((stream->stream->flags & TRIGGER_ONLY) && stream->stream->triggered) continue; input_data_get_for_stream(idev->input_data, stream->stream, idev->buf_state, &area, &area_offset); /* * APM has more advanced gain control mechanism, so * don't apply the CRAS software gain to this stream * if APM is used. */ software_gain_scaler = stream->stream->apm_list ? 1.0f : cras_iodev_get_software_gain_scaler(idev); this_read = dev_stream_capture( stream, area, area_offset, software_gain_scaler); input_data_put_for_stream(idev->input_data, stream->stream, idev->buf_state, this_read); } rc = cras_iodev_put_input_buffer(idev); if (rc < 0) return rc; total_read = rc; remainder -= nread; if (total_read < nread) break; } ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE, remainder, 0, 0); return 0; } /* Fill the buffer with samples from the attached streams. * Args: * odevs - The list of open output devices, provided so streams can be * removed from all devices on error. * adev - The device to write to. * dst - The buffer to put the samples in (returned from snd_pcm_mmap_begin) * write_limit - The maximum number of frames to write to dst. * * Returns: * The number of frames rendered on success, a negative error code otherwise. * This number of frames is the minimum of the amount of frames each stream * could provide which is the maximum that can currently be rendered. */ static int write_streams(struct open_dev **odevs, struct open_dev *adev, uint8_t *dst, size_t write_limit) { struct cras_iodev *odev = adev->dev; struct dev_stream *curr; unsigned int max_offset = 0; unsigned int frame_bytes = cras_get_format_bytes(odev->ext_format); unsigned int num_playing = 0; unsigned int drain_limit = write_limit; /* Mix as much as we can, the minimum fill level of any stream. */ max_offset = cras_iodev_max_stream_offset(odev); /* Mix as much as we can, the minimum fill level of any stream. */ DL_FOREACH(adev->dev->streams, curr) { int dev_frames; /* If this is a single output dev stream, updates the latest * number of frames for playback. */ if (dev_stream_attached_devs(curr) == 1) dev_stream_update_frames(curr); dev_frames = dev_stream_playback_frames(curr); if (dev_frames < 0) { dev_io_remove_stream( odevs, curr->stream, NULL); continue; } ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_STREAM, curr->stream->stream_id, dev_frames, dev_stream_is_pending_reply(curr)); if (cras_rstream_get_is_draining(curr->stream)) { drain_limit = MIN((size_t)dev_frames, drain_limit); if (!dev_frames) dev_io_remove_stream( odevs, curr->stream, NULL); } else { write_limit = MIN((size_t)dev_frames, write_limit); num_playing++; } } if (!num_playing) write_limit = drain_limit; if (write_limit > max_offset) memset(dst + max_offset * frame_bytes, 0, (write_limit - max_offset) * frame_bytes); ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_MIX, write_limit, max_offset, 0); DL_FOREACH(adev->dev->streams, curr) { unsigned int offset; int nwritten; offset = cras_iodev_stream_offset(odev, curr); if (offset >= write_limit) continue; nwritten = dev_stream_mix(curr, odev->ext_format, dst + frame_bytes * offset, write_limit - offset); if (nwritten < 0) { dev_io_remove_stream(odevs, curr->stream, NULL); continue; } cras_iodev_stream_written(odev, curr, nwritten); } write_limit = cras_iodev_all_streams_written(odev); ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_MIXED, write_limit, 0, 0); return write_limit; } /* Update next wake up time of the device. * Args: * adev[in] - The device to update to. * hw_level[out] - Pointer to number of frames in hardware. */ void update_dev_wakeup_time(struct open_dev *adev, unsigned int *hw_level) { struct timespec now; struct timespec sleep_time; double est_rate; unsigned int frames_to_play_in_sleep; clock_gettime(CLOCK_MONOTONIC_RAW, &now); frames_to_play_in_sleep = cras_iodev_frames_to_play_in_sleep( adev->dev, hw_level, &adev->wake_ts); if (!timespec_is_nonzero(&adev->wake_ts)) adev->wake_ts = now; if (cras_iodev_state(adev->dev) == CRAS_IODEV_STATE_NORMAL_RUN) cras_iodev_update_highest_hw_level(adev->dev, *hw_level); est_rate = adev->dev->ext_format->frame_rate * cras_iodev_get_est_rate_ratio(adev->dev); ATLOG(atlog, AUDIO_THREAD_SET_DEV_WAKE, adev->dev->info.idx, *hw_level, frames_to_play_in_sleep); cras_frames_to_time_precise( frames_to_play_in_sleep, est_rate, &sleep_time); add_timespecs(&adev->wake_ts, &sleep_time); ATLOG(atlog, AUDIO_THREAD_DEV_SLEEP_TIME, adev->dev->info.idx, adev->wake_ts.tv_sec, adev->wake_ts.tv_nsec); } /* Returns 0 on success negative error on device failure. */ int write_output_samples(struct open_dev **odevs, struct open_dev *adev, struct cras_fmt_conv *output_converter) { struct cras_iodev *odev = adev->dev; unsigned int hw_level; struct timespec hw_tstamp; unsigned int frames, fr_to_req; snd_pcm_sframes_t written; snd_pcm_uframes_t total_written = 0; int rc; int non_empty = 0; int *non_empty_ptr = NULL; uint8_t *dst = NULL; struct cras_audio_area *area = NULL; /* Possibly fill zeros for no_stream state and possibly transit state. */ rc = cras_iodev_prepare_output_before_write_samples(odev); if (rc < 0) { syslog(LOG_ERR, "Failed to prepare output dev for write"); return rc; } if (cras_iodev_state(odev) != CRAS_IODEV_STATE_NORMAL_RUN) return 0; rc = cras_iodev_frames_queued(odev, &hw_tstamp); if (rc < 0) return rc; hw_level = rc; ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_TSTAMP, adev->dev->info.idx, hw_tstamp.tv_sec, hw_tstamp.tv_nsec); if (timespec_is_nonzero(&hw_tstamp)) { if (hw_level < odev->min_cb_level / 2) adev->coarse_rate_adjust = 1; else if (hw_level > odev->max_cb_level * 2) adev->coarse_rate_adjust = -1; else adev->coarse_rate_adjust = 0; if (cras_iodev_update_rate(odev, hw_level, &hw_tstamp)) update_estimated_rate(adev); } ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO, adev->dev->info.idx, hw_level, 0); /* Don't request more than hardware can hold. Note that min_buffer_level * has been subtracted from the actual hw_level so we need to take it * into account here. */ fr_to_req = cras_iodev_buffer_avail(odev, hw_level); /* Have to loop writing to the device, will be at most 2 loops, this * only happens when the circular buffer is at the end and returns us a * partial area to write to from mmap_begin */ while (total_written < fr_to_req) { frames = fr_to_req - total_written; rc = cras_iodev_get_output_buffer(odev, &area, &frames); if (rc < 0) return rc; /* TODO(dgreid) - This assumes interleaved audio. */ dst = area->channels[0].buf; written = write_streams(odevs, adev, dst, frames); if (written < 0) /* pcm has been closed */ return (int)written; if (written < (snd_pcm_sframes_t)frames) /* Got all the samples from client that we can, but it * won't fill the request. */ fr_to_req = 0; /* break out after committing samples */ // This interval is lazily initialized once per device. // Note that newly opened devices are considered non-empty // (until their status is updated through the normal flow). if (!adev->non_empty_check_pi) { adev->non_empty_check_pi = pic_polled_interval_create( NON_EMPTY_UPDATE_INTERVAL_SEC); } // If we were empty last iteration, or the sampling interval // has elapsed, check for emptiness. if (adev->empty_pi || pic_interval_elapsed(adev->non_empty_check_pi)) { non_empty_ptr = &non_empty; pic_interval_reset(adev->non_empty_check_pi); } rc = cras_iodev_put_output_buffer(odev, dst, written, non_empty_ptr, output_converter); if (rc < 0) return rc; total_written += written; if (non_empty && adev->empty_pi) { // We're not empty, but we were previously. // Reset the empty period. pic_polled_interval_destroy(&adev->empty_pi); } if (non_empty_ptr && !non_empty && !adev->empty_pi) // We checked for emptiness, we were empty, and we // previously weren't. Start the empty period. adev->empty_pi = pic_polled_interval_create( MIN_EMPTY_PERIOD_SEC); } ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_DONE, hw_level, total_written, odev->min_cb_level); return total_written; } /* * Public funcitons. */ int dev_io_send_captured_samples(struct open_dev *idev_list) { struct open_dev *adev; int rc; // TODO(dgreid) - once per rstream, not once per dev_stream. DL_FOREACH(idev_list, adev) { struct dev_stream *stream; if (!cras_iodev_is_open(adev->dev)) continue; /* Post samples to rstream if there are enough samples. */ DL_FOREACH(adev->dev->streams, stream) { dev_stream_capture_update_rstream(stream); } /* Set wake_ts for this device. */ rc = set_input_dev_wake_ts(adev); if (rc < 0) return rc; } return 0; } static void handle_dev_err( int err_rc, struct open_dev **odevs, struct open_dev *adev) { if (err_rc == -EPIPE) { /* Handle severe underrun. */ ATLOG(atlog, AUDIO_THREAD_SEVERE_UNDERRUN, adev->dev->info.idx, 0, 0); cras_iodev_reset_request(adev->dev); } else { /* Device error, close it. */ dev_io_rm_open_dev(odevs, adev); } } int dev_io_capture(struct open_dev **list) { struct open_dev *idev_list = *list; struct open_dev *adev; int rc; DL_FOREACH(idev_list, adev) { if (!cras_iodev_is_open(adev->dev)) continue; rc = capture_to_streams(adev); if (rc < 0) handle_dev_err(rc, list, adev); } return 0; } void dev_io_playback_fetch(struct open_dev *odev_list) { struct open_dev *adev; DL_FOREACH(odev_list, adev) { if (!cras_iodev_is_open(adev->dev)) continue; fetch_streams(adev); } } int dev_io_playback_write(struct open_dev **odevs, struct cras_fmt_conv *output_converter) { struct open_dev *adev; struct dev_stream *curr; int rc; unsigned int hw_level, total_written; /* For multiple output case, update the number of queued frames in shm * of all streams before starting write output samples. */ adev = *odevs; if (adev && adev->next) { DL_FOREACH(*odevs, adev) { DL_FOREACH(adev->dev->streams, curr) dev_stream_update_frames(curr); } } DL_FOREACH(*odevs, adev) { if (!cras_iodev_is_open(adev->dev)) continue; rc = write_output_samples(odevs, adev, output_converter); if (rc < 0) { handle_dev_err(rc, odevs, adev); } else { total_written = rc; /* * Skip the underrun check and device wake up time update if * device should not wake up. */ if (!cras_iodev_odev_should_wake(adev->dev)) continue; /* * Update device wake up time and get the new hardware * level. */ update_dev_wakeup_time(adev, &hw_level); /* * If new hardware level is less than or equal to the * written frames, we can suppose underrun happened. But * keep in mind there may have a false positive. If * hardware level changed just after frames being * written, we may get hw_level <= total_written here * without underrun happened. However, we can still * treat it as underrun because it is an abnormal state * we should handle it. */ if (hw_level <= total_written) { ATLOG(atlog, AUDIO_THREAD_UNDERRUN, adev->dev->info.idx, hw_level, total_written); rc = cras_iodev_output_underrun(adev->dev); if(rc < 0) { handle_dev_err(rc, odevs, adev); } else { update_dev_wakeup_time(adev, &hw_level); } } } } /* TODO(dgreid) - once per rstream, not once per dev_stream. */ DL_FOREACH(*odevs, adev) { struct dev_stream *stream; if (!cras_iodev_is_open(adev->dev)) continue; DL_FOREACH(adev->dev->streams, stream) { dev_stream_playback_update_rstream(stream); } } return 0; } void dev_io_run(struct open_dev **odevs, struct open_dev **idevs, struct cras_fmt_conv *output_converter) { pic_update_current_time(); dev_io_playback_fetch(*odevs); dev_io_capture(idevs); dev_io_send_captured_samples(*idevs); dev_io_playback_write(odevs, output_converter); check_non_empty_state_transition(*odevs); } static int input_adev_ignore_wake(const struct open_dev *adev) { if (!cras_iodev_is_open(adev->dev)) return 1; if (!adev->dev->active_node) return 1; if (adev->dev->active_node->type == CRAS_NODE_TYPE_HOTWORD && !cras_iodev_input_streaming(adev->dev)) return 1; return 0; } int dev_io_next_input_wake(struct open_dev **idevs, struct timespec *min_ts) { struct open_dev *adev; int ret = 0; /* The total number of devices to wait on. */ DL_FOREACH(*idevs, adev) { if (input_adev_ignore_wake(adev)) continue; ret++; ATLOG(atlog, AUDIO_THREAD_DEV_SLEEP_TIME, adev->dev->info.idx, adev->wake_ts.tv_sec, adev->wake_ts.tv_nsec); if (timespec_after(min_ts, &adev->wake_ts)) *min_ts = adev->wake_ts; } return ret; } struct open_dev *dev_io_find_open_dev(struct open_dev *odev_list, const struct cras_iodev *dev) { struct open_dev *odev; DL_FOREACH(odev_list, odev) if (odev->dev == dev) return odev; return NULL; } void dev_io_rm_open_dev(struct open_dev **odev_list, struct open_dev *dev_to_rm) { struct open_dev *odev; struct dev_stream *dev_stream; /* Do nothing if dev_to_rm wasn't already in the active dev list. */ odev = dev_io_find_open_dev(*odev_list, dev_to_rm->dev); if (!odev) return; DL_DELETE(*odev_list, dev_to_rm); /* Metrics logs the number of underruns of this device. */ cras_server_metrics_num_underruns( cras_iodev_get_num_underruns(dev_to_rm->dev)); /* Metrics logs the highest_hw_level of this device. */ cras_server_metrics_highest_hw_level( dev_to_rm->dev->highest_hw_level, dev_to_rm->dev->direction); check_non_empty_state_transition(*odev_list); ATLOG(atlog, AUDIO_THREAD_DEV_REMOVED, dev_to_rm->dev->info.idx, 0, 0); DL_FOREACH(dev_to_rm->dev->streams, dev_stream) { cras_iodev_rm_stream(dev_to_rm->dev, dev_stream->stream); dev_stream_destroy(dev_stream); } if (dev_to_rm->empty_pi) pic_polled_interval_destroy(&dev_to_rm->empty_pi); if (dev_to_rm->non_empty_check_pi) pic_polled_interval_destroy(&dev_to_rm->non_empty_check_pi); free(dev_to_rm); } static void delete_stream_from_dev(struct cras_iodev *dev, struct cras_rstream *stream) { struct dev_stream *out; out = cras_iodev_rm_stream(dev, stream); if (out) dev_stream_destroy(out); } int dev_io_remove_stream(struct open_dev **dev_list, struct cras_rstream *stream, struct cras_iodev *dev) { struct open_dev *open_dev; struct timespec delay; unsigned fetch_delay_msec; /* Metrics log the longest fetch delay of this stream. */ if (timespec_after(&stream->longest_fetch_interval, &stream->sleep_interval_ts)) { subtract_timespecs(&stream->longest_fetch_interval, &stream->sleep_interval_ts, &delay); fetch_delay_msec = delay.tv_sec * 1000 + delay.tv_nsec / 1000000; if (fetch_delay_msec) cras_server_metrics_longest_fetch_delay( fetch_delay_msec); } ATLOG(atlog, AUDIO_THREAD_STREAM_REMOVED, stream->stream_id, 0, 0); if (dev == NULL) { DL_FOREACH(*dev_list, open_dev) { delete_stream_from_dev(open_dev->dev, stream); } } else { delete_stream_from_dev(dev, stream); } return 0; }