Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/winealsa.drv/Makefile.in | 2 +
dlls/winealsa.drv/alsa.c | 458 ++++++++++++++++++++++++++++++++++
dlls/winealsa.drv/mmdevdrv.c | 458 +++++-----------------------------
dlls/winealsa.drv/unixlib.h | 42 ++++
4 files changed, 560 insertions(+), 400 deletions(-)
create mode 100644 dlls/winealsa.drv/alsa.c
create mode 100644 dlls/winealsa.drv/unixlib.h
diff --git a/dlls/winealsa.drv/Makefile.in b/dlls/winealsa.drv/Makefile.in
index 7ce382e64c2..2158e087251 100644
--- a/dlls/winealsa.drv/Makefile.in
+++ b/dlls/winealsa.drv/Makefile.in
@@ -1,5 +1,6 @@
EXTRADEFS = -DWINE_NO_LONG_TYPES
MODULE = winealsa.drv
+UNIXLIB = winealsa.so
IMPORTS = uuid ole32 advapi32
DELAYIMPORTS = winmm
EXTRALIBS = $(ALSA_LIBS)
@@ -7,5 +8,6 @@ EXTRALIBS = $(ALSA_LIBS)
EXTRADLLFLAGS = -mcygwin
C_SRCS = \
+ alsa.c \
midi.c \
mmdevdrv.c
diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c
new file mode 100644
index 00000000000..ae05c7a8b9b
--- /dev/null
+++ b/dlls/winealsa.drv/alsa.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright 2010 Maarten Lankhorst for CodeWeavers
+ * Copyright 2011 Andrew Eikum for CodeWeavers
+ * Copyright 2022 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#if 0
+#pragma makedep unix
+#endif
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <alsa/asoundlib.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "mmdeviceapi.h"
+
+#include "wine/debug.h"
+#include "wine/list.h"
+#include "wine/unixlib.h"
+
+#include "unixlib.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(alsa);
+
+static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
+ 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
+ 'w','i','n','e','a','l','s','a','.','d','r','v'};
+
+static inline void ascii_to_unicode( WCHAR *dst, const char *src, size_t len )
+{
+ while (len--) *dst++ = (unsigned char)*src++;
+}
+
+static HKEY reg_open_key( HKEY root, const WCHAR *name, ULONG name_len )
+{
+ UNICODE_STRING nameW = { name_len, name_len, (WCHAR *)name };
+ OBJECT_ATTRIBUTES attr;
+ HANDLE ret;
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = root;
+ attr.ObjectName = &nameW;
+ attr.Attributes = 0;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+
+ if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 )) return 0;
+ return ret;
+}
+
+static HKEY open_hkcu(void)
+{
+ char buffer[256];
+ WCHAR bufferW[256];
+ DWORD_PTR sid_data[(sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE) / sizeof(DWORD_PTR)];
+ DWORD i, len = sizeof(sid_data);
+ SID *sid;
+
+ if (NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser, sid_data, len, &len ))
+ return 0;
+
+ sid = ((TOKEN_USER *)sid_data)->User.Sid;
+ len = sprintf( buffer, "\\Registry\\User\\S-%u-%u", sid->Revision,
+ MAKELONG( MAKEWORD( sid->IdentifierAuthority.Value[5], sid->IdentifierAuthority.Value[4] ),
+ MAKEWORD( sid->IdentifierAuthority.Value[3], sid->IdentifierAuthority.Value[2] )));
+ for (i = 0; i < sid->SubAuthorityCount; i++)
+ len += sprintf( buffer + len, "-%u", sid->SubAuthority[i] );
+ ascii_to_unicode( bufferW, buffer, len + 1 );
+
+ return reg_open_key( NULL, bufferW, len * sizeof(WCHAR) );
+}
+
+static HKEY reg_open_hkcu_key( const WCHAR *name, ULONG name_len )
+{
+ HKEY hkcu = open_hkcu(), key;
+
+ key = reg_open_key( hkcu, name, name_len );
+ NtClose( hkcu );
+
+ return key;
+}
+
+ULONG reg_query_value( HKEY hkey, const WCHAR *name,
+ KEY_VALUE_PARTIAL_INFORMATION *info, ULONG size )
+{
+ unsigned int name_size = name ? wcslen( name ) * sizeof(WCHAR) : 0;
+ UNICODE_STRING nameW = { name_size, name_size, (WCHAR *)name };
+
+ if (NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
+ info, size, &size ))
+ return 0;
+
+ return size - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
+}
+
+static snd_pcm_stream_t alsa_get_direction(EDataFlow flow)
+{
+ return (flow == eRender) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
+}
+
+static WCHAR *strdupAtoW(const char *str)
+{
+ unsigned int len;
+ WCHAR *ret;
+
+ if(!str) return NULL;
+
+ len = strlen(str) + 1;
+ ret = malloc(len * sizeof(WCHAR));
+ if(ret) ntdll_umbstowcs(str, len, ret, len);
+ return ret;
+}
+
+static BOOL alsa_try_open(const char *devnode, EDataFlow flow)
+{
+ snd_pcm_t *handle;
+ int err;
+
+ TRACE("devnode: %s, flow: %d\n", devnode, flow);
+
+ if((err = snd_pcm_open(&handle, devnode, alsa_get_direction(flow), SND_PCM_NONBLOCK)) < 0){
+ WARN("The device \"%s\" failed to open: %d (%s).\n", devnode, err, snd_strerror(err));
+ return FALSE;
+ }
+
+ snd_pcm_close(handle);
+ return TRUE;
+}
+
+static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const WCHAR *chunk2)
+{
+ WCHAR *ret;
+ const WCHAR *prefix;
+ size_t len_wchars = 0, chunk1_len = 0, chunk2_len = 0, copied = 0, prefix_len;
+
+ static const WCHAR dashW[] = {' ','-',' ',0};
+ static const size_t dashW_len = ARRAY_SIZE(dashW) - 1;
+ static const WCHAR outW[] = {'O','u','t',':',' ',0};
+ static const WCHAR inW[] = {'I','n',':',' ',0};
+
+ if(flow == eRender){
+ prefix = outW;
+ prefix_len = ARRAY_SIZE(outW) - 1;
+ len_wchars += prefix_len;
+ }else{
+ prefix = inW;
+ prefix_len = ARRAY_SIZE(inW) - 1;
+ len_wchars += prefix_len;
+ }
+ if(chunk1){
+ chunk1_len = wcslen(chunk1);
+ len_wchars += chunk1_len;
+ }
+ if(chunk1 && chunk2)
+ len_wchars += dashW_len;
+ if(chunk2){
+ chunk2_len = wcslen(chunk2);
+ len_wchars += chunk2_len;
+ }
+ len_wchars += 1; /* NULL byte */
+
+ ret = malloc(len_wchars * sizeof(WCHAR));
+
+ memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
+ copied += prefix_len;
+ if(chunk1){
+ memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
+ copied += chunk1_len;
+ }
+ if(chunk1 && chunk2){
+ memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
+ copied += dashW_len;
+ }
+ if(chunk2){
+ memcpy(ret + copied, chunk2, chunk2_len * sizeof(WCHAR));
+ copied += chunk2_len;
+ }
+ ret[copied] = 0;
+
+ TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
+
+ return ret;
+}
+
+struct endpoints_info
+{
+ unsigned int num, size;
+ struct endpoint *endpoints;
+};
+
+static void endpoints_add(struct endpoints_info *endpoints, WCHAR *name, char *device)
+{
+ if(endpoints->num >= endpoints->size){
+ if (!endpoints->size) endpoints->size = 16;
+ else endpoints->size *= 2;
+ endpoints->endpoints = realloc(endpoints->endpoints, endpoints->size * sizeof(*endpoints->endpoints));
+ }
+
+ endpoints->endpoints[endpoints->num].name = name;
+ endpoints->endpoints[endpoints->num++].device = device;
+}
+
+static HRESULT alsa_get_card_devices(EDataFlow flow, struct endpoints_info *endpoints_info,
+ snd_ctl_t *ctl, int card, const WCHAR *cardname)
+{
+ int err, device;
+ snd_pcm_info_t *info;
+
+ info = calloc(1, snd_pcm_info_sizeof());
+ if(!info)
+ return E_OUTOFMEMORY;
+
+ snd_pcm_info_set_subdevice(info, 0);
+ snd_pcm_info_set_stream(info, alsa_get_direction(flow));
+
+ device = -1;
+ for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
+ err = snd_ctl_pcm_next_device(ctl, &device)){
+ char devnode[32];
+ WCHAR *devname;
+
+ snd_pcm_info_set_device(info, device);
+
+ if((err = snd_ctl_pcm_info(ctl, info)) < 0){
+ if(err == -ENOENT)
+ /* This device doesn't have the right stream direction */
+ continue;
+
+ WARN("Failed to get info for card %d, device %d: %d (%s)\n",
+ card, device, err, snd_strerror(err));
+ continue;
+ }
+
+ sprintf(devnode, "plughw:%d,%d", card, device);
+ if(!alsa_try_open(devnode, flow))
+ continue;
+
+ devname = strdupAtoW(snd_pcm_info_get_name(info));
+ if(!devname){
+ WARN("Unable to get device name for card %d, device %d\n", card, device);
+ continue;
+ }
+
+ endpoints_add(endpoints_info, construct_device_id(flow, cardname, devname), strdup(devnode));
+ free(devname);
+ }
+
+ free(info);
+
+ if(err != 0)
+ WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
+ card, err, snd_strerror(err));
+
+ return S_OK;
+}
+
+static void get_reg_devices(EDataFlow flow, struct endpoints_info *endpoints_info)
+{
+ static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
+ static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
+ char buffer[4096];
+ KEY_VALUE_PARTIAL_INFORMATION *key_info = (void *)buffer;
+ HKEY key;
+ DWORD size;
+ const WCHAR *value_name = (flow == eRender) ? ALSAOutputDevices : ALSAInputDevices;
+
+ /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
+ if((key = reg_open_hkcu_key(drv_keyW, sizeof(drv_keyW)))){
+ if((size = reg_query_value(key, value_name, key_info, sizeof(buffer)))){
+ WCHAR *p = (WCHAR *)key_info->Data;
+
+ if(key_info->Type != REG_MULTI_SZ){
+ ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
+ NtClose(key);
+ return;
+ }
+
+ while(*p){
+ int len = wcslen(p);
+ char *devname = malloc(len * 3 + 1);
+
+ ntdll_wcstoumbs(p, len + 1, devname, len * 3 + 1, FALSE);
+
+ if(alsa_try_open(devname, flow))
+ endpoints_add(endpoints_info, construct_device_id(flow, p, NULL), strdup(devname));
+
+ free(devname);
+ p += len + 1;
+ }
+ }
+
+ NtClose(key);
+ }
+}
+
+struct card_type {
+ struct list entry;
+ int first_card_number;
+ char string[1];
+};
+
+static struct list card_types = LIST_INIT(card_types);
+
+static BOOL need_card_number(int card, const char *string)
+{
+ struct card_type *cptr;
+
+ LIST_FOR_EACH_ENTRY(cptr, &card_types, struct card_type, entry)
+ {
+ if(!strcmp(string, cptr->string))
+ return card != cptr->first_card_number;
+ }
+
+ /* this is the first instance of string */
+ cptr = malloc(sizeof(struct card_type) + strlen(string));
+ if(!cptr)
+ /* Default to displaying card number if we can't track cards */
+ return TRUE;
+
+ cptr->first_card_number = card;
+ strcpy(cptr->string, string);
+ list_add_head(&card_types, &cptr->entry);
+ return FALSE;
+}
+
+static WCHAR *alsa_get_card_name(int card)
+{
+ char *cardname;
+ WCHAR *ret;
+ int err;
+
+ if((err = snd_card_get_name(card, &cardname)) < 0){
+ /* FIXME: Should be localized */
+ WARN("Unable to get card name for ALSA device %d: %d (%s)\n", card, err, snd_strerror(err));
+ cardname = strdup("Unknown soundcard");
+ }
+
+ if(need_card_number(card, cardname)){
+ char *cardnameN;
+ /*
+ * For identical card names, second and subsequent instances get
+ * card number prefix to distinguish them (like Windows).
+ */
+ if(asprintf(&cardnameN, "%u-%s", card, cardname) > 0){
+ free(cardname);
+ cardname = cardnameN;
+ }
+ }
+
+ ret = strdupAtoW(cardname);
+ free(cardname);
+
+ return ret;
+}
+
+static NTSTATUS get_endpoint_ids(void *args)
+{
+ static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
+ struct get_endpoint_ids_params *params = args;
+ struct endpoints_info endpoints_info;
+ unsigned int i, needed, name_len, device_len;
+ struct endpoint *endpoint;
+ int err, card;
+ char *ptr;
+
+ card = -1;
+
+ endpoints_info.num = endpoints_info.size = 0;
+ endpoints_info.endpoints = NULL;
+
+ if(alsa_try_open("default", params->flow))
+ endpoints_add(&endpoints_info, construct_device_id(params->flow, defaultW, NULL), strdup("default"));
+
+ get_reg_devices(params->flow, &endpoints_info);
+
+ for(err = snd_card_next(&card); card != -1 && err >= 0; err = snd_card_next(&card)){
+ char cardpath[64];
+ WCHAR *cardname;
+ snd_ctl_t *ctl;
+
+ sprintf(cardpath, "hw:%u", card);
+
+ if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
+ WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
+ err, snd_strerror(err));
+ continue;
+ }
+
+ cardname = alsa_get_card_name(card);
+ alsa_get_card_devices(params->flow, &endpoints_info, ctl, card, cardname);
+ free(cardname);
+
+ snd_ctl_close(ctl);
+ }
+
+ if(err != 0)
+ WARN("Got a failure during card enumeration: %d (%s)\n", err, snd_strerror(err));
+
+ needed = endpoints_info.num * sizeof(*params->endpoints);
+ endpoint = params->endpoints;
+ ptr = (char *)(endpoint + endpoints_info.num);
+
+ for(i = 0; i < endpoints_info.num; i++){
+ name_len = wcslen(endpoints_info.endpoints[i].name) + 1;
+ device_len = strlen(endpoints_info.endpoints[i].device) + 1;
+ needed += name_len * sizeof(WCHAR) + ((device_len + 1) & ~1);
+
+ if(needed <= params->size){
+ endpoint->name = (WCHAR *)ptr;
+ memcpy(endpoint->name, endpoints_info.endpoints[i].name, name_len * sizeof(WCHAR));
+ ptr += name_len * sizeof(WCHAR);
+ endpoint->device = ptr;
+ memcpy(endpoint->device, endpoints_info.endpoints[i].device, device_len);
+ ptr += (device_len + 1) & ~1;
+ endpoint++;
+ }
+ free(endpoints_info.endpoints[i].name);
+ free(endpoints_info.endpoints[i].device);
+ }
+ free(endpoints_info.endpoints);
+
+ params->num = endpoints_info.num;
+ params->default_idx = 0;
+
+ if(needed > params->size){
+ params->size = needed;
+ params->result = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ } else
+ params->result = S_OK;
+
+ return STATUS_SUCCESS;
+}
+
+unixlib_entry_t __wine_unix_call_funcs[] =
+{
+ get_endpoint_ids,
+};
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 9a8f80a7beb..516bba1e5fc 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -1,6 +1,7 @@
/*
* Copyright 2010 Maarten Lankhorst for CodeWeavers
* Copyright 2011 Andrew Eikum for CodeWeavers
+ * Copyright 2022 Huw Davies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -32,6 +33,7 @@
#include "wine/debug.h"
#include "wine/unicode.h"
#include "wine/list.h"
+#include "wine/unixlib.h"
#include "propsys.h"
#include "initguid.h"
@@ -49,8 +51,12 @@
#include <alsa/asoundlib.h>
+#include "unixlib.h"
+
WINE_DEFAULT_DEBUG_CHANNEL(alsa);
+unixlib_handle_t alsa_handle = 0;
+
#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
static const REFERENCE_TIME DefaultPeriod = 100000;
@@ -161,12 +167,6 @@ static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
static struct list g_sessions = LIST_INIT(g_sessions);
-static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
-static const char defname[] = "default";
-
-static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
- 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
- 'w','i','n','e','a','l','s','a','.','d','r','v'};
static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
@@ -240,6 +240,9 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
switch (reason)
{
case DLL_PROCESS_ATTACH:
+ if(NtQueryVirtualMemory(GetCurrentProcess(), dll, MemoryWineUnixFuncs,
+ &alsa_handle, sizeof(alsa_handle), NULL))
+ return FALSE;
g_timer_q = CreateTimerQueue();
if(!g_timer_q)
return FALSE;
@@ -266,19 +269,6 @@ int WINAPI AUDDRV_GetPriority(void)
return Priority_Neutral;
}
-static WCHAR *strdupAtoW(const char *str)
-{
- unsigned int len;
- WCHAR *ret;
-
- if(!str) return NULL;
-
- len = MultiByteToWideChar(CP_UNIXCP, 0, str, -1, NULL, 0);
- ret = malloc(len * sizeof(WCHAR));
- if(ret) MultiByteToWideChar(CP_UNIXCP, 0, str, -1, ret, len);
- return ret;
-}
-
static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
GUID *guid)
{
@@ -351,399 +341,67 @@ static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
RegCloseKey(key);
}
-static inline void ascii_to_unicode( WCHAR *dst, const char *src, size_t len )
-{
- while (len--) *dst++ = (unsigned char)*src++;
-}
-
-static HKEY reg_open_key( HKEY root, const WCHAR *name, ULONG name_len )
-{
- UNICODE_STRING nameW = { name_len, name_len, (WCHAR *)name };
- OBJECT_ATTRIBUTES attr;
- HANDLE ret;
-
- attr.Length = sizeof(attr);
- attr.RootDirectory = root;
- attr.ObjectName = &nameW;
- attr.Attributes = 0;
- attr.SecurityDescriptor = NULL;
- attr.SecurityQualityOfService = NULL;
-
- if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 )) return 0;
- return ret;
-}
-
-static HKEY open_hkcu(void)
-{
- char buffer[256];
- WCHAR bufferW[256];
- DWORD_PTR sid_data[(sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE) / sizeof(DWORD_PTR)];
- DWORD i, len = sizeof(sid_data);
- SID *sid;
-
- if (NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser, sid_data, len, &len ))
- return 0;
-
- sid = ((TOKEN_USER *)sid_data)->User.Sid;
- len = sprintf( buffer, "\\Registry\\User\\S-%u-%u", sid->Revision,
- MAKELONG( MAKEWORD( sid->IdentifierAuthority.Value[5], sid->IdentifierAuthority.Value[4] ),
- MAKEWORD( sid->IdentifierAuthority.Value[3], sid->IdentifierAuthority.Value[2] )));
- for (i = 0; i < sid->SubAuthorityCount; i++)
- len += sprintf( buffer + len, "-%u", sid->SubAuthority[i] );
- ascii_to_unicode( bufferW, buffer, len + 1 );
-
- return reg_open_key( NULL, bufferW, len * sizeof(WCHAR) );
-}
-
-static HKEY reg_open_hkcu_key( const WCHAR *name, ULONG name_len )
-{
- HKEY hkcu = open_hkcu(), key;
-
- key = reg_open_key( hkcu, name, name_len );
- NtClose( hkcu );
-
- return key;
-}
-
-ULONG reg_query_value( HKEY hkey, const WCHAR *name,
- KEY_VALUE_PARTIAL_INFORMATION *info, ULONG size )
-{
- unsigned int name_size = name ? lstrlenW( name ) * sizeof(WCHAR) : 0;
- UNICODE_STRING nameW = { name_size, name_size, (WCHAR *)name };
-
- if (NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
- info, size, &size ))
- return 0;
-
- return size - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
-}
-
static snd_pcm_stream_t alsa_get_direction(EDataFlow flow)
{
return (flow == eRender) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
}
-static BOOL alsa_try_open(const char *devnode, EDataFlow flow)
-{
- snd_pcm_t *handle;
- int err;
-
- TRACE("devnode: %s, flow: %d\n", devnode, flow);
-
- if((err = snd_pcm_open(&handle, devnode, alsa_get_direction(flow), SND_PCM_NONBLOCK)) < 0){
- WARN("The device \"%s\" failed to open: %d (%s).\n", devnode, err, snd_strerror(err));
- return FALSE;
- }
-
- snd_pcm_close(handle);
- return TRUE;
-}
-
-static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const WCHAR *chunk2)
-{
- WCHAR *ret;
- const WCHAR *prefix;
- size_t len_wchars = 0, chunk1_len = 0, chunk2_len = 0, copied = 0, prefix_len;
-
- static const WCHAR dashW[] = {' ','-',' ',0};
- static const size_t dashW_len = ARRAY_SIZE(dashW) - 1;
- static const WCHAR outW[] = {'O','u','t',':',' ',0};
- static const WCHAR inW[] = {'I','n',':',' ',0};
-
- if(flow == eRender){
- prefix = outW;
- prefix_len = ARRAY_SIZE(outW) - 1;
- len_wchars += prefix_len;
- }else{
- prefix = inW;
- prefix_len = ARRAY_SIZE(inW) - 1;
- len_wchars += prefix_len;
- }
- if(chunk1){
- chunk1_len = strlenW(chunk1);
- len_wchars += chunk1_len;
- }
- if(chunk1 && chunk2)
- len_wchars += dashW_len;
- if(chunk2){
- chunk2_len = strlenW(chunk2);
- len_wchars += chunk2_len;
- }
- len_wchars += 1; /* NULL byte */
-
- ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
-
- memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
- copied += prefix_len;
- if(chunk1){
- memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
- copied += chunk1_len;
- }
- if(chunk1 && chunk2){
- memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
- copied += dashW_len;
- }
- if(chunk2){
- memcpy(ret + copied, chunk2, chunk2_len * sizeof(WCHAR));
- copied += chunk2_len;
- }
- ret[copied] = 0;
-
- TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
-
- return ret;
-}
-
-static HRESULT alsa_get_card_devices(EDataFlow flow, WCHAR ***ids, GUID **guids, UINT *num,
- snd_ctl_t *ctl, int card, const WCHAR *cardname)
-{
- int err, device;
- snd_pcm_info_t *info;
-
- info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
- if(!info)
- return E_OUTOFMEMORY;
-
- snd_pcm_info_set_subdevice(info, 0);
- snd_pcm_info_set_stream(info, alsa_get_direction(flow));
-
- device = -1;
- for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
- err = snd_ctl_pcm_next_device(ctl, &device)){
- char devnode[32];
- WCHAR *devname;
-
- snd_pcm_info_set_device(info, device);
-
- if((err = snd_ctl_pcm_info(ctl, info)) < 0){
- if(err == -ENOENT)
- /* This device doesn't have the right stream direction */
- continue;
-
- WARN("Failed to get info for card %d, device %d: %d (%s)\n",
- card, device, err, snd_strerror(err));
- continue;
- }
-
- sprintf(devnode, "plughw:%d,%d", card, device);
- if(!alsa_try_open(devnode, flow))
- continue;
-
- if(*num){
- *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
- *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
- }else{
- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
- *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
- }
-
- devname = strdupAtoW(snd_pcm_info_get_name(info));
- if(!devname){
- WARN("Unable to get device name for card %d, device %d\n", card, device);
- continue;
- }
-
- (*ids)[*num] = construct_device_id(flow, cardname, devname);
- get_device_guid(flow, devnode, &(*guids)[*num]);
- free(devname);
-
- ++(*num);
- }
-
- HeapFree(GetProcessHeap(), 0, info);
-
- if(err != 0)
- WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
- card, err, snd_strerror(err));
-
- return S_OK;
-}
-
-static void get_reg_devices(EDataFlow flow, WCHAR ***ids, GUID **guids, UINT *num)
-{
- static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
- static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
- char buffer[4096];
- KEY_VALUE_PARTIAL_INFORMATION *key_info = (void *)buffer;
- HKEY key;
- DWORD size;
- const WCHAR *value_name = (flow == eRender) ? ALSAOutputDevices : ALSAInputDevices;
-
- /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
- if((key = reg_open_hkcu_key(drv_keyW, sizeof(drv_keyW)))){
- if((size = reg_query_value(key, value_name, key_info, sizeof(buffer)))){
- WCHAR *p = (WCHAR *)key_info->Data;
-
- if(key_info->Type != REG_MULTI_SZ){
- ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
- NtClose(key);
- return;
- }
-
- while(*p){
- int len = lstrlenW(p);
- char *devname = malloc(len * 3 + 1);
-
- WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, len * 3 + 1, NULL, NULL);
-
- if(alsa_try_open(devname, flow)){
- if(*num){
- *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
- *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
- }else{
- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
- *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
- }
- (*ids)[*num] = construct_device_id(flow, p, NULL);
- get_device_guid(flow, devname, &(*guids)[*num]);
- ++*num;
- }
- free(devname);
- p += len + 1;
- }
- }
-
- NtClose(key);
- }
-}
-
-struct card_type {
- struct list entry;
- int first_card_number;
- char string[1];
-};
-
-static struct list card_types = LIST_INIT(card_types);
-
-static BOOL need_card_number(int card, const char *string)
-{
- struct card_type *cptr;
-
- LIST_FOR_EACH_ENTRY(cptr, &card_types, struct card_type, entry)
- {
- if(!strcmp(string, cptr->string))
- return card != cptr->first_card_number;
- }
-
- /* this is the first instance of string */
- cptr = HeapAlloc(GetProcessHeap(), 0, sizeof(struct card_type) + strlen(string));
- if(!cptr)
- /* Default to displaying card number if we can't track cards */
- return TRUE;
-
- cptr->first_card_number = card;
- strcpy(cptr->string, string);
- list_add_head(&card_types, &cptr->entry);
- return FALSE;
-}
-
-static WCHAR *alsa_get_card_name(int card)
-{
- char *cardname;
- WCHAR *ret;
- int err;
-
- if((err = snd_card_get_name(card, &cardname)) < 0){
- /* FIXME: Should be localized */
- WARN("Unable to get card name for ALSA device %d: %d (%s)\n", card, err, snd_strerror(err));
- cardname = strdup("Unknown soundcard");
- }
-
- if(need_card_number(card, cardname)){
- char *cardnameN;
- /*
- * For identical card names, second and subsequent instances get
- * card number prefix to distinguish them (like Windows).
- */
- if(asprintf(&cardnameN, "%u-%s", card, cardname) > 0){
- free(cardname);
- cardname = cardnameN;
- }
- }
-
- ret = strdupAtoW(cardname);
- free(cardname);
-
- return ret;
-}
-
-static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
- UINT *num)
-{
- int err, card;
-
- card = -1;
- *num = 0;
-
- if(alsa_try_open(defname, flow)){
- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
- (*ids)[0] = construct_device_id(flow, defaultW, NULL);
- *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
- get_device_guid(flow, defname, &(*guids)[0]);
- ++*num;
- }
-
- get_reg_devices(flow, ids, guids, num);
-
- for(err = snd_card_next(&card); card != -1 && err >= 0; err = snd_card_next(&card)){
- char cardpath[64];
- WCHAR *cardname;
- snd_ctl_t *ctl;
-
- sprintf(cardpath, "hw:%u", card);
-
- if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
- WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
- err, snd_strerror(err));
- continue;
- }
-
- cardname = alsa_get_card_name(card);
- alsa_get_card_devices(flow, ids, guids, num, ctl, card, cardname);
- free(cardname);
-
- snd_ctl_close(ctl);
- }
-
- if(err != 0)
- WARN("Got a failure during card enumeration: %d (%s)\n",
- err, snd_strerror(err));
-
- return S_OK;
-}
-
-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **guids_out,
UINT *num, UINT *def_index)
{
- HRESULT hr;
+ struct get_endpoint_ids_params params;
+ unsigned int i;
+ GUID *guids = NULL;
+ WCHAR **ids = NULL;
TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
- *ids = NULL;
- *guids = NULL;
-
- hr = alsa_enum_devices(flow, ids, guids, num);
- if(FAILED(hr)){
- UINT i;
- for(i = 0; i < *num; ++i)
- HeapFree(GetProcessHeap(), 0, (*ids)[i]);
- HeapFree(GetProcessHeap(), 0, *ids);
- HeapFree(GetProcessHeap(), 0, *guids);
- return E_OUTOFMEMORY;
- }
-
- TRACE("Enumerated %u devices\n", *num);
-
- if(*num == 0){
- HeapFree(GetProcessHeap(), 0, *ids);
- *ids = NULL;
- HeapFree(GetProcessHeap(), 0, *guids);
- *guids = NULL;
+ params.flow = flow;
+ params.size = 1000;
+ params.endpoints = NULL;
+ do{
+ HeapFree(GetProcessHeap(), 0, params.endpoints);
+ params.endpoints = HeapAlloc(GetProcessHeap(), 0, params.size);
+ ALSA_CALL(get_endpoint_ids, ¶ms);
+ }while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
+
+ if(FAILED(params.result)) goto end;
+
+ ids = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, params.num * sizeof(*ids));
+ guids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*guids));
+ if(!ids || !guids){
+ params.result = E_OUTOFMEMORY;
+ goto end;
+ }
+
+ for(i = 0; i < params.num; i++){
+ unsigned int size = (strlenW(params.endpoints[i].name) + 1) * sizeof(WCHAR);
+ ids[i] = HeapAlloc(GetProcessHeap(), 0, size);
+ if(!ids[i]){
+ params.result = E_OUTOFMEMORY;
+ goto end;
+ }
+ memcpy(ids[i], params.endpoints[i].name, size);
+ get_device_guid(flow, params.endpoints[i].device, guids + i);
+ }
+ *def_index = params.default_idx;
+
+end:
+ HeapFree(GetProcessHeap(), 0, params.endpoints);
+ if(FAILED(params.result)){
+ HeapFree(GetProcessHeap(), 0, guids);
+ if(ids){
+ for(i = 0; i < params.num; i++)
+ HeapFree(GetProcessHeap(), 0, ids[i]);
+ HeapFree(GetProcessHeap(), 0, ids);
+ }
+ }else{
+ *ids_out = ids;
+ *guids_out = guids;
+ *num = params.num;
}
- *def_index = 0;
-
- return S_OK;
+ return params.result;
}
static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h
new file mode 100644
index 00000000000..f3014d0b448
--- /dev/null
+++ b/dlls/winealsa.drv/unixlib.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+struct endpoint
+{
+ WCHAR *name;
+ char *device;
+};
+
+struct get_endpoint_ids_params
+{
+ EDataFlow flow;
+ struct endpoint *endpoints;
+ unsigned int size;
+ HRESULT result;
+ unsigned int num;
+ unsigned int default_idx;
+};
+
+enum alsa_funcs
+{
+ alsa_get_endpoint_ids,
+};
+
+extern unixlib_handle_t alsa_handle;
+
+#define ALSA_CALL(func, params) __wine_unix_call(alsa_handle, alsa_ ## func, params)
--
2.25.1