* Re: [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> @ 2012-07-26 16:21 Konke Radlow 2012-07-28 11:08 ` Hans de Goede 0 siblings, 1 reply; 10+ messages in thread From: Konke Radlow @ 2012-07-26 16:21 UTC (permalink / raw) To: linux-media PATCH 1/2 was missing the public header for the rds library. I'm sorry for that mistake: diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h new file mode 100644 index 0000000..04843d3 --- /dev/null +++ b/lib/include/libv4l2rds.h @@ -0,0 +1,203 @@ +/* + * 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 <sys/types.h> +#include <sys/mman.h> + +#include <linux/videodev2.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if __GNUC__ >= 4 +#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_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 code */ +#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 state_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 */ +struct v4l2_rds_group { + uint16_t pi; + char group_version; + bool traffic_prog; + uint8_t group_id; + uint8_t pty; + 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; + uint32_t group_cnt; + uint32_t block_error_cnt; + uint32_t group_error_cnt; + uint32_t block_corrected_cnt; + uint32_t group_type_cnt[16]; +}; + +/* struct to encapsulate the definition of one ODA (Open Data Application) type */ +struct v4l2_rds_oda { + uint8_t group_id; + char group_version; + uint16_t aid; /* Application Identification */ +}; + +/* struct to encapsulate an array of all defined ODA types for a channel */ +struct v4l2_rds_oda_set { + uint8_t size; + struct v4l2_rds_oda oda[MAX_ODA_CNT]; +}; + +/* struct to encapsulate an array of all defined Alternative Frequencies for a channel */ +struct v4l2_rds_af_set { + uint8_t 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 */ +struct v4l2_rds { + uint32_t version; + + /** state information **/ + uint32_t decode_information; /* defines the state of the decoding process */ + uint32_t valid_fields; /* defines the RDS info fields that are valid atm */ + + /** RDS info fields **/ + bool is_rbds; /* use RBDS standard version of LUTs */ + uint16_t pi; + uint8_t ps[8]; + uint8_t pty; + uint8_t ptyn[8]; + bool ptyn_ab_flag; /* PTYN A/B flag (toggled), to signal change of program type */ + uint8_t rt_pos; + uint8_t rt_length; + uint8_t rt[64]; + bool rt_ab_flag; /* RT A/B flag (toggled), to signal transmission of new RT */ + bool ta; + bool tp; + bool ms; + uint8_t di; + uint8_t ecc; + uint8_t lc; + uint32_t julian_date; + uint8_t utc_hour; + uint8_t utc_minute; + uint8_t utc_offset; + + struct v4l2_rds_statistics rds_statistics; + struct v4l2_rds_oda_set rds_oda; + struct v4l2_rds_af_set rds_af; +}; + +/* v4l2_rds_init() - initializes a new decoding process + * @rds: defines which standard is used: true=RDS, false=RBDS + * + * 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_rds); + +/* frees all memory allocated for the 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 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> 2012-07-26 16:21 [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> Konke Radlow @ 2012-07-28 11:08 ` Hans de Goede 2012-07-30 14:41 ` Konke Radlow 2012-07-30 14:46 ` Konke Radlow 0 siblings, 2 replies; 10+ messages in thread From: Hans de Goede @ 2012-07-28 11:08 UTC (permalink / raw) To: Konke Radlow; +Cc: linux-media Hi, First of all many thanks for working on this! Note I've also taken a quick look at the original patch with the actual implementation and that looks good. I'm replying here because in my mind the API is the most interesting thing to discuss. Comments inline. On 07/26/2012 06:21 PM, Konke Radlow wrote: > PATCH 1/2 was missing the public header for the rds library. I'm sorry for > that mistake: > > diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h > new file mode 100644 > index 0000000..04843d3 > --- /dev/null > +++ b/lib/include/libv4l2rds.h > @@ -0,0 +1,203 @@ > +/* > + * 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 <sys/types.h> > +#include <sys/mman.h> > + > +#include <linux/videodev2.h> > + > +#ifdef __cplusplus > +extern "C" { > +#endif /* __cplusplus */ > + > +#if __GNUC__ >= 4 > +#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_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 code */ > +#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 state_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 */ > +struct v4l2_rds_group { > + uint16_t pi; > + char group_version; > + bool traffic_prog; > + uint8_t group_id; > + uint8_t pty; > + 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; > + uint32_t group_cnt; > + uint32_t block_error_cnt; > + uint32_t group_error_cnt; > + uint32_t block_corrected_cnt; > + uint32_t group_type_cnt[16]; > +}; > + > +/* struct to encapsulate the definition of one ODA (Open Data Application) > type */ > +struct v4l2_rds_oda { > + uint8_t group_id; > + char group_version; > + uint16_t aid; /* Application Identification */ > +}; > + > +/* struct to encapsulate an array of all defined ODA types for a channel */ > +struct v4l2_rds_oda_set { > + uint8_t size; > + struct v4l2_rds_oda oda[MAX_ODA_CNT]; > +}; > + > +/* struct to encapsulate an array of all defined Alternative Frequencies for a > channel */ > +struct v4l2_rds_af_set { > + uint8_t 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 */ Most fields in this struct (and in the other structs for that matter) could do with some more documentation. > +struct v4l2_rds { > + uint32_t version; > + > + /** state information **/ > + uint32_t decode_information; /* defines the state of the decoding process > */ > + uint32_t valid_fields; /* defines the RDS info fields that are valid atm > */ > + > + /** RDS info fields **/ > + bool is_rbds; /* use RBDS standard version of LUTs */ > + uint16_t pi; > + uint8_t ps[8]; Looking at rds-ctl, this contains a string, please make it 9 bytes and always 0 terminate it! I also notice in rds-ctl that you filter the chars for being valid ascii and if not replace them with a space. Does the spec say anything about the encoding used for this string? Could we maybe convert it to UTF-8 inside the library so that apps can just consume the string? > + uint8_t pty; > + uint8_t ptyn[8]; Same remark as for ps. > + bool ptyn_ab_flag; /* PTYN A/B flag (toggled), to signal change of program > type */ > + uint8_t rt_pos; > + uint8_t rt_length; > + uint8_t rt[64]; Same remark as for ps. > + bool rt_ab_flag; /* RT A/B flag (toggled), to signal transmission of new > RT */ > + bool ta; > + bool tp; > + bool ms; > + uint8_t di; > + uint8_t ecc; > + uint8_t lc; > + uint32_t julian_date; > + uint8_t utc_hour; > + uint8_t utc_minute; > + uint8_t utc_offset; > + > + struct v4l2_rds_statistics rds_statistics; > + struct v4l2_rds_oda_set rds_oda; > + struct v4l2_rds_af_set rds_af; > +}; > + > +/* v4l2_rds_init() - initializes a new decoding process > + * @rds: defines which standard is used: true=RDS, false=RBDS > + * > + * 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_rds); > + > +/* frees all memory allocated for the 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); Unless I'm missing something, you are no defining struct v4l2_rds_data anywhere, why not just make this a uint8_t ? > + > +/* > + * 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 Regards, Hans ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> 2012-07-28 11:08 ` Hans de Goede @ 2012-07-30 14:41 ` Konke Radlow 2012-07-30 14:46 ` Konke Radlow 1 sibling, 0 replies; 10+ messages in thread From: Konke Radlow @ 2012-07-30 14:41 UTC (permalink / raw) To: Hans de Goede; +Cc: linux-media Hello Hans, no need to thank me for working on it. First of all I do it as a part of a summerjob, and more importantly I quite enjoy it and intend to stay a active member of the development process after my time at Cisco is over. Thank you for your comments. > Most fields in this struct (and in the other structs for that matter) could > do with some more documentation. I added a lot of short comments explaining the meaning of the fields, and extended the explanation part that comes before each struct definition. > > + /** RDS info fields **/ > > + bool is_rbds; /* use RBDS standard version of LUTs */ > > + uint16_t pi; > > + uint8_t ps[8]; > > Looking at rds-ctl, this contains a string, please make it 9 bytes and > always 0 terminate it! I also notice in rds-ctl that you filter the chars > for being valid ascii and if not replace them with a space. Does the spec > say anything about the encoding used for this string? Could we maybe > convert it to UTF-8 inside the library so that apps can just consume the > string? The character encoding complies with ISO/IEC 10646, so it basically already is UTF-8, and the data could be stored in a wchar_t array without conversion. Is that preferred over uint8_t? > > +/* 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); > > Unless I'm missing something, you are no defining struct v4l2_rds_data > anywhere, why not just make this a uint8_t ? The v4l2_rds_data structure is defined by v4l in the videodev2.h header, and is returned when calling the read function on a rds capable device On Saturday, July 28, 2012 11:08:24 AM Hans de Goede wrote: > Hi, > > First of all many thanks for working on this! Note I've also taken > a quick look at the original patch with the actual implementation and that > looks good. I'm replying here because in my mind the API is the most > interesting thing to discuss. > > Comments inline. > > On 07/26/2012 06:21 PM, Konke Radlow wrote: > > PATCH 1/2 was missing the public header for the rds library. I'm sorry > > for that mistake: > > > > diff --git a/lib/include/libv4l2rds.h b/lib/include/libv4l2rds.h > > new file mode 100644 > > index 0000000..04843d3 > > --- /dev/null > > +++ b/lib/include/libv4l2rds.h > > @@ -0,0 +1,203 @@ > > +/* > > + * 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 <sys/types.h> > > +#include <sys/mman.h> > > + > > +#include <linux/videodev2.h> > > + > > +#ifdef __cplusplus > > +extern "C" { > > +#endif /* __cplusplus */ > > + > > +#if __GNUC__ >= 4 > > +#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_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 code */ > > +#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 state_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 */ > > +struct v4l2_rds_group { > > + uint16_t pi; > > + char group_version; > > + bool traffic_prog; > > + uint8_t group_id; > > + uint8_t pty; > > + 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; > > + uint32_t group_cnt; > > + uint32_t block_error_cnt; > > + uint32_t group_error_cnt; > > + uint32_t block_corrected_cnt; > > + uint32_t group_type_cnt[16]; > > +}; > > + > > +/* struct to encapsulate the definition of one ODA (Open Data > > Application) type */ > > +struct v4l2_rds_oda { > > + uint8_t group_id; > > + char group_version; > > + uint16_t aid; /* Application Identification */ > > +}; > > + > > +/* struct to encapsulate an array of all defined ODA types for a channel > > */ +struct v4l2_rds_oda_set { > > + uint8_t size; > > + struct v4l2_rds_oda oda[MAX_ODA_CNT]; > > +}; > > + > > +/* struct to encapsulate an array of all defined Alternative Frequencies > > for a channel */ > > +struct v4l2_rds_af_set { > > + uint8_t 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 */ > > Most fields in this struct (and in the other structs for that matter) could > do with some more documentation. > > > +struct v4l2_rds { > > + uint32_t version; > > + > > + /** state information **/ > > + uint32_t decode_information; /* defines the state of the decoding > > process */ > > + uint32_t valid_fields; /* defines the RDS info fields that are valid > > atm */ > > + > > + /** RDS info fields **/ > > + bool is_rbds; /* use RBDS standard version of LUTs */ > > + uint16_t pi; > > + uint8_t ps[8]; > > Looking at rds-ctl, this contains a string, please make it 9 bytes and > always 0 terminate it! I also notice in rds-ctl that you filter the chars > for being valid ascii and if not replace them with a space. Does the spec > say anything about the encoding used for this string? Could we maybe > convert it to UTF-8 inside the library so that apps can just consume the > string? > > > + uint8_t pty; > > + uint8_t ptyn[8]; > > Same remark as for ps. > > > + bool ptyn_ab_flag; /* PTYN A/B flag (toggled), to signal change of > > program type */ > > + uint8_t rt_pos; > > + uint8_t rt_length; > > + uint8_t rt[64]; > > Same remark as for ps. > > > + bool rt_ab_flag; /* RT A/B flag (toggled), to signal transmission of > > new RT */ > > + bool ta; > > + bool tp; > > + bool ms; > > + uint8_t di; > > + uint8_t ecc; > > + uint8_t lc; > > + uint32_t julian_date; > > + uint8_t utc_hour; > > + uint8_t utc_minute; > > + uint8_t utc_offset; > > + > > + struct v4l2_rds_statistics rds_statistics; > > + struct v4l2_rds_oda_set rds_oda; > > + struct v4l2_rds_af_set rds_af; > > +}; > > + > > +/* v4l2_rds_init() - initializes a new decoding process > > + * @rds: defines which standard is used: true=RDS, false=RBDS > > + * > > + * 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_rds); > > + > > +/* frees all memory allocated for the 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); > > Unless I'm missing something, you are no defining struct v4l2_rds_data > anywhere, why not just make this a uint8_t ? > > > + > > +/* > > + * 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 > > Regards, > > Hans ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> 2012-07-28 11:08 ` Hans de Goede 2012-07-30 14:41 ` Konke Radlow @ 2012-07-30 14:46 ` Konke Radlow 1 sibling, 0 replies; 10+ messages in thread From: Konke Radlow @ 2012-07-30 14:46 UTC (permalink / raw) To: Hans de Goede; +Cc: linux-media Hello Hans, no need to thank me for working on it. First of all I do it as a part of a summerjob, and more importantly I quite enjoy it and intend to stay a active member of the development process after my time at Cisco is over. Thank you for your comments. > Most fields in this struct (and in the other structs for that matter) could > do with some more documentation. I added a lot of short comments explaining the meaning of the fields, and extended the explanation part that comes before each struct definition. > > + /** RDS info fields **/ > > + bool is_rbds; /* use RBDS standard version of LUTs */ > > + uint16_t pi; > > + uint8_t ps[8]; > > Looking at rds-ctl, this contains a string, please make it 9 bytes and > always 0 terminate it! I also notice in rds-ctl that you filter the chars > for being valid ascii and if not replace them with a space. Does the spec > say anything about the encoding used for this string? Could we maybe > convert it to UTF-8 inside the library so that apps can just consume the > string? The character encoding complies with ISO/IEC 10646, so it basically already is UTF-8, and the data could be stored in a wchar_t array without conversion. Is that preferred over uint8_t? > > +/* 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); > > Unless I'm missing something, you are no defining struct v4l2_rds_data > anywhere, why not just make this a uint8_t ? The v4l2_rds_data structure is defined by v4l in the videodev2.h header, and is returned when calling the read function on a rds capable device source: http://hverkuil.home.xs4all.nl/spec/media.html#v4l2-rds-data Maybe I didn't get you point though? greetings, Konke ^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC PATCH 0/2] Add support for RDS decoding @ 2012-07-25 17:43 Konke Radlow 2012-07-25 17:44 ` [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> Konke Radlow 0 siblings, 1 reply; 10+ messages in thread From: Konke Radlow @ 2012-07-25 17:43 UTC (permalink / raw) To: linux-media; +Cc: hverkuil, hdegoede Hello, Over the last couple of weeks I have been working on a library that adds RDS decoding support to the v4l-utils repository. It currently supports the core RDS standard but no advanced features yet like ODA (TMC). I also wrote a control application that can be used to test the library with any RDS-capable v4l device. This application is based on the v4l2-ctl tool and many of the basic options have been taken over to ease the usage. By default the tool will print all RDS fields that it was able to decode to the std output. The latest version of the code can always be found in my github repository: https://github.com/koradlow/v4l2-rds-ctl You can also find a tool that was created to generate test RDS data with the pcimax-3000+ card under Linux there. This program is functional, but still a early version, and the functionality will be improved in the future: https://github.com/koradlow/pcimax-ctl Upcoming: TMC support (Traffic Message Channel) This work is being done as part of a summerjob I`m doing at Cisco Systems Norway with Hans Verkuil as my mentor. Comments and remarks are very welcome. Regards, Konke ^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> 2012-07-25 17:43 [RFC PATCH 0/2] Add support for RDS decoding Konke Radlow @ 2012-07-25 17:44 ` Konke Radlow 2012-07-26 14:28 ` Ezequiel Garcia ` (2 more replies) 0 siblings, 3 replies; 10+ messages in thread From: Konke Radlow @ 2012-07-25 17:44 UTC (permalink / raw) To: linux-media; +Cc: hverkuil, hdegoede --- Makefile.am | 3 +- configure.ac | 10 +- lib/libv4l2rds/Makefile.am | 11 + lib/libv4l2rds/libv4l2rds.c | 871 +++++++++++++++++++++++++++++++++++++++ lib/libv4l2rds/libv4l2rds.pc.in | 11 + 5 files changed, 904 insertions(+), 2 deletions(-) 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 b7b356c..6707f5f 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..1d7eb29 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,13 +148,17 @@ 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" libv4lconvertprivdir="$libdir/$libv4lconvertsubdir" +libv4l2rdsprivdir="$libdir/$libv4l2rdssubdir" keytablesystemdir="$udevdir/rc_keymaps" keytableuserdir="$sysconfdir/rc_keymaps" @@ -166,6 +172,7 @@ AC_SUBST(libv4lconvertprivdir) AC_SUBST(keytablesystemdir) AC_SUBST(keytableuserdir) AC_SUBST(udevrulesdir) +AC_SUBST(libv4l2rdsprivdir) AC_SUBST(pkgconfigdir) AC_DEFINE_UNQUOTED([V4L_UTILS_VERSION], ["$PACKAGE_VERSION"], [v4l-utils version string]) @@ -173,6 +180,7 @@ AC_DEFINE_DIR([LIBV4L1_PRIV_DIR], [libv4l1privdir], [libv4l1 private lib directo AC_DEFINE_DIR([LIBV4L2_PRIV_DIR], [libv4l2privdir], [libv4l2 private lib directory]) AC_DEFINE_DIR([LIBV4L2_PLUGIN_DIR], [libv4l2plugindir], [libv4l2 plugin directory]) AC_DEFINE_DIR([LIBV4LCONVERT_PRIV_DIR], [libv4lconvertprivdir], [libv4lconvert private lib directory]) +AC_DEFINE_DIR([LIBV4L2RDS_PRIV_DIR], [libv4l2rdsprivdir], [libv4l2rds private lib directory]) AC_DEFINE_DIR([IR_KEYTABLE_SYSTEM_DIR], [keytablesystemdir], [ir-keytable preinstalled tables directory]) AC_DEFINE_DIR([IR_KEYTABLE_USER_DIR], [keytableuserdir], [ir-keytable user defined tables directory]) diff --git a/lib/libv4l2rds/Makefile.am b/lib/libv4l2rds/Makefile.am new file mode 100644 index 0000000..5796890 --- /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 $(DLOPEN_LIBS) $(ENFORCE_LIBV4L_STATIC) diff --git a/lib/libv4l2rds/libv4l2rds.c b/lib/libv4l2rds/libv4l2rds.c new file mode 100644 index 0000000..0bacaa2 --- /dev/null +++ b/lib/libv4l2rds/libv4l2rds.c @@ -0,0 +1,871 @@ +/* + * 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 */ +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; + + uint8_t decode_state; + 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 new_rt_pos; + uint8_t new_ecc; + uint8_t new_lc; + uint8_t new_di; + uint8_t new_di_pos; + 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 = 0x00; + 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; + uint32_t updated_fields = 0x00; + + /* 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 */ + grp->traffic_prog = rds_data->msb & 0x04; + if (handle->tp != grp->traffic_prog) { + handle->tp = grp->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 */ + grp->pty = (rds_data->msb << 3) | (rds_data->lsb >> 5); + grp->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 == grp->pty) { + priv_state->new_pty = grp->pty; + return updated_fields; + } + + if (priv_state->new_pty == grp->pty) { + handle->pty = priv_state->new_pty; + updated_fields |= V4L2_RDS_PTY; + handle->valid_fields |= V4L2_RDS_PTY; + } else { + priv_state->new_pty = grp->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; +} + +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; + + /* LF / MF frequency follows */ + if (c_msb == 250) { + updated_af = (rds_add_af_to_list(af_set, c_lsb, false)) ? + true : updated_af; + c_lsb = 0; /* invalidate */ + } + /* announcement of AF count */ + if (c_msb >= 225 && c_msb <= 249) + af_set->announced_af = c_msb - 224; + /* check if the data represents an AF */ + if (c_msb < 205) + updated_af = (rds_add_af_to_list(af_set, c_msb, true)) ? + true : updated_af; + if (c_lsb < 205) + updated_af = (rds_add_af_to_list(af_set, c_lsb, true)) ? + true : updated_af; + /* 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 = 0x00; + + /* 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->new_di_pos + 1) { + switch (segment) { + case 0: + priv_state->new_di = set_bit(priv_state->new_di, + V4L2_RDS_FLAG_STEREO, bit2); + break; + case 1: + priv_state->new_di = set_bit(priv_state->new_di, + V4L2_RDS_FLAG_ARTIFICIAL_HEAD, bit2); + priv_state->new_di_pos = segment; + break; + case 2: + priv_state->new_di = set_bit(priv_state->new_di, + V4L2_RDS_FLAG_COMPRESSED, bit2); + priv_state->new_di_pos = segment; + break; + case 3: + priv_state->new_di = set_bit(priv_state->new_di, + V4L2_RDS_FLAG_STATIC_PTY, bit2); + if (handle->di != priv_state->new_di) + updated_fields |= V4L2_RDS_DI; + handle->di = priv_state->new_di; + handle->valid_fields |= V4L2_RDS_DI; + break; + } + } else { + /* wrong order of DI segments -> restart */ + priv_state->new_di_pos = 0; + priv_state->new_di = 0x00; + } + + /* 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 = 0x00; + uint8_t variant_code = 0x00; + + /* 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 0x00; + + /* bit 14-12 of block c contain the variant code */ + variant_code = (grp->data_c_msb >> 4) & 0x07; + if (variant_code == 0x00) { + /* var 0x00 -> 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 = 0x00; + + /* 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, 0x00, 64); + handle->valid_fields &= ~V4L2_RDS_RT; + updated_fields |= V4L2_RDS_RT; + } + + /* 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->new_rt_pos + 1) { + 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->new_rt_pos = segment; + 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; + } + } + } + } else { + if (segment == 0 || segment == priv_state->new_rt_pos + 1) { + 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->new_rt_pos = segment; + 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; + } + } + } + } + + /* 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) { + handle->rt_length = i + 1; + 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; + } + } + } + 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 = 0x00; + + if (grp->group_version != 'A') + return 0x00; + + /* 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; +} + +/* 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 julian_date; + uint8_t utc_hour; + uint8_t utc_minute; + uint8_t utc_offset; + uint32_t updated_fields = 0x00; + + if (grp->group_version != 'A') + return 0x00; + + /* bits 0-1 of block b lsb contain bits 15 and 16 of Julian date + * bits 0-7 of block c msb contain bits 7 to 14 of Julian date + * bits 1-7 of block c lsb contain bits 0 to 6 of Julian date */ + julian_date = ((grp->data_b_lsb & 0x03) << 15) | + (grp->data_c_msb << 7) | (grp->data_c_lsb >> 1); + if (handle->julian_date != julian_date) { + handle->julian_date = julian_date; + updated_fields |= V4L2_RDS_TIME; + } + + /* 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 */ + utc_hour = ((grp->data_c_lsb & 0x01) << 4) | (grp->data_d_msb >> 4); + if (handle->utc_hour != utc_hour) { + handle->utc_hour = utc_hour; + updated_fields |= V4L2_RDS_TIME; + } + + /* 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 */ + utc_minute = ((grp->data_d_msb & 0x0f) << 2) | (grp->data_d_lsb >> 6); + if (handle->utc_minute != utc_minute) { + handle->utc_minute = utc_minute; + updated_fields |= V4L2_RDS_TIME; + } + + /* bits 0-5 of block d lsb contain bits 0 to 5 of local time offset */ + utc_offset = grp->data_d_lsb & 0x3f; + if (handle->utc_offset != utc_offset) { + handle->utc_offset = utc_offset; + updated_fields |= V4L2_RDS_TIME; + } + + handle->valid_fields |= V4L2_RDS_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 = 0x00; + 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 0x00; + + /* 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 0x00; +} + +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 = 0x00; + 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 0x00; +} + +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 defines 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" + }, { + " ", "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; + 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", + " ", " ", " ", " ", " ", " ", " ", " ", + " ", " ", " ", " ", " ", " ", " ", " ", + " ", " ", " ", " ", " ", " ", " ", " ", + " ", "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", "Azerbijani", + "Assamese", "Armenian", "Arabic", "Amharic" + }; + return (lc > max_lc) ? "Unknown" : language_lut[lc]; +} + +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] 10+ messages in thread
* Re: [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> 2012-07-25 17:44 ` [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> Konke Radlow @ 2012-07-26 14:28 ` Ezequiel Garcia 2012-07-26 14:39 ` Hans Verkuil 2012-07-26 18:46 ` Gregor Jasny 2012-07-26 18:49 ` Gregor Jasny 2 siblings, 1 reply; 10+ messages in thread From: Ezequiel Garcia @ 2012-07-26 14:28 UTC (permalink / raw) To: Konke Radlow; +Cc: linux-media, hverkuil, hdegoede Hi Konke, > + > +libv4l2rds_la_SOURCES = libv4l2rds.c > +libv4l2rds_la_CPPFLAGS = -fvisibility=hidden $(ENFORCE_LIBV4L_STATIC) -std=c99 > +libv4l2rds_la_LDFLAGS = -version-info 0 -lpthread $(DLOPEN_LIBS) $(ENFORCE_LIBV4L_STATIC) > diff --git a/lib/libv4l2rds/libv4l2rds.c b/lib/libv4l2rds/libv4l2rds.c > new file mode 100644 > index 0000000..0bacaa2 > --- /dev/null > +++ b/lib/libv4l2rds/libv4l2rds.c > @@ -0,0 +1,871 @@ > +/* > + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. [snip] > + * This program is free software; you can redistribute it and/or modify Just a -probably silly- question... How can it be "free software" yet claim "All rights reserved" ? Is this correct? Regards, Ezequiel. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> 2012-07-26 14:28 ` Ezequiel Garcia @ 2012-07-26 14:39 ` Hans Verkuil 2012-07-26 14:41 ` Ezequiel Garcia 0 siblings, 1 reply; 10+ messages in thread From: Hans Verkuil @ 2012-07-26 14:39 UTC (permalink / raw) To: Ezequiel Garcia; +Cc: Konke Radlow, linux-media, hdegoede On Thu 26 July 2012 16:28:20 Ezequiel Garcia wrote: > Hi Konke, > > > + > > +libv4l2rds_la_SOURCES = libv4l2rds.c > > +libv4l2rds_la_CPPFLAGS = -fvisibility=hidden $(ENFORCE_LIBV4L_STATIC) -std=c99 > > +libv4l2rds_la_LDFLAGS = -version-info 0 -lpthread $(DLOPEN_LIBS) $(ENFORCE_LIBV4L_STATIC) > > diff --git a/lib/libv4l2rds/libv4l2rds.c b/lib/libv4l2rds/libv4l2rds.c > > new file mode 100644 > > index 0000000..0bacaa2 > > --- /dev/null > > +++ b/lib/libv4l2rds/libv4l2rds.c > > @@ -0,0 +1,871 @@ > > +/* > > + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. > [snip] > > + * This program is free software; you can redistribute it and/or modify > > Just a -probably silly- question... > > How can it be "free software" yet claim "All rights reserved" ? Is this correct? Yeah, it's correct. I had the same question when I was told that this was the correct phrase to use. Check other copyright lines in the kernel and you'll see the same line. Since it is covered by the LGPLv2 license there aren't many rights to reserve :-) The only right there is in practice is the right to decide whether or not to allow other licenses as well. Regards, Hans ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> 2012-07-26 14:39 ` Hans Verkuil @ 2012-07-26 14:41 ` Ezequiel Garcia 0 siblings, 0 replies; 10+ messages in thread From: Ezequiel Garcia @ 2012-07-26 14:41 UTC (permalink / raw) To: Hans Verkuil; +Cc: Konke Radlow, linux-media, hdegoede On Thu, Jul 26, 2012 at 11:39 AM, Hans Verkuil <hverkuil@xs4all.nl> wrote: > On Thu 26 July 2012 16:28:20 Ezequiel Garcia wrote: >> Hi Konke, >> >> > + >> > +libv4l2rds_la_SOURCES = libv4l2rds.c >> > +libv4l2rds_la_CPPFLAGS = -fvisibility=hidden $(ENFORCE_LIBV4L_STATIC) -std=c99 >> > +libv4l2rds_la_LDFLAGS = -version-info 0 -lpthread $(DLOPEN_LIBS) $(ENFORCE_LIBV4L_STATIC) >> > diff --git a/lib/libv4l2rds/libv4l2rds.c b/lib/libv4l2rds/libv4l2rds.c >> > new file mode 100644 >> > index 0000000..0bacaa2 >> > --- /dev/null >> > +++ b/lib/libv4l2rds/libv4l2rds.c >> > @@ -0,0 +1,871 @@ >> > +/* >> > + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. >> [snip] >> > + * This program is free software; you can redistribute it and/or modify >> >> Just a -probably silly- question... >> >> How can it be "free software" yet claim "All rights reserved" ? Is this correct? > > Yeah, it's correct. I had the same question when I was told that this was the > correct phrase to use. Check other copyright lines in the kernel and you'll see > the same line. > > Since it is covered by the LGPLv2 license there aren't many rights to reserve :-) > > The only right there is in practice is the right to decide whether or not to > allow other licenses as well. > For some reason, I must read this several times before really catching it. Thanks for the explanation, Ezequiel. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> 2012-07-25 17:44 ` [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> Konke Radlow 2012-07-26 14:28 ` Ezequiel Garcia @ 2012-07-26 18:46 ` Gregor Jasny 2012-07-26 18:49 ` Gregor Jasny 2 siblings, 0 replies; 10+ messages in thread From: Gregor Jasny @ 2012-07-26 18:46 UTC (permalink / raw) To: Konke Radlow; +Cc: linux-media, hverkuil, hdegoede On 7/25/12 7:44 PM, Konke Radlow wrote: > diff --git a/configure.ac b/configure.ac > index 8ddcc9d..1d7eb29 100644 > --- a/configure.ac > +++ b/configure.ac ... > @@ -146,13 +148,17 @@ 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" > libv4lconvertprivdir="$libdir/$libv4lconvertsubdir" > +libv4l2rdsprivdir="$libdir/$libv4l2rdssubdir" > > keytablesystemdir="$udevdir/rc_keymaps" > keytableuserdir="$sysconfdir/rc_keymaps" > @@ -166,6 +172,7 @@ AC_SUBST(libv4lconvertprivdir) > AC_SUBST(keytablesystemdir) > AC_SUBST(keytableuserdir) > AC_SUBST(udevrulesdir) > +AC_SUBST(libv4l2rdsprivdir) > AC_SUBST(pkgconfigdir) > > AC_DEFINE_UNQUOTED([V4L_UTILS_VERSION], ["$PACKAGE_VERSION"], [v4l-utils version string]) > @@ -173,6 +180,7 @@ AC_DEFINE_DIR([LIBV4L1_PRIV_DIR], [libv4l1privdir], [libv4l1 private lib directo > AC_DEFINE_DIR([LIBV4L2_PRIV_DIR], [libv4l2privdir], [libv4l2 private lib directory]) > AC_DEFINE_DIR([LIBV4L2_PLUGIN_DIR], [libv4l2plugindir], [libv4l2 plugin directory]) > AC_DEFINE_DIR([LIBV4LCONVERT_PRIV_DIR], [libv4lconvertprivdir], [libv4lconvert private lib directory]) > +AC_DEFINE_DIR([LIBV4L2RDS_PRIV_DIR], [libv4l2rdsprivdir], [libv4l2rds private lib directory]) > AC_DEFINE_DIR([IR_KEYTABLE_SYSTEM_DIR], [keytablesystemdir], [ir-keytable preinstalled tables directory]) > AC_DEFINE_DIR([IR_KEYTABLE_USER_DIR], [keytableuserdir], [ir-keytable user defined tables directory]) > I don't think you need these changes. In libv4l these are for the wrapper libraries. You don't have one. Thanks, Gregor ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> 2012-07-25 17:44 ` [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> Konke Radlow 2012-07-26 14:28 ` Ezequiel Garcia 2012-07-26 18:46 ` Gregor Jasny @ 2012-07-26 18:49 ` Gregor Jasny 2 siblings, 0 replies; 10+ messages in thread From: Gregor Jasny @ 2012-07-26 18:49 UTC (permalink / raw) To: Konke Radlow; +Cc: linux-media, hverkuil, hdegoede On 7/25/12 7:44 PM, Konke Radlow wrote: > --- /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 $(DLOPEN_LIBS) $(ENFORCE_LIBV4L_STATIC) You don't call dlopen, so you can drop $(DLOPEN_LIBS) ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2012-07-30 12:59 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-07-26 16:21 [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> Konke Radlow 2012-07-28 11:08 ` Hans de Goede 2012-07-30 14:41 ` Konke Radlow 2012-07-30 14:46 ` Konke Radlow -- strict thread matches above, loose matches on Subject: below -- 2012-07-25 17:43 [RFC PATCH 0/2] Add support for RDS decoding Konke Radlow 2012-07-25 17:44 ` [RFC PATCH 1/2] Initial version of the RDS-decoder library Signed-off-by: Konke Radlow <kradlow@cisco.com> Konke Radlow 2012-07-26 14:28 ` Ezequiel Garcia 2012-07-26 14:39 ` Hans Verkuil 2012-07-26 14:41 ` Ezequiel Garcia 2012-07-26 18:46 ` Gregor Jasny 2012-07-26 18:49 ` Gregor Jasny
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).