Commit a35865b7 authored by Cedric Roux's avatar Cedric Roux

Merge remote-tracking branch 'internal/mobipass-standalone' into develop_integration_w34

parents cdcc1d54 f38572b4
......@@ -532,6 +532,19 @@ set(TPLIB_ETHERNET_SOURCE
)
add_library(oai_eth_transpro MODULE ${TPLIB_ETHERNET_SOURCE} )
include_directories("${OPENAIR_TARGETS}/ARCH/mobipass/")
set(TPLIB_MOBIPASS_SOURCE
${OPENAIR_TARGETS}/ARCH/mobipass/interface.c
${OPENAIR_TARGETS}/ARCH/mobipass/mobipass.c
${OPENAIR_TARGETS}/ARCH/mobipass/queues.c
)
add_library(oai_mobipass MODULE ${TPLIB_MOBIPASS_SOURCE} )
# Hide all functions/variables in the mobipass library.
# Use __attribute__((__visibility__("default")))
# in the source code to unhide a function/variable.
get_target_property(mobipas_cflags oai_mobipass COMPILE_FLAGS)
set_target_properties(oai_mobipass PROPERTIES COMPILE_FLAGS "${mobipass_cflags} -fvisibility=hidden")
##########################################################
......
This diff is collapsed.
......@@ -165,8 +165,9 @@ typedef enum {
} eNB_func_t;
typedef enum {
synch_to_ext_device=0, // synch to RF or Ethernet device
synch_to_other // synch to another source (timer, other CC_id)
synch_to_ext_device=0, // synch to RF or Ethernet device
synch_to_other, // synch to another source (timer, other CC_id)
synch_to_mobipass_standalone // special case for mobipass in standalone mode
} eNB_timing_t;
#endif
......
......@@ -947,6 +947,8 @@ const Enb_properties_array_t *enb_config_init(char* lib_config_file_name_pP)
enb_properties.properties[enb_properties_index]->cc_node_timing[j] = synch_to_ext_device;
} else if (strcmp(cc_node_timing, "synch_to_other") == 0) {
enb_properties.properties[enb_properties_index]->cc_node_timing[j] = synch_to_other;
} else if (strcmp(cc_node_timing, "synch_to_mobipass_standalone") == 0) {
enb_properties.properties[enb_properties_index]->cc_node_timing[j] = synch_to_mobipass_standalone;
} else {
AssertError (0, parse_errors ++,
"Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for node_function choice: SYNCH_TO_DEVICE or SYNCH_TO_OTHER !\n",
......
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <unistd.h>
#include <errno.h>
#include <linux/sysctl.h>
#include <sys/sysctl.h>
#include "common_lib.h"
#include "ethernet_lib.h"
#include "mobipass.h"
#include "queues.h"
struct mobipass_header {
uint16_t flags;
uint16_t fifo_status;
unsigned char seqno;
unsigned char ack;
uint32_t word0;
uint32_t timestamp;
} __attribute__((__packed__));
int mobipass_start(openair0_device *device) { init_mobipass(device->priv); return 0; }
int mobipass_request(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; }
int mobipass_reply(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; }
int mobipass_get_stats(openair0_device* device) { return 0; }
int mobipass_reset_stats(openair0_device* device) { return 0; }
void mobipass_end(openair0_device *device) {}
int mobipass_stop(openair0_device *device) { return 0; }
int mobipass_set_freq(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config) { return 0; }
int mobipass_set_gains(openair0_device* device, openair0_config_t *openair0_cfg) { return 0; }
int mobipass_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
mobipass_state_t *mobi = device->priv;
struct mobipass_header *mh = (struct mobipass_header *)(((char *)buff[0]) + 14);
mobi->mobipass_write_last_timestamp += 640;
mobi->mobipass_write_last_timestamp %= mobi->samples_per_1024_frames;
mh->timestamp = htonl(ntohl(mh->timestamp) % mobi->samples_per_1024_frames);
if (mobi->mobipass_write_last_timestamp != ntohl(mh->timestamp))
{ printf("mobipass: ERROR: bad timestamp wanted %d got %d\n", mobi->mobipass_write_last_timestamp, ntohl(mh->timestamp)); exit(1); }
//printf("__write nsamps %d timestamps %ld seqno %d (packet timestamp %d)\n", nsamps, timestamp, mh->seqno, ntohl(mh->timestamp));
if (nsamps != 640) abort();
enqueue_to_mobipass(mobi->qstate, buff[0]);
return nsamps;
}
int mobipass_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
mobipass_state_t *mobi = device->priv;
//printf("__read nsamps %d return timestamp %d\n", nsamps, ts);
*timestamp = htonl(mobi->mobipass_read_ts);
mobi->mobipass_read_ts += nsamps;
mobi->mobipass_read_ts %= mobi->samples_per_1024_frames;
if (nsamps != 640) { printf("mobipass: ERROR: bad nsamps %d, should be 640\n", nsamps); fflush(stdout); abort(); }
dequeue_from_mobipass(mobi->qstate, ntohl(*timestamp), buff[0]);
#if 1
struct mobipass_header *mh = (struct mobipass_header *)(((char *)buff[0]) + 14);
mh->flags = 0;
mh->fifo_status = 0;
mh->seqno = mobi->mobipass_read_seqno++;
mh->ack = 0;
mh->word0 = 0;
mh->timestamp = htonl(mobi->mobipass_read_ts);
#endif
return nsamps;
}
/* this is the only function in the library that is visible from outside
* because in CMakeLists.txt we use -fvisibility=hidden
*/
__attribute__((__visibility__("default")))
int transport_init(openair0_device *device, openair0_config_t *openair0_cfg,
eth_params_t * eth_params )
{
//init_mobipass();
mobipass_state_t *mobi = (mobipass_state_t*)malloc(sizeof(mobipass_state_t));
memset(mobi, 0, sizeof(mobipass_state_t));
if (eth_params->transp_preference != 4) goto err;
if (eth_params->if_compress != 0) goto err;
/* only 50 PRBs handled for the moment */
if (openair0_cfg[0].sample_rate != 15360000) {
printf("mobipass: ERROR: only 50 PRBs supported\n");
exit(1);
}
mobi->eth.flags = ETH_RAW_IF5_MOBIPASS;
mobi->eth.compression = NO_COMPRESS;
device->Mod_id = 0;//num_devices_eth++;
device->transp_type = ETHERNET_TP;
device->trx_start_func = mobipass_start;
device->trx_request_func = mobipass_request;
device->trx_reply_func = mobipass_reply;
device->trx_get_stats_func = mobipass_get_stats;
device->trx_reset_stats_func = mobipass_reset_stats;
device->trx_end_func = mobipass_end;
device->trx_stop_func = mobipass_stop;
device->trx_set_freq_func = mobipass_set_freq;
device->trx_set_gains_func = mobipass_set_gains;
device->trx_write_func = mobipass_write;
device->trx_read_func = mobipass_read;
device->priv = mobi;
mobi->eth.if_name = strdup(eth_params->local_if_name);
if (mobi->eth.if_name == NULL) abort();
if (sscanf(eth_params->my_addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&mobi->eth_local[0],
&mobi->eth_local[1],
&mobi->eth_local[2],
&mobi->eth_local[3],
&mobi->eth_local[4],
&mobi->eth_local[5]) != 6) {
printf("mobipass: ERROR: bad local ethernet address '%s', check configuration file\n",
eth_params->my_addr);
exit(1);
}
if (sscanf(eth_params->remote_addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&mobi->eth_remote[0],
&mobi->eth_remote[1],
&mobi->eth_remote[2],
&mobi->eth_remote[3],
&mobi->eth_remote[4],
&mobi->eth_remote[5]) != 6) {
printf("mobipass: ERROR: bad remote ethernet address '%s', check configuration file\n",
eth_params->remote_addr);
exit(1);
}
/* note: this only works for 50 PRBs */
mobi->samples_per_1024_frames = 7680*2*10*1024;
/* TX starts at subframe 4, let's pretend we are at the right position */
/* note: this only works for 50 PRBs */
mobi->mobipass_write_last_timestamp = 4*7680*2-640;
/* device specific */
openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
openair0_cfg[0].iq_txshift = eth_params->iq_txshift;// shift
openair0_cfg[0].tx_sample_advance = eth_params->tx_sample_advance;
/* this is useless, I think */
if (device->host_type == BBU_HOST) {
/*Note scheduling advance values valid only for case 7680000 */
switch ((int)openair0_cfg[0].sample_rate) {
case 30720000:
openair0_cfg[0].samples_per_packet = 3840;
break;
case 23040000:
openair0_cfg[0].samples_per_packet = 2880;
break;
case 15360000:
openair0_cfg[0].samples_per_packet = 1920;
break;
case 7680000:
openair0_cfg[0].samples_per_packet = 960;
break;
case 1920000:
openair0_cfg[0].samples_per_packet = 240;
break;
default:
printf("mobipass: ERROR: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
exit(-1);
break;
}
}
device->openair0_cfg=&openair0_cfg[0];
return 0;
err:
printf("mobipass: ERROR: bad configuration file?\n");
exit(1);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <linux/if_packet.h>
#include <netinet/ether.h>
#include <unistd.h>
#include <pthread.h>
#include "queues.h"
#include "mobipass.h"
/******************************************************************/
/* time begin */
/******************************************************************/
#include <time.h>
static void init_time(mobipass_state_t *mobi)
{
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &now)) abort();
mobi->t0 = (uint64_t)now.tv_sec * (uint64_t)1000000000 + (uint64_t)now.tv_nsec;
}
/* called before sending data to mobipass
* waits if called too early with respect to system clock
* does not wait more than 1ms in any case
*/
static void synch_time(mobipass_state_t *mobi, uint32_t ts)
{
if (ts < mobi->synch_time_last_ts) mobi->synch_time_mega_ts++;
mobi->synch_time_last_ts = ts;
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &now)) abort();
uint64_t tnow;
tnow = (uint64_t)now.tv_sec * (uint64_t)1000000000 + (uint64_t)now.tv_nsec;
uint64_t cur = tnow - mobi->t0;
/* 15360000 samples/second, in nanoseconds:
* = 15360000 / 1000000000 = 1536 / 100000 = 48 / 3125*/
uint64_t ts_ns = ((uint64_t)ts + mobi->synch_time_mega_ts * (uint64_t)mobi->samples_per_1024_frames) * (uint64_t)3125 / (uint64_t)48;
/* TODO: if cur is way higher than ts_ns, we are very late, log something? */
if (cur >= ts_ns) return;
uint64_t delta = ts_ns - cur;
/* don't sleep more than 1 ms */
if (delta > 1000*1000) delta = 1000*1000;
delta = delta/1000;
if (delta) usleep(delta);
}
/******************************************************************/
/* time end */
/******************************************************************/
struct ethernet_header {
unsigned char dst[6];
unsigned char src[6];
uint16_t packet_type;
} __attribute__((__packed__));
struct mobipass_header {
uint16_t flags;
uint16_t fifo_status;
unsigned char seqno;
unsigned char ack;
uint32_t word0;
uint32_t timestamp;
} __attribute__((__packed__));
static void do_receive(mobipass_state_t *mobi, unsigned char *b)
{
if (recv(mobi->sock, b, 14+14+1280, 0) != 14+14+1280) { perror("recv"); exit(1); }
struct mobipass_header *mh = (struct mobipass_header *)(b+14);
mh->timestamp = htonl((ntohl(mh->timestamp)-45378/*40120*/) % mobi->samples_per_1024_frames);
//printf("recv timestamp %u\n", ntohl(mh->timestamp));
}
/* receiver thread */
static void *receiver(void *_mobi)
{
mobipass_state_t *mobi = _mobi;
unsigned char receive_packet[14 + 14 + 1280];
while (1) {
do_receive(mobi, receive_packet);
enqueue_from_mobipass(mobi->qstate, receive_packet);
}
return 0;
}
static void do_send(mobipass_state_t *mobi, int seqno, uint32_t ts,
unsigned char *packet)
{
struct ethernet_header *eh = (struct ethernet_header *)packet;
struct mobipass_header *mh = (struct mobipass_header *)(packet+14);
ts %= mobi->samples_per_1024_frames;
//printf("SEND seqno %d ts %d\n", seqno, ts);
memcpy(eh->dst, mobi->eth_remote, 6);
memcpy(eh->src, mobi->eth_local, 6);
eh->packet_type = htons(0xbffe);
mh->flags = 0;
mh->fifo_status = 0;
mh->seqno = seqno;
mh->ack = 0;
mh->word0 = 0;
mh->timestamp = htonl(ts);
synch_time(mobi, ts);
if (send(mobi->sock, packet, 14+14+1280, 0) != 14+14+1280) { perror("send"); exit(1); }
}
/* sender thread */
static void *sender(void *_mobi)
{
mobipass_state_t *mobi = _mobi;
unsigned char packet[14 + 14 + 1280];
uint32_t ts = 0;
unsigned char seqno = 0;
while (1) {
dequeue_to_mobipass(mobi->qstate, ts, packet);
do_send(mobi, seqno, ts, packet);
seqno++;
ts += 640;
ts %= mobi->samples_per_1024_frames;
}
return 0;
}
static void new_thread(void *(*f)(void *), void *data)
{
pthread_t t;
pthread_attr_t att;
if (pthread_attr_init(&att))
{ fprintf(stderr, "pthread_attr_init err\n"); exit(1); }
if (pthread_attr_setdetachstate(&att, PTHREAD_CREATE_DETACHED))
{ fprintf(stderr, "pthread_attr_setdetachstate err\n"); exit(1); }
if (pthread_attr_setstacksize(&att, 10000000))
{ fprintf(stderr, "pthread_attr_setstacksize err\n"); exit(1); }
if (pthread_create(&t, &att, f, data))
{ fprintf(stderr, "pthread_create err\n"); exit(1); }
if (pthread_attr_destroy(&att))
{ fprintf(stderr, "pthread_attr_destroy err\n"); exit(1); }
}
void init_mobipass(mobipass_state_t *mobi)
{
int i;
unsigned char data[14+14+640];
memset(data, 0, 14+14+640);
init_time(mobi);
mobi->qstate = init_queues(mobi->samples_per_1024_frames);
for (i = 0; i < 24*4; i++) {
uint32_t timestamp = i*640;
unsigned char seqno = i;
struct mobipass_header *mh = (struct mobipass_header *)(data+14);
mh->seqno = seqno;
mh->timestamp = htonl(timestamp);
enqueue_to_mobipass(mobi->qstate, data);
}
mobi->sock = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (mobi->sock == -1) { perror("socket"); exit(1); }
/* get if index */
struct ifreq if_index;
memset(&if_index, 0, sizeof(struct ifreq));
strcpy(if_index.ifr_name, mobi->eth.if_name);
if (ioctl(mobi->sock, SIOCGIFINDEX, &if_index)<0) {perror("SIOCGIFINDEX");exit(1);}
struct sockaddr_ll local_addr;
local_addr.sll_family = AF_PACKET;
local_addr.sll_ifindex = if_index.ifr_ifindex;
local_addr.sll_protocol = htons(0xbffe);
local_addr.sll_halen = ETH_ALEN;
local_addr.sll_pkttype = PACKET_OTHERHOST;
if (bind(mobi->sock, (struct sockaddr *)&local_addr, sizeof(struct sockaddr_ll))<0)
{ perror("bind"); exit(1); }
new_thread(receiver, mobi);
new_thread(sender, mobi);
}
#ifndef _MOBIPASS_H_
#define _MOBIPASS_H_
#include <stdint.h>
#include "ethernet_lib.h"
typedef struct {
/* this has to come first */
eth_state_t eth;
void *qstate;
uint8_t eth_local[6];
uint8_t eth_remote[6];
int samples_per_1024_frames;
/* variables used by the function interface.c:mobipass_read */
uint32_t mobipass_read_ts;
unsigned char mobipass_read_seqno;
/* variables used by the function interface.c:mobipass_write */
uint32_t mobipass_write_last_timestamp;
/* variables used by the function mobipass.c:[init_time|synch_time] */
uint64_t t0;
/* variables used by the function mobipass.c:synch_time */
uint32_t synch_time_last_ts;
uint64_t synch_time_mega_ts;
/* sock is used in mobipass.c */
int sock;
} mobipass_state_t;
void init_mobipass(mobipass_state_t *mobi);
#endif /* _MOBIPASS_H_ */
#include "queues.h"
#include "mobipass.h"
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#define QSIZE 10000
struct mobipass_header {
uint16_t flags;
uint16_t fifo_status;
unsigned char seqno;
unsigned char ack;
uint32_t word0;
uint32_t timestamp;
} __attribute__((__packed__));
struct queue {
unsigned char buf[QSIZE][14+14+640*2];
volatile int start;
volatile int len;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
typedef struct {
struct queue to_mobipass;
struct queue from_mobipass;
int samples_per_1024_frames;
/* variables used by dequeue_from_mobipass */
int dequeue_from_mobipass_seqno;
/* variables used to manage logging of "missing samples"
* coming from mobipass. (Yes, this can happen, mostly
* at startup.)
* This idea is to print some logs, but not too much to
* flood stdout. (This is already a bad idea to call
* printf in a 'realtime' thread.)
*/
int no_sample_log_running;
uint32_t no_sample_log_start_sample;
uint32_t no_sample_log_next_sample;
} queue_state_t;
static void enqueue(void *data, struct queue *q)
{
int pos;
if (pthread_mutex_lock(&q->mutex)) abort();
if (q->len == QSIZE) {
printf("mobipass: WARNING: enqueue: full\n");
goto done;
}
pos = (q->start + q->len) % QSIZE;
memcpy(q->buf[pos], data, 14+14+640*2);
q->len++;
done:
if (pthread_cond_signal(&q->cond)) abort();
if (pthread_mutex_unlock(&q->mutex)) abort();
}
void enqueue_to_mobipass(void *_qstate, void *data)
{
queue_state_t *qstate = _qstate;
enqueue(data, &qstate->to_mobipass);
}
void dequeue_to_mobipass(void *_qstate, uint32_t timestamp, void *data)
{
queue_state_t *qstate = _qstate;
if (pthread_mutex_lock(&qstate->to_mobipass.mutex)) abort();
while (qstate->to_mobipass.len == 0) {
if (pthread_cond_wait(&qstate->to_mobipass.cond, &qstate->to_mobipass.mutex)) abort();
}
memcpy(data, qstate->to_mobipass.buf[qstate->to_mobipass.start], 14+14+640*2);
qstate->to_mobipass.len--;
qstate->to_mobipass.start = (qstate->to_mobipass.start + 1) % QSIZE;
if (pthread_mutex_unlock(&qstate->to_mobipass.mutex)) abort();
}
void enqueue_from_mobipass(void *_qstate, void *data)
{
queue_state_t *qstate = _qstate;
struct mobipass_header *mh = (struct mobipass_header *)((char*)data+14);
mh->timestamp = htonl(ntohl(mh->timestamp) % qstate->samples_per_1024_frames);
//printf("from mobipass! timestamp %u seqno %d\n", ntohl(mh->timestamp), mh->seqno);
enqueue(data, &qstate->from_mobipass);
}
static int cmp_timestamps(uint32_t a, uint32_t b, int samples_per_1024_frames)
{
if (a == b) return 0;
if (a < b) {
if (b-a > samples_per_1024_frames/2) return 1;
return -1;
}
if (a-b > samples_per_1024_frames/2) return -1;
return 1;
}
/*************************************************/
/* missing samples logging management begin */
/*************************************************/
static void log_flush(queue_state_t *qstate)
{
/* print now if there is something to print */
if (qstate->no_sample_log_running == 0)
return;
qstate->no_sample_log_running = 0;
printf("mobipass: WARNING: missing samples [%u-%u]\n",
qstate->no_sample_log_start_sample,
(uint32_t)(qstate->no_sample_log_next_sample-1));
}
static void log_missed_sample(queue_state_t *qstate, uint32_t timestamp)
{
/* collect data, print if there is a discontinuity */
if (qstate->no_sample_log_running == 0 ||
timestamp != qstate->no_sample_log_next_sample) {
log_flush(qstate);
qstate->no_sample_log_start_sample = timestamp;
}
qstate->no_sample_log_next_sample = timestamp+1;
qstate->no_sample_log_running = 1;
}
static void log_flush_if_old(queue_state_t *qstate, uint32_t timestamp)
{
/* log every second (more or less), if we have to */
/* note that if mobipass stopped, it may take much more
* than one second to log, due to the sleeps done while
* waiting for samples (that never come)
*/
if (qstate->no_sample_log_running == 1 &&
labs(timestamp-qstate->no_sample_log_start_sample) > qstate->samples_per_1024_frames/10)
log_flush(qstate);
}
/*************************************************/
/* missing samples logging management end */
/*************************************************/
/* to be called with lock on */
static void get_sample_from_mobipass(queue_state_t *qstate, char *I, char *Q, uint32_t timestamp)
{
unsigned char *b = NULL;
unsigned char *data = NULL;
struct mobipass_header *mh = NULL;
uint32_t packet_timestamp = 0;
#if 0
uint32_t old_start = qstate->from_mobipass.start;
uint32_t old_len = qstate->from_mobipass.len;
b = qstate->from_mobipass.buf[qstate->from_mobipass.start];
mh = (struct mobipass_header *)(b+14);
uint32_t old_pts = qstate->from_mobipass.len ? ntohl(mh->timestamp) : -1;
b=NULL;
mh=NULL;
#endif
while (qstate->from_mobipass.len) {
b = qstate->from_mobipass.buf[qstate->from_mobipass.start];
mh = (struct mobipass_header *)(b+14);
data = b + 14*2;
packet_timestamp = ntohl(mh->timestamp);
if (cmp_timestamps(timestamp, packet_timestamp, qstate->samples_per_1024_frames) < 0) goto nodata;
if (cmp_timestamps(timestamp, (packet_timestamp+640) % qstate->samples_per_1024_frames, qstate->samples_per_1024_frames) < 0) break;
qstate->from_mobipass.len--;
qstate->from_mobipass.start = (qstate->from_mobipass.start+1) % QSIZE;
}
if (qstate->from_mobipass.len == 0) goto nodata;
if (timestamp == (packet_timestamp + 639) % qstate->samples_per_1024_frames) {
qstate->from_mobipass.len--;
qstate->from_mobipass.start = (qstate->from_mobipass.start+1) % QSIZE;
}
if (timestamp < packet_timestamp) timestamp += qstate->samples_per_1024_frames;
*I = data[(timestamp - packet_timestamp) * 2];
*Q = data[(timestamp - packet_timestamp) * 2 + 1];
return;
nodata:
*I = 0;
*Q = 0;
log_missed_sample(qstate, timestamp);
#if 0
printf("no sample timestamp %u pt %u start %d old_start %d old_pt %u len %d old len %d\n", timestamp, packet_timestamp, qstate->from_mobipass.start, old_start, old_pts, qstate->from_mobipass.len, old_len);
#endif
}
/* doesn't work with delay more than 1s */
static void wait_for_data(pthread_cond_t *cond, pthread_mutex_t *mutex, int delay_us)
{
struct timeval now;
struct timespec target;
gettimeofday(&now, NULL);
target.tv_sec = now.tv_sec;
target.tv_nsec = (now.tv_usec + delay_us) * 1000;
if (target.tv_nsec >= 1000 * 1000 * 1000) { target.tv_nsec -= 1000 * 1000 * 1000; target.tv_sec++; }
int err = pthread_cond_timedwait(cond, mutex, &target);
if (err != 0 && err != ETIMEDOUT) { printf("mobipass: ERROR: pthread_cond_timedwait: err (%d) %s\n", err, strerror(err)); abort(); }
}
/* don't block infinitely when waiting for data
* if waiting for too long, just return some zeros
*/
void dequeue_from_mobipass(void *_qstate, uint32_t timestamp, void *data)
{
queue_state_t *qstate = _qstate;
int i;
// int ts = timestamp;
int waiting_allowed;
if (pthread_mutex_lock(&qstate->from_mobipass.mutex)) abort();