Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/wineoss.drv/mmdevdrv.c | 251 ++++++++++--------------------------
dlls/wineoss.drv/oss.c | 206 +++++++++++++++++++++++++++++
dlls/wineoss.drv/unixlib.h | 19 +++
3 files changed, 293 insertions(+), 183 deletions(-)
diff --git a/dlls/wineoss.drv/mmdevdrv.c b/dlls/wineoss.drv/mmdevdrv.c
index e5206627528..fd35a439c5a 100644
--- a/dlls/wineoss.drv/mmdevdrv.c
+++ b/dlls/wineoss.drv/mmdevdrv.c
@@ -53,6 +53,7 @@
#include "wine/debug.h"
#include "wine/list.h"
+#include "wine/unicode.h"
#include "wine/unixlib.h"
#include "unixlib.h"
@@ -147,11 +148,10 @@ typedef struct _SessionMgr {
} SessionMgr;
typedef struct _OSSDevice {
+ struct list entry;
EDataFlow flow;
- char devnode[OSS_DEVNODE_SIZE];
GUID guid;
-
- struct list entry;
+ char devnode[0];
} OSSDevice;
static struct list g_devices = LIST_INIT(g_devices);
@@ -346,25 +346,6 @@ static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
RegCloseKey(key);
}
-/* dst must be large enough to hold devnode */
-static void oss_clean_devnode(char *dest, const char *devnode)
-{
- const char *dot, *slash;
- size_t len;
-
- strcpy(dest, devnode);
- dot = strrchr(dest, '.');
- if(!dot)
- return;
-
- slash = strrchr(dest, '/');
- if(slash && dot < slash)
- return;
-
- len = dot - dest;
- dest[len] = '\0';
-}
-
static int open_device(const char *device, EDataFlow flow)
{
int flags = ((flow == eRender) ? O_WRONLY : O_RDONLY) | O_NONBLOCK;
@@ -372,44 +353,6 @@ static int open_device(const char *device, EDataFlow flow)
return open(device, flags, 0);
}
-static UINT get_default_index(EDataFlow flow)
-{
- int fd = -1, err;
- UINT i;
- oss_audioinfo ai;
- char devnode[OSS_DEVNODE_SIZE];
- OSSDevice *dev_item;
-
- fd = open_device("/dev/dsp", flow);
- if(fd < 0){
- WARN("Couldn't open default device!\n");
- return 0;
- }
-
- ai.dev = -1;
- if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
- WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
- close(fd);
- return 0;
- }
-
- close(fd);
-
- TRACE("Default devnode: %s\n", ai.devnode);
- oss_clean_devnode(devnode, ai.devnode);
- i = 0;
- LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
- if(dev_item->flow == flow){
- if(!strcmp(devnode, dev_item->devnode))
- return i;
- ++i;
- }
- }
-
- WARN("Couldn't find default device! Choosing first.\n");
- return 0;
-}
-
static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
{
OSSDevice *dev_item;
@@ -419,138 +362,80 @@ static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
return NULL;
}
-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
- UINT *num, UINT *def_index)
+static void device_add(OSSDevice *oss_dev)
{
- int i, mixer_fd;
- oss_sysinfo sysinfo;
- static int print_once = 0;
+ if(get_ossdevice_from_guid(&oss_dev->guid)) /* already in list */
+ HeapFree(GetProcessHeap(), 0, oss_dev);
+ else
+ list_add_tail(&g_devices, &oss_dev->entry);
+}
- static const WCHAR outW[] = {'O','u','t',':',' ',0};
- static const WCHAR inW[] = {'I','n',':',' ',0};
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **guids_out,
+ UINT *num, UINT *def_index)
+{
+ struct get_endpoint_ids_params params;
+ GUID *guids = NULL;
+ WCHAR **ids = NULL;
+ unsigned int i;
TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
- mixer_fd = open("/dev/mixer", O_RDONLY, 0);
- if(mixer_fd < 0){
- ERR("OSS /dev/mixer doesn't seem to exist\n");
- return AUDCLNT_E_SERVICE_NOT_RUNNING;
- }
-
- if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
- close(mixer_fd);
-
- if(errno == EINVAL){
- ERR("OSS version too old, need at least OSSv4\n");
- return AUDCLNT_E_SERVICE_NOT_RUNNING;
- }
-
- ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
- return E_FAIL;
- }
-
- if(!print_once){
- TRACE("OSS sysinfo:\n");
- TRACE("product: %s\n", sysinfo.product);
- TRACE("version: %s\n", sysinfo.version);
- TRACE("versionnum: %x\n", sysinfo.versionnum);
- TRACE("numaudios: %d\n", sysinfo.numaudios);
- TRACE("nummixers: %d\n", sysinfo.nummixers);
- TRACE("numcards: %d\n", sysinfo.numcards);
- TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
- print_once = 1;
- }
-
- if(sysinfo.numaudios <= 0){
- WARN("No audio devices!\n");
- close(mixer_fd);
- return AUDCLNT_E_SERVICE_NOT_RUNNING;
- }
-
- *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
- *guids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(GUID));
-
- *num = 0;
- for(i = 0; i < sysinfo.numaudios; ++i){
- oss_audioinfo ai = {0};
- char devnode[OSS_DEVNODE_SIZE];
- OSSDevice *dev_item;
- int fd;
-
- ai.dev = i;
- if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
- WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
- strerror(errno));
- continue;
- }
-
- oss_clean_devnode(devnode, ai.devnode);
-
- /* check for duplicates */
- LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry){
- if(dev_item->flow == flow && !strcmp(devnode, dev_item->devnode))
- break;
+ params.flow = flow;
+ params.size = 1000;
+ params.endpoints = NULL;
+ do{
+ HeapFree(GetProcessHeap(), 0, params.endpoints);
+ params.endpoints = HeapAlloc(GetProcessHeap(), 0, params.size);
+ OSS_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 name_size = (strlenW(params.endpoints[i].name) + 1) * sizeof(WCHAR);
+ unsigned int dev_size = strlen(params.endpoints[i].device) + 1;
+ OSSDevice *oss_dev;
+
+ ids[i] = HeapAlloc(GetProcessHeap(), 0, name_size);
+ oss_dev = HeapAlloc(GetProcessHeap(), 0, offsetof(OSSDevice, devnode[dev_size]));
+ if(!ids[i] || !oss_dev){
+ HeapFree(GetProcessHeap, 0, oss_dev);
+ params.result = E_OUTOFMEMORY;
+ goto end;
}
- if(&dev_item->entry != &g_devices)
- continue;
-
- fd = open_device(devnode, flow);
- if(fd < 0){
- WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
- devnode, errno, strerror(errno));
- continue;
- }
- close(fd);
-
- if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
- (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
- size_t len, prefix_len;
- const WCHAR *prefix;
-
- dev_item = HeapAlloc(GetProcessHeap(), 0, sizeof(*dev_item));
-
- dev_item->flow = flow;
- get_device_guid(flow, devnode, &dev_item->guid);
- strcpy(dev_item->devnode, devnode);
-
- (*guids)[*num] = dev_item->guid;
-
- len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
- if(flow == eRender){
- prefix = outW;
- prefix_len = ARRAY_SIZE(outW) - 1;
- len += prefix_len;
- }else{
- prefix = inW;
- prefix_len = ARRAY_SIZE(inW) - 1;
- len += prefix_len;
- }
- (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
- len * sizeof(WCHAR));
- if(!(*ids)[*num]){
- for(i = 0; i < *num; ++i)
- HeapFree(GetProcessHeap(), 0, (*ids)[i]);
- HeapFree(GetProcessHeap(), 0, *ids);
- HeapFree(GetProcessHeap(), 0, *guids);
- HeapFree(GetProcessHeap(), 0, dev_item);
- close(mixer_fd);
- return E_OUTOFMEMORY;
- }
- memcpy((*ids)[*num], prefix, prefix_len * sizeof(WCHAR));
- MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
- (*ids)[*num] + prefix_len, len - prefix_len);
-
- list_add_tail(&g_devices, &dev_item->entry);
-
- (*num)++;
+ memcpy(ids[i], params.endpoints[i].name, name_size);
+ get_device_guid(flow, params.endpoints[i].device, guids + i);
+
+ oss_dev->flow = flow;
+ oss_dev->guid = guids[i];
+ memcpy(oss_dev->devnode, params.endpoints[i].device, dev_size);
+ device_add(oss_dev);
+ }
+ *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;
}
- close(mixer_fd);
-
- *def_index = get_default_index(flow);
-
- return S_OK;
+ return params.result;
}
HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev,
diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
index 9c1de25acb5..ae4e4981d2d 100644
--- a/dlls/wineoss.drv/oss.c
+++ b/dlls/wineoss.drv/oss.c
@@ -29,11 +29,13 @@
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
+#include <errno.h>
#include <sys/soundcard.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "winternl.h"
+#include "audioclient.h"
#include "wine/debug.h"
#include "wine/unixlib.h"
@@ -88,7 +90,211 @@ static NTSTATUS test_connect(void *args)
return STATUS_SUCCESS;
}
+/* dst must be large enough to hold devnode */
+static void oss_clean_devnode(char *dest, const char *devnode)
+{
+ const char *dot, *slash;
+ size_t len;
+
+ strcpy(dest, devnode);
+ dot = strrchr(dest, '.');
+ if(!dot)
+ return;
+
+ slash = strrchr(dest, '/');
+ if(slash && dot < slash)
+ return;
+
+ len = dot - dest;
+ dest[len] = '\0';
+}
+
+static int open_device(const char *device, EDataFlow flow)
+{
+ int flags = ((flow == eRender) ? O_WRONLY : O_RDONLY) | O_NONBLOCK;
+
+ return open(device, flags, 0);
+}
+
+static void get_default_device(EDataFlow flow, char device[OSS_DEVNODE_SIZE])
+{
+ int fd, err;
+ oss_audioinfo ai;
+
+ device[0] = '\0';
+ fd = open_device("/dev/dsp", flow);
+ if(fd < 0){
+ WARN("Couldn't open default device!\n");
+ return;
+ }
+
+ ai.dev = -1;
+ if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
+ WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
+ close(fd);
+ return;
+ }
+ close(fd);
+
+ TRACE("Default devnode: %s\n", ai.devnode);
+ oss_clean_devnode(device, ai.devnode);
+ return;
+}
+
+static NTSTATUS get_endpoint_ids(void *args)
+{
+ struct get_endpoint_ids_params *params = args;
+ oss_sysinfo sysinfo;
+ static int print_once = 0;
+ static const WCHAR outW[] = {'O','u','t',':',' ',0};
+ static const WCHAR inW[] = {'I','n',':',' ',0};
+ struct endpoint_info
+ {
+ WCHAR name[OSS_DEVNAME_SIZE + ARRAY_SIZE(outW)];
+ char device[OSS_DEVNODE_SIZE];
+ } *info;
+ unsigned int i, j, num, needed, name_len, device_len, default_idx = 0;
+ char default_device[OSS_DEVNODE_SIZE];
+ struct endpoint *endpoint;
+ int mixer_fd;
+ char *ptr;
+
+ mixer_fd = open("/dev/mixer", O_RDONLY, 0);
+ if(mixer_fd < 0){
+ ERR("OSS /dev/mixer doesn't seem to exist\n");
+ params->result = AUDCLNT_E_SERVICE_NOT_RUNNING;
+ return STATUS_SUCCESS;
+ }
+
+ if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
+ close(mixer_fd);
+ if(errno == EINVAL){
+ ERR("OSS version too old, need at least OSSv4\n");
+ params->result = AUDCLNT_E_SERVICE_NOT_RUNNING;
+ return STATUS_SUCCESS;
+ }
+
+ ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
+ params->result = E_FAIL;
+ return STATUS_SUCCESS;
+ }
+
+ if(!print_once){
+ TRACE("OSS sysinfo:\n");
+ TRACE("product: %s\n", sysinfo.product);
+ TRACE("version: %s\n", sysinfo.version);
+ TRACE("versionnum: %x\n", sysinfo.versionnum);
+ TRACE("numaudios: %d\n", sysinfo.numaudios);
+ TRACE("nummixers: %d\n", sysinfo.nummixers);
+ TRACE("numcards: %d\n", sysinfo.numcards);
+ TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
+ print_once = 1;
+ }
+
+ if(sysinfo.numaudios <= 0){
+ WARN("No audio devices!\n");
+ close(mixer_fd);
+ params->result = AUDCLNT_E_SERVICE_NOT_RUNNING;
+ return STATUS_SUCCESS;
+ }
+
+ info = malloc(sysinfo.numaudios * sizeof(*info));
+ if(!info){
+ close(mixer_fd);
+ params->result = E_OUTOFMEMORY;
+ return STATUS_SUCCESS;
+ }
+
+ get_default_device(params->flow, default_device);
+
+ num = 0;
+ for(i = 0; i < sysinfo.numaudios; ++i){
+ oss_audioinfo ai = {0};
+ char devnode[OSS_DEVNODE_SIZE];
+ int fd, prefix_len;
+ const WCHAR *prefix;
+
+ ai.dev = i;
+ if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
+ WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
+ strerror(errno));
+ continue;
+ }
+
+ oss_clean_devnode(devnode, ai.devnode);
+
+ /* check for duplicates */
+ for(j = 0; j < num; j++)
+ if(!strcmp(devnode, info[j].device))
+ break;
+ if(j < num)
+ continue;
+
+ fd = open_device(devnode, params->flow);
+ if(fd < 0){
+ WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
+ devnode, errno, strerror(errno));
+ continue;
+ }
+ close(fd);
+
+ if((params->flow == eCapture && !(ai.caps & PCM_CAP_INPUT)) ||
+ (params->flow == eRender && !(ai.caps & PCM_CAP_OUTPUT)))
+ continue;
+
+ strcpy(info[num].device, devnode);
+
+ if(params->flow == eRender){
+ prefix = outW;
+ prefix_len = ARRAY_SIZE(outW) - 1;
+ }else{
+ prefix = inW;
+ prefix_len = ARRAY_SIZE(inW) - 1;
+ }
+ memcpy(info[num].name, prefix, prefix_len * sizeof(WCHAR));
+ ntdll_umbstowcs(ai.name, strlen(ai.name) + 1, info[num].name + prefix_len,
+ ARRAY_SIZE(info[num].name) - prefix_len);
+ if(!strcmp(default_device, info[num].device))
+ default_idx = num;
+ num++;
+ }
+ close(mixer_fd);
+
+ needed = num * sizeof(*params->endpoints);
+ endpoint = params->endpoints;
+ ptr = (char *)(endpoint + num);
+
+ for(i = 0; i < num; i++){
+ name_len = wcslen(info[i].name) + 1;
+ device_len = strlen(info[i].device) + 1;
+ needed += name_len * sizeof(WCHAR) + ((device_len + 1) & ~1);
+
+ if(needed <= params->size){
+ endpoint->name = (WCHAR *)ptr;
+ memcpy(endpoint->name, info[i].name, name_len * sizeof(WCHAR));
+ ptr += name_len * sizeof(WCHAR);
+ endpoint->device = ptr;
+ memcpy(endpoint->device, info[i].device, device_len);
+ ptr += (device_len + 1) & ~1;
+ endpoint++;
+ }
+ }
+ free(info);
+
+ params->num = num;
+ params->default_idx = default_idx;
+
+ 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[] =
{
test_connect,
+ get_endpoint_ids,
};
diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
index d4fb5db3102..1a80794f150 100644
--- a/dlls/wineoss.drv/unixlib.h
+++ b/dlls/wineoss.drv/unixlib.h
@@ -16,6 +16,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#include "mmdeviceapi.h"
+
/* From <dlls/mmdevapi/mmdevapi.h> */
enum DriverPriority
{
@@ -30,9 +32,26 @@ struct test_connect_params
enum DriverPriority priority;
};
+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 oss_funcs
{
oss_test_connect,
+ oss_get_endpoint_ids,
};
extern unixlib_handle_t oss_handle;
--
2.25.1