From 027623a0077182da4ec29f03e4a79173d81cc088 Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Tue, 3 Apr 2012 13:47:59 -0500 Subject: [PATCH] winealsa.drv: Use device GUIDs as keys. --- dlls/winealsa.drv/mmdevdrv.c | 226 ++++++++++++++++++++++++++++------- 1 file changed, 186 insertions(+), 40 deletions(-) diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c index 0f710a5c89e..514401c3474 100644 --- a/dlls/winealsa.drv/mmdevdrv.c +++ b/dlls/winealsa.drv/mmdevdrv.c @@ -150,6 +150,14 @@ 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',0}; +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}; +static const WCHAR guidW[] = {'g','u','i','d',0}; + static const IAudioClientVtbl AudioClient_Vtbl; static const IAudioRenderClientVtbl AudioRenderClient_Vtbl; static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl; @@ -243,6 +251,79 @@ int WINAPI AUDDRV_GetPriority(void) return Priority_Neutral; } +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: %u\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: %u\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: %u\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, + (sizeof(key_name) / sizeof(*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: %u; 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); +} + static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream) { snd_pcm_t *handle; @@ -258,8 +339,9 @@ static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream) return TRUE; } -static HRESULT alsa_get_card_devices(snd_pcm_stream_t stream, WCHAR **ids, char **keys, - UINT *num, snd_ctl_t *ctl, int card, const WCHAR *cardnameW) +static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream, + WCHAR **ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card, + const WCHAR *cardnameW) { static const WCHAR dashW[] = {' ','-',' ',0}; int err, device; @@ -294,7 +376,7 @@ static HRESULT alsa_get_card_devices(snd_pcm_stream_t stream, WCHAR **ids, char if(!alsa_try_open(devnode, stream)) continue; - if(ids && keys){ + if(ids && guids){ DWORD len, cardlen; devname = snd_pcm_info_get_name(info); @@ -319,13 +401,13 @@ static HRESULT alsa_get_card_devices(snd_pcm_stream_t stream, WCHAR **ids, char MultiByteToWideChar(CP_UNIXCP, 0, devname, -1, ids[*num] + cardlen, len - cardlen); - keys[*num] = HeapAlloc(GetProcessHeap(), 0, 32); - if(!keys[*num]){ + guids[*num] = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID)); + if(!guids[*num]){ HeapFree(GetProcessHeap(), 0, info); HeapFree(GetProcessHeap(), 0, ids[*num]); return E_OUTOFMEMORY; } - memcpy(keys[*num], devnode, sizeof(devnode)); + get_device_guid(flow, devnode, guids[*num]); } ++(*num); @@ -340,12 +422,9 @@ static HRESULT alsa_get_card_devices(snd_pcm_stream_t stream, WCHAR **ids, char return S_OK; } -static void get_reg_devices(snd_pcm_stream_t stream, WCHAR **ids, char **keys, - UINT *num) +static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR **ids, + GUID **guids, UINT *num) { - 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',0}; 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}; HKEY key; @@ -371,14 +450,13 @@ static void get_reg_devices(snd_pcm_stream_t stream, WCHAR **ids, char **keys, WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL); if(alsa_try_open(devname, stream)){ - if(ids && keys){ + if(ids && guids){ len = lstrlenW(p) + 1; ids[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); memcpy(ids[*num], p, len * sizeof(WCHAR)); - len = strlen(devname) + 1; - keys[*num] = HeapAlloc(GetProcessHeap(), 0, len); - memcpy(keys[*num], devname, len); + guids[*num] = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID)); + get_device_guid(flow, devname, guids[*num]); } ++*num; } @@ -391,7 +469,7 @@ static void get_reg_devices(snd_pcm_stream_t stream, WCHAR **ids, char **keys, } } -static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys, +static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, GUID **guids, UINT *num) { snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK : @@ -402,16 +480,16 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys, *num = 0; if(alsa_try_open(defname, stream)){ - if(ids && keys){ + if(ids && guids){ *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW)); memcpy(*ids, defaultW, sizeof(defaultW)); - *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(defname)); - memcpy(*keys, defname, sizeof(defname)); + *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID)); + get_device_guid(flow, defname, *guids); } ++*num; } - get_reg_devices(stream, ids, keys, num); + get_reg_devices(flow, stream, ids, guids, num); for(err = snd_card_next(&card); card != -1 && err >= 0; err = snd_card_next(&card)){ @@ -434,7 +512,7 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys, static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0}; WARN("Unable to get card name for ALSA device %s: %d (%s)\n", cardpath, err, snd_strerror(err)); - alsa_get_card_devices(stream, ids, keys, num, ctl, card, nameW); + alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW); }else{ len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0); cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); @@ -446,7 +524,7 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys, } MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len); - alsa_get_card_devices(stream, ids, keys, num, ctl, card, cardnameW); + alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW); HeapFree(GetProcessHeap(), 0, cardnameW); free(cardname); @@ -462,12 +540,12 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys, return S_OK; } -HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys, +HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID ***guids, UINT *num, UINT *def_index) { HRESULT hr; - TRACE("%d %p %p %p %p\n", flow, ids, keys, num, def_index); + TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index); hr = alsa_enum_devices(flow, NULL, NULL, num); if(FAILED(hr)) @@ -476,29 +554,29 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys, if(*num == 0) { *ids = NULL; - *keys = NULL; + *guids = NULL; return S_OK; } *ids = HeapAlloc(GetProcessHeap(), 0, *num * sizeof(WCHAR *)); - *keys = HeapAlloc(GetProcessHeap(), 0, *num * sizeof(char *)); - if(!*ids || !*keys){ + *guids = HeapAlloc(GetProcessHeap(), 0, *num * sizeof(GUID *)); + if(!*ids || !*guids){ HeapFree(GetProcessHeap(), 0, *ids); - HeapFree(GetProcessHeap(), 0, *keys); + HeapFree(GetProcessHeap(), 0, *guids); return E_OUTOFMEMORY; } *def_index = 0; - hr = alsa_enum_devices(flow, *ids, *keys, num); + hr = alsa_enum_devices(flow, *ids, *guids, num); if(FAILED(hr)){ int i; for(i = 0; i < *num; ++i){ HeapFree(GetProcessHeap(), 0, (*ids)[i]); - HeapFree(GetProcessHeap(), 0, (*keys)[i]); + HeapFree(GetProcessHeap(), 0, (*guids)[i]); } HeapFree(GetProcessHeap(), 0, *ids); - HeapFree(GetProcessHeap(), 0, *keys); + HeapFree(GetProcessHeap(), 0, *guids); return E_OUTOFMEMORY; } @@ -588,16 +666,84 @@ static snd_config_t *make_handle_underrun_config(const char *name) return lconf; } -HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev, - EDataFlow dataflow, IAudioClient **out) +static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, 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 = sizeof(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)){ + 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; + } + + WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL); + + return TRUE; + } + } + + RegCloseKey(key); + + ++i; + } + + RegCloseKey(devices_key); + + WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid)); + + return FALSE; +} + +HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, EDataFlow unused_flow, + IAudioClient **out) { ACImpl *This; int err; snd_pcm_stream_t stream; snd_config_t *lconf; static int handle_underrun = 1; + char alsa_name[256]; + EDataFlow dataflow; - TRACE("\"%s\" %p %d %p\n", key, dev, dataflow, out); + TRACE("%s %p %p\n", debugstr_guid(guid), dev, out); + + if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow)) + return AUDCLNT_E_DEVICE_INVALIDATED; This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl)); if(!This) @@ -620,24 +766,24 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev, } This->dataflow = dataflow; - if(handle_underrun && ((lconf = make_handle_underrun_config(key)))){ - err = snd_pcm_open_lconf(&This->pcm_handle, key, stream, SND_PCM_NONBLOCK, lconf); - TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", key, err); + if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){ + err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf); + TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err); snd_config_delete(lconf); /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */ if(err == -EINVAL){ ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang." - " Please upgrade to alsa_plugins >= 1.0.24\n", key, err); + " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err); handle_underrun = 0; } }else err = -EINVAL; if(err == -EINVAL){ - err = snd_pcm_open(&This->pcm_handle, key, stream, SND_PCM_NONBLOCK); + err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK); } if(err < 0){ HeapFree(GetProcessHeap(), 0, This); - WARN("Unable to open PCM \"%s\": %d (%s)\n", key, err, snd_strerror(err)); + WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err)); switch(err){ case -EBUSY: return AUDCLNT_E_DEVICE_IN_USE;