2010-07-27 13:02:24 -06:00
|
|
|
/*
|
|
|
|
* Copyright 2009 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
* copy of this software and associated documentation files (the "Software")
|
|
|
|
* to deal in the software without restriction, including without limitation
|
|
|
|
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
|
|
|
* license, and/or sell copies of the Software, and to permit persons to whom
|
|
|
|
* them Software is furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice (including the next
|
|
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
|
|
* Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTIBILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER
|
|
|
|
* IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Adam Jackson <ajax@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "xorg-config.h"
|
|
|
|
#include "xf86.h"
|
|
|
|
#include "xf86str.h"
|
|
|
|
#include "edid.h"
|
|
|
|
#include "xf86DDC.h"
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
typedef void (*did_proc) (int scrnIndex, unsigned char *data, void *closure);
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
#define DID_PRODUCT_ID 0x00
|
|
|
|
#define DID_DISPLAY_PARAMETERS 0x01
|
|
|
|
#define DID_COLOR_INFO 0x02
|
|
|
|
#define DID_TIMING_1_DETAILED 0x03
|
|
|
|
#define DID_TIMING_2_DETAILED 0x04
|
|
|
|
#define DID_TIMING_3_SHORT 0x05
|
|
|
|
#define DID_TIMING_4_DMT 0x06
|
|
|
|
#define DID_TIMING_VESA 0x07
|
|
|
|
#define DID_TIMING_CEA 0x08
|
|
|
|
#define DID_TIMING_RANGE_LIMITS 0x09
|
|
|
|
#define DID_PRODUCT_SERIAL 0x0A
|
|
|
|
#define DID_ASCII_STRING 0x0B
|
|
|
|
#define DID_DISPLAY_DEVICE 0x0C
|
|
|
|
#define DID_POWER_SEQUENCING 0x0D
|
|
|
|
#define DID_TRANSFER_INFO 0x0E
|
|
|
|
#define DID_DISPLAY_INTERFACE 0x0F
|
|
|
|
#define DID_STEREO 0x10
|
|
|
|
#define DID_VENDOR 0x7F
|
|
|
|
|
|
|
|
#define extract_le16(x, i) ((x[i+1] << 8) + (x[i]))
|
|
|
|
#define extract_le24(x, i) ((x[i+2] << 16) + (x[i+1] << 8) + (x[i]))
|
|
|
|
|
|
|
|
static DisplayModePtr
|
|
|
|
modeCalloc(void)
|
|
|
|
{
|
2010-12-05 08:36:02 -07:00
|
|
|
return calloc(1, sizeof(DisplayModeRec));
|
2010-07-27 13:02:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* How awesome is it to have two detailed timing formats, neither of which
|
|
|
|
* are compatible with the format in EDID? So awesome.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
didDetailedTiming1(int i, unsigned char *x, MonPtr mon)
|
|
|
|
{
|
|
|
|
DisplayModePtr m = modeCalloc();
|
|
|
|
|
|
|
|
if (!m)
|
2012-06-10 07:21:05 -06:00
|
|
|
return;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
m->Clock = extract_le24(x, 0);
|
|
|
|
|
|
|
|
m->HDisplay = extract_le16(x, 4);
|
|
|
|
m->HSyncStart = m->HDisplay + (extract_le16(x, 8) & 0x7f);
|
|
|
|
m->HSyncEnd = m->HSyncStart + extract_le16(x, 10);
|
|
|
|
m->HTotal = m->HDisplay + extract_le16(x, 6);
|
|
|
|
m->Flags |= (x[9] & 0x80) ? V_PHSYNC : V_NHSYNC;
|
|
|
|
|
|
|
|
m->VDisplay = extract_le16(x, 12);
|
|
|
|
m->VSyncStart = m->VDisplay + (extract_le16(x, 16) & 0x7f);
|
|
|
|
m->VSyncEnd = m->VSyncStart + extract_le16(x, 18);
|
|
|
|
m->VTotal = m->VDisplay + extract_le16(x, 14);
|
|
|
|
m->Flags |= (x[17] & 0x80) ? V_PVSYNC : V_NVSYNC;
|
|
|
|
|
|
|
|
m->type = M_T_DRIVER;
|
|
|
|
if (x[3] & 0x80)
|
2012-06-10 07:21:05 -06:00
|
|
|
m->type |= M_T_PREFERRED;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
/* XXX double check handling of this */
|
|
|
|
if (x[3] & 0x10)
|
2012-06-10 07:21:05 -06:00
|
|
|
m->Flags |= V_INTERLACE;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
mon->Modes = xf86ModesAdd(mon->Modes, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX no sync bits. what to do? */
|
|
|
|
static void
|
|
|
|
didDetailedTiming2(int i, unsigned char *x, MonPtr mon)
|
|
|
|
{
|
|
|
|
DisplayModePtr mode = modeCalloc();
|
|
|
|
|
|
|
|
if (!mode)
|
2012-06-10 07:21:05 -06:00
|
|
|
return;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
mode->Clock = extract_le24(x, 0);
|
|
|
|
|
|
|
|
/* horiz sizes are in character cells, not pixels, hence * 8 */
|
|
|
|
mode->HDisplay = ((extract_le16(x, 4) & 0x01ff) + 1) * 8;
|
|
|
|
mode->HSyncStart = mode->HDisplay + (((x[6] & 0xf0) >> 4) + 1) * 8;
|
|
|
|
mode->HSyncEnd = mode->HSyncStart + ((x[6] & 0x0f) + 1) * 8;
|
|
|
|
mode->HTotal = mode->HDisplay + ((x[5] >> 1) + 1) * 8;
|
|
|
|
|
|
|
|
mode->VDisplay = extract_le16(x, 7) & 0x07ff;
|
|
|
|
mode->VSyncStart = mode->VDisplay + (x[10] >> 4) + 1;
|
|
|
|
mode->VSyncEnd = mode->VSyncStart + (x[10] & 0x0f) + 1;
|
|
|
|
mode->VTotal = mode->VDisplay + x[9];
|
|
|
|
|
|
|
|
mode->status = M_T_DRIVER;
|
|
|
|
if (x[3] & 0x80)
|
2012-06-10 07:21:05 -06:00
|
|
|
mode->status |= M_T_PREFERRED;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
/* XXX double check handling of this */
|
|
|
|
if (x[3] & 0x10)
|
2012-06-10 07:21:05 -06:00
|
|
|
mode->Flags |= V_INTERLACE;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
mon->Modes = xf86ModesAdd(mon->Modes, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
didShortTiming(int i, unsigned char *x, MonPtr mon)
|
|
|
|
{
|
|
|
|
DisplayModePtr m;
|
|
|
|
int w, h, r;
|
|
|
|
|
|
|
|
w = (x[1] + 1) * 8;
|
|
|
|
switch (x[0] & 0x0f) {
|
2012-06-10 07:21:05 -06:00
|
|
|
case 0:
|
|
|
|
h = w;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
h = (w * 4) / 5;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
h = (w * 3) / 4;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
h = (w * 9) / 15;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
h = (w * 9) / 16;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
h = (w * 10) / 16;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
2010-07-27 13:02:24 -06:00
|
|
|
}
|
|
|
|
r = (x[2] & 0x7f) + 1;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
|
|
|
m = xf86CVTMode(w, h, r, ! !(x[0] & 0x10), ! !(x[2] & 0x80));
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
m->type = M_T_DRIVER;
|
|
|
|
if (x[0] & 0x80)
|
2012-06-10 07:21:05 -06:00
|
|
|
m->type |= M_T_PREFERRED;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
mon->Modes = xf86ModesAdd(mon->Modes, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
didDMTTiming(int i, unsigned char *x, void *closure)
|
|
|
|
{
|
|
|
|
MonPtr mon = closure;
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
mon->Modes = xf86ModesAdd(mon->Modes, xf86DuplicateMode(DMTModes + *x));
|
2010-07-27 13:02:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#define RB 1
|
|
|
|
#define INT 2
|
|
|
|
static const struct did_dmt {
|
|
|
|
short w, h, r, f;
|
|
|
|
} did_dmt[] = {
|
|
|
|
/* byte 3 */
|
2013-06-07 11:28:45 -06:00
|
|
|
{640, 350, 85, 0},
|
|
|
|
{640, 400, 85, 0},
|
|
|
|
{720, 400, 85, 0},
|
|
|
|
{640, 480, 60, 0},
|
|
|
|
{640, 480, 72, 0},
|
|
|
|
{640, 480, 75, 0},
|
|
|
|
{640, 480, 85, 0},
|
|
|
|
{800, 600, 56, 0},
|
2012-06-10 07:21:05 -06:00
|
|
|
/* byte 4 */
|
2013-06-07 11:28:45 -06:00
|
|
|
{800, 600, 60, 0},
|
|
|
|
{800, 600, 72, 0},
|
|
|
|
{800, 600, 75, 0},
|
|
|
|
{800, 600, 85, 0},
|
|
|
|
{800, 600, 120, RB},
|
|
|
|
{848, 480, 60, 0},
|
|
|
|
{1024, 768, 43, INT},
|
|
|
|
{1024, 768, 60, 0},
|
2012-06-10 07:21:05 -06:00
|
|
|
/* byte 5 */
|
2013-06-07 11:28:45 -06:00
|
|
|
{1024, 768, 70, 0},
|
|
|
|
{1024, 768, 75, 0},
|
|
|
|
{1024, 768, 85, 0},
|
|
|
|
{1024, 768, 120, RB},
|
|
|
|
{1152, 864, 75, 0},
|
|
|
|
{1280, 768, 60, RB},
|
|
|
|
{1280, 768, 60, 0},
|
|
|
|
{1280, 768, 75, 0},
|
2012-06-10 07:21:05 -06:00
|
|
|
/* byte 6 */
|
2013-06-07 11:28:45 -06:00
|
|
|
{1280, 768, 85, 0},
|
|
|
|
{1280, 768, 120, RB},
|
|
|
|
{1280, 800, 60, RB},
|
|
|
|
{1280, 800, 60, 0},
|
|
|
|
{1280, 800, 75, 0},
|
|
|
|
{1280, 800, 85, 0},
|
|
|
|
{1280, 800, 120, RB},
|
|
|
|
{1280, 960, 60, 0},
|
2012-06-10 07:21:05 -06:00
|
|
|
/* byte 7 */
|
2013-06-07 11:28:45 -06:00
|
|
|
{1280, 960, 85, 0},
|
|
|
|
{1280, 960, 120, RB},
|
|
|
|
{1280, 1024, 60, 0},
|
|
|
|
{1280, 1024, 75, 0},
|
|
|
|
{1280, 1024, 85, 0},
|
|
|
|
{1280, 1024, 120, RB},
|
|
|
|
{1360, 768, 60, 0},
|
|
|
|
{1360, 768, 120, RB},
|
2012-06-10 07:21:05 -06:00
|
|
|
/* byte 8 */
|
2013-06-07 11:28:45 -06:00
|
|
|
{1400, 1050, 60, RB},
|
|
|
|
{1400, 1050, 60, 0},
|
|
|
|
{1400, 1050, 75, 0},
|
|
|
|
{1400, 1050, 85, 0},
|
|
|
|
{1400, 1050, 120, RB},
|
|
|
|
{1440, 900, 60, RB},
|
|
|
|
{1440, 900, 60, 0},
|
|
|
|
{1440, 900, 75, 0},
|
2012-06-10 07:21:05 -06:00
|
|
|
/* byte 9 */
|
2013-06-07 11:28:45 -06:00
|
|
|
{1440, 900, 85, 0},
|
|
|
|
{1440, 900, 120, RB},
|
|
|
|
{1600, 1200, 60, 0},
|
|
|
|
{1600, 1200, 65, 0},
|
|
|
|
{1600, 1200, 70, 0},
|
|
|
|
{1600, 1200, 75, 0},
|
|
|
|
{1600, 1200, 85, 0},
|
|
|
|
{1600, 1200, 120, RB},
|
2012-06-10 07:21:05 -06:00
|
|
|
/* byte a */
|
2013-06-07 11:28:45 -06:00
|
|
|
{1680, 1050, 60, RB},
|
|
|
|
{1680, 1050, 60, 0},
|
|
|
|
{1680, 1050, 75, 0},
|
|
|
|
{1680, 1050, 85, 0},
|
|
|
|
{1680, 1050, 120, RB},
|
|
|
|
{1792, 1344, 60, 0},
|
|
|
|
{1792, 1344, 75, 0},
|
|
|
|
{1792, 1344, 120, RB},
|
2012-06-10 07:21:05 -06:00
|
|
|
/* byte b */
|
2013-06-07 11:28:45 -06:00
|
|
|
{1856, 1392, 60, 0},
|
|
|
|
{1856, 1392, 75, 0},
|
|
|
|
{1856, 1392, 120, RB},
|
|
|
|
{1920, 1200, 60, RB},
|
|
|
|
{1920, 1200, 60, 0},
|
|
|
|
{1920, 1200, 75, 0},
|
|
|
|
{1920, 1200, 85, 0},
|
|
|
|
{1920, 1200, 120, RB},
|
2012-06-10 07:21:05 -06:00
|
|
|
/* byte c */
|
2013-06-07 11:28:45 -06:00
|
|
|
{1920, 1440, 60, 0},
|
|
|
|
{1920, 1440, 75, 0},
|
|
|
|
{1920, 1440, 120, RB},
|
|
|
|
{2560, 1600, 60, RB},
|
|
|
|
{2560, 1600, 60, 0},
|
|
|
|
{2560, 1600, 75, 0},
|
|
|
|
{2560, 1600, 85, 0},
|
|
|
|
{2560, 1600, 120, RB},
|
|
|
|
};
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
static void
|
|
|
|
didVesaTiming(int scrn, unsigned char *x, MonPtr mon)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
x += 3;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
for (i = 0; i < 10; i++)
|
2012-06-10 07:21:05 -06:00
|
|
|
for (j = 0; j < 8; j++)
|
|
|
|
if (x[i] & (1 << j)) {
|
|
|
|
const struct did_dmt *d = &(did_dmt[i * 8 + j]);
|
|
|
|
|
|
|
|
if (d->f == INT)
|
|
|
|
continue;
|
|
|
|
mon->Modes = xf86ModesAdd(mon->Modes,
|
|
|
|
FindDMTMode(d->w, d->h, d->r,
|
|
|
|
d->f == RB));
|
|
|
|
}
|
2010-07-27 13:02:24 -06:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
handleDisplayIDBlock(int scrnIndex, unsigned char *x, void *closure)
|
|
|
|
{
|
|
|
|
MonPtr mon = closure;
|
|
|
|
|
|
|
|
switch (x[0]) {
|
2012-06-10 07:21:05 -06:00
|
|
|
case DID_DISPLAY_PARAMETERS:
|
|
|
|
/* w/h are in decimillimeters */
|
|
|
|
mon->widthmm = (extract_le16(x, 3) + 5) / 10;
|
|
|
|
mon->heightmm = (extract_le16(x, 5) + 5) / 10;
|
|
|
|
/* XXX pixel count, feature flags, gamma, aspect, color depth */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DID_TIMING_RANGE_LIMITS:
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
mon->maxPixClock = max(mon->maxPixClock, extract_le24(x, 6) * 10);
|
|
|
|
|
|
|
|
n = mon->nHsync++;
|
|
|
|
if (n < MAX_HSYNC) {
|
|
|
|
mon->hsync[n].lo = x[9];
|
|
|
|
mon->hsync[n].hi = x[10];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
n = MAX_HSYNC;
|
|
|
|
}
|
|
|
|
n = mon->nVrefresh++;
|
|
|
|
if (n < MAX_VREFRESH) {
|
|
|
|
mon->vrefresh[n].lo = x[13];
|
|
|
|
mon->vrefresh[n].hi = x[14];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
n = MAX_VREFRESH;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DID_TIMING_1_DETAILED:
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < x[2]; i += 20)
|
|
|
|
didDetailedTiming1(scrnIndex, x + i + 3, mon);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DID_TIMING_2_DETAILED:
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < x[2]; i += 11)
|
|
|
|
didDetailedTiming2(scrnIndex, x + i + 3, mon);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DID_TIMING_3_SHORT:
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < x[2]; i += 3)
|
|
|
|
didShortTiming(scrnIndex, x + i + 3, mon);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DID_TIMING_4_DMT:
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < x[2]; i++)
|
|
|
|
didDMTTiming(scrnIndex, x + i + 3, mon);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DID_TIMING_VESA:
|
|
|
|
didVesaTiming(scrnIndex, x, mon);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* XXX pixel format, ar, orientation, subpixel, dot pitch, bit depth */
|
|
|
|
case DID_DISPLAY_DEVICE:
|
|
|
|
|
|
|
|
/* XXX interface, links, color encoding, ss, drm */
|
|
|
|
case DID_DISPLAY_INTERFACE:
|
|
|
|
|
|
|
|
/* XXX stereo */
|
|
|
|
case DID_STEREO:
|
|
|
|
|
|
|
|
/* nothing interesting in these */
|
|
|
|
case DID_COLOR_INFO:
|
|
|
|
case DID_PRODUCT_SERIAL:
|
|
|
|
case DID_ASCII_STRING:
|
|
|
|
case DID_POWER_SEQUENCING:
|
|
|
|
case DID_TRANSFER_INFO:
|
|
|
|
case DID_VENDOR:
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* warn about anything else */
|
|
|
|
default:
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"Unknown DisplayID block type %hx\n", x[0]);
|
|
|
|
break;
|
2010-07-27 13:02:24 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
forEachDisplayIDBlock(int scrnIndex, unsigned char *did, did_proc proc,
|
2012-06-10 07:21:05 -06:00
|
|
|
void *closure)
|
2010-07-27 13:02:24 -06:00
|
|
|
{
|
|
|
|
int num_extensions = did[3];
|
|
|
|
int section_size = did[1];
|
|
|
|
unsigned char *block;
|
|
|
|
|
|
|
|
do {
|
2012-06-10 07:21:05 -06:00
|
|
|
if ((did[0] & 0xf0) != 0x10) /* not 1.x, abort */
|
|
|
|
return;
|
|
|
|
/* XXX also, checksum */
|
2010-07-27 13:02:24 -06:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
block = did + 4;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
while (section_size > 0) {
|
|
|
|
int block_size = (block[2] + 2);
|
2010-07-27 13:02:24 -06:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
proc(scrnIndex, block, closure);
|
2010-07-27 13:02:24 -06:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
section_size -= block_size;
|
|
|
|
block += block_size;
|
|
|
|
}
|
2010-07-27 13:02:24 -06:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
did += (did[1] + 5);
|
2010-07-27 13:02:24 -06:00
|
|
|
} while (num_extensions--);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill out MonPtr with xf86MonPtr information.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
xf86DisplayIDMonitorSet(int scrnIndex, MonPtr mon, xf86MonPtr DDC)
|
|
|
|
{
|
|
|
|
if (!mon || !DDC)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mon->DDC = DDC;
|
|
|
|
|
|
|
|
forEachDisplayIDBlock(scrnIndex, DDC->rawData, handleDisplayIDBlock, mon);
|
|
|
|
}
|