Commit 027623a0 authored by Andrew Eikum's avatar Andrew Eikum Committed by Alexandre Julliard

winealsa.drv: Use device GUIDs as keys.

parent 1a98c339
...@@ -150,6 +150,14 @@ static struct list g_sessions = LIST_INIT(g_sessions); ...@@ -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 WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
static const char defname[] = "default"; 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 IAudioClientVtbl AudioClient_Vtbl;
static const IAudioRenderClientVtbl AudioRenderClient_Vtbl; static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl; static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
...@@ -243,6 +251,79 @@ int WINAPI AUDDRV_GetPriority(void) ...@@ -243,6 +251,79 @@ int WINAPI AUDDRV_GetPriority(void)
return Priority_Neutral; 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) static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
{ {
snd_pcm_t *handle; snd_pcm_t *handle;
...@@ -258,8 +339,9 @@ static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream) ...@@ -258,8 +339,9 @@ static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
return TRUE; return TRUE;
} }
static HRESULT alsa_get_card_devices(snd_pcm_stream_t stream, WCHAR **ids, char **keys, static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
UINT *num, snd_ctl_t *ctl, int card, const WCHAR *cardnameW) WCHAR **ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
const WCHAR *cardnameW)
{ {
static const WCHAR dashW[] = {' ','-',' ',0}; static const WCHAR dashW[] = {' ','-',' ',0};
int err, device; int err, device;
...@@ -294,7 +376,7 @@ static HRESULT alsa_get_card_devices(snd_pcm_stream_t stream, WCHAR **ids, char ...@@ -294,7 +376,7 @@ static HRESULT alsa_get_card_devices(snd_pcm_stream_t stream, WCHAR **ids, char
if(!alsa_try_open(devnode, stream)) if(!alsa_try_open(devnode, stream))
continue; continue;
if(ids && keys){ if(ids && guids){
DWORD len, cardlen; DWORD len, cardlen;
devname = snd_pcm_info_get_name(info); 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 ...@@ -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, MultiByteToWideChar(CP_UNIXCP, 0, devname, -1, ids[*num] + cardlen,
len - cardlen); len - cardlen);
keys[*num] = HeapAlloc(GetProcessHeap(), 0, 32); guids[*num] = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
if(!keys[*num]){ if(!guids[*num]){
HeapFree(GetProcessHeap(), 0, info); HeapFree(GetProcessHeap(), 0, info);
HeapFree(GetProcessHeap(), 0, ids[*num]); HeapFree(GetProcessHeap(), 0, ids[*num]);
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
} }
memcpy(keys[*num], devnode, sizeof(devnode)); get_device_guid(flow, devnode, guids[*num]);
} }
++(*num); ++(*num);
...@@ -340,12 +422,9 @@ static HRESULT alsa_get_card_devices(snd_pcm_stream_t stream, WCHAR **ids, char ...@@ -340,12 +422,9 @@ static HRESULT alsa_get_card_devices(snd_pcm_stream_t stream, WCHAR **ids, char
return S_OK; return S_OK;
} }
static void get_reg_devices(snd_pcm_stream_t stream, WCHAR **ids, char **keys, static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR **ids,
UINT *num) 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 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}; static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
HKEY key; HKEY key;
...@@ -371,14 +450,13 @@ static void get_reg_devices(snd_pcm_stream_t stream, WCHAR **ids, char **keys, ...@@ -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); WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
if(alsa_try_open(devname, stream)){ if(alsa_try_open(devname, stream)){
if(ids && keys){ if(ids && guids){
len = lstrlenW(p) + 1; len = lstrlenW(p) + 1;
ids[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); ids[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
memcpy(ids[*num], p, len * sizeof(WCHAR)); memcpy(ids[*num], p, len * sizeof(WCHAR));
len = strlen(devname) + 1; guids[*num] = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
keys[*num] = HeapAlloc(GetProcessHeap(), 0, len); get_device_guid(flow, devname, guids[*num]);
memcpy(keys[*num], devname, len);
} }
++*num; ++*num;
} }
...@@ -391,7 +469,7 @@ static void get_reg_devices(snd_pcm_stream_t stream, WCHAR **ids, char **keys, ...@@ -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) UINT *num)
{ {
snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK : 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, ...@@ -402,16 +480,16 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys,
*num = 0; *num = 0;
if(alsa_try_open(defname, stream)){ if(alsa_try_open(defname, stream)){
if(ids && keys){ if(ids && guids){
*ids = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW)); *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
memcpy(*ids, defaultW, sizeof(defaultW)); memcpy(*ids, defaultW, sizeof(defaultW));
*keys = HeapAlloc(GetProcessHeap(), 0, sizeof(defname)); *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
memcpy(*keys, defname, sizeof(defname)); get_device_guid(flow, defname, *guids);
} }
++*num; ++*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; for(err = snd_card_next(&card); card != -1 && err >= 0;
err = snd_card_next(&card)){ err = snd_card_next(&card)){
...@@ -434,7 +512,7 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys, ...@@ -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}; 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", WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
cardpath, err, snd_strerror(err)); 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{ }else{
len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0); len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
...@@ -446,7 +524,7 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys, ...@@ -446,7 +524,7 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys,
} }
MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len); 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); HeapFree(GetProcessHeap(), 0, cardnameW);
free(cardname); free(cardname);
...@@ -462,12 +540,12 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys, ...@@ -462,12 +540,12 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR **ids, char **keys,
return S_OK; 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) UINT *num, UINT *def_index)
{ {
HRESULT hr; 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); hr = alsa_enum_devices(flow, NULL, NULL, num);
if(FAILED(hr)) if(FAILED(hr))
...@@ -476,29 +554,29 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys, ...@@ -476,29 +554,29 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
if(*num == 0) if(*num == 0)
{ {
*ids = NULL; *ids = NULL;
*keys = NULL; *guids = NULL;
return S_OK; return S_OK;
} }
*ids = HeapAlloc(GetProcessHeap(), 0, *num * sizeof(WCHAR *)); *ids = HeapAlloc(GetProcessHeap(), 0, *num * sizeof(WCHAR *));
*keys = HeapAlloc(GetProcessHeap(), 0, *num * sizeof(char *)); *guids = HeapAlloc(GetProcessHeap(), 0, *num * sizeof(GUID *));
if(!*ids || !*keys){ if(!*ids || !*guids){
HeapFree(GetProcessHeap(), 0, *ids); HeapFree(GetProcessHeap(), 0, *ids);
HeapFree(GetProcessHeap(), 0, *keys); HeapFree(GetProcessHeap(), 0, *guids);
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
} }
*def_index = 0; *def_index = 0;
hr = alsa_enum_devices(flow, *ids, *keys, num); hr = alsa_enum_devices(flow, *ids, *guids, num);
if(FAILED(hr)){ if(FAILED(hr)){
int i; int i;
for(i = 0; i < *num; ++i){ for(i = 0; i < *num; ++i){
HeapFree(GetProcessHeap(), 0, (*ids)[i]); HeapFree(GetProcessHeap(), 0, (*ids)[i]);
HeapFree(GetProcessHeap(), 0, (*keys)[i]); HeapFree(GetProcessHeap(), 0, (*guids)[i]);
} }
HeapFree(GetProcessHeap(), 0, *ids); HeapFree(GetProcessHeap(), 0, *ids);
HeapFree(GetProcessHeap(), 0, *keys); HeapFree(GetProcessHeap(), 0, *guids);
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
} }
...@@ -588,16 +666,84 @@ static snd_config_t *make_handle_underrun_config(const char *name) ...@@ -588,16 +666,84 @@ static snd_config_t *make_handle_underrun_config(const char *name)
return lconf; return lconf;
} }
HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev, static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
EDataFlow dataflow, IAudioClient **out) {
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*)&reg_guid, &size) == ERROR_SUCCESS){
if(IsEqualGUID(&reg_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; ACImpl *This;
int err; int err;
snd_pcm_stream_t stream; snd_pcm_stream_t stream;
snd_config_t *lconf; snd_config_t *lconf;
static int handle_underrun = 1; static int handle_underrun = 1;
char alsa_name[256];
EDataFlow dataflow;
TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
TRACE("\"%s\" %p %d %p\n", key, dev, dataflow, 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)); This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
if(!This) if(!This)
...@@ -620,24 +766,24 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev, ...@@ -620,24 +766,24 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev,
} }
This->dataflow = dataflow; This->dataflow = dataflow;
if(handle_underrun && ((lconf = make_handle_underrun_config(key)))){ if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
err = snd_pcm_open_lconf(&This->pcm_handle, key, stream, SND_PCM_NONBLOCK, lconf); 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", key, err); TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
snd_config_delete(lconf); snd_config_delete(lconf);
/* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */ /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
if(err == -EINVAL){ if(err == -EINVAL){
ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang." 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; handle_underrun = 0;
} }
}else }else
err = -EINVAL; err = -EINVAL;
if(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){ if(err < 0){
HeapFree(GetProcessHeap(), 0, This); 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){ switch(err){
case -EBUSY: case -EBUSY:
return AUDCLNT_E_DEVICE_IN_USE; return AUDCLNT_E_DEVICE_IN_USE;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment