-
Notifications
You must be signed in to change notification settings - Fork 6
/
i1d3.c
4150 lines (3456 loc) · 112 KB
/
i1d3.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Argyll Color Correction System
*
* GretagMacbeth Huey related functions
*
* Author: Graeme W. Gill
* Date: 28/7/2011
*
* Copyright 2006 - 2014, Graeme W. Gill
* All rights reserved.
*
* (Based on huey.c)
*
* This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
* see the License2.txt file for licencing details.
*/
/*
If you make use of the instrument driver code here, please note
that it is the author(s) of the code who take responsibility
for its operation. Any problems or queries regarding driving
instruments with the Argyll drivers, should be directed to
the Argyll's author(s), and not to any other party.
If there is some instrument feature or function that you
would like supported here, it is recommended that you
contact Argyll's author(s) first, rather than attempt to
modify the software yourself, if you don't have firm knowledge
of the instrument communicate protocols. There is a chance
that an instrument could be damaged by an incautious command
sequence, and the instrument companies generally cannot and
will not support developers that they have not qualified
and agreed to support.
*/
/* TTBD:
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <math.h>
#ifndef SALONEINSTLIB
#include "copyright.h"
#include "aconfig.h"
#include "numlib.h"
#else /* SALONEINSTLIB */
#include "sa_config.h"
#include "numsup.h"
#endif /* SALONEINSTLIB */
#include "xspect.h"
#include "insttypes.h"
#include "conv.h"
#include "icoms.h"
#include "i1d3.h"
#undef PLOT_SPECTRA /* Plot the sensor senitivity spectra */
#undef PLOT_XYZSPECTRA /* Plot the calibrated sensor senitivity spectra */
#undef SAVE_SPECTRA /* Save the sensor senitivity spectra to "sensors.cmf" */
#undef SAVE_XYZSPECTRA /* Save the XYZ senitivity spectra to "sensorsxyz.cmf" (scale 1.4) */
#undef SAVE_STDXYZ /* save 1931 2 degree to stdobsxyz.cmf */
#undef PLOT_REFRESH /* Plot data used to determine refresh rate */
#undef PLOT_UPDELAY /* Plot data used to determine display update delay */
#undef DEBUG_TWEAKS /* Allow environment variable tweaks to int time etc. */
/* I1D3_MIN_REF_QUANT_TIME in seconds. Default 0.05 */
/* I1D3_MIN_INT_TIME in seconds. Default 0.4 for refresh displays */
#define I1D3_MEAS_TIMEOUT 40.0 /* Longest reading timeout in seconds */
/* Typically 20.0 is the maximum needed. */
#define I1D3_SAT_FREQ 100000.0 /* L2F sensor frequency limit */
static inst_code i1d3_interp_code(inst *pp, int ec);
static inst_code i1d3_check_unlock(i1d3 *p);
/* ------------------------------------------------------------------- */
#if defined(__APPLE__) && defined(__POWERPC__)
/* Workaround for a PPC gcc 3.3 optimiser bug... */
/* It seems to cause a segmentation fault instead of */
/* converting an integer loop index into a float, */
/* when there are sufficient variables in play. */
static int gcc_bug_fix(int i) {
static int nn;
nn += i;
return nn;
}
#endif /* APPLE */
/* ------------------------------------------------------------------------ */
/* Implementation */
/* Interpret an icoms error into a I1D3 error */
/* If torc is nz, then a trigger or command is OK, */
/* othewise they are treated as an abort. */
static int icoms2i1d3_err(int se, int torc) {
if (se != ICOM_OK)
return I1D3_COMS_FAIL;
return I1D3_OK;
}
/* i1d3 command codes. */
/* A 64 bit command/response buffer is always used, communicating */
/* over EP 0x81 and 0x01. The command byte 0 is the major code, */
/* and byte 1 is the sub code for command 0x00 . The response is byte 0 */
/* error code, byte 1 echoing the major command number. */
/* Major code 00 works when locked ? */
/* Response codes:
00 OK
83 After pulse count measure in low light. Means ???
*/
typedef enum {
i1d3_getinfo = 0x0000, /* Product name + Firmware version + Firmware Date string */
i1d3_status = 0x0001, /* status number ?? */
i1d3_prodname = 0x0010, /* Product name string */
i1d3_prodtype = 0x0011, /* Product type number */
i1d3_firmver = 0x0012, /* Firmware version string */
i1d3_firmdate = 0x0013, /* Firmware date string */
i1d3_locked = 0x0020, /* Get locked status */
i1d3_measure1 = 0x0100, /* Used by all measure */
i1d3_measure2 = 0x0200, /* Used by all measure except ambient */
i1d3_readintee = 0x0800, /* Read internal EEPROM */
i1d3_readextee = 0x1200, /* Read external EEPROM */
i1d3_setled = 0x2100, /* Set the LED state */
i1d3_rd_sensor = 0x9300, /* Read the analog sensor */
i1d3_get_diff = 0x9400, /* Get the diffuser position */
i1d3_lockchal = 0x9900, /* Request lock challenge */
i1d3_lockresp = 0x9a00, /* Unlock response */
i1d3_relock = 0x9b00 /* Close device - relock ? */
} i1Disp3CC;
/* Diagnostic - return a description given the instruction code */
static char *inst_desc(i1Disp3CC cc) {
static char buf[40]; /* Fallback string */
switch(cc) {
case i1d3_getinfo:
return "GetInfo";
case i1d3_status:
return "GetStatus";
case i1d3_prodname:
return "GetProductName";
case i1d3_prodtype:
return "GetProductType";
case i1d3_firmver:
return "GetFirmwareVersion";
case i1d3_firmdate:
return "GetFirmwareDate";
case i1d3_locked:
return "GetLockedStatus";
case i1d3_measure1:
return "Measure1";
case i1d3_measure2:
return "Measure2";
case i1d3_readintee:
return "ReadInternalEEPROM";
case i1d3_readextee:
return "ReadExternalEEPROM";
case i1d3_setled:
return "SetLED";
case i1d3_rd_sensor:
return "ReadAnalogSensor";
case i1d3_get_diff:
return "GetDiffuserPosition";
case i1d3_lockchal:
return "GetLockChallenge";
case i1d3_lockresp:
return "SendLockResponse";
case i1d3_relock:
return "ReLock";
}
sprintf(buf,"Unknown %04x",cc);
return buf;
}
/* Do a command/response exchange with the i1d3. */
/* Return the error code */
/* This is protected by a mutex, so it is multi-thread safe. */
/* The i1d3 is set up as an HID device, which can ease the need */
/* for providing a kernel driver on MSWindows systems, */
/* but it doesn't seem to actually be used as an HID device. */
/* We allow for communicating via usbio, or an HID driver. */
static inst_code
i1d3_command(
i1d3 *p, /* i1d3 object */
i1Disp3CC cc, /* Command code */
unsigned char *send, /* 64 Command bytes to send */
unsigned char *recv, /* 64 Response bytes returned */
double to, /* Timeout in seconds */
int nd /* nz to disable debug messages */
) {
unsigned char cmd; /* Major command code */
int wbytes; /* bytes written */
int rbytes; /* bytes read from ep */
int se, ua = 0, rv = inst_ok;
int ishid = p->icom->port_type(p->icom) == icomt_hid;
amutex_lock(p->lock);
/* Send the command using interrupt transfer to EP 0x01 */
send[0] = cmd = (cc >> 8) & 0xff; /* Major command == HID report number */
if (cmd == 0x00)
send[1] = (cc & 0xff); /* Minor command */
if (!nd) {
if (cc == i1d3_lockresp)
a1logd(p->log, 4, "i1d3_command: Sending cmd '%s' args '%s'\n",
inst_desc(cc), icoms_tohex(send, 64));
else
a1logd(p->log, 4, "i1d3_command: Sending cmd '%s' args '%s'\n",
inst_desc(cc), icoms_tohex(send, 8));
}
if (p->icom->port_type(p->icom) == icomt_hid) {
se = p->icom->hid_write(p->icom, send, 64, &wbytes, to);
} else {
se = p->icom->usb_write(p->icom, NULL, 0x01, send, 64, &wbytes, to);
}
if (se != 0) {
if (!nd) a1logd(p->log, 1, "i1d3_command: Command send failed with ICOM err 0x%x\n",se);
/* Flush any response */
if (ishid) {
p->icom->hid_read(p->icom, recv, 64, &rbytes, to);
} else {
p->icom->usb_read(p->icom, NULL, 0x81, recv, 64, &rbytes, to);
}
amutex_unlock(p->lock);
return i1d3_interp_code((inst *)p, I1D3_COMS_FAIL);
}
rv = i1d3_interp_code((inst *)p, icoms2i1d3_err(ua, 0));
if (!nd) a1logd(p->log, 5, "i1d3_command: ICOM err 0x%x\n",ua);
if (rv == inst_ok && wbytes != 64) {
if (!nd) a1logd(p->log, 1, "i1d3_command: wbytes = %d != 64\n",wbytes);
rv = i1d3_interp_code((inst *)p, I1D3_BAD_WR_LENGTH);
}
if (rv != inst_ok) {
/* Flush any response */
if (ishid) {
p->icom->hid_read(p->icom, recv, 64, &rbytes, to);
} else {
p->icom->usb_read(p->icom, NULL, 0x81, recv, 64, &rbytes, to);
}
amutex_unlock(p->lock);
return rv;
}
/* Now fetch the response */
if (!nd) a1logd(p->log, 5, "i1d3_command: Reading response\n");
if (ishid) {
se = p->icom->hid_read(p->icom, recv, 64, &rbytes, to);
} else {
se = p->icom->usb_read(p->icom, NULL, 0x81, recv, 64, &rbytes, to);
}
if (se != 0) {
if (!nd) a1logd(p->log, 1, "i1d3_command: response read failed with ICOM err 0x%x\n",se);
/* Flush any extra response, in case responses are out of sync */
if (ishid) {
p->icom->hid_read(p->icom, recv, 64, &rbytes, 0.2);
} else {
p->icom->usb_read(p->icom, NULL, 0x81, recv, 64, &rbytes, 0.2);
}
amutex_unlock(p->lock);
return i1d3_interp_code((inst *)p, I1D3_COMS_FAIL);
}
if (rv == inst_ok && rbytes != 64) {
if (!nd) a1logd(p->log, 1, "i1d3_command: rbytes = %d != 64\n",rbytes);
rv = i1d3_interp_code((inst *)p, I1D3_BAD_RD_LENGTH);
}
/* Hmm. Not sure about this bug workaround. Is this a rev B thing ? */
/* May get status 0x83 on i1d3_measure2 when there are no transitions ? */
/* If so, ignore the error. */
if (rv == inst_ok && cc == i1d3_measure2 && recv[1] == 0x02 && recv[0] == 0x83) {
int i;
for (i = 2; i < 14; i++) {
if (recv[i] != 0)
break;
}
if (i >= 14) { /* returned all zero's */
if (!nd) a1logd(p->log, 1, "i1d3_command: ignoring status byte = 0x%x\n",recv[0]);
recv[0] = 0x00; /* Fudge OK status */
}
}
/* The first byte returned seems to be a command result error code. */
if (rv == inst_ok && recv[0] != 0x00) {
if (!nd) a1logd(p->log, 1, "i1d3_command: status byte != 00 = 0x%x\n",recv[0]);
rv = i1d3_interp_code((inst *)p, I1D3_BAD_RET_STAT);
}
/* The second byte is usually the command code being echo'd back, but not always. */
/* ie., get i1d3_get_diff returns the status instead. */
if (rv == inst_ok) {
if (cc != i1d3_get_diff) {
if (recv[1] != cmd) {
if (!nd) a1logd(p->log, 1, "i1d3_command: major cmd not echo'd != 0x%02x = 0x%02x\n",
cmd,recv[1]);
rv = i1d3_interp_code((inst *)p, I1D3_BAD_RET_CMD);
}
} else { /* i1d3_get_diff as special case */
int i;
for (i = 2; i < 64; i++) {
if (recv[i] != 0x00) {
if (!nd) a1logd(p->log, 1, "i1d3_command: i1d3_get_diff not zero filled\n");
rv = i1d3_interp_code((inst *)p, I1D3_BAD_RET_CMD);
break;
}
}
}
}
if (!nd) {
if (cc == i1d3_lockchal)
a1logd(p->log, 4, "i1d3_command: got '%s' ICOM err 0x%x\n",icoms_tohex(recv, 64),ua);
else
a1logd(p->log, 4, "i1d3_command: got '%s' ICOM err 0x%x\n",icoms_tohex(recv, 14),ua);
}
if (rv != inst_ok) {
/* Flush any extra response, in case responses are out of sync */
if (ishid) {
p->icom->hid_read(p->icom, recv, 64, &rbytes, 0.2);
} else {
p->icom->usb_read(p->icom, NULL, 0x81, recv, 64, &rbytes, 0.2);
}
}
amutex_unlock(p->lock);
return rv;
}
/* Byte to int conversion. Most things seem to be little endian... */
/* Take an int, and convert it into a byte buffer */
static void int2buf(unsigned char *buf, int inv) {
buf[0] = (inv >> 0) & 0xff;
buf[1] = (inv >> 8) & 0xff;
buf[2] = (inv >> 16) & 0xff;
buf[3] = (inv >> 24) & 0xff;
}
/* Take a short, and convert it into a byte buffer */
static void short2buf(unsigned char *buf, int inv) {
buf[0] = (inv >> 0) & 0xff;
buf[1] = (inv >> 8) & 0xff;
}
/* Take a short, and convert it into a byte buffer (Big Endian) */
static void short2bufBE(unsigned char *buf, int inv) {
buf[0] = (inv >> 8) & 0xff;
buf[1] = (inv >> 0) & 0xff;
}
/* Take a 64 sized return buffer, and convert it to an ORD64 */
static ORD64 buf2ord64(unsigned char *buf) {
ORD64 val;
val = buf[7];
val = ((val << 8) + (0xff & buf[6]));
val = ((val << 8) + (0xff & buf[5]));
val = ((val << 8) + (0xff & buf[4]));
val = ((val << 8) + (0xff & buf[3]));
val = ((val << 8) + (0xff & buf[2]));
val = ((val << 8) + (0xff & buf[1]));
val = ((val << 8) + (0xff & buf[0]));
return val;
}
/* Take a word sized return buffer, and convert it to an unsigned int */
static unsigned int buf2uint(unsigned char *buf) {
unsigned int val;
val = buf[3];
val = ((val << 8) + (0xff & buf[2]));
val = ((val << 8) + (0xff & buf[1]));
val = ((val << 8) + (0xff & buf[0]));
return val;
}
/* Take a word sized return buffer, and convert it to an int */
static int buf2int(unsigned char *buf) {
int val;
val = buf[3];
val = ((val << 8) + (0xff & buf[2]));
val = ((val << 8) + (0xff & buf[1]));
val = ((val << 8) + (0xff & buf[0]));
return val;
}
/* Take a short sized return buffer, and convert it to an int */
static int buf2short(unsigned char *buf) {
int val;
val = buf[1];
val = ((val << 8) + (0xff & buf[0]));
return val;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Get Product name + Firmware version + Firmware Date string */
static inst_code
i1d3_get_info(
i1d3 *p, /* Object */
char *rv /* 64 byte buffer */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
if ((ev = i1d3_command(p, i1d3_getinfo, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
strncpy((char *)rv, (char *)fromdev + 2, 63);
a1logd(p->log, 3, "i1d3_get_info: got '%s'\n",rv);
return inst_ok;
}
/* Check the status. 0 = OK, 1 = BAD */
/* Not sure what sort of status this is. The result changes some */
/* other command parameter treatment. Could it be somthing like */
/* "factory calibrated" status ? */
static inst_code
i1d3_check_status(
i1d3 *p, /* Object */
int *stat /* Status - 0 if OK, 1 if not OK */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
if ((ev = i1d3_command(p, i1d3_status, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
*stat = 1; /* Bad */
if (fromdev[2] != 0 || (buf2short(fromdev + 3) >= 5))
*stat = 0; /* OK */
a1logd(p->log, 3, "i1d3_check_status: got %s\n",*stat == 0 ? "OK" : "Bad");
return inst_ok;
}
/* Get Product name */
static inst_code
i1d3_get_prodname(
i1d3 *p, /* Object */
char *rv /* 32 byte buffer */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
if ((ev = i1d3_command(p, i1d3_prodname, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
strncpy((char *)rv, (char *)fromdev + 2, 31);
a1logd(p->log, 3, "i1d3_get_prodname: got '%s'\n",rv);
return inst_ok;
}
/* Get Product type number */
static inst_code
i1d3_get_prodtype(
i1d3 *p, /* Object */
int *stat /* 16 bit version number */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
if ((ev = i1d3_command(p, i1d3_prodtype, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
*stat = buf2short(fromdev + 3);
a1logd(p->log, 3, "i1d3_get_prodtype: got 0x%x\n",*stat);
return inst_ok;
}
/* Get firmware version */
static inst_code
i1d3_get_firmver(
i1d3 *p, /* Object */
char *rv /* 32 byte buffer */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
if ((ev = i1d3_command(p, i1d3_firmver, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
strncpy((char *)rv, (char *)fromdev + 2, 31);
a1logd(p->log, 3, "i1d3_get_firmver: got '%s'\n",rv);
return inst_ok;
}
/* Get firmware date name */
static inst_code
i1d3_get_firmdate(
i1d3 *p, /* Object */
char *rv /* 32 byte buffer */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
if ((ev = i1d3_command(p, i1d3_firmdate, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
strncpy((char *)rv, (char *)fromdev + 2, 31);
a1logd(p->log, 3, "i1d3_get_firmdate: got '%s'\n",rv);
return inst_ok;
}
/* Check the lock status */
static inst_code
i1d3_lock_status(
i1d3 *p, /* Object */
int *stat /* Status - 0 if Unlocked, 1 if locked */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
if ((ev = i1d3_command(p, i1d3_locked, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
*stat = 1; /* Locked */
if (fromdev[2] != 0 || fromdev[3] == 0)
*stat = 0; /* Not Locked */
a1logd(p->log, 3, "i1d3_lock_status: got %s\n",*stat == 1 ? "Locked" : "Unlocked");
return inst_ok;
}
static void create_unlock_response(unsigned int *k, unsigned char *c, unsigned char *r);
/* Unlock the device */
static inst_code
i1d3_unlock(
i1d3 *p /* Object */
) {
unsigned char todev[64];
unsigned char fromdev[64];
struct {
char *pname; /* Product name */
unsigned int key[2]; /* Unlock code */
i1d3_dtype dtype; /* Base type enumerator */
i1d3_dtype stype; /* Sub type enumerator */
} codes[] = {
{ "i1Display3 ", { 0xe9622e9f, 0x8d63e133 }, i1d3_disppro, i1d3_disppro },
{ "Colormunki Display ", { 0xe01e6e0a, 0x257462de }, i1d3_munkdisp, i1d3_munkdisp },
{ "i1Display3 ", { 0xcaa62b2c, 0x30815b61 }, i1d3_disppro, i1d3_oem },
{ "i1Display3 ", { 0xa9119479, 0x5b168761 }, i1d3_disppro, i1d3_nec_ssp },
{ "i1Display3 ", { 0x160eb6ae, 0x14440e70 }, i1d3_disppro, i1d3_quato_sh3 },
{ "i1Display3 ", { 0x291e41d7, 0x51937bdd }, i1d3_disppro, i1d3_hp_dreamc },
{ "i1Display3 ", { 0xc9bfafe0, 0x02871166 }, i1d3_disppro, i1d3_sc_c6 },
{ NULL }
};
inst_code ev;
int ix, nix;
a1logd(p->log, 2, "i1d3_unlock: called\n");
/* Count the keys */
for (nix = 0;;nix++) {
if (codes[nix].pname == NULL)
break;
}
/* Until we give up */
for (ix = 0;;ix++) {
/* If we've run out of unlock keys */
if (codes[ix].pname == NULL) {
a1logw(p->log, "i1d3: Unknown lock code. Please contact ArgyllCMS for help\n");
return i1d3_interp_code((inst *)p, I1D3_UNKNOWN_UNLOCK);
}
// return i1d3_interp_code((inst *)p, I1D3_UNLOCK_FAIL);
/* Skip any keys that don't match the product name */
if (strcmp(p->prod_name, codes[ix].pname) != 0) {
continue;
}
// a1logd(p->log, 3, "i1d3_unlock: Trying unlock key 0x%08x 0x%08x\n",
// codes[ix].key[0], codes[ix].key[1]);
a1logd(p->log, 3, "i1d3_unlock: Trying unlock key %d/%d\n", ix+1, nix);
p->dtype = codes[ix].dtype;
p->stype = codes[ix].stype;
/* Attempt unlock */
memset(todev, 0, 64);
memset(fromdev, 0, 64);
/* Get a challenge */
if ((ev = i1d3_command(p, i1d3_lockchal, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
/* Convert challenge to response */
create_unlock_response(codes[ix].key, fromdev, todev);
/* Send the response */
if ((ev = i1d3_command(p, i1d3_lockresp, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
if (fromdev[2] == 0x77) { /* Sucess */
break;
}
a1logd(p->log, 3, "i1d3_unlock: Trying next unlock key\n");
/* Try the next key */
}
return inst_ok;
}
/* Get the ambient diffuser position */
static inst_code
i1d3_get_diffpos(
i1d3 *p, /* Object */
int *pos, /* 0 = display, 1 = ambient */
int nd /* nz = no debug message */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
if ((ev = i1d3_command(p, i1d3_get_diff, todev, fromdev, 1.0, nd)) != inst_ok)
return ev;
*pos = fromdev[1];
if (nd == 0)
a1logd(p->log, 3, "i1d3_get_diffpos: got %d\n",*pos);
return inst_ok;
}
/* Read bytes from the internal EEPROM */
static inst_code
i1d3_read_internal_eeprom(
i1d3 *p, /* Object */
int addr, /* address, 0 .. 255 */
int len, /* length, 0 .. 255 */
unsigned char *bytes /* return bytes here */
) {
inst_code ev;
unsigned char todev[64];
unsigned char fromdev[64];
int ll;
if (addr < 0 || addr > 255)
return i1d3_interp_code((inst *)p, I1D3_BAD_MEM_ADDRESS);
if (len < 0 || (addr + len) > 256)
return i1d3_interp_code((inst *)p, I1D3_BAD_MEM_LENGTH);
memset(todev, 0, 64);
memset(fromdev, 0, 64);
/* Bread read up into 60 bytes packets */
for (; len > 0; addr += ll, bytes += ll, len -= ll) {
ll = len;
if (ll > 60)
ll = 60;
/* OEM driver retries several times after a 10msec sleep on failure. */
/* Can a failure actually happen though ? */
todev[1] = (unsigned char)addr;
todev[2] = (unsigned char)ll;
if ((ev = i1d3_command(p, i1d3_readintee, todev, fromdev, 1.0, 0)) != inst_ok) {
return ev;
}
memmove(bytes, fromdev + 4, ll);
}
return inst_ok;
}
/* Read bytes from the external EEPROM */
static inst_code
i1d3_read_external_eeprom(
i1d3 *p, /* Object */
int addr, /* address, 0 .. 8191 */
int len, /* length, 0 .. 8192 */
unsigned char *bytes /* return bytes here */
) {
inst_code ev;
unsigned char todev[64];
unsigned char fromdev[64];
int ll;
int sdebug;
if (addr < 0 || addr > 8191)
return i1d3_interp_code((inst *)p, I1D3_BAD_MEM_ADDRESS);
if (len < 0 || (addr + len) > 8192)
return i1d3_interp_code((inst *)p, I1D3_BAD_MEM_LENGTH);
memset(todev, 0, 64);
memset(fromdev, 0, 64);
/* Bread read up into 59 bytes packets */
sdebug = p->log->debug;
p->log->debug = p->log->debug >= 2 ? p->log->debug - 2 : 0; /* Supress command traces */
for (; len > 0; addr += ll, bytes += ll, len -= ll) {
ll = len;
if (ll > 59)
ll = 59;
/* OEM driver retries several times after a 10msec sleep on failure. */
/* Can a failure actually happen though ? */
short2bufBE(todev + 1, addr);
todev[3] = (unsigned char)ll;
if ((ev = i1d3_command(p, i1d3_readextee, todev, fromdev, 1.0, 0)) != inst_ok) {
p->log->debug = sdebug;
return ev;
}
memmove(bytes, fromdev + 5, ll);
}
p->log->debug = sdebug;
return inst_ok;
}
/* Take a raw measurement using a given integration time. */
/* The measurent is the count of (both) edges from the L2V */
/* over the integration time */
static inst_code
i1d3_freq_measure(
i1d3 *p, /* Object */
double *inttime, /* Integration time in seconds. (Return clock rounded) */
double rgb[3] /* Return the RGB count values */
) {
int intclks;
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
if (*inttime > 20.0) /* Hmm */
*inttime = 20.0;
/* Max = 357.9 seconds ? */
intclks = (int)(*inttime * p->clk_freq + 0.5);
*inttime = (double)intclks / p->clk_freq;
int2buf(todev + 1, intclks);
todev[23] = 0; /* Unknown parameter, always 0 */
if ((ev = i1d3_command(p, i1d3_measure1, todev, fromdev, I1D3_MEAS_TIMEOUT, 0)) != inst_ok)
return ev;
rgb[0] = (double)buf2uint(fromdev + 2);
rgb[1] = (double)buf2uint(fromdev + 6);
rgb[2] = (double)buf2uint(fromdev + 10);
return inst_ok;
}
/* Take a raw measurement that returns the number of clocks */
/* between and initial edge and edgec[] subsequent edges of the L2F. */
/* The edge count must be between 1 and 65535 inclusive. */
/* Both edges are counted. It's advisable to use an even edgec[], */
/* because the L2F output may not be symetric. */
/* If there are no edges within 10 seconds, return a count of 0 */
static inst_code
i1d3_period_measure(
i1d3 *p, /* Object */
int edgec[3], /* Measurement edge count for each channel */
int mask, /* Bit mask to enable channels */
double rgb[3] /* Return the RGB values */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
short2buf(todev + 1, edgec[0]);
short2buf(todev + 3, edgec[1]);
short2buf(todev + 5, edgec[2]);
todev[7] = (unsigned char)mask;
todev[8] = 0; /* Unknown parameter, always 0 */
if ((ev = i1d3_command(p, i1d3_measure2, todev, fromdev, I1D3_MEAS_TIMEOUT, 0)) != inst_ok)
return ev;
rgb[0] = (double)buf2uint(fromdev + 2);
rgb[1] = (double)buf2uint(fromdev + 6);
rgb[2] = (double)buf2uint(fromdev + 10);
return inst_ok;
}
typedef enum {
i1d3_flash = 1,
i1d3_fade = 3,
} i1d3_ledmode;
static inst_code
i1d3_set_LEDs(
i1d3 *p, /* Object */
i1d3_ledmode mode, /* 1 = off & on, 3 = off & fade on */
double offtime, /* Off time */
double ontime, /* On time. Fade is included in this */
int count /* Pulse count. 0x80 = infinity ? */
) {
unsigned char todev[64];
unsigned char fromdev[64];
inst_code ev;
double mul1, mul2;
int ftime, ntime;
memset(todev, 0, 64);
memset(fromdev, 0, 64);
mul1 = p->clk_freq/(1 << 23);
mul2 = p->clk_freq/(1 << 19);
ftime = (int)(0.5 + offtime * mul2);
if (ftime < 0)
ftime = 0;
else if (ftime > 255)
ftime = 255;
if (mode == 1)
ntime = (int)(0.5 + ontime * mul2);
else if (mode == 3)
ntime = (int)(0.5 + ontime * mul1);
else
return i1d3_interp_code((inst *)p, I1D3_BAD_LED_MODE);
if (ntime < 0)
ntime = 0;
else if (ntime > 255)
ntime = 255;
if (count < 0)
count = 0;
else if (count > 0x80)
count = 0x80;
todev[1] = (unsigned char)mode;
todev[2] = (unsigned char)ftime;
todev[3] = (unsigned char)ntime;
todev[4] = (unsigned char)count;
if ((ev = i1d3_command(p, i1d3_setled, todev, fromdev, 1.0, 0)) != inst_ok)
return ev;
return inst_ok;
}
/* - - - - - - - - - - - - - - - - - - - - - - */
/*
determining the refresh rate for a refresh type display;
Read 1300 .5 msec samples as fast as possible, and
timestamp them.
Interpolate values up to .05 msec regular samples.
Do an auto-correlation on the samples.
Pick the longest peak between 10 and 40Hz as the best sample period,
and halve this to use as the quantization value (ie. make
it lie between 20 and 80 Hz).
If there was no error, return refresh quanization period it.
If there is no aparent refresh, or the refresh rate is not determinable,
return a period of 0.0 and inst_ok;
To break up the USB synchronization, the integration time
is randomized slightly.
*/
#ifndef PSRAND32L
# define PSRAND32L(S) ((S) * 1664525L + 1013904223L)
#endif
#undef FREQ_SLOW_PRECISE /* [und] Interpolate then autocorrelate, else autc & filter */
#define NFSAMPS 1300 /* Number of samples to read */
#define NFMXTIME 6.0 /* Maximum time to take (2000 == 6) */
#define PBPMS 20 /* bins per msec */
#define PERMIN ((1000 * PBPMS)/40) /* 40 Hz */
#define PERMAX ((1000 * PBPMS)/5) /* 5 Hz*/
#define NPER (PERMAX - PERMIN + 1)
//#define PWIDTH (3 * PBPMS) /* 3 msec bin spread to look for peak in */
#define PWIDTH (8 * PBPMS) /* 3 msec bin spread to look for peak in */
#define MAXPKS 20 /* Number of peaks to find */
/* Set refperiod, refrate if possible */
static inst_code
i1d3_imp_measure_refresh(
i1d3 *p, /* Object */
double *prefrate, /* Return value, 0.0 if none */
double *ppval /* Return period value, 0.0 if none */
) {
inst_code ev;
int i, j, k;
double ucalf = 1.0; /* usec_time calibration factor */
double inttimel = 0.0003;
double inttimeh = 0.0040;
double sutime, putime, cutime, eutime;
static unsigned int randn = 0x12345678;
struct {
double itime; /* Integration time */
double sec;
double rgb[3];
} samp[NFSAMPS];
int nfsamps; /* Actual samples read */
double maxt; /* Time range */
double rms[3]; /* RMS value of each channel */
double trms; /* Total RMS */
int nbins;
double *bins[3]; /* PBPMS sample bins */
double tcorr[NPER]; /* Temp for initial autocorrelation */
double corr[NPER]; /* Filtered correlation for each period value */
double mincv, maxcv; /* Max and min correlation values */
double crange; /* Correlation range */
double peaks[MAXPKS]; /* Each peak from longest to shortest */
int npeaks = 0; /* Number of peaks */
double pval; /* Period value */
int isdeb;
int isth;
if (prefrate != NULL)
*prefrate = 0.0;
if (ppval != NULL)
*ppval = 0.0;
if (usec_time() < 0.0) {
a1loge(p->log, inst_internal_error, "i1d3_measure_refresh: No high resolution timers\n");
return inst_internal_error;
}
/* Turn debug and thread off so that they doesn't intefere with measurement timing */
isdeb = p->log->debug;
p->icom->log->debug = 0;
isth = p->th_en;
p->th_en = 0;