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