dvb-fe: add support for DVBv5 stats
authorMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 15 Jan 2013 01:25:41 +0000 (23:25 -0200)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 23 Jan 2013 21:18:08 +0000 (19:18 -0200)
In order to avoid breakage on packages that depend on this
library, backward support was preserved. Of course, the backward
call dvb_fe_retrieve_stats() won't bring anything new; it will
only return what it was provided already by the old API.

In order to get full benefit of the new API, the new DVBv5
library call should be used: dvb_fe_retrieve_stats_layer().

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
lib/include/dvb-fe.h
lib/include/dvb-v5-std.h
lib/libdvbv5/dvb-fe.c
lib/libdvbv5/dvb-file.c
lib/libdvbv5/dvb-v5-std.c

index 46615cf..27b9aed 100644 (file)
  * code to v5 style, if such change gets merged upstream.
  */
 
-#define DTV_MAX_STATS 5
-#define DTV_STATUS                     (DTV_MAX_COMMAND + 100)
-#define DTV_BER                                (DTV_MAX_COMMAND + 101)
-#define DTV_SIGNAL_STRENGTH            (DTV_MAX_COMMAND + 102)
-#define DTV_SNR                                (DTV_MAX_COMMAND + 103)
-#define DTV_UNCORRECTED_BLOCKS         (DTV_MAX_COMMAND + 104)
+/*
+ * Those are needed to avoid breaking apps that depend on the library
+ * but shoudn't be used anymore
+ */
+#define DTV_MAX_STATS                  DTV_NUM_STATS_PROPS
+#define DTV_SIGNAL_STRENGTH            DTV_STAT_SIGNAL_STRENGTH
+#define DTV_SNR                                DTV_STAT_CNR
+#define DTV_UNCORRECTED_BLOCKS         DTV_STAT_ERROR_BLOCK_COUNT
 
 enum dvbv3_emulation_type {
        DVBV3_UNKNOWN = -1,
@@ -59,11 +61,9 @@ enum dvbv3_emulation_type {
 };
 
 struct dvb_v5_stats {
-       struct dtv_property             prop[DTV_MAX_STATS];
-       int                             valid[DTV_MAX_STATS];
+       struct dtv_property             prop[DTV_NUM_STATS_PROPS];
 };
 
-
 struct dvb_v5_fe_parms {
        int                             fd;
        char                            *fname;
@@ -130,6 +130,8 @@ int dvb_fe_get_parms(struct dvb_v5_fe_parms *parms);
 
 /* Get statistics */
 
+struct dtv_stats *dvb_fe_retrieve_stats_layer(struct dvb_v5_fe_parms *parms,
+                                              unsigned cmd, unsigned layer);
 int dvb_fe_retrieve_stats(struct dvb_v5_fe_parms *parms,
                          unsigned cmd, uint32_t *value);
 int dvb_fe_get_stats(struct dvb_v5_fe_parms *parms);
index 0b454a1..351ed88 100644 (file)
@@ -53,8 +53,18 @@ extern const void *dvb_v5_attr_names[];
 #define DTV_DISEQC_WAIT         (DTV_USER_COMMAND_START + 7)
 #define DTV_DISEQC_LNB          (DTV_USER_COMMAND_START + 8)
 #define DTV_FREQ_BPF            (DTV_USER_COMMAND_START + 9)
+
 #define DTV_MAX_USER_COMMAND    DTV_FREQ_BPF
 
+/* For status and statistics */
+
+#define DTV_STATUS              (DTV_MAX_USER_COMMAND + 1)
+#define DTV_BER                 (DTV_MAX_USER_COMMAND + 2)
+
+#define DTV_MAX_STAT_COMMAND    DTV_BER
+
+#define DTV_NUM_STATS_PROPS 8              /* 6 from DVBv5.10 API plus Status and BER */
+
 enum dvb_sat_polarization {
        POLARIZATION_OFF        = 0,
        POLARIZATION_H          = 1,
@@ -64,7 +74,7 @@ enum dvb_sat_polarization {
 };
 
 extern const char *dvb_sat_pol_name[6];
-extern const char *dvb_user_name[2];
+extern const char *dvb_user_name[13];
 extern const void *dvb_user_attr_names[];
 
 #endif
index e1abe86..ec35922 100644 (file)
@@ -199,12 +199,19 @@ struct dvb_v5_fe_parms *dvb_fe_open2(int adapter, int frontend, unsigned verbose
        /* Prepare to use the delivery system */
        dvb_set_sys(parms, parms->current_sys);
 
-       /* Prepare the status struct */
-       parms->stats.prop[0].cmd = DTV_STATUS;
-       parms->stats.prop[1].cmd = DTV_BER;
-       parms->stats.prop[2].cmd = DTV_SIGNAL_STRENGTH;
-       parms->stats.prop[3].cmd = DTV_SNR;
-       parms->stats.prop[4].cmd = DTV_UNCORRECTED_BLOCKS;
+       /*
+        * Prepare the status struct - DVBv5.10 parameters should
+        * come first, as they'll be read together.
+        */
+       parms->stats.prop[0].cmd = DTV_STAT_SIGNAL_STRENGTH;
+       parms->stats.prop[1].cmd = DTV_STAT_CNR;
+       parms->stats.prop[2].cmd = DTV_STAT_POST_BIT_ERROR_COUNT;
+       parms->stats.prop[3].cmd = DTV_STAT_POST_TOTAL_BIT_COUNT;
+       parms->stats.prop[4].cmd = DTV_STAT_ERROR_BLOCK_COUNT;
+       parms->stats.prop[5].cmd = DTV_STAT_TOTAL_BLOCK_COUNT;
+
+       parms->stats.prop[6].cmd = DTV_STATUS;
+       parms->stats.prop[7].cmd = DTV_BER;
 
        return parms;
 }
@@ -410,7 +417,7 @@ const char *dvb_cmd_name(int cmd)
 {
        if (cmd >= 0 && cmd < DTV_USER_COMMAND_START)
                return dvb_v5_name[cmd];
-       else if (cmd >= 0 && cmd <= DTV_MAX_USER_COMMAND)
+       else if (cmd >= 0 && cmd <= DTV_MAX_STAT_COMMAND)
                return dvb_user_name[cmd - DTV_USER_COMMAND_START];
        return NULL;
 }
@@ -419,7 +426,7 @@ const char *const *dvb_attr_names(int cmd)
 {
        if (cmd >= 0 && cmd < DTV_USER_COMMAND_START)
                return dvb_v5_attr_names[cmd];
-       else if (cmd >= 0 && cmd <= DTV_MAX_USER_COMMAND)
+       else if (cmd >= 0 && cmd <= DTV_MAX_STAT_COMMAND)
                return dvb_user_attr_names[cmd - DTV_USER_COMMAND_START];
        return NULL;
 }
@@ -659,39 +666,68 @@ ret:
        return 0;
 }
 
-int dvb_fe_retrieve_stats(struct dvb_v5_fe_parms *parms,
-                         unsigned cmd, uint32_t *value)
+struct dtv_stats *dvb_fe_retrieve_stats_layer(struct dvb_v5_fe_parms *parms,
+                                             unsigned cmd, unsigned layer)
 {
-       int i, valid;
-       for (i = 0; i < DTV_MAX_STATS; i++) {
+       int i;
+
+       for (i = 0; i < DTV_NUM_STATS_PROPS; i++) {
                if (parms->stats.prop[i].cmd != cmd)
                        continue;
-               *value = parms->stats.prop[i].u.data;
-               valid = parms->stats.valid[i];
-               if (!valid)
-                       return EINVAL;
-               return 0;
+               if (layer >= parms->stats.prop[2].u.st.len)
+                       return NULL;
+               return &parms->stats.prop[i].u.st.stat[layer];
        }
-       dvb_logerr("%s not found on retrieve",
-               dvb_cmd_name(cmd));
+       dvb_logerr("%s not found on retrieve", dvb_cmd_name(cmd));
 
-       return EINVAL;
+       return NULL;
+}
+
+int dvb_fe_retrieve_stats(struct dvb_v5_fe_parms *parms,
+                         unsigned cmd, uint32_t *value)
+{
+       struct dtv_stats *stat;
+       enum fecap_scale_params scale;
+
+       stat = dvb_fe_retrieve_stats_layer(parms, cmd, 0);
+       if (!stat) {
+               if (parms->verbose)
+                       dvb_logdbg("%s not found on retrieve", dvb_cmd_name(cmd));
+               return EINVAL;
+       }
+
+       scale = stat->scale;
+       if (scale == FE_SCALE_NOT_AVAILABLE) {
+               if (parms->verbose)
+                       dvb_logdbg("%s not available", dvb_cmd_name(cmd));
+               return EINVAL;
+       }
+
+       *value = stat->uvalue;
+
+       if (parms->verbose)
+               dvb_logdbg("Stats for %s = %d", dvb_cmd_name(cmd), *value);
+
+       return 0;
 }
 
 static int dvb_fe_store_stats(struct dvb_v5_fe_parms *parms,
-                       unsigned cmd, uint32_t value,
-                       int valid)
+                             unsigned cmd,
+                             enum fecap_scale_params scale,
+                             unsigned layer,
+                             uint32_t value)
 {
        int i;
-       for (i = 0; i < DTV_MAX_STATS; i++) {
+       for (i = 0; i < DTV_NUM_STATS_PROPS; i++) {
                if (parms->stats.prop[i].cmd != cmd)
                        continue;
-               parms->stats.prop[i].u.data = value;
-               parms->stats.valid[i] = valid;
+               parms->stats.prop[i].u.st.stat[layer].scale = scale;
+               parms->stats.prop[i].u.st.stat[layer].uvalue = value;
+               if (parms->stats.prop[i].u.st.len < layer + 1)
+                       parms->stats.prop[i].u.st.len = layer + 1;
                return 0;
        }
-       dvb_logerr("%s not found on store",
-               dvb_cmd_name(cmd));
+       dvb_logerr("%s not found on store", dvb_cmd_name(cmd));
 
        return EINVAL;
 }
@@ -701,38 +737,87 @@ int dvb_fe_get_stats(struct dvb_v5_fe_parms *parms)
        fe_status_t status = 0;
        uint32_t ber= 0, ucb = 0;
        uint16_t strength = 0, snr = 0;
-       int i, valid;
+       int i;
+       enum fecap_scale_params scale;
 
        if (ioctl(parms->fd, FE_READ_STATUS, &status) == -1) {
                dvb_perror("FE_READ_STATUS");
                return EINVAL;
        }
-       dvb_fe_store_stats(parms, DTV_STATUS, status, 1);
+       dvb_fe_store_stats(parms, DTV_STATUS, FE_SCALE_RELATIVE, 0, status);
+
+       if (parms->version >= 0x50a) {
+               struct dtv_properties props;
+               struct dtv_stats *st_bec, *st_bc;
+
+               props.num = 6;
+               props.props = parms->stats.prop;
+
+               /* Do a DVBv5.10 stats call */
+               if (ioctl(parms->fd, FE_GET_PROPERTY, &props) == -1)
+                       goto dvbv3_fallback;
+
+               /* If no stats i returned, fallback to the legacy API */
+               for (i = 0; i < props.num; i++)
+                       if (parms->stats.prop[i].u.st.len)
+                               break;
+               if (i == props.num)
+                       goto dvbv3_fallback;
+
+               /* Calculate and store BER */
+               for (i = 0; i < 4; i++) {
+                       uint64_t ber64;
+
+                       st_bec = dvb_fe_retrieve_stats_layer(parms, DTV_STAT_POST_BIT_ERROR_COUNT, i);
+                       if (!st_bec)
+                               goto no_ber;
+
+                       st_bc = dvb_fe_retrieve_stats_layer(parms, DTV_STAT_POST_TOTAL_BIT_COUNT, i);
+                       if (!st_bc || !st_bc->uvalue)
+                               goto no_ber;
+
+                       ber64 = (1E9 * st_bec->uvalue) / st_bc->uvalue;
+
+                       dvb_fe_store_stats(parms, DTV_BER, FE_SCALE_COUNTER, i, ber64);
+                       continue;
+no_ber:
+                       dvb_fe_store_stats(parms, DTV_BER, FE_SCALE_NOT_AVAILABLE, i, 0);
+               }
+
+               return 0;
+       }
 
+dvbv3_fallback:
+       /* DVB v3 stats*/
        if (ioctl(parms->fd, FE_READ_BER, &ber) == -1)
-               valid = 0;
+               scale = FE_SCALE_NOT_AVAILABLE;
        else
-               valid = 1;
-       dvb_fe_store_stats(parms, DTV_BER, ber, valid);
+               scale = FE_SCALE_RELATIVE;
+
+       /*
+        * BER scale on DVBv3 is not defined - different drivers use
+        * different scales, even weird ones, like multiples of 1/65280
+        */
+       dvb_fe_store_stats(parms, DTV_BER, scale, 0, ber);
 
        if (ioctl(parms->fd, FE_READ_SIGNAL_STRENGTH, &strength) == -1)
-               valid = 0;
+               scale = FE_SCALE_NOT_AVAILABLE;
        else
-               valid = 1;
+               scale = FE_SCALE_RELATIVE;
 
-       dvb_fe_store_stats(parms, DTV_SIGNAL_STRENGTH, strength, valid);
+       dvb_fe_store_stats(parms, DTV_STAT_SIGNAL_STRENGTH, scale, 0, strength);
 
        if (ioctl(parms->fd, FE_READ_SNR, &snr) == -1)
-               valid = 0;
+               scale = FE_SCALE_NOT_AVAILABLE;
        else
-               valid = 1;
-       dvb_fe_store_stats(parms, DTV_SNR, snr, valid);
+               scale = FE_SCALE_RELATIVE;
+       dvb_fe_store_stats(parms, DTV_STAT_CNR, scale, 0, snr);
 
        if (ioctl(parms->fd, FE_READ_UNCORRECTED_BLOCKS, &ucb) == -1)
-               valid = 0;
+               scale = FE_SCALE_NOT_AVAILABLE;
        else
-               valid = 1;
-       dvb_fe_store_stats(parms, DTV_UNCORRECTED_BLOCKS, snr, valid);
+               scale = FE_SCALE_COUNTER;
+       dvb_fe_store_stats(parms, DTV_STAT_ERROR_BLOCK_COUNT, scale, 0, snr);
 
        if (parms->verbose > 1) {
                dvb_log("Status: ");
@@ -770,7 +855,7 @@ int dvb_fe_get_event(struct dvb_v5_fe_parms *parms)
                                dvb_log ("    %s", fe_status_name[i].name);
                }
        }
-       dvb_fe_store_stats(parms, DTV_STATUS, status, 1);
+       dvb_fe_store_stats(parms, DTV_STATUS, FE_SCALE_RELATIVE, 0, status);
 
        dvb_fe_retrieve_parm(parms, DTV_FREQUENCY, &event.parameters.frequency);
        dvb_fe_retrieve_parm(parms, DTV_INVERSION, &event.parameters.inversion);
index 31db74c..d8d583c 100644 (file)
 #include "dvb-v5-std.h"
 #include "dvb-scan.h"
 
-static const char *parm_name(const struct parse_table *table)
-{
-       if (table->prop < DTV_MAX_COMMAND)
-               return dvb_v5_name[table->prop];
-       switch (table->prop) {
-       case DTV_CH_NAME:
-               return ("CHANNEL");
-       case DTV_POLARIZATION:
-               return ("POLARIZATION");
-       case DTV_VIDEO_PID:
-               return ("VIDEO PID");
-       case DTV_AUDIO_PID:
-               return ("AUDIO PID");
-       case DTV_SERVICE_ID:
-               return ("SERVICE ID");
-       default:
-               return ("unknown");
-       }
-}
-
 /*
  * Generic parse function for all formats each channel is contained into
  * just one line.
@@ -145,7 +125,7 @@ struct dvb_file *parse_format_oneline(const char *fname,
                                p = strtok(NULL, delimiter);
                        if (!p) {
                                sprintf(err_msg, "parameter %i (%s) missing",
-                                       i, parm_name(table));
+                                       i, dvb_cmd_name(table->prop));
                                goto error;
                        }
                        if (table->size) {
@@ -154,7 +134,7 @@ struct dvb_file *parse_format_oneline(const char *fname,
                                                break;
                                if (j == table->size) {
                                        sprintf(err_msg, "parameter %s invalid: %s",
-                                               parm_name(table), p);
+                                               dvb_cmd_name(table->prop), p);
                                        goto error;
                                }
                                if (table->prop == DTV_BANDWIDTH_HZ)
index 7cb23d5..c7101eb 100644 (file)
@@ -221,13 +221,24 @@ const char *dvb_sat_pol_name[6] = {
        [5] = NULL,
 };
 
-const char *dvb_user_name[2] = {
-       [DTV_POLARIZATION - DTV_USER_COMMAND_START] =                    "POLARIZATION",
-       [1] = NULL,
+const char *dvb_user_name[13] = {
+       [DTV_POLARIZATION - DTV_USER_COMMAND_START] =   "POLARIZATION",
+       [DTV_VIDEO_PID - DTV_USER_COMMAND_START] =      "VIDEO PID",
+       [DTV_AUDIO_PID - DTV_USER_COMMAND_START] =      "AUDIO PID",
+       [DTV_SERVICE_ID - DTV_USER_COMMAND_START] =     "SERVICE ID",
+       [DTV_CH_NAME - DTV_USER_COMMAND_START] =        "CHANNEL",
+       [DTV_VCHANNEL - DTV_USER_COMMAND_START] =       "VCHANNEL",
+       [DTV_SAT_NUMBER - DTV_USER_COMMAND_START] =     "SAT NUMBER",
+       [DTV_DISEQC_WAIT - DTV_USER_COMMAND_START] =    "DISEQC WAIT",
+       [DTV_DISEQC_LNB - DTV_USER_COMMAND_START] =     "DISEQC LNB",
+       [DTV_FREQ_BPF - DTV_USER_COMMAND_START] =       "FREQ BPF",
+       [DTV_STATUS - DTV_USER_COMMAND_START] =         "STATUS",
+       [DTV_BER - DTV_USER_COMMAND_START] =            "BER",
+       [12] = NULL,
 };
 
 const void *dvb_user_attr_names[] = {
-       [0 ... DTV_MAX_USER_COMMAND - DTV_USER_COMMAND_START] = NULL,
+       [0 ... DTV_MAX_STAT_COMMAND - DTV_USER_COMMAND_START] = NULL,
        [DTV_POLARIZATION - DTV_USER_COMMAND_START]           = dvb_sat_pol_name,
 };