1 
2 /* General instrument + serial I/O support */
3 
4 /*
5  * Argyll Color Correction System
6  *
7  * Author: Graeme W. Gill
8  * Date:   28/9/97
9  *
10  * Copyright 1997 - 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 /* These routines supliement the class code in ntio.c and unixio.c */
18 /* with common and USB specific routines */
19 /* Rename to icoms.c ?? */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <time.h>
25 #include <signal.h>
26 #if defined(UNIX)
27 #include <termios.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #endif
31 #ifndef SALONEINSTLIB
32 #include "copyright.h"
33 #include "aconfig.h"
34 #else
35 #include "sa_config.h"
36 #endif
37 #include "numsup.h"
38 #include "xspect.h"
39 #include "insttypes.h"
40 #include "conv.h"
41 #include "icoms.h"
42 
43 
44 /* ----------------------------------------------------- */
45 
46 /* Fake display & instrument device */
47 icompath icomFakeDevice = { instFakeDisp, "Fake Display Device" };
48 
49 
50 
51 /* Free an icompath */
52 static
icompath_del_contents(icompath * p)53 void icompath_del_contents(icompath *p) {
54 
55 	if (p->name != NULL)
56 		free(p->name);
57 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
58 	if (p->spath != NULL)
59 		free(p->spath);
60 #endif /* ENABLE_SERIAL */
61 #ifdef ENABLE_USB
62 	usb_del_usb_idevice(p->usbd);
63 	hid_del_hid_idevice(p->hidd);
64 #endif /* ENABLE_USB */
65 }
66 
67 /* Free an icompath */
icompath_del(icompath * p)68 static void icompath_del(icompath *p) {
69 
70 	icompath_del_contents(p);
71 	free(p);
72 }
73 
74 /* Delete the last combined path */
75 /* (Assume this is before icompaths_make_dslists() has been called) */
icompaths_del_last_path(icompaths * p)76 static void icompaths_del_last_path(
77 	icompaths *p
78 ) {
79 	icom_dtix ix = dtix_combined;
80 
81 	if (p->ndpaths[ix] == 0)
82 		return;
83 
84 	icompath_del(p->dpaths[ix][p->ndpaths[ix]-1]);
85 	p->dpaths[ix][p->ndpaths[ix]-1] = NULL;
86 	p->ndpaths[ix]--;
87 }
88 
89 /* Return the last combined path */
90 /* (Assume this is before icompaths_make_dslists() has been called) */
icompaths_get_last_path(icompaths * p)91 static icompath *icompaths_get_last_path(
92 	icompaths *p
93 ) {
94 	icom_dtix ix = dtix_combined;
95 
96 	if (p->ndpaths[ix] == 0)
97 		return NULL;
98 
99 	return p->dpaths[ix][p->ndpaths[ix]-1];
100 }
101 
102 /* Return the instrument path corresponding to the port number, or NULL if out of range */
icompaths_get_path(icompaths * p,int port)103 static icompath *icompaths_get_path(
104 	icompaths *p,
105 	int port		/* Enumerated port number, 1..n */
106 ) {
107 	if (port == FAKE_DEVICE_PORT)
108 		return &icomFakeDevice;
109 
110 
111 
112 	if (port <= 0 || port > p->ndpaths[dtix_inst])
113 		return NULL;
114 
115 	return p->dpaths[dtix_inst][port-1];
116 }
117 
118 /* Return the device path corresponding to the port number, or NULL if out of range */
icompaths_get_path_sel(icompaths * p,icom_type dctype,int port)119 static icompath *icompaths_get_path_sel(
120 	icompaths *p,
121 	icom_type dctype,	/* Device type list */
122 	int port			/* Enumerated port number, 1..n */
123 ) {
124 	/* Check it is an enumeration */
125 	if (dctype != dtix_combined
126 	 && dctype != dtix_inst
127 	 && dctype != dtix_3dlut
128 	 && dctype != dtix_vtpg
129 	 && dctype != dtix_printer)
130 		return NULL;
131 
132 	if (dctype == dtix_inst)
133 		return icompaths_get_path(p, port);
134 
135 	if (port <= 0 || port > p->ndpaths[dctype])
136 		return NULL;
137 
138 	return p->dpaths[dctype][port-1];
139 }
140 
141 /* Clear a single device paths list */
icompaths_clear(icompaths * p,icom_dtix ix)142 static void icompaths_clear(icompaths *p, icom_dtix ix) {
143 
144 	if (p->dpaths[ix] != NULL) {
145 		int i;
146 		/* If underlying owner of icompaths */
147 		if (ix == dtix_combined) {
148 			for (i = 0; i < p->ndpaths[ix]; i++) {
149 				icompath_del(p->dpaths[ix][i]);
150 			}
151 		}
152 		free(p->dpaths[ix]);
153 		p->dpaths[ix] = NULL;
154 		p->ndpaths[ix] = 0;
155 
156 		/* Clear instrument alias */
157 		if (ix == dtix_inst) {
158 			p->npaths = 0;
159 			p->paths = NULL;
160 		}
161 	}
162 }
163 
164 /* Clear all device paths */
icompaths_clear_all(icompaths * p)165 static void icompaths_clear_all(icompaths *p) {
166 
167 	if (p == NULL)
168 		return;
169 
170 	/* Free any old instrument list */
171 	icompaths_clear(p, dtix_inst);
172 	icompaths_clear(p, dtix_3dlut);
173 	icompaths_clear(p, dtix_vtpg);
174 	icompaths_clear(p, dtix_printer);
175 	icompaths_clear(p, dtix_combined);
176 }
177 
178 /* Add the give path to the given list. */
179 /* If the path is NULL, allocat an empty */
180 /* one and add it to the combined list */
181 /* Return icom error */
icompaths_add_path(icompaths * p,int ix,icompath * xp)182 static int icompaths_add_path(icompaths *p, int ix, icompath *xp) {
183 	if (xp == NULL)
184 		ix = dtix_combined;
185 	if (p->dpaths[ix] == NULL) {
186 		if ((p->dpaths[ix] = (icompath **)calloc(sizeof(icompath *), 1 + 1)) == NULL) {
187 			a1loge(p->log, ICOM_SYS, "icompaths: calloc failed!\n");
188 			return ICOM_SYS;
189 		}
190 	} else {
191 		icompath **npaths;
192 		if ((npaths = (icompath **)realloc(p->dpaths[ix],
193 		                     sizeof(icompath *) * (p->ndpaths[ix] + 2))) == NULL) {
194 			a1loge(p->log, ICOM_SYS, "icompaths: realloc failed!\n");
195 			return ICOM_SYS;
196 		}
197 		p->dpaths[ix] = npaths;
198 		p->dpaths[ix][p->ndpaths[ix]+1] = NULL;
199 	}
200 	if (xp == NULL) {
201 		if ((xp = calloc(sizeof(icompath), 1)) == NULL) {
202 			a1loge(p->log, ICOM_SYS, "icompaths: malloc failed!\n");
203 			return ICOM_SYS;
204 		}
205 	}
206 	p->dpaths[ix][p->ndpaths[ix]] = xp;
207 	p->ndpaths[ix]++;
208 	p->dpaths[ix][p->ndpaths[ix]] = NULL;
209 	return ICOM_OK;
210 }
211 
212 /* Based on each icompath's dctype, create aliase lists for everything */
213 /* on the combined path based on each device type. */
214 /* (We are only called after clearing all lists) */
icompaths_make_dslists(icompaths * p)215 int icompaths_make_dslists(icompaths *p) {
216 	int rv, i;
217 
218 	for (i = 0; i < p->ndpaths[dtix_combined]; i++) {
219 		icompath *xp = p->dpaths[dtix_combined][i];
220 
221 		if (xp == NULL)
222 			break;
223 
224 		a1logd(g_log, 8, "icompaths_make_dslists '%s' dctype 0x%x\n",xp->name,xp->dctype);
225 
226 		if (xp->dctype & icomt_instrument) {
227 			if ((rv = icompaths_add_path(p, dtix_inst, xp)) != ICOM_OK)
228 				return rv;
229 		}
230 		if (xp->dctype & icomt_3dlut) {
231 			if ((rv = icompaths_add_path(p, dtix_3dlut, xp)) != ICOM_OK)
232 				return rv;
233 		}
234 		if (xp->dctype & icomt_vtpg) {
235 			if ((rv = icompaths_add_path(p, dtix_vtpg, xp)) != ICOM_OK)
236 				return rv;
237 		}
238 		if (xp->dctype & icomt_printer) {
239 			if ((rv = icompaths_add_path(p, dtix_printer, xp)) != ICOM_OK)
240 				return rv;
241 		}
242 	}
243 
244 	/* Maintain backwards compatible instrument list alias */
245 	p->npaths = p->ndpaths[dtix_inst];
246 	p->paths = p->dpaths[dtix_inst];
247 
248 	return ICOM_OK;
249 }
250 
251 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
252 
253 /* Add a serial path. */
254 /* return icom error */
icompaths_add_serial(icompaths * p,char * name,char * spath,icom_type dctype)255 static int icompaths_add_serial(icompaths *p, char *name, char *spath, icom_type dctype)  {
256 	icom_dtix ix = dtix_combined;
257 	icompath *xp;
258 	int rv;
259 
260 	if ((rv = icompaths_add_path(p, ix, NULL)) != ICOM_OK)
261 		return rv;
262 
263 	xp = p->dpaths[ix][p->ndpaths[ix]-1];
264 
265 	a1logd(g_log, 8, "icompaths_add_serial got '%s' dctype 0x%x\n",name,dctype);
266 
267 	/* Type of port, port attributes, device category */
268 	xp->dctype |= icomt_cat_any;		/* Assume any for now */
269 	xp->dctype |= icomt_serial;
270 	xp->dctype |= icomt_seriallike;
271 	xp->dctype |= dctype;
272 
273 	if ((xp->name = strdup(name)) == NULL) {
274 		a1loge(p->log, ICOM_SYS, "icompaths: strdup failed!\n");
275 		return ICOM_SYS;
276 	}
277 	if ((xp->spath = strdup(spath)) == NULL) {
278 		a1loge(p->log, ICOM_SYS, "icompaths: strdup failed!\n");
279 		return ICOM_SYS;
280 	}
281 
282 	a1logd(g_log, 8, "icompaths_add_serial returning '%s' dctype 0x%x\n",xp->name,xp->dctype);
283 
284 	return ICOM_OK;
285 }
286 
287 /* Modify a serial path to add the device type */
icompaths_set_serial_itype(icompath * p,devType itype)288 int icompaths_set_serial_itype(icompath *p, devType itype) {
289 	char pname[400], *cp;
290 
291 	/* Convert device type to category */
292 	p->dctype = (p->dctype & ~icomt_cat_mask) | inst_category(itype);
293 
294 	p->itype = itype;
295 
296 	/* Strip any existing description in brackets */
297 	if ((cp = strchr(p->name, '(')) != NULL && cp > p->name)
298 		cp[-1] = '\000';
299 	sprintf(pname,"%s (%s)", p->name, inst_name(itype));
300 	cp = p->name;
301 	if ((p->name = strdup(pname)) == NULL) {
302 		p->name = cp;
303 		a1loge(g_log, ICOM_SYS, "icompaths_set_serial_itype: strdup path failed!\n");
304 		return ICOM_SYS;
305 	}
306 	free(cp);
307 
308 	a1logd(g_log, 8, "icompaths_set_serial_itype '%s' returning dctype 0x%x\n",p->name,p->dctype);
309 
310 	return ICOM_OK;
311 }
312 
313 #endif /* ENABLE_SERIAL */
314 
315 #ifdef ENABLE_USB
316 
317 /* Set an icompath details. */
318 /* return icom error */
319 static
icompath_set_usb(a1log * log,icompath * p,char * name,unsigned int vid,unsigned int pid,int nep,struct usb_idevice * usbd,devType itype)320 int icompath_set_usb(a1log *log, icompath *p, char *name, unsigned int vid, unsigned int pid,
321                               int nep, struct usb_idevice *usbd, devType itype) {
322 	int rv;
323 
324 
325 	if ((p->name = strdup(name)) == NULL) {
326 		a1loge(log, ICOM_SYS, "icompath: strdup failed!\n");
327 		return ICOM_SYS;
328 	}
329 
330 	a1logd(g_log, 8, "icompath_set_usb '%s' got dctype 0x%x\n",p->name,p->dctype);
331 
332 	p->dctype |= icomt_usb;
333 	p->dctype = (p->dctype & ~icomt_cat_mask) | inst_category(itype);
334 
335 	p->nep = nep;
336 	p->vid = vid;
337 	p->pid = pid;
338 	p->usbd = usbd;
339 	p->itype = itype;
340 
341 	a1logd(g_log, 8, "icompath_set_usb '%s' returning dctype 0x%x\n",p->name,p->dctype);
342 
343 	return ICOM_OK;
344 }
345 
346 /* Add a usb path. usbd is taken, others are copied. */
347 /* return icom error */
icompaths_add_usb(icompaths * p,char * name,unsigned int vid,unsigned int pid,int nep,struct usb_idevice * usbd,devType itype)348 static int icompaths_add_usb(icompaths *p, char *name, unsigned int vid, unsigned int pid,
349                              int nep, struct usb_idevice *usbd, devType itype) {
350 	icom_dtix ix = dtix_combined;
351 	icompath *xp;
352 	int rv;
353 
354 	if ((rv = icompaths_add_path(p, 0, NULL)) != ICOM_OK)
355 		return rv;
356 
357 	xp = p->dpaths[ix][p->ndpaths[ix]-1];
358 
359 	return icompath_set_usb(p->log, xp, name, vid, pid, nep, usbd, itype);
360 }
361 
362 /* Add an hid path. hidd is taken, others are copied. */
363 /* return icom error */
icompaths_add_hid(icompaths * p,char * name,unsigned int vid,unsigned int pid,int nep,struct hid_idevice * hidd,devType itype)364 static int icompaths_add_hid(icompaths *p, char *name, unsigned int vid, unsigned int pid,
365                              int nep, struct hid_idevice *hidd, devType itype) {
366 	icom_dtix ix = dtix_combined;
367 	icompath *xp;
368 	int rv;
369 
370 	if ((rv = icompaths_add_path(p, 0, NULL)) != ICOM_OK)
371 		return rv;
372 
373 	xp = p->dpaths[ix][p->ndpaths[ix]-1];
374 
375 	a1logd(g_log, 8, "icompaths_add_hid '%s' got dctype 0x%x\n",xp->name,xp->dctype);
376 
377 	xp->dctype |= icomt_hid;
378 	xp->dctype = (xp->dctype & ~icomt_cat_mask) | inst_category(itype);
379 
380 	if ((xp->name = strdup(name)) == NULL) {
381 		a1loge(p->log, ICOM_SYS, "icompaths: strdup failed!\n");
382 		return ICOM_SYS;
383 	}
384 
385 	xp->nep = nep;
386 	xp->vid = vid;
387 	xp->pid = pid;
388 	xp->hidd = hidd;
389 	xp->itype = itype;
390 
391 	a1logd(g_log, 8, "icompath_set_usb '%s' returning dctype 0x%x\n",xp->name,xp->dctype);
392 
393 	return ICOM_OK;
394 }
395 #endif /* ENABLE_USB */
396 
icompaths_del(icompaths * p)397 static void icompaths_del(icompaths *p) {
398 	if (p != NULL) {
399 		icompaths_clear_all(p);
400 
401 		p->log = del_a1log(p->log);		/* Unreference it and set to NULL */
402 		free(p);
403 	}
404 }
405 
406 /* Create and return a list of available serial ports or USB devices for this system. */
407 /* We look at the registry key "HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM" */
408 /* to determine serial ports, and use libusb to discover USB devices. */
409 /* return icom error */
icompaths_refresh_paths_sel(icompaths * p,icom_type mask)410 int icompaths_refresh_paths_sel(icompaths *p, icom_type mask) {
411 	int rv, usbend = 0;
412 	int i, j;
413 
414 	a1logd(p->log, 7, "icoms_refresh_paths: called with mask = %d\n",mask);
415 
416 	/* Clear any existing device paths */
417 	p->clear(p);
418 
419 #ifdef ENABLE_USB
420 	if (mask & icomt_hid) {
421 		a1logd(p->log, 6, "icoms_refresh_paths: looking for HID device\n");
422 		if ((rv = hid_get_paths(p)) != ICOM_OK)
423 			return rv;
424 	}
425 	if (mask & icomt_usb) {
426 		a1logd(p->log, 6, "icoms_refresh_paths: looking for USB device\n");
427 		if ((rv = usb_get_paths(p)) != ICOM_OK)
428 			return rv;
429 	}
430 	usbend = p->ndpaths[dtix_combined];
431 	a1logd(p->log, 6, "icoms_refresh_paths: now got %d paths\n",usbend);
432 #endif /* ENABLE_USB */
433 
434 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
435 
436 	if (mask & (icomt_serial | icomt_fastserial | icomt_btserial)) {
437 		a1logd(p->log, 6, "icoms_refresh_paths: looking for serial ports\n");
438 		if ((rv = serial_get_paths(p, mask)) != ICOM_OK)
439 			return rv;
440 
441 	}
442 #endif /* ENABLE_SERIAL || ENABLE_FAST_SERIAL */
443 
444 	/* Sort the COM keys so people don't get confused... */
445 	/* Sort identified instruments ahead of unknown serial ports */
446 	a1logd(p->log, 6, "icoms_refresh_paths: we now have %d devices and are about to sort them\n",p->ndpaths[dtix_combined]);
447 
448 	{
449 		icompath **pl = p->dpaths[dtix_combined];
450 		int np = p->ndpaths[dtix_combined];
451 
452 		for (i = usbend; i < (np-1); i++) {
453 			for (j = i+1; j < np; j++) {
454 				if ((pl[i]->itype == instUnknown && pl[j]->itype != instUnknown)
455 				 || (((pl[i]->itype == instUnknown && pl[j]->itype == instUnknown)
456 				   || (pl[i]->itype != instUnknown && pl[j]->itype != instUnknown))
457 				  && strcmp(pl[i]->name, pl[j]->name) > 0)) {
458 					icompath *tt = pl[i];
459 					pl[i] = pl[j];
460 					pl[j] = tt;
461 				}
462 			}
463 		}
464 	}
465 
466 	/* Create the device specific lists */
467 	if ((rv = icompaths_make_dslists(p)) != ICOM_OK) {
468 		a1logd(p->log, 1, "icoms_refresh_paths: icompaths_make_dslists failed with %d\n",rv);
469 		return rv;
470 	}
471 
472 	a1logd(p->log, 8, "icoms_refresh_paths: returning %d paths and ICOM_OK\n",p->ndpaths[dtix_combined]);
473 
474 	return ICOM_OK;
475 }
476 
477 /* (In implementation specific) */
478 int serial_is_open(icoms *p);
479 void serial_close_port(icoms *p);
480 
481 /* Close the port */
icoms_close_port(icoms * p)482 static void icoms_close_port(icoms *p) {
483 	if (p->is_open) {
484 #ifdef ENABLE_USB
485 		if (p->usbd) {
486 			usb_close_port(p);
487 		} else if (p->hidd) {
488 			hid_close_port(p);
489 		}
490 #endif
491 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
492 		if (serial_is_open(p)) {
493 			serial_close_port(p);
494 		}
495 #endif /* ENABLE_SERIAL */
496 		p->is_open = 0;
497 	}
498 }
499 
icompaths_refresh_paths(icompaths * p)500 int icompaths_refresh_paths(icompaths *p) {
501 	return icompaths_refresh_paths_sel(p, icomt_instrument | icomt_portattr_all);
502 }
503 
504 /* Allocate an icom paths and set it to the list of available devices */
505 /* that match the icom_type mask. */
506 /* Return NULL on error */
new_icompaths_sel(a1log * log,icom_type mask)507 icompaths *new_icompaths_sel(a1log *log, icom_type mask) {
508 	icompaths *p;
509 	if ((p = (icompaths *)calloc(sizeof(icompaths), 1)) == NULL) {
510 		a1loge(log, ICOM_SYS, "new_icompath: calloc failed!\n");
511 		return NULL;
512 	}
513 
514 	p->log = new_a1log_d(log);
515 
516 	p->clear         = icompaths_clear_all;
517 	p->refresh       = icompaths_refresh_paths;
518 	p->refresh_sel   = icompaths_refresh_paths_sel;
519 	p->get_path      = icompaths_get_path;
520 	p->get_path_sel  = icompaths_get_path_sel;
521 	p->del           = icompaths_del;
522 
523 	/* ====== internal implementation ======= */
524 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
525 	p->add_serial    = icompaths_add_serial;
526 #endif /* ENABLE_SERIAL */
527 #ifdef ENABLE_USB
528 	p->add_usb       = icompaths_add_usb;
529 	p->add_hid       = icompaths_add_hid;
530 #endif /* ENABLE_USB */
531 	p->del_last_path = icompaths_del_last_path;
532 	p->get_last_path = icompaths_get_last_path;
533 	/* ====================================== */
534 
535 	if (icompaths_refresh_paths_sel(p, mask)) {
536 		a1loge(log, ICOM_SYS, "new_icompaths: icompaths_refresh_paths failed!\n");
537 		free(p);
538 		return NULL;
539 	}
540 
541 	return p;
542 }
543 
544 /* Allocate an icom paths and set it to the list of available instruments */
545 /* Return NULL on error */
new_icompaths(a1log * log)546 icompaths *new_icompaths(a1log *log) {
547 	return new_icompaths_sel(log, icomt_instrument | icomt_portattr_all);
548 }
549 
550 
551 /* ----------------------------------------------------- */
552 
553 /* Copy an icompath into an icom */
554 /* return icom error */
icom_copy_path_to_icom(icoms * p,icompath * pp)555 static int icom_copy_path_to_icom(icoms *p, icompath *pp) {
556 	int rv;
557 
558 	if (p->name != NULL)
559 		free(p->name);
560 	if ((p->name = strdup(pp->name)) == NULL) {
561 		a1loge(p->log, ICOM_SYS, "copy_path_to_icom: malloc name failed\n");
562 		return ICOM_SYS;
563 	}
564 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
565 	if (pp->spath != NULL) {
566 		if ((p->spath = strdup(pp->spath)) == NULL) {
567 			a1loge(p->log, ICOM_SYS, "copy_path_to_icom: malloc spath failed\n");
568 			return ICOM_SYS;
569 		}
570 	} else {
571 		p->spath = NULL;
572 	}
573 #endif /* ENABLE_SERIAL */
574 #ifdef ENABLE_USB
575 	p->nep = pp->nep;
576 	p->vid = pp->vid;
577 	p->pid = pp->pid;
578 	if ((rv = usb_copy_usb_idevice(p, pp)) != ICOM_OK)
579 		return rv;
580 	if ((rv = hid_copy_hid_idevice(p, pp)) != ICOM_OK)
581 		return rv;
582 #endif
583 	p->dctype = pp->dctype;
584 	p->itype = pp->itype;
585 
586 	a1logd(g_log, 8, "icom_copy_path_to_icom '%s' returning dctype 0x%x\n",p->name,p->dctype);
587 
588 	return ICOM_OK;
589 }
590 
591 /* Return the device category */
592 /* (Returns bit flags) */
icoms_cat_type(icoms * p)593 static icom_type icoms_cat_type(
594 icoms *p
595 ) {
596 	return p->dctype & icomt_cat_mask;
597 }
598 
599 /* Return the communication port type */
600 /* (Can use equality tests on return value) */
icoms_port_type(icoms * p)601 static icom_type icoms_port_type(
602 icoms *p
603 ) {
604 	return p->dctype & icomt_port_mask;
605 }
606 
607 /* Return the communication port attributes */
608 /* (Returns bit flags) */
icoms_port_attr(icoms * p)609 static icom_type icoms_port_attr(
610 icoms *p
611 ) {
612 	return p->dctype & icomt_attr_mask;
613 }
614 
615 /* ----------------------------------------------------- */
616 
617 /* Include the icoms implementation dependent function implementations */
618 #ifdef NT
619 # include "icoms_nt.c"
620 #endif
621 #if defined(UNIX)
622 # include "icoms_ux.c"
623 #endif
624 
625 /* write and read convenience function with read flush */
626 /* return icom error */
627 static int
icoms_write_read_ex(icoms * p,char * wbuf,int nwch,char * rbuf,int bsize,int * bread,char * tc,int ntc,double tout,int frbw)628 icoms_write_read_ex(
629 icoms *p,
630 char *wbuf,			/* Write puffer */
631 int nwch,			/* if > 0, number of characters to write, else nul terminated */
632 char *rbuf,			/* Read buffer */
633 int bsize,			/* Buffer size */
634 int *bread,			/* Bytes read (not including forced '\000') */
635 char *tc,			/* Terminating characers, NULL for none or char count mode */
636 int ntc,			/* Number of terminating characters needed, or char count needed */
637 double tout,		/* Timeout for write and then read (i.e. max = 2 x tout) */
638 int frbw			/* nz to Flush Read Before Write */
639 ) {
640 	int rv = ICOM_OK;
641 
642 	a1logd(p->log, 8, "icoms_write_read: called\n");
643 
644 	if (p->write == NULL || p->read == NULL) {	/* Neither serial nor USB ? */
645 		a1loge(p->log, ICOM_NOTS, "icoms_write_read: No write and read functions set!\n");
646 		return ICOM_NOTS;
647 	}
648 
649 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
650 	/* Flush any stray chars if serial */
651 	if (frbw && (p->dctype & icomt_serial)) {
652 		char tbuf[500];
653 		int debug = p->log->debug;
654 		int bread;
655 
656 		if (debug < 8)
657 			p->log->debug =  0;
658 		for (;;) {
659 			bread = 0;
660 			p->read(p, tbuf, 500, &bread, NULL, 500, 0.02);
661 			if (bread == 0)
662 				break;
663 		}
664 		p->log->debug = debug;
665 		rv = ICOM_OK;
666 	}
667 #endif
668 
669 	/* Write the write data */
670 	rv = p->write(p, wbuf, nwch, tout);
671 
672 	/* Return error if coms */
673 	if (rv != ICOM_OK) {
674 		a1logd(p->log, 8, "icoms_write_read: failed - returning 0x%x\n",rv);
675 		return rv;
676 	}
677 
678 	/* Read response */
679 	rv = p->read(p, rbuf, bsize, bread, tc, ntc, tout);
680 
681 	a1logd(p->log, 8, "icoms_write_read: returning 0x%x\n",rv);
682 
683 	return rv;
684 }
685 
686 /* write and read convenience function */
687 /* return icom error */
688 static int
icoms_write_read(icoms * p,char * wbuf,int nwch,char * rbuf,int bsize,int * bread,char * tc,int ntc,double tout)689 icoms_write_read(
690 icoms *p,
691 char *wbuf,			/* Write puffer */
692 int nwch,			/* if > 0, number of characters to write, else nul terminated */
693 char *rbuf,			/* Read buffer */
694 int bsize,			/* Buffer size */
695 int *bread,			/* Bytes read (not including forced '\000') */
696 char *tc,			/* Terminating characers, NULL for none or char count mode */
697 int ntc,			/* Number of terminating characters needed, or char count needed */
698 double tout		/* Timeout for write and then read (i.e. max = 2 x tout) */
699 ) {
700 	return icoms_write_read_ex(p, wbuf, nwch, rbuf, bsize, bread, tc, ntc, tout, 0);
701 }
702 
703 /* Optional callback to client from device */
704 /* Default implementation is a NOOP */
icoms_interrupt(icoms * p,int icom_int)705 static int icoms_interrupt(icoms *p,
706 	int icom_int		/* Interrupt cause */
707 ) {
708 	return ICOM_OK;
709 }
710 
711 /* Destroy ourselves */
712 static void
icoms_del(icoms * p)713 icoms_del(icoms *p) {
714 	a1logd(p->log, 8, "icoms_del: called\n");
715 	if (p->is_open) {
716 		a1logd(p->log, 8, "icoms_del: closing port\n");
717 		p->close_port(p);
718 	}
719 #ifdef ENABLE_USB
720 	usb_del_usb(p);
721 	hid_del_hid(p);
722 #endif
723 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
724 	if (p->spath != NULL)
725 		free(p->spath);
726 #endif
727 	p->log = del_a1log(p->log);
728 	if (p->name != NULL)
729 		free(p->name);
730 	p->log = del_a1log(p->log);		/* unref */
731 	free (p);
732 }
733 
734 /* icoms Constructor */
735 /* Return NULL on error */
new_icoms(icompath * ipath,a1log * log)736 icoms *new_icoms(
737 	icompath *ipath,	/* instrument to open */
738 	a1log *log			/* log to take reference from, NULL for default */
739 ) {
740 	icoms *p;
741 
742 	a1logd(log, 2, "new_icoms '%s' itype '%s' dctype 0x%x\n",ipath->name,inst_sname(ipath->itype),ipath->dctype);
743 
744 	if ((p = (icoms *)calloc(sizeof(icoms), 1)) == NULL) {
745 		a1loge(log, ICOM_SYS, "new_icoms: calloc failed!\n");
746 		return NULL;
747 	}
748 
749 	if ((p->name = strdup(ipath->name)) == NULL) {
750 		a1loge(log, ICOM_SYS, "new_icoms: strdup failed!\n");
751 		return NULL;
752 	}
753 	p->itype = ipath->itype;
754 
755 	/* Copy ipath info to this icoms */
756 	if (icom_copy_path_to_icom(p, ipath)) {
757 		free(p->name);
758 		free(p);
759 		return NULL;
760 	}
761 
762 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
763 #ifdef NT
764 	p->phandle = NULL;
765 #endif
766 #ifdef UNIX
767 	p->fd = -1;
768 #endif
769 	p->fc = fc_nc;
770 	p->br = baud_nc;
771 	p->py = parity_nc;
772 	p->sb = stop_nc;
773 	p->wl = length_nc;
774 #endif	/* ENABLE_SERIAL */
775 
776 	p->lserr = 0;
777 //	p->tc = -1;
778 
779 	p->log = new_a1log_d(log);
780 	p->debug = p->log->debug;		/* Legacy code */
781 
782 	p->close_port = icoms_close_port;
783 
784 	p->dev_cat   = icoms_cat_type;
785 	p->port_type = icoms_port_type;
786 	p->port_attr = icoms_port_attr;
787 
788 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
789 	p->set_ser_port_ex = icoms_set_ser_port_ex;
790 	p->set_ser_port = icoms_set_ser_port;
791 #endif	/* ENABLE_SERIAL */
792 
793 	p->write = NULL;	/* Serial open or set_methods will set */
794 	p->read = NULL;
795 	p->write_read = icoms_write_read;
796 	p->write_read_ex = icoms_write_read_ex;
797 	p->interrupt = icoms_interrupt;
798 
799 	p->del = icoms_del;
800 
801 #ifdef ENABLE_USB
802 	usb_set_usb_methods(p);
803 	hid_set_hid_methods(p);
804 #endif /* ENABLE_USB */
805 
806 	return p;
807 }
808 
809 /* ---------------------------------------------------------------------------------*/
810 /* utilities */
811 
812 /* Emit a "normal" beep */
normal_beep()813 void normal_beep() {
814 	/* 0msec delay, 1.0KHz for 200 msec */
815 	msec_beep(0, 1000, 200);
816 }
817 
818 /* Emit a "good" beep */
good_beep()819 void good_beep() {
820 	/* 0msec delay, 1.2KHz for 200 msec */
821 	msec_beep(0, 1200, 200);
822 }
823 
824 /* Emit a "bad" double beep */
bad_beep()825 void bad_beep() {
826 	/* 0 msec delay, 800Hz for 200 msec */
827 	msec_beep(0, 800, 200);
828 	/* 350 msec delay, 800Hz for 200 msec */
829 	msec_beep(350, 800, 200);
830 }
831 
baud_rate_to_str(baud_rate br)832 char *baud_rate_to_str(baud_rate br) {
833 	switch (br) {
834 		case baud_nc:
835 			return "Not Configured";
836 		case baud_110:
837 			return "110";
838 		case baud_300:
839 			return "300";
840 		case baud_600:
841 			return "600";
842 		case baud_1200:
843 			return "1400";
844 		case baud_2400:
845 			return "2400";
846 		case baud_4800:
847 			return "4800";
848 		case baud_9600:
849 			return "9600";
850 		case baud_14400:
851 			return "14400";
852 		case baud_19200:
853 			return "19200";
854 		case baud_38400:
855 			return "38400";
856 		case baud_57600:
857 			return "57600";
858 		case baud_115200:
859 			return "115200";
860 		case baud_921600:
861 			return "921600";
862 	}
863 	return "Unknown";
864 }
865 
866 /* Convert control chars to ^[A-Z] notation in a string */
867 /* Can be called 3 times without reusing the static buffer */
868 /* Returns a maximum of 5000 characters */
869 char *
icoms_fix(char * ss)870 icoms_fix(char *ss) {
871 	static unsigned char buf[3][10005];
872 	static int ix = 0;
873 	unsigned char *d;
874 	unsigned char *s = (unsigned char *)ss;
875 	if (++ix >= 3)
876 		ix = 0;
877 	if (ss == NULL) {
878 		strcpy((char *)buf[ix],"(null)");
879 		return (char *)buf[ix];
880 	}
881 	for(d = buf[ix]; (d - buf[ix]) < 10000;) {
882 		if (*s < ' ' && *s > '\000') {
883 			*d++ = '^';
884 			*d++ = *s++ + '@';
885 		} else if (*s >= 0x80) {
886 			*d++ = '\\';
887 			*d++ = '0' + ((*s >> 6) & 0x3);
888 			*d++ = '0' + ((*s >> 3) & 0x7);
889 			*d++ = '0' + ((*s++ >> 0) & 0x7);
890 		} else {
891 			*d++ = *s++;
892 		}
893 		if (s[-1] == '\000')
894 			break;
895 	}
896 	*d++ = '.';
897 	*d++ = '.';
898 	*d++ = '.';
899 	*d++ = '\000';
900 	return (char *)buf[ix];
901 }
902 
903 /* Convert a limited binary buffer to a list of hex */
904 char *
icoms_tohex(unsigned char * s,int len)905 icoms_tohex(unsigned char *s, int len) {
906 	static char buf[64 * 3 + 10];
907 	int i;
908 	char *d;
909 
910 	buf[0] = '\000';
911 	for(i = 0, d = buf; i < 64 && i < len; i++, s++) {
912 		sprintf(d, "%s%02x", i > 0 ? " " : "", *s);
913 		d += strlen(d);
914 	}
915 	if (i < len)
916 		sprintf(d, " ...");
917 
918 	return buf;
919 }
920 
921