static int get_audiosup( void ) { 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 (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 (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 (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 (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( const char *filename ) { uint8_t buffer[128]; size_t written = 0, offset = 0; int i, extensions = 0; FILE *fp = NULL; int siz = 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 = 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 %d bytes to %s", written, filename); } else { LOG_STD( "Nothing written!"); } return written < sizeof(buffer); }
static int get_status( void ) { TV_DISPLAY_STATE_T tvstate; if( vc_tv_get_display_state( &tvstate ) == 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; 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 void control_c( int signum ) { (void)signum; LOG_STD( "Shutting down..." ); vcos_event_signal( &quit_event ); }
static int get_modes( HDMI_RES_GROUP_T group) { static TV_SUPPORTED_MODE_T supported_modes[MAX_MODE_ID] = {{0}}; 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 )); num_modes = vc_tv_hdmi_get_supported_modes( group, supported_modes, vcos_countof(supported_modes), &preferred_group, &preferred_mode ); if ( num_modes < 0 ) { LOG_ERR( "Failed to get modes" ); return -1; } 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 ) snprintf(p, sizeof(p)-1, "x%d ", supported_modes[i].pixel_rep); else snprintf(p, sizeof(p)-1, " "); LOG_STD( "%s mode %u: %ux%u @ %uHz %s, clock:%luMHz %s%s %s", 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 / 1000000UL, p, supported_modes[i].scan_mode ? "interlaced" : "progressive", supported_modes[i].struct_3d_mask ? threed_str(supported_modes[i].struct_3d_mask) : ""); } return 0; }
static int get_status( void ) { TV_DISPLAY_STATE_T tvstate; if( vc_tv_get_display_state( &tvstate ) == 0) { //The width/height parameters are in the same position in the union //for HDMI and SDTV if(tvstate.display.hdmi.width && tvstate.display.hdmi.height) { LOG_STD( "state 0x%x [%s], %ux%u @ %uHz, %s", tvstate.state, status_mode(&tvstate), tvstate.display.hdmi.width, tvstate.display.hdmi.height, tvstate.display.hdmi.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 power_off( void ) { int ret; LOG_STD( "Powering off HDMI"); ret = vc_tv_power_off(); if ( ret != 0 ) { LOG_ERR( "Failed to power off HDMI" ); } return ret; }
static int power_on_preferred( void ) { int ret; LOG_STD( "Powering on HDMI with preferred settings" ); ret = vc_tv_hdmi_power_on_preferred(); if ( ret != 0 ) { LOG_ERR( "Failed to power on HDMI with preferred settings" ); } return ret; }
static int set_property(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_STD( "Setting property %d with params %d, %d", prop, param1, param2); ret = vc_tv_hdmi_set_property(&property); if(ret != 0) { LOG_ERR( "Failed to set property %d", prop); } return ret; }
static int power_on_explicit( 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 = 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 power_on_sdtv( SDTV_MODE_T mode, SDTV_ASPECT_T aspect ) { int ret; SDTV_OPTIONS_T options; memset(&options, 0, sizeof options); options.aspect = aspect; LOG_STD( "Powering on SDTV with explicit settings (mode:%d aspect:%d)", mode, aspect ); ret = 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; }
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_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; 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_ASPECT_T sdtvon_aspect; // 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 { 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_SDTVON: { char mode_str[32], aspect_str[32]; if ( sscanf( optarg, "%s %s", mode_str, aspect_str ) != 2 ) { 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; } 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; } 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; } // LOG_INFO( "Starting tvservice" ); // Initialize the tvservice vc_vchi_tv_init( vchi_instance, &vchi_connection, 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( get_modes_group, opt_json ) != 0 ) { goto err_stop_service; } } if ( opt_preferred == 1 ) { if ( power_on_preferred() != 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 == 1 && set_property( HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_SBS_HALF, 0) != 0) { goto err_stop_service; } else if(opt_3d == 2 && set_property( HDMI_PROPERTY_3D_STRUCTURE, HDMI_3D_FORMAT_TB_HALF, 0) != 0) { goto err_stop_service; } if ( power_on_explicit( 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( sdtvon_mode, sdtvon_aspect ) != 0 ) { goto err_stop_service; } } else if (opt_off == 1 ) { if ( power_off() != 0 ) { goto err_stop_service; } } if ( opt_status == 1 ) { if ( get_status() != 0 ) { goto err_stop_service; } } if ( opt_audiosup == 1 ) { if ( get_audiosup() != 0 ) { goto err_stop_service; } } if ( opt_dumpedid == 1 ) { if ( dump_edid(dumpedid_filename) != 0 ) { goto err_stop_service; } } if ( opt_showinfo ) { if ( show_info(opt_showinfo-1) != 0 ) { goto err_stop_service; } } if ( opt_name == 1 ) { TV_DEVICE_ID_T id; memset(&id, 0, sizeof(id)); if(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 ); } 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 ); }
static int get_modes( 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)); num_modes = vc_tv_hdmi_get_supported_modes( 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 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)\n" " MODE (see --modes) and DRIVE (HDMI, DVI)" ); LOG_STD( " -c, --sdtvon=\"MODE ASPECT\" Power on SDTV with MODE (PAL or NTSC) and ASPECT (4:3 14:9 or 16:9)" ); 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( " -h, --help Print this information" ); }