aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Fjeldtvedt <jaffe1@gmail.com>2016-07-18 15:55:49 +0200
committerHans Verkuil <hans.verkuil@cisco.com>2016-07-18 16:11:22 +0200
commit347fbc8e448b15ecba7351a53889afde0ddb4193 (patch)
treec2cea7301c83c087db77ef061aa42cd7ddbb7fa6
parent4a467f0f126e255b4202e23784be702eedc074de (diff)
cec-follower: check timing of RC messages
This adds some checks for the Remote Control Passthrough messages, to give information and warnings about the behavior of the DUT. Signed-off-by: Johan Fjeldtvedt <jaffe1@gmail.com> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
-rw-r--r--utils/cec-follower/cec-follower.h10
-rw-r--r--utils/cec-follower/cec-processing.cpp185
2 files changed, 195 insertions, 0 deletions
diff --git a/utils/cec-follower/cec-follower.h b/utils/cec-follower/cec-follower.h
index 836b60ac..e010d02f 100644
--- a/utils/cec-follower/cec-follower.h
+++ b/utils/cec-follower/cec-follower.h
@@ -51,6 +51,11 @@ struct state {
bool sac_active;
__u8 volume;
bool mute;
+ unsigned rc_state;
+ __u8 rc_ui_cmd;
+ __u64 rc_press_rx_ts;
+ unsigned rc_press_hold_count;
+ unsigned rc_duration_sum;
};
struct node {
@@ -171,6 +176,11 @@ int cec_named_ioctl(int fd, const char *name,
#define transmit(n, m) (doioctl(n, CEC_TRANSMIT, m))
+static inline unsigned ts_to_ms(__u64 ts)
+{
+ return ts / 1000000;
+}
+
const char *la2s(unsigned la);
const char *la_type2s(unsigned type);
const char *prim_type2s(unsigned type);
diff --git a/utils/cec-follower/cec-processing.cpp b/utils/cec-follower/cec-processing.cpp
index 8b9097ee..79ca9018 100644
--- a/utils/cec-follower/cec-processing.cpp
+++ b/utils/cec-follower/cec-processing.cpp
@@ -49,9 +49,124 @@
#define VOLUME_MAX 0x64
#define VOLUME_MIN 0
+/* States for the RC handling */
+#define NOPRESS 0
+#define PRESS 1
+#define PRESS_HOLD 2
+
+/* The follower safety timeout as defined in the spec */
+#define FOLLOWER_SAFETY_TIMEOUT 450
+#define MIN_INITIATOR_REP_TIME 200
+#define MAX_INITIATOR_REP_TIME 500
+
#define ARRAY_SIZE(a) \
(sizeof(a) / sizeof(*a))
+struct cec_enum_values {
+ const char *type_name;
+ __u8 value;
+};
+
+static const struct cec_enum_values type_ui_cmd[] = {
+ { "Select", 0x00 },
+ { "Up", 0x01 },
+ { "Down", 0x02 },
+ { "Left", 0x03 },
+ { "Right", 0x04 },
+ { "Right-Up", 0x05 },
+ { "Right-Down", 0x06 },
+ { "Left-Up", 0x07 },
+ { "Left-Down", 0x08 },
+ { "Device Root Menu", 0x09 },
+ { "Device Setup Menu", 0x0a },
+ { "Contents Menu", 0x0b },
+ { "Favorite Menu", 0x0c },
+ { "Back", 0x0d },
+ { "Media Top Menu", 0x10 },
+ { "Media Context-sensitive Menu", 0x11 },
+ { "Number Entry Mode", 0x1d },
+ { "Number 11", 0x1e },
+ { "Number 12", 0x1f },
+ { "Number 0 or Number 10", 0x20 },
+ { "Number 1", 0x21 },
+ { "Number 2", 0x22 },
+ { "Number 3", 0x23 },
+ { "Number 4", 0x24 },
+ { "Number 5", 0x25 },
+ { "Number 6", 0x26 },
+ { "Number 7", 0x27 },
+ { "Number 8", 0x28 },
+ { "Number 9", 0x29 },
+ { "Dot", 0x2a },
+ { "Enter", 0x2b },
+ { "Clear", 0x2c },
+ { "Next Favorite", 0x2f },
+ { "Channel Up", 0x30 },
+ { "Channel Down", 0x31 },
+ { "Previous Channel", 0x32 },
+ { "Sound Select", 0x33 },
+ { "Input Select", 0x34 },
+ { "Display Information", 0x35 },
+ { "Help", 0x36 },
+ { "Page Up", 0x37 },
+ { "Page Down", 0x38 },
+ { "Power", 0x40 },
+ { "Volume Up", 0x41 },
+ { "Volume Down", 0x42 },
+ { "Mute", 0x43 },
+ { "Play", 0x44 },
+ { "Stop", 0x45 },
+ { "Pause", 0x46 },
+ { "Record", 0x47 },
+ { "Rewind", 0x48 },
+ { "Fast forward", 0x49 },
+ { "Eject", 0x4a },
+ { "Skip Forward", 0x4b },
+ { "Skip Backward", 0x4c },
+ { "Stop-Record", 0x4d },
+ { "Pause-Record", 0x4e },
+ { "Angle", 0x50 },
+ { "Sub picture", 0x51 },
+ { "Video on Demand", 0x52 },
+ { "Electronic Program Guide", 0x53 },
+ { "Timer Programming", 0x54 },
+ { "Initial Configuration", 0x55 },
+ { "Select Broadcast Type", 0x56 },
+ { "Select Sound Presentation", 0x57 },
+ { "Audio Description", 0x58 },
+ { "Internet", 0x59 },
+ { "3D Mode", 0x5a },
+ { "Play Function", 0x60 },
+ { "Pause-Play Function", 0x61 },
+ { "Record Function", 0x62 },
+ { "Pause-Record Function", 0x63 },
+ { "Stop Function", 0x64 },
+ { "Mute Function", 0x65 },
+ { "Restore Volume Function", 0x66 },
+ { "Tune Function", 0x67 },
+ { "Select Media Function", 0x68 },
+ { "Select A/V Input Function", 0x69 },
+ { "Select Audio Input Function", 0x6a },
+ { "Power Toggle Function", 0x6b },
+ { "Power Off Function", 0x6c },
+ { "Power On Function", 0x6d },
+ { "F1 (Blue)", 0x71 },
+ { "F2 (Red)", 0x72 },
+ { "F3 (Green)", 0x73 },
+ { "F4 (Yellow)", 0x74 },
+ { "F5", 0x75 },
+ { "Data", 0x76 },
+};
+
+static const char *get_ui_cmd_string(__u8 ui_cmd)
+{
+ for (unsigned i = 0; i < ARRAY_SIZE(type_ui_cmd); i++) {
+ if (type_ui_cmd[i].value == ui_cmd)
+ return type_ui_cmd[i].type_name;
+ }
+ return "Unknown";
+}
+
static void log_event(struct cec_event &ev)
{
__u16 pa;
@@ -110,6 +225,28 @@ static bool enter_standby(struct node *node)
return false;
}
+static unsigned get_duration_ms(__u64 ts_a, __u64 ts_b)
+{
+ return (ts_a - ts_b) / 1000000;
+}
+
+static void rc_press_hold_stop(const struct state *state)
+{
+ unsigned mean_duration = state->rc_duration_sum / state->rc_press_hold_count;
+
+ dev_info("Stop Press and Hold. Mean duration between User Control Pressed messages: %dms\n",
+ mean_duration);
+ if (mean_duration < MIN_INITIATOR_REP_TIME) {
+ warn("The mean duration between User Control Pressed messages is lower\n");
+ warn("than the Minimum Initiator Repetition Time (200ms).\n");
+ }
+ if (mean_duration > MAX_INITIATOR_REP_TIME) {
+ warn("The mean duration between User Control Pressed messages is higher\n");
+ warn("than the Maximum Initiator Repetition Time (500ms).\n");
+ }
+}
+
+
static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
{
__u8 to = cec_msg_destination(&msg);
@@ -228,8 +365,47 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
case CEC_MSG_USER_CONTROL_PRESSED: {
struct cec_op_ui_command rc_press;
+ unsigned new_state;
+ unsigned duration;
cec_ops_user_control_pressed(&msg, &rc_press);
+ duration = get_duration_ms(msg.rx_ts, node->state.rc_press_rx_ts);
+
+ new_state = PRESS;
+ if (node->state.rc_state == NOPRESS)
+ dev_info("Button press: %s\n", get_ui_cmd_string(rc_press.ui_cmd));
+ else if (rc_press.ui_cmd != node->state.rc_ui_cmd) {
+ /* We have not yet received User Control Released, but have received
+ another User Control Pressed with a different UI Command. */
+ if (node->state.rc_state == PRESS_HOLD)
+ rc_press_hold_stop(&node->state);
+ dev_info("Button press (no User Control Released between): %s\n",
+ get_ui_cmd_string(rc_press.ui_cmd));
+
+ /* The device shall send User Control Released if the time between
+ two messages is longer than the maximum Initiator Repetition Time. */
+ if (duration > MAX_INITIATOR_REP_TIME)
+ warn("Device waited more than the maximum Initiatior Repetition Time and should have sent a User Control Released message.");
+ } else {
+ /* We have not yet received a User Control Released, but received
+ another User Control Pressed, with the same UI Command as the
+ previous, which means that the Press and Hold behavior should
+ be invoked. */
+ new_state = PRESS_HOLD;
+ if (node->state.rc_state != PRESS_HOLD) {
+ dev_info("Start Press and Hold with button %s\n",
+ get_ui_cmd_string(rc_press.ui_cmd));
+ node->state.rc_duration_sum = 0;
+ node->state.rc_press_hold_count = 0;
+ }
+ node->state.rc_duration_sum += duration;
+ node->state.rc_press_hold_count++;
+ }
+
+ node->state.rc_state = new_state;
+ node->state.rc_ui_cmd = rc_press.ui_cmd;
+ node->state.rc_press_rx_ts = msg.rx_ts;
+
switch (rc_press.ui_cmd) {
case 0x41:
if (node->state.volume < VOLUME_MAX)
@@ -263,6 +439,15 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
return;
}
case CEC_MSG_USER_CONTROL_RELEASED:
+ if (node->state.rc_state == PRESS_HOLD)
+ rc_press_hold_stop(&node->state);
+
+ if (node->state.rc_state == NOPRESS)
+ warn("Unexpected User Control Released\n");
+ else if (get_duration_ms(msg.rx_ts, node->state.rc_press_rx_ts) > FOLLOWER_SAFETY_TIMEOUT) {
+ warn("User Control Released received after Follower Safety Timeout period.\n");
+ }
+ node->state.rc_state = NOPRESS;
return;

Privacy Policy