diff --git a/apps/modconfig.c b/apps/modconfig.c new file mode 100644 index 0000000..ce1c95b --- /dev/null +++ b/apps/modconfig.c @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef uint64_t u64; + +#include "../ddbridge/ddbridge-mci.h" +#include "../ddbridge/ddbridge-ioctl.h" + +struct mconf { + int set_output; + int set_channels; + int fd; + + struct mci_command channels; + struct mci_command stream; + struct mci_command output; +}; + +void strim(char *s) +{ + int l = strlen(s); + + while (l && isspace(s[l-1])) + l--; + s[l] = 0; +} + +void parse(char *fname, char *sec, void *priv, void (*cb)(void *, char *, char *)) +{ + char line[256], csec[80], par[80], val[80], *p; + FILE *f; + + if ((f = fopen(fname, "r")) == NULL) + return; + while ((p = fgets(line, sizeof(line), f))) { + if (*p == '\r' || *p == '\n' || *p == '#') + continue; + if (*p == '[') { + if ((p = strtok(line + 1, "]")) == NULL) + continue; + strncpy(csec, p, sizeof(csec)); + if (!strcmp(sec, csec) && cb) + cb(priv, NULL, NULL); + continue; + } + if (!(p = strtok(line, "="))) + continue; + while (isspace(*p)) + p++; + strncpy(par, p, sizeof(par)); + strim(par); + if (!(p = strtok(NULL, "="))) + continue; + while (isspace(*p)) + p++; + strncpy (val, p, sizeof(val)); + strim(val); + if (!strcmp(sec, csec) && cb) + cb(priv, par, val); + } + if (!strcmp(sec, csec) && cb) + cb(priv, NULL, NULL); + fclose(f); +} + +int mci_cmd(int dev, struct mci_command *cmd) +{ + int ret; + struct ddb_mci_msg msg; + + msg.link = 0; + memcpy(&msg.cmd, cmd, sizeof(msg.cmd)); + ret = ioctl(dev, IOCTL_DDB_MCI_CMD, &msg); + if (ret < 0) { + printf("mci_cmd error %d\n", errno); + return ret; + } + return ret; +} + +struct mci_command msg_channels = { + .mod_command = MOD_SETUP_CHANNELS, + .mod_channel = 0, + .mod_stream = 0, + .mod_setup_channels[0] = { + .flags = MOD_SETUP_FLAG_FIRST|MOD_SETUP_FLAG_LAST|MOD_SETUP_FLAG_VALID, + .standard = MOD_STANDARD_DVBT_8, + .num_channels = 25, + .frequency = 474000000, + }, +}; + +struct mci_command msg_stream = { + .mod_command = MOD_SETUP_STREAM, + .mod_channel = 1, + .mod_stream = 0, + .mod_setup_stream = { + .standard = MOD_STANDARD_DVBT_8, + .fft_size = 1, + .guard_interval = 0, + }, +}; + +struct mci_command msg_output = { + .mod_command = MOD_SETUP_OUTPUT, + .mod_channel = 0, + .mod_stream = 0, + .mod_setup_output = { + .connector = MOD_CONNECTOR_F, + .num_channels = 16, + .unit = MOD_UNIT_DBUV, + .channel_power = 5000, + }, +}; + +void output_cb(void *priv, char *par, char *val) +{ + struct mconf *mc = (struct mconf *) priv; + + if (!par && !val) { + mc->set_output = 1; + return; + } + if (!strcasecmp(par, "connector")) { + if (!strcasecmp(val, "F")) { + mc->output.mod_setup_output.connector = MOD_CONNECTOR_F; + } else if (!strcasecmp(val, "SMA")) { + mc->output.mod_setup_output.connector = MOD_CONNECTOR_SMA; + } else if (!strcasecmp(val, "OFF")) { + mc->output.mod_setup_output.connector = MOD_CONNECTOR_OFF; + } else + printf("invalid connector\n"); + } else if (!strcasecmp(par, "power")) { + mc->output.mod_setup_output.channel_power = strtol(val, NULL, 10); + } else if (!strcasecmp(par, "channels")) { + mc->output.mod_setup_output.num_channels = strtol(val, NULL, 10); + }else if (!strcasecmp(par, "unit")) { + if (!strcasecmp(val, "DBUV")) { + mc->output.mod_setup_output.unit = MOD_UNIT_DBUV; + } else if (!strcasecmp(val, "DBM")) { + mc->output.mod_setup_output.unit = MOD_UNIT_DBM; + } else + printf("invalid unit\n"); + } else + printf("invalid output parameter: %s\n", par); +} + +void channels_cb(void *priv, char *par, char *val) +{ + struct mconf *mc = (struct mconf *) priv; + + if (!par && !val) { + mc->set_channels = 1; + return; + } + if (!strcasecmp(par, "frequency")) { + mc->channels.mod_setup_channels[0].frequency = strtol(val, NULL, 10); + printf("frequency = %u\n", mc->channels.mod_setup_channels[0].frequency); + } else if (!strcasecmp(par, "channels")) { + mc->channels.mod_setup_channels[0].num_channels = strtol(val, NULL, 10); + } else if (!strcasecmp(par, "standard")) { + mc->channels.mod_setup_channels[0].standard = strtol(val, NULL, 10); + } else if (!strcasecmp(par, "offset")) { + mc->channels.mod_setup_channels[0].offset = strtol(val, NULL, 10); + } else if (!strcasecmp(par, "bandwidth")) { + mc->channels.mod_setup_channels[0].bandwidth = strtol(val, NULL, 10); + mc->channels.mod_setup_channels[0].offset = + mc->channels.mod_setup_channels[0].bandwidth / 2; + } else + printf("invalid channels parameter: %s\n", par); +} + +void streams_cb(void *priv, char *par, char *val) +{ + struct mconf *mc = (struct mconf *) priv; + + if (!par && !val) { + return; + } + if (!strcasecmp(par, "fft_size")) { + mc->stream.mod_setup_stream.fft_size = strtol(val, NULL, 10); + } else if (!strcasecmp(par, "guard_interval")) { + mc->stream.mod_setup_stream.guard_interval = strtol(val, NULL, 10); + } else if (!strcasecmp(par, "standard")) { + mc->stream.mod_setup_stream.standard = strtol(val, NULL, 10); + } else if (!strcasecmp(par, "stream_format")) { + mc->stream.mod_setup_stream.stream_format = strtol(val, NULL, 10); + } else if (!strcasecmp(par, "symbol_rate")) { + mc->stream.mod_setup_stream.symbol_rate = strtol(val, NULL, 10); + } else if (!strcasecmp(par, "stream")) { + mc->stream.mod_stream = strtol(val, NULL, 10); + printf("set stream %u to channel %u\n", mc->stream.mod_stream, mc->stream.mod_channel); + mci_cmd(mc->fd, &mc->stream); + } else if (!strcasecmp(par, "channel")) { + mc->stream.mod_channel = strtol(val, NULL, 10); + } else + printf("invalid streams parameter: %s\n", par); +} + +int main(int argc, char*argv[]) +{ + int fd = -1; + char fn[128]; + uint32_t device = 0; + uint32_t frequency = 0; + char *configname = "modulator.conf"; + struct mconf mc; + + memset(&mc, 0, sizeof(mc)); + mc.channels = msg_channels; + mc.stream = msg_stream; + mc.output = msg_output; + + while (1) { + int cur_optind = optind ? optind : 1; + int option_index = 0; + int c; + static struct option long_options[] = { + {"device", required_argument, 0, 'd'}, + {"config", required_argument, 0, 'c'}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "d:c:", + long_options, &option_index); + if (c == -1) + break; + switch (c) { + case 'd': + device = strtoul(optarg, NULL, 0); + break; + case 'c': + configname = optarg; + break; + default: + break; + } + } + if (optind < argc) { + printf("too many arguments\n"); + exit(1); + } + snprintf(fn, 127, "/dev/ddbridge/card%u", device); + fd = open(fn, O_RDWR); + if (fd < 0) + return -1; + mc.fd = fd; + parse(configname, "output", (void *) &mc, output_cb); + if (mc.set_output) + mci_cmd(fd, &mc.output); + parse(configname, "channels", (void *) &mc, channels_cb); + if (mc.set_channels) + mci_cmd(fd, &mc.channels); + parse(configname, "streams", (void *) &mc, streams_cb); +} diff --git a/ddbridge/ddbridge-mci.h b/ddbridge/ddbridge-mci.h index d770a6d..5645a9c 100644 --- a/ddbridge/ddbridge-mci.h +++ b/ddbridge/ddbridge-mci.h @@ -185,6 +185,60 @@ #define MCI_SUCCESS(status) ((status & MCI_STATUS_UNSUPPORTED) == 0) +/********************************************************/ + +#define MOD_SETUP_CHANNELS (0x60) +#define MOD_SETUP_OUTPUT (0x61) +#define MOD_SETUP_STREAM (0x62) + +#define MOD_SETUP_FLAG_FIRST (0x01) +#define MOD_SETUP_FLAG_LAST (0x02) +#define MOD_SETUP_FLAG_VALID (0x80) + +#define MOD_STANDARD_GENERIC (0x00) +#define MOD_STANDARD_DVBT_8 (0x01) +#define MOD_STANDARD_DVBT_7 (0x02) +#define MOD_STANDARD_DVBT_6 (0x03) + +#define MOD_CONNECTOR_OFF (0x00) +#define MOD_CONNECTOR_F (0x01) +#define MOD_CONNECTOR_SMA (0x02) + +#define MOD_UNIT_DBUV (0x00) +#define MOD_UNIT_DBM (0x01) + +#define MOD_FORMAT_DEFAULT (0x00) +#define MOD_FORMAT_IQ16 (0x01) +#define MOD_FORMAT_IQ8 (0x02) +#define MOD_FORMAT_IDX8 (0x03) +#define MOD_FORMAT_TS (0x04) + +struct mod_setup_channels { + u8 flags; + u8 standard; + u8 num_channels; + u8 rsvd; + u32 frequency; + u32 offset; /* used only when Standard == 0 */ + u32 bandwidth; /* used only when Standard == 0 */ +}; + +struct mod_setup_stream { + u8 standard; + u8 stream_format; + u8 rsvd[2]; + u32 symbol_rate; /* only used when Standard doesn't define a fixed symbol rate */ + u8 fft_size; /* 0 = 2K, 1 = 8K (2K yet supported) */ + u8 guard_interval; /* 0 = 1/32, 1 = 1/16, 2 = 1/8, 3 = 1/4 (DVB-T Encoding) */ +}; + +struct mod_setup_output { + u8 connector; /* 0 = OFF, 1 = F, 2 = SMA */ + u8 num_channels; /* max active channels, determines max power for each channel. */ + u8 unit; /* 0 = dBµV, 1 = dBm, */ + u8 rsvd; + s16 channel_power; +}; /********************************************************/ @@ -197,6 +251,12 @@ struct mci_command { u8 demod; u8 output; }; + struct { + u8 mod_command; + u8 mod_channel; + u8 mod_stream; + u8 mod_rsvd1; + }; }; union { u32 params[31]; @@ -344,6 +404,10 @@ struct mci_command { struct { u8 select; // 0 = Data PLP, 1 = Common PLP, only DVB-T2 and DVB-C2 } get_bb_header; + + struct mod_setup_channels mod_setup_channels[4]; + struct mod_setup_stream mod_setup_stream; + struct mod_setup_output mod_setup_output; }; };