mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-10-31 12:03:50 +00:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			1.13.0
			...
			laforge/rt
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | b7a0e043b5 | ||
|  | 004ea1398a | ||
|  | 4f0d72d4c1 | ||
|  | c7d15fb70d | 
							
								
								
									
										11
									
								
								contrib/rtp-load-gen/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contrib/rtp-load-gen/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | CFLAGS=-g -Wall $(shell pkg-config --cflags libosmocore libosmoctrl libosmo-netif liburing) | ||||||
|  | LIBS=-lpthread $(shell pkg-config --libs libosmocore libosmoctrl libosmo-netif liburing) | ||||||
|  |  | ||||||
|  | rtp-load-gen: rtp-load-gen.o rtp_provider.o rtp_provider_static.o ctrl_if.o | ||||||
|  | 	$(CC) -o $@ $^ $(LIBS) | ||||||
|  |  | ||||||
|  | %.o: %.c | ||||||
|  | 	$(CC) $(CFLAGS) -o $@ -c $^ | ||||||
|  |  | ||||||
|  | clean: | ||||||
|  | 	@rm rtp-load-gen *.o | ||||||
							
								
								
									
										214
									
								
								contrib/rtp-load-gen/ctrl_if.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								contrib/rtp-load-gen/ctrl_if.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | |||||||
|  | /* CTRL interface of rtpsource program | ||||||
|  |  * | ||||||
|  |  * (C) 2020 by Harald Welte <laforge@gnumonks.org> | ||||||
|  |  * | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <osmocom/ctrl/control_cmd.h> | ||||||
|  |  | ||||||
|  | #include "internal.h" | ||||||
|  | #include "rtp_provider.h" | ||||||
|  |  | ||||||
|  | static struct rtpsim_connection *find_connection_by_cname(const char *cname) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_connection *rtpc; | ||||||
|  | 	struct rtpsim_instance *ri; | ||||||
|  |  | ||||||
|  | 	pthread_rwlock_rdlock(&g_rtpsim->rwlock); | ||||||
|  | 	llist_for_each_entry(ri, &g_rtpsim->instances, list) { | ||||||
|  | 		rtpc = rtpsim_conn_find(ri, cname); | ||||||
|  | 		if (rtpc) { | ||||||
|  | 			pthread_rwlock_unlock(&g_rtpsim->rwlock); | ||||||
|  | 			return rtpc; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	pthread_rwlock_unlock(&g_rtpsim->rwlock); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct rtpsim_connection *create_connection(const char *cname, enum codec_type codec) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_connection *rtpc; | ||||||
|  | 	struct rtpsim_instance *ri; | ||||||
|  |  | ||||||
|  | 	pthread_rwlock_rdlock(&g_rtpsim->rwlock); | ||||||
|  | 	llist_for_each_entry(ri, &g_rtpsim->instances, list) { | ||||||
|  | 		rtpc = rtpsim_conn_reserve(ri, cname, codec); | ||||||
|  | 		if (rtpc) { | ||||||
|  | 			pthread_rwlock_unlock(&g_rtpsim->rwlock); | ||||||
|  | 			return rtpc; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	pthread_rwlock_unlock(&g_rtpsim->rwlock); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int connect_connection(struct rtpsim_connection *rtpc, const char *remote_host, | ||||||
|  | 			      uint16_t remote_port, uint8_t pt) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	osmo_sockaddr_str_from_str(&rtpc->cfg.remote, remote_host, remote_port); | ||||||
|  | 	rtpc->cfg.pt = pt; | ||||||
|  |  | ||||||
|  | 	rc = rtpsim_conn_connect(rtpc); | ||||||
|  | 	if (rc < 0) | ||||||
|  | 		return rc; | ||||||
|  |  | ||||||
|  | 	rc = rtpsim_conn_start(rtpc); | ||||||
|  |  | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int delete_connection(struct rtpsim_connection *rtpc) | ||||||
|  | { | ||||||
|  | 	rtpsim_conn_stop(rtpc); | ||||||
|  | 	rtpsim_conn_unreserve(rtpc); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CTRL_CMD_DEFINE_WO_NOVRF(rtp_create, "rtp_create"); | ||||||
|  | static int set_rtp_create(struct ctrl_cmd *cmd, void *data) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_connection *conn; | ||||||
|  | 	const char *cname, *codec_str; | ||||||
|  | 	char *tmp, *saveptr; | ||||||
|  | 	enum codec_type codec; | ||||||
|  |  | ||||||
|  | 	tmp = talloc_strdup(cmd, cmd->value); | ||||||
|  | 	OSMO_ASSERT(tmp); | ||||||
|  |  | ||||||
|  | 	cname = strtok_r(tmp, ",", &saveptr); | ||||||
|  | 	codec_str = strtok_r(NULL, ",", &saveptr); | ||||||
|  |  | ||||||
|  | 	if (!cname || !codec_str) { | ||||||
|  | 		cmd->reply = "Format is cname,codec"; | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (find_connection_by_cname(cname)) { | ||||||
|  | 		cmd->reply = "Connection already exists for cname"; | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	codec = get_string_value(codec_type_names, codec_str); | ||||||
|  | 	if (codec < 0) { | ||||||
|  | 		cmd->reply = "Invalid codec name (try GSM_FR, GSM_EFR etc.)"; | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	conn = create_connection(cname, codec); | ||||||
|  | 	if (!conn) { | ||||||
|  | 		cmd->reply = "Error creating RTP connection"; | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Respond */ | ||||||
|  | 	cmd->reply = talloc_asprintf(cmd, "%s,%s,%d", conn->cname, conn->cfg.local.ip, conn->cfg.local.port); | ||||||
|  | 	talloc_free(tmp); | ||||||
|  | 	return CTRL_CMD_REPLY; | ||||||
|  |  | ||||||
|  | error: | ||||||
|  | 	talloc_free(tmp); | ||||||
|  | 	return CTRL_CMD_ERROR; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CTRL_CMD_DEFINE_WO_NOVRF(rtp_connect, "rtp_connect"); | ||||||
|  | static int set_rtp_connect(struct ctrl_cmd *cmd, void *data) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_connection *conn; | ||||||
|  | 	const char *cname, *remote_host, *remote_port, *pt; | ||||||
|  | 	char *tmp, *saveptr; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	tmp = talloc_strdup(cmd, cmd->value); | ||||||
|  | 	OSMO_ASSERT(tmp); | ||||||
|  |  | ||||||
|  | 	/* FIXME: parse command */ | ||||||
|  | 	cname = strtok_r(tmp, ",", &saveptr); | ||||||
|  | 	remote_host = strtok_r(NULL, ",", &saveptr); | ||||||
|  | 	remote_port = strtok_r(NULL, ",", &saveptr); | ||||||
|  | 	pt = strtok_r(NULL, ",", &saveptr); | ||||||
|  |  | ||||||
|  | 	if (!cname || !remote_host || !remote_port || !pt) { | ||||||
|  | 		cmd->reply = "Format is cname,remote_host,remote_port,pt"; | ||||||
|  | 		talloc_free(tmp); | ||||||
|  | 		return CTRL_CMD_ERROR; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	conn = find_connection_by_cname(cname); | ||||||
|  | 	if (!conn) { | ||||||
|  | 		cmd->reply = "Error finding RTP connection for connect"; | ||||||
|  | 		talloc_free(tmp); | ||||||
|  | 		return CTRL_CMD_ERROR; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rc = connect_connection(conn, remote_host, atoi(remote_port), atoi(pt)); | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		cmd->reply = "Error binding RTP connection"; | ||||||
|  | 		talloc_free(tmp); | ||||||
|  | 		return CTRL_CMD_ERROR; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Respond */ | ||||||
|  | 	talloc_free(tmp); | ||||||
|  | 	cmd->reply = talloc_asprintf(cmd, "%s,%s,%d,%d", conn->cname, conn->cfg.remote.ip, | ||||||
|  | 					conn->cfg.remote.port, conn->cfg.pt); | ||||||
|  | 	return CTRL_CMD_REPLY; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CTRL_CMD_DEFINE_WO_NOVRF(rtp_delete, "rtp_delete"); | ||||||
|  | static int set_rtp_delete(struct ctrl_cmd *cmd, void *data) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_connection *conn; | ||||||
|  | 	const char *cname = cmd->value; | ||||||
|  |  | ||||||
|  | 	conn = find_connection_by_cname(cname); | ||||||
|  | 	if (!conn) { | ||||||
|  | 		cmd->reply = "Error finding RTP connection for delete"; | ||||||
|  | 		return CTRL_CMD_ERROR; | ||||||
|  | 	} | ||||||
|  | 	cmd->reply = talloc_asprintf(cmd, "%s", conn->cname); | ||||||
|  |  | ||||||
|  | 	delete_connection(conn); | ||||||
|  |  | ||||||
|  | 	/* Respond */ | ||||||
|  | 	return CTRL_CMD_REPLY; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int rtpsource_ctrl_cmds_install(void) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rtp_create); | ||||||
|  | 	if (rc) | ||||||
|  | 		goto end; | ||||||
|  |  | ||||||
|  | 	rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rtp_connect); | ||||||
|  | 	if (rc) | ||||||
|  | 		goto end; | ||||||
|  |  | ||||||
|  | 	rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rtp_delete); | ||||||
|  | 	if (rc) | ||||||
|  | 		goto end; | ||||||
|  | end: | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
							
								
								
									
										112
									
								
								contrib/rtp-load-gen/internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								contrib/rtp-load-gen/internal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <pthread.h> | ||||||
|  | #include <liburing.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/core/linuxlist.h> | ||||||
|  | #include <osmocom/core/sockaddr_str.h> | ||||||
|  | #include <osmocom/core/rate_ctr.h> | ||||||
|  | #include <osmocom/core/select.h> | ||||||
|  | #include <osmocom/ctrl/control_if.h> | ||||||
|  |  | ||||||
|  | struct rtp_provider_instance; | ||||||
|  |  | ||||||
|  | /* configuration of one RTP connection/socket */ | ||||||
|  | struct rtpsim_connection_cfg { | ||||||
|  | 	struct osmo_sockaddr_str local; | ||||||
|  | 	struct osmo_sockaddr_str remote; | ||||||
|  | 	uint8_t pt; | ||||||
|  | 	uint32_t ssrc; | ||||||
|  | 	uint32_t duration; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* TX side state of RTP connection/socket */ | ||||||
|  | struct rtpsim_connection_tx { | ||||||
|  | 	bool enabled; | ||||||
|  | 	uint32_t timestamp; | ||||||
|  | 	uint16_t seq; | ||||||
|  |  | ||||||
|  | 	struct rtp_provider_instance *rtp_prov_inst; | ||||||
|  |  | ||||||
|  | 	/* transmit buffer for outgoing messages */ | ||||||
|  | 	uint8_t *buf; | ||||||
|  | 	/* used part of buffer */ | ||||||
|  | 	size_t buf_len; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* RX side state of RTP connection/socket */ | ||||||
|  | struct rtpsim_connection_rx { | ||||||
|  | 	bool enabled; | ||||||
|  | 	/* receive buffer for incoming messages */ | ||||||
|  | 	uint8_t *buf; | ||||||
|  | 	/* used length of buffer */ | ||||||
|  | 	size_t buf_len; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct rtpsim_instance; | ||||||
|  |  | ||||||
|  | /* One RTP connection/socket */ | ||||||
|  | struct rtpsim_connection { | ||||||
|  | 	/* index in rtp_instance.connections */ | ||||||
|  | 	unsigned int idx; | ||||||
|  | 	/* back-pointer */ | ||||||
|  | 	struct rtpsim_instance *inst; | ||||||
|  |  | ||||||
|  | 	struct rtpsim_connection_cfg cfg; | ||||||
|  | 	struct rtpsim_connection_tx tx; | ||||||
|  | 	struct rtpsim_connection_rx rx; | ||||||
|  | 	struct rate_ctr_group *ctrg; | ||||||
|  |  | ||||||
|  | 	/* socket file descriptor */ | ||||||
|  | 	int fd; | ||||||
|  | 	char *cname; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct rtpsim_instance_cfg { | ||||||
|  | 	int num; | ||||||
|  | 	uint16_t base_port; | ||||||
|  | 	unsigned int num_flows; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* one instance of the RTP simulator; typically one per worker thread */ | ||||||
|  | struct rtpsim_instance { | ||||||
|  | 	/* element in application global list of instances */ | ||||||
|  | 	struct llist_head list; | ||||||
|  | 	struct rtpsim_instance_cfg cfg; | ||||||
|  | 	/* per-instance io_uring */ | ||||||
|  | 	struct io_uring ring; | ||||||
|  | 	/* per-instance timerfd */ | ||||||
|  | 	int timerfd; | ||||||
|  | 	/* counter group of per-instance counters */ | ||||||
|  | 	struct rate_ctr_group *ctrg; | ||||||
|  |  | ||||||
|  | 	struct rtpsim_connection **connections; | ||||||
|  | 	/* size of 'connections' in number of pointers */ | ||||||
|  | 	unsigned int connections_size; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct rtpsim_global { | ||||||
|  | 	/* global list of instances */ | ||||||
|  | 	struct llist_head instances; | ||||||
|  | 	pthread_rwlock_t rwlock; | ||||||
|  |  | ||||||
|  | 	struct ctrl_handle *ctrl; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | enum { | ||||||
|  | 	DMAIN, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum codec_type; | ||||||
|  |  | ||||||
|  | extern struct rtpsim_global *g_rtpsim; | ||||||
|  |  | ||||||
|  | struct rtpsim_connection *rtpsim_conn_find(struct rtpsim_instance *ri, const char *cname); | ||||||
|  | struct rtpsim_connection *rtpsim_conn_reserve(struct rtpsim_instance *ri, const char *cname, enum codec_type codec); | ||||||
|  | int rtpsim_conn_start(struct rtpsim_connection *rtpc); | ||||||
|  | void rtpsim_conn_stop(struct rtpsim_connection *rtpc); | ||||||
|  | void rtpsim_conn_unreserve(struct rtpsim_connection *rtpc); | ||||||
|  | int rtpsim_conn_connect(struct rtpsim_connection *rtpc); | ||||||
|  |  | ||||||
|  | int rtpsource_ctrl_cmds_install(void); | ||||||
							
								
								
									
										560
									
								
								contrib/rtp-load-gen/rtp-load-gen.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										560
									
								
								contrib/rtp-load-gen/rtp-load-gen.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,560 @@ | |||||||
|  | #include <liburing.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <pthread.h> | ||||||
|  | #include <sys/timerfd.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/core/talloc.h> | ||||||
|  | #include <osmocom/core/utils.h> | ||||||
|  | #include <osmocom/core/linuxlist.h> | ||||||
|  | #include <osmocom/core/sockaddr_str.h> | ||||||
|  | #include <osmocom/core/socket.h> | ||||||
|  | #include <osmocom/core/rate_ctr.h> | ||||||
|  | #include <osmocom/core/application.h> | ||||||
|  | #include <osmocom/core/stats.h> | ||||||
|  | #include <osmocom/netif/rtp.h> | ||||||
|  |  | ||||||
|  | #include "internal.h" | ||||||
|  | #include "rtp_provider.h" | ||||||
|  | #include "internal.h" | ||||||
|  |  | ||||||
|  | /* use a separate rx-completion thread: submit from main, reap from completion */ | ||||||
|  | //#define USE_CQ_THREAD | ||||||
|  |  | ||||||
|  | /* use registered files: Doesn't seem to work with sockets? */ | ||||||
|  | //#define USE_REGISTERED_FILES | ||||||
|  |  | ||||||
|  | /* use registered buffers (mapped once into kernel, rather than at every write */ | ||||||
|  | #define USE_REGISTERED_BUFFERS | ||||||
|  |  | ||||||
|  | /* number of sockets/flows to create */ | ||||||
|  | #define NUM_FLOWS	4096 | ||||||
|  |  | ||||||
|  | /* number of workers to spawn.  Each worker will get an equal share of NR_FLOWS to work on */ | ||||||
|  | #define NR_WORKERS	4 | ||||||
|  |  | ||||||
|  | /* size of rx/tx buffer for one RTP frame */ | ||||||
|  | #define BUF_SIZE	256 | ||||||
|  |  | ||||||
|  | #define NUM_FLOWS_PER_WORKER	(NUM_FLOWS/NR_WORKERS) | ||||||
|  |  | ||||||
|  | #define TX_BUF_IDX	0 | ||||||
|  | #define RX_BUF_IDX	1 | ||||||
|  |  | ||||||
|  | struct rtpsim_global *g_rtpsim; | ||||||
|  |  | ||||||
|  | enum rtpsim_conn_ctr { | ||||||
|  | 	RTP_CONN_CTR_TX_PKTS, | ||||||
|  | 	RTP_CONN_CTR_TX_BYTES, | ||||||
|  | 	RTP_CONN_CTR_RX_PKTS, | ||||||
|  | 	RTP_CONN_CTR_RX_BYTES, | ||||||
|  | 	RTP_CONN_CTR_RX_INVALID, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct rate_ctr_desc rtpsim_conn_ctrs[] = { | ||||||
|  | 	[RTP_CONN_CTR_TX_PKTS] = { "tx_pkts:total", "Transmitted packets" }, | ||||||
|  | 	[RTP_CONN_CTR_TX_BYTES] = { "tx_bytes:total", "Transmitted bytes" }, | ||||||
|  | 	[RTP_CONN_CTR_RX_PKTS] = { "rx_pkts:total", "Received packets (total)" }, | ||||||
|  | 	[RTP_CONN_CTR_RX_BYTES] = { "rx_bytes:total", "Transmitted bytes" }, | ||||||
|  | 	[RTP_CONN_CTR_RX_INVALID] = { "rx_pkts:invalid", "Received packets (invalidl)" }, | ||||||
|  | }; | ||||||
|  | static const struct rate_ctr_group_desc rtpsim_conn_ctrg_desc = { | ||||||
|  | 	.group_name_prefix = "rtpsim_conn", | ||||||
|  | 	.group_description = "RTP Simulator Connection", | ||||||
|  | 	.class_id = 0, | ||||||
|  | 	.num_ctr = ARRAY_SIZE(rtpsim_conn_ctrs), | ||||||
|  | 	.ctr_desc = rtpsim_conn_ctrs, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum rtpsim_ctr { | ||||||
|  | 	RTP_INST_TIMERS_TOTAL, | ||||||
|  | 	RTP_INST_TIMERS_LATE, | ||||||
|  | }; | ||||||
|  | static const struct rate_ctr_desc rtpsim_ctrs[] = { | ||||||
|  | 	[RTP_INST_TIMERS_TOTAL] = { "timers:total", "Timers expiring (total)" }, | ||||||
|  | 	[RTP_INST_TIMERS_LATE] = { "timers:late", "Timers expiring (late)" }, | ||||||
|  | }; | ||||||
|  | static const struct rate_ctr_group_desc rtpsim_ctrg_desc = { | ||||||
|  | 	.group_name_prefix = "rtpsim", | ||||||
|  | 	.group_description = "RTP Simulator Instance", | ||||||
|  | 	.class_id = 0, | ||||||
|  | 	.num_ctr = ARRAY_SIZE(rtpsim_ctrs), | ||||||
|  | 	.ctr_desc = rtpsim_ctrs, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct rtpsim_instance *rtpsim_instance_init(void *ctx, const struct rtpsim_instance_cfg *rmp) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_instance *ri = talloc_zero(ctx, struct rtpsim_instance); | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	if (!ri) | ||||||
|  | 		return NULL; | ||||||
|  | 	ri->connections_size = NUM_FLOWS_PER_WORKER; | ||||||
|  | 	ri->connections = talloc_zero_size(ri, sizeof(struct rtpsim_connection *)*ri->connections_size); | ||||||
|  | 	if (!ri->connections) { | ||||||
|  | 		talloc_free(ri); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ri->cfg = *rmp; | ||||||
|  | 	rc = io_uring_queue_init(NUM_FLOWS_PER_WORKER*2, &ri->ring, 0); | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		talloc_free(ri); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	ri->ctrg = rate_ctr_group_alloc(ri, &rtpsim_ctrg_desc, rmp->num); | ||||||
|  | 	OSMO_ASSERT(ri->ctrg); | ||||||
|  | 	return ri; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int rtpsim_instance_conn_add(struct rtpsim_instance *ri, struct rtpsim_connection *rtpc) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ri->connections_size; i++) { | ||||||
|  | 		if (ri->connections[i] == NULL) { | ||||||
|  | 			ri->connections[i] = rtpc; | ||||||
|  | 			rtpc->idx = i; | ||||||
|  | 			return i; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return -ENOSPC; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct rtpsim_connection * | ||||||
|  | rtpsim_conn_open_bind(struct rtpsim_instance *ri, const struct rtpsim_connection_cfg *rcfg) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_connection *rtpc = talloc_zero(ri, struct rtpsim_connection); | ||||||
|  | 	struct osmo_sockaddr sa_local; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	if (!rtpc) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	rtpc->inst = ri; | ||||||
|  | 	rtpc->cfg = *rcfg; | ||||||
|  |  | ||||||
|  | 	osmo_sockaddr_str_to_sockaddr(&rtpc->cfg.local, &sa_local.u.sas); | ||||||
|  |  | ||||||
|  | 	rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &sa_local, NULL, OSMO_SOCK_F_BIND); | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		talloc_free(rtpc); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rtpc->fd = rc; | ||||||
|  | 	rtpc->ctrg = rate_ctr_group_alloc(rtpc, &rtpsim_conn_ctrg_desc, rtpc->cfg.local.port); | ||||||
|  | 	OSMO_ASSERT(rtpc->ctrg); | ||||||
|  | #ifndef USE_REGISTERED_BUFFERS | ||||||
|  | 	rtpc->tx.buf = talloc_zero_size(rtpc, BUF_SIZE); | ||||||
|  | 	rtpc->rx.buf = talloc_zero_size(rtpc, BUF_SIZE); | ||||||
|  | #endif | ||||||
|  | 	OSMO_ASSERT(rtpsim_instance_conn_add(ri, rtpc) >= 0); | ||||||
|  |  | ||||||
|  | 	return rtpc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* find a connection for given cname (may be NULL to find unused connection) */ | ||||||
|  | struct rtpsim_connection *rtpsim_conn_find(struct rtpsim_instance *ri, const char *cname) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ri->connections_size; i++) { | ||||||
|  | 		struct rtpsim_connection *rtpc = ri->connections[i]; | ||||||
|  | 		if (!rtpc) | ||||||
|  | 			continue; | ||||||
|  | 		if (!rtpc->cname) { | ||||||
|  | 			if (!cname) | ||||||
|  | 				return rtpc; | ||||||
|  | 		} else { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		if (!strcmp(rtpc->cname, cname)) | ||||||
|  | 			return rtpc; | ||||||
|  | 	} | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* reserve a connection; associates cname with it */ | ||||||
|  | struct rtpsim_connection *rtpsim_conn_reserve(struct rtpsim_instance *ri, const char *cname, | ||||||
|  | 					      enum codec_type codec) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_connection *rtpc; | ||||||
|  | 	const struct rtp_provider *rtp_prov; | ||||||
|  |  | ||||||
|  | 	rtp_prov = rtp_provider_find("static"); // TODO: configurable */ | ||||||
|  | 	OSMO_ASSERT(rtp_prov); | ||||||
|  |  | ||||||
|  | 	rtpc = rtpsim_conn_find(ri, NULL); | ||||||
|  | 	if (!rtpc) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	/* this is called from main thread, we cannot use per-thread talloc contexts | ||||||
|  | 	 * such as ri or rtpc */ | ||||||
|  | 	rtpc->cname = talloc_strdup(g_rtpsim, cname); | ||||||
|  | 	rtpc->tx.rtp_prov_inst = rtp_provider_instance_alloc(g_rtpsim, rtp_prov, codec); | ||||||
|  | 	OSMO_ASSERT(rtpc->tx.rtp_prov_inst); | ||||||
|  |  | ||||||
|  | 	/* re-start from zero transmit sequence number */ | ||||||
|  | 	rtpc->tx.seq = 0; | ||||||
|  |  | ||||||
|  | 	return rtpc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int rtpsim_conn_start(struct rtpsim_connection *rtpc) | ||||||
|  | { | ||||||
|  | 	rtpc->tx.enabled = true; | ||||||
|  | 	rtpc->rx.enabled = true; | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void rtpsim_conn_stop(struct rtpsim_connection *rtpc) | ||||||
|  | { | ||||||
|  | 	/* disable Rx and Tx */ | ||||||
|  | 	rtpc->tx.enabled = false; | ||||||
|  | 	rtpc->rx.enabled = false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* unreserve a connection; stops all rx/tx and removes cname */ | ||||||
|  | void rtpsim_conn_unreserve(struct rtpsim_connection *rtpc) | ||||||
|  | { | ||||||
|  | 	rtp_provider_instance_free(rtpc->tx.rtp_prov_inst); | ||||||
|  | 	rtpc->tx.rtp_prov_inst = NULL; | ||||||
|  |  | ||||||
|  | 	talloc_free(rtpc->cname); | ||||||
|  | 	rtpc->cname = NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* connect a RTP connection to its remote peer (as in rtpc->cfg.remote) */ | ||||||
|  | int rtpsim_conn_connect(struct rtpsim_connection *rtpc) | ||||||
|  | { | ||||||
|  | 	struct osmo_sockaddr sa_remote; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	osmo_sockaddr_str_to_sockaddr(&rtpc->cfg.remote, &sa_remote.u.sas); | ||||||
|  | 	rc = connect(rtpc->fd, &sa_remote.u.sa, sizeof(struct osmo_sockaddr)); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* transmit one RTP frame for given connection */ | ||||||
|  | static int rtpsim_conn_tx_frame(struct rtpsim_connection *rtpc) | ||||||
|  | { | ||||||
|  | 	struct rtp_hdr *rtph = (struct rtp_hdr *) rtpc->tx.buf; | ||||||
|  | 	struct io_uring_sqe *sqe; | ||||||
|  | 	uint8_t *payload; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	rtph->version = RTP_VERSION; | ||||||
|  | 	rtph->padding = 0; | ||||||
|  | 	rtph->extension = 0; | ||||||
|  | 	rtph->csrc_count = 0; | ||||||
|  | 	rtph->marker = 0; | ||||||
|  | 	rtph->payload_type = rtpc->cfg.pt; | ||||||
|  | 	rtph->sequence = htons(rtpc->tx.seq++); | ||||||
|  | 	rtph->timestamp = htonl(rtpc->tx.timestamp); | ||||||
|  | 	rtpc->tx.timestamp += rtpc->cfg.duration; | ||||||
|  | 	rtph->ssrc = htonl(rtpc->cfg.ssrc); | ||||||
|  | 	payload = rtpc->tx.buf + sizeof(*rtph); | ||||||
|  | 	/* add payload data */ | ||||||
|  |  | ||||||
|  | 	rc = rtp_provider_instance_gen_frame(rtpc->tx.rtp_prov_inst, payload, BUF_SIZE-sizeof(*rtph)); | ||||||
|  | 	OSMO_ASSERT(rc >= 0); | ||||||
|  | 	rtpc->tx.buf_len = sizeof(*rtph) + rc; | ||||||
|  |  | ||||||
|  | 	sqe = io_uring_get_sqe(&rtpc->inst->ring); | ||||||
|  | 	OSMO_ASSERT(sqe); | ||||||
|  | 	sqe->user_data = rtpc->idx; | ||||||
|  | #ifdef USE_REGISTERED_FILES | ||||||
|  | 	io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE); | ||||||
|  | #ifdef USE_REGISTERED_BUFFERS | ||||||
|  | 	io_uring_prep_write_fixed(sqe, rtpc->idx, rtpc->tx.buf, rtpc->tx.buf_len, 0, TX_BUF_IDX); | ||||||
|  | #else | ||||||
|  | 	io_uring_prep_write(sqe, rtpc->idx, rtpc->tx.buf, rtpc->tx.buf_len, 0); | ||||||
|  | #endif | ||||||
|  | #else /* REGISTERED FILES */ | ||||||
|  | #ifdef USE_REGISTERED_BUFFERS | ||||||
|  | 	io_uring_prep_write_fixed(sqe, rtpc->fd, rtpc->tx.buf, rtpc->tx.buf_len, 0, TX_BUF_IDX); | ||||||
|  | #else | ||||||
|  | 	io_uring_prep_write(sqe, rtpc->fd, rtpc->tx.buf, rtpc->tx.buf_len, 0); | ||||||
|  | #endif | ||||||
|  | #endif /* REGISTERED_FILES */ | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* submit RX buffer for a RTP frame on given connection */ | ||||||
|  | static int rtpsim_conn_rx_prep(struct rtpsim_connection *rtpc) | ||||||
|  | { | ||||||
|  | 	struct io_uring_sqe *sqe; | ||||||
|  |  | ||||||
|  | 	sqe = io_uring_get_sqe(&rtpc->inst->ring); | ||||||
|  | 	OSMO_ASSERT(sqe); | ||||||
|  | 	sqe->user_data = 0x8000000 | rtpc->idx; | ||||||
|  | #ifdef USE_REGISTERED_FILES | ||||||
|  | 	io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE); | ||||||
|  | 	/* FIXME */ | ||||||
|  | #else /* REGISTERED FILES */ | ||||||
|  | #ifdef USE_REGISTERED_BUFFERS | ||||||
|  | 	io_uring_prep_read_fixed(sqe, rtpc->fd, rtpc->rx.buf, BUF_SIZE, 0, RX_BUF_IDX); | ||||||
|  | #else | ||||||
|  | 	io_uring_prep_read(sqe, rtpc->fd, rtpc->rx.buf, BUF_SIZE, 0); | ||||||
|  | #endif | ||||||
|  | #endif /* REGISTERED_FILES */ | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* process one completion entry */ | ||||||
|  | static void handle_completion(struct rtpsim_instance *ri, struct io_uring_cqe *cqe) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_connection *rtpc = ri->connections[cqe->user_data & 0x7fffffff]; | ||||||
|  | 	OSMO_ASSERT(rtpc); | ||||||
|  |  | ||||||
|  | 	if (cqe->user_data & 0x80000000) { | ||||||
|  | 		/* read */ | ||||||
|  | 		rate_ctr_inc(&rtpc->ctrg->ctr[RTP_CONN_CTR_RX_PKTS]); | ||||||
|  | 		rate_ctr_add(&rtpc->ctrg->ctr[RTP_CONN_CTR_RX_BYTES], cqe->res); | ||||||
|  | 		OSMO_ASSERT(cqe->res >= 0); | ||||||
|  | 	} else { | ||||||
|  | 		/* write */ | ||||||
|  | 		rate_ctr_inc(&rtpc->ctrg->ctr[RTP_CONN_CTR_TX_PKTS]); | ||||||
|  | 		rate_ctr_add(&rtpc->ctrg->ctr[RTP_CONN_CTR_TX_BYTES], cqe->res); | ||||||
|  | 		OSMO_ASSERT(cqe->res == sizeof(struct rtp_hdr) + 33); | ||||||
|  | 	} | ||||||
|  | 	io_uring_cqe_seen(&ri->ring, cqe); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef USE_CQ_THREAD | ||||||
|  | /* 'main' function for separate completion queue reaping thread */ | ||||||
|  | static void *reap_completion(void *_ri) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_instance *ri = _ri; | ||||||
|  | 	while (1) { | ||||||
|  | 		struct io_uring_cqe *cqe; | ||||||
|  | 		int rc; | ||||||
|  |  | ||||||
|  | 		rc = io_uring_wait_cqe(&ri->ring, &cqe); | ||||||
|  | 		OSMO_ASSERT(rc >= 0); | ||||||
|  | 		handle_completion(ri, cqe); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | extern int osmo_ctx_init(const char *id); | ||||||
|  |  | ||||||
|  | static void rtpsim_main(const struct rtpsim_instance_cfg *rmp) | ||||||
|  | { | ||||||
|  | 	struct rtpsim_instance *ri; | ||||||
|  | 	struct rtpsim_connection *rtpc; | ||||||
|  | 	int i, rc; | ||||||
|  |  | ||||||
|  | 	char namebuf[32]; | ||||||
|  | 	snprintf(namebuf, sizeof(namebuf), "rtpsim_worker%d", rmp->num); | ||||||
|  | 	osmo_ctx_init(namebuf); | ||||||
|  |  | ||||||
|  | 	ri = rtpsim_instance_init(OTC_GLOBAL, rmp); | ||||||
|  | 	OSMO_ASSERT(ri); | ||||||
|  |  | ||||||
|  | 	pthread_rwlock_wrlock(&g_rtpsim->rwlock); | ||||||
|  | 	llist_add_tail(&ri->list, &g_rtpsim->instances); | ||||||
|  | 	pthread_rwlock_unlock(&g_rtpsim->rwlock); | ||||||
|  |  | ||||||
|  | 	/* create desired number of sockets */ | ||||||
|  | 	printf("binding sockets\n"); | ||||||
|  | 	for (i = 0; i < rmp->num_flows; i++) { | ||||||
|  | 		struct rtpsim_connection *rtpc; | ||||||
|  | 		struct rtpsim_connection_cfg rcfg = {}; | ||||||
|  | 		rcfg.local = (struct osmo_sockaddr_str) { | ||||||
|  | 			.af = AF_INET, | ||||||
|  | 			.ip = "127.0.0.1", | ||||||
|  | 			.port = rmp->base_port + 2*i, | ||||||
|  | 		}; | ||||||
|  | 		rcfg.remote = (struct osmo_sockaddr_str) { | ||||||
|  | 			.af = AF_INET, | ||||||
|  | 			.ip = "127.0.0.1", | ||||||
|  | 			.port = rmp->base_port + 2*i, | ||||||
|  | 		}; | ||||||
|  | 		rcfg.pt = 3; | ||||||
|  | 		rcfg.ssrc = 0x80000000 + rmp->base_port + i; | ||||||
|  | 		rcfg.duration = 160;	/* 8000 Hz sampling rate / 50 Hz RTP rate */ | ||||||
|  |  | ||||||
|  | 		rtpc = rtpsim_conn_open_bind(ri, &rcfg); | ||||||
|  | 		OSMO_ASSERT(rtpc); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | #if 1 | ||||||
|  | 	/* HACK */ | ||||||
|  | 	printf("connecting sockets\n"); | ||||||
|  | 	for (i = 0; i < rmp->num_flows; i++) { | ||||||
|  | 		char namebuf[32]; | ||||||
|  | 		snprintf(namebuf, sizeof(namebuf), "conn%d", i); | ||||||
|  | 		struct rtpsim_connection *rtpc = rtpsim_conn_reserve(ri, namebuf, CODEC_GSM_FR); | ||||||
|  | 		OSMO_ASSERT(rtpc); | ||||||
|  | 		OSMO_ASSERT(rtpsim_conn_connect(rtpc) == 0); | ||||||
|  | 		OSMO_ASSERT(rtpsim_conn_start(rtpc) == 0); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_REGISTERED_FILES | ||||||
|  | 	/* register all our file descriptors; seems to fail on 5.8.x ? */ | ||||||
|  | 	int fds[NUM_FLOWS_PER_WORKER]; | ||||||
|  | 	for (i = 0; i < ri->connections_size; i++) { | ||||||
|  | 		if (!rtpc) { | ||||||
|  | 			fds[i] = -1; | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		rtpc = ri->connections[i]; | ||||||
|  | 		fds[i] = rtpc->fd; | ||||||
|  | 	} | ||||||
|  | 	printf("Registering %d files\n", i); | ||||||
|  | 	rc = io_uring_register_files(&ri->ring, fds, i); | ||||||
|  | 	printf("rc = %d: %s\n", rc, strerror(-rc)); | ||||||
|  | 	OSMO_ASSERT(rc == 0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_REGISTERED_BUFFERS | ||||||
|  | 	/* register two large buffers for Rx and Tx; assign per-connection | ||||||
|  | 	 * buffers within those two registered buffers */ | ||||||
|  | 	void *largebuf_tx = talloc_zero_size(ri, rmp->num_flows * BUF_SIZE); | ||||||
|  | 	void *largebuf_rx = talloc_zero_size(ri, rmp->num_flows * BUF_SIZE); | ||||||
|  | 	struct iovec iov[2] = { | ||||||
|  | 		[TX_BUF_IDX] = { | ||||||
|  | 			.iov_base = largebuf_tx, | ||||||
|  | 			.iov_len = rmp->num_flows * BUF_SIZE, | ||||||
|  | 		}, | ||||||
|  | 		[RX_BUF_IDX] = { | ||||||
|  | 			.iov_base = largebuf_rx, | ||||||
|  | 			.iov_len = rmp->num_flows * BUF_SIZE, | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 	printf("Registering buffers for %d sockets\n", i); | ||||||
|  | 	rc = io_uring_register_buffers(&ri->ring, iov, ARRAY_SIZE(iov)); | ||||||
|  | 	printf("rc = %d: %s\n", rc, strerror(-rc)); | ||||||
|  | 	OSMO_ASSERT(rc == 0); | ||||||
|  | 	for (i = 0; i < ri->connections_size; i++) { | ||||||
|  | 		rtpc = ri->connections[i]; | ||||||
|  | 		if (!rtpc) | ||||||
|  | 			continue; | ||||||
|  | 		rtpc->tx.buf = largebuf_tx + (i * BUF_SIZE); | ||||||
|  | 		rtpc->rx.buf = largebuf_rx + (i * BUF_SIZE); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_CQ_THREAD | ||||||
|  | 	/* start a separate completion thread instead of handling completions in-line */ | ||||||
|  | 	pthread_t complete; | ||||||
|  | 	rc = pthread_create(&complete, NULL, reap_completion, ri); | ||||||
|  | 	OSMO_ASSERT(rc >= 0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	/* start timerfd every 20ms */ | ||||||
|  | 	ri->timerfd = timerfd_create(CLOCK_MONOTONIC, 0); | ||||||
|  | 	OSMO_ASSERT(ri->timerfd >= 0); | ||||||
|  | 	struct itimerspec its = (struct itimerspec) { | ||||||
|  | 		.it_interval = { 0, 20*1000*1000 }, | ||||||
|  | 		.it_value = { 0, 20*1000*1000 }, | ||||||
|  | 	}; | ||||||
|  | 	rc = timerfd_settime(ri->timerfd, 0, &its, NULL); | ||||||
|  | 	OSMO_ASSERT(rc == 0); | ||||||
|  |  | ||||||
|  | 	/* start transmitting */ | ||||||
|  |  | ||||||
|  | 	while (1) { | ||||||
|  | 		/* the assumption here is that every flow wants to write 50 | ||||||
|  | 		 * packets per second, so we try try to submit one write for each | ||||||
|  | 		 * flow every 20ms */ | ||||||
|  | 		unsigned int submit_granularity = rmp->num_flows/50; | ||||||
|  | 		/* number of 20ms timer expirations */ | ||||||
|  | 		uint64_t num_exp; | ||||||
|  | 		unsigned int t; | ||||||
|  |  | ||||||
|  | 		if (submit_granularity <= 0) | ||||||
|  | 			submit_granularity = 1; | ||||||
|  |  | ||||||
|  | 		/* read from timerfd to pace the 20ms inter packet interval */ | ||||||
|  | 		rc = read(ri->timerfd, &num_exp, sizeof(num_exp)); | ||||||
|  | 		OSMO_ASSERT(rc == sizeof(num_exp)); | ||||||
|  |  | ||||||
|  | 		rate_ctr_add(&ri->ctrg->ctr[RTP_INST_TIMERS_TOTAL], num_exp); | ||||||
|  | 		if (num_exp != 1) { | ||||||
|  | 			fputc('X', stdout); | ||||||
|  | 			rate_ctr_add(&ri->ctrg->ctr[RTP_INST_TIMERS_LATE], num_exp-1); | ||||||
|  | 		} else { | ||||||
|  | 			fputc('.', stdout); | ||||||
|  | 		} | ||||||
|  | 		fflush(stdout); | ||||||
|  |  | ||||||
|  | 		for (t = 0; t < num_exp; t++) { | ||||||
|  | 			for (i = 0; i < ri->connections_size; i++) { | ||||||
|  | 				rtpc = ri->connections[i]; | ||||||
|  | 				if (!rtpc) | ||||||
|  | 					continue; | ||||||
|  | 				if (rtpc->tx.enabled) | ||||||
|  | 					rtpsim_conn_tx_frame(rtpc); | ||||||
|  | 				if (rtpc->rx.enabled) | ||||||
|  | 					rtpsim_conn_rx_prep(rtpc); | ||||||
|  | 				if ((i % submit_granularity) == 0) { | ||||||
|  | 					int pending = io_uring_submit(&ri->ring); | ||||||
|  |  | ||||||
|  | #ifndef USE_CQ_THREAD | ||||||
|  | 					for (int j = 0; j < pending; j++) { | ||||||
|  | 						struct io_uring_cqe *cqe; | ||||||
|  | 						int rc; | ||||||
|  |  | ||||||
|  | 						rc = io_uring_wait_cqe(&ri->ring, &cqe); | ||||||
|  | 						OSMO_ASSERT(rc >= 0); | ||||||
|  | 						handle_completion(ri, cqe); | ||||||
|  | 					} | ||||||
|  | #endif /* USE_CQ_THREAD */ | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void *rtpsim_worker_thread(void *_rmp) | ||||||
|  | { | ||||||
|  | 	rtpsim_main((struct rtpsim_instance_cfg *)_rmp); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	pthread_t worker[NR_WORKERS]; | ||||||
|  | 	struct rtpsim_instance_cfg rmp[NR_WORKERS]; | ||||||
|  | 	int i, rc; | ||||||
|  |  | ||||||
|  | 	g_rtpsim = talloc_zero(NULL, struct rtpsim_global); | ||||||
|  | 	OSMO_ASSERT(g_rtpsim); | ||||||
|  | 	INIT_LLIST_HEAD(&g_rtpsim->instances); | ||||||
|  | 	pthread_rwlock_init(&g_rtpsim->rwlock, NULL); | ||||||
|  |  | ||||||
|  | 	osmo_init_logging2(g_rtpsim, NULL); | ||||||
|  | //	osmo_stats_init(g_rtpsim); | ||||||
|  |  | ||||||
|  | 	/* Create worker threads */ | ||||||
|  | 	for (i = 0; i < NR_WORKERS; i++) { | ||||||
|  | 		int rc; | ||||||
|  | 		rmp[i].num = i; | ||||||
|  | 		rmp[i].num_flows = NUM_FLOWS_PER_WORKER; | ||||||
|  | 		rmp[i].base_port = 10000 + i * (2 * rmp[i].num_flows); | ||||||
|  | 		rc = pthread_create(&worker[i], NULL, rtpsim_worker_thread, &rmp[i]); | ||||||
|  | 		OSMO_ASSERT(rc >= 0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* CTRL interface */ | ||||||
|  | 	//g_rtpsim->ctrl = ctrl_interface_setup_dynip(g_rss, ctrl_vty_get_bind_addr(), 11111, NULL); | ||||||
|  | 	g_rtpsim->ctrl = ctrl_interface_setup_dynip(g_rtpsim, "127.0.0.1", 11111, NULL); | ||||||
|  | 	OSMO_ASSERT(g_rtpsim->ctrl); | ||||||
|  | 	rc = rtpsource_ctrl_cmds_install(); | ||||||
|  | 	OSMO_ASSERT(rc == 0); | ||||||
|  |  | ||||||
|  | 	while (1) { | ||||||
|  | 		osmo_select_main(0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < NR_WORKERS; i++) { | ||||||
|  | 		pthread_join(worker[i], NULL); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										71
									
								
								contrib/rtp-load-gen/rtp_provider.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								contrib/rtp-load-gen/rtp_provider.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | |||||||
|  | #include <osmocom/core/talloc.h> | ||||||
|  | #include <osmocom/core/utils.h> | ||||||
|  | #include <osmocom/core/linuxlist.h> | ||||||
|  | #include <osmocom/core/logging.h> | ||||||
|  |  | ||||||
|  | #include "rtp_provider.h" | ||||||
|  | #include "internal.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static LLIST_HEAD(g_providers); | ||||||
|  |  | ||||||
|  | const struct value_string codec_type_names[] = { | ||||||
|  | 	{ CODEC_ULAW,		"ULAW" }, | ||||||
|  | 	{ CODEC_ALAW,		"ALAW" }, | ||||||
|  | 	{ CODEC_GSM_FR,		"GSM_FR" }, | ||||||
|  | 	{ CODEC_GSM_EFR,	"GSM_EFR" }, | ||||||
|  | 	{ CODEC_GSM_HR,		"GSM_HR" }, | ||||||
|  | 	{ CODEC_AMR_4_75,	"AMR_4_75" }, | ||||||
|  | 	{ CODEC_AMR_5_15,	"AMR_5_15" }, | ||||||
|  | 	{ CODEC_AMR_5_90,	"AMR_5_90" }, | ||||||
|  | 	{ CODEC_AMR_6_70,	"AMR_6_70" }, | ||||||
|  | 	{ CODEC_AMR_7_40,	"AMR_7_40" }, | ||||||
|  | 	{ CODEC_AMR_7_95,	"AMR_7_95" }, | ||||||
|  | 	{ CODEC_AMR_10_2,	"AMR_10_2" }, | ||||||
|  | 	{ CODEC_AMR_12_2,	"AMR_12_2" }, | ||||||
|  | 	{ CODEC_AMR_SID,	"AMR_SID" }, | ||||||
|  | 	{ 0, NULL } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | void rtp_provider_register(struct rtp_provider *prov) | ||||||
|  | { | ||||||
|  | 	llist_add_tail(&prov->list, &g_providers); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const struct rtp_provider *rtp_provider_find(const char *name) | ||||||
|  | { | ||||||
|  | 	struct rtp_provider *p; | ||||||
|  | 	llist_for_each_entry(p, &g_providers, list) { | ||||||
|  | 		if (!strcmp(name, p->name)) | ||||||
|  | 			return p; | ||||||
|  | 	} | ||||||
|  | 	LOGP(DMAIN, LOGL_ERROR, "Couldn't find RTP provider '%s'\n", name); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct rtp_provider_instance * | ||||||
|  | rtp_provider_instance_alloc(void *ctx, const struct rtp_provider *provider, enum codec_type codec) | ||||||
|  | { | ||||||
|  | 	struct rtp_provider_instance *pi; | ||||||
|  |  | ||||||
|  | 	pi = talloc_zero(ctx, struct rtp_provider_instance); | ||||||
|  | 	if (!pi) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	pi->provider = provider; | ||||||
|  | 	pi->codec = codec; | ||||||
|  |  | ||||||
|  | 	return pi; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void rtp_provider_instance_free(struct rtp_provider_instance *pi) | ||||||
|  | { | ||||||
|  | 	llist_del(&pi->list); | ||||||
|  | 	talloc_free(pi); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int rtp_provider_instance_gen_frame(struct rtp_provider_instance *pi, uint8_t *out, size_t out_size) | ||||||
|  | { | ||||||
|  | 	OSMO_ASSERT(pi->provider); | ||||||
|  | 	return pi->provider->rtp_gen(pi, out, out_size); | ||||||
|  | } | ||||||
							
								
								
									
										57
									
								
								contrib/rtp-load-gen/rtp_provider.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								contrib/rtp-load-gen/rtp_provider.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <osmocom/core/linuxlist.h> | ||||||
|  | #include <osmocom/core/utils.h> | ||||||
|  |  | ||||||
|  | enum codec_type { | ||||||
|  | 	CODEC_ULAW, | ||||||
|  | 	CODEC_ALAW, | ||||||
|  | 	CODEC_GSM_FR, | ||||||
|  | 	CODEC_GSM_EFR, | ||||||
|  | 	CODEC_GSM_HR, | ||||||
|  | 	CODEC_AMR_4_75, | ||||||
|  | 	CODEC_AMR_5_15, | ||||||
|  | 	CODEC_AMR_5_90, | ||||||
|  | 	CODEC_AMR_6_70, | ||||||
|  | 	CODEC_AMR_7_40, | ||||||
|  | 	CODEC_AMR_7_95, | ||||||
|  | 	CODEC_AMR_10_2, | ||||||
|  | 	CODEC_AMR_12_2, | ||||||
|  | 	CODEC_AMR_SID, | ||||||
|  | 	_NUM_CODECS | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | extern const struct value_string codec_type_names[]; | ||||||
|  |  | ||||||
|  | struct rtp_provider_instance; | ||||||
|  |  | ||||||
|  | struct rtp_provider { | ||||||
|  | 	/* global list of RTP providers */ | ||||||
|  | 	struct llist_head list; | ||||||
|  | 	const char *name; | ||||||
|  |  | ||||||
|  | 	/* create/initialie a RTP provider with specified argument string */ | ||||||
|  | 	int (*setup)(struct rtp_provider_instance *inst, const char *arg); | ||||||
|  |  | ||||||
|  | 	/* generate the next RTP packet; return length in octests or negative on error */ | ||||||
|  | 	int (*rtp_gen)(struct rtp_provider_instance *inst, uint8_t *out, size_t out_size); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct rtp_provider_instance { | ||||||
|  | 	/* entry in global list of RTP provider instances */ | ||||||
|  | 	struct llist_head list; | ||||||
|  | 	/* pointer to provider of which we are an instance */ | ||||||
|  | 	const struct rtp_provider *provider; | ||||||
|  | 	/* codec payload we are to generate */ | ||||||
|  | 	enum codec_type codec; | ||||||
|  |  | ||||||
|  | 	/* private user data */ | ||||||
|  | 	void *priv; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | void rtp_provider_register(struct rtp_provider *prov); | ||||||
|  | const struct rtp_provider *rtp_provider_find(const char *name); | ||||||
|  |  | ||||||
|  | struct rtp_provider_instance *rtp_provider_instance_alloc(void *ctx, const struct rtp_provider *provider, enum codec_type codec); | ||||||
|  | void rtp_provider_instance_free(struct rtp_provider_instance *pi); | ||||||
|  | int rtp_provider_instance_gen_frame(struct rtp_provider_instance *pi, uint8_t *out, size_t out_size); | ||||||
							
								
								
									
										108
									
								
								contrib/rtp-load-gen/rtp_provider_static.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								contrib/rtp-load-gen/rtp_provider_static.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  |  | ||||||
|  | #include <errno.h> | ||||||
|  | #include <osmocom/codec/codec.h> | ||||||
|  | #include <osmocom/core/utils.h> | ||||||
|  | #include <osmocom/core/logging.h> | ||||||
|  |  | ||||||
|  | #include "rtp_provider.h" | ||||||
|  | #include "internal.h" | ||||||
|  |  | ||||||
|  | static struct rtp_provider static_provider; | ||||||
|  |  | ||||||
|  | static const uint8_t len4codec[_NUM_CODECS] = { | ||||||
|  |     [CODEC_ULAW] = 160, | ||||||
|  |     [CODEC_ALAW] = 160, | ||||||
|  |     [CODEC_GSM_FR] = GSM_FR_BYTES, | ||||||
|  |     [CODEC_GSM_EFR] = GSM_EFR_BYTES, | ||||||
|  |     [CODEC_GSM_HR] = GSM_HR_BYTES, | ||||||
|  |     [CODEC_AMR_4_75] = 12, | ||||||
|  |     [CODEC_AMR_5_15] = 13, | ||||||
|  |     [CODEC_AMR_5_90] = 15, | ||||||
|  |     [CODEC_AMR_6_70] = 17, | ||||||
|  |     [CODEC_AMR_7_40] = 19, | ||||||
|  |     [CODEC_AMR_7_95] = 20, | ||||||
|  |     [CODEC_AMR_10_2] = 26, | ||||||
|  |     [CODEC_AMR_12_2] = 31, | ||||||
|  |     [CODEC_AMR_SID] = 5, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* generate a static / fixed RTP payload of matching codec/mode */ | ||||||
|  | static int rtp_gen_static(struct rtp_provider_instance *pi, uint8_t *out, size_t out_size) | ||||||
|  | { | ||||||
|  | 	uint8_t len; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(pi->provider == &static_provider); | ||||||
|  |  | ||||||
|  | 	len = len4codec[pi->codec]; | ||||||
|  | 	if (out_size < len) { | ||||||
|  | 		LOGP(DMAIN, LOGL_ERROR, "out_size %zu < %u\n", out_size, len); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	memset(out, 0, len); | ||||||
|  |  | ||||||
|  | 	switch (pi->codec) { | ||||||
|  | 	case CODEC_ULAW: | ||||||
|  | 	case CODEC_ALAW: | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_GSM_FR: | ||||||
|  | 		out[0] = (out[0] & 0x0f) | 0xD0; /* mask in first four bit for FR */ | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_GSM_EFR: | ||||||
|  | 		out[0] = (out[0] & 0x0f) | 0xC0; /* mask in first four bit for EFR */ | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_GSM_HR: | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_AMR_4_75: | ||||||
|  | 		out[0] = 0 << 4; | ||||||
|  | 		out[1] = 0 << 3; | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_AMR_5_15: | ||||||
|  | 		out[0] = 1 << 4; | ||||||
|  | 		out[1] = 1 << 3; | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_AMR_5_90: | ||||||
|  | 		out[0] = 2 << 4; | ||||||
|  | 		out[1] = 2 << 3; | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_AMR_6_70: | ||||||
|  | 		out[0] = 3 << 4; | ||||||
|  | 		out[1] = 3 << 3; | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_AMR_7_40: | ||||||
|  | 		out[0] = 4 << 4; | ||||||
|  | 		out[1] = 4 << 3; | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_AMR_7_95: | ||||||
|  | 		out[0] = 5 << 4; | ||||||
|  | 		out[1] = 5 << 3; | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_AMR_10_2: | ||||||
|  | 		out[0] = 6 << 4; | ||||||
|  | 		out[1] = 6 << 3; | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_AMR_12_2: | ||||||
|  | 		out[0] = 7 << 4; | ||||||
|  | 		out[1] = 7 << 3; | ||||||
|  | 		break; | ||||||
|  | 	case CODEC_AMR_SID: | ||||||
|  | 		out[0] = 2 << 4; /* CMR: 5.90 */ | ||||||
|  | 		out[0] = 8 << 3; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		OSMO_ASSERT(0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static struct rtp_provider static_provider = { | ||||||
|  | 	.name = "static", | ||||||
|  | 	.rtp_gen = &rtp_gen_static, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void __attribute__((constructor)) rtp_provider_static_constr(void) | ||||||
|  | { | ||||||
|  | 	rtp_provider_register(&static_provider); | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								contrib/simcom2rtp/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contrib/simcom2rtp/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | CFLAGS:= -O2 -g -Wall $(shell pkg-config --cflags libosmocore libosmotrau) | ||||||
|  | LIBS:= $(shell pkg-config --libs libosmocore libosmotrau) | ||||||
|  |  | ||||||
|  | all: osmo-simcom2rtp | ||||||
|  |  | ||||||
|  | osmo-simcom2rtp: g711.o g711_table.o simcom2rtp.o | ||||||
|  | 	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) | ||||||
|  |  | ||||||
|  | %.o: %.c | ||||||
|  | 	$(CC) $(CFLAGS) -o $@ -c $^ | ||||||
|  |  | ||||||
							
								
								
									
										313
									
								
								contrib/simcom2rtp/g711.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								contrib/simcom2rtp/g711.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,313 @@ | |||||||
|  | /* | ||||||
|  |  * This source code is a product of Sun Microsystems, Inc. and is provided | ||||||
|  |  * for unrestricted use.  Users may copy or modify this source code without | ||||||
|  |  * charge. | ||||||
|  |  * | ||||||
|  |  * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING | ||||||
|  |  * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR | ||||||
|  |  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. | ||||||
|  |  * | ||||||
|  |  * Sun source code is provided with no support and without any obligation on | ||||||
|  |  * the part of Sun Microsystems, Inc. to assist in its use, correction, | ||||||
|  |  * modification or enhancement. | ||||||
|  |  * | ||||||
|  |  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE | ||||||
|  |  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE | ||||||
|  |  * OR ANY PART THEREOF. | ||||||
|  |  * | ||||||
|  |  * In no event will Sun Microsystems, Inc. be liable for any lost revenue | ||||||
|  |  * or profits or other special, indirect and consequential damages, even if | ||||||
|  |  * Sun has been advised of the possibility of such damages. | ||||||
|  |  * | ||||||
|  |  * Sun Microsystems, Inc. | ||||||
|  |  * 2550 Garcia Avenue | ||||||
|  |  * Mountain View, California  94043 | ||||||
|  |  */ | ||||||
|  | /* | ||||||
|  |  * December 30, 1994: | ||||||
|  |  * Functions linear2alaw, linear2ulaw have been updated to correctly | ||||||
|  |  * convert unquantized 16 bit values. | ||||||
|  |  * Tables for direct u- to A-law and A- to u-law conversions have been | ||||||
|  |  * corrected. | ||||||
|  |  * Borge Lindberg, Center for PersonKommunikation, Aalborg University. | ||||||
|  |  * bli@cpk.auc.dk | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | /* | ||||||
|  |  * Downloaded from comp.speech site in Cambridge. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "g711.h" | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * g711.c | ||||||
|  |  * | ||||||
|  |  * u-law, A-law and linear PCM conversions. | ||||||
|  |  * Source: http://www.speech.kth.se/cost250/refsys/latest/src/g711.c | ||||||
|  |  */ | ||||||
|  | #define	SIGN_BIT	(0x80)		/* Sign bit for a A-law byte. */ | ||||||
|  | #define	QUANT_MASK	(0xf)		/* Quantization field mask. */ | ||||||
|  | #define	NSEGS		(8)		/* Number of A-law segments. */ | ||||||
|  | #define	SEG_SHIFT	(4)		/* Left shift for segment number. */ | ||||||
|  | #define	SEG_MASK	(0x70)		/* Segment field mask. */ | ||||||
|  |  | ||||||
|  | static short seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF, | ||||||
|  | 			    0x1FF, 0x3FF, 0x7FF, 0xFFF}; | ||||||
|  | static short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF, | ||||||
|  | 			    0x3FF, 0x7FF, 0xFFF, 0x1FFF}; | ||||||
|  |  | ||||||
|  | /* copy from CCITT G.711 specifications */ | ||||||
|  | unsigned char _u2a[128] = {			/* u- to A-law conversions */ | ||||||
|  | 	1,	1,	2,	2,	3,	3,	4,	4, | ||||||
|  | 	5,	5,	6,	6,	7,	7,	8,	8, | ||||||
|  | 	9,	10,	11,	12,	13,	14,	15,	16, | ||||||
|  | 	17,	18,	19,	20,	21,	22,	23,	24, | ||||||
|  | 	25,	27,	29,	31,	33,	34,	35,	36, | ||||||
|  | 	37,	38,	39,	40,	41,	42,	43,	44, | ||||||
|  | 	46,	48,	49,	50,	51,	52,	53,	54, | ||||||
|  | 	55,	56,	57,	58,	59,	60,	61,	62, | ||||||
|  | 	64,	65,	66,	67,	68,	69,	70,	71, | ||||||
|  | 	72,	73,	74,	75,	76,	77,	78,	79, | ||||||
|  | /* corrected: | ||||||
|  | 	81,	82,	83,	84,	85,	86,	87,	88,  | ||||||
|  |    should be: */ | ||||||
|  | 	80,	82,	83,	84,	85,	86,	87,	88, | ||||||
|  | 	89,	90,	91,	92,	93,	94,	95,	96, | ||||||
|  | 	97,	98,	99,	100,	101,	102,	103,	104, | ||||||
|  | 	105,	106,	107,	108,	109,	110,	111,	112, | ||||||
|  | 	113,	114,	115,	116,	117,	118,	119,	120, | ||||||
|  | 	121,	122,	123,	124,	125,	126,	127,	128}; | ||||||
|  |  | ||||||
|  | unsigned char _a2u[128] = {			/* A- to u-law conversions */ | ||||||
|  | 	1,	3,	5,	7,	9,	11,	13,	15, | ||||||
|  | 	16,	17,	18,	19,	20,	21,	22,	23, | ||||||
|  | 	24,	25,	26,	27,	28,	29,	30,	31, | ||||||
|  | 	32,	32,	33,	33,	34,	34,	35,	35, | ||||||
|  | 	36,	37,	38,	39,	40,	41,	42,	43, | ||||||
|  | 	44,	45,	46,	47,	48,	48,	49,	49, | ||||||
|  | 	50,	51,	52,	53,	54,	55,	56,	57, | ||||||
|  | 	58,	59,	60,	61,	62,	63,	64,	64, | ||||||
|  | 	65,	66,	67,	68,	69,	70,	71,	72, | ||||||
|  | /* corrected: | ||||||
|  | 	73,	74,	75,	76,	77,	78,	79,	79, | ||||||
|  |    should be: */ | ||||||
|  | 	73,	74,	75,	76,	77,	78,	79,	80, | ||||||
|  | 	80,	81,	82,	83,	84,	85,	86,	87, | ||||||
|  | 	88,	89,	90,	91,	92,	93,	94,	95, | ||||||
|  | 	96,	97,	98,	99,	100,	101,	102,	103, | ||||||
|  | 	104,	105,	106,	107,	108,	109,	110,	111, | ||||||
|  | 	112,	113,	114,	115,	116,	117,	118,	119, | ||||||
|  | 	120,	121,	122,	123,	124,	125,	126,	127}; | ||||||
|  |  | ||||||
|  | static short search( | ||||||
|  |    short val, | ||||||
|  |    short *table, | ||||||
|  |    short size) | ||||||
|  | { | ||||||
|  |    short i; | ||||||
|  |     | ||||||
|  |    for (i = 0; i < size; i++) { | ||||||
|  |       if (val <= *table++) | ||||||
|  | 	 return (i); | ||||||
|  |    } | ||||||
|  |    return (size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law | ||||||
|  |  * | ||||||
|  |  * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. | ||||||
|  |  * | ||||||
|  |  *		Linear Input Code	Compressed Code | ||||||
|  |  *	------------------------	--------------- | ||||||
|  |  *	0000000wxyza			000wxyz | ||||||
|  |  *	0000001wxyza			001wxyz | ||||||
|  |  *	000001wxyzab			010wxyz | ||||||
|  |  *	00001wxyzabc			011wxyz | ||||||
|  |  *	0001wxyzabcd			100wxyz | ||||||
|  |  *	001wxyzabcde			101wxyz | ||||||
|  |  *	01wxyzabcdef			110wxyz | ||||||
|  |  *	1wxyzabcdefg			111wxyz | ||||||
|  |  * | ||||||
|  |  * For further information see John C. Bellamy's Digital Telephony, 1982, | ||||||
|  |  * John Wiley & Sons, pps 98-111 and 472-476. | ||||||
|  |  */ | ||||||
|  | unsigned char | ||||||
|  | linear2alaw(short pcm_val)	/* 2's complement (16-bit range) */ | ||||||
|  | { | ||||||
|  |    short	 mask; | ||||||
|  |    short	 seg; | ||||||
|  |    unsigned char aval; | ||||||
|  |     | ||||||
|  |    pcm_val = pcm_val >> 3; | ||||||
|  |  | ||||||
|  |    if (pcm_val >= 0) { | ||||||
|  |       mask = 0xD5;		/* sign (7th) bit = 1 */ | ||||||
|  |    } else { | ||||||
|  |       mask = 0x55;		/* sign bit = 0 */ | ||||||
|  |       pcm_val = -pcm_val - 1; | ||||||
|  |    } | ||||||
|  |     | ||||||
|  |    /* Convert the scaled magnitude to segment number. */ | ||||||
|  |    seg = search(pcm_val, seg_aend, 8); | ||||||
|  |     | ||||||
|  |    /* Combine the sign, segment, and quantization bits. */ | ||||||
|  |     | ||||||
|  |    if (seg >= 8)		/* out of range, return maximum value. */ | ||||||
|  |       return (unsigned char) (0x7F ^ mask); | ||||||
|  |    else { | ||||||
|  |       aval = (unsigned char) seg << SEG_SHIFT; | ||||||
|  |       if (seg < 2) | ||||||
|  | 	 aval |= (pcm_val >> 1) & QUANT_MASK; | ||||||
|  |       else | ||||||
|  | 	 aval |= (pcm_val >> seg) & QUANT_MASK; | ||||||
|  |       return (aval ^ mask); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * alaw2linear() - Convert an A-law value to 16-bit linear PCM | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | short | ||||||
|  | alaw2linear( | ||||||
|  |    unsigned char	a_val) | ||||||
|  | { | ||||||
|  |    short t; | ||||||
|  |    short seg; | ||||||
|  |     | ||||||
|  |    a_val ^= 0x55; | ||||||
|  |     | ||||||
|  |    t = (a_val & QUANT_MASK) << 4; | ||||||
|  |    seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; | ||||||
|  |    switch (seg) { | ||||||
|  |    case 0: | ||||||
|  |       t += 8; | ||||||
|  |       break; | ||||||
|  |    case 1: | ||||||
|  |       t += 0x108; | ||||||
|  |       break; | ||||||
|  |    default: | ||||||
|  |       t += 0x108; | ||||||
|  |       t <<= seg - 1; | ||||||
|  |    } | ||||||
|  |    return ((a_val & SIGN_BIT) ? t : -t); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define	BIAS		(0x84)		/* Bias for linear code. */ | ||||||
|  | #define CLIP            8159 | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | * linear2ulaw() - Convert a linear PCM value to u-law | ||||||
|  | * | ||||||
|  | * In order to simplify the encoding process, the original linear magnitude | ||||||
|  | * is biased by adding 33 which shifts the encoding range from (0 - 8158) to | ||||||
|  | * (33 - 8191). The result can be seen in the following encoding table: | ||||||
|  | * | ||||||
|  | *	Biased Linear Input Code	Compressed Code | ||||||
|  | *	------------------------	--------------- | ||||||
|  | *	00000001wxyza			000wxyz | ||||||
|  | *	0000001wxyzab			001wxyz | ||||||
|  | *	000001wxyzabc			010wxyz | ||||||
|  | *	00001wxyzabcd			011wxyz | ||||||
|  | *	0001wxyzabcde			100wxyz | ||||||
|  | *	001wxyzabcdef			101wxyz | ||||||
|  | *	01wxyzabcdefg			110wxyz | ||||||
|  | *	1wxyzabcdefgh			111wxyz | ||||||
|  | * | ||||||
|  | * Each biased linear code has a leading 1 which identifies the segment | ||||||
|  | * number. The value of the segment number is equal to 7 minus the number | ||||||
|  | * of leading 0's. The quantization interval is directly available as the | ||||||
|  | * four bits wxyz.  * The trailing bits (a - h) are ignored. | ||||||
|  | * | ||||||
|  | * Ordinarily the complement of the resulting code word is used for | ||||||
|  | * transmission, and so the code word is complemented before it is returned. | ||||||
|  | * | ||||||
|  | * For further information see John C. Bellamy's Digital Telephony, 1982, | ||||||
|  | * John Wiley & Sons, pps 98-111 and 472-476. | ||||||
|  | */ | ||||||
|  | unsigned char | ||||||
|  | linear2ulaw( | ||||||
|  |    short pcm_val)	/* 2's complement (16-bit range) */ | ||||||
|  | { | ||||||
|  |    short         mask; | ||||||
|  |    short	 seg; | ||||||
|  |    unsigned char uval; | ||||||
|  |     | ||||||
|  |    /* Get the sign and the magnitude of the value. */ | ||||||
|  |    pcm_val = pcm_val >> 2; | ||||||
|  |    if (pcm_val < 0) { | ||||||
|  |       pcm_val = -pcm_val; | ||||||
|  |       mask = 0x7F; | ||||||
|  |    } else { | ||||||
|  |       mask = 0xFF; | ||||||
|  |    } | ||||||
|  |    if ( pcm_val > CLIP ) pcm_val = CLIP;		/* clip the magnitude */ | ||||||
|  |    pcm_val += (BIAS >> 2); | ||||||
|  |     | ||||||
|  |    /* Convert the scaled magnitude to segment number. */ | ||||||
|  |    seg = search(pcm_val, seg_uend, 8); | ||||||
|  |     | ||||||
|  |    /* | ||||||
|  |    * Combine the sign, segment, quantization bits; | ||||||
|  |    * and complement the code word. | ||||||
|  |    */ | ||||||
|  |    if (seg >= 8)		/* out of range, return maximum value. */ | ||||||
|  |       return (unsigned char) (0x7F ^ mask); | ||||||
|  |    else { | ||||||
|  |       uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); | ||||||
|  |       return (uval ^ mask); | ||||||
|  |    } | ||||||
|  |     | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * ulaw2linear() - Convert a u-law value to 16-bit linear PCM | ||||||
|  |  * | ||||||
|  |  * First, a biased linear code is derived from the code word. An unbiased | ||||||
|  |  * output can then be obtained by subtracting 33 from the biased code. | ||||||
|  |  * | ||||||
|  |  * Note that this function expects to be passed the complement of the | ||||||
|  |  * original code word. This is in keeping with ISDN conventions. | ||||||
|  |  */ | ||||||
|  | short | ||||||
|  | ulaw2linear( | ||||||
|  |    unsigned char	u_val) | ||||||
|  | { | ||||||
|  |    short t; | ||||||
|  |     | ||||||
|  |    /* Complement to obtain normal u-law value. */ | ||||||
|  |    u_val = ~u_val; | ||||||
|  |     | ||||||
|  |    /* | ||||||
|  |     * Extract and bias the quantization bits. Then | ||||||
|  |     * shift up by the segment number and subtract out the bias. | ||||||
|  |     */ | ||||||
|  |    t = ((u_val & QUANT_MASK) << 3) + BIAS; | ||||||
|  |    t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; | ||||||
|  |     | ||||||
|  |    return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* A-law to u-law conversion */ | ||||||
|  | unsigned char | ||||||
|  | alaw2ulaw( | ||||||
|  |    unsigned char	aval) | ||||||
|  | { | ||||||
|  |    aval &= 0xff; | ||||||
|  |    return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : | ||||||
|  | 	   (0x7F ^ _a2u[aval ^ 0x55])); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* u-law to A-law conversion */ | ||||||
|  | unsigned char | ||||||
|  | ulaw2alaw( | ||||||
|  |    unsigned char	uval) | ||||||
|  | { | ||||||
|  |    uval &= 0xff; | ||||||
|  |    return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : | ||||||
|  | 			   (0x55 ^ (_u2a[0x7F ^ uval] - 1))); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* ---------- end of g711.c ----------------------------------------------------- */ | ||||||
							
								
								
									
										27
									
								
								contrib/simcom2rtp/g711.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								contrib/simcom2rtp/g711.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | /* | ||||||
|  |  * g711.h | ||||||
|  |  * | ||||||
|  |  * u-law, A-law and linear PCM conversions. | ||||||
|  |  * Source: http://www.speech.kth.se/cost250/refsys/latest/src/g711.h | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _G711_H_ | ||||||
|  | #define _G711_H_ | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | unsigned char	linear2alaw(short pcm_val); | ||||||
|  | short		    alaw2linear(unsigned char a_val); | ||||||
|  | unsigned char	linear2ulaw(short pcm_val); | ||||||
|  | short		    ulaw2linear(unsigned char u_val); | ||||||
|  | unsigned char	alaw2ulaw(unsigned char aval); | ||||||
|  | unsigned char	ulaw2alaw(unsigned char uval); | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif /* _G711_H_ */ | ||||||
							
								
								
									
										102
									
								
								contrib/simcom2rtp/g711_table.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								contrib/simcom2rtp/g711_table.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | #ifndef G711_TABLE_H | ||||||
|  | #define G711_TABLE_H | ||||||
|  |  | ||||||
|  | #include "g711.h" | ||||||
|  |  | ||||||
|  | /* 16384 entries per table (16 bit) */ | ||||||
|  | unsigned char linear_to_alaw[65536]; | ||||||
|  | unsigned char linear_to_ulaw[65536]; | ||||||
|  |  | ||||||
|  | /* 16384 entries per table (8 bit) */ | ||||||
|  | unsigned short alaw_to_linear[256]; | ||||||
|  | unsigned short ulaw_to_linear[256]; | ||||||
|  |  | ||||||
|  | static void build_linear_to_xlaw_table(unsigned char *linear_to_xlaw, | ||||||
|  |                                        unsigned char (*linear2xlaw)(short)) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i=0; i<65536;i++){ | ||||||
|  |         linear_to_xlaw[i] = linear2xlaw((short) i); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void build_xlaw_to_linear_table(unsigned short *xlaw_to_linear, | ||||||
|  |                                        short (*xlaw2linear)(unsigned char)) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i=0; i<256;i++){ | ||||||
|  |         xlaw_to_linear[i] = (unsigned short) xlaw2linear(i); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void pcm16_to_xlaw(unsigned char *linear_to_xlaw, int src_length, const char *src_samples, char *dst_samples) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     const unsigned short *s_samples; | ||||||
|  |  | ||||||
|  |     s_samples = (const unsigned short *)src_samples; | ||||||
|  |  | ||||||
|  |     for (i=0; i < src_length / 2; i++) | ||||||
|  |     { | ||||||
|  |         dst_samples[i] = linear_to_xlaw[s_samples[i]]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void xlaw_to_pcm16(unsigned short *xlaw_to_linear, int src_length, const char *src_samples, char *dst_samples) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     unsigned char *s_samples; | ||||||
|  |     unsigned short *d_samples; | ||||||
|  |  | ||||||
|  |     s_samples = (unsigned char *) src_samples; | ||||||
|  |     d_samples = (unsigned short *)dst_samples; | ||||||
|  |  | ||||||
|  |     for (i=0; i < src_length; i++) | ||||||
|  |     { | ||||||
|  |         d_samples[i] = xlaw_to_linear[s_samples[i]]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void pcm16_to_alaw(int src_length, const char *src_samples, char *dst_samples) | ||||||
|  | { | ||||||
|  |     pcm16_to_xlaw(linear_to_alaw, src_length, src_samples, dst_samples); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void pcm16_to_ulaw(int src_length, const char *src_samples, char *dst_samples) | ||||||
|  | { | ||||||
|  |     pcm16_to_xlaw(linear_to_ulaw, src_length, src_samples, dst_samples); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void alaw_to_pcm16(int src_length, const char *src_samples, char *dst_samples) | ||||||
|  | { | ||||||
|  |     xlaw_to_pcm16(alaw_to_linear, src_length, src_samples, dst_samples); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ulaw_to_pcm16(int src_length, const char *src_samples, char *dst_samples) | ||||||
|  | { | ||||||
|  |     xlaw_to_pcm16(ulaw_to_linear, src_length, src_samples, dst_samples); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void pcm16_alaw_tableinit() | ||||||
|  | { | ||||||
|  |     build_linear_to_xlaw_table(linear_to_alaw, linear2alaw); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void pcm16_ulaw_tableinit() | ||||||
|  | { | ||||||
|  |     build_linear_to_xlaw_table(linear_to_ulaw, linear2ulaw); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void alaw_pcm16_tableinit() | ||||||
|  | { | ||||||
|  |     build_xlaw_to_linear_table(alaw_to_linear, alaw2linear); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ulaw_pcm16_tableinit() | ||||||
|  | { | ||||||
|  |     build_xlaw_to_linear_table(ulaw_to_linear, ulaw2linear); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif // G711_TABLE_H | ||||||
							
								
								
									
										14
									
								
								contrib/simcom2rtp/g711_table.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								contrib/simcom2rtp/g711_table.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | #ifndef G711_TABLE_H | ||||||
|  | #define G711_TABLE_H | ||||||
|  |  | ||||||
|  | void pcm16_to_alaw(int length, const char *src_samples, char *dst_samples); | ||||||
|  | void pcm16_to_ulaw(int length, const char *src_samples, char *dst_samples); | ||||||
|  | void alaw_to_pcm16(int length, const char *src_samples, char *dst_samples); | ||||||
|  | void ulaw_to_pcm16(int length, const char *src_samples, char *dst_samples); | ||||||
|  |  | ||||||
|  | void pcm16_alaw_tableinit(); | ||||||
|  | void pcm16_ulaw_tableinit(); | ||||||
|  | void alaw_pcm16_tableinit(); | ||||||
|  | void ulaw_pcm16_tableinit(); | ||||||
|  |  | ||||||
|  | #endif // G711_TABLE_H | ||||||
							
								
								
									
										218
									
								
								contrib/simcom2rtp/simcom2rtp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								contrib/simcom2rtp/simcom2rtp.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | |||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <osmocom/core/select.h> | ||||||
|  | #include <osmocom/core/msgb.h> | ||||||
|  | #include <osmocom/core/fsm.h> | ||||||
|  | #include <osmocom/core/logging.h> | ||||||
|  | #include <osmocom/core/application.h> | ||||||
|  | #include <osmocom/core/serial.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/trau/osmo_ortp.h> | ||||||
|  |  | ||||||
|  | #include "g711.h" | ||||||
|  |  | ||||||
|  | #define RTP_PT_PCMU	0 | ||||||
|  | #define RTP_PT_PCMA	8 | ||||||
|  |  | ||||||
|  | struct modem_state { | ||||||
|  | 	struct osmo_fd data_fd; | ||||||
|  | 	struct osmo_rtp_socket *rtp; | ||||||
|  | 	/* queue of linear PCM audio in RTP -> modem direction */ | ||||||
|  | 	struct llist_head rtp2modem; | ||||||
|  | 	/* message buffer used if samples insufficient for next RTP frame were received */ | ||||||
|  | 	struct msgb *modem2rtp; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void *g_tall_ctx; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* call-back on received RTP data */ | ||||||
|  | static void ortp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *payload, | ||||||
|  | 			unsigned int payload_len, uint16_t seq_nr, uint32_t timestamp, bool marker) | ||||||
|  | { | ||||||
|  | 	/* we received a RTP frame */ | ||||||
|  | 	struct modem_state *ms = rs->priv; | ||||||
|  | 	struct msgb *msg = msgb_alloc(payload_len*2, "RTP Rx"); | ||||||
|  | 	unsigned int i; | ||||||
|  | 	int16_t *out; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(msg); | ||||||
|  |  | ||||||
|  | 	out = (int16_t *) msgb_put(msg, payload_len*2); | ||||||
|  |  | ||||||
|  | 	if (payload_len != 160) { | ||||||
|  | 		fprintf(stderr, "RTP payload length %d != 160, dropping\n", payload_len); | ||||||
|  | 		msgb_free(msg); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* convert from Alaw to linear PCM (160 -> 320 bytes) */ | ||||||
|  | 	for (i = 0; i < payload_len; i++) | ||||||
|  | 		out[i] = alaw2linear(payload[i]); | ||||||
|  |  | ||||||
|  | 	/* append to the write queue */ | ||||||
|  | 	msgb_enqueue(&ms->rtp2modem, msg); | ||||||
|  | 	ms->data_fd.when |= OSMO_FD_WRITE; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void modem2rtp(struct modem_state *ms, const uint8_t *data, unsigned int len) | ||||||
|  | { | ||||||
|  | 	const int16_t *data16 = (const int16_t *)data; | ||||||
|  | 	unsigned int samples = len / 2; | ||||||
|  | 	unsigned int offset = 0; | ||||||
|  | 	unsigned int i; | ||||||
|  |  | ||||||
|  | 	/* samples are always 16bit, we cannot read half a sample */ | ||||||
|  | 	OSMO_ASSERT((len & 1) == 0); | ||||||
|  |  | ||||||
|  | 	/* first complete any pending incomplete RTP frame */ | ||||||
|  | 	if (ms->modem2rtp) { | ||||||
|  | 		struct msgb *msg = ms->modem2rtp; | ||||||
|  | 		unsigned int missing_samples = 160 - msgb_length(msg); | ||||||
|  | 		for (i = 0; i < missing_samples; i++) { | ||||||
|  | 			if (i >= samples) | ||||||
|  | 				break; | ||||||
|  | 			msgb_put_u8(msg, linear2alaw(data16[i])); | ||||||
|  | 		} | ||||||
|  | 		offset = i; | ||||||
|  | 		if (msgb_length(msg) == 160) { | ||||||
|  | 			osmo_rtp_send_frame_ext(ms->rtp, msgb_data(msg), msgb_length(msg), 160, false); | ||||||
|  | 			msgb_free(msg); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* then send as many RTP frames as we have samples */ | ||||||
|  | 	for (offset = offset; offset + 160 <= samples; offset += 160) { | ||||||
|  | 		uint8_t buf[160]; | ||||||
|  | 		for (i = 0; i < sizeof(buf); i++) | ||||||
|  | 			buf[i] = linear2alaw(data16[offset + i]); | ||||||
|  | 		osmo_rtp_send_frame_ext(ms->rtp, buf, sizeof(buf), 160, false); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* store remainder in msgb */ | ||||||
|  | 	if (offset < samples) { | ||||||
|  | 		struct msgb *msg = msgb_alloc_c(ms, 160, "modem2rtp"); | ||||||
|  | 		OSMO_ASSERT(msg); | ||||||
|  | 		OSMO_ASSERT(len - offset < 160); | ||||||
|  | 		for (i = 0; i < len - offset; i++) | ||||||
|  | 			msgb_put_u8(msg, linear2alaw(data16[offset + i])); | ||||||
|  | 		ms->modem2rtp = msg; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* call back on file descriptor events of the modem DATA ttyUSB device */ | ||||||
|  | static int modem_data_fd_cb(struct osmo_fd *ofd, unsigned int what) | ||||||
|  | { | ||||||
|  | 	struct modem_state *ms = ofd->data; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	if (what & OSMO_FD_READ) { | ||||||
|  | 		/* SIM5360 USB AUDIO Application Note v1.01 states 1600 bytes every 100ms */ | ||||||
|  | 		uint8_t rx_buf[1600]; | ||||||
|  | 		rc = read(ofd->fd, rx_buf, sizeof(rx_buf)); | ||||||
|  | 		OSMO_ASSERT(rc > 0); | ||||||
|  | 		modem2rtp(ms, rx_buf, rc); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (what & OSMO_FD_WRITE) { | ||||||
|  | 		struct msgb *msg = msgb_dequeue(&ms->rtp2modem); | ||||||
|  | 		if (!msg) | ||||||
|  | 			ofd->when &= ~OSMO_FD_WRITE; | ||||||
|  | 		else { | ||||||
|  | 			/* SIM5300 USB AUDIO Application Note v1.01 states 640 bytes every 40ms; | ||||||
|  | 			 * we simply write every RTP frame individually (320 bytes every 20ms) */ | ||||||
|  | 			rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); | ||||||
|  | 			if (rc != msgb_length(msg)) | ||||||
|  | 				fprintf(stderr, "Short write: %d < %u\n", rc, msgb_length(msg)); | ||||||
|  | 			msgb_free(msg); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int modem_data_open(struct modem_state *ms, const char *basepath) | ||||||
|  | { | ||||||
|  | 	char fname[PATH_MAX+1]; | ||||||
|  | 	int fd; | ||||||
|  |  | ||||||
|  | 	/* the assumption is that the caller provides something like | ||||||
|  | 	 * "/dev/serial/by-path/pci-0000:00:14.0-usb-0:2:1" */ | ||||||
|  | 	snprintf(fname, sizeof(fname), "%s.0-port0", basepath); | ||||||
|  |  | ||||||
|  | 	fd = osmo_serial_init(fname, 921600); | ||||||
|  | 	if (fd < 0) { | ||||||
|  | 		fprintf(stderr, "failed to open device '%s': %s\n", fname, strerror(errno)); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	osmo_fd_setup(&ms->data_fd, fd, OSMO_FD_READ, modem_data_fd_cb, ms, 0); | ||||||
|  | 	osmo_fd_register(&ms->data_fd); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct modem_state *modem_create(void *ctx) | ||||||
|  | { | ||||||
|  | 	struct modem_state *ms = talloc_zero(ctx, struct modem_state); | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	INIT_LLIST_HEAD(&ms->rtp2modem); | ||||||
|  |  | ||||||
|  | 	ms->rtp = osmo_rtp_socket_create(ms, 0); | ||||||
|  | 	OSMO_ASSERT(ms->rtp); | ||||||
|  | 	osmo_rtp_socket_set_pt(ms->rtp, RTP_PT_PCMA); | ||||||
|  | 	ms->rtp->priv = ms; | ||||||
|  | 	ms->rtp->rx_cb = ortp_rx_cb; | ||||||
|  |  | ||||||
|  | 	rc = osmo_rtp_socket_bind(ms->rtp, "0.0.0.0", 1111); | ||||||
|  | 	OSMO_ASSERT(rc == 0); | ||||||
|  |  | ||||||
|  | 	rc = osmo_rtp_socket_connect(ms->rtp, "127.0.0.1", 2222); | ||||||
|  | 	//rc = osmo_rtp_socket_autoconnect(ms->rtp); | ||||||
|  | 	OSMO_ASSERT(rc == 0); | ||||||
|  |  | ||||||
|  | 	osmo_rtp_set_source_desc(ms->rtp, "cname", "simcom2rtp", NULL, NULL, NULL, | ||||||
|  | 				 "osmo-simcom2rtp", NULL); | ||||||
|  |  | ||||||
|  | 	return ms; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  |  | ||||||
|  | 	talloc_enable_null_tracking(); | ||||||
|  | 	g_tall_ctx = talloc_named_const(NULL, 1, "simcom2rtp"); | ||||||
|  |  | ||||||
|  | 	msgb_talloc_ctx_init(g_tall_ctx, 0); | ||||||
|  | 	osmo_init_logging2(g_tall_ctx, NULL); | ||||||
|  | 	osmo_fsm_log_timeouts(true); | ||||||
|  | 	osmo_fsm_log_addr(true); | ||||||
|  | 	//osmo_stats_init(g_tall_ctx); | ||||||
|  | 	osmo_rtp_init(g_tall_ctx); | ||||||
|  |  | ||||||
|  | 	struct modem_state *ms = modem_create(g_tall_ctx); | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(ms); | ||||||
|  | 	rc = modem_data_open(ms, "/dev/serial/by-path/pci-0000:00:14.0-usb-0:2:1"); | ||||||
|  | 	OSMO_ASSERT(rc == 0); | ||||||
|  |  | ||||||
|  | 	while (1) { | ||||||
|  | 		osmo_select_main(0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user