/* * 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 */ #define COBJMACROS #include #include #include "windef.h" #include "winbase.h" #include "winnls.h" #include "winreg.h" #include "winternl.h" #include "propsys.h" #include "propkey.h" #include "initguid.h" #include "ole2.h" #include "mmdeviceapi.h" #include "devpkey.h" #include "mmsystem.h" #include "dsound.h" #include "endpointvolume.h" #include "audioclient.h" #include "audiopolicy.h" #include "wine/debug.h" #include "wine/list.h" #include "wine/unixlib.h" #include "unixlib.h" #include "../mmdevapi/mmdevdrv.h" WINE_DEFAULT_DEBUG_CHANNEL(alsa); static WCHAR drv_key_devicesW[256]; static const WCHAR guidW[] = {'g','u','i','d',0}; BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) { switch (reason) { case DLL_PROCESS_ATTACH: { WCHAR buf[MAX_PATH]; WCHAR *filename; if(__wine_init_unix_call()) return FALSE; GetModuleFileNameW(dll, buf, ARRAY_SIZE(buf)); filename = wcsrchr(buf, '\\'); filename = filename ? filename + 1 : buf; swprintf(drv_key_devicesW, ARRAY_SIZE(drv_key_devicesW), L"Software\\Wine\\Drivers\\%s\\devices", filename); break; } case DLL_PROCESS_DETACH: if (reserved) break; break; } return TRUE; } static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name, GUID *guid) { HKEY key; BOOL opened = FALSE; LONG lr; if(!drv_key){ lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE, NULL, &drv_key, NULL); if(lr != ERROR_SUCCESS){ ERR("RegCreateKeyEx(drv_key) failed: %lu\n", lr); return; } opened = TRUE; } lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE, NULL, &key, NULL); if(lr != ERROR_SUCCESS){ ERR("RegCreateKeyEx(%s) failed: %lu\n", wine_dbgstr_w(key_name), lr); goto exit; } lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid, sizeof(GUID)); if(lr != ERROR_SUCCESS) ERR("RegSetValueEx(%s\\guid) failed: %lu\n", wine_dbgstr_w(key_name), lr); RegCloseKey(key); exit: if(opened) RegCloseKey(drv_key); } static void get_device_guid(EDataFlow flow, const char *device, GUID *guid) { HKEY key = NULL, dev_key; DWORD type, size = sizeof(*guid); WCHAR key_name[256]; if(flow == eCapture) key_name[0] = '1'; else key_name[0] = '0'; key_name[1] = ','; MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2, ARRAY_SIZE(key_name) - 2); if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){ if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){ if(RegQueryValueExW(dev_key, guidW, 0, &type, (BYTE*)guid, &size) == ERROR_SUCCESS){ if(type == REG_BINARY){ RegCloseKey(dev_key); RegCloseKey(key); return; } ERR("Invalid type for device %s GUID: %lu; ignoring and overwriting\n", wine_dbgstr_w(key_name), type); } RegCloseKey(dev_key); } } CoCreateGuid(guid); set_device_guid(flow, key, key_name, guid); if(key) RegCloseKey(key); } HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **guids_out, UINT *num, UINT *def_index) { 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); 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++){ WCHAR *name = (WCHAR *)((char *)params.endpoints + params.endpoints[i].name); char *device = (char *)params.endpoints + params.endpoints[i].device; unsigned int size = (wcslen(name) + 1) * sizeof(WCHAR); ids[i] = HeapAlloc(GetProcessHeap(), 0, size); if(!ids[i]){ params.result = E_OUTOFMEMORY; goto end; } memcpy(ids[i], name, size); get_device_guid(flow, 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; } return params.result; } BOOL WINAPI get_device_name_from_guid(GUID *guid, char **name, EDataFlow *flow) { HKEY devices_key; UINT i = 0; WCHAR key_name[256]; DWORD key_name_size; if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){ ERR("No devices found in registry?\n"); return FALSE; } while(1){ HKEY key; DWORD size, type; GUID reg_guid; key_name_size = ARRAY_SIZE(key_name); if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) break; if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){ WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name)); continue; } size = sizeof(reg_guid); if(RegQueryValueExW(key, guidW, 0, &type, (BYTE*)®_guid, &size) == ERROR_SUCCESS){ if(IsEqualGUID(®_guid, guid)){ INT size; RegCloseKey(key); RegCloseKey(devices_key); TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name)); if(key_name[0] == '0') *flow = eRender; else if(key_name[0] == '1') *flow = eCapture; else{ ERR("Unknown device type: %c\n", key_name[0]); return FALSE; } if(!(size = WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, NULL, 0, NULL, NULL))) return FALSE; if(!(*name = malloc(size))) return FALSE; if(!WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, *name, size, NULL, NULL)){ free(*name); return FALSE; } return TRUE; } } RegCloseKey(key); } RegCloseKey(devices_key); WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid)); return FALSE; }