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

Huw Davies huw at codeweavers.com
Mon Mar 14 05:21:46 CDT 2022


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