All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] Add support for RDS decoding (updated)
       [not found] <[RFC PATCH 0/2] Add support for RDS decoding>
@ 2012-08-07 15:11 ` Konke Radlow
  2012-08-07 15:11   ` [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC) Konke Radlow
  2012-08-08 12:03   ` [RFC PATCH 0/2] Add support for RDS decoding (updated) Konke Radlow
  0 siblings, 2 replies; 15+ messages in thread
From: Konke Radlow @ 2012-08-07 15:11 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, hdegoede, koradlow

Hello,
first of all: thank you for the comments to my previous RFC for the
libv4l2rds library and the rds-ctl control & testing tool.
The proposed changes have been implemented, and the code has been     
further improved after a thorough code review by Hans Verkuil.

Changes:
  -the code is rebased on the latest v4l-utils code (as of today 07.08)
  -added feature: time/date decoding
  -implementing proposed changes
  -code cleanup
  -extended comments

Status:
>From my point of view the RDS decoding is now almost feature complete.
There are some obscure RDS features like paging that are not supported,
but they do not seem to used anywhere. 
So in the near future no features will be added and the goal is to get 
the library and control tool merged into the v4l-utils codebase.

Upcoming:
Work on RDS-TMC decoding is going well and is being done in a seperate 
branch. It will be the subject of a future RFC, once it has reached a 
mature stage. But TMC is not a core feature of RDS but an addition.

Regards,
Konke


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-07 15:11 ` [RFC PATCH 0/2] Add support for RDS decoding (updated) Konke Radlow
@ 2012-08-07 15:11   ` Konke Radlow
  2012-08-07 15:11     ` [RFC PATCH 2/2] Add rds-ctl tool " Konke Radlow
                       ` (3 more replies)
  2012-08-08 12:03   ` [RFC PATCH 0/2] Add support for RDS decoding (updated) Konke Radlow
  1 sibling, 4 replies; 15+ messages in thread
From: Konke Radlow @ 2012-08-07 15:11 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, hdegoede, koradlow

---
 Makefile.am                     |    3 +-
 configure.ac                    |    7 +-
 lib/include/libv4l2rds.h        |  228 ++++++++++
 lib/libv4l2rds/Makefile.am      |   11 +
 lib/libv4l2rds/libv4l2rds.c     |  953 +++++++++++++++++++++++++++++++++++++++
 lib/libv4l2rds/libv4l2rds.pc.in |   11 +
 6 files changed, 1211 insertions(+), 2 deletions(-)
 create mode 100644 lib/include/libv4l2rds.h
 create mode 100644 lib/libv4l2rds/Makefile.am
 create mode 100644 lib/libv4l2rds/libv4l2rds.c
 create mode 100644 lib/libv4l2rds/libv4l2rds.pc.in

diff --git a/Makefile.am b/Makefile.am
index a754955..621d8d9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,7 +5,8 @@ SUBDIRS = \
 	lib/libv4lconvert \
 	lib/libv4l2 \
 	lib/libv4l1 \
-	lib/libdvbv5
+	lib/libdvbv5 \
+	lib/libv4l2rds
 
 if WITH_V4LUTILS
 SUBDIRS += \
diff --git a/configure.ac b/configure.ac
index 8ddcc9d..1109c4d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -14,6 +14,7 @@ AC_CONFIG_FILES([Makefile
 	lib/libv4l2/Makefile
 	lib/libv4l1/Makefile
 	lib/libdvbv5/Makefile
+	lib/libv4l2rds/Makefile
 
 	utils/libv4l2util/Makefile
 	utils/libmedia_dev/Makefile
@@ -36,6 +37,7 @@ AC_CONFIG_FILES([Makefile
 	lib/libv4l1/libv4l1.pc
 	lib/libv4l2/libv4l2.pc
 	lib/libdvbv5/libdvbv5.pc
+	lib/libv4l2rds/libv4l2rds.pc
 ])
 
 AM_INIT_AUTOMAKE([1.9 no-dist-gzip dist-bzip2 -Wno-portability]) # 1.10 is needed for target_LIBTOOLFLAGS
@@ -146,9 +148,12 @@ AC_ARG_WITH(libv4l2subdir, AS_HELP_STRING(--with-libv4l2subdir=DIR,set libv4l2 l
 AC_ARG_WITH(libv4lconvertsubdir, AS_HELP_STRING(--with-libv4lconvertsubdir=DIR,set libv4lconvert library subdir [default=libv4l]),
    libv4lconvertsubdir=$withval, libv4lconvertsubdir="libv4l")
 
+AC_ARG_WITH(libv4l2rdssubdir, AS_HELP_STRING(--with-libv4l2rdssubdir=DIR,set libv4l2rds library subdir [default=libv4l]),
+   libv4l2rdssubdir=$withval, libv4l2rdssubdir="libv4l")
+
 AC_ARG_WITH(udevdir, AS_HELP_STRING(--with-udevdir=DIR,set udev directory [default=/lib/udev]),
    udevdir=$withval, udevdir="/lib/udev")
-
+   
 libv4l1privdir="$libdir/$libv4l1subdir"
 libv4l2privdir="$libdir/$libv4l2subdir"
 libv4l2plugindir="$libv4l2privdir/plugins"
diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h
new file mode 100644
index 0000000..4aa8593
--- /dev/null
+++ b/lib/include/libv4l2rds.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ * Author: Konke Radlow <koradlow@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
+ */
+
+#ifndef __LIBV4L2RDS
+#define __LIBV4L2RDS
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <config.h>
+
+#include <linux/videodev2.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#if HAVE_VISIBILITY
+#define LIBV4L_PUBLIC __attribute__ ((visibility("default")))
+#else
+#define LIBV4L_PUBLIC
+#endif
+
+/* used to define the current version (version field) of the v4l2_rds struct */
+#define V4L2_RDS_VERSION (1)
+
+/* Constants used to define the size of arrays used to store RDS information */
+#define MAX_ODA_CNT 18 	/* there are 16 groups each with type a or b. Of these
+			 * 32 distinct groups, 18 can be used for ODA purposes */
+#define MAX_AF_CNT 25	/* AF Method A allows a maximum of 25 AFs to be defined
+			 * AF Method B does not impose a limit on the number of AFs
+			 * but it is not fully supported at the moment and will
+			 * not receive more than 25 AFs */
+
+/* Define Constants for the possible types of RDS information
+ * used to address the relevant bit in the valid_fields bitmask */
+#define V4L2_RDS_PI 		0x01	/* Program Identification */
+#define V4L2_RDS_PTY		0x02	/* Program Type */
+#define V4L2_RDS_TP		0x04	/* Traffic Program */
+#define V4L2_RDS_PS		0x08	/* Program Service Name */
+#define V4L2_RDS_TA		0x10	/* Traffic Announcement */
+#define V4L2_RDS_DI		0x20	/* Decoder Information */
+#define V4L2_RDS_MS		0x40	/* Music / Speech flag */
+#define V4L2_RDS_PTYN		0x80	/* Program Type Name */
+#define V4L2_RDS_RT		0x100 	/* Radio-Text */
+#define V4L2_RDS_TIME		0x200	/* Date and Time information */
+#define V4L2_RDS_TMC		0x400	/* TMC availability */
+#define V4L2_RDS_AF		0x800	/* AF (alternative freq) available */
+#define V4L2_RDS_ECC		0x1000	/* Extended County Code */
+#define V4L2_RDS_LC		0x2000	/* Language Code */
+
+/* Define Constants for the state of the RDS decoding process
+ * used to address the relevant bit in the decode_information bitmask */
+#define V4L2_RDS_GROUP_NEW 	0x01	/* New group received */
+#define V4L2_RDS_ODA		0x02	/* Open Data Group announced */
+
+/* Decoder Information (DI) codes
+ * used to decode the DI information according to the RDS standard */
+#define V4L2_RDS_FLAG_STEREO 		0x01
+#define V4L2_RDS_FLAG_ARTIFICIAL_HEAD	0x02
+#define V4L2_RDS_FLAG_COMPRESSED	0x04
+#define V4L2_RDS_FLAG_STATIC_PTY	0x08
+
+/* struct to encapsulate one complete RDS group */
+/* This structure is used internally to store data until a complete RDS
+ * group was received and group id dependent decoding can be done.
+ * It is also used to provide external access to uninterpreted RDS groups
+ * when manual decoding is required (e.g. special ODA types) */
+struct v4l2_rds_group {
+	uint16_t pi;		/* Program Identification */
+	char group_version;	/* group version ('A' / 'B') */
+	uint8_t group_id;	/* group number (0..16) */
+
+	/* uninterpreted data blocks for decoding (e.g. ODA) */
+	uint8_t data_b_lsb;
+	uint8_t data_c_msb;
+	uint8_t data_c_lsb;
+	uint8_t data_d_msb;
+	uint8_t data_d_lsb;
+};
+
+/* struct to encapsulate some statistical information about the decoding process */
+struct v4l2_rds_statistics {
+	uint32_t block_cnt;		/* total amount of received blocks */
+	uint32_t group_cnt;		/* total amount of successfully
+					 * decoded groups */
+	uint32_t block_error_cnt;	/* blocks that were marked as erroneous
+					 * and had to be dropped */
+	uint32_t group_error_cnt;	/* group decoding processes that had to be
+					 * aborted because of erroneous blocks
+					 * or wrong order of blocks */
+	uint32_t block_corrected_cnt;	/* blocks that contained 1-bit errors
+					 * which were corrected */
+	uint32_t group_type_cnt[16];	/* number of occurrence for each
+					 * defined RDS group */
+};
+
+/* struct to encapsulate the definition of one ODA (Open Data Application) type */
+struct v4l2_rds_oda {
+	uint8_t group_id;	/* RDS group used to broadcast this ODA */
+	char group_version;	/* group version (A / B) for this ODA */
+	uint16_t aid;		/* Application Identification for this ODA,
+				 * AIDs are centrally administered by the
+				 * RDS Registration Office (rds.org.uk) */
+};
+
+/* struct to encapsulate an array of all defined ODA types for a channel */
+/* This structure will grow with ODA announcements broadcasted in type 3A
+ * groups, that were verified not to be no duplicates or redefinitions */
+struct v4l2_rds_oda_set {
+	uint8_t size;		/* number of ODAs defined by this channel */
+	struct v4l2_rds_oda oda[MAX_ODA_CNT];
+};
+
+/* struct to encapsulate an array of Alternative Frequencies for a channel */
+/* Every channel can send out AFs for his program. The number of AFs that
+ * will be broadcasted is announced by the channel */
+struct v4l2_rds_af_set {
+	uint8_t size;			/* size of the set (might be smaller
+					 * than the announced size) */
+	uint8_t announced_af;		/* number of announced AF */
+	uint32_t af[MAX_AF_CNT];	/* AFs defined in Hz */
+};
+
+/* struct to encapsulate state and RDS information for current decoding process */
+/* This is the structure that will be used by external applications, to
+ * communicate with the library and get access to RDS data */
+struct v4l2_rds {
+	uint32_t version;	/* version number of this structure */
+
+	/** state information **/
+	uint32_t decode_information;	/* state of decoding process */
+	uint32_t valid_fields;		/* currently valid info fields
+					 * of this structure */
+
+	/** RDS info fields **/
+	bool is_rbds; 		/* use RBDS standard version of LUTs */
+	uint16_t pi;		/* Program Identification */
+	uint8_t ps[9];		/* Program Service Name, UTF-8 encoding,
+				 * '\0' terminated */
+	uint8_t pty;		/* Program Type */
+	uint8_t ptyn[9];	/* Program Type Name, UTF-8 encoding,
+				 * '\0' terminated */
+	bool ptyn_ab_flag;	/* PTYN A/B flag (toggled), to signal
+				 * change of PTYN */
+	uint8_t rt_length;	/* length of RT string */
+	uint8_t rt[65];		/* Radio-Text string, UTF-8 encoding,
+				 * '\0' terminated */
+	bool rt_ab_flag;	/* RT A/B flag (toggled), to signal
+				 * transmission of new RT */
+	bool ta;		/* Traffic Announcement */
+	bool tp;		/* Traffic Program */
+	bool ms;		/* Music / Speech flag */
+	uint8_t di;		/* Decoder Information */
+	uint8_t ecc;		/* Extended Country Code */
+	uint8_t lc;		/* Language Code */
+	time_t time;		/* Time and Date of transmission */
+
+	struct v4l2_rds_statistics rds_statistics;
+	struct v4l2_rds_oda_set rds_oda;	/* Open Data Services */
+	struct v4l2_rds_af_set rds_af; 		/* Alternative Frequencies */
+};
+
+/* v4l2_rds_init() - initializes a new decoding process
+ * @is_rbds:	defines which standard is used: true=RBDS, false=RDS
+ *
+ * initialize a new instance of the RDS-decoding struct and return
+ * a handle containing state and RDS information, used to interact
+ * with the library functions */
+LIBV4L_PUBLIC struct v4l2_rds *v4l2_rds_create(bool is_rdbs);
+
+/* frees all memory allocated for the RDS-decoding struct */
+LIBV4L_PUBLIC void v4l2_rds_destroy(struct v4l2_rds *handle);
+
+/* resets the RDS information in the handle to initial values
+ * e.g. can be used when radio channel is changed
+ * @reset_statistics:	true = set all statistic values to 0, false = keep them untouched */
+LIBV4L_PUBLIC void v4l2_rds_reset(struct v4l2_rds *handle, bool reset_statistics);
+
+/* adds a raw RDS block to decode it into RDS groups
+ * @return:	bitmask with with updated fields set to 1
+ * @rds_data: 	3 bytes of raw RDS data, obtained by calling read()
+ * 				on RDS capable V4L2 devices */
+LIBV4L_PUBLIC uint32_t v4l2_rds_add(struct v4l2_rds *handle, struct v4l2_rds_data *rds_data);
+
+/*
+ * group of functions to translate numerical RDS data into strings
+ *
+ * return program description string defined in the RDS/RBDS Standard
+ * ! return value deepens on selected Standard !*/
+LIBV4L_PUBLIC const char *v4l2_rds_get_pty_str(const struct v4l2_rds *handle);
+LIBV4L_PUBLIC const char *v4l2_rds_get_language_str(const struct v4l2_rds *handle);
+LIBV4L_PUBLIC const char *v4l2_rds_get_country_str(const struct v4l2_rds *handle);
+LIBV4L_PUBLIC const char *v4l2_rds_get_coverage_str(const struct v4l2_rds *handle);
+
+/* returns a pointer to the last decoded RDS group, in order to give raw
+ * access to RDS data if it is required (e.g. ODA decoding) */
+LIBV4L_PUBLIC const struct v4l2_rds_group *v4l2_rds_get_group
+	(const struct v4l2_rds *handle);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/lib/libv4l2rds/Makefile.am b/lib/libv4l2rds/Makefile.am
new file mode 100644
index 0000000..05c54b0
--- /dev/null
+++ b/lib/libv4l2rds/Makefile.am
@@ -0,0 +1,11 @@
+if WITH_LIBV4L
+lib_LTLIBRARIES = libv4l2rds.la
+include_HEADERS = ../include/libv4l2rds.h
+pkgconfig_DATA = libv4l2rds.pc
+else
+noinst_LTLIBRARIES = libv4l2rds.la
+endif
+
+libv4l2rds_la_SOURCES = libv4l2rds.c
+libv4l2rds_la_CPPFLAGS = -fvisibility=hidden $(ENFORCE_LIBV4L_STATIC) -std=c99
+libv4l2rds_la_LDFLAGS = -version-info 0 -lpthread $(ENFORCE_LIBV4L_STATIC)
diff --git a/lib/libv4l2rds/libv4l2rds.c b/lib/libv4l2rds/libv4l2rds.c
new file mode 100644
index 0000000..5aa3088
--- /dev/null
+++ b/lib/libv4l2rds/libv4l2rds.c
@@ -0,0 +1,953 @@
+/*
+ * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ * Author: Konke Radlow <koradlow@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
+ */
+
+#include <linux/videodev2.h>
+
+#include "../include/libv4l2rds.h"
+
+/* struct to encapsulate the private state information of the decoding process */
+/* the fields (except for handle) are for internal use only - new information
+ * is decoded and stored in them until it can be verified and copied to the
+ * public part of the  rds structure (handle) */
+/* for meaning of abbreviations check the library header libv4l2rds.h */
+struct rds_private_state {
+	/* v4l2_rds has to be in first position, to allow typecasting between
+	 * v4l2_rds and rds_private_state pointers */
+	struct v4l2_rds handle;
+
+	/* current state of rds group decoding */
+	uint8_t decode_state;
+
+	/* temporal storage locations for rds fields */
+	uint16_t new_pi;
+	uint8_t new_ps[8];
+	uint8_t new_ps_valid[8];
+	uint8_t new_pty;
+	uint8_t new_ptyn[2][4];
+	bool new_ptyn_valid[2];
+	uint8_t new_rt[64];
+	uint8_t next_rt_segment;
+	uint8_t new_di;
+	uint8_t next_di_segment;
+	uint8_t new_ecc;
+	uint8_t new_lc;
+	/* RDS date / time representation */
+	uint32_t new_mjd;	/* modified Julian Day code */
+	uint8_t utc_hour;
+	uint8_t utc_minute;
+	uint8_t utc_offset;
+
+	struct v4l2_rds_group rds_group;
+	struct v4l2_rds_data rds_data_raw[4];
+};
+
+/* states of the RDS block into group decoding state machine */
+enum rds_state {
+	RDS_EMPTY,
+	RDS_A_RECEIVED,
+	RDS_B_RECEIVED,
+	RDS_C_RECEIVED,
+};
+
+static inline uint8_t set_bit(uint8_t input, uint8_t bitmask, bool bitvalue)
+{
+	return bitvalue ? input | bitmask : input & ~bitmask;
+}
+
+/* rds_decode_a-d(..): group of functions to decode different RDS blocks
+ * into the RDS group that's currently being received
+ *
+ * block A of RDS group always contains PI code of program */
+static uint32_t rds_decode_a(struct rds_private_state *priv_state, struct v4l2_rds_data *rds_data)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+	uint32_t updated_fields = 0;
+	uint16_t pi = (rds_data->msb << 8) | rds_data->lsb;
+
+	/* data in RDS group is uninterpreted */
+	priv_state->rds_group.pi = pi;
+
+	/* compare PI values to detect PI update (Channel Switch)
+	 * --> new PI is only accepted, if the same PI is received
+	 * at least 2 times in a row */
+	if (pi != handle->pi && pi == priv_state->new_pi) {
+		handle->pi = pi;
+		handle->valid_fields |= V4L2_RDS_PI;
+		updated_fields |= V4L2_RDS_PI;
+	} else if (pi != handle->pi && pi != priv_state->new_pi) {
+		priv_state->new_pi = pi;
+	}
+
+	return updated_fields;
+}
+
+/* block B of RDS group always contains Group Type Code, Group Type information
+ * Traffic Program Code and Program Type Code as well as 5 bits of Group Type
+ * depending information */
+static uint32_t rds_decode_b(struct rds_private_state *priv_state, struct v4l2_rds_data *rds_data)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+	struct v4l2_rds_group *grp = &priv_state->rds_group;
+	bool traffic_prog;
+	uint8_t pty;
+	uint32_t updated_fields = 0;
+
+	/* bits 12-15 (4-7 of msb) contain the Group Type Code */
+	grp->group_id = rds_data->msb >> 4 ;
+
+	/* bit 11 (3 of msb) defines Group Type info: 0 = A, 1 = B */
+	grp->group_version = (rds_data->msb & 0x08) ? 'B' : 'A';
+
+	/* bit 10 (2 of msb) defines Traffic program Code */
+	traffic_prog = (bool)rds_data->msb & 0x04;
+	if (handle->tp != traffic_prog) {
+		handle->tp = traffic_prog;
+		updated_fields |= V4L2_RDS_TP;
+	}
+	handle->valid_fields |= V4L2_RDS_TP;
+
+	/* bits 0-4 contains Group Type depending information */
+	grp->data_b_lsb = rds_data->lsb & 0x1f;
+
+	/* bits 5-9 contain the PTY code */
+	pty = (rds_data->msb << 3) | (rds_data->lsb >> 5);
+	pty &= 0x1f; /* mask out 3 irrelevant bits */
+	/* only accept new PTY if same PTY is received twice in a row
+	 * and filter out cases where the PTY is already known */
+	if (handle->pty == pty) {
+		priv_state->new_pty = pty;
+		return updated_fields;
+	}
+
+	if (priv_state->new_pty == pty) {
+		handle->pty = priv_state->new_pty;
+		updated_fields |= V4L2_RDS_PTY;
+		handle->valid_fields |= V4L2_RDS_PTY;
+	} else {
+		priv_state->new_pty = pty;
+	}
+
+	return updated_fields;
+}
+
+/* block C of RDS group contains either data or the PI code, depending
+ * on the Group Type - store the raw data for later decoding */
+static void rds_decode_c(struct rds_private_state *priv_state, struct v4l2_rds_data *rds_data)
+{
+	struct v4l2_rds_group *grp = &priv_state->rds_group;
+
+	grp->data_c_msb = rds_data->msb;
+	grp->data_c_lsb = rds_data->lsb;
+	/* we could decode the PI code here, because we already know if the
+	 * group is of type A or B, but it doesn't give any advantage because
+	 * we only get here after the PI code has been decoded in the first
+	 * state of the state machine */
+}
+
+/* block D of RDS group contains data - store the raw data for later decoding */
+static void rds_decode_d(struct rds_private_state *priv_state, struct v4l2_rds_data *rds_data)
+{
+	struct v4l2_rds_group *grp = &priv_state->rds_group;
+
+	grp->data_d_msb = rds_data->msb;
+	grp->data_d_lsb = rds_data->lsb;
+}
+
+static bool rds_add_oda(struct rds_private_state *priv_state, struct v4l2_rds_oda oda)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+
+	/* check if there was already an ODA announced for this group type */
+	for (int i = 0; i < handle->rds_oda.size; i++) {
+		if (handle->rds_oda.oda[i].group_id == oda.group_id)
+			/* update the AID for this ODA */
+			handle->rds_oda.oda[i].aid = oda.aid;
+			return false;
+	}
+	/* add the new ODA */
+	if (handle->rds_oda.size >= MAX_ODA_CNT)
+		return false;
+	handle->rds_oda.oda[handle->rds_oda.size++] = oda;
+	return true;
+}
+
+/* add a new AF to the list, if it doesn't exist yet */
+static bool rds_add_af_to_list(struct v4l2_rds_af_set *af_set, uint8_t af, bool is_vhf)
+{
+	uint32_t freq = 0;
+
+	/* AF0 -> "Not to be used" */
+	if (af == 0)
+		return false;
+
+	/* calculate the AF values in HZ */
+	if (is_vhf)
+		freq = 87500000 + af * 100000;
+	else if (freq <= 15)
+		freq = 152000 + af * 9000;
+	else
+		freq = 531000 + af * 9000;
+
+	/* prevent buffer overflows */
+	if (af_set->size >= MAX_AF_CNT || af_set->size >= af_set->announced_af)
+		return false;
+	/* check if AF already exists */
+	for (int i = 0; i < af_set->size; i++) {
+		if (af_set->af[i] == freq)
+			return false;
+	}
+	/* it's a new AF, add it to the list */
+	af_set->af[(af_set->size)++] = freq;
+	return true;
+}
+
+/* extracts the AF information from Block 3 of type 0A groups, and tries
+ * to add them to the AF list with a helper function */
+static bool rds_add_af(struct rds_private_state *priv_state)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+
+	/* AFs are submitted in Block 3 of type 0A groups */
+	uint8_t c_msb = priv_state->rds_group.data_c_msb;
+	uint8_t c_lsb = priv_state->rds_group.data_c_lsb;
+	bool updated_af = false;
+	struct v4l2_rds_af_set *af_set = &handle->rds_af;
+
+	/* the 4 8-bit values in the block's data fields (c_msb/c_lsb,
+	 * d_msb/d_lsb) represent either a carrier frequency (1..204)
+	 * or a special meaning (205..255).
+	 * Translation tables can be found in IEC 62106 section 6.2.1.6 */
+
+	/* 250: LF / MF frequency follows */
+	if (c_msb == 250) {
+		if (rds_add_af_to_list(af_set, c_lsb, false))
+			updated_af = true;
+		c_lsb = 0; /* invalidate */
+	}
+	/* 224..249: announcement of AF count (224=0, 249=25)*/
+	if (c_msb >= 224 && c_msb <= 249)
+		af_set->announced_af = c_msb - 224;
+	/* check if the data represents an AF (for 1 =< val <= 204 the
+	 * value represents an AF) */
+	if (c_msb < 205)
+		if (rds_add_af_to_list(af_set, c_msb, true))
+			updated_af = true;
+	if (c_lsb < 205)
+		if (rds_add_af_to_list(af_set, c_lsb, true))
+			updated_af = true;
+	/* did we receive all announced AFs? */
+	if (af_set->size >= af_set->announced_af && af_set->announced_af != 0)
+		handle->valid_fields |= V4L2_RDS_AF;
+	return updated_af;
+}
+
+/* adds one char of the ps name to temporal storage, the value is validated
+ * if it is received twice in a row
+ * @pos:	position of the char within the PS name (0..7)
+ * @ps_char:	the new character to be added
+ * @return:	true, if all 8 temporal ps chars have been validated */
+static bool rds_add_ps(struct rds_private_state *priv_state, uint8_t pos, uint8_t ps_char)
+{
+	if (ps_char == priv_state->new_ps[pos]) {
+		priv_state->new_ps_valid[pos] = 1;
+	} else {
+		priv_state->new_ps[pos] = ps_char;
+		memset(priv_state->new_ps_valid, 0, 8);
+	}
+
+	/* check if all ps positions have been validated */
+	for (int i = 0; i < 8; i++)
+		if (priv_state->new_ps_valid[i] != 1)
+			return false;
+	return true;
+}
+
+/* group of functions to decode successfully received RDS groups into
+ * easily accessible data fields
+ *
+ * group 0: basic tuning and switching */
+static uint32_t rds_decode_group0(struct rds_private_state *priv_state)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+	struct v4l2_rds_group *grp = &priv_state->rds_group;
+	bool new_ps = false;
+	bool tmp;
+	uint32_t updated_fields = 0;
+
+	/* bit 4 of block B contains the TA flag */
+	tmp = grp->data_b_lsb & 0x10;
+	if (handle->ta != tmp) {
+		handle->ta = tmp;
+		updated_fields |= V4L2_RDS_TA;
+	}
+	handle->valid_fields |= V4L2_RDS_TA;
+
+	/* bit 3 of block B contains the Music/Speech flag */
+	tmp = grp->data_b_lsb & 0x08;
+	if (handle->ms != tmp) {
+		handle->ms = tmp;
+		updated_fields |= V4L2_RDS_MS;
+	}
+	handle->valid_fields |= V4L2_RDS_MS;
+
+	/* bit 0-1 of block b contain program service name and decoder
+	 * control segment address */
+	uint8_t segment = grp->data_b_lsb & 0x03;
+
+	/* put the received station-name characters into the correct position
+	 * of the station name, and check if the new PS is validated */
+	rds_add_ps(priv_state, segment * 2, grp->data_d_msb);
+	new_ps = rds_add_ps(priv_state, segment * 2 + 1, grp->data_d_lsb);
+	if (new_ps) {
+		/* check if new PS is the same as the old one */
+		if (memcmp(priv_state->new_ps, handle->ps, 8) != 0) {
+			memcpy(handle->ps, priv_state->new_ps, 8);
+			updated_fields |= V4L2_RDS_PS;
+		}
+		handle->valid_fields |= V4L2_RDS_PS;
+	}
+
+	/* bit 2 of block B contains 1 bit of the Decoder Control Information (DI)
+	 * the segment number defines the bit position
+	 * New bits are only accepted the segments arrive in the correct order */
+	bool bit2 = grp->data_b_lsb & 0x04;
+	if (segment == 0 || segment == priv_state->next_di_segment) {
+		switch (segment) {
+		case 0:
+			priv_state->new_di = set_bit(priv_state->new_di,
+				V4L2_RDS_FLAG_STEREO, bit2);
+			priv_state->next_di_segment = 1;
+			break;
+		case 1:
+			priv_state->new_di = set_bit(priv_state->new_di,
+				V4L2_RDS_FLAG_ARTIFICIAL_HEAD, bit2);
+			priv_state->next_di_segment = 2;
+			break;
+		case 2:
+			priv_state->new_di = set_bit(priv_state->new_di,
+				V4L2_RDS_FLAG_COMPRESSED, bit2);
+			priv_state->next_di_segment = 3;
+			break;
+		case 3:
+			priv_state->new_di = set_bit(priv_state->new_di,
+				V4L2_RDS_FLAG_STATIC_PTY, bit2);
+			/* check if the value of DI has changed, and store
+			 * and signal DI update in case */
+			if (handle->di != priv_state->new_di) {
+				handle->di = priv_state->new_di;
+				updated_fields |= V4L2_RDS_DI;
+			}
+			priv_state->next_di_segment = 0;
+			handle->valid_fields |= V4L2_RDS_DI;
+			break;
+		}
+	} else {
+		/* wrong order of DI segments -> restart */
+		priv_state->next_di_segment = 0;
+		priv_state->new_di = 0;
+	}
+
+	/* version A groups contain AFs in block C */
+	if (grp->group_version == 'A')
+		if (rds_add_af(priv_state))
+			updated_fields |= V4L2_RDS_AF;
+
+	return updated_fields;
+}
+
+/* group 1: slow labeling codes & program item number */
+static uint32_t rds_decode_group1(struct rds_private_state *priv_state)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+	struct v4l2_rds_group *grp = &priv_state->rds_group;
+	uint32_t updated_fields = 0;
+	uint8_t variant_code = 0;
+
+	/* version A groups contain slow labeling codes,
+	 * version B groups only contain program item number which is a
+	 * very uncommonly used feature */
+	if (grp->group_version != 'A')
+		return 0;
+
+	/* bit 14-12 of block c contain the variant code */
+	variant_code = (grp->data_c_msb >> 4) & 0x07;
+	if (variant_code == 0) {
+		/* var 0 -> ECC, only accept if same lc is
+		 * received twice */
+		if (grp->data_c_lsb == priv_state->new_ecc) {
+			handle->valid_fields |= V4L2_RDS_ECC;
+			if (handle->ecc != grp->data_c_lsb)
+				updated_fields |= V4L2_RDS_ECC;
+			handle->ecc = grp->data_c_lsb;
+		} else {
+			priv_state->new_ecc = grp->data_c_lsb;
+		}
+	} else if (variant_code == 0x03) {
+		/* var 0x03 -> Language Code, only accept if same lc is
+		 * received twice */
+		if (grp->data_c_lsb == priv_state->new_lc) {
+			handle->valid_fields |= V4L2_RDS_LC;
+			updated_fields |= V4L2_RDS_LC;
+			handle->lc = grp->data_c_lsb;
+		} else {
+			priv_state->new_lc = grp->data_c_lsb;
+		}
+	}
+	return updated_fields;
+}
+
+/* group 2: radio text */
+static uint32_t rds_decode_group2(struct rds_private_state *priv_state)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+	struct v4l2_rds_group *grp = &priv_state->rds_group;
+	uint32_t updated_fields = 0;
+
+	/* bit 0-3 of block B contain the segment code */
+	uint8_t segment = grp->data_b_lsb & 0x0f;
+	/* bit 4 of block b contains the A/B text flag (new radio text
+	 * will be transmitted) */
+	bool rt_ab_flag_n = grp->data_b_lsb & 0x10;
+
+	/* new Radio Text will be transmitted */
+	if (rt_ab_flag_n != handle->rt_ab_flag) {
+		handle->rt_ab_flag = rt_ab_flag_n;
+		memset(handle->rt, 0, 64);
+		handle->valid_fields &= ~V4L2_RDS_RT;
+		updated_fields |= V4L2_RDS_RT;
+		priv_state->next_rt_segment = 0;
+	}
+
+	/* further decoding of data depends on type of message (A or B)
+	 * Type A allows RTs with a max length of 64 chars
+	 * Type B allows RTs with a max length of 32 chars */
+	if (grp->group_version == 'A') {
+		if (segment == 0 || segment == priv_state->next_rt_segment) {
+			priv_state->new_rt[segment * 4] = grp->data_c_msb;
+			priv_state->new_rt[segment * 4 + 1] = grp->data_c_lsb;
+			priv_state->new_rt[segment * 4 + 2] = grp->data_d_msb;
+			priv_state->new_rt[segment * 4 + 3] = grp->data_d_lsb;
+			priv_state->next_rt_segment = segment + 1;
+			if (segment == 0x0f) {
+				handle->rt_length = 64;
+				handle->valid_fields |= V4L2_RDS_RT;
+				if (memcmp(handle->rt, priv_state->new_rt, 64)) {
+					memcpy(handle->rt, priv_state->new_rt, 64);
+					updated_fields |= V4L2_RDS_RT;
+				}
+				priv_state->next_rt_segment = 0;
+			}
+		}
+	} else {
+		if (segment == 0 || segment == priv_state->next_rt_segment) {
+			priv_state->new_rt[segment * 2] = grp->data_d_msb;
+			priv_state->new_rt[segment * 2 + 1] = grp->data_d_lsb;
+			/* PI code in block C will be ignored */
+			priv_state->next_rt_segment = segment + 1;
+			if (segment == 0x0f) {
+				handle->rt_length = 32;
+				handle->valid_fields |= V4L2_RDS_RT;
+				updated_fields |= V4L2_RDS_RT;
+				if (memcmp(handle->rt, priv_state->new_rt, 32)) {
+					memcpy(handle->rt, priv_state->new_rt, 32);
+					updated_fields |= V4L2_RDS_RT;
+				}
+				priv_state->next_rt_segment = 0;
+			}
+		}
+	}
+
+	/* determine if complete rt was received
+	 * a carriage return (0x0d) can end a message early */
+	for (int i = 0; i < 64; i++) {
+		if (priv_state->new_rt[i] == 0x0d) {
+			/* replace CR with terminating character */
+			priv_state->new_rt[i] = '\0';
+			handle->rt_length = i;
+			handle->valid_fields |= V4L2_RDS_RT;
+			if (memcmp(handle->rt, priv_state->new_rt, handle->rt_length)) {
+					memcpy(handle->rt, priv_state->new_rt,
+						handle->rt_length);
+					updated_fields |= V4L2_RDS_RT;
+				}
+			priv_state->next_rt_segment = 0;
+		}
+	}
+	return updated_fields;
+}
+
+/* group 3: Open Data Announcements */
+static uint32_t rds_decode_group3(struct rds_private_state *priv_state)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+	struct v4l2_rds_group *grp = &priv_state->rds_group;
+	struct v4l2_rds_oda new_oda;
+	uint32_t updated_fields = 0;
+
+	if (grp->group_version != 'A')
+		return 0;
+
+	/* 0th bit of block b contains Group Type Info version of announced ODA
+	 * Group Type info: 0 = A, 1 = B */
+	new_oda.group_version = (grp->data_b_lsb & 0x01) ? 'B' : 'A';
+	/* 1st to 4th bit contain Group ID of announced ODA */
+	new_oda.group_id = (grp->data_b_lsb & 0x1e) >> 1;
+	/* block D contains the 16bit Application Identification Code */
+	new_oda.aid = (grp->data_d_msb << 8) | grp->data_d_lsb;
+
+	/* try to add the new ODA to the set of defined ODAs */
+	if (rds_add_oda(priv_state, new_oda)) {
+		handle->decode_information |= V4L2_RDS_ODA;
+		updated_fields |= V4L2_RDS_ODA;
+	}
+	return updated_fields;
+}
+
+/* decodes the RDS date/time representation into a standard c representation
+ * that can be used with c-library functions */
+static time_t rds_decode_mjd(const struct rds_private_state *priv_state)
+{
+	struct tm new_time;
+	int y, m, d, k = 0;
+	/* offset is given in multiples of half hrs */
+	uint32_t offset = priv_state->utc_offset & 0x1f;
+	uint32_t local_mjd = priv_state->new_mjd;
+	uint8_t local_hour = priv_state->utc_hour;
+	uint8_t local_minute = priv_state->utc_minute;
+
+	/* add / subtract the local offset to get the local time */
+	/* local offset is expressed in multiples of half hours */
+	if (priv_state->utc_offset & 0x20) { /* bit 5 indicates -/+ */
+		local_hour -= (offset * 2);
+		local_minute -= (offset % 2) * 30;
+	} else {
+		local_hour += (offset * 2);
+		local_minute += (offset % 2) * 30;
+	}
+
+	/* the formulas for the conversion are taken from Annex G of the
+	 * IEC 62106 RDS standard */
+	y = (int)((local_mjd - 15078.2) / 365.25);
+	m = (int)((local_mjd - 14956.1 - (int)(y * 365.25)) / 30.6001);
+	d = (int)(local_mjd - 14956 - (int)(y * 365.25) - (int)(m * 30.6001));
+	if (m == 14 || m == 15)
+		k = 1;
+	y = y + k;
+	m = m - 1 - k*12;
+
+	/* put the values into a tm struct for conversion into time_t value */
+	new_time.tm_sec = 0;
+	new_time.tm_min = priv_state->utc_minute;
+	new_time.tm_hour = priv_state->utc_hour;
+	new_time.tm_mday = d;
+	new_time.tm_mon = m;
+	new_time.tm_year = y;
+	if (priv_state->utc_offset & 0x20) /* bit 5 indicates -/+ */
+		new_time.tm_gmtoff = -2 * offset * 3600;
+	else
+		new_time.tm_gmtoff = 2 * offset * 3600;
+
+	/* convert tm struct to time_t value and return it */
+	return mktime(&new_time);
+}
+
+/* group 4: Date and Time */
+static uint32_t rds_decode_group4(struct rds_private_state *priv_state)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+	struct v4l2_rds_group *grp = &priv_state->rds_group;
+	uint32_t mjd;
+	uint32_t updated_fields = 0;
+
+	if (grp->group_version != 'A')
+		return 0;
+
+	/* bits 0-1 of block b lsb contain bits 15 and 16 of Julian day code
+	 * bits 0-7 of block c msb contain bits 7 to 14 of Julian day code
+	 * bits 1-7 of block c lsb contain bits 0 to 6 of Julian day code */
+	mjd = ((grp->data_b_lsb & 0x03) << 15) |
+		(grp->data_c_msb << 7) | (grp->data_c_lsb >> 1);
+	/* the same mjd has to be received twice in order to accept the data */
+	if (priv_state->new_mjd != mjd) {
+		priv_state->new_mjd = mjd;
+		return 0;
+	}
+	/* same mjd received at least twice --> decode time & date */
+
+	/* bit 0 of block c lsb contains bit 4 of utc_hour
+	 * bits 4-7 of block d contains bits 0 to 3 of utc_hour */
+	priv_state->utc_hour = ((grp->data_c_lsb & 0x01) << 4) |
+		(grp->data_d_msb >> 4);
+
+	/* bits 0-3 of block d msb contain bits 2 to 5 of utc_minute
+	 * bits 6-7 of block d lsb contain bits 0 and 1 utc_minute */
+	priv_state->utc_minute = ((grp->data_d_msb & 0x0f) << 2) |
+		(grp->data_d_lsb >> 6);
+
+	/* bits 0-5 of block d lsb contain bits 0 to 5 of local time offset */
+	priv_state->utc_offset = grp->data_d_lsb & 0x3f;
+
+	/* decode RDS time representation into commonly used c representation */
+	handle->time = rds_decode_mjd(priv_state);
+	updated_fields |= V4L2_RDS_TIME;
+	handle->valid_fields |= V4L2_RDS_TIME;
+	printf("\nLIB: time_t: %ld", handle->time);
+	return updated_fields;
+}
+
+/* group 10: Program Type Name */
+static uint32_t rds_decode_group10(struct rds_private_state *priv_state)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+	struct v4l2_rds_group *grp = &priv_state->rds_group;
+	uint32_t updated_fields = 0;
+	uint8_t ptyn_tmp[4];
+
+	/* bit 0 of block B contain the segment code */
+	uint8_t segment_code = grp->data_b_lsb & 0x01;
+	/* bit 4 of block b contains the A/B text flag (new ptyn
+	 * will be transmitted) */
+	bool ptyn_ab_flag_n = grp->data_b_lsb & 0x10;
+
+	if (grp->group_version != 'A')
+		return 0;
+
+	/* new Program Type Text will be transmitted */
+	if (ptyn_ab_flag_n != handle->ptyn_ab_flag) {
+		handle->ptyn_ab_flag = ptyn_ab_flag_n;
+		memset(handle->ptyn, 0, 8 * sizeof(char));
+		memset(priv_state->new_ptyn, 0, 8 * sizeof(char));
+		memset(priv_state->new_ptyn_valid, 0, 2 * sizeof(bool));
+		handle->valid_fields &= ~V4L2_RDS_PTYN;
+		updated_fields |= V4L2_RDS_PTYN;
+	}
+	/* copy chars to designated position within temp text field */
+	ptyn_tmp[0] = grp->data_c_msb;
+	ptyn_tmp[1] = grp->data_c_lsb;
+	ptyn_tmp[2] = grp->data_d_msb;
+	ptyn_tmp[3] = grp->data_d_lsb;
+
+	/* only validate ptyn segment if the same data is received twice */
+	if (memcmp(ptyn_tmp, priv_state->new_ptyn[segment_code], 4) == 0) {
+		priv_state->new_ptyn_valid[segment_code] = true;
+	} else {
+		for (int i = 0; i < 4; i++)
+			priv_state->new_ptyn[segment_code][i] = ptyn_tmp[i];
+		priv_state->new_ptyn_valid[segment_code] = false;
+	}
+
+	/* if both ptyn segments have been validated, accept the new ptyn */
+	if (priv_state->new_ptyn_valid[0] && priv_state->new_ptyn_valid[1]) {
+		for (int i = 0; i < 4; i++) {
+			handle->ptyn[i] = priv_state->new_ptyn[0][i];
+			handle->ptyn[4 + i] = priv_state->new_ptyn[1][i];
+		}
+		handle->valid_fields |= V4L2_RDS_PTYN;
+		updated_fields |= V4L2_RDS_PTYN;
+	}
+	return updated_fields;
+}
+
+typedef uint32_t (*decode_group_func)(struct rds_private_state *);
+
+/* array of function pointers to contain all group specific decoding functions */
+static const decode_group_func decode_group[16] = {
+	[0] = rds_decode_group0,
+	[1] = rds_decode_group1,
+	[2] = rds_decode_group2,
+	[3] = rds_decode_group3,
+	[4] = rds_decode_group4,
+	[10] = rds_decode_group10,
+};
+
+static uint32_t rds_decode_group(struct rds_private_state *priv_state)
+{
+	struct v4l2_rds *handle = &priv_state->handle;
+	uint8_t group_id = priv_state->rds_group.group_id;
+
+	/* count the group type, and decode it if it is supported */
+	handle->rds_statistics.group_type_cnt[group_id]++;
+	if (decode_group[group_id])
+		return (*decode_group[group_id])(priv_state);
+	return 0;
+}
+
+struct v4l2_rds *v4l2_rds_create(bool is_rbds)
+{
+	struct rds_private_state *internal_handle =
+		calloc(1, sizeof(struct rds_private_state));
+	internal_handle->handle.version = V4L2_RDS_VERSION;
+	internal_handle->handle.is_rbds = is_rbds;
+
+	return (struct v4l2_rds *)internal_handle;
+}
+
+void v4l2_rds_destroy(struct v4l2_rds *handle)
+{
+	if (handle)
+		free(handle);
+}
+
+void v4l2_rds_reset(struct v4l2_rds *handle, bool reset_statistics)
+{
+	/* treat the private & the public part of the handle */
+	struct rds_private_state *priv_state = (struct rds_private_state *) handle;
+
+	/* store members of handle that shouldn't be affected by reset */
+	bool is_rbds = handle->is_rbds;
+	struct v4l2_rds_statistics rds_statistics = handle->rds_statistics;
+
+	/* reset the handle */
+	memset(priv_state, 0, sizeof(*priv_state));
+	/* re-initialize members */
+	handle->is_rbds = is_rbds;
+	if (!reset_statistics)
+		handle->rds_statistics = rds_statistics;
+}
+
+/* function decodes raw RDS data blocks into complete groups. Once a full group is
+ * successfully received, the group is decoded into the fields of the RDS handle.
+ * Decoding is only done once a complete group was received. This is slower compared
+ * to decoding the group type independent information up front, but adds a barrier
+ * against corrupted data (happens regularly when reception is weak) */
+uint32_t v4l2_rds_add(struct v4l2_rds *handle, struct v4l2_rds_data *rds_data)
+{
+	struct rds_private_state *priv_state = (struct rds_private_state *) handle;
+	struct v4l2_rds_data *rds_data_raw = priv_state->rds_data_raw;
+	struct v4l2_rds_statistics *rds_stats = &handle->rds_statistics;
+	uint32_t updated_fields = 0;
+	uint8_t *decode_state = &(priv_state->decode_state);
+
+	/* get the block id by masking out irrelevant bits */
+	int block_id = rds_data->block & V4L2_RDS_BLOCK_MSK;
+
+	rds_stats->block_cnt++;
+	/* check for corrected / uncorrectable errors in the data */
+	if (rds_data->block & V4L2_RDS_BLOCK_ERROR) {
+		block_id = -1;
+		rds_stats->block_error_cnt++;
+	} else if (rds_data->block & V4L2_RDS_BLOCK_CORRECTED) {
+		rds_stats->block_corrected_cnt++;
+	}
+
+	switch (*decode_state) {
+	case RDS_EMPTY:
+		if (block_id == 0) {
+			*decode_state = RDS_A_RECEIVED;
+			/* begin reception of a new data group, reset raw buffer to 0 */
+			memset(rds_data_raw, 0, sizeof(rds_data_raw));
+			rds_data_raw[0] = *rds_data;
+		} else {
+			/* ignore block if it is not the first block of a group */
+			rds_stats->group_error_cnt++;
+		}
+		break;
+
+	case RDS_A_RECEIVED:
+		if (block_id == 1) {
+			*decode_state = RDS_B_RECEIVED;
+			rds_data_raw[1] = *rds_data;
+		} else {
+			/* received block with unexpected block id, reset state machine */
+			rds_stats->group_error_cnt++;
+			*decode_state = RDS_EMPTY;
+		}
+		break;
+
+	case RDS_B_RECEIVED:
+		/* handle type C and C' blocks alike */
+		if (block_id == 2 || block_id ==  4) {
+			*decode_state = RDS_C_RECEIVED;
+			rds_data_raw[2] = *rds_data;
+		} else {
+			rds_stats->group_error_cnt++;
+			*decode_state = RDS_EMPTY;
+		}
+		break;
+
+	case RDS_C_RECEIVED:
+		if (block_id == 3) {
+			*decode_state = RDS_EMPTY;
+			rds_data_raw[3] = *rds_data;
+			/* a full group was received */
+			rds_stats->group_cnt++;
+			/* decode group type independent fields */
+			memset(&priv_state->rds_group, 0, sizeof(priv_state->rds_group));
+			updated_fields |= rds_decode_a(priv_state, &rds_data_raw[0]);
+			updated_fields |= rds_decode_b(priv_state, &rds_data_raw[1]);
+			rds_decode_c(priv_state, &rds_data_raw[2]);
+			rds_decode_d(priv_state, &rds_data_raw[3]);
+			/* decode group type dependent fields */
+			updated_fields |= rds_decode_group(priv_state);
+			return updated_fields;
+		}
+		rds_stats->group_error_cnt++;
+		*decode_state = RDS_EMPTY;
+		break;
+
+	default:
+		/* every unexpected block leads to a reset of the sm */
+		rds_stats->group_error_cnt++;
+		*decode_state = RDS_EMPTY;
+	}
+	/* if we reach here, no RDS group was completed */
+	return 0;
+}
+
+const char *v4l2_rds_get_pty_str(const struct v4l2_rds *handle)
+{
+	const uint8_t pty = handle->pty;
+
+	if (pty >= 32)
+		return NULL;
+
+	static const char *rds_lut[32] = {
+		"None", "News", "Affairs", "Info", "Sport", "Education", "Drama",
+		"Culture", "Science", "Varied Speech", "Pop Music",
+		"Rock Music", "Easy Listening", "Light Classics M",
+		"Serious Classics", "Other Music", "Weather", "Finance",
+		"Children", "Social Affairs", "Religion", "Phone In",
+		"Travel & Touring", "Leisure & Hobby", "Jazz Music",
+		"Country Music", "National Music", "Oldies Music", "Folk Music",
+		"Documentary", "Alarm Test", "Alarm!"
+	};
+	static const char *rbds_lut[32] = {
+		"None", "News", "Information", "Sports", "Talk", "Rock",
+		"Classic Rock", "Adult Hits", "Soft Rock", "Top 40", "Country",
+		"Oldies", "Soft", "Nostalgia", "Jazz", "Classical",
+		"R&B", "Soft R&B", "Foreign Language", "Religious Music",
+		"Religious Talk", "Personality", "Public", "College",
+		"Spanish Talk", "Spanish Music", "Hip-Hop", "Unassigned",
+		"Unassigned", "Weather", "Emergency Test", "Emergency"
+	};
+
+	return handle->is_rbds ? rbds_lut[pty] : rds_lut[pty];
+}
+
+const char *v4l2_rds_get_country_str(const struct v4l2_rds *handle)
+{
+	/* defines the  region of the world
+	 * 0x0e = Europe, 0x0d = Africa, 0x0a = ITU Region 2,
+	 * 0x0f = ITU Region 3 */
+	uint8_t ecc_h = handle->ecc >> 4;
+	/* sub identifier for the region, valid range 0..4 */
+	uint8_t ecc_l = handle->ecc & 0x0f;
+	/* bits 12-15 pi contain the country code */
+	uint8_t country_code = handle->pi >> 12;
+
+	/* LUT for European countries
+	 * the standard doesn't define every possible value but leaves some
+	 * undefined. An exception is e4-7 which is defined as a dash ("-") */
+	static const char *e_lut[5][16] = {
+	{
+		NULL, "DE", "DZ", "AD", "IL", "IT", "BE", "RU", "PS", "AL",
+		"AT", "HU", "MT", "DE", NULL, "EG"
+	}, {
+		NULL, "GR", "CY", "SM", "CH", "JO", "FI", "LU", "BG", "DK",
+		"GI", "IQ", "GB", "LY", "RO", "FR"
+	}, {
+		NULL, "MA", "CZ", "PL", "VA", "SK", "SY", "TN", NULL, "LI",
+		"IS", "MC", "LT", "RS", "ES", "NO"
+	}, {
+		NULL, "ME", "IE", "TR", "MK", NULL, NULL, NULL, "NL", "LV",
+		"LB", "AZ", "HR", "KZ", "SE", "BY"
+	}, {
+		NULL, "MD", "EE", "KG", NULL, NULL, "UA", "-", "PT", "SI",
+		"AM", NULL, "GE", NULL, NULL, "BA"
+	}
+	};
+
+	/* for now only European countries are supported -> ECC E0 - E4
+	 * but the standard defines country codes for the whole world,
+	 * that's the reason for returning "unknown" instead of a NULL
+	 * pointer until all defined countries are supported */
+	if (ecc_h == 0x0e && ecc_l <= 0x04)
+		return e_lut[ecc_l][country_code];
+	return "Unknown";
+}
+
+static const char *rds_language_lut(const uint8_t lc)
+{
+	const uint8_t max_lc = 127;
+	const char *language;
+
+	static const char *language_lut[128] = {
+		"Unknown", "Albanian", "Breton", "Catalan",
+		"Croatian", "Welsh", "Czech", "Danish",
+		"German", "English", "Spanish", "Esperanto",
+		"Estonian", "Basque", "Faroese", "French",
+		"Frisian", "Irish", "Gaelic", "Galician",
+		"Icelandic", "Italian", "Lappish", "Latin",
+		"Latvian", "Luxembourgian", "Lithuanian", "Hungarian",
+		"Maltese", "Dutch", "Norwegian", "Occitan",
+		"Polish", "Portuguese", "Romanian", "Ramansh",
+		"Serbian", "Slovak", "Slovene", "Finnish",
+		"Swedish", "Turkish", "Flemish", "Walloon",
+		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+		NULL, "Zulu", "Vietnamese", "Uzbek",
+		"Urdu", "Ukrainian", "Thai", "Telugu",
+		"Tatar", "Tamil", "Tadzhik", "Swahili",
+		"Sranan Tongo", "Somali", "Sinhalese", "Shona",
+		"Serbo-Croat", "Ruthenian", "Russian", "Quechua",
+		"Pushtu", "Punjabi", "Persian", "Papamiento",
+		"Oriya", "Nepali", "Ndebele", "Marathi",
+		"Moldavian", "Malaysian", "Malagasay", "Macedonian",
+		"Laotian", "Korean", "Khmer", "Kazahkh",
+		"Kannada", "Japanese", "Indonesian", "Hindi",
+		"Hebrew", "Hausa", "Gurani", "Gujurati",
+		"Greek", "Georgian", "Fulani", "Dani",
+		"Churash", "Chinese", "Burmese", "Bulgarian",
+		"Bengali", "Belorussian", "Bambora", "Azerbaijani",
+		"Assamese", "Armenian", "Arabic", "Amharic"
+	};
+
+	/* filter invalid values and undefined table entries */
+	language = (lc > max_lc) ? "Unknown" : language_lut[lc];
+	if (!language)
+		return "Unknown";
+	return language;
+}
+
+const char *v4l2_rds_get_language_str(const struct v4l2_rds *handle)
+{
+	return rds_language_lut(handle->lc);
+}
+
+const char *v4l2_rds_get_coverage_str(const struct v4l2_rds *handle)
+{
+	/* bits 8-11 contain the area coverage code */
+	uint8_t coverage = (handle->pi >> 8) & 0X0f;
+	static const char *coverage_lut[16] = {
+		"Local", "International", "National", "Supra-Regional",
+		"Regional 1", "Regional 2", "Regional 3", "Regional 4",
+		"Regional 5", "Regional 6", "Regional 7", "Regional 8",
+		"Regional 9", "Regional 10", "Regional 11", "Regional 12"
+	};
+
+	return coverage_lut[coverage];
+}
+
+const struct v4l2_rds_group *v4l2_rds_get_group
+	(const struct v4l2_rds *handle)
+{
+	struct rds_private_state *priv_state = (struct rds_private_state *) handle;
+	return &priv_state->rds_group;
+}
diff --git a/lib/libv4l2rds/libv4l2rds.pc.in b/lib/libv4l2rds/libv4l2rds.pc.in
new file mode 100644
index 0000000..cc1d5f6
--- /dev/null
+++ b/lib/libv4l2rds/libv4l2rds.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+libdir=@libdir@
+
+Name: libv4l2rds
+Description: v4l2 RDS decode library
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lv4l2rds
+Libs.private: -lpthread
+Cflags: -I${includedir}
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 2/2] Add rds-ctl tool (with changes proposed in RFC)
  2012-08-07 15:11   ` [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC) Konke Radlow
@ 2012-08-07 15:11     ` Konke Radlow
  2012-08-09 12:05       ` Hans de Goede
  2012-08-09  7:22     ` [RFC PATCH 1/2] Add libv4l2rds library " Gregor Jasny
                       ` (2 subsequent siblings)
  3 siblings, 1 reply; 15+ messages in thread
From: Konke Radlow @ 2012-08-07 15:11 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, hdegoede, koradlow

---
 Makefile.am               |    3 +-
 configure.ac              |    1 +
 utils/rds-ctl/Makefile.am |    5 +
 utils/rds-ctl/rds-ctl.cpp |  959 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 967 insertions(+), 1 deletion(-)
 create mode 100644 utils/rds-ctl/Makefile.am
 create mode 100644 utils/rds-ctl/rds-ctl.cpp

diff --git a/Makefile.am b/Makefile.am
index 621d8d9..8ef0d00 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,7 +18,8 @@ SUBDIRS += \
 	utils/v4l2-compliance \
 	utils/v4l2-ctl \
 	utils/v4l2-dbg \
-	utils/v4l2-sysfs-path
+	utils/v4l2-sysfs-path \
+	utils/rds-ctl
 
 if LINUX_OS
 SUBDIRS += \
diff --git a/configure.ac b/configure.ac
index 1109c4d..a99f1c6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -28,6 +28,7 @@ AC_CONFIG_FILES([Makefile
 	utils/v4l2-sysfs-path/Makefile
 	utils/xc3028-firmware/Makefile
 	utils/qv4l2/Makefile
+	utils/rds-ctl/Makefile
 
 	contrib/freebsd/Makefile
 	contrib/test/Makefile
diff --git a/utils/rds-ctl/Makefile.am b/utils/rds-ctl/Makefile.am
new file mode 100644
index 0000000..9a84257
--- /dev/null
+++ b/utils/rds-ctl/Makefile.am
@@ -0,0 +1,5 @@
+bin_PROGRAMS = rds-ctl
+
+rds_ctl_SOURCES = rds-ctl.cpp
+rds_ctl_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4l2rds/libv4l2rds.la
+
diff --git a/utils/rds-ctl/rds-ctl.cpp b/utils/rds-ctl/rds-ctl.cpp
new file mode 100644
index 0000000..072ffb7
--- /dev/null
+++ b/utils/rds-ctl/rds-ctl.cpp
@@ -0,0 +1,959 @@
+/*
+ * rds-ctl.cpp is based on v4l2-ctl.cpp
+ *
+ * the following applies for all RDS related parts:
+ * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ * Author: Konke Radlow <koradlow@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <locale.h>
+#include <inttypes.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <config.h>
+#include <signal.h>
+
+#include <linux/videodev2.h>
+#include <libv4l2.h>
+#include <libv4l2rds.h>
+
+#include <list>
+#include <vector>
+#include <map>
+#include <string>
+#include <algorithm>
+
+#define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0])))
+
+typedef std::vector<std::string> dev_vec;
+typedef std::map<std::string, std::string> dev_map;
+
+/* Short option list
+
+   Please keep in alphabetical order.
+   That makes it easier to see which short options are still free.
+
+   In general the lower case is used to set something and the upper
+   case is used to retrieve a setting. */
+enum Option {
+	OptSetDevice = 'd',
+	OptGetDriverInfo = 'D',
+	OptGetFreq = 'F',
+	OptSetFreq = 'f',
+	OptHelp = 'h',
+	OptReadRds = 'R',
+	OptGetTuner = 'T',
+	OptSetTuner = 't',
+	OptUseWrapper = 'w',
+	OptAll = 128,
+	OptFreqSeek,
+	OptListDevices,
+	OptListFreqBands,
+	OptOpenFile,
+	OptPrintBlock,
+	OptSilent,
+	OptTunerIndex,
+	OptVerbose,
+	OptWaitLimit,
+	OptLast = 256
+};
+
+struct ctl_parameters {
+	bool terminate_decoding;
+	char options[OptLast];
+	char fd_name[80];
+	bool filemode_active;
+	double freq;
+	uint32_t wait_limit;
+	uint8_t tuner_index;
+	struct v4l2_hw_freq_seek freq_seek;
+};
+
+static struct ctl_parameters params;
+static int app_result;
+
+static struct option long_options[] = {
+	{"all", no_argument, 0, OptAll},
+	{"device", required_argument, 0, OptSetDevice},
+	{"file", required_argument, 0, OptOpenFile},
+	{"freq-seek", required_argument, 0, OptFreqSeek},
+	{"get-freq", no_argument, 0, OptGetFreq},
+	{"get-tuner", no_argument, 0, OptGetTuner},
+	{"help", no_argument, 0, OptHelp},
+	{"info", no_argument, 0, OptGetDriverInfo},
+	{"list-devices", no_argument, 0, OptListDevices},
+	{"list-freq-bands", no_argument, 0, OptListFreqBands},
+	{"print-block", no_argument, 0, OptPrintBlock},
+	{"read-rds", no_argument, 0, OptReadRds},
+	{"set-freq", required_argument, 0, OptSetFreq},
+	{"tuner-index", required_argument, 0, OptTunerIndex},
+	{"verbose", no_argument, 0, OptVerbose},
+	{"wait-limit", required_argument, 0, OptWaitLimit},
+	{"wrapper", no_argument, 0, OptUseWrapper},
+	{0, 0, 0, 0}
+};
+
+static void usage_hint(void)
+{
+	fprintf(stderr, "Try 'rds-ctl --help' for more information.\n");
+}
+
+static void usage_common(void)
+{
+	printf("\nGeneral/Common options:\n"
+	       "  --all              display all information available\n"
+	       "  -D, --info         show driver info [VIDIOC_QUERYCAP]\n"
+	       "  -d, --device=<dev> use device <dev>\n"
+	       "                     if <dev> is a single digit, then /dev/video<dev> is used\n"
+	       "                     default: checks for RDS-capable devices,\n"
+	       "                     uses device with lowest ID\n"
+	       "  -h, --help         display this help message\n"
+	       "  -w, --wrapper      use the libv4l2 wrapper library.\n"
+	       "  --list-devices     list all v4l radio devices with RDS capabilities\n"
+	       );
+}
+
+static void usage_tuner(void)
+{
+	printf("\nTuner/Modulator options:\n"
+	       "  -F, --get-freq     query the frequency [VIDIOC_G_FREQUENCY]\n"
+	       "  -f, --set-freq=<freq>\n"
+	       "                     set the frequency to <freq> MHz [VIDIOC_S_FREQUENCY]\n"
+	       "  -T, --get-tuner    query the tuner settings [VIDIOC_G_TUNER]\n"
+	       "  --tuner-index=<idx> Use idx as tuner idx for tuner/modulator commands\n"
+	       "  --freq-seek=dir=<0/1>,wrap=<0/1>,spacing=<hz>\n"
+	       "                     perform a hardware frequency seek [VIDIOC_S_HW_FREQ_SEEK]\n"
+	       "                     dir is 0 (seek downward) or 1 (seek upward)\n"
+	       "                     wrap is 0 (do not wrap around) or 1 (wrap around)\n"
+	       "                     spacing sets the seek resolution (use 0 for default)\n"
+	       "  --list-freq-bands  display all frequency bands for the tuner/modulator\n"
+	       "                     [VIDIOC_ENUM_FREQ_BANDS]\n"
+	       );
+}
+
+static void usage_rds(void)
+{
+	printf("\nRDS options: \n"
+	       "  -R, --read-rds\n"
+	       "                     enable reading of RDS data from device\n"
+	       "  --file=<path>\n"
+	       "                     open a RDS stream file dump instead of a device\n"
+	       "                     all General and Tuner Options are disabled in this mode\n"
+	       "  --wait-limit=<ms>\n"
+	       "                     defines the maximum wait duration for avaibility of new\n"
+	       "                     RDS data\n"
+	       "                     <default>: 5000ms\n"
+	       "  --print-block\n"
+	       "                     prints all valid RDS fields, whenever a value is updated\n"
+	       "                     instead of printing only updated values\n"
+	       "  --verbose\n"
+	       "                     turn on verbose mode - every received RDS group\n"
+	       "                     will be printed\n"
+	       );
+}
+
+static void usage(void)
+{
+	printf("Usage:\n");
+	usage_common();
+	usage_tuner();
+	usage_rds();
+}
+
+static void signal_handler_interrupt(int signum)
+{
+	fprintf(stderr, "Interrupt received: Terminating program\n");
+	params.terminate_decoding = true;
+}
+
+static int test_open(const char *file, int oflag)
+{
+ 	return params.options[OptUseWrapper] ? v4l2_open(file, oflag) : open(file, oflag);
+}
+
+static int test_close(int fd)
+{
+	return params.options[OptUseWrapper] ? v4l2_close(fd) : close(fd);
+}
+
+static int test_ioctl(int fd, int cmd, void *arg)
+{
+	return params.options[OptUseWrapper] ? v4l2_ioctl(fd, cmd, arg) : ioctl(fd, cmd, arg);
+}
+
+static int doioctl_name(int fd, unsigned long int request, void *parm, const char *name)
+{
+	int retval = test_ioctl(fd, request, parm);
+
+	if (retval < 0) {
+		app_result = -1;
+	}
+	if (params.options[OptSilent]) return retval;
+	if (retval < 0)
+		printf("%s: failed: %s\n", name, strerror(errno));
+	else if (params.options[OptVerbose])
+		printf("%s: ok\n", name);
+
+	return retval;
+}
+
+#define doioctl(n, r, p) doioctl_name(n, r, p, #r)
+
+static const char *audmode2s(int audmode)
+{
+	switch (audmode) {
+		case V4L2_TUNER_MODE_STEREO: return "stereo";
+		case V4L2_TUNER_MODE_LANG1: return "lang1";
+		case V4L2_TUNER_MODE_LANG2: return "lang2";
+		case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual";
+		case V4L2_TUNER_MODE_MONO: return "mono";
+		default: return "unknown";
+	}
+}
+
+static std::string rxsubchans2s(int rxsubchans)
+{
+	std::string s;
+
+	if (rxsubchans & V4L2_TUNER_SUB_MONO)
+		s += "mono ";
+	if (rxsubchans & V4L2_TUNER_SUB_STEREO)
+		s += "stereo ";
+	if (rxsubchans & V4L2_TUNER_SUB_LANG1)
+		s += "lang1 ";
+	if (rxsubchans & V4L2_TUNER_SUB_LANG2)
+		s += "lang2 ";
+	if (rxsubchans & V4L2_TUNER_SUB_RDS)
+		s += "rds ";
+	return s;
+}
+
+static std::string tcap2s(unsigned cap)
+{
+	std::string s;
+
+	if (cap & V4L2_TUNER_CAP_LOW)
+		s += "62.5 Hz ";
+	else
+		s += "62.5 kHz ";
+	if (cap & V4L2_TUNER_CAP_NORM)
+		s += "multi-standard ";
+	if (cap & V4L2_TUNER_CAP_HWSEEK_BOUNDED)
+		s += "hwseek-bounded ";
+	if (cap & V4L2_TUNER_CAP_HWSEEK_WRAP)
+		s += "hwseek-wrap ";
+	if (cap & V4L2_TUNER_CAP_STEREO)
+		s += "stereo ";
+	if (cap & V4L2_TUNER_CAP_LANG1)
+		s += "lang1 ";
+	if (cap & V4L2_TUNER_CAP_LANG2)
+		s += "lang2 ";
+	if (cap & V4L2_TUNER_CAP_RDS)
+		s += "rds ";
+	if (cap & V4L2_TUNER_CAP_RDS_BLOCK_IO)
+		s += "rds block I/O ";
+	if (cap & V4L2_TUNER_CAP_RDS_CONTROLS)
+		s += "rds control ";
+	if (cap & V4L2_TUNER_CAP_FREQ_BANDS)
+		s += "freq-bands ";
+	if (cap & V4L2_TUNER_CAP_HWSEEK_PROG_LIM)
+		s += "hwseek-prog-lim ";
+	return s;
+}
+
+static std::string cap2s(unsigned cap)
+{
+	std::string s;
+
+	if (cap & V4L2_CAP_RDS_CAPTURE)
+		s += "\t\tRDS Capture\n";
+	if (cap & V4L2_CAP_RDS_OUTPUT)
+		s += "\t\tRDS Output\n";
+	if (cap & V4L2_CAP_TUNER)
+		s += "\t\tTuner\n";
+	if (cap & V4L2_CAP_MODULATOR)
+		s += "\t\tModulator\n";
+	if (cap & V4L2_CAP_AUDIO)
+		s += "\t\tAudio\n";
+	if (cap & V4L2_CAP_RADIO)
+		s += "\t\tRadio\n";
+	if (cap & V4L2_CAP_READWRITE)
+		s += "\t\tRead/Write\n";
+	if (cap & V4L2_CAP_ASYNCIO)
+		s += "\t\tAsync I/O\n";
+	if (cap & V4L2_CAP_STREAMING)
+		s += "\t\tStreaming\n";
+	if (cap & V4L2_CAP_DEVICE_CAPS)
+		s += "\t\tDevice Capabilities\n";
+	return s;
+}
+
+static std::string modulation2s(unsigned modulation)
+{
+	switch (modulation) {
+	case V4L2_BAND_MODULATION_VSB:
+		return "VSB";
+	case V4L2_BAND_MODULATION_FM:
+		return "FM";
+	case V4L2_BAND_MODULATION_AM:
+		return "AM";
+	}
+	return "Unknown";
+}
+
+static bool is_radio_dev(const char *name)
+{
+	return !memcmp(name, "radio", 5);
+}
+
+static void print_devices(dev_vec files)
+{
+	dev_map cards;
+	int fd = -1;
+	std::string bus_info;
+	struct v4l2_capability vcap;
+
+	for (dev_vec::iterator iter = files.begin();
+		iter != files.end(); ++iter) {
+		fd = open(iter->c_str(), O_RDWR);
+		memset(&vcap, 0, sizeof(vcap));
+		if (fd < 0)
+			continue;
+		doioctl(fd, VIDIOC_QUERYCAP, &vcap);
+		close(fd);
+		bus_info = (const char *)vcap.bus_info;
+	if (cards[bus_info].empty())
+			cards[bus_info] += std::string((char *)vcap.card)
+				+ " (" + bus_info + "):\n";
+		cards[bus_info] += "\t" + (*iter);
+		cards[bus_info] += "\n";
+	}
+	for (dev_map::iterator iter = cards.begin();
+			iter != cards.end(); ++iter) {
+		printf("%s\n", iter->second.c_str());
+	}
+}
+static dev_vec list_devices(void)
+{
+	DIR *dp;
+	struct dirent *ep;
+	dev_vec files;
+	dev_map links;
+
+	struct v4l2_tuner vt;
+
+	dp = opendir("/dev");
+	if (dp == NULL) {
+		perror ("Couldn't open the directory");
+		exit(1);
+	}
+	while ((ep = readdir(dp)))
+		if (is_radio_dev(ep->d_name))
+			files.push_back(std::string("/dev/") + ep->d_name);
+	closedir(dp);
+
+	/* Iterate through all devices, and remove all non-accessible devices
+	 * and all devices that don't offer the RDS_BLOCK_IO capability */
+	for (dev_vec::iterator iter = files.begin();
+			iter != files.end(); ++iter) {
+		int fd = open(iter->c_str(), O_RDONLY | O_NONBLOCK);
+		std::string bus_info;
+
+		if (fd < 0) {
+			iter = files.erase(iter);
+			continue;
+		}
+		memset(&vt, 0, sizeof(vt));
+		if (doioctl(fd, VIDIOC_G_TUNER, &vt) != 0) {
+			close(fd);
+			iter = files.erase(iter);
+			continue;
+		}
+		/* remove device if it doesn't support rds block I/O */
+		if (!vt.capability & V4L2_TUNER_CAP_RDS_BLOCK_IO)
+			iter = files.erase(iter);
+	}
+	return files;
+}
+
+static int parse_subopt(char **subs, const char * const *subopts, char **value)
+{
+	int opt = getsubopt(subs, (char * const *)subopts, value);
+
+	if (opt == -1) {
+		fprintf(stderr, "Invalid suboptions specified\n");
+		return -1;
+	}
+	if (value == NULL) {
+		fprintf(stderr, "No value given to suboption <%s>\n",
+				subopts[opt]);
+		return -1;
+	}
+	return opt;
+}
+
+static void parse_freq_seek(char *optarg, struct v4l2_hw_freq_seek &seek)
+{
+	char *value;
+	char *subs = optarg;
+
+	while (*subs != '\0') {
+		static const char *const subopts[] = {
+			"dir",
+			"wrap",
+			"spacing",
+			NULL
+		};
+
+		switch (parse_subopt(&subs, subopts, &value)) {
+		case 0:
+			seek.seek_upward = strtol(value, 0L, 0);
+			break;
+		case 1:
+			seek.wrap_around = strtol(value, 0L, 0);
+			break;
+		case 2:
+			seek.spacing = strtol(value, 0L, 0);
+			break;
+		default:
+			usage_tuner();
+			exit(1);
+		}
+	}
+}
+
+static void print_byte(char byte, bool linebreak)
+{
+	int count = 8;
+	printf(" ");
+	while (count--) {
+		printf("%d", (byte & 128) ? 1 : 0 );
+		byte <<= 1;
+	}
+	if (linebreak)
+		printf("\n");
+}
+
+static void print_rds_group(const struct v4l2_rds_group *rds_group)
+{
+	printf("\nComplete RDS data group received \n");
+	printf("PI: %04x\n", rds_group->pi);
+	printf("Group: %u%c \n", rds_group->group_id, rds_group->group_version);
+	printf("Data B:");
+	print_byte(rds_group->data_b_lsb, true);
+	printf("Data C:");
+	print_byte(rds_group->data_c_msb, false);
+	print_byte(rds_group->data_c_lsb, true);
+	printf("Data D:");
+	print_byte(rds_group->data_d_msb, false);
+	print_byte(rds_group->data_d_lsb, true);
+	printf("\n");
+}
+
+static void print_decoder_info(uint8_t di)
+{
+	printf("\nDI: ");
+	if (di & V4L2_RDS_FLAG_STEREO)
+		printf("Stereo, ");
+	else
+		printf("Mono, ");
+	if (di & V4L2_RDS_FLAG_ARTIFICIAL_HEAD)
+		printf("Artificial Head, ");
+	else
+		printf("No Artificial Head, ");
+	if (di & V4L2_RDS_FLAG_COMPRESSED)
+		printf("Compressed");
+	else
+		printf("Not Compressed");
+}
+
+static void print_rds_statistics(struct v4l2_rds_statistics *statistics)
+{
+	printf("\n\nRDS Statistics: \n");
+	printf("received blocks / received groups: %u / %u\n",
+		statistics->block_cnt, statistics->group_cnt);
+
+	float block_error_percentage =
+	((float)statistics->block_error_cnt / statistics->block_cnt) * 100.0;
+	printf("block errors / group errors: %u (%3.2f%%) / %u \n",
+		statistics->block_error_cnt,
+		block_error_percentage, statistics->group_error_cnt);
+	float block_corrected_percentage =
+	((float)statistics->block_corrected_cnt / statistics->block_cnt) * 100.0;
+	printf("corrected blocks: %u (%3.2f%%)\n",
+		statistics->block_corrected_cnt, block_corrected_percentage);
+	for(int i=0; i<16; i++)
+		printf("Group %02d: %u\n", i, statistics->group_type_cnt[i]);
+}
+
+static void print_rds_af(const struct v4l2_rds_af_set *af_set)
+{
+	int counter = 0;
+
+	printf("\nAnnounced AFs: %u", af_set->announced_af);
+	for (int i = 0; i < af_set->size && i < af_set->announced_af; i++, counter++) {
+		if (af_set->af[i] >= 87500000 ) {
+			printf("\nAF%02d: %.1fMHz", counter, af_set->af[i] / 1000000.0);
+			continue;
+		}
+		printf("\nAF%02d: %.3fkHz", counter, af_set->af[i] / 1000.0);
+	}
+}
+
+static void print_rds_pi(const struct v4l2_rds *handle)
+{
+	printf("\nArea Coverage: %s", v4l2_rds_get_coverage_str(handle));
+}
+
+static void print_rds_data(const struct v4l2_rds *handle, uint32_t updated_fields)
+{
+	if (params.options[OptPrintBlock])
+		updated_fields = 0xffffffff;
+
+	if ((updated_fields & V4L2_RDS_PI) &&
+			(handle->valid_fields & V4L2_RDS_PI)) {
+		printf("\nPI: %04x", handle->pi);
+		print_rds_pi(handle);
+	}
+
+	if (updated_fields & V4L2_RDS_PS &&
+			handle->valid_fields & V4L2_RDS_PS) {
+		printf("\nPS: ");
+		for (int i = 0; i < 8; ++i) {
+			/* filter out special characters */
+			if (handle->ps[i] >= 0x20 && handle->ps[i] <= 0x7E)
+				printf("%lc",handle->ps[i]);
+			else
+				printf(" ");
+		}
+	}
+
+	if (updated_fields & V4L2_RDS_PTY && handle->valid_fields & V4L2_RDS_PTY)
+		printf("\nPTY: %0u -> %s",handle->pty, v4l2_rds_get_pty_str(handle));
+
+	if (updated_fields & V4L2_RDS_PTYN && handle->valid_fields & V4L2_RDS_PTYN) {
+		printf("\nPTYN: ");
+		for (int i = 0; i < 8; ++i) {
+			/* filter out special characters */
+			if (handle->ptyn[i] >= 0x20 && handle->ptyn[i] <= 0x7E)
+				printf("%lc",handle->ptyn[i]);
+			else
+				printf(" ");
+		}
+	}
+
+	if (updated_fields & V4L2_RDS_TIME) {
+		printf("\nTime: %s", ctime(&handle->time));
+	}
+	if (updated_fields & V4L2_RDS_RT && handle->valid_fields & V4L2_RDS_RT) {
+		printf("\nRT: ");
+		for (int i = 0; i < handle->rt_length; ++i) {
+			/* filter out special characters */
+			if (handle->rt[i] >= 0x20 && handle->rt[i] <= 0x7E)
+				printf("%lc",handle->rt[i]);
+			else
+				printf(" ");
+		}
+	}
+
+	if (updated_fields & V4L2_RDS_TP && handle->valid_fields & V4L2_RDS_TP)
+		printf("\nTP: %s  TA: %s", (handle->tp)? "yes":"no",
+			handle->ta? "yes":"no");
+	if (updated_fields & V4L2_RDS_MS && handle->valid_fields & V4L2_RDS_MS)
+		printf("\nMS Flag: %s", (handle->ms)? "Music" : "Speech");
+	if (updated_fields & V4L2_RDS_ECC && handle->valid_fields & V4L2_RDS_ECC)
+		printf("\nECC: %X%x, Country: %u -> %s",
+		handle->ecc >> 4, handle->ecc & 0x0f, handle->pi >> 12,
+		v4l2_rds_get_country_str(handle));
+	if (updated_fields & V4L2_RDS_LC && handle->valid_fields & V4L2_RDS_LC)
+		printf("\nLanguage: %u -> %s ", handle->lc,
+		v4l2_rds_get_language_str(handle));
+	if (updated_fields & V4L2_RDS_DI && handle->valid_fields & V4L2_RDS_DI)
+		print_decoder_info(handle->di);
+	if (updated_fields & V4L2_RDS_ODA &&
+			handle->decode_information & V4L2_RDS_ODA) {
+		for (int i = 0; i < handle->rds_oda.size; ++i)
+			printf("\nODA Group: %02u%c, AID: %08x",handle->rds_oda.oda[i].group_id,
+			handle->rds_oda.oda[i].group_version, handle->rds_oda.oda[i].aid);
+	}
+	if (updated_fields & V4L2_RDS_AF && handle->valid_fields & V4L2_RDS_AF)
+		print_rds_af(&handle->rds_af);
+	if (params.options[OptPrintBlock])
+		printf("\n");
+}
+
+static void read_rds(struct v4l2_rds *handle, const int fd, const int wait_limit)
+{
+	int byte_cnt = 0;
+	int error_cnt = 0;
+	uint32_t updated_fields = 0x00;
+	struct v4l2_rds_data rds_data; /* read buffer for rds blocks */
+
+	while (!params.terminate_decoding) {
+		memset(&rds_data, 0, sizeof(rds_data));
+		if ((byte_cnt=read(fd, &rds_data, 3)) != 3) {
+			if (byte_cnt == 0) {
+				printf("\nEnd of input file reached \n");
+				break;
+			} else if(++error_cnt > 2) {
+				fprintf(stderr, "\nError reading from "
+					"device (no RDS data available)\n");
+				break;
+			}
+			/* wait for new data to arrive: transmission of 1
+			 * group takes ~88.7ms */
+			usleep(wait_limit * 1000);
+		}
+		else if (byte_cnt == 3) {
+			error_cnt = 0;
+			/* true if a new group was decoded */
+			if ((updated_fields = v4l2_rds_add(handle, &rds_data))) {
+				print_rds_data(handle, updated_fields);
+				if (params.options[OptVerbose])
+					 print_rds_group(v4l2_rds_get_group(handle));
+			}
+		}
+	}
+	/* print a summary of all valid RDS-fields before exiting */
+	printf("\nSummary of valid RDS-fields:");
+	print_rds_data(handle, 0xFFFFFFFF);
+}
+
+static void read_rds_from_fd(const int fd)
+{
+	struct v4l2_rds *rds_handle;
+
+	/* create an rds handle for the current device */
+	if (!(rds_handle = v4l2_rds_create(true))) {
+		fprintf(stderr, "Failed to init RDS lib: %s\n", strerror(errno));
+		exit(1);
+	}
+
+	/* try to receive and decode RDS data */
+	read_rds(rds_handle, fd, params.wait_limit);
+	print_rds_statistics(&rds_handle->rds_statistics);
+
+	v4l2_rds_destroy(rds_handle);
+}
+
+static int parse_cl(int argc, char **argv)
+{
+	int i = 0;
+	int idx = 0;
+	int opt = 0;
+	/* 26 letters in the alphabet, case sensitive = 26 * 2 possible
+	 * short options, where each option requires at most two chars
+	 * {option, optional argument} */
+	char short_options[26 * 2 * 2 + 1];
+
+	if (argc == 1) {
+		usage_hint();
+		exit(1);
+	}
+	for (i = 0; long_options[i].name; i++) {
+		if (!isalpha(long_options[i].val))
+			continue;
+		short_options[idx++] = long_options[i].val;
+		if (long_options[i].has_arg == required_argument)
+			short_options[idx++] = ':';
+	}
+	while (1) {
+		int option_index = 0;
+
+		short_options[idx] = 0;
+		opt = getopt_long(argc, argv, short_options,
+				 long_options, &option_index);
+		if (opt == -1)
+			break;
+
+		params.options[(int)opt] = 1;
+		switch (opt) {
+		case OptSetDevice:
+			strncpy(params.fd_name, optarg, 80);
+			if (isdigit(optarg[0]) && optarg[1] == 0) {
+				char newdev[20];
+				sprintf(newdev, "/dev/radio%c", optarg[0]);
+				strncpy(params.fd_name, newdev, 20);
+			}
+			break;
+		case OptSetFreq:
+			params.freq = strtod(optarg, NULL);
+			break;
+		case OptListDevices:
+			print_devices(list_devices());
+			break;
+		case OptFreqSeek:
+			parse_freq_seek(optarg, params.freq_seek);
+			break;
+		case OptTunerIndex:
+			params.tuner_index = strtoul(optarg, NULL, 0);
+			break;
+		case OptOpenFile:
+		{
+			if (access(optarg, F_OK) != -1) {
+				params.filemode_active = true;
+				strncpy(params.fd_name, optarg, 80);
+			} else {
+				fprintf(stderr, "Unable to open file: %s\n", optarg);
+				return -1;
+			}
+			/* enable the read-rds option by default for convenience */
+			params.options[OptReadRds] = 1;
+			break;
+		}
+		case OptWaitLimit:
+			params.wait_limit = strtoul(optarg, NULL, 0);
+			break;
+		case ':':
+			fprintf(stderr, "Option '%s' requires a value\n",
+				argv[optind]);
+			usage_hint();
+			return 1;
+		case '?':
+			if (argv[optind])
+				fprintf(stderr, "Unknown argument '%s'\n", argv[optind]);
+			usage_hint();
+			return 1;
+		}
+	}
+	if (optind < argc) {
+		printf("unknown arguments: ");
+		while (optind < argc)
+			printf("%s ", argv[optind++]);
+		printf("\n");
+		usage_hint();
+		return 1;
+	}
+	if (params.options[OptAll]) {
+		params.options[OptGetDriverInfo] = 1;
+		params.options[OptGetFreq] = 1;
+		params.options[OptGetTuner] = 1;
+		params.options[OptSilent] = 1;
+	}
+
+	return 0;
+}
+
+static void print_driver_info(const struct v4l2_capability *vcap)
+{
+
+	printf("Driver Info (%susing libv4l2):\n",
+			params.options[OptUseWrapper] ? "" : "not ");
+	printf("\tDriver name   : %s\n", vcap->driver);
+	printf("\tCard type     : %s\n", vcap->card);
+	printf("\tBus info      : %s\n", vcap->bus_info);
+	printf("\tDriver version: %d.%d.%d\n",
+			vcap->version >> 16,
+			(vcap->version >> 8) & 0xff,
+			vcap->version & 0xff);
+	printf("\tCapabilities  : 0x%08X\n", vcap->capabilities);
+	printf("%s", cap2s(vcap->capabilities).c_str());
+	if (vcap->capabilities & V4L2_CAP_DEVICE_CAPS) {
+		printf("\tDevice Caps   : 0x%08X\n", vcap->device_caps);
+		printf("%s", cap2s(vcap->device_caps).c_str());
+	}
+}
+
+static void set_options(const int fd, const int capabilities, struct v4l2_frequency *vf,
+			struct v4l2_tuner *tuner)
+{
+	int mode = -1;			/* set audio mode */
+	double fac = 16;		/* factor for frequency division */
+
+	if (params.options[OptSetFreq]) {
+		vf->type = V4L2_TUNER_RADIO;
+		tuner->index = params.tuner_index;
+		if (doioctl(fd, VIDIOC_G_TUNER, tuner) == 0) {
+			fac = (tuner->capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16;
+			vf->type = tuner->type;
+		}
+
+		vf->tuner = params.tuner_index;
+		vf->frequency = __u32(params.freq * fac);
+		if (doioctl(fd, VIDIOC_S_FREQUENCY, vf) == 0)
+			printf("Frequency for tuner %d set to %d (%f MHz)\n",
+				vf->tuner, vf->frequency, vf->frequency / fac);
+	}
+
+	if (params.options[OptSetTuner]) {
+		struct v4l2_tuner vt;
+
+		memset(&vt, 0, sizeof(struct v4l2_tuner));
+		vt.index = params.tuner_index;
+		if (doioctl(fd, VIDIOC_G_TUNER, &vt) == 0) {
+			if (mode != -1)
+				vt.audmode = mode;
+			doioctl(fd, VIDIOC_S_TUNER, &vt);
+		}
+	}
+
+	if (params.options[OptFreqSeek]) {
+		params.freq_seek.tuner = params.tuner_index;
+		params.freq_seek.type = V4L2_TUNER_RADIO;
+		doioctl(fd, VIDIOC_S_HW_FREQ_SEEK, &params.freq_seek);
+	}
+}
+
+static void get_options(const int fd, const int capabilities, struct v4l2_frequency *vf,
+			struct v4l2_tuner *tuner)
+{
+	double fac = 16;		/* factor for frequency division */
+
+	if (params.options[OptGetFreq]) {
+		vf->type = V4L2_TUNER_RADIO;
+		tuner->index = params.tuner_index;
+		if (doioctl(fd, VIDIOC_G_TUNER, tuner) == 0) {
+			fac = (tuner->capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16;
+			vf->type = tuner->type;
+		}
+		vf->tuner = params.tuner_index;
+		if (doioctl(fd, VIDIOC_G_FREQUENCY, vf) == 0)
+			printf("Frequency for tuner %d: %d (%f MHz)\n",
+				   vf->tuner, vf->frequency, vf->frequency / fac);
+	}
+
+	if (params.options[OptGetTuner]) {
+		struct v4l2_tuner vt;
+
+		memset(&vt, 0, sizeof(struct v4l2_tuner));
+		vt.index = params.tuner_index;
+		if (doioctl(fd, VIDIOC_G_TUNER, &vt) == 0) {
+			printf("Tuner %d:\n", vt.index);
+			printf("\tName                 : %s\n", vt.name);
+			printf("\tCapabilities         : %s\n",
+				tcap2s(vt.capability).c_str());
+			if (vt.capability & V4L2_TUNER_CAP_LOW)
+				printf("\tFrequency range      : %.1f MHz - %.1f MHz\n",
+					 vt.rangelow / 16000.0, vt.rangehigh / 16000.0);
+			else
+				printf("\tFrequency range      : %.1f MHz - %.1f MHz\n",
+					 vt.rangelow / 16.0, vt.rangehigh / 16.0);
+			printf("\tSignal strength/AFC  : %d%%/%d\n",
+				(int)((vt.signal / 655.35)+0.5), vt.afc);
+			printf("\tCurrent audio mode   : %s\n", audmode2s(vt.audmode));
+			printf("\tAvailable subchannels: %s\n",
+					rxsubchans2s(vt.rxsubchans).c_str());
+		}
+	}
+
+	if (params.options[OptListFreqBands]) {
+		struct v4l2_frequency_band band;
+
+		memset(&band, 0, sizeof(band));
+		band.tuner = params.tuner_index;
+		band.type = V4L2_TUNER_RADIO;
+		band.index = 0;
+		printf("ioctl: VIDIOC_ENUM_FREQ_BANDS\n");
+		while (test_ioctl(fd, VIDIOC_ENUM_FREQ_BANDS, &band) >= 0) {
+			if (band.index)
+				printf("\n");
+			printf("\tIndex          : %d\n", band.index);
+			printf("\tModulation     : %s\n", modulation2s(band.modulation).c_str());
+			printf("\tCapability     : %s\n", tcap2s(band.capability).c_str());
+			if (band.capability & V4L2_TUNER_CAP_LOW)
+				printf("\tFrequency Range: %.3f MHz - %.3f MHz\n",
+				     band.rangelow / 16000.0, band.rangehigh / 16000.0);
+			else
+				printf("\tFrequency Range: %.3f MHz - %.3f MHz\n",
+				     band.rangelow / 16.0, band.rangehigh / 16.0);
+			band.index++;
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	int fd = -1;
+
+	/* command args */
+	struct v4l2_tuner tuner;	/* set_freq/get_freq */
+	struct v4l2_capability vcap;	/* list_cap */
+	struct v4l2_frequency vf;	/* get_freq/set_freq */
+
+	memset(&tuner, 0, sizeof(tuner));
+	memset(&vcap, 0, sizeof(vcap));
+	memset(&vf, 0, sizeof(vf));
+	strcpy(params.fd_name, "/dev/radio0");
+
+	/* define locale for unicode support */
+	if (!setlocale(LC_CTYPE, "")) {
+		fprintf(stderr, "Can't set the specified locale!\n");
+		return 1;
+	}
+	/* register signal handler for interrupt signal, to exit gracefully */
+	signal(SIGINT, signal_handler_interrupt);
+
+	/* try to parse the command line */
+	parse_cl(argc, argv);
+	if (params.options[OptHelp]) {
+		usage();
+		exit(0);
+	}
+
+	/* File Mode: disables all other features, except for RDS decoding */
+	if (params.filemode_active) {
+		if ((fd = open(params.fd_name, O_RDONLY|O_NONBLOCK)) < 0){
+			perror("error opening file");
+			exit(1);
+		}
+		read_rds_from_fd(fd);
+		test_close(fd);
+		exit(0);
+	}
+
+	/* Device Mode: open the radio device as read-only and non-blocking */
+	if (!params.options[OptSetDevice]) {
+		/* check the system for RDS capable devices */
+		dev_vec devices = list_devices();
+		if (devices.size() == 0) {
+			fprintf(stderr, "No RDS-capable device found\n");
+			exit(1);
+		}
+		strncpy(params.fd_name, devices[0].c_str(), 80);
+		printf("Using device: %s\n", params.fd_name);
+	}
+	if ((fd = test_open(params.fd_name, O_RDONLY | O_NONBLOCK)) < 0) {
+		fprintf(stderr, "Failed to open %s: %s\n", params.fd_name,
+			strerror(errno));
+		exit(1);
+	}
+	doioctl(fd, VIDIOC_QUERYCAP, &vcap);
+
+	/* Info options */
+	if (params.options[OptGetDriverInfo])
+		print_driver_info(&vcap);
+	/* Set options */
+	set_options(fd, vcap.capabilities, &vf, &tuner);
+	/* Get options */
+	get_options(fd, vcap.capabilities, &vf, &tuner);
+	/* RDS decoding */
+	if (params.options[OptReadRds])
+		read_rds_from_fd(fd);
+
+	test_close(fd);
+	exit(app_result);
+}
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 0/2] Add support for RDS decoding (updated)
  2012-08-07 15:11 ` [RFC PATCH 0/2] Add support for RDS decoding (updated) Konke Radlow
  2012-08-07 15:11   ` [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC) Konke Radlow
@ 2012-08-08 12:03   ` Konke Radlow
  1 sibling, 0 replies; 15+ messages in thread
From: Konke Radlow @ 2012-08-08 12:03 UTC (permalink / raw)
  To: Konke Radlow; +Cc: linux-media, hverkuil, hdegoede, koradlow

just for the record, these patches are:

Signed-off-by: Konke Radlow <kradlow@cisco.com>

Regards,
Konke

On Tue, Aug 7, 2012 at 3:11 PM, Konke Radlow <kradlow@cisco.com> wrote:
> Hello,
> first of all: thank you for the comments to my previous RFC for the
> libv4l2rds library and the rds-ctl control & testing tool.
> The proposed changes have been implemented, and the code has been
> further improved after a thorough code review by Hans Verkuil.
>
> Changes:
>   -the code is rebased on the latest v4l-utils code (as of today 07.08)
>   -added feature: time/date decoding
>   -implementing proposed changes
>   -code cleanup
>   -extended comments
>
> Status:
> From my point of view the RDS decoding is now almost feature complete.
> There are some obscure RDS features like paging that are not supported,
> but they do not seem to used anywhere.
> So in the near future no features will be added and the goal is to get
> the library and control tool merged into the v4l-utils codebase.
>
> Upcoming:
> Work on RDS-TMC decoding is going well and is being done in a seperate
> branch. It will be the subject of a future RFC, once it has reached a
> mature stage. But TMC is not a core feature of RDS but an addition.
>
> Regards,
> Konke
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-07 15:11   ` [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC) Konke Radlow
  2012-08-07 15:11     ` [RFC PATCH 2/2] Add rds-ctl tool " Konke Radlow
@ 2012-08-09  7:22     ` Gregor Jasny
  2012-08-09 14:12       ` Konke Radlow
  2012-08-09 11:58     ` Hans de Goede
  2012-08-09 12:17     ` Hans Verkuil
  3 siblings, 1 reply; 15+ messages in thread
From: Gregor Jasny @ 2012-08-09  7:22 UTC (permalink / raw)
  To: Konke Radlow, linux-media

Hello Konke,

On 8/7/12 5:11 PM, Konke Radlow wrote:
> diff --git a/configure.ac b/configure.ac
> index 8ddcc9d..1109c4d 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -146,9 +148,12 @@ AC_ARG_WITH(libv4l2subdir, AS_HELP_STRING(--with-libv4l2subdir=DIR,set libv4l2 l
>   AC_ARG_WITH(libv4lconvertsubdir, AS_HELP_STRING(--with-libv4lconvertsubdir=DIR,set libv4lconvert library subdir [default=libv4l]),
>      libv4lconvertsubdir=$withval, libv4lconvertsubdir="libv4l")
>
> +AC_ARG_WITH(libv4l2rdssubdir, AS_HELP_STRING(--with-libv4l2rdssubdir=DIR,set libv4l2rds library subdir [default=libv4l]),
> +   libv4l2rdssubdir=$withval, libv4l2rdssubdir="libv4l")
> +
>   AC_ARG_WITH(udevdir, AS_HELP_STRING(--with-udevdir=DIR,set udev directory [default=/lib/udev]),
>      udevdir=$withval, udevdir="/lib/udev")
> -
> +
>   libv4l1privdir="$libdir/$libv4l1subdir"
>   libv4l2privdir="$libdir/$libv4l2subdir"
>   libv4l2plugindir="$libv4l2privdir/plugins"

please remove these changes. They are not needed for the RDS library.

Thanks,
Gregor

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-07 15:11   ` [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC) Konke Radlow
  2012-08-07 15:11     ` [RFC PATCH 2/2] Add rds-ctl tool " Konke Radlow
  2012-08-09  7:22     ` [RFC PATCH 1/2] Add libv4l2rds library " Gregor Jasny
@ 2012-08-09 11:58     ` Hans de Goede
  2012-08-09 12:14       ` Hans Verkuil
  2012-08-09 14:30       ` Konke Radlow
  2012-08-09 12:17     ` Hans Verkuil
  3 siblings, 2 replies; 15+ messages in thread
From: Hans de Goede @ 2012-08-09 11:58 UTC (permalink / raw)
  To: Konke Radlow; +Cc: linux-media, hverkuil, koradlow

Hi Konke,

As Gregor already mentioned there is no need to define libv4l2rdssubdir in configure.ac ,
so please drop that.

Other then that I've some minor remarks (comments inline), with all those
fixed, this one is could to go. So hopefully the next version can be added
to git master!

On 08/07/2012 05:11 PM, Konke Radlow wrote:
> ---
>   Makefile.am                     |    3 +-
>   configure.ac                    |    7 +-
>   lib/include/libv4l2rds.h        |  228 ++++++++++
>   lib/libv4l2rds/Makefile.am      |   11 +
>   lib/libv4l2rds/libv4l2rds.c     |  953 +++++++++++++++++++++++++++++++++++++++
>   lib/libv4l2rds/libv4l2rds.pc.in |   11 +
>   6 files changed, 1211 insertions(+), 2 deletions(-)
>   create mode 100644 lib/include/libv4l2rds.h
>   create mode 100644 lib/libv4l2rds/Makefile.am
>   create mode 100644 lib/libv4l2rds/libv4l2rds.c
>   create mode 100644 lib/libv4l2rds/libv4l2rds.pc.in
>

<snip>

> diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h
> new file mode 100644
> index 0000000..4aa8593
> --- /dev/null
> +++ b/lib/include/libv4l2rds.h
> @@ -0,0 +1,228 @@
> +/*
> + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + * Author: Konke Radlow <koradlow@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation; either version 2.1 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, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
> + */
> +
> +#ifndef __LIBV4L2RDS
> +#define __LIBV4L2RDS
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +#include <stdint.h>
> +#include <time.h>
> +#include <sys/types.h>
> +#include <sys/mman.h>
> +#include <config.h>

You should never include config.h in a public header, also
are all the headers really needed for the prototypes in this header?

I don't think so! Please move all the unneeded ones to the libv4l2rds.c
file!

> +
> +#include <linux/videodev2.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif /* __cplusplus */
> +
> +#if HAVE_VISIBILITY
> +#define LIBV4L_PUBLIC __attribute__ ((visibility("default")))
> +#else
> +#define LIBV4L_PUBLIC
> +#endif
> +
> +/* used to define the current version (version field) of the v4l2_rds struct */
> +#define V4L2_RDS_VERSION (1)
> +

What is the purpose of this field? Once we've released a v4l-utils with this
library we are stuck to the API we've defined, having a version field & changing it,
won't stop us from breaking existing apps, so once we've an official release we
simply cannot make ABI breaking changes, which is why most of my review sofar
has concentrated on the API side :)

I suggest dropping this define and the version field from the struct.

> +/* Constants used to define the size of arrays used to store RDS information */
> +#define MAX_ODA_CNT 18 	/* there are 16 groups each with type a or b. Of these
> +			 * 32 distinct groups, 18 can be used for ODA purposes */
> +#define MAX_AF_CNT 25	/* AF Method A allows a maximum of 25 AFs to be defined
> +			 * AF Method B does not impose a limit on the number of AFs
> +			 * but it is not fully supported at the moment and will
> +			 * not receive more than 25 AFs */
> +
> +/* Define Constants for the possible types of RDS information
> + * used to address the relevant bit in the valid_fields bitmask */
> +#define V4L2_RDS_PI 		0x01	/* Program Identification */
> +#define V4L2_RDS_PTY		0x02	/* Program Type */
> +#define V4L2_RDS_TP		0x04	/* Traffic Program */
> +#define V4L2_RDS_PS		0x08	/* Program Service Name */
> +#define V4L2_RDS_TA		0x10	/* Traffic Announcement */
> +#define V4L2_RDS_DI		0x20	/* Decoder Information */
> +#define V4L2_RDS_MS		0x40	/* Music / Speech flag */
> +#define V4L2_RDS_PTYN		0x80	/* Program Type Name */
> +#define V4L2_RDS_RT		0x100 	/* Radio-Text */
> +#define V4L2_RDS_TIME		0x200	/* Date and Time information */
> +#define V4L2_RDS_TMC		0x400	/* TMC availability */
> +#define V4L2_RDS_AF		0x800	/* AF (alternative freq) available */
> +#define V4L2_RDS_ECC		0x1000	/* Extended County Code */
> +#define V4L2_RDS_LC		0x2000	/* Language Code */
> +
> +/* Define Constants for the state of the RDS decoding process
> + * used to address the relevant bit in the decode_information bitmask */
> +#define V4L2_RDS_GROUP_NEW 	0x01	/* New group received */
> +#define V4L2_RDS_ODA		0x02	/* Open Data Group announced */
> +
> +/* Decoder Information (DI) codes
> + * used to decode the DI information according to the RDS standard */
> +#define V4L2_RDS_FLAG_STEREO 		0x01
> +#define V4L2_RDS_FLAG_ARTIFICIAL_HEAD	0x02
> +#define V4L2_RDS_FLAG_COMPRESSED	0x04
> +#define V4L2_RDS_FLAG_STATIC_PTY	0x08
> +
> +/* struct to encapsulate one complete RDS group */
> +/* This structure is used internally to store data until a complete RDS
> + * group was received and group id dependent decoding can be done.
> + * It is also used to provide external access to uninterpreted RDS groups
> + * when manual decoding is required (e.g. special ODA types) */
> +struct v4l2_rds_group {
> +	uint16_t pi;		/* Program Identification */
> +	char group_version;	/* group version ('A' / 'B') */
> +	uint8_t group_id;	/* group number (0..16) */
> +
> +	/* uninterpreted data blocks for decoding (e.g. ODA) */
> +	uint8_t data_b_lsb;
> +	uint8_t data_c_msb;
> +	uint8_t data_c_lsb;
> +	uint8_t data_d_msb;
> +	uint8_t data_d_lsb;
> +};
> +
> +/* struct to encapsulate some statistical information about the decoding process */
> +struct v4l2_rds_statistics {
> +	uint32_t block_cnt;		/* total amount of received blocks */
> +	uint32_t group_cnt;		/* total amount of successfully
> +					 * decoded groups */
> +	uint32_t block_error_cnt;	/* blocks that were marked as erroneous
> +					 * and had to be dropped */
> +	uint32_t group_error_cnt;	/* group decoding processes that had to be
> +					 * aborted because of erroneous blocks
> +					 * or wrong order of blocks */
> +	uint32_t block_corrected_cnt;	/* blocks that contained 1-bit errors
> +					 * which were corrected */
> +	uint32_t group_type_cnt[16];	/* number of occurrence for each
> +					 * defined RDS group */
> +};
> +
> +/* struct to encapsulate the definition of one ODA (Open Data Application) type */
> +struct v4l2_rds_oda {
> +	uint8_t group_id;	/* RDS group used to broadcast this ODA */
> +	char group_version;	/* group version (A / B) for this ODA */
> +	uint16_t aid;		/* Application Identification for this ODA,
> +				 * AIDs are centrally administered by the
> +				 * RDS Registration Office (rds.org.uk) */
> +};
> +
> +/* struct to encapsulate an array of all defined ODA types for a channel */
> +/* This structure will grow with ODA announcements broadcasted in type 3A
> + * groups, that were verified not to be no duplicates or redefinitions */
> +struct v4l2_rds_oda_set {
> +	uint8_t size;		/* number of ODAs defined by this channel */
> +	struct v4l2_rds_oda oda[MAX_ODA_CNT];
> +};
> +
> +/* struct to encapsulate an array of Alternative Frequencies for a channel */
> +/* Every channel can send out AFs for his program. The number of AFs that
> + * will be broadcasted is announced by the channel */
> +struct v4l2_rds_af_set {
> +	uint8_t size;			/* size of the set (might be smaller
> +					 * than the announced size) */
> +	uint8_t announced_af;		/* number of announced AF */
> +	uint32_t af[MAX_AF_CNT];	/* AFs defined in Hz */
> +};
> +
> +/* struct to encapsulate state and RDS information for current decoding process */
> +/* This is the structure that will be used by external applications, to
> + * communicate with the library and get access to RDS data */
> +struct v4l2_rds {
> +	uint32_t version;	/* version number of this structure */
> +
> +	/** state information **/
> +	uint32_t decode_information;	/* state of decoding process */
> +	uint32_t valid_fields;		/* currently valid info fields
> +					 * of this structure */
> +
> +	/** RDS info fields **/
> +	bool is_rbds; 		/* use RBDS standard version of LUTs */
> +	uint16_t pi;		/* Program Identification */
> +	uint8_t ps[9];		/* Program Service Name, UTF-8 encoding,
> +				 * '\0' terminated */
> +	uint8_t pty;		/* Program Type */
> +	uint8_t ptyn[9];	/* Program Type Name, UTF-8 encoding,
> +				 * '\0' terminated */
> +	bool ptyn_ab_flag;	/* PTYN A/B flag (toggled), to signal
> +				 * change of PTYN */
> +	uint8_t rt_length;	/* length of RT string */
> +	uint8_t rt[65];		/* Radio-Text string, UTF-8 encoding,
> +				 * '\0' terminated */
> +	bool rt_ab_flag;	/* RT A/B flag (toggled), to signal
> +				 * transmission of new RT */
> +	bool ta;		/* Traffic Announcement */
> +	bool tp;		/* Traffic Program */
> +	bool ms;		/* Music / Speech flag */
> +	uint8_t di;		/* Decoder Information */
> +	uint8_t ecc;		/* Extended Country Code */
> +	uint8_t lc;		/* Language Code */
> +	time_t time;		/* Time and Date of transmission */
> +
> +	struct v4l2_rds_statistics rds_statistics;
> +	struct v4l2_rds_oda_set rds_oda;	/* Open Data Services */
> +	struct v4l2_rds_af_set rds_af; 		/* Alternative Frequencies */
> +};
> +
> +/* v4l2_rds_init() - initializes a new decoding process
> + * @is_rbds:	defines which standard is used: true=RBDS, false=RDS
> + *
> + * initialize a new instance of the RDS-decoding struct and return
> + * a handle containing state and RDS information, used to interact
> + * with the library functions */
> +LIBV4L_PUBLIC struct v4l2_rds *v4l2_rds_create(bool is_rdbs);
> +
> +/* frees all memory allocated for the RDS-decoding struct */
> +LIBV4L_PUBLIC void v4l2_rds_destroy(struct v4l2_rds *handle);
> +
> +/* resets the RDS information in the handle to initial values
> + * e.g. can be used when radio channel is changed
> + * @reset_statistics:	true = set all statistic values to 0, false = keep them untouched */
> +LIBV4L_PUBLIC void v4l2_rds_reset(struct v4l2_rds *handle, bool reset_statistics);
> +
> +/* adds a raw RDS block to decode it into RDS groups
> + * @return:	bitmask with with updated fields set to 1
> + * @rds_data: 	3 bytes of raw RDS data, obtained by calling read()
> + * 				on RDS capable V4L2 devices */
> +LIBV4L_PUBLIC uint32_t v4l2_rds_add(struct v4l2_rds *handle, struct v4l2_rds_data *rds_data);
> +
> +/*
> + * group of functions to translate numerical RDS data into strings
> + *
> + * return program description string defined in the RDS/RBDS Standard
> + * ! return value deepens on selected Standard !*/

                      ^^^^^^^ Typo!

> +LIBV4L_PUBLIC const char *v4l2_rds_get_pty_str(const struct v4l2_rds *handle);
> +LIBV4L_PUBLIC const char *v4l2_rds_get_language_str(const struct v4l2_rds *handle);
> +LIBV4L_PUBLIC const char *v4l2_rds_get_country_str(const struct v4l2_rds *handle);
> +LIBV4L_PUBLIC const char *v4l2_rds_get_coverage_str(const struct v4l2_rds *handle);
> +
> +/* returns a pointer to the last decoded RDS group, in order to give raw
> + * access to RDS data if it is required (e.g. ODA decoding) */
> +LIBV4L_PUBLIC const struct v4l2_rds_group *v4l2_rds_get_group
> +	(const struct v4l2_rds *handle);
> +
> +
> +#ifdef __cplusplus
> +}
> +#endif /* __cplusplus */
> +#endif

<snip>

The rest looks good to me.

Regards,

Hans


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 2/2] Add rds-ctl tool (with changes proposed in RFC)
  2012-08-07 15:11     ` [RFC PATCH 2/2] Add rds-ctl tool " Konke Radlow
@ 2012-08-09 12:05       ` Hans de Goede
  2012-08-09 14:34         ` Konke Radlow
  0 siblings, 1 reply; 15+ messages in thread
From: Hans de Goede @ 2012-08-09 12:05 UTC (permalink / raw)
  To: Konke Radlow; +Cc: linux-media, hverkuil, koradlow

Hi,

Comments inline.

On 08/07/2012 05:11 PM, Konke Radlow wrote:
> ---
>   Makefile.am               |    3 +-
>   configure.ac              |    1 +
>   utils/rds-ctl/Makefile.am |    5 +
>   utils/rds-ctl/rds-ctl.cpp |  959 +++++++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 967 insertions(+), 1 deletion(-)
>   create mode 100644 utils/rds-ctl/Makefile.am
>   create mode 100644 utils/rds-ctl/rds-ctl.cpp
>
> diff --git a/Makefile.am b/Makefile.am
> index 621d8d9..8ef0d00 100644
> --- a/Makefile.am
> +++ b/Makefile.am

<Snip>

> +static void print_rds_data(const struct v4l2_rds *handle, uint32_t updated_fields)
> +{
> +	if (params.options[OptPrintBlock])
> +		updated_fields = 0xffffffff;
> +
> +	if ((updated_fields & V4L2_RDS_PI) &&
> +			(handle->valid_fields & V4L2_RDS_PI)) {
> +		printf("\nPI: %04x", handle->pi);
> +		print_rds_pi(handle);
> +	}
> +
> +	if (updated_fields & V4L2_RDS_PS &&
> +			handle->valid_fields & V4L2_RDS_PS) {
> +		printf("\nPS: ");
> +		for (int i = 0; i < 8; ++i) {
> +			/* filter out special characters */
> +			if (handle->ps[i] >= 0x20 && handle->ps[i] <= 0x7E)
> +				printf("%lc",handle->ps[i]);
> +			else
> +				printf(" ");
> +		}


Since ps now is a 0 terminated UTF-8 string you should be able to just do:
		printf("\nPS: %s", handle->ps);

And likewise for the other strings.

> +	}
> +
> +	if (updated_fields & V4L2_RDS_PTY && handle->valid_fields & V4L2_RDS_PTY)
> +		printf("\nPTY: %0u -> %s",handle->pty, v4l2_rds_get_pty_str(handle));
> +
> +	if (updated_fields & V4L2_RDS_PTYN && handle->valid_fields & V4L2_RDS_PTYN) {
> +		printf("\nPTYN: ");
> +		for (int i = 0; i < 8; ++i) {
> +			/* filter out special characters */
> +			if (handle->ptyn[i] >= 0x20 && handle->ptyn[i] <= 0x7E)
> +				printf("%lc",handle->ptyn[i]);
> +			else
> +				printf(" ");
> +		}

Likewise.

> +	}
> +
> +	if (updated_fields & V4L2_RDS_TIME) {
> +		printf("\nTime: %s", ctime(&handle->time));
> +	}
> +	if (updated_fields & V4L2_RDS_RT && handle->valid_fields & V4L2_RDS_RT) {
> +		printf("\nRT: ");
> +		for (int i = 0; i < handle->rt_length; ++i) {
> +			/* filter out special characters */
> +			if (handle->rt[i] >= 0x20 && handle->rt[i] <= 0x7E)
> +				printf("%lc",handle->rt[i]);
> +			else
> +				printf(" ");
> +		}

Likewise.

> +	}
> +
> +	if (updated_fields & V4L2_RDS_TP && handle->valid_fields & V4L2_RDS_TP)
> +		printf("\nTP: %s  TA: %s", (handle->tp)? "yes":"no",
> +			handle->ta? "yes":"no");
> +	if (updated_fields & V4L2_RDS_MS && handle->valid_fields & V4L2_RDS_MS)
> +		printf("\nMS Flag: %s", (handle->ms)? "Music" : "Speech");
> +	if (updated_fields & V4L2_RDS_ECC && handle->valid_fields & V4L2_RDS_ECC)
> +		printf("\nECC: %X%x, Country: %u -> %s",
> +		handle->ecc >> 4, handle->ecc & 0x0f, handle->pi >> 12,
> +		v4l2_rds_get_country_str(handle));
> +	if (updated_fields & V4L2_RDS_LC && handle->valid_fields & V4L2_RDS_LC)
> +		printf("\nLanguage: %u -> %s ", handle->lc,
> +		v4l2_rds_get_language_str(handle));
> +	if (updated_fields & V4L2_RDS_DI && handle->valid_fields & V4L2_RDS_DI)
> +		print_decoder_info(handle->di);
> +	if (updated_fields & V4L2_RDS_ODA &&
> +			handle->decode_information & V4L2_RDS_ODA) {
> +		for (int i = 0; i < handle->rds_oda.size; ++i)
> +			printf("\nODA Group: %02u%c, AID: %08x",handle->rds_oda.oda[i].group_id,
> +			handle->rds_oda.oda[i].group_version, handle->rds_oda.oda[i].aid);
> +	}
> +	if (updated_fields & V4L2_RDS_AF && handle->valid_fields & V4L2_RDS_AF)
> +		print_rds_af(&handle->rds_af);
> +	if (params.options[OptPrintBlock])
> +		printf("\n");
> +}
> +
> +static void read_rds(struct v4l2_rds *handle, const int fd, const int wait_limit)
> +{
> +	int byte_cnt = 0;
> +	int error_cnt = 0;
> +	uint32_t updated_fields = 0x00;
> +	struct v4l2_rds_data rds_data; /* read buffer for rds blocks */
> +
> +	while (!params.terminate_decoding) {
> +		memset(&rds_data, 0, sizeof(rds_data));
> +		if ((byte_cnt=read(fd, &rds_data, 3)) != 3) {
> +			if (byte_cnt == 0) {
> +				printf("\nEnd of input file reached \n");
> +				break;
> +			} else if(++error_cnt > 2) {
> +				fprintf(stderr, "\nError reading from "
> +					"device (no RDS data available)\n");
> +				break;
> +			}
> +			/* wait for new data to arrive: transmission of 1
> +			 * group takes ~88.7ms */
> +			usleep(wait_limit * 1000);
> +		}
> +		else if (byte_cnt == 3) {
> +			error_cnt = 0;
> +			/* true if a new group was decoded */
> +			if ((updated_fields = v4l2_rds_add(handle, &rds_data))) {
> +				print_rds_data(handle, updated_fields);
> +				if (params.options[OptVerbose])
> +					 print_rds_group(v4l2_rds_get_group(handle));
> +			}
> +		}
> +	}
> +	/* print a summary of all valid RDS-fields before exiting */
> +	printf("\nSummary of valid RDS-fields:");
> +	print_rds_data(handle, 0xFFFFFFFF);
> +}
> +
> +static void read_rds_from_fd(const int fd)
> +{
> +	struct v4l2_rds *rds_handle;
> +
> +	/* create an rds handle for the current device */
> +	if (!(rds_handle = v4l2_rds_create(true))) {
> +		fprintf(stderr, "Failed to init RDS lib: %s\n", strerror(errno));
> +		exit(1);
> +	}
> +
> +	/* try to receive and decode RDS data */
> +	read_rds(rds_handle, fd, params.wait_limit);
> +	print_rds_statistics(&rds_handle->rds_statistics);
> +
> +	v4l2_rds_destroy(rds_handle);
> +}
> +
> +static int parse_cl(int argc, char **argv)
> +{
> +	int i = 0;
> +	int idx = 0;
> +	int opt = 0;
> +	/* 26 letters in the alphabet, case sensitive = 26 * 2 possible
> +	 * short options, where each option requires at most two chars
> +	 * {option, optional argument} */
> +	char short_options[26 * 2 * 2 + 1];
> +
> +	if (argc == 1) {
> +		usage_hint();
> +		exit(1);
> +	}
> +	for (i = 0; long_options[i].name; i++) {
> +		if (!isalpha(long_options[i].val))
> +			continue;
> +		short_options[idx++] = long_options[i].val;
> +		if (long_options[i].has_arg == required_argument)
> +			short_options[idx++] = ':';
> +	}
> +	while (1) {
> +		int option_index = 0;
> +
> +		short_options[idx] = 0;
> +		opt = getopt_long(argc, argv, short_options,
> +				 long_options, &option_index);
> +		if (opt == -1)
> +			break;
> +
> +		params.options[(int)opt] = 1;
> +		switch (opt) {
> +		case OptSetDevice:
> +			strncpy(params.fd_name, optarg, 80);
> +			if (isdigit(optarg[0]) && optarg[1] == 0) {
> +				char newdev[20];
> +				sprintf(newdev, "/dev/radio%c", optarg[0]);
> +				strncpy(params.fd_name, newdev, 20);
> +			}
> +			break;
> +		case OptSetFreq:
> +			params.freq = strtod(optarg, NULL);
> +			break;
> +		case OptListDevices:
> +			print_devices(list_devices());
> +			break;
> +		case OptFreqSeek:
> +			parse_freq_seek(optarg, params.freq_seek);
> +			break;
> +		case OptTunerIndex:
> +			params.tuner_index = strtoul(optarg, NULL, 0);
> +			break;
> +		case OptOpenFile:
> +		{
> +			if (access(optarg, F_OK) != -1) {
> +				params.filemode_active = true;
> +				strncpy(params.fd_name, optarg, 80);
> +			} else {
> +				fprintf(stderr, "Unable to open file: %s\n", optarg);
> +				return -1;
> +			}
> +			/* enable the read-rds option by default for convenience */
> +			params.options[OptReadRds] = 1;
> +			break;
> +		}
> +		case OptWaitLimit:
> +			params.wait_limit = strtoul(optarg, NULL, 0);
> +			break;
> +		case ':':
> +			fprintf(stderr, "Option '%s' requires a value\n",
> +				argv[optind]);
> +			usage_hint();
> +			return 1;
> +		case '?':
> +			if (argv[optind])
> +				fprintf(stderr, "Unknown argument '%s'\n", argv[optind]);
> +			usage_hint();
> +			return 1;
> +		}
> +	}
> +	if (optind < argc) {
> +		printf("unknown arguments: ");
> +		while (optind < argc)
> +			printf("%s ", argv[optind++]);
> +		printf("\n");
> +		usage_hint();
> +		return 1;
> +	}
> +	if (params.options[OptAll]) {
> +		params.options[OptGetDriverInfo] = 1;
> +		params.options[OptGetFreq] = 1;
> +		params.options[OptGetTuner] = 1;
> +		params.options[OptSilent] = 1;
> +	}
> +
> +	return 0;
> +}
> +
> +static void print_driver_info(const struct v4l2_capability *vcap)
> +{
> +
> +	printf("Driver Info (%susing libv4l2):\n",
> +			params.options[OptUseWrapper] ? "" : "not ");
> +	printf("\tDriver name   : %s\n", vcap->driver);
> +	printf("\tCard type     : %s\n", vcap->card);
> +	printf("\tBus info      : %s\n", vcap->bus_info);
> +	printf("\tDriver version: %d.%d.%d\n",
> +			vcap->version >> 16,
> +			(vcap->version >> 8) & 0xff,
> +			vcap->version & 0xff);
> +	printf("\tCapabilities  : 0x%08X\n", vcap->capabilities);
> +	printf("%s", cap2s(vcap->capabilities).c_str());
> +	if (vcap->capabilities & V4L2_CAP_DEVICE_CAPS) {
> +		printf("\tDevice Caps   : 0x%08X\n", vcap->device_caps);
> +		printf("%s", cap2s(vcap->device_caps).c_str());
> +	}
> +}
> +
> +static void set_options(const int fd, const int capabilities, struct v4l2_frequency *vf,
> +			struct v4l2_tuner *tuner)
> +{
> +	int mode = -1;			/* set audio mode */
> +	double fac = 16;		/* factor for frequency division */
> +
> +	if (params.options[OptSetFreq]) {
> +		vf->type = V4L2_TUNER_RADIO;
> +		tuner->index = params.tuner_index;
> +		if (doioctl(fd, VIDIOC_G_TUNER, tuner) == 0) {
> +			fac = (tuner->capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16;
> +			vf->type = tuner->type;
> +		}
> +
> +		vf->tuner = params.tuner_index;
> +		vf->frequency = __u32(params.freq * fac);
> +		if (doioctl(fd, VIDIOC_S_FREQUENCY, vf) == 0)
> +			printf("Frequency for tuner %d set to %d (%f MHz)\n",
> +				vf->tuner, vf->frequency, vf->frequency / fac);
> +	}
> +
> +	if (params.options[OptSetTuner]) {
> +		struct v4l2_tuner vt;
> +
> +		memset(&vt, 0, sizeof(struct v4l2_tuner));
> +		vt.index = params.tuner_index;
> +		if (doioctl(fd, VIDIOC_G_TUNER, &vt) == 0) {
> +			if (mode != -1)
> +				vt.audmode = mode;
> +			doioctl(fd, VIDIOC_S_TUNER, &vt);
> +		}
> +	}
> +
> +	if (params.options[OptFreqSeek]) {
> +		params.freq_seek.tuner = params.tuner_index;
> +		params.freq_seek.type = V4L2_TUNER_RADIO;
> +		doioctl(fd, VIDIOC_S_HW_FREQ_SEEK, &params.freq_seek);
> +	}
> +}
> +
> +static void get_options(const int fd, const int capabilities, struct v4l2_frequency *vf,
> +			struct v4l2_tuner *tuner)
> +{
> +	double fac = 16;		/* factor for frequency division */
> +
> +	if (params.options[OptGetFreq]) {
> +		vf->type = V4L2_TUNER_RADIO;
> +		tuner->index = params.tuner_index;
> +		if (doioctl(fd, VIDIOC_G_TUNER, tuner) == 0) {
> +			fac = (tuner->capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16;
> +			vf->type = tuner->type;
> +		}
> +		vf->tuner = params.tuner_index;
> +		if (doioctl(fd, VIDIOC_G_FREQUENCY, vf) == 0)
> +			printf("Frequency for tuner %d: %d (%f MHz)\n",
> +				   vf->tuner, vf->frequency, vf->frequency / fac);
> +	}
> +
> +	if (params.options[OptGetTuner]) {
> +		struct v4l2_tuner vt;
> +
> +		memset(&vt, 0, sizeof(struct v4l2_tuner));
> +		vt.index = params.tuner_index;
> +		if (doioctl(fd, VIDIOC_G_TUNER, &vt) == 0) {
> +			printf("Tuner %d:\n", vt.index);
> +			printf("\tName                 : %s\n", vt.name);
> +			printf("\tCapabilities         : %s\n",
> +				tcap2s(vt.capability).c_str());
> +			if (vt.capability & V4L2_TUNER_CAP_LOW)
> +				printf("\tFrequency range      : %.1f MHz - %.1f MHz\n",
> +					 vt.rangelow / 16000.0, vt.rangehigh / 16000.0);
> +			else
> +				printf("\tFrequency range      : %.1f MHz - %.1f MHz\n",
> +					 vt.rangelow / 16.0, vt.rangehigh / 16.0);
> +			printf("\tSignal strength/AFC  : %d%%/%d\n",
> +				(int)((vt.signal / 655.35)+0.5), vt.afc);
> +			printf("\tCurrent audio mode   : %s\n", audmode2s(vt.audmode));
> +			printf("\tAvailable subchannels: %s\n",
> +					rxsubchans2s(vt.rxsubchans).c_str());
> +		}
> +	}
> +
> +	if (params.options[OptListFreqBands]) {
> +		struct v4l2_frequency_band band;
> +
> +		memset(&band, 0, sizeof(band));
> +		band.tuner = params.tuner_index;
> +		band.type = V4L2_TUNER_RADIO;
> +		band.index = 0;
> +		printf("ioctl: VIDIOC_ENUM_FREQ_BANDS\n");
> +		while (test_ioctl(fd, VIDIOC_ENUM_FREQ_BANDS, &band) >= 0) {
> +			if (band.index)
> +				printf("\n");
> +			printf("\tIndex          : %d\n", band.index);
> +			printf("\tModulation     : %s\n", modulation2s(band.modulation).c_str());
> +			printf("\tCapability     : %s\n", tcap2s(band.capability).c_str());
> +			if (band.capability & V4L2_TUNER_CAP_LOW)
> +				printf("\tFrequency Range: %.3f MHz - %.3f MHz\n",
> +				     band.rangelow / 16000.0, band.rangehigh / 16000.0);
> +			else
> +				printf("\tFrequency Range: %.3f MHz - %.3f MHz\n",
> +				     band.rangelow / 16.0, band.rangehigh / 16.0);
> +			band.index++;
> +		}
> +	}
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	int fd = -1;
> +
> +	/* command args */
> +	struct v4l2_tuner tuner;	/* set_freq/get_freq */
> +	struct v4l2_capability vcap;	/* list_cap */
> +	struct v4l2_frequency vf;	/* get_freq/set_freq */
> +
> +	memset(&tuner, 0, sizeof(tuner));
> +	memset(&vcap, 0, sizeof(vcap));
> +	memset(&vf, 0, sizeof(vf));
> +	strcpy(params.fd_name, "/dev/radio0");
> +
> +	/* define locale for unicode support */
> +	if (!setlocale(LC_CTYPE, "")) {
> +		fprintf(stderr, "Can't set the specified locale!\n");
> +		return 1;
> +	}
> +	/* register signal handler for interrupt signal, to exit gracefully */
> +	signal(SIGINT, signal_handler_interrupt);
> +
> +	/* try to parse the command line */
> +	parse_cl(argc, argv);
> +	if (params.options[OptHelp]) {
> +		usage();
> +		exit(0);
> +	}
> +
> +	/* File Mode: disables all other features, except for RDS decoding */
> +	if (params.filemode_active) {
> +		if ((fd = open(params.fd_name, O_RDONLY|O_NONBLOCK)) < 0){
> +			perror("error opening file");
> +			exit(1);
> +		}
> +		read_rds_from_fd(fd);
> +		test_close(fd);
> +		exit(0);
> +	}
> +
> +	/* Device Mode: open the radio device as read-only and non-blocking */
> +	if (!params.options[OptSetDevice]) {
> +		/* check the system for RDS capable devices */
> +		dev_vec devices = list_devices();
> +		if (devices.size() == 0) {
> +			fprintf(stderr, "No RDS-capable device found\n");
> +			exit(1);
> +		}
> +		strncpy(params.fd_name, devices[0].c_str(), 80);
> +		printf("Using device: %s\n", params.fd_name);
> +	}
> +	if ((fd = test_open(params.fd_name, O_RDONLY | O_NONBLOCK)) < 0) {
> +		fprintf(stderr, "Failed to open %s: %s\n", params.fd_name,
> +			strerror(errno));
> +		exit(1);
> +	}
> +	doioctl(fd, VIDIOC_QUERYCAP, &vcap);
> +
> +	/* Info options */
> +	if (params.options[OptGetDriverInfo])
> +		print_driver_info(&vcap);
> +	/* Set options */
> +	set_options(fd, vcap.capabilities, &vf, &tuner);
> +	/* Get options */
> +	get_options(fd, vcap.capabilities, &vf, &tuner);
> +	/* RDS decoding */
> +	if (params.options[OptReadRds])
> +		read_rds_from_fd(fd);
> +
> +	test_close(fd);
> +	exit(app_result);
> +}
>


Other then that this looks good to me.

Regards,

Hans

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-09 11:58     ` Hans de Goede
@ 2012-08-09 12:14       ` Hans Verkuil
  2012-08-10  7:16         ` Hans de Goede
  2012-08-09 14:30       ` Konke Radlow
  1 sibling, 1 reply; 15+ messages in thread
From: Hans Verkuil @ 2012-08-09 12:14 UTC (permalink / raw)
  To: Hans de Goede; +Cc: Konke Radlow, linux-media, koradlow

On Thu August 9 2012 13:58:07 Hans de Goede wrote:
> Hi Konke,
> 
> As Gregor already mentioned there is no need to define libv4l2rdssubdir in configure.ac ,
> so please drop that.
> 
> Other then that I've some minor remarks (comments inline), with all those
> fixed, this one is could to go. So hopefully the next version can be added
> to git master!
> 
> On 08/07/2012 05:11 PM, Konke Radlow wrote:
> > ---
> >   Makefile.am                     |    3 +-
> >   configure.ac                    |    7 +-
> >   lib/include/libv4l2rds.h        |  228 ++++++++++
> >   lib/libv4l2rds/Makefile.am      |   11 +
> >   lib/libv4l2rds/libv4l2rds.c     |  953 +++++++++++++++++++++++++++++++++++++++
> >   lib/libv4l2rds/libv4l2rds.pc.in |   11 +
> >   6 files changed, 1211 insertions(+), 2 deletions(-)
> >   create mode 100644 lib/include/libv4l2rds.h
> >   create mode 100644 lib/libv4l2rds/Makefile.am
> >   create mode 100644 lib/libv4l2rds/libv4l2rds.c
> >   create mode 100644 lib/libv4l2rds/libv4l2rds.pc.in
> >
> 
> <snip>
> 
> > diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h
> > new file mode 100644
> > index 0000000..4aa8593
> > --- /dev/null
> > +++ b/lib/include/libv4l2rds.h
> > @@ -0,0 +1,228 @@
> > +/*
> > + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> > + * Author: Konke Radlow <koradlow@gmail.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU Lesser General Public License as published by
> > + * the Free Software Foundation; either version 2.1 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, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
> > + */
> > +
> > +#ifndef __LIBV4L2RDS
> > +#define __LIBV4L2RDS
> > +
> > +#include <errno.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <stdbool.h>
> > +#include <unistd.h>
> > +#include <stdint.h>
> > +#include <time.h>
> > +#include <sys/types.h>
> > +#include <sys/mman.h>
> > +#include <config.h>
> 
> You should never include config.h in a public header, also
> are all the headers really needed for the prototypes in this header?
> 
> I don't think so! Please move all the unneeded ones to the libv4l2rds.c
> file!
> 
> > +
> > +#include <linux/videodev2.h>
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif /* __cplusplus */
> > +
> > +#if HAVE_VISIBILITY
> > +#define LIBV4L_PUBLIC __attribute__ ((visibility("default")))
> > +#else
> > +#define LIBV4L_PUBLIC
> > +#endif
> > +
> > +/* used to define the current version (version field) of the v4l2_rds struct */
> > +#define V4L2_RDS_VERSION (1)
> > +
> 
> What is the purpose of this field? Once we've released a v4l-utils with this
> library we are stuck to the API we've defined, having a version field & changing it,
> won't stop us from breaking existing apps, so once we've an official release we
> simply cannot make ABI breaking changes, which is why most of my review sofar
> has concentrated on the API side :)
> 
> I suggest dropping this define and the version field from the struct.

I think it is useful, actually. The v4l2_rds struct is allocated by the v4l2_rds_create
so at least in theory it is possible to extend the struct in the future without breaking
existing apps, provided you have a version number to check.

Regards,

	Hans

> 
> > +/* Constants used to define the size of arrays used to store RDS information */
> > +#define MAX_ODA_CNT 18 	/* there are 16 groups each with type a or b. Of these
> > +			 * 32 distinct groups, 18 can be used for ODA purposes */
> > +#define MAX_AF_CNT 25	/* AF Method A allows a maximum of 25 AFs to be defined
> > +			 * AF Method B does not impose a limit on the number of AFs
> > +			 * but it is not fully supported at the moment and will
> > +			 * not receive more than 25 AFs */
> > +
> > +/* Define Constants for the possible types of RDS information
> > + * used to address the relevant bit in the valid_fields bitmask */
> > +#define V4L2_RDS_PI 		0x01	/* Program Identification */
> > +#define V4L2_RDS_PTY		0x02	/* Program Type */
> > +#define V4L2_RDS_TP		0x04	/* Traffic Program */
> > +#define V4L2_RDS_PS		0x08	/* Program Service Name */
> > +#define V4L2_RDS_TA		0x10	/* Traffic Announcement */
> > +#define V4L2_RDS_DI		0x20	/* Decoder Information */
> > +#define V4L2_RDS_MS		0x40	/* Music / Speech flag */
> > +#define V4L2_RDS_PTYN		0x80	/* Program Type Name */
> > +#define V4L2_RDS_RT		0x100 	/* Radio-Text */
> > +#define V4L2_RDS_TIME		0x200	/* Date and Time information */
> > +#define V4L2_RDS_TMC		0x400	/* TMC availability */
> > +#define V4L2_RDS_AF		0x800	/* AF (alternative freq) available */
> > +#define V4L2_RDS_ECC		0x1000	/* Extended County Code */
> > +#define V4L2_RDS_LC		0x2000	/* Language Code */
> > +
> > +/* Define Constants for the state of the RDS decoding process
> > + * used to address the relevant bit in the decode_information bitmask */
> > +#define V4L2_RDS_GROUP_NEW 	0x01	/* New group received */
> > +#define V4L2_RDS_ODA		0x02	/* Open Data Group announced */
> > +
> > +/* Decoder Information (DI) codes
> > + * used to decode the DI information according to the RDS standard */
> > +#define V4L2_RDS_FLAG_STEREO 		0x01
> > +#define V4L2_RDS_FLAG_ARTIFICIAL_HEAD	0x02
> > +#define V4L2_RDS_FLAG_COMPRESSED	0x04
> > +#define V4L2_RDS_FLAG_STATIC_PTY	0x08
> > +
> > +/* struct to encapsulate one complete RDS group */
> > +/* This structure is used internally to store data until a complete RDS
> > + * group was received and group id dependent decoding can be done.
> > + * It is also used to provide external access to uninterpreted RDS groups
> > + * when manual decoding is required (e.g. special ODA types) */
> > +struct v4l2_rds_group {
> > +	uint16_t pi;		/* Program Identification */
> > +	char group_version;	/* group version ('A' / 'B') */
> > +	uint8_t group_id;	/* group number (0..16) */
> > +
> > +	/* uninterpreted data blocks for decoding (e.g. ODA) */
> > +	uint8_t data_b_lsb;
> > +	uint8_t data_c_msb;
> > +	uint8_t data_c_lsb;
> > +	uint8_t data_d_msb;
> > +	uint8_t data_d_lsb;
> > +};
> > +
> > +/* struct to encapsulate some statistical information about the decoding process */
> > +struct v4l2_rds_statistics {
> > +	uint32_t block_cnt;		/* total amount of received blocks */
> > +	uint32_t group_cnt;		/* total amount of successfully
> > +					 * decoded groups */
> > +	uint32_t block_error_cnt;	/* blocks that were marked as erroneous
> > +					 * and had to be dropped */
> > +	uint32_t group_error_cnt;	/* group decoding processes that had to be
> > +					 * aborted because of erroneous blocks
> > +					 * or wrong order of blocks */
> > +	uint32_t block_corrected_cnt;	/* blocks that contained 1-bit errors
> > +					 * which were corrected */
> > +	uint32_t group_type_cnt[16];	/* number of occurrence for each
> > +					 * defined RDS group */
> > +};
> > +
> > +/* struct to encapsulate the definition of one ODA (Open Data Application) type */
> > +struct v4l2_rds_oda {
> > +	uint8_t group_id;	/* RDS group used to broadcast this ODA */
> > +	char group_version;	/* group version (A / B) for this ODA */
> > +	uint16_t aid;		/* Application Identification for this ODA,
> > +				 * AIDs are centrally administered by the
> > +				 * RDS Registration Office (rds.org.uk) */
> > +};
> > +
> > +/* struct to encapsulate an array of all defined ODA types for a channel */
> > +/* This structure will grow with ODA announcements broadcasted in type 3A
> > + * groups, that were verified not to be no duplicates or redefinitions */
> > +struct v4l2_rds_oda_set {
> > +	uint8_t size;		/* number of ODAs defined by this channel */
> > +	struct v4l2_rds_oda oda[MAX_ODA_CNT];
> > +};
> > +
> > +/* struct to encapsulate an array of Alternative Frequencies for a channel */
> > +/* Every channel can send out AFs for his program. The number of AFs that
> > + * will be broadcasted is announced by the channel */
> > +struct v4l2_rds_af_set {
> > +	uint8_t size;			/* size of the set (might be smaller
> > +					 * than the announced size) */
> > +	uint8_t announced_af;		/* number of announced AF */
> > +	uint32_t af[MAX_AF_CNT];	/* AFs defined in Hz */
> > +};
> > +
> > +/* struct to encapsulate state and RDS information for current decoding process */
> > +/* This is the structure that will be used by external applications, to
> > + * communicate with the library and get access to RDS data */
> > +struct v4l2_rds {
> > +	uint32_t version;	/* version number of this structure */
> > +
> > +	/** state information **/
> > +	uint32_t decode_information;	/* state of decoding process */
> > +	uint32_t valid_fields;		/* currently valid info fields
> > +					 * of this structure */
> > +
> > +	/** RDS info fields **/
> > +	bool is_rbds; 		/* use RBDS standard version of LUTs */
> > +	uint16_t pi;		/* Program Identification */
> > +	uint8_t ps[9];		/* Program Service Name, UTF-8 encoding,
> > +				 * '\0' terminated */
> > +	uint8_t pty;		/* Program Type */
> > +	uint8_t ptyn[9];	/* Program Type Name, UTF-8 encoding,
> > +				 * '\0' terminated */
> > +	bool ptyn_ab_flag;	/* PTYN A/B flag (toggled), to signal
> > +				 * change of PTYN */
> > +	uint8_t rt_length;	/* length of RT string */
> > +	uint8_t rt[65];		/* Radio-Text string, UTF-8 encoding,
> > +				 * '\0' terminated */
> > +	bool rt_ab_flag;	/* RT A/B flag (toggled), to signal
> > +				 * transmission of new RT */
> > +	bool ta;		/* Traffic Announcement */
> > +	bool tp;		/* Traffic Program */
> > +	bool ms;		/* Music / Speech flag */
> > +	uint8_t di;		/* Decoder Information */
> > +	uint8_t ecc;		/* Extended Country Code */
> > +	uint8_t lc;		/* Language Code */
> > +	time_t time;		/* Time and Date of transmission */
> > +
> > +	struct v4l2_rds_statistics rds_statistics;
> > +	struct v4l2_rds_oda_set rds_oda;	/* Open Data Services */
> > +	struct v4l2_rds_af_set rds_af; 		/* Alternative Frequencies */
> > +};
> > +
> > +/* v4l2_rds_init() - initializes a new decoding process
> > + * @is_rbds:	defines which standard is used: true=RBDS, false=RDS
> > + *
> > + * initialize a new instance of the RDS-decoding struct and return
> > + * a handle containing state and RDS information, used to interact
> > + * with the library functions */
> > +LIBV4L_PUBLIC struct v4l2_rds *v4l2_rds_create(bool is_rdbs);
> > +
> > +/* frees all memory allocated for the RDS-decoding struct */
> > +LIBV4L_PUBLIC void v4l2_rds_destroy(struct v4l2_rds *handle);
> > +
> > +/* resets the RDS information in the handle to initial values
> > + * e.g. can be used when radio channel is changed
> > + * @reset_statistics:	true = set all statistic values to 0, false = keep them untouched */
> > +LIBV4L_PUBLIC void v4l2_rds_reset(struct v4l2_rds *handle, bool reset_statistics);
> > +
> > +/* adds a raw RDS block to decode it into RDS groups
> > + * @return:	bitmask with with updated fields set to 1
> > + * @rds_data: 	3 bytes of raw RDS data, obtained by calling read()
> > + * 				on RDS capable V4L2 devices */
> > +LIBV4L_PUBLIC uint32_t v4l2_rds_add(struct v4l2_rds *handle, struct v4l2_rds_data *rds_data);
> > +
> > +/*
> > + * group of functions to translate numerical RDS data into strings
> > + *
> > + * return program description string defined in the RDS/RBDS Standard
> > + * ! return value deepens on selected Standard !*/
> 
>                       ^^^^^^^ Typo!
> 
> > +LIBV4L_PUBLIC const char *v4l2_rds_get_pty_str(const struct v4l2_rds *handle);
> > +LIBV4L_PUBLIC const char *v4l2_rds_get_language_str(const struct v4l2_rds *handle);
> > +LIBV4L_PUBLIC const char *v4l2_rds_get_country_str(const struct v4l2_rds *handle);
> > +LIBV4L_PUBLIC const char *v4l2_rds_get_coverage_str(const struct v4l2_rds *handle);
> > +
> > +/* returns a pointer to the last decoded RDS group, in order to give raw
> > + * access to RDS data if it is required (e.g. ODA decoding) */
> > +LIBV4L_PUBLIC const struct v4l2_rds_group *v4l2_rds_get_group
> > +	(const struct v4l2_rds *handle);
> > +
> > +
> > +#ifdef __cplusplus
> > +}
> > +#endif /* __cplusplus */
> > +#endif
> 
> <snip>
> 
> The rest looks good to me.
> 
> Regards,
> 
> Hans
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-07 15:11   ` [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC) Konke Radlow
                       ` (2 preceding siblings ...)
  2012-08-09 11:58     ` Hans de Goede
@ 2012-08-09 12:17     ` Hans Verkuil
  3 siblings, 0 replies; 15+ messages in thread
From: Hans Verkuil @ 2012-08-09 12:17 UTC (permalink / raw)
  To: Konke Radlow; +Cc: linux-media, hdegoede, koradlow

On Tue August 7 2012 17:11:54 Konke Radlow wrote:
> ---
>  Makefile.am                     |    3 +-
>  configure.ac                    |    7 +-
>  lib/include/libv4l2rds.h        |  228 ++++++++++
>  lib/libv4l2rds/Makefile.am      |   11 +
>  lib/libv4l2rds/libv4l2rds.c     |  953 +++++++++++++++++++++++++++++++++++++++
>  lib/libv4l2rds/libv4l2rds.pc.in |   11 +
>  6 files changed, 1211 insertions(+), 2 deletions(-)
>  create mode 100644 lib/include/libv4l2rds.h
>  create mode 100644 lib/libv4l2rds/Makefile.am
>  create mode 100644 lib/libv4l2rds/libv4l2rds.c
>  create mode 100644 lib/libv4l2rds/libv4l2rds.pc.in
> 
> diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h
> new file mode 100644
> index 0000000..4aa8593
> --- /dev/null
> +++ b/lib/include/libv4l2rds.h
> @@ -0,0 +1,228 @@
> +/*
> + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + * Author: Konke Radlow <koradlow@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation; either version 2.1 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, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
> + */
> +
> +#ifndef __LIBV4L2RDS
> +#define __LIBV4L2RDS
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +#include <stdint.h>
> +#include <time.h>
> +#include <sys/types.h>
> +#include <sys/mman.h>
> +#include <config.h>
> +
> +#include <linux/videodev2.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif /* __cplusplus */
> +
> +#if HAVE_VISIBILITY
> +#define LIBV4L_PUBLIC __attribute__ ((visibility("default")))
> +#else
> +#define LIBV4L_PUBLIC
> +#endif
> +
> +/* used to define the current version (version field) of the v4l2_rds struct */
> +#define V4L2_RDS_VERSION (1)
> +
> +/* Constants used to define the size of arrays used to store RDS information */
> +#define MAX_ODA_CNT 18 	/* there are 16 groups each with type a or b. Of these
> +			 * 32 distinct groups, 18 can be used for ODA purposes */
> +#define MAX_AF_CNT 25	/* AF Method A allows a maximum of 25 AFs to be defined
> +			 * AF Method B does not impose a limit on the number of AFs
> +			 * but it is not fully supported at the moment and will
> +			 * not receive more than 25 AFs */
> +
> +/* Define Constants for the possible types of RDS information
> + * used to address the relevant bit in the valid_fields bitmask */
> +#define V4L2_RDS_PI 		0x01	/* Program Identification */
> +#define V4L2_RDS_PTY		0x02	/* Program Type */
> +#define V4L2_RDS_TP		0x04	/* Traffic Program */
> +#define V4L2_RDS_PS		0x08	/* Program Service Name */
> +#define V4L2_RDS_TA		0x10	/* Traffic Announcement */
> +#define V4L2_RDS_DI		0x20	/* Decoder Information */
> +#define V4L2_RDS_MS		0x40	/* Music / Speech flag */
> +#define V4L2_RDS_PTYN		0x80	/* Program Type Name */
> +#define V4L2_RDS_RT		0x100 	/* Radio-Text */
> +#define V4L2_RDS_TIME		0x200	/* Date and Time information */
> +#define V4L2_RDS_TMC		0x400	/* TMC availability */
> +#define V4L2_RDS_AF		0x800	/* AF (alternative freq) available */
> +#define V4L2_RDS_ECC		0x1000	/* Extended County Code */
> +#define V4L2_RDS_LC		0x2000	/* Language Code */
> +
> +/* Define Constants for the state of the RDS decoding process
> + * used to address the relevant bit in the decode_information bitmask */
> +#define V4L2_RDS_GROUP_NEW 	0x01	/* New group received */
> +#define V4L2_RDS_ODA		0x02	/* Open Data Group announced */
> +
> +/* Decoder Information (DI) codes
> + * used to decode the DI information according to the RDS standard */
> +#define V4L2_RDS_FLAG_STEREO 		0x01
> +#define V4L2_RDS_FLAG_ARTIFICIAL_HEAD	0x02
> +#define V4L2_RDS_FLAG_COMPRESSED	0x04
> +#define V4L2_RDS_FLAG_STATIC_PTY	0x08
> +
> +/* struct to encapsulate one complete RDS group */
> +/* This structure is used internally to store data until a complete RDS
> + * group was received and group id dependent decoding can be done.
> + * It is also used to provide external access to uninterpreted RDS groups
> + * when manual decoding is required (e.g. special ODA types) */
> +struct v4l2_rds_group {
> +	uint16_t pi;		/* Program Identification */
> +	char group_version;	/* group version ('A' / 'B') */
> +	uint8_t group_id;	/* group number (0..16) */
> +
> +	/* uninterpreted data blocks for decoding (e.g. ODA) */
> +	uint8_t data_b_lsb;
> +	uint8_t data_c_msb;
> +	uint8_t data_c_lsb;
> +	uint8_t data_d_msb;
> +	uint8_t data_d_lsb;
> +};
> +
> +/* struct to encapsulate some statistical information about the decoding process */
> +struct v4l2_rds_statistics {
> +	uint32_t block_cnt;		/* total amount of received blocks */
> +	uint32_t group_cnt;		/* total amount of successfully
> +					 * decoded groups */
> +	uint32_t block_error_cnt;	/* blocks that were marked as erroneous
> +					 * and had to be dropped */
> +	uint32_t group_error_cnt;	/* group decoding processes that had to be
> +					 * aborted because of erroneous blocks
> +					 * or wrong order of blocks */
> +	uint32_t block_corrected_cnt;	/* blocks that contained 1-bit errors
> +					 * which were corrected */
> +	uint32_t group_type_cnt[16];	/* number of occurrence for each
> +					 * defined RDS group */
> +};
> +
> +/* struct to encapsulate the definition of one ODA (Open Data Application) type */
> +struct v4l2_rds_oda {
> +	uint8_t group_id;	/* RDS group used to broadcast this ODA */
> +	char group_version;	/* group version (A / B) for this ODA */
> +	uint16_t aid;		/* Application Identification for this ODA,
> +				 * AIDs are centrally administered by the
> +				 * RDS Registration Office (rds.org.uk) */
> +};
> +
> +/* struct to encapsulate an array of all defined ODA types for a channel */
> +/* This structure will grow with ODA announcements broadcasted in type 3A
> + * groups, that were verified not to be no duplicates or redefinitions */
> +struct v4l2_rds_oda_set {
> +	uint8_t size;		/* number of ODAs defined by this channel */
> +	struct v4l2_rds_oda oda[MAX_ODA_CNT];
> +};
> +
> +/* struct to encapsulate an array of Alternative Frequencies for a channel */
> +/* Every channel can send out AFs for his program. The number of AFs that
> + * will be broadcasted is announced by the channel */
> +struct v4l2_rds_af_set {
> +	uint8_t size;			/* size of the set (might be smaller
> +					 * than the announced size) */
> +	uint8_t announced_af;		/* number of announced AF */
> +	uint32_t af[MAX_AF_CNT];	/* AFs defined in Hz */
> +};
> +
> +/* struct to encapsulate state and RDS information for current decoding process */
> +/* This is the structure that will be used by external applications, to
> + * communicate with the library and get access to RDS data */
> +struct v4l2_rds {
> +	uint32_t version;	/* version number of this structure */
> +
> +	/** state information **/
> +	uint32_t decode_information;	/* state of decoding process */
> +	uint32_t valid_fields;		/* currently valid info fields
> +					 * of this structure */
> +
> +	/** RDS info fields **/
> +	bool is_rbds; 		/* use RBDS standard version of LUTs */
> +	uint16_t pi;		/* Program Identification */
> +	uint8_t ps[9];		/* Program Service Name, UTF-8 encoding,
> +				 * '\0' terminated */
> +	uint8_t pty;		/* Program Type */
> +	uint8_t ptyn[9];	/* Program Type Name, UTF-8 encoding,
> +				 * '\0' terminated */
> +	bool ptyn_ab_flag;	/* PTYN A/B flag (toggled), to signal
> +				 * change of PTYN */
> +	uint8_t rt_length;	/* length of RT string */
> +	uint8_t rt[65];		/* Radio-Text string, UTF-8 encoding,
> +				 * '\0' terminated */
> +	bool rt_ab_flag;	/* RT A/B flag (toggled), to signal
> +				 * transmission of new RT */
> +	bool ta;		/* Traffic Announcement */
> +	bool tp;		/* Traffic Program */
> +	bool ms;		/* Music / Speech flag */
> +	uint8_t di;		/* Decoder Information */
> +	uint8_t ecc;		/* Extended Country Code */
> +	uint8_t lc;		/* Language Code */
> +	time_t time;		/* Time and Date of transmission */

The comment needs to be improved: is this local time or UTC?
That's not clear from the comment.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-09  7:22     ` [RFC PATCH 1/2] Add libv4l2rds library " Gregor Jasny
@ 2012-08-09 14:12       ` Konke Radlow
  0 siblings, 0 replies; 15+ messages in thread
From: Konke Radlow @ 2012-08-09 14:12 UTC (permalink / raw)
  To: Gregor Jasny; +Cc: Konke Radlow, linux-media

they are gone.

And btw: I'm sorry for fiddling with your build environment in such a
way. I have to
admit that the additions I made were based more on copying from
existing libraries than
really understanding the effects of each command.

Regards,
Konke

On Thu, Aug 9, 2012 at 7:22 AM, Gregor Jasny <gjasny@googlemail.com> wrote:
> Hello Konke,
>
>
> On 8/7/12 5:11 PM, Konke Radlow wrote:
>>
>> diff --git a/configure.ac b/configure.ac
>> index 8ddcc9d..1109c4d 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -146,9 +148,12 @@ AC_ARG_WITH(libv4l2subdir,
>> AS_HELP_STRING(--with-libv4l2subdir=DIR,set libv4l2 l
>>   AC_ARG_WITH(libv4lconvertsubdir,
>> AS_HELP_STRING(--with-libv4lconvertsubdir=DIR,set libv4lconvert library
>> subdir [default=libv4l]),
>>      libv4lconvertsubdir=$withval, libv4lconvertsubdir="libv4l")
>>
>> +AC_ARG_WITH(libv4l2rdssubdir,
>> AS_HELP_STRING(--with-libv4l2rdssubdir=DIR,set libv4l2rds library subdir
>> [default=libv4l]),
>> +   libv4l2rdssubdir=$withval, libv4l2rdssubdir="libv4l")
>> +
>>   AC_ARG_WITH(udevdir, AS_HELP_STRING(--with-udevdir=DIR,set udev
>> directory [default=/lib/udev]),
>>      udevdir=$withval, udevdir="/lib/udev")
>> -
>> +
>>   libv4l1privdir="$libdir/$libv4l1subdir"
>>   libv4l2privdir="$libdir/$libv4l2subdir"
>>   libv4l2plugindir="$libv4l2privdir/plugins"
>
>
> please remove these changes. They are not needed for the RDS library.
>
> Thanks,
> Gregor
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-09 11:58     ` Hans de Goede
  2012-08-09 12:14       ` Hans Verkuil
@ 2012-08-09 14:30       ` Konke Radlow
  1 sibling, 0 replies; 15+ messages in thread
From: Konke Radlow @ 2012-08-09 14:30 UTC (permalink / raw)
  To: Hans de Goede; +Cc: Konke Radlow, linux-media, hverkuil, koradlow

On Thu, Aug 9, 2012 at 11:58 AM, Hans de Goede <hdegoede@redhat.com> wrote:

> Other then that I've some minor remarks (comments inline), with all those
> fixed, this one is could to go. So hopefully the next version can be added
> to git master!

I'm very happy to hear that :)

>
>
> On 08/07/2012 05:11 PM, Konke Radlow wrote:
>>
>> ---
>>   Makefile.am                     |    3 +-
>>   configure.ac                    |    7 +-
>>   lib/include/libv4l2rds.h        |  228 ++++++++++
>>   lib/libv4l2rds/Makefile.am      |   11 +
>>   lib/libv4l2rds/libv4l2rds.c     |  953
>> +++++++++++++++++++++++++++++++++++++++
>>   lib/libv4l2rds/libv4l2rds.pc.in |   11 +
>>   6 files changed, 1211 insertions(+), 2 deletions(-)
>>   create mode 100644 lib/include/libv4l2rds.h
>>   create mode 100644 lib/libv4l2rds/Makefile.am
>>   create mode 100644 lib/libv4l2rds/libv4l2rds.c
>>   create mode 100644 lib/libv4l2rds/libv4l2rds.pc.in
>>
>
> <snip>
>
>
>> diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h
>> new file mode 100644
>> index 0000000..4aa8593
>> --- /dev/null
>> +++ b/lib/include/libv4l2rds.h
>> @@ -0,0 +1,228 @@
>> +/*
>> + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights
>> reserved.
>> + * Author: Konke Radlow <koradlow@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU Lesser General Public License as
>> published by
>> + * the Free Software Foundation; either version 2.1 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, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA
>> 02110-1335  USA
>> + */
>> +
>> +#ifndef __LIBV4L2RDS
>> +#define __LIBV4L2RDS
>> +
>> +#include <errno.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <stdbool.h>
>> +#include <unistd.h>
>> +#include <stdint.h>
>> +#include <time.h>
>> +#include <sys/types.h>
>> +#include <sys/mman.h>
>> +#include <config.h>
>
>
> You should never include config.h in a public header, also
> are all the headers really needed for the prototypes in this header?
>
> I don't think so! Please move all the unneeded ones to the libv4l2rds.c
> file!
>
That's true, I don't know why I had all these include directives in the public
header. After cleaning up we're left with only three (stdbool, stdint
and videodev2)
>
>> +
>> +#include <linux/videodev2.h>
>> +
>> +#ifdef __cplusplus
>> +extern "C" {
>> +#endif /* __cplusplus */
>> +
>> +#if HAVE_VISIBILITY
>> +#define LIBV4L_PUBLIC __attribute__ ((visibility("default")))
>> +#else
>> +#define LIBV4L_PUBLIC
>> +#endif
>> +
>> +/* used to define the current version (version field) of the v4l2_rds
>> struct */
>> +#define V4L2_RDS_VERSION (1)
>> +
>
>
> What is the purpose of this field? Once we've released a v4l-utils with this
> library we are stuck to the API we've defined, having a version field &
> changing it,
> won't stop us from breaking existing apps, so once we've an official release
> we
> simply cannot make ABI breaking changes, which is why most of my review
> sofar
> has concentrated on the API side :)
>
> I suggest dropping this define and the version field from the struct.
>
As Hans mentioned this version field is not supposed to denote the API version,
but rather the version of the v4l2_rds_handle struct. This struct could then
theoretically be updated in the future and provide applications a way
of verifying
their compatibility with the rds-library version installed on the system.
>
>> +/* Constants used to define the size of arrays used to store RDS
>> information */
>> +#define MAX_ODA_CNT 18         /* there are 16 groups each with type a or
>> b. Of these
>> +                        * 32 distinct groups, 18 can be used for ODA
>> purposes */
>> +#define MAX_AF_CNT 25  /* AF Method A allows a maximum of 25 AFs to be
>> defined
>> +                        * AF Method B does not impose a limit on the
>> number of AFs
>> +                        * but it is not fully supported at the moment and
>> will
>> +                        * not receive more than 25 AFs */
>> +
>> +/* Define Constants for the possible types of RDS information
>> + * used to address the relevant bit in the valid_fields bitmask */
>> +#define V4L2_RDS_PI            0x01    /* Program Identification */
>> +#define V4L2_RDS_PTY           0x02    /* Program Type */
>> +#define V4L2_RDS_TP            0x04    /* Traffic Program */
>> +#define V4L2_RDS_PS            0x08    /* Program Service Name */
>> +#define V4L2_RDS_TA            0x10    /* Traffic Announcement */
>> +#define V4L2_RDS_DI            0x20    /* Decoder Information */
>> +#define V4L2_RDS_MS            0x40    /* Music / Speech flag */
>> +#define V4L2_RDS_PTYN          0x80    /* Program Type Name */
>> +#define V4L2_RDS_RT            0x100   /* Radio-Text */
>> +#define V4L2_RDS_TIME          0x200   /* Date and Time information */
>> +#define V4L2_RDS_TMC           0x400   /* TMC availability */
>> +#define V4L2_RDS_AF            0x800   /* AF (alternative freq) available
>> */
>> +#define V4L2_RDS_ECC           0x1000  /* Extended County Code */
>> +#define V4L2_RDS_LC            0x2000  /* Language Code */
>> +
>> +/* Define Constants for the state of the RDS decoding process
>> + * used to address the relevant bit in the decode_information bitmask */
>> +#define V4L2_RDS_GROUP_NEW     0x01    /* New group received */
>> +#define V4L2_RDS_ODA           0x02    /* Open Data Group announced */
>> +
>> +/* Decoder Information (DI) codes
>> + * used to decode the DI information according to the RDS standard */
>> +#define V4L2_RDS_FLAG_STEREO           0x01
>> +#define V4L2_RDS_FLAG_ARTIFICIAL_HEAD  0x02
>> +#define V4L2_RDS_FLAG_COMPRESSED       0x04
>> +#define V4L2_RDS_FLAG_STATIC_PTY       0x08
>> +
>> +/* struct to encapsulate one complete RDS group */
>> +/* This structure is used internally to store data until a complete RDS
>> + * group was received and group id dependent decoding can be done.
>> + * It is also used to provide external access to uninterpreted RDS groups
>> + * when manual decoding is required (e.g. special ODA types) */
>> +struct v4l2_rds_group {
>> +       uint16_t pi;            /* Program Identification */
>> +       char group_version;     /* group version ('A' / 'B') */
>> +       uint8_t group_id;       /* group number (0..16) */
>> +
>> +       /* uninterpreted data blocks for decoding (e.g. ODA) */
>> +       uint8_t data_b_lsb;
>> +       uint8_t data_c_msb;
>> +       uint8_t data_c_lsb;
>> +       uint8_t data_d_msb;
>> +       uint8_t data_d_lsb;
>> +};
>> +
>> +/* struct to encapsulate some statistical information about the decoding
>> process */
>> +struct v4l2_rds_statistics {
>> +       uint32_t block_cnt;             /* total amount of received blocks
>> */
>> +       uint32_t group_cnt;             /* total amount of successfully
>> +                                        * decoded groups */
>> +       uint32_t block_error_cnt;       /* blocks that were marked as
>> erroneous
>> +                                        * and had to be dropped */
>> +       uint32_t group_error_cnt;       /* group decoding processes that
>> had to be
>> +                                        * aborted because of erroneous
>> blocks
>> +                                        * or wrong order of blocks */
>> +       uint32_t block_corrected_cnt;   /* blocks that contained 1-bit
>> errors
>> +                                        * which were corrected */
>> +       uint32_t group_type_cnt[16];    /* number of occurrence for each
>> +                                        * defined RDS group */
>> +};
>> +
>> +/* struct to encapsulate the definition of one ODA (Open Data
>> Application) type */
>> +struct v4l2_rds_oda {
>> +       uint8_t group_id;       /* RDS group used to broadcast this ODA */
>> +       char group_version;     /* group version (A / B) for this ODA */
>> +       uint16_t aid;           /* Application Identification for this
>> ODA,
>> +                                * AIDs are centrally administered by the
>> +                                * RDS Registration Office (rds.org.uk) */
>> +};
>> +
>> +/* struct to encapsulate an array of all defined ODA types for a channel
>> */
>> +/* This structure will grow with ODA announcements broadcasted in type 3A
>> + * groups, that were verified not to be no duplicates or redefinitions */
>> +struct v4l2_rds_oda_set {
>> +       uint8_t size;           /* number of ODAs defined by this channel
>> */
>> +       struct v4l2_rds_oda oda[MAX_ODA_CNT];
>> +};
>> +
>> +/* struct to encapsulate an array of Alternative Frequencies for a
>> channel */
>> +/* Every channel can send out AFs for his program. The number of AFs that
>> + * will be broadcasted is announced by the channel */
>> +struct v4l2_rds_af_set {
>> +       uint8_t size;                   /* size of the set (might be
>> smaller
>> +                                        * than the announced size) */
>> +       uint8_t announced_af;           /* number of announced AF */
>> +       uint32_t af[MAX_AF_CNT];        /* AFs defined in Hz */
>> +};
>> +
>> +/* struct to encapsulate state and RDS information for current decoding
>> process */
>> +/* This is the structure that will be used by external applications, to
>> + * communicate with the library and get access to RDS data */
>> +struct v4l2_rds {
>> +       uint32_t version;       /* version number of this structure */
>> +
>> +       /** state information **/
>> +       uint32_t decode_information;    /* state of decoding process */
>> +       uint32_t valid_fields;          /* currently valid info fields
>> +                                        * of this structure */
>> +
>> +       /** RDS info fields **/
>> +       bool is_rbds;           /* use RBDS standard version of LUTs */
>> +       uint16_t pi;            /* Program Identification */
>> +       uint8_t ps[9];          /* Program Service Name, UTF-8 encoding,
>> +                                * '\0' terminated */
>> +       uint8_t pty;            /* Program Type */
>> +       uint8_t ptyn[9];        /* Program Type Name, UTF-8 encoding,
>> +                                * '\0' terminated */
>> +       bool ptyn_ab_flag;      /* PTYN A/B flag (toggled), to signal
>> +                                * change of PTYN */
>> +       uint8_t rt_length;      /* length of RT string */
>> +       uint8_t rt[65];         /* Radio-Text string, UTF-8 encoding,
>> +                                * '\0' terminated */
>> +       bool rt_ab_flag;        /* RT A/B flag (toggled), to signal
>> +                                * transmission of new RT */
>> +       bool ta;                /* Traffic Announcement */
>> +       bool tp;                /* Traffic Program */
>> +       bool ms;                /* Music / Speech flag */
>> +       uint8_t di;             /* Decoder Information */
>> +       uint8_t ecc;            /* Extended Country Code */
>> +       uint8_t lc;             /* Language Code */
>> +       time_t time;            /* Time and Date of transmission */
>> +
>> +       struct v4l2_rds_statistics rds_statistics;
>> +       struct v4l2_rds_oda_set rds_oda;        /* Open Data Services */
>> +       struct v4l2_rds_af_set rds_af;          /* Alternative Frequencies
>> */
>> +};
>> +
>> +/* v4l2_rds_init() - initializes a new decoding process
>> + * @is_rbds:   defines which standard is used: true=RBDS, false=RDS
>> + *
>> + * initialize a new instance of the RDS-decoding struct and return
>> + * a handle containing state and RDS information, used to interact
>> + * with the library functions */
>> +LIBV4L_PUBLIC struct v4l2_rds *v4l2_rds_create(bool is_rdbs);
>> +
>> +/* frees all memory allocated for the RDS-decoding struct */
>> +LIBV4L_PUBLIC void v4l2_rds_destroy(struct v4l2_rds *handle);
>> +
>> +/* resets the RDS information in the handle to initial values
>> + * e.g. can be used when radio channel is changed
>> + * @reset_statistics:  true = set all statistic values to 0, false = keep
>> them untouched */
>> +LIBV4L_PUBLIC void v4l2_rds_reset(struct v4l2_rds *handle, bool
>> reset_statistics);
>> +
>> +/* adds a raw RDS block to decode it into RDS groups
>> + * @return:    bitmask with with updated fields set to 1
>> + * @rds_data:  3 bytes of raw RDS data, obtained by calling read()
>> + *                             on RDS capable V4L2 devices */
>> +LIBV4L_PUBLIC uint32_t v4l2_rds_add(struct v4l2_rds *handle, struct
>> v4l2_rds_data *rds_data);
>> +
>> +/*
>> + * group of functions to translate numerical RDS data into strings
>> + *
>> + * return program description string defined in the RDS/RBDS Standard
>> + * ! return value deepens on selected Standard !*/
>
>
>                      ^^^^^^^ Typo!
>
>
>> +LIBV4L_PUBLIC const char *v4l2_rds_get_pty_str(const struct v4l2_rds
>> *handle);
>> +LIBV4L_PUBLIC const char *v4l2_rds_get_language_str(const struct v4l2_rds
>> *handle);
>> +LIBV4L_PUBLIC const char *v4l2_rds_get_country_str(const struct v4l2_rds
>> *handle);
>> +LIBV4L_PUBLIC const char *v4l2_rds_get_coverage_str(const struct v4l2_rds
>> *handle);
>> +
>> +/* returns a pointer to the last decoded RDS group, in order to give raw
>> + * access to RDS data if it is required (e.g. ODA decoding) */
>> +LIBV4L_PUBLIC const struct v4l2_rds_group *v4l2_rds_get_group
>> +       (const struct v4l2_rds *handle);
>> +
>> +
>> +#ifdef __cplusplus
>> +}
>> +#endif /* __cplusplus */
>> +#endif
>
>
> <snip>
>
> The rest looks good to me.
>
> Regards,
>
> Hans
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 2/2] Add rds-ctl tool (with changes proposed in RFC)
  2012-08-09 12:05       ` Hans de Goede
@ 2012-08-09 14:34         ` Konke Radlow
  0 siblings, 0 replies; 15+ messages in thread
From: Konke Radlow @ 2012-08-09 14:34 UTC (permalink / raw)
  To: Hans de Goede; +Cc: linux-media, hverkuil, koradlow

On Thu, Aug 9, 2012 at 12:05 PM, Hans de Goede <hdegoede@redhat.com> wrote:
> Hi,
>
> Comments inline.
>
>
> On 08/07/2012 05:11 PM, Konke Radlow wrote:
>>
>> ---
>>   Makefile.am               |    3 +-
>>   configure.ac              |    1 +
>>   utils/rds-ctl/Makefile.am |    5 +
>>   utils/rds-ctl/rds-ctl.cpp |  959
>> +++++++++++++++++++++++++++++++++++++++++++++
>>   4 files changed, 967 insertions(+), 1 deletion(-)
>>   create mode 100644 utils/rds-ctl/Makefile.am
>>   create mode 100644 utils/rds-ctl/rds-ctl.cpp
>>
>> diff --git a/Makefile.am b/Makefile.am
>> index 621d8d9..8ef0d00 100644
>> --- a/Makefile.am
>> +++ b/Makefile.am
>
>
> <Snip>
>
>
>> +static void print_rds_data(const struct v4l2_rds *handle, uint32_t
>> updated_fields)
>> +{
>> +       if (params.options[OptPrintBlock])
>> +               updated_fields = 0xffffffff;
>> +
>> +       if ((updated_fields & V4L2_RDS_PI) &&
>> +                       (handle->valid_fields & V4L2_RDS_PI)) {
>> +               printf("\nPI: %04x", handle->pi);
>> +               print_rds_pi(handle);
>> +       }
>> +
>> +       if (updated_fields & V4L2_RDS_PS &&
>> +                       handle->valid_fields & V4L2_RDS_PS) {
>> +               printf("\nPS: ");
>> +               for (int i = 0; i < 8; ++i) {
>> +                       /* filter out special characters */
>> +                       if (handle->ps[i] >= 0x20 && handle->ps[i] <=
>> 0x7E)
>> +                               printf("%lc",handle->ps[i]);
>> +                       else
>> +                               printf(" ");
>> +               }
>
>
>
> Since ps now is a 0 terminated UTF-8 string you should be able to just do:
>                 printf("\nPS: %s", handle->ps);
>
> And likewise for the other strings.
>

changed as proposed, and works like a charm :)

>
>
> Other then that this looks good to me.
>
> Regards,
>
> Hans

thank you for the review

Regards,
Konke

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-09 12:14       ` Hans Verkuil
@ 2012-08-10  7:16         ` Hans de Goede
  2012-08-10  7:36           ` Hans Verkuil
  0 siblings, 1 reply; 15+ messages in thread
From: Hans de Goede @ 2012-08-10  7:16 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Konke Radlow, linux-media, koradlow

Hi,

On 08/09/2012 02:14 PM, Hans Verkuil wrote:
> On Thu August 9 2012 13:58:07 Hans de Goede wrote:
>> Hi Konke,
>>
>> As Gregor already mentioned there is no need to define libv4l2rdssubdir in configure.ac ,
>> so please drop that.
>>
>> Other then that I've some minor remarks (comments inline), with all those
>> fixed, this one is could to go. So hopefully the next version can be added
>> to git master!
>>
>> On 08/07/2012 05:11 PM, Konke Radlow wrote:
>>> ---
>>>    Makefile.am                     |    3 +-
>>>    configure.ac                    |    7 +-
>>>    lib/include/libv4l2rds.h        |  228 ++++++++++
>>>    lib/libv4l2rds/Makefile.am      |   11 +
>>>    lib/libv4l2rds/libv4l2rds.c     |  953 +++++++++++++++++++++++++++++++++++++++
>>>    lib/libv4l2rds/libv4l2rds.pc.in |   11 +
>>>    6 files changed, 1211 insertions(+), 2 deletions(-)
>>>    create mode 100644 lib/include/libv4l2rds.h
>>>    create mode 100644 lib/libv4l2rds/Makefile.am
>>>    create mode 100644 lib/libv4l2rds/libv4l2rds.c
>>>    create mode 100644 lib/libv4l2rds/libv4l2rds.pc.in
>>>
>>
>> <snip>
>>
>>> diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h
>>> new file mode 100644
>>> index 0000000..4aa8593
>>> --- /dev/null
>>> +++ b/lib/include/libv4l2rds.h
>>> @@ -0,0 +1,228 @@
>>> +/*
>>> + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
>>> + * Author: Konke Radlow <koradlow@gmail.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU Lesser General Public License as published by
>>> + * the Free Software Foundation; either version 2.1 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, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
>>> + */
>>> +
>>> +#ifndef __LIBV4L2RDS
>>> +#define __LIBV4L2RDS
>>> +
>>> +#include <errno.h>
>>> +#include <stdio.h>
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +#include <stdbool.h>
>>> +#include <unistd.h>
>>> +#include <stdint.h>
>>> +#include <time.h>
>>> +#include <sys/types.h>
>>> +#include <sys/mman.h>
>>> +#include <config.h>
>>
>> You should never include config.h in a public header, also
>> are all the headers really needed for the prototypes in this header?
>>
>> I don't think so! Please move all the unneeded ones to the libv4l2rds.c
>> file!
>>
>>> +
>>> +#include <linux/videodev2.h>
>>> +
>>> +#ifdef __cplusplus
>>> +extern "C" {
>>> +#endif /* __cplusplus */
>>> +
>>> +#if HAVE_VISIBILITY
>>> +#define LIBV4L_PUBLIC __attribute__ ((visibility("default")))
>>> +#else
>>> +#define LIBV4L_PUBLIC
>>> +#endif
>>> +
>>> +/* used to define the current version (version field) of the v4l2_rds struct */
>>> +#define V4L2_RDS_VERSION (1)
>>> +
>>
>> What is the purpose of this field? Once we've released a v4l-utils with this
>> library we are stuck to the API we've defined, having a version field & changing it,
>> won't stop us from breaking existing apps, so once we've an official release we
>> simply cannot make ABI breaking changes, which is why most of my review sofar
>> has concentrated on the API side :)
>>
>> I suggest dropping this define and the version field from the struct.
>
> I think it is useful, actually. The v4l2_rds struct is allocated by the v4l2_rds_create
> so at least in theory it is possible to extend the struct in the future without breaking
> existing apps, provided you have a version number to check.

I disagree, if it gets extended only, then existing apps will just work, if an apps gets
compiled against a newer version with the extension then it is safe to assume it will run
against that newer version. The only reason I can see a version define being useful is
to make a newer app compile with an older version of the librarry, but that only requires
a version define, not a version field in the struct.

Regards,

Hans

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-10  7:16         ` Hans de Goede
@ 2012-08-10  7:36           ` Hans Verkuil
  2012-08-10  8:20             ` Hans de Goede
  0 siblings, 1 reply; 15+ messages in thread
From: Hans Verkuil @ 2012-08-10  7:36 UTC (permalink / raw)
  To: Hans de Goede; +Cc: Konke Radlow, linux-media, koradlow

On Fri 10 August 2012 09:16:34 Hans de Goede wrote:
> Hi,
> 
> On 08/09/2012 02:14 PM, Hans Verkuil wrote:
> > On Thu August 9 2012 13:58:07 Hans de Goede wrote:
> >> Hi Konke,
> >>
> >> As Gregor already mentioned there is no need to define libv4l2rdssubdir in configure.ac ,
> >> so please drop that.
> >>
> >> Other then that I've some minor remarks (comments inline), with all those
> >> fixed, this one is could to go. So hopefully the next version can be added
> >> to git master!
> >>
> >> On 08/07/2012 05:11 PM, Konke Radlow wrote:
> >>> ---
> >>>    Makefile.am                     |    3 +-
> >>>    configure.ac                    |    7 +-
> >>>    lib/include/libv4l2rds.h        |  228 ++++++++++
> >>>    lib/libv4l2rds/Makefile.am      |   11 +
> >>>    lib/libv4l2rds/libv4l2rds.c     |  953 +++++++++++++++++++++++++++++++++++++++
> >>>    lib/libv4l2rds/libv4l2rds.pc.in |   11 +
> >>>    6 files changed, 1211 insertions(+), 2 deletions(-)
> >>>    create mode 100644 lib/include/libv4l2rds.h
> >>>    create mode 100644 lib/libv4l2rds/Makefile.am
> >>>    create mode 100644 lib/libv4l2rds/libv4l2rds.c
> >>>    create mode 100644 lib/libv4l2rds/libv4l2rds.pc.in
> >>>
> >>
> >> <snip>
> >>
> >>> diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h
> >>> new file mode 100644
> >>> index 0000000..4aa8593
> >>> --- /dev/null
> >>> +++ b/lib/include/libv4l2rds.h
> >>> @@ -0,0 +1,228 @@
> >>> +/*
> >>> + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> >>> + * Author: Konke Radlow <koradlow@gmail.com>
> >>> + *
> >>> + * This program is free software; you can redistribute it and/or modify
> >>> + * it under the terms of the GNU Lesser General Public License as published by
> >>> + * the Free Software Foundation; either version 2.1 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, write to the Free Software
> >>> + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
> >>> + */
> >>> +
> >>> +#ifndef __LIBV4L2RDS
> >>> +#define __LIBV4L2RDS
> >>> +
> >>> +#include <errno.h>
> >>> +#include <stdio.h>
> >>> +#include <stdlib.h>
> >>> +#include <string.h>
> >>> +#include <stdbool.h>
> >>> +#include <unistd.h>
> >>> +#include <stdint.h>
> >>> +#include <time.h>
> >>> +#include <sys/types.h>
> >>> +#include <sys/mman.h>
> >>> +#include <config.h>
> >>
> >> You should never include config.h in a public header, also
> >> are all the headers really needed for the prototypes in this header?
> >>
> >> I don't think so! Please move all the unneeded ones to the libv4l2rds.c
> >> file!
> >>
> >>> +
> >>> +#include <linux/videodev2.h>
> >>> +
> >>> +#ifdef __cplusplus
> >>> +extern "C" {
> >>> +#endif /* __cplusplus */
> >>> +
> >>> +#if HAVE_VISIBILITY
> >>> +#define LIBV4L_PUBLIC __attribute__ ((visibility("default")))
> >>> +#else
> >>> +#define LIBV4L_PUBLIC
> >>> +#endif
> >>> +
> >>> +/* used to define the current version (version field) of the v4l2_rds struct */
> >>> +#define V4L2_RDS_VERSION (1)
> >>> +
> >>
> >> What is the purpose of this field? Once we've released a v4l-utils with this
> >> library we are stuck to the API we've defined, having a version field & changing it,
> >> won't stop us from breaking existing apps, so once we've an official release we
> >> simply cannot make ABI breaking changes, which is why most of my review sofar
> >> has concentrated on the API side :)
> >>
> >> I suggest dropping this define and the version field from the struct.
> >
> > I think it is useful, actually. The v4l2_rds struct is allocated by the v4l2_rds_create
> > so at least in theory it is possible to extend the struct in the future without breaking
> > existing apps, provided you have a version number to check.
> 
> I disagree, if it gets extended only, then existing apps will just work, if an apps gets
> compiled against a newer version with the extension then it is safe to assume it will run
> against that newer version. The only reason I can see a version define being useful is
> to make a newer app compile with an older version of the librarry, but that only requires
> a version define, not a version field in the struct.

That's true, you only need the define, not the version field.

So let's keep the define and ditch the version field. I think that
should do it.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC)
  2012-08-10  7:36           ` Hans Verkuil
@ 2012-08-10  8:20             ` Hans de Goede
  0 siblings, 0 replies; 15+ messages in thread
From: Hans de Goede @ 2012-08-10  8:20 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Konke Radlow, linux-media, koradlow

Hi,

On 08/10/2012 09:36 AM, Hans Verkuil wrote:
> On Fri 10 August 2012 09:16:34 Hans de Goede wrote:
>> Hi,
>>
>> On 08/09/2012 02:14 PM, Hans Verkuil wrote:
>>> On Thu August 9 2012 13:58:07 Hans de Goede wrote:
>>>> Hi Konke,
>>>>
>>>> As Gregor already mentioned there is no need to define libv4l2rdssubdir in configure.ac ,
>>>> so please drop that.
>>>>
>>>> Other then that I've some minor remarks (comments inline), with all those
>>>> fixed, this one is could to go. So hopefully the next version can be added
>>>> to git master!
>>>>
>>>> On 08/07/2012 05:11 PM, Konke Radlow wrote:
>>>>> ---
>>>>>     Makefile.am                     |    3 +-
>>>>>     configure.ac                    |    7 +-
>>>>>     lib/include/libv4l2rds.h        |  228 ++++++++++
>>>>>     lib/libv4l2rds/Makefile.am      |   11 +
>>>>>     lib/libv4l2rds/libv4l2rds.c     |  953 +++++++++++++++++++++++++++++++++++++++
>>>>>     lib/libv4l2rds/libv4l2rds.pc.in |   11 +
>>>>>     6 files changed, 1211 insertions(+), 2 deletions(-)
>>>>>     create mode 100644 lib/include/libv4l2rds.h
>>>>>     create mode 100644 lib/libv4l2rds/Makefile.am
>>>>>     create mode 100644 lib/libv4l2rds/libv4l2rds.c
>>>>>     create mode 100644 lib/libv4l2rds/libv4l2rds.pc.in
>>>>>
>>>>
>>>> <snip>
>>>>
>>>>> diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h
>>>>> new file mode 100644
>>>>> index 0000000..4aa8593
>>>>> --- /dev/null
>>>>> +++ b/lib/include/libv4l2rds.h
>>>>> @@ -0,0 +1,228 @@
>>>>> +/*
>>>>> + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
>>>>> + * Author: Konke Radlow <koradlow@gmail.com>
>>>>> + *
>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>> + * it under the terms of the GNU Lesser General Public License as published by
>>>>> + * the Free Software Foundation; either version 2.1 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, write to the Free Software
>>>>> + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
>>>>> + */
>>>>> +
>>>>> +#ifndef __LIBV4L2RDS
>>>>> +#define __LIBV4L2RDS
>>>>> +
>>>>> +#include <errno.h>
>>>>> +#include <stdio.h>
>>>>> +#include <stdlib.h>
>>>>> +#include <string.h>
>>>>> +#include <stdbool.h>
>>>>> +#include <unistd.h>
>>>>> +#include <stdint.h>
>>>>> +#include <time.h>
>>>>> +#include <sys/types.h>
>>>>> +#include <sys/mman.h>
>>>>> +#include <config.h>
>>>>
>>>> You should never include config.h in a public header, also
>>>> are all the headers really needed for the prototypes in this header?
>>>>
>>>> I don't think so! Please move all the unneeded ones to the libv4l2rds.c
>>>> file!
>>>>
>>>>> +
>>>>> +#include <linux/videodev2.h>
>>>>> +
>>>>> +#ifdef __cplusplus
>>>>> +extern "C" {
>>>>> +#endif /* __cplusplus */
>>>>> +
>>>>> +#if HAVE_VISIBILITY
>>>>> +#define LIBV4L_PUBLIC __attribute__ ((visibility("default")))
>>>>> +#else
>>>>> +#define LIBV4L_PUBLIC
>>>>> +#endif
>>>>> +
>>>>> +/* used to define the current version (version field) of the v4l2_rds struct */
>>>>> +#define V4L2_RDS_VERSION (1)
>>>>> +
>>>>
>>>> What is the purpose of this field? Once we've released a v4l-utils with this
>>>> library we are stuck to the API we've defined, having a version field & changing it,
>>>> won't stop us from breaking existing apps, so once we've an official release we
>>>> simply cannot make ABI breaking changes, which is why most of my review sofar
>>>> has concentrated on the API side :)
>>>>
>>>> I suggest dropping this define and the version field from the struct.
>>>
>>> I think it is useful, actually. The v4l2_rds struct is allocated by the v4l2_rds_create
>>> so at least in theory it is possible to extend the struct in the future without breaking
>>> existing apps, provided you have a version number to check.
>>
>> I disagree, if it gets extended only, then existing apps will just work, if an apps gets
>> compiled against a newer version with the extension then it is safe to assume it will run
>> against that newer version. The only reason I can see a version define being useful is
>> to make a newer app compile with an older version of the librarry, but that only requires
>> a version define, not a version field in the struct.
>
> That's true, you only need the define, not the version field.
>
> So let's keep the define and ditch the version field. I think that
> should do it.

Ack.

Regards,

Hans

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2012-08-10  8:19 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <[RFC PATCH 0/2] Add support for RDS decoding>
2012-08-07 15:11 ` [RFC PATCH 0/2] Add support for RDS decoding (updated) Konke Radlow
2012-08-07 15:11   ` [RFC PATCH 1/2] Add libv4l2rds library (with changes proposed in RFC) Konke Radlow
2012-08-07 15:11     ` [RFC PATCH 2/2] Add rds-ctl tool " Konke Radlow
2012-08-09 12:05       ` Hans de Goede
2012-08-09 14:34         ` Konke Radlow
2012-08-09  7:22     ` [RFC PATCH 1/2] Add libv4l2rds library " Gregor Jasny
2012-08-09 14:12       ` Konke Radlow
2012-08-09 11:58     ` Hans de Goede
2012-08-09 12:14       ` Hans Verkuil
2012-08-10  7:16         ` Hans de Goede
2012-08-10  7:36           ` Hans Verkuil
2012-08-10  8:20             ` Hans de Goede
2012-08-09 14:30       ` Konke Radlow
2012-08-09 12:17     ` Hans Verkuil
2012-08-08 12:03   ` [RFC PATCH 0/2] Add support for RDS decoding (updated) Konke Radlow

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.