1 /* Spa
2 *
3 * Copyright © 2019 Wim Taymans
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include <unistd.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <sys/eventfd.h>
33 #include <sys/signalfd.h>
34
35 #include <evl/evl.h>
36 #include <evl/timer.h>
37
38 #include <spa/support/log.h>
39 #include <spa/support/system.h>
40 #include <spa/support/plugin.h>
41 #include <spa/utils/type.h>
42 #include <spa/utils/result.h>
43 #include <spa/utils/string.h>
44
45 #define NAME "evl-system"
46
47 #define MAX_POLL 512
48
49 struct poll_entry {
50 int pfd;
51 int fd;
52 uint32_t events;
53 void *data;
54 };
55
56 struct impl {
57 struct spa_handle handle;
58 struct spa_system system;
59
60 struct spa_log *log;
61
62 struct poll_entry entries[MAX_POLL];
63 uint32_t n_entries;
64
65 uint32_t n_xbuf;
66 int attached;
67 int pid;
68 };
69
impl_read(void * object,int fd,void * buf,size_t count)70 static ssize_t impl_read(void *object, int fd, void *buf, size_t count)
71 {
72 return oob_read(fd, buf, count);
73 }
74
impl_write(void * object,int fd,const void * buf,size_t count)75 static ssize_t impl_write(void *object, int fd, const void *buf, size_t count)
76 {
77 return oob_write(fd, buf, count);
78 }
79
impl_ioctl(void * object,int fd,unsigned long request,...)80 static int impl_ioctl(void *object, int fd, unsigned long request, ...)
81 {
82 int res;
83 va_list ap;
84 long arg;
85
86 va_start(ap, request);
87 arg = va_arg(ap, long);
88 res = oob_ioctl(fd, request, arg);
89 va_end(ap);
90
91 return res;
92 }
93
impl_close(void * object,int fd)94 static int impl_close(void *object, int fd)
95 {
96 return close(fd);
97 }
98
clock_id_to_evl(int clockid)99 static inline int clock_id_to_evl(int clockid)
100 {
101 switch(clockid) {
102 case CLOCK_MONOTONIC:
103 return EVL_CLOCK_MONOTONIC;
104 case CLOCK_REALTIME:
105 return EVL_CLOCK_REALTIME;
106 default:
107 return -clockid;
108 }
109 }
110
111 /* clock */
impl_clock_gettime(void * object,int clockid,struct timespec * value)112 static int impl_clock_gettime(void *object,
113 int clockid, struct timespec *value)
114 {
115 return evl_read_clock(clock_id_to_evl(clockid), value);
116 }
117
impl_clock_getres(void * object,int clockid,struct timespec * res)118 static int impl_clock_getres(void *object,
119 int clockid, struct timespec *res)
120 {
121 return evl_get_clock_resolution(clock_id_to_evl(clockid), res);
122 }
123
124 /* poll */
impl_pollfd_create(void * object,int flags)125 static int impl_pollfd_create(void *object, int flags)
126 {
127 int retval;
128 retval = evl_new_poll();
129 return retval;
130 }
131
find_entry(struct impl * impl,int pfd,int fd)132 static inline struct poll_entry *find_entry(struct impl *impl, int pfd, int fd)
133 {
134 uint32_t i;
135 for (i = 0; i < impl->n_entries; i++) {
136 struct poll_entry *e = &impl->entries[i];
137 if (e->pfd == pfd && e->fd == fd)
138 return e;
139 }
140 return NULL;
141 }
142
impl_pollfd_add(void * object,int pfd,int fd,uint32_t events,void * data)143 static int impl_pollfd_add(void *object, int pfd, int fd, uint32_t events, void *data)
144 {
145 struct impl *impl = object;
146 struct poll_entry *e;
147
148 if (impl->n_entries == MAX_POLL)
149 return -ENOSPC;
150
151 e = &impl->entries[impl->n_entries++];
152 e->pfd = pfd;
153 e->fd = fd;
154 e->events = events;
155 e->data = data;
156 return evl_add_pollfd(pfd, fd, e->events);
157 }
158
impl_pollfd_mod(void * object,int pfd,int fd,uint32_t events,void * data)159 static int impl_pollfd_mod(void *object, int pfd, int fd, uint32_t events, void *data)
160 {
161 struct impl *impl = object;
162 struct poll_entry *e;
163
164 e = find_entry(impl, pfd, fd);
165 if (e == NULL)
166 return -ENOENT;
167
168 e->events = events;
169 e->data = data;
170 return evl_mod_pollfd(pfd, fd, e->events);
171 }
172
impl_pollfd_del(void * object,int pfd,int fd)173 static int impl_pollfd_del(void *object, int pfd, int fd)
174 {
175 struct impl *impl = object;
176 struct poll_entry *e;
177
178 e = find_entry(impl, pfd, fd);
179 if (e == NULL)
180 return -ENOENT;
181
182 e->pfd = -1;
183 e->fd = -1;
184 return evl_del_pollfd(pfd, fd);
185 }
186
impl_pollfd_wait(void * object,int pfd,struct spa_poll_event * ev,int n_ev,int timeout)187 static int impl_pollfd_wait(void *object, int pfd,
188 struct spa_poll_event *ev, int n_ev, int timeout)
189 {
190 struct impl *impl = object;
191 struct evl_poll_event pollset[n_ev];
192 struct timespec tv;
193 int i, j, res;
194
195 if (impl->attached == 0) {
196 res = evl_attach_self("evl-thread-%d-%p", impl->pid, impl);
197 if (res < 0)
198 return res;
199 impl->attached = res;
200 }
201
202 if (timeout == -1) {
203 tv.tv_sec = 0;
204 tv.tv_nsec = 0;
205 } else {
206 tv.tv_sec = timeout / SPA_MSEC_PER_SEC;
207 tv.tv_nsec = (timeout % SPA_MSEC_PER_SEC) * SPA_NSEC_PER_MSEC;
208 }
209 res = evl_timedpoll(pfd, pollset, n_ev, &tv);
210 if (SPA_UNLIKELY(res < 0))
211 return res;
212
213 for (i = 0, j = 0; i < res; i++) {
214 struct poll_entry *e;
215
216 e = find_entry(impl, pfd, pollset[i].fd);
217 if (e == NULL)
218 continue;
219
220 ev[j].events = pollset[i].events;
221 ev[j].data = e->data;
222 j++;
223 }
224 return j;
225 }
226
227 /* timers */
impl_timerfd_create(void * object,int clockid,int flags)228 static int impl_timerfd_create(void *object, int clockid, int flags)
229 {
230 int cid;
231
232 switch (clockid) {
233 case CLOCK_MONOTONIC:
234 cid = EVL_CLOCK_MONOTONIC;
235 break;
236 default:
237 return -ENOTSUP;
238 }
239 return evl_new_timer(cid);
240 }
241
impl_timerfd_settime(void * object,int fd,int flags,const struct itimerspec * new_value,struct itimerspec * old_value)242 static int impl_timerfd_settime(void *object,
243 int fd, int flags,
244 const struct itimerspec *new_value,
245 struct itimerspec *old_value)
246 {
247 struct itimerspec val = *new_value;
248
249 if (!(flags & SPA_FD_TIMER_ABSTIME)) {
250 struct timespec now;
251
252 evl_read_clock(EVL_CLOCK_MONOTONIC, &now);
253 val.it_value.tv_sec += now.tv_sec;
254 val.it_value.tv_nsec += now.tv_nsec;
255 if (val.it_value.tv_nsec >= 1000000000) {
256 val.it_value.tv_sec++;
257 val.it_value.tv_nsec -= 1000000000;
258 }
259 }
260 return evl_set_timer(fd, &val, old_value);
261 }
262
impl_timerfd_gettime(void * object,int fd,struct itimerspec * curr_value)263 static int impl_timerfd_gettime(void *object,
264 int fd, struct itimerspec *curr_value)
265 {
266 return evl_get_timer(fd, curr_value);
267
268 }
impl_timerfd_read(void * object,int fd,uint64_t * expirations)269 static int impl_timerfd_read(void *object, int fd, uint64_t *expirations)
270 {
271 uint32_t ticks;
272 if (oob_read(fd, &ticks, sizeof(ticks)) != sizeof(ticks))
273 return -errno;
274 *expirations = ticks;
275 return 0;
276 }
277
278 /* events */
impl_eventfd_create(void * object,int flags)279 static int impl_eventfd_create(void *object, int flags)
280 {
281 struct impl *impl = object;
282 int res;
283
284 res = evl_new_xbuf(1024, 1024, "xbuf-%d-%p-%d", impl->pid, impl, impl->n_xbuf);
285 if (res < 0)
286 return res;
287
288 impl->n_xbuf++;
289
290 if (flags & SPA_FD_NONBLOCK)
291 fcntl(res, F_SETFL, fcntl(res, F_GETFL) | O_NONBLOCK);
292
293 return res;
294 }
295
impl_eventfd_write(void * object,int fd,uint64_t count)296 static int impl_eventfd_write(void *object, int fd, uint64_t count)
297 {
298 if (write(fd, &count, sizeof(uint64_t)) != sizeof(uint64_t))
299 return -errno;
300 return 0;
301 }
302
impl_eventfd_read(void * object,int fd,uint64_t * count)303 static int impl_eventfd_read(void *object, int fd, uint64_t *count)
304 {
305 if (oob_read(fd, count, sizeof(uint64_t)) != sizeof(uint64_t))
306 return -errno;
307 return 0;
308 }
309
310 /* signals */
impl_signalfd_create(void * object,int signal,int flags)311 static int impl_signalfd_create(void *object, int signal, int flags)
312 {
313 sigset_t mask;
314 int res, fl = 0;
315
316 if (flags & SPA_FD_CLOEXEC)
317 fl |= SFD_CLOEXEC;
318 if (flags & SPA_FD_NONBLOCK)
319 fl |= SFD_NONBLOCK;
320
321 sigemptyset(&mask);
322 sigaddset(&mask, signal);
323 res = signalfd(-1, &mask, fl);
324 sigprocmask(SIG_BLOCK, &mask, NULL);
325
326 return res;
327 }
328
impl_signalfd_read(void * object,int fd,int * signal)329 static int impl_signalfd_read(void *object, int fd, int *signal)
330 {
331 struct signalfd_siginfo signal_info;
332 int len;
333
334 len = read(fd, &signal_info, sizeof signal_info);
335 if (!(len == -1 && errno == EAGAIN) && len != sizeof signal_info)
336 return -errno;
337
338 *signal = signal_info.ssi_signo;
339
340 return 0;
341 }
342
343 static const struct spa_system_methods impl_system = {
344 SPA_VERSION_SYSTEM_METHODS,
345 .read = impl_read,
346 .write = impl_write,
347 .ioctl = impl_ioctl,
348 .close = impl_close,
349 .clock_gettime = impl_clock_gettime,
350 .clock_getres = impl_clock_getres,
351 .pollfd_create = impl_pollfd_create,
352 .pollfd_add = impl_pollfd_add,
353 .pollfd_mod = impl_pollfd_mod,
354 .pollfd_del = impl_pollfd_del,
355 .pollfd_wait = impl_pollfd_wait,
356 .timerfd_create = impl_timerfd_create,
357 .timerfd_settime = impl_timerfd_settime,
358 .timerfd_gettime = impl_timerfd_gettime,
359 .timerfd_read = impl_timerfd_read,
360 .eventfd_create = impl_eventfd_create,
361 .eventfd_write = impl_eventfd_write,
362 .eventfd_read = impl_eventfd_read,
363 .signalfd_create = impl_signalfd_create,
364 .signalfd_read = impl_signalfd_read,
365 };
366
impl_get_interface(struct spa_handle * handle,const char * type,void ** interface)367 static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
368 {
369 struct impl *impl;
370
371 spa_return_val_if_fail(handle != NULL, -EINVAL);
372 spa_return_val_if_fail(interface != NULL, -EINVAL);
373
374 impl = (struct impl *) handle;
375
376 if (spa_streq(type, SPA_TYPE_INTERFACE_System))
377 *interface = &impl->system;
378 else
379 return -ENOENT;
380
381 return 0;
382 }
383
impl_clear(struct spa_handle * handle)384 static int impl_clear(struct spa_handle *handle)
385 {
386 spa_return_val_if_fail(handle != NULL, -EINVAL);
387 return 0;
388 }
389
390 static size_t
impl_get_size(const struct spa_handle_factory * factory,const struct spa_dict * params)391 impl_get_size(const struct spa_handle_factory *factory,
392 const struct spa_dict *params)
393 {
394 return sizeof(struct impl);
395 }
396
397 static int
impl_init(const struct spa_handle_factory * factory,struct spa_handle * handle,const struct spa_dict * info,const struct spa_support * support,uint32_t n_support)398 impl_init(const struct spa_handle_factory *factory,
399 struct spa_handle *handle,
400 const struct spa_dict *info,
401 const struct spa_support *support,
402 uint32_t n_support)
403 {
404 struct impl *impl;
405 int res;
406
407 spa_return_val_if_fail(factory != NULL, -EINVAL);
408 spa_return_val_if_fail(handle != NULL, -EINVAL);
409
410 handle->get_interface = impl_get_interface;
411 handle->clear = impl_clear;
412
413 impl = (struct impl *) handle;
414 impl->system.iface = SPA_INTERFACE_INIT(
415 SPA_TYPE_INTERFACE_System,
416 SPA_VERSION_SYSTEM,
417 &impl_system, impl);
418
419 impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
420
421 impl->pid = getpid();
422
423 if ((res = evl_attach_self("evl-system-%d-%p", impl->pid, impl)) < 0) {
424 spa_log_error(impl->log, NAME " %p: init failed: %s", impl, spa_strerror(res));
425 return res;
426 }
427
428 spa_log_debug(impl->log, NAME " %p: initialized", impl);
429
430 return 0;
431 }
432
433 static const struct spa_interface_info impl_interfaces[] = {
434 {SPA_TYPE_INTERFACE_System,},
435 };
436
437 static int
impl_enum_interface_info(const struct spa_handle_factory * factory,const struct spa_interface_info ** info,uint32_t * index)438 impl_enum_interface_info(const struct spa_handle_factory *factory,
439 const struct spa_interface_info **info,
440 uint32_t *index)
441 {
442 spa_return_val_if_fail(factory != NULL, -EINVAL);
443 spa_return_val_if_fail(info != NULL, -EINVAL);
444 spa_return_val_if_fail(index != NULL, -EINVAL);
445
446 if (*index >= SPA_N_ELEMENTS(impl_interfaces))
447 return 0;
448
449 *info = &impl_interfaces[(*index)++];
450 return 1;
451 }
452
453 const struct spa_handle_factory spa_support_evl_system_factory = {
454 SPA_VERSION_HANDLE_FACTORY,
455 SPA_NAME_SUPPORT_SYSTEM,
456 NULL,
457 impl_get_size,
458 impl_init,
459 impl_enum_interface_info
460 };
461