1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, 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
70enum
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
92static 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
115static VCOS_EVENT_T quit_event;
116
117// ---- Private Functions ---------------------------------------------------
118
119static 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
140static 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 */
168static 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 */
190static 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
205static 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 */
223static 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
253static 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
332static 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
451static 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
485static 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
524static 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
555static 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
560static void control_c( int signum )
561{
562 (void)signum;
563
564 LOG_STD( "Shutting down..." );
565
566 vcos_event_signal( &quit_event );
567}
568
569static 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
629static 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
647static 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
662static 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
680static 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
696static 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
719static 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
734static 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
775int 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
1262err_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
1273err_usage:
1274 show_usage();
1275
1276err_out:
1277 exit( 1 );
1278}
1279