mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-13 02:35:54 +00:00
1279 lines
42 KiB
C
1279 lines
42 KiB
C
/*
|
|
Copyright (c) 2012, Broadcom Europe Ltd
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the copyright holder nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
// ---- Include Files -------------------------------------------------------
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include "interface/vmcs_host/vc_tvservice.h"
|
|
|
|
#define TV_SUPPORTED_MODE_T TV_SUPPORTED_MODE_NEW_T
|
|
#define vc_tv_hdmi_get_supported_modes vc_tv_hdmi_get_supported_modes_new
|
|
#define vc_tv_hdmi_power_on_explicit vc_tv_hdmi_power_on_explicit_new
|
|
|
|
// ---- Public Variables ----------------------------------------------------
|
|
|
|
// ---- Private Constants and Types -----------------------------------------
|
|
|
|
// Logging macros (for remapping to other logging mechanisms, i.e., vcos_log)
|
|
#define LOG_ERR( fmt, arg... ) fprintf( stderr, "[E] " fmt "\n", ##arg )
|
|
#define LOG_WARN( fmt, arg... ) fprintf( stderr, "[W] " fmt "\n", ##arg )
|
|
#define LOG_INFO( fmt, arg... ) fprintf( stderr, "[I] " fmt "\n", ##arg )
|
|
#define LOG_DBG( fmt, arg... ) fprintf( stdout, "[D] " fmt "\n", ##arg )
|
|
|
|
// Standard output log (for printing normal information to users)
|
|
#define LOG_STD( fmt, arg... ) fprintf( stdout, fmt "\n", ##arg )
|
|
|
|
// Maximum length of option string (3 characters max for each option + NULL)
|
|
#define OPTSTRING_LEN ( sizeof( long_opts ) / sizeof( *long_opts ) * 3 + 1 )
|
|
|
|
// Maximum mode ID
|
|
#define MAX_MODE_ID (127)
|
|
|
|
// Maximum status string length
|
|
#define MAX_STATUS_STR_LENGTH (128)
|
|
|
|
// ---- Private Variables ---------------------------------------------------
|
|
|
|
enum
|
|
{
|
|
OPT_PREFERRED = 'p',
|
|
OPT_EXPLICIT = 'e',
|
|
OPT_NTSC = 't',
|
|
OPT_OFF = 'o',
|
|
OPT_SDTVON = 'c',
|
|
OPT_MODES = 'm',
|
|
OPT_MONITOR = 'M',
|
|
OPT_STATUS = 's',
|
|
OPT_DUMPEDID = 'd',
|
|
OPT_AUDIOSUP = 'a',
|
|
OPT_SHOWINFO = 'i',
|
|
OPT_JSON = 'j',
|
|
OPT_HELP = 'h',
|
|
OPT_NAME = 'n',
|
|
OPT_DEVICE = 'v',
|
|
OPT_LIST = 'l',
|
|
// Options from this point onwards don't have any short option equivalents
|
|
OPT_FIRST_LONG_OPT = 0x80,
|
|
};
|
|
|
|
static struct option long_opts[] =
|
|
{
|
|
// name has_arg flag val
|
|
// ------------------- ------------------ ---- ---------------
|
|
{ "preferred", no_argument, NULL, OPT_PREFERRED },
|
|
{ "explicit", required_argument, NULL, OPT_EXPLICIT },
|
|
{ "ntsc", no_argument, NULL, OPT_NTSC },
|
|
{ "off", no_argument, NULL, OPT_OFF },
|
|
{ "sdtvon", required_argument, NULL, OPT_SDTVON },
|
|
{ "modes", required_argument, NULL, OPT_MODES },
|
|
{ "monitor", no_argument, NULL, OPT_MONITOR },
|
|
{ "status", no_argument, NULL, OPT_STATUS },
|
|
{ "dumpedid", required_argument, NULL, OPT_DUMPEDID},
|
|
{ "audio", no_argument, NULL, OPT_AUDIOSUP},
|
|
{ "json", no_argument, NULL, OPT_JSON },
|
|
{ "info", required_argument, NULL, OPT_SHOWINFO},
|
|
{ "help", no_argument, NULL, OPT_HELP },
|
|
{ "name", no_argument, NULL, OPT_NAME },
|
|
{ "device", required_argument, NULL, OPT_DEVICE },
|
|
{ "list", no_argument, NULL, OPT_LIST },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static VCOS_EVENT_T quit_event;
|
|
|
|
// ---- Private Functions ---------------------------------------------------
|
|
|
|
static void show_usage( void )
|
|
{
|
|
LOG_STD( "Usage: tvservice [OPTION]..." );
|
|
LOG_STD( " -p, --preferred Power on HDMI with preferred settings" );
|
|
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"
|
|
" MODE (see --modes) and DRIVE (HDMI, DVI)" );
|
|
LOG_STD( " -t, --ntsc Use NTSC frequency for HDMI mode (e.g. 59.94Hz rather than 60Hz)" );
|
|
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" );
|
|
LOG_STD( " -o, --off Power off the display" );
|
|
LOG_STD( " -m, --modes=GROUP Get supported modes for GROUP (CEA, DMT)" );
|
|
LOG_STD( " -M, --monitor Monitor HDMI events" );
|
|
LOG_STD( " -s, --status Get HDMI status" );
|
|
LOG_STD( " -a, --audio Get supported audio information" );
|
|
LOG_STD( " -d, --dumpedid <filename> Dump EDID information to file" );
|
|
LOG_STD( " -j, --json Use JSON format for --modes output" );
|
|
LOG_STD( " -n, --name Print the device ID from EDID" );
|
|
LOG_STD( " -l, --list List all attached devices" );
|
|
LOG_STD( " -v, --device Specify the device to use (see --list)");
|
|
LOG_STD( " -h, --help Print this information" );
|
|
}
|
|
|
|
static void create_optstring( char *optstring )
|
|
{
|
|
char *short_opts = optstring;
|
|
struct option *option;
|
|
|
|
// Figure out the short options from our options structure
|
|
for ( option = long_opts; option->name != NULL; option++ )
|
|
{
|
|
if (( option->flag == NULL ) && ( option->val < OPT_FIRST_LONG_OPT ))
|
|
{
|
|
*short_opts++ = (char)option->val;
|
|
|
|
if ( option->has_arg != no_argument )
|
|
{
|
|
*short_opts++ = ':';
|
|
}
|
|
|
|
// Optional arguments require two ':'
|
|
if ( option->has_arg == optional_argument )
|
|
{
|
|
*short_opts++ = ':';
|
|
}
|
|
}
|
|
}
|
|
*short_opts++ = '\0';
|
|
}
|
|
|
|
/* Return the string presentation of aspect ratio */
|
|
static const char *aspect_ratio_str(HDMI_ASPECT_T aspect_ratio) {
|
|
switch(aspect_ratio) {
|
|
case HDMI_ASPECT_4_3:
|
|
return "4:3";
|
|
case HDMI_ASPECT_14_9:
|
|
return "14:9";
|
|
case HDMI_ASPECT_16_9:
|
|
return "16:9";
|
|
case HDMI_ASPECT_5_4:
|
|
return "5:4";
|
|
case HDMI_ASPECT_16_10:
|
|
return "16:10";
|
|
case HDMI_ASPECT_15_9:
|
|
return "15:9";
|
|
case HDMI_ASPECT_64_27:
|
|
return "64:27 (21:9)";
|
|
default:
|
|
return "unknown AR";
|
|
}
|
|
}
|
|
|
|
/* Return the string presentation of aspect ratio */
|
|
static const char *aspect_ratio_sd_str(SDTV_ASPECT_T aspect_ratio) {
|
|
switch(aspect_ratio) {
|
|
case SDTV_ASPECT_4_3:
|
|
return "4:3";
|
|
case SDTV_ASPECT_14_9:
|
|
return "14:9";
|
|
case SDTV_ASPECT_16_9:
|
|
return "16:9";
|
|
default:
|
|
return "unknown AR";
|
|
}
|
|
}
|
|
|
|
//Print a string and update the offset into the status buffer
|
|
//Return non-zero if string is truncated, zero otherwise
|
|
static int status_sprintf(char *buf, size_t buflen, size_t *offset, const char *fmt, ...) {
|
|
int ret;
|
|
size_t length;
|
|
va_list ap;
|
|
va_start(ap,fmt);
|
|
length = (size_t) vcos_safe_vsprintf(buf, buflen, *offset, fmt, ap);
|
|
va_end(ap);
|
|
if(length >= buflen) {
|
|
ret = -1;
|
|
*offset = buflen;
|
|
} else {
|
|
ret = 0;
|
|
*offset = length;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* Return the string representation of 3D support bit mask */
|
|
static const char* threed_str(uint32_t struct_3d_mask, int json_output) {
|
|
#define THREE_D_FORMAT_NAME_MAX_LEN 10 //Including the separator
|
|
static const char* three_d_format_names[] = { //See HDMI_3D_STRUCT_T bit fields
|
|
"FP", "F-Alt", "L-Alt", "SbS-Full",
|
|
"Ldep", "Ldep+Gfx", "TopBot", "SbS-HH",
|
|
"SbS-OLOR", "SbS-OLER", "SbS-ELOR", "SbS-ELER"
|
|
};
|
|
//Longest possible string is the concatenation of all of the above
|
|
static char struct_desc[vcos_countof(three_d_format_names)*THREE_D_FORMAT_NAME_MAX_LEN+4] = {0};
|
|
const size_t struct_desc_length = sizeof(struct_desc);
|
|
size_t j, added = 0, offset = 0;
|
|
int tmp = 0;
|
|
if(!json_output)
|
|
tmp = status_sprintf(struct_desc, struct_desc_length, &offset, "3D:");
|
|
if(struct_3d_mask) {
|
|
for(j = 0; !tmp && j < vcos_countof(three_d_format_names); j++) {
|
|
if(struct_3d_mask & (1 << j)) {
|
|
tmp = status_sprintf(struct_desc, struct_desc_length, &offset, json_output ? "\"%s\"," : "%s|", three_d_format_names[j]);
|
|
added++;
|
|
}
|
|
}
|
|
if(!tmp && added > 0)
|
|
struct_desc[strlen(struct_desc)-1] = '\0'; //Get rid of final "|"
|
|
} else if(!tmp && !added && !json_output) {
|
|
status_sprintf(struct_desc, struct_desc_length, &offset, "none");
|
|
}
|
|
|
|
return struct_desc;
|
|
}
|
|
|
|
static int get_modes( uint32_t display_id, HDMI_RES_GROUP_T group, int json_output)
|
|
{
|
|
static TV_SUPPORTED_MODE_T supported_modes[MAX_MODE_ID];
|
|
HDMI_RES_GROUP_T preferred_group;
|
|
uint32_t preferred_mode;
|
|
int num_modes;
|
|
int i;
|
|
|
|
vcos_assert(( group == HDMI_RES_GROUP_CEA ) ||
|
|
( group == HDMI_RES_GROUP_DMT ));
|
|
|
|
memset(supported_modes, 0, sizeof(supported_modes));
|
|
|
|
if (display_id != -1)
|
|
num_modes = vc_tv_hdmi_get_supported_modes_new_id(display_id,
|
|
group, supported_modes,
|
|
vcos_countof(supported_modes),
|
|
&preferred_group,
|
|
&preferred_mode );
|
|
else
|
|
num_modes = vc_tv_hdmi_get_supported_modes_new(group, supported_modes,
|
|
vcos_countof(supported_modes),
|
|
&preferred_group,
|
|
&preferred_mode );
|
|
|
|
|
|
if ( num_modes < 0 )
|
|
{
|
|
LOG_ERR( "Failed to get modes" );
|
|
return -1;
|
|
}
|
|
|
|
if (json_output)
|
|
{
|
|
LOG_STD( "[" );
|
|
}
|
|
else
|
|
{
|
|
LOG_STD( "Group %s has %u modes:",
|
|
HDMI_RES_GROUP_NAME(group), num_modes );
|
|
}
|
|
|
|
for ( i = 0; i < num_modes; i++ )
|
|
{
|
|
char p[8] = {0};
|
|
if( supported_modes[i].pixel_rep )
|
|
vcos_safe_sprintf(p, sizeof(p)-1, 0, "x%d ", supported_modes[i].pixel_rep);
|
|
|
|
if (json_output)
|
|
{
|
|
LOG_STD( "{ \"code\":%u, \"width\":%u, \"height\":%u, \"rate\":%u, \"aspect_ratio\":\"%s\", \"scan\":\"%s\", \"3d_modes\":[%s] }%s",
|
|
supported_modes[i].code, supported_modes[i].width,
|
|
supported_modes[i].height, supported_modes[i].frame_rate,
|
|
aspect_ratio_str(supported_modes[i].aspect_ratio),
|
|
supported_modes[i].scan_mode ? "i" : "p",
|
|
supported_modes[i].struct_3d_mask ? threed_str(supported_modes[i].struct_3d_mask, 1) : "",
|
|
(i+1 < num_modes) ? "," : "");
|
|
}
|
|
else
|
|
{
|
|
int preferred = supported_modes[i].group == preferred_group && supported_modes[i].code == preferred_mode;
|
|
LOG_STD( "%s mode %u: %ux%u @ %uHz %s, clock:%uMHz %s%s %s",
|
|
preferred ? " (prefer)" : supported_modes[i].native ? " (native)" : " ",
|
|
supported_modes[i].code, supported_modes[i].width,
|
|
supported_modes[i].height, supported_modes[i].frame_rate,
|
|
aspect_ratio_str(supported_modes[i].aspect_ratio),
|
|
supported_modes[i].pixel_freq / 1000000U, p,
|
|
supported_modes[i].scan_mode ? "interlaced" : "progressive",
|
|
supported_modes[i].struct_3d_mask ? threed_str(supported_modes[i].struct_3d_mask, 0) : "");
|
|
}
|
|
}
|
|
|
|
if (json_output)
|
|
{
|
|
LOG_STD( "]" );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char *status_mode( TV_DISPLAY_STATE_T *tvstate ) {
|
|
static char mode_str[MAX_STATUS_STR_LENGTH] = {0};
|
|
int tmp = 0;
|
|
size_t offset = 0;
|
|
if(tvstate->state & ( VC_HDMI_HDMI | VC_HDMI_DVI )) {
|
|
//HDMI or DVI on
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, (tvstate->state & VC_HDMI_HDMI) ? "HDMI" : "DVI");
|
|
if(!tmp) {
|
|
//We should still have space at this point
|
|
switch(tvstate->display.hdmi.group) {
|
|
case HDMI_RES_GROUP_CEA:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CEA (%d)", tvstate->display.hdmi.mode); break;
|
|
case HDMI_RES_GROUP_DMT:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " DMT (%d)", tvstate->display.hdmi.mode); break;
|
|
case HDMI_RES_GROUP_CUSTOM:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CUSTOM"); break;
|
|
default: break;
|
|
}
|
|
}
|
|
if(!tmp && tvstate->display.hdmi.format_3d) {
|
|
switch(tvstate->display.hdmi.format_3d) {
|
|
case HDMI_3D_FORMAT_SBS_HALF:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " 3D SbS"); break;
|
|
case HDMI_3D_FORMAT_TB_HALF:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " 3D T&B"); break;
|
|
case HDMI_3D_FORMAT_FRAME_PACKING:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " 3D FP"); break;
|
|
case HDMI_3D_FORMAT_FRAME_SEQUENTIAL:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " 3D FS"); break;
|
|
default: break;
|
|
}
|
|
}
|
|
if(!tmp) {
|
|
if (tvstate->state & VC_HDMI_HDMI)
|
|
//Only HDMI mode can signal pixel encoding in AVI infoframe
|
|
switch(tvstate->display.hdmi.pixel_encoding) {
|
|
case HDMI_PIXEL_ENCODING_RGB_LIMITED:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " RGB lim"); break;
|
|
case HDMI_PIXEL_ENCODING_RGB_FULL:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " RGB full"); break;
|
|
case HDMI_PIXEL_ENCODING_YCbCr444_LIMITED:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " YCbCr444 lim"); break;
|
|
case HDMI_PIXEL_ENCODING_YCbCr444_FULL:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " YCbCr444 full"); break;
|
|
case HDMI_PIXEL_ENCODING_YCbCr422_LIMITED:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " YCbCr422 lim"); break;
|
|
case HDMI_PIXEL_ENCODING_YCbCr422_FULL:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " YCbCr422 full"); break;
|
|
default: break;
|
|
}
|
|
else
|
|
//DVI will always be RGB, and CEA mode will have limited range, DMT mode full range
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset,
|
|
(tvstate->display.hdmi.group == HDMI_RES_GROUP_CEA)?
|
|
" RGB lim" : " RGB full");
|
|
}
|
|
|
|
//This is the format's aspect ratio, not the one
|
|
//signaled in the AVI infoframe
|
|
if(!tmp)
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " %s", aspect_ratio_str(tvstate->display.hdmi.aspect_ratio));
|
|
|
|
if(!tmp &&tvstate->display.hdmi.pixel_rep) {
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " x%d", tvstate->display.hdmi.pixel_rep);
|
|
}
|
|
if(!tmp && tvstate->state & VC_HDMI_HDCP_AUTH) {
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " HDCP");
|
|
}
|
|
} else if(tvstate->state & ( VC_SDTV_NTSC | VC_SDTV_PAL )) {
|
|
if(!tmp) {
|
|
if(tvstate->state & VC_SDTV_NTSC) {
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, "NTSC");
|
|
} else {
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, "PAL");
|
|
}
|
|
}
|
|
if(!tmp && tvstate->display.sdtv.cp_mode) {
|
|
switch(tvstate->display.sdtv.cp_mode) {
|
|
case SDTV_CP_MACROVISION_TYPE1:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision type 1"); break;
|
|
case SDTV_CP_MACROVISION_TYPE2:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision type 2"); break;
|
|
case SDTV_CP_MACROVISION_TYPE3:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision type 3"); break;
|
|
case SDTV_CP_MACROVISION_TEST1:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision test 1"); break;
|
|
case SDTV_CP_MACROVISION_TEST2:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " Macrovision test 2"); break;
|
|
case SDTV_CP_CGMS_COPYFREE:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CGMS copy free"); break;
|
|
case SDTV_CP_CGMS_COPYNOMORE:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CGMS copy no more"); break;
|
|
case SDTV_CP_CGMS_COPYONCE:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CGMS copy once"); break;
|
|
case SDTV_CP_CGMS_COPYNEVER:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " CGMS copy never"); break;
|
|
case SDTV_CP_WSS_COPYFREE:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " WSS copy free"); break;
|
|
case SDTV_CP_WSS_COPYRIGHT_COPYFREE:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " WSS (c) copy free"); break;
|
|
case SDTV_CP_WSS_NOCOPY:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " WSS no copy"); break;
|
|
case SDTV_CP_WSS_COPYRIGHT_NOCOPY:
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " WSS (c) no copy"); break;
|
|
default: break;
|
|
}
|
|
}
|
|
//This is the format's aspect ratio
|
|
tmp = status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, " %s", aspect_ratio_sd_str(tvstate->display.sdtv.display_options.aspect));
|
|
} else if (tvstate->state & VC_LCD_ATTACHED_DEFAULT) {
|
|
status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, "LCD");
|
|
} else {
|
|
status_sprintf(mode_str, MAX_STATUS_STR_LENGTH, &offset, "TV is off");
|
|
}
|
|
|
|
return mode_str;
|
|
}
|
|
|
|
|
|
static int get_status(int display_id)
|
|
{
|
|
TV_DISPLAY_STATE_T tvstate;
|
|
|
|
int ok = display_id != -1 ? vc_tv_get_display_state_id( display_id, &tvstate) : vc_tv_get_display_state(&tvstate);
|
|
|
|
if(ok == 0) {
|
|
//The width/height parameters are in the same position in the union
|
|
//for HDMI and SDTV
|
|
HDMI_PROPERTY_PARAM_T property;
|
|
property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
|
|
|
|
if (display_id != -1)
|
|
vc_tv_hdmi_get_property_id(display_id, &property);
|
|
else
|
|
vc_tv_hdmi_get_property(&property);
|
|
|
|
float frame_rate = property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC ? tvstate.display.hdmi.frame_rate * (1000.0f/1001.0f) : tvstate.display.hdmi.frame_rate;
|
|
|
|
if(tvstate.display.hdmi.width && tvstate.display.hdmi.height) {
|
|
LOG_STD( "state 0x%x [%s], %ux%u @ %.2fHz, %s", tvstate.state,
|
|
status_mode(&tvstate),
|
|
tvstate.display.hdmi.width, tvstate.display.hdmi.height,
|
|
frame_rate,
|
|
tvstate.display.hdmi.scan_mode ? "interlaced" : "progressive" );
|
|
} else {
|
|
LOG_STD( "state 0x%x [%s]", tvstate.state, status_mode(&tvstate));
|
|
}
|
|
} else {
|
|
LOG_STD( "Error getting current display state");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int get_audiosup(int display_id)
|
|
{
|
|
uint8_t sample_rates[] = {32, 44, 48, 88, 96, 176, 192};
|
|
uint8_t sample_sizes[] = {16, 20, 24};
|
|
const char *formats[] = {"Reserved", "PCM", "AC3", "MPEG1", "MP3", "MPEG2", "AAC", "DTS", "ATRAC", "DSD", "EAC3", "DTS_HD", "MLP", "DST", "WMAPRO", "Extended"};
|
|
int i, j, k;
|
|
for (k=EDID_AudioFormat_ePCM; k<EDID_AudioFormat_eMaxCount; k++) {
|
|
int num_channels = 0, max_sample_rate = 0, max_sample_size = 0;
|
|
for (i=1; i<=8; i++) {
|
|
if ((display_id != -1 ? vc_tv_hdmi_audio_supported_id(display_id, k, i, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) :
|
|
vc_tv_hdmi_audio_supported(k, i, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit )) == 0)
|
|
num_channels = i;
|
|
}
|
|
for (i=0, j=EDID_AudioSampleRate_e32KHz; j<=EDID_AudioSampleRate_e192KHz; i++, j<<=1) {
|
|
if ((display_id != -1 ? vc_tv_hdmi_audio_supported_id(display_id, k, 1, j, EDID_AudioSampleSize_16bit ) :
|
|
vc_tv_hdmi_audio_supported(k, 1, j, EDID_AudioSampleSize_16bit )) == 0)
|
|
max_sample_rate = i;
|
|
}
|
|
if (k==EDID_AudioFormat_ePCM) {
|
|
for (i=0, j=EDID_AudioSampleSize_16bit; j<=EDID_AudioSampleSize_24bit; i++, j<<=1) {
|
|
if (display_id != -1 ? vc_tv_hdmi_audio_supported_id(display_id, k, 1, EDID_AudioSampleRate_e44KHz, j ) :
|
|
vc_tv_hdmi_audio_supported(k, 1, EDID_AudioSampleRate_e44KHz, j ) == 0)
|
|
max_sample_size = i;
|
|
}
|
|
if (num_channels>0)
|
|
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]);
|
|
} else {
|
|
for (i=0; i<256; i++) {
|
|
if (display_id != -1 ? vc_tv_hdmi_audio_supported_id(display_id, k, 1, EDID_AudioSampleRate_e44KHz, i ) :
|
|
vc_tv_hdmi_audio_supported(k, 1, EDID_AudioSampleRate_e44KHz, i ) == 0)
|
|
max_sample_size = i;
|
|
}
|
|
if (num_channels>0)
|
|
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);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int dump_edid( int display_id, const char *filename )
|
|
{
|
|
uint8_t buffer[128];
|
|
size_t written = 0, offset = 0;
|
|
int i, extensions = 0;
|
|
FILE *fp = NULL;
|
|
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);;
|
|
offset += sizeof( buffer);
|
|
/* First block always exist */
|
|
if (siz == sizeof(buffer) && (fp = fopen(filename, "wb")) != NULL) {
|
|
written += fwrite(buffer, 1, sizeof buffer, fp);
|
|
extensions = buffer[0x7e]; /* This tells you how many more blocks to read */
|
|
for(i = 0; i < extensions; i++, offset += sizeof( buffer)) {
|
|
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);
|
|
if(siz == sizeof(buffer)) {
|
|
written += fwrite(buffer, 1, sizeof (buffer), fp);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (fp)
|
|
fclose(fp);
|
|
if(written) {
|
|
LOG_STD( "Written %zu bytes to %s", written, filename);
|
|
} else {
|
|
LOG_STD( "Nothing written!");
|
|
}
|
|
return written < sizeof(buffer);
|
|
}
|
|
|
|
static int show_info(int display_id, int on )
|
|
{
|
|
return display_id != -1 ? vc_tv_show_info_id(display_id, on) : vc_tv_show_info(on);
|
|
}
|
|
|
|
static void control_c( int signum )
|
|
{
|
|
(void)signum;
|
|
|
|
LOG_STD( "Shutting down..." );
|
|
|
|
vcos_event_signal( &quit_event );
|
|
}
|
|
|
|
static void tvservice_callback( void *callback_data,
|
|
uint32_t reason,
|
|
uint32_t param1,
|
|
uint32_t param2 )
|
|
{
|
|
(void)callback_data;
|
|
(void)param1;
|
|
(void)param2;
|
|
|
|
switch ( reason )
|
|
{
|
|
case VC_HDMI_UNPLUGGED:
|
|
{
|
|
LOG_INFO( "HDMI cable is unplugged. Display %d", param1 );
|
|
break;
|
|
}
|
|
case VC_HDMI_ATTACHED:
|
|
{
|
|
LOG_INFO( "HDMI is attached" );
|
|
break;
|
|
}
|
|
case VC_HDMI_DVI:
|
|
{
|
|
LOG_INFO( "HDMI in DVI mode" );
|
|
break;
|
|
}
|
|
case VC_HDMI_HDMI:
|
|
{
|
|
LOG_INFO( "HDMI in HDMI mode" );
|
|
break;
|
|
}
|
|
case VC_HDMI_HDCP_UNAUTH:
|
|
{
|
|
LOG_INFO( "HDCP authentication is broken" );
|
|
break;
|
|
}
|
|
case VC_HDMI_HDCP_AUTH:
|
|
{
|
|
LOG_INFO( "HDCP is active" );
|
|
break;
|
|
}
|
|
case VC_HDMI_HDCP_KEY_DOWNLOAD:
|
|
{
|
|
LOG_INFO( "HDCP key download" );
|
|
break;
|
|
}
|
|
case VC_HDMI_HDCP_SRM_DOWNLOAD:
|
|
{
|
|
LOG_INFO( "HDCP revocation list download" );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Ignore all other reasons
|
|
LOG_INFO( "Callback with reason %d", reason );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int start_monitor( void )
|
|
{
|
|
if ( vcos_event_create( &quit_event, "tvservice" ) != VCOS_SUCCESS )
|
|
{
|
|
LOG_ERR( "Failed to create quit event" );
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Handle the INT and TERM signals so we can quit
|
|
signal( SIGINT, control_c );
|
|
signal( SIGTERM, control_c );
|
|
|
|
vc_tv_register_callback( &tvservice_callback, NULL );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int power_on_preferred(int display_id)
|
|
{
|
|
int ret;
|
|
|
|
LOG_STD( "Powering on HDMI with preferred settings" );
|
|
|
|
ret = display_id != -1 ? vc_tv_hdmi_power_on_preferred_id(display_id) : vc_tv_hdmi_power_on_preferred();
|
|
if ( ret != 0 )
|
|
{
|
|
LOG_ERR( "Failed to power on HDMI with preferred settings" );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int power_on_explicit( int display_id, HDMI_RES_GROUP_T group,
|
|
uint32_t mode, uint32_t drive )
|
|
{
|
|
int ret;
|
|
|
|
LOG_STD( "Powering on HDMI with explicit settings (%s mode %u)",
|
|
group == HDMI_RES_GROUP_CEA ? "CEA" : group == HDMI_RES_GROUP_DMT ? "DMT" : "CUSTOM", mode );
|
|
|
|
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 );
|
|
if ( ret != 0 )
|
|
{
|
|
LOG_ERR( "Failed to power on HDMI with explicit settings (%s mode %u)",
|
|
group == HDMI_RES_GROUP_CEA ? "CEA" : group == HDMI_RES_GROUP_DMT ? "DMT" : "CUSTOM", mode );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int set_property(int display_id, HDMI_PROPERTY_T prop, uint32_t param1, uint32_t param2)
|
|
{
|
|
int ret;
|
|
HDMI_PROPERTY_PARAM_T property;
|
|
property.property = prop;
|
|
property.param1 = param1;
|
|
property.param2 = param2;
|
|
//LOG_DBG( "Setting property %d with params %d, %d", prop, param1, param2);
|
|
ret = display_id != -1 ? vc_tv_hdmi_set_property_id(display_id, &property) : vc_tv_hdmi_set_property(&property);
|
|
if(ret != 0)
|
|
{
|
|
LOG_ERR( "Failed to set property %d", prop);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int power_on_sdtv(int display_id, SDTV_MODE_T mode,
|
|
SDTV_ASPECT_T aspect, int sdtv_progressive )
|
|
{
|
|
int ret;
|
|
SDTV_OPTIONS_T options;
|
|
memset(&options, 0, sizeof options);
|
|
options.aspect = aspect;
|
|
if (sdtv_progressive)
|
|
mode |= SDTV_MODE_PROGRESSIVE;
|
|
LOG_STD( "Powering on SDTV with explicit settings (mode:%d aspect:%d)",
|
|
mode, aspect );
|
|
|
|
ret = display_id != -1 ? vc_tv_sdtv_power_on_id(display_id, mode, &options) : vc_tv_sdtv_power_on(mode, &options);
|
|
|
|
if ( ret != 0 )
|
|
{
|
|
LOG_ERR( "Failed to power on SDTV with explicit settings (mode:%d aspect:%d)",
|
|
mode, aspect );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int power_off(int display_id)
|
|
{
|
|
int ret;
|
|
|
|
LOG_STD( "Powering off HDMI");
|
|
|
|
ret = display_id ? vc_tv_power_off_id(display_id) : vc_tv_power_off();
|
|
if ( ret != 0 )
|
|
{
|
|
LOG_ERR( "Failed to power off HDMI" );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int list_attached_devices()
|
|
{
|
|
char *display_to_text[] =
|
|
{
|
|
"Main LCD",
|
|
"Auxiliary LCD",
|
|
"HDMI 0",
|
|
"Composite",
|
|
"Forced LCD",
|
|
"Forced TV",
|
|
"Forced Other",
|
|
"HDMI 1",
|
|
"Forced TV2"
|
|
};
|
|
|
|
const int display_to_text_size = sizeof(display_to_text)/sizeof(display_to_text[0]);
|
|
|
|
TV_ATTACHED_DEVICES_T devices;
|
|
int i;
|
|
|
|
if (vc_tv_get_attached_devices(&devices) == -1)
|
|
{
|
|
LOG_ERR("No multi display support in firmware!");
|
|
return -1;
|
|
}
|
|
|
|
LOG_STD("%d attached device(s), display ID's are : ", devices.num_attached);
|
|
|
|
for (i=0;i<devices.num_attached;i++)
|
|
{
|
|
if (devices.display_number[i] <= display_to_text_size)
|
|
LOG_STD("Display Number %d, type %s", devices.display_number[i], display_to_text[devices.display_number[i]]);
|
|
else
|
|
LOG_STD("Display Number %d, out of range", devices.display_number[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ---- Public Functions -----------------------------------------------------
|
|
|
|
int main( int argc, char **argv )
|
|
{
|
|
int32_t ret;
|
|
char optstring[OPTSTRING_LEN];
|
|
int opt;
|
|
int opt_preferred = 0;
|
|
int opt_explicit = 0;
|
|
int opt_ntsc = 0;
|
|
int opt_sdtvon = 0;
|
|
int opt_off = 0;
|
|
int opt_modes = 0;
|
|
int opt_monitor = 0;
|
|
int opt_status = 0;
|
|
int opt_audiosup = 0;
|
|
int opt_dumpedid = 0;
|
|
int opt_showinfo = 0;
|
|
int opt_3d = 0;
|
|
int opt_json = 0;
|
|
int opt_name = 0;
|
|
int opt_list = 0;
|
|
int opt_device = -1;
|
|
|
|
char *dumpedid_filename = NULL;
|
|
VCHI_INSTANCE_T vchi_instance;
|
|
VCHI_CONNECTION_T *vchi_connection;
|
|
HDMI_RES_GROUP_T power_on_explicit_group = HDMI_RES_GROUP_INVALID;
|
|
uint32_t power_on_explicit_mode;
|
|
uint32_t power_on_explicit_drive = HDMI_MODE_HDMI;
|
|
HDMI_RES_GROUP_T get_modes_group = HDMI_RES_GROUP_INVALID;
|
|
SDTV_MODE_T sdtvon_mode = SDTV_MODE_NTSC;
|
|
SDTV_ASPECT_T sdtvon_aspect = SDTV_ASPECT_UNKNOWN;
|
|
int sdtvon_progressive = 0;
|
|
TV_ATTACHED_DEVICES_T devices;
|
|
|
|
// Initialize VCOS
|
|
vcos_init();
|
|
|
|
// Create the option string that we will be using to parse the arguments
|
|
create_optstring( optstring );
|
|
|
|
// Parse the command line arguments
|
|
while (( opt = getopt_long_only( argc, argv, optstring, long_opts,
|
|
NULL )) != -1 )
|
|
{
|
|
switch ( opt )
|
|
{
|
|
case 0:
|
|
{
|
|
// getopt_long returns 0 for entries where flag is non-NULL
|
|
break;
|
|
}
|
|
case OPT_PREFERRED:
|
|
{
|
|
opt_preferred = 1;
|
|
break;
|
|
}
|
|
case OPT_EXPLICIT:
|
|
{
|
|
char group_str[32], drive_str[32];
|
|
|
|
/* coverity[secure_coding] String length specified, so can't overflow */
|
|
int s = sscanf( optarg, "%31s %u %31s", group_str, &power_on_explicit_mode, drive_str );
|
|
if ( s != 2 && s != 3 )
|
|
{
|
|
LOG_ERR( "Invalid arguments '%s'", optarg );
|
|
goto err_out;
|
|
}
|
|
|
|
// Check the group first
|
|
if ( vcos_strcasecmp( "CEA", group_str ) == 0 )
|
|
{
|
|
power_on_explicit_group = HDMI_RES_GROUP_CEA;
|
|
}
|
|
else if ( vcos_strcasecmp( "DMT", group_str ) == 0 )
|
|
{
|
|
power_on_explicit_group = HDMI_RES_GROUP_DMT;
|
|
}
|
|
else if ( vcos_strcasecmp( "CEA_3D", group_str ) == 0 ||
|
|
vcos_strcasecmp( "CEA_3D_SBS", group_str ) == 0)
|
|
{
|
|
power_on_explicit_group = HDMI_RES_GROUP_CEA;
|
|
opt_3d = 1;
|
|
}
|
|
else if ( vcos_strcasecmp( "CEA_3D_TB", group_str ) == 0 )
|
|
{
|
|
power_on_explicit_group = HDMI_RES_GROUP_CEA;
|
|
opt_3d = 2;
|
|
}
|
|
else if ( vcos_strcasecmp( "CEA_3D_FP", group_str ) == 0 )
|
|
{
|
|
power_on_explicit_group = HDMI_RES_GROUP_CEA;
|
|
opt_3d = 3;
|
|
}
|
|
else if ( vcos_strcasecmp( "CEA_3D_FS", group_str ) == 0 )
|
|
{
|
|
power_on_explicit_group = HDMI_RES_GROUP_CEA;
|
|
opt_3d = 4;
|
|
}
|
|
else
|
|
{
|
|
LOG_ERR( "Invalid group '%s'", group_str );
|
|
goto err_out;
|
|
}
|
|
if (s==3)
|
|
{
|
|
if (vcos_strcasecmp( "HDMI", drive_str ) == 0 )
|
|
{
|
|
power_on_explicit_drive = HDMI_MODE_HDMI;
|
|
}
|
|
else if (vcos_strcasecmp( "DVI", drive_str ) == 0 )
|
|
{
|
|
power_on_explicit_drive = HDMI_MODE_DVI;
|
|
}
|
|
else
|
|
{
|
|
LOG_ERR( "Invalid drive '%s'", drive_str );
|
|
goto err_out;
|
|
}
|
|
}
|
|
// Then check if mode is a sane number
|
|
if ( power_on_explicit_mode > MAX_MODE_ID )
|
|
{
|
|
LOG_ERR( "Invalid mode '%u'", power_on_explicit_mode );
|
|
goto err_out;
|
|
}
|
|
|
|
opt_explicit = 1;
|
|
break;
|
|
}
|
|
case OPT_NTSC:
|
|
{
|
|
opt_ntsc = 1;
|
|
break;
|
|
}
|
|
case OPT_SDTVON:
|
|
{
|
|
char mode_str[32], aspect_str[32], progressive_str[32];
|
|
int s = sscanf( optarg, "%s %s %s", mode_str, aspect_str, progressive_str );
|
|
if ( s != 2 && s != 3 )
|
|
{
|
|
LOG_ERR( "Invalid arguments '%s'", optarg );
|
|
goto err_out;
|
|
}
|
|
|
|
// Check the group first
|
|
if ( vcos_strcasecmp( "NTSC", mode_str ) == 0 )
|
|
{
|
|
sdtvon_mode = SDTV_MODE_NTSC;
|
|
}
|
|
else if ( vcos_strcasecmp( "NTSC_J", mode_str ) == 0 )
|
|
{
|
|
sdtvon_mode = SDTV_MODE_NTSC_J;
|
|
}
|
|
else if ( vcos_strcasecmp( "PAL", mode_str ) == 0 )
|
|
{
|
|
sdtvon_mode = SDTV_MODE_PAL;
|
|
}
|
|
else if ( vcos_strcasecmp( "PAL_M", mode_str ) == 0 )
|
|
{
|
|
sdtvon_mode = SDTV_MODE_PAL_M;
|
|
}
|
|
else
|
|
{
|
|
LOG_ERR( "Invalid mode '%s'", mode_str );
|
|
goto err_out;
|
|
}
|
|
|
|
if ( vcos_strcasecmp( "4:3", aspect_str ) == 0 )
|
|
{
|
|
sdtvon_aspect = SDTV_ASPECT_4_3;
|
|
}
|
|
else if ( vcos_strcasecmp( "14:9", aspect_str ) == 0 )
|
|
{
|
|
sdtvon_aspect = SDTV_ASPECT_14_9;
|
|
}
|
|
else if ( vcos_strcasecmp( "16:9", aspect_str ) == 0 )
|
|
{
|
|
sdtvon_aspect = SDTV_ASPECT_16_9;
|
|
}
|
|
|
|
if (s == 3 && vcos_strcasecmp( "P", progressive_str ) == 0 )
|
|
{
|
|
sdtvon_progressive = 1;
|
|
}
|
|
opt_sdtvon = 1;
|
|
break;
|
|
}
|
|
case OPT_OFF:
|
|
{
|
|
opt_off = 1;
|
|
break;
|
|
}
|
|
case OPT_MODES:
|
|
{
|
|
if ( vcos_strcasecmp( "CEA", optarg ) == 0 )
|
|
{
|
|
get_modes_group = HDMI_RES_GROUP_CEA;
|
|
}
|
|
else if ( vcos_strcasecmp( "DMT", optarg ) == 0 )
|
|
{
|
|
get_modes_group = HDMI_RES_GROUP_DMT;
|
|
}
|
|
else
|
|
{
|
|
LOG_ERR( "Invalid group '%s'", optarg );
|
|
goto err_out;
|
|
}
|
|
|
|
opt_modes = 1;
|
|
break;
|
|
}
|
|
case OPT_MONITOR:
|
|
{
|
|
opt_monitor = 1;
|
|
break;
|
|
}
|
|
case OPT_STATUS:
|
|
{
|
|
opt_status = 1;
|
|
break;
|
|
}
|
|
case OPT_AUDIOSUP:
|
|
{
|
|
opt_audiosup = 1;
|
|
break;
|
|
}
|
|
case OPT_DUMPEDID:
|
|
{
|
|
opt_dumpedid = 1;
|
|
dumpedid_filename = optarg;
|
|
break;
|
|
}
|
|
case OPT_SHOWINFO:
|
|
{
|
|
opt_showinfo = atoi(optarg)+1;
|
|
break;
|
|
}
|
|
case OPT_JSON:
|
|
{
|
|
opt_json = 1;
|
|
break;
|
|
}
|
|
case OPT_NAME:
|
|
{
|
|
opt_name = 1;
|
|
break;
|
|
}
|
|
|
|
case OPT_LIST:
|
|
{
|
|
opt_list = 1;
|
|
break;
|
|
}
|
|
|
|
case OPT_DEVICE:
|
|
{
|
|
opt_device = atoi(optarg);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
LOG_ERR( "Unrecognized option '%d'\n", opt );
|
|
goto err_usage;
|
|
}
|
|
case '?':
|
|
case OPT_HELP:
|
|
{
|
|
goto err_usage;
|
|
}
|
|
} // end switch
|
|
} // end while
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (( optind == 1 ) || ( argc > 0 ))
|
|
{
|
|
if ( argc > 0 )
|
|
{
|
|
LOG_ERR( "Unrecognized argument -- '%s'", *argv );
|
|
}
|
|
|
|
goto err_usage;
|
|
}
|
|
|
|
if (( opt_preferred + opt_explicit + opt_sdtvon > 1 ))
|
|
{
|
|
LOG_ERR( "Conflicting power on options" );
|
|
goto err_usage;
|
|
}
|
|
|
|
if ((( opt_preferred == 1 ) || ( opt_explicit == 1 ) || ( opt_sdtvon == 1)) && ( opt_off == 1 ))
|
|
{
|
|
LOG_ERR( "Cannot power on and power off simultaneously" );
|
|
goto err_out;
|
|
}
|
|
|
|
// Initialize the VCHI connection
|
|
ret = vchi_initialise( &vchi_instance );
|
|
if ( ret != 0 )
|
|
{
|
|
LOG_ERR( "Failed to initialize VCHI (ret=%d)", ret );
|
|
goto err_out;
|
|
}
|
|
|
|
ret = vchi_connect( NULL, 0, vchi_instance );
|
|
if ( ret != 0)
|
|
{
|
|
LOG_ERR( "Failed to create VCHI connection (ret=%d)", ret );
|
|
goto err_out;
|
|
}
|
|
|
|
// Initialize the tvservice
|
|
vc_vchi_tv_init( vchi_instance, &vchi_connection, 1 );
|
|
|
|
// Make a call that will determine whether we have multi display support
|
|
if (vc_tv_get_attached_devices(&devices) != -1)
|
|
{
|
|
// Check any device_id specified
|
|
if (opt_device != -1)
|
|
{
|
|
int i;
|
|
|
|
// Check against the connected devices
|
|
for (i=0;i<devices.num_attached;i++)
|
|
{
|
|
if (devices.display_number[i] == opt_device)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == devices.num_attached)
|
|
{
|
|
LOG_ERR( "Invalid device/display ID");
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
opt_device = -1;
|
|
|
|
if ( opt_monitor == 1 )
|
|
{
|
|
LOG_STD( "Starting to monitor for HDMI events" );
|
|
|
|
if ( start_monitor() != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
|
|
if ( opt_modes == 1 )
|
|
{
|
|
if ( get_modes( opt_device, get_modes_group, opt_json ) != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
|
|
if ( opt_preferred == 1 )
|
|
{
|
|
if(set_property( opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_NONE, 0) != 0)
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
if ( power_on_preferred(opt_device) != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
else if ( opt_explicit == 1 )
|
|
{
|
|
//Distinguish between turning on 3D side by side and 3D top/bottom
|
|
if(opt_3d == 0 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_NONE, 0) != 0)
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
else if(opt_3d == 1 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_SBS_HALF, 0) != 0)
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
else if(opt_3d == 2 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_TB_HALF, 0) != 0)
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
else if(opt_3d == 3 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_FRAME_PACKING, 0) != 0)
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
else if(opt_3d == 4 && set_property(opt_device, HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_FRAME_SEQUENTIAL, 0) != 0)
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
if (set_property(opt_device, HDMI_PROPERTY_PIXEL_CLOCK_TYPE, opt_ntsc ? HDMI_PIXEL_CLOCK_TYPE_NTSC : HDMI_PIXEL_CLOCK_TYPE_PAL, 0) != 0)
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
if ( power_on_explicit( opt_device,
|
|
power_on_explicit_group,
|
|
power_on_explicit_mode, power_on_explicit_drive ) != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
else if ( opt_sdtvon == 1 )
|
|
{
|
|
if ( power_on_sdtv( opt_device, sdtvon_mode,
|
|
sdtvon_aspect, sdtvon_progressive ) != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
else if (opt_off == 1 )
|
|
{
|
|
if ( power_off(opt_device) != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
|
|
if ( opt_status == 1 )
|
|
{
|
|
if ( get_status(opt_device) != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
|
|
if ( opt_audiosup == 1 )
|
|
{
|
|
if ( get_audiosup(opt_device) != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
|
|
if ( opt_dumpedid == 1 )
|
|
{
|
|
if ( dump_edid(opt_device, dumpedid_filename) != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
|
|
if ( opt_showinfo )
|
|
{
|
|
if ( show_info(opt_device, opt_showinfo-1) != 0 )
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
|
|
if ( opt_name == 1 )
|
|
{
|
|
TV_DEVICE_ID_T id;
|
|
memset(&id, 0, sizeof(id));
|
|
if( (opt_device != -1 ? vc_tv_get_device_id_id(opt_device, &id) : vc_tv_get_device_id(&id) )== 0) {
|
|
if(id.vendor[0] == '\0' || id.monitor_name[0] == '\0') {
|
|
LOG_ERR( "No device present" );
|
|
} else {
|
|
LOG_STD( "device_name=%s-%s", id.vendor, id.monitor_name);
|
|
}
|
|
} else {
|
|
LOG_ERR( "Failed to obtain device name" );
|
|
}
|
|
}
|
|
|
|
if ( opt_monitor == 1 )
|
|
{
|
|
// Wait until we get the signal to exit
|
|
vcos_event_wait( &quit_event );
|
|
|
|
vcos_event_delete( &quit_event );
|
|
}
|
|
|
|
if ( opt_list == 1 )
|
|
{
|
|
if (list_attached_devices() != 0)
|
|
{
|
|
goto err_stop_service;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
err_stop_service:
|
|
// LOG_INFO( "Stopping tvservice" );
|
|
|
|
// Stop the tvservice
|
|
vc_vchi_tv_stop();
|
|
|
|
// Disconnect the VCHI connection
|
|
vchi_disconnect( vchi_instance );
|
|
|
|
exit( 0 );
|
|
|
|
err_usage:
|
|
show_usage();
|
|
|
|
err_out:
|
|
exit( 1 );
|
|
}
|