+ snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
+
+ unsigned num_channels = 0;
+
+ // Find all channel maps for this device, and pick out the one
+ // with the most channels.
+ snd_pcm_chmap_query_t **cmaps = snd_pcm_query_chmaps_from_hw(card_index, dev_index, 0, SND_PCM_STREAM_CAPTURE);
+ if (cmaps != nullptr) {
+ for (snd_pcm_chmap_query_t **ptr = cmaps; *ptr; ++ptr) {
+ num_channels = max(num_channels, (*ptr)->map.channels);
+ }
+ snd_pcm_free_chmaps(cmaps);
+ }
+ if (num_channels == 0) {
+ // Device had no channel maps. We need to open it to query.
+ // TODO: Do this asynchronously.
+ snd_pcm_t *pcm_handle;
+ int err = snd_pcm_open(&pcm_handle, address, SND_PCM_STREAM_CAPTURE, 0);
+ if (err < 0) {
+ printf("%s: %s\n", address, snd_strerror(err));
+ return ALSAPool::ProbeResult::DEFER;
+ }
+ snd_pcm_hw_params_t *hw_params;
+ snd_pcm_hw_params_alloca(&hw_params);
+ unsigned sample_rate;
+ if (!set_base_params(address, pcm_handle, hw_params, &sample_rate)) {
+ snd_pcm_close(pcm_handle);
+ return ALSAPool::ProbeResult::DEFER;
+ }
+ err = snd_pcm_hw_params_get_channels_max(hw_params, &num_channels);
+ if (err < 0) {
+ fprintf(stderr, "[%s] snd_pcm_hw_params_get_channels_max(): %s\n",
+ address, snd_strerror(err));
+ snd_pcm_close(pcm_handle);
+ return ALSAPool::ProbeResult::DEFER;
+ }
+ snd_pcm_close(pcm_handle);
+ }
+
+ if (num_channels == 0) {
+ printf("%s: No channel maps with channels\n", address);
+ return ALSAPool::ProbeResult::FAILURE;
+ }
+
+ snd_ctl_card_info_t *card_info;
+ snd_ctl_card_info_alloca(&card_info);
+ snd_ctl_card_info(ctl, card_info);
+
+ string name = snd_ctl_card_info_get_name(card_info);
+ string info = snd_pcm_info_get_name(pcm_info);
+
+ unsigned internal_dev_index;
+ string display_name;
+ {
+ lock_guard<mutex> lock(mu);
+ internal_dev_index = find_free_device_index(name, info, num_channels, address);
+ devices[internal_dev_index].address = address;
+ devices[internal_dev_index].name = name;
+ devices[internal_dev_index].info = info;
+ devices[internal_dev_index].num_channels = num_channels;
+ // Note: Purposefully does not overwrite held.
+
+ display_name = devices[internal_dev_index].display_name();
+ }
+
+ fprintf(stderr, "%s: Probed successfully.\n", address);
+
+ reset_device(internal_dev_index); // Restarts it if it is held (ie., we just replaced a dead card).
+
+ DeviceSpec spec{InputSourceType::ALSA_INPUT, internal_dev_index};
+ global_audio_mixer->set_display_name(spec, display_name);
+ global_audio_mixer->trigger_state_changed_callback();
+
+ return ALSAPool::ProbeResult::SUCCESS;
+}
+
+void ALSAPool::unplug_device(unsigned card_index, unsigned dev_index)
+{
+ char address[256];
+ snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
+ for (unsigned i = 0; i < devices.size(); ++i) {
+ if (devices[i].state != Device::State::EMPTY &&
+ devices[i].state != Device::State::DEAD &&
+ devices[i].address == address) {
+ free_card(i);
+ }
+ }
+}
+
+void ALSAPool::init()
+{
+ thread(&ALSAPool::inotify_thread_func, this).detach();
+ enumerate_devices();
+}
+
+void ALSAPool::inotify_thread_func()
+{
+ int inotify_fd = inotify_init();
+ if (inotify_fd == -1) {
+ perror("inotify_init()");
+ fprintf(stderr, "No hotplug of ALSA devices available.\n");
+ return;
+ }
+
+ int watch_fd = inotify_add_watch(inotify_fd, "/dev/snd", IN_MOVE | IN_CREATE | IN_DELETE);
+ if (watch_fd == -1) {
+ perror("inotify_add_watch()");
+ fprintf(stderr, "No hotplug of ALSA devices available.\n");
+ close(inotify_fd);
+ return;
+ }
+
+ int size = sizeof(inotify_event) + NAME_MAX + 1;
+ unique_ptr<char[]> buf(new char[size]);
+ for ( ;; ) {
+ int ret = read(inotify_fd, buf.get(), size);
+ if (ret < int(sizeof(inotify_event))) {
+ fprintf(stderr, "inotify read unexpectedly returned %d, giving up hotplug of ALSA devices.\n",
+ int(ret));
+ close(watch_fd);
+ close(inotify_fd);
+ return;
+ }
+
+ for (int i = 0; i < ret; ) {
+ const inotify_event *event = reinterpret_cast<const inotify_event *>(&buf[i]);
+ i += sizeof(inotify_event) + event->len;
+
+ if (event->mask & IN_Q_OVERFLOW) {
+ fprintf(stderr, "WARNING: inotify overflowed, may lose ALSA hotplug events.\n");