aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiunn Chang <c0d1n61at3@gmail.com>2019-10-13 23:17:19 -0500
committerHans Verkuil <hverkuil-cisco@xs4all.nl>2019-10-22 14:48:44 +0200
commit937bdb7899727a73cd412a403006c1648ee2107f (patch)
treef98ee6346dd30b0017af5b56271ed924a669c9e3
parent2e133a8fcece4fdc4d9cace9e8cf66aa1861c8ee (diff)
cec-follower: add tuner digital service emulation
The cec-follower will now emulate a digital service. This allows an initiator device to directly select a digital service by choosing a digital service ID method and broadcast system along with the proper digital IDs or channel data. After a digital service is selected, the cec-follower will also provide the tuner device status upon request. The follower reports digital services either by channel (default) or by digital ID. A new option --service-by-dig-id was added for that purpose. Opcodes implemented: - <Select Digital Service> Signed-off-by: Jiunn Chang <c0d1n61at3@gmail.com> Co-developed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
-rw-r--r--utils/cec-follower/cec-follower.1.in4
-rw-r--r--utils/cec-follower/cec-follower.cpp30
-rw-r--r--utils/cec-follower/cec-follower.h1
-rw-r--r--utils/cec-follower/cec-tuner.cpp188
4 files changed, 210 insertions, 13 deletions
diff --git a/utils/cec-follower/cec-follower.1.in b/utils/cec-follower/cec-follower.1.in
index 427f96f7..1ed77286 100644
--- a/utils/cec-follower/cec-follower.1.in
+++ b/utils/cec-follower/cec-follower.1.in
@@ -78,6 +78,10 @@ Show received messages.
.TP
\fB\-s\fR, \fB\-\-show\-state\fR
Show state changes from the emulated device.
+.TP
+\fB\-\-service\-by\-dig\-id\fR
+Report digital services by digital ID instead of by channel.
+
.SH EXIT STATUS
On success, it returns 0. Otherwise, it will return the error code.
.SH BUGS
diff --git a/utils/cec-follower/cec-follower.cpp b/utils/cec-follower/cec-follower.cpp
index 00783d15..2d6adfc0 100644
--- a/utils/cec-follower/cec-follower.cpp
+++ b/utils/cec-follower/cec-follower.cpp
@@ -41,7 +41,8 @@ enum Option {
OptShowMsgs = 'm',
OptShowState = 's',
OptWallClock = 'w',
- OptLast = 128
+ OptServiceByDigID = 128,
+ OptLast = 256
};
static char options[OptLast];
@@ -53,18 +54,19 @@ bool show_warnings = true;
unsigned warnings;
static struct option long_options[] = {
- {"device", required_argument, 0, OptSetDevice},
- {"adapter", required_argument, 0, OptSetAdapter},
- {"driver", required_argument, 0, OptSetDriver},
- {"help", no_argument, 0, OptHelp},
- {"no-warnings", no_argument, 0, OptNoWarnings},
- {"trace", no_argument, 0, OptTrace},
- {"verbose", no_argument, 0, OptVerbose},
- {"show-msgs", no_argument, 0, OptShowMsgs},
- {"show-state", no_argument, 0, OptShowState},
- {"wall-clock", no_argument, 0, OptWallClock},
-
- {0, 0, 0, 0}
+ { "device", required_argument, 0, OptSetDevice },
+ { "adapter", required_argument, 0, OptSetAdapter },
+ { "driver", required_argument, 0, OptSetDriver },
+ { "help", no_argument, 0, OptHelp },
+ { "no-warnings", no_argument, 0, OptNoWarnings },
+ { "trace", no_argument, 0, OptTrace },
+ { "verbose", no_argument, 0, OptVerbose },
+ { "show-msgs", no_argument, 0, OptShowMsgs },
+ { "show-state", no_argument, 0, OptShowState },
+ { "wall-clock", no_argument, 0, OptWallClock },
+ { "service-by-dig-id", no_argument, 0, OptServiceByDigID },
+
+ { 0, 0, 0, 0 }
};
static void usage(void)
@@ -81,6 +83,7 @@ static void usage(void)
" -w, --wall-clock Show timestamps as wall-clock time (implies -v)\n"
" -m, --show-msgs Show received messages\n"
" -s, --show-state Show state changes from the emulated device\n"
+ " --service-by-dig-id Report digital services by digital ID instead of by channel\n"
);
}
@@ -423,6 +426,7 @@ int main(int argc, char **argv)
doioctl(&node, CEC_ADAP_G_CAPS, &caps);
node.caps = caps.capabilities;
node.available_log_addrs = caps.available_log_addrs;
+ node.state.service_by_dig_id = options[OptServiceByDigID];
state_init(node);
#ifdef SHA
diff --git a/utils/cec-follower/cec-follower.h b/utils/cec-follower/cec-follower.h
index 2a9e397a..74d082ef 100644
--- a/utils/cec-follower/cec-follower.h
+++ b/utils/cec-follower/cec-follower.h
@@ -54,6 +54,7 @@ struct state {
unsigned rc_duration_sum;
struct cec_op_tuner_device_info tuner_dev_info;
unsigned int service_idx;
+ bool service_by_dig_id;
};
struct node {
diff --git a/utils/cec-follower/cec-tuner.cpp b/utils/cec-follower/cec-tuner.cpp
index 991f9194..aa6554e4 100644
--- a/utils/cec-follower/cec-tuner.cpp
+++ b/utils/cec-follower/cec-tuner.cpp
@@ -226,6 +226,181 @@ void analog_tuner_init(struct state *state)
info->analog.ana_freq = (freq_khz * 10) / 625;
}
+static int digital_get_service_offset(const struct service_info *info,
+ const struct cec_op_digital_service_id *digital)
+{
+ __u8 method = digital->service_id_method;
+ const struct cec_op_arib_data *arib = &digital->arib;
+ const struct cec_op_atsc_data *atsc = &digital->atsc;
+ const struct cec_op_dvb_data *dvb = &digital->dvb;
+ const struct cec_op_channel_data *channel = &digital->channel;
+
+ for (int i = 0; i < NUM_DIGITAL_CHANS; i++, info++) {
+ switch (method) {
+ case CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID:
+ switch (digital->dig_bcast_system) {
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
+ if (arib->transport_id == info->tsid &&
+ arib->service_id == info->sid &&
+ arib->orig_network_id == info->onid)
+ return i;
+ break;
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
+ if (atsc->transport_id == info->tsid &&
+ atsc->program_number == info->sid)
+ return i;
+ break;
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
+ if (dvb->transport_id == info->tsid &&
+ dvb->service_id == info->sid &&
+ dvb->orig_network_id == info->onid)
+ return i;
+ break;
+ }
+ break;
+ case CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL:
+ switch (channel->channel_number_fmt) {
+ case CEC_OP_CHANNEL_NUMBER_FMT_1_PART:
+ if (channel->minor == info->minor)
+ return i;
+ break;
+ case CEC_OP_CHANNEL_NUMBER_FMT_2_PART:
+ if (channel->major == info->major &&
+ channel->minor == info->minor)
+ return i;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return -1;
+}
+
+static int digital_get_service_idx(struct cec_op_digital_service_id *digital)
+{
+ const struct service_info *info;
+ bool is_terrestrial = false;
+ unsigned offset;
+ int idx;
+
+ switch (digital->dig_bcast_system) {
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
+ is_terrestrial = true;
+ /* fall through */
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
+ info = &digital_arib_data[is_terrestrial][0];
+ offset = is_terrestrial * NUM_DIGITAL_CHANS;
+ break;
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
+ is_terrestrial = true;
+ /* fall through */
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
+ info = &digital_atsc_data[is_terrestrial][0];
+ offset = (2 + is_terrestrial) * NUM_DIGITAL_CHANS;
+ break;
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
+ is_terrestrial = true;
+ /* fall through */
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
+ info = &digital_dvb_data[is_terrestrial][0];
+ offset = (4 + is_terrestrial) * NUM_DIGITAL_CHANS;
+ break;
+ default:
+ return -1;
+ }
+ idx = digital_get_service_offset(info, digital);
+ return idx >= 0 ? idx + offset : -1;
+}
+
+static bool digital_update_tuner_dev_info(struct node *node, int idx)
+{
+ if (idx < 0)
+ return false;
+
+ struct cec_op_tuner_device_info *info = &node->state.tuner_dev_info;
+ struct cec_op_digital_service_id *digital = &info->digital;
+ struct cec_op_arib_data *arib = &digital->arib;
+ struct cec_op_dvb_data *dvb = &digital->dvb;
+ struct cec_op_atsc_data *atsc = &digital->atsc;
+ struct cec_op_channel_data *channel = &digital->channel;
+ unsigned int tbl = idx / (NUM_DIGITAL_CHANS * 2);
+ unsigned int sys = (idx % (NUM_DIGITAL_CHANS * 2)) / NUM_DIGITAL_CHANS;
+ unsigned int offset = idx % NUM_DIGITAL_CHANS;
+
+ node->state.service_idx = idx;
+ info->tuner_display_info = CEC_OP_TUNER_DISPLAY_INFO_DIGITAL;
+ info->is_analog = false;
+ digital->service_id_method = node->state.service_by_dig_id ?
+ CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID :
+ CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
+
+ switch (tbl) {
+ case 0: {
+ if (sys)
+ digital->dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T;
+ else
+ digital->dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS;
+ if (node->state.service_by_dig_id) {
+ arib->transport_id = digital_arib_data[sys][offset].tsid;
+ arib->orig_network_id = digital_arib_data[sys][offset].onid;
+ arib->service_id = digital_arib_data[sys][offset].sid;
+ } else {
+ channel->channel_number_fmt = digital_arib_data[sys][offset].fmt;
+ channel->major = digital_arib_data[sys][offset].major;
+ channel->minor = digital_arib_data[sys][offset].minor;
+ }
+ break;
+ }
+ case 1: {
+ if (sys)
+ digital->dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T;
+ else
+ digital->dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT;
+ if (node->state.service_by_dig_id) {
+ atsc->transport_id = digital_atsc_data[sys][offset].tsid;
+ atsc->program_number = digital_atsc_data[sys][offset].sid;
+ } else {
+ channel->channel_number_fmt = digital_atsc_data[sys][offset].fmt;
+ channel->major = digital_atsc_data[sys][offset].major;
+ channel->minor = digital_atsc_data[sys][offset].minor;
+ }
+ break;
+ }
+ case 2: {
+ if (sys)
+ digital->dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T;
+ else
+ digital->dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2;
+ if (node->state.service_by_dig_id) {
+ dvb->transport_id = digital_dvb_data[sys][offset].tsid;
+ dvb->orig_network_id = digital_dvb_data[sys][offset].onid;
+ dvb->service_id = digital_dvb_data[sys][offset].sid;
+ } else {
+ channel->channel_number_fmt = digital_dvb_data[sys][offset].fmt;
+ channel->major = digital_dvb_data[sys][offset].major;
+ channel->minor = digital_dvb_data[sys][offset].minor;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+}
+
+static bool digital_set_tuner_dev_info(struct node *node, struct cec_msg *msg)
+{
+ struct cec_op_digital_service_id digital = {};
+
+ cec_ops_select_digital_service(msg, &digital);
+ return digital_update_tuner_dev_info(node, digital_get_service_idx(&digital));
+}
+
static unsigned int analog_get_nearest_service_idx(__u8 ana_bcast_type, __u8 ana_bcast_system,
int ana_freq_khz)
{
@@ -322,6 +497,19 @@ void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, uns
return;
case CEC_MSG_SELECT_DIGITAL_SERVICE:
+ if (!cec_has_tuner(1 << me) && !cec_has_tv(1 << me))
+ break;
+
+ if (node->state.tuner_dev_info.rec_flag == CEC_OP_REC_FLAG_USED) {
+ reply_feature_abort(node, &msg, CEC_OP_ABORT_REFUSED);
+ return;
+ }
+ if (!digital_set_tuner_dev_info(node, &msg)) {
+ reply_feature_abort(node, &msg, CEC_OP_ABORT_INVALID_OP);
+ return;
+ }
+ return;
+
case CEC_MSG_TUNER_STEP_DECREMENT: {
if (!cec_has_tuner(1 << me) && !cec_has_tv(1 << me))
break;

Privacy Policy