mirror of
https://gitee.com/openharmony/third_party_alsa-lib
synced 2024-11-23 07:30:32 +00:00
First version of ALSA client/server
This commit is contained in:
parent
e94033141d
commit
4637f62ff5
@ -53,6 +53,6 @@ AC_OUTPUT(Makefile doc/Makefile include/Makefile src/Makefile \
|
||||
src/control/Makefile src/mixer/Makefile src/pcm/Makefile \
|
||||
src/pcm/plugin/Makefile src/rawmidi/Makefile src/timer/Makefile \
|
||||
src/hwdep/Makefile src/seq/Makefile src/instr/Makefile \
|
||||
src/compat/Makefile src/conf/Makefile \
|
||||
src/compat/Makefile src/conf/Makefile src/aserver/Makefile \
|
||||
test/Makefile utils/Makefile \
|
||||
utils/alsa-lib.spec)
|
||||
|
@ -6,7 +6,7 @@ sysinclude_HEADERS = asoundlib.h
|
||||
header_files=header.h version.h error.h control.h mixer.h pcm.h rawmidi.h \
|
||||
timer.h hwdep.h seq.h seqmid.h conv.h instr.h conf.h footer.h
|
||||
|
||||
noinst_HEADERS=$(header_files) search.h
|
||||
noinst_HEADERS=$(header_files) search.h list.h aserver.h
|
||||
|
||||
asoundlib.h: $(header_files)
|
||||
cat $^ > $@
|
||||
|
77
include/aserver.h
Normal file
77
include/aserver.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* ALSA client/server header file
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#define SND_PCM_IOCTL_MMAP_DATA _IO ('A', 0xf0)
|
||||
#define SND_PCM_IOCTL_MMAP_CONTROL _IO ('A', 0xf1)
|
||||
#define SND_PCM_IOCTL_MMAP_STATUS _IO ('A', 0xf2)
|
||||
#define SND_PCM_IOCTL_MUNMAP_DATA _IO ('A', 0xf3)
|
||||
#define SND_PCM_IOCTL_MUNMAP_CONTROL _IO ('A', 0xf4)
|
||||
#define SND_PCM_IOCTL_MUNMAP_STATUS _IO ('A', 0xf5)
|
||||
#define SND_PCM_IOCTL_CLOSE _IO ('A', 0xf6)
|
||||
|
||||
typedef struct {
|
||||
int result;
|
||||
int cmd;
|
||||
union {
|
||||
snd_pcm_info_t info;
|
||||
snd_pcm_params_t params;
|
||||
snd_pcm_params_info_t params_info;
|
||||
snd_pcm_setup_t setup;
|
||||
snd_pcm_status_t status;
|
||||
int pause;
|
||||
snd_pcm_channel_info_t channel_info;
|
||||
snd_pcm_channel_params_t channel_params;
|
||||
snd_pcm_channel_setup_t channel_setup;
|
||||
off_t frame_data;
|
||||
int frame_io;
|
||||
int link;
|
||||
snd_xfer_t read;
|
||||
snd_xfer_t write;
|
||||
snd_xferv_t readv;
|
||||
snd_xferv_t writev;
|
||||
} u;
|
||||
char data[0];
|
||||
} snd_pcm_client_shm_t;
|
||||
|
||||
#define PCM_SHM_SIZE 65536
|
||||
#define PCM_SHM_DATA_MAXLEN (PCM_SHM_SIZE - offsetof(snd_pcm_client_shm_t, data))
|
||||
|
||||
typedef struct {
|
||||
unsigned char dev_type;
|
||||
unsigned char transport_type;
|
||||
unsigned char stream;
|
||||
unsigned char mode;
|
||||
unsigned char namelen;
|
||||
char name[0];
|
||||
} snd_client_open_request_t;
|
||||
|
||||
typedef struct {
|
||||
long result;
|
||||
long cookie;
|
||||
} snd_client_open_answer_t;
|
||||
|
||||
struct cmsg_fd
|
||||
{
|
||||
int len; /* sizeof structure */
|
||||
int level; /* SOL_SOCKET */
|
||||
int type; /* SCM_RIGHTS */
|
||||
int fd; /* fd to pass */
|
||||
};
|
@ -35,3 +35,13 @@
|
||||
#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
|
||||
#endif
|
||||
|
||||
#define SND_DEV_TYPE_PCM 0
|
||||
#define SND_DEV_TYPE_CONTROL 1
|
||||
#define SND_DEV_TYPE_RAWMIDI 2
|
||||
#define SND_DEV_TYPE_TIMER 3
|
||||
#define SND_DEV_TYPE_HWDEP 4
|
||||
#define SND_DEV_TYPE_SEQ 5
|
||||
|
||||
#define SND_TRANSPORT_TYPE_SHM 0
|
||||
#define SND_TRANSPORT_TYPE_TCP 1
|
||||
|
||||
|
@ -98,28 +98,7 @@ static inline size_t bitset_count(bitset_t *bitset, size_t nbits)
|
||||
typedef struct snd_pcm snd_pcm_t;
|
||||
typedef struct snd_pcm_loopback snd_pcm_loopback_t;
|
||||
|
||||
typedef enum { SND_PCM_TYPE_HW, SND_PCM_TYPE_PLUG, SND_PCM_TYPE_MULTI } snd_pcm_type_t;
|
||||
|
||||
#if 0
|
||||
typedef struct {
|
||||
snd_pcm_t *handle;
|
||||
snd_timestamp_t tstamp;
|
||||
int result;
|
||||
union {
|
||||
char reserved[256];
|
||||
} arg;
|
||||
} snd_pcm_synchro_request_t;
|
||||
|
||||
typedef enum { SND_PCM_SYNCHRO_GO } snd_pcm_synchro_cmd_t;
|
||||
|
||||
#define snd_pcm_synchro_mode_t snd_pcm_sync_mode_t
|
||||
#define SND_PCM_SYNCHRO_MODE_NORMAL SND_PCM_SYNC_MODE_NORMAL
|
||||
#define SND_PCM_SYNCHRO_MODE_HARDWARE SND_PCM_SYNC_MODE_HARDWARE
|
||||
#define SND_PCM_SYNCHRO_MODE_RELAXED SND_PCM_SYNC_MODE_RELAXED
|
||||
int snd_pcm_synchro(snd_pcm_synchro_cmd_t cmd,
|
||||
unsigned int reqs_count, snd_pcm_synchro_request_t *reqs,
|
||||
snd_pcm_synchro_mode_t mode);
|
||||
#endif
|
||||
typedef enum { SND_PCM_TYPE_HW, SND_PCM_TYPE_PLUG, SND_PCM_TYPE_MULTI, SND_PCM_TYPE_CLIENT } snd_pcm_type_t;
|
||||
|
||||
|
||||
int snd_pcm_open(snd_pcm_t **handle, char *name,
|
||||
@ -384,6 +363,8 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
|
||||
unsigned int *binds_slave, unsigned int *binds_slave_channel,
|
||||
int close_slaves);
|
||||
|
||||
int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transport, char *name, int stream, int mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
SUBDIRS=control mixer pcm rawmidi timer hwdep seq instr compat conf
|
||||
SUBDIRS=control mixer pcm rawmidi timer hwdep seq instr compat conf aserver
|
||||
COMPATNUM=@LIBTOOL_VERSION_INFO@
|
||||
|
||||
lib_LTLIBRARIES = libasound.la
|
||||
|
8
src/aserver/Makefile.am
Normal file
8
src/aserver/Makefile.am
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
bin_PROGRAMS = aserver
|
||||
aserver_SOURCES = aserver.c
|
||||
aserver_LDADD = -lasound
|
||||
|
||||
all: aserver
|
||||
|
||||
INCLUDES=-I$(top_srcdir)/include -I$(top_srcdir)/src/pcm
|
885
src/aserver/aserver.c
Normal file
885
src/aserver/aserver.c
Normal file
@ -0,0 +1,885 @@
|
||||
/*
|
||||
* ALSA server
|
||||
* Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/shm.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/uio.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "asoundlib.h"
|
||||
#include "pcm_local.h"
|
||||
#include "aserver.h"
|
||||
|
||||
char *command;
|
||||
|
||||
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
|
||||
#define error(...) do {\
|
||||
fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
putc('\n', stderr); \
|
||||
} while (0)
|
||||
#else
|
||||
#define error(args...) do {\
|
||||
fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
|
||||
fprintf(stderr, ##args); \
|
||||
putc('\n', stderr); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define perrno(string) error("%s", strerror(errno))
|
||||
|
||||
int make_local_socket(const char *filename)
|
||||
{
|
||||
size_t l = strlen(filename);
|
||||
size_t size = offsetof(struct sockaddr_un, sun_path) + l;
|
||||
struct sockaddr_un *addr = alloca(size);
|
||||
int sock;
|
||||
|
||||
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
int result = -errno;
|
||||
perrno("socket");
|
||||
return result;
|
||||
}
|
||||
|
||||
unlink(filename);
|
||||
|
||||
addr->sun_family = AF_LOCAL;
|
||||
memcpy(addr->sun_path, filename, l);
|
||||
|
||||
if (bind(sock, (struct sockaddr *) addr, size) < 0) {
|
||||
int result = -errno;
|
||||
perrno("bind");
|
||||
return result;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int make_inet_socket(int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int sock;
|
||||
|
||||
sock = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
int result = -errno;
|
||||
perrno("socket");
|
||||
return result;
|
||||
}
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
int result = -errno;
|
||||
perrno("bind");
|
||||
return result;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int send_fd(int socket, void *data, size_t len, int fd)
|
||||
{
|
||||
int ret;
|
||||
struct cmsg_fd cmsg;
|
||||
struct msghdr msghdr;
|
||||
struct iovec vec;
|
||||
|
||||
vec.iov_base = (void *)&data;
|
||||
vec.iov_len = len;
|
||||
|
||||
cmsg.len = sizeof(cmsg);
|
||||
cmsg.level = SOL_SOCKET;
|
||||
cmsg.type = SCM_RIGHTS;
|
||||
cmsg.fd = fd;
|
||||
|
||||
msghdr.msg_name = NULL;
|
||||
msghdr.msg_namelen = 0;
|
||||
msghdr.msg_iov = &vec;
|
||||
msghdr.msg_iovlen = 1;
|
||||
msghdr.msg_control = &cmsg;
|
||||
msghdr.msg_controllen = sizeof(cmsg);
|
||||
msghdr.msg_flags = 0;
|
||||
|
||||
ret = sendmsg(socket, &msghdr, 0 );
|
||||
if (ret < 0)
|
||||
return -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct client client_t;
|
||||
|
||||
typedef struct {
|
||||
int (*open)(client_t *client, long *cookie);
|
||||
int (*cmd)(client_t *client);
|
||||
int (*close)(client_t *client);
|
||||
int (*poll_prepare)(client_t *client, struct pollfd *pfds, int pindex);
|
||||
void (*poll_events)(client_t *client, struct pollfd *pfds);
|
||||
} transport_ops_t;
|
||||
|
||||
struct client {
|
||||
struct socket {
|
||||
int fd;
|
||||
int pindex;
|
||||
int local;
|
||||
} data, ctrl;
|
||||
int transport_type;
|
||||
int dev_type;
|
||||
char name[256];
|
||||
int stream;
|
||||
int mode;
|
||||
transport_ops_t *ops;
|
||||
union {
|
||||
struct {
|
||||
snd_pcm_t *handle;
|
||||
int fd;
|
||||
int pindex;
|
||||
} pcm;
|
||||
#if 0
|
||||
struct {
|
||||
snd_ctl_t *handle;
|
||||
} control;
|
||||
struct {
|
||||
snd_rawmidi_t *handle;
|
||||
} rawmidi;
|
||||
struct {
|
||||
snd_timer_open_t *handle;
|
||||
} timer;
|
||||
struct {
|
||||
snd_hwdep_t *handle;
|
||||
} hwdep;
|
||||
struct {
|
||||
snd_seq_t *handle;
|
||||
} seq;
|
||||
#endif
|
||||
} device;
|
||||
enum { CLOSED = 0, STOPPED, NORMAL, UNKNOWN } state;
|
||||
int cookie;
|
||||
union {
|
||||
struct {
|
||||
int ctrl_id;
|
||||
void *ctrl;
|
||||
} shm;
|
||||
} transport;
|
||||
};
|
||||
|
||||
#define PENDINGS_MAX 4
|
||||
#define CLIENTS_MAX 2
|
||||
|
||||
client_t clients[CLIENTS_MAX];
|
||||
int clients_count = 0;
|
||||
|
||||
int pcm_shm_open(client_t *client, long *cookie)
|
||||
{
|
||||
int shmid;
|
||||
snd_pcm_t *pcm;
|
||||
int err;
|
||||
int result;
|
||||
err = snd_pcm_open(&pcm, client->name, client->stream, client->mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
client->device.pcm.handle = pcm;
|
||||
client->device.pcm.fd = snd_pcm_file_descriptor(pcm);
|
||||
|
||||
shmid = shmget(IPC_PRIVATE, PCM_SHM_SIZE, 0666);
|
||||
if (shmid < 0) {
|
||||
result = -errno;
|
||||
perrno("shmget");
|
||||
goto _err;
|
||||
}
|
||||
client->transport.shm.ctrl_id = shmid;
|
||||
client->transport.shm.ctrl = shmat(shmid, 0, 0);
|
||||
if (!client->transport.shm.ctrl) {
|
||||
result = -errno;
|
||||
shmctl(shmid, IPC_RMID, 0);
|
||||
perrno("shmat");
|
||||
goto _err;
|
||||
}
|
||||
*cookie = shmid;
|
||||
return 0;
|
||||
|
||||
_err:
|
||||
snd_pcm_close(pcm);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
int pcm_shm_close(client_t *client)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_client_shm_t *ctrl = client->transport.shm.ctrl;
|
||||
/* FIXME: blocking */
|
||||
err = snd_pcm_close(client->device.pcm.handle);
|
||||
ctrl->result = err;
|
||||
if (err < 0)
|
||||
perrno("snd_pcm_close");
|
||||
if (client->transport.shm.ctrl) {
|
||||
err = shmdt((void *)client->transport.shm.ctrl);
|
||||
if (err < 0)
|
||||
perrno("shmdt");
|
||||
err = shmctl(client->transport.shm.ctrl_id, IPC_RMID, 0);
|
||||
if (err < 0)
|
||||
perrno("shmctl");
|
||||
client->transport.shm.ctrl = 0;
|
||||
}
|
||||
client->state = CLOSED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcm_poll_prepare(client_t *client, struct pollfd *pfds, int pindex)
|
||||
{
|
||||
struct pollfd *pfd = &pfds[pindex];
|
||||
pfd->events = 0;
|
||||
switch (client->state) {
|
||||
case UNKNOWN:
|
||||
pfd->events = client->stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
|
||||
case NORMAL:
|
||||
pfd->fd = client->device.pcm.fd;
|
||||
client->device.pcm.pindex = pindex;
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
client->device.pcm.pindex = -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void pcm_poll_events(client_t *client, struct pollfd *pfds)
|
||||
{
|
||||
int n;
|
||||
char zero = 0;
|
||||
struct pollfd *pfd;
|
||||
if (client->device.pcm.pindex < 0)
|
||||
return;
|
||||
pfd = &pfds[client->device.pcm.pindex];
|
||||
if (pfd->revents & POLLIN) {
|
||||
client->state = NORMAL;
|
||||
n = write(client->data.fd, &zero, 1);
|
||||
if (n != 1) {
|
||||
perrno("write");
|
||||
exit(1);
|
||||
}
|
||||
} else if (pfd->revents & POLLOUT) {
|
||||
client->state = NORMAL;
|
||||
n = read(client->data.fd, &zero, 1);
|
||||
if (n != 1) {
|
||||
perrno("read");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int pcm_shm_cmd(client_t *client)
|
||||
{
|
||||
snd_pcm_client_shm_t *ctrl = client->transport.shm.ctrl;
|
||||
struct pollfd pfd;
|
||||
char buf[1];
|
||||
int err;
|
||||
int cmd;
|
||||
snd_pcm_t *pcm;
|
||||
err = read(client->ctrl.fd, buf, 1);
|
||||
if (err != 1)
|
||||
return -EBADFD;
|
||||
cmd = ctrl->cmd;
|
||||
ctrl->cmd = 0;
|
||||
pcm = client->device.pcm.handle;
|
||||
switch (cmd) {
|
||||
case SND_PCM_IOCTL_INFO:
|
||||
ctrl->result = snd_pcm_info(pcm, &ctrl->u.info);
|
||||
break;
|
||||
case SND_PCM_IOCTL_PARAMS:
|
||||
ctrl->result = snd_pcm_params(pcm, &ctrl->u.params);
|
||||
break;
|
||||
case SND_PCM_IOCTL_PARAMS_INFO:
|
||||
ctrl->result = snd_pcm_params_info(pcm, &ctrl->u.params_info);
|
||||
break;
|
||||
case SND_PCM_IOCTL_SETUP:
|
||||
ctrl->result = snd_pcm_setup(pcm, &ctrl->u.setup);
|
||||
break;
|
||||
case SND_PCM_IOCTL_STATUS:
|
||||
ctrl->result = snd_pcm_status(pcm, &ctrl->u.status);
|
||||
break;
|
||||
case SND_PCM_IOCTL_FRAME_IO:
|
||||
ctrl->result = snd_pcm_frame_io(pcm, ctrl->u.frame_io);
|
||||
break;
|
||||
case SND_PCM_IOCTL_PREPARE:
|
||||
ctrl->result = snd_pcm_prepare(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_GO:
|
||||
ctrl->result = snd_pcm_go(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_FLUSH:
|
||||
ctrl->result = snd_pcm_flush(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_DRAIN:
|
||||
ctrl->result = snd_pcm_drain(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_PAUSE:
|
||||
ctrl->result = snd_pcm_pause(pcm, ctrl->u.pause);
|
||||
break;
|
||||
case SND_PCM_IOCTL_CHANNEL_INFO:
|
||||
ctrl->result = snd_pcm_channel_info(pcm, &ctrl->u.channel_info);
|
||||
break;
|
||||
case SND_PCM_IOCTL_CHANNEL_PARAMS:
|
||||
ctrl->result = snd_pcm_channel_params(pcm, &ctrl->u.channel_params);
|
||||
break;
|
||||
case SND_PCM_IOCTL_CHANNEL_SETUP:
|
||||
ctrl->result = snd_pcm_channel_setup(pcm, &ctrl->u.channel_setup);
|
||||
break;
|
||||
case SND_PCM_IOCTL_WRITE_FRAMES:
|
||||
{
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
client->state = UNKNOWN;
|
||||
maxsize = snd_pcm_bytes_to_frames(pcm, maxsize);
|
||||
if (ctrl->u.write.count > maxsize) {
|
||||
ctrl->result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
/* FIXME: blocking */
|
||||
ctrl->result = snd_pcm_write(pcm, ctrl->data, ctrl->u.write.count);
|
||||
break;
|
||||
}
|
||||
case SND_PCM_IOCTL_READ_FRAMES:
|
||||
{
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
client->state = UNKNOWN;
|
||||
maxsize = snd_pcm_bytes_to_frames(pcm, maxsize);
|
||||
if (ctrl->u.read.count > maxsize) {
|
||||
ctrl->result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
/* FIXME: blocking */
|
||||
ctrl->result = snd_pcm_read(pcm, ctrl->data, ctrl->u.read.count);
|
||||
break;
|
||||
}
|
||||
case SND_PCM_IOCTL_WRITEV_FRAMES:
|
||||
{
|
||||
/* FIXME: interleaved */
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
unsigned long k;
|
||||
struct iovec *vector;
|
||||
size_t vecsize;
|
||||
char *base;
|
||||
int bits_per_sample = snd_pcm_samples_to_bytes(pcm, 8);
|
||||
client->state = UNKNOWN;
|
||||
vecsize = ctrl->u.writev.count * sizeof(struct iovec);
|
||||
if (vecsize > maxsize) {
|
||||
ctrl->result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
maxsize -= vecsize;
|
||||
vector = (struct iovec *) ctrl->data;
|
||||
base = ctrl->data + vecsize;
|
||||
for (k = 0; k < ctrl->u.writev.count; ++k) {
|
||||
unsigned long ofs = (unsigned long) vector[k].iov_base;
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
if (ofs + len > maxsize)
|
||||
return -EFAULT;
|
||||
vector[k].iov_base = base + ofs;
|
||||
}
|
||||
/* FIXME: blocking */
|
||||
ctrl->result = snd_pcm_writev(pcm, vector, ctrl->u.writev.count);
|
||||
break;
|
||||
}
|
||||
case SND_PCM_IOCTL_READV_FRAMES:
|
||||
{
|
||||
/* FIXME: interleaved */
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
unsigned long k;
|
||||
struct iovec *vector;
|
||||
size_t vecsize;
|
||||
char *base;
|
||||
int bits_per_sample = snd_pcm_samples_to_bytes(pcm, 8);
|
||||
client->state = UNKNOWN;
|
||||
vecsize = ctrl->u.readv.count * sizeof(struct iovec);
|
||||
if (vecsize > maxsize) {
|
||||
ctrl->result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
maxsize -= vecsize;
|
||||
vector = (struct iovec *) ctrl->data;
|
||||
base = ctrl->data + vecsize;
|
||||
for (k = 0; k < ctrl->u.readv.count; ++k) {
|
||||
unsigned long ofs = (unsigned long) vector[k].iov_base;
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
if (ofs + len > maxsize)
|
||||
return -EFAULT;
|
||||
vector[k].iov_base = base + ofs;
|
||||
}
|
||||
/* FIXME: blocking */
|
||||
ctrl->result = snd_pcm_readv(pcm, vector, ctrl->u.readv.count);
|
||||
break;
|
||||
}
|
||||
case SND_PCM_IOCTL_FRAME_DATA:
|
||||
client->state = UNKNOWN;
|
||||
ctrl->result = snd_pcm_frame_data(pcm, ctrl->u.frame_data);
|
||||
break;
|
||||
case SND_PCM_IOCTL_LINK:
|
||||
{
|
||||
int k;
|
||||
for (k = 0; k < clients_count; ++k) {
|
||||
if (clients[k].state == CLOSED)
|
||||
continue;
|
||||
if (clients[k].data.fd == ctrl->u.link) {
|
||||
ctrl->result = snd_pcm_link(pcm, clients[k].device.pcm.handle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctrl->result = -EBADFD;
|
||||
break;
|
||||
}
|
||||
case SND_PCM_IOCTL_UNLINK:
|
||||
ctrl->result = snd_pcm_unlink(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_MMAP_DATA:
|
||||
case SND_PCM_IOCTL_MMAP_CONTROL:
|
||||
case SND_PCM_IOCTL_MMAP_STATUS:
|
||||
{
|
||||
pfd.fd = client->ctrl.fd;
|
||||
pfd.events = POLLHUP;
|
||||
if (poll(&pfd, 1, 0) == 1)
|
||||
return -EBADFD;
|
||||
err = send_fd(client->ctrl.fd, buf, 1, client->device.pcm.fd);
|
||||
if (err != 1)
|
||||
return -EBADFD;
|
||||
ctrl->result = 0;
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
case SND_PCM_IOCTL_MUNMAP_DATA:
|
||||
case SND_PCM_IOCTL_MUNMAP_CONTROL:
|
||||
case SND_PCM_IOCTL_MUNMAP_STATUS:
|
||||
ctrl->result = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case SND_PCM_IOCTL_CLOSE:
|
||||
client->ops->close(client);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Bogus cmd: %x\n", ctrl->cmd);
|
||||
ctrl->result = -ENOSYS;
|
||||
}
|
||||
pfd.fd = client->ctrl.fd;
|
||||
pfd.events = POLLHUP;
|
||||
if (poll(&pfd, 1, 0) == 1)
|
||||
return -EBADFD;
|
||||
err = write(client->ctrl.fd, buf, 1);
|
||||
if (err != 1)
|
||||
return -EBADFD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
transport_ops_t pcm_shm_ops = {
|
||||
open: pcm_shm_open,
|
||||
cmd: pcm_shm_cmd,
|
||||
close: pcm_shm_close,
|
||||
poll_prepare: pcm_poll_prepare,
|
||||
poll_events: pcm_poll_events,
|
||||
};
|
||||
|
||||
void snd_client_open(client_t *client)
|
||||
{
|
||||
int err;
|
||||
snd_client_open_request_t req;
|
||||
snd_client_open_answer_t ans;
|
||||
char *name;
|
||||
memset(&ans, 0, sizeof(ans));
|
||||
err = read(client->ctrl.fd, &req, sizeof(req));
|
||||
if (err < 0) {
|
||||
perrno("read");
|
||||
exit(1);
|
||||
}
|
||||
if (err != sizeof(req)) {
|
||||
ans.result = -EINVAL;
|
||||
goto _answer;
|
||||
}
|
||||
name = alloca(req.namelen);
|
||||
err = read(client->ctrl.fd, name, req.namelen);
|
||||
if (err < 0) {
|
||||
perrno("read");
|
||||
exit(1);
|
||||
}
|
||||
if (err != req.namelen) {
|
||||
ans.result = -EINVAL;
|
||||
goto _answer;
|
||||
}
|
||||
|
||||
switch (req.dev_type) {
|
||||
case SND_DEV_TYPE_PCM:
|
||||
switch (req.transport_type) {
|
||||
case SND_TRANSPORT_TYPE_SHM:
|
||||
client->ops = &pcm_shm_ops;
|
||||
break;
|
||||
default:
|
||||
ans.result = -EINVAL;
|
||||
goto _answer;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ans.result = -EINVAL;
|
||||
goto _answer;
|
||||
}
|
||||
|
||||
name[req.namelen] = '\0';
|
||||
|
||||
client->transport_type = req.transport_type;
|
||||
strcpy(client->name, name);
|
||||
client->stream = req.stream;
|
||||
client->mode = req.mode;
|
||||
|
||||
err = client->ops->open(client, &ans.cookie);
|
||||
if (err < 0) {
|
||||
ans.result = err;
|
||||
} else {
|
||||
client->state = STOPPED;
|
||||
ans.result = client->data.fd;
|
||||
}
|
||||
|
||||
_answer:
|
||||
ans.result = htonl(ans.result);
|
||||
ans.cookie = htonl(ans.cookie);
|
||||
err = write(client->ctrl.fd, &ans, sizeof(ans));
|
||||
if (err != sizeof(ans)) {
|
||||
perrno("write");
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int server(char *sockname, int port)
|
||||
{
|
||||
typedef struct {
|
||||
int fd;
|
||||
int pindex;
|
||||
int local;
|
||||
} master_t;
|
||||
master_t masters[2];
|
||||
int masters_count = 0;
|
||||
typedef struct {
|
||||
int fd;
|
||||
int pindex;
|
||||
uint32_t cookie;
|
||||
int local;
|
||||
} pending_t;
|
||||
pending_t pendings[PENDINGS_MAX];
|
||||
int pendings_count = 0;
|
||||
struct pollfd pfds[CLIENTS_MAX * 3 + 16];
|
||||
int pfds_count;
|
||||
int err;
|
||||
int k;
|
||||
|
||||
if (sockname) {
|
||||
int master = make_local_socket(sockname);
|
||||
if (master < 0)
|
||||
return master;
|
||||
masters[masters_count].fd = master;
|
||||
masters[masters_count].local = 1;
|
||||
masters_count++;
|
||||
}
|
||||
if (port >= 0) {
|
||||
int master = make_inet_socket(port);
|
||||
if (master < 0)
|
||||
return master;
|
||||
masters[masters_count].fd = master;
|
||||
masters[masters_count].local = 0;
|
||||
masters_count++;
|
||||
}
|
||||
|
||||
if (masters_count == 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (k = 0; k < masters_count; ++k) {
|
||||
master_t *master = &masters[k];
|
||||
if (fcntl(master->fd, F_SETFL, O_NONBLOCK) < 0) {
|
||||
int result = -errno;
|
||||
perrno("fcntl");
|
||||
return result;
|
||||
}
|
||||
if (listen(master->fd, 4) < 0) {
|
||||
int result = -errno;
|
||||
perrno("listen");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
for (k = 0; k < PENDINGS_MAX; ++k) {
|
||||
pendings[k].fd = -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
pfds_count = 0;
|
||||
|
||||
/* Prepare to poll masters */
|
||||
if (pendings_count < PENDINGS_MAX) {
|
||||
for (k = 0; k < masters_count; ++k) {
|
||||
master_t *master = &masters[k];
|
||||
master->pindex = pfds_count;
|
||||
pfds[pfds_count].fd = master->fd;
|
||||
pfds[pfds_count].events = POLLIN;
|
||||
pfds_count++;
|
||||
}
|
||||
} else {
|
||||
for (k = 0; k < masters_count; ++k) {
|
||||
master_t *master = &masters[k];
|
||||
master->pindex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare to poll pendings */
|
||||
for (k = 0; k < PENDINGS_MAX; ++k) {
|
||||
pending_t *pending = &pendings[k];
|
||||
if (pending->fd < 0 ||
|
||||
pending->cookie != 0) {
|
||||
pending->pindex = -1;
|
||||
continue;
|
||||
}
|
||||
pending->pindex = pfds_count;
|
||||
pfds[pfds_count].fd = pending->fd;
|
||||
pfds[pfds_count].events = POLLHUP;
|
||||
if (pendings_count < PENDINGS_MAX &&
|
||||
clients_count < CLIENTS_MAX)
|
||||
pfds[pfds_count].events |= POLLIN;
|
||||
pfds_count++;
|
||||
}
|
||||
|
||||
/* Prepare to poll clients */
|
||||
for (k = 0; k < clients_count; ++k) {
|
||||
client_t *client = &clients[k];
|
||||
client->data.pindex = pfds_count;
|
||||
pfds[pfds_count].fd = client->data.fd;
|
||||
pfds[pfds_count].events = POLLHUP;
|
||||
pfds_count++;
|
||||
|
||||
client->ctrl.pindex = pfds_count;
|
||||
pfds[pfds_count].fd = client->ctrl.fd;
|
||||
pfds[pfds_count].events = POLLIN | POLLHUP;
|
||||
pfds_count++;
|
||||
}
|
||||
|
||||
/* Prepare to poll devices */
|
||||
for (k = 0; k < clients_count; ++k) {
|
||||
client_t *client = &clients[k];
|
||||
int n;
|
||||
if (client->state == CLOSED)
|
||||
continue;
|
||||
n = client->ops->poll_prepare(client, pfds, pfds_count);
|
||||
pfds_count += n;
|
||||
}
|
||||
|
||||
|
||||
/* Poll */
|
||||
do {
|
||||
err = poll(pfds, pfds_count, 1000);
|
||||
} while (err == 0);
|
||||
if (err < 0) {
|
||||
int result = -errno;
|
||||
perrno("poll");
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Handle clients events */
|
||||
for (k = clients_count - 1; k >= 0; --k) {
|
||||
client_t *client;
|
||||
struct pollfd *data_pfd = &pfds[clients[k].data.pindex];
|
||||
struct pollfd *ctrl_pfd = &pfds[clients[k].ctrl.pindex];
|
||||
if (!data_pfd->revents && !ctrl_pfd->revents)
|
||||
continue;
|
||||
client = &clients[k];
|
||||
if ((data_pfd->revents & POLLHUP) ||
|
||||
(ctrl_pfd->revents & POLLHUP)) {
|
||||
if (client->state != CLOSED) {
|
||||
client->ops->close(client);
|
||||
}
|
||||
close(client->data.fd);
|
||||
if (client->ctrl.fd >= 0)
|
||||
close(client->ctrl.fd);
|
||||
memmove(client, client + 1,
|
||||
(clients_count - k) * sizeof(client_t));
|
||||
clients_count--;
|
||||
continue;
|
||||
}
|
||||
if (ctrl_pfd->revents & POLLIN) {
|
||||
if (client->state == CLOSED)
|
||||
snd_client_open(client);
|
||||
else
|
||||
client->ops->cmd(client);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle device events */
|
||||
for (k = 0; k < clients_count; ++k) {
|
||||
client_t *client = &clients[k];
|
||||
client->ops->poll_events(client, pfds);
|
||||
}
|
||||
|
||||
/* Handle pending events */
|
||||
for (k = 0; k < PENDINGS_MAX; ++k) {
|
||||
struct pollfd *pfd;
|
||||
uint32_t cookie;
|
||||
int j;
|
||||
pending_t *pending = &pendings[k];
|
||||
client_t *client;
|
||||
int remove = 0;
|
||||
if (pending->pindex < 0)
|
||||
continue;
|
||||
pfd = &pfds[pending->pindex];
|
||||
if (!pfd->revents)
|
||||
continue;
|
||||
if (pfd->revents & POLLHUP)
|
||||
remove = 1;
|
||||
else {
|
||||
if (clients_count >= CLIENTS_MAX)
|
||||
continue;
|
||||
err = read(pfd->fd, &cookie, sizeof(cookie));
|
||||
if (err != sizeof(cookie))
|
||||
remove = 1;
|
||||
else {
|
||||
err = write(pfd->fd, &cookie, sizeof(cookie));
|
||||
if (err != sizeof(cookie))
|
||||
remove = 1;
|
||||
}
|
||||
}
|
||||
if (remove) {
|
||||
close(pending->fd);
|
||||
pending->fd = -1;
|
||||
pendings_count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < PENDINGS_MAX; ++j) {
|
||||
if (pendings[j].cookie == cookie)
|
||||
break;
|
||||
}
|
||||
if (j == PENDINGS_MAX) {
|
||||
pendings[k].cookie = cookie;
|
||||
continue;
|
||||
}
|
||||
|
||||
client = &clients[clients_count];
|
||||
memset(client, 0, sizeof(*client));
|
||||
client->data.fd = pendings[j].fd;
|
||||
client->data.local = pendings[j].local;
|
||||
client->ctrl.fd = pendings[k].fd;
|
||||
client->ctrl.local = pendings[k].local;
|
||||
client->state = CLOSED;
|
||||
clients_count++;
|
||||
pendings[j].fd = -1;
|
||||
pendings[k].fd = -1;
|
||||
pendings_count -= 2;
|
||||
}
|
||||
|
||||
/* Handle master events */
|
||||
for (k = 0; k < masters_count; ++k) {
|
||||
struct pollfd *pfd;
|
||||
master_t *master;
|
||||
int sock;
|
||||
if (pendings_count >= PENDINGS_MAX)
|
||||
break;
|
||||
master = &masters[k];
|
||||
if (master->pindex < 0)
|
||||
continue;
|
||||
pfd = &pfds[master->pindex];
|
||||
if (!pfd->revents)
|
||||
continue;
|
||||
|
||||
sock = accept(master->fd, 0, 0);
|
||||
if (sock < 0) {
|
||||
int result = -errno;
|
||||
perrno("accept");
|
||||
return result;
|
||||
} else {
|
||||
int j;
|
||||
for (j = 0; j < PENDINGS_MAX; ++j) {
|
||||
if (pendings[j].fd < 0)
|
||||
break;
|
||||
}
|
||||
assert(j < PENDINGS_MAX);
|
||||
pendings[j].fd = sock;
|
||||
pendings[j].local = master->local;
|
||||
pendings[j].cookie = 0;
|
||||
pendings_count++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void usage()
|
||||
{
|
||||
fprintf(stderr, "\
|
||||
Usage: %s [OPTIONS]
|
||||
|
||||
--help help
|
||||
--version print current version
|
||||
-l,--local SOCKNAME local socket name
|
||||
-p,--port PORT port number
|
||||
", command);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static struct option long_options[] = {
|
||||
{"local", 1, 0, 'l'},
|
||||
{"port", 1, 0, 'p'},
|
||||
{"help", 0, 0, 'h'}
|
||||
};
|
||||
int c;
|
||||
char *local = NULL;
|
||||
int port = -1;
|
||||
command = argv[0];
|
||||
while ((c = getopt_long(argc, argv, "hl:p:", long_options, 0)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage();
|
||||
return 0;
|
||||
case 'l':
|
||||
local = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Try `%s --help' for more information\n", command);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!local && port == -1) {
|
||||
fprintf(stderr, "%s: you need to specify at least one master socket\n", command);
|
||||
return 1;
|
||||
}
|
||||
|
||||
server(local, port);
|
||||
return 0;
|
||||
}
|
@ -3,7 +3,7 @@ SUBDIRS = plugin
|
||||
EXTRA_LTLIBRARIES = libpcm.la
|
||||
|
||||
libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plug.c pcm_common.c \
|
||||
pcm_misc.c pcm_mmap.c pcm_multi.c
|
||||
pcm_misc.c pcm_mmap.c pcm_multi.c pcm_client.c
|
||||
libpcm_la_LIBADD = plugin/libpcmplugin.la
|
||||
noinst_HEADERS = pcm_local.h
|
||||
|
||||
|
@ -736,6 +736,62 @@ _free:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int _snd_pcm_open_client(snd_pcm_t **handlep, snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
char *socket = NULL;
|
||||
char *name = NULL;
|
||||
char *host = NULL;
|
||||
long port = -1;
|
||||
int err;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "socket") == 0) {
|
||||
err = snd_config_string_get(n, &socket);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "host") == 0) {
|
||||
err = snd_config_string_get(n, &host);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "port") == 0) {
|
||||
err = snd_config_integer_get(n, &port);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "name") == 0) {
|
||||
err = snd_config_string_get(n, &name);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
if (socket) {
|
||||
if (port >= 0 || host)
|
||||
return -EINVAL;
|
||||
return snd_pcm_client_create(handlep, socket, -1, SND_TRANSPORT_TYPE_SHM, name, stream, mode);
|
||||
} else {
|
||||
if (port < 0 || !name)
|
||||
return -EINVAL;
|
||||
return snd_pcm_client_create(handlep, host, port, SND_TRANSPORT_TYPE_SHM, name, stream, mode);
|
||||
}
|
||||
}
|
||||
|
||||
int snd_pcm_open(snd_pcm_t **handlep, char *name,
|
||||
int stream, int mode)
|
||||
{
|
||||
@ -777,6 +833,8 @@ int snd_pcm_open(snd_pcm_t **handlep, char *name,
|
||||
return _snd_pcm_open_plug(handlep, pcm_conf, stream, mode);
|
||||
else if (strcmp(str, "multi") == 0)
|
||||
return _snd_pcm_open_multi(handlep, pcm_conf, stream, mode);
|
||||
else if (strcmp(str, "client") == 0)
|
||||
return _snd_pcm_open_client(handlep, pcm_conf, stream, mode);
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
878
src/pcm/pcm_client.c
Normal file
878
src/pcm/pcm_client.c
Normal file
@ -0,0 +1,878 @@
|
||||
/*
|
||||
* PCM - Client
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include "pcm_local.h"
|
||||
#include "aserver.h"
|
||||
|
||||
typedef struct {
|
||||
snd_pcm_t *handle;
|
||||
int data_fd;
|
||||
int ctrl_fd;
|
||||
union {
|
||||
struct {
|
||||
void *ctrl;
|
||||
} shm;
|
||||
} u;
|
||||
} snd_pcm_client_t;
|
||||
|
||||
int receive_fd(int socket, void *data, size_t len, int *fd)
|
||||
{
|
||||
int ret;
|
||||
struct cmsg_fd cmsg;
|
||||
struct msghdr msghdr;
|
||||
struct iovec vec;
|
||||
|
||||
vec.iov_base = (void *)&data;
|
||||
vec.iov_len = len;
|
||||
|
||||
cmsg.len = sizeof(cmsg);
|
||||
cmsg.level = SOL_SOCKET;
|
||||
cmsg.type = SCM_RIGHTS;
|
||||
cmsg.fd = -1;
|
||||
|
||||
msghdr.msg_name = NULL;
|
||||
msghdr.msg_namelen = 0;
|
||||
msghdr.msg_iov = &vec;
|
||||
msghdr.msg_iovlen = 1;
|
||||
msghdr.msg_control = &cmsg;
|
||||
msghdr.msg_controllen = sizeof(cmsg);
|
||||
msghdr.msg_flags = 0;
|
||||
|
||||
ret = recvmsg(socket, &msghdr, 0);
|
||||
if (ret < 0)
|
||||
return -errno;
|
||||
*fd = cmsg.fd;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clean_state(snd_pcm_client_t *client)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
int err;
|
||||
char buf[1];
|
||||
pfd.fd = client->data_fd;
|
||||
switch (client->handle->stream) {
|
||||
case SND_PCM_STREAM_PLAYBACK:
|
||||
pfd.events = POLLOUT;
|
||||
while (1) {
|
||||
err = poll(&pfd, 1, 0);
|
||||
if (err == 0)
|
||||
break;
|
||||
assert(err > 0);
|
||||
err = write(client->data_fd, buf, 1);
|
||||
assert(err == 1);
|
||||
}
|
||||
break;
|
||||
case SND_PCM_STREAM_CAPTURE:
|
||||
pfd.events = POLLIN;
|
||||
while (1) {
|
||||
err = poll(&pfd, 1, 0);
|
||||
if (err == 0)
|
||||
break;
|
||||
assert(err > 0);
|
||||
err = read(client->data_fd, buf, 1);
|
||||
assert(err == 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_action(snd_pcm_client_t *client)
|
||||
{
|
||||
int err;
|
||||
char buf[1];
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
err = write(client->ctrl_fd, buf, 1);
|
||||
if (err != 1)
|
||||
return -EBADFD;
|
||||
err = read(client->ctrl_fd, buf, 1);
|
||||
if (err != 1)
|
||||
return -EBADFD;
|
||||
if (ctrl->cmd) {
|
||||
fprintf(stderr, "Server has not done the cmd\n");
|
||||
return -EBADFD;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_action_fd(snd_pcm_client_t *client)
|
||||
{
|
||||
int err;
|
||||
char buf[1];
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int fd;
|
||||
err = write(client->ctrl_fd, buf, 1);
|
||||
if (err != 1)
|
||||
return -EBADFD;
|
||||
err = receive_fd(client->ctrl_fd, buf, 1, &fd);
|
||||
if (err != 1)
|
||||
return -EBADFD;
|
||||
if (ctrl->cmd) {
|
||||
fprintf(stderr, "Server has not done the cmd\n");
|
||||
return -EBADFD;
|
||||
}
|
||||
if (ctrl->result < 0)
|
||||
return ctrl->result;
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_close(void *private)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int result;
|
||||
ctrl->cmd = SND_PCM_IOCTL_CLOSE;
|
||||
result = snd_pcm_client_shm_action(client);
|
||||
if (result >= 0)
|
||||
result = ctrl->result;
|
||||
shmdt((void *)ctrl);
|
||||
close(client->data_fd);
|
||||
close(client->ctrl_fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_nonblock(void *private, int nonblock)
|
||||
{
|
||||
/* FIXME */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_info(void *private, snd_pcm_info_t * info)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
// ctrl->u.info = *info;
|
||||
ctrl->cmd = SND_PCM_IOCTL_INFO;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
memcpy(info, &ctrl->u.info, sizeof(*info));
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_params_info(void *private, snd_pcm_params_info_t * info)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_PARAMS_INFO;
|
||||
ctrl->u.params_info = *info;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*info = ctrl->u.params_info;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_params(void *private, snd_pcm_params_t * params)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_PARAMS;
|
||||
ctrl->u.params = *params;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*params = ctrl->u.params;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_setup(void *private, snd_pcm_setup_t * setup)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_SETUP;
|
||||
// ctrl->u.setup = *setup;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*setup = ctrl->u.setup;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_channel_info(void *private, snd_pcm_channel_info_t * info)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_CHANNEL_INFO;
|
||||
ctrl->u.channel_info = *info;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*info = ctrl->u.channel_info;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_channel_params(void *private, snd_pcm_channel_params_t * params)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_CHANNEL_PARAMS;
|
||||
ctrl->u.channel_params = *params;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*params = ctrl->u.channel_params;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_channel_setup(void *private, snd_pcm_channel_setup_t * setup)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_CHANNEL_SETUP;
|
||||
ctrl->u.channel_setup = *setup;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*setup = ctrl->u.channel_setup;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_status(void *private, snd_pcm_status_t * status)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_STATUS;
|
||||
// ctrl->u.status = *status;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*status = ctrl->u.status;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_state(void *private)
|
||||
{
|
||||
snd_pcm_status_t status;
|
||||
int err = snd_pcm_client_shm_status(private, &status);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return status.state;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_shm_frame_io(void *private, int update)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_FRAME_IO;
|
||||
ctrl->u.frame_io = update;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_prepare(void *private)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_PREPARE;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_go(void *private)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_GO;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_drain(void *private)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_DRAIN;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_flush(void *private)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_FLUSH;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_pause(void *private, int enable)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_PAUSE;
|
||||
ctrl->u.pause = enable;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_shm_frame_data(void *private, off_t offset)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_FRAME_DATA;
|
||||
ctrl->u.frame_data = offset;
|
||||
clean_state(client);
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_shm_write(void *private, snd_timestamp_t *tstamp, const void *buffer, size_t size)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
size_t bytes = snd_pcm_frames_to_bytes(client->handle, size);
|
||||
int err;
|
||||
if (bytes > maxsize)
|
||||
return -EINVAL;
|
||||
ctrl->cmd = SND_PCM_IOCTL_WRITE_FRAMES;
|
||||
// ctrl->u.write.tstamp = *tstamp;
|
||||
ctrl->u.write.count = size;
|
||||
memcpy(ctrl->data, buffer, bytes);
|
||||
clean_state(client);
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_shm_writev(void *private, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count)
|
||||
{
|
||||
/* FIXME: interleaved */
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
size_t vecsize = count * sizeof(struct iovec);
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
int bits_per_sample = client->handle->bits_per_sample;
|
||||
char *base;
|
||||
struct iovec *vec;
|
||||
unsigned long k;
|
||||
size_t ofs;
|
||||
int err;
|
||||
if (vecsize > maxsize)
|
||||
return -EINVAL;
|
||||
maxsize -= vecsize;
|
||||
ctrl->cmd = SND_PCM_IOCTL_WRITEV_FRAMES;
|
||||
// ctrl->u.writev.tstamp = *tstamp;
|
||||
ctrl->u.writev.count = count;
|
||||
memcpy(ctrl->data, vector, vecsize);
|
||||
vec = (struct iovec *) ctrl->data;
|
||||
base = ctrl->data + vecsize;
|
||||
ofs = 0;
|
||||
for (k = 0; k < count; ++k) {
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
memcpy(base + ofs, vector[k].iov_base, len);
|
||||
vec[k].iov_base = (void *) ofs;
|
||||
ofs += len;
|
||||
}
|
||||
clean_state(client);
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_shm_read(void *private, snd_timestamp_t *tstamp, void *buffer, size_t size)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
size_t bytes = snd_pcm_frames_to_bytes(client->handle, size);
|
||||
int err;
|
||||
if (bytes > maxsize)
|
||||
return -EINVAL;
|
||||
ctrl->cmd = SND_PCM_IOCTL_READ_FRAMES;
|
||||
// ctrl->u.read.tstamp = *tstamp;
|
||||
ctrl->u.read.count = size;
|
||||
clean_state(client);
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (ctrl->result <= 0)
|
||||
return ctrl->result;
|
||||
bytes = snd_pcm_frames_to_bytes(client->handle, ctrl->result);
|
||||
memcpy(buffer, ctrl->data, bytes);
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_client_shm_readv(void *private, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count)
|
||||
{
|
||||
/* FIXME: interleaved */
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
size_t vecsize = count * sizeof(struct iovec);
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
int bits_per_sample = client->handle->bits_per_sample;
|
||||
char *base;
|
||||
struct iovec *vec;
|
||||
unsigned long k;
|
||||
size_t ofs, bytes;
|
||||
int err;
|
||||
if (vecsize > maxsize)
|
||||
return -EINVAL;
|
||||
maxsize -= vecsize;
|
||||
ctrl->cmd = SND_PCM_IOCTL_WRITEV_FRAMES;
|
||||
// ctrl->u.writev.tstamp = *tstamp;
|
||||
ctrl->u.writev.count = count;
|
||||
memcpy(ctrl->data, vector, vecsize);
|
||||
vec = (struct iovec *) ctrl->data;
|
||||
base = ctrl->data + vecsize;
|
||||
ofs = 0;
|
||||
for (k = 0; k < count; ++k) {
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
vec[k].iov_base = (void *) ofs;
|
||||
ofs += len;
|
||||
}
|
||||
clean_state(client);
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (ctrl->result <= 0)
|
||||
return ctrl->result;
|
||||
bytes = snd_pcm_frames_to_bytes(client->handle, ctrl->result);
|
||||
ofs = 0;
|
||||
for (k = 0; k < count; ++k) {
|
||||
/* FIXME: optimize partial read */
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
memcpy(vector[k].iov_base, base + ofs, len);
|
||||
ofs += len;
|
||||
}
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_mmap_status(void *private, snd_pcm_mmap_status_t **status)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
void *ptr;
|
||||
int fd;
|
||||
ctrl->cmd = SND_PCM_IOCTL_MMAP_STATUS;
|
||||
fd = snd_pcm_client_shm_action_fd(client);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
ptr = mmap(NULL, sizeof(snd_pcm_mmap_status_t), PROT_READ, MAP_FILE|MAP_SHARED,
|
||||
fd, SND_PCM_MMAP_OFFSET_STATUS);
|
||||
close(fd);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*status = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_mmap_control(void *private, snd_pcm_mmap_control_t **control)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
void *ptr;
|
||||
int fd;
|
||||
ctrl->cmd = SND_PCM_IOCTL_MMAP_CONTROL;
|
||||
fd = snd_pcm_client_shm_action_fd(client);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
|
||||
fd, SND_PCM_MMAP_OFFSET_CONTROL);
|
||||
close(fd);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*control = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_mmap_data(void *private, void **buffer, size_t bsize ATTRIBUTE_UNUSED)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
void *ptr;
|
||||
int prot;
|
||||
int fd;
|
||||
ctrl->cmd = SND_PCM_IOCTL_MMAP_DATA;
|
||||
fd = snd_pcm_client_shm_action_fd(client);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
prot = client->handle->stream == SND_PCM_STREAM_PLAYBACK ? PROT_WRITE : PROT_READ;
|
||||
ptr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED,
|
||||
fd, SND_PCM_MMAP_OFFSET_DATA);
|
||||
close(fd);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*buffer = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_munmap_status(void *private ATTRIBUTE_UNUSED, snd_pcm_mmap_status_t *status ATTRIBUTE_UNUSED)
|
||||
{
|
||||
#if 0
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_MUNMAP_STATUS;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
#else
|
||||
if (munmap(status, sizeof(*status)) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_munmap_control(void *private ATTRIBUTE_UNUSED, snd_pcm_mmap_control_t *control ATTRIBUTE_UNUSED)
|
||||
{
|
||||
#if 0
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_MUNMAP_CONTROL;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
#else
|
||||
if (munmap(control, sizeof(*control)) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_munmap_data(void *private ATTRIBUTE_UNUSED, void *buffer, size_t bsize)
|
||||
{
|
||||
#if 0
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_MUNMAP_DATA;
|
||||
err = snd_pcm_client_shm_action(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
#else
|
||||
if (munmap(buffer, bsize) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int snd_pcm_client_file_descriptor(void *private)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
return client->data_fd;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_channels_mask(void *private ATTRIBUTE_UNUSED,
|
||||
bitset_t *client_vmask ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_pcm_client_dump(void *private, FILE *fp)
|
||||
{
|
||||
snd_pcm_client_t *client = (snd_pcm_client_t*) private;
|
||||
snd_pcm_t *handle = client->handle;
|
||||
fprintf(fp, "Client PCM\n");
|
||||
if (handle->valid_setup) {
|
||||
fprintf(fp, "\nIts setup is:\n");
|
||||
snd_pcm_dump_setup(handle, fp);
|
||||
}
|
||||
}
|
||||
|
||||
struct snd_pcm_ops snd_pcm_client_ops = {
|
||||
close: snd_pcm_client_shm_close,
|
||||
info: snd_pcm_client_shm_info,
|
||||
params_info: snd_pcm_client_shm_params_info,
|
||||
params: snd_pcm_client_shm_params,
|
||||
setup: snd_pcm_client_shm_setup,
|
||||
dump: snd_pcm_client_dump,
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops snd_pcm_client_fast_ops = {
|
||||
nonblock: snd_pcm_client_shm_nonblock,
|
||||
channel_info: snd_pcm_client_shm_channel_info,
|
||||
channel_params: snd_pcm_client_shm_channel_params,
|
||||
channel_setup: snd_pcm_client_shm_channel_setup,
|
||||
status: snd_pcm_client_shm_status,
|
||||
frame_io: snd_pcm_client_shm_frame_io,
|
||||
state: snd_pcm_client_shm_state,
|
||||
prepare: snd_pcm_client_shm_prepare,
|
||||
go: snd_pcm_client_shm_go,
|
||||
drain: snd_pcm_client_shm_drain,
|
||||
flush: snd_pcm_client_shm_flush,
|
||||
pause: snd_pcm_client_shm_pause,
|
||||
frame_data: snd_pcm_client_shm_frame_data,
|
||||
write: snd_pcm_client_shm_write,
|
||||
writev: snd_pcm_client_shm_writev,
|
||||
read: snd_pcm_client_shm_read,
|
||||
readv: snd_pcm_client_shm_readv,
|
||||
mmap_status: snd_pcm_client_shm_mmap_status,
|
||||
mmap_control: snd_pcm_client_shm_mmap_control,
|
||||
mmap_data: snd_pcm_client_shm_mmap_data,
|
||||
munmap_status: snd_pcm_client_shm_munmap_status,
|
||||
munmap_control: snd_pcm_client_shm_munmap_control,
|
||||
munmap_data: snd_pcm_client_shm_munmap_data,
|
||||
file_descriptor: snd_pcm_client_file_descriptor,
|
||||
channels_mask: snd_pcm_client_channels_mask,
|
||||
};
|
||||
|
||||
static int make_local_socket(const char *filename)
|
||||
{
|
||||
size_t l = strlen(filename);
|
||||
size_t size = offsetof(struct sockaddr_un, sun_path) + l;
|
||||
struct sockaddr_un *addr = alloca(size);
|
||||
int sock;
|
||||
|
||||
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
return -errno;
|
||||
|
||||
addr->sun_family = AF_LOCAL;
|
||||
memcpy(addr->sun_path, filename, l);
|
||||
|
||||
if (connect(sock, (struct sockaddr *) addr, size) < 0)
|
||||
return -errno;
|
||||
return sock;
|
||||
}
|
||||
|
||||
static int make_inet_socket(const char *host, int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int sock;
|
||||
struct hostent *h = gethostbyname(host);
|
||||
if (!h)
|
||||
return -ENOENT;
|
||||
|
||||
sock = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
return -errno;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
memcpy(&addr.sin_addr, h->h_addr_list[0], sizeof(struct in_addr));
|
||||
|
||||
if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
|
||||
return -errno;
|
||||
return sock;
|
||||
}
|
||||
|
||||
/* port == -1 -> PF_LOCAL and host is the socket name */
|
||||
int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transport, char *name, int stream, int mode)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_client_t *client;
|
||||
snd_client_open_request_t *req;
|
||||
snd_client_open_answer_t ans;
|
||||
size_t namelen, reqlen;
|
||||
int err;
|
||||
int result;
|
||||
int fds[2] = {-1, -1};
|
||||
int k;
|
||||
snd_pcm_client_shm_t *ctrl = NULL;
|
||||
uint32_t rcookie, scookie = getpid();
|
||||
namelen = strlen(name);
|
||||
if (namelen > 255)
|
||||
return -EINVAL;
|
||||
|
||||
for (k = 0; k < 2; ++k) {
|
||||
if (port == -1)
|
||||
fds[k] = make_local_socket(host);
|
||||
else
|
||||
fds[k] = make_inet_socket(host, port);
|
||||
if (fds[k] < 0) {
|
||||
result = fds[k];
|
||||
goto _err;
|
||||
}
|
||||
err = write(fds[k], &scookie, sizeof(scookie));
|
||||
if (err != sizeof(scookie)) {
|
||||
result = -EBADFD;
|
||||
goto _err;
|
||||
}
|
||||
err = read(fds[k], &rcookie, sizeof(rcookie));
|
||||
if (err != sizeof(rcookie) ||
|
||||
rcookie != scookie) {
|
||||
result = -EBADFD;
|
||||
goto _err;
|
||||
}
|
||||
}
|
||||
|
||||
reqlen = sizeof(*req) + namelen;
|
||||
req = alloca(reqlen);
|
||||
memcpy(req->name, name, namelen);
|
||||
req->dev_type = SND_DEV_TYPE_PCM;
|
||||
req->transport_type = transport;
|
||||
req->stream = stream;
|
||||
req->mode = mode;
|
||||
req->namelen = namelen;
|
||||
err = write(fds[1], req, reqlen);
|
||||
if (err < 0) {
|
||||
result = -errno;
|
||||
goto _err;
|
||||
}
|
||||
if ((size_t) err != reqlen) {
|
||||
result = -EINVAL;
|
||||
goto _err;
|
||||
}
|
||||
err = read(fds[1], &ans, sizeof(ans));
|
||||
if (err < 0) {
|
||||
result = -errno;
|
||||
goto _err;
|
||||
}
|
||||
if (err != sizeof(ans)) {
|
||||
result = -EINVAL;
|
||||
goto _err;
|
||||
}
|
||||
result = ntohl(ans.result);
|
||||
ans.cookie = ntohl(ans.cookie);
|
||||
if (result < 0)
|
||||
goto _err;
|
||||
|
||||
switch (transport) {
|
||||
case SND_TRANSPORT_TYPE_SHM:
|
||||
ctrl = shmat(ans.cookie, 0, 0);
|
||||
if (!ctrl) {
|
||||
result = -errno;
|
||||
goto _err;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result = -ENOSYS;
|
||||
goto _err;
|
||||
}
|
||||
|
||||
if (stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
struct pollfd pfd;
|
||||
char buf[1];
|
||||
int bufsize = 1;
|
||||
pfd.fd = fds[0];
|
||||
pfd.events = POLLOUT;
|
||||
err = setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
|
||||
if (err < 0) {
|
||||
result = -errno;
|
||||
goto _err;
|
||||
}
|
||||
if (poll(&pfd, 1, 0) != 1) {
|
||||
result = -errno;
|
||||
goto _err;
|
||||
}
|
||||
while (1) {
|
||||
err = write(fds[0], buf, 1);
|
||||
if (err != 1) {
|
||||
result = -errno;
|
||||
goto _err;
|
||||
}
|
||||
err = poll(&pfd, 1, 0);
|
||||
if (err < 0) {
|
||||
result = -errno;
|
||||
goto _err;
|
||||
}
|
||||
if (err == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
result = -ENOMEM;
|
||||
goto _err;
|
||||
}
|
||||
client = calloc(1, sizeof(snd_pcm_client_t));
|
||||
if (!handle) {
|
||||
free(handle);
|
||||
result = -ENOMEM;
|
||||
goto _err;
|
||||
}
|
||||
|
||||
client->handle = handle;
|
||||
client->data_fd = fds[0];
|
||||
client->ctrl_fd = fds[1];
|
||||
switch (transport) {
|
||||
case SND_TRANSPORT_TYPE_SHM:
|
||||
client->u.shm.ctrl = ctrl;
|
||||
break;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_CLIENT;
|
||||
handle->stream = stream;
|
||||
handle->ops = &snd_pcm_client_ops;
|
||||
handle->op_arg = client;
|
||||
handle->fast_ops = &snd_pcm_client_fast_ops;
|
||||
handle->fast_op_arg = client;
|
||||
handle->mode = mode;
|
||||
handle->private = client;
|
||||
*handlep = handle;
|
||||
return 0;
|
||||
|
||||
_err:
|
||||
if (fds[0] >= 0)
|
||||
close(fds[0]);
|
||||
if (fds[1] >= 0)
|
||||
close(fds[1]);
|
||||
switch (transport) {
|
||||
case SND_TRANSPORT_TYPE_SHM:
|
||||
if (ctrl)
|
||||
shmdt(ctrl);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -292,9 +292,9 @@ ssize_t snd_pcm_hw_readv(void *private, snd_timestamp_t *tstamp, const struct io
|
||||
|
||||
static int snd_pcm_hw_mmap_status(void *private, snd_pcm_mmap_status_t **status)
|
||||
{
|
||||
void *ptr;
|
||||
snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
|
||||
ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ, MAP_FILE|MAP_SHARED,
|
||||
void *ptr;
|
||||
ptr = mmap(NULL, sizeof(snd_pcm_mmap_status_t), PROT_READ, MAP_FILE|MAP_SHARED,
|
||||
hw->fd, SND_PCM_MMAP_OFFSET_STATUS);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
@ -304,8 +304,8 @@ static int snd_pcm_hw_mmap_status(void *private, snd_pcm_mmap_status_t **status)
|
||||
|
||||
static int snd_pcm_hw_mmap_control(void *private, snd_pcm_mmap_control_t **control)
|
||||
{
|
||||
void *ptr;
|
||||
snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
|
||||
void *ptr;
|
||||
ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
|
||||
hw->fd, SND_PCM_MMAP_OFFSET_CONTROL);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
@ -316,15 +316,15 @@ static int snd_pcm_hw_mmap_control(void *private, snd_pcm_mmap_control_t **contr
|
||||
|
||||
static int snd_pcm_hw_mmap_data(void *private, void **buffer, size_t bsize)
|
||||
{
|
||||
int prot;
|
||||
void *daddr;
|
||||
snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
|
||||
void *ptr;
|
||||
int prot;
|
||||
prot = hw->handle->stream == SND_PCM_STREAM_PLAYBACK ? PROT_WRITE : PROT_READ;
|
||||
daddr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED,
|
||||
ptr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED,
|
||||
hw->fd, SND_PCM_MMAP_OFFSET_DATA);
|
||||
if (daddr == MAP_FAILED || daddr == NULL)
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*buffer = daddr;
|
||||
*buffer = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user