source: trunk/third/sysinfo/edid.c @ 12269

Revision 12269, 12.4 KB checked in by ghudson, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12268, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1992-1998 Michael A. Cooper.
3 * This software may be freely used and distributed provided it is not
4 * sold for profit or used in part or in whole for commercial gain
5 * without prior written agreement, and the author is credited
6 * appropriately.
7 */
8
9#ifndef lint
10static char *RCSid = "$Revision: 1.1.1.1 $";
11#endif
12
13/*
14 * Common SysInfo EDID functions
15 */
16
17#include "defs.h"
18#include "edid.h"
19
20static u_char EdidHeader[] = EDID_HEADER;
21
22/*
23 * Established Timings I, II, III in decreasing order (bit 7->0)
24 */
25static char     *EstTimingI[] = {
26    "720x400@70", "720x400@88", "640x480@60", "640x480@67",
27    "640x480@72", "640x480@75", "800x600@56", "800x600@60", NULL
28};
29static char     *EstTimingII[] = {
30    "800x600@72", "800x600@75", "832x624@75", "1024x768@87",
31    "1024x768@60", "1024x768@70", "1024x768@75", "1280x1024@75", NULL
32};
33static char     *EstTimingIII[] = {
34    "1152x870@75", NULL
35};
36
37/*
38 * Is Header an EDID Header?
39 * Return TRUE if yes or FALSE if not.
40 */
41static int EdidIsHeader(Header)
42     u_char                    *Header;
43{
44    register int                i;
45    int                         Max;
46
47    if (!Header)
48        return(FALSE);
49
50    Max = sizeof(EdidHeader) / sizeof(u_char);
51    for (i = 0; i < Max; ++i)
52        if (Header[i] != EdidHeader[i])
53            return(FALSE);
54
55    return(TRUE);
56}
57
58/*
59 * Get the Manufacturer ID from Edid.
60 */
61static char *EdidGetMan(Edid)
62     EDIDv1_t                  *Edid;
63{
64    static char                 Buff[4];
65
66    if (!Edid)
67        return((char *) NULL);
68
69    if (Edid->ManIDc1 == 0 && Edid->ManIDc2 == 0 && Edid->ManIDc3 == 0) {
70        SImsg(SIM_DBG, "EDID: GetMan: No Manufacturer ID present.");
71        return((char *) NULL);
72    }
73
74    (void) snprintf(Buff, sizeof(Buff), "%c%c%c",
75                    'A' + Edid->ManIDc1 - 1, 'A' + Edid->ManIDc2 - 1,
76                    'A' + Edid->ManIDc3 - 1);
77
78    return(Buff);
79}
80
81/*
82 * Get the Product ID from Edid.
83 */
84static char *EdidGetProductID(Edid)
85     EDIDv1_t                  *Edid;
86{
87    static char                 Buff[32];
88
89    if (!Edid)
90        return((char *) NULL);
91
92    if (Edid->ProductIdCode[0] == 0 && Edid->ProductIdCode[1] == 0) {
93        SImsg(SIM_DBG, "EDID: GetProductID: No Product ID present.");
94        return((char *) NULL);
95    }
96
97    /*
98     * Stored LSB
99     */
100    (void) snprintf(Buff, sizeof(Buff), "%02X%02X",
101                    Edid->ProductIdCode[1], Edid->ProductIdCode[0]);
102
103    return(Buff);
104}
105
106/*
107 * Get the Serial from Edid.
108 */
109static char *EdidGetSerial(Edid)
110     EDIDv1_t                  *Edid;
111{
112    static char                 Buff[32];
113
114    if (!Edid)
115        return((char *) NULL);
116
117    if (Edid->Serial[0] == 0 && Edid->Serial[1] == 0 &&
118        Edid->Serial[2] == 0 && Edid->Serial[3] == 0) {
119        SImsg(SIM_DBG, "EDID: GetSerial: No Serial ID present.");
120        return((char *) NULL);
121    }
122
123    if (Edid->Serial[0] == 0x1 && Edid->Serial[1] == 0x1 &&
124        Edid->Serial[2] == 0x1 && Edid->Serial[3] == 0x1) {
125        SImsg(SIM_DBG, "EDID: GetSerial: Serial ID unused.");
126        return((char *) NULL);
127    }
128
129    (void) snprintf(Buff, sizeof(Buff), "%02X%02X%02X%02X",
130                    Edid->Serial[0], Edid->Serial[1],
131                    Edid->Serial[2], Edid->Serial[3]);
132
133    return(Buff);
134}
135
136/*
137 * Set Monitor Range info into DevInfo
138 */
139static int EdidSetRange(DevInfo, Range)
140     DevInfo_t                 *DevInfo;
141     MonRange_t                *Range;
142{
143    if (!DevInfo || !Range)
144        return(-1);
145
146    if (Range->MinVertical)
147        AddDevDesc(DevInfo, itoa(Range->MinVertical),
148                   "RG: Minimum Vertical Rate (Hz)", DA_APPEND);
149    if (Range->MaxVertical)
150        AddDevDesc(DevInfo, itoa(Range->MaxVertical),
151                   "RG: Maximum Vertical Rate (Hz)", DA_APPEND);
152    if (Range->MinHorizontal)
153        AddDevDesc(DevInfo, itoa(Range->MinHorizontal),
154                   "RG: Minimum Horizontal Rate (KHz)", DA_APPEND);
155    if (Range->MaxHorizontal)
156        AddDevDesc(DevInfo, itoa(Range->MaxHorizontal),
157                   "RG: Maximum Horizontal Rate (KHz)", DA_APPEND);
158    if (Range->MaxPixelClock)
159        AddDevDesc(DevInfo, itoa(Range->MaxPixelClock * 10),
160                   "RG: Maximum Pixel Clock (MHz)", DA_APPEND);
161
162    return(0);
163}
164
165/*
166 * Set specific Established Timing Info
167 */
168static int EdidSetEstTiming(DevInfo, Timing, EstTimings)
169     DevInfo_t                 *DevInfo;
170     u_char                     Timing;
171     char                     **EstTimings;
172{
173    register int                i;
174    register int                e;
175    u_char                      mask;
176
177    for (i = 1, e = 0, mask = 1; i < 8 && EstTimings[e]; ++i, ++e) {
178        if (Timing & mask)
179            DevAddRes(DevInfo, EstTimings[e]);
180        mask <<= 1;
181    }
182
183    return(0);
184}
185
186/*
187 * Set Established Timing Info
188 */
189static int EdidSetEstTimings(DevInfo, Timings, NumTimings)
190     DevInfo_t                 *DevInfo;
191     u_char                    *Timings;
192     size_t                     NumTimings;
193{
194    if (NumTimings >= 1)
195        EdidSetEstTiming(DevInfo, Timings[0], EstTimingI);
196    if (NumTimings >= 2)
197        EdidSetEstTiming(DevInfo, Timings[1], EstTimingII);
198    if (NumTimings >= 3)
199        EdidSetEstTiming(DevInfo, Timings[2], EstTimingIII);
200
201    return(0);
202}
203
204/*
205 * Set Standard Timing Info
206 */
207static int EdidSetStdTimings(DevInfo, Timings, NumTimings)
208     DevInfo_t                 *DevInfo;
209     StdTiming_t               *Timings;
210     size_t                     NumTimings;
211{
212    static char                 Buff[32];
213    register int                t;
214    register StdTiming_t       *Ptr;
215    int                         HorPixels;
216    int                         VerPixels;
217    float                       Multi;
218
219    for (t = 0, Ptr = Timings; t < NumTimings; ++t, ++Ptr) {
220        if (Ptr->HorActivePixels == 0 ||
221            Ptr->HorActivePixels == 0x1)    /* Unused? */
222            continue;
223        /*
224         * Get the Aspect Ratio and use it as a multiplier
225         */
226        switch (Ptr->Aspect) {
227        case 0: Multi = (float)1 / (float)1;            break;
228        case 1: Multi = (float)3 / (float)4;            break;
229        case 2: Multi = (float)4 / (float)5;            break;
230        case 3: Multi = (float)9 / (float)16;           break;
231        default:
232            SImsg(SIM_DBG, "%s: Unknown Aspect Ratio type: %d",
233                  DevInfo->Name, Ptr->Aspect);
234            continue;
235        }
236        /*
237         * Magic formulas as specified by VESA EDID standard.
238         */
239        HorPixels = ((Ptr->HorActivePixels - 1) * 8) + 256;
240        VerPixels = (int)( (float)HorPixels * (float)Multi );
241        (void) snprintf(Buff, sizeof(Buff), "%dx%d@%d",
242                        HorPixels, VerPixels, Ptr->RefreshRate + 60);
243
244        DevAddRes(DevInfo, strdup(Buff));
245    }
246
247    return(0);
248}
249
250/*
251 * Set Detailed Timing Info
252 */
253static int EdidSetDetail(DevInfo, Details, NumDetails)
254     DevInfo_t                 *DevInfo;
255     Detail_t                  *Details;
256     size_t                     NumDetails;
257{
258    register Detail_t          *Ptr;
259    register char              *cp;
260    register int                Blk;
261    char                       *String = NULL;
262    char                       *Model = NULL;
263    char                       *Serial = NULL;
264
265    if (!DevInfo || !Details)
266        return(-1);
267
268    for (Blk = 0, Ptr = Details; Blk < NumDetails; ++Blk, ++Ptr) {
269        if (Ptr->Flag1 != 0) {
270            SImsg(SIM_DBG, "%s: EDID: Detail Block %d has Flag1=0x%x",
271                  DevInfo->Name, Blk, Ptr->Flag1);
272            /*
273             * This must be a Detailed Timing Descriptor
274             * Skip for now.
275             */
276            continue;
277        } else {
278            switch (Ptr->DataType) {
279            case MDD_NAME:
280                cp = CleanString(Ptr->Data, sizeof(Ptr->Data), 0);
281                if (cp && strlen(cp))
282                    DevInfo->Model = Model = cp;
283                break;
284            case MDD_SERIAL:
285                cp = CleanString(Ptr->Data, sizeof(Ptr->Data), 0);
286                if (cp && strlen(cp))
287                    DevInfo->Serial = Serial = cp;
288                break;
289            case MDD_STRING:
290                /* Not sure what's stored here so we'll print out on debug */
291                cp = CleanString(Ptr->Data, sizeof(Ptr->Data), 0);
292                if (cp && strlen(cp))
293                    String = cp;
294                break;
295            case MDD_RANGE:
296                EdidSetRange(DevInfo, (MonRange_t *) Ptr->Data);
297                break;
298            case MDD_STDTIMING:
299                EdidSetStdTimings(DevInfo, (StdTiming_t *) Ptr->Data, 6);
300                break;
301            default:
302                if (Ptr->DataType > 0x00 && Ptr->DataType <= 0x0F)
303                    SImsg(SIM_DBG,
304                          "%s: EDID: Ignoring manufacturer DataType=0x%X",
305                          DevInfo->Name, Ptr->DataType);
306                else
307                    SImsg(SIM_DBG, "%s: EDID: Ignoring Detail DataType=0x%X",
308                          DevInfo->Name, Ptr->DataType);
309            }
310        }
311    }
312
313    SImsg(SIM_DBG, "%s: EDID: Model=<%s> Serial=<%s> String=<%s>",
314          DevInfo->Name, PRTS(Model), PRTS(Serial), PRTS(String));
315
316    return(0);
317}
318
319/*
320 * Decode EDID Version 1
321 */
322static int EdidV1Decode(Query)
323     Query_t                   *Query;
324{
325    static EDIDv1_t             Edid;
326    static char                 Buff[256];
327    DevInfo_t                  *DevInfo = NULL;
328    Define_t                   *Define;
329    char                       *DevName = NULL;
330    char                       *ManID = NULL;
331    char                       *ProductID = NULL;
332    register char              *cp;
333    Monitor_t                  *Mon = NULL;
334
335    if (!Query || Query->DataLen != EDID_V1_SIZE || !Query->Data ||
336        !Query->DevInfo)
337        return(-1);
338
339    /*
340     * Setup what vars we're going to use
341     */
342    DevInfo = Query->DevInfo;
343    if (DevInfo && DevInfo->Name)
344        DevName = DevInfo->Name;
345    else
346        DevName = "unknown";
347    if (DevInfo->DevSpec)
348        Mon = (Monitor_t *) DevInfo->DevSpec;
349    else {
350        Mon = NewMonitor(NULL);
351        DevInfo->DevSpec = (void *) Mon;
352    }
353
354    (void) memcpy(&Edid, Query->Data, EDID_V1_SIZE);
355
356    /*
357     * It's possible to have EDID data that's not really EDID.
358     */
359    if (!EdidIsHeader(Edid.Header)) {
360        SImsg(SIM_DBG, "%s: EDID present, but header is not EDID.", DevName);
361        return(-1);
362    }
363
364    SImsg(SIM_DBG, "%s: EDID present: Version %u Revision %u ExtFlag=%d",
365          DevName, Edid.EDIDversion, Edid.EDIDrevision, Edid.ExtFlag);
366
367    (void) snprintf(Buff, sizeof(Buff), "%u", Edid.EDIDversion);
368    AddDevDesc(DevInfo, Buff, "EDID Version", DA_APPEND);
369    (void) snprintf(Buff, sizeof(Buff), "%u", Edid.EDIDrevision);
370    AddDevDesc(DevInfo, Buff, "EDID Revision", DA_APPEND);
371
372    /*
373     * Get the Manufacturer and Product ID codes
374     */
375    ManID = EdidGetMan(&Edid);
376    ProductID = EdidGetProductID(&Edid);
377    SImsg(SIM_DBG, "%s: EDID ManID=<%s> ProductID=<%s>",
378          DevName, PRTS(ManID), PRTS(ProductID));
379    /*
380     * See if we can look up translated names for ManID and ProductID
381     */
382    if (ManID) {
383        AddDevDesc(DevInfo, ManID, "EDID Manufacturer ID", DA_APPEND);
384        if(Define = DefGet("EISAvendors", ManID, 0, 0))
385            DevInfo->Vendor = Define->ValStr1;
386        else
387            SImsg(SIM_DBG, "%s: `%s' is not in `EISAvendors' in config/*.cf",
388                  DevName, ManID);
389    }
390    if (ProductID)
391        AddDevDesc(DevInfo, ProductID, "EDID Product ID", DA_APPEND);
392    if (ProductID && ManID) {
393        (void) snprintf(Buff, sizeof(Buff), "%s%s", ManID, ProductID);
394        if (Define = DefGet("EISAdevices", Buff, 0, 0))
395            DevInfo->Model = Define->ValStr1;
396        else
397            SImsg(SIM_DBG, "%s: `%s' is not in `EISAdevices' in config/*.cf",
398                  DevName, Buff);
399    }
400
401    if (!DevInfo->Vendor)
402        DevInfo->Vendor = strdup(ManID);
403    if (!DevInfo->Model)
404        DevInfo->Model = strdup(ProductID);
405
406    if (cp = EdidGetSerial(&Edid))
407        DevInfo->Serial = strdup(cp);
408
409    (void) EdidSetEstTimings(DevInfo, Edid.EstTimings,
410                             sizeof(Edid.EstTimings)/sizeof(u_char));
411    (void) EdidSetStdTimings(DevInfo, Edid.StdTimings,
412                             sizeof(Edid.StdTimings)/sizeof(StdTiming_t));
413    (void) EdidSetDetail(DevInfo, Edid.Details,
414                         sizeof(Edid.Details)/sizeof(Detail_t));
415
416    /*
417     * Get Manufacture date (MM/YYYY)
418     */
419    if (Edid.ManWeek && Edid.ManYear) {
420        (void) snprintf(Buff, sizeof(Buff), "%d/%d",
421                        (int)(Edid.ManWeek / 4.33), Edid.ManYear + 1990);
422        AddDevDesc(DevInfo, Buff, "Manufacture Date", DA_APPEND);
423    }
424
425    if (Edid.HorImageSize && Edid.VerImageSize) {
426        Mon->MaxHorSize = Edid.HorImageSize;
427        Mon->MaxVerSize = Edid.VerImageSize;
428    }
429
430    /*
431     * Determine Class Type
432     */
433    switch (Edid.DisplayType) {
434    case 0:     DevInfo->ClassType = CT_MONO;           break;
435    case 1:     DevInfo->ClassType = CT_RGBCOLOR;       break;
436    case 2:     DevInfo->ClassType = CT_NONRGBCOLOR;    break;
437    default:
438        SImsg(SIM_DBG, "%s: %d is an unknown DisplayType value from EDID.",
439              DevInfo->Name, Edid.DisplayType);
440    }
441
442    AddDevDesc(DevInfo, (Edid.SigType) ? "Digital" : "Analog",
443               "Signal Input Type", DA_APPEND);
444
445    if (Edid.DpmsStandBy)
446        AddDevDesc(DevInfo, "Stand-By", "DPMS Support", DA_APPEND);
447    if (Edid.DpmsSuspend)
448        AddDevDesc(DevInfo, "Suspend", "DPMS Support", DA_APPEND);
449    if (Edid.DpmsActiveOff)
450        AddDevDesc(DevInfo, "Active-Off", "DPMS Support", DA_APPEND);
451
452    if (Edid.StdColorSpace)
453        AddDevDesc(DevInfo, "Uses standard default color space",
454                   NULL, DA_APPEND);
455
456    if (Edid.HasPrefTiming)
457        AddDevDesc(DevInfo, "Preferred Timing in 1st Detail Block",
458                   "Has", DA_APPEND);
459
460    if (Edid.HasGtf)
461        AddDevDesc(DevInfo, "GTF Standard Timings", "Has", DA_APPEND);
462
463    return(0);
464}
465
466/*
467 * Decode an EDID structure and set Query->DevInfo with what we find.
468 */
469extern int EdidDecode(Query)
470     Query_t                   *Query;
471{
472    if (!Query)
473        return(-1);
474
475    /*
476     * We only support V1 (128-byte) EDID right now
477     */
478    if (Query->DataLen == EDID_V1_SIZE)
479        return EdidV1Decode(Query);
480    else {
481        SImsg(SIM_DBG, "%s: EdidDecode: Unknown EDID Structure size: %d",
482              (Query->DevInfo && Query->DevInfo->Name) ?
483              Query->DevInfo->Name : "unknown", Query->DataLen);
484        return(-1);
485    }
486}
Note: See TracBrowser for help on using the repository browser.