Using Udev Sound Device with ALSA

Inside of a C program, using libudev and libasound, I have determined a struct udev_device *device is part of the sound subsystem with udev_device_get_subsystem(device) == "sound". Now I have obtained the device, I want to obtain more information about it to use with ALSA.

I want to know how to query information about this device, e.g. playback/capture, number of channels, etc. Furthermore, I want to know how to query this device to obtain the parameters to use for a call to ALSA snd_pcm_open()

I can only think of parsing the contents of /proc/asound/*, however this seems very tedious and rigid. Also, as udev supports detecting sound devices, it seems logical to me that it would have (or there would be) some 'clean' mechanism of obtaining more information about it.

Edited by Ryan on Reason: Initial post
To check supported formats/channels/etc you simply open device with snd_pcm_open and then use snd_pcm_hw_params_test_* functions to check what device supports.

Like use snd_pcm_hw_params_test_format to check if they support specific format and snd_pcm_hw_params_test_rate to check if they support specific samplerate, and so on... Iterate over available enums/values to see what card supports.

Here's example that will print out all supported formats:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
snd_pcm_t* pcm;
// ... open pcm device

snd_pcm_hw_params_t* hw;
snd_pcm_hw_params_alloca(&hw);
snd_pcm_hw_params_any(pcm, hw);

for (int i=0; i<SND_PCM_FORMAT_LAST; i++) {
  if (snd_pcm_hw_params_test_format(pcm, hw, (snd_pcm_format_t)i) == 0) {
    printf("%s format supported\n", snd_pcm_format_name((snd_pcm_format_t)i));
  }
}
Thanks for your code. Perhaps I should have clarified. I want to know how I should parse information from udev for ALSA:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  struct udev *udev_obj = udev_new();
  struct udev_enumerate *udev_enum = udev_enumerate_new(udev_obj);
  udev_enumerate_add_match_subsystem(udev_enum, "sound");
  udev_enumerate_scan_devices(udev_enum);

  struct udev_list_entry *udev_entries = udev_enumerate_get_list_entry(udev_enum);
  struct udev_list_entry *udev_entry = NULL;
  udev_list_entry_foreach(udev_entry, udev_entries) {
    printf("%s\n", udev_list_entry_get_name(udev_entry));
  }

On my ThinkPad X1 Carbon with no external sound peripherals, this outputs:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/hwC0D0
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/hwC0D2
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D0c
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D0p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D10p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D3p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D7p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D8p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/pcmC0D9p
/sys/devices/pci0000:00/0000:00:1f.3/sound/card0/controlC0
/sys/devices/virtual/sound/seq
/sys/devices/virtual/sound/timer

I understand that C0 = card 0, D0 = device 0, and c = capture/p = playback. However:
1. What is the difference between card0/hw/pcm/control/seq/timer?
2. Why is there so many odd combinations, e.g card 0, device 3 and then card 0, device 7, etc. It does not seem to logically increment by one
3. Should I just parse these strings to get the arguments for snd_pcm_open? This is definitely doable however feels clunky. E.g, with input devices you can:
1
2
3
  device = udev_device_get_parent_with_subsystem_devtype(device ,"input", NULL);
  char const *mask = udev_device_get_sysattr_value(device, "capabilities/ev");
  // Perform bit-wise operations on strl(mask) to get information about the input device

However, the contents of /dev/snd/pcmC0D0p for example contain no informative files, so I don't think this method will work.

Edited by Ryan on
Sorry, no idea about udev, I've never used it.

I'd assume those pcmC0D*p devices are audio over HDMI/DisplayPort. I think you gan get names for them by doing "cat /proc/asound/card0/pcmC0D0p/info" .
No idea how kernel assigns numbers to devices, you'll probably need to look in kernel source to figure out.

If you have card & device, you can use snd_pcm_open to pass "hw:C,D" format string. That should open correct device.