diff options
Diffstat (limited to 'package/libtapi/src')
-rw-r--r-- | package/libtapi/src/Makefile | 27 | ||||
-rw-r--r-- | package/libtapi/src/dialdetector.c | 109 | ||||
-rw-r--r-- | package/libtapi/src/dialdetector.h | 109 | ||||
-rw-r--r-- | package/libtapi/src/events.c | 58 | ||||
-rw-r--r-- | package/libtapi/src/events.h | 18 | ||||
-rw-r--r-- | package/libtapi/src/list.h | 601 | ||||
-rw-r--r-- | package/libtapi/src/tapi-device.c | 46 | ||||
-rw-r--r-- | package/libtapi/src/tapi-device.h | 25 | ||||
-rw-r--r-- | package/libtapi/src/tapi-ioctl.h | 20 | ||||
-rw-r--r-- | package/libtapi/src/tapi-port.c | 126 | ||||
-rw-r--r-- | package/libtapi/src/tapi-port.h | 66 | ||||
-rw-r--r-- | package/libtapi/src/tapi-session.c | 122 | ||||
-rw-r--r-- | package/libtapi/src/tapi-session.h | 14 | ||||
-rw-r--r-- | package/libtapi/src/tapi-stream.c | 42 | ||||
-rw-r--r-- | package/libtapi/src/tapi-stream.h | 19 | ||||
-rw-r--r-- | package/libtapi/src/tapidemo.c | 56 | ||||
-rw-r--r-- | package/libtapi/src/timer_fd.c | 33 | ||||
-rw-r--r-- | package/libtapi/src/timerfd.h | 61 |
18 files changed, 1552 insertions, 0 deletions
diff --git a/package/libtapi/src/Makefile b/package/libtapi/src/Makefile new file mode 100644 index 0000000000..70b46eb577 --- /dev/null +++ b/package/libtapi/src/Makefile @@ -0,0 +1,27 @@ +ifndef CFLAGS +CFLAGS = -O2 -g -I ../src +endif + +FPIC=-fPIC + +all: libtapi.so + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $^ $(FPIC) + +TAPI_OBJS = \ + timer_fd.o \ + events.o \ + tapi-port.o \ + tapi-device.o \ + tapi-session.o \ + tapi-stream.o + +tapidemo: tapidemo.o libtapi.so + $(CC) $(LDFLAGS) -o $@ $^ + +libtapi.so: $(TAPI_OBJS) + $(CC) $(LDFLAGS) -shared -o $@ $^ $(FPIC) + +clean: + rm -rf *.o *.so diff --git a/package/libtapi/src/dialdetector.c b/package/libtapi/src/dialdetector.c new file mode 100644 index 0000000000..ff91b37aa2 --- /dev/null +++ b/package/libtapi/src/dialdetector.c @@ -0,0 +1,109 @@ +#include <linux/input.h> +#include <sys/epoll.h> +#include <stdint.h> +#include <stdbool.h> + + +#include <stdlib.h> +#include <stdio.h> + +#include "events.h" +#include "timerfd.h" + +#include "tapi-port.h" +#include "dialdetector.h" + +static const struct itimerspec dialdetector_timeout = { + .it_value.tv_sec = 3, +}; + +static void dialdetector_note_digit(struct dialdetector *d, unsigned char digit) +{ + printf("note digit: %d\n", d->num_digits); + d->digits[d->num_digits] = digit; + ++d->num_digits; +} + +static void dialdetector_reset(struct dialdetector *d) +{ + event_unregister(d->timer_fd); + d->num_digits = 0; + d->state = DIALDETECTOR_IDLE; +} + +static bool dialdetector_timeout_event(int events, void *data) +{ + char num[20]; + struct dialdetector *dialdetector = data; + int i; + + for (i = 0; i < dialdetector->num_digits; ++i) { + num[i] = '0' + dialdetector->digits[i]; + } + num[i] = '\0'; + + printf("Dialing: %s\n", num); + dialdetector->dial_callback(dialdetector->port, dialdetector->num_digits, + dialdetector->digits); + + dialdetector_reset(dialdetector); + + return false; +} + +static void dialdetector_port_event(struct tapi_port *port, + struct tapi_event *event, void *data) +{ + struct dialdetector *d = data; + + printf("port event: %d %d\n", d->state, event->hook.on); + + switch (d->state) { + case DIALDETECTOR_IDLE: + if (event->type == TAPI_EVENT_TYPE_HOOK && event->hook.on == false) { + d->state = DIALDETECTOR_WAIT_FOR_NUMBER; + event_register(d->timer_fd, EPOLLIN, &d->timeout_cb); + timerfd_settime(d->timer_fd, 0, &dialdetector_timeout, NULL); + } + break; + case DIALDETECTOR_WAIT_FOR_NUMBER: + case DIALDETECTOR_WAIT_FOR_NUMBER_TIMEOUT: + switch (event->type) { + case TAPI_EVENT_TYPE_HOOK: + if (event->hook.on == true) + dialdetector_reset(d); + break; + case TAPI_EVENT_TYPE_DTMF: + if (d->state == DIALDETECTOR_WAIT_FOR_NUMBER) + event_register(d->timer_fd, EPOLLIN, &d->timeout_cb); + timerfd_settime(d->timer_fd, 0, &dialdetector_timeout, NULL); + d->state = DIALDETECTOR_WAIT_FOR_NUMBER_TIMEOUT; + dialdetector_note_digit(d, event->dtmf.code); + break; + default: + break; + } + } +} + +struct dialdetector *dialdetector_alloc(struct tapi_port *port) +{ + struct dialdetector *dialdetector; + dialdetector = malloc(sizeof(*dialdetector)); + + dialdetector->timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); + dialdetector->port = port; + dialdetector->num_digits = 0; + dialdetector->state = DIALDETECTOR_IDLE; + + dialdetector->timeout_cb.callback = dialdetector_timeout_event; + dialdetector->timeout_cb.data = dialdetector; + + dialdetector->port_listener.callback = dialdetector_port_event; + dialdetector->port_listener.data = dialdetector; + + tapi_port_register_event(port, &dialdetector->port_listener); + + + return dialdetector; +} diff --git a/package/libtapi/src/dialdetector.h b/package/libtapi/src/dialdetector.h new file mode 100644 index 0000000000..ff91b37aa2 --- /dev/null +++ b/package/libtapi/src/dialdetector.h @@ -0,0 +1,109 @@ +#include <linux/input.h> +#include <sys/epoll.h> +#include <stdint.h> +#include <stdbool.h> + + +#include <stdlib.h> +#include <stdio.h> + +#include "events.h" +#include "timerfd.h" + +#include "tapi-port.h" +#include "dialdetector.h" + +static const struct itimerspec dialdetector_timeout = { + .it_value.tv_sec = 3, +}; + +static void dialdetector_note_digit(struct dialdetector *d, unsigned char digit) +{ + printf("note digit: %d\n", d->num_digits); + d->digits[d->num_digits] = digit; + ++d->num_digits; +} + +static void dialdetector_reset(struct dialdetector *d) +{ + event_unregister(d->timer_fd); + d->num_digits = 0; + d->state = DIALDETECTOR_IDLE; +} + +static bool dialdetector_timeout_event(int events, void *data) +{ + char num[20]; + struct dialdetector *dialdetector = data; + int i; + + for (i = 0; i < dialdetector->num_digits; ++i) { + num[i] = '0' + dialdetector->digits[i]; + } + num[i] = '\0'; + + printf("Dialing: %s\n", num); + dialdetector->dial_callback(dialdetector->port, dialdetector->num_digits, + dialdetector->digits); + + dialdetector_reset(dialdetector); + + return false; +} + +static void dialdetector_port_event(struct tapi_port *port, + struct tapi_event *event, void *data) +{ + struct dialdetector *d = data; + + printf("port event: %d %d\n", d->state, event->hook.on); + + switch (d->state) { + case DIALDETECTOR_IDLE: + if (event->type == TAPI_EVENT_TYPE_HOOK && event->hook.on == false) { + d->state = DIALDETECTOR_WAIT_FOR_NUMBER; + event_register(d->timer_fd, EPOLLIN, &d->timeout_cb); + timerfd_settime(d->timer_fd, 0, &dialdetector_timeout, NULL); + } + break; + case DIALDETECTOR_WAIT_FOR_NUMBER: + case DIALDETECTOR_WAIT_FOR_NUMBER_TIMEOUT: + switch (event->type) { + case TAPI_EVENT_TYPE_HOOK: + if (event->hook.on == true) + dialdetector_reset(d); + break; + case TAPI_EVENT_TYPE_DTMF: + if (d->state == DIALDETECTOR_WAIT_FOR_NUMBER) + event_register(d->timer_fd, EPOLLIN, &d->timeout_cb); + timerfd_settime(d->timer_fd, 0, &dialdetector_timeout, NULL); + d->state = DIALDETECTOR_WAIT_FOR_NUMBER_TIMEOUT; + dialdetector_note_digit(d, event->dtmf.code); + break; + default: + break; + } + } +} + +struct dialdetector *dialdetector_alloc(struct tapi_port *port) +{ + struct dialdetector *dialdetector; + dialdetector = malloc(sizeof(*dialdetector)); + + dialdetector->timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); + dialdetector->port = port; + dialdetector->num_digits = 0; + dialdetector->state = DIALDETECTOR_IDLE; + + dialdetector->timeout_cb.callback = dialdetector_timeout_event; + dialdetector->timeout_cb.data = dialdetector; + + dialdetector->port_listener.callback = dialdetector_port_event; + dialdetector->port_listener.data = dialdetector; + + tapi_port_register_event(port, &dialdetector->port_listener); + + + return dialdetector; +} diff --git a/package/libtapi/src/events.c b/package/libtapi/src/events.c new file mode 100644 index 0000000000..36e9b62a01 --- /dev/null +++ b/package/libtapi/src/events.c @@ -0,0 +1,58 @@ +#include <stdbool.h> +#include <sys/epoll.h> +#include <stdlib.h> +#include <errno.h> + +#include <stdio.h> + +#include "events.h" + +struct event_callback *event_callbacks; + +static int event_epoll_fd = -1; + +int event_register(int fd, int events, struct event_callback *cb) +{ + struct epoll_event ev; + + if (event_epoll_fd == -1) + event_epoll_fd = epoll_create(1); + + ev.events = events; + ev.data.ptr = cb; + + cb->fd = fd; + + return epoll_ctl(event_epoll_fd, EPOLL_CTL_ADD, fd, &ev); +} + +int event_unregister(int fd) +{ + return epoll_ctl(event_epoll_fd, EPOLL_CTL_DEL, fd, NULL); +} + +int tapi_mainloop(void) +{ + struct epoll_event ev[10]; + struct event_callback *cb; + int ret; + bool keep; + int i; + + if (event_epoll_fd == -1) + event_epoll_fd = epoll_create(1); + + while(true) { + ret = epoll_wait(event_epoll_fd, ev, 10, -1); + for(i = 0; i < ret; ++i) { + cb = ev[i].data.ptr; + keep = cb->callback(ev[i].events, cb->data); + if (!keep) + event_unregister(cb->fd); + } + if (ret < 0) + printf("epoll: %d\n", errno); + } + + return 0; +} diff --git a/package/libtapi/src/events.h b/package/libtapi/src/events.h new file mode 100644 index 0000000000..9912fac3a4 --- /dev/null +++ b/package/libtapi/src/events.h @@ -0,0 +1,18 @@ +#ifndef __EVENTS_H__ +#define __EVENTS_H__ + +struct event_callback { + bool (*callback)(int events, void *data); + void *data; + int fd; +}; + +int event_register(int fd, int events, + struct event_callback *callback); + +int event_unregister(int fd); + + +int tapi_mainloop(void); + +#endif diff --git a/package/libtapi/src/list.h b/package/libtapi/src/list.h new file mode 100644 index 0000000000..2959a061d3 --- /dev/null +++ b/package/libtapi/src/list.h @@ -0,0 +1,601 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include <stddef.h> +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#ifndef container_of +#define container_of(ptr, type, member) ( \ + (type *)( (char *)ptr - offsetof(type,member) )) +#endif + + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +static inline void INIT_HLIST_NODE(struct hlist_node *h) +{ + h->next = NULL; + h->pprev = NULL; +} + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = NULL; + n->pprev = NULL; +} + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos; pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos; pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + +#endif diff --git a/package/libtapi/src/tapi-device.c b/package/libtapi/src/tapi-device.c new file mode 100644 index 0000000000..66e4260a0b --- /dev/null +++ b/package/libtapi/src/tapi-device.c @@ -0,0 +1,46 @@ +#include <stdio.h> + +#include "tapi-device.h" +#include "tapi-ioctl.h" + +int tapi_device_open(unsigned int id, struct tapi_device *dev) +{ + char path[100]; + + snprintf(path, sizeof(path), "/dev/tapi%dC", id); + dev->control_fd = open(path, 0); + if (dev->control_fd < 0) + return -1; + + snprintf(dev->stream_path, 100, "/dev/tapi%dS", id); + + dev->id = id; + dev->num_ports = 2; + + return 0; +} + +int tapi_link_alloc(struct tapi_device *dev, unsigned int ep1, unsigned int ep2) +{ + return ioctl(dev->control_fd, TAPI_CONTROL_IOCTL_LINK_ALLOC, (ep1 << 16) | ep2); +} + +int tapi_link_free(struct tapi_device *dev, unsigned int link) +{ + return ioctl(dev->control_fd, TAPI_CONTROL_IOCTL_LINK_FREE, link); +} + +int tapi_link_enable(struct tapi_device *dev, unsigned int link) +{ + return ioctl(dev->control_fd, TAPI_CONTROL_IOCTL_LINK_ENABLE, link); +} + +int tapi_link_disable(struct tapi_device *dev, unsigned int link) +{ + return ioctl(dev->control_fd, TAPI_CONTROL_IOCTL_LINK_DISABLE, link); +} + +int tapi_sync(struct tapi_device *dev) +{ + return ioctl(dev->control_fd, TAPI_CONTROL_IOCTL_SYNC, 0); +} diff --git a/package/libtapi/src/tapi-device.h b/package/libtapi/src/tapi-device.h new file mode 100644 index 0000000000..27658b9d83 --- /dev/null +++ b/package/libtapi/src/tapi-device.h @@ -0,0 +1,25 @@ +#ifndef __TAPI_DEVICE_H__ +#define __TAPI_DEVICE_H__ + +struct tapi_device { + int control_fd; + int stream_fd; + struct tapi_port *ports; + char stream_path[100]; + + unsigned int id; + + unsigned int num_ports; +}; + +struct tapi_endpoint; + +int tapi_device_open(unsigned int id, struct tapi_device *dev); + +int tapi_link_alloc(struct tapi_device *dev, unsigned int ep1, unsigned int ep2); +int tapi_link_free(struct tapi_device *dev, unsigned int link); +int tapi_link_enable(struct tapi_device *dev, unsigned int link); +int tapi_link_disable(struct tapi_device *dev, unsigned int link); +int tapi_sync(struct tapi_device *dev); + +#endif diff --git a/package/libtapi/src/tapi-ioctl.h b/package/libtapi/src/tapi-ioctl.h new file mode 100644 index 0000000000..c43fac1e73 --- /dev/null +++ b/package/libtapi/src/tapi-ioctl.h @@ -0,0 +1,20 @@ + +#include <linux/ioctl.h> + +#define TAPI_MAGIC 't' +#define TAPI_IOCTL(x) _IO(TAPI_MAGIC, (x)) + +#define TAPI_CONTROL_IOCTL_LINK_ALLOC TAPI_IOCTL(0) +#define TAPI_CONTROL_IOCTL_LINK_FREE TAPI_IOCTL(1) +#define TAPI_CONTROL_IOCTL_LINK_ENABLE TAPI_IOCTL(2) +#define TAPI_CONTROL_IOCTL_LINK_DISABLE TAPI_IOCTL(3) + +#define TAPI_CONTROL_IOCTL_SYNC TAPI_IOCTL(4) + +#define TAPI_PORT_IOCTL_GET_ENDPOINT TAPI_IOCTL(5) +#define TAPI_PORT_IOCTL_SET_RING TAPI_IOCTL(6) + +#define TAPI_STREAM_IOCTL_GET_ENDPOINT TAPI_IOCTL(7) +#define TAPI_STREAM_IOCTL_CONFIGURE TAPI_IOCTL(8) +#define TAPI_STREAM_IOCTL_START TAPI_IOCTL(9) +#define TAPI_STREAM_IOCTL_STOP TAPI_IOCTL(10) diff --git a/package/libtapi/src/tapi-port.c b/package/libtapi/src/tapi-port.c new file mode 100644 index 0000000000..d06d946e12 --- /dev/null +++ b/package/libtapi/src/tapi-port.c @@ -0,0 +1,126 @@ +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/epoll.h> +#include <unistd.h> + +#include <linux/input.h> + +#include "tapi-ioctl.h" +#include "tapi-device.h" +#include "tapi-port.h" + +#include "events.h" +#include "list.h" + +static void tapi_port_event_dispatch(struct tapi_port *port, + struct tapi_event *event) +{ + struct tapi_port_event_listener *l; + + list_for_each_entry(l, &port->event_listeners, head) { + l->callback(port, event, l->data); + } +} + +static bool tapi_port_input_event(int events, void *data) +{ + struct tapi_port *port = data; + struct input_event event; + struct tapi_event tapi_event; + int ret; + + ret = read(port->input_fd, &event, sizeof(event)); + if (ret < 0) { + fprintf(stderr, "Port %d failed to read from input device: %d\n", + port->id, errno); + return true; + } + + if (!event.value) + return true; + + switch (event.code) { + case KEY_NUMERIC_0 ... KEY_NUMERIC_9: + tapi_event.type = TAPI_EVENT_TYPE_DTMF; + tapi_event.dtmf.code = event.code - KEY_NUMERIC_0; + break; + case KEY_NUMERIC_POUND: + tapi_event.type = TAPI_EVENT_TYPE_DTMF; + tapi_event.dtmf.code = 10; + break; + case KEY_NUMERIC_STAR: + tapi_event.type = TAPI_EVENT_TYPE_DTMF; + tapi_event.dtmf.code = 11; + break; + case KEY_ESC: + tapi_event.type = TAPI_EVENT_TYPE_HOOK; + tapi_event.hook.on = true; + break; + case KEY_ENTER: + tapi_event.type = TAPI_EVENT_TYPE_HOOK; + tapi_event.hook.on = false; + break; + default: + return true; + } + + if (tapi_event.type == TAPI_EVENT_TYPE_DTMF) + tapi_event.dtmf.time = event.time; + + tapi_port_event_dispatch(port, &tapi_event); + + return true; +} + +int tapi_port_open(struct tapi_device *dev, unsigned int id, struct tapi_port *port) +{ + int ret; + char path[100]; + + port->id = id; + + snprintf(path, 100, "/dev/tapi%uP%u", dev->id, id); + port->fd = open(path, 0); + if (port->fd < 0) { + printf("Failed to open %s: %d\n", path, errno); + return errno; + } + + snprintf(path, 100, "/dev/event%u", id); + port->input_fd = open(path, O_RDONLY); + if (port->input_fd < 0) { + printf("Failed to open %s: %d\n", path, errno); + return errno; + } + + port->ep = ioctl(port->fd, TAPI_PORT_IOCTL_GET_ENDPOINT, 0); + + INIT_LIST_HEAD(&port->event_listeners); + + port->input_cb.callback = tapi_port_input_event; + port->input_cb.data = port; + + return event_register(port->input_fd, EPOLLIN, + &port->input_cb); +} + +int tapi_port_set_ring(struct tapi_port *port, bool ring) +{ + return ioctl(port->fd, TAPI_PORT_IOCTL_SET_RING, ring); +} + +int tapi_port_register_event(struct tapi_port *port, + struct tapi_port_event_listener *cb) +{ + list_add_tail(&cb->head, &port->event_listeners); + return 0; +} + +void tapi_port_unregister_event(struct tapi_port *port, + struct tapi_port_event_listener *cb) +{ + list_del(&cb->head); +} diff --git a/package/libtapi/src/tapi-port.h b/package/libtapi/src/tapi-port.h new file mode 100644 index 0000000000..a290a4002d --- /dev/null +++ b/package/libtapi/src/tapi-port.h @@ -0,0 +1,66 @@ +#ifndef __TAPI_PORT_H__ +#define __TAPI_PORT_H__ + +#include <sys/time.h> + +#include "list.h" +#include "events.h" + +struct tapi_port; +struct tapi_device; + +struct tapi_dtmf_event { + struct timeval time; + unsigned char code; +}; + +struct tapi_hook_event { + bool on; +}; + +enum tapi_event_type { + TAPI_EVENT_TYPE_DTMF, + TAPI_EVENT_TYPE_HOOK, +}; + +struct tapi_event { + enum tapi_event_type type; + union { + struct tapi_dtmf_event dtmf; + struct tapi_hook_event hook; + }; +}; + +struct tapi_port_event_listener { + void (*callback)(struct tapi_port *, struct tapi_event *event, void *data); + void *data; + + struct list_head head; +}; + +struct tapi_port { + int id; + int fd; + int input_fd; + unsigned int ep; + + struct event_callback input_cb; + + struct list_head event_listeners; +}; + +int tapi_port_open(struct tapi_device *dev, unsigned int id, struct tapi_port +*port); +int tapi_port_set_ring(struct tapi_port *port, bool ring); +int tapi_port_register_event(struct tapi_port *port, + struct tapi_port_event_listener *cb); + +void tapi_port_unregister_event(struct tapi_port *port, + struct tapi_port_event_listener *cb); + +static inline int tapi_port_get_endpoint(struct tapi_port *port) +{ + return port->ep; +} + +#endif diff --git a/package/libtapi/src/tapi-session.c b/package/libtapi/src/tapi-session.c new file mode 100644 index 0000000000..8b17053115 --- /dev/null +++ b/package/libtapi/src/tapi-session.c @@ -0,0 +1,122 @@ +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> + +#include "tapi-device.h" +#include "tapi-port.h" +#include "tapi-session.h" + +enum tapi_session_port_state { + TAPI_SESSION_PORT_STATE_IDLE, + TAPI_SESSION_PORT_STATE_RINGING, + TAPI_SESSION_PORT_STATE_ACTIVE, +}; + +struct tapi_session_port { + struct tapi_port *port; + struct tapi_port_event_listener event_listener; + + enum tapi_session_port_state state; +}; + +struct tapi_session { + struct tapi_device *dev; + struct tapi_session_port caller; + struct tapi_session_port callee; + + bool active; + unsigned int link; + + void (*release)(struct tapi_session *session, void *data); +}; + +static void tapi_session_terminate(struct tapi_session *session) +{ + if (session->active) { + tapi_link_enable(session->dev, session->link); + tapi_sync(session->dev); + tapi_link_free(session->dev, session->link); + } + + switch (session->callee.state) { + case TAPI_SESSION_PORT_STATE_RINGING: + tapi_port_set_ring(session->callee.port, false); + break; + default: + break; + } + + session->active = false; +} + +static void tapi_session_caller_event(struct tapi_port *port, + struct tapi_event *event, void *data) +{ + struct tapi_session *session = data; + + if (event->type != TAPI_EVENT_TYPE_HOOK) + return; + + if (event->hook.on) { + tapi_session_terminate(session); + } +} + +static void tapi_session_callee_event(struct tapi_port *port, + struct tapi_event *event, void *data) +{ + struct tapi_session *session = data; + + if (event->type != TAPI_EVENT_TYPE_HOOK) + return; + + if (event->hook.on) { + if (session->callee.state == TAPI_SESSION_PORT_STATE_ACTIVE) { + tapi_session_terminate(session); + } + } else { + if (session->callee.state == TAPI_SESSION_PORT_STATE_RINGING) { + tapi_port_set_ring(session->callee.port, false); + session->link = tapi_link_alloc(session->dev, + session->caller.port->ep, session->callee.port->ep); + session->callee.state = TAPI_SESSION_PORT_STATE_ACTIVE; + tapi_link_enable(session->dev, session->link); + tapi_sync(session->dev); + session->active = true; + } + } +} + +struct tapi_session *tapi_session_alloc(struct tapi_device *dev, + struct tapi_port *caller, struct tapi_port *callee, + void (*release)(struct tapi_session *session, void *data), void *release_data) +{ + struct tapi_session *session; + struct tapi_session_port *session_port; + + session = malloc(sizeof(*session)); + + session->dev = dev; + + session->callee.port = callee; + session->callee.state = TAPI_SESSION_PORT_STATE_RINGING; + session->callee.event_listener.callback = tapi_session_callee_event; + session->callee.event_listener.data = session; + tapi_port_register_event(callee, &session->callee.event_listener); + + session->caller.port = caller; + session->caller.state = TAPI_SESSION_PORT_STATE_ACTIVE; + session->caller.event_listener.callback = tapi_session_caller_event; + session->caller.event_listener.data = session; + tapi_port_register_event(caller, &session->caller.event_listener); + + tapi_port_set_ring(callee, true); +} + +void tapi_session_free(struct tapi_session *session) +{ + tapi_session_terminate(session); + tapi_port_register_event(session->callee.port, &session->callee.event_listener); + tapi_port_register_event(session->caller.port, &session->caller.event_listener); + free(session); +} diff --git a/package/libtapi/src/tapi-session.h b/package/libtapi/src/tapi-session.h new file mode 100644 index 0000000000..15d94735eb --- /dev/null +++ b/package/libtapi/src/tapi-session.h @@ -0,0 +1,14 @@ +#ifndef __TAPI_SESSION_H__ +#define __TAPI_SESSION_H__ + +struct tapi_device; +struct tapi_port; +struct tapi_session; + +struct tapi_session *tapi_session_alloc(struct tapi_device *dev, + struct tapi_port *caller, struct tapi_port *callee, + void (*release)(struct tapi_session *session, void *data), void *release_data); + +void tapi_session_free(struct tapi_session *session); + +#endif diff --git a/package/libtapi/src/tapi-stream.c b/package/libtapi/src/tapi-stream.c new file mode 100644 index 0000000000..5ae44a1631 --- /dev/null +++ b/package/libtapi/src/tapi-stream.c @@ -0,0 +1,42 @@ +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "tapi-device.h" +#include "tapi-stream.h" +#include "tapi-ioctl.h" + +struct tapi_stream *tapi_stream_alloc(struct tapi_device *dev) +{ + struct tapi_stream *stream; + + stream = malloc(sizeof(*stream)); + if (!stream) + return NULL; + + stream->fd = open(dev->stream_path, O_RDWR); + + if (stream->fd < 0) { + free(stream); + return NULL; + } + + stream->ep = ioctl(stream->fd, TAPI_STREAM_IOCTL_GET_ENDPOINT, 0); + + if (stream->ep < 0) { + close(stream->fd); + free(stream); + return NULL; + } + + return stream; +} + +void tapi_stream_free(struct tapi_stream *stream) +{ + close(stream->fd); + free(stream); +} diff --git a/package/libtapi/src/tapi-stream.h b/package/libtapi/src/tapi-stream.h new file mode 100644 index 0000000000..0e3e04af24 --- /dev/null +++ b/package/libtapi/src/tapi-stream.h @@ -0,0 +1,19 @@ +#ifndef __TAPI_STREAM_H__ +#define __TAPI_STREAM_H__ + +struct tapi_device; + +struct tapi_stream { + int fd; + int ep; +}; + +struct tapi_stream *tapi_stream_alloc(struct tapi_device *); +void tapi_stream_free(struct tapi_stream *); + +static inline int tapi_stream_get_endpoint(struct tapi_stream *stream) +{ + return stream->ep; +} + +#endif diff --git a/package/libtapi/src/tapidemo.c b/package/libtapi/src/tapidemo.c new file mode 100644 index 0000000000..63098b89cf --- /dev/null +++ b/package/libtapi/src/tapidemo.c @@ -0,0 +1,56 @@ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <poll.h> + +#include <linux/input.h> +/*#include <sys/timerfd.h>*/ +#include "timerfd.h" +#include "dialdetector.h" + +#include "tapi-ioctl.h" + +#include "tapi-device.h" +#include "tapi-port.h" + +static struct tapi_device dev; +static struct tapi_port ports[2]; + +void dial_callback(struct tapi_port *port, size_t num_digits, const unsigned char *digits) +{ + unsigned int link; + + if (num_digits != 1) + return; + if (port->id == digits[0] || digits[0] > 1) + return; + + tapi_port_set_ring(&ports[digits[0]], true); + + tapi_session_alloc(&dev, port, &ports[digits[0]]); +} + +int main(int argc, char *argv[]) +{ + struct dialdetector *dd, *dd2; + unsigned int link; + unsigned char buf[1024]; + int ret; + + tapi_device_open(0, &dev); + tapi_port_open(0, 0, &ports[0]); + tapi_port_open(0, 1, &ports[1]); + + dd = dialdetector_alloc(&ports[0]); + dd->dial_callback = dial_callback; + dd2 = dialdetector_alloc(&ports[1]); + dd2->dial_callback = dial_callback; + + tapi_mainloop(); + + return 0; +} diff --git a/package/libtapi/src/timer_fd.c b/package/libtapi/src/timer_fd.c new file mode 100644 index 0000000000..fbc951a9fc --- /dev/null +++ b/package/libtapi/src/timer_fd.c @@ -0,0 +1,33 @@ +/* vi: set sw=4 ts=4: */ +/* + * timerfd_create() / timerfd_settime() / timerfd_gettime() for uClibc + * + * Copyright (C) 2009 Stephan Raue <stephan@openelec.tv> + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + */ + +#include <sys/syscall.h> +/*#include <sys/timerfd.h>*/ +#include "timerfd.h" + +/* + * timerfd_create() + */ +#ifdef __NR_timerfd_create +int timerfd_create(int clockid, int flags) +{ + return syscall(__NR_timerfd_create, clockid, flags); +} +#endif + +/* + * timerfd_settime() + */ +#ifdef __NR_timerfd_settime +int timerfd_settime(int ufd, int flags, const struct itimerspec *umtr, struct itimerspec *otmr) +{ + return syscall(__NR_timerfd_settime, ufd, flags, umtr, otmr); +} +#endif + diff --git a/package/libtapi/src/timerfd.h b/package/libtapi/src/timerfd.h new file mode 100644 index 0000000000..f17bc798a6 --- /dev/null +++ b/package/libtapi/src/timerfd.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2008 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _SYS_TIMERFD_H +#define _SYS_TIMERFD_H 1 + +#include <time.h> + + +/* Bits to be set in the FLAGS parameter of `timerfd_create'. */ +enum + { + TFD_CLOEXEC = 02000000, +#define TFD_CLOEXEC TFD_CLOEXEC + TFD_NONBLOCK = 04000 +#define TFD_NONBLOCK TFD_NONBLOCK + }; + + +/* Bits to be set in the FLAGS parameter of `timerfd_settime'. */ +enum + { + TFD_TIMER_ABSTIME = 1 << 0 +#define TFD_TIMER_ABSTIME TFD_TIMER_ABSTIME + }; + + +__BEGIN_DECLS + +/* Return file descriptor for new interval timer source. */ +extern int timerfd_create (clockid_t __clock_id, int __flags) __THROW; + +/* Set next expiration time of interval timer source UFD to UTMR. If + FLAGS has the TFD_TIMER_ABSTIME flag set the timeout value is + absolute. Optionally return the old expiration time in OTMR. */ +extern int timerfd_settime (int __ufd, int __flags, + __const struct itimerspec *__utmr, + struct itimerspec *__otmr) __THROW; + +/* Return the next expiration time of UFD. */ +extern int timerfd_gettime (int __ufd, struct itimerspec *__otmr) __THROW; + +__END_DECLS + +#endif /* sys/timerfd.h */ + |