src/config2.c \
src/lang_codes.c \
src/lang_str.c \
- src/imagecache.c
+ src/imagecache.c \
+ src/tvhtime.c
SRCS += src/epggrab/module.c\
src/epggrab/channel.c\
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <time.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include "psi.h"
#include "notify.h"
#include "cwc.h"
+#include "tvhtime.h"
#if TDT_TRACE
#define TRACE(_pre, _fmt, ...)\
return 0;
}
+/*
+ * Time Offset table handler
+ */
+static int
+dvb_tot_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
+ uint8_t tableid, void *opaque)
+{
+ uint16_t mjd;
+ uint8_t hour, min, sec;
+ int year, mon, day;
+ struct tm utc;
+
+ if (tableid != 0x73)
+ return -1;
+
+ /* DVB format MJD, Hour, Min, Sec */
+ mjd = (buf[0] << 8) | buf[1];
+ hour = bcdtoint(buf[2]);
+ min = bcdtoint(buf[3]);
+ sec = bcdtoint(buf[4]);
+
+ /* Convert MJD (using algo from EN 300 468 v1.13.1 Annex C) */
+ year = (int)((mjd - 15078.2) / 365.25);
+ mon = (int)((mjd - 14956.1 - (int)(year * 365.25)) / 30.6001);
+ day = mjd - 14956 - (int)(year * 365.25) - (int)(mon * 30.6001);
+ if (mon == 14 || mon == 15) {
+ year++;
+ mon -= 12;
+ }
+ mon--;
+
+ tvhlog(LOG_DEBUG, "tdt-tot", "time is %04d/%02d/%02d %02d:%02d:%02d",
+ year+1900, mon, day, hour, min, sec);
+
+ /* Convert to UTC time_t */
+ utc.tm_wday = 0;
+ utc.tm_yday = 0;
+ utc.tm_isdst = 0;
+ utc.tm_year = year;
+ utc.tm_mon = mon - 1;
+ utc.tm_mday = day;
+ utc.tm_hour = hour;
+ utc.tm_min = min;
+ utc.tm_sec = sec;
+ tvhtime_update(&utc);
+
+ return 0;
+}
/**
* Demux for default DVB tables that we want
tdt_add(tdmi, 0, 0, dvb_pidx11_callback, NULL, "pidx11",
TDT_QUICKREQ | TDT_CRC, 0x11);
+
+ /* Time Offset Table */
+
+ tdt_add(tdmi, 0, 0, dvb_tot_callback, NULL, "tot", TDT_CRC, 0x14);
}
--- /dev/null
+#include <time.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "tvhtime.h"
+#include "tvheadend.h"
+#include "settings.h"
+
+uint32_t tvhtime_update_enabled;
+uint32_t tvhtime_ntp_enabled;
+uint32_t tvhtime_tolerance;
+
+/*
+ * NTP processing
+ */
+#define NTPD_BASE 0x4e545030 /* "NTP0" */
+#define NTPD_UNIT 2
+
+typedef struct
+{
+ int mode; /* 0 - if valid set
+ * use values,
+ * clear valid
+ * 1 - if valid set
+ * if count before and after read of values is equal,
+ * use values
+ * clear valid
+ */
+ int count;
+ time_t clockTimeStampSec;
+ int clockTimeStampUSec;
+ time_t receiveTimeStampSec;
+ int receiveTimeStampUSec;
+ int leap;
+ int precision;
+ int nsamples;
+ int valid;
+ int pad[10];
+} ntp_shm_t;
+
+static ntp_shm_t *
+ntp_shm_init ( void )
+{
+ int shmid, unit, mode;
+ static ntp_shm_t *shmptr = NULL;
+
+ if (shmptr != NULL)
+ return shmptr;
+
+ unit = getuid() ? 2 : 0;
+ mode = getuid() ? 0666 : 0600;
+
+ shmid = shmget((key_t)NTPD_BASE + unit, sizeof(ntp_shm_t), IPC_CREAT | mode);
+ if (shmid == -1)
+ return NULL;
+
+ shmptr = shmat(shmid, 0, 0);
+ memset(shmptr, 0, sizeof(ntp_shm_t));
+ if (shmptr) {
+ shmptr->mode = 1;
+ shmptr->precision = -1;
+ shmptr->nsamples = 1;
+ }
+
+ return shmptr;
+}
+
+/*
+ * Update time
+ */
+void
+tvhtime_update ( struct tm *tm )
+{
+ time_t now;
+ struct timeval tv;
+ ntp_shm_t *ntp_shm;
+ int64_t t1, t2;
+
+ /* Current and reported time */
+ now = mktime(tm);
+ gettimeofday(&tv, NULL);
+
+ /* Delta */
+ t1 = now * 1000000;
+ t2 = tv.tv_sec * 1000000 + tv.tv_usec;
+#if NTP_TRACE
+ tvhlog(LOG_DEBUG, "ntp", "delta = %"PRId64" us\n", t2 - t1);
+#endif
+
+ /* Update local clock */
+ if (tvhtime_update_enabled)
+ if (llabs(t2 - t1) > tvhtime_tolerance)
+ stime(&now);
+
+ /* NTP */
+ if (tvhtime_ntp_enabled) {
+ if (!(ntp_shm = ntp_shm_init()))
+ return;
+
+ ntp_shm->valid = 0;
+ ntp_shm->count++;
+ ntp_shm->clockTimeStampSec = now;
+ ntp_shm->clockTimeStampUSec = 0;
+ ntp_shm->receiveTimeStampSec = tv.tv_sec;
+ ntp_shm->receiveTimeStampUSec = (int)tv.tv_usec;
+ ntp_shm->count++;
+ ntp_shm->valid = 1;
+ }
+}
+
+/* Initialise */
+void tvhtime_init ( void )
+{
+ htsmsg_t *m = hts_settings_load("tvhtime/config");
+ if (htsmsg_get_u32(m, "update_enabled", &tvhtime_update_enabled))
+ tvhtime_update_enabled = 0;
+ if (htsmsg_get_u32(m, "ntp_enabled", &tvhtime_ntp_enabled))
+ tvhtime_ntp_enabled = 0;
+ if (htsmsg_get_u32(m, "tolerance", &tvhtime_tolerance))
+ tvhtime_tolerance = 5000;
+}
+
+static void tvhtime_save ( void )
+{
+ htsmsg_t *m = htsmsg_create_map();
+ htsmsg_add_u32(m, "update_enabled", tvhtime_update_enabled);
+ htsmsg_add_u32(m, "ntp_enabled", tvhtime_ntp_enabled);
+ htsmsg_add_u32(m, "tolerance", tvhtime_tolerance);
+ hts_settings_save(m, "tvhtime/config");
+}
+
+void tvhtime_set_update_enabled ( uint32_t on )
+{
+ if (tvhtime_update_enabled == on)
+ return;
+ tvhtime_update_enabled = on;
+ tvhtime_save();
+}
+
+void tvhtime_set_ntp_enabled ( uint32_t on )
+{
+ if (tvhtime_ntp_enabled == on)
+ return;
+ tvhtime_ntp_enabled = on;
+ tvhtime_save();
+}
+
+void tvhtime_set_tolerance ( uint32_t v )
+{
+ if (tvhtime_tolerance == v)
+ return;
+ tvhtime_tolerance = v;
+ tvhtime_save();
+}
--- /dev/null
+/*
+ * TVheadend - time processing
+ *
+ * Copyright (C) 2013 Adam Sutton
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TVH_TIME_H__
+#define __TVH_TIME_H_
+
+extern uint32_t tvhtime_update_enabled;
+extern uint32_t tvhtime_ntp_enabled;
+extern uint32_t tvhtime_tolerance;
+
+void tvhtime_init ( void );
+void tvhtime_update ( struct tm *now );
+
+void tvhtime_set_update_enabled ( uint32_t on );
+void tvhtime_set_ntp_enabled ( uint32_t on );
+void tvhtime_set_tolerance ( uint32_t v );
+
+#endif /* __TVH_TIME_H__ */
#include "subscriptions.h"
#include "imagecache.h"
#include "timeshift.h"
+#include "tvhtime.h"
/**
*
/* Misc */
pthread_mutex_lock(&global_lock);
m = config_get_all();
+
+ /* Time */
+ htsmsg_add_u32(m, "tvhtime_update_enabled", tvhtime_update_enabled);
+ htsmsg_add_u32(m, "tvhtime_ntp_enabled", tvhtime_ntp_enabled);
+ htsmsg_add_u32(m, "tvhtime_tolerance", tvhtime_tolerance);
+
pthread_mutex_unlock(&global_lock);
/* Image cache */
save |= config_set_language(str);
if (save)
config_save();
+
+ /* Time */
+ if ((str = http_arg_get(&hc->hc_req_args, "tvhtime_update_enabled")))
+ tvhtime_set_update_enabled(!!str);
+ if ((str = http_arg_get(&hc->hc_req_args, "tvhtime_ntp_enabled")))
+ tvhtime_set_ntp_enabled(!!str);
+ if ((str = http_arg_get(&hc->hc_req_args, "tvhtime_tolerance")))
+ tvhtime_set_tolerance(atoi(str));
+
pthread_mutex_unlock(&global_lock);
/* Image Cache */
root : 'config'
}, [ 'muxconfpath', 'language',
'imagecache_enabled', 'imagecache_ok_period',
- 'imagecache_fail_period', 'imagecache_ignore_sslcert']);
+ 'imagecache_fail_period', 'imagecache_ignore_sslcert',
+ 'tvhtime_update_enabled', 'tvhtime_ntp_enabled',
+ 'tvhtime_tolerance']);
/* ****************************************************************
* Form Fields
fromLegend: 'Available'
});
+ /*
+ * Time/Date
+ */
+ var tvhtimeUpdateEnabled = new Ext.form.Checkbox({
+ name: 'tvhtime_update_enabled',
+ fieldLabel: 'Update time'
+ });
+
+ var tvhtimeNtpEnabled = new Ext.form.Checkbox({
+ name: 'tvhtime_ntp_enabled',
+ fieldLabel: 'Enable NTP driver'
+ });
+
+ var tvhtimeTolerance = new Ext.form.NumberField({
+ name: 'tvhtime_tolerance',
+ fieldLabel: 'Update tolerance (ms)'
+ });
+
+ var tvhtimePanel = new Ext.form.FieldSet({
+ title: 'Time Update',
+ width: 700,
+ autoHeight: true,
+ collapsible: true,
+ items : [ tvhtimeUpdateEnabled, tvhtimeNtpEnabled, tvhtimeTolerance ]
+ });
+
/*
* Image cache
*/
defaultType : 'textfield',
autoHeight : true,
items : [ language, dvbscanPath,
- imagecachePanel ],
+ imagecachePanel, tvhtimePanel ],
tbar : [ saveButton, '->', helpButton ]
});