1 
2 /* Abstract instrument class implemenation */
3 
4 /*
5  * Argyll Color Correction System
6  *
7  * Author: Graeme W. Gill
8  * Date:   10/3/2001
9  *
10  * Copyright 2001 - 2013 Graeme W. Gill
11  * All rights reserved.
12  *
13  * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
14  * see the License2.txt file for licencing details.
15  */
16 
17 /*
18    If you make use of the instrument driver code here, please note
19    that it is the author(s) of the code who take responsibility
20    for its operation. Any problems or queries regarding driving
21    instruments with the Argyll drivers, should be directed to
22    the Argyll's author(s), and not to any other party.
23 
24    If there is some instrument feature or function that you
25    would like supported here, it is recommended that you
26    contact Argyll's author(s) first, rather than attempt to
27    modify the software yourself, if you don't have firm knowledge
28    of the instrument communicate protocols. There is a chance
29    that an instrument could be damaged by an incautious command
30    sequence, and the instrument companies generally cannot and
31    will not support developers that they have not qualified
32    and agreed to support.
33  */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <ctype.h>
39 #include <string.h>
40 #include <time.h>
41 #ifndef SALONEINSTLIB
42 #include "copyright.h"
43 #include "aconfig.h"
44 #else
45 #include "sa_config.h"
46 #endif /* !SALONEINSTLIB */
47 #include "numsup.h"
48 #include "xspect.h"
49 #include "conv.h"
50 
51 #include "insttypes.h"
52 #include "icoms.h"
53 #include "inst.h"
54 #include "rspec.h"
55 #include "insttypeinst.h"
56 #include "ccmx.h"
57 #include "ccss.h"
58 #include "sort.h"
59 
60 #if defined(ENABLE_FAST_SERIAL)
61 instType fast_ser_inst_type(icoms *p, int tryhard,
62        inst_code (*uicallback)(void *cntx, inst_ui_purp purp), void *cntx);
63 # if defined(ENABLE_SERIAL)
64 static instType ser_inst_type(icoms *p,
65        inst_code (*uicallback)(void *cntx, inst_ui_purp purp), void *cntx);
66 # endif /* ENABLE_SERIAL */
67 #endif /* ENABLE_FAST_SERIAL */
68 
69 /* ------------------------------------ */
70 /* Default methods for instrument class */
71 
72 /* Establish communications at the indicated baud rate. */
73 /* Timout in to seconds, and return non-zero error code */
init_coms(inst * p,baud_rate br,flow_control fc,double tout)74 static inst_code init_coms(
75 inst *p,
76 baud_rate br,		/* Baud rate */
77 flow_control fc,	/* Flow control */
78 double tout) {		/* Timeout */
79 	return inst_unsupported;
80 }
81 
82 /* Initialise or re-initialise the INST */
83 /* return non-zero on an error, with inst error code */
init_inst(inst * p)84 static inst_code init_inst(
85 inst *p) {
86 	return inst_unsupported;
87 }
88 
89 /* Return the instrument type */
get_itype(inst * p)90 static instType get_itype(inst *p) {
91 	if (p != NULL)
92 		return p->itype;
93 	return instUnknown;
94 }
95 
96 /* Return the instrument serial number. */
97 /* (This will be an empty string if there is no serial no) */
get_serial_no(inst * p)98 static char *get_serial_no(inst *p) {
99 	return "";
100 }
101 
102 /* Return the instrument mode capabilities */
capabilities(inst * p,inst_mode * cap1,inst2_capability * cap2,inst3_capability * cap3)103 static void capabilities(inst *p, inst_mode *cap1,
104                          inst2_capability *cap2, inst3_capability *cap3) {
105 	if (cap1 != NULL)
106 		*cap1 = inst_mode_none;
107 	if (cap2 != NULL)
108 		*cap2 = inst2_none;
109 	if (cap3 != NULL)
110 		*cap3 = inst3_none;
111 }
112 
113 /* Return current or given configuration available measurement modes. */
114 /* This default routine assumed one confiration which will be */
115 /* all the measurement modes returned by capabilities(). */
meas_config(inst * p,inst_mode * mmodes,inst_cal_cond * cconds,int * conf_ix)116 static inst_code meas_config(inst *p,
117 inst_mode *mmodes,		/* Return all the valid measurement modes */
118 						/* for the current configuration */
119 inst_cal_cond *cconds,	/* Return the valid calibration conditions */
120 int *conf_ix) {			/* Request mode for given configuration, and */
121 						/* return the index of the configuration returned */
122 	if (mmodes != NULL) {
123 		*mmodes = inst_mode_none;
124 
125 		/* Available measurement modes is current capabilities */
126 		p->capabilities(p, mmodes, NULL, NULL);
127 	}
128 
129 	if (cconds != NULL)
130 		*cconds = inst_calc_unknown;
131 
132 	if (conf_ix != NULL)
133 		*conf_ix = 0;
134 
135 	return inst_ok;
136 }
137 
138 /* Check the device measurement mode */
check_mode(inst * p,inst_mode m)139 static inst_code check_mode(
140 inst *p,
141 inst_mode m) {		/* Requested mode */
142 	return inst_unsupported;
143 }
144 
145 /* Set the device measurement mode */
set_mode(inst * p,inst_mode m)146 static inst_code set_mode(
147 inst *p,
148 inst_mode m) {		/* Requested mode */
149 	return inst_unsupported;
150 }
151 
152 /* Return array of display type selectors */
get_disptypesel(inst * pp,int * pnsels,inst_disptypesel ** psels,int allconfig,int recreate)153 static inst_code get_disptypesel(
154 inst *pp,
155 int *pnsels,				/* Return number of display types */
156 inst_disptypesel **psels,	/* Return the array of display types */
157 int allconfig,				/* nz to return list for all configs, not just current. */
158 int recreate				/* nz to re-check for new ccmx & ccss files */
159 ) {
160 	return inst_unsupported;
161 }
162 
163 /* Set the display type */
set_disptype(inst * pp,int ix)164 static inst_code set_disptype(
165 inst *pp,
166 int ix						/* index into the inst_disptypesel[] */
167 ) {
168 	return inst_unsupported;
169 }
170 
171 /* Get the disptech and other corresponding info for the current */
172 /* selected display type. Returns disptype_unknown by default. */
173 /* Because refrmode can be overridden, it may not match the refrmode */
174 /* of the dtech. (Pointers may be NULL if not needed) */
get_disptechi(inst * pp,disptech * dtech,int * refrmode,int * cbid)175 static inst_code get_disptechi(
176 inst *pp,
177 disptech *dtech,
178 int *refrmode,
179 int *cbid) {
180 	if (dtech != NULL)
181 		*dtech = disptech_unknown;
182 	if (refrmode != NULL)
183 		*refrmode = disptech_get_id(disptech_unknown)->refr;
184 	if (cbid != NULL)
185 		*cbid = 0;
186 	return inst_ok;
187 }
188 
189 /* Get a status or set or get an option (default implementation) */
inst_get_set_opt_def(inst * p,inst_opt_type m,va_list args)190 inst_code inst_get_set_opt_def(
191 inst *p,
192 inst_opt_type m,	/* Requested option type */
193 va_list args) {		/* Option parameters */
194 
195 	return inst_unsupported;
196 }
197 
198 /* Get a status or set or get an option */
get_set_opt(inst * p,inst_opt_type m,...)199 static inst_code get_set_opt(
200 inst *p,
201 inst_opt_type m,	/* Requested status type */
202 ...) {				/* Status parameters */
203 	inst_code rv;
204 	va_list args;
205 
206 	va_start(args, m);
207 	rv = inst_get_set_opt_def(p, m, args);	/* Call the above */
208 	va_end(args);
209 
210 	return rv;
211 }
212 
213 /* Read a full test chart composed of multiple sheets */
214 /* DOESN'T use the trigger mode */
215 /* Return the inst error code */
read_chart(inst * p,int npatch,int pich,int sip,int * pis,int chid,ipatch * vals)216 static inst_code read_chart(
217 inst *p,
218 int npatch,			/* Total patches/values in chart */
219 int pich,			/* Passes (rows) in chart */
220 int sip,			/* Steps in each pass (patches in each row) */
221 int *pis,			/* Passes in each strip (rows in each sheet) */
222 int chid,			/* Chart ID number */
223 ipatch *vals) {		/* Pointer to array of values */
224 
225 	return inst_unsupported;
226 }
227 
228 /* For an xy instrument, release the paper */
229 /* (if cap has inst_xy_holdrel) */
230 /* Return the inst error code */
xy_sheet_release(inst * p)231 static inst_code xy_sheet_release(
232 inst *p) {
233 	return inst_unsupported;
234 }
235 
236 /* For an xy instrument, hold the paper */
237 /* (if cap has inst_xy_holdrel) */
238 /* Return the inst error code */
xy_sheet_hold(inst * p)239 static inst_code xy_sheet_hold(
240 inst *p) {
241 	return inst_unsupported;
242 }
243 
244 /* For an xy instrument, allow the user to locate a point */
245 /* (if cap has inst_xy_locate) */
246 /* Return the inst error code */
xy_locate_start(inst * p)247 static inst_code xy_locate_start(
248 inst *p) {
249 	return inst_unsupported;
250 }
251 
252 /* For an xy instrument, read back the location */
253 /* (if cap has inst_xy_locate) */
254 /* Return the inst error code */
xy_get_location(inst * p,double * x,double * y)255 static inst_code xy_get_location(
256 inst *p,
257 double *x, double *y) {
258 	return inst_unsupported;
259 }
260 
261 /* For an xy instrument, end allowing the user to locate a point */
262 /* (if cap has inst_xy_locate) */
263 /* Return the inst error code */
xy_locate_end(inst * p)264 static inst_code xy_locate_end(
265 inst *p) {
266 	return inst_unsupported;
267 }
268 
269 /* For an xy instrument, move the measurement point */
270 /* (if cap has inst_xy_position) */
271 /* Return the inst error code */
xy_position(inst * p,int measure,double x,double y)272 static inst_code xy_position(
273 inst *p,
274 int measure,	/* nz for measure point, z for locate point */
275 double x, double y) {
276 	return inst_unsupported;
277 }
278 
279 /* For an xy instrument, try and clear the table after an abort */
280 /* Return the inst error code */
xy_clear(inst * p)281 static inst_code xy_clear(
282 inst *p) {
283 	return inst_unsupported;
284 }
285 
286 /* Read a sheet full of patches using xy mode */
287 /* DOESN'T use the trigger mode */
288 /* Return the inst error code */
read_xy(inst * p,int pis,int sip,int npatch,char * pname,char * sname,double ox,double oy,double ax,double ay,double aax,double aay,double px,double py,ipatch * vals)289 static inst_code read_xy(
290 inst *p,
291 int pis,			/* Passes in strips (letters in sheet) */
292 int sip,			/* Steps in pass (numbers in sheet) */
293 int npatch,			/* Total patches in strip (skip in last pass) */
294 char *pname,		/* Starting pass name (' A' to 'ZZ') */
295 char *sname,		/* Starting step name (' 1' to '99') */
296 double ox, double oy,	/* Origin of first patch */
297 double ax, double ay,	/* pass increment */
298 double aax, double aay,	/* pass offset for odd patches */
299 double px, double py,	/* step/patch increment */
300 ipatch *vals) {		/* Pointer to array of values */
301 	return inst_unsupported;
302 }
303 
304 /* Read a set of strips (applicable to strip reader) */
305 /* Obeys the trigger mode set */
306 /* Return the inst error code */
read_strip(inst * p,char * name,int npatch,char * pname,int sguide,double pwid,double gwid,double twid,ipatch * vals)307 static inst_code read_strip(
308 inst *p,
309 char *name,			/* Strip name (up to 7 chars) */
310 int npatch,			/* Number of patches in each pass */
311 char *pname,		/* Pass name (3 chars) */
312 int sguide,			/* Guide number (decrements by 5) */
313 double pwid,		/* Patch width in mm (For DTP20/DTP41) */
314 double gwid,		/* Gap width in mm  (For DTP20/DTP41) */
315 double twid,		/* Trailer width in mm  (For DTP41T) */
316 ipatch *vals) {		/* Pointer to array of values */
317 	return inst_unsupported;
318 }
319 
320 /* Read a single sample (applicable to spot instruments) */
321 /* Obeys the trigger mode set */
322 /* Return the inst error code */
read_sample(inst * p,char * name,ipatch * val,instClamping clamp)323 static inst_code read_sample(
324 inst *p,
325 char *name,		 			/* Patch identifier (up to 7 chars) */
326 ipatch *val,				/* Pointer to value to be returned */
327 instClamping clamp) {		/* NZ to clamp XYZ to be +ve */
328 	return inst_unsupported;
329 }
330 
331 /* Measure the emissive refresh rate in Hz. */
read_refrate(inst * p,double * ref_rate)332 static inst_code read_refrate(
333 inst *p,
334 double *ref_rate) {
335 	if (ref_rate != NULL)
336 		*ref_rate = 0.0;
337 	return inst_unsupported;
338 }
339 
340 /* Determine if a calibration is needed. Returns inst_calt_none if not */
341 /* or unknown, or a mask of the needed calibrations. */
342 /* Default implementation - call through to get_n_a_cals() */
needs_calibration(inst * p)343 static inst_cal_type needs_calibration(
344 inst *p) {
345 	inst_cal_type n_cals;
346 
347 	if (p->get_n_a_cals(p, &n_cals, NULL) != inst_ok)
348 		return inst_calt_none;
349 
350 	return n_cals;
351 }
352 
353 /* Return combined mask of  of needed or available inst_cal_type's */
354 /* for the current mode. */
inst_get_n_a_cals(struct _inst * p,inst_cal_type * n_cals,inst_cal_type * a_cals)355 inst_code inst_get_n_a_cals(
356 struct _inst *p,
357 inst_cal_type *n_cals,
358 inst_cal_type *a_cals) {
359 
360 	if (n_cals != NULL)
361 		*n_cals = inst_calt_none;
362 
363 	if (a_cals != NULL)
364 		*a_cals = inst_calt_none;
365 
366 	return inst_ok;
367 }
368 
369 /* Request an instrument calibration. */
370 /* DOESN'T use the trigger mode */
371 /* Return inst_unsupported if calibration type is not appropriate. */
calibrate(inst * p,inst_cal_type * calt,inst_cal_cond * calc,inst_calc_id_type * idtype,char id[CALIDLEN])372 static inst_code calibrate(
373 inst *p,
374 inst_cal_type *calt,	/* Calibration type to do/remaining */
375 inst_cal_cond *calc,	/* Current condition/desired condition */
376 inst_calc_id_type *idtype,	/* Condition identifier type */
377 char id[CALIDLEN]) {	/* Condition identifier (ie. white reference ID, filter ID) */
378 	return inst_unsupported;
379 }
380 
381 /* Return a description of the calibration type */
calt2str(inst_cal_type calt)382 char *calt2str(inst_cal_type calt) {
383 	calt &= inst_calt_all_mask;
384 
385 	if (calt & inst_calt_all)
386 		return "All";
387 	if (calt & inst_calt_needed)
388 		return "Needed";
389 	if (calt & inst_calt_available)
390 		return "Needed";
391 	if (calt & inst_calt_wavelength)
392 		return "Wavelength";
393 	if (calt & inst_calt_ref_white)
394 		return "Reflective White or Emissive Dark";
395 	if (calt & inst_calt_ref_dark)
396 		return "Reflective Light Trap (Black)";
397 	if (calt & inst_calt_ref_dark_gl)
398 		return "Reflective Gloss";
399 	if (calt & inst_calt_emis_offset)
400 		return "Emissive Offset";
401 	if (calt & inst_calt_emis_ratio)
402 		return "Emissive Ratio";
403 	if (calt & inst_calt_em_dark)
404 		return "Emissive Dark";
405 	if (calt & inst_calt_trans_white)
406 		return "Transmissive White";
407 	if (calt & inst_calt_trans_vwhite)
408 		return "Transmissive Variable White";
409 	if (calt & inst_calt_trans_dark)
410 		return "Transmissive Dark";
411 	if (calt & inst_calt_emis_int_time)
412 		return "Emissive Integration Time";
413 	if (calt & inst_calt_ref_freq)
414 		return "Display Refresh Rate";
415 
416 	return "None or Unknown";
417 }
418 
419 /* Measure a display update delay. It is assumed that a */
420 /* black to white change has been made to the displayed color, */
421 /* and this will measure the time it took for the update to */
422 /* (It is assumed that white_change() will be called at the time the patch */
423 /* changes color.) */
424 /* be noticed by the instrument, up to 1.0 seconds. */
425 /* inst_misread will be returned on failure to find a transition. */
meas_delay(inst * p,int * pdispmsec,int * pinstmsec)426 static inst_code meas_delay(
427 inst *p,
428 int *pdispmsec,
429 int *pinstmsec) {
430 	return inst_unsupported;
431 }
432 
433 /* Call used by other thread to timestamp the transition. */
white_change(struct _inst * p,int init)434 static inst_code white_change(
435 struct _inst *p, int init) {
436 	return inst_unsupported;
437 }
438 																				\
439 /* Return the last calibrated refresh rate in Hz. Returns: */
440 /* inst_unsupported - if this instrument doesn't suport a refresh mode */
441 /*                    or is unable to retrieve the refresh rate */
442 /* inst_needs_cal   - if the refresh rate value is not valid */
443 /* inst_misread     - if no refresh rate could be determined */
444 /* inst_ok          - on returning a valid reading */
get_refr_rate(struct _inst * p,double * ref_rate)445 static inst_code get_refr_rate(
446 struct _inst *p,
447 double *ref_rate) {
448 	if (ref_rate != NULL)
449 		*ref_rate = 0.0;
450 	return inst_unsupported;
451 }
452 
453 /* Set the calibrated refresh rate in Hz. */
454 /* Set refresh rate to 0.0 to mark it as invalid */
455 /* Rates outside the range 5.0 to 150.0 Hz will return an error */
set_refr_rate(struct _inst * p,double ref_rate)456 static inst_code set_refr_rate(
457 struct _inst *p,
458 double ref_rate) {
459 	return inst_unsupported;
460 }
461 
462 /* Insert a compensation filter in the instrument readings */
463 /* This is typically needed if an adapter is being used, that alters */
464 /* the spectrum of the light reaching the instrument */
465 /* To remove the filter, pass NULL for the filter filename */
comp_filter(inst * p,char * filtername)466 static inst_code comp_filter(
467 inst *p,
468 char *filtername) {		/* File containing compensating filter */
469 	return inst_unsupported;
470 }
471 
472 /* Insert a colorimetric correction matrix in the instrument XYZ readings */
473 /* This is only valid for colorimetric instruments. */
474 /* To remove the matrix, pass NULL for the matrix */
col_cor_mat(struct _inst * p,disptech dtech,int cbid,double mtx[3][3])475 static inst_code col_cor_mat(
476 struct _inst *p,
477 disptech dtech,		/* Use disptech_unknown if not known */
478 int cbid,       	/* Calibration display type base ID needed, 1 if unknown */
479 double mtx[3][3]) {	/* XYZ matrix */
480 	return inst_unsupported;
481 }
482 
483 /* Use a Colorimeter Calibration Spectral Set to set the */
484 /* instrumen calibration. */
485 /* This is only valid for colorimetric instruments. */
486 /* To set calibration back to default, pass NULL for ccss. */
col_cal_spec_set(inst * pp,disptech dtech,xspect * sets,int no_sets)487 static inst_code col_cal_spec_set(
488 inst *pp,
489 disptech dtech,		/* Use disptech_unknown if not known */
490 xspect *sets,
491 int no_sets) {
492 	return inst_unsupported;
493 }
494 
495 /* Supply a user interaction callback function. */
set_uicallback(inst * pp,inst_code (* uicallback)(void * cntx,inst_ui_purp purp),void * cntx)496 static void set_uicallback(
497 inst *pp,
498 inst_code (*uicallback)(void *cntx, inst_ui_purp purp),
499 void *cntx)	 {
500 	pp->uicallback = uicallback;
501 	pp->uic_cntx = cntx;
502 }
503 
504 /* Supply an asynchronous event callback function. */
set_event_callback(inst * pp,void (* eventcallback)(void * cntx,inst_event_type event),void * cntx)505 static void set_event_callback(
506 inst *pp,
507 void (*eventcallback)(void *cntx, inst_event_type event),
508 void *cntx)	 {
509 	pp->eventcallback = eventcallback;
510 	pp->event_cntx = cntx;
511 }
512 
513 /* Generic inst error codes interpretation */
inst_interp_error(inst * p,inst_code ec)514 static char *inst_interp_error(inst *p, inst_code ec) {
515 	switch(ec & inst_mask) {
516 		case inst_ok:
517 			return "No error";
518 		case inst_notify:
519 			return "Notification";
520 		case inst_warning:
521 			return "Warning";
522 		case inst_no_coms:
523 			return "Internal error - communications needed but not established";
524 		case inst_no_init:
525 			return "Internal error - initialisation needed but not done";
526 		case inst_internal_error:
527 			return "Internal software error";
528 		case inst_coms_fail:
529 			return "Communications failure";
530 		case inst_unknown_model:
531 			return "Not expected instrument model";
532 		case inst_protocol_error:
533 			return "Communication protocol breakdown";
534 		case inst_user_abort:
535 			return "User hit Abort Key";
536 		case inst_user_trig:
537 			return "User hit Trigger Key";
538 		case inst_misread:
539 			return "Measurement misread";
540 		case inst_nonesaved:
541 			return "No saved data to read";
542 		case inst_nochmatch:
543 			return "Chart being read doesn't match chart expected";
544 		case inst_needs_cal:
545 			return "Instrument needs calibration";
546 		case inst_cal_setup:
547 			return "Instrument needs to be setup for calibration";
548 		case inst_unsupported:
549 			return "Unsupported function";
550 		case inst_unexpected_reply:
551 			return "Unexpected Reply";
552 		case inst_wrong_config:
553 			return "Wrong Sensor Position";
554 		case inst_wrong_setup:
555 			return "Wrong or conflicting setup";
556 		case inst_bad_parameter:
557 			return "Bad Parameter Value";
558 		case inst_hardware_fail:
559 			return "Hardware Failure";
560 		case inst_system_error:
561 			return "Operating System Error";
562 		case inst_other_error:
563 			return "Non-specific error";
564 	}
565 	return "Unknown inst error code";
566 }
567 
568 /* Instrument specific error codes interpretation */
interp_error(inst * p,int ec)569 static char *interp_error(inst *p, int ec) {
570 	return "interp_error is uninplemented in base class!";
571 }
572 
573 /* Return the last serial communication error code */
574 /* (This is used for deciding fallback/retry strategies) */
last_scomerr(inst * p)575 static int last_scomerr(inst *p) {
576 	return p->icom->lserr;
577 }
578 
579 /* Convert instrument specific inst_wrong_config error to inst_config enum */
config_enum(inst * p,int ec)580 static inst_config config_enum(inst *p, int ec) {
581 	return inst_conf_unknown;
582 }
583 
584 /* ---------------------------------------------- */
585 
586 /* Delete things set/done by new_inst() */
virtual_del(inst * p)587 static inst_code virtual_del(inst *p) {
588 
589 #if defined(UNIX_APPLE)
590 	osx_latencycritical_end();
591 #endif
592 
593 	return inst_ok;
594 }
595 
596 
597 /* Virtual constructor. */
598 /* Return NULL for unknown instrument, */
599 /* or serial instrument if nocoms == 0. */
new_inst(icompath * path,int nocoms,a1log * log,inst_code (* uicallback)(void * cntx,inst_ui_purp purp),void * cntx)600 extern inst *new_inst(
601 icompath *path,		/* Device path to instrument */
602 int nocoms,			/* Don't open if communications are needed to establish inst type */
603 a1log *log,			/* log to use */
604 inst_code (*uicallback)(void *cntx, inst_ui_purp purp),		/* optional uicallback */
605 void *cntx			/* Context for callback */
606 ) {
607 	instType itype = instUnknown;	/* Actual type */
608 	icoms *icom;
609 	inst *p = NULL;
610 
611 	if (path == NULL) {
612 		a1logd(log, 2, "new_inst: got NULL path\n");
613 		return NULL;
614 	}
615 
616 	a1logd(log, 2, "new_inst: called with path '%s' type '%s'\n",path->name,inst_sname(path->itype));
617 
618 	if ((icom = new_icoms(path, log)) == NULL) {
619 		a1logd(log, 2, "new_inst: new_icoms failed to open it\n");
620 		return NULL;
621 	}
622 
623 
624 
625 	/* Set instrument type from USB port, if not specified */
626 	itype = icom->itype;		/* Instrument type if its known from usb/hid */
627 
628 #if defined(ENABLE_FAST_SERIAL)
629 	if (itype == instUnknown && !nocoms && (icom->dctype & icomt_fastserial)) {
630 		itype = fast_ser_inst_type(icom, 1, uicallback, cntx);		/* Else type from serial */
631 		icom->dctype = (icom->dctype & ~icomt_cat_mask) | inst_category(itype);
632 		a1logd(log, 8, "new_inst: fast set '%s' dctype 0x%x\n",icom->name,icom->dctype);
633 	}
634 #endif /* ENABLE_FAST_SERIAL */
635 
636 #if defined(ENABLE_SERIAL)
637 	if (itype == instUnknown && !nocoms) {
638 		itype = ser_inst_type(icom, uicallback, cntx);		/* Else type from serial */
639 		icom->dctype = (icom->dctype & ~icomt_cat_mask) | inst_category(itype);
640 		a1logd(log, 8, "new_inst: set '%s' dctype 0x%x\n",icom->name,icom->dctype);
641 	}
642 #endif /* ENABLE_SERIAL */
643 
644 
645 #ifdef ENABLE_SERIAL
646 	if (itype == instDTP22)
647 		p = (inst *)new_dtp22(icom, itype);
648 	else if (itype == instDTP41)
649 		p = (inst *)new_dtp41(icom, itype);
650 	else if (itype == instDTP51)
651 		p = (inst *)new_dtp51(icom, itype);
652 	else if (itype == instDTP92)
653 		p = (inst *)new_dtp92(icom, itype);
654 	else if ((itype == instSpectrolino ) ||
655 			 (itype == instSpectroScan ) ||
656 			 (itype == instSpectroScanT))
657 		p = (inst *)new_ss(icom, itype);
658 /* NYI
659 	else if (itype == instSpectrocam)
660 		p = (inst *)new_spc(icom, itype);
661 */
662 #endif /* ENABLE_SERIAL */
663 
664 #ifdef ENABLE_FAST_SERIAL
665 	if (itype == instSpecbos1201
666 	 || itype == instSpecbos
667 	 || itype == instSpectraval)
668 		p = (inst *)new_specbos(icom, itype);
669 	if (itype == instKleinK10)
670 		p = (inst *)new_kleink10(icom, itype);
671 #endif /* ENABLE_SERIAL */
672 
673 #ifdef ENABLE_USB
674 	if (itype == instDTP94)
675 		p = (inst *)new_dtp92(icom, itype);
676 	else if (itype == instDTP20)
677 		p = (inst *)new_dtp20(icom, itype);
678 	else if (itype == instI1Disp1 ||
679 		    itype == instI1Disp2)
680 		p = (inst *)new_i1disp(icom, itype);
681 	else if (itype == instI1Disp3)
682 		p = (inst *)new_i1d3(icom, itype);
683 	else if (itype == instI1Monitor)
684 		p = (inst *)new_i1pro(icom, itype);
685 	else if ((itype == instI1Pro) ||
686 	         (itype == instI1Pro2))
687 		p = (inst *)new_i1pro(icom, itype);
688 	else if (itype == instColorMunki)
689 		p = (inst *)new_munki(icom, itype);
690 	else if (itype == instSpyder1)
691 		p = (inst *)new_spyd2(icom, itype);
692 	else if (itype == instSpyder2)
693 		p = (inst *)new_spyd2(icom, itype);
694 	else if (itype == instSpyder3)
695 		p = (inst *)new_spyd2(icom, itype);
696 	else if (itype == instSpyder4)
697 		p = (inst *)new_spyd2(icom, itype);
698 	else if (itype == instSpyder5)
699 		p = (inst *)new_spyd2(icom, itype);
700 	else if (itype == instEX1)
701 		p = (inst *)new_ex1(icom, itype);
702 	if (itype == instHuey)
703 		p = (inst *)new_huey(icom, itype);
704 	else if (itype == instSmile)
705 		p = (inst *)new_i1disp(icom, itype);
706 #ifdef ENABLE_FAST_SERIAL
707 	else if (itype == instSMCube)
708 		p = (inst *)new_smcube(icom, itype);
709 #endif
710 	else if (itype == instHCFR)
711 		p = (inst *)new_hcfr(icom, itype);
712 	else if (itype == instColorHug
713 	      || itype == instColorHug2)
714 		p = (inst *)new_colorhug(icom, itype);
715 #endif /* ENABLE_USB */
716 
717 
718 
719 	/* Nothing matched */
720 	if (p == NULL) {
721 		a1logd(log, 2, "new_inst: instrument type not recognised\n");
722 		icom->del(icom);
723 		return NULL;
724 	}
725 
726 	p->vdel = virtual_del;
727 
728 	/* Add default methods if constructor did not supply them */
729 	if (p->init_coms == NULL)
730 		p->init_coms = init_coms;
731 	if (p->init_inst == NULL)
732 		p->init_inst = init_inst;
733 	if (p->get_itype == NULL)
734 		p->get_itype = get_itype;
735 	if (p->get_serial_no == NULL)
736 		p->get_serial_no = get_serial_no;
737 	if (p->capabilities == NULL)
738 		p->capabilities = capabilities;
739 	if (p->meas_config == NULL)
740 		p->meas_config = meas_config;
741 	if (p->set_mode == NULL)
742 		p->set_mode = set_mode;
743 	if (p->get_disptypesel == NULL)
744 		p->get_disptypesel = get_disptypesel;
745 	if (p->set_disptype == NULL)
746 		p->set_disptype = set_disptype;
747 	if (p->get_disptechi == NULL)
748 		p->get_disptechi = get_disptechi;
749 	if (p->get_set_opt == NULL)
750 		p->get_set_opt = get_set_opt;
751 	if (p->read_chart == NULL)
752 		p->read_chart = read_chart;
753 	if (p->xy_sheet_release == NULL)
754 		p->xy_sheet_release = xy_sheet_release;
755 	if (p->xy_sheet_hold == NULL)
756 		p->xy_sheet_hold = xy_sheet_hold;
757 	if (p->xy_locate_start == NULL)
758 		p->xy_locate_start = xy_locate_start;
759 	if (p->xy_get_location == NULL)
760 		p->xy_get_location = xy_get_location;
761 	if (p->xy_locate_end == NULL)
762 		p->xy_locate_end = xy_locate_end;
763 	if (p->xy_position == NULL)
764 		p->xy_position = xy_position;
765 	if (p->xy_clear == NULL)
766 		p->xy_clear = xy_clear;
767 	if (p->read_xy == NULL)
768 		p->read_xy = read_xy;
769 	if (p->read_strip == NULL)
770 		p->read_strip = read_strip;
771 	if (p->read_sample == NULL)
772 		p->read_sample = read_sample;
773 	if (p->read_refrate == NULL)
774 		p->read_refrate = read_refrate;
775 	if (p->needs_calibration == NULL)
776 		p->needs_calibration = needs_calibration;
777 	if (p->get_n_a_cals == NULL)
778 		p->get_n_a_cals = inst_get_n_a_cals;
779 	if (p->calibrate == NULL)
780 		p->calibrate = calibrate;
781 	if (p->meas_delay == NULL)
782 		p->meas_delay = meas_delay;
783 	if (p->white_change == NULL)
784 		p->white_change = white_change;
785 	if (p->get_refr_rate == NULL)
786 		p->get_refr_rate = get_refr_rate;
787 	if (p->set_refr_rate == NULL)
788 		p->set_refr_rate = set_refr_rate;
789 	if (p->comp_filter == NULL)
790 		p->comp_filter = comp_filter;
791 	if (p->col_cor_mat == NULL)
792 		p->col_cor_mat = col_cor_mat;
793 	if (p->col_cal_spec_set == NULL)
794 		p->col_cal_spec_set = col_cal_spec_set;
795 	if (p->set_uicallback == NULL)
796 		p->set_uicallback = set_uicallback;
797 	if (p->set_event_callback == NULL)
798 		p->set_event_callback = set_event_callback;
799 	if (p->inst_interp_error == NULL)
800 		p->inst_interp_error = inst_interp_error;
801 	if (p->interp_error == NULL)
802 		p->interp_error = interp_error;
803 	if (p->last_scomerr == NULL)
804 		p->last_scomerr = last_scomerr;
805 	if (p->config_enum == NULL)
806 		p->config_enum = config_enum;
807 
808 	/* Set the provided user interaction callback */
809 	p->set_uicallback(p, uicallback, cntx);
810 
811 #if defined(UNIX_APPLE)
812 	osx_latencycritical_start();
813 #endif
814 
815 	return p;
816 }
817 
818 /* --------------------------------------------------- */
819 
820 /* Free a display type list */
inst_del_disptype_list(inst_disptypesel * list,int no)821 void inst_del_disptype_list(inst_disptypesel *list, int no) {
822 
823 	if (list != NULL) {
824 		int i;
825 		for (i = 0; i < no; i++) {
826 			if (list[i].path != NULL)
827 				free(list[i].path);
828 			if (list[i].sets != NULL)
829 				free(list[i].sets);
830 		}
831 		free(list);
832 	}
833 }
834 
835 /* Ensure that the list is large enough for n+1 entries (+1 == end marker) */
836 /* Return NULL on malloc error */
expand_dlist(inst_disptypesel * list,int nlist,int * nalist)837 static inst_disptypesel *expand_dlist(inst_disptypesel *list, int nlist, int *nalist) {
838 
839 	if ((nlist+1) > *nalist) {
840 		int xnalist = (2 * nlist + 6);
841 		inst_disptypesel *xlist;
842 
843 		if ((xlist = realloc(list, xnalist * sizeof(inst_disptypesel))) == NULL) {
844 			inst_del_disptype_list(list, nlist);
845 			*nalist = 0;
846 			return NULL;
847 		}
848 		*nalist = xnalist;
849 		list = xlist;
850 	}
851 
852 	/* Set end marker */
853 	list[nlist].flags = inst_dtflags_end;
854 	list[nlist].cbid = 0;
855 	list[nlist].sel[0] = '\000';
856 	list[nlist].desc[0] = '\000';
857 	list[nlist].refr = 0;
858 	list[nlist].ix = 0;
859 	list[nlist].cc_cbid = 0;
860 	list[nlist].path = NULL;
861 	list[nlist].sets = NULL;
862 	list[nlist].no_sets = 0;
863 
864 	return list;
865 }
866 
867 /* For each selector we need to:
868 
869 	check each selector char
870 	if already used,
871 		remove it.
872 	if no selector remain,
873 		allocate a free one from the fallback list.
874 	mark all used selectors
875 
876 	We treat the first selector as more important
877 	than any aliases that come after it, and the
878 	aliases as more important than the fallback list,
879 	so we need to do three passes through all the selections.
880 */
881 
882 /* Create the display type list */
inst_creat_disptype_list(inst * p,int * pndtlist,inst_disptypesel ** pdtlist,inst_disptypesel * sdtlist,int doccss,int doccmx)883 inst_code inst_creat_disptype_list(inst *p,
884 int *pndtlist,					/* Number in returned list */
885 inst_disptypesel **pdtlist,		/* Returned list */
886 inst_disptypesel *sdtlist,		/* Static list */
887 int doccss,						/* Add installed ccss files */
888 int doccmx						/* Add matching installed ccmx files */
889 ) {
890 	inst_disptypesel *list = NULL;
891 	int i, j, k, nlist = 0, nalist = 0;
892 	char usels[256];			/* Used selectors */
893 	static char *asels = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
894 	int fail = 0;
895 
896 	/* free the old list */
897 	inst_del_disptype_list(*pdtlist, *pndtlist);
898 	*pdtlist = NULL;
899 	*pndtlist = 0;
900 
901 	for (i = 0; i < 256; i++)
902 		usels[i] = ((char)-1);
903 	k = 0;		/* Next selector index */
904 
905 	/* First create a list of calibrations and their desired selectors: */
906 
907 	/* Add entries from the static list and their primary selectors */
908 	/* (We're currently assuming that calibrations that the instrument */
909 	/*  returns are not custom.) */
910 	/* Count the number in the static list. */
911 	for (i = 0; !(sdtlist[i].flags & inst_dtflags_end); i++) {
912 
913 		if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL)
914 			return inst_internal_error;
915 
916 		list[nlist-1] = sdtlist[i];		/* Struct copy */
917 	}
918 
919 	/* Add any OEM and custom ccss's */
920 	if (doccss) {
921 		iccss *ss_list;
922 		if ((ss_list = list_iccss(NULL)) == NULL) {
923 			free(list);
924 			return inst_internal_error;
925 		}
926 
927 		for (i = 0; ss_list[i].path != NULL; i++) {
928 
929 			if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) {
930 				free_iccss(ss_list);
931 				return inst_internal_error;
932 			}
933 
934 			list[nlist-1].flags = inst_dtflags_ccss | inst_dtflags_ld | inst_dtflags_wr;
935 			if (!ss_list[i].oem)
936 				list[nlist-1].flags |= inst_dtflags_custom;
937 
938 			if (ss_list[i].sel != NULL) {
939 				strncpy(list[nlist-1].sel, ss_list[i].sel, INST_DTYPE_SEL_LEN);
940 				list[nlist-1].sel[INST_DTYPE_SEL_LEN-1] = '\000';
941 			}
942 			strncpy(list[nlist-1].desc, ss_list[i].desc, INST_DTYPE_DESC_LEN);
943 			list[nlist-1].desc[INST_DTYPE_DESC_LEN-1] = '\000';
944 			list[nlist-1].dtech = ss_list[i].dtech;
945 			list[nlist-1].refr = ss_list[i].refr;
946 			list[nlist-1].ix = 0;
947 			list[nlist-1].path = ss_list[i].path; ss_list[i].path = NULL;
948 			list[nlist-1].cbid = 0;
949 			list[nlist-1].sets = ss_list[i].sets; ss_list[i].sets = NULL;
950 			list[nlist-1].no_sets = ss_list[i].no_sets; ss_list[i].no_sets = 0;
951 		}
952 		free_iccss(ss_list);
953 	}
954 
955 	/* Add any OEM and custom ccmx's */
956 	if (doccmx) {
957 		iccmx *ss_list;
958 
959 		/* Just ccmx's for this instrument */
960 		if ((ss_list = list_iccmx(p->itype, NULL)) == NULL) {
961 			free(list);
962 			return inst_internal_error;
963 		}
964 
965 		for (i = 0; ss_list[i].path != NULL; i++) {
966 
967 			/* Check that there is a matching base calibation */
968 			for (j = 0; j < nlist; j++) {
969 				if (ss_list[i].cc_cbid != 0
970 					 && list[j].cbid == ss_list[i].cc_cbid)
971 					break;
972 			}
973 			if (j >= nlist) {
974 				a1loge(p->log, 1, "inst_creat_disptype_list can't find cbid %d for '%s'\n",ss_list[i].cc_cbid, ss_list[i].path);
975 				continue;
976 			}
977 
978 			if ((list = expand_dlist(list, ++nlist, &nalist)) == NULL) {
979 				free_iccmx(ss_list);
980 				return inst_internal_error;
981 			}
982 
983 			list[nlist-1].flags = inst_dtflags_ccmx | inst_dtflags_ld | inst_dtflags_wr;
984 			if (!ss_list[i].oem)
985 				list[nlist-1].flags |= inst_dtflags_custom;
986 
987 			if (ss_list[i].sel != NULL) {
988 				strncpy(list[nlist-1].sel, ss_list[i].sel, INST_DTYPE_SEL_LEN);
989 				list[nlist-1].sel[INST_DTYPE_SEL_LEN-1] = '\000';
990 			}
991 			strncpy(list[nlist-1].desc, ss_list[i].desc, INST_DTYPE_DESC_LEN);
992 			list[nlist-1].desc[INST_DTYPE_DESC_LEN-1] = '\000';
993 			list[nlist-1].dtech = ss_list[i].dtech;
994 			list[nlist-1].refr = ss_list[i].refr;
995 			list[nlist-1].ix = list[j].ix; /* Copy underlying cal selection from base */
996 			list[nlist-1].path = ss_list[i].path; ss_list[i].path = NULL;
997 			list[nlist-1].cbid = 0;
998 			list[nlist-1].cc_cbid = ss_list[i].cc_cbid;
999 			icmCpy3x3(list[nlist-1].mat, ss_list[i].mat);
1000 		}
1001 		free_iccmx(ss_list);
1002 	}
1003 
1004 	/* Copy candidate selectors to private isel[] list */
1005 	for (i = 0; i < nlist; i++) {
1006 		strcpy(list[i].isel, list[i].sel);
1007 		list[i].sel[0] = '\000';
1008 	}
1009 
1010 	/* Then allocate a slector for each calibration: */
1011 
1012 	/* Set selectors from primary for cbid or custom first */
1013 	for (i = 0; i < nlist; i++) {
1014 		if (list[i].cbid > 0
1015 		 || (list[i].flags & inst_dtflags_custom) != 0) {
1016 			disptechs_set_sel(0, i, list[i].sel, list[i].isel, usels, &k, asels);
1017 		}
1018 	}
1019 
1020 	/* Set selectors from primary for rest */
1021 	for (i = 0; i < nlist; i++)
1022 		disptechs_set_sel(0, i, list[i].sel, list[i].isel, usels, &k, asels);
1023 
1024 	/* Set remaining selectors from primaries or secondaries */
1025 	for (i = 0; i < nlist; i++)
1026 		disptechs_set_sel(1, i, list[i].sel, list[i].isel, usels, &k, asels);
1027 
1028 	/* Set remaining from fallback */
1029 	for (i = 0; i < nlist; i++) {
1030 		disptechs_set_sel(2, i, list[i].sel, list[i].isel, usels, &k, asels);
1031 		if (list[i].sel[0] == '\000')
1032 			fail = 1;
1033 	}
1034 
1035 	/* Any calibrations that failed to find a character will be left as a nul string */
1036 
1037 	/* Add alternate selectors if they are free. */
1038 	for (;;) {
1039 		int more = 0;
1040 		for (i = 0; i < nlist; i++) {
1041 			/* Add unused secondaries */
1042 			disptechs_set_sel(3, i, list[i].sel, list[i].isel, usels, &k, asels);
1043 
1044 			if (list[i].isel[0] != '\000') {		/* Still more secondaries available */
1045 				more = 1;
1046 			}
1047 		}
1048 		if (!more)
1049 			break;
1050 	}
1051 
1052 	if (pndtlist != NULL)
1053 		*pndtlist = nlist;
1054 	if (pdtlist != NULL)
1055 		*pdtlist = list;
1056 
1057 	if (fail) {
1058 		a1loge(p->log, 1, "inst_creat_disptype_list run out of selectors\n");
1059 		return inst_internal_error;
1060 	}
1061 
1062 	return inst_ok;
1063 }
1064 
1065 /* --------------------------------------------------- */
1066 
1067 /* Delayed scan-ready prompt handler */
delayed_scan_ready(void * pp)1068 static int delayed_scan_ready(void *pp) {
1069 	inst *p = (inst *)pp;
1070 
1071 	msec_sleep(p->scan_ready_delay);
1072 	a1logd(g_log,8, "delayed scan_ready activate\n");
1073 
1074 	if (p->eventcallback != NULL)
1075 		p->eventcallback(p->event_cntx, inst_event_scan_ready);
1076 	return 0;
1077 }
1078 
1079 /* Issue an inst_event_scan_ready event/prompt after a delay */
issue_scan_ready(inst * p,int delay)1080 void issue_scan_ready(inst *p, int delay) {
1081 	a1logd(g_log,8, "msec_scan_ready %d msec\n",delay);
1082 
1083 	if (p->eventcallback == NULL)		/* Hmm. */
1084 		return;
1085 
1086 	if (delay > 0) {
1087 		if (p->scan_ready_thread != NULL)
1088 			p->scan_ready_thread->del(p->scan_ready_thread);
1089 		p->scan_ready_delay = delay;
1090 		if ((p->scan_ready_thread = new_athread(delayed_scan_ready, (void *)p)) == NULL)
1091 			a1logw(g_log, "msec_scan_ready: Delayed scan_ready failed to create thread\n");
1092 	} else {
1093 		a1logd(g_log,8, "msec_scan_ready activate\n");
1094 		p->eventcallback(p->event_cntx, inst_event_scan_ready);
1095 	}
1096 }
1097 
1098 /* ============================================================= */
1099 /* CCMX location support */
1100 
1101 /* return a list of installed ccmx files. */
1102 /* if itype != instUnknown, return those that match the given instrument. */
1103 /* The list is sorted by description and terminated by a NULL entry. */
1104 /* If no is != NULL, return the number in the list */
1105 /* Return NULL and -1 if there is a malloc error */
list_iccmx(instType itype,int * no)1106 iccmx *list_iccmx(instType itype, int *no) {
1107 	int i, j;
1108 	iccmx *rv;
1109 
1110 	char **paths = NULL;
1111 	int npaths = 0;
1112 
1113 
1114 	npaths = xdg_bds(NULL, &paths, xdg_data, xdg_read, xdg_user,
1115 						"ArgyllCMS/\052.ccmx" XDG_FUDGE "color/\052.ccmx"
1116 	);
1117 
1118 	if ((rv = malloc(sizeof(iccmx) * (npaths + 1))) == NULL) {
1119 		a1loge(g_log, 1, "list_iccmx: malloc of paths failed\n");
1120 		xdg_free(paths, npaths);
1121 		if (no != NULL) *no = -1;
1122 		return NULL;
1123 	}
1124 
1125 	for (i = j = 0; i < npaths; i++) {
1126 		ccmx *cs;
1127 		int len;
1128 		char *pp;
1129 		disptech dtech;
1130 		char *tech, *disp;
1131 		int cc_cbid, refr;
1132 
1133 		if ((cs = new_ccmx()) == NULL) {
1134 			a1loge(g_log, 1, "list_iccmx: new_ccmx failed\n");
1135 			for (--j; j>= 0; j--) {
1136 				free(rv[j].path);
1137 				free(rv[j].desc);
1138 			}
1139 			xdg_free(paths, npaths);
1140 			free(rv);
1141 			if (no != NULL) *no = -1;
1142 			return NULL;
1143 		}
1144 		if (cs->read_ccmx(cs, paths[i])) {
1145 			cs->del(cs);
1146 			continue;		/* Skip any unreadable ccmx's */
1147 		}
1148 
1149 		/* Skip any that don't match */
1150 		if (itype != instUnknown && cs->inst != NULL && inst_enum(cs->inst) != itype) {
1151 			cs->del(cs);
1152 			continue;
1153 		}
1154 
1155 		a1logd(g_log, 5, "Reading '%s'\n",paths[i]);
1156 		if ((tech = cs->tech) == NULL)
1157 			tech = "";
1158 		if ((disp = cs->disp) == NULL)
1159 			disp = "";
1160 		cc_cbid = cs->cc_cbid;
1161 		dtech = cs->dtech;
1162 		refr = cs->refrmode;
1163 		len = strlen(tech) + strlen(disp) + 4;
1164 		if ((pp = malloc(len)) == NULL) {
1165 			a1loge(g_log, 1, "list_iccmx: malloc failed\n");
1166 			for (--j; j >= 0; j--) {
1167 				free(rv[j].path);
1168 				free(rv[j].desc);
1169 			}
1170 			cs->del(cs);
1171 			free(rv);
1172 			xdg_free(paths, npaths);
1173 			if (no != NULL) *no = -1;
1174 			return NULL;
1175 		}
1176 		if ((rv[j].path = strdup(paths[i])) == NULL) {
1177 			a1loge(g_log, 1, "list_iccmx: strdup failed\n");
1178 			for (--j; j >= 0; j--) {
1179 				free(rv[j].path);
1180 				free(rv[j].desc);
1181 			}
1182 			cs->del(cs);
1183 			free(rv);
1184 			free(pp);
1185 			xdg_free(paths, npaths);
1186 			if (no != NULL) *no = -1;
1187 			return NULL;
1188 		}
1189 		strcpy(pp, tech);
1190 		strcat(pp, " (");
1191 		strcat(pp, disp);
1192 		strcat(pp, ")");
1193 		rv[j].desc = pp;
1194 		rv[j].cc_cbid = cc_cbid;
1195 		rv[j].dtech = dtech;
1196 		rv[j].refr = refr;
1197 		rv[j].sel = cs->sel;  cs->sel = NULL;
1198 		rv[j].oem = cs->oem;
1199 		icmCpy3x3(rv[j].mat, cs->matrix);
1200 		cs->del(cs);
1201 		j++;
1202 	}
1203 	xdg_free(paths, npaths);
1204 	rv[j].path = NULL;
1205 	rv[j].desc = NULL;
1206 	rv[j].cc_cbid = 0;
1207 	rv[j].dtech = disptech_unknown;
1208 	rv[j].refr = -1;
1209 	rv[j].sel = NULL;
1210 	rv[j].oem = 0;
1211 	if (no != NULL)
1212 		*no = j;
1213 
1214 	/* Sort the list */
1215 #define HEAP_COMPARE(A,B) (strcmp(A.desc, B.desc) < 0)
1216 	HEAPSORT(iccmx, rv, j)
1217 #undef HEAP_COMPARE
1218 
1219 	return rv;
1220 }
1221 
1222 /* Free up a iccmx list */
free_iccmx(iccmx * list)1223 void free_iccmx(iccmx *list) {
1224 	int i;
1225 
1226 	if (list != NULL) {
1227 		for (i = 0; list[i].path != NULL || list[i].desc != NULL; i++) {
1228 			if (list[i].path != NULL)
1229 				free(list[i].path);
1230 			if (list[i].desc != NULL)
1231 				free(list[i].desc);
1232 			if (list[i].sel != NULL)
1233 				free(list[i].sel);
1234 		}
1235 		free(list);
1236 	}
1237 }
1238 
1239 /* ============================================================= */
1240 /* CCSS location support */
1241 
1242 /* return a list of installed ccss files. */
1243 /* The list is sorted by description and terminated by a NULL entry. */
1244 /* If no is != NULL, return the number in the list */
1245 /* Return NULL and -1 if there is a malloc error */
list_iccss(int * no)1246 iccss *list_iccss(int *no) {
1247 	int i, j;
1248 	iccss *rv;
1249 
1250 	char **paths = NULL;
1251 	int npaths = 0;
1252 
1253 
1254 	npaths = xdg_bds(NULL, &paths, xdg_data, xdg_read, xdg_user,
1255 						"ArgyllCMS/\052.ccss" XDG_FUDGE "color/\052.ccss"
1256 	);
1257 
1258 	if ((rv = malloc(sizeof(iccss) * (npaths + 1))) == NULL) {
1259 		a1loge(g_log, 1, "list_iccss: malloc of paths failed\n");
1260 		xdg_free(paths, npaths);
1261 		if (no != NULL) *no = -1;
1262 		return NULL;
1263 	}
1264 
1265 	for (i = j = 0; i < npaths; i++) {
1266 		ccss *cs;
1267 		int len;
1268 		char *pp;
1269 		disptech dtech;
1270 		char *tech, *disp;
1271 		int refr;
1272 
1273 		if ((cs = new_ccss()) == NULL) {
1274 			a1loge(g_log, 1, "list_iccss: new_ccss failed\n");
1275 			for (--j; j>= 0; j--) {
1276 				free(rv[j].path);
1277 				free(rv[j].desc);
1278 			}
1279 			xdg_free(paths, npaths);
1280 			if (no != NULL) *no = -1;
1281 			return NULL;
1282 		}
1283 		if (cs->read_ccss(cs, paths[i])) {
1284 			cs->del(cs);
1285 			continue;		/* Skip any unreadable ccss's */
1286 		}
1287 
1288 		a1logd(g_log, 5, "Reading '%s'\n",paths[i]);
1289 		if ((tech = cs->tech) == NULL)
1290 			tech = "";
1291 		if ((disp = cs->disp) == NULL)
1292 			disp = "";
1293 		dtech = cs->dtech;
1294 		refr = cs->refrmode;
1295 		len = strlen(tech) + strlen(disp) + 4;
1296 		if ((pp = malloc(len)) == NULL) {
1297 			a1loge(g_log, 1, "list_iccss: malloc failed\n");
1298 			for (--j; j >= 0; j--) {
1299 				free(rv[j].path);
1300 				free(rv[j].desc);
1301 			}
1302 			cs->del(cs);
1303 			free(rv);
1304 			xdg_free(paths, npaths);
1305 			if (no != NULL) *no = -1;
1306 			return NULL;
1307 		}
1308 		if ((rv[j].path = strdup(paths[i])) == NULL) {
1309 			a1loge(g_log, 1, "list_iccss: strdup failed\n");
1310 			for (--j; j >= 0; j--) {
1311 				free(rv[j].path);
1312 				free(rv[j].desc);
1313 			}
1314 			cs->del(cs);
1315 			free(rv);
1316 			free(pp);
1317 			xdg_free(paths, npaths);
1318 			if (no != NULL) *no = -1;
1319 			return NULL;
1320 		}
1321 		strcpy(pp, tech);
1322 		strcat(pp, " (");
1323 		strcat(pp, disp);
1324 		strcat(pp, ")");
1325 		rv[j].desc = pp;
1326 		rv[j].dtech = dtech;
1327 		rv[j].refr = refr;
1328 		rv[j].sel = cs->sel;  cs->sel = NULL;
1329 		rv[j].oem = cs->oem;
1330 		rv[j].sets = cs->samples;  cs->samples = NULL;
1331 		rv[j].no_sets = cs->no_samp; cs->no_samp = 0;
1332 		cs->del(cs);
1333 		j++;
1334 	}
1335 	xdg_free(paths, npaths);
1336 	rv[j].path = NULL;
1337 	rv[j].desc = NULL;
1338 	rv[j].dtech = disptech_unknown;
1339 	rv[j].refr = -1;
1340 	rv[j].sel = NULL;
1341 	rv[j].oem = 0;
1342 	rv[j].sets = NULL;
1343 	rv[j].no_sets = 0;
1344 	if (no != NULL)
1345 		*no = j;
1346 
1347 	/* Sort the list */
1348 #define HEAP_COMPARE(A,B) (strcmp(A.desc, B.desc) < 0)
1349 	HEAPSORT(iccss, rv, j)
1350 #undef HEAP_COMPARE
1351 
1352 	return rv;
1353 }
1354 
1355 /* Free up a iccss list */
free_iccss(iccss * list)1356 void free_iccss(iccss *list) {
1357 	int i;
1358 
1359 	if (list != NULL) {
1360 		for (i = 0; list[i].path != NULL || list[i].desc != NULL; i++) {
1361 			if (list[i].path != NULL)
1362 				free(list[i].path);
1363 			if (list[i].desc != NULL)
1364 				free(list[i].desc);
1365 			if (list[i].sel != NULL)
1366 				free(list[i].sel);
1367 			if (list[i].sets != NULL)
1368 				free(list[i].sets);
1369 		}
1370 		free(list);
1371 	}
1372 }
1373 
1374 /* ============================================================= */
1375 /* Detect fast serial instruments */
1376 
1377 #ifdef ENABLE_FAST_SERIAL
1378 static void hex2bin(char *buf, int len);
1379 
1380 /* Heuristicly determine the instrument type for */
1381 /* a fast serial connection, and instUnknown if not serial. */
1382 /* Set it in icoms and also return it. */
fast_ser_inst_type(icoms * p,int tryhard,inst_code (* uicallback)(void * cntx,inst_ui_purp purp),void * cntx)1383 instType fast_ser_inst_type(
1384 	icoms *p,
1385 	int tryhard,
1386 	inst_code (*uicallback)(void *cntx, inst_ui_purp purp),		/* optional uicallback */
1387 	void *cntx			/* Context for callback */
1388 ) {
1389 	instType rv = instUnknown;
1390 #define BUFSZ (2048 + 10)
1391 	char buf[BUFSZ];
1392 	baud_rate brt1[] = { baud_9600, baud_921600, baud_115200,
1393 	                     baud_38400, baud_nc };			/* HS - do K10/Spectrolino first */
1394 	baud_rate brt2[] = { baud_115200, baud_nc };		/* Bluetooth */
1395 
1396 	baud_rate *brt = brt1;
1397 	unsigned int etime;
1398 	unsigned int i;
1399 	int delayms = 0;
1400 	int se, len;
1401 	double tryto = 0.1;		/* [0.1] Communication timout */
1402 //	double tryto = 0.9;		/* Communication timout (test) */
1403 
1404 	a1logd(p->log, 8, "fast_ser_inst_type: on '%s' dctype 0x%x\n",p->name,p->dctype);
1405 
1406 	if (!(p->dctype & icomt_seriallike)
1407 	 && !(p->dctype & icomt_fastserial)) {
1408 		return p->itype;
1409 	}
1410 
1411 	/* The tick to give up on */
1412 	etime = msec_time() + (long)(2000.0 + 0.5);
1413 //	etime = msec_time() + (long)(20000.0 + 0.5);		/* (test) */
1414 
1415 	a1logd(p->log, 1, "fser_inst_type: Trying different baud rates (%u msec to go) Path %s%s\n",
1416 	                  etime - msec_time(),p->spath,(p->dctype & icomt_btserial) ? " [Bluetooth]" : "");
1417 
1418 	if (p->dctype & icomt_btserial) {
1419 		brt = brt2;		/* Only try BT relevant baud rates. */
1420 		delayms = 600;	/* Spectraval locks up otherwise. */
1421 	}
1422 
1423 	/* Until we time out, find the correct baud rate */
1424 	for (i = 0; msec_time() < etime; i++) {
1425 		if (brt[i] == baud_nc) {
1426 			i = 0;
1427 			if (!tryhard)
1428 				break;		/* try only once */
1429 		}
1430 		a1logd(p->log, 5, "Trying %s baud\n",baud_rate_to_str(brt[i]));
1431 
1432 		if ((se = p->set_ser_port_ex(p, fc_None, brt[i], parity_none,
1433 			                         stop_1, length_8, delayms)) != ICOM_OK) {
1434 			a1logd(p->log, 5, "fser_inst_type: set_ser_port failed with 0x%x\n",se);
1435 			return instUnknown;		/* Give up on port */
1436 		}
1437 
1438 		/* Assume Klein K10 only uses 9600. */
1439 		/* We need to also assume that we might be talking to a Spectrolino, */
1440 		/* and avoid upsetting it. */
1441 		if ((p->dctype & icomt_btserial) == 0 && brt[i] == baud_9600) {
1442 			double sto = 0.2;	/* Give 9600 a little more time */
1443 			int bread, len;
1444 
1445 			/* Try a spectrolino/spectroscan command first */
1446 			if (tryto > sto)
1447 				sto = tryto;
1448 			p->write_read_ex(p, ";D024\r\n", 0, buf, BUFSZ-1, &bread, "\r", 1, sto, 1);
1449 
1450 			if (bread == 0) {
1451 				a1logd(p->log, 5, "fser_inst_type: Spectroino command returned nothing\n");
1452 				goto not_k10;
1453 			}
1454 			buf[bread] = '\000';
1455 			len = strlen(buf);
1456 
1457 			a1logd(p->log, 5, "fser_inst_type: got %d bytes\n",len);
1458 
1459 			if (len < 4) {
1460 				a1logd(p->log, 5, "fser_inst_type: Reply was too short\n");
1461 				goto not_k10;		/* Not K10, X-Rite or Spectrolino */
1462 			}
1463 
1464 			/* Is this a Spectrolino or Spectroscan error resonse ? */
1465 			if (len >= 5 && strncmp(buf, ":26", 3) == 0
1466 			 || len >= 7 && strncmp(buf, ":D183", 5) == 0) {
1467 				a1logd(p->log, 5, "fser_inst_type: Ignore Spectrolino\n");
1468 				return instUnknown;		/* Not doing Spectrolino detection here */
1469 			}
1470 
1471 			/* Is this an X-Rite error value such as "<01>" ? */
1472 			if (buf[0] == '<' && isdigit(buf[1]) && isdigit(buf[2]) && buf[3] == '>') {
1473 				a1logd(p->log, 5, "fser_inst_type: Ignore X-Rite\n");
1474 				return instUnknown;		/* Not doing X-Rite detection here */
1475 			}
1476 
1477 			/* The Klein K10 seems to respond with it's calibration list, preceeded by "D4" ? */
1478 			if (buf[0] != 'D' || buf[1] != '4') {
1479 				a1logd(p->log, 5, "fser_inst_type: Not Klein response\n");
1480 				goto not_k10;;
1481 			}
1482 
1483 			a1logd(p->log, 5, "fser_inst_type: Looks like it may be a Klein\n");
1484 
1485 			/* Confirm this is a Klein instrument  */
1486 
1487 			/* The first response is the calibration list, and it may need flushing. */
1488 			/* (write_read_ex doesn't cope with time it takes to dump this.) */
1489 			for (;;) {
1490 				bread = 0;
1491 				p->read(p, buf, BUFSZ, &bread, NULL, BUFSZ, 0.1);
1492 				if (bread == 0)
1493 					break;
1494 			}
1495 
1496 			if ((se = p->write_read_ex(p, "P0\r", 0, buf, BUFSZ, NULL, ">", 1, tryto, 1)) == inst_ok) {
1497 
1498 				/* Is this a Klein K1/K8/K10 response ? */
1499 				if (strncmp(buf, "P0K-1 ", 6) == 0
1500 				 || strncmp(buf, "P0K-8 ", 6) == 0
1501 				 || strncmp(buf, "P0K-10", 6) == 0
1502 				 || strncmp(buf, "P0KV-10", 7) == 0) {
1503 					a1logd(p->log, 5, "fser_inst_type: found Klein K1/K8/K10\n");
1504 					rv = instKleinK10;
1505 					break;
1506 				}
1507 			}
1508 
1509 		not_k10:;
1510 
1511 			/* Check for user abort */
1512 			if (uicallback != NULL) {
1513 				inst_code ev;
1514 				if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) {
1515 						a1logd(p->log, 5, "fser_inst_type: User aborted\n");
1516 						return instUnknown;
1517 				}
1518 			}
1519 		}
1520 
1521 		/* SwatchMate Cube only uses 38400 */
1522 		if ((p->dctype & icomt_btserial) == 0 && brt[i] == baud_38400) {
1523 			int bread;
1524 
1525 			/* See if it's a SwatchMate Cube. */
1526 			/* The Cube uses RTS/CTS handshaking, but ignore this for identification. */
1527 			buf[0] = 0x7e;
1528 			buf[1] = 0x00;
1529 			buf[2] = 0x02;		/* Ping command */
1530 			buf[3] = 0x00;
1531 			if ((se = p->write_read_ex(p, buf, 4, buf, BUFSZ, &bread, NULL, 4, tryto, 1)) == inst_ok) {
1532 				if (bread == 4) {
1533 					if (buf[0] == 0x7e && buf[1] == 0x20 && buf[2] == 0x02 && buf[3] == 0x00) {
1534 						a1logd(p->log, 5, "fser_inst_type: found SwatchMate Cube\n");
1535 						rv = instSMCube;
1536 						break;
1537 					}
1538 				}
1539 			}
1540 
1541 			/* Check for user abort */
1542 			if (uicallback != NULL) {
1543 				inst_code ev;
1544 				if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) {
1545 					a1logd(p->log, 5, "fser_inst_type: User aborted\n");
1546 					return instUnknown;
1547 				}
1548 			}
1549 		}
1550 
1551 		/* JETI specbos or spectraval. */
1552 		/* We are fudging the baud rate selection here - */
1553 		/* the 1211 RS and BT can't handle 921600, */
1554 		/* while the 15x1 can handle 230400 & 3000000, which we don't test for... */
1555 //		if ((p->dctype & icomt_btserial) == 0 || brt[i] == baud_115200)
1556 		if (brt[i] == baud_38400 || brt[i] == baud_115200 || brt[i] == baud_921600)
1557 		{
1558 			int bread;
1559 
1560 			/* See if it's a JETI specbos */
1561 			p->write_read_ex(p, "*idn?\r", 0, buf, BUFSZ, &bread, "\r", 1, tryto, 1);
1562 			if (bread > 0) {
1563 				len = strlen(buf);
1564 
1565 				/* JETI specbos returns "JETI_SBXXXX", where XXXX is the instrument type, */
1566 				/* except for the 1201 which returns "SB05" */
1567 				/* The spectraval 1501 returns JETI_SDCM3 NNNNNNN */
1568 
1569 				/* Over Bluetooth, we get an erronious string "AT+JSCR\r\n" mixed in our output. */
1570 				/* This would appear to be from the eBMU Bluetooth adapter AT command set. */
1571 				if (len > 9 && strncmp(buf, "AT+JSCR\r\n", 9) == 0) {
1572 					memmove(buf, buf+9, len-9);
1573 					len -= 9;
1574 				}
1575 
1576 				/* Is this a JETI specbos 1201 response ? */
1577 				if (strncmp(buf, "SB05", 4) == 0) {
1578 					a1logd(p->log, 5, "fser_inst_type: found JETI specbos 1201\n");
1579 					rv = instSpecbos1201;
1580 					break;
1581 				}
1582 				/* Is this a JETI specbos XXXX response ? */
1583 				if (len >= 11 && strncmp(buf, "JETI_SB", 7) == 0) {
1584 					a1logd(p->log, 5, "fser_inst_type: found JETI specbos\n");
1585 					rv = instSpecbos;
1586 					break;
1587 				}
1588 				/* Is this a JETI spectraval response ? */
1589 				/* (Bluetooth returns "DCM3_JETI ... and other rubbish for some reason.) */
1590 				if ((len >= 10  && strncmp(buf, "JETI_SDCM3", 10) == 0)
1591 				 || (len >= 9   && strncmp(buf, "DCM3_JETI", 9) == 0)
1592 				 || (len >= 17  && strncmp(buf, "PECFIRM_JETI_1501", 17) == 0)
1593 				 || (len >= 18  && strncmp(buf, "SPECFIRM_JETI_1501", 18) == 0)) {
1594 					a1logd(p->log, 5, "fser_inst_type: found JETI spectraval\n");
1595 					rv = instSpectraval;
1596 					break;
1597 				}
1598 			}
1599 			/* Check for user abort */
1600 			if (uicallback != NULL) {
1601 				inst_code ev;
1602 				if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) {
1603 					a1logd(p->log, 5, "fser_inst_type: User aborted\n");
1604 					return instUnknown;
1605 				}
1606 			}
1607 		}
1608 	}
1609 
1610 	if (rv == instUnknown
1611 	 && msec_time() >= etime) {		/* We haven't established comms */
1612 		a1logd(p->log, 5, "fser_inst_type: Failed to establish coms\n");
1613 		p->itype = rv;
1614 		return instUnknown;
1615 	}
1616 
1617 	a1logd(p->log, 5, "fser_inst_type: Instrument type is '%s'\n", inst_name(rv));
1618 
1619 	p->itype = rv;
1620 
1621 	return rv;
1622 }
1623 #undef BUFSZ
1624 
1625 #endif /* ENABLE_FAST_SERIAL */
1626 
1627 /* ============================================================= */
1628 /* Detect serial instruments */
1629 
1630 #ifdef ENABLE_SERIAL
1631 static void hex2bin(char *buf, int len);
1632 
1633 /* Heuristicly determine the instrument type for */
1634 /* a serial connection, and instUnknown if not serial. */
1635 /* Set it in icoms and also return it. */
ser_inst_type(icoms * p,inst_code (* uicallback)(void * cntx,inst_ui_purp purp),void * cntx)1636 static instType ser_inst_type(
1637 	icoms *p,
1638 	inst_code (*uicallback)(void *cntx, inst_ui_purp purp),		/* optional uicallback */
1639 	void *cntx			/* Context for callback */
1640 ) {
1641 	instType rv = instUnknown;
1642 #define BUFSZ (128 + 10)
1643 	char buf[BUFSZ];
1644 	baud_rate brt[] = { baud_9600, baud_19200, baud_4800, baud_2400,
1645 	                    baud_1200, baud_38400, baud_57600, baud_115200,
1646 	                    baud_600, baud_300, baud_110, baud_nc };
1647 	unsigned int etime;
1648 	unsigned int bi, i;
1649 	int se, len, bread;
1650 	int klein = 0;
1651 	int xrite = 0;
1652 	int ss = 0;
1653 	int so = 0;
1654 
1655 #ifdef ENABLE_USB
1656 	if (p->usbd != NULL || p->hidd != NULL)
1657 		return p->itype;
1658 #endif /* ENABLE_USB */
1659 
1660 	bi = 0;
1661 
1662 	/* The tick to give up on */
1663 	etime = msec_time() + (long)(20.0 * 1000.0 + 0.5);
1664 
1665 	a1logd(p->log, 1, "ser_inst_type: Trying different baud rates (%u msec to go)\n",etime - msec_time());
1666 
1667 	/* Until we time out, find the correct baud rate */
1668 	for (i = bi; msec_time() < etime; i++) {
1669 		if (brt[i] == baud_nc)
1670 			i = 0;
1671 		if ((se = p->set_ser_port(p, fc_None, brt[i], parity_none,
1672 			                         stop_1, length_8)) != ICOM_OK) {
1673 			a1logd(p->log, 5, "ser_inst_type: set_ser_port failed with 0x%x\n",se);
1674 			return instUnknown;		/* Give up */
1675 		}
1676 
1677 		a1logd(p->log, 5, "Trying %s baud\n",baud_rate_to_str(brt[i]));
1678 		bread = 0;
1679 
1680 		/* Try a spectrolino/spectroscan command first */
1681 		p->write_read_ex(p, ";D024\r\n", 0, buf, BUFSZ-1, &bread, "\r", 1, 0.5, 1);
1682 
1683 		if (bread == 0) {
1684 			/* Check for user abort */
1685 			if (uicallback != NULL) {
1686 				inst_code ev;
1687 				if ((ev = uicallback(cntx, inst_negcoms)) == inst_user_abort) {
1688 					a1logd(p->log, 5, "ser_inst_type: User aborted\n");
1689 					return instUnknown;
1690 				}
1691 			}
1692 			continue;
1693 		}
1694 		buf[bread] = '\000';
1695 		len = strlen(buf);
1696 
1697 //		a1logd(p->log, 5, "len = %d\n",len);
1698 		if (len < 4)
1699 			continue;
1700 
1701 		/* The Klein K10 seems to respond with it's calibration list, preceeded by "D4" ? */
1702 		/* - don't know how reliable this is though. Another way may be to look for a */
1703 		/* response len > 100 characters ?? */
1704 		if (buf[0] == 'D' && buf[1] == '4') {
1705 //			a1logd(p->log, 5, "klein\n");
1706 			klein = 1;
1707 			break;
1708 		}
1709 
1710 		/* Is this an X-Rite error value such as "<01>" ? */
1711 		if (buf[0] == '<' && isdigit(buf[1]) && isdigit(buf[2]) && buf[3] == '>') {
1712 //			a1logd(p->log, 5, "xrite\n");
1713 			xrite = 1;
1714 			break;
1715 		}
1716 
1717 		/* Is this a Spectrolino error resonse ? */
1718 		if (len >= 5 && strncmp(buf, ":26", 3) == 0) {
1719 //			a1logd(p->log, 5, "spectrolino\n");
1720 			so = 1;
1721 			break;
1722 		}
1723 		/* Is this a SpectroScan response ? */
1724 		if (len >= 7 && strncmp(buf, ":D183", 5) == 0) {
1725 //			a1logd(p->log, 5, "spectroscan\n");
1726 			ss = 1;
1727 			break;
1728 		}
1729 	}
1730 
1731 	if (rv == instUnknown
1732 	 && msec_time() >= etime) {		/* We haven't established comms */
1733 		a1logd(p->log, 5, "ser_inst_type: Failed to establish coms\n");
1734 		return instUnknown;
1735 	}
1736 
1737 	a1logd(p->log, 5, "ser_inst_type: Got coms with instrument\n");
1738 
1739 	/* Spectrolino */
1740 	if (so) {
1741 		rv = instSpectrolino;
1742 	}
1743 
1744 	/* SpectroScan */
1745 	if (ss) {
1746 		rv = instSpectroScan;
1747 		if ((se = p->write_read_ex(p, ";D030\r\n", 0, buf, BUFSZ, NULL, "\n", 1, 1.5, 1)) == 0)  {
1748 			if (strlen(buf) >= 41) {
1749 				hex2bin(&buf[5], 12);
1750 //				a1logd(p->log, 5, "spectroscan type = '%s'\n",buf);
1751 				if (strncmp(buf, ":D190SpectroScanT", 17) == 0)
1752 					rv = instSpectroScanT;
1753 			}
1754 		}
1755 	}
1756 	if (xrite) {
1757 
1758 		/* Get the X-Rite model and version number */
1759 		if ((se = p->write_read_ex(p, "SV\r\n", 0, buf, BUFSZ, NULL, ">", 1, 2.5, 1)) != 0)
1760 			return instUnknown;
1761 
1762 		if (strlen(buf) >= 12) {
1763 		    if (strncmp(buf,"X-Rite DTP22",12) == 0)
1764 				rv = instDTP22;
1765 		    if (strncmp(buf,"X-Rite DTP41",12) == 0)
1766 				rv = instDTP41;
1767 		    if (strncmp(buf,"X-Rite DTP42",12) == 0)
1768 				rv = instDTP41;
1769 		    if (strncmp(buf,"X-Rite DTP51",12) == 0)
1770 				rv = instDTP51;
1771 		    if (strncmp(buf,"X-Rite DTP52",12) == 0)
1772 				rv = instDTP51;
1773 		    if (strncmp(buf,"X-Rite DTP92",12) == 0)
1774 				rv = instDTP92;
1775 		    if (strncmp(buf,"X-Rite DTP94",12) == 0)
1776 				rv = instDTP94;
1777 		}
1778 	}
1779 
1780 	if (klein) {
1781 
1782 		/* The first response is the calibration list, and it may need flushing. */
1783 		/* (write_read_ex doesn't cope with time it takes to dump this.) */
1784 		for (;;) {
1785 			bread = 0;
1786 			p->read(p, buf, BUFSZ, &bread, NULL, BUFSZ, 0.1);
1787 			if (bread == 0)
1788 				break;
1789 		}
1790 
1791 		/* See if this is a Klein K10 or similar */
1792 		if ((se = p->write_read_ex(p, "P0\r", 0, buf, BUFSZ, NULL, ">", 1, 1.5, 1)) != inst_ok)
1793 			return instUnknown;
1794 
1795 		/* Is this a Klein K1/K8/K10 response ? */
1796 		if (strncmp(buf, "P0K-1 ", 6) == 0
1797 		 || strncmp(buf, "P0K-8 ", 6) == 0
1798 		 || strncmp(buf, "P0K-10", 6) == 0
1799 		 || strncmp(buf, "P0KV-10", 7) == 0) {
1800 			a1logd(p->log, 5, "fser_inst_type: found Klein K1/K8/K10\n");
1801 			rv = instKleinK10;
1802 		}
1803 	}
1804 
1805 	a1logd(p->log, 5, "ser_inst_type: Instrument type is '%s'\n", inst_name(rv));
1806 
1807 	p->close_port(p);	/* Or should we leave it open ?? */
1808 
1809 	p->itype = rv;
1810 
1811 	return rv;
1812 }
1813 #undef BUFSZ
1814 
1815 #endif /* ENABLE_SERIAL */
1816 
1817 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
1818 
1819 /* Convert an ASCII Hex character to an integer. */
h2b(char c)1820 static int h2b(char c) {
1821 	if (c >= '0' && c <= '9')
1822 		return (c-(int)'0');
1823 	if (c >= 'A' && c <= 'F')
1824 		return (10 + c-(int)'A');
1825 	if (c >= 'a' && c <= 'f')
1826 		return (10 + c-(int)'a');
1827 	return 0;
1828 }
1829 
1830 /* Convert a Hex encoded buffer into binary. */
1831 /* len is number of bytes out */
hex2bin(char * buf,int len)1832 static void hex2bin(char *buf, int len) {
1833 	int i;
1834 
1835 	for (i = 0; i < len; i++) {
1836 		buf[i] = (char)((h2b(buf[2 * i + 0]) << 4)
1837 		              | (h2b(buf[2 * i + 1]) << 0));
1838 	}
1839 }
1840 
1841 #endif /* ENABLE_SERIAL */
1842 
1843 /* ============================================================= */
1844 /* inst_mode persistent storage support */
1845 
1846 /* Table listing and relating masks to symbols. */
1847 /* Need to keep this in sync with inst.h */
1848 struct {
1849 	int mode;		/* Mode bits */
1850 	char *sym;		/* 4 character symbol */
1851 } inst_mode_sym[] = {
1852 	{ inst_mode_reflection, inst_mode_reflection_sym },
1853 	{ inst_mode_s_reflection, inst_mode_s_reflection_sym },
1854 	{ inst_mode_transmission, inst_mode_transmission_sym },
1855 	{ inst_mode_emission, inst_mode_emission_sym },
1856 
1857 	{ inst_mode_spot, inst_mode_spot_sym },
1858 	{ inst_mode_strip, inst_mode_strip_sym },
1859 	{ inst_mode_xy, inst_mode_xy_sym },
1860 	{ inst_mode_chart, inst_mode_chart_sym },
1861 	{ inst_mode_ambient, inst_mode_ambient_sym },
1862 	{ inst_mode_ambient_flash, inst_mode_ambient_flash_sym },
1863 	{ inst_mode_tele, inst_mode_tele_sym },
1864 
1865 	{ inst_mode_emis_nonadaptive, inst_mode_emis_nonadaptive_sys },
1866 	{ inst_mode_ref_uv, inst_mode_ref_uv_sym },
1867 	{ inst_mode_emis_refresh_ovd, inst_mode_emis_refresh_ovd_sym },
1868 	{ inst_mode_emis_norefresh_ovd, inst_mode_emis_norefresh_ovd_sym },
1869 
1870 	{ inst_mode_colorimeter, inst_mode_colorimeter_sym },
1871 	{ inst_mode_spectral, inst_mode_spectral_sym },
1872 	{ inst_mode_highres, inst_mode_highres_sym },
1873 
1874 	{ inst_mode_calibration, inst_mode_calibration_sym },
1875 
1876 	{ 0, NULL }
1877 };
1878 
1879 /* Return a string with a symbolic encoding of the mode flags */
inst_mode_to_sym(char sym[MAX_INST_MODE_SYM_SZ],inst_mode mode)1880 void inst_mode_to_sym(char sym[MAX_INST_MODE_SYM_SZ], inst_mode mode) {
1881 	int i;
1882 	char *cp = sym;
1883 
1884 	for (i = 0; inst_mode_sym[i].mode != 0; i++) {
1885 		if (mode & inst_mode_sym[i].mode) {
1886 			if (cp != sym)
1887 				*cp++ = '_';
1888 			strncpy(cp, inst_mode_sym[i].sym, 4);
1889 			cp += 4;
1890 		}
1891 	}
1892 	*cp++ = '\000';
1893 }
1894 
1895 /* Return a set of mode flags that correspond to the symbolic encoding */
1896 /* Return nz if a symbol wasn't recognized */
sym_to_inst_mode(inst_mode * mode,const char * sym)1897 int sym_to_inst_mode(inst_mode *mode, const char *sym) {
1898 	int i;
1899 	const char *cp = sym;
1900 	int rv = 0;
1901 
1902 	for (*mode = 0;;) {
1903 		if (cp[0] == '\000' || cp[1] == '\000' || cp[2] == '\000' || cp[3] == '\000')
1904 			break;
1905 
1906 		for (i = 0; inst_mode_sym[i].mode != 0; i++) {
1907 			if (strncmp(inst_mode_sym[i].sym, cp, 4) == 0) {
1908 				*mode |= inst_mode_sym[i].mode;
1909 				break;
1910 			}
1911 		}
1912 		if (inst_mode_sym[i].mode == 0)
1913 			rv = 1;
1914 
1915 		cp += 4;
1916 
1917 		if (*cp == '_')
1918 			cp++;
1919 	}
1920 
1921 	return rv;
1922 }
1923 
1924 /* ============================================================= */
1925 /* Utilities */
1926 
1927 /* Return a string for the xcalstd enum */
xcalstd2str(xcalstd std)1928 char *xcalstd2str(xcalstd std) {
1929 	switch(std) {
1930 		case xcalstd_native:
1931 			return "NATIVE";
1932 		case xcalstd_xrdi:
1933 			return "XRDI";
1934 		case xcalstd_gmdi:
1935 			return "GMDI";
1936 		case xcalstd_xrga:
1937 			return "XRGA";
1938 		default:
1939 			break;
1940 	}
1941 	return "None";
1942 }
1943 
1944 
1945 
1946 
1947 
1948 
1949 
1950 
1951 
1952 
1953 
1954 
1955 
1956 
1957 
1958 
1959 
1960 
1961 
1962 
1963 
1964 
1965