[PATCH 1/4] winealsa: Move get_prop_value to the unixlib.

Andrew Eikum aeikum at codeweavers.com
Mon Mar 14 08:45:02 CDT 2022


Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>

On Mon, Mar 14, 2022 at 10:21:46AM +0000, Huw Davies wrote:
> Signed-off-by: Huw Davies <huw at codeweavers.com>
> ---
>  dlls/winealsa.drv/alsa.c     | 181 ++++++++++++++++++++++++++++++++++-
>  dlls/winealsa.drv/mmdevdrv.c | 177 ++++------------------------------
>  dlls/winealsa.drv/unixlib.h  |  13 +++
>  3 files changed, 213 insertions(+), 158 deletions(-)
> 
> diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c
> index c199a2e603d..085066622c0 100644
> --- a/dlls/winealsa.drv/alsa.c
> +++ b/dlls/winealsa.drv/alsa.c
> @@ -34,13 +34,13 @@
>  #include "windef.h"
>  #include "winbase.h"
>  #include "winternl.h"
> +#include "initguid.h"
>  #include "mmdeviceapi.h"
>  
>  #include "wine/debug.h"
>  #include "wine/list.h"
>  #include "wine/unixlib.h"
>  
> -#include "initguid.h"
>  #include "unixlib.h"
>  
>  WINE_DEFAULT_DEBUG_CHANNEL(alsa);
> @@ -2241,6 +2241,184 @@ static NTSTATUS is_started(void *args)
>      return alsa_unlock_result(stream, &params->result, stream->started ? S_OK : S_FALSE);
>  }
>  
> +static unsigned int alsa_probe_num_speakers(char *name)
> +{
> +    snd_pcm_t *handle;
> +    snd_pcm_hw_params_t *params;
> +    int err;
> +    unsigned int max_channels = 0;
> +
> +    if ((err = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
> +        WARN("The device \"%s\" failed to open: %d (%s).\n",
> +                name, err, snd_strerror(err));
> +        return 0;
> +    }
> +
> +    params = malloc(snd_pcm_hw_params_sizeof());
> +    if (!params) {
> +        WARN("Out of memory.\n");
> +        snd_pcm_close(handle);
> +        return 0;
> +    }
> +
> +    if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
> +        WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
> +                name, err, snd_strerror(err));
> +        goto exit;
> +    }
> +
> +    if ((err = snd_pcm_hw_params_get_channels_max(params,
> +                    &max_channels)) < 0){
> +        WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
> +        goto exit;
> +    }
> +
> +exit:
> +    free(params);
> +    snd_pcm_close(handle);
> +
> +    return max_channels;
> +}
> +
> +enum AudioDeviceConnectionType {
> +    AudioDeviceConnectionType_Unknown = 0,
> +    AudioDeviceConnectionType_PCI,
> +    AudioDeviceConnectionType_USB
> +};
> +
> +static NTSTATUS get_prop_value(void *args)
> +{
> +    struct get_prop_value_params *params = args;
> +    const char *name = params->alsa_name;
> +    EDataFlow flow = params->flow;
> +    const GUID *guid = params->guid;
> +    const PROPERTYKEY *prop = params->prop;
> +    PROPVARIANT *out = params->value;
> +    static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
> +        {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
> +    };
> +
> +    if(IsEqualPropertyKey(*prop, devicepath_key))
> +    {
> +        char uevent[MAX_PATH];
> +        FILE *fuevent;
> +        int card, device;
> +
> +        /* only implemented for identifiable devices, i.e. not "default" */
> +        if(!sscanf(name, "plughw:%u,%u", &card, &device)){
> +            params->result = E_NOTIMPL;
> +            return STATUS_SUCCESS;
> +        }
> +        sprintf(uevent, "/sys/class/sound/card%u/device/uevent", card);
> +        fuevent = fopen(uevent, "r");
> +
> +        if(fuevent){
> +            enum AudioDeviceConnectionType connection = AudioDeviceConnectionType_Unknown;
> +            USHORT vendor_id = 0, product_id = 0;
> +            char line[256];
> +
> +            while (fgets(line, sizeof(line), fuevent)) {
> +                char *val;
> +                size_t val_len;
> +
> +                if((val = strchr(line, '='))) {
> +                    val[0] = 0;
> +                    val++;
> +
> +                    val_len = strlen(val);
> +                    if(val_len > 0 && val[val_len - 1] == '\n') { val[val_len - 1] = 0; }
> +
> +                    if(!strcmp(line, "PCI_ID")){
> +                        connection = AudioDeviceConnectionType_PCI;
> +                        if(sscanf(val, "%hX:%hX", &vendor_id, &product_id)<2){
> +                            WARN("Unexpected input when reading PCI_ID in uevent file.\n");
> +                            connection = AudioDeviceConnectionType_Unknown;
> +                            break;
> +                        }
> +                    }else if(!strcmp(line, "DEVTYPE") && !strcmp(val,"usb_interface"))
> +                        connection = AudioDeviceConnectionType_USB;
> +                    else if(!strcmp(line, "PRODUCT"))
> +                        if(sscanf(val, "%hx/%hx/", &vendor_id, &product_id)<2){
> +                            WARN("Unexpected input when reading PRODUCT in uevent file.\n");
> +                            connection = AudioDeviceConnectionType_Unknown;
> +                            break;
> +                        }
> +                }
> +            }
> +
> +            fclose(fuevent);
> +
> +            if(connection == AudioDeviceConnectionType_USB || connection == AudioDeviceConnectionType_PCI){
> +                UINT serial_number;
> +                char buf[128];
> +                int len;
> +
> +                /* As hardly any audio devices have serial numbers, Windows instead
> +                appears to use a persistent random number. We emulate this here
> +                by instead using the last 8 hex digits of the GUID. */
> +                serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7];
> +
> +                if(connection == AudioDeviceConnectionType_USB)
> +                    sprintf(buf, "{1}.USB\\VID_%04X&PID_%04X\\%u&%08X",
> +                            vendor_id, product_id, device, serial_number);
> +                else /* AudioDeviceConnectionType_PCI */
> +                    sprintf(buf, "{1}.HDAUDIO\\FUNC_01&VEN_%04X&DEV_%04X\\%u&%08X",
> +                            vendor_id, product_id, device, serial_number);
> +
> +                len = strlen(buf) + 1;
> +                if(*params->buffer_size < len * sizeof(WCHAR)){
> +                    params->result = E_NOT_SUFFICIENT_BUFFER;
> +                    *params->buffer_size = len * sizeof(WCHAR);
> +                    return STATUS_SUCCESS;
> +                }
> +                out->vt = VT_LPWSTR;
> +                out->pwszVal = params->buffer;
> +                ntdll_umbstowcs(buf, len, out->pwszVal, len);
> +                params->result = S_OK;
> +                return STATUS_SUCCESS;
> +            }
> +        }else{
> +            WARN("Could not open %s for reading\n", uevent);
> +            params->result = E_NOTIMPL;
> +            return STATUS_SUCCESS;
> +        }
> +    } else if (flow != eCapture && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
> +        unsigned int num_speakers, card, device;
> +        char hwname[255];
> +
> +        if (sscanf(name, "plughw:%u,%u", &card, &device))
> +            sprintf(hwname, "hw:%u,%u", card, device); /* must be hw rather than plughw to work */
> +        else
> +            strcpy(hwname, name);
> +
> +        num_speakers = alsa_probe_num_speakers(hwname);
> +        if (num_speakers == 0){
> +            params->result = E_FAIL;
> +            return STATUS_SUCCESS;
> +        }
> +        out->vt = VT_UI4;
> +
> +        if (num_speakers > 6)
> +            out->ulVal = KSAUDIO_SPEAKER_STEREO;
> +        else if (num_speakers == 6)
> +            out->ulVal = KSAUDIO_SPEAKER_5POINT1;
> +        else if (num_speakers >= 4)
> +            out->ulVal = KSAUDIO_SPEAKER_QUAD;
> +        else if (num_speakers >= 2)
> +            out->ulVal = KSAUDIO_SPEAKER_STEREO;
> +        else if (num_speakers == 1)
> +            out->ulVal = KSAUDIO_SPEAKER_MONO;
> +
> +        params->result = S_OK;
> +        return STATUS_SUCCESS;
> +    }
> +
> +    TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid);
> +
> +    params->result = E_NOTIMPL;
> +    return STATUS_SUCCESS;
> +}
> +
>  unixlib_entry_t __wine_unix_call_funcs[] =
>  {
>      get_endpoint_ids,
> @@ -2265,4 +2443,5 @@ unixlib_entry_t __wine_unix_call_funcs[] =
>      set_volumes,
>      set_event_handle,
>      is_started,
> +    get_prop_value,
>  };
> diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
> index c2ec3dd54af..389c40b47de 100644
> --- a/dlls/winealsa.drv/mmdevdrv.c
> +++ b/dlls/winealsa.drv/mmdevdrv.c
> @@ -49,8 +49,6 @@
>  #include "audioclient.h"
>  #include "audiopolicy.h"
>  
> -#include <alsa/asoundlib.h>
> -
>  #include "unixlib.h"
>  
>  WINE_DEFAULT_DEBUG_CHANNEL(alsa);
> @@ -2444,58 +2442,12 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
>      return S_OK;
>  }
>  
> -static unsigned int alsa_probe_num_speakers(char *name) {
> -    snd_pcm_t *handle;
> -    snd_pcm_hw_params_t *params;
> -    int err;
> -    unsigned int max_channels = 0;
> -
> -    if ((err = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
> -        WARN("The device \"%s\" failed to open: %d (%s).\n",
> -                name, err, snd_strerror(err));
> -        return 0;
> -    }
> -
> -    params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_hw_params_sizeof());
> -    if (!params) {
> -        WARN("Out of memory.\n");
> -        snd_pcm_close(handle);
> -        return 0;
> -    }
> -
> -    if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
> -        WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
> -                name, err, snd_strerror(err));
> -        goto exit;
> -    }
> -
> -    if ((err = snd_pcm_hw_params_get_channels_max(params,
> -                    &max_channels)) < 0){
> -        WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
> -        goto exit;
> -    }
> -
> -exit:
> -    HeapFree(GetProcessHeap(), 0, params);
> -    snd_pcm_close(handle);
> -
> -    return max_channels;
> -}
> -
> -enum AudioDeviceConnectionType {
> -    AudioDeviceConnectionType_Unknown = 0,
> -    AudioDeviceConnectionType_PCI,
> -    AudioDeviceConnectionType_USB
> -};
> -
>  HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
>  {
> +    struct get_prop_value_params params;
>      char name[256];
>      EDataFlow flow;
> -
> -    static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
> -        {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
> -    };
> +    unsigned int size = 0;
>  
>      TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
>  
> @@ -2505,116 +2457,27 @@ HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARI
>          return E_NOINTERFACE;
>      }
>  
> -    if(IsEqualPropertyKey(*prop, devicepath_key))
> -    {
> -        char uevent[MAX_PATH];
> -        FILE *fuevent;
> -        int card, device;
> -
> -        /* only implemented for identifiable devices, i.e. not "default" */
> -        if(!sscanf(name, "plughw:%u,%u", &card, &device))
> -            return E_NOTIMPL;
> -
> -        sprintf(uevent, "/sys/class/sound/card%u/device/uevent", card);
> -        fuevent = fopen(uevent, "r");
> -
> -        if(fuevent){
> -            enum AudioDeviceConnectionType connection = AudioDeviceConnectionType_Unknown;
> -            USHORT vendor_id = 0, product_id = 0;
> -            char line[256];
> -
> -            while (fgets(line, sizeof(line), fuevent)) {
> -                char *val;
> -                size_t val_len;
> -
> -                if((val = strchr(line, '='))) {
> -                    val[0] = 0;
> -                    val++;
> -
> -                    val_len = strlen(val);
> -                    if(val_len > 0 && val[val_len - 1] == '\n') { val[val_len - 1] = 0; }
> -
> -                    if(!strcmp(line, "PCI_ID")){
> -                        connection = AudioDeviceConnectionType_PCI;
> -                        if(sscanf(val, "%hX:%hX", &vendor_id, &product_id)<2){
> -                            WARN("Unexpected input when reading PCI_ID in uevent file.\n");
> -                            connection = AudioDeviceConnectionType_Unknown;
> -                            break;
> -                        }
> -                    }else if(!strcmp(line, "DEVTYPE") && !strcmp(val,"usb_interface"))
> -                        connection = AudioDeviceConnectionType_USB;
> -                    else if(!strcmp(line, "PRODUCT"))
> -                        if(sscanf(val, "%hx/%hx/", &vendor_id, &product_id)<2){
> -                            WARN("Unexpected input when reading PRODUCT in uevent file.\n");
> -                            connection = AudioDeviceConnectionType_Unknown;
> -                            break;
> -                        }
> -                }
> -            }
> -
> -            fclose(fuevent);
> -
> -            if(connection == AudioDeviceConnectionType_USB || connection == AudioDeviceConnectionType_PCI){
> -                static const WCHAR usbformatW[] = { '{','1','}','.','U','S','B','\\','V','I','D','_',
> -                    '%','0','4','X','&','P','I','D','_','%','0','4','X','\\',
> -                    '%','u','&','%','0','8','X',0 }; /* "{1}.USB\VID_%04X&PID_%04X\%u&%08X" */
> -                static const WCHAR pciformatW[] = { '{','1','}','.','H','D','A','U','D','I','O','\\','F','U','N','C','_','0','1','&',
> -                    'V','E','N','_','%','0','4','X','&','D','E','V','_',
> -                    '%','0','4','X','\\','%','u','&','%','0','8','X',0 }; /* "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X" */
> -                UINT serial_number;
> -
> -                /* As hardly any audio devices have serial numbers, Windows instead
> -                appears to use a persistent random number. We emulate this here
> -                by instead using the last 8 hex digits of the GUID. */
> -                serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7];
> -
> -                out->vt = VT_LPWSTR;
> -                out->pwszVal = CoTaskMemAlloc(128 * sizeof(WCHAR));
> -
> -                if(!out->pwszVal)
> -                    return E_OUTOFMEMORY;
> -
> -                if(connection == AudioDeviceConnectionType_USB)
> -                    sprintfW( out->pwszVal, usbformatW, vendor_id, product_id, device, serial_number);
> -                else if(connection == AudioDeviceConnectionType_PCI)
> -                    sprintfW( out->pwszVal, pciformatW, vendor_id, product_id, device, serial_number);
> -
> -                return S_OK;
> -            }
> -        }else{
> -            WARN("Could not open %s for reading\n", uevent);
> -            return E_NOTIMPL;
> -        }
> -    } else if (flow != eCapture && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
> -        unsigned int num_speakers, card, device;
> -        char hwname[255];
> -
> -        if (sscanf(name, "plughw:%u,%u", &card, &device))
> -            sprintf(hwname, "hw:%u,%u", card, device); /* must be hw rather than plughw to work */
> -        else
> -            strcpy(hwname, name);
> -
> -        num_speakers = alsa_probe_num_speakers(hwname);
> -        if (num_speakers == 0)
> -            return E_FAIL;
> +    params.alsa_name = name;
> +    params.flow = flow;
> +    params.guid = guid;
> +    params.prop = prop;
> +    params.value = out;
> +    params.buffer = NULL;
> +    params.buffer_size = &size;
>  
> -        out->vt = VT_UI4;
> +    while(1) {
> +        ALSA_CALL(get_prop_value, &params);
>  
> -        if (num_speakers > 6)
> -            out->ulVal = KSAUDIO_SPEAKER_STEREO;
> -        else if (num_speakers == 6)
> -            out->ulVal = KSAUDIO_SPEAKER_5POINT1;
> -        else if (num_speakers >= 4)
> -            out->ulVal = KSAUDIO_SPEAKER_QUAD;
> -        else if (num_speakers >= 2)
> -            out->ulVal = KSAUDIO_SPEAKER_STEREO;
> -        else if (num_speakers == 1)
> -            out->ulVal = KSAUDIO_SPEAKER_MONO;
> +        if(params.result != E_NOT_SUFFICIENT_BUFFER)
> +            break;
>  
> -        return S_OK;
> +        CoTaskMemFree(params.buffer);
> +        params.buffer = CoTaskMemAlloc(*params.buffer_size);
> +        if(!params.buffer)
> +            return E_OUTOFMEMORY;
>      }
> +    if(FAILED(params.result))
> +        CoTaskMemFree(params.buffer);
>  
> -    TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid);
> -
> -    return E_NOTIMPL;
> +    return params.result;
>  }
> diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h
> index 4326f79a50b..c2de48cef65 100644
> --- a/dlls/winealsa.drv/unixlib.h
> +++ b/dlls/winealsa.drv/unixlib.h
> @@ -195,6 +195,18 @@ struct is_started_params
>      HRESULT result;
>  };
>  
> +struct get_prop_value_params
> +{
> +    const char *alsa_name;
> +    EDataFlow flow;
> +    const GUID *guid;
> +    const PROPERTYKEY *prop;
> +    HRESULT result;
> +    PROPVARIANT *value;
> +    void *buffer; /* caller allocated buffer to hold value's strings */
> +    unsigned int *buffer_size;
> +};
> +
>  enum alsa_funcs
>  {
>      alsa_get_endpoint_ids,
> @@ -219,6 +231,7 @@ enum alsa_funcs
>      alsa_set_volumes,
>      alsa_set_event_handle,
>      alsa_is_started,
> +    alsa_get_prop_value,
>  };
>  
>  extern unixlib_handle_t alsa_handle;
> -- 
> 2.25.1
> 
> 



More information about the wine-devel mailing list