From a8c7d06316a898069098c292e2f1c225fcd85c70 Mon Sep 17 00:00:00 2001 From: Ralph Metzler Date: Wed, 24 Jul 2019 12:43:49 +0200 Subject: [PATCH] add basic CI support and signal quality --- lib/src/Makefile | 6 +- lib/src/ca.c | 636 ++++++++++++++++++++++++++++++++++++++++++ lib/src/dddvb.c | 1 - lib/src/dddvb.h | 48 +++- lib/src/dvb.c | 6 +- lib/src/dvb_quality.c | 388 ++++++++++++++++++++++++++ lib/src/libdddvb.h | 18 ++ 7 files changed, 1097 insertions(+), 6 deletions(-) create mode 100644 lib/src/ca.c create mode 100644 lib/src/dvb_quality.c diff --git a/lib/src/Makefile b/lib/src/Makefile index b6148fc..4d39d4f 100644 --- a/lib/src/Makefile +++ b/lib/src/Makefile @@ -5,10 +5,10 @@ all: libdddvb.so.1.0.1 %.o: %.c $(CC) $(LIB_FLAGS) $(CFLAGS) -c $< -libdddvb.a: dvb.o dddvb.o tools.o config.o +libdddvb.a: dvb.o dddvb.o tools.o config.o ca.o $(AR) -cvq libdddvb.a $^ -libdddvb.so.1.0.1: dvb.o dddvb.o tools.o config.o +libdddvb.so.1.0.1: dvb.o dddvb.o tools.o config.o ca.o $(CC) $(LIB_FLAGS) $(CFLAGS) -shared -Wl,-soname,libdddvb.so.1 -o libdddvb.so.1.0.1 $^ -lc ln -sf libdddvb.so.1.0.1 libdddvb.so.1 ln -sf libdddvb.so.1.0.1 libdddvb.so @@ -17,4 +17,4 @@ dddvb_test: dddvb_test.o $(CC) -o dddvb_test $< -L . -l dddvb clean: - rm *.o + -rm -f *.o diff --git a/lib/src/ca.c b/lib/src/ca.c new file mode 100644 index 0000000..0f8b133 --- /dev/null +++ b/lib/src/ca.c @@ -0,0 +1,636 @@ +#include "libdddvb.h" +#include "dddvb.h" +#include "tools.h" +#include "debug.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MMI_STATE_CLOSED 0 +#define MMI_STATE_OPEN 1 +#define MMI_STATE_ENQ 2 +#define MMI_STATE_MENU 3 + + +int set_nonblock(int fd) +{ + int fl = fcntl(fd, F_GETFL); + + if (fl < 0) + return fl; + return fcntl(fd, F_SETFL, fl | O_NONBLOCK); +} + +int streamsock(const char *port, int family, struct sockaddr *sadr) +{ + int one=1, sock; + struct addrinfo *ais, *ai, hints = { + .ai_flags = AI_PASSIVE, + .ai_family = family, + .ai_socktype = SOCK_STREAM, + .ai_protocol = 0, .ai_addrlen = 0, + .ai_addr = NULL, .ai_canonname = NULL, .ai_next = NULL, + }; + if (getaddrinfo(NULL, port, &hints, &ais) < 0) + return -1; + for (ai = ais; ai; ai = ai->ai_next) { + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock == -1) + continue; + if (!setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) && + !bind(sock, ai->ai_addr, ai->ai_addrlen)) { + *sadr = *ai->ai_addr; + break; + } + close(sock); + sock = -1; + } + freeaddrinfo(ais); + return sock; +} + +static int ai_callback(void *arg, uint8_t slot_id, uint16_t session_number, + uint8_t application_type, uint16_t application_manufacturer, + uint16_t manufacturer_code, uint8_t menu_string_length, + uint8_t *menu_string) +{ + struct dddvb_ca *ca = arg; + + dbgprintf(DEBUG_CA, "Application type: %02x\n", application_type); + dbgprintf(DEBUG_CA, "Application manufacturer: %04x\n", application_manufacturer); + dbgprintf(DEBUG_CA, "Manufacturer code: %04x\n", manufacturer_code); + dbgprintf(DEBUG_CA, "Menu string: %.*s\n", menu_string_length, menu_string); + + return 0; +} + +static int ca_info_callback(void *arg, uint8_t slot_id, uint16_t snum, + uint32_t id_count, uint16_t *ids) +{ + struct dddvb_ca *ca = arg; + uint32_t i; + + dbgprintf(DEBUG_CA, "CAM supports the following ca system ids:\n"); + for (i = 0; i < id_count; i++) { + dbgprintf(DEBUG_CA, " 0x%04x\n", ids[i]); + } + ca->resource_ready = 1; + return 0; +} + +#if 0 +static int handle_pmt(struct dvbca *ca, uint8_t *buf, int size) +{ + int listmgmt = CA_LIST_MANAGEMENT_ONLY; + uint8_t capmt[4096]; + struct section *section = section_codec(buf, size); + struct section_ext *section_ext = section_ext_decode(section, 0); + struct mpeg_pmt_section *pmt = mpeg_pmt_section_codec(section_ext); + + dbgprintf(DEBUG_CA, "handle pmt\n"); + if (section_ext->version_number == ca->ca_pmt_version && + ca->pmt == ca->pmt_old) + return; + if (ca->pmt != ca->pmt_old) { + ca->pmt_old = ca->pmt; + ca->sentpmt = 0; + } + if (ca->resource_ready) { + ca->data_pmt_version = pmt->head.version_number; + + if (ca->sentpmt) { + listmgmt = CA_LIST_MANAGEMENT_UPDATE; + //return; + } + ca->sentpmt = 1; + dbgprintf(DEBUG_CA, "set ca_pmt\n"); + + if ((size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), ca->moveca, listmgmt, + CA_PMT_CMD_ID_OK_DESCRAMBLING)) < 0) { + dbgprintf(DEBUG_CA, "Failed to format PMT\n"); + return -1; + } + if (en50221_app_ca_pmt(ca->stdcam->ca_resource, ca->stdcam->ca_session_number, capmt, size)) { + dbgprintf(DEBUG_CA, "Failed to send PMT\n"); + return -1; + } + } + +} +#endif + + +#if 0 +static void handle_tdt(struct dddvb_ca *ca) +{ + struct section *section; + struct dvb_tdt_section *tdt; + uint8_t sec[4096]; + time_t dvb_time; + int len; + + if (ca->stdcam == NULL) + return; + if (ca->stdcam->dvbtime == NULL) + return; + len = getsec(ca->input, 0x14, 0, 0x70, sec); + if (len < 0) + return; + dbgprintf(DEBUG_CA, "got tdt\n"); + + section = section_codec(sec, len); + if (section == NULL) + return; + tdt = dvb_tdt_section_codec(section); + if (tdt == NULL) + return; + dvb_time = dvbdate_to_unixtime(tdt->utc_time); + + dbgprintf(DEBUG_CA, "set dvbtime\n"); + if (ca->stdcam->dvbtime) + ca->stdcam->dvbtime(ca->stdcam, dvb_time); +} +#endif + +static int handle_pmts(struct dddvb_ca *ca) +{ + int listmgmt = CA_LIST_MANAGEMENT_ONLY; + uint8_t sec[4096], capmt[4096]; + struct section *section; + struct section_ext *section_ext; + struct mpeg_pmt_section *pmt; + int i, size, num, len; + + if (!ca->resource_ready) + return 0; + dbgprintf(DEBUG_CA, "handle pmts\n"); + for (i = num = 0; i < MAX_PMT; i++) + if (ca->pmt[i]) + num++; + for (i = 0; i < num; i++) { + len = -1;//getsec(ca->input, ca->pmt[i] & 0xffff, ca->pmt[i] >> 16, 2, sec); + if (len < 0) + continue; + section = section_codec(sec, len); + section_ext = section_ext_decode(section, 0); + pmt = mpeg_pmt_section_codec(section_ext); + + ca->ca_pmt_version[i] = section_ext->version_number; + if (ca->sentpmt) { + //return 0; + listmgmt = CA_LIST_MANAGEMENT_UPDATE; + } else { + listmgmt = CA_LIST_MANAGEMENT_ONLY; + if (num > 1) { + listmgmt = CA_LIST_MANAGEMENT_MORE; + if (i == 0) + listmgmt = CA_LIST_MANAGEMENT_FIRST; + if (i == num - 1) + listmgmt = CA_LIST_MANAGEMENT_LAST; + } + } + dbgprintf(DEBUG_CA, "set ca_pmt\n"); + + if ((size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), ca->moveca, listmgmt, + CA_PMT_CMD_ID_OK_DESCRAMBLING)) < 0) { + dbgprintf(DEBUG_CA, "Failed to format PMT\n"); + return -1; + } + //dump(capmt, size); + if (en50221_app_ca_pmt(ca->stdcam->ca_resource, ca->stdcam->ca_session_number, capmt, size)) { + dbgprintf(DEBUG_CA, "Failed to send PMT\n"); + return -1; + } + } + if (num) + ca->sentpmt = 1; + return 0; +} + +static int set_pmts(struct dddvb_ca *ca, uint8_t **pmts) +{ + int listmgmt = CA_LIST_MANAGEMENT_ONLY; + uint8_t sec[4096], capmt[4096]; + struct section *section; + struct section_ext *section_ext; + struct mpeg_pmt_section *pmt; + int i, size, num, len; + + if (!ca->resource_ready) + return 0; + dbgprintf(DEBUG_CA, "handle pmts\n"); + for (i = num = 0; i < MAX_PMT; i++) + if (pmts[i]) + num++; + for (i = 0; i < num; i++) { + section = section_codec(pmts[i], len); + section_ext = section_ext_decode(section, 0); + pmt = mpeg_pmt_section_codec(section_ext); + + ca->ca_pmt_version[i] = section_ext->version_number; + if (ca->sentpmt) { + //return 0; + listmgmt = CA_LIST_MANAGEMENT_UPDATE; + } else { + listmgmt = CA_LIST_MANAGEMENT_ONLY; + if (num > 1) { + listmgmt = CA_LIST_MANAGEMENT_MORE; + if (i == 0) + listmgmt = CA_LIST_MANAGEMENT_FIRST; + if (i == num - 1) + listmgmt = CA_LIST_MANAGEMENT_LAST; + } + } + dbgprintf(DEBUG_CA, "set ca_pmt\n"); + + if ((size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), ca->moveca, listmgmt, + CA_PMT_CMD_ID_OK_DESCRAMBLING)) < 0) { + dbgprintf(DEBUG_CA, "Failed to format PMT\n"); + return -1; + } + //dump(capmt, size); + if (en50221_app_ca_pmt(ca->stdcam->ca_resource, ca->stdcam->ca_session_number, capmt, size)) { + dbgprintf(DEBUG_CA, "Failed to send PMT\n"); + return -1; + } + } + if (num) + ca->sentpmt = 1; + return 0; +} + +static void proc_csock_msg(struct dddvb_ca *ca, uint8_t *buf, int len) +{ + if (*buf == '\r') { + return; + } else if (*buf == '\n') { + switch(ca->mmi_state) { + case MMI_STATE_CLOSED: + case MMI_STATE_OPEN: + if ((ca->mmi_bufp == 0) && (ca->resource_ready)) { + en50221_app_ai_entermenu(ca->stdcam->ai_resource, + ca->stdcam->ai_session_number); + } + break; + + case MMI_STATE_ENQ: + if (ca->mmi_bufp == 0) { + en50221_app_mmi_answ(ca->stdcam->mmi_resource, + ca->stdcam->mmi_session_number, + MMI_ANSW_ID_CANCEL, NULL, 0); + } else { + en50221_app_mmi_answ(ca->stdcam->mmi_resource, + ca->stdcam->mmi_session_number, + MMI_ANSW_ID_ANSWER, + ca->mmi_buf, ca->mmi_bufp); + } + ca->mmi_state = MMI_STATE_OPEN; + break; + + case MMI_STATE_MENU: + ca->mmi_buf[ca->mmi_bufp] = 0; + en50221_app_mmi_menu_answ(ca->stdcam->mmi_resource, + ca->stdcam->mmi_session_number, + atoi(ca->mmi_buf)); + ca->mmi_state = MMI_STATE_OPEN; + break; + } + ca->mmi_bufp = 0; + } else { + if (ca->mmi_bufp < (sizeof(ca->mmi_buf) - 1)) { + ca->mmi_buf[ca->mmi_bufp++] = *buf; + } + } +} + +static int proc_csock(struct dddvb_ca *ca) +{ + uint8_t buf[1024]; + int len, i, res; + + if (ca->stdcam == NULL) + return -1; + while ((len = recv(ca->sock, buf, 1, 0)) >= 0) { + if (len == 0) + goto release; + if (len < 0) { + if (errno != EAGAIN) + goto release; + return 0; + } + proc_csock_msg(ca, buf, len); + } + return 0; +release: + close(ca->sock); + ca->sock = -1; + return -1; +} + + +static void handle_ci(struct dddvb_ca *ca) +{ + uint8_t sec[4096]; + uint32_t pmt_count, tdt_count; + int len; + int sock, i; + struct sockaddr sadr; + char port[6]; + + snprintf(port, sizeof(port), "%u", (uint16_t) (8888 + ca->nr)); + sock = streamsock(port, AF_INET, &sadr); + if (listen(sock, 4) < 0) { + dbgprintf(DEBUG_CA, "listen error"); + return; + } + ca->sock = -1; + + while (1) {//!ca->exit) { + struct timeval timeout; + uint32_t count = 0; + int num; + int mfd; + fd_set fds; + + timeout.tv_sec = 0; + timeout.tv_usec = 200000; + FD_ZERO(&fds); + if (ca->sock < 0) { + FD_SET(sock, &fds); + num = select(sock + 1, &fds, NULL, NULL, &timeout); + } else { + FD_SET(ca->sock, &fds); + num = select(ca->sock + 1, &fds, NULL, NULL, &timeout); + } + if (num > 0) { + if (ca->sock < 0) { + if (FD_ISSET(sock, &fds)) { + socklen_t len; + struct sockaddr cadr; + + ca->sock = accept(sock, &cadr, &len); + if (ca->sock >= 0) { + set_nonblock(ca->sock); + } + } + } else { + if (FD_ISSET(ca->sock, &fds)) { + proc_csock(ca); + } + } + } + + pthread_mutex_lock(&ca->mutex); + if (!ca->state) { + pthread_mutex_unlock(&ca->mutex); + continue; + } + if (ca->setpmt) { + dbgprintf(DEBUG_CA, "got new PMT %08x\n", ca->pmt_new); + memcpy(ca->pmt, ca->pmt_new, sizeof(ca->pmt)); + memset(ca->pmt_old, 0, sizeof(ca->pmt_old)); + for (i = 0; i < MAX_PMT; i++) + ca->ca_pmt_version[i] = -1; + ca->sentpmt = 0; + ca->setpmt = 0; + pmt_count = 0; + tdt_count = 0; + } + pthread_mutex_unlock(&ca->mutex); + + if (!ca->sentpmt) + handle_pmts(ca); + else { + pmt_count++; + if (pmt_count == 10) { + //handle_pmts(ca); + pmt_count = 0; + } + } + tdt_count++; + if (tdt_count == 10) { + //handle_tdt(ca); + tdt_count = 0; + } + } +} + + +int set_pmt(struct dddvb_ca *ca, uint32_t *pmt) +{ + dbgprintf(DEBUG_CA, "set_pmt %08x %08x %08x\n", pmt[0], pmt[1], pmt[2]); + pthread_mutex_lock(&ca->mutex); + ca->setpmt = 1; + memcpy(ca->pmt_new, pmt, sizeof(ca->pmt_new)); + pthread_mutex_unlock(&ca->mutex); + return 0; +} + + +static void ci_poll(struct dddvb_ca *ca) +{ + while (!ca->dd->exit) { + ca->stdcam->poll(ca->stdcam); + + } +} + + +static int mmi_close_callback(void *arg, uint8_t slot_id, uint16_t snum, + uint8_t cmd_id, uint8_t delay) +{ + struct dddvb_ca *ca = arg; + + ca->mmi_state = MMI_STATE_CLOSED; + return 0; +} + +static int mmi_display_control_callback(void *arg, uint8_t slot_id, uint16_t snum, + uint8_t cmd_id, uint8_t mmi_mode) +{ + struct dddvb_ca *ca = arg; + struct en50221_app_mmi_display_reply_details reply; + + if (cmd_id != MMI_DISPLAY_CONTROL_CMD_ID_SET_MMI_MODE) { + en50221_app_mmi_display_reply(ca->stdcam->mmi_resource, snum, + MMI_DISPLAY_REPLY_ID_UNKNOWN_CMD_ID, &reply); + return 0; + } + + // we only support high level mode + if (mmi_mode != MMI_MODE_HIGH_LEVEL) { + en50221_app_mmi_display_reply(ca->stdcam->mmi_resource, snum, + MMI_DISPLAY_REPLY_ID_UNKNOWN_MMI_MODE, &reply); + return 0; + } + + reply.u.mode_ack.mmi_mode = mmi_mode; + en50221_app_mmi_display_reply(ca->stdcam->mmi_resource, snum, + MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK, &reply); + ca->mmi_state = MMI_STATE_OPEN; + return 0; +} + +static int mmi_enq_callback(void *arg, uint8_t slot_id, uint16_t snum, + uint8_t blind_answer, uint8_t expected_answer_length, + uint8_t *text, uint32_t text_size) +{ + struct dddvb_ca *ca = arg; + + if (ca->sock >= 0) { + sendstring(ca->sock, "%.*s: ", text_size, text); + } + //mmi_enq_blind = blind_answer; + //mmi_enq_length = expected_answer_length; + ca->mmi_state = MMI_STATE_ENQ; + return 0; +} + +static int mmi_menu_callback(void *arg, uint8_t slot_id, uint16_t snum, + struct en50221_app_mmi_text *title, + struct en50221_app_mmi_text *sub_title, + struct en50221_app_mmi_text *bottom, + uint32_t item_count, struct en50221_app_mmi_text *items, + uint32_t item_raw_length, uint8_t *items_raw) +{ + uint32_t i; + struct dddvb_ca *ca = arg; + + if (ca->sock >= 0) { + if (title->text_length) + sendstring(ca->sock, "%.*s\n", title->text_length, title->text); + if (sub_title->text_length) + sendstring(ca->sock, "%.*s\n", sub_title->text_length, sub_title->text); + for (i = 0; i < item_count; i++) + sendstring(ca->sock, "%i. %.*s\n", i + 1, items[i].text_length, items[i].text); + if (bottom->text_length) + sendstring(ca->sock, "%.*s\n", bottom->text_length, bottom->text); + } + ca->mmi_state = MMI_STATE_MENU; + return 0; +} + + +static int init_ca_stack(struct dddvb_ca *ca) +{ + ca->tl = en50221_tl_create(1, 16); + if (ca->tl == NULL) { + dbgprintf(DEBUG_CA, "Failed to create transport layer\n"); + return -1; + } + ca->sl = en50221_sl_create(ca->tl, 16); + if (ca->sl == NULL) { + dbgprintf(DEBUG_CA, "Failed to create session layer\n"); + en50221_tl_destroy(ca->tl); + return -1; + } + + ca->stdcam = en50221_stdcam_llci_create(ca->fd, 0, ca->tl, ca->sl); + if (!ca->stdcam) { + dbgprintf(DEBUG_CA, "Failed to create stdcam\n"); + en50221_sl_destroy(ca->sl); + en50221_tl_destroy(ca->tl); + return -1; + } + if (ca->stdcam->ai_resource) { + en50221_app_ai_register_callback(ca->stdcam->ai_resource, ai_callback, ca); + } + if (ca->stdcam->ca_resource) { + en50221_app_ca_register_info_callback(ca->stdcam->ca_resource, ca_info_callback, ca); + } + if (ca->stdcam->mmi_resource) { + en50221_app_mmi_register_close_callback(ca->stdcam->mmi_resource, mmi_close_callback, ca); + en50221_app_mmi_register_display_control_callback(ca->stdcam->mmi_resource, + mmi_display_control_callback, ca); + en50221_app_mmi_register_enq_callback(ca->stdcam->mmi_resource, mmi_enq_callback, ca); + en50221_app_mmi_register_menu_callback(ca->stdcam->mmi_resource, mmi_menu_callback, ca); + en50221_app_mmi_register_list_callback(ca->stdcam->mmi_resource, mmi_menu_callback, ca); + } else { + dbgprintf(DEBUG_CA, + "CAM Menus are not supported by this interface hardware\n"); + } + return 0; +} + + +static int init_ca(struct dddvb *dd, int a, int f, int fd) +{ + struct dddvb_ca *ca; + char fname[80]; + + ca = &dd->dvbca[dd->dvbca_num]; + ca->dd = dd; + ca->anum = a; + ca->fnum = f; + ca->nr = dd->dvbca_num + 1; + ca->fd = fd; + pthread_mutex_init(&ca->mutex, 0); + + init_ca_stack(ca); + + pthread_create(&ca->poll_pt, NULL, (void *) ci_poll, ca); + pthread_create(&ca->pt, NULL, (void *) handle_ci, ca); + + sprintf(fname, "/dev/dvb/adapter%d/ci%d", a, f); + ca->ci_wfd = open(fname, O_WRONLY); + ca->ci_rfd = open(fname, O_RDONLY); + dd->dvbca_num++; + return 0; +} + +int dddvb_ca_write(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len) +{ + dbgprintf(DEBUG_CA, "ca_write\n"); + return 0; +} + +int dddvb_ca_read(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len) +{ + dbgprintf(DEBUG_CA, "ca_read\n"); + return 0; +} + +int dddvb_ca_set_pmts(struct dddvb *dd, uint32_t nr, uint8_t **pmts) +{ + struct dddvb_ca *ca = &dd->dvbca[nr]; + + dbgprintf(DEBUG_CA, "ca_set_pmt\n"); + return set_pmts(ca, pmts); +} + + +int scan_dvbca(struct dddvb *dd) +{ + int a, f, fd; + char fname[80]; + + for (a = 0; a < 16; a++) { + for (f = 0; f < 16; f++) { + sprintf(fname, "/dev/dvb/adapter%d/ca%d", a, f); + fd = open(fname, O_RDWR); + if (fd >= 0) { + init_ca(dd, a, f, fd); + //close(fd); + } + } + } + dbgprintf(DEBUG_CA, "Found %d CA interfaces\n", dd->dvbca_num); +} + diff --git a/lib/src/dddvb.c b/lib/src/dddvb.c index c5dc19a..17af839 100644 --- a/lib/src/dddvb.c +++ b/lib/src/dddvb.c @@ -3,7 +3,6 @@ #include "debug.h" #include - LIBDDDVB_EXPORTED uint32_t dddvb_debug; LIBDDDVB_EXPORTED struct dddvb *global_dd = NULL; LIBDDDVB_EXPORTED pthread_mutex_t dddvb_mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/lib/src/dddvb.h b/lib/src/dddvb.h index 8552c71..545e877 100644 --- a/lib/src/dddvb.h +++ b/lib/src/dddvb.h @@ -13,7 +13,10 @@ #include +#include + #define DDDVB_MAX_DVB_FE 256 +#define DDDVB_MAX_DVB_CA 256 #define DDDVB_MAX_SOURCE 4 @@ -74,6 +77,7 @@ struct dddvb_fe { uint32_t quality; int64_t strength; int64_t cnr; + int64_t ber; int first; uint32_t tune; @@ -85,6 +89,44 @@ struct dddvb_fe { struct dddvb_status status; }; +struct dddvb_ca { + struct dddvb *dd; + struct osstrm *stream; + int fd; + int ci_rfd; + int ci_wfd; + uint32_t type; + int anum; + int fnum; + int state; + int nr; + int input; + + pthread_t pt; + pthread_t poll_pt; + + pthread_mutex_t mutex; + + struct en50221_transport_layer *tl; + struct en50221_session_layer *sl; + struct en50221_stdcam *stdcam; + int resource_ready; + int sentpmt; + int moveca; + int ca_pmt_version[MAX_PMT]; + int data_pmt_version; + + int setpmt; + uint32_t pmt[MAX_PMT]; + uint32_t pmt_new[MAX_PMT]; + uint32_t pmt_old[MAX_PMT]; + + int mmi_state; + uint8_t mmi_buf[16]; + int mmi_bufp; + int sock; +}; + struct dddvb { pthread_mutex_t lock; pthread_mutex_t uni_lock; @@ -103,7 +145,11 @@ struct dddvb { uint32_t dvbfe_num; uint32_t scif_type; + uint32_t dvbca_num; + int exit; + struct dddvb_fe dvbfe[DDDVB_MAX_DVB_FE]; + struct dddvb_ca dvbca[DDDVB_MAX_DVB_CA]; }; int dddvb_dvb_init(struct dddvb *dd); @@ -112,7 +158,7 @@ int parse_config(struct dddvb *dd, char *name, char *sec, void dddvb_fe_handle(struct dddvb_fe *fe); int dddvb_fe_tune(struct dddvb_fe *fe, struct dddvb_params *p); int dddvb_fe_start(struct dddvb_fe *fe); - +int scan_dvbca(struct dddvb *dd); #endif /* _DDDVB_H_ */ diff --git a/lib/src/dvb.c b/lib/src/dvb.c index be2d76c..5ff9863 100644 --- a/lib/src/dvb.c +++ b/lib/src/dvb.c @@ -533,6 +533,9 @@ static int open_fe(struct dddvb_fe *fe) return 0; } + +#include "dvb_quality.c" + static void get_stats(struct dddvb_fe *fe) { uint16_t sig = 0, snr = 0; @@ -545,7 +548,7 @@ static void get_stats(struct dddvb_fe *fe) ioctl(fe->fd, FE_READ_STATUS, &stat); fe->stat = stat; fe->lock = (stat == 0x1f) ? 1 : 0; - //calc_lq(fe); + calc_lq(fe); if (!get_stat(fe->fd, DTV_STAT_SIGNAL_STRENGTH, &st)) { fe->strength = str = st.stat[0].svalue; @@ -886,6 +889,7 @@ int dddvb_dvb_init(struct dddvb *dd) parse_config(dd, "", "scif", &scif_config); set_lnb(dd, 0, 0, 9750000, 10600000, 11700000); parse_config(dd, "", "LNB", &lnb_config); + scan_dvbca(dd); } diff --git a/lib/src/dvb_quality.c b/lib/src/dvb_quality.c new file mode 100644 index 0000000..ecedf93 --- /dev/null +++ b/lib/src/dvb_quality.c @@ -0,0 +1,388 @@ +static int32_t Log10x100(uint32_t x) +{ + static uint32_t LookupTable[100] = { + 101157945, 103514217, 105925373, 108392691, 110917482, + 113501082, 116144861, 118850223, 121618600, 124451461, // 800.5 - 809.5 + 127350308, 130316678, 133352143, 136458314, 139636836, + 142889396, 146217717, 149623566, 153108746, 156675107, // 810.5 - 819.5 + 160324539, 164058977, 167880402, 171790839, 175792361, + 179887092, 184077200, 188364909, 192752491, 197242274, // 820.5 - 829.5 + 201836636, 206538016, 211348904, 216271852, 221309471, + 226464431, 231739465, 237137371, 242661010, 248313311, // 830.5 - 839.5 + 254097271, 260015956, 266072506, 272270131, 278612117, + 285101827, 291742701, 298538262, 305492111, 312607937, // 840.5 - 849.5 + 319889511, 327340695, 334965439, 342767787, 350751874, + 358921935, 367282300, 375837404, 384591782, 393550075, // 850.5 - 859.5 + 402717034, 412097519, 421696503, 431519077, 441570447, + 451855944, 462381021, 473151259, 484172368, 495450191, // 860.5 - 869.5 + 506990708, 518800039, 530884444, 543250331, 555904257, + 568852931, 582103218, 595662144, 609536897, 623734835, // 870.5 - 879.5 + 638263486, 653130553, 668343918, 683911647, 699841996, + 716143410, 732824533, 749894209, 767361489, 785235635, // 880.5 - 889.5 + 803526122, 822242650, 841395142, 860993752, 881048873, + 901571138, 922571427, 944060876, 966050879, 988553095, // 890.5 - 899.5 + }; + int32_t y = 800; + int i = 0; + + if( x == 0 ) return 0; + + if( x >= 1000000000 ) { + x /= 10; + y += 100; + } + + while (x < 100000000 ) { + x *= 10; + y -= 100; + } + + while ( i < 100 && x > LookupTable[i] ) i += 1; + y += i; + return y; +} + +static int32_t berq_rs(uint32_t BERNumerator, uint32_t BERDenominator) +{ + int32_t LogBER = Log10x100(BERDenominator) - Log10x100(BERNumerator); + int32_t BERQual = 100; + + if ( BERNumerator == 0 ) + return 100; + if (LogBER < 700) { + if (LogBER < 300) + BERQual = 0; + else + BERQual = (LogBER + 5) / 5 - 40; + } + return BERQual; +} + +static int32_t berq_bch(uint32_t BERNumerator, uint32_t BERDenominator) +{ + int32_t LogBER = Log10x100(BERDenominator) - Log10x100(BERNumerator); + int32_t BERQual = 100; + + if (BERNumerator == 0 ) + return 100; + if (LogBER < 700) { + if( LogBER < 400 ) + BERQual = 0; + else + BERQual = 40; + } + return BERQual; +} + + +static uint32_t ber_quality(struct dddvb_fe *fe) +{ + struct dtv_fe_stats ber; + + get_stat(fe->fd, DTV_STAT_PRE_ERROR_BIT_COUNT, &ber); + get_stat(fe->fd, DTV_STAT_PRE_TOTAL_BIT_COUNT, &ber); + + return 100; +} + +static int32_t dvbsq(uint32_t snr, uint32_t fec, + uint32_t ber_num, uint32_t ber_den) +{ + int32_t SignalToNoiseRel = -1000; + int32_t Quality = 0; + int32_t BERQuality = berq_rs(ber_num, ber_den); + + // SNR Values for quasi errorfree reception from Nordig 2.2 + static const int32_t DVBS_SN[] = { + // 1/2 2/3 3/4 5/6 7/8 + 0, 38, 56, 67, 0, 77, 0, 84 + }; + + if (fec >= FEC_NONE && fec <= FEC_7_8 ) + SignalToNoiseRel = snr / 100 - DVBS_SN[fec]; + + if( SignalToNoiseRel < -70 ) + Quality = 0; + else if( SignalToNoiseRel < 30 ) + Quality = ((SignalToNoiseRel + 70) * BERQuality) / 100; + else + Quality = BERQuality; + return Quality; +} + + +int32_t dvbs2q(int32_t snr, uint32_t fec, uint32_t mod, + uint32_t ber_num, uint32_t ber_den) +{ + static const int32_t DVBS2_SN_QPSK[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 + 0, 20, 41, 50, 57, 62, 0, 0, 72, 0, 32, 74, 7, + }; + static const int32_t DVBS2_SN_8PSK[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 + 0, 0, 76, 89, 0, 104, 0, 0, 117, 0, 65, 120, 0, + }; + static const int32_t DVBS2_SN_16APSK[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 + 0, 0, 100, 112, 120, 126, 0, 0, 139, 0, 0, 141, 0, + }; + static const int32_t DVBS2_SN_32APSK[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 + 0, 0, 0, 137, 146, 153, 0, 0, 167, 0, 0, 171, 0, + }; + int32_t BERQuality = berq_bch(ber_num, ber_den); + int32_t Quality = 0; + int32_t SignalToNoiseRel = -1000, snc = 0; + + if (fec > FEC_2_5 ) + return 0; + switch (mod) { + case QPSK: + snc = DVBS2_SN_QPSK[fec]; + break; + case PSK_8: + snc = DVBS2_SN_8PSK[fec]; + break; + case APSK_16: + snc = DVBS2_SN_16APSK[fec]; + break; + case APSK_32: + snc = DVBS2_SN_32APSK[fec]; + break; + default: + return 0; + } + SignalToNoiseRel = snr / 100 - snc; + + if (SignalToNoiseRel < -30 ) + Quality = 0; + else if( SignalToNoiseRel < 30 ) + Quality = ((SignalToNoiseRel + 30) * BERQuality) / 60; + else + Quality = 100; + return Quality; +} + +static int32_t dvbcq(int32_t snr, uint32_t mod, + uint32_t ber_num, uint32_t ber_den) +{ + int32_t SignalToNoiseRel = 0; + int32_t Quality = 0; + int32_t BERQuality = berq_rs(ber_num, ber_den); + + switch (mod) { + case QAM_16: SignalToNoiseRel = snr - 200; break; + case QAM_32: SignalToNoiseRel = snr - 230; break; + case QAM_64: SignalToNoiseRel = snr - 260; break; + case QAM_128: SignalToNoiseRel = snr - 290; break; + case QAM_256: SignalToNoiseRel = snr - 320; break; + } + + if (SignalToNoiseRel < -70) + Quality = 0; + else if (SignalToNoiseRel < 30) + Quality = ((SignalToNoiseRel + 70) * BERQuality) / 100; + else + Quality = BERQuality; + return Quality; +} + +static int32_t dvbtq(int32_t snr, uint32_t mod, uint32_t fec, + uint32_t ber_num, uint32_t ber_den) +{ + int32_t Quality = 0; + int32_t BERQuality = berq_rs(ber_num, ber_den); + int32_t SignalToNoiseRel = -1000, snc = 0; + + // SNR Values for quasi error free reception from Nordig 2.2 + static const int32_t DVBT_SN_QPSK[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 + 0, 51, 69, 79, 0, 89, 0, 97, 0, 0, 0, 0, 0, + }; + static const int32_t DVBT_SN_QAM16[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 + 0, 108, 131, 146, 0, 156, 0, 160, 0, 0, 0, 0, 0, + }; + static const int32_t DVBT_SN_QAM64[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 + 0, 165, 187, 202, 0, 216, 0, 225, 0, 0, 0, 0, 0, + }; + + if (fec > FEC_2_5 ) + return 0; + switch (mod) { + case QPSK: + snc = DVBT_SN_QPSK[fec]; + break; + case QAM_16: + snc = DVBT_SN_QAM16[fec]; + break; + case QAM_64: + snc = DVBT_SN_QAM64[fec]; + break; + default: + break; + } + SignalToNoiseRel = snr / 100 - snc; + + if (SignalToNoiseRel < -70 ) + Quality = 0; + else if (SignalToNoiseRel < 30) + Quality = ((SignalToNoiseRel + 70) * BERQuality)/100; + else + Quality = BERQuality; + + return Quality; +} + +static int32_t dvbt2q(int32_t snr, uint32_t mod, uint32_t fec, uint32_t trans, uint32_t pilot, + uint32_t ber_num, uint32_t ber_den) +{ + int32_t Quality = 0; + int32_t BERQuality = berq_bch(ber_num, ber_den); + int32_t SignalToNoiseRel = -1000, snc = 0; + + static const int32_t QE_SN_16K_QPSK[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3 + 0, 32, 59, 68, 74, 80, 0, 0, 0, 0, 49, 0, 24, 0, 15 }; + static const int32_t QE_SN_16K_16QAM[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3 + 0, 82,116,130,136,141, 0, 0, 0, 0, 104, 0, 74, 0, 62 }; + static const int32_t QE_SN_16K_64QAM[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3 + 0,123,165,181,190,197, 0, 0, 0, 0, 151, 0,114, 0,101 }; + static const int32_t QE_SN_16K_256QAM[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3 + 0,164,211,232,246,255, 0, 0, 0, 0, 202, 0,153, 0,137 }; + static const int32_t QE_SN_64K_QPSK[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3 + 0, 35, 56, 66, 72, 77, 0, 0, 0, 0, 47, 0, 22, 0, 13 }; + static const int32_t QE_SN_64K_16QAM[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3 + 0, 87,114,125,133,138, 0, 0, 0, 0, 101, 0, 72, 0, 60 }; + static const int32_t QE_SN_64K_64QAM[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3 + 0,130,162,177,187,194, 0, 0, 0, 0, 148, 0,111, 0, 98 }; + static const int32_t QE_SN_64K_256QAM[] = { + // 1/2 2/3 3/4 4/5 5/6 6/7 7/8 8/9 AUT 3/5 9/10 2/5 1/4 1/3 + 0,170,208,229,243,251, 0, 0, 0, 0, 194, 0,148, 0,132 }; + + if (trans == TRANSMISSION_MODE_16K) { + switch (mod) { + case QPSK: + snc = QE_SN_16K_QPSK[fec]; + break; + case QAM_16: + snc = QE_SN_16K_16QAM[fec]; + break; + case QAM_64: + snc = QE_SN_16K_64QAM[fec]; + break; + case QAM_256: + snc = QE_SN_16K_256QAM[fec]; + break; + default: + break; + } + } + if (trans == TRANSMISSION_MODE_C3780 + 1) { /* TRANSMISSION_MODE_64K */ + switch (mod) { + case QPSK: + snc = QE_SN_64K_QPSK[fec]; + break; + case QAM_16: + snc = QE_SN_64K_16QAM[fec]; + break; + case QAM_64: + snc = QE_SN_64K_64QAM[fec]; + break; + case QAM_256: + snc = QE_SN_64K_256QAM[fec]; + break; + default: + break; + } + } + + if (snc) { + SignalToNoiseRel = snr - snc; +#if 0 //FIXME + if (PilotPattern >= DVBT2_PP3 && + PilotPattern <= DVBT2_PP4 ) + SignalToNoiseRel += 5; + else if + ( PilotPattern >= DVBT2_PP5 && PilotPattern <= DVBT2_PP8 ) + SignalToNoiseRel += 10; +#endif + } + if( SignalToNoiseRel < -30 ) + Quality = 0; + else if (SignalToNoiseRel < 30 ) + Quality = ((SignalToNoiseRel + 30) * BERQuality)/60; + else + Quality = 100; + + return Quality; +} + +static void calc_lq(struct dddvb_fe *fe) +{ + struct dtv_fe_stats st; + int64_t str, snr; + uint32_t mod, fec, ber_num, ber_den, trans, pilot = 0, quality = 0; + + get_stat(fe->fd, DTV_STAT_SIGNAL_STRENGTH, &st); + str = st.stat[0].svalue; + dbgprintf(DEBUG_DVB, "fe%d: str=%lld\n", fe->nr, str); + fe->strength = str; + str = (str * 48) / 10000 + 344; + if (str < 0) + str = 0; + if (str > 255) + str = 255; + fe->level = str; + // str: 0-255: -25dbm = 224, -65dbm = 32 + // qual: 0-15 15=BER<2*10^-4 PER<10^-7 + get_stat(fe->fd, DTV_STAT_CNR, &st); + snr = st.stat[0].svalue; + fe->cnr = snr; + get_property(fe->fd, DTV_INNER_FEC, &fec); + fe->param.param[PARAM_FEC] = fec; + get_property(fe->fd, DTV_MODULATION, &mod); + fe->param.param[PARAM_MTYPE] = mod; + + get_stat(fe->fd, DTV_STAT_PRE_ERROR_BIT_COUNT, &st); + ber_num = st.stat[0].uvalue; + get_stat(fe->fd, DTV_STAT_PRE_TOTAL_BIT_COUNT, &st); + ber_den = st.stat[0].uvalue; + + dbgprintf(DEBUG_DVB, "fe%d: snr=%lld ber=%llu/%llu\n", + fe->nr, snr, ber_num, ber_den); + dbgprintf(DEBUG_DVB, "fe%d: fec=%u mod=%u\n", fe->nr, fec, mod); + switch (fe->n_param.param[PARAM_MSYS]) { + case SYS_DVBS: + quality = dvbsq(snr, fec, ber_num, ber_den); + break; + case SYS_DVBS2: + quality = dvbs2q(snr, fec, mod, ber_num, ber_den); + break; + case SYS_DVBC_ANNEX_A: + quality = dvbcq(snr, mod, ber_num, ber_den); + break; + case SYS_DVBT: + quality = dvbtq(snr, mod, fec, ber_num, ber_den); + break; + case SYS_DVBT2: + get_property(fe->fd, DTV_TRANSMISSION_MODE, &trans); + dbgprintf(DEBUG_DVB, "fe%d: trans=%u pilot=%u\n", fe->nr, trans, pilot); + quality = dvbt2q(snr, mod, fec, trans, pilot, ber_num, ber_den); + break; + case SYS_DVBC2: + break; + default: + break; + } + fe->quality = quality; + dbgprintf(DEBUG_DVB, "fe%d: level=%u quality=%u\n", fe->nr, fe->level, fe->quality); +} + diff --git a/lib/src/libdddvb.h b/lib/src/libdddvb.h index dd9e4c4..ffe8a6b 100644 --- a/lib/src/libdddvb.h +++ b/lib/src/libdddvb.h @@ -60,6 +60,10 @@ LIBDDDVB_EXPORTED struct dddvb *dddvb_init(char *config, uint32_t flags); LIBDDDVB_EXPORTED int dddvb_dvb_tune(struct dddvb_fe *fe, struct dddvb_params *p); LIBDDDVB_EXPORTED struct dddvb_fe *dddvb_fe_alloc(struct dddvb *dd, uint32_t type); LIBDDDVB_EXPORTED struct dddvb_fe *dddvb_fe_alloc_num(struct dddvb *dd, uint32_t type, uint32_t num); +LIBDDDVB_EXPORTED int dddvb_ca_write(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len); +LIBDDDVB_EXPORTED int dddvb_ca_read(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len); +LIBDDDVB_EXPORTED int dddvb_ca_set_pmts(struct dddvb *dd, uint32_t nr, uint8_t **pmts); + static inline void dddvb_set_frequency(struct dddvb_params *p, uint32_t freq) { p->param[PARAM_FREQ] = freq; @@ -109,6 +113,14 @@ static inline int64_t dddvb_get_cnr(struct dddvb_fe *fe) { return fe->cnr; }; +static inline int64_t dddvb_get_ber(struct dddvb_fe *fe) { + return fe->ber; +}; + +static inline uint32_t dddvb_get_quality(struct dddvb_fe *fe) { + return fe->quality; +}; + static inline void dddvb_param_init(struct dddvb_params *p) { int i; @@ -116,4 +128,10 @@ static inline void dddvb_param_init(struct dddvb_params *p) { p->param[i] = DDDVB_UNDEF; }; +#if 0 +static inline int dddvb_ca_write(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len) { + return ca_write(dd, nr, buf, len); +}; +#endif + #endif /* _LIBDDDVB_H_ */