Re: [PATCH v4 2/3] cec: expand One Touch Record tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 24/06/2021 06:06, Deborah Brouwer wrote:
> Expand the One Touch Record tests so that the follower and initiator know
> their local and remote device types and respond accordingly. Send Record
> TV Screen and check that Record On source replies are valid. Send Record
> On source messages and check that Record Status replies are valid. Send
> Record Off and check that the recording terminates.
> 
> Signed-off-by: Deborah Brouwer <deborahbrouwer3563@xxxxxxxxx>
> ---
>  utils/cec-compliance/cec-compliance.cpp |   1 +
>  utils/cec-compliance/cec-compliance.h   |   1 +
>  utils/cec-compliance/cec-test.cpp       | 366 ++++++++++++++++++++++--
>  utils/cec-follower/cec-follower.cpp     |   1 +
>  utils/cec-follower/cec-follower.h       |   3 +-
>  utils/cec-follower/cec-processing.cpp   |   4 +-
>  utils/cec-follower/cec-tuner.cpp        | 222 ++++++++++++--
>  7 files changed, 549 insertions(+), 49 deletions(-)
> 
> diff --git a/utils/cec-compliance/cec-compliance.cpp b/utils/cec-compliance/cec-compliance.cpp
> index c04904c2..d4b12298 100644
> --- a/utils/cec-compliance/cec-compliance.cpp
> +++ b/utils/cec-compliance/cec-compliance.cpp
> @@ -1236,6 +1236,7 @@ int main(int argc, char **argv)
>  	node.num_log_addrs = laddrs.num_log_addrs;
>  	memcpy(node.log_addr, laddrs.log_addr, laddrs.num_log_addrs);
>  	node.adap_la_mask = laddrs.log_addr_mask;
> +	node.prim_devtype = laddrs.primary_device_type[0];
>  
>  	printf("Find remote devices:\n");
>  	printf("\tPolling: %s\n", ok(poll_remote_devs(&node)));
> diff --git a/utils/cec-compliance/cec-compliance.h b/utils/cec-compliance/cec-compliance.h
> index 818181ab..41e2d63d 100644
> --- a/utils/cec-compliance/cec-compliance.h
> +++ b/utils/cec-compliance/cec-compliance.h
> @@ -166,6 +166,7 @@ struct node {
>  	struct remote remote[16];
>  	__u16 phys_addr;
>  	bool in_standby;
> +	__u8 prim_devtype;
>  };
>  
>  struct remote_subtest {
> diff --git a/utils/cec-compliance/cec-test.cpp b/utils/cec-compliance/cec-test.cpp
> index 40d8369d..864ab83c 100644
> --- a/utils/cec-compliance/cec-test.cpp
> +++ b/utils/cec-compliance/cec-test.cpp
> @@ -48,6 +48,69 @@ static int test_play_mode(struct node *node, unsigned me, unsigned la, __u8 play
>  	return OK;
>  }
>  
> +static int one_touch_rec_on_send(struct node *node, unsigned me, unsigned la,
> +                                 const struct cec_op_record_src &rec_src, __u8 &rec_status)
> +{
> +	struct cec_msg msg;
> +
> +	cec_msg_init(&msg, me, la);
> +	cec_msg_record_off(&msg, false);
> +	fail_on_test(!transmit_timeout(node, &msg));
> +
> +	cec_msg_init(&msg, me, la);
> +	cec_msg_record_on(&msg, true, &rec_src);

Add a comment here why you need a 10000 ms timeout.

> +	fail_on_test(!transmit_timeout(node, &msg, 10000));
> +	fail_on_test(timed_out_or_abort(&msg));
> +	cec_ops_record_status(&msg, &rec_status);
> +
> +	return OK;
> +}
> +
> +static int one_touch_rec_on_send_invalid(struct node *node, unsigned me, unsigned la,
> +                                         const struct cec_op_record_src &rec_src)
> +{
> +	struct cec_msg msg;
> +
> +	cec_msg_init(&msg, me, la);
> +	cec_msg_record_on(&msg, true, &rec_src);
> +	fail_on_test(!transmit_timeout(node, &msg));
> +	fail_on_test(!cec_msg_status_is_abort(&msg));
> +	fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
> +
> +	return OK;
> +}
> +
> +/*
> + * Returns true if the Record Status is an error and the request

I would rephrase this to: ...is an error indicating that the request...

> + * to start recording has failed.
> + */
> +static bool rec_status_is_a_valid_error_status(__u8 rec_status)
> +{
> +	switch (rec_status) {
> +	case CEC_OP_RECORD_STATUS_NO_DIG_SERVICE:
> +	case CEC_OP_RECORD_STATUS_NO_ANA_SERVICE:
> +	case CEC_OP_RECORD_STATUS_NO_SERVICE:
> +	case CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG:
> +	case CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR:
> +	case CEC_OP_RECORD_STATUS_UNSUP_CA:
> +	case CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS:
> +	case CEC_OP_RECORD_STATUS_CANT_COPY_SRC:
> +	case CEC_OP_RECORD_STATUS_NO_MORE_COPIES:
> +	case CEC_OP_RECORD_STATUS_NO_MEDIA:
> +	case CEC_OP_RECORD_STATUS_PLAYING:
> +	case CEC_OP_RECORD_STATUS_ALREADY_RECORDING:
> +	case CEC_OP_RECORD_STATUS_MEDIA_PROT:
> +	case CEC_OP_RECORD_STATUS_NO_SIGNAL:
> +	case CEC_OP_RECORD_STATUS_MEDIA_PROBLEM:
> +	case CEC_OP_RECORD_STATUS_NO_SPACE:
> +	case CEC_OP_RECORD_STATUS_PARENTAL_LOCK:
> +	case CEC_OP_RECORD_STATUS_OTHER:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
>  /* System Information */
>  
>  int system_info_polling(struct node *node, unsigned me, unsigned la, bool interactive)
> @@ -1141,24 +1204,14 @@ static const vec_remote_subtests tuner_ctl_subtests{
>  
>  /* One Touch Record */
>  
> -/*
> -  TODO: These are very rudimentary tests which should be expanded.
> -
> -  - The HDMI CEC 1.4b spec details that Standby shall not be acted upon while the
> -    device is recording, but it should remember that it received Standby.
> - */
> -
>  static int one_touch_rec_tv_screen(struct node *node, unsigned me, unsigned la, bool interactive)
>  {
> -	/*
> -	  TODO:
> -	  - Page 36 in HDMI CEC 1.4b spec lists additional behaviors that should be
> -	    checked for.
> -	  - The TV should ignore this message when received from other LA than Recording or
> -	    Reserved.
> -	 */
>  	struct cec_msg msg;
>  
> +	cec_msg_init(&msg, me, la);
> +	cec_msg_report_physical_addr(&msg, node->phys_addr, node->prim_devtype);
> +	fail_on_test(!transmit_timeout(node, &msg));
> +
>  	cec_msg_init(&msg, me, la);
>  	cec_msg_record_tv_screen(&msg, true);
>  	fail_on_test(!transmit_timeout(node, &msg));
> @@ -1172,45 +1225,283 @@ static int one_touch_rec_tv_screen(struct node *node, unsigned me, unsigned la,
>  		return OK_REFUSED;
>  	if (cec_msg_status_is_abort(&msg))
>  		return OK_PRESUMED;
> +	/* Follower should ignore this message if it is not sent by a recording device */
> +	if (node->prim_devtype != CEC_OP_PRIM_DEVTYPE_RECORD) {
> +		fail_on_test(!timed_out(&msg));
> +		return OK;
> +	}
> +	fail_on_test(timed_out(&msg));
>  
> -	return 0;
> +	struct cec_op_record_src rec_src = {};
> +
> +	cec_ops_record_on(&msg, &rec_src);
> +
> +	if (rec_src.type < CEC_OP_RECORD_SRC_OWN || rec_src.type > CEC_OP_RECORD_SRC_EXT_PHYS_ADDR)
> +		return fail("Invalid source.\n");

I would use the normal fail_on_test for this.

> +
> +	if (rec_src.type == CEC_OP_RECORD_SRC_DIGITAL &&
> +	    rec_src.digital.service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID) {
> +		switch (rec_src.digital.dig_bcast_system) {
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
> +		case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
> +			break;
> +		default:
> +			return fail("Invalid digital service broadcast system operand.\n");
> +		}
> +	}
> +
> +	if (rec_src.type == CEC_OP_RECORD_SRC_ANALOG) {
> +		fail_on_test(rec_src.analog.ana_bcast_type > CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL);
> +		fail_on_test(rec_src.analog.bcast_system > CEC_OP_BCAST_SYSTEM_PAL_DK &&
> +		             rec_src.analog.bcast_system != CEC_OP_BCAST_SYSTEM_OTHER);
> +		fail_on_test(rec_src.analog.ana_freq == 0 || rec_src.analog.ana_freq == 0xffff);
> +	}
> +
> +	if (rec_src.type == CEC_OP_RECORD_SRC_EXT_PLUG)
> +		fail_on_test(rec_src.ext_plug.plug == 0);
> +
> +	return OK;
>  }
>  
>  static int one_touch_rec_on(struct node *node, unsigned me, unsigned la, bool interactive)
>  {
> -	/*
> -	  TODO: Page 36 in HDMI CEC 1.4b spec lists additional behaviors that should be
> -	  checked for.
> -	 */
>  	struct cec_msg msg;
>  	struct cec_op_record_src rec_src = {};
>  
>  	rec_src.type = CEC_OP_RECORD_SRC_OWN;
>  	cec_msg_init(&msg, me, la);
>  	cec_msg_record_on(&msg, true, &rec_src);
> -	fail_on_test(!transmit_timeout(node, &msg));
> +	/* Allow 10s for reply because the spec says it may take several seconds to accurately respond. */
> +	fail_on_test(!transmit_timeout(node, &msg, 10000));
>  	fail_on_test(timed_out(&msg));
> -	fail_on_test(cec_has_record(1 << la) && unrecognized_op(&msg));
> -	if (unrecognized_op(&msg))
> +	if (unrecognized_op(&msg)) {
> +		fail_on_test(node->remote[la].prim_type == CEC_OP_PRIM_DEVTYPE_RECORD);
>  		return OK_NOT_SUPPORTED;
> +	}
>  	if (refused(&msg))
>  		return OK_REFUSED;
>  	if (cec_msg_status_is_abort(&msg))
>  		return OK_PRESUMED;
>  
> -	return 0;
> +	__u8 rec_status;
> +
> +	cec_ops_record_status(&msg, &rec_status);
> +	if (rec_status != CEC_OP_RECORD_STATUS_CUR_SRC)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +

Add a comment here stating that these digital services are taken from the
cec-follower tuner emulation.

I would also order these tests by broadcast system: so first test the ARIB variants,
then ATSC, then DVB.

> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
> +	rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
> +	rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS;
> +	rec_src.digital.arib.transport_id = 1046;
> +	rec_src.digital.arib.service_id = 30505;
> +	rec_src.digital.arib.orig_network_id = 1;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_DIG_SERVICE)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
> +	rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
> +	rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T;
> +	rec_src.digital.atsc.transport_id = 1675;
> +	rec_src.digital.atsc.program_number = 3;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_DIG_SERVICE)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
> +	rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
> +	rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2;
> +	rec_src.digital.dvb.transport_id = 61;
> +	rec_src.digital.dvb.service_id = 7193;
> +	rec_src.digital.dvb.orig_network_id = 70;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_DIG_SERVICE)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
> +	rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
> +	rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T;
> +	rec_src.digital.channel.channel_number_fmt = 1;
> +	rec_src.digital.channel.major = 0;
> +	rec_src.digital.channel.minor = 51992;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_DIG_SERVICE)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
> +	rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
> +	rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT;
> +	rec_src.digital.channel.channel_number_fmt = 2;
> +	rec_src.digital.channel.major = 3;
> +	rec_src.digital.channel.minor = 55295;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_DIG_SERVICE)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
> +	rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
> +	rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T;
> +	rec_src.digital.channel.channel_number_fmt = 1;
> +	rec_src.digital.channel.major = 0;
> +	rec_src.digital.channel.minor = 21;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_DIG_SERVICE)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +

Add a comment here as well, mentioning that these channels are based on what
the cec-follower emulates.

> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_STATUS_ANA_SERVICE;
> +	rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_CABLE;
> +	rec_src.analog.ana_freq = (471250 * 10) / 625;
> +	rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_PAL_BG;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_ANA_SERVICE)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_STATUS_ANA_SERVICE;
> +	rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_SATELLITE;
> +	rec_src.analog.ana_freq = (551250 * 10) / 625;
> +	rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_SECAM_BG;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_ANA_SERVICE)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_STATUS_ANA_SERVICE;
> +	rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL;
> +	rec_src.analog.ana_freq = (185250 * 10) / 625;
> +	rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_PAL_DK;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_ANA_SERVICE)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_EXT_PLUG;
> +	rec_src.ext_plug.plug = 1;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_EXT_INPUT)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_EXT_PHYS_ADDR;
> +	fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
> +	if (rec_status != CEC_OP_RECORD_STATUS_EXT_INPUT)
> +		fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
> +
> +	return OK;
>  }
>  
> -static int one_touch_rec_off(struct node *node, unsigned me, unsigned la, bool interactive)
> +static int one_touch_rec_on_invalid(struct node *node, unsigned me, unsigned la, bool interactive)
>  {
>  	struct cec_msg msg;
>  
>  	cec_msg_init(&msg, me, la);
> -	cec_msg_record_off(&msg, false);
> +	cec_msg_record_on_own(&msg);
> +	msg.msg[2] = 0;  /* Invalid source operand */
>  	fail_on_test(!transmit_timeout(node, &msg));
> -	fail_on_test(cec_has_record(1 << la) && unrecognized_op(&msg));
>  	if (unrecognized_op(&msg))
>  		return OK_NOT_SUPPORTED;
> +	fail_on_test(!cec_msg_status_is_abort(&msg));
> +	fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
> +
> +	cec_msg_init(&msg, me, la);
> +	cec_msg_record_on_own(&msg);
> +	msg.msg[2] = 6;  /* Invalid source operand */
> +	fail_on_test(!transmit_timeout(node, &msg));
> +	fail_on_test(!cec_msg_status_is_abort(&msg));
> +	fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
> +
> +	struct cec_op_record_src rec_src = {};
> +
> +	rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
> +	rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
> +	rec_src.digital.dig_bcast_system = 0x7f; /* Invalid digital service broadcast system */
> +	rec_src.digital.channel.channel_number_fmt = 1;
> +	rec_src.digital.channel.major = 0;
> +	rec_src.digital.channel.minor = 30203;
> +	fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
> +	rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
> +	rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT;
> +	rec_src.digital.atsc.transport_id = 0; /* Invalid transport id */
> +	rec_src.digital.atsc.program_number = 50316;
> +	fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
> +	rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
> +	rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T;
> +	rec_src.digital.dvb.transport_id = 1004;
> +	rec_src.digital.dvb.service_id = 0; /* Invalid service id */
> +	rec_src.digital.dvb.orig_network_id = 8945;
> +	fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_ANALOG;
> +	rec_src.analog.ana_bcast_type = 0xff; /* Invalid analog broadcast type */
> +	rec_src.analog.ana_freq = (519250 * 10) / 625;
> +	rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_PAL_BG;
> +	fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_ANALOG;
> +	rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL;
> +	rec_src.analog.ana_freq = 0; /* Invalid analog frequency */
> +	rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_NTSC_M;
> +	fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_ANALOG;
> +	rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_CABLE;
> +	rec_src.analog.ana_freq = 0xffff; /* Invalid analog frequency */
> +	rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_SECAM_L;
> +	fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_ANALOG;
> +	rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_SATELLITE;
> +	rec_src.analog.ana_freq = (703250 * 10) / 625;
> +	rec_src.analog.bcast_system = 0xff; /* Invalid analog broadcast system */
> +	fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
> +
> +	memset(&rec_src, 0, sizeof(rec_src));
> +	rec_src.type = CEC_OP_RECORD_SRC_EXT_PLUG;
> +	rec_src.ext_plug.plug = 0; /* Invalid plug */
> +	fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
> +
> +	return OK;
> +}
> +
> +static int one_touch_rec_off(struct node *node, unsigned me, unsigned la, bool interactive)
> +{
> +	struct cec_msg msg;
> +
> +	cec_msg_init(&msg, me, la);
> +	cec_msg_record_off(&msg, true);
> +	fail_on_test(!transmit_timeout(node, &msg));

Is a 10s timeout also needed for <Record Off>? It sounds reasonable.

> +	if (unrecognized_op(&msg)) {
> +		fail_on_test(node->remote[la].prim_type == CEC_OP_PRIM_DEVTYPE_RECORD);
> +		return OK_NOT_SUPPORTED;
> +	}
>  	if (refused(&msg))
>  		return OK_REFUSED;
>  	if (cec_msg_status_is_abort(&msg))
> @@ -1218,13 +1509,30 @@ static int one_touch_rec_off(struct node *node, unsigned me, unsigned la, bool i
>  	if (timed_out(&msg))
>  		return OK_PRESUMED;
>  
> -	return 0;
> +	__u8 rec_status;
> +
> +	cec_ops_record_status(&msg, &rec_status);
> +
> +	fail_on_test(rec_status != CEC_OP_RECORD_STATUS_TERMINATED_OK &&
> +	             rec_status != CEC_OP_RECORD_STATUS_ALREADY_TERM);
> +
> +	return OK;
>  }
>  
>  static const vec_remote_subtests one_touch_rec_subtests{
>  	{ "Record TV Screen", CEC_LOG_ADDR_MASK_TV, one_touch_rec_tv_screen },
> -	{ "Record On", CEC_LOG_ADDR_MASK_RECORD, one_touch_rec_on },
> -	{ "Record Off", CEC_LOG_ADDR_MASK_RECORD, one_touch_rec_off },
> +	{
> +		"Record On",
> +		CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
> +		one_touch_rec_on,
> +	},
> +	{
> +		"Record On Invalid Operand",
> +		CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
> +		one_touch_rec_on_invalid,
> +	},
> +	{ "Record Off", CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP, one_touch_rec_off },
> +
>  };
>  
>  /* Timer Programming */
> diff --git a/utils/cec-follower/cec-follower.cpp b/utils/cec-follower/cec-follower.cpp
> index ff47d698..482192e7 100644
> --- a/utils/cec-follower/cec-follower.cpp
> +++ b/utils/cec-follower/cec-follower.cpp
> @@ -317,6 +317,7 @@ void state_init(struct node &node)
>  	node.state.deck_report_changes_to = 0;
>  	node.state.deck_state = CEC_OP_DECK_INFO_STOP;
>  	node.state.deck_skip_start = 0;
> +	node.state.one_touch_record_on = false;
>  	tuner_dev_info_init(&node.state);
>  	node.state.last_aud_rate_rx_ts = 0;
>  }
> diff --git a/utils/cec-follower/cec-follower.h b/utils/cec-follower/cec-follower.h
> index 343ae998..589ea6bf 100644
> --- a/utils/cec-follower/cec-follower.h
> +++ b/utils/cec-follower/cec-follower.h
> @@ -53,6 +53,7 @@ struct state {
>  	__u8 deck_report_changes_to;
>  	__u8 deck_state;
>  	__u64 deck_skip_start;
> +	bool one_touch_record_on;
>  	time_t toggle_power_status;
>  	__u64 last_aud_rate_rx_ts;
>  };
> @@ -62,6 +63,7 @@ struct node {
>  	const char *device;
>  	unsigned caps;
>  	unsigned available_log_addrs;
> +	__u8 remote_prim_devtype;
>  	unsigned adap_la_mask;
>  	unsigned remote_la_mask;
>  	__u16 remote_phys_addr[15];
> @@ -74,7 +76,6 @@ struct node {
>  	bool has_deck_ctl;
>  	bool has_rec_tv;
>  	bool has_osd_string;
> -

Please keep this newline.

>  	bool ignore_la[16];
>  	unsigned short ignore_opcode[256];
>  };
> diff --git a/utils/cec-follower/cec-processing.cpp b/utils/cec-follower/cec-processing.cpp
> index b1c8f3d9..f985a841 100644
> --- a/utils/cec-follower/cec-processing.cpp
> +++ b/utils/cec-follower/cec-processing.cpp
> @@ -396,8 +396,10 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me, __u8
>  		__u8 prim_dev_type;
>  
>  		cec_ops_report_physical_addr(&msg, &phys_addr, &prim_dev_type);
> -		if (from < 15)
> +		if (from < 15) {
>  			node->remote_phys_addr[from] = phys_addr;
> +			node->remote_prim_devtype = prim_dev_type;
> +		}
>  		return;
>  	}
>  
> diff --git a/utils/cec-follower/cec-tuner.cpp b/utils/cec-follower/cec-tuner.cpp
> index d1718986..4ecb62a6 100644
> --- a/utils/cec-follower/cec-tuner.cpp
> +++ b/utils/cec-follower/cec-tuner.cpp
> @@ -482,6 +482,155 @@ static bool analog_set_tuner_dev_info(struct node *node, struct cec_msg *msg)
>  	return false;
>  }
>  
> +static bool digital_service_info_valid(const struct cec_op_record_src &rec_src)
> +{
> +	switch (rec_src.digital.dig_bcast_system) {
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
> +		return true;
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
> +		if (rec_src.digital.service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID) {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.arib.transport_id == digital_arib_data[0][i].tsid &&
> +				    rec_src.digital.arib.service_id == digital_arib_data[0][i].sid &&
> +				    rec_src.digital.arib.orig_network_id == digital_arib_data[0][i].onid)
> +				    return true;
> +			}
> +		} else {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.channel.channel_number_fmt == digital_arib_data[0][i].fmt &&
> +				    rec_src.digital.channel.major == digital_arib_data[0][i].major &&
> +				    rec_src.digital.channel.minor == digital_arib_data[0][i].minor)
> +				    return true;
> +			}
> +		}
> +		return false;

I think you are mixing up two different things here: detecting whether one of the
operands is invalid, and whether the operands are valid, but do not match one of
the emulated channels/services.

In the first case you Feature Abort with Invalid Op, in the second case the recording
status changes to an error (CEC_OP_RECORD_STATUS_NO_DIG/ANA_SERVICE).

For the second case (validating service information), you should be able to use
existing functions in cec-tuner.cpp, I think at least digital_get_service_idx() can
be used. I'm not sure about analog_get_nearest_service_idx(): getting the closest
analog channel may not be appropriate.

> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
> +		if (rec_src.digital.service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID) {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.arib.transport_id == digital_arib_data[1][i].tsid &&
> +				    rec_src.digital.arib.service_id == digital_arib_data[1][i].sid &&
> +				    rec_src.digital.arib.orig_network_id == digital_arib_data[1][i].onid)
> +				    return true;
> +			}
> +		} else {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.channel.channel_number_fmt == digital_arib_data[1][i].fmt &&
> +				    rec_src.digital.channel.major == digital_arib_data[1][i].major &&
> +				    rec_src.digital.channel.minor == digital_arib_data[1][i].minor)
> +				    return true;
> +			}
> +		}
> +		return false;
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
> +		if (rec_src.digital.service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID) {
> +
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.atsc.transport_id == digital_atsc_data[0][i].tsid &&
> +				    rec_src.digital.atsc.program_number == digital_atsc_data[0][i].sid)
> +				    return true;
> +			}
> +		} else {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.channel.channel_number_fmt == digital_atsc_data[0][i].fmt &&
> +				    rec_src.digital.channel.major == digital_atsc_data[0][i].major &&
> +				    rec_src.digital.channel.minor == digital_atsc_data[0][i].minor)
> +				    return true;
> +			}
> +		}
> +		return false;
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
> +		if (rec_src.digital.service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID) {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.atsc.transport_id == digital_atsc_data[1][i].tsid &&
> +				    rec_src.digital.atsc.program_number == digital_atsc_data[1][i].sid)
> +				    return true;
> +			}
> +		} else {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.channel.channel_number_fmt == digital_atsc_data[1][i].fmt &&
> +				    rec_src.digital.channel.major == digital_atsc_data[1][i].major &&
> +				    rec_src.digital.channel.minor == digital_atsc_data[1][i].minor)
> +				    return true;
> +			}
> +		}
> +		return false;
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
> +		if (rec_src.digital.service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID) {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +				if (rec_src.digital.dvb.transport_id == digital_dvb_data[0][i].tsid &&
> +				    rec_src.digital.dvb.service_id == digital_dvb_data[0][i].sid &&
> +				    rec_src.digital.dvb.orig_network_id == digital_dvb_data[0][i].onid)
> +				    return true;
> +			}
> +		} else {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.channel.channel_number_fmt == digital_dvb_data[0][i].fmt &&
> +				    rec_src.digital.channel.major == digital_dvb_data[0][i].major &&
> +				    rec_src.digital.channel.minor == digital_dvb_data[0][i].minor)
> +				    return true;
> +			}
> +		}
> +		return false;
> +	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
> +		if (rec_src.digital.service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID) {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.dvb.transport_id == digital_dvb_data[1][i].tsid &&
> +				    rec_src.digital.dvb.service_id == digital_dvb_data[1][i].sid &&
> +				    rec_src.digital.dvb.orig_network_id == digital_dvb_data[1][i].onid)
> +				    return true;
> +			}
> +		} else {
> +			for (unsigned i = 0; i < NUM_DIGITAL_CHANS; i++) {
> +
> +				if (rec_src.digital.channel.channel_number_fmt == digital_dvb_data[1][i].fmt &&
> +				    rec_src.digital.channel.major == digital_dvb_data[1][i].major &&
> +				    rec_src.digital.channel.minor == digital_dvb_data[1][i].minor)
> +				    return true;
> +			}
> +		}
> +		return false;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool analog_service_info_valid(const cec_op_record_src &rec_src)
> +{
> +	__u8 bcast_type = rec_src.analog.ana_bcast_type;
> +	unsigned freq = (rec_src.analog.ana_freq * 625) / 10;
> +	__u8 bcast_system = rec_src.analog.bcast_system;
> +
> +	if (bcast_type > CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL)
> +		return false;
> +
> +	if (bcast_system > CEC_OP_BCAST_SYSTEM_PAL_DK && bcast_system != CEC_OP_BCAST_SYSTEM_OTHER)
> +		return false;
> +
> +	for (unsigned i = 0; i < NUM_ANALOG_FREQS; i++) {
> +
> +		if (freq == analog_freqs_khz[bcast_type][bcast_system][i])
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
>  void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, unsigned me, __u8 type)
>  {
>  	bool is_bcast = cec_msg_is_broadcast(&msg);
> @@ -577,23 +726,16 @@ void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, uns
>  		return;
>  	}
>  
> -		/*
> -		  One Touch Record
> -
> -		  This is only a basic implementation.
> -
> -		  TODO:
> -		  - If we are a TV, we should only send Record On if the
> -		    remote end is a Recording device or Reserved. Otherwise ignore.
> -
> -		  - Device state should reflect whether we are recording, etc. In
> -		    recording mode we should ignore Standby messages.
> -		*/
> +		/* One Touch Record */
>  
>  	case CEC_MSG_RECORD_TV_SCREEN: {
>  		if (!node->has_rec_tv)
>  			break;
>  
> +		/* Ignore if initiator is not a recording device */
> +		if (node->remote_prim_devtype != CEC_OP_PRIM_DEVTYPE_RECORD)
> +			return;
> +
>  		struct cec_op_record_src rec_src = {};
>  
>  		rec_src.type = CEC_OP_RECORD_SRC_OWN;
> @@ -602,24 +744,68 @@ void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, uns
>  		transmit(node, &msg);
>  		return;
>  	}
> -	case CEC_MSG_RECORD_ON:
> -		if (!cec_has_record(1 << me))
> +	case CEC_MSG_RECORD_ON: {
> +		if (type != CEC_LOG_ADDR_TYPE_RECORD)
> +			break;
> +
> +		__u8 rec_status;
> +		bool feature_abort = false;
> +		struct cec_op_record_src rec_src = {};
> +
> +		cec_ops_record_on(&msg, &rec_src);
> +		switch (rec_src.type) {
> +		case CEC_OP_RECORD_SRC_OWN:
> +			rec_status = CEC_OP_RECORD_STATUS_CUR_SRC;
> +			break;
> +		case CEC_OP_RECORD_SRC_DIGITAL:
> +			if (digital_service_info_valid(rec_src))
> +				rec_status = CEC_OP_RECORD_STATUS_DIG_SERVICE;
> +			else
> +				feature_abort = true;
> +			break;
> +		case CEC_OP_RECORD_SRC_ANALOG:
> +			if (analog_service_info_valid(rec_src))
> +				rec_status = CEC_OP_RECORD_STATUS_ANA_SERVICE;
> +			else
> +				feature_abort = true;
> +			break;
> +		case CEC_OP_RECORD_SRC_EXT_PLUG:
> +			/* Plug number range is 1-255 in spec, but a realistic range of connectors is 6. */
> +			if (rec_src.ext_plug.plug < 1 || rec_src.ext_plug.plug > 6)
> +				feature_abort = true;
> +			else
> +				rec_status = CEC_OP_RECORD_STATUS_EXT_INPUT;
> +			break;
> +		case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
> +			rec_status = CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR;
>  			break;
> +		default:
> +			feature_abort = true;
> +			break;
> +		}
> +		if (feature_abort) {
> +			reply_feature_abort(node, &msg, CEC_OP_ABORT_INVALID_OP);
> +			return;
> +		}
> +		if (node->state.one_touch_record_on)
> +			rec_status = CEC_OP_RECORD_STATUS_ALREADY_RECORDING;
>  		cec_msg_set_reply_to(&msg, &msg);
> -		cec_msg_record_status(&msg, CEC_OP_RECORD_STATUS_CUR_SRC);
> +		cec_msg_record_status(&msg, rec_status);
>  		transmit(node, &msg);
> +		node->state.one_touch_record_on = true;
>  		return;
> +	}
>  	case CEC_MSG_RECORD_OFF:
> -		if (!cec_has_record(1 << me))
> +		if (type != CEC_LOG_ADDR_TYPE_RECORD)
>  			break;
> +
>  		cec_msg_set_reply_to(&msg, &msg);
>  		cec_msg_record_status(&msg, CEC_OP_RECORD_STATUS_TERMINATED_OK);
>  		transmit(node, &msg);
> +		node->state.one_touch_record_on = false;
>  		return;
>  	case CEC_MSG_RECORD_STATUS:
>  		return;
> -
> -
>  		/*
>  		  Timer Programming
>  
> 

Regards,

	Hans



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux