Skip to content
Snippets Groups Projects
Commit 718f754c authored by frtabu's avatar frtabu
Browse files

modify tcp_bridge_oai.c to support remote connections, old behavior is...

modify tcp_bridge_oai.c to support remote connections, old behavior is maintained, define TCPBRIDGE environment variable to the enb ip address on ue side.
parent 1e0d8161
No related branches found
No related tags found
No related merge requests found
...@@ -8,33 +8,47 @@ ...@@ -8,33 +8,47 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
int fullread(int fd, void *_buf, int count) const int port = 4043;
{ #define helpTxt "\
\x1b[31m\
tcp_bridge: error: you have to run one UE and one eNB\n\
For this, export TCPBRIDGE=enb (eNB case) or \n\
TCPBRIDGE=<an ip address> (UE case)\n\
\x1b[m"
int fullread(int fd, void *_buf, int count) {
char *buf = _buf; char *buf = _buf;
int ret = 0; int ret = 0;
int l; int l;
while (count) { while (count) {
l = read(fd, buf, count); l = read(fd, buf, count);
if (l <= 0) return -1; if (l <= 0) return -1;
count -= l; count -= l;
buf += l; buf += l;
ret += l; ret += l;
} }
return ret; return ret;
} }
int fullwrite(int fd, void *_buf, int count) int fullwrite(int fd, void *_buf, int count) {
{
char *buf = _buf; char *buf = _buf;
int ret = 0; int ret = 0;
int l; int l;
while (count) { while (count) {
l = write(fd, buf, count); l = write(fd, buf, count);
if (l <= 0) return -1; if (l <= 0) return -1;
count -= l; count -= l;
buf += l; buf += l;
ret += l; ret += l;
} }
return ret; return ret;
} }
...@@ -44,157 +58,188 @@ typedef struct { ...@@ -44,157 +58,188 @@ typedef struct {
int sock; int sock;
int samples_per_subframe; int samples_per_subframe;
uint64_t timestamp; uint64_t timestamp;
uint64_t next_tx_timestamp;
int is_enb; int is_enb;
char *ip;
} tcp_bridge_state_t; } tcp_bridge_state_t;
void verify_connection(int fd, int is_enb) void verify_connection(int fd, int is_enb) {
{
char c = is_enb; char c = is_enb;
if (fullwrite(fd, &c, 1) != 1) exit(1); if (fullwrite(fd, &c, 1) != 1) exit(1);
if (fullread(fd, &c, 1) != 1) exit(1); if (fullread(fd, &c, 1) != 1) exit(1);
if (c == is_enb) { if (c == is_enb) {
printf("\x1b[31mtcp_bridge: error: you have to run one UE and one eNB" printf(helpTxt);
" (did you run 'export ENODEB=1' in the eNB terminal?)\x1b[m\n");
exit(1); exit(1);
} }
} }
int tcp_bridge_start(openair0_device *device)
{
int port = 4043;
tcp_bridge_state_t *tcp_bridge = device->priv;
int try;
int max_try = 5;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) { perror("tcp_bridge: socket"); exit(1); }
int enable = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)))
{ perror("tcp_bridge: SO_REUSEADDR"); exit(1); }
int start_enb(tcp_bridge_state_t *tcp_bridge) {
struct sockaddr_in addr = { struct sockaddr_in addr = {
sin_family: AF_INET, sin_family:
sin_port: htons(port), AF_INET,
sin_addr: { s_addr: INADDR_ANY } sin_port:
htons(port),
sin_addr:
{ s_addr: INADDR_ANY }
}; };
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) { if (bind(tcp_bridge->sock, (struct sockaddr *)&addr, sizeof(addr))) {
if (errno == EADDRINUSE) goto client_mode; perror("tcp_bridge: bind");
{ perror("tcp_bridge: bind"); exit(1); } exit(1);
} }
if (listen(sock, 5)) if (listen(tcp_bridge->sock, 5)) {
{ perror("tcp_bridge: listen"); exit(1); } perror("tcp_bridge: listen");
exit(1);
}
printf("tcp_bridge: wait for connection on port %d\n", port); printf("tcp_bridge: wait for connection on port %d\n", port);
socklen_t len = sizeof(addr); socklen_t len = sizeof(addr);
int sock2 = accept(sock, (struct sockaddr *)&addr, &len); int sockServ = accept(tcp_bridge->sock, (struct sockaddr *)&addr, &len);
if (sock2 == -1)
{ perror("tcp_bridge: accept"); exit(1); }
close(sock); if ( sockServ == -1) {
perror("tcp_bridge: accept");
tcp_bridge->sock = sock2; exit(1);
}
verify_connection(sockServ, tcp_bridge->is_enb);
printf("tcp_bridge: connection established\n"); printf("tcp_bridge: connection established\n");
close(tcp_bridge->sock);
verify_connection(sock2, tcp_bridge->is_enb); tcp_bridge->sock=sockServ;
return 0; return 0;
}
client_mode: int start_ue(tcp_bridge_state_t *tcp_bridge) {
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); struct sockaddr_in addr = {sin_family:
AF_INET,
for (try = 0; try < max_try; try++) { sin_port:
if (try != 0) sleep(1); htons(port),
sin_addr:
{ s_addr: INADDR_ANY }
};
addr.sin_addr.s_addr = inet_addr(tcp_bridge->ip);
printf("tcp_bridge: trying to connect to 127.0.0.1:%d (attempt %d/%d)\n", while(1) {
port, try+1, max_try); printf("tcp_bridge: trying to connect to %s:%d\n", tcp_bridge->ip, port);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) { if (connect(tcp_bridge->sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
verify_connection(tcp_bridge->sock, tcp_bridge->is_enb);
printf("tcp_bridge: connection established\n"); printf("tcp_bridge: connection established\n");
tcp_bridge->sock = sock;
verify_connection(sock, tcp_bridge->is_enb);
return 0; return 0;
} }
perror("tcp_bridge"); perror("tcp_bridge");
sleep(1);
} }
printf("tcp_bridge: connection failed\n"); return 0;
}
exit(1); int tcp_bridge_start(openair0_device *device) {
tcp_bridge_state_t *tcp_bridge = device->priv;
tcp_bridge->sock = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_bridge->sock == -1) {
perror("tcp_bridge: socket");
exit(1);
}
int enable = 1;
if (setsockopt(tcp_bridge->sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int))) {
perror("tcp_bridge: SO_REUSEADDR");
exit(1);
}
if ( tcp_bridge->is_enb )
return start_enb(tcp_bridge);
else
return start_ue(tcp_bridge);
} }
int tcp_bridge_request(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; } int tcp_bridge_request(openair0_device *device, void *msg, ssize_t msg_len) {
int tcp_bridge_reply(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; } abort();
int tcp_bridge_get_stats(openair0_device* device) { return 0; } return 0;
int tcp_bridge_reset_stats(openair0_device* device) { return 0; } }
int tcp_bridge_reply(openair0_device *device, void *msg, ssize_t msg_len) {
abort();
return 0;
}
int tcp_bridge_get_stats(openair0_device *device) {
return 0;
}
int tcp_bridge_reset_stats(openair0_device *device) {
return 0;
}
void tcp_bridge_end(openair0_device *device) {} void tcp_bridge_end(openair0_device *device) {}
int tcp_bridge_stop(openair0_device *device) { return 0; } int tcp_bridge_stop(openair0_device *device) {
int tcp_bridge_set_freq(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config) { return 0; } return 0;
int tcp_bridge_set_gains(openair0_device* device, openair0_config_t *openair0_cfg) { return 0; } }
int tcp_bridge_set_freq(openair0_device *device, openair0_config_t *openair0_cfg,int exmimo_dump_config) {
return 0;
}
int tcp_bridge_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) {
return 0;
}
int tcp_bridge_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) int tcp_bridge_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
{ if (cc != 1) {
if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); } printf("tcp_bridge: only 1 antenna supported\n");
tcp_bridge_state_t *t = device->priv; exit(1);
/* deal with discontinuities in output (think: eNB in TDD mode) */
if (t->next_tx_timestamp && timestamp != t->next_tx_timestamp) {
uint32_t b[4096];
uint64_t to_send = timestamp - t->next_tx_timestamp;
memset(b, 0, 4096 * sizeof(uint32_t));
while (to_send) {
int len = to_send > 4096 ? 4096 : to_send;
int n = fullwrite(t->sock, b, len * 4);
if (n != len * 4) {
printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno));
abort();
}
to_send -= len;
}
} }
tcp_bridge_state_t *t = device->priv;
int n = fullwrite(t->sock, buff[0], nsamps * 4); int n = fullwrite(t->sock, buff[0], nsamps * 4);
if (n != nsamps * 4) { if (n != nsamps * 4) {
printf("tcp_bridge: write error ret %d (wanted %d) error %s\n", n, nsamps*4, strerror(errno)); printf("tcp_bridge: write error ret %d (wanted %d) error %s\n", n, nsamps*4, strerror(errno));
abort(); abort();
} }
t->next_tx_timestamp = timestamp + nsamps;
return nsamps; return nsamps;
} }
int tcp_bridge_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) int tcp_bridge_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
{ if (cc != 1) {
if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); } printf("tcp_bridge: only 1 antenna supported\n");
exit(1);
}
tcp_bridge_state_t *t = device->priv; tcp_bridge_state_t *t = device->priv;
int n = fullread(t->sock, buff[0], nsamps * 4); int n = fullread(t->sock, buff[0], nsamps * 4);
if (n != nsamps * 4) { if (n != nsamps * 4) {
printf("tcp_bridge: read error ret %d nsamps*4 %d error %s\n", n, nsamps * 4, strerror(errno)); printf("tcp_bridge: read error ret %d nsamps*4 %d error %s\n", n, nsamps * 4, strerror(errno));
abort(); abort();
} }
*timestamp = t->timestamp; *timestamp = t->timestamp;
t->timestamp += nsamps; t->timestamp += nsamps;
return nsamps; return nsamps;
} }
int tcp_bridge_read_ue(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) int tcp_bridge_read_ue(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
{ if (cc != 1) {
if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); } printf("tcp_bridge: only 1 antenna supported\n");
exit(1);
}
tcp_bridge_state_t *t = device->priv; tcp_bridge_state_t *t = device->priv;
int n; int n;
/* In synch mode, UE does not write, but we need to /* In synch mode, UE does not write, but we need to
* send something to the eNodeB. send something to the eNodeB.
* We know that UE is in synch mode when it reads We know that UE is in synch mode when it reads
* 10 subframes at a time. 10 subframes at a time.
*/ */
if (nsamps == t->samples_per_subframe * 10) { if (nsamps == t->samples_per_subframe * 10) {
uint32_t b[nsamps]; uint32_t b[nsamps];
memset(b, 0, nsamps * 4); memset(b, 0, nsamps * 4);
n = fullwrite(t->sock, b, nsamps * 4); n = fullwrite(t->sock, b, nsamps * 4);
if (n != nsamps * 4) { if (n != nsamps * 4) {
printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno)); printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno));
abort(); abort();
...@@ -205,96 +250,106 @@ int tcp_bridge_read_ue(openair0_device *device, openair0_timestamp *timestamp, v ...@@ -205,96 +250,106 @@ int tcp_bridge_read_ue(openair0_device *device, openair0_timestamp *timestamp, v
} }
/* To startup proper communcation between eNB and UE, /* To startup proper communcation between eNB and UE,
* we need to understand that: we need to understand that:
* - eNodeB starts reading subframe 0 - eNodeB starts reading subframe 0
* - then eNodeB starts sending subframe 4 - then eNodeB starts sending subframe 4
* and then repeats read/write for each subframe. and then repeats read/write for each subframe.
* The UE: The UE:
* - reads 10 subframes at a time until it is synchronized - reads 10 subframes at a time until it is synchronized
* - then reads subframe n and writes subframe n+2 - then reads subframe n and writes subframe n+2
* We also want to enforce that the subframe 0 is read We also want to enforce that the subframe 0 is read
* at the beginning of the UE RX buffer, not in the middle at the beginning of the UE RX buffer, not in the middle
* of it. of it.
* So it means: So it means:
* - for the eNodeB: let it run as in normal mode (as with a B210) - for the eNodeB: let it run as in normal mode (as with a B210)
* - for the UE, on its very first read: - for the UE, on its very first read:
* - we want this read to get data from subframe 0 - we want this read to get data from subframe 0
* but the first write of eNodeB is subframe 4 but the first write of eNodeB is subframe 4
* so we first need to read and ignore 6 subframes so we first need to read and ignore 6 subframes
* - the UE will start its TX only at the subframe 2 - the UE will start its TX only at the subframe 2
* corresponding to the subframe 0 it just read, corresponding to the subframe 0 it just read,
* so we need to write 12 subframes before anything so we need to write 12 subframes before anything
* (the function tcp_bridge_read_ue takes care to (the function tcp_bridge_read_ue takes care to
* insert dummy TX data during the synch phase) insert dummy TX data during the synch phase)
*
* Here is a drawing of the beginning of things to make Here is a drawing of the beginning of things to make
* this logic clearer. this logic clearer.
*
* We see that eNB starts RX at subframe 0, starts TX at subfram 4, We see that eNB starts RX at subframe 0, starts TX at subfram 4,
* and that UE starts RX at subframe 10 and TX at subframe 12. and that UE starts RX at subframe 10 and TX at subframe 12.
*
* We understand that the UE has to transmit 12 empty We understand that the UE has to transmit 12 empty
* subframes for the eNodeB to start its processing. subframes for the eNodeB to start its processing.
*
* And because the eNodeB starts its TX at subframe 4 and we And because the eNodeB starts its TX at subframe 4 and we
* want the UE to start its RX at subframe 10, we need to want the UE to start its RX at subframe 10, we need to
* read and ignore 6 subframes in the UE. read and ignore 6 subframes in the UE.
*
* ------------------------------------------------------------------------- -------------------------------------------------------------------------
* eNB RX: | *0* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 ... eNB RX: | *0* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 ...
* ------------------------------------------------------------------------- -------------------------------------------------------------------------
*
* ------------------------------------------------------------------------- -------------------------------------------------------------------------
* eNB TX: | 0 | 1 | 2 | 3 | *4* | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 ... eNB TX: | 0 | 1 | 2 | 3 | *4* | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 ...
* ------------------------------------------------------------------------- -------------------------------------------------------------------------
*
* ------------------------------------------------------------------------- -------------------------------------------------------------------------
* UE RX: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | *10* | 11 | 12 | 13 | 14 ... UE RX: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | *10* | 11 | 12 | 13 | 14 ...
* ------------------------------------------------------------------------- -------------------------------------------------------------------------
*
* ------------------------------------------------------------------------- -------------------------------------------------------------------------
* UE TX: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | *12* | 13 | 14 ... UE TX: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | *12* | 13 | 14 ...
* ------------------------------------------------------------------------- -------------------------------------------------------------------------
*
* As a final note, we do TX before RX to ensure that the eNB will As a final note, we do TX before RX to ensure that the eNB will
* get some data and send us something so there is no deadlock get some data and send us something so there is no deadlock
* at the beginning of things. Hopefully the kernel buffers for at the beginning of things. Hopefully the kernel buffers for
* the sockets are big enough so that the first (big) TX can the sockets are big enough so that the first (big) TX can
* return to user mode before the buffers are full. If this return to user mode before the buffers are full. If this
* is wrong in some environment, we will need to work by smaller is wrong in some environment, we will need to work by smaller
* units of data at a time. units of data at a time.
*/ */
int tcp_bridge_ue_first_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) int tcp_bridge_ue_first_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
{ if (cc != 1) {
if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); } printf("tcp_bridge: only 1 antenna supported\n");
tcp_bridge_state_t *t = device->priv; exit(1);
}
tcp_bridge_state_t *t = device->priv;
uint32_t b[t->samples_per_subframe * 12]; uint32_t b[t->samples_per_subframe * 12];
memset(b, 0, t->samples_per_subframe * 12 * 4); memset(b, 0, t->samples_per_subframe * 12 * 4);
int n = fullwrite(t->sock, b, t->samples_per_subframe * 12 * 4); int n = fullwrite(t->sock, b, t->samples_per_subframe * 12 * 4);
if (n != t->samples_per_subframe * 12 * 4) { if (n != t->samples_per_subframe * 12 * 4) {
printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno)); printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno));
abort(); abort();
} }
n = fullread(t->sock, b, t->samples_per_subframe * 6 * 4); n = fullread(t->sock, b, t->samples_per_subframe * 6 * 4);
if (n != t->samples_per_subframe * 6 * 4) { if (n != t->samples_per_subframe * 6 * 4) {
printf("tcp_bridge: read error ret %d error %s\n", n, strerror(errno)); printf("tcp_bridge: read error ret %d error %s\n", n, strerror(errno));
abort(); abort();
} }
device->trx_read_func = tcp_bridge_read_ue; device->trx_read_func = tcp_bridge_read_ue;
return tcp_bridge_read_ue(device, timestamp, buff, nsamps, cc); return tcp_bridge_read_ue(device, timestamp, buff, nsamps, cc);
} }
__attribute__((__visibility__("default"))) __attribute__((__visibility__("default")))
int device_init(openair0_device* device, openair0_config_t *openair0_cfg) int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
{ tcp_bridge_state_t *tcp_bridge = (tcp_bridge_state_t *)calloc(sizeof(tcp_bridge_state_t),1);
tcp_bridge_state_t *tcp_bridge = (tcp_bridge_state_t*)malloc(sizeof(tcp_bridge_state_t));
memset(tcp_bridge, 0, sizeof(tcp_bridge_state_t)); if ((tcp_bridge->ip=getenv("TCPBRIDGE")) == NULL ) {
/* for compatibility, we test the ENB environment variable */
tcp_bridge->is_enb = getenv("ENODEB") != NULL; if ((tcp_bridge->ip=getenv("ENODEB")) != NULL ) {
tcp_bridge->ip=strdup("enb");
} else {
tcp_bridge->ip=strdup("127.0.0.1");
}
}
tcp_bridge->is_enb = strncasecmp(tcp_bridge->ip,"enb",3) == 0;
printf("tcp_bridge: running as %s\n", tcp_bridge->is_enb ? "eNB" : "UE"); printf("tcp_bridge: running as %s\n", tcp_bridge->is_enb ? "eNB" : "UE");
/* only 25, 50 or 100 PRBs handled for the moment */ /* only 25, 50 or 100 PRBs handled for the moment */
...@@ -323,15 +378,21 @@ int device_init(openair0_device* device, openair0_config_t *openair0_cfg) ...@@ -323,15 +378,21 @@ int device_init(openair0_device* device, openair0_config_t *openair0_cfg)
device->priv = tcp_bridge; device->priv = tcp_bridge;
switch ((int)openair0_cfg[0].sample_rate) { switch ((int)openair0_cfg[0].sample_rate) {
case 30720000: tcp_bridge->samples_per_subframe = 30720; break; case 30720000:
case 15360000: tcp_bridge->samples_per_subframe = 15360; break; tcp_bridge->samples_per_subframe = 30720;
case 7680000: tcp_bridge->samples_per_subframe = 7680; break; break;
case 15360000:
tcp_bridge->samples_per_subframe = 15360;
break;
case 7680000:
tcp_bridge->samples_per_subframe = 7680;
break;
} }
/* let's pretend to be a b2x0 */ /* let's pretend to be a b2x0 */
device->type = USRP_B200_DEV; device->type = USRP_B200_DEV;
device->openair0_cfg=&openair0_cfg[0]; device->openair0_cfg=&openair0_cfg[0];
return 0; return 0;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment