The cec-follower will now emulate a digital service.  This allows an
initiator device can 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.

Opcodes implemented:
  - <Select Digital Service>

Signed-off-by: Jiunn Chang <c0d1n61...@gmail.com>
---
 utils/cec-follower/cec-tuner.cpp | 169 +++++++++++++++++++++++++++++++
 1 file changed, 169 insertions(+)

diff --git a/utils/cec-follower/cec-tuner.cpp b/utils/cec-follower/cec-tuner.cpp
index 04e7e4c3..760eed2a 100644
--- a/utils/cec-follower/cec-tuner.cpp
+++ b/utils/cec-follower/cec-tuner.cpp
@@ -244,6 +244,162 @@ void analog_tuner_init(struct state *state)
        info->analog.ana_freq = (freq_khz * 10) / 625;
 }
 
+static int digital_get_service_offset(struct cec_op_digital_service_id 
*digital)
+{
+       __u8 method = digital->service_id_method;
+       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 sys =
+               (digital->dig_bcast_system == 
CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T ||
+                digital->dig_bcast_system == 
CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T ||
+                digital->dig_bcast_system == 
CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T) ? 1 : 0;
+
+       for (int i = 0; i < NUM_DIGITAL_CHANS; i++) {
+               switch (method) {
+               case CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID:
+                       if (dvb->transport_id == 
digital_arib_data[sys][0][i].tsid &&
+                           dvb->service_id == digital_arib_data[sys][0][i].sid 
&&
+                           dvb->orig_network_id == 
digital_arib_data[sys][0][i].onid) {
+                               return (sys * NUM_DIGITAL_CHANS) + i;
+                       }
+                       if (atsc->transport_id == 
digital_atsc_data[sys][0][i].tsid &&
+                           atsc->program_number == 
digital_atsc_data[sys][0][i].sid) {
+                               return (sys * NUM_DIGITAL_CHANS) + i;
+                       }
+                       if (dvb->transport_id == 
digital_dvb_data[sys][0][i].tsid &&
+                           dvb->service_id == digital_dvb_data[sys][0][i].sid 
&&
+                           dvb->orig_network_id == 
digital_dvb_data[sys][0][i].onid) {
+                               return (sys * NUM_DIGITAL_CHANS) + i;
+                       }
+                       break;
+               case CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL:
+                       if (channel->minor == 
digital_arib_data[sys][0][i].minor) {
+                               return (sys * NUM_DIGITAL_CHANS) + i;
+                       }
+                       if (channel->major == 
digital_atsc_data[sys][0][i].major &&
+                           channel->minor == 
digital_atsc_data[sys][0][i].minor) {
+                               return (sys * NUM_DIGITAL_CHANS) + i;
+                       }
+                       if (channel->minor == 
digital_dvb_data[sys][0][i].minor) {
+                               return (sys * NUM_DIGITAL_CHANS) + i;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return -1;
+}
+
+static int digital_get_service_idx(struct cec_op_digital_service_id *digital)
+{
+       __u8 system = digital->dig_bcast_system;
+       int offset = digital_get_service_offset(digital);
+
+       switch (system) {
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
+               return offset;
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T: {
+               return offset == -1 ? offset : NUM_DIGITAL_CHANS * 2 + offset;
+       }
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T: {
+               return offset == -1 ? offset : NUM_DIGITAL_CHANS * 4 + offset;
+       }
+       default:
+               break;
+       }
+       return -1;
+}
+
+static void digital_update_tuner_dev_info(struct node *node, unsigned int idx,
+                                         __u8 method = 
CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID)
+{
+       struct cec_op_tuner_device_info *info = &node->state.tuner_dev_info;
+       struct cec_op_digital_service_id *digital = &info->digital;
+       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 = method;
+       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 (digital->service_id_method) {
+                       channel->channel_number_fmt = 
digital_arib_data[sys][0][offset].fmt;
+                       channel->major = 
digital_arib_data[sys][0][offset].major;
+                       channel->minor = 
digital_arib_data[sys][0][offset].minor;
+               } else {
+                       dvb->transport_id = 
digital_arib_data[sys][0][offset].tsid;
+                       dvb->orig_network_id = 
digital_arib_data[sys][0][offset].onid;
+                       dvb->service_id = digital_arib_data[sys][0][offset].sid;
+               }
+               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 (digital->service_id_method) {
+                       channel->channel_number_fmt = 
digital_atsc_data[sys][0][offset].fmt;
+                       channel->major = 
digital_atsc_data[sys][0][offset].major;
+                       channel->minor = 
digital_atsc_data[sys][0][offset].minor;
+               } else {
+                       atsc->transport_id = 
digital_atsc_data[sys][0][offset].tsid;
+                       atsc->program_number = 
digital_atsc_data[sys][0][offset].sid;
+               }
+               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 (digital->service_id_method) {
+                       channel->channel_number_fmt = 
digital_dvb_data[sys][0][offset].fmt;
+                       channel->major = digital_dvb_data[sys][0][offset].major;
+                       channel->minor = digital_dvb_data[sys][0][offset].minor;
+               } else {
+                       dvb->transport_id = 
digital_dvb_data[sys][0][offset].tsid;
+                       dvb->orig_network_id = 
digital_dvb_data[sys][0][offset].onid;
+                       dvb->service_id = digital_dvb_data[sys][0][offset].sid;
+               }
+               break;
+       }
+       default:
+               break;
+       }
+}
+
+static bool digital_set_tuner_dev_info(struct node *node, struct cec_msg *msg)
+{
+       struct cec_op_digital_service_id digital = {};
+       __u8 method;
+       int idx;
+
+       cec_ops_select_digital_service(msg, &digital);
+       method = digital.service_id_method;
+       idx = digital_get_service_idx(&digital);
+       if (idx > -1) {
+               digital_update_tuner_dev_info(node, idx, method);
+               return true;
+       }
+       return false;
+}
+
 static unsigned int analog_get_nearest_service_idx(__u8 ana_bcast_type, __u8 
ana_bcast_system,
                                                   int ana_freq_khz)
 {
@@ -340,6 +496,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;
-- 
2.23.0

Reply via email to