Related
This question is elaboration and continuation of my previous question - On epoll_pwait, POSIX timers and X11 events. Most of X11 events is either delayed or dropped. I work with XCB window that displays some graphical information. The application has an insteractive input and had more or less static display. Now the requirenments had changed and I need to add some pereodic computations and have a interactive input.
The problem with the new requirenments is that computations goes at high rate. With that, I get inconsistent interaction with the XCB window. Input may lag behind, or change the rate of processing.
The setup is multiplex events with epoll_pwait. The events are signals, X11 events and recently added timers/timeout.
What I understand as of now, I need to separate user interaction from the computations. The problem with my setup, as of now, is that rate of X11 events changes in a way I can't explain.
So I decide to separate waiting on X11 events with the rest of the logic. Can you suggest a proper way to do it? Will having a X11 window in a separate thread/process/epoll set help?
And the actual question, as I look upon it now, is, what is a frequency of epoll_wait wakeups? I plan to have one epoll_wait in a loop. Maybe some processes to be wait on. I understand that epoll_wait will "wakeup" at some random points int time.
Update
My setup is close to this one:
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <math.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/signalfd.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sys/socket.h>
#include <xcb/xcb.h>
static struct timespec tm_p, tm_c, tm_diff;
static unsigned long nevents = 0;
static inline void timespec_diff(struct timespec *a, struct timespec *b, struct timespec *res) {
res->tv_sec = a->tv_sec - b->tv_sec;
res->tv_nsec = a->tv_nsec - b->tv_nsec;
if (res->tv_nsec < 0) {
--res->tv_sec;
res->tv_nsec += 1000000000L;
}
}
static double compute(){
double t = 1.0;
double eps = 1e-7;
double eps_2 = eps / 2.0;
clock_gettime(CLOCK_MONOTONIC, &tm_p);
while(t > eps){
t -= eps;
t += eps;
t -= eps_2 + eps_2;
}
clock_gettime(CLOCK_MONOTONIC, &tm_c);
timespec_diff(&tm_c, &tm_p, &tm_diff);
printf(" compute: %ld %f\n", tm_diff.tv_sec, tm_diff.tv_nsec / 1e9);
return (int)t;
}
/* defines for epoll */
#define MAXEVENTS 64
#define SET_EV(_ev,_fd,_events) _ev.data.fd = _fd; _ev.events = _events
static int xcb_get_atom(xcb_connection_t *c,
const char *name,
xcb_atom_t *atom)
{
xcb_intern_atom_cookie_t cookie;
xcb_generic_error_t *error;
xcb_intern_atom_reply_t *reply;
cookie = xcb_intern_atom(c, 0, strlen(name), name);
reply = xcb_intern_atom_reply(c, cookie, &error);
if(NULL == reply){
free(error);
return -1;
}
*atom = reply->atom;
free(reply);
return 0;
}
static int xcb_change_prop_wm_close(xcb_connection_t *c,
xcb_window_t window,
xcb_atom_t wm_p,
xcb_atom_t atom)
{
xcb_void_cookie_t cookie;
xcb_generic_error_t *error;
xcb_atom_enum_t type = XCB_ATOM_ATOM;
uint8_t format = 32;
uint32_t data_len = 1;
cookie = xcb_change_property_checked(c, /* xcb connection */
XCB_PROP_MODE_REPLACE, /* mode */
window, /* window */
wm_p, /* the property to change */
type, /* type of the property */
format, /* format(bits) */
data_len, /* number of elements(see format) */
&atom /* property data */
);
error = xcb_request_check(c, cookie);
if (error) {
free(error);
return -1;
}
return 0;
}
int main()
{
xcb_connection_t *c;
xcb_screen_t *screen;
xcb_window_t win;
xcb_atom_t a_wm_p;
xcb_atom_t wm_p_close;
struct epoll_event ep_ev, *ep_evs = NULL;
struct signalfd_siginfo siginf;
sigset_t mask_sigs, mask_osigs;
int sig_fd = -1, x11_fd = -1, ep_fd = -1, tm_fd = -1;
/* set up signals */
if(sigemptyset(&mask_sigs) < 0){
perror(" * sigemptyset(&mask_sigs)");
goto main_terminate;
}
if(sigaddset(&mask_sigs, SIGINT)){ /* these signals will be blocked. the signals will arrive */
perror(" * sigaddset(&mask_sigs, SIGINT)");
goto main_terminate;
}
if(sigaddset(&mask_sigs, SIGQUIT)){ /* to epoll and not to a default signal handler. */
perror(" * sigaddset(&mask_sigs, SIGQUIT)");
goto main_terminate;
}
/* save old sigmask, replace it with new sigmask */
if(sigprocmask(SIG_BLOCK, &mask_sigs, &mask_osigs) < 0){
perror(" * sigprocmask(SIG_BLOCK, &mask_sigs, &mask_osigs)");
goto main_terminate;
}
/* get signal file descriptor */
if((sig_fd = signalfd(-1, &mask_sigs, 0)) < 0){
perror(" * signalfd(-1, &mask_sigs, 0)");
goto main_terminate;
}
/* set signal fd as non-blocking */
{
int on = 1;
if(ioctl(sig_fd, FIONBIO, (char *)&on) < 0){
perror(" * ioctl(sig_fd, FIONBIO)");
goto main_terminate;
}
}
/* Open the connection to the X server */
c = xcb_connect (NULL, NULL);
/* Get the first screen */
screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
/* Ask for our window's Id */
win = xcb_generate_id(c);
/* Create the window */
{
unsigned int cw_mask = XCB_CW_BORDER_PIXEL
| XCB_CW_EVENT_MASK
;
/* values must follow in the incresing order of the cw_mask constants */
unsigned int cw_values[] = {screen->white_pixel,
XCB_EVENT_MASK_KEY_PRESS|XCB_EVENT_MASK_KEY_RELEASE
};
xcb_create_window (c, /* Connection */
XCB_COPY_FROM_PARENT, /* depth (same as root)*/
win, /* window Id */
screen->root, /* parent window */
0, 0, /* x, y */
150, 150, /* width, height */
10, /* border_width */
XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */
screen->root_visual, /* visual */
cw_mask, cw_values); /* masks */
}
/* get x11 connection file descriptor */
x11_fd = xcb_get_file_descriptor(c);
/* atom WM_PROTOCOLS */
if(xcb_get_atom(c, "WM_PROTOCOLS", &a_wm_p) < 0){
fprintf(stderr, " %s:%s:%d\n", __FILE__, __func__, __LINE__);
return -1;
}
/* atom window close */
if(xcb_get_atom(c, "WM_DELETE_WINDOW", &wm_p_close) < 0){
fprintf(stderr, " %s:%s:%d\n", __FILE__, __func__, __LINE__);
return -1;
}
{ /* wm prop: intercept close */
if(xcb_change_prop_wm_close(c, win, a_wm_p, wm_p_close) < 0){
fprintf(stderr, " %s:%s:%d\n", __FILE__, __func__, __LINE__);
goto main_terminate;
}
}
/* create epoll set file descriptor */
if((ep_fd = epoll_create(1)) < 0){
perror(" * epoll_create");
goto main_terminate;
}
/* allocate events for epoll queue */
if(NULL == (ep_evs = (struct epoll_event*)calloc(MAXEVENTS,sizeof(ep_ev)))){
perror(" * calloc(MAXEVENTS)");
goto main_terminate;
}
{ /* fd timer */
struct itimerspec ts;
if((tm_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) < 0){
perror(" * timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)");
goto main_terminate;
}
ts.it_value.tv_sec = 1;
ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = (unsigned long)10e6; /* 10 msec */
if(timerfd_settime(tm_fd, 0, &ts, NULL) < 0){
perror(" * timerfd_settime(tm_fd, 0, &ts, NULL)");
goto main_terminate;
}
}
/* add X11 event */
SET_EV(ep_ev,x11_fd,EPOLLIN);
if(epoll_ctl (ep_fd, EPOLL_CTL_ADD, x11_fd, &ep_ev) < 0){
perror(" * epoll_ctl x11_fd");
goto main_terminate;
}
/* add timer event */
SET_EV(ep_ev,tm_fd,EPOLLIN);
if(epoll_ctl (ep_fd, EPOLL_CTL_ADD, tm_fd, &ep_ev) < 0){
perror(" * epoll_ctl tm_fd");
goto main_terminate;
}
/* add signal event */
SET_EV(ep_ev,sig_fd,EPOLLIN);
if(epoll_ctl (ep_fd, EPOLL_CTL_ADD, sig_fd, &ep_ev) < 0){
perror(" * epoll_ctl sig_fd");
goto main_terminate;
}
/* window title */
const char *title = "epoll_pwait";
xcb_change_property (c,
XCB_PROP_MODE_REPLACE,
win,
XCB_ATOM_WM_NAME,
XCB_ATOM_STRING,
8,
strlen (title),
title );
/* Map the window on the screen */
xcb_map_window (c, win);
/* Make sure commands are sent before we pause, so window is shown */
xcb_flush (c);
while(1){
int n, i, fd, status;
bool f_compute = false;
bool f_exit_sig = false;
bool f_win_close = false;
n = epoll_pwait (ep_fd, ep_evs, MAXEVENTS, -1, &mask_sigs); /* wait, signal safe */
if(n < 0){
fprintf(stderr, " * main(): %s:%s:%d\n", __FILE__, __func__, __LINE__);
status = 1;
goto main_terminate;
}
for(i = 0; i < n; ++i){ /* service epoll events */
fd = ep_evs[i].data.fd;
if(fd == sig_fd){ /* signal */
status = read(fd, &siginf, sizeof(siginf));
if(status != sizeof(siginf)){
fprintf(stderr,"read != sizeof(siginf)");
goto main_terminate;
}
if(siginf.ssi_signo == SIGINT){
printf("got SIGINT\n");
f_exit_sig = true;
}else if(siginf.ssi_signo == SIGQUIT){
printf("got SIGQUIT\n");
f_exit_sig = true;
goto main_terminate;
}else {
printf("got unregistered signal\n");
}
}else if(fd == x11_fd){ /* x11 event */
xcb_generic_event_t *event;
while((event = xcb_poll_for_event(c))){
if (event && (event->response_type == 0)){ /* error recieved */
free(event);
goto main_terminate;
}
switch(event->response_type & ~0x80){
case XCB_CLIENT_MESSAGE: { /* window close */
xcb_client_message_event_t *ce = (xcb_client_message_event_t*)event;
if(ce->data.data32[0] == wm_p_close){ /* window should close */
f_win_close = true;
}
} break;
case XCB_KEY_PRESS: { /* keyboard key press */
printf("XCB_KEY_PRESS\n");
nevents++;
} break;
} /* end switch */
free(event);
} /* end while event loop */
}else if(fd == tm_fd){ /* timer event */
uint64_t overrun;
status = read(fd, &overrun, sizeof(uint64_t));
if(status != EAGAIN) {
//~ printf(" ! timer overruns: %lu\n", overrun);
}
f_compute = true;
}
} /* finish service epoll events */
if(f_exit_sig){ /* exit signal */
goto main_terminate;
}
if(f_win_close){ /* window close */
goto main_terminate;
}
if(f_compute){ /* do some computations */
compute();
xcb_flush(c);
}
} /* end while(1) */
main_terminate:
if(sig_fd != -1){
close(sig_fd);
}
if(tm_fd != -1){
close(tm_fd);
}
if(ep_fd != -1){
close(ep_fd);
}
if(ep_evs){
free(ep_evs);
}
xcb_disconnect(c);
if (sigprocmask(SIG_SETMASK, &mask_osigs, NULL) < 0){
perror(" * sigprocmask(SIG_SETMASK, &mask_osigs, NULL)");
}
printf("received %lu events\n", nevents);
return 0;
}
With the above, I can not reproduce input lag I do have in more verbose program. I test the above with xdotool, send some input X11 events, and my eye can't catch any visible input delay. All events get delivered. For now I can not post a full code that I have problem with.
what is a frequency of epoll_wait wakeups?
I'll assume you are talking about the precision of wakeups. This surely depends on kernel details, but let's just make an experiment: We can write a small program that tests how long epoll_wait takes. This will test both a timeout of zero and a timeout of 1 ms since a timeout of zero might be treated specially.
Output on my system:
empty: avg 0.015636 μs, min 0.014 μs, max 0.065 μs
sleep 1 ms: avg 1108.55 μs, min 1014.72 μs, max 1577.42 μs
epoll_wait 0: avg 0.761084 μs, min 0.38 μs, max 37.787 μs
epoll_wait 1: avg 1108.97 μs, min 1017.22 μs, max 2602.4 μs
Doing nothing (empty) measures less than a microsecond, so the following results should be somewhat reliable. The minimum time is also not zero, so the clock has enough precision for what we are doing.
Sleeping 1 ms sleeps at least 1.014 ms, but there also was a case of 1.5 ms. I guess that means that wakeups are not all that precise.
Using epoll_wait() to do nothing take less than a microsecond. More than doing nothing, but this still does basically nothing, so perhaps this really just measures the syscall overhead...?
Sleeping 1 ms with epoll_wait() behaves more or less the same as sleeping for 1 ms with nanosleep().
If you want to improve this experiment, you could actually register some FDs via epoll_ctl(). It might be that the kernel handles "empty" epoll FDs specially.
#include <stdio.h>
#include <sys/epoll.h>
#include <time.h>
#include <unistd.h>
#define MEASUREMENT_COUNT 10000
#define NUM_EVENTS 42
static int epoll_fd;
double diff_micro_sec(struct timespec *a, struct timespec *b) {
double sec = a->tv_sec - b->tv_sec;
double ns = a->tv_nsec - b->tv_nsec;
return sec * 1e6 + ns / 1e3;
}
static void empty(void) {
}
static void sleep_one_ms(void) {
struct timespec spec;
spec.tv_sec = 0;
spec.tv_nsec = 1000 * 1000;
nanosleep(&spec, NULL);
}
static void epoll_wait_0(void) {
struct epoll_event events[NUM_EVENTS];
epoll_wait(epoll_fd, events, NUM_EVENTS, 0);
}
static void epoll_wait_1(void) {
struct epoll_event events[NUM_EVENTS];
epoll_wait(epoll_fd, events, NUM_EVENTS, 1);
}
static void do_it(const char *name, void (*func)(void)) {
double sum, min, max;
struct timespec a, b;
for (int i = 0; i < MEASUREMENT_COUNT; i++) {
double diff;
clock_gettime(CLOCK_MONOTONIC, &a);
func();
clock_gettime(CLOCK_MONOTONIC, &b);
diff = diff_micro_sec(&b, &a);
if (i == 0) {
sum = diff;
min = diff;
max = diff;
} else {
sum += diff;
if (diff < min)
min = diff;
if (diff > max)
max = diff;
}
}
printf("%14s: avg %g μs, min %g μs, max %g μs\n", name, sum / MEASUREMENT_COUNT, min, max);
}
int main() {
do_it("empty", empty);
do_it("sleep 1 ms", sleep_one_ms);
epoll_fd = epoll_create(1);
do_it("epoll_wait 0", epoll_wait_0);
do_it("epoll_wait 1", epoll_wait_1);
close(epoll_fd);
return 0;
}
I'm using libusb to communicate with a Philips ISP1362 configured as a USB device. I am able to successfully loopback data using Synchronous I/O without any problems. For some reason when using Asynchronous I/O there appears to be a race condition.
I am transferring 64-byte packets using back-to-back OUT-IN transfers. Occasionally when I run my program libusb throws a timeout error and some of the loopback data is lost. When analyzing the USB bus using my Beagle 12 I can see the OUT-IN transactions are out of order (i.e. OUT-OUT-IN-TIMEOUT) when it should be (OUT-IN-OUT-IN).
Update The transfers are appearing out of order in the callback function which is strange because they are not coinciding with what is actually on the bus analyzer.
Example 1: (IN-OUT-IN-OUT)
main(): submitting transfer 0, endpoint 1
main(): submitting transfer 1, endpoint 82
main(): submitting transfer 2, endpoint 1
main(): submitting transfer 3, endpoint 82
xfr_cb(): count 0, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 1, status = 0, endpoint = 1, actual_length = 64, completed = 0
xfr_cb(): count 2, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 3, status = 0, endpoint = 1, actual_length = 64, completed = 0
completed
Example 2: (OUT-IN-IN-OUT)
main(): submitting transfer 0, endpoint 1
main(): submitting transfer 1, endpoint 82
main(): submitting transfer 2, endpoint 1
main(): submitting transfer 3, endpoint 82
xfr_cb(): count 0, status = 0, endpoint = 1, actual_length = 64, completed = 0
xfr_cb(): count 1, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 2, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 3, status = 0, endpoint = 1, actual_length = 64, completed = 0
completed
Below is a screenshot from the analyzer:
Below is the code:
#include <stdlib.h>
#include <stdio.h>
#include <libusb-1.0/libusb.h>
/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define EP_SIZE 64 // bytes
#define TRANSFERS 4 // number of transfers
#define BYTES EP_SIZE*TRANSFERS
#define TIMEOUT 3*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *devh = NULL;
/* use a global variable to keep the context */
static struct libusb_context *usb_context = NULL;
/* count variable */
int count = 0;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in = 0x82;
static int ep_out = 0x01;
void xfr_cb(struct libusb_transfer *transfer )
{
int *completed = transfer->user_data;
/* callback - This is called after the transfer has been received by libusb */
fprintf(stderr, "xfr_cb(): count %d, status = %d, endpoint = %x, actual_length = %d, completed = %d\n",
count,
transfer->status,
transfer->endpoint,
transfer->actual_length,
*completed);
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
{
/* Error! */
fprintf(stderr, "Error: %s\n", libusb_error_name((int)transfer->status));
}
if (count == TRANSFERS-1)
*completed = 1;
count++;
}
int main(int argc, char **argv)
{
int ep_addr;
int completed = 0;
unsigned char *buf;
size_t length = 64;
int n;
int i;
int rc;
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!devh)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* allocate memory */
buf = malloc(length);
/* start with OUT transfer */
ep_addr = ep_out;
/* queue up alternating OUT-IN transfers */
for (i = 0; i < TRANSFERS; i++)
{
/* fill the buffer with incrementing data */
for (n = 0; n < EP_SIZE; n++)
{
buf[n] = i+n;
}
/* Set up the transfer object */
struct libusb_transfer *transfer;
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, devh, ep_addr, buf, EP_SIZE, xfr_cb, &completed, TIMEOUT); /* callback data = &completed */
/* Submit the transfer object */
libusb_submit_transfer(transfer);
fprintf(stderr, "main(): submitting transfer %d, endpoint %x\n", i, ep_addr);
/* alternate writing and reading for loopback */
ep_addr = (ep_addr == ep_out) ? ep_in : ep_out;
}
/* Handle Events */
while (!completed)
{
rc = libusb_handle_events_completed(NULL, &completed);
if (rc < 0)
{
if (rc == LIBUSB_ERROR_INTERRUPTED)
continue;
fprintf(stderr, "Transfer Error: %s", libusb_error_name(rc));
continue;
}
}
fprintf(stderr, "completed\n");
/* Release the interface */
libusb_release_interface(devh, 0);
/* Close the device handle */
if (devh)
libusb_close(devh);
out:
if (devh)
{
libusb_close(devh);
}
libusb_exit(NULL);
return rc;
}
Update 2 I successfully eliminated the timeout. The cause of the libusb timeout is because the Host was sending two consecutive OUT transactions intermittently on the bus.
Analyzer screenshot:
The following is the working code (no timeouts). Ran these thousands of times with no issues
static void LIBUSB_CALL xfr_cb(struct libusb_transfer *transfer )
{
int *completed = transfer->user_data;
unsigned char *wbuf, *rbuf;
size_t length = 64;
fprintf(stderr, "xfr_cb(): status = %d, endpoint = %x, actual_length = %d\n",
transfer->status,
transfer->endpoint,
transfer->actual_length);
*completed = 1;
}
int main(int argc, char **argv)
{
const struct libusb_version *version;
int ep_addr;
int completed = 0;
unsigned char *buf, *wbuf1, *wbuf2, *rbuf1, *rbuf2;
size_t length = 64;
int n;
int m;
int i;
int rc;
/* Get libusb version */
version = libusb_get_version();
fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!handle)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* claim interface */
rc = libusb_claim_interface(handle, 0);
if (rc < 0)
{
fprintf(stderr, "Error claiming interface.\n");
goto out;
}
/* allocate memory */
wbuf1 = malloc(length);
wbuf2 = malloc(length);
rbuf1 = malloc(length);
rbuf2 = malloc(length);
/* fill the buffer with incrementing data */
for (n = 0; n < EP_SIZE; n++)
wbuf1[n] = n;
for (m = 0; m < EP_SIZE; m++)
wbuf2[m] = m+1;
struct libusb_transfer *transfer1;
struct libusb_transfer *transfer2;
struct libusb_transfer *transfer3;
struct libusb_transfer *transfer4;
/* Set up the transfer object */
transfer1 = libusb_alloc_transfer(0);
transfer2 = libusb_alloc_transfer(0);
transfer3 = libusb_alloc_transfer(0);
transfer4 = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer1, handle, ep_out, wbuf1, EP_SIZE, xfr_cb, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer2, handle, ep_in, rbuf1, EP_SIZE, xfr_cb, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer3, handle, ep_out, wbuf2, EP_SIZE, xfr_cb, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer4, handle, ep_in, rbuf2, EP_SIZE, xfr_cb, &completed, TIMEOUT); /* callback data = &completed */
/* Submit the transfers */
libusb_submit_transfer(transfer1);
libusb_submit_transfer(transfer2);
libusb_submit_transfer(transfer3);
libusb_submit_transfer(transfer4);
/* Handle Events */
while (!completed)
{
rc = libusb_handle_events_completed(NULL, &completed);
if (rc != LIBUSB_SUCCESS)
{
fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
break;
}
}
fprintf(stderr, "completed\n");
//* Release the interface */
libusb_release_interface(handle, 0);
/* Close the device handle */
if (handle)
libusb_close(handle);
out:
if (handle)
{
libusb_close(handle);
}
libusb_exit(NULL);
return rc;
}
Changing the code as follows (i.e. callback = NULL for transfer 1-3) re-creates intermittent duplicate transactions, as shown in the screenshots.
libusb_fill_bulk_transfer(transfer1, handle, ep_out, wbuf1, EP_SIZE, NULL, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer2, handle, ep_in, rbuf1, EP_SIZE, NULL, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer3, handle, ep_out, wbuf2, EP_SIZE, NULL, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer4, handle, ep_in, rbuf2, EP_SIZE, xfr_cb, &completed, TIMEOUT); /* callback data = &completed */
I honestly don't understand why the loop would cause race conditions based on their documentation and examples. Queueing up multiple transfers is actually suggested in one of the libusb examples (sam3u_benchmark.c) and also demonstrated (using loops) in the following .pdfs.
See asynchronous I/O sections:
https://www.socallinuxexpo.org/sites/default/files/presentations/scale_2017_usb.pdf
http://www.signal11.us/oss/elc2014/elc_2014_usb_0.pdf
From my understanding, the use of libusb_handle_events_completed(NULL, &completed) is supposed to resolve synchronization issues. Am I misunderstanding something?
See libusb_handle_events() from multiple threads
http://libusb.sourceforge.net/api-1.0/libusb_mtasync.html
-"This is why libusb-1.0.9 introduces the new libusb_handle_events_timeout_completed() and libusb_handle_events_completed() functions, which handles doing the completion check for you after they have acquired the lock:"
What they need are crystal clear examples of how to use their API if this is the case.
I can add more event checking but something does not seem right here.
Update 3: See accepted answer.
I started reading the documentation in the libusb source code and understood what was happening.
Particularly the section about how libusb deals with packet sizes:
http://libusb.sourceforge.net/api-1.0/libusb_packetoverflow.html
After reading that it clicked for me and I found two ways to accomplish a loopback test with large data size using asynchronous I/O.
The first way is submitting two transfers consecutively with transfer->buffer containing the entire data structure (i.e. total bytes to send and receive). The second way is submitting the two transfers with transfer->buffer containing wMaxPacketSize (e.g. 64-bytes) and having the out and in callback functions submit additional transfers to transceive the rest of the data.
For the second case, extra code needed to be added to keep track of the number of transfers and to set the completed signal when finished. The OUT-IN packet interleaving is handled by libusb and the OS - which was the part I didn't realize. In other words, not every OUT-IN transfer needed to be specified and queued individually.
Here is the asynchronous code along with the transfer rates to my USB device (ISP1362). My USB device controller is an FPGA coded in pure SystemVerilog.
Note: Regarding the transfer rates, I only have double-buffering enabled on BULK_EP_IN. I am assuming the IN-NAK's (# POLL) and transfer rate would improve in the second approach if double-buffering was enabled on BULK_EP_OUT. So this may not be a fair comparison due to device configuration.
First approach: ~1.161 MB/s (~9.288 Mb/s)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include </usr/include/libusb-1.0/libusb.h>
/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define EP_SIZE 64 // bytes
#define TRANSFERS 1024*768*3/EP_SIZE // number of transfers
#define TIMEOUT 10*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *handle = NULL;
/* count variables */
unsigned int count = 0;
unsigned int count_in = 0;
unsigned int count_out = 0;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in = 0x82;
static int ep_out = 0x01;
/* Write and Read buffers */
unsigned char wbuf[EP_SIZE*TRANSFERS];
unsigned char wbuf_tmp[EP_SIZE*TRANSFERS];
unsigned char rbuf[EP_SIZE*TRANSFERS];
unsigned char rbuf_tmp[EP_SIZE*TRANSFERS];
static void LIBUSB_CALL xfr_cb_out(struct libusb_transfer *transfer )
{
memcpy(wbuf+count_out*EP_SIZE, transfer->buffer, EP_SIZE);
}
static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer )
{
int *completed = transfer->user_data;
memcpy(rbuf+count_in*EP_SIZE, transfer->buffer, EP_SIZE);
count_in++; // one transfer complete
if (count_in < TRANSFERS)
*completed = 1;
}
int main(int argc, char **argv)
{
const struct libusb_version *version;
int completed = 0;
size_t length = 64;
int n;
int m;
int rc;
/* Get libusb version */
version = libusb_get_version();
fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!handle)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* claim interface */
rc = libusb_claim_interface(handle, 0);
if (rc < 0)
{
fprintf(stderr, "Error claiming interface.\n");
goto out;
}
/* fill the buffer with incrementing data */
for (n = 0; n < TRANSFERS; n++)
{
for (m = 0; m < EP_SIZE; m++)
{
wbuf_tmp[m+n*EP_SIZE] = m+n;
}
}
struct libusb_transfer *transfer;
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_out, wbuf_tmp, EP_SIZE*TRANSFERS, xfr_cb_out, NULL, TIMEOUT);
libusb_submit_transfer(transfer);
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_in, rbuf_tmp, EP_SIZE*TRANSFERS, xfr_cb_in, &completed, TIMEOUT);
libusb_submit_transfer(transfer);
/* Handle Events */
while (!completed)
{
rc = libusb_handle_events_completed(NULL, &completed);
if (rc != LIBUSB_SUCCESS)
{
fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
break;
}
}
fprintf(stderr, "completed\n");
int res;
res = memcmp(rbuf, wbuf, sizeof(wbuf));
if (res != 0)
fprintf(stderr, "miscompare\n");
else
fprintf(stderr, "success\n");
//* Release the interface */
libusb_release_interface(handle, 0);
/* Close the device handle */
if (handle)
libusb_close(handle);
out:
if (handle)
{
libusb_close(handle);
}
libusb_exit(NULL);
return rc;
}
Second approach: ~755.9 MB/s (~6.047 Mb/s)
include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include </usr/include/libusb-1.0/libusb.h>
/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define EP_SIZE 64 // bytes
#define TRANSFERS 1024*768*3/EP_SIZE // number of transfers
#define TIMEOUT 10*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *handle = NULL;
/* count variables */
unsigned int count = 0;
unsigned int count_in = 0;
unsigned int count_out = 0;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in = 0x82;
static int ep_out = 0x01;
/* Write and Read buffers */
unsigned char wbuf[EP_SIZE*TRANSFERS];
unsigned char *wbuf_tmp;
unsigned char rbuf[EP_SIZE*TRANSFERS];
unsigned char rbuf_tmp[EP_SIZE*TRANSFERS];
static void LIBUSB_CALL xfr_cb_out(struct libusb_transfer *transfer )
{
memcpy(wbuf+count_out*EP_SIZE, transfer->buffer, EP_SIZE);
count_out++; // one transfer complete
if (count_out < TRANSFERS)
{
transfer->buffer = ++wbuf_tmp;
libusb_submit_transfer(transfer);
}
}
static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer )
{
int *completed = transfer->user_data;
memcpy(rbuf+count_in*EP_SIZE, transfer->buffer, EP_SIZE);
count_in++; // one transfer complete
if (count_in < TRANSFERS)
libusb_submit_transfer(transfer);
else
*completed = 1;
}
int main(int argc, char **argv)
{
const struct libusb_version *version;
int completed = 0;
size_t length = 64;
int n;
int rc;
/* Get libusb version */
version = libusb_get_version();
fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!handle)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* claim interface */
rc = libusb_claim_interface(handle, 0);
if (rc < 0)
{
fprintf(stderr, "Error claiming interface.\n");
goto out;
}
/* allocate memory */
wbuf_tmp = malloc(length*TRANSFERS);
/* fill the buffer with incrementing data */
for (n = 0; n < EP_SIZE*TRANSFERS; n++)
{
wbuf_tmp[n] = n;
}
struct libusb_transfer *transfer;
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_out, wbuf_tmp, EP_SIZE, xfr_cb_out, NULL, TIMEOUT);
libusb_submit_transfer(transfer);
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_in, rbuf_tmp, EP_SIZE, xfr_cb_in, &completed, TIMEOUT);
libusb_submit_transfer(transfer);
/* Handle Events */
while (!completed)
{
rc = libusb_handle_events_completed(NULL, &completed);
if (rc != LIBUSB_SUCCESS)
{
fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
break;
}
}
fprintf(stderr, "completed\n");
int res;
res = memcmp(rbuf, wbuf, sizeof(wbuf));
if (res != 0)
fprintf(stderr, "miscompare\n");
else
fprintf(stderr, "success\n");
//* Release the interface */
libusb_release_interface(handle, 0);
/* Close the device handle */
if (handle)
libusb_close(handle);
out:
if (handle)
{
libusb_close(handle);
}
libusb_exit(NULL);
return rc;
}
Update: See accepted answer.
The following is an example using Synchronous I/O. I had a lot of trouble getting the transactions to come out in the expected order using Asynchronous I/O. I assume this was due to transfers racing with each other as #Gene had mentioned.
The main gripe I have about the libusb API is the lack of examples to illustrate proper use. The API would lead someone to believe that asynchronous transactions are placed on the bus in the order they are "submitted" and from what I gather this is not true. This functionality would be fine for submitting transactions with all the same packet TOKEN (i.e. OUT or IN).
The following code works for large bulk transfers.
Using Synchronous I/O
#include <stdlib.h>
#include <stdio.h>
#include <libusb-1.0/libusb.h>
/* Change VENDOR_ID and PRODUCT_ID depending on device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define BYTES 1024*768*3 // bytes
#define EP_SIZE 64 // bytes
#define TIMEOUT 5*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *devh = NULL;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in_addr = 0x82;
static int ep_out_addr = 0x01;
int write_chars(unsigned char * data, int length)
{
/* To send a char to the device simply initiate a bulk_transfer to the Endpoint
* with the address ep_out_addr.
*/
int actual_length;
int rc = libusb_bulk_transfer(devh, ep_out_addr, data, length, &actual_length, TIMEOUT);
if (rc < 0)
{
fprintf(stderr, "Error while sending char: %d\n", rc);
return -1;
}
return actual_length;
}
int read_chars(unsigned char * data, int length)
{
/* To receive characters from the device initiate a bulk_transfer to the Entpoint
* with address ep_in_addr
*/
int actual_length;
int rc = libusb_bulk_transfer(devh, ep_in_addr, data, length, &actual_length, TIMEOUT);
if (rc == LIBUSB_ERROR_TIMEOUT)
{
printf("timeout (%d)\n", actual_length);
return -1;
}
else if (rc < 0)
{
fprintf(stderr, "Error while waiting for char: %d\n", rc);
return -1;
}
return actual_length;
}
int main(int argc, char **argv)
{
int rc;
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!devh)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* We can now start sending or receiving data to the device */
unsigned char buf[BYTES];
unsigned char rbuf[EP_SIZE];
int len;
int n;
int l;
int res;
// fill buffer
for (n = 0; n < BYTES; n++)
{
buf[n] = 0x00+n;
}
// loopback data, write-read
for (l = 0; l < BYTES/EP_SIZE; l++)
{
len = write_chars(buf+l*EP_SIZE, EP_SIZE);
len = read_chars(rbuf, EP_SIZE);
res = memcmp(rbuf, buf+l*EP_SIZE, sizeof(rbuf));
if (res != 0)
fprintf(stderr, "Miscompare: block %d\n", l);
}
libusb_release_interface(devh, 0);
out:
if (devh)
{
libusb_close(devh);
}
libusb_exit(NULL);
return rc;
}
Using Asynchronous and Synchronous together (i.e. OUT is submitted Asynchronously and IN is Synchronous)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include </usr/include/libusb-1.0/libusb.h>
/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define EP_SIZE 64 // bytes
#define TRANSFERS 1024*768*3/EP_SIZE // number of transfers
#define BYTES EP_SIZE*TRANSFERS
#define TIMEOUT 15*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *handle = NULL;
/* count variable */
unsigned int count_out = 0;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in = 0x82;
static int ep_out = 0x01;
unsigned char rbuf[EP_SIZE*TRANSFERS];
unsigned char wbuf[EP_SIZE*TRANSFERS];
static void LIBUSB_CALL xfr_cb_out(struct libusb_transfer *transfer )
{
memcpy(wbuf+count_out*EP_SIZE, transfer->buffer, EP_SIZE);
count_out++;
}
int main(int argc, char **argv)
{
const struct libusb_version *version;
unsigned char *buf, *rbuf_tmp;
size_t length = 64;
int n;
int i;
int rc;
/* Get libusb version */
version = libusb_get_version();
fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!handle)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* claim interface */
rc = libusb_claim_interface(handle, 0);
if (rc < 0)
{
fprintf(stderr, "Error claiming interface.\n");
goto out;
}
/* allocate memory */
buf = malloc(length*TRANSFERS);
/* fill the buffer with incrementing data */
for (n = 0; n < EP_SIZE*TRANSFERS; n++)
{
buf[n] = n;
}
/* allocate memory */
rbuf_tmp = malloc(length);
/* set up alternating OUT-IN transfers */
for (i = 0; i < TRANSFERS; i++)
{
struct libusb_transfer *transfer;
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_out, buf+i, EP_SIZE, xfr_cb_out, NULL, TIMEOUT);
libusb_submit_transfer(transfer);
int actual_length;
int rc = libusb_bulk_transfer(handle, ep_in, rbuf_tmp, EP_SIZE, &actual_length, TIMEOUT);
if (rc != LIBUSB_SUCCESS)
{
fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
break;
}
memcpy(rbuf+i*EP_SIZE, rbuf_tmp, EP_SIZE);
}
fprintf(stderr, "completed\n");
int res;
res = memcmp(rbuf, wbuf, sizeof(wbuf));
if (res != 0)
fprintf(stderr, "miscompare\n");
//* Release the interface */
libusb_release_interface(handle, 0);
/* Close the device handle */
if (handle)
libusb_close(handle);
out:
if (handle)
{
libusb_close(handle);
}
libusb_exit(NULL);
return rc;
}
The above code was an experiment to see if performance increased. Interestingly, the speed difference between the two was negligible.
The version of libusb was 1.0.17.10830
I'm trying to add a feature to nweb which allows it to render MIDI .mid files into .wav sound files so that a browser such as Chrome with a built-in .wav player can play them. I have the nweb but other than that I'm completely stumped. Help would be appreciated!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 8096
#define ERROR 42
#define SORRY 43
#define LOG 44
struct {
char *ext;
char *filetype;
} extensions [] = {
{"gif", "image/gif" },
{"jpg", "image/jpeg"},
{"jpeg","image/jpeg"},
{"png", "image/png" },
{"zip", "image/zip" },
{"gz", "image/gz" },
{"tar", "image/tar" },
{"htm", "text/html" },
{"html","text/html" },
{"mp3","music/mp3"},
{0,0} };
void log(int type, char *s1, char *s2, int num)
{
int fd ;
char logbuffer[BUFSIZE*2];
switch (type) {
case ERROR: (void)sprintf(logbuffer,"ERROR: %s:%s Errno=%d exiting pid=%d",s1, s2, errno,getpid()); break;
case SORRY:
(void)sprintf(logbuffer, "<HTML><BODY><H1>nweb Web Server Sorry: %s %s</H1></BODY></HTML>\r\n", s1, s2);
(void)write(num,logbuffer,strlen(logbuffer));
(void)sprintf(logbuffer,"SORRY: %s:%s",s1, s2);
break;
case LOG: (void)sprintf(logbuffer," INFO: %s:%s:%d",s1, s2,num); break;
}
/* no checks here, nothing can be done a failure anyway */
if((fd = open("nweb.log", O_CREAT| O_WRONLY | O_APPEND,0644)) >= 0) {
(void)write(fd,logbuffer,strlen(logbuffer));
(void)write(fd,"\n",1);
(void)close(fd);
}
if(type == ERROR || type == SORRY) exit(3);
}
/* this is a child web server process, so we can exit on errors */
void web(int fd, int hit)
{
int j, file_fd, buflen, len;
long i, ret;
char * fstr;
static char buffer[BUFSIZE+1]; /* static so zero filled */
ret =read(fd,buffer,BUFSIZE); /* read Web request in one go */
if(ret == 0 || ret == -1) { /* read failure stop now */
log(SORRY,"failed to read browser request","",fd);
}
if(ret > 0 && ret < BUFSIZE) /* return code is valid chars */
buffer[ret]=0; /* terminate the buffer */
else buffer[0]=0;
for(i=0;i<ret;i++) /* remove CF and LF characters */
if(buffer[i] == '\r' || buffer[i] == '\n')
buffer[i]='*';
log(LOG,"request",buffer,hit);
if( strncmp(buffer,"GET ",4) && strncmp(buffer,"get ",4) )
log(SORRY,"Only simple GET operation supported",buffer,fd);
for(i=4;i<BUFSIZE;i++) { /* null terminate after the second space to ignore extra stuff */
if(buffer[i] == ' ') { /* string is "GET URL " +lots of other stuff */
buffer[i] = 0;
break;
}
}
for(j=0;j<i-1;j++) /* check for illegal parent directory use .. */
if(buffer[j] == '.' && buffer[j+1] == '.')
log(SORRY,"Parent directory (..) path names not supported",buffer,fd);
if( !strncmp(&buffer[0],"GET /\0",6) || !strncmp(&buffer[0],"get /\0",6) ) /* convert no filename to index file */
(void)strcpy(buffer,"GET /index.html");
/* work out the file type and check we support it */
buflen=strlen(buffer);
fstr = (char *)0;
for(i=0;extensions[i].ext != 0;i++) {
len = strlen(extensions[i].ext);
if( !strncmp(&buffer[buflen-len], extensions[i].ext, len)) {
fstr =extensions[i].filetype;
break;
}
}
if(fstr == 0) log(SORRY,"file extension type not supported",buffer,fd);
if(( file_fd = open(&buffer[5],O_RDONLY)) == -1) /* open the file for reading */
log(SORRY, "failed to open file",&buffer[5],fd);
log(LOG,"SEND",&buffer[5],hit);
(void)sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr);
(void)write(fd,buffer,strlen(buffer));
/* send file in 8KB block - last block may be smaller */
while ( (ret = read(file_fd, buffer, BUFSIZE)) > 0 ) {
(void)write(fd,buffer,ret);
}
#ifdef LINUX
sleep(1); /* to allow socket to drain */
#endif
exit(1);
}
int main(int argc, char **argv)
{
int i, port, pid, listenfd, socketfd, hit;
size_t length;
static struct sockaddr_in cli_addr; /* static = initialised to zeros */
static struct sockaddr_in serv_addr; /* static = initialised to zeros */
if( argc < 3 || argc > 3 || !strcmp(argv[1], "-?") ) {
(void)printf("hint: nweb Port-Number Top-Directory\n\n"
"\tnweb is a small and very safe mini web server\n"
"\tnweb only servers out file/web pages with extensions named below\n"
"\t and only from the named directory or its sub-directories.\n"
"\tThere is no fancy features = safe and secure.\n\n"
"\tExample: nweb 8181 /home/nwebdir &\n\n"
"\tOnly Supports:");
for(i=0;extensions[i].ext != 0;i++)
(void)printf(" %s",extensions[i].ext);
(void)printf("\n\tNot Supported: URLs including \"..\", Java, Javascript, CGI\n"
"\tNot Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n"
"\tNo warranty given or implied\n\tNigel Griffiths nag#uk.ibm.com\n"
);
exit(0);
}
if( !strncmp(argv[2],"/" ,2 ) || !strncmp(argv[2],"/etc", 5 ) ||
!strncmp(argv[2],"/bin",5 ) || !strncmp(argv[2],"/lib", 5 ) ||
!strncmp(argv[2],"/tmp",5 ) || !strncmp(argv[2],"/usr", 5 ) ||
!strncmp(argv[2],"/dev",5 ) || !strncmp(argv[2],"/sbin",6) ){
(void)printf("ERROR: Bad top directory %s, see nweb -?\n",argv[2]);
exit(3);
}
if(chdir(argv[2]) == -1){
(void)printf("ERROR: Can't Change to directory %s\n",argv[2]);
exit(4);
}
/* Become daemon + unstoppable and no zombies children (= no wait()) */
if(fork() != 0)
return 0; /* parent returns OK to shell */
(void)signal(SIGCLD, SIG_IGN); /* ignore child death */
(void)signal(SIGHUP, SIG_IGN); /* ignore terminal hangups */
for(i=0;i<32;i++)
(void)close(i); /* close open files */
(void)setpgrp(); /* break away from process group */
log(LOG,"nweb starting",argv[1],getpid());
/* setup the network socket */
if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0)
log(ERROR, "system call","socket",0);
port = atoi(argv[1]);
if(port < 0 || port >60000)
log(ERROR,"Invalid port number (try 1->60000)",argv[1],0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0)
log(ERROR,"system call","bind",0);
if( listen(listenfd,64) <0)
log(ERROR,"system call","listen",0);
for(hit=1; ;hit++) {
length = sizeof(cli_addr);
if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)
log(ERROR,"system call","accept",0);
if((pid = fork()) < 0) {
log(ERROR,"system call","fork",0);
}
else {
if(pid == 0) { /* child */
(void)close(listenfd);
web(socketfd,hit); /* never returns */
} else { /* parent */
(void)close(socketfd);
}
}
}
}
You can use FluidSynth (http://www.fluidsynth.org/). It comes with a C library. The API is documented here:
http://fluidsynth.sourceforge.net/api/
You can render a MIDI file with something like:
#include <fluidsynth.h>
// ...
fluid_settings_t* settings = new_fluid_settings()
// Render at 44.1KHz.
fluid_settings_setnum(settings, "synth.sample-rate", 44100);
fluid_synth_t* synth = new_fluid_synth(settings)
// Set volume to 70%. High volume levels may cause clipping.
fluid_synth_set_gain(synth, 0.7f);
// Quality of interpolation. This is high quality. Needs more CPU.
fluid_synth_set_interp_method(synth, -1, FLUID_INTERP_7THORDER);
// Which soundfont file to use.
fluid_synth_sfload(synth, "soundfont.sf2", true);
fluid_player_t* player = new_fluid_player(synth);
// Load a MIDI file.
fluid_player_add(player, "midi_file.mid");
// Start "playing" the MIDI file. This simply prepares the file for
// rendering, it doesn't really "play" anything.
fluid_player_play(player);
Note: instead of fluid_player_add(), you can use fluid_player_add_mem() instead to feed MIDI bytes to FluidSynth. This depends on how you get your MIDI data from your source.
The above initializes and sets up a synth and a player. Note that you need a soundfont file (in SF2 format), otherwise you can't render anything. SF2 files contain audio data for the various MIDI instruments. Assuming your MIDI files are targeting the GM standard (General MIDI), you will need to find a GM soundfont (or a GS one; GS is an extension of GM with more instruments, some MIDI files need that.) There many free SF2 soundfonts available, with varying sizes (ranging anywhere between 2MB and several GB.)
To actually do rendering and get audio data back, you make calls to fluid_synth_write_float() (for floating point samples) or fluid_synth_write_s16() (for integer samples). After that, check whether the MIDI files has ended playing with fluid_player_get_status().
Once you get the audio samples, you can either serve them in WAV format, or use an encoder (like the libvorbisfile library) to compress the samples into Ogg/Vorbis audio. Or maybe with libmpg123 to serve MP3 audio instead.
i am trying to record my voice from the microphone on my laptop using the simple.h pulseaudio header file into an array, but i cant seem to figure it out. Every time i record and i replay my recording , it is a high pitched beep i followed examples, etc but i can't seem to get this down can someone please help me .
I am basically hacking the example "parec-simple.c" given in the doxygen page. I've tried routing the output of buf to stdout, then using libre-office calc to plot a graph to see if the output looks anything like sound but it does not.
here is the code i used
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#define BUFSIZE 1024
FILE *output;
/* A simple routine calling UNIX write() in a loop */
void loop_write(uint8_t *data) {
register int size = 1023;
while (size > 0)
{
fprintf(output,"%"SCNu8"\n",data[size] ) ;
size --;
}
}
int main(int argc, char*argv[]) {
output = fopen("/home/donnell/output", "w");
/* The sample type to use */
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 41000,
.channels = 2
};
pa_simple *s = NULL;
int ret = 1;
int error;
/* Create the recording stream */
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
goto finish;
}
for (;;) {
uint8_t buf[BUFSIZE];
/* Record some data ... */
if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
goto finish;
}
/* and write it to fle*/
loop_write(buf);
}
ret = 0;
finish:
if (s)
pa_simple_free(s);
return ret;
}
Could somebody please explain how to make a countdown timer using clock_gettime, under Linux. I know you can use the clock() function to get cpu time, and multiply it by CLOCKS_PER_SEC to get actual time, but I'm told the clock() function is not well suited for this.
So far I have attempted this (a billion is to pause for one second)
#include <stdio.h>
#include <time.h>
#define BILLION 1000000000
int main()
{
struct timespec rawtime;
clock_gettime(CLOCK_MONOTONIC_RAW, &rawtime);
unsigned long int current = ( rawtime.tv_sec + rawtime.tv_nsec );
unsigned long int end = (( rawtime.tv_sec + rawtime.tv_nsec ) + BILLION );
while ( current < end )
{
clock_gettime(CLOCK_MONOTONIC_RAW, &rawtime);
current = ( rawtime.tv_sec + rawtime.tv_nsec );
}
return 0;
}
I know this wouldn't be very useful on its own, but once I've found out how to time correctly I can use this in my projects. I know that sleep() can be used for this purpose, but I want to code the timer myself so that I can better integrate it in my projects - such as the possibility of it returning the time left, as opposed to pausing the whole program.
Please, do not do that. You're burning CPU power for nothing in a busy loop.
Why not use the nanosleep() function instead? It's perfectly suited to the use case you outlined. Or, if you want an easier interface, perhaps something like
#define _POSIX_C_SOURCE 200809L
#include <time.h>
#include <errno.h>
/* Sleep for the specified number of seconds,
* and return the time left over.
*/
double dsleep(const double seconds)
{
struct timespec req, rem;
/* No sleep? */
if (seconds <= 0.0)
return 0.0;
/* Convert to seconds and nanoseconds. */
req.tv_sec = (time_t)seconds;
req.tv_nsec = (long)((seconds - (double)req.tv_sec) * 1000000000.0);
/* Take care of any rounding errors. */
if (req.tv_nsec < 0L)
req.tv_nsec = 0L;
else
if (req.tv_nsec > 999999999L)
req.tv_nsec = 999999999L;
/* Do the nanosleep. */
if (nanosleep(&req, &rem) != -1)
return 0.0;
/* Error? */
if (errno != EINTR)
return 0.0;
/* Return remainder. */
return (double)rem.tv_sec + (double)rem.tv_nsec / 1000000000.0;
}
The difference is that using this one the CPU is free to do something else, rather than spin like a crazed squirrel on speed.
This is not an answer, but an example of how to use signals and a POSIX timer to implement a timeout timer; intended as a response to the OP's followup question in a comment to the accepted answer.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
/* Timeout timer.
*/
static timer_t timeout_timer;
static volatile sig_atomic_t timeout_state = 0;
static volatile sig_atomic_t timeout_armed = 2;
static const int timeout_signo = SIGALRM;
#define TIMEDOUT() (timeout_state != 0)
/* Timeout signal handler.
*/
static void timeout_handler(int signo, siginfo_t *info, void *context __attribute__((unused)))
{
if (timeout_armed == 1)
if (signo == timeout_signo && info && info->si_code == SI_TIMER)
timeout_state = ~0;
}
/* Unset timeout.
* Returns nonzero if timeout had expired, zero otherwise.
*/
static int timeout_unset(void)
{
struct itimerspec t;
const int retval = timeout_state;
/* Not armed? */
if (timeout_armed != 1)
return retval;
/* Disarm. */
t.it_value.tv_sec = 0;
t.it_value.tv_nsec = 0;
t.it_interval.tv_sec = 0;
t.it_interval.tv_nsec = 0;
timer_settime(timeout_timer, 0, &t, NULL);
return retval;
}
/* Set timeout (in wall clock seconds).
* Cancels any pending timeouts.
*/
static int timeout_set(const double seconds)
{
struct itimerspec t;
/* Uninitialized yet? */
if (timeout_armed == 2) {
struct sigaction act;
struct sigevent evt;
/* Use timeout_handler() for timeout_signo signal. */
sigemptyset(&act.sa_mask);
act.sa_sigaction = timeout_handler;
act.sa_flags = SA_SIGINFO;
if (sigaction(timeout_signo, &act, NULL) == -1)
return errno;
/* Create a monotonic timer, delivering timeout_signo signal. */
evt.sigev_value.sival_ptr = NULL;
evt.sigev_signo = timeout_signo;
evt.sigev_notify = SIGEV_SIGNAL;
if (timer_create(CLOCK_MONOTONIC, &evt, &timeout_timer) == -1)
return errno;
/* Timeout is initialzied but unarmed. */
timeout_armed = 0;
}
/* Disarm timer, if armed. */
if (timeout_armed == 1) {
/* Set zero timeout, disarming the timer. */
t.it_value.tv_sec = 0;
t.it_value.tv_nsec = 0;
t.it_interval.tv_sec = 0;
t.it_interval.tv_nsec = 0;
if (timer_settime(timeout_timer, 0, &t, NULL) == -1)
return errno;
timeout_armed = 0;
}
/* Clear timeout state. It should be safe (no pending signals). */
timeout_state = 0;
/* Invalid timeout? */
if (seconds <= 0.0)
return errno = EINVAL;
/* Set new timeout. Check for underflow/overflow. */
t.it_value.tv_sec = (time_t)seconds;
t.it_value.tv_nsec = (long)((seconds - (double)t.it_value.tv_sec) * 1000000000.0);
if (t.it_value.tv_nsec < 0L)
t.it_value.tv_nsec = 0L;
else
if (t.it_value.tv_nsec > 999999999L)
t.it_value.tv_nsec = 999999999L;
/* Set it repeat once every millisecond, just in case the initial
* interrupt is missed. */
t.it_interval.tv_sec = 0;
t.it_interval.tv_nsec = 1000000L;
if (timer_settime(timeout_timer, 0, &t, NULL) == -1)
return errno;
timeout_armed = 1;
return 0;
}
int main(void)
{
char *line = NULL;
size_t size = 0;
ssize_t len;
fprintf(stderr, "Please supply input. The program will exit automatically if\n");
fprintf(stderr, "it takes more than five seconds for the next line to arrive.\n");
fflush(stderr);
while (1) {
if (timeout_set(5.0)) {
const char *const errmsg = strerror(errno);
fprintf(stderr, "Cannot set timeout: %s.\n", errmsg);
return 1;
}
len = getline(&line, &size, stdin);
if (len == (ssize_t)-1)
break;
if (len < (ssize_t)1) {
/* This should never occur (except for -1, of course). */
errno = EIO;
break;
}
/* We do not want *output* to be interrupted,
* so we cancel the timeout. */
timeout_unset();
if (fwrite(line, (size_t)len, 1, stdout) != 1) {
fprintf(stderr, "Error writing to standard output.\n");
fflush(stderr);
return 1;
}
fflush(stdout);
/* Next line. */
}
/* Remember to cancel the timeout. Also check it. */
if (timeout_unset())
fprintf(stderr, "Timed out.\n");
else
if (ferror(stdin) || !feof(stdin))
fprintf(stderr, "Error reading standard input.\n");
else
fprintf(stderr, "End of input.\n");
fflush(stderr);
/* Free line buffer. */
free(line);
line = NULL;
size = 0;
/* Done. */
return 0;
}
If you save the above as timer.c, you can compile it using e.g.
gcc -W -Wall -O3 -std=c99 -pedantic timer.c -lrt -o timer
and run it using ./timer.
If you read the code above carefully, you'll see that it is actually a periodic timer signal (at millisecond intervals), with a variable delay before the first signal. That is just a technique I like to use to make sure I don't miss the signal. (The signal repeats until the timeout is unset.)
Note that although you can do computation in an signal handler, you should only use functions that are async-signal-safe; see man 7 signal. Also, only the sig_atomic_t type is atomic wrt. normal single-threaded code and a signal handler. So, it is better to just use the signal as an indicator, and do the actual code in your own program.
If you wanted to e.g. update monster coordinates in a signal handler, it is possible but a bit tricky. I'd use three arrays containing the monster information, and use GCC __sync_bool_compare_and_swap() to update the array pointers -- very much the same technique as triple-buffering in graphics.
If you need more than one concurrent timeout, you could use multiple timers (there is a number of them available), but the best option is to define timeout slots. (You can use generation counters to detect "forgotten" timeouts, and so on.) Whenever a new timeout is set or unset, you update the timeout to reflect the next timeout that expires. It's a bit more code, but really a straightforward extension of the above.