File Coverage

DDCCI.xs
Criterion Covered Total %
statement 90 221 40.7
branch 9 80 11.2
condition n/a
subroutine n/a
pod n/a
total 99 301 32.8


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2              
3             #include "EXTERN.h"
4             #include "perl.h"
5             #include "XSUB.h"
6              
7             #include "ppport.h"
8              
9             /* I2C & DDC/CI functions */
10              
11             #include
12             #include
13             #include
14             #include
15             #include
16             #include
17             #include
18             #include
19             #include
20              
21             // general constants
22             #define MSG_MAX_LEN 127 // max i2c message length
23             #define REPLY_DELAY 50000 // uS to wait after write to get the response
24              
25             // ddc/ci constants
26             #define DDCCI_COMMAND_READ 0x01 // read ctrl value
27             #define DDCCI_REPLY_READ 0x02 // read ctrl value reply */
28             #define DDCCI_COMMAND_WRITE 0x03 // write ctrl value */
29             //#define DDCCI_COMMAND_SAVE 0x0c // save current settings */
30             #define DDCCI_COMMAND_CAPS 0xf3 // get monitor caps */
31             #define DDCCI_REPLY_CAPS 0xe3 // get monitor caps reply */
32              
33             #define DDCCI_ADDR 0x37 // DDC/CI default addr
34             #define EDID_ADDR 0x50 // EDID default addr
35              
36             // magic numbers
37             #define MAGIC_1 0x51 // first byte to send, host address
38             #define MAGIC_2 0x80 // second byte to send, ORed with length
39             #define MAGIC_XOR 0x50 // initial XOR for received frame
40              
41             /*
42             * VCPs lookup table functions
43             */
44 772           const char *_vcp_name(unsigned char addr) {
45 772           switch (addr) {
46 6           case 0x00: return "Code Page";
47 3           case 0x01: return "Degauss";
48 3           case 0x02: return "Secondary Degauss";
49 3           case 0x04: return "Reset Factory Defaults";
50 3           case 0x05: return "Reset Brightness and Contrast";
51 3           case 0x06: return "Reset Factory Geometry";
52 3           case 0x08: return "Reset Factory Default Color";
53 3           case 0x0a: return "Reset Factory Default Position";
54 3           case 0x0c: return "Reset Factory Default Size";
55 3           case 0x0e: return "Image Lock Coarse";
56 3           case 0x10: return "Brightness";
57 3           case 0x12: return "Contrast";
58 3           case 0x14: return "Select Color Preset";
59 3           case 0x16: return "Red Video Gain";
60 3           case 0x18: return "Green Video Gain";
61 3           case 0x1a: return "Blue Video Gain";
62 3           case 0x1c: return "Focus";
63 3           case 0x1e: return "Auto Size Center";
64 3           case 0x20: return "Horizontal Position";
65 3           case 0x22: return "Horizontal Size";
66 3           case 0x24: return "Horizontal Pincushion";
67 3           case 0x26: return "Horizontal Pincushion Balance";
68 3           case 0x28: return "Horizontal Misconvergence";
69 3           case 0x2a: return "Horizontal Linearity";
70 3           case 0x2c: return "Horizontal Linearity Balance";
71 3           case 0x30: return "Vertical Position";
72 3           case 0x32: return "Vertical Size";
73 3           case 0x34: return "Vertical Pincushion";
74 3           case 0x36: return "Vertical Pincushion Balance";
75 3           case 0x38: return "Vertical Misconvergence";
76 3           case 0x3a: return "Vertical Linearity";
77 3           case 0x3c: return "Vertical Linearity Balance";
78 3           case 0x3e: return "Image Lock Fine";
79 3           case 0x40: return "Parallelogram Distortion";
80 3           case 0x42: return "Trapezoidal Distortion";
81 3           case 0x44: return "Tilt (Rotation)";
82 3           case 0x46: return "Top Corner Distortion Control";
83 3           case 0x48: return "Top Corner Distortion Balance";
84 3           case 0x4a: return "Bottom Corner Distortion Control";
85 3           case 0x4c: return "Bottom Corner Distortion Balance";
86 3           case 0x50: return "Hue";
87 3           case 0x52: return "Saturation";
88 3           case 0x54: return "Color Curve Adjust";
89 3           case 0x56: return "Horizontal Moire";
90 3           case 0x58: return "Vertical Moire";
91 3           case 0x5a: return "Auto Size Center Enable/Disable";
92 3           case 0x5c: return "Landing Adjust";
93 3           case 0x5e: return "Input Level Select";
94 3           case 0x60: return "Input Source Select";
95 3           case 0x62: return "Audio Speaker Volume Adjust";
96 3           case 0x64: return "Audio Microphone Volume Adjust";
97 3           case 0x66: return "On Screen Display Enable/Disable";
98 3           case 0x68: return "Language Select";
99 3           case 0x6c: return "Red Video Black Level";
100 3           case 0x6e: return "Green Video Black Level";
101 3           case 0x70: return "Blue Video Black Level";
102 3           case 0xa2: return "Auto Size Center";
103 3           case 0xa4: return "Polarity Horizontal Synchronization";
104 3           case 0xa6: return "Polarity Vertical Synchronization";
105 3           case 0xa8: return "Synchronization Type";
106 3           case 0xaa: return "Screen Orientation";
107 3           case 0xac: return "Horizontal Frequency";
108 3           case 0xae: return "Vertical Frequency";
109 3           case 0xb0: return "Restore Settings";
110 3           case 0xca: return "On Screen Display";
111 3           case 0xcc: return "On Screen Display Language";
112 3           case 0xd4: return "Stereo Mode";
113 3           case 0xd6: return "DPMS control";
114 3           case 0xdc: return "MagicBright";
115 3           case 0xdf: return "VCP Version";
116 3           case 0xe0: return "Color preset";
117 3           case 0xe1: return "Power control";
118 3           case 0xed: return "Red Video Black Level";
119 3           case 0xee: return "Green Video Black Level";
120 3           case 0xef: return "Blue Video Black Level";
121 3           case 0xf5: return "VCP Enable";
122 541           default: return "???";
123             }
124             }
125 5           int _vcp_addr(const char *name) {
126             int i;
127             const char *n;
128              
129 5 50         if (!name || (*name == '\0'))
    100          
130 2           return -1;
131              
132 259 100         for (i = 0; i <= 0xff; i++) {
133 258 100         if (strcmp((n = _vcp_name(i)), "???") == 0)
134 180           continue;
135 78 100         if (strcasecmp(name, n) == 0)
136 2           return i;
137             }
138              
139 1           return -1;
140             }
141              
142             /*
143             * write len bytes (stored in buf) to i2c address addr
144             * return 0 on success, < 0 on failure
145             */
146 0           int _i2c_write(int fd, unsigned char addr, unsigned char *buf, unsigned char len) {
147             struct i2c_rdwr_ioctl_data msg_rdwr;
148             struct i2c_msg i2cmsg;
149              
150 0           msg_rdwr.msgs = &i2cmsg;
151 0           msg_rdwr.nmsgs = 1;
152              
153 0           i2cmsg.addr = addr;
154 0           i2cmsg.flags = 0;
155 0           i2cmsg.len = len;
156 0           i2cmsg.buf = buf;
157              
158 0           return ioctl(fd, I2C_RDWR, &msg_rdwr);
159             }
160              
161             /*
162             * read at most len bytes from i2c address addr, to buf
163             * return 0/1 on success, < 0 on failure
164             */
165 0           int _i2c_read(int fd, unsigned char addr, unsigned char *buf, unsigned char len) {
166             struct i2c_rdwr_ioctl_data msg_rdwr;
167             struct i2c_msg i2cmsg;
168              
169 0           msg_rdwr.msgs = &i2cmsg;
170 0           msg_rdwr.nmsgs = 1;
171              
172 0           i2cmsg.addr = addr;
173 0           i2cmsg.flags = I2C_M_RD;
174 0           i2cmsg.len = len;
175 0           i2cmsg.buf = buf;
176              
177 0           return ioctl(fd, I2C_RDWR, &msg_rdwr);
178             }
179              
180             /*
181             * write len bytes from *buf to ddc/ci at address addr
182             * return 0 on success, < 0 on failure
183             */
184 0           int _ddcci_write(int fd, unsigned char addr, unsigned char *buf, unsigned char len) {
185 0           int i = 0;
186             unsigned char tmp[MSG_MAX_LEN + 3];
187 0           unsigned char xor = (unsigned char)(addr << 1);
188              
189             // first magic
190 0           xor ^= (tmp[i++] = MAGIC_1);
191            
192             // 2nd magic + message size
193 0           xor ^= (tmp[i++] = MAGIC_2 | len);
194            
195             // msg
196 0 0         while (len--)
197 0           xor ^= (tmp[i++] = *buf++);
198            
199             // msg checksum
200 0           tmp[i++] = xor;
201              
202             // write to i2c
203 0           return _i2c_write(fd, addr, tmp, i);
204             }
205              
206             /*
207             * read ddc/ci formatted frame from ddc/ci at address addr to buf
208             * return msg len on success, < 0 on failure
209             */
210 0           int _ddcci_read(int fd, unsigned char addr, unsigned char *buf, unsigned char len) {
211             int i, r;
212             unsigned char tmp[MSG_MAX_LEN + 3];
213 0           unsigned char xor = MAGIC_XOR;
214              
215 0           memset(buf, 0, len);
216              
217             // read raw data
218 0 0         if (
219 0 0         (_i2c_read(fd, addr, tmp, len + 3) <= 0) ||
220 0 0         (tmp[0] == 0x51) ||
221 0           (tmp[0] == 0xff)
222             )
223 0           return -1;
224            
225             // validate answer
226 0 0         if (tmp[0] != addr << 1)
227 0           return -1;
228 0 0         if ((tmp[1] & MAGIC_2) == 0)
229 0           return -1;
230 0           r = tmp[1] & ~MAGIC_2;
231 0 0         for (i = 0; i <= r + 2; i++)
232 0           xor ^= tmp[i];
233 0 0         if (xor != 0)
234 0           return -1;
235 0 0         if (r > len)
236 0           return -1;
237              
238             // copy payload data
239 0           memcpy(buf, tmp + 2, r);
240            
241 0           return r;
242             }
243              
244             /*
245             * write value to register of ddc/ci at default address
246             * return 0 on success, < 0 on failure
247             */
248 0           int _ddcci_write_vcp(int fd, unsigned char vcp, unsigned short value) {
249             unsigned char tmp[4];
250              
251 0           tmp[0] = DDCCI_COMMAND_WRITE;
252 0           tmp[1] = vcp;
253 0           tmp[2] = (value >> 8);
254 0           tmp[3] = (value & 255);
255              
256 0           return _ddcci_write(fd, DDCCI_ADDR, tmp, sizeof(tmp));
257             }
258              
259             /*
260             * read value from register of ddc/ci from default address
261             * return 0 on success, < 0 on failure
262             */
263 0           int _ddcci_read_vcp(int fd, unsigned char vcp, unsigned short *value, unsigned short *max, unsigned char *type) {
264             unsigned char buf[8];
265             int r;
266             uint16_t vc, vm;
267              
268 0           buf[0] = DDCCI_COMMAND_READ;
269 0           buf[1] = vcp;
270              
271 0 0         if (_ddcci_write(fd, DDCCI_ADDR, buf, 2) < 0)
272 0           return -1;
273              
274 0           usleep(REPLY_DELAY);
275            
276 0 0         if ((r = _ddcci_read(fd, DDCCI_ADDR, buf, sizeof(buf))) < 0)
277 0           return r;
278              
279             // data structure:
280             // 0 1 2 3 4 5 6 7
281             // [reply opcode]-[result code: 0=ok, 1=fail]-[reg]-[type: 0=permanent, 1=temporary]-[max value H]-[max value L]-[curr value H]-[currvalue L]
282 0 0         if (
283 0 0         (r != sizeof(buf)) ||
284 0 0         (buf[0] != DDCCI_REPLY_READ) ||
285 0 0         buf[1] ||
286 0           (buf[2] != vcp)
287             )
288 0           return -1;
289              
290 0           vc = (buf[6] << 8) + buf[7];
291 0           vm = (buf[4] << 8) + buf[5];
292              
293 0 0         if (value) *value = vc;
294 0 0         if (max) *max = vm;
295 0 0         if (type) *type = buf[3];
296 0           return 0;
297             }
298              
299             /*
300             * read capabilities raw data of ddc/ci from default address starting at offset to buf
301             * return msg len on success, < 0 on failure
302             */
303 0           int _ddcci_caps(int fd, char **buf) {
304             unsigned char tmp[32 + 3]; // max chunk size + hdr (not sure about hdr, conservative programming...)
305             void *p;
306 0           int i, r, len = 0;
307 0           unsigned short offset = 0;
308              
309 0           *buf = NULL;
310              
311             do {
312 0           tmp[0] = DDCCI_COMMAND_CAPS;
313 0           tmp[1] = offset >> 8;
314 0           tmp[2] = offset & 0xff;
315              
316 0 0         if (_ddcci_write(fd, DDCCI_ADDR, tmp, sizeof(tmp)) < 0)
317 0           goto err;
318            
319 0           usleep(REPLY_DELAY);
320              
321 0 0         if ((r = _ddcci_read(fd, DDCCI_ADDR, tmp, sizeof(tmp))) < 0)
322 0           goto err;
323              
324 0 0         if (
325 0 0         (r < 3) ||
326 0 0         (tmp[0] != DDCCI_REPLY_CAPS) ||
327 0           ((tmp[1] << 8) + tmp[2] != offset)
328             )
329             goto err;
330            
331 0 0         if (!(p = realloc(*buf, len + (r - 3) * 6 + 1)))
332 0           goto err;
333 0           *buf = p;
334              
335 0 0         for (i = 3; i < r; i++)
336 0 0         len += sprintf(*buf+len, ((tmp[i] >= 0x20) && (tmp[i] < 127)) ? "%c" : " 0x%02x ", tmp[i]);
    0          
337            
338 0           offset += r - 3;
339 0           usleep(REPLY_DELAY);
340 0 0         } while (r > 3);
341              
342 0           return len;
343              
344             err:
345 0           free(*buf);
346 0           *buf = NULL;
347 0           return -1;
348             }
349              
350             /*
351             * read the first (mandatory) block of EDID (128 bytes)
352             * return len on success, < 0 on failure
353             */
354 0           int _ddcci_edid(int fd, unsigned char **buf) {
355             int r;
356 0           unsigned char tmp = 0;
357              
358 0 0         if (!(*buf = malloc(128)))
359 0           return -1;
360              
361 0           memset(*buf, 0, 128);
362              
363 0 0         if ((r = _i2c_write(fd, EDID_ADDR, &tmp, 1)) < 0)
364 0           goto err;
365            
366 0 0         if ((r = _i2c_read(fd, EDID_ADDR, *buf, 128)) < 0)
367 0           goto err;
368              
369 0           return 128;
370              
371             err:
372 0           free(*buf);
373 0           *buf = NULL;
374 0           return -1;
375             }
376              
377             MODULE = DDCCI PACKAGE = DDCCI
378              
379             ### XS code ###
380              
381             SV *
382             _get_vcp_name(addr)
383             unsigned char addr
384             CODE:
385 514           RETVAL = newSVpv(_vcp_name(addr), 0);
386             OUTPUT:
387             RETVAL
388              
389             SV *
390             _get_vcp_addr(name)
391             const char *name
392             CODE:
393 5           RETVAL = newSViv(_vcp_addr(name));
394             OUTPUT:
395             RETVAL
396              
397             int
398             _open_dev(fn)
399             const char *fn
400             CODE:
401 0           RETVAL = open(fn, O_RDWR);
402             OUTPUT:
403             RETVAL
404              
405             int
406             _close_dev(fd)
407             int fd
408             CODE:
409 0           RETVAL = close(fd);
410             OUTPUT:
411             RETVAL
412              
413             SV *
414             _read_vcp(fd, vcp)
415             int fd
416             unsigned char vcp
417             CODE:
418             unsigned short value;
419 0 0         if (_ddcci_read_vcp(fd, vcp, &value, NULL, NULL) < 0)
420 0           RETVAL = newSV(0);
421             else
422 0           RETVAL = newSVuv(value);
423             OUTPUT:
424             RETVAL
425              
426             SV *
427             _write_vcp(fd, vcp, value)
428             int fd
429             unsigned char vcp
430             unsigned short value
431             CODE:
432 0 0         if (_ddcci_write_vcp(fd, vcp, value) < 0)
433 0           RETVAL = newSV(0);
434             else
435 0           RETVAL = newSVuv(value);
436             OUTPUT:
437             RETVAL
438              
439             SV *
440             _read_caps(fd)
441             int fd
442             CODE:
443 0           char *caps = NULL;
444             int len;
445 0 0         if ((len = _ddcci_caps(fd, &caps)) < 0)
446 0           RETVAL = newSV(0);
447             else
448 0           RETVAL = newSVpvn(caps, len);
449 0           free(caps);
450             OUTPUT:
451             RETVAL
452              
453             SV *
454             _read_edid(fd)
455             int fd
456             CODE:
457 0           unsigned char *edid = NULL;
458             int len;
459 0 0         if ((len = _ddcci_edid(fd, &edid)) < 0)
460 0           RETVAL = newSV(0);
461             else
462 0           RETVAL = newSVpvn((const char *)edid, len);
463 0           free(edid);
464             OUTPUT:
465             RETVAL
466