commit eb11e68412b49b0945ab6bb332ac9486f1ebeb9e
Author: Matthias P. Braendli <matthias.braendli@mpb.li>
Date:   Mon Apr 30 20:12:42 2018 +0200

    Add support for interleaved R/G samples
    
    This is useful for transmitting complex I/Q data on the
    red and green DACs

diff --git a/include/osmo-fl2k.h b/include/osmo-fl2k.h
index 8d3cc3b..6a1b3ee 100644
--- a/include/osmo-fl2k.h
+++ b/include/osmo-fl2k.h
@@ -50,6 +50,9 @@ typedef struct fl2k_data_info {
 
 	/* filled in by application */
 	int sampletype_signed;		/* are samples signed or unsigned? */
+	/* Either iq_buf or (r_buf, g_buf and b_buf) must be non-null */
+	char *iq_buf;			/* pointer to data containing interleaved r and g
+							   data */
 	char *r_buf;			/* pointer to red buffer */
 	char *g_buf;			/* pointer to green buffer */
 	char *b_buf;			/* pointer to blue buffer */
diff --git a/src/fl2k_file.c b/src/fl2k_file.c
index 1d3697d..0da1cbc 100644
--- a/src/fl2k_file.c
+++ b/src/fl2k_file.c
@@ -40,7 +40,8 @@
 static fl2k_dev_t *dev = NULL;
 
 static volatile int do_exit = 0;
-static volatile int repeat = 1;
+static int repeat = 1;
+static int interleaved = 1;
 FILE *file;
 char *txbuf = NULL;
 
@@ -52,6 +53,7 @@ void usage(void)
 		"\t[-d device_index (default: 0)]\n"
 		"\t[-r repeat file (default: 1)]\n"
 		"\t[-s samplerate (default: 100 MS/s)]\n"
+		"\t[-i interleave (input samples are interleaved Red/Green values, useful for I/Q data, default: 0)]\n"
 		"\tfilename (use '-' to read from stdin)\n\n"
 	);
 	exit(1);
@@ -80,14 +82,20 @@ static void sighandler(int signum)
 
 void fl2k_callback(fl2k_data_info_t *data_info)
 {
-	int r, left = FL2K_BUF_LEN;
+	int r;
+	const int required_samples = FL2K_BUF_LEN * (interleaved ? 2 : 1);
+	int left = required_samples;
 	static uint32_t repeat_cnt = 0;
 
 	data_info->sampletype_signed = 1;
-	data_info->r_buf = txbuf;
+	if (interleaved) {
+		data_info->iq_buf = txbuf;
+	} else {
+		data_info->r_buf = txbuf;
+	}
 
 	while (!do_exit && (left > 0)) {
-		r = fread(txbuf + (FL2K_BUF_LEN - left), 1, left, file);
+		r = fread(txbuf + (required_samples - left), 1, left, file);
 
 		if (ferror(file))
 			fprintf(stderr, "File Error\n");
@@ -120,11 +128,14 @@ int main(int argc, char **argv)
 	void *status;
 	char *filename = NULL;
 
-	while ((opt = getopt(argc, argv, "d:r:s:")) != -1) {
+	while ((opt = getopt(argc, argv, "d:i:r:s:")) != -1) {
 		switch (opt) {
 		case 'd':
 			dev_index = (uint32_t)atoi(optarg);
 			break;
+		case 'i':
+			interleaved = (int)atoi(optarg);
+			break;
 		case 'r':
 			repeat = (int)atoi(optarg);
 			break;
@@ -151,7 +162,7 @@ int main(int argc, char **argv)
 		goto out;
 	}
 
-	txbuf = malloc(FL2K_BUF_LEN);
+	txbuf = malloc(FL2K_BUF_LEN * (interleaved ? 2 : 1));
 	if (!txbuf) {
 		fprintf(stderr, "malloc error!\n");
 		goto out;
diff --git a/src/libosmo-fl2k.c b/src/libosmo-fl2k.c
index 0285f9a..09380b6 100644
--- a/src/libosmo-fl2k.c
+++ b/src/libosmo-fl2k.c
@@ -767,6 +767,37 @@ static void *fl2k_usb_worker(void *arg)
 }
 
 /* Buffer format conversion functions for R, G, B DACs */
+static inline void fl2k_convert_rg_interleaved(
+				  char *out,
+				  char *in,
+				  uint32_t len,
+				  uint8_t offset)
+{
+	unsigned int i, j = 0;
+
+	if (!in || !out)
+		return;
+
+	for (i = 0; i < len; i += 24) {
+		out[i+ 6] = in[j++] + offset;
+		out[i+ 5] = in[j++] + offset;
+		out[i+ 1] = in[j++] + offset;
+		out[i+ 0] = in[j++] + offset;
+		out[i+12] = in[j++] + offset;
+		out[i+ 3] = in[j++] + offset;
+		out[i+15] = in[j++] + offset;
+		out[i+14] = in[j++] + offset;
+		out[i+10] = in[j++] + offset;
+		out[i+ 9] = in[j++] + offset;
+		out[i+21] = in[j++] + offset;
+		out[i+20] = in[j++] + offset;
+		out[i+16] = in[j++] + offset;
+		out[i+23] = in[j++] + offset;
+		out[i+19] = in[j++] + offset;
+		out[i+18] = in[j++] + offset;
+	}
+}
+
 static inline void fl2k_convert_r(char *out,
 				  char *in,
 				  uint32_t len,
@@ -882,15 +913,21 @@ static void *fl2k_sample_worker(void *arg)
 		xfer_info = (fl2k_xfer_info_t *)xfer->user_data;
 		out_buf = (char *)xfer->buffer;
 
-		/* Re-arrange and copy bytes in buffer for DACs */
-		fl2k_convert_r(out_buf, data_info.r_buf, dev->xfer_buf_len,
-			       data_info.sampletype_signed ? 128 : 0);
+		if (data_info.iq_buf) {
+			/* Re-arrange interleaved samples and copy bytes in buffer for DACs */
+			fl2k_convert_rg_interleaved(out_buf, data_info.iq_buf,
+					dev->xfer_buf_len, data_info.sampletype_signed ? 128 : 0);
+		} else {
+			/* Re-arrange and copy bytes in buffer for DACs */
+			fl2k_convert_r(out_buf, data_info.r_buf, dev->xfer_buf_len,
+					data_info.sampletype_signed ? 128 : 0);
 
-		fl2k_convert_g(out_buf, data_info.g_buf, dev->xfer_buf_len,
-			       data_info.sampletype_signed ? 128 : 0);
+			fl2k_convert_g(out_buf, data_info.g_buf, dev->xfer_buf_len,
+					data_info.sampletype_signed ? 128 : 0);
 
-		fl2k_convert_b(out_buf, data_info.b_buf, dev->xfer_buf_len,
-			       data_info.sampletype_signed ? 128 : 0);
+			fl2k_convert_b(out_buf, data_info.b_buf, dev->xfer_buf_len,
+					data_info.sampletype_signed ? 128 : 0);
+		}
 
 		xfer_info->seq = buf_cnt++;
 		xfer_info->state = BUF_FILLED;
