Signed-off-by: Huw Davies huw@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)