| 1 | /* |
| 2 | Copyright (c) 2012, Broadcom Europe Ltd |
| 3 | All rights reserved. |
| 4 | |
| 5 | Redistribution and use in source and binary forms, with or without |
| 6 | modification, are permitted provided that the following conditions are met: |
| 7 | * Redistributions of source code must retain the above copyright |
| 8 | notice, this list of conditions and the following disclaimer. |
| 9 | * Redistributions in binary form must reproduce the above copyright |
| 10 | notice, this list of conditions and the following disclaimer in the |
| 11 | documentation and/or other materials provided with the distribution. |
| 12 | * Neither the name of the copyright holder nor the |
| 13 | names of its contributors may be used to endorse or promote products |
| 14 | derived from this software without specific prior written permission. |
| 15 | |
| 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
| 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | |
| 28 | |
| 29 | // ---- Include Files ------------------------------------------------------- |
| 30 | |
| 31 | #include <stdio.h> |
| 32 | #include <stdlib.h> |
| 33 | #include <unistd.h> |
| 34 | #include <getopt.h> |
| 35 | #include <errno.h> |
| 36 | #include <signal.h> |
| 37 | #include <assert.h> |
| 38 | #include <string.h> |
| 39 | |
| 40 | #include "interface/vmcs_host/vc_tvservice.h" |
| 41 | |
| 42 | #define TV_SUPPORTED_MODE_T TV_SUPPORTED_MODE_NEW_T |
| 43 | #define vc_tv_hdmi_get_supported_modes vc_tv_hdmi_get_supported_modes_new |
| 44 | #define vc_tv_hdmi_power_on_explicit vc_tv_hdmi_power_on_explicit_new |
| 45 | |
| 46 | // ---- Public Variables ---------------------------------------------------- |
| 47 | |
| 48 | // ---- Private Constants and Types ----------------------------------------- |
| 49 | |
| 50 | // Logging macros (for remapping to other logging mechanisms, i.e., vcos_log) |
| 51 | #define LOG_ERR( fmt, arg... ) fprintf( stderr, "[E] " fmt "\n", ##arg ) |
| 52 | #define LOG_WARN( fmt, arg... ) fprintf( stderr, "[W] " fmt "\n", ##arg ) |
| 53 | #define LOG_INFO( fmt, arg... ) fprintf( stderr, "[I] " fmt "\n", ##arg ) |
| 54 | #define LOG_DBG( fmt, arg... ) fprintf( stdout, "[D] " fmt "\n", ##arg ) |
| 55 | |
| 56 | // Standard output log (for printing normal information to users) |
| 57 | #define LOG_STD( fmt, arg... ) fprintf( stdout, fmt "\n", ##arg ) |
| 58 | |
| 59 | // Maximum length of option string (3 characters max for each option + NULL) |
| 60 | #define OPTSTRING_LEN ( sizeof( long_opts ) / sizeof( *long_opts ) * 3 + 1 ) |
| 61 | |
| 62 | // Maximum mode ID |
| 63 | #define MAX_MODE_ID (127) |
| 64 | |
| 65 | // Maximum status string length |
| 66 | #define MAX_STATUS_STR_LENGTH (128) |
| 67 | |
| 68 | // ---- Private Variables --------------------------------------------------- |
| 69 | |
| 70 | enum |
| 71 | { |
| 72 | OPT_PREFERRED = 'p', |
| 73 | OPT_EXPLICIT = 'e', |
| 74 | OPT_NTSC = 't', |
| 75 | OPT_OFF = 'o', |
| 76 | OPT_SDTVON = 'c', |
| 77 | OPT_MODES = 'm', |
| 78 | OPT_MONITOR = 'M', |
| 79 | OPT_STATUS = 's', |
| 80 | OPT_DUMPEDID = 'd', |
| 81 | OPT_AUDIOSUP = 'a', |
| 82 | OPT_SHOWINFO = 'i', |
| 83 | OPT_JSON = 'j', |
| 84 | OPT_HELP = 'h', |
| 85 | OPT_NAME = 'n', |
| 86 | OPT_DEVICE = 'v', |
| 87 | OPT_LIST = 'l', |
| 88 | // Options from this point onwards don't have any short option equivalents |
| 89 | OPT_FIRST_LONG_OPT = 0x80, |
| 90 | }; |
| 91 | |
| 92 | static struct option long_opts[] = |
| 93 | { |
| 94 | // name has_arg flag val |
| 95 | // ------------------- ------------------ ---- --------------- |
| 96 | { "preferred" , no_argument, NULL, OPT_PREFERRED }, |
| 97 | { "explicit" , required_argument, NULL, OPT_EXPLICIT }, |
| 98 | { "ntsc" , no_argument, NULL, OPT_NTSC }, |
| 99 | { "off" , no_argument, NULL, OPT_OFF }, |
| 100 | { "sdtvon" , required_argument, NULL, OPT_SDTVON }, |
| 101 | { "modes" , required_argument, NULL, OPT_MODES }, |
| 102 | { "monitor" , no_argument, NULL, OPT_MONITOR }, |
| 103 | { "status" , no_argument, NULL, OPT_STATUS }, |
| 104 | { "dumpedid" , required_argument, NULL, OPT_DUMPEDID}, |
| 105 | { "audio" , no_argument, NULL, OPT_AUDIOSUP}, |
| 106 | { "json" , no_argument, NULL, OPT_JSON }, |
| 107 | { "info" , required_argument, NULL, OPT_SHOWINFO}, |
| 108 | { "help" , no_argument, NULL, OPT_HELP }, |
| 109 | { "name" , no_argument, NULL, OPT_NAME }, |
| 110 | { "device" , required_argument, NULL, OPT_DEVICE }, |
| 111 | { "list" , no_argument, NULL, OPT_LIST }, |
| 112 | { 0, 0, 0, 0 } |
| 113 | }; |
| 114 | |
| 115 | static VCOS_EVENT_T quit_event; |
| 116 | |
| 117 | // ---- Private Functions --------------------------------------------------- |
| 118 | |
| 119 | static void show_usage( void ) |
| 120 | { |
| 121 | LOG_STD( "Usage: tvservice [OPTION]..." ); |
| 122 | LOG_STD( " -p, --preferred Power on HDMI with preferred settings" ); |
| 123 | LOG_STD( " -e, --explicit=\"GROUP MODE DRIVE\" Power on HDMI with explicit GROUP (CEA, DMT, CEA_3D_SBS, CEA_3D_TB, CEA_3D_FP, CEA_3D_FS)\n" |
| 124 | " MODE (see --modes) and DRIVE (HDMI, DVI)" ); |
| 125 | LOG_STD( " -t, --ntsc Use NTSC frequency for HDMI mode (e.g. 59.94Hz rather than 60Hz)" ); |
| 126 | LOG_STD( " -c, --sdtvon=\"MODE ASPECT [P]\" Power on SDTV with MODE (PAL or NTSC) and ASPECT (4:3 14:9 or 16:9) Add P for progressive" ); |
| 127 | LOG_STD( " -o, --off Power off the display" ); |
| 128 | LOG_STD( " -m, --modes=GROUP Get supported modes for GROUP (CEA, DMT)" ); |
| 129 | LOG_STD( " -M, --monitor Monitor HDMI events" ); |
| 130 | LOG_STD( " -s, --status Get HDMI status" ); |
| 131 | LOG_STD( " -a, --audio Get supported audio information" ); |
| 132 | LOG_STD( " -d, --dumpedid <filename> Dump EDID information to file" ); |
| 133 | LOG_STD( " -j, --json Use JSON format for --modes output" ); |
| 134 | LOG_STD( " -n, --name Print the device ID from EDID" ); |
| 135 | LOG_STD( " -l, --list List all attached devices" ); |
| 136 | LOG_STD( " -v, --device Specify the device to use (see --list)" ); |
| 137 | LOG_STD( " -h, --help Print this information" ); |
| 138 | } |
| 139 | |
| 140 | static void create_optstring( char *optstring ) |
| 141 | { |
| 142 | char *short_opts = optstring; |
| 143 | struct option *option; |
| 144 | |
| 145 | // Figure out the short options from our options structure |
| 146 | for ( option = long_opts; option->name != NULL; option++ ) |
| 147 | { |
| 148 | if (( option->flag == NULL ) && ( option->val < OPT_FIRST_LONG_OPT )) |
| 149 | { |
| 150 | *short_opts++ = (char)option->val; |
| 151 | |
| 152 | if ( option->has_arg != no_argument ) |
| 153 | { |
| 154 | *short_opts++ = ':'; |
| 155 | } |
| 156 | |
| 157 | // Optional arguments require two ':' |
| 158 | if ( option->has_arg == optional_argument ) |
| 159 | { |
| 160 | *short_opts++ = ':'; |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | *short_opts++ = '\0'; |
| 165 | } |
| 166 | |
| 167 | /* Return the string presentation of aspect ratio */ |
| 168 | static const char *aspect_ratio_str(HDMI_ASPECT_T aspect_ratio) { |
| 169 | switch(aspect_ratio) { |
| 170 | case HDMI_ASPECT_4_3: |
| 171 | return "4:3" ; |
| 172 | case HDMI_ASPECT_14_9: |
| 173 | return "14:9" ; |
| 174 | case HDMI_ASPECT_16_9: |
| 175 | return "16:9" ; |
| 176 | case HDMI_ASPECT_5_4: |
| 177 | return "5:4" ; |
| 178 | case HDMI_ASPECT_16_10: |
| 179 | return "16:10" ; |
| 180 | case HDMI_ASPECT_15_9: |
| 181 | return "15:9" ; |
| 182 | case HDMI_ASPECT_64_27: |
| 183 | return "64:27 (21:9)" ; |
| 184 | default: |
| 185 | return "unknown AR" ; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | /* Return the string presentation of aspect ratio */ |
| 190 | static const char *aspect_ratio_sd_str(SDTV_ASPECT_T aspect_ratio) { |
| 191 | switch(aspect_ratio) { |
| 192 | case SDTV_ASPECT_4_3: |
| 193 | return "4:3" ; |
| 194 | case SDTV_ASPECT_14_9: |
| 195 | return "14:9" ; |
| 196 | case SDTV_ASPECT_16_9: |
| 197 | return "16:9" ; |
| 198 | default: |
| 199 | return "unknown AR" ; |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | //Print a string and update the offset into the status buffer |
| 204 | //Return non-zero if string is truncated, zero otherwise |
| 205 | static int status_sprintf(char *buf, size_t buflen, size_t *offset, const char *fmt, ...) { |
| 206 | int ret; |
| 207 | size_t length; |
| 208 | va_list ap; |
| 209 | va_start(ap,fmt); |
| 210 | length = (size_t) vcos_safe_vsprintf(buf, buflen, *offset, fmt, ap); |
| 211 | va_end(ap); |
| 212 | if(length >= buflen) { |
| 213 | ret = -1; |
| 214 | *offset = buflen; |
| 215 | } else { |
| 216 | ret = 0; |
| 217 | *offset = length; |
| 218 | } |
| 219 | return ret; |
| 220 | } |
| 221 | |
| 222 | /* Return the string representation of 3D support bit mask */ |
| 223 | static const char* threed_str(uint32_t struct_3d_mask, int json_output) { |
| 224 | #define THREE_D_FORMAT_NAME_MAX_LEN 10 //Including the separator |
| 225 | static const char* three_d_format_names[] = { //See HDMI_3D_STRUCT_T bit fields |
| 226 | "FP" , "F-Alt" , "L-Alt" , "SbS-Full" , |
| 227 | "Ldep" , "Ldep+Gfx" , "TopBot" , "SbS-HH" , |
| 228 | "SbS-OLOR" , "SbS-OLER" , "SbS-ELOR" , "SbS-ELER" |
| 229 | }; |
| 230 | //Longest possible string is the concatenation of all of the above |
| 231 | static char struct_desc[vcos_countof(three_d_format_names)*THREE_D_FORMAT_NAME_MAX_LEN+4] = {0}; |
| 232 | const size_t struct_desc_length = sizeof(struct_desc); |
| 233 | size_t j, added = 0, offset = 0; |
| 234 | int tmp = 0; |
| 235 | if(!json_output) |
| 236 | tmp = status_sprintf(struct_desc, struct_desc_length, &offset, "3D:" ); |
| 237 | if(struct_3d_mask) { |
| 238 | for(j = 0; !tmp && j < vcos_countof(three_d_format_names); j++) { |
| 239 | if(struct_3d_mask & (1 << j)) { |
| 240 | tmp = status_sprintf(struct_desc, struct_desc_length, &offset, json_output ? "\"%s\"," : "%s|" , three_d_format_names[j]); |
| 241 | added++; |
| 242 | } |
| 243 | } |
| 244 | if(!tmp && added > 0) |
| 245 | struct_desc[strlen(struct_desc)-1] = '\0'; //Get rid of final "|" |
| 246 | } else if(!tmp && !added && !json_output) { |
| 247 | status_sprintf(struct_desc, struct_desc_length, &offset, "none" ); |
| 248 | } |
| 249 | |
| 250 | return struct_desc; |
| 251 | } |
| 252 | |
| 253 | static int get_modes( uint32_t display_id, HDMI_RES_GROUP_T group, int json_output) |
| 254 | { |
| 255 | static TV_SUPPORTED_MODE_T supported_modes[MAX_MODE_ID]; |
| 256 | HDMI_RES_GROUP_T preferred_group; |
| 257 | uint32_t preferred_mode; |
| 258 | int num_modes; |
| 259 | int i; |
| 260 | |
| 261 | vcos_assert(( group == HDMI_RES_GROUP_CEA ) || |
| 262 | ( group == HDMI_RES_GROUP_DMT )); |
| 263 | |
| 264 | memset(supported_modes, 0, sizeof(supported_modes)); |
| 265 | |
| 266 | if (display_id != -1) |
| 267 | num_modes = vc_tv_hdmi_get_supported_modes_new_id(display_id, |
| 268 | group, supported_modes, |
| 269 | vcos_countof(supported_modes), |
| 270 | &preferred_group, |
| 271 | &preferred_mode ); |
| 272 | else |
| 273 | num_modes = vc_tv_hdmi_get_supported_modes_new(group, supported_modes, |
| 274 | vcos_countof(supported_modes), |
| 275 | &preferred_group, |
| 276 | &preferred_mode ); |
| 277 | |
| 278 | |
| 279 | if ( num_modes < 0 ) |
| 280 | { |
| 281 | LOG_ERR( "Failed to get modes" ); |
| 282 | return -1; |
| 283 | } |
| 284 | |
| 285 | if (json_output) |
| 286 | { |
| 287 | LOG_STD( "[" ); |
| 288 | } |
| 289 | else |
| 290 | { |
| 291 | LOG_STD( "Group %s has %u modes:" , |
| 292 | HDMI_RES_GROUP_NAME(group), num_modes ); |
| 293 | } |
| 294 | |
| 295 | for ( i = 0; i < num_modes; i++ ) |
| 296 | { |
| 297 | char p[8] = {0}; |
| 298 | if( supported_modes[i].pixel_rep ) |
| 299 | vcos_safe_sprintf(p, sizeof(p)-1, 0, "x%d " , supported_modes[i].pixel_rep); |
| 300 | |
| 301 | if (json_output) |
| 302 | { |
| 303 | LOG_STD( "{ \"code\":%u, \"width\":%u, \"height\":%u, \"rate\":%u, \"aspect_ratio\":\"%s\", \"scan\":\"%s\", \"3d_modes\":[%s] }%s" , |
| 304 | supported_modes[i].code, supported_modes[i].width, |
| 305 | supported_modes[i].height, supported_modes[i].frame_rate, |
| 306 | aspect_ratio_str(supported_modes[i].aspect_ratio), |
| 307 | supported_modes[i].scan_mode ? "i" : "p" , |
| 308 | supported_modes[i].struct_3d_mask ? threed_str(supported_modes[i].struct_3d_mask, 1) : "" , |
| 309 | (i+1 < num_modes) ? "," : "" ); |
| 310 | } |
| 311 | else |
| 312 | { |
| 313 | int preferred = supported_modes[i].group == preferred_group && supported_modes[i].code == preferred_mode; |
| 314 | LOG_STD( "%s mode %u: %ux%u @ %uHz %s, clock:%uMHz %s%s %s" , |
| 315 | preferred ? " (prefer)" : supported_modes[i].native ? " (native)" : " " , |
| 316 | supported_modes[i].code, supported_modes[i].width, |
| 317 | supported_modes[i].height, supported_modes[i].frame_rate, |
| 318 | aspect_ratio_str(supported_modes[i].aspect_ratio), |
| 319 | supported_modes[i].pixel_freq / 1000000U, p, |
| 320 | supported_modes[i].scan_mode ? "interlaced" : "progressive" , |
| 321 | supported_modes[i].struct_3d_mask ? threed_str(supported_modes[i].struct_3d_mask, 0) : "" ); |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | if (json_output) |
| 326 | { |
| 327 | LOG_STD( "]" ); |
| 328 | } |
| 329 | return 0; |
| 330 | } |
| 331 | |
| 332 | static const char *status_mode( TV_DISPLAY_STATE_T *tvstate ) { |
| 333 | static char mode_str[MAX_STATUS_STR_LENGTH] = {0}; |
| 334 | int tmp = 0; |
| 335 | size_t offset = 0; |
| 336 | if(tvstate->state & ( VC_HDMI_HDMI | VC_HDMI_DVI )) { |
| 337 | //HDMI or DVI on |
| 338 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, (tvstate->state & VC_HDMI_HDMI) ? "HDMI" : "DVI" ); |
| 339 | if(!tmp) { |
| 340 | //We should still have space at this point |
| 341 | switch(tvstate->display.hdmi.group) { |
| 342 | case HDMI_RES_GROUP_CEA: |
| 343 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CEA (%d)" , tvstate->display.hdmi.mode); break; |
| 344 | case HDMI_RES_GROUP_DMT: |
| 345 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " DMT (%d)" , tvstate->display.hdmi.mode); break; |
| 346 | case HDMI_RES_GROUP_CUSTOM: |
| 347 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CUSTOM" ); break; |
| 348 | default: break; |
| 349 | } |
| 350 | } |
| 351 | if(!tmp && tvstate->display.hdmi.format_3d) { |
| 352 | switch(tvstate->display.hdmi.format_3d) { |
| 353 | case HDMI_3D_FORMAT_SBS_HALF: |
| 354 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " 3D SbS" ); break; |
| 355 | case HDMI_3D_FORMAT_TB_HALF: |
| 356 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " 3D T&B" ); break; |
| 357 | case HDMI_3D_FORMAT_FRAME_PACKING: |
| 358 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " 3D FP" ); break; |
| 359 | case HDMI_3D_FORMAT_FRAME_SEQUENTIAL: |
| 360 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " 3D FS" ); break; |
| 361 | default: break; |
| 362 | } |
| 363 | } |
| 364 | if(!tmp) { |
| 365 | if (tvstate->state & VC_HDMI_HDMI) |
| 366 | //Only HDMI mode can signal pixel encoding in AVI infoframe |
| 367 | switch(tvstate->display.hdmi.pixel_encoding) { |
| 368 | case HDMI_PIXEL_ENCODING_RGB_LIMITED: |
| 369 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " RGB lim" ); break; |
| 370 | case HDMI_PIXEL_ENCODING_RGB_FULL: |
| 371 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " RGB full" ); break; |
| 372 | case HDMI_PIXEL_ENCODING_YCbCr444_LIMITED: |
| 373 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " YCbCr444 lim" ); break; |
| 374 | case HDMI_PIXEL_ENCODING_YCbCr444_FULL: |
| 375 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " YCbCr444 full" ); break; |
| 376 | case HDMI_PIXEL_ENCODING_YCbCr422_LIMITED: |
| 377 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " YCbCr422 lim" ); break; |
| 378 | case HDMI_PIXEL_ENCODING_YCbCr422_FULL: |
| 379 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " YCbCr422 full" ); break; |
| 380 | default: break; |
| 381 | } |
| 382 | else |
| 383 | //DVI will always be RGB, and CEA mode will have limited range, DMT mode full range |
| 384 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, |
| 385 | (tvstate->display.hdmi.group == HDMI_RES_GROUP_CEA)? |
| 386 | " RGB lim" : " RGB full" ); |
| 387 | } |
| 388 | |
| 389 | //This is the format's aspect ratio, not the one |
| 390 | //signaled in the AVI infoframe |
| 391 | if(!tmp) |
| 392 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " %s" , aspect_ratio_str(tvstate->display.hdmi.aspect_ratio)); |
| 393 | |
| 394 | if(!tmp &&tvstate->display.hdmi.pixel_rep) { |
| 395 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " x%d" , tvstate->display.hdmi.pixel_rep); |
| 396 | } |
| 397 | if(!tmp && tvstate->state & VC_HDMI_HDCP_AUTH) { |
| 398 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " HDCP" ); |
| 399 | } |
| 400 | } else if(tvstate->state & ( VC_SDTV_NTSC | VC_SDTV_PAL )) { |
| 401 | if(!tmp) { |
| 402 | if(tvstate->state & VC_SDTV_NTSC) { |
| 403 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, "NTSC" ); |
| 404 | } else { |
| 405 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, "PAL" ); |
| 406 | } |
| 407 | } |
| 408 | if(!tmp && tvstate->display.sdtv.cp_mode) { |
| 409 | switch(tvstate->display.sdtv.cp_mode) { |
| 410 | case SDTV_CP_MACROVISION_TYPE1: |
| 411 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision type 1" ); break; |
| 412 | case SDTV_CP_MACROVISION_TYPE2: |
| 413 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision type 2" ); break; |
| 414 | case SDTV_CP_MACROVISION_TYPE3: |
| 415 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision type 3" ); break; |
| 416 | case SDTV_CP_MACROVISION_TEST1: |
| 417 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision test 1" ); break; |
| 418 | case SDTV_CP_MACROVISION_TEST2: |
| 419 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision test 2" ); break; |
| 420 | case SDTV_CP_CGMS_COPYFREE: |
| 421 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CGMS copy free" ); break; |
| 422 | case SDTV_CP_CGMS_COPYNOMORE: |
| 423 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CGMS copy no more" ); break; |
| 424 | case SDTV_CP_CGMS_COPYONCE: |
| 425 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CGMS copy once" ); break; |
| 426 | case SDTV_CP_CGMS_COPYNEVER: |
| 427 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CGMS copy never" ); break; |
| 428 | case SDTV_CP_WSS_COPYFREE: |
| 429 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " WSS copy free" ); break; |
| 430 | case SDTV_CP_WSS_COPYRIGHT_COPYFREE: |
| 431 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " WSS (c) copy free" ); break; |
| 432 | case SDTV_CP_WSS_NOCOPY: |
| 433 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " WSS no copy" ); break; |
| 434 | case SDTV_CP_WSS_COPYRIGHT_NOCOPY: |
| 435 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " WSS (c) no copy" ); break; |
| 436 | default: break; |
| 437 | } |
| 438 | } |
| 439 | //This is the format's aspect ratio |
| 440 | tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " %s" , aspect_ratio_sd_str(tvstate->display.sdtv.display_options.aspect)); |
| 441 | } else if (tvstate->state & VC_LCD_ATTACHED_DEFAULT) { |
| 442 | status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, "LCD" ); |
| 443 | } else { |
| 444 | status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, "TV is off" ); |
| 445 | } |
| 446 | |
| 447 | return mode_str; |
| 448 | } |
| 449 | |
| 450 | |
| 451 | static int get_status(int display_id) |
| 452 | { |
| 453 | TV_DISPLAY_STATE_T tvstate; |
| 454 | |
| 455 | int ok = display_id != -1 ? vc_tv_get_display_state_id( display_id, &tvstate) : vc_tv_get_display_state(&tvstate); |
| 456 | |
| 457 | if(ok == 0) { |
| 458 | //The width/height parameters are in the same position in the union |
| 459 | //for HDMI and SDTV |
| 460 | HDMI_PROPERTY_PARAM_T property; |
| 461 | property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE; |
| 462 | |
| 463 | if (display_id != -1) |
| 464 | vc_tv_hdmi_get_property_id(display_id, &property); |
| 465 | else |
| 466 | vc_tv_hdmi_get_property(&property); |
| 467 | |
| 468 | float frame_rate = property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC ? tvstate.display.hdmi.frame_rate * (1000.0f/1001.0f) : tvstate.display.hdmi.frame_rate; |
| 469 | |
| 470 | if(tvstate.display.hdmi.width && tvstate.display.hdmi.height) { |
| 471 | LOG_STD( "state 0x%x [%s], %ux%u @ %.2fHz, %s" , tvstate.state, |
| 472 | status_mode(&tvstate), |
| 473 | tvstate.display.hdmi.width, tvstate.display.hdmi.height, |
| 474 | frame_rate, |
| 475 | tvstate.display.hdmi.scan_mode ? "interlaced" : "progressive" ); |
| 476 | } else { |
| 477 | LOG_STD( "state 0x%x [%s]" , tvstate.state, status_mode(&tvstate)); |
| 478 | } |
| 479 | } else { |
| 480 | LOG_STD( "Error getting current display state" ); |
| 481 | } |
| 482 | return 0; |
| 483 | } |
| 484 | |
| 485 | static int get_audiosup(int display_id) |
| 486 | { |
| 487 | uint8_t sample_rates[] = {32, 44, 48, 88, 96, 176, 192}; |
| 488 | uint8_t sample_sizes[] = {16, 20, 24}; |
| 489 | const char *formats[] = {"Reserved" , "PCM" , "AC3" , "MPEG1" , "MP3" , "MPEG2" , "AAC" , "DTS" , "ATRAC" , "DSD" , "EAC3" , "DTS_HD" , "MLP" , "DST" , "WMAPRO" , "Extended" }; |
| 490 | int i, j, k; |
| 491 | for (k=EDID_AudioFormat_ePCM; k<EDID_AudioFormat_eMaxCount; k++) { |
| 492 | int num_channels = 0, max_sample_rate = 0, max_sample_size = 0; |
| 493 | for (i=1; i<=8; i++) { |
| 494 | if ((display_id != -1 ? vc_tv_hdmi_audio_supported_id(display_id, k, i, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) : |
| 495 | vc_tv_hdmi_audio_supported(k, i, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit )) == 0) |
| 496 | num_channels = i; |
| 497 | } |
| 498 | for (i=0, j=EDID_AudioSampleRate_e32KHz; j<=EDID_AudioSampleRate_e192KHz; i++, j<<=1) { |
| 499 | if ((display_id != -1 ? vc_tv_hdmi_audio_supported_id(display_id, k, 1, j, EDID_AudioSampleSize_16bit ) : |
| 500 | vc_tv_hdmi_audio_supported(k, 1, j, EDID_AudioSampleSize_16bit )) == 0) |
| 501 | max_sample_rate = i; |
| 502 | } |
| 503 | if (k==EDID_AudioFormat_ePCM) { |
| 504 | for (i=0, j=EDID_AudioSampleSize_16bit; j<=EDID_AudioSampleSize_24bit; i++, j<<=1) { |
| 505 | if (display_id != -1 ? vc_tv_hdmi_audio_supported_id(display_id, k, 1, EDID_AudioSampleRate_e44KHz, j ) : |
| 506 | vc_tv_hdmi_audio_supported(k, 1, EDID_AudioSampleRate_e44KHz, j ) == 0) |
| 507 | max_sample_size = i; |
| 508 | } |
| 509 | if (num_channels>0) |
| 510 | LOG_STD("%8s supported: Max channels: %d, Max samplerate:%4dkHz, Max samplesize %2d bits." , formats[k], num_channels, sample_rates[max_sample_rate], sample_sizes[max_sample_size]); |
| 511 | } else { |
| 512 | for (i=0; i<256; i++) { |
| 513 | if (display_id != -1 ? vc_tv_hdmi_audio_supported_id(display_id, k, 1, EDID_AudioSampleRate_e44KHz, i ) : |
| 514 | vc_tv_hdmi_audio_supported(k, 1, EDID_AudioSampleRate_e44KHz, i ) == 0) |
| 515 | max_sample_size = i; |
| 516 | } |
| 517 | if (num_channels>0) |
| 518 | LOG_STD("%8s supported: Max channels: %d, Max samplerate:%4dkHz, Max rate %4d kb/s." , formats[k], num_channels, sample_rates[max_sample_rate], 8*max_sample_size); |
| 519 | } |
| 520 | } |
| 521 | return 0; |
| 522 | } |
| 523 | |
| 524 | static int dump_edid( int display_id, const char *filename ) |
| 525 | { |
| 526 | uint8_t buffer[128]; |
| 527 | size_t written = 0, offset = 0; |
| 528 | int i, extensions = 0; |
| 529 | FILE *fp = NULL; |
| 530 | int siz = display_id != -1 ? vc_tv_hdmi_ddc_read_id(display_id, offset, sizeof (buffer), buffer) : vc_tv_hdmi_ddc_read(offset, sizeof (buffer), buffer);; |
| 531 | offset += sizeof( buffer); |
| 532 | /* First block always exist */ |
| 533 | if (siz == sizeof(buffer) && (fp = fopen(filename, "wb" )) != NULL) { |
| 534 | written += fwrite(buffer, 1, sizeof buffer, fp); |
| 535 | extensions = buffer[0x7e]; /* This tells you how many more blocks to read */ |
| 536 | for(i = 0; i < extensions; i++, offset += sizeof( buffer)) { |
| 537 | siz = display_id != -1 ? vc_tv_hdmi_ddc_read_id(display_id, offset, sizeof( buffer), buffer) : vc_tv_hdmi_ddc_read(offset, sizeof( buffer), buffer); |
| 538 | if(siz == sizeof(buffer)) { |
| 539 | written += fwrite(buffer, 1, sizeof (buffer), fp); |
| 540 | } else { |
| 541 | break; |
| 542 | } |
| 543 | } |
| 544 | } |
| 545 | if (fp) |
| 546 | fclose(fp); |
| 547 | if(written) { |
| 548 | LOG_STD( "Written %d bytes to %s" , written, filename); |
| 549 | } else { |
| 550 | LOG_STD( "Nothing written!" ); |
| 551 | } |
| 552 | return written < sizeof(buffer); |
| 553 | } |
| 554 | |
| 555 | static int show_info(int display_id, int on ) |
| 556 | { |
| 557 | return display_id != -1 ? vc_tv_show_info_id(display_id, on) : vc_tv_show_info(on); |
| 558 | } |
| 559 | |
| 560 | static void control_c( int signum ) |
| 561 | { |
| 562 | (void)signum; |
| 563 | |
| 564 | LOG_STD( "Shutting down..." ); |
| 565 | |
| 566 | vcos_event_signal( &quit_event ); |
| 567 | } |
| 568 | |
| 569 | static void tvservice_callback( void *callback_data, |
| 570 | uint32_t reason, |
| 571 | uint32_t param1, |
| 572 | uint32_t param2 ) |
| 573 | { |
| 574 | (void)callback_data; |
| 575 | (void)param1; |
| 576 | (void)param2; |
| 577 | |
| 578 | switch ( reason ) |
| 579 | { |
| 580 | case VC_HDMI_UNPLUGGED: |
| 581 | { |
| 582 | LOG_INFO( "HDMI cable is unplugged. Display %d" , param1 ); |
| 583 | break; |
| 584 | } |
| 585 | case VC_HDMI_ATTACHED: |
| 586 | { |
| 587 | LOG_INFO( "HDMI is attached" ); |
| 588 | break; |
| 589 | } |
| 590 | case VC_HDMI_DVI: |
| 591 | { |
| 592 | LOG_INFO( "HDMI in DVI mode" ); |
| 593 | break; |
| 594 | } |
| 595 | case VC_HDMI_HDMI: |
| 596 | { |
| 597 | LOG_INFO( "HDMI in HDMI mode" ); |
| 598 | break; |
| 599 | } |
| 600 | case VC_HDMI_HDCP_UNAUTH: |
| 601 | { |
| 602 | LOG_INFO( "HDCP authentication is broken" ); |
| 603 | break; |
| 604 | } |
| 605 | case VC_HDMI_HDCP_AUTH: |
| 606 | { |
| 607 | LOG_INFO( "HDCP is active" ); |
| 608 | break; |
| 609 | } |
| 610 | case VC_HDMI_HDCP_KEY_DOWNLOAD: |
| 611 | { |
| 612 | LOG_INFO( "HDCP key download" ); |
| 613 | break; |
| 614 | } |
| 615 | case VC_HDMI_HDCP_SRM_DOWNLOAD: |
| 616 | { |
| 617 | LOG_INFO( "HDCP revocation list download" ); |
| 618 | break; |
| 619 | } |
| 620 | default: |
| 621 | { |
| 622 | // Ignore all other reasons |
| 623 | LOG_INFO( "Callback with reason %d" , reason ); |
| 624 | break; |
| 625 | } |
| 626 | } |
| 627 | } |
| 628 | |
| 629 | static int start_monitor( void ) |
| 630 | { |
| 631 | if ( vcos_event_create( &quit_event, "tvservice" ) != VCOS_SUCCESS ) |
| 632 | { |
| 633 | LOG_ERR( "Failed to create quit event" ); |
| 634 | |
| 635 | return -1; |
| 636 | } |
| 637 | |
| 638 | // Handle the INT and TERM signals so we can quit |
| 639 | signal( SIGINT, control_c ); |
| 640 | signal( SIGTERM, control_c ); |
| 641 | |
| 642 | vc_tv_register_callback( &tvservice_callback, NULL ); |
| 643 | |
| 644 | return 0; |
| 645 | } |
| 646 | |
| 647 | static int power_on_preferred(int display_id) |
| 648 | { |
| 649 | int ret; |
| 650 | |
| 651 | LOG_STD( "Powering on HDMI with preferred settings" ); |
| 652 | |
| 653 | ret = display_id != -1 ? vc_tv_hdmi_power_on_preferred_id(display_id) : vc_tv_hdmi_power_on_preferred(); |
| 654 | if ( ret != 0 ) |
| 655 | { |
| 656 | LOG_ERR( "Failed to power on HDMI with preferred settings" ); |
| 657 | } |
| 658 | |
| 659 | return ret; |
| 660 | } |
| 661 | |
| 662 | static int power_on_explicit( int display_id, HDMI_RES_GROUP_T group, |
| 663 | uint32_t mode, uint32_t drive ) |
| 664 | { |
| 665 | int ret; |
| 666 | |
| 667 | LOG_STD( "Powering on HDMI with explicit settings (%s mode %u)" , |
| 668 | group == HDMI_RES_GROUP_CEA ? "CEA" : group == HDMI_RES_GROUP_DMT ? "DMT" : "CUSTOM" , mode ); |
| 669 | |
| 670 | ret = display_id != -1 ? vc_tv_hdmi_power_on_explicit_id(display_id, drive, group, mode ) : vc_tv_hdmi_power_on_explicit( drive, group, mode ); |
| 671 | if ( ret != 0 ) |
| 672 | { |
| 673 | LOG_ERR( "Failed to power on HDMI with explicit settings (%s mode %u)" , |
| 674 | group == HDMI_RES_GROUP_CEA ? "CEA" : group == HDMI_RES_GROUP_DMT ? "DMT" : "CUSTOM" , mode ); |
| 675 | } |
| 676 | |
| 677 | return ret; |
| 678 | } |
| 679 | |
| 680 | static int set_property(int display_id, HDMI_PROPERTY_T prop, uint32_t param1, uint32_t param2) |
| 681 | { |
| 682 | int ret; |
| 683 | HDMI_PROPERTY_PARAM_T property; |
| 684 | property.property = prop; |
| 685 | property.param1 = param1; |
| 686 | property.param2 = param2; |
| 687 | //LOG_DBG( "Setting property %d with params %d, %d", prop, param1, param2); |
| 688 | ret = display_id != -1 ? vc_tv_hdmi_set_property_id(display_id, &property) : vc_tv_hdmi_set_property(&property); |
| 689 | if(ret != 0) |
| 690 | { |
| 691 | LOG_ERR( "Failed to set property %d" , prop); |
| 692 | } |
| 693 | return ret; |
| 694 | } |
| 695 | |
| 696 | static int power_on_sdtv(int display_id, SDTV_MODE_T mode, |
| 697 | SDTV_ASPECT_T aspect, int sdtv_progressive ) |
| 698 | { |
| 699 | int ret; |
| 700 | SDTV_OPTIONS_T options; |
| 701 | memset(&options, 0, sizeof options); |
| 702 | options.aspect = aspect; |
| 703 | if (sdtv_progressive) |
| 704 | mode |= SDTV_MODE_PROGRESSIVE; |
| 705 | LOG_STD( "Powering on SDTV with explicit settings (mode:%d aspect:%d)" , |
| 706 | mode, aspect ); |
| 707 | |
| 708 | ret = display_id != -1 ? vc_tv_sdtv_power_on_id(display_id, mode, &options) : vc_tv_sdtv_power_on(mode, &options); |
| 709 | |
| 710 | if ( ret != 0 ) |
| 711 | { |
| 712 | LOG_ERR( "Failed to power on SDTV with explicit settings (mode:%d aspect:%d)" , |
| 713 | mode, aspect ); |
| 714 | } |
| 715 | |
| 716 | return ret; |
| 717 | } |
| 718 | |
| 719 | static int power_off(int display_id) |
| 720 | { |
| 721 | int ret; |
| 722 | |
| 723 | LOG_STD( "Powering off HDMI" ); |
| 724 | |
| 725 | ret = display_id ? vc_tv_power_off_id(display_id) : vc_tv_power_off(); |
| 726 | if ( ret != 0 ) |
| 727 | { |
| 728 | LOG_ERR( "Failed to power off HDMI" ); |
| 729 | } |
| 730 | |
| 731 | return ret; |
| 732 | } |
| 733 | |
| 734 | static int list_attached_devices() |
| 735 | { |
| 736 | char *display_to_text[] = |
| 737 | { |
| 738 | "Main LCD" , |
| 739 | "Auxiliary LCD" , |
| 740 | "HDMI 0" , |
| 741 | "Composite" , |
| 742 | "Forced LCD" , |
| 743 | "Forced TV" , |
| 744 | "Forced Other" , |
| 745 | "HDMI 1" , |
| 746 | "Forced TV2" |
| 747 | }; |
| 748 | |
| 749 | const int display_to_text_size = sizeof(display_to_text)/sizeof(display_to_text[0]); |
| 750 | |
| 751 | TV_ATTACHED_DEVICES_T devices; |
| 752 | int i; |
| 753 | |
| 754 | if (vc_tv_get_attached_devices(&devices) == -1) |
| 755 | { |
| 756 | LOG_ERR("No multi display support in firmware!" ); |
| 757 | return -1; |
| 758 | } |
| 759 | |
| 760 | LOG_STD("%d attached device(s), display ID's are : " , devices.num_attached); |
| 761 | |
| 762 | for (i=0;i<devices.num_attached;i++) |
| 763 | { |
| 764 | if (devices.display_number[i] <= display_to_text_size) |
| 765 | LOG_STD("Display Number %d, type %s" , devices.display_number[i], display_to_text[devices.display_number[i]]); |
| 766 | else |
| 767 | LOG_STD("Display Number %d, out of range" , devices.display_number[i]); |
| 768 | } |
| 769 | |
| 770 | return 0; |
| 771 | } |
| 772 | |
| 773 | // ---- Public Functions ----------------------------------------------------- |
| 774 | |
| 775 | int main( int argc, char **argv ) |
| 776 | { |
| 777 | int32_t ret; |
| 778 | char optstring[OPTSTRING_LEN]; |
| 779 | int opt; |
| 780 | int opt_preferred = 0; |
| 781 | int opt_explicit = 0; |
| 782 | int opt_ntsc = 0; |
| 783 | int opt_sdtvon = 0; |
| 784 | int opt_off = 0; |
| 785 | int opt_modes = 0; |
| 786 | int opt_monitor = 0; |
| 787 | int opt_status = 0; |
| 788 | int opt_audiosup = 0; |
| 789 | int opt_dumpedid = 0; |
| 790 | int opt_showinfo = 0; |
| 791 | int opt_3d = 0; |
| 792 | int opt_json = 0; |
| 793 | int opt_name = 0; |
| 794 | int opt_list = 0; |
| 795 | int opt_device = -1; |
| 796 | |
| 797 | char *dumpedid_filename = NULL; |
| 798 | VCHI_INSTANCE_T vchi_instance; |
| 799 | VCHI_CONNECTION_T *vchi_connection; |
| 800 | HDMI_RES_GROUP_T power_on_explicit_group = HDMI_RES_GROUP_INVALID; |
| 801 | uint32_t power_on_explicit_mode; |
| 802 | uint32_t power_on_explicit_drive = HDMI_MODE_HDMI; |
| 803 | HDMI_RES_GROUP_T get_modes_group = HDMI_RES_GROUP_INVALID; |
| 804 | SDTV_MODE_T sdtvon_mode = SDTV_MODE_NTSC; |
| 805 | SDTV_ASPECT_T sdtvon_aspect = SDTV_ASPECT_UNKNOWN; |
| 806 | int sdtvon_progressive = 0; |
| 807 | TV_ATTACHED_DEVICES_T devices; |
| 808 | |
| 809 | // Initialize VCOS |
| 810 | vcos_init(); |
| 811 | |
| 812 | // Create the option string that we will be using to parse the arguments |
| 813 | create_optstring( optstring ); |
| 814 | |
| 815 | // Parse the command line arguments |
| 816 | while (( opt = getopt_long_only( argc, argv, optstring, long_opts, |
| 817 | NULL )) != -1 ) |
| 818 | { |
| 819 | switch ( opt ) |
| 820 | { |
| 821 | case 0: |
| 822 | { |
| 823 | // getopt_long returns 0 for entries where flag is non-NULL |
| 824 | break; |
| 825 | } |
| 826 | case OPT_PREFERRED: |
| 827 | { |
| 828 | opt_preferred = 1; |
| 829 | break; |
| 830 | } |
| 831 | case OPT_EXPLICIT: |
| 832 | { |
| 833 | char group_str[32], drive_str[32]; |
| 834 | |
| 835 | /* coverity[secure_coding] String length specified, so can't overflow */ |
| 836 | int s = sscanf( optarg, "%31s %u %31s" , group_str, &power_on_explicit_mode, drive_str ); |
| 837 | if ( s != 2 && s != 3 ) |
| 838 | { |
| 839 | LOG_ERR( "Invalid arguments '%s'" , optarg ); |
| 840 | goto err_out; |
| 841 | } |
| 842 | |
| 843 | // Check the group first |
| 844 | if ( vcos_strcasecmp( "CEA" , group_str ) == 0 ) |
| 845 | { |
| 846 | power_on_explicit_group = HDMI_RES_GROUP_CEA; |
| 847 | } |
| 848 | else if ( vcos_strcasecmp( "DMT" , group_str ) == 0 ) |
| 849 | { |
| 850 | power_on_explicit_group = HDMI_RES_GROUP_DMT; |
| 851 | } |
| 852 | else if ( vcos_strcasecmp( "CEA_3D" , group_str ) == 0 || |
| 853 | vcos_strcasecmp( "CEA_3D_SBS" , group_str ) == 0) |
| 854 | { |
| 855 | power_on_explicit_group = HDMI_RES_GROUP_CEA; |
| 856 | opt_3d = 1; |
| 857 | } |
| 858 | else if ( vcos_strcasecmp( "CEA_3D_TB" , group_str ) == 0 ) |
| 859 | { |
| 860 | power_on_explicit_group = HDMI_RES_GROUP_CEA; |
| 861 | opt_3d = 2; |
| 862 | } |
| 863 | else if ( vcos_strcasecmp( "CEA_3D_FP" , group_str ) == 0 ) |
| 864 | { |
| 865 | power_on_explicit_group = HDMI_RES_GROUP_CEA; |
| 866 | opt_3d = 3; |
| 867 | } |
| 868 | else if ( vcos_strcasecmp( "CEA_3D_FS" , group_str ) == 0 ) |
| 869 | { |
| 870 | power_on_explicit_group = HDMI_RES_GROUP_CEA; |
| 871 | opt_3d = 4; |
| 872 | } |
| 873 | else |
| 874 | { |
| 875 | LOG_ERR( "Invalid group '%s'" , group_str ); |
| 876 | goto err_out; |
| 877 | } |
| 878 | if (s==3) |
| 879 | { |
| 880 | if (vcos_strcasecmp( "HDMI" , drive_str ) == 0 ) |
| 881 | { |
| 882 | power_on_explicit_drive = HDMI_MODE_HDMI; |
| 883 | } |
| 884 | else if (vcos_strcasecmp( "DVI" , drive_str ) == 0 ) |
| 885 | { |
| 886 | power_on_explicit_drive = HDMI_MODE_DVI; |
| 887 | } |
| 888 | else |
| 889 | { |
| 890 | LOG_ERR( "Invalid drive '%s'" , drive_str ); |
| 891 | goto err_out; |
| 892 | } |
| 893 | } |
| 894 | // Then check if mode is a sane number |
| 895 | if ( power_on_explicit_mode > MAX_MODE_ID ) |
| 896 | { |
| 897 | LOG_ERR( "Invalid mode '%u'" , power_on_explicit_mode ); |
| 898 | goto err_out; |
| 899 | } |
| 900 | |
| 901 | opt_explicit = 1; |
| 902 | break; |
| 903 | } |
| 904 | case OPT_NTSC: |
| 905 | { |
| 906 | opt_ntsc = 1; |
| 907 | break; |
| 908 | } |
| 909 | case OPT_SDTVON: |
| 910 | { |
| 911 | char mode_str[32], aspect_str[32], progressive_str[32]; |
| 912 | int s = sscanf( optarg, "%s %s %s" , mode_str, aspect_str, progressive_str ); |
| 913 | if ( s != 2 && s != 3 ) |
| 914 | { |
| 915 | LOG_ERR( "Invalid arguments '%s'" , optarg ); |
| 916 | goto err_out; |
| 917 | } |
| 918 | |
| 919 | // Check the group first |
| 920 | if ( vcos_strcasecmp( "NTSC" , mode_str ) == 0 ) |
| 921 | { |
| 922 | sdtvon_mode = SDTV_MODE_NTSC; |
| 923 | } |
| 924 | else if ( vcos_strcasecmp( "NTSC_J" , mode_str ) == 0 ) |
| 925 | { |
| 926 | sdtvon_mode = SDTV_MODE_NTSC_J; |
| 927 | } |
| 928 | else if ( vcos_strcasecmp( "PAL" , mode_str ) == 0 ) |
| 929 | { |
| 930 | sdtvon_mode = SDTV_MODE_PAL; |
| 931 | } |
| 932 | else if ( vcos_strcasecmp( "PAL_M" , mode_str ) == 0 ) |
| 933 | { |
| 934 | sdtvon_mode = SDTV_MODE_PAL_M; |
| 935 | } |
| 936 | else |
| 937 | { |
| 938 | LOG_ERR( "Invalid mode '%s'" , mode_str ); |
| 939 | goto err_out; |
| 940 | } |
| 941 | |
| 942 | if ( vcos_strcasecmp( "4:3" , aspect_str ) == 0 ) |
| 943 | { |
| 944 | sdtvon_aspect = SDTV_ASPECT_4_3; |
| 945 | } |
| 946 | else if ( vcos_strcasecmp( "14:9" , aspect_str ) == 0 ) |
| 947 | { |
| 948 | sdtvon_aspect = SDTV_ASPECT_14_9; |
| 949 | } |
| 950 | else if ( vcos_strcasecmp( "16:9" , aspect_str ) == 0 ) |
| 951 | { |
| 952 | sdtvon_aspect = SDTV_ASPECT_16_9; |
| 953 | } |
| 954 | |
| 955 | if (s == 3 && vcos_strcasecmp( "P" , progressive_str ) == 0 ) |
| 956 | { |
| 957 | sdtvon_progressive = 1; |
| 958 | } |
| 959 | opt_sdtvon = 1; |
| 960 | break; |
| 961 | } |
| 962 | case OPT_OFF: |
| 963 | { |
| 964 | opt_off = 1; |
| 965 | break; |
| 966 | } |
| 967 | case OPT_MODES: |
| 968 | { |
| 969 | if ( vcos_strcasecmp( "CEA" , optarg ) == 0 ) |
| 970 | { |
| 971 | get_modes_group = HDMI_RES_GROUP_CEA; |
| 972 | } |
| 973 | else if ( vcos_strcasecmp( "DMT" , optarg ) == 0 ) |
| 974 | { |
| 975 | get_modes_group = HDMI_RES_GROUP_DMT; |
| 976 | } |
| 977 | else |
| 978 | { |
| 979 | LOG_ERR( "Invalid group '%s'" , optarg ); |
| 980 | goto err_out; |
| 981 | } |
| 982 | |
| 983 | opt_modes = 1; |
| 984 | break; |
| 985 | } |
| 986 | case OPT_MONITOR: |
| 987 | { |
| 988 | opt_monitor = 1; |
| 989 | break; |
| 990 | } |
| 991 | case OPT_STATUS: |
| 992 | { |
| 993 | opt_status = 1; |
| 994 | break; |
| 995 | } |
| 996 | case OPT_AUDIOSUP: |
| 997 | { |
| 998 | opt_audiosup = 1; |
| 999 | break; |
| 1000 | } |
| 1001 | case OPT_DUMPEDID: |
| 1002 | { |
| 1003 | opt_dumpedid = 1; |
| 1004 | dumpedid_filename = optarg; |
| 1005 | break; |
| 1006 | } |
| 1007 | case OPT_SHOWINFO: |
| 1008 | { |
| 1009 | opt_showinfo = atoi(optarg)+1; |
| 1010 | break; |
| 1011 | } |
| 1012 | case OPT_JSON: |
| 1013 | { |
| 1014 | opt_json = 1; |
| 1015 | break; |
| 1016 | } |
| 1017 | case OPT_NAME: |
| 1018 | { |
| 1019 | opt_name = 1; |
| 1020 | break; |
| 1021 | } |
| 1022 | |
| 1023 | case OPT_LIST: |
| 1024 | { |
| 1025 | opt_list = 1; |
| 1026 | break; |
| 1027 | } |
| 1028 | |
| 1029 | case OPT_DEVICE: |
| 1030 | { |
| 1031 | opt_device = atoi(optarg); |
| 1032 | break; |
| 1033 | } |
| 1034 | |
| 1035 | default: |
| 1036 | { |
| 1037 | LOG_ERR( "Unrecognized option '%d'\n" , opt ); |
| 1038 | goto err_usage; |
| 1039 | } |
| 1040 | case '?': |
| 1041 | case OPT_HELP: |
| 1042 | { |
| 1043 | goto err_usage; |
| 1044 | } |
| 1045 | } // end switch |
| 1046 | } // end while |
| 1047 | |
| 1048 | argc -= optind; |
| 1049 | argv += optind; |
| 1050 | |
| 1051 | if (( optind == 1 ) || ( argc > 0 )) |
| 1052 | { |
| 1053 | if ( argc > 0 ) |
| 1054 | { |
| 1055 | LOG_ERR( "Unrecognized argument -- '%s'" , *argv ); |
| 1056 | } |
| 1057 | |
| 1058 | goto err_usage; |
| 1059 | } |
| 1060 | |
| 1061 | if (( opt_preferred + opt_explicit + opt_sdtvon > 1 )) |
| 1062 | { |
| 1063 | LOG_ERR( "Conflicting power on options" ); |
| 1064 | goto err_usage; |
| 1065 | } |
| 1066 | |
| 1067 | if ((( opt_preferred == 1 ) || ( opt_explicit == 1 ) || ( opt_sdtvon == 1)) && ( opt_off == 1 )) |
| 1068 | { |
| 1069 | LOG_ERR( "Cannot power on and power off simultaneously" ); |
| 1070 | goto err_out; |
| 1071 | } |
| 1072 | |
| 1073 | // Initialize the VCHI connection |
| 1074 | ret = vchi_initialise( &vchi_instance ); |
| 1075 | if ( ret != 0 ) |
| 1076 | { |
| 1077 | LOG_ERR( "Failed to initialize VCHI (ret=%d)" , ret ); |
| 1078 | goto err_out; |
| 1079 | } |
| 1080 | |
| 1081 | ret = vchi_connect( NULL, 0, vchi_instance ); |
| 1082 | if ( ret != 0) |
| 1083 | { |
| 1084 | LOG_ERR( "Failed to create VCHI connection (ret=%d)" , ret ); |
| 1085 | goto err_out; |
| 1086 | } |
| 1087 | |
| 1088 | // Initialize the tvservice |
| 1089 | vc_vchi_tv_init( vchi_instance, &vchi_connection, 1 ); |
| 1090 | |
| 1091 | // Make a call that will determine whether we have multi display support |
| 1092 | if (vc_tv_get_attached_devices(&devices) != -1) |
| 1093 | { |
| 1094 | // Check any device_id specified |
| 1095 | if (opt_device != -1) |
| 1096 | { |
| 1097 | int i; |
| 1098 | |
| 1099 | // Check against the connected devices |
| 1100 | for (i=0;i<devices.num_attached;i++) |
| 1101 | { |
| 1102 | if (devices.display_number[i] == opt_device) |
| 1103 | { |
| 1104 | break; |
| 1105 | } |
| 1106 | } |
| 1107 | |
| 1108 | if (i == devices.num_attached) |
| 1109 | { |
| 1110 | LOG_ERR( "Invalid device/display ID" ); |
| 1111 | goto err_stop_service; |
| 1112 | } |
| 1113 | } |
| 1114 | } |
| 1115 | else |
| 1116 | opt_device = -1; |
| 1117 | |
| 1118 | if ( opt_monitor == 1 ) |
| 1119 | { |
| 1120 | LOG_STD( "Starting to monitor for HDMI events" ); |
| 1121 | |
| 1122 | if ( start_monitor() != 0 ) |
| 1123 | { |
| 1124 | goto err_stop_service; |
| 1125 | } |
| 1126 | } |
| 1127 | |
| 1128 | if ( opt_modes == 1 ) |
| 1129 | { |
| 1130 | if ( get_modes( opt_device, get_modes_group, opt_json ) != 0 ) |
| 1131 | { |
| 1132 | goto err_stop_service; |
| 1133 | } |
| 1134 | } |
| 1135 | |
| 1136 | if ( opt_preferred == 1 ) |
| 1137 | { |
| 1138 | if(set_property( opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_NONE, 0) != 0) |
| 1139 | { |
| 1140 | goto err_stop_service; |
| 1141 | } |
| 1142 | if ( power_on_preferred(opt_device) != 0 ) |
| 1143 | { |
| 1144 | goto err_stop_service; |
| 1145 | } |
| 1146 | } |
| 1147 | else if ( opt_explicit == 1 ) |
| 1148 | { |
| 1149 | //Distinguish between turning on 3D side by side and 3D top/bottom |
| 1150 | if(opt_3d == 0 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_NONE, 0) != 0) |
| 1151 | { |
| 1152 | goto err_stop_service; |
| 1153 | } |
| 1154 | else if(opt_3d == 1 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_SBS_HALF, 0) != 0) |
| 1155 | { |
| 1156 | goto err_stop_service; |
| 1157 | } |
| 1158 | else if(opt_3d == 2 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_TB_HALF, 0) != 0) |
| 1159 | { |
| 1160 | goto err_stop_service; |
| 1161 | } |
| 1162 | else if(opt_3d == 3 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_FRAME_PACKING, 0) != 0) |
| 1163 | { |
| 1164 | goto err_stop_service; |
| 1165 | } |
| 1166 | else if(opt_3d == 4 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_FRAME_SEQUENTIAL, 0) != 0) |
| 1167 | { |
| 1168 | goto err_stop_service; |
| 1169 | } |
| 1170 | if (set_property(opt_device, HDMI_PROPERTY_PIXEL_CLOCK_TYPE, opt_ntsc ? HDMI_PIXEL_CLOCK_TYPE_NTSC : HDMI_PIXEL_CLOCK_TYPE_PAL, 0) != 0) |
| 1171 | { |
| 1172 | goto err_stop_service; |
| 1173 | } |
| 1174 | if ( power_on_explicit( opt_device, |
| 1175 | power_on_explicit_group, |
| 1176 | power_on_explicit_mode, power_on_explicit_drive ) != 0 ) |
| 1177 | { |
| 1178 | goto err_stop_service; |
| 1179 | } |
| 1180 | } |
| 1181 | else if ( opt_sdtvon == 1 ) |
| 1182 | { |
| 1183 | if ( power_on_sdtv( opt_device, sdtvon_mode, |
| 1184 | sdtvon_aspect, sdtvon_progressive ) != 0 ) |
| 1185 | { |
| 1186 | goto err_stop_service; |
| 1187 | } |
| 1188 | } |
| 1189 | else if (opt_off == 1 ) |
| 1190 | { |
| 1191 | if ( power_off(opt_device) != 0 ) |
| 1192 | { |
| 1193 | goto err_stop_service; |
| 1194 | } |
| 1195 | } |
| 1196 | |
| 1197 | if ( opt_status == 1 ) |
| 1198 | { |
| 1199 | if ( get_status(opt_device) != 0 ) |
| 1200 | { |
| 1201 | goto err_stop_service; |
| 1202 | } |
| 1203 | } |
| 1204 | |
| 1205 | if ( opt_audiosup == 1 ) |
| 1206 | { |
| 1207 | if ( get_audiosup(opt_device) != 0 ) |
| 1208 | { |
| 1209 | goto err_stop_service; |
| 1210 | } |
| 1211 | } |
| 1212 | |
| 1213 | if ( opt_dumpedid == 1 ) |
| 1214 | { |
| 1215 | if ( dump_edid(opt_device, dumpedid_filename) != 0 ) |
| 1216 | { |
| 1217 | goto err_stop_service; |
| 1218 | } |
| 1219 | } |
| 1220 | |
| 1221 | if ( opt_showinfo ) |
| 1222 | { |
| 1223 | if ( show_info(opt_device, opt_showinfo-1) != 0 ) |
| 1224 | { |
| 1225 | goto err_stop_service; |
| 1226 | } |
| 1227 | } |
| 1228 | |
| 1229 | if ( opt_name == 1 ) |
| 1230 | { |
| 1231 | TV_DEVICE_ID_T id; |
| 1232 | memset(&id, 0, sizeof(id)); |
| 1233 | if( (opt_device != -1 ? vc_tv_get_device_id_id(opt_device, &id) : vc_tv_get_device_id(&id) )== 0) { |
| 1234 | if(id.vendor[0] == '\0' || id.monitor_name[0] == '\0') { |
| 1235 | LOG_ERR( "No device present" ); |
| 1236 | } else { |
| 1237 | LOG_STD( "device_name=%s-%s" , id.vendor, id.monitor_name); |
| 1238 | } |
| 1239 | } else { |
| 1240 | LOG_ERR( "Failed to obtain device name" ); |
| 1241 | } |
| 1242 | } |
| 1243 | |
| 1244 | if ( opt_monitor == 1 ) |
| 1245 | { |
| 1246 | // Wait until we get the signal to exit |
| 1247 | vcos_event_wait( &quit_event ); |
| 1248 | |
| 1249 | vcos_event_delete( &quit_event ); |
| 1250 | } |
| 1251 | |
| 1252 | if ( opt_list == 1 ) |
| 1253 | { |
| 1254 | if (list_attached_devices() != 0) |
| 1255 | { |
| 1256 | goto err_stop_service; |
| 1257 | } |
| 1258 | } |
| 1259 | |
| 1260 | |
| 1261 | |
| 1262 | err_stop_service: |
| 1263 | // LOG_INFO( "Stopping tvservice" ); |
| 1264 | |
| 1265 | // Stop the tvservice |
| 1266 | vc_vchi_tv_stop(); |
| 1267 | |
| 1268 | // Disconnect the VCHI connection |
| 1269 | vchi_disconnect( vchi_instance ); |
| 1270 | |
| 1271 | exit( 0 ); |
| 1272 | |
| 1273 | err_usage: |
| 1274 | show_usage(); |
| 1275 | |
| 1276 | err_out: |
| 1277 | exit( 1 ); |
| 1278 | } |
| 1279 | |