1d5ac70f0Sopenharmony_ci/*
2d5ac70f0Sopenharmony_ci *  ALSA server
3d5ac70f0Sopenharmony_ci *  Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
4d5ac70f0Sopenharmony_ci *
5d5ac70f0Sopenharmony_ci *   This program is free software; you can redistribute it and/or modify
6d5ac70f0Sopenharmony_ci *   it under the terms of the GNU General Public License as published by
7d5ac70f0Sopenharmony_ci *   the Free Software Foundation; either version 2 of the License, or
8d5ac70f0Sopenharmony_ci *   (at your option) any later version.
9d5ac70f0Sopenharmony_ci *
10d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
11d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13d5ac70f0Sopenharmony_ci *   GNU General Public License for more details.
14d5ac70f0Sopenharmony_ci *
15d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU General Public License
16d5ac70f0Sopenharmony_ci *   along with this program; if not, write to the Free Software
17d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18d5ac70f0Sopenharmony_ci *
19d5ac70f0Sopenharmony_ci */
20d5ac70f0Sopenharmony_ci
21d5ac70f0Sopenharmony_ci#include "aserver.h"
22d5ac70f0Sopenharmony_ci
23d5ac70f0Sopenharmony_ci#include <sys/shm.h>
24d5ac70f0Sopenharmony_ci#include <sys/socket.h>
25d5ac70f0Sopenharmony_ci#include <poll.h>
26d5ac70f0Sopenharmony_ci#include <sys/un.h>
27d5ac70f0Sopenharmony_ci#include <sys/uio.h>
28d5ac70f0Sopenharmony_ci#include <stdio.h>
29d5ac70f0Sopenharmony_ci#include <unistd.h>
30d5ac70f0Sopenharmony_ci#include <fcntl.h>
31d5ac70f0Sopenharmony_ci#include <stddef.h>
32d5ac70f0Sopenharmony_ci#include <getopt.h>
33d5ac70f0Sopenharmony_ci#include <netinet/in.h>
34d5ac70f0Sopenharmony_ci#include <netdb.h>
35d5ac70f0Sopenharmony_ci#include <limits.h>
36d5ac70f0Sopenharmony_ci#include <signal.h>
37d5ac70f0Sopenharmony_ci
38d5ac70f0Sopenharmony_ci
39d5ac70f0Sopenharmony_cichar *command;
40d5ac70f0Sopenharmony_ci
41d5ac70f0Sopenharmony_ci#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
42d5ac70f0Sopenharmony_ci#define ERROR(...) do {\
43d5ac70f0Sopenharmony_ci	fprintf(stderr, "%s %s:%i:(%s) ", command, __FILE__, __LINE__, __func__); \
44d5ac70f0Sopenharmony_ci	fprintf(stderr, __VA_ARGS__); \
45d5ac70f0Sopenharmony_ci	putc('\n', stderr); \
46d5ac70f0Sopenharmony_ci} while (0)
47d5ac70f0Sopenharmony_ci#else
48d5ac70f0Sopenharmony_ci#define ERROR(args...) do {\
49d5ac70f0Sopenharmony_ci	fprintf(stderr, "%s %s:%i:(%s) ", command, __FILE__, __LINE__, __func__); \
50d5ac70f0Sopenharmony_ci	fprintf(stderr, ##args); \
51d5ac70f0Sopenharmony_ci	putc('\n', stderr); \
52d5ac70f0Sopenharmony_ci} while (0)
53d5ac70f0Sopenharmony_ci#endif
54d5ac70f0Sopenharmony_ci
55d5ac70f0Sopenharmony_ci#define SYSERROR(string) ERROR(string ": %s", strerror(errno))
56d5ac70f0Sopenharmony_ci
57d5ac70f0Sopenharmony_cistatic int make_local_socket(const char *filename)
58d5ac70f0Sopenharmony_ci{
59d5ac70f0Sopenharmony_ci	size_t l = strlen(filename);
60d5ac70f0Sopenharmony_ci	size_t size = offsetof(struct sockaddr_un, sun_path) + l;
61d5ac70f0Sopenharmony_ci	struct sockaddr_un *addr = alloca(size);
62d5ac70f0Sopenharmony_ci	int sock;
63d5ac70f0Sopenharmony_ci
64d5ac70f0Sopenharmony_ci	sock = socket(PF_LOCAL, SOCK_STREAM, 0);
65d5ac70f0Sopenharmony_ci	if (sock < 0) {
66d5ac70f0Sopenharmony_ci		int result = -errno;
67d5ac70f0Sopenharmony_ci		SYSERROR("socket failed");
68d5ac70f0Sopenharmony_ci		return result;
69d5ac70f0Sopenharmony_ci	}
70d5ac70f0Sopenharmony_ci
71d5ac70f0Sopenharmony_ci	unlink(filename);
72d5ac70f0Sopenharmony_ci
73d5ac70f0Sopenharmony_ci	addr->sun_family = AF_LOCAL;
74d5ac70f0Sopenharmony_ci	memcpy(addr->sun_path, filename, l);
75d5ac70f0Sopenharmony_ci
76d5ac70f0Sopenharmony_ci	if (bind(sock, (struct sockaddr *) addr, size) < 0) {
77d5ac70f0Sopenharmony_ci		int result = -errno;
78d5ac70f0Sopenharmony_ci		SYSERROR("bind failed");
79d5ac70f0Sopenharmony_ci		close(sock);
80d5ac70f0Sopenharmony_ci		return result;
81d5ac70f0Sopenharmony_ci	}
82d5ac70f0Sopenharmony_ci
83d5ac70f0Sopenharmony_ci	return sock;
84d5ac70f0Sopenharmony_ci}
85d5ac70f0Sopenharmony_ci
86d5ac70f0Sopenharmony_cistatic int make_inet_socket(int port)
87d5ac70f0Sopenharmony_ci{
88d5ac70f0Sopenharmony_ci	struct sockaddr_in addr;
89d5ac70f0Sopenharmony_ci	int sock;
90d5ac70f0Sopenharmony_ci
91d5ac70f0Sopenharmony_ci	sock = socket(PF_INET, SOCK_STREAM, 0);
92d5ac70f0Sopenharmony_ci	if (sock < 0) {
93d5ac70f0Sopenharmony_ci		int result = -errno;
94d5ac70f0Sopenharmony_ci		SYSERROR("socket failed");
95d5ac70f0Sopenharmony_ci		return result;
96d5ac70f0Sopenharmony_ci	}
97d5ac70f0Sopenharmony_ci
98d5ac70f0Sopenharmony_ci	memset(&addr, 0, sizeof(addr));
99d5ac70f0Sopenharmony_ci	addr.sin_family = AF_INET;
100d5ac70f0Sopenharmony_ci	addr.sin_port = htons(port);
101d5ac70f0Sopenharmony_ci	addr.sin_addr.s_addr = INADDR_ANY;
102d5ac70f0Sopenharmony_ci
103d5ac70f0Sopenharmony_ci	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
104d5ac70f0Sopenharmony_ci		int result = -errno;
105d5ac70f0Sopenharmony_ci		SYSERROR("bind failed");
106d5ac70f0Sopenharmony_ci		close(sock);
107d5ac70f0Sopenharmony_ci		return result;
108d5ac70f0Sopenharmony_ci	}
109d5ac70f0Sopenharmony_ci
110d5ac70f0Sopenharmony_ci	return sock;
111d5ac70f0Sopenharmony_ci}
112d5ac70f0Sopenharmony_ci
113d5ac70f0Sopenharmony_cistruct pollfd *pollfds;
114d5ac70f0Sopenharmony_ciunsigned int pollfds_count = 0;
115d5ac70f0Sopenharmony_citypedef struct waiter waiter_t;
116d5ac70f0Sopenharmony_citypedef int (*waiter_handler_t)(waiter_t *waiter, unsigned short events);
117d5ac70f0Sopenharmony_cistruct waiter {
118d5ac70f0Sopenharmony_ci	int fd;
119d5ac70f0Sopenharmony_ci	void *private_data;
120d5ac70f0Sopenharmony_ci	waiter_handler_t handler;
121d5ac70f0Sopenharmony_ci};
122d5ac70f0Sopenharmony_ciwaiter_t *waiters;
123d5ac70f0Sopenharmony_ci
124d5ac70f0Sopenharmony_cistatic void add_waiter(int fd, unsigned short events, waiter_handler_t handler,
125d5ac70f0Sopenharmony_ci		void *data)
126d5ac70f0Sopenharmony_ci{
127d5ac70f0Sopenharmony_ci	waiter_t *w = &waiters[fd];
128d5ac70f0Sopenharmony_ci	struct pollfd *pfd = &pollfds[pollfds_count];
129d5ac70f0Sopenharmony_ci	assert(!w->handler);
130d5ac70f0Sopenharmony_ci	pfd->fd = fd;
131d5ac70f0Sopenharmony_ci	pfd->events = events;
132d5ac70f0Sopenharmony_ci	pfd->revents = 0;
133d5ac70f0Sopenharmony_ci	w->fd = fd;
134d5ac70f0Sopenharmony_ci	w->private_data = data;
135d5ac70f0Sopenharmony_ci	w->handler = handler;
136d5ac70f0Sopenharmony_ci	pollfds_count++;
137d5ac70f0Sopenharmony_ci}
138d5ac70f0Sopenharmony_ci
139d5ac70f0Sopenharmony_cistatic void del_waiter(int fd)
140d5ac70f0Sopenharmony_ci{
141d5ac70f0Sopenharmony_ci	waiter_t *w = &waiters[fd];
142d5ac70f0Sopenharmony_ci	unsigned int k;
143d5ac70f0Sopenharmony_ci	assert(w->handler);
144d5ac70f0Sopenharmony_ci	w->handler = 0;
145d5ac70f0Sopenharmony_ci	for (k = 0; k < pollfds_count; ++k) {
146d5ac70f0Sopenharmony_ci		if (pollfds[k].fd == fd)
147d5ac70f0Sopenharmony_ci			break;
148d5ac70f0Sopenharmony_ci	}
149d5ac70f0Sopenharmony_ci	assert(k < pollfds_count);
150d5ac70f0Sopenharmony_ci	pollfds_count--;
151d5ac70f0Sopenharmony_ci	memmove(&pollfds[k], &pollfds[k + 1], pollfds_count - k);
152d5ac70f0Sopenharmony_ci}
153d5ac70f0Sopenharmony_ci
154d5ac70f0Sopenharmony_citypedef struct client client_t;
155d5ac70f0Sopenharmony_ci
156d5ac70f0Sopenharmony_citypedef struct {
157d5ac70f0Sopenharmony_ci	int (*open)(client_t *client, int *cookie);
158d5ac70f0Sopenharmony_ci	int (*cmd)(client_t *client);
159d5ac70f0Sopenharmony_ci	int (*close)(client_t *client);
160d5ac70f0Sopenharmony_ci} transport_ops_t;
161d5ac70f0Sopenharmony_ci
162d5ac70f0Sopenharmony_cistruct client {
163d5ac70f0Sopenharmony_ci	struct list_head list;
164d5ac70f0Sopenharmony_ci	int poll_fd;
165d5ac70f0Sopenharmony_ci	int ctrl_fd;
166d5ac70f0Sopenharmony_ci	int local;
167d5ac70f0Sopenharmony_ci	int transport_type;
168d5ac70f0Sopenharmony_ci	int dev_type;
169d5ac70f0Sopenharmony_ci	char name[256];
170d5ac70f0Sopenharmony_ci	int stream;
171d5ac70f0Sopenharmony_ci	int mode;
172d5ac70f0Sopenharmony_ci	transport_ops_t *ops;
173d5ac70f0Sopenharmony_ci	snd_async_handler_t *async_handler;
174d5ac70f0Sopenharmony_ci	int async_sig;
175d5ac70f0Sopenharmony_ci	pid_t async_pid;
176d5ac70f0Sopenharmony_ci	union {
177d5ac70f0Sopenharmony_ci		struct {
178d5ac70f0Sopenharmony_ci			snd_pcm_t *handle;
179d5ac70f0Sopenharmony_ci			int fd;
180d5ac70f0Sopenharmony_ci		} pcm;
181d5ac70f0Sopenharmony_ci		struct {
182d5ac70f0Sopenharmony_ci			snd_ctl_t *handle;
183d5ac70f0Sopenharmony_ci			int fd;
184d5ac70f0Sopenharmony_ci		} ctl;
185d5ac70f0Sopenharmony_ci#if 0
186d5ac70f0Sopenharmony_ci		struct {
187d5ac70f0Sopenharmony_ci			snd_rawmidi_t *handle;
188d5ac70f0Sopenharmony_ci		} rawmidi;
189d5ac70f0Sopenharmony_ci		struct {
190d5ac70f0Sopenharmony_ci			snd_timer_open_t *handle;
191d5ac70f0Sopenharmony_ci		} timer;
192d5ac70f0Sopenharmony_ci		struct {
193d5ac70f0Sopenharmony_ci			snd_hwdep_t *handle;
194d5ac70f0Sopenharmony_ci		} hwdep;
195d5ac70f0Sopenharmony_ci		struct {
196d5ac70f0Sopenharmony_ci			snd_seq_t *handle;
197d5ac70f0Sopenharmony_ci		} seq;
198d5ac70f0Sopenharmony_ci#endif
199d5ac70f0Sopenharmony_ci	} device;
200d5ac70f0Sopenharmony_ci	int polling;
201d5ac70f0Sopenharmony_ci	int open;
202d5ac70f0Sopenharmony_ci	int cookie;
203d5ac70f0Sopenharmony_ci	union {
204d5ac70f0Sopenharmony_ci		struct {
205d5ac70f0Sopenharmony_ci			int ctrl_id;
206d5ac70f0Sopenharmony_ci			void *ctrl;
207d5ac70f0Sopenharmony_ci		} shm;
208d5ac70f0Sopenharmony_ci	} transport;
209d5ac70f0Sopenharmony_ci};
210d5ac70f0Sopenharmony_ci
211d5ac70f0Sopenharmony_ciLIST_HEAD(clients);
212d5ac70f0Sopenharmony_ci
213d5ac70f0Sopenharmony_citypedef struct {
214d5ac70f0Sopenharmony_ci	struct list_head list;
215d5ac70f0Sopenharmony_ci	int fd;
216d5ac70f0Sopenharmony_ci	uint32_t cookie;
217d5ac70f0Sopenharmony_ci} inet_pending_t;
218d5ac70f0Sopenharmony_ciLIST_HEAD(inet_pendings);
219d5ac70f0Sopenharmony_ci
220d5ac70f0Sopenharmony_ci#if 0
221d5ac70f0Sopenharmony_cistatic int pcm_handler(waiter_t *waiter, unsigned short events)
222d5ac70f0Sopenharmony_ci{
223d5ac70f0Sopenharmony_ci	client_t *client = waiter->private_data;
224d5ac70f0Sopenharmony_ci	char buf[1];
225d5ac70f0Sopenharmony_ci	ssize_t n;
226d5ac70f0Sopenharmony_ci	if (events & POLLIN) {
227d5ac70f0Sopenharmony_ci		n = write(client->poll_fd, buf, 1);
228d5ac70f0Sopenharmony_ci		if (n != 1) {
229d5ac70f0Sopenharmony_ci			SYSERROR("write failed");
230d5ac70f0Sopenharmony_ci			return -errno;
231d5ac70f0Sopenharmony_ci		}
232d5ac70f0Sopenharmony_ci	} else if (events & POLLOUT) {
233d5ac70f0Sopenharmony_ci		n = read(client->poll_fd, buf, 1);
234d5ac70f0Sopenharmony_ci		if (n != 1) {
235d5ac70f0Sopenharmony_ci			SYSERROR("read failed");
236d5ac70f0Sopenharmony_ci			return -errno;
237d5ac70f0Sopenharmony_ci		}
238d5ac70f0Sopenharmony_ci	}
239d5ac70f0Sopenharmony_ci	del_waiter(waiter->fd);
240d5ac70f0Sopenharmony_ci	client->polling = 0;
241d5ac70f0Sopenharmony_ci	return 0;
242d5ac70f0Sopenharmony_ci}
243d5ac70f0Sopenharmony_ci#endif
244d5ac70f0Sopenharmony_ci
245d5ac70f0Sopenharmony_cistatic void pcm_shm_hw_ptr_changed(snd_pcm_t *pcm, snd_pcm_t *src ATTRIBUTE_UNUSED)
246d5ac70f0Sopenharmony_ci{
247d5ac70f0Sopenharmony_ci	client_t *client = pcm->hw.private_data;
248d5ac70f0Sopenharmony_ci	volatile snd_pcm_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
249d5ac70f0Sopenharmony_ci	snd_pcm_t *loop;
250d5ac70f0Sopenharmony_ci
251d5ac70f0Sopenharmony_ci	ctrl->hw.changed = 1;
252d5ac70f0Sopenharmony_ci	if (pcm->hw.fd >= 0) {
253d5ac70f0Sopenharmony_ci		ctrl->hw.use_mmap = 1;
254d5ac70f0Sopenharmony_ci		ctrl->hw.offset = pcm->hw.offset;
255d5ac70f0Sopenharmony_ci		return;
256d5ac70f0Sopenharmony_ci	}
257d5ac70f0Sopenharmony_ci	ctrl->hw.use_mmap = 0;
258d5ac70f0Sopenharmony_ci	ctrl->hw.ptr = pcm->hw.ptr ? *pcm->hw.ptr : 0;
259d5ac70f0Sopenharmony_ci	for (loop = pcm->hw.master; loop; loop = loop->hw.master)
260d5ac70f0Sopenharmony_ci		loop->hw.ptr = &ctrl->hw.ptr;
261d5ac70f0Sopenharmony_ci	pcm->hw.ptr = &ctrl->hw.ptr;
262d5ac70f0Sopenharmony_ci}
263d5ac70f0Sopenharmony_ci
264d5ac70f0Sopenharmony_cistatic void pcm_shm_appl_ptr_changed(snd_pcm_t *pcm, snd_pcm_t *src ATTRIBUTE_UNUSED)
265d5ac70f0Sopenharmony_ci{
266d5ac70f0Sopenharmony_ci	client_t *client = pcm->appl.private_data;
267d5ac70f0Sopenharmony_ci	volatile snd_pcm_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
268d5ac70f0Sopenharmony_ci	snd_pcm_t *loop;
269d5ac70f0Sopenharmony_ci
270d5ac70f0Sopenharmony_ci	ctrl->appl.changed = 1;
271d5ac70f0Sopenharmony_ci	if (pcm->appl.fd >= 0) {
272d5ac70f0Sopenharmony_ci		ctrl->appl.use_mmap = 1;
273d5ac70f0Sopenharmony_ci		ctrl->appl.offset = pcm->appl.offset;
274d5ac70f0Sopenharmony_ci		return;
275d5ac70f0Sopenharmony_ci	}
276d5ac70f0Sopenharmony_ci	ctrl->appl.use_mmap = 0;
277d5ac70f0Sopenharmony_ci	ctrl->appl.ptr = pcm->appl.ptr ? *pcm->appl.ptr : 0;
278d5ac70f0Sopenharmony_ci	for (loop = pcm->appl.master; loop; loop = loop->appl.master)
279d5ac70f0Sopenharmony_ci		loop->appl.ptr = &ctrl->appl.ptr;
280d5ac70f0Sopenharmony_ci	pcm->appl.ptr = &ctrl->appl.ptr;
281d5ac70f0Sopenharmony_ci}
282d5ac70f0Sopenharmony_ci
283d5ac70f0Sopenharmony_cistatic int pcm_shm_open(client_t *client, int *cookie)
284d5ac70f0Sopenharmony_ci{
285d5ac70f0Sopenharmony_ci	int shmid;
286d5ac70f0Sopenharmony_ci	snd_pcm_t *pcm;
287d5ac70f0Sopenharmony_ci	int err;
288d5ac70f0Sopenharmony_ci	int result;
289d5ac70f0Sopenharmony_ci	err = snd_pcm_open(&pcm, client->name, client->stream, SND_PCM_NONBLOCK);
290d5ac70f0Sopenharmony_ci	if (err < 0)
291d5ac70f0Sopenharmony_ci		return err;
292d5ac70f0Sopenharmony_ci	client->device.pcm.handle = pcm;
293d5ac70f0Sopenharmony_ci	client->device.pcm.fd = _snd_pcm_poll_descriptor(pcm);
294d5ac70f0Sopenharmony_ci	pcm->hw.private_data = client;
295d5ac70f0Sopenharmony_ci	pcm->hw.changed = pcm_shm_hw_ptr_changed;
296d5ac70f0Sopenharmony_ci	pcm->appl.private_data = client;
297d5ac70f0Sopenharmony_ci	pcm->appl.changed = pcm_shm_appl_ptr_changed;
298d5ac70f0Sopenharmony_ci
299d5ac70f0Sopenharmony_ci	shmid = shmget(IPC_PRIVATE, PCM_SHM_SIZE, 0666);
300d5ac70f0Sopenharmony_ci	if (shmid < 0) {
301d5ac70f0Sopenharmony_ci		result = -errno;
302d5ac70f0Sopenharmony_ci		SYSERROR("shmget failed");
303d5ac70f0Sopenharmony_ci		goto _err;
304d5ac70f0Sopenharmony_ci	}
305d5ac70f0Sopenharmony_ci	client->transport.shm.ctrl_id = shmid;
306d5ac70f0Sopenharmony_ci	client->transport.shm.ctrl = shmat(shmid, 0, 0);
307d5ac70f0Sopenharmony_ci	if (client->transport.shm.ctrl == (void*) -1) {
308d5ac70f0Sopenharmony_ci		result = -errno;
309d5ac70f0Sopenharmony_ci		shmctl(shmid, IPC_RMID, 0);
310d5ac70f0Sopenharmony_ci		SYSERROR("shmat failed");
311d5ac70f0Sopenharmony_ci		goto _err;
312d5ac70f0Sopenharmony_ci	}
313d5ac70f0Sopenharmony_ci	*cookie = shmid;
314d5ac70f0Sopenharmony_ci	return 0;
315d5ac70f0Sopenharmony_ci
316d5ac70f0Sopenharmony_ci _err:
317d5ac70f0Sopenharmony_ci	snd_pcm_close(pcm);
318d5ac70f0Sopenharmony_ci	return result;
319d5ac70f0Sopenharmony_ci
320d5ac70f0Sopenharmony_ci}
321d5ac70f0Sopenharmony_ci
322d5ac70f0Sopenharmony_cistatic int pcm_shm_close(client_t *client)
323d5ac70f0Sopenharmony_ci{
324d5ac70f0Sopenharmony_ci	int err;
325d5ac70f0Sopenharmony_ci	snd_pcm_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
326d5ac70f0Sopenharmony_ci	if (client->polling) {
327d5ac70f0Sopenharmony_ci		del_waiter(client->device.pcm.fd);
328d5ac70f0Sopenharmony_ci		client->polling = 0;
329d5ac70f0Sopenharmony_ci	}
330d5ac70f0Sopenharmony_ci	err = snd_pcm_close(client->device.pcm.handle);
331d5ac70f0Sopenharmony_ci	ctrl->result = err;
332d5ac70f0Sopenharmony_ci	if (err < 0)
333d5ac70f0Sopenharmony_ci		ERROR("snd_pcm_close");
334d5ac70f0Sopenharmony_ci	if (client->transport.shm.ctrl) {
335d5ac70f0Sopenharmony_ci		err = shmdt((void *)client->transport.shm.ctrl);
336d5ac70f0Sopenharmony_ci		if (err < 0)
337d5ac70f0Sopenharmony_ci			SYSERROR("shmdt failed");
338d5ac70f0Sopenharmony_ci		err = shmctl(client->transport.shm.ctrl_id, IPC_RMID, 0);
339d5ac70f0Sopenharmony_ci		if (err < 0)
340d5ac70f0Sopenharmony_ci			SYSERROR("shmctl IPC_RMID failed");
341d5ac70f0Sopenharmony_ci		client->transport.shm.ctrl = 0;
342d5ac70f0Sopenharmony_ci	}
343d5ac70f0Sopenharmony_ci	client->open = 0;
344d5ac70f0Sopenharmony_ci	return 0;
345d5ac70f0Sopenharmony_ci}
346d5ac70f0Sopenharmony_ci
347d5ac70f0Sopenharmony_cistatic int shm_ack(client_t *client)
348d5ac70f0Sopenharmony_ci{
349d5ac70f0Sopenharmony_ci	struct pollfd pfd;
350d5ac70f0Sopenharmony_ci	int err;
351d5ac70f0Sopenharmony_ci	char buf[1];
352d5ac70f0Sopenharmony_ci	pfd.fd = client->ctrl_fd;
353d5ac70f0Sopenharmony_ci	pfd.events = POLLHUP;
354d5ac70f0Sopenharmony_ci	if (poll(&pfd, 1, 0) == 1)
355d5ac70f0Sopenharmony_ci		return -EBADFD;
356d5ac70f0Sopenharmony_ci	err = write(client->ctrl_fd, buf, 1);
357d5ac70f0Sopenharmony_ci	if (err != 1)
358d5ac70f0Sopenharmony_ci		return -EBADFD;
359d5ac70f0Sopenharmony_ci	return 0;
360d5ac70f0Sopenharmony_ci}
361d5ac70f0Sopenharmony_ci
362d5ac70f0Sopenharmony_cistatic int shm_ack_fd(client_t *client, int fd)
363d5ac70f0Sopenharmony_ci{
364d5ac70f0Sopenharmony_ci	struct pollfd pfd;
365d5ac70f0Sopenharmony_ci	int err;
366d5ac70f0Sopenharmony_ci	char buf[1];
367d5ac70f0Sopenharmony_ci	pfd.fd = client->ctrl_fd;
368d5ac70f0Sopenharmony_ci	pfd.events = POLLHUP;
369d5ac70f0Sopenharmony_ci	if (poll(&pfd, 1, 0) == 1)
370d5ac70f0Sopenharmony_ci		return -EBADFD;
371d5ac70f0Sopenharmony_ci	err = snd_send_fd(client->ctrl_fd, buf, 1, fd);
372d5ac70f0Sopenharmony_ci	if (err != 1)
373d5ac70f0Sopenharmony_ci		return -EBADFD;
374d5ac70f0Sopenharmony_ci	return 0;
375d5ac70f0Sopenharmony_ci}
376d5ac70f0Sopenharmony_ci
377d5ac70f0Sopenharmony_cistatic int shm_rbptr_fd(client_t *client, snd_pcm_rbptr_t *rbptr)
378d5ac70f0Sopenharmony_ci{
379d5ac70f0Sopenharmony_ci	if (rbptr->fd < 0)
380d5ac70f0Sopenharmony_ci		return -EINVAL;
381d5ac70f0Sopenharmony_ci	return shm_ack_fd(client, rbptr->fd);
382d5ac70f0Sopenharmony_ci}
383d5ac70f0Sopenharmony_ci
384d5ac70f0Sopenharmony_cistatic void async_handler(snd_async_handler_t *handler)
385d5ac70f0Sopenharmony_ci{
386d5ac70f0Sopenharmony_ci	client_t *client = snd_async_handler_get_callback_private(handler);
387d5ac70f0Sopenharmony_ci	/* FIXME: use sigqueue */
388d5ac70f0Sopenharmony_ci	kill(client->async_pid, client->async_sig);
389d5ac70f0Sopenharmony_ci}
390d5ac70f0Sopenharmony_ci
391d5ac70f0Sopenharmony_cistatic int pcm_shm_cmd(client_t *client)
392d5ac70f0Sopenharmony_ci{
393d5ac70f0Sopenharmony_ci	volatile snd_pcm_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
394d5ac70f0Sopenharmony_ci	char buf[1];
395d5ac70f0Sopenharmony_ci	int err;
396d5ac70f0Sopenharmony_ci	int cmd;
397d5ac70f0Sopenharmony_ci	snd_pcm_t *pcm;
398d5ac70f0Sopenharmony_ci	err = read(client->ctrl_fd, buf, 1);
399d5ac70f0Sopenharmony_ci	if (err != 1)
400d5ac70f0Sopenharmony_ci		return -EBADFD;
401d5ac70f0Sopenharmony_ci	cmd = ctrl->cmd;
402d5ac70f0Sopenharmony_ci	ctrl->cmd = 0;
403d5ac70f0Sopenharmony_ci	pcm = client->device.pcm.handle;
404d5ac70f0Sopenharmony_ci	switch (cmd) {
405d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_ASYNC:
406d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_async(pcm, ctrl->u.async.sig, ctrl->u.async.pid);
407d5ac70f0Sopenharmony_ci		if (ctrl->result < 0)
408d5ac70f0Sopenharmony_ci			break;
409d5ac70f0Sopenharmony_ci		if (ctrl->u.async.sig >= 0) {
410d5ac70f0Sopenharmony_ci			assert(client->async_sig < 0);
411d5ac70f0Sopenharmony_ci			ctrl->result = snd_async_add_pcm_handler(&client->async_handler, pcm, async_handler, client);
412d5ac70f0Sopenharmony_ci			if (ctrl->result < 0)
413d5ac70f0Sopenharmony_ci				break;
414d5ac70f0Sopenharmony_ci		} else {
415d5ac70f0Sopenharmony_ci			assert(client->async_sig >= 0);
416d5ac70f0Sopenharmony_ci			snd_async_del_handler(client->async_handler);
417d5ac70f0Sopenharmony_ci		}
418d5ac70f0Sopenharmony_ci		client->async_sig = ctrl->u.async.sig;
419d5ac70f0Sopenharmony_ci		client->async_pid = ctrl->u.async.pid;
420d5ac70f0Sopenharmony_ci		break;
421d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_INFO:
422d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_info(pcm, (snd_pcm_info_t *) &ctrl->u.info);
423d5ac70f0Sopenharmony_ci		break;
424d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_HW_REFINE:
425d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_hw_refine(pcm, (snd_pcm_hw_params_t *) &ctrl->u.hw_refine);
426d5ac70f0Sopenharmony_ci		break;
427d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_HW_PARAMS:
428d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_hw_params(pcm, (snd_pcm_hw_params_t *) &ctrl->u.hw_params);
429d5ac70f0Sopenharmony_ci		break;
430d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_HW_FREE:
431d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_hw_free(pcm);
432d5ac70f0Sopenharmony_ci		break;
433d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_SW_PARAMS:
434d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_sw_params(pcm, (snd_pcm_sw_params_t *) &ctrl->u.sw_params);
435d5ac70f0Sopenharmony_ci		break;
436d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_STATUS:
437d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_status(pcm, (snd_pcm_status_t *) &ctrl->u.status);
438d5ac70f0Sopenharmony_ci		break;
439d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_STATE:
440d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_state(pcm);
441d5ac70f0Sopenharmony_ci		break;
442d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_HWSYNC:
443d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_hwsync(pcm);
444d5ac70f0Sopenharmony_ci		break;
445d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_DELAY:
446d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_delay(pcm, (snd_pcm_sframes_t *) &ctrl->u.delay.frames);
447d5ac70f0Sopenharmony_ci		break;
448d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_AVAIL_UPDATE:
449d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_avail_update(pcm);
450d5ac70f0Sopenharmony_ci		break;
451d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_PREPARE:
452d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_prepare(pcm);
453d5ac70f0Sopenharmony_ci		break;
454d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_RESET:
455d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_reset(pcm);
456d5ac70f0Sopenharmony_ci		break;
457d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_START:
458d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_start(pcm);
459d5ac70f0Sopenharmony_ci		break;
460d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_DRAIN:
461d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_drain(pcm);
462d5ac70f0Sopenharmony_ci		break;
463d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_DROP:
464d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_drop(pcm);
465d5ac70f0Sopenharmony_ci		break;
466d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_PAUSE:
467d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_pause(pcm, ctrl->u.pause.enable);
468d5ac70f0Sopenharmony_ci		break;
469d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_CHANNEL_INFO:
470d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_channel_info(pcm, (snd_pcm_channel_info_t *) &ctrl->u.channel_info);
471d5ac70f0Sopenharmony_ci		if (ctrl->result >= 0 &&
472d5ac70f0Sopenharmony_ci		    ctrl->u.channel_info.type == SND_PCM_AREA_MMAP)
473d5ac70f0Sopenharmony_ci			return shm_ack_fd(client, ctrl->u.channel_info.u.mmap.fd);
474d5ac70f0Sopenharmony_ci		break;
475d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_REWIND:
476d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_rewind(pcm, ctrl->u.rewind.frames);
477d5ac70f0Sopenharmony_ci		break;
478d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_FORWARD:
479d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_forward(pcm, ctrl->u.forward.frames);
480d5ac70f0Sopenharmony_ci		break;
481d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_LINK:
482d5ac70f0Sopenharmony_ci	{
483d5ac70f0Sopenharmony_ci		/* FIXME */
484d5ac70f0Sopenharmony_ci		ctrl->result = -ENOSYS;
485d5ac70f0Sopenharmony_ci		break;
486d5ac70f0Sopenharmony_ci	}
487d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_UNLINK:
488d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_unlink(pcm);
489d5ac70f0Sopenharmony_ci		break;
490d5ac70f0Sopenharmony_ci	case SNDRV_PCM_IOCTL_RESUME:
491d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_resume(pcm);
492d5ac70f0Sopenharmony_ci		break;
493d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_MMAP:
494d5ac70f0Sopenharmony_ci	{
495d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_mmap(pcm);
496d5ac70f0Sopenharmony_ci		break;
497d5ac70f0Sopenharmony_ci	}
498d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_MUNMAP:
499d5ac70f0Sopenharmony_ci	{
500d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_munmap(pcm);
501d5ac70f0Sopenharmony_ci		break;
502d5ac70f0Sopenharmony_ci	}
503d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_MMAP_COMMIT:
504d5ac70f0Sopenharmony_ci		ctrl->result = snd_pcm_mmap_commit(pcm,
505d5ac70f0Sopenharmony_ci						   ctrl->u.mmap_commit.offset,
506d5ac70f0Sopenharmony_ci						   ctrl->u.mmap_commit.frames);
507d5ac70f0Sopenharmony_ci		break;
508d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_POLL_DESCRIPTOR:
509d5ac70f0Sopenharmony_ci		ctrl->result = 0;
510d5ac70f0Sopenharmony_ci		return shm_ack_fd(client, _snd_pcm_poll_descriptor(pcm));
511d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_CLOSE:
512d5ac70f0Sopenharmony_ci		client->ops->close(client);
513d5ac70f0Sopenharmony_ci		break;
514d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_HW_PTR_FD:
515d5ac70f0Sopenharmony_ci		return shm_rbptr_fd(client, &pcm->hw);
516d5ac70f0Sopenharmony_ci	case SND_PCM_IOCTL_APPL_PTR_FD:
517d5ac70f0Sopenharmony_ci		return shm_rbptr_fd(client, &pcm->appl);
518d5ac70f0Sopenharmony_ci	default:
519d5ac70f0Sopenharmony_ci		ERROR("Bogus cmd: %x", ctrl->cmd);
520d5ac70f0Sopenharmony_ci		ctrl->result = -ENOSYS;
521d5ac70f0Sopenharmony_ci	}
522d5ac70f0Sopenharmony_ci	return shm_ack(client);
523d5ac70f0Sopenharmony_ci}
524d5ac70f0Sopenharmony_ci
525d5ac70f0Sopenharmony_citransport_ops_t pcm_shm_ops = {
526d5ac70f0Sopenharmony_ci	.open	= pcm_shm_open,
527d5ac70f0Sopenharmony_ci	.cmd	= pcm_shm_cmd,
528d5ac70f0Sopenharmony_ci	.close	= pcm_shm_close,
529d5ac70f0Sopenharmony_ci};
530d5ac70f0Sopenharmony_ci
531d5ac70f0Sopenharmony_cistatic int ctl_handler(waiter_t *waiter, unsigned short events)
532d5ac70f0Sopenharmony_ci{
533d5ac70f0Sopenharmony_ci	client_t *client = waiter->private_data;
534d5ac70f0Sopenharmony_ci	char buf[1] = "";
535d5ac70f0Sopenharmony_ci	ssize_t n;
536d5ac70f0Sopenharmony_ci	if (events & POLLIN) {
537d5ac70f0Sopenharmony_ci		n = write(client->poll_fd, buf, 1);
538d5ac70f0Sopenharmony_ci		if (n != 1) {
539d5ac70f0Sopenharmony_ci			SYSERROR("write failed");
540d5ac70f0Sopenharmony_ci			return -errno;
541d5ac70f0Sopenharmony_ci		}
542d5ac70f0Sopenharmony_ci	}
543d5ac70f0Sopenharmony_ci	del_waiter(waiter->fd);
544d5ac70f0Sopenharmony_ci	client->polling = 0;
545d5ac70f0Sopenharmony_ci	return 0;
546d5ac70f0Sopenharmony_ci}
547d5ac70f0Sopenharmony_ci
548d5ac70f0Sopenharmony_cistatic int ctl_shm_open(client_t *client, int *cookie)
549d5ac70f0Sopenharmony_ci{
550d5ac70f0Sopenharmony_ci	int shmid;
551d5ac70f0Sopenharmony_ci	snd_ctl_t *ctl;
552d5ac70f0Sopenharmony_ci	int err;
553d5ac70f0Sopenharmony_ci	int result;
554d5ac70f0Sopenharmony_ci	err = snd_ctl_open(&ctl, client->name, SND_CTL_NONBLOCK);
555d5ac70f0Sopenharmony_ci	if (err < 0)
556d5ac70f0Sopenharmony_ci		return err;
557d5ac70f0Sopenharmony_ci	client->device.ctl.handle = ctl;
558d5ac70f0Sopenharmony_ci	client->device.ctl.fd = _snd_ctl_poll_descriptor(ctl);
559d5ac70f0Sopenharmony_ci
560d5ac70f0Sopenharmony_ci	shmid = shmget(IPC_PRIVATE, CTL_SHM_SIZE, 0666);
561d5ac70f0Sopenharmony_ci	if (shmid < 0) {
562d5ac70f0Sopenharmony_ci		result = -errno;
563d5ac70f0Sopenharmony_ci		SYSERROR("shmget failed");
564d5ac70f0Sopenharmony_ci		goto _err;
565d5ac70f0Sopenharmony_ci	}
566d5ac70f0Sopenharmony_ci	client->transport.shm.ctrl_id = shmid;
567d5ac70f0Sopenharmony_ci	client->transport.shm.ctrl = shmat(shmid, 0, 0);
568d5ac70f0Sopenharmony_ci	if (!client->transport.shm.ctrl) {
569d5ac70f0Sopenharmony_ci		result = -errno;
570d5ac70f0Sopenharmony_ci		shmctl(shmid, IPC_RMID, 0);
571d5ac70f0Sopenharmony_ci		SYSERROR("shmat failed");
572d5ac70f0Sopenharmony_ci		goto _err;
573d5ac70f0Sopenharmony_ci	}
574d5ac70f0Sopenharmony_ci	*cookie = shmid;
575d5ac70f0Sopenharmony_ci	add_waiter(client->device.ctl.fd, POLLIN, ctl_handler, client);
576d5ac70f0Sopenharmony_ci	client->polling = 1;
577d5ac70f0Sopenharmony_ci	return 0;
578d5ac70f0Sopenharmony_ci
579d5ac70f0Sopenharmony_ci _err:
580d5ac70f0Sopenharmony_ci	snd_ctl_close(ctl);
581d5ac70f0Sopenharmony_ci	return result;
582d5ac70f0Sopenharmony_ci
583d5ac70f0Sopenharmony_ci}
584d5ac70f0Sopenharmony_ci
585d5ac70f0Sopenharmony_cistatic int ctl_shm_close(client_t *client)
586d5ac70f0Sopenharmony_ci{
587d5ac70f0Sopenharmony_ci	int err;
588d5ac70f0Sopenharmony_ci	snd_ctl_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
589d5ac70f0Sopenharmony_ci	if (client->polling) {
590d5ac70f0Sopenharmony_ci		del_waiter(client->device.ctl.fd);
591d5ac70f0Sopenharmony_ci		client->polling = 0;
592d5ac70f0Sopenharmony_ci	}
593d5ac70f0Sopenharmony_ci	err = snd_ctl_close(client->device.ctl.handle);
594d5ac70f0Sopenharmony_ci	ctrl->result = err;
595d5ac70f0Sopenharmony_ci	if (err < 0)
596d5ac70f0Sopenharmony_ci		ERROR("snd_ctl_close");
597d5ac70f0Sopenharmony_ci	if (client->transport.shm.ctrl) {
598d5ac70f0Sopenharmony_ci		err = shmdt((void *)client->transport.shm.ctrl);
599d5ac70f0Sopenharmony_ci		if (err < 0)
600d5ac70f0Sopenharmony_ci			SYSERROR("shmdt failed");
601d5ac70f0Sopenharmony_ci		err = shmctl(client->transport.shm.ctrl_id, IPC_RMID, 0);
602d5ac70f0Sopenharmony_ci		if (err < 0)
603d5ac70f0Sopenharmony_ci			SYSERROR("shmctl failed");
604d5ac70f0Sopenharmony_ci		client->transport.shm.ctrl = 0;
605d5ac70f0Sopenharmony_ci	}
606d5ac70f0Sopenharmony_ci	client->open = 0;
607d5ac70f0Sopenharmony_ci	return 0;
608d5ac70f0Sopenharmony_ci}
609d5ac70f0Sopenharmony_ci
610d5ac70f0Sopenharmony_cistatic int ctl_shm_cmd(client_t *client)
611d5ac70f0Sopenharmony_ci{
612d5ac70f0Sopenharmony_ci	snd_ctl_shm_ctrl_t *ctrl = client->transport.shm.ctrl;
613d5ac70f0Sopenharmony_ci	char buf[1];
614d5ac70f0Sopenharmony_ci	int err;
615d5ac70f0Sopenharmony_ci	int cmd;
616d5ac70f0Sopenharmony_ci	snd_ctl_t *ctl;
617d5ac70f0Sopenharmony_ci	err = read(client->ctrl_fd, buf, 1);
618d5ac70f0Sopenharmony_ci	if (err != 1)
619d5ac70f0Sopenharmony_ci		return -EBADFD;
620d5ac70f0Sopenharmony_ci	cmd = ctrl->cmd;
621d5ac70f0Sopenharmony_ci	ctrl->cmd = 0;
622d5ac70f0Sopenharmony_ci	ctl = client->device.ctl.handle;
623d5ac70f0Sopenharmony_ci	switch (cmd) {
624d5ac70f0Sopenharmony_ci	case SND_CTL_IOCTL_ASYNC:
625d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_async(ctl, ctrl->u.async.sig, ctrl->u.async.pid);
626d5ac70f0Sopenharmony_ci		if (ctrl->result < 0)
627d5ac70f0Sopenharmony_ci			break;
628d5ac70f0Sopenharmony_ci		if (ctrl->u.async.sig >= 0) {
629d5ac70f0Sopenharmony_ci			assert(client->async_sig < 0);
630d5ac70f0Sopenharmony_ci			ctrl->result = snd_async_add_ctl_handler(&client->async_handler, ctl, async_handler, client);
631d5ac70f0Sopenharmony_ci			if (ctrl->result < 0)
632d5ac70f0Sopenharmony_ci				break;
633d5ac70f0Sopenharmony_ci		} else {
634d5ac70f0Sopenharmony_ci			assert(client->async_sig >= 0);
635d5ac70f0Sopenharmony_ci			snd_async_del_handler(client->async_handler);
636d5ac70f0Sopenharmony_ci		}
637d5ac70f0Sopenharmony_ci		client->async_sig = ctrl->u.async.sig;
638d5ac70f0Sopenharmony_ci		client->async_pid = ctrl->u.async.pid;
639d5ac70f0Sopenharmony_ci		break;
640d5ac70f0Sopenharmony_ci		break;
641d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
642d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_subscribe_events(ctl, ctrl->u.subscribe_events);
643d5ac70f0Sopenharmony_ci		break;
644d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_CARD_INFO:
645d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_card_info(ctl, &ctrl->u.card_info);
646d5ac70f0Sopenharmony_ci		break;
647d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_LIST:
648d5ac70f0Sopenharmony_ci	{
649d5ac70f0Sopenharmony_ci		size_t maxsize = CTL_SHM_DATA_MAXLEN;
650d5ac70f0Sopenharmony_ci		if (ctrl->u.element_list.space * sizeof(*ctrl->u.element_list.pids) > maxsize) {
651d5ac70f0Sopenharmony_ci			ctrl->result = -EFAULT;
652d5ac70f0Sopenharmony_ci			break;
653d5ac70f0Sopenharmony_ci		}
654d5ac70f0Sopenharmony_ci		ctrl->u.element_list.pids = (snd_ctl_elem_id_t*) ctrl->data;
655d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_elem_list(ctl, &ctrl->u.element_list);
656d5ac70f0Sopenharmony_ci		break;
657d5ac70f0Sopenharmony_ci	}
658d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_INFO:
659d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_elem_info(ctl, &ctrl->u.element_info);
660d5ac70f0Sopenharmony_ci		break;
661d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_READ:
662d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_elem_read(ctl, &ctrl->u.element_read);
663d5ac70f0Sopenharmony_ci		break;
664d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_WRITE:
665d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_elem_write(ctl, &ctrl->u.element_write);
666d5ac70f0Sopenharmony_ci		break;
667d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_LOCK:
668d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_elem_lock(ctl, &ctrl->u.element_lock);
669d5ac70f0Sopenharmony_ci		break;
670d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
671d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_elem_unlock(ctl, &ctrl->u.element_unlock);
672d5ac70f0Sopenharmony_ci		break;
673d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE:
674d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_hwdep_next_device(ctl, &ctrl->u.device);
675d5ac70f0Sopenharmony_ci		break;
676d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_HWDEP_INFO:
677d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_hwdep_info(ctl, &ctrl->u.hwdep_info);
678d5ac70f0Sopenharmony_ci		break;
679d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE:
680d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_pcm_next_device(ctl, &ctrl->u.device);
681d5ac70f0Sopenharmony_ci		break;
682d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_PCM_INFO:
683d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_pcm_info(ctl, &ctrl->u.pcm_info);
684d5ac70f0Sopenharmony_ci		break;
685d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
686d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_pcm_prefer_subdevice(ctl, ctrl->u.pcm_prefer_subdevice);
687d5ac70f0Sopenharmony_ci		break;
688d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE:
689d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_rawmidi_next_device(ctl, &ctrl->u.device);
690d5ac70f0Sopenharmony_ci		break;
691d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_RAWMIDI_INFO:
692d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_rawmidi_info(ctl, &ctrl->u.rawmidi_info);
693d5ac70f0Sopenharmony_ci		break;
694d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
695d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_rawmidi_prefer_subdevice(ctl, ctrl->u.rawmidi_prefer_subdevice);
696d5ac70f0Sopenharmony_ci		break;
697d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_POWER:
698d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_set_power_state(ctl, ctrl->u.power_state);
699d5ac70f0Sopenharmony_ci		break;
700d5ac70f0Sopenharmony_ci	case SNDRV_CTL_IOCTL_POWER_STATE:
701d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_get_power_state(ctl, &ctrl->u.power_state);
702d5ac70f0Sopenharmony_ci		break;
703d5ac70f0Sopenharmony_ci	case SND_CTL_IOCTL_READ:
704d5ac70f0Sopenharmony_ci		ctrl->result = snd_ctl_read(ctl, &ctrl->u.read);
705d5ac70f0Sopenharmony_ci		break;
706d5ac70f0Sopenharmony_ci	case SND_CTL_IOCTL_CLOSE:
707d5ac70f0Sopenharmony_ci		client->ops->close(client);
708d5ac70f0Sopenharmony_ci		break;
709d5ac70f0Sopenharmony_ci	case SND_CTL_IOCTL_POLL_DESCRIPTOR:
710d5ac70f0Sopenharmony_ci		ctrl->result = 0;
711d5ac70f0Sopenharmony_ci		return shm_ack_fd(client, _snd_ctl_poll_descriptor(ctl));
712d5ac70f0Sopenharmony_ci	default:
713d5ac70f0Sopenharmony_ci		ERROR("Bogus cmd: %x", ctrl->cmd);
714d5ac70f0Sopenharmony_ci		ctrl->result = -ENOSYS;
715d5ac70f0Sopenharmony_ci	}
716d5ac70f0Sopenharmony_ci	return shm_ack(client);
717d5ac70f0Sopenharmony_ci}
718d5ac70f0Sopenharmony_ci
719d5ac70f0Sopenharmony_citransport_ops_t ctl_shm_ops = {
720d5ac70f0Sopenharmony_ci	.open	= ctl_shm_open,
721d5ac70f0Sopenharmony_ci	.cmd	= ctl_shm_cmd,
722d5ac70f0Sopenharmony_ci	.close	= ctl_shm_close,
723d5ac70f0Sopenharmony_ci};
724d5ac70f0Sopenharmony_ci
725d5ac70f0Sopenharmony_cistatic int snd_client_open(client_t *client)
726d5ac70f0Sopenharmony_ci{
727d5ac70f0Sopenharmony_ci	int err;
728d5ac70f0Sopenharmony_ci	snd_client_open_request_t req;
729d5ac70f0Sopenharmony_ci	snd_client_open_answer_t ans;
730d5ac70f0Sopenharmony_ci	char *name;
731d5ac70f0Sopenharmony_ci	memset(&ans, 0, sizeof(ans));
732d5ac70f0Sopenharmony_ci	err = read(client->ctrl_fd, &req, sizeof(req));
733d5ac70f0Sopenharmony_ci	if (err < 0) {
734d5ac70f0Sopenharmony_ci		SYSERROR("read failed");
735d5ac70f0Sopenharmony_ci		exit(1);
736d5ac70f0Sopenharmony_ci	}
737d5ac70f0Sopenharmony_ci	if (err != sizeof(req)) {
738d5ac70f0Sopenharmony_ci		ans.result = -EINVAL;
739d5ac70f0Sopenharmony_ci		goto _answer;
740d5ac70f0Sopenharmony_ci	}
741d5ac70f0Sopenharmony_ci	name = alloca(req.namelen + 1);
742d5ac70f0Sopenharmony_ci	err = read(client->ctrl_fd, name, req.namelen);
743d5ac70f0Sopenharmony_ci	if (err < 0) {
744d5ac70f0Sopenharmony_ci		SYSERROR("read failed");
745d5ac70f0Sopenharmony_ci		exit(1);
746d5ac70f0Sopenharmony_ci	}
747d5ac70f0Sopenharmony_ci	if (err != req.namelen) {
748d5ac70f0Sopenharmony_ci		ans.result = -EINVAL;
749d5ac70f0Sopenharmony_ci		goto _answer;
750d5ac70f0Sopenharmony_ci	}
751d5ac70f0Sopenharmony_ci
752d5ac70f0Sopenharmony_ci	switch (req.transport_type) {
753d5ac70f0Sopenharmony_ci	case SND_TRANSPORT_TYPE_SHM:
754d5ac70f0Sopenharmony_ci		if (!client->local) {
755d5ac70f0Sopenharmony_ci			ans.result = -EINVAL;
756d5ac70f0Sopenharmony_ci			goto _answer;
757d5ac70f0Sopenharmony_ci		}
758d5ac70f0Sopenharmony_ci		switch (req.dev_type) {
759d5ac70f0Sopenharmony_ci		case SND_DEV_TYPE_PCM:
760d5ac70f0Sopenharmony_ci			client->ops = &pcm_shm_ops;
761d5ac70f0Sopenharmony_ci			break;
762d5ac70f0Sopenharmony_ci		case SND_DEV_TYPE_CONTROL:
763d5ac70f0Sopenharmony_ci			client->ops = &ctl_shm_ops;
764d5ac70f0Sopenharmony_ci			break;
765d5ac70f0Sopenharmony_ci		default:
766d5ac70f0Sopenharmony_ci			ans.result = -EINVAL;
767d5ac70f0Sopenharmony_ci			goto _answer;
768d5ac70f0Sopenharmony_ci		}
769d5ac70f0Sopenharmony_ci		break;
770d5ac70f0Sopenharmony_ci	default:
771d5ac70f0Sopenharmony_ci		ans.result = -EINVAL;
772d5ac70f0Sopenharmony_ci		goto _answer;
773d5ac70f0Sopenharmony_ci	}
774d5ac70f0Sopenharmony_ci
775d5ac70f0Sopenharmony_ci	name[req.namelen] = '\0';
776d5ac70f0Sopenharmony_ci
777d5ac70f0Sopenharmony_ci	client->transport_type = req.transport_type;
778d5ac70f0Sopenharmony_ci	if (sizeof(client->name) < (size_t)(req.namelen + 1)) {
779d5ac70f0Sopenharmony_ci		ans.result = -ENOMEM;
780d5ac70f0Sopenharmony_ci		goto _answer;
781d5ac70f0Sopenharmony_ci	}
782d5ac70f0Sopenharmony_ci	strcpy(client->name, name);
783d5ac70f0Sopenharmony_ci	client->stream = req.stream;
784d5ac70f0Sopenharmony_ci	client->mode = req.mode;
785d5ac70f0Sopenharmony_ci
786d5ac70f0Sopenharmony_ci	err = client->ops->open(client, &ans.cookie);
787d5ac70f0Sopenharmony_ci	if (err < 0) {
788d5ac70f0Sopenharmony_ci		ans.result = err;
789d5ac70f0Sopenharmony_ci	} else {
790d5ac70f0Sopenharmony_ci		client->open = 1;
791d5ac70f0Sopenharmony_ci		ans.result = 0;
792d5ac70f0Sopenharmony_ci	}
793d5ac70f0Sopenharmony_ci
794d5ac70f0Sopenharmony_ci _answer:
795d5ac70f0Sopenharmony_ci	err = write(client->ctrl_fd, &ans, sizeof(ans));
796d5ac70f0Sopenharmony_ci	if (err != sizeof(ans)) {
797d5ac70f0Sopenharmony_ci		SYSERROR("write failed");
798d5ac70f0Sopenharmony_ci		exit(1);
799d5ac70f0Sopenharmony_ci	}
800d5ac70f0Sopenharmony_ci	return 0;
801d5ac70f0Sopenharmony_ci}
802d5ac70f0Sopenharmony_ci
803d5ac70f0Sopenharmony_cistatic int client_poll_handler(waiter_t *waiter, unsigned short events ATTRIBUTE_UNUSED)
804d5ac70f0Sopenharmony_ci{
805d5ac70f0Sopenharmony_ci	client_t *client = waiter->private_data;
806d5ac70f0Sopenharmony_ci	if (client->open)
807d5ac70f0Sopenharmony_ci		client->ops->close(client);
808d5ac70f0Sopenharmony_ci	close(client->poll_fd);
809d5ac70f0Sopenharmony_ci	close(client->ctrl_fd);
810d5ac70f0Sopenharmony_ci	del_waiter(client->poll_fd);
811d5ac70f0Sopenharmony_ci	del_waiter(client->ctrl_fd);
812d5ac70f0Sopenharmony_ci	list_del(&client->list);
813d5ac70f0Sopenharmony_ci	free(client);
814d5ac70f0Sopenharmony_ci	return 0;
815d5ac70f0Sopenharmony_ci}
816d5ac70f0Sopenharmony_ci
817d5ac70f0Sopenharmony_cistatic int client_ctrl_handler(waiter_t *waiter, unsigned short events)
818d5ac70f0Sopenharmony_ci{
819d5ac70f0Sopenharmony_ci	client_t *client = waiter->private_data;
820d5ac70f0Sopenharmony_ci	if (events & POLLHUP) {
821d5ac70f0Sopenharmony_ci		if (client->open)
822d5ac70f0Sopenharmony_ci			client->ops->close(client);
823d5ac70f0Sopenharmony_ci		close(client->ctrl_fd);
824d5ac70f0Sopenharmony_ci		del_waiter(client->ctrl_fd);
825d5ac70f0Sopenharmony_ci		list_del(&client->list);
826d5ac70f0Sopenharmony_ci		free(client);
827d5ac70f0Sopenharmony_ci		return 0;
828d5ac70f0Sopenharmony_ci	}
829d5ac70f0Sopenharmony_ci	if (client->open)
830d5ac70f0Sopenharmony_ci		return client->ops->cmd(client);
831d5ac70f0Sopenharmony_ci	else
832d5ac70f0Sopenharmony_ci		return snd_client_open(client);
833d5ac70f0Sopenharmony_ci}
834d5ac70f0Sopenharmony_ci
835d5ac70f0Sopenharmony_cistatic int inet_pending_handler(waiter_t *waiter, unsigned short events)
836d5ac70f0Sopenharmony_ci{
837d5ac70f0Sopenharmony_ci	inet_pending_t *pending = waiter->private_data;
838d5ac70f0Sopenharmony_ci	inet_pending_t *pdata;
839d5ac70f0Sopenharmony_ci	client_t *client;
840d5ac70f0Sopenharmony_ci	uint32_t cookie;
841d5ac70f0Sopenharmony_ci	struct list_head *item;
842d5ac70f0Sopenharmony_ci	int remove = 0;
843d5ac70f0Sopenharmony_ci	if (events & POLLHUP)
844d5ac70f0Sopenharmony_ci		remove = 1;
845d5ac70f0Sopenharmony_ci	else {
846d5ac70f0Sopenharmony_ci		int err = read(waiter->fd, &cookie, sizeof(cookie));
847d5ac70f0Sopenharmony_ci		if (err != sizeof(cookie))
848d5ac70f0Sopenharmony_ci			remove = 1;
849d5ac70f0Sopenharmony_ci		else {
850d5ac70f0Sopenharmony_ci			err = write(waiter->fd, &cookie, sizeof(cookie));
851d5ac70f0Sopenharmony_ci			if (err != sizeof(cookie))
852d5ac70f0Sopenharmony_ci				remove = 1;
853d5ac70f0Sopenharmony_ci		}
854d5ac70f0Sopenharmony_ci	}
855d5ac70f0Sopenharmony_ci	del_waiter(waiter->fd);
856d5ac70f0Sopenharmony_ci	if (remove) {
857d5ac70f0Sopenharmony_ci		close(waiter->fd);
858d5ac70f0Sopenharmony_ci		list_del(&pending->list);
859d5ac70f0Sopenharmony_ci		free(pending);
860d5ac70f0Sopenharmony_ci		return 0;
861d5ac70f0Sopenharmony_ci	}
862d5ac70f0Sopenharmony_ci
863d5ac70f0Sopenharmony_ci	list_for_each(item, &inet_pendings) {
864d5ac70f0Sopenharmony_ci		pdata = list_entry(item, inet_pending_t, list);
865d5ac70f0Sopenharmony_ci		if (pdata->cookie == cookie)
866d5ac70f0Sopenharmony_ci			goto found;
867d5ac70f0Sopenharmony_ci	}
868d5ac70f0Sopenharmony_ci	pending->cookie = cookie;
869d5ac70f0Sopenharmony_ci	return 0;
870d5ac70f0Sopenharmony_ci
871d5ac70f0Sopenharmony_ci found:
872d5ac70f0Sopenharmony_ci	client = calloc(1, sizeof(*client));
873d5ac70f0Sopenharmony_ci	client->local = 0;
874d5ac70f0Sopenharmony_ci	client->poll_fd = pdata->fd;
875d5ac70f0Sopenharmony_ci	client->ctrl_fd = waiter->fd;
876d5ac70f0Sopenharmony_ci	add_waiter(client->ctrl_fd, POLLIN | POLLHUP, client_ctrl_handler, client);
877d5ac70f0Sopenharmony_ci	add_waiter(client->poll_fd, POLLHUP, client_poll_handler, client);
878d5ac70f0Sopenharmony_ci	client->open = 0;
879d5ac70f0Sopenharmony_ci	list_add_tail(&client->list, &clients);
880d5ac70f0Sopenharmony_ci	list_del(&pending->list);
881d5ac70f0Sopenharmony_ci	list_del(&pdata->list);
882d5ac70f0Sopenharmony_ci	free(pending);
883d5ac70f0Sopenharmony_ci	free(pdata);
884d5ac70f0Sopenharmony_ci	return 0;
885d5ac70f0Sopenharmony_ci}
886d5ac70f0Sopenharmony_ci
887d5ac70f0Sopenharmony_cistatic int local_handler(waiter_t *waiter, unsigned short events ATTRIBUTE_UNUSED)
888d5ac70f0Sopenharmony_ci{
889d5ac70f0Sopenharmony_ci	int sock;
890d5ac70f0Sopenharmony_ci	sock = accept(waiter->fd, 0, 0);
891d5ac70f0Sopenharmony_ci	if (sock < 0) {
892d5ac70f0Sopenharmony_ci		int result = -errno;
893d5ac70f0Sopenharmony_ci		SYSERROR("accept failed");
894d5ac70f0Sopenharmony_ci		return result;
895d5ac70f0Sopenharmony_ci	} else {
896d5ac70f0Sopenharmony_ci		client_t *client = calloc(1, sizeof(*client));
897d5ac70f0Sopenharmony_ci		client->ctrl_fd = sock;
898d5ac70f0Sopenharmony_ci		client->local = 1;
899d5ac70f0Sopenharmony_ci		client->open = 0;
900d5ac70f0Sopenharmony_ci		add_waiter(sock, POLLIN | POLLHUP, client_ctrl_handler, client);
901d5ac70f0Sopenharmony_ci		list_add_tail(&client->list, &clients);
902d5ac70f0Sopenharmony_ci	}
903d5ac70f0Sopenharmony_ci	return 0;
904d5ac70f0Sopenharmony_ci}
905d5ac70f0Sopenharmony_ci
906d5ac70f0Sopenharmony_cistatic int inet_handler(waiter_t *waiter, unsigned short events ATTRIBUTE_UNUSED)
907d5ac70f0Sopenharmony_ci{
908d5ac70f0Sopenharmony_ci	int sock;
909d5ac70f0Sopenharmony_ci	sock = accept(waiter->fd, 0, 0);
910d5ac70f0Sopenharmony_ci	if (sock < 0) {
911d5ac70f0Sopenharmony_ci		int result = -errno;
912d5ac70f0Sopenharmony_ci		SYSERROR("accept failed");
913d5ac70f0Sopenharmony_ci		return result;
914d5ac70f0Sopenharmony_ci	} else {
915d5ac70f0Sopenharmony_ci		inet_pending_t *pending = calloc(1, sizeof(*pending));
916d5ac70f0Sopenharmony_ci		pending->fd = sock;
917d5ac70f0Sopenharmony_ci		pending->cookie = 0;
918d5ac70f0Sopenharmony_ci		add_waiter(sock, POLLIN, inet_pending_handler, pending);
919d5ac70f0Sopenharmony_ci		list_add_tail(&pending->list, &inet_pendings);
920d5ac70f0Sopenharmony_ci	}
921d5ac70f0Sopenharmony_ci	return 0;
922d5ac70f0Sopenharmony_ci}
923d5ac70f0Sopenharmony_ci
924d5ac70f0Sopenharmony_cistatic int server(const char *sockname, int port)
925d5ac70f0Sopenharmony_ci{
926d5ac70f0Sopenharmony_ci	int err, result, sockn = -1, socki = -1;
927d5ac70f0Sopenharmony_ci	unsigned int k;
928d5ac70f0Sopenharmony_ci	long open_max;
929d5ac70f0Sopenharmony_ci
930d5ac70f0Sopenharmony_ci	if (!sockname && port < 0)
931d5ac70f0Sopenharmony_ci		return -EINVAL;
932d5ac70f0Sopenharmony_ci	open_max = sysconf(_SC_OPEN_MAX);
933d5ac70f0Sopenharmony_ci	if (open_max < 0) {
934d5ac70f0Sopenharmony_ci		result = -errno;
935d5ac70f0Sopenharmony_ci		SYSERROR("sysconf failed");
936d5ac70f0Sopenharmony_ci		return result;
937d5ac70f0Sopenharmony_ci	}
938d5ac70f0Sopenharmony_ci	pollfds = calloc((size_t) open_max, sizeof(*pollfds));
939d5ac70f0Sopenharmony_ci	waiters = calloc((size_t) open_max, sizeof(*waiters));
940d5ac70f0Sopenharmony_ci
941d5ac70f0Sopenharmony_ci	if (sockname) {
942d5ac70f0Sopenharmony_ci		sockn = make_local_socket(sockname);
943d5ac70f0Sopenharmony_ci		if (sockn < 0)
944d5ac70f0Sopenharmony_ci			return sockn;
945d5ac70f0Sopenharmony_ci		if (fcntl(sockn, F_SETFL, O_NONBLOCK) < 0) {
946d5ac70f0Sopenharmony_ci			result = -errno;
947d5ac70f0Sopenharmony_ci			SYSERROR("fcntl O_NONBLOCK failed");
948d5ac70f0Sopenharmony_ci			goto _end;
949d5ac70f0Sopenharmony_ci		}
950d5ac70f0Sopenharmony_ci		if (listen(sockn, 4) < 0) {
951d5ac70f0Sopenharmony_ci			result = -errno;
952d5ac70f0Sopenharmony_ci			SYSERROR("listen failed");
953d5ac70f0Sopenharmony_ci			goto _end;
954d5ac70f0Sopenharmony_ci		}
955d5ac70f0Sopenharmony_ci		add_waiter(sockn, POLLIN, local_handler, NULL);
956d5ac70f0Sopenharmony_ci	}
957d5ac70f0Sopenharmony_ci	if (port >= 0) {
958d5ac70f0Sopenharmony_ci		socki = make_inet_socket(port);
959d5ac70f0Sopenharmony_ci		if (socki < 0)
960d5ac70f0Sopenharmony_ci			return socki;
961d5ac70f0Sopenharmony_ci		if (fcntl(socki, F_SETFL, O_NONBLOCK) < 0) {
962d5ac70f0Sopenharmony_ci			result = -errno;
963d5ac70f0Sopenharmony_ci			SYSERROR("fcntl failed");
964d5ac70f0Sopenharmony_ci			goto _end;
965d5ac70f0Sopenharmony_ci		}
966d5ac70f0Sopenharmony_ci		if (listen(socki, 4) < 0) {
967d5ac70f0Sopenharmony_ci			result = -errno;
968d5ac70f0Sopenharmony_ci			SYSERROR("listen failed");
969d5ac70f0Sopenharmony_ci			goto _end;
970d5ac70f0Sopenharmony_ci		}
971d5ac70f0Sopenharmony_ci		add_waiter(socki, POLLIN, inet_handler, NULL);
972d5ac70f0Sopenharmony_ci	}
973d5ac70f0Sopenharmony_ci
974d5ac70f0Sopenharmony_ci	while (1) {
975d5ac70f0Sopenharmony_ci		struct pollfd pfds[open_max];
976d5ac70f0Sopenharmony_ci		size_t pfds_count;
977d5ac70f0Sopenharmony_ci		do {
978d5ac70f0Sopenharmony_ci			err = poll(pollfds, pollfds_count, -1);
979d5ac70f0Sopenharmony_ci		} while (err == 0);
980d5ac70f0Sopenharmony_ci		if (err < 0) {
981d5ac70f0Sopenharmony_ci			SYSERROR("poll failed");
982d5ac70f0Sopenharmony_ci			continue;
983d5ac70f0Sopenharmony_ci		}
984d5ac70f0Sopenharmony_ci
985d5ac70f0Sopenharmony_ci		pfds_count = pollfds_count;
986d5ac70f0Sopenharmony_ci		memcpy(pfds, pollfds, sizeof(*pfds) * pfds_count);
987d5ac70f0Sopenharmony_ci		for (k = 0; k < pfds_count; k++) {
988d5ac70f0Sopenharmony_ci			struct pollfd *pfd = &pfds[k];
989d5ac70f0Sopenharmony_ci			if (pfd->revents) {
990d5ac70f0Sopenharmony_ci				waiter_t *w = &waiters[pfd->fd];
991d5ac70f0Sopenharmony_ci				if (!w->handler)
992d5ac70f0Sopenharmony_ci					continue;
993d5ac70f0Sopenharmony_ci				err = w->handler(w, pfd->revents);
994d5ac70f0Sopenharmony_ci				if (err < 0)
995d5ac70f0Sopenharmony_ci					ERROR("waiter handler failed");
996d5ac70f0Sopenharmony_ci			}
997d5ac70f0Sopenharmony_ci		}
998d5ac70f0Sopenharmony_ci	}
999d5ac70f0Sopenharmony_ci _end:
1000d5ac70f0Sopenharmony_ci	if (sockn >= 0)
1001d5ac70f0Sopenharmony_ci		close(sockn);
1002d5ac70f0Sopenharmony_ci	if (socki >= 0)
1003d5ac70f0Sopenharmony_ci		close(socki);
1004d5ac70f0Sopenharmony_ci	free(pollfds);
1005d5ac70f0Sopenharmony_ci	free(waiters);
1006d5ac70f0Sopenharmony_ci	return result;
1007d5ac70f0Sopenharmony_ci}
1008d5ac70f0Sopenharmony_ci
1009d5ac70f0Sopenharmony_ci
1010d5ac70f0Sopenharmony_cistatic void usage(void)
1011d5ac70f0Sopenharmony_ci{
1012d5ac70f0Sopenharmony_ci	fprintf(stderr,
1013d5ac70f0Sopenharmony_ci		"Usage: %s [OPTIONS] server\n"
1014d5ac70f0Sopenharmony_ci		"--help			help\n",
1015d5ac70f0Sopenharmony_ci		command);
1016d5ac70f0Sopenharmony_ci}
1017d5ac70f0Sopenharmony_ci
1018d5ac70f0Sopenharmony_ciint main(int argc, char **argv)
1019d5ac70f0Sopenharmony_ci{
1020d5ac70f0Sopenharmony_ci	static const struct option long_options[] = {
1021d5ac70f0Sopenharmony_ci		{"help", 0, 0, 'h'},
1022d5ac70f0Sopenharmony_ci		{ 0 , 0 , 0, 0 }
1023d5ac70f0Sopenharmony_ci	};
1024d5ac70f0Sopenharmony_ci	int c;
1025d5ac70f0Sopenharmony_ci	snd_config_t *conf;
1026d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
1027d5ac70f0Sopenharmony_ci	const char *sockname = NULL;
1028d5ac70f0Sopenharmony_ci	long port = -1;
1029d5ac70f0Sopenharmony_ci	int err;
1030d5ac70f0Sopenharmony_ci	char *srvname;
1031d5ac70f0Sopenharmony_ci
1032d5ac70f0Sopenharmony_ci	command = argv[0];
1033d5ac70f0Sopenharmony_ci	while ((c = getopt_long(argc, argv, "h", long_options, 0)) != -1) {
1034d5ac70f0Sopenharmony_ci		switch (c) {
1035d5ac70f0Sopenharmony_ci		case 'h':
1036d5ac70f0Sopenharmony_ci			usage();
1037d5ac70f0Sopenharmony_ci			return 0;
1038d5ac70f0Sopenharmony_ci		default:
1039d5ac70f0Sopenharmony_ci			fprintf(stderr, "Try `%s --help' for more information\n", command);
1040d5ac70f0Sopenharmony_ci			return 1;
1041d5ac70f0Sopenharmony_ci		}
1042d5ac70f0Sopenharmony_ci	}
1043d5ac70f0Sopenharmony_ci	if (argc - optind != 1) {
1044d5ac70f0Sopenharmony_ci		ERROR("you need to specify server name");
1045d5ac70f0Sopenharmony_ci		return 1;
1046d5ac70f0Sopenharmony_ci	}
1047d5ac70f0Sopenharmony_ci	err = snd_config_update();
1048d5ac70f0Sopenharmony_ci	if (err < 0) {
1049d5ac70f0Sopenharmony_ci		ERROR("cannot read configuration file");
1050d5ac70f0Sopenharmony_ci		return 1;
1051d5ac70f0Sopenharmony_ci	}
1052d5ac70f0Sopenharmony_ci	srvname = argv[optind];
1053d5ac70f0Sopenharmony_ci	err = snd_config_search_definition(snd_config, "server", srvname, &conf);
1054d5ac70f0Sopenharmony_ci	if (err < 0) {
1055d5ac70f0Sopenharmony_ci		ERROR("Missing definition for server %s", srvname);
1056d5ac70f0Sopenharmony_ci		return 1;
1057d5ac70f0Sopenharmony_ci	}
1058d5ac70f0Sopenharmony_ci	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
1059d5ac70f0Sopenharmony_ci		SNDERR("Invalid type for server %s definition", srvname);
1060d5ac70f0Sopenharmony_ci		return -EINVAL;
1061d5ac70f0Sopenharmony_ci	}
1062d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
1063d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
1064d5ac70f0Sopenharmony_ci		const char *id;
1065d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
1066d5ac70f0Sopenharmony_ci			continue;
1067d5ac70f0Sopenharmony_ci		if (strcmp(id, "comment") == 0)
1068d5ac70f0Sopenharmony_ci			continue;
1069d5ac70f0Sopenharmony_ci		if (strcmp(id, "host") == 0)
1070d5ac70f0Sopenharmony_ci			continue;
1071d5ac70f0Sopenharmony_ci		if (strcmp(id, "socket") == 0) {
1072d5ac70f0Sopenharmony_ci			err = snd_config_get_string(n, &sockname);
1073d5ac70f0Sopenharmony_ci			if (err < 0) {
1074d5ac70f0Sopenharmony_ci				ERROR("Invalid type for %s", id);
1075d5ac70f0Sopenharmony_ci				return 1;
1076d5ac70f0Sopenharmony_ci			}
1077d5ac70f0Sopenharmony_ci			continue;
1078d5ac70f0Sopenharmony_ci		}
1079d5ac70f0Sopenharmony_ci		if (strcmp(id, "port") == 0) {
1080d5ac70f0Sopenharmony_ci			err = snd_config_get_integer(n, &port);
1081d5ac70f0Sopenharmony_ci			if (err < 0) {
1082d5ac70f0Sopenharmony_ci				ERROR("Invalid type for %s", id);
1083d5ac70f0Sopenharmony_ci				return 1;
1084d5ac70f0Sopenharmony_ci			}
1085d5ac70f0Sopenharmony_ci			continue;
1086d5ac70f0Sopenharmony_ci		}
1087d5ac70f0Sopenharmony_ci		ERROR("Unknown field %s", id);
1088d5ac70f0Sopenharmony_ci		return 1;
1089d5ac70f0Sopenharmony_ci	}
1090d5ac70f0Sopenharmony_ci	if (!sockname && port < 0) {
1091d5ac70f0Sopenharmony_ci		ERROR("either socket or port need to be defined");
1092d5ac70f0Sopenharmony_ci		return 1;
1093d5ac70f0Sopenharmony_ci	}
1094d5ac70f0Sopenharmony_ci	server(sockname, port);
1095d5ac70f0Sopenharmony_ci	return 0;
1096d5ac70f0Sopenharmony_ci}
1097