diff --git a/Makefile b/Makefile index a631a57..23b2c4a 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,9 @@ libdddvb: libdddvb-install: $(MAKE) -C lib install +libdddvb-clean: + $(MAKE) -C lib clean + dep: DIR=`pwd`; (cd $(TOPDIR); make SUBDIRS=$$DIR dep) diff --git a/apps/cit.c b/apps/cit.c index 8e93ea9..9b55109 100644 --- a/apps/cit.c +++ b/apps/cit.c @@ -27,6 +27,7 @@ uint8_t fill[188]={0x47, 0x1f, 0xff, 0x10, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }; +#if 0 uint8_t ts[188]={0x47, 0x0a, 0xaa, 0x00, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, @@ -40,6 +41,21 @@ uint8_t ts[188]={0x47, 0x0a, 0xaa, 0x00, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }; +#else +uint8_t ts[188]={0x47, 0x0a, 0xaa, 0x00, + 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, + 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, + 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, + 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, + 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, + 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, + 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, + 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, + 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, + 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, + 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, + 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa }; +#endif void proc_buf(uint8_t *buf, uint32_t *d) { diff --git a/apps/flashprog.c b/apps/flashprog.c index a770b6c..399df7e 100644 --- a/apps/flashprog.c +++ b/apps/flashprog.c @@ -238,7 +238,7 @@ int main(int argc, char **argv) printf("Octopus MAXM4\n"); break; case 0x0011: - fname="CIBridgeV1B_CIBridgeV1B.fpga"; + fname="DVBBridgeV2B_DD01_0011.fpga"; printf("Octopus CI\n"); break; case 0x0012: @@ -250,7 +250,7 @@ int main(int argc, char **argv) printf("Octopus PRO\n"); break; case 0x0020: - fname="DVBBridgeV2B_DD01_0020.fpga"; + fname="DVBBridgeV2C_DD01_0020.fpga"; printf("Octopus GT Mini\n"); break; case 0x0201: @@ -273,6 +273,10 @@ int main(int argc, char **argv) fname="SDRModulatorV1A_DD01_0221_IQ.fpga"; printf("SDRModulator IQ\n"); break; + case 0x0222: + fname="SDRModulatorV1A_DD01_0222_DVBT.fpga"; + printf("SDRModulator DVBT\n"); + break; default: printf("UNKNOWN\n"); break; @@ -285,7 +289,7 @@ int main(int argc, char **argv) printf("Using bitstream %s\n", fname); fsize = lseek(fh,0,SEEK_END); - if( fsize > 4000000 || fsize < SectorSize ) + if( fsize > FlashSize/2 - 0x10000 || fsize < SectorSize ) { close(fh); printf("Invalid File Size \n"); diff --git a/apps/setmod3.c b/apps/setmod3.c index 302cb09..83c10f8 100644 --- a/apps/setmod3.c +++ b/apps/setmod3.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -51,33 +52,96 @@ static int get_property(int fd, uint32_t cmd, uint32_t *data) -int main() +int main(int argc, char*argv[]) { int fd; struct dvb_mod_params mp; struct dvb_mod_channel_params mc; uint32_t data; + int adapter = 0, channel = 0, gain = -1; + int32_t base = -1, freq = -1, rate = -1; + char mod_name[128]; - fd = open("/dev/dvb/adapter0/mod0", O_RDONLY); + while (1) { + int cur_optind = optind ? optind : 1; + int option_index = 0; + int c; + static struct option long_options[] = { + {"adapter", required_argument, 0, 'a'}, + {"channel", required_argument, 0, 'c'}, + {"gain", required_argument, 0, 'g'}, + {"base", required_argument, 0, 'b'}, + {"frequency", required_argument, 0, 'f'}, + {"rate", required_argument, 0, 'r'}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, + "a:c:g:b:f:r:", + long_options, &option_index); + if (c==-1) + break; + switch (c) { + case 'a': + adapter = strtoul(optarg, NULL, 0); + break; + case 'c': + channel = strtoul(optarg, NULL, 0); + break; + case 'g': + gain = strtoul(optarg, NULL, 0); + break; + case 'b': + base = strtoul(optarg, NULL, 0); + break; + case 'f': + freq = strtoul(optarg, NULL, 0); + break; + case 'r': + if (!strcmp(optarg, "DVBT_8")) + rate = SYS_DVBT_8; + else if (!strcmp(optarg, "DVBT_7")) + rate = SYS_DVBT_7; + else if (!strcmp(optarg, "DVBT_6")) + rate = SYS_DVBT_6; + else if (!strcmp(optarg, "ISDBT_6")) + rate = SYS_ISDBT_6; + else rate = strtoul(optarg, NULL, 0); + break; + default: + break; + } + } + if (optind < argc) { + printf("too man arguments\n"); + exit(1); + } + + snprintf(mod_name, 127, "/dev/dvb/adapter%d/mod%d", adapter, channel); + fd = open(mod_name, O_RDONLY); + + if (fd < 0) { + printf("Could not open modulator device.\n"); + exit(1); + } + + /* gain 0-255 */ - get_property(fd, MODULATOR_GAIN, &data); - printf("Modulator gain = %u\n", data); - set_property(fd, MODULATOR_GAIN, 100); + //get_property(fd, MODULATOR_GAIN, &data); + //printf("Modulator gain = %u\n", data); + //set_property(fd, MODULATOR_GAIN, 100); - get_property(fd, MODULATOR_GAIN, &data); - printf("Modulator gain = %u\n", data); + //get_property(fd, MODULATOR_ATTENUATOR, &data); + //printf("Modulator attenuator = %u\n", data); - get_property(fd, MODULATOR_ATTENUATOR, &data); - printf("Modulator attenuator = %u\n", data); - - - get_property(fd, MODULATOR_STATUS, &data); - printf("Modulator status = %u\n", data); - set_property(fd, MODULATOR_STATUS, 2); + if (base > 0) + set_property(fd, MODULATOR_BASE_FREQUENCY, base); + if (freq > 0) + set_property(fd, MODULATOR_FREQUENCY, base); + if (rate > 0) + set_property(fd, MODULATOR_OUTPUT_RATE, rate); - //set_property(fd, MODULATOR_RESET, 0); close(fd); } diff --git a/ddbridge/ddbridge-ci.c b/ddbridge/ddbridge-ci.c index d1ab8b9..160bbea 100644 --- a/ddbridge/ddbridge-ci.c +++ b/ddbridge/ddbridge-ci.c @@ -1,7 +1,7 @@ /* * ddbridge-ci.c: Digital Devices bridge and DuoFlex CI driver * - * Copyright (C) 2010-2017 Digital Devices GmbH + * Copyright (C) 2010-2019 Digital Devices GmbH * Ralph Metzler * Marcus Metzler * diff --git a/ddbridge/ddbridge-core.c b/ddbridge/ddbridge-core.c index ec49468..d04e286 100644 --- a/ddbridge/ddbridge-core.c +++ b/ddbridge/ddbridge-core.c @@ -1,7 +1,7 @@ /* * ddbridge-core.c: Digital Devices bridge core functions * - * Copyright (C) 2010-2016 Digital Devices GmbH + * Copyright (C) 2010-2019 Digital Devices GmbH * Marcus Metzler * Ralph Metzler * @@ -1105,7 +1105,7 @@ static void dummy_release(struct dvb_frontend *fe) } static struct dvb_frontend_ops dummy_ops = { - .delsys = { SYS_DVBC_ANNEX_A }, + .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBS, SYS_DVBS2 }, .info = { .name = "DUMMY DVB-C/C2 DVB-T/T2", .frequency_stepsize = 166667, /* DVB-T only */ @@ -1399,7 +1399,7 @@ static struct stv0910_cfg stv0910_p = { .parallel = 1, .rptlvl = 4, .clk = 30000000, - .tsspeed = 0x10, + .tsspeed = 0x20, }; static int has_lnbh25(struct i2c_adapter *i2c, u8 adr) @@ -1524,6 +1524,7 @@ static void dvb_input_detach(struct ddb_input *input) case 0x41: if (dvb->fe2) dvb_unregister_frontend(dvb->fe2); + /* fallthrough */ case 0x40: if (dvb->fe) dvb_unregister_frontend(dvb->fe); @@ -1843,8 +1844,8 @@ static int dvb_input_attach(struct ddb_input *input) memcpy(&dvb->fe2->ops.tuner_ops, &dvb->fe->ops.tuner_ops, sizeof(struct dvb_tuner_ops)); + dvb->attached = 0x41; } - dvb->attached = 0x41; return 0; } @@ -2071,7 +2072,8 @@ static void ddb_port_probe(struct ddb_port *port) /* Handle missing ports and ports without I2C */ if (dummy_tuner && !port->nr && - link->ids.device == 0x0005) { + (link->ids.device == 0x0005 || + link->ids.device == 0x000a)) { port->name = "DUMMY"; port->class = DDB_PORT_TUNER; port->type = DDB_TUNER_DUMMY; @@ -2561,7 +2563,7 @@ static void ddb_dma_init(struct ddb_io *io, int nr, int out, int irq_nr) dma->regs = rm->odma->base + rm->odma->size * nr; dma->bufregs = rm->odma_buf->base + rm->odma_buf->size * nr; if (io->port->dev->link[0].info->type == DDB_MOD && - io->port->dev->link[0].info->version == 3) { + io->port->dev->link[0].info->version >= 16) { dma->num = OUTPUT_DMA_BUFS_SDR; dma->size = OUTPUT_DMA_SIZE_SDR; dma->div = 1; @@ -3496,7 +3498,7 @@ static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (copy_from_user(&i2c, parg, sizeof(i2c))) return -EFAULT; - if (i2c.bus > dev->link[0].info->regmap->i2c->num) + if (i2c.bus > dev->i2c_num) return -EINVAL; if (i2c.mlen + i2c.hlen > 512) return -EINVAL; @@ -3520,7 +3522,7 @@ static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (copy_from_user(&i2c, parg, sizeof(i2c))) return -EFAULT; - if (i2c.bus > dev->link[0].info->regmap->i2c->num) + if (i2c.bus > dev->i2c_num) return -EINVAL; if (i2c.mlen + i2c.hlen > 250) return -EINVAL; @@ -3876,6 +3878,24 @@ static ssize_t bpsnr_show(struct device *device, return sprintf(buf, "%s\n", snr); } +static ssize_t gtl_snr_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct ddb *dev = dev_get_drvdata(device); + int num = attr->attr.name[6] - 0x30; + char snr[16]; + + ddbridge_flashread(dev, num, snr, 0x10, 15); + snr[15] = 0; /* in case it is not terminated on EEPROM */ + return sprintf(buf, "%s\n", snr); +} + +static ssize_t gtl_snr_store(struct device *device, struct device_attribute *attr, + const char *buf, size_t count) +{ + return 0; +} + static ssize_t redirect_show(struct device *device, struct device_attribute *attr, char *buf) { @@ -4121,6 +4141,12 @@ static struct device_attribute ddb_attrs_snr[] = { __ATTR(snr3, 0664, snr_show, snr_store), }; +static struct device_attribute ddb_attrs_gtl_snr[] = { + __ATTR(gtlsnr1, 0664, gtl_snr_show, gtl_snr_store), + __ATTR(gtlsnr2, 0664, gtl_snr_show, gtl_snr_store), + __ATTR(gtlsnr3, 0664, gtl_snr_show, gtl_snr_store), +}; + static struct device_attribute ddb_attrs_ctemp[] = { __ATTR_MRO(temp0, ctemp_show), __ATTR_MRO(temp1, ctemp_show), @@ -4191,6 +4217,9 @@ static void ddb_device_attrs_del(struct ddb *dev) device_remove_file(dev->ddb_dev, &ddb_attrs_snr[i]); device_remove_file(dev->ddb_dev, &ddb_attrs_ctemp[i]); } + if (dev->link[0].info->regmap->gtl) + for (i = 0; i < dev->link[0].info->regmap->gtl->num; i++) + device_remove_file(dev->ddb_dev, &ddb_attrs_gtl_snr[i]); for (i = 0; ddb_attrs[i].attr.name; i++) device_remove_file(dev->ddb_dev, &ddb_attrs[i]); } @@ -4223,6 +4252,10 @@ static int ddb_device_attrs_add(struct ddb *dev) &ddb_attrs_led[i])) goto fail; } + if (dev->link[0].info->regmap->gtl) + for (i = 0; i < dev->link[0].info->regmap->gtl->num; i++) + if (device_create_file(dev->ddb_dev, &ddb_attrs_gtl_snr[i])) + goto fail; for (i = 0; i < 4; i++) if (dev->link[i].info && dev->link[i].info->tempmon_irq) @@ -4439,7 +4472,7 @@ static void tempmon_setfan(struct ddb_link *link) while (pwm < 10 && temp >= link->temp_tab[pwm + 1]) pwm += 1; } else { - while (pwm > 1 && temp < link->temp_tab[pwm - 2]) + while (pwm > 1 && temp < (link->temp_tab[pwm] - 2)) pwm -= 1; } ddblwritel(link, (pwm << 8), TEMPMON_FANCONTROL); diff --git a/ddbridge/ddbridge-hw.c b/ddbridge/ddbridge-hw.c index 6e5f843..02a1550 100644 --- a/ddbridge/ddbridge-hw.c +++ b/ddbridge/ddbridge-hw.c @@ -1,7 +1,7 @@ /* * ddbridge-hw.c: Digital Devices device information tables * - * Copyright (C) 2010-2017 Digital Devices GmbH + * Copyright (C) 2010-2019 Digital Devices GmbH * Ralph Metzler * Marcus Metzler * @@ -537,6 +537,16 @@ static const struct ddb_info ddb_sdr_iq = { .tempmon_irq = 8, }; +static const struct ddb_info ddb_sdr_dvbt = { + .type = DDB_MOD, + .name = "Digital Devices DVBT", + .version = 18, + .regmap = &octopus_sdr_map, + .port_num = 16, + .temp_num = 1, + .tempmon_irq = 8, +}; + static const struct ddb_info ddb_octopro_hdin = { .type = DDB_OCTOPRO_HDIN, .name = "Digital Devices OctopusNet Pro HDIN", @@ -789,7 +799,7 @@ static const struct ddb_device_id ddb_device_ids[] = { DDB_DEVID(0x0210, 0x0003, ddb_mod_fsm_8), DDB_DEVID(0x0220, 0x0001, ddb_sdr_atv), DDB_DEVID(0x0221, 0x0001, ddb_sdr_iq), - DDB_DEVID(0x0222, 0x0001, ddb_sdr_iq), + DDB_DEVID(0x0222, 0x0001, ddb_sdr_dvbt), /* testing on OctopusNet Pro */ DDB_DEVID(0x0320, 0xffff, ddb_octopro_hdin), diff --git a/ddbridge/ddbridge-i2c.c b/ddbridge/ddbridge-i2c.c index 331d4c9..126c52a 100644 --- a/ddbridge/ddbridge-i2c.c +++ b/ddbridge/ddbridge-i2c.c @@ -1,7 +1,7 @@ /* * ddbridge-i2c.c: Digital Devices bridge i2c driver * - * Copyright (C) 2010-2017 Digital Devices GmbH + * Copyright (C) 2010-2019 Digital Devices GmbH * Ralph Metzler * Marcus Metzler * @@ -65,7 +65,7 @@ static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) } val &= 0x70000; if (val == 0x20000) - dev_err(dev->dev, "I2C bus errorx\n"); + dev_err(dev->dev, "I2C bus error\n"); if (val) return -EIO; return 0; diff --git a/ddbridge/ddbridge-i2c.h b/ddbridge/ddbridge-i2c.h index c6495ed..11cf68d 100644 --- a/ddbridge/ddbridge-i2c.h +++ b/ddbridge/ddbridge-i2c.h @@ -1,7 +1,7 @@ /* * ddbridge-i2c.h: Digital Devices bridge i2c driver * - * Copyright (C) 2010-2017 Digital Devices GmbH + * Copyright (C) 2010-2019 Digital Devices GmbH * Marcus Metzler * Ralph Metzler * diff --git a/ddbridge/ddbridge-mci.c b/ddbridge/ddbridge-mci.c index dec7861..1322eb9 100644 --- a/ddbridge/ddbridge-mci.c +++ b/ddbridge/ddbridge-mci.c @@ -83,7 +83,17 @@ static int ddb_mci_cmd_raw_unlocked(struct mci *state, stat = wait_for_completion_timeout(&state->base->completion, HZ); if (stat == 0) { + u32 istat = ddblreadl(link, INTERRUPT_STATUS); + printk("MCI timeout\n"); + val = ddblreadl(link, MCI_CONTROL); + if (val == 0xffffffff) + printk("Lost PCIe link!\n"); + else { + printk("DDBridge IRS %08x\n", istat); + if (istat & 1) + ddblwritel(link, istat & 1, INTERRUPT_ACK); + } return -EIO; } if (res && res_len) diff --git a/ddbridge/ddbridge-mci.h b/ddbridge/ddbridge-mci.h index 8e56cb7..2c0d274 100644 --- a/ddbridge/ddbridge-mci.h +++ b/ddbridge/ddbridge-mci.h @@ -114,7 +114,7 @@ #define MCI_CMD_STOP (0x01) #define MCI_CMD_GETSTATUS (0x02) #define MCI_CMD_GETSIGNALINFO (0x03) -#define MCI_CMD_RFPOWER (0x04) +//#define MCI_CMD_RFPOWER (0x04) #define MCI_CMD_SEARCH_DVBS (0x10) #define MCI_CMD_SEARCH_ISDBS (0x11) @@ -147,7 +147,8 @@ #define M4_CMD_GET_L1INFO (0x50) #define M4_CMD_GET_IDS (0x51) #define M4_CMD_GET_DVBT_TPS (0x52) -#define M4_CMD_GET_BBHEADER (0x53) +#define MCI_CMD_GET_BBHEADER (0x53) +#define M4_CMD_GET_BBHEADER (MCI_CMD_GET_BBHEADER) #define M4_CMD_GET_ISDBT_TMCC (0x54) #define M4_CMD_GET_ISDBS_TMCC (0x55) #define M4_CMD_GET_ISDBC_TSMF (0x56) @@ -179,6 +180,12 @@ #define M4_SIGNALINFO_FLAG_CHANGE (0x01) #define M4_SIGNALINFO_FLAG_EWS (0x02) +#define SX8_ROLLOFF_25 1 +#define SX8_ROLLOFF_20 2 +#define SX8_ROLLOFF_15 5 +#define SX8_ROLLOFF_10 3 +#define SX8_ROLLOFF_05 4 + #define MCI_SUCCESS(status) ((status & MCI_STATUS_UNSUPPORTED) == 0) @@ -286,27 +293,38 @@ struct mci_command { u8 retry; u32 frequency; } j83b_search; - + + struct { + u8 flags; // Bit 0 : 1 = short info (1st 4 Bytes) + } get_signalinfo; + struct { u8 tap; u8 rsvd; u16 point; } get_iq_symbol; + struct { - u8 flags; /* Bit 0 : 0 = VTM, 1 = SCAN. Bit 1: Set Gain */ - u8 roll_off; - u8 rsvd1; - u8 rsvd2; - u32 frequency; - u32 symbol_rate; /* Only in VTM mode. */ - u16 gain; + uint8_t flags; /* Bit 0 : 0 = VTM/SDR, 1 = SCAN, Bit 1: Disable AGC, Bit 2: Set Gain */ + uint8_t roll_off; + uint8_t rsvd1; + uint8_t rsvd2; + uint32_t frequency; + uint32_t symbol_rate; /* Only in VTM/SDR mode, SCAN Mode uses exactly 1550/24 MSymbols/s.*/ + uint8_t gain; /* Gain in 0.25 dB Steps */ + /* Frequency, symbolrate and gain can be schanged while running */ } sx8_start_iq; struct { - /* Bit 1:0 = STVVGLNA Gain. 0 = AGC, 1 = 0dB, - 2 = Minimum, 3 = Maximum */ - u8 flags; + uint8_t flags; + /* Bit 0:1 Preamp Mode; 0 = Preamp AGC, 1 == Minimum (~ -17dB) , + 2 = Medium, 3 = Maximum gain {~ 15dB} + Bit 2: Bypass Input LNA (6 dB less gain) (Note this is after Preamp) + Bit 4: Set RF Gain + Bit 5: Freeze RF Gain (Turn AGC off at current gain, only when already enabled) + Bit 7: Optimize RF Gain and freeze for FFT */ + uint8_t rf_gain; /* 0 .. 50 dB */ } sx8_input_enable; struct { @@ -357,8 +375,8 @@ struct mci_result { struct { u8 standard; /* 1 = DVB-S, 2 = DVB-S2X */ - u8 pls_code; /* puncture rate for DVB-S */ - u8 roll_off; /* 2-0: rolloff */ + u8 pls_code; /* PLS code for DVB-S2/S2X, puncture rate for DVB-S */ + u8 roll_off; /* 2-0: rolloff, 7: spectrum inversion */ u8 flags; u32 frequency; /* actual frequency in Hz */ u32 symbol_rate; /* actual symbolrate in Hz */ @@ -391,8 +409,6 @@ struct mci_result { u8 modulation2; // bit 7..5: CodeRate Low, bit 4..3 Guard Interval, bit 2..1 FFT Mode u8 Rsvd0; u8 Flags; - //u16 tps_cell_id; /* Cell Identifier */ - u32 frequency; /* actual frequency in Hz */ u32 rsvd1; s16 channel_power; /* channel power in dBm x 100 */ @@ -492,6 +508,7 @@ struct mci_result { struct { u8 TPSInfo[7]; + // uint16_t TPS_CellID; // Cell Identifier } DVBT_TPSInfo; struct { @@ -620,6 +637,8 @@ struct mci_result { u8 SYNCD[2]; u8 rsvd; u8 ISSY[3]; + u8 min_input_stream_id; + u8 max_input_stream_id; } BBHeader; struct { @@ -644,7 +663,11 @@ struct mci_result { u8 Extension[8]; // 61 bits, right aligned } ISDBS_TMCCInfo; }; - u32 version[4]; + u32 version[3]; + u32 version_rsvd; + u8 version_major; + u8 version_minor; + u8 version_sub; }; diff --git a/ddbridge/ddbridge-modulator.c b/ddbridge/ddbridge-modulator.c index 4106cf8..dd19996 100644 --- a/ddbridge/ddbridge-modulator.c +++ b/ddbridge/ddbridge-modulator.c @@ -1477,12 +1477,46 @@ static int mod3_set_ari(struct ddb_mod *mod, u32 rate) } +static int mod3_set_sample_rate(struct ddb_mod *mod, u32 rate) +{ + u32 cic, inc; + + switch (rate) { + case SYS_DVBT_6: + inc = 1917396114; + cic = 8; + break; + case SYS_DVBT_7: + inc = 1957341867; + cic = 7; + break; + case SYS_DVBT_8: + //rate = 8126984; + inc = 1917396114; + cic = 6; + break; + case SYS_ISDBT_6: + inc = 1988410754; + cic = 7; + break; + default: + return -EINVAL; + } + ddbwritel(mod->port->dev, inc, SDR_CHANNEL_ARICW(mod->port->nr)); + ddbwritel(mod->port->dev, cic << 8, SDR_CHANNEL_CONFIG(mod->port->nr)); + return 0; +} + + static int mod3_prop_proc(struct ddb_mod *mod, struct dtv_property *tvp) { switch (tvp->cmd) { case MODULATOR_OUTPUT_ARI: return mod3_set_ari(mod, tvp->u.data); + case MODULATOR_OUTPUT_RATE: + return mod3_set_sample_rate(mod, tvp->u.data); + case MODULATOR_FREQUENCY: return mod3_set_frequency(mod, tvp->u.data); @@ -1542,15 +1576,37 @@ static int mod_prop_proc(struct ddb_mod *mod, struct dtv_property *tvp) return 0; } +static int mod_prop_get3(struct ddb_mod *mod, struct dtv_property *tvp) +{ + struct ddb *dev = mod->port->dev; + + switch (tvp->cmd) { + case MODULATOR_INFO: + tvp->u.data = dev->link[0].info->version; + return 0; + case MODULATOR_GAIN: + tvp->u.data = 0xff & ddbreadl(dev, RF_VGA); + return 0; + default: + return -1; + } +} + static int mod_prop_get(struct ddb_mod *mod, struct dtv_property *tvp) { struct ddb *dev = mod->port->dev; + if (mod->port->dev->link[0].info->version >= 16) + return mod_prop_get3(mod, tvp); if (mod->port->dev->link[0].info->version != 2) return -1; switch (tvp->cmd) { + case MODULATOR_INFO: + tvp->u.data = 2; + return 0; + case MODULATOR_GAIN: - tvp->u.data = 0xff & ddbreadl(dev, RF_VGA);; + tvp->u.data = 0xff & ddbreadl(dev, RF_VGA); return 0; case MODULATOR_ATTENUATOR: @@ -1897,6 +1953,8 @@ int ddbridge_mod_init(struct ddb *dev) return mod_init_3(dev, 503250000); case 17: return mod_init_sdr_iq(dev); + case 18: + return mod_init_sdr_iq(dev); default: return -1; } diff --git a/ddbridge/ddbridge-ns.c b/ddbridge/ddbridge-ns.c index bde6463..8a48022 100644 --- a/ddbridge/ddbridge-ns.c +++ b/ddbridge/ddbridge-ns.c @@ -44,7 +44,7 @@ static u16 calc_pcs16(struct dvb_ns_params *p, int ipv) u32 sum = 0, i; u16 pcs; - for (i = 0; i < ipv ? 16 : 4; i += 2) { + for (i = 0; i < (ipv ? 16 : 4); i += 2) { sum += (p->sip[i] << 8) | p->sip[i + 1]; sum += (p->dip[i] << 8) | p->dip[i + 1]; } diff --git a/ddbridge/ddbridge-sx8.c b/ddbridge/ddbridge-sx8.c index cb78cf4..d4a5626 100644 --- a/ddbridge/ddbridge-sx8.c +++ b/ddbridge/ddbridge-sx8.c @@ -26,8 +26,14 @@ #include "ddbridge-i2c.h" #include "ddbridge-mci.h" +static int default_mod = 3; +module_param(default_mod, int, 0444); +MODULE_PARM_DESC(default_mod, "default modulations enabled, default is 3 (1 = QPSK, 2 = 8PSK, 4 = 16APSK, ...)"); + static const u32 MCLK = (1550000000 / 12); -static const u32 MAX_LDPC_BITRATE = (720000000); + +/* Add 2MBit/s overhead allowance (minimum factor is 90/32400 for QPSK w/o Pilots) */ +static const u32 MAX_LDPC_BITRATE = (720000000 + 2000000); static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); #define SX8_TUNER_NUM 4 @@ -52,6 +58,7 @@ struct sx8 { int first_time_lock; int started; + int iq_started; u32 bb_mode; u32 local_frequency; @@ -131,6 +138,7 @@ static int read_status(struct dvb_frontend *fe, enum fe_status *status) dvbs2_bits_per_symbol[ state->mci.signal_info. dvbs2_signal_info.pls_code]; + //printk("PLS %02x\n", state->mci.signal_info.dvbs2_signal_info.pls_code); } else sx8_base->used_ldpc_bitrate[state->mci.nr] = 0; } @@ -151,6 +159,25 @@ static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) return ddb_mci_cmd(&state->mci, &cmd, NULL); } +static int stop_iq(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *) mci_base; + struct mci_command cmd; + u32 input = state->mci.tuner; + + if (!state->iq_started) + return -1; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = SX8_CMD_STOP_IQ; + cmd.demod = state->mci.demod; + ddb_mci_cmd(&state->mci, &cmd, NULL); + ddb_mci_config(&state->mci, SX8_TSCONFIG_MODE_NORMAL); + state->iq_started = 0; + return 0; +} + static int stop(struct dvb_frontend *fe) { struct sx8 *state = fe->demodulator_priv; @@ -285,13 +312,13 @@ unlock: if (sx8_base->iq_mode) { cmd.command = SX8_CMD_ENABLE_IQOUTPUT; cmd.demod = state->mci.demod; - cmd.output = 0; + cmd.output = p->stream_id & 7; ddb_mci_cmd(&state->mci, &cmd, NULL); ddb_mci_config(&state->mci, ts_config); } - if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) + if (p->stream_id != NO_STREAM_ID_FILTER && !(p->stream_id & 0xf0000000)) flags |= 0x80; - printk("frontend %u: tuner=%u demod=%u\n", state->mci.nr, state->mci.tuner, state->mci.demod); + //printk("frontend %u: tuner=%u demod=%u\n", state->mci.nr, state->mci.tuner, state->mci.demod); cmd.command = MCI_CMD_SEARCH_DVBS; cmd.dvbs2_search.flags = flags; cmd.dvbs2_search.s2_modulation_mask = modmask; @@ -357,7 +384,7 @@ unlock: cmd.demod = state->mci.demod; stat = ddb_mci_cmd(&state->mci, &cmd, NULL); if (stat) - stop(fe); + stop_iq(fe); ddb_mci_config(&state->mci, ts_config); return stat; } @@ -369,16 +396,18 @@ static int set_parameters(struct dvb_frontend *fe) struct dtv_frontend_properties *p = &fe->dtv_property_cache; u32 ts_config = SX8_TSCONFIG_MODE_NORMAL, iq_mode = 0, isi; - stop(fe); + isi = p->stream_id; if (isi != NO_STREAM_ID_FILTER) { iq_mode = (isi & 0x30000000) >> 28; } if (iq_mode) ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); + stop(fe); if (iq_mode < 2) { u32 mask; + stop_iq(fe); switch (p->modulation) { case APSK_256: mask = 0x7f; @@ -396,17 +425,22 @@ static int set_parameters(struct dvb_frontend *fe) mask = 0x07; break; default: - mask = 0x03; + mask = default_mod; break; } stat = start(fe, 3, mask, ts_config); + if (!stat) { + state->started = 1; + state->first_time_lock = 1; + state->mci.signal_info.status = MCI_DEMOD_WAIT_SIGNAL; + } } else { stat = start_iq(fe, iq_mode & 1, 4, ts_config); - } - if (!stat) { - state->started = 1; - state->first_time_lock = 1; - state->mci.signal_info.status = MCI_DEMOD_WAIT_SIGNAL; + if (!stat) { + state->iq_started = 1; + state->first_time_lock = 1; + state->mci.signal_info.status = MCI_DEMOD_WAIT_SIGNAL; + } } return stat; } @@ -446,6 +480,7 @@ static int set_input(struct dvb_frontend *fe, int input) return -EINVAL; if (state->mci.tuner == input) return 0; + stop_iq(fe); stop(fe); state->mci.tuner = p->input = input; return 0; @@ -453,6 +488,7 @@ static int set_input(struct dvb_frontend *fe, int input) static int sleep(struct dvb_frontend *fe) { + stop_iq(fe); stop(fe); return 0; } diff --git a/ddbridge/ddbridge.h b/ddbridge/ddbridge.h index 2a2fc81..475c370 100644 --- a/ddbridge/ddbridge.h +++ b/ddbridge/ddbridge.h @@ -1,8 +1,9 @@ /* * ddbridge.h: Digital Devices PCIe bridge driver * - * Copyright (C) 2010-2017 Digital Devices GmbH - * Ralph Metzler + * Copyright (C) 2010-2019 Digital Devices GmbH + * Marcus Metzler + * Ralph Metzler * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/dvb-core/dvb_ringbuffer.c b/dvb-core/dvb_ringbuffer.c index e771825..6406e6e 100644 --- a/dvb-core/dvb_ringbuffer.c +++ b/dvb-core/dvb_ringbuffer.c @@ -115,7 +115,7 @@ ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf) void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) - rbuf->pread = &rbuf->pwrite; + rbuf->pread = rbuf->pwrite; #else /* dvb_ringbuffer_flush() counts as read operation * smp_load_acquire() to load write pointer diff --git a/dvb-core/dvbdev.c b/dvb-core/dvbdev.c index 219b869..6d63578 100644 --- a/dvb-core/dvbdev.c +++ b/dvb-core/dvbdev.c @@ -598,6 +598,7 @@ int dvb_create_media_graph(struct dvb_adapter *adap, unsigned demux_pad = 0; unsigned dvr_pad = 0; unsigned ntuner = 0, ndemod = 0; + u16 source_pad = 0; int ret; static const char *connector_name = "Television"; @@ -657,8 +658,19 @@ int dvb_create_media_graph(struct dvb_adapter *adap, ret = media_device_register_entity(mdev, conn); if (ret) return ret; + + if (!ntuner) { - if (!ntuner) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)) + if ((ret = media_get_pad_index(tuner, true, + PAD_SIGNAL_ANALOG)) < 0) + return ret; + source_pad = (u16) ret; + ret = 0; +#else + source_pad = TUNER_PAD_RF_INPUT; +#endif + ret = media_create_pad_links(mdev, MEDIA_ENT_F_CONN_RF, conn, 0, @@ -666,22 +678,33 @@ int dvb_create_media_graph(struct dvb_adapter *adap, demod, 0, MEDIA_LNK_FL_ENABLED, false); - else + } else { ret = media_create_pad_links(mdev, MEDIA_ENT_F_CONN_RF, conn, 0, MEDIA_ENT_F_TUNER, - tuner, TUNER_PAD_RF_INPUT, + tuner, source_pad, MEDIA_LNK_FL_ENABLED, false); + } if (ret) return ret; } if (ntuner && ndemod) { + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)) + if ((ret = media_get_pad_index(tuner, true, PAD_SIGNAL_ANALOG)) < 0) + return ret; + source_pad = (u16) ret; + ret = 0; +#else + source_pad = TUNER_PAD_OUTPUT; +#endif + ret = media_create_pad_links(mdev, MEDIA_ENT_F_TUNER, - tuner, TUNER_PAD_OUTPUT, + tuner, source_pad, MEDIA_ENT_F_DTV_DEMOD, demod, 0, MEDIA_LNK_FL_ENABLED, false); diff --git a/frontends/cxd2843.c b/frontends/cxd2843.c index a08dd23..8695207 100644 --- a/frontends/cxd2843.c +++ b/frontends/cxd2843.c @@ -355,6 +355,7 @@ static int read_tps(struct cxd_state *state, u8 *tps) /* OFDMInfo[3] [3:0] OFDM_NDSYM[11:8] */ /* OFDMInfo[4] [7:0] OFDM_NDSYM[7:0] */ +#if 0 static int read_t2_ofdm_info(struct cxd_state *state, u8 *ofdm) { if (state->last_status != 0x1f) @@ -365,6 +366,7 @@ static int read_t2_ofdm_info(struct cxd_state *state, u8 *ofdm) unfreeze_regst(state); return 0; } +#endif /* Read DVBT2 QAM, Data PLP @@ -390,6 +392,7 @@ static int read_t2_ofdm_info(struct cxd_state *state, u8 *ofdm) 19-37 same for common PLP */ +#if 0 static int read_t2_tlp_info(struct cxd_state *state, u8 off, u8 count, u8 *tlp) { if (state->last_status != 0x1f) @@ -400,6 +403,7 @@ static int read_t2_tlp_info(struct cxd_state *state, u8 off, u8 count, u8 *tlp) unfreeze_regst(state); return 0; } +#endif static void Active_to_Sleep(struct cxd_state *state) { @@ -2243,7 +2247,7 @@ static int get_algo(struct dvb_frontend *fe) static int get_fe_t2(struct cxd_state *state, struct dtv_frontend_properties *p) { - struct dvb_frontend *fe = &state->frontend; + //struct dvb_frontend *fe = &state->frontend; u8 ofdm[5], modcod[2]; freeze_regst(state); @@ -2343,7 +2347,7 @@ static int get_fe_t2(struct cxd_state *state, struct dtv_frontend_properties *p) static int get_fe_t(struct cxd_state *state, struct dtv_frontend_properties *p) { - struct dvb_frontend *fe = &state->frontend; + //struct dvb_frontend *fe = &state->frontend; u8 tps[7]; read_tps(state, tps); @@ -2444,7 +2448,7 @@ static int get_fe_t(struct cxd_state *state, struct dtv_frontend_properties *p) static int get_fe_c(struct cxd_state *state, struct dtv_frontend_properties *p) { - struct dvb_frontend *fe = &state->frontend; + //struct dvb_frontend *fe = &state->frontend; u8 qam; freeze_regst(state); diff --git a/frontends/lnbh25.c b/frontends/lnbh25.c index ec2d286..9e904c5 100644 --- a/frontends/lnbh25.c +++ b/frontends/lnbh25.c @@ -106,6 +106,7 @@ static int lnbh25_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) return 0; } +#if 0 static int lnbh25_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { @@ -113,6 +114,7 @@ static int lnbh25_set_tone(struct dvb_frontend *fe, return 0; } +#endif static int lnbh25_init(struct lnbh25 *lnbh) { diff --git a/frontends/mxl5xx.c b/frontends/mxl5xx.c index 02cb5db..576252b 100644 --- a/frontends/mxl5xx.c +++ b/frontends/mxl5xx.c @@ -98,26 +98,6 @@ struct mxl { unsigned long tune_time; }; -static void le32_to_cpusn(u32 *data, u32 size) -{ - u32 i; - - for (i = 0; i < size; data++, i += 4) - le32_to_cpus(data); -} - -static void flip_data_in_dword(u32 size, u8 *d) -{ - u32 i; - u8 t; - - for (i = 0; i < size; i += 4) { - t = d[i + 3]; d[i + 3] = d[i]; d[i] = t; - t = d[i + 2]; d[i + 2] = d[i + 1]; d[i + 1] = t; - } -} - - static void convert_endian(u8 flag, u32 size, u8 *d) { u32 i; @@ -249,6 +229,7 @@ static int write_register(struct mxl *state, u32 reg, u32 val) return stat; } +#if 0 static int write_register_block(struct mxl *state, u32 reg, u32 size, u8 *data) { @@ -272,6 +253,7 @@ static int write_register_block(struct mxl *state, u32 reg, mutex_unlock(&state->base->i2c_lock); return stat; } +#endif static int write_firmware_block(struct mxl *state, u32 reg, u32 size, u8 *regDataPtr) @@ -375,6 +357,7 @@ static int update_by_mnemonic(struct mxl *state, return stat; } +#if 0 static void extract_from_mnemonic(u32 regAddr, u8 lsbPos, u8 width, u32 *toAddr, u8 *toLsbPos, u8 *toWidth) { @@ -385,6 +368,7 @@ static void extract_from_mnemonic(u32 regAddr, u8 lsbPos, u8 width, if (toWidth) *toWidth = width; } +#endif static int firmware_is_alive(struct mxl *state) { @@ -397,6 +381,8 @@ static int firmware_is_alive(struct mxl *state) return 0; if (hb1 == hb0) return 0; + + pr_info("mxl5xx: Hydra FW alive. Hail!\n"); return 1; } @@ -470,9 +456,9 @@ static int CfgDemodAbortTune(struct mxl *state) &cmdBuff[0]); } +#if 0 static int reset_fec_counter(struct mxl *state) { - MXL_HYDRA_DEMOD_ABORT_TUNE_T abortTuneCmd; u32 demodIndex = (u32) state->demod; u8 cmdSize = sizeof(u32); u8 cmdBuff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN]; @@ -482,6 +468,7 @@ static int reset_fec_counter(struct mxl *state) return send_command(state, cmdSize + MXL_HYDRA_CMD_HEADER_SIZE, &cmdBuff[0]); } +#endif static int send_master_cmd(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) @@ -1118,8 +1105,6 @@ static int firmware_download(struct mxl *state, u8 *mbin, u32 mbin_len) if (!firmware_is_alive(state)) return -1; - pr_info("mxl5xx: Hydra FW alive. Hail!\n"); - /* sometimes register values are wrong shortly after first heart beats */ msleep(50); @@ -1437,6 +1422,7 @@ static int config_ts(struct mxl *state, MXL_HYDRA_DEMOD_ID_E demodId, {XPT_NCO_COUNT_MIN4}, {XPT_NCO_COUNT_MIN5}, {XPT_NCO_COUNT_MIN6}, {XPT_NCO_COUNT_MIN7} }; +#if 0 MXL_REG_FIELD_T mxl561_xpt_ts_sync[MXL_HYDRA_DEMOD_ID_6] = { {PAD_MUX_DIGIO_25_PINMUX_SEL}, {PAD_MUX_DIGIO_20_PINMUX_SEL}, {PAD_MUX_DIGIO_17_PINMUX_SEL}, {PAD_MUX_DIGIO_11_PINMUX_SEL}, @@ -1445,6 +1431,7 @@ static int config_ts(struct mxl *state, MXL_HYDRA_DEMOD_ID_E demodId, {PAD_MUX_DIGIO_26_PINMUX_SEL}, {PAD_MUX_DIGIO_19_PINMUX_SEL}, {PAD_MUX_DIGIO_18_PINMUX_SEL}, {PAD_MUX_DIGIO_10_PINMUX_SEL}, {PAD_MUX_DIGIO_09_PINMUX_SEL}, {PAD_MUX_DIGIO_02_PINMUX_SEL}}; +#endif demodId = state->base->ts_map[demodId]; @@ -1609,6 +1596,7 @@ static int config_mux(struct mxl *state) return 0; } +#if 0 static int config_dis(struct mxl *state, u32 id) { MXL_HYDRA_DISEQC_ID_E diseqcId = id; @@ -1630,6 +1618,7 @@ static int config_dis(struct mxl *state, u32 id) return send_command(state, cmdSize + MXL_HYDRA_CMD_HEADER_SIZE, &cmdBuff[0]); } +#endif static int load_fw(struct mxl *state, struct mxl5xx_cfg *cfg) { diff --git a/frontends/stv090x.c b/frontends/stv090x.c index fbe0169..e874eff 100644 --- a/frontends/stv090x.c +++ b/frontends/stv090x.c @@ -3644,7 +3644,6 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) { u16 val; - u32 ber; stv090x_read_cnr(fe, &val); stv090x_read_signal_strength(fe, &val); @@ -3657,7 +3656,7 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) static int stv090x_read_ber(struct dvb_frontend *fe, u32 *ber) { struct stv090x_state *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; + //struct dtv_frontend_properties *p = &fe->dtv_property_cache; u32 reg, h, m, l; enum fe_status status; @@ -5075,7 +5074,7 @@ static int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir, static int stv090x_get_frontend(struct dvb_frontend *fe, struct dtv_frontend_properties *p) { struct stv090x_state *state = fe->demodulator_priv; - u8 tmp; + //u8 tmp; u32 reg = 0; if (state->rec_mode == 2) { diff --git a/frontends/stv0910.c b/frontends/stv0910.c index e31f82c..ab1b4c4 100644 --- a/frontends/stv0910.c +++ b/frontends/stv0910.c @@ -150,10 +150,12 @@ static int write_reg(struct stv *state, u16 reg, u8 val) return (i2c_transfer(state->base->i2c, &msg, 1) == 1) ? 0 : -1; } +#if 0 static int write_reg_off(struct stv *state, u16 reg, u8 val) { return write_reg(state, reg + state->regoff, val); } +#endif static inline int i2c_read_regs16(struct i2c_adapter *adapter, u8 adr, u16 reg, u8 *val, int len) @@ -1639,6 +1641,7 @@ static int recv_slave_reply(struct dvb_frontend *fe, static int send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd burst) { +#if 0 struct stv *state = fe->demodulator_priv; u8 value; @@ -1653,6 +1656,7 @@ static int send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd burst) set_reg(DISTXFIFO, value); set_reg(DISTXCFG, 0x3a); wait_dis(state, 0x20, 0x20); +#endif return 0; } diff --git a/frontends/stv6111.c b/frontends/stv6111.c index 28acf40..0689df5 100644 --- a/frontends/stv6111.c +++ b/frontends/stv6111.c @@ -100,12 +100,12 @@ static int read_reg(struct stv *state, u8 reg, u8 *val) return i2c_read(state->i2c, state->adr, ®, 1, val, 1); } +#if 0 static int read_regs(struct stv *state, u8 reg, u8 *val, int len) { return i2c_read(state->i2c, state->adr, ®, 1, val, len); } -#if 0 static void dump_regs(struct stv *state) { u8 d[11], *c = &state->reg[0]; diff --git a/include/linux/dvb/mod.h b/include/linux/dvb/mod.h index 0c5633e..49fd1a8 100644 --- a/include/linux/dvb/mod.h +++ b/include/linux/dvb/mod.h @@ -32,6 +32,16 @@ struct dvb_mod_channel_params { #define MODULATOR_GAIN 35 #define MODULATOR_RESET 36 #define MODULATOR_STATUS 37 +#define MODULATOR_INFO 38 #define MODULATOR_OUTPUT_ARI 64 +#define MODULATOR_OUTPUT_RATE 65 + +enum mod_output_rate { + SYS_DVBT_6 = 0, + SYS_DVBT_7, + SYS_DVBT_8, + SYS_ISDBT_6 = 16, +}; + #endif /*_UAPI_DVBMOD_H_*/ diff --git a/lib/Makefile b/lib/Makefile index 123920a..ecc0981 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -13,11 +13,11 @@ install: all $(CC) $(CFLAGS) -fPIC -c $< dddvb_test: dddvb_test.o - $(CC) -o dddvb_test $< -L ./src -l dddvb -l pthread + $(CC) -o dddvb_test $< -L ./src -l dddvb -l pthread -l dvben50221 -l dvbapi -l ucsi ddzap: ddzap.o - $(CC) -o ddzap $< -L ./src -l dddvb -l pthread + $(CC) -o ddzap $< -L ./src -l dddvb -l pthread -l dvben50221 -l dvbapi -l ucsi clean: make -C ./src clean - rm *.o + -rm -f *.o diff --git a/lib/ddzap.c b/lib/ddzap.c index 58b85f3..09ea092 100644 --- a/lib/ddzap.c +++ b/lib/ddzap.c @@ -24,7 +24,8 @@ int main(int argc, char **argv) struct dddvb_fe *fe; struct dddvb_params p; uint32_t bandwidth = 8000000, frequency = 0, symbol_rate = 0, pol = DDDVB_UNDEF; - uint32_t id = DDDVB_UNDEF, pls = DDDVB_UNDEF, num = DDDVB_UNDEF; + uint32_t id = DDDVB_UNDEF, pls = DDDVB_UNDEF, num = DDDVB_UNDEF, source = 0; + uint32_t verbosity = 0; enum fe_code_rate fec = FEC_AUTO; enum fe_delivery_system delsys = ~0; char *config = "config/"; @@ -38,16 +39,18 @@ int main(int argc, char **argv) {"frequency", required_argument, 0, 'f'}, {"bandwidth", required_argument, 0, 'b'}, {"symbolrate", required_argument, 0, 's'}, + {"source", required_argument, 0, 'l'}, {"delsys", required_argument, 0, 'd'}, {"id", required_argument, 0, 'i'}, {"pls", required_argument, 0, 'g'}, {"root", required_argument, 0, 'r'}, {"num", required_argument, 0, 'n'}, + {"verbosity", required_argument, 0, 'v'}, {"help", no_argument , 0, 'h'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, - "c:i:f:s:d:p:hg:r:n:b:", + "c:i:f:s:d:p:hg:r:n:b:l:v:", long_options, &option_index); if (c==-1) break; @@ -65,6 +68,12 @@ int main(int argc, char **argv) case 's': symbol_rate = strtoul(optarg, NULL, 0); break; + case 'l': + source = strtoul(optarg, NULL, 0); + break; + case 'v': + verbosity = strtoul(optarg, NULL, 0); + break; case 'g': pls = strtoul(optarg, NULL, 0); break; @@ -102,7 +111,14 @@ int main(int argc, char **argv) pol = 0; break; case 'h': - printf("no help yet\n"); + printf("ddzap [-d delivery_system] [-p polarity] [-c config_dir] [-f frequency(Hz)]\n" + " [-b bandwidth(Hz)] [-s symbol_rate(Hz)]\n" + " [-g gold_code] [-r root_code] [-i id] [-n device_num]\n" + "\n" + " delivery_system = C,S,S2,T,T2,J83B,ISDBC,ISDBT\n" + " polarity = h,v\n" + " polarity = h,v\n" + "\n"); exit(-1); default: break; @@ -124,7 +140,7 @@ int main(int argc, char **argv) break; } - dd = dddvb_init(config, 0);//0xffff); + dd = dddvb_init(config, verbosity); if (!dd) { printf("dddvb_init failed\n"); exit(-1); @@ -141,6 +157,7 @@ int main(int argc, char **argv) } dddvb_param_init(&p); dddvb_set_frequency(&p, frequency); + dddvb_set_src(&p, source); dddvb_set_bandwidth(&p, bandwidth); dddvb_set_symbol_rate(&p, symbol_rate); dddvb_set_polarization(&p, pol); @@ -148,6 +165,12 @@ int main(int argc, char **argv) dddvb_set_id(&p, id); dddvb_set_pls(&p, pls); dddvb_dvb_tune(fe, &p); + { + uint8_t ts[188]; + + dddvb_ca_write(dd, 0, ts, 188); + + } while (1) { fe_status_t stat; int64_t str, cnr; 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..6460758 --- /dev/null +++ b/lib/src/ca.c @@ -0,0 +1,645 @@ +#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 -EBUSY; + for (i = 0; i < MAX_PMT; i++) + if (!pmts[i]) + break; + num = i; + dbgprintf(DEBUG_CA, "handle %d pmts\n", num); + for (i = 0; i < num; i++) { + memcpy(sec, pmts[i], 3); + len = ((sec[1] & 0x0f) << 8) | sec[2]; + len += 3; + memcpy(sec, pmts[i], len); + 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 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 | O_NONBLOCK); + dd->dvbca_num++; + return 0; +} + +int dddvb_ca_write(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len) +{ + struct dddvb_ca *ca = &dd->dvbca[nr]; + + return write(ca->ci_wfd, buf, len); +} + +int dddvb_ca_read(struct dddvb *dd, uint32_t nr, uint8_t *buf, uint32_t len) +{ + struct dddvb_ca *ca = &dd->dvbca[nr]; + + return read(ca->ci_rfd, buf, len); +} + +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/debug.h b/lib/src/debug.h index d05474c..7501c0c 100644 --- a/lib/src/debug.h +++ b/lib/src/debug.h @@ -14,6 +14,7 @@ extern uint32_t dddvb_debug; #define DEBUG_DVB 16 #define DEBUG_IGMP 32 #define DEBUG_SWITCH 64 +#define DEBUG_CA 128 #define DEBUG_DEBUG 256 diff --git a/lib/src/dvb.c b/lib/src/dvb.c index be2d76c..cb31b72 100644 --- a/lib/src/dvb.c +++ b/lib/src/dvb.c @@ -80,6 +80,24 @@ static int get_stat(int fd, uint32_t cmd, struct dtv_fe_stats *stats) return 0; } +static int get_stat_num(int fd, uint32_t cmd, struct dtv_fe_stats *stats, int num) +{ + struct dtv_property p; + struct dtv_properties c; + int ret; + + p.cmd = cmd; + c.num = num; + c.props = &p; + ret = ioctl(fd, FE_GET_PROPERTY, &c); + if (ret < 0) { + fprintf(stderr, "FE_GET_PROPERTY returned %d\n", ret); + return -1; + } + memcpy(stats, &p.u.st, num*sizeof(struct dtv_fe_stats)); + return 0; +} + static int set_fe_input(struct dddvb_fe *fe, uint32_t fr, @@ -533,6 +551,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 +566,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 +907,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_ */ diff --git a/lib/src/tools.c b/lib/src/tools.c index bcf533a..4bacb8a 100644 --- a/lib/src/tools.c +++ b/lib/src/tools.c @@ -1,4 +1,37 @@ #include "time.h" +#include +#include +#include +#include +#include + + +int sendlen(int sock, char *buf, int len) +{ + int done, todo; + + for (todo = len; todo; todo -= done, buf += done) + if ((done = send(sock, buf, todo, 0)) < 0) { + printf("sendlen error\n"); + return done; + } + return len; +} + +int sendstring(int sock, char *fmt, ...) +{ + int len; + uint8_t buf[2048]; + va_list args; + + va_start(args, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, args); + if (len <= 0 || len >= sizeof(buf)) + return 0; + sendlen(sock, buf, len); + va_end(args); + return len; +} time_t mtime(time_t *t) { diff --git a/lib/src/tools.h b/lib/src/tools.h index fe4d637..06c8593 100644 --- a/lib/src/tools.h +++ b/lib/src/tools.h @@ -2,5 +2,7 @@ #define _DDDVB_TOOLS_H_ time_t mtime(time_t *t); +int sendlen(int sock, char *buf, int len); +int sendstring(int sock, char *fmt, ...); #endif /* _DDDVB_TOOLS_H_ */