Bug Summary

File:/shared/playproj/i2c/src/ddc/ddc_displays.c
Warning:line 207, column 16
Value stored to 'psc' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name ddc_displays.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/shared/playproj/i2c/src/ddc -resource-dir /usr/lib/llvm-13/lib/clang/13.0.0 -D HAVE_CONFIG_H -I . -I ../.. -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I ../../src -I ../../src/public -D PIC -internal-isystem /usr/lib/llvm-13/lib/clang/13.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-compound-token-split-by-macro -fdebug-compilation-dir=/shared/playproj/i2c/src/ddc -ferror-limit 19 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o ddc_displays.plist -x c ddc_displays.c
1/** @file ddc_displays.c
2 * Access displays, whether DDC or USB
3 *
4 * This file and ddc_display_ref_reports.c cross-reference each other.
5 */
6
7// Copyright (C) 2014-2022 Sanford Rockowitz <rockowitz@minsoft.com>
8// SPDX-License-Identifier: GPL-2.0-or-later
9
10#include "config.h"
11
12/** \cond */
13#include <assert.h>
14#include <errno(*__errno_location ()).h>
15#include <glib-2.0/glib.h>
16#include <string.h>
17#include <sys/stat.h>
18#include <time.h>
19
20#include "util/debug_util.h"
21#include "util/edid.h"
22#include "util/error_info.h"
23#include "util/failsim.h"
24#include "util/report_util.h"
25#include "util/string_util.h"
26#include "util/sysfs_util.h"
27#ifdef ENABLE_UDEV1
28#include "util/udev_usb_util.h"
29#include "util/udev_util.h"
30#endif
31/** \endcond */
32
33#include "public/ddcutil_types.h"
34#include "private/ddcutil_types_private.h"
35
36#include "base/core.h"
37#include "base/ddc_packets.h"
38#include "base/feature_metadata.h"
39#include "base/linux_errno.h"
40#include "base/monitor_model_key.h"
41#include "base/parms.h"
42#include "base/rtti.h"
43
44#include "vcp/vcp_feature_codes.h"
45
46#include "i2c/i2c_bus_core.h"
47#include "i2c/i2c_strategy_dispatcher.h"
48#include "i2c/i2c_sysfs.h"
49
50#ifdef USE_USB1
51#include "usb/usb_displays.h"
52#endif
53
54#include "dynvcp/dyn_feature_files.h"
55
56#include "ddc/ddc_packet_io.h"
57#include "ddc/ddc_vcp.h"
58#include "ddc/ddc_vcp_version.h"
59
60#include "ddc/ddc_display_ref_reports.h"
61#include "ddc/ddc_displays.h"
62
63// Default trace class for this file
64static DDCA_Trace_Group TRACE_GROUP = DDCA_TRC_DDC;
65
66static GPtrArray * all_displays = NULL((void*)0); // all detected displays
67static GPtrArray * display_open_errors = NULL((void*)0); // array of Bus_Open_Error
68static int dispno_max = 0; // highest assigned display number
69static int async_threshold = DISPLAY_CHECK_ASYNC_THRESHOLD_DEFAULT0xff;
70#ifdef USE_USB1
71static bool_Bool detect_usb_displays = true1;
72#else
73static bool_Bool detect_usb_displays = false0;
74#endif
75
76//
77// Functions to perform initial checks
78//
79
80/** Sets the threshold for async display examination.
81 * If the number of /dev/i2c devices for which DDC communication is to be
82 * checked is greater than or equal to the threshold value, examine each
83 * device in a separate thread.
84 *
85 * @param threshold threshold value
86 */
87void
88ddc_set_async_threshold(int threshold) {
89 // DBGMSG("threshold = %d", threshold);
90 async_threshold = threshold;
91}
92
93
94static inline bool_Bool
95value_bytes_zero_for_any_value(DDCA_Any_Vcp_Value * pvalrec) {
96 bool_Bool result = pvalrec && pvalrec->value_type == DDCA_NON_TABLE_VCP_VALUE &&
97 pvalrec->val.c_nc.mh == 0 &&
98 pvalrec->val.c_nc.ml == 0 &&
99 pvalrec->val.c_nc.sh == 0 &&
100 pvalrec->val.c_nc.sl == 0;
101 return result;
102}
103
104
105/** Collects initial monitor checks to perform them on a single open of the
106 * monitor device, and to avoid repeating them.
107 *
108 * Performs the following tests:
109 * - Checks that DDC communication is working.
110 * - Checks if the monitor uses DDC Null Response to indicate invalid VCP code
111 * - Checks if the monitor uses mh=ml=sh=sl=0 to indicate invalid VCP code
112 *
113 * @param dh pointer to #Display_Handle for open monitor device
114 * @return **true** if DDC communication with the display succeeded, **false** otherwise.
115 *
116 * @remark
117 * Sets bits in dh->dref->flags
118 * * @remark
119 * It has been observed that DDC communication can fail even if slave address x37
120 * is valid on the I2C bus.
121 * @remark
122 * ADL does not notice that a reported display, e.g. Dell 1905FP, does not support
123 * DDC.
124 * @remark
125 * Monitors are supposed to set the unsupported feFFature bit in a valid DDC
126 * response, but a few monitors (mis)use the Null Response instead to indicate
127 * an unsupported feature. Others return with the unsupported feature bit not
128 * set, but all bytes (mh, ml, sh, sl) zero.
129 * @remark
130 * Note that the test here is not perfect, as a Null Response might
131 * in fact indicate a transient error, but that is rare.
132 * @remark
133 * Output level should have been set <= DDCA_OL_NORMAL prior to this call since
134 * verbose output is distracting.
135 */
136// static // non-static for backtrace
137bool_Bool
138ddc_initial_checks_by_dh(Display_Handle * dh) {
139 bool_Bool debug = false0;
140 TRACED_ASSERT(dh && dh->dref)do { if (dh && dh->dref) { ; } else { dbgtrc(DDCA_TRC_ALL
, 0, __func__, 140, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "dh && dh->dref", "ddc_displays.c", 140); syslog
(3, "Assertion failed: \"%s\" in file %s at line %d", "dh && dh->dref"
, "ddc_displays.c", 140); exit(1); } } while (0)
;
141 DBGTRC_STARTING(debug, TRACE_GROUP, "dh=%s", dh_repr(dh))dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 141
, "ddc_displays.c", "Starting ""dh=%s", dh_repr(dh))
;
142 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "communication flags: %s", interpret_dref_flags_t(dh->dref->flags))dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 142
, "ddc_displays.c", " ""communication flags: %s", interpret_dref_flags_t
(dh->dref->flags))
;
143
144 DDCA_Any_Vcp_Value * pvalrec;
145
146 if (!(dh->dref->flags & DREF_DDC_COMMUNICATION_CHECKED0x0080)) {
147 Public_Status_Code psc = 0;
148 Error_Info * ddc_excp = ddc_get_vcp_value(dh, 0x00, DDCA_NON_TABLE_VCP_VALUE, &pvalrec);
149 psc = (ddc_excp) ? ddc_excp->status_code : 0;
150 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "ddc_get_vcp_value() for feature 0x00 returned: %s, pvalrec=%p",dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 151
, "ddc_displays.c", " ""ddc_get_vcp_value() for feature 0x00 returned: %s, pvalrec=%p"
, errinfo_summary(ddc_excp), pvalrec)
151 errinfo_summary(ddc_excp), pvalrec)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 151
, "ddc_displays.c", " ""ddc_get_vcp_value() for feature 0x00 returned: %s, pvalrec=%p"
, errinfo_summary(ddc_excp), pvalrec)
;
152 TRACED_ASSERT( (psc == 0 && pvalrec) || (psc != 0 && !pvalrec) )do { if ((psc == 0 && pvalrec) || (psc != 0 &&
!pvalrec)) { ; } else { dbgtrc(DDCA_TRC_ALL, 0, __func__, 152
, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "(psc == 0 && pvalrec) || (psc != 0 && !pvalrec)"
, "ddc_displays.c", 152); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "(psc == 0 && pvalrec) || (psc != 0 && !pvalrec)"
, "ddc_displays.c", 152); exit(1); } } while (0)
;
153
154 if (psc == DDCRC_RETRIES(-(3000 +7 ) ) && debug)
155 DBGMSG(" Try errors: %s", errinfo_causes_string(ddc_excp))dbgtrc(DDCA_TRC_ALL, 0, __func__, 155, "ddc_displays.c", " Try errors: %s"
, errinfo_causes_string(ddc_excp))
;
156 if (ddc_excp)
157 errinfo_free(ddc_excp);
158
159 DDCA_IO_Mode io_mode = dh->dref->io_path.io_mode;
160 if (io_mode == DDCA_IO_USB) {
161 if (psc == 0 || psc == DDCRC_DETERMINED_UNSUPPORTED(-(3000 +12) )) {
162 dh->dref->flags |= DREF_DDC_COMMUNICATION_WORKING0x0040;
163 }
164 }
165 else {
166 TRACED_ASSERT(psc != DDCRC_DETERMINED_UNSUPPORTED)do { if (psc != (-(3000 +12) )) { ; } else { dbgtrc(DDCA_TRC_ALL
, 0, __func__, 166, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "psc != DDCRC_DETERMINED_UNSUPPORTED", "ddc_displays.c", 166
); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "psc != DDCRC_DETERMINED_UNSUPPORTED", "ddc_displays.c", 166
); exit(1); } } while (0)
; // only set at higher levels, unless USB
167
168 // What if returns -EIO? Dell AW3418D returns -EIO for unsupported features
169 // EXCEPT that it returns mh=ml=sh=sl=0 for feature 0x00 (2/2019)
170
171 if ( psc == DDCRC_NULL_RESPONSE(-(3000 +2 ) ) ||
172 psc == DDCRC_ALL_RESPONSES_NULL(-(3000 +11) ) ||
173 psc == 0 ||
174 psc == DDCRC_REPORTED_UNSUPPORTED(-(3000 +5 ) ) )
175 {
176 dh->dref->flags |= DREF_DDC_COMMUNICATION_WORKING0x0040;
177
178 if (psc == DDCRC_REPORTED_UNSUPPORTED(-(3000 +5 ) ))
179 dh->dref->flags |= DREF_DDC_USES_DDC_FLAG_FOR_UNSUPPORTED0x0200;
180
181 else if (psc == DDCRC_NULL_RESPONSE(-(3000 +2 ) ) || psc == DDCRC_ALL_RESPONSES_NULL(-(3000 +11) ))
182 dh->dref->flags |= DREF_DDC_USES_NULL_RESPONSE_FOR_UNSUPPORTED0x0800;
183
184 else {
185 TRACED_ASSERT( psc == 0)do { if (psc == 0) { ; } else { dbgtrc(DDCA_TRC_ALL, 0, __func__
, 185, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "psc == 0", "ddc_displays.c", 185); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "psc == 0", "ddc_displays.c", 185); exit(1); } } while (0)
;
186 TRACED_ASSERT(pvalrec && pvalrec->value_type == DDCA_NON_TABLE_VCP_VALUE )do { if (pvalrec && pvalrec->value_type == DDCA_NON_TABLE_VCP_VALUE
) { ; } else { dbgtrc(DDCA_TRC_ALL, 0, __func__, 186, "ddc_displays.c"
, "Assertion failed: \"%s\" in file %s at line %d", "pvalrec && pvalrec->value_type == DDCA_NON_TABLE_VCP_VALUE"
, "ddc_displays.c", 186); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "pvalrec && pvalrec->value_type == DDCA_NON_TABLE_VCP_VALUE"
, "ddc_displays.c", 186); exit(1); } } while (0)
;
187 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "pvalrec: value_type=%d, mh=%d, ml=%d, sh=%d, sl=%d",dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 189
, "ddc_displays.c", " ""pvalrec: value_type=%d, mh=%d, ml=%d, sh=%d, sl=%d"
, pvalrec->value_type, pvalrec->val.c_nc.mh, pvalrec->
val.c_nc.ml, pvalrec->val.c_nc.sh, pvalrec->val.c_nc.sl
)
188 pvalrec->value_type, pvalrec->val.c_nc.mh, pvalrec->val.c_nc.ml,dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 189
, "ddc_displays.c", " ""pvalrec: value_type=%d, mh=%d, ml=%d, sh=%d, sl=%d"
, pvalrec->value_type, pvalrec->val.c_nc.mh, pvalrec->
val.c_nc.ml, pvalrec->val.c_nc.sh, pvalrec->val.c_nc.sl
)
189 pvalrec->val.c_nc.sh, pvalrec->val.c_nc.sl)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 189
, "ddc_displays.c", " ""pvalrec: value_type=%d, mh=%d, ml=%d, sh=%d, sl=%d"
, pvalrec->value_type, pvalrec->val.c_nc.mh, pvalrec->
val.c_nc.ml, pvalrec->val.c_nc.sh, pvalrec->val.c_nc.sl
)
;
190
191 if (value_bytes_zero_for_any_value(pvalrec))
192 {
193 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "Setting DREF_DDC_USES_MH_ML_SH_SL_ZERO_FOR_UNSUPPORTED")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 193
, "ddc_displays.c", " ""Setting DREF_DDC_USES_MH_ML_SH_SL_ZERO_FOR_UNSUPPORTED"
)
;
194 dh->dref->flags |= DREF_DDC_USES_MH_ML_SH_SL_ZERO_FOR_UNSUPPORTED0x0400;
195 }
196 else {
197 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "Setting DREF_DDC_DOES_NOT_INDICATE_UNSUPPORTED")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 197
, "ddc_displays.c", " ""Setting DREF_DDC_DOES_NOT_INDICATE_UNSUPPORTED"
)
;
198 dh->dref->flags |= DREF_DDC_DOES_NOT_INDICATE_UNSUPPORTED0x0100;
199 }
200 }
201 } // end, communication working
202
203 else { // communication failed
204 if ( i2c_force_bus /* && psc == DDCRC_RETRIES */) {
205 DBGTRC_NOPREFIX(debug || true , TRACE_GROUP, "dh=%s, Forcing DDC communication success.",dbgtrc( (debug || 1) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__
, 206, "ddc_displays.c", " ""dh=%s, Forcing DDC communication success."
, dh_repr(dh))
206 dh_repr(dh) )dbgtrc( (debug || 1) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__
, 206, "ddc_displays.c", " ""dh=%s, Forcing DDC communication success."
, dh_repr(dh))
;
207 psc = 0;
Value stored to 'psc' is never read
208 dh->dref->flags |= DREF_DDC_COMMUNICATION_WORKING0x0040;
209 // dh->dref->flags |= DREF_DDC_DOES_NOT_INDICATE_UNSUPPORTED;
210 dh->dref->flags |= DREF_DDC_USES_DDC_FLAG_FOR_UNSUPPORTED0x0200; // good_enuf_for_test
211 if ( vcp_version_eq(dh->dref->vcp_version_xdf, DDCA_VSPEC_UNQUERIED)) // may have been forced by option --mccs
212 dh->dref->vcp_version_xdf = DDCA_VSPEC_V22; // good enuf for test
213 }
214 }
215 } // end, io_mode == DDC_IO_I2C
216 dh->dref->flags |= DREF_DDC_COMMUNICATION_CHECKED0x0080;
217
218 if ( dh->dref->flags & DREF_DDC_COMMUNICATION_WORKING0x0040 ) {
219 // Would prefer to defer checking version until actually needed to avoid additional DDC io
220 // during monitor detection. Unfortunately, this would introduce ddc_open_display(), with
221 // its possible error states, into other functions, e.g. ddca_get_feature_list_by_dref()
222 if ( vcp_version_eq(dh->dref->vcp_version_xdf, DDCA_VSPEC_UNQUERIED)) { // may have been forced by option --mccs
223 set_vcp_version_xdf_by_dh(dh);
224 }
225 }
226
227 } // end, !DREF_DDC_COMMUNICATION_CHECKED
228
229 DBGTRC_RET_BOOL(debug, TRACE_GROUP, dh->dref->flags & DREF_DDC_COMMUNICATION_WORKING, "dh=%s", dh_repr(dh))dbgtrc_returning_expression( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP
), 0, __func__, 229, "ddc_displays.c", ( (dh->dref->flags
& 0x0040) ? "true" : "false" ), "dh=%s", dh_repr(dh))
;
230 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "communication flags: %s", interpret_dref_flags_t(dh->dref->flags))dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 230
, "ddc_displays.c", " ""communication flags: %s", interpret_dref_flags_t
(dh->dref->flags))
;
231 return dh->dref->flags & DREF_DDC_COMMUNICATION_WORKING0x0040;
232}
233
234
235/** Given a #Display_Ref, opens the monitor device and calls #initial_checks_by_dh()
236 * to perform initial monitor checks.
237 *
238 * @param dref pointer to #Display_Ref for monitor
239 * @return **true** if DDC communication with the display succeeded, **false** otherwise.
240 */
241bool_Bool
242ddc_initial_checks_by_dref(Display_Ref * dref) {
243 bool_Bool debug = false0;
244 DBGTRC_STARTING(debug, TRACE_GROUP, "dref=%s", dref_repr_t(dref))dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 244
, "ddc_displays.c", "Starting ""dref=%s", dref_repr_t(dref))
;
245 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "dref->flags: %s", interpret_dref_flags_t(dref->flags))dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 245
, "ddc_displays.c", " ""dref->flags: %s", interpret_dref_flags_t
(dref->flags))
;
246
247 bool_Bool result = false0;
248 Display_Handle * dh = NULL((void*)0);
249 Public_Status_Code psc = 0;
250
251 psc = ddc_open_display(dref, CALLOPT_ERR_MSG0x80, &dh);
252 if (psc == 0) {
253 result = ddc_initial_checks_by_dh(dh);
254 ddc_close_display(dh);
255 }
256 // else { // why else?
257 dref->flags |= DREF_DDC_COMMUNICATION_CHECKED0x0080;
258 // }
259 if (psc == -EBUSY16)
260 dref->flags |= DREF_DDC_BUSY0x8000;
261
262 DBGTRC_DONE(debug, TRACE_GROUP, "Returning %s. dref = %s", sbool(result), dref_repr_t(dref) )dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 262
, "ddc_displays.c", "Done ""Returning %s. dref = %s", sbool
(result), dref_repr_t(dref))
;
263 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "communication flags: %s", interpret_dref_flags_t(dref->flags))dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 263
, "ddc_displays.c", " ""communication flags: %s", interpret_dref_flags_t
(dref->flags))
;
264 return result;
265}
266
267
268/** Performs initial checks in a thread
269 *
270 * @param data display reference
271 */
272void *
273threaded_initial_checks_by_dref(gpointer data) {
274 bool_Bool debug = false0;
275
276 Display_Ref * dref = data;
277 TRACED_ASSERT(memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0 )do { if (memcmp(dref->marker, "DREF", 4) == 0) { ; } else {
dbgtrc(DDCA_TRC_ALL, 0, __func__, 277, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 277); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 277); exit(1); } } while (0)
;
278 DBGTRC_STARTING(debug, TRACE_GROUP, "dref = %s", dref_repr_t(dref) )dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 278
, "ddc_displays.c", "Starting ""dref = %s", dref_repr_t(dref
))
;
279
280 ddc_initial_checks_by_dref(dref);
281 // g_thread_exit(NULL);
282 DBGTRC_DONE(debug, TRACE_GROUP, "Returning NULL. dref = %s,", dref_repr_t(dref) )dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 282
, "ddc_displays.c", "Done ""Returning NULL. dref = %s,",
dref_repr_t(dref))
;
283 return NULL((void*)0);
284}
285
286
287/** Spawns threads to perform initial checks and waits for them all to complete.
288 *
289 * @param all_displays #GPtrArray of pointers to #Display_Ref
290 */
291void ddc_async_scan(GPtrArray * all_displays) {
292 bool_Bool debug = false0;
293 DBGTRC_STARTING(debug, TRACE_GROUP, "all_displays=%p, display_count=%d", all_displays, all_displays->len)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 293
, "ddc_displays.c", "Starting ""all_displays=%p, display_count=%d"
, all_displays, all_displays->len)
;
294
295 GPtrArray * threads = g_ptr_array_new();
296 for (int ndx = 0; ndx < all_displays->len; ndx++) {
297 Display_Ref * dref = g_ptr_array_index(all_displays, ndx)((all_displays)->pdata)[ndx];
298 TRACED_ASSERT( memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0 )do { if (memcmp(dref->marker, "DREF", 4) == 0) { ; } else {
dbgtrc(DDCA_TRC_ALL, 0, __func__, 298, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 298); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 298); exit(1); } } while (0)
;
299
300 GThread * th =
301 g_thread_new(
302 dref_repr_t(dref), // thread name
303 threaded_initial_checks_by_dref,
304 dref); // pass pointer to display ref as data
305 g_ptr_array_add(threads, th);
306 }
307 DBGMSF(debug, "Started %d threads", threads->len)do { if (debug) dbgtrc(DDCA_TRC_ALL, 0, __func__, 307, "ddc_displays.c"
, "Started %d threads", threads->len); } while(0)
;
308 for (int ndx = 0; ndx < threads->len; ndx++) {
309 GThread * thread = g_ptr_array_index(threads, ndx)((threads)->pdata)[ndx];
310 g_thread_join(thread); // implicitly unrefs the GThread
311 }
312 DBGMSF(debug, "Threads joined")do { if (debug) dbgtrc(DDCA_TRC_ALL, 0, __func__, 312, "ddc_displays.c"
, "Threads joined"); } while(0)
;
313 g_ptr_array_free(threads, true1);
314
315 DBGTRC_DONE(debug, TRACE_GROUP, "")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 315
, "ddc_displays.c", "Done """)
;
316}
317
318
319/** Loops through a list of display refs, performing initial checks on each.
320 *
321 * @param all_displays #GPtrArray of pointers to #Display_Ref
322 */
323void
324ddc_non_async_scan(GPtrArray * all_displays) {
325 bool_Bool debug = false0;
326 DBGTRC_STARTING(debug, TRACE_GROUP, "checking %d displays", all_displays->len)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 326
, "ddc_displays.c", "Starting ""checking %d displays", all_displays
->len)
;
327
328 for (int ndx = 0; ndx < all_displays->len; ndx++) {
329 Display_Ref * dref = g_ptr_array_index(all_displays, ndx)((all_displays)->pdata)[ndx];
330 TRACED_ASSERT( memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0 )do { if (memcmp(dref->marker, "DREF", 4) == 0) { ; } else {
dbgtrc(DDCA_TRC_ALL, 0, __func__, 330, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 330); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 330); exit(1); } } while (0)
;
331 ddc_initial_checks_by_dref(dref);
332 }
333 DBGTRC_DONE(debug, TRACE_GROUP, "")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 333
, "ddc_displays.c", "Done """)
;
334}
335
336
337//
338// Functions to get display information
339//
340
341/** Gets a list of all detected displays, whether they support DDC or not.
342 *
343 * Detection must already have occurred.
344 *
345 * @return **GPtrArray of #Display_Ref instances
346 */
347GPtrArray *
348ddc_get_all_displays() {
349 // ddc_ensure_displays_detected();
350 TRACED_ASSERT(all_displays)do { if (all_displays) { ; } else { dbgtrc(DDCA_TRC_ALL, 0, __func__
, 350, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "all_displays", "ddc_displays.c", 350); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "all_displays", "ddc_displays.c", 350); exit(1); } } while (
0)
;
351 return all_displays;
352}
353
354
355/** Gets a list of all detected displays, optionally excluding those
356 * that are invalid.
357 *
358 * @return **GPtrArray of #Display_Ref instances
359 */
360GPtrArray *
361ddc_get_filtered_displays(bool_Bool include_invalid_displays) {
362 bool_Bool debug = false0;
363 DBGTRC_STARTING(debug, TRACE_GROUP, "include_invalid_displays=%s", sbool(include_invalid_displays))dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 363
, "ddc_displays.c", "Starting ""include_invalid_displays=%s"
, sbool(include_invalid_displays))
;
364 TRACED_ASSERT(all_displays)do { if (all_displays) { ; } else { dbgtrc(DDCA_TRC_ALL, 0, __func__
, 364, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "all_displays", "ddc_displays.c", 364); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "all_displays", "ddc_displays.c", 364); exit(1); } } while (
0)
;
365 GPtrArray * result = g_ptr_array_sized_new(all_displays->len);
366 for (int ndx = 0; ndx < all_displays->len; ndx++) {
367 Display_Ref * cur = g_ptr_array_index(all_displays, ndx)((all_displays)->pdata)[ndx];
368 if (include_invalid_displays || cur->dispno > 0) {
369 g_ptr_array_add(result, cur);
370 }
371 }
372 DBGTRC_DONE(debug, TRACE_GROUP, "Returning array of size %d", result->len)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 372
, "ddc_displays.c", "Done ""Returning array of size %d",
result->len)
;
373 if (debug || IS_TRACING()is_tracing(TRACE_GROUP, "ddc_displays.c", __func__)) {
374 ddc_dbgrpt_drefs("Display_Refs:", result, 2);
375 }
376 return result;
377}
378
379
380/** Returns the number of detected displays.
381 *
382 * @param include_invalid_displays
383 * @return number of displays, 0 if display detection has not yet occurred.
384 */
385int
386ddc_get_display_count(bool_Bool include_invalid_displays) {
387 int display_ct = -1;
388 if (all_displays) {
389 display_ct = 0;
390 for (int ndx=0; ndx<all_displays->len; ndx++) {
391 Display_Ref * dref = g_ptr_array_index(all_displays, ndx)((all_displays)->pdata)[ndx];
392 TRACED_ASSERT(memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0)do { if (memcmp(dref->marker, "DREF", 4) == 0) { ; } else {
dbgtrc(DDCA_TRC_ALL, 0, __func__, 392, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 392); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 392); exit(1); } } while (0)
;
393 if (dref->dispno > 0 || include_invalid_displays) {
394 display_ct++;
395 }
396 }
397 }
398 return display_ct;
399}
400
401
402/** Returns list of all open() errors encountered during display detection.
403 *
404 * @return **GPtrArray of #Bus_Open_Error.
405 */
406GPtrArray *
407ddc_get_bus_open_errors() {
408 return display_open_errors;
409}
410
411
412
413
414//
415// Phantom displays
416//
417
418static bool_Bool
419edid_ids_match(Parsed_Edid * edid1, Parsed_Edid * edid2) {
420 bool_Bool result = false0;
421 result = streq(edid1->mfg_id, edid2->mfg_id) &&
422 streq(edid1->model_name, edid2->model_name) &&
423 edid1->product_code == edid2->product_code &&
424 streq(edid1->serial_ascii, edid2->serial_ascii) &&
425 edid1->serial_binary == edid2->serial_binary;
426 return result;
427}
428
429
430/** Check if an invalid #Display_Reference can be regarded as a phantom
431 * of a given valid #Display_Reference.
432 *
433 * @param invalid_dref
434 * @param valid_dref
435 * @return true/false
436 *
437 * - Both are /dev/i2c devices
438 * - The EDID id fields must match
439 * - For the invalid #Display_Reference:
440 * - attribute status must exist and equal "disconnected"
441 * - attribute enabled must exist and equal "disabled"
442 * - attribute edid must not exist
443 */
444bool_Bool
445is_phantom_display(Display_Ref* invalid_dref, Display_Ref * valid_dref) {
446 bool_Bool debug = false0;
447 char * invalid_repr = strdup(dref_repr_t(invalid_dref));
448 char * valid_repr = strdup(dref_repr_t(valid_dref));
449 DBGTRC_STARTING(debug, TRACE_GROUP, "invalid_dref=%s, valid_dref=%s",dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 450
, "ddc_displays.c", "Starting ""invalid_dref=%s, valid_dref=%s"
, invalid_repr, valid_repr)
450 invalid_repr, valid_repr)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 450
, "ddc_displays.c", "Starting ""invalid_dref=%s, valid_dref=%s"
, invalid_repr, valid_repr)
;
451 free(invalid_repr);
452 free(valid_repr);
453
454 bool_Bool result = false0;
455 // User report has shown that 128 byte EDIDs can differ for the valid and
456 // invalid display. Specifically, byte 24 was seen to differ, with one
457 // having RGB 4:4:4 and the other RGB 4:4:4 + YCrCb 4:2:2!. So instead of
458 // simply byte comparing the 2 EDIDs, check the identifiers.
459 if (edid_ids_match(invalid_dref->pedid, valid_dref->pedid)) {
460 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "EDIDs match")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 460
, "ddc_displays.c", " ""EDIDs match")
;
461 if (invalid_dref->io_path.io_mode == DDCA_IO_I2C &&
462 valid_dref->io_path.io_mode == DDCA_IO_I2C)
463 {
464 int invalid_busno = invalid_dref->io_path.path.i2c_busno;
465 // int valid_busno = valid_dref->io_path.path.i2c_busno;
466 char buf0[40];
467 snprintf(buf0, 40, "/sys/bus/i2c/devices/i2c-%d", invalid_busno);
468 bool_Bool old_silent = set_rpt_sysfs_attr_silent(!(debug|| IS_TRACING()is_tracing(TRACE_GROUP, "ddc_displays.c", __func__)));
469 char * invalid_rpath = NULL((void*)0);
470 bool_Bool ok = RPT_ATTR_REALPATH(0, &invalid_rpath, buf0, "device")rpt_attr_realpath(0, &invalid_rpath, buf0, "device", ((void
*)0))
;
471 if (ok) {
472 result = true1;
473 char * attr_value = NULL((void*)0);
474 ok = RPT_ATTR_TEXT(0, &attr_value, invalid_rpath, "status")rpt_attr_text(0, &attr_value, invalid_rpath, "status", ((
void*)0))
;
475 if (!ok || !streq(attr_value, "disconnected"))
476 result = false0;
477 ok = RPT_ATTR_TEXT(0, &attr_value, invalid_rpath, "enabled")rpt_attr_text(0, &attr_value, invalid_rpath, "enabled", (
(void*)0))
;
478 if (!ok || !streq(attr_value, "disabled"))
479 result = false0;
480 GByteArray * edid;
481 ok = RPT_ATTR_EDID(0, &edid, invalid_rpath, "edid")rpt_attr_edid(0, &edid, invalid_rpath, "edid", ((void*)0)
)
; // is "edid" needed
482 if (ok) {
483 result = false0;
484 g_byte_array_free(edid, true1);
485 }
486 }
487 set_rpt_sysfs_attr_silent(old_silent);
488 }
489 }
490 DBGTRC_DONE(debug, TRACE_GROUP, "Returning: %s", sbool(result) )dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 490
, "ddc_displays.c", "Done ""Returning: %s", sbool(result
))
;
491 return result;
492}
493
494
495/** Mark phantom displays.
496 *
497 * Solit the #Display_Ref's in a GPtrArray into those that have
498 * already been determined to be valid (dispno > 0) and those
499 * that are invalid (dispno < 0).
500 *
501 * For each invalid array, check to see if it is a phantom display
502 * corresponding to one of the valid displays. If so, set its dispno
503 * to DISPNO_INVALID and save a pointer to the valid display ref.
504 *
505 * @param all_displays array of pointers to #Display_Ref
506 *
507 * @remark
508 * This handles the case where DDC communication works for one
509 * /dev/i2c bus but not another. It does not handle the case where
510 * communication succeeds on both /dev/i2c devices.
511 */
512void
513filter_phantom_displays(GPtrArray * all_displays) {
514 bool_Bool debug = false0;
515 DBGTRC_STARTING(debug, TRACE_GROUP, "all_displays->len = %d", all_displays->len)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 515
, "ddc_displays.c", "Starting ""all_displays->len = %d", all_displays
->len)
;
516 GPtrArray* valid_displays = g_ptr_array_sized_new(all_displays->len);
517 GPtrArray* invalid_displays = g_ptr_array_sized_new(all_displays->len);
518 for (int ndx = 0; ndx < all_displays->len; ndx++) {
519 Display_Ref * dref = g_ptr_array_index(all_displays, ndx)((all_displays)->pdata)[ndx];
520 TRACED_ASSERT( memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0 )do { if (memcmp(dref->marker, "DREF", 4) == 0) { ; } else {
dbgtrc(DDCA_TRC_ALL, 0, __func__, 520, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 520); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 520); exit(1); } } while (0)
;
521 if (dref->dispno < 0) // DISPNO_INVALID, DISPNO_PHANTOM, DISPNO_REMOVED
522 g_ptr_array_add(invalid_displays, dref);
523 else
524 g_ptr_array_add(valid_displays, dref);
525 }
526 DBGTRC_NOPREFIX(debug, TRACE_GROUP, "%d valid displays, %d invalid displays",dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 527
, "ddc_displays.c", " ""%d valid displays, %d invalid displays"
, valid_displays->len, invalid_displays->len)
527 valid_displays->len, invalid_displays->len)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 527
, "ddc_displays.c", " ""%d valid displays, %d invalid displays"
, valid_displays->len, invalid_displays->len)
;
528 if (invalid_displays->len > 0 || valid_displays->len == 0 ) {
529 for (int invalid_ndx = 0; invalid_ndx < invalid_displays->len; invalid_ndx++) {
530 Display_Ref * invalid_ref = g_ptr_array_index(invalid_displays, invalid_ndx)((invalid_displays)->pdata)[invalid_ndx];
531 for (int valid_ndx = 0; valid_ndx < valid_displays->len; valid_ndx++) {
532 Display_Ref * valid_ref = g_ptr_array_index(valid_displays, valid_ndx)((valid_displays)->pdata)[valid_ndx];
533 if (is_phantom_display(invalid_ref, valid_ref)) {
534 invalid_ref->dispno = DISPNO_PHANTOM-2; // -2
535 invalid_ref->actual_display = valid_ref;
536 }
537 }
538 }
539 }
540 g_ptr_array_free(invalid_displays, true1);
541 g_ptr_array_free(valid_displays, true1);
542 DBGTRC_DONE(debug, TRACE_GROUP, "")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 542
, "ddc_displays.c", "Done """)
;
543}
544
545
546//
547// Display Detection
548//
549
550/** Emits a debug report of a list of #Bus_Open_Error.
551 *
552 * @param open_errors array of #Bus_Open_Error
553 * @param depth logical indentation depth
554 */
555void dbgrpt_bus_open_errors(GPtrArray * open_errors, int depth) {
556 int d1 = depth+1;
557 if (!open_errors || open_errors->len == 0) {
558 rpt_vstring(depth, "Bus open errors: None");
559 }
560 else {
561 rpt_vstring(depth, "Bus open errors:");
562 for (int ndx = 0; ndx < open_errors->len; ndx++) {
563 Bus_Open_Error * cur = g_ptr_array_index(open_errors, ndx)((open_errors)->pdata)[ndx];
564 assert(cur->io_mode != DDCA_IO_ADL)((void) sizeof ((cur->io_mode != DDCA_IO_ADL) ? 1 : 0), __extension__
({ if (cur->io_mode != DDCA_IO_ADL) ; else __assert_fail (
"cur->io_mode != DDCA_IO_ADL", "ddc_displays.c", 564, __extension__
__PRETTY_FUNCTION__); }))
;
565 rpt_vstring(d1, "%s bus: %-2d, error: %d",
566 (cur->io_mode == DDCA_IO_I2C) ? "I2C" : "hiddev",
567 cur->devno, cur->error);
568 }
569 }
570}
571
572
573/** Detects all connected displays by querying the I2C and USB subsystems.
574 *
575 * @param open_errors_loc where to return address of #GPtrArray of #Bus_Open_Error
576 * @return array of #Display_Ref
577 */
578// static
579GPtrArray *
580ddc_detect_all_displays(GPtrArray ** i2c_open_errors_loc) {
581 bool_Bool debug = false0;
582 DBGTRC_STARTING(debug, TRACE_GROUP, "")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 582
, "ddc_displays.c", "Starting """)
;
583 dispno_max = 0;
584 GPtrArray * bus_open_errors = g_ptr_array_new();
585 GPtrArray * display_list = g_ptr_array_new();
586
587 int busct = i2c_detect_buses();
588 DBGMSF(debug, "i2c_detect_buses() returned: %d", busct)do { if (debug) dbgtrc(DDCA_TRC_ALL, 0, __func__, 588, "ddc_displays.c"
, "i2c_detect_buses() returned: %d", busct); } while(0)
;
589 uint busndx = 0;
590 for (busndx=0; busndx < busct; busndx++) {
591 I2C_Bus_Info * businfo = i2c_get_bus_info_by_index(busndx);
592 if ( (businfo->flags & I2C_BUS_ADDR_0X500x20) && businfo->edid ) {
593 Display_Ref * dref = create_bus_display_ref(businfo->busno);
594 dref->dispno = DISPNO_INVALID-1; // -1, guilty until proven innocent
595 dref->pedid = businfo->edid; // needed?
596 dref->mmid = monitor_model_key_new(
597 dref->pedid->mfg_id,
598 dref->pedid->model_name,
599 dref->pedid->product_code);
600
601 // drec->detail.bus_detail = businfo;
602 dref->detail = businfo;
603 dref->flags |= DREF_DDC_IS_MONITOR_CHECKED0x0010;
604 dref->flags |= DREF_DDC_IS_MONITOR0x0008;
605 g_ptr_array_add(display_list, dref);
606 }
607 else if ( !(businfo->flags & I2C_BUS_ACCESSIBLE0x40) ) {
608 Bus_Open_Error * boe = calloc(1, sizeof(Bus_Open_Error));
609 boe->io_mode = DDCA_IO_I2C;
610 boe->devno = businfo->busno;
611 boe->error = businfo->open_errno;
612 g_ptr_array_add(bus_open_errors, boe);
613 }
614 }
615
616#ifdef USE_USB1
617 if (detect_usb_displays) {
618 GPtrArray * usb_monitors = get_usb_monitor_list(); // array of USB_Monitor_Info
619 // DBGMSF(debug, "Found %d USB displays", usb_monitors->len);
620 for (int ndx=0; ndx<usb_monitors->len; ndx++) {
621 Usb_Monitor_Info * curmon = g_ptr_array_index(usb_monitors,ndx)((usb_monitors)->pdata)[ndx];
622 TRACED_ASSERT(memcmp(curmon->marker, USB_MONITOR_INFO_MARKER, 4) == 0)do { if (memcmp(curmon->marker, "UMNF", 4) == 0) { ; } else
{ dbgtrc(DDCA_TRC_ALL, 0, __func__, 622, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(curmon->marker, USB_MONITOR_INFO_MARKER, 4) == 0"
, "ddc_displays.c", 622); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(curmon->marker, USB_MONITOR_INFO_MARKER, 4) == 0"
, "ddc_displays.c", 622); exit(1); } } while (0)
;
623 Display_Ref * dref = create_usb_display_ref(
624 curmon->hiddev_devinfo->busnum,
625 curmon->hiddev_devinfo->devnum,
626 curmon->hiddev_device_name);
627 dref->dispno = DISPNO_INVALID-1; // -1
628 dref->pedid = curmon->edid;
629 if (dref->pedid)
630 dref->mmid = monitor_model_key_new(
631 dref->pedid->mfg_id,
632 dref->pedid->model_name,
633 dref->pedid->product_code);
634 else
635 dref->mmid = monitor_model_key_new("UNK", "UNK", 0);
636 // drec->detail.usb_detail = curmon;
637 dref->detail = curmon;
638 dref->flags |= DREF_DDC_IS_MONITOR_CHECKED0x0010;
639 dref->flags |= DREF_DDC_IS_MONITOR0x0008;
640 g_ptr_array_add(display_list, dref);
641 }
642
643 GPtrArray * usb_open_errors = get_usb_open_errors();
644 if (usb_open_errors && usb_open_errors->len > 0) {
645 for (int ndx = 0; ndx < usb_open_errors->len; ndx++) {
646 Bus_Open_Error * usb_boe = (Bus_Open_Error *) g_ptr_array_index(usb_open_errors, ndx)((usb_open_errors)->pdata)[ndx];
647 Bus_Open_Error * boe_copy = calloc(1, sizeof(Bus_Open_Error));
648 boe_copy->io_mode = DDCA_IO_USB;
649 boe_copy->devno = usb_boe->devno;
650 boe_copy->error = usb_boe->error;
651 g_ptr_array_add(bus_open_errors, boe_copy);
652 }
653 }
654 }
655#endif
656
657 // verbose output is distracting within scans
658 // saved and reset here so that async threads are not adjusting output level
659 DDCA_Output_Level olev = get_output_level();
660 if (olev == DDCA_OL_VERBOSE)
661 set_output_level(DDCA_OL_NORMAL);
662
663 DBGMSF(debug, "display_list->len=%d, async_threshold=%d",do { if (debug) dbgtrc(DDCA_TRC_ALL, 0, __func__, 664, "ddc_displays.c"
, "display_list->len=%d, async_threshold=%d", display_list
->len, async_threshold); } while(0)
664 display_list->len, async_threshold)do { if (debug) dbgtrc(DDCA_TRC_ALL, 0, __func__, 664, "ddc_displays.c"
, "display_list->len=%d, async_threshold=%d", display_list
->len, async_threshold); } while(0)
;
665 if (display_list->len >= async_threshold)
666 ddc_async_scan(display_list);
667 else
668 ddc_non_async_scan(display_list);
669
670 if (olev == DDCA_OL_VERBOSE)
671 set_output_level(olev);
672
673 // assign display numbers
674 for (int ndx = 0; ndx < display_list->len; ndx++) {
675 Display_Ref * dref = g_ptr_array_index(display_list, ndx)((display_list)->pdata)[ndx];
676 TRACED_ASSERT( memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0 )do { if (memcmp(dref->marker, "DREF", 4) == 0) { ; } else {
dbgtrc(DDCA_TRC_ALL, 0, __func__, 676, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 676); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "memcmp(dref->marker, DISPLAY_REF_MARKER, 4) == 0", "ddc_displays.c"
, 676); exit(1); } } while (0)
;
677 if (dref->flags & DREF_DDC_COMMUNICATION_WORKING0x0040)
678 dref->dispno = ++dispno_max;
679 else if (dref->flags & DREF_DDC_BUSY0x8000)
680 dref->dispno = DISPNO_BUSY-4;
681 else {
682 dref->dispno = DISPNO_INVALID-1; // -1;
683 }
684 }
685
686 filter_phantom_displays(display_list);
687
688 if (bus_open_errors->len > 0) {
689 *i2c_open_errors_loc = bus_open_errors;
690 }
691 else {
692 g_ptr_array_free(bus_open_errors, false0);
693 *i2c_open_errors_loc = NULL((void*)0);
694 }
695
696 if (debug) {
697 DBGMSG("Displays detected:")dbgtrc(DDCA_TRC_ALL, 0, __func__, 697, "ddc_displays.c", "Displays detected:"
)
;
698 ddc_dbgrpt_drefs("display_list:", display_list, 1);
699 dbgrpt_bus_open_errors(*i2c_open_errors_loc, 1);
700 }
701 DBGTRC_DONE(debug, TRACE_GROUP, "Returning %p, Detected %d valid displays",dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 702
, "ddc_displays.c", "Done ""Returning %p, Detected %d valid displays"
, display_list, dispno_max)
702 display_list, dispno_max)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 702
, "ddc_displays.c", "Done ""Returning %p, Detected %d valid displays"
, display_list, dispno_max)
;
703 return display_list;
704}
705
706
707/** Initializes the master display list in global variable #all_displays and
708 * records open errors in global variable #display_open_errors.
709 *
710 * Does nothing if the list has already been initialized.
711 */
712void
713ddc_ensure_displays_detected() {
714 bool_Bool debug = false0;
715 DBGTRC_STARTING(debug, TRACE_GROUP, "")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 715
, "ddc_displays.c", "Starting """)
;
716 if (!all_displays) {
717 // i2c_detect_buses(); // called in ddc_detect_all_displays()
718 all_displays = ddc_detect_all_displays(&display_open_errors);
719 }
720 DBGTRC_DONE(debug, TRACE_GROUP,dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 722
, "ddc_displays.c", "Done ""all_displays=%p, all_displays has %d displays"
, all_displays, all_displays->len)
721 "all_displays=%p, all_displays has %d displays",dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 722
, "ddc_displays.c", "Done ""all_displays=%p, all_displays has %d displays"
, all_displays, all_displays->len)
722 all_displays, all_displays->len)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 722
, "ddc_displays.c", "Done ""all_displays=%p, all_displays has %d displays"
, all_displays, all_displays->len)
;
723}
724
725
726/** Discards all detected displays.
727 *
728 * - All open displays are closed
729 * - The list of open displays in #all_displays is discarded
730 * - The list of errors in #display_open_errors is discarded
731 * - The list of detected I2C buses is discarded
732 * - The USB monitor list is discarded
733 */
734void
735ddc_discard_detected_displays() {
736 bool_Bool debug = false0;
737 DBGTRC_STARTING(debug, TRACE_GROUP, "")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 737
, "ddc_displays.c", "Starting """)
;
738 // grab locks to prevent any opens?
739 ddc_close_all_displays();
740#ifdef USE_USB1
741 discard_usb_monitor_list();
742#endif
743 if (all_displays) {
744 for (int ndx = 0; ndx < all_displays->len; ndx++) {
745 Display_Ref * dref = g_ptr_array_index(all_displays, ndx)((all_displays)->pdata)[ndx];
746 dref->flags |= DREF_TRANSIENT0x0004; // hack to allow all Display References to be freed
747#ifndef NDEBUG
748 DDCA_Status ddcrc = free_display_ref(dref);
749 TRACED_ASSERT(ddcrc==0)do { if (ddcrc==0) { ; } else { dbgtrc(DDCA_TRC_ALL, 0, __func__
, 749, "ddc_displays.c", "Assertion failed: \"%s\" in file %s at line %d"
, "ddcrc==0", "ddc_displays.c", 749); syslog(3, "Assertion failed: \"%s\" in file %s at line %d"
, "ddcrc==0", "ddc_displays.c", 749); exit(1); } } while (0)
;
750#endif
751 }
752 g_ptr_array_free(all_displays, true1);
753 all_displays = NULL((void*)0);
754 if (display_open_errors) {
755 g_ptr_array_free(display_open_errors, true1);
756 display_open_errors = NULL((void*)0);
757 }
758 }
759 free_sys_drm_connectors();
760 i2c_discard_buses();
761 DBGTRC_DONE(debug, TRACE_GROUP, "")dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 761
, "ddc_displays.c", "Done """)
;
762}
763
764
765void
766ddc_redetect_displays() {
767 bool_Bool debug = false0;
768 DBGTRC_STARTING(debug, TRACE_GROUP, "all_displays=%p", all_displays)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 768
, "ddc_displays.c", "Starting ""all_displays=%p", all_displays
)
;
769 ddc_discard_detected_displays();
770 // i2c_detect_buses(); // called in ddc_detect_all_displays()
771 all_displays = ddc_detect_all_displays(&display_open_errors);
772 if (debug) {
773 ddc_dbgrpt_drefs("all_displays:", all_displays, 1);
774 // dbgrpt_valid_display_refs(1);
775 }
776 DBGTRC_DONE(debug, TRACE_GROUP, "all_displays=%p, all_displays->len = %d",dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 777
, "ddc_displays.c", "Done ""all_displays=%p, all_displays->len = %d"
, all_displays, all_displays->len)
777 all_displays, all_displays->len)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 777
, "ddc_displays.c", "Done ""all_displays=%p, all_displays->len = %d"
, all_displays, all_displays->len)
;
778}
779
780
781/** Checks that a #Display_Ref is in array **all_displays**
782 * of all valid #Display_Ref values.
783 *
784 * @param dref #Display_Ref
785 * @return true/false
786 */
787bool_Bool
788ddc_is_valid_display_ref(Display_Ref * dref) {
789 bool_Bool debug = false0;
790 DBGTRC_STARTING(debug, TRACE_GROUP, "dref=%p -> %s", dref, dref_repr_t(dref))dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 790
, "ddc_displays.c", "Starting ""dref=%p -> %s", dref, dref_repr_t
(dref))
;
791 bool_Bool result = false0;
792 if (all_displays) {
793 for (int ndx = 0; ndx < all_displays->len; ndx++) {
794 Display_Ref* cur = g_ptr_array_index(all_displays, ndx)((all_displays)->pdata)[ndx];
795 DBGMSF(debug, "Checking vs valid dref %p", cur)do { if (debug) dbgtrc(DDCA_TRC_ALL, 0, __func__, 795, "ddc_displays.c"
, "Checking vs valid dref %p", cur); } while(0)
;
796
797 if (cur == dref) {
798 // if (cur->dispno > 0) // why?
799 result = true1;
800 break;
801 }
802 }
803 }
804 DBGTRC_DONE(debug, TRACE_GROUP, "Returning %s. dref=%p, dispno=%d", sbool(result), dref, dref->dispno)dbgtrc( (debug) ? DDCA_TRC_ALL : (TRACE_GROUP), 0, __func__, 804
, "ddc_displays.c", "Done ""Returning %s. dref=%p, dispno=%d"
, sbool(result), dref, dref->dispno)
;
805 return result;
806}
807
808
809/** Indicates whether displays have already been detected
810 *
811 * @return true/false
812 */
813bool_Bool
814ddc_displays_already_detected()
815{
816 return all_displays;
817}
818
819
820/** Controls whether USB displays are to be detected.
821 *
822 * Must be called before any function that triggers display detection.
823 *
824 * @param onoff true if USB displays are to be detected, false if not
825 * @retval DDCRC_OK normal
826 * @retval DDCRC_INVALID_OPERATION function called after displays have been detected
827 * @retval DDCRC_UNIMPLEMENTED ddcutil was not built with USB support
828 *
829 * @remark
830 * If this function is not called, the default (if built with USB support) is on
831 */
832DDCA_Status
833ddc_enable_usb_display_detection(bool_Bool onoff) {
834 bool_Bool debug = false0;
835 DBGMSF(debug, "Starting. onoff=%s", sbool(onoff))do { if (debug) dbgtrc(DDCA_TRC_ALL, 0, __func__, 835, "ddc_displays.c"
, "Starting. onoff=%s", sbool(onoff)); } while(0)
;
836
837 DDCA_Status rc = DDCRC_UNIMPLEMENTED(-(3000 +15) );
838#ifdef USE_USB1
839 if (ddc_displays_already_detected()) {
840 rc = DDCRC_INVALID_OPERATION(-(3000 +14) );
841 }
842 else {
843 detect_usb_displays = onoff;
844 rc = DDCRC_OK0;
845 }
846#endif
847 DBGMSF(debug, "Done. Returning %s", psc_name_code(rc))do { if (debug) dbgtrc(DDCA_TRC_ALL, 0, __func__, 847, "ddc_displays.c"
, "Done. Returning %s", psc_name_code(rc)); } while(0)
;
848 return rc;
849}
850
851
852/** Indicates whether USB displays are to be detected
853 *
854 * @return true/false
855 */
856bool_Bool
857ddc_is_usb_display_detection_enabled() {
858 return detect_usb_displays;
859}
860
861
862void
863init_ddc_displays() {
864 RTTI_ADD_FUNC(ddc_async_scan)rtti_func_name_table_add(ddc_async_scan, "ddc_async_scan");;
865 RTTI_ADD_FUNC(ddc_detect_all_displays)rtti_func_name_table_add(ddc_detect_all_displays, "ddc_detect_all_displays"
);
;
866 RTTI_ADD_FUNC(ddc_initial_checks_by_dh)rtti_func_name_table_add(ddc_initial_checks_by_dh, "ddc_initial_checks_by_dh"
);
;
867 RTTI_ADD_FUNC(ddc_initial_checks_by_dref)rtti_func_name_table_add(ddc_initial_checks_by_dref, "ddc_initial_checks_by_dref"
);
;
868 RTTI_ADD_FUNC(ddc_is_valid_display_ref)rtti_func_name_table_add(ddc_is_valid_display_ref, "ddc_is_valid_display_ref"
);
;
869 RTTI_ADD_FUNC(ddc_non_async_scan)rtti_func_name_table_add(ddc_non_async_scan, "ddc_non_async_scan"
);
;
870 RTTI_ADD_FUNC(ddc_redetect_displays)rtti_func_name_table_add(ddc_redetect_displays, "ddc_redetect_displays"
);
;
871 RTTI_ADD_FUNC(filter_phantom_displays)rtti_func_name_table_add(filter_phantom_displays, "filter_phantom_displays"
);
;
872 RTTI_ADD_FUNC(is_phantom_display)rtti_func_name_table_add(is_phantom_display, "is_phantom_display"
);
;
873 RTTI_ADD_FUNC(threaded_initial_checks_by_dref)rtti_func_name_table_add(threaded_initial_checks_by_dref, "threaded_initial_checks_by_dref"
);
;
874}
875