1 
2  /* General USB I/O support */
3 
4 /*
5  * Argyll Color Correction System
6  *
7  * Author: Graeme W. Gill
8  * Date:   2006/22/4
9  *
10  * Copyright 2006 - 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 icoms_nt.c and icoms_ux.c */
18 /* with common and USB specific routines */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <time.h>
23 #include <signal.h>
24 #if defined(UNIX)
25 #include <termios.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #endif
29 #ifndef SALONEINSTLIB
30 #include "copyright.h"
31 #include "aconfig.h"
32 #else
33 #include "sa_config.h"
34 #endif
35 #include "numsup.h"
36 #include "xspect.h"
37 #include "insttypes.h"
38 #include "conv.h"
39 #include "icoms.h"
40 
41 #ifdef ENABLE_USB
42 
43 /* Counter set when we're in a USB read or write */
44 /* Note - this isn't perfectly thread safe */
45 int in_usb_rw = 0;
46 
47 /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
48 /* Cancel token utility functions */
49 
50 /* Used by caller of icoms to init and uninit token */
usb_init_cancel(usb_cancelt * p)51 void usb_init_cancel(usb_cancelt *p) {
52 
53 	amutex_init(p->cmtx);
54 	amutex_init(p->condx);
55 
56 	p->hcancel = NULL;
57 }
58 
usb_uninit_cancel(usb_cancelt * p)59 void usb_uninit_cancel(usb_cancelt *p) {
60 	amutex_del(p->cmtx);
61 	amutex_del(p->condx);
62 }
63 
64 /* Used by caller of icoms to re-init for wait_io */
65 /* Must be called before icoms_usb_wait_io() */
usb_reinit_cancel(usb_cancelt * p)66 void usb_reinit_cancel(usb_cancelt *p) {
67 
68 	amutex_lock(p->cmtx);
69 
70 	p->hcancel = NULL;
71 	p->state = 0;
72 	amutex_lock(p->condx);		/* Block until IO is started */
73 
74 	amutex_unlock(p->cmtx);
75 }
76 
77 /* Wait for the given transaction to be pending or complete. */
icoms_usb_wait_io(icoms * p,usb_cancelt * cancelt)78 static int icoms_usb_wait_io(
79 	icoms *p,
80 	usb_cancelt *cancelt
81 ) {
82 	amutex_lock(cancelt->condx);	/* Wait for unlock */
83 	amutex_unlock(cancelt->condx);	/* Free it up for next time */
84 	return ICOM_OK;
85 }
86 
87 /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
88 
89 /* Include the USB implementation dependent function implementations */
90 # ifdef NT
91 #  include "usbio_nt.c"
92 # endif
93 # if defined(UNIX_APPLE)
94 #  include "usbio_ox.c"
95 # endif
96 # if defined(UNIX_X11)
97 #  if defined(__FreeBSD__) || defined(__OpenBSD__)
98 #   include "usbio_bsd.c"
99 #  else
100 #   include "usbio_lx.c"
101 #  endif
102 # endif
103 
104 /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
105 /* I/O routines supported by icoms - uses platform independent */
106 /* USB routines implemented by code in usbio_*.c above */
107 
108 /* USB control message */
109 static int
icoms_usb_control(icoms * p,int requesttype,int request,int value,int index,unsigned char * rwbuf,int rwsize,double tout)110 icoms_usb_control(
111 icoms *p,
112 int requesttype,		/* 8 bit request type (USB bmRequestType) */
113 int request,			/* 8 bit request code (USB bRequest) */
114 int value,				/* 16 bit value (USB wValue) */
115 int index,				/* 16 bit index (USB wIndex) */
116 unsigned char *rwbuf,	/* Write or read buffer */
117 int rwsize, 			/* Bytes to read or write */
118 double tout				/* Timeout in seconds */
119 ) {
120 	int rv = 0;			/* Return value */
121 	int rwbytes;		/* Data bytes read or written */
122 	long top;			/* timeout in msec */
123 
124 	if (p->log->debug >= 8) {
125 		a1logd(p->log, 8, "icoms_usb_control: message  %02x, %02x %04x %04x %04x\n",
126 	                                 requesttype, request, value, index, rwsize);
127 		if ((requesttype & IUSB_ENDPOINT_IN) == 0)
128 			a1logd(p->log, 8, " writing data %s\n",icoms_tohex(rwbuf, rwsize));
129 	}
130 
131 	if (!p->is_open) {
132 		a1loge(p->log, ICOM_SYS, "icoms_usb_control: device not open\n");
133 		return ICOM_SYS;
134 	}
135 
136 	top = (int)(tout * 1000.0 + 0.5);		/* Timout in msec */
137 
138 #ifdef QUIET_MEMCHECKERS
139 	if (requesttype & IUSB_ENDPOINT_IN)
140 		memset(rwbuf, 0, rwsize);
141 #endif
142 
143 	/* Call back end implementation */
144 	rv = icoms_usb_control_msg(p, &rwbytes, requesttype, request, value, index,
145 	                                                          rwbuf, rwsize, top);
146 	a1logd(p->log, 8, "icoms_usb_control: returning ICOM err 0x%x\n",rv);
147 
148 	if (p->log->debug >= 8 && (requesttype & IUSB_ENDPOINT_IN))
149 		a1logd(p->log, 8, " read data %s\n",icoms_tohex(rwbuf, rwsize));
150 
151 	return rv;
152 }
153 
154 /* - - - - - - - - - - - - - */
155 /* USB Read/Write. Direction is set by ep. */
156 /* Don't retry on a short read, return ICOM_SHORT. */
157 static int
icoms_usb_rw(icoms * p,usb_cancelt * cancelt,int ep,unsigned char * rbuf,int bsize,int * breadp,double tout)158 icoms_usb_rw(icoms *p,
159 	usb_cancelt  *cancelt,	/* Cancel handle */
160 	int ep,					/* End point address */
161 	unsigned char *rbuf,	/* Read/Write buffer */
162 	int bsize,				/* Bytes to read */
163 	int *breadp,			/* Bytes read/written */
164 	double tout				/* Timeout in seconds */
165 ) {
166 	int lerr;				/* Last error */
167 	int bread, qa;
168 	long top;				/* Timeout period */
169 	icom_usb_trantype type;	/* bulk or interrupt */
170 
171 #ifdef QUIET_MEMCHECKERS
172 	memset(rbuf, 0, bsize);
173 #endif
174 
175 	if (!p->is_open) {
176 		a1loge(p->log, ICOM_SYS, "icoms_usb_rw: device not initialised\n");
177 		return ICOM_SYS;
178 	}
179 
180 	if (p->EPINFO(ep).valid == 0) {
181 		a1loge(p->log, ICOM_SYS, "icoms_usb_rw: invalid end point 0x%02x\n",ep);
182 		return ICOM_SYS;
183 	}
184 
185 	if (p->EPINFO(ep).type != ICOM_EP_TYPE_BULK
186 	 && p->EPINFO(ep).type != ICOM_EP_TYPE_INTERRUPT) {
187 		a1loge(p->log, ICOM_SYS, "icoms_usb_rw: unhandled end point type %d\n",p->EPINFO(ep).type);
188 		return ICOM_SYS;
189 	}
190 
191 	if (p->EPINFO(ep).type == ICOM_EP_TYPE_BULK)
192 		type = icom_usb_trantype_bulk;
193 	else
194 		type = icom_usb_trantype_interrutpt;
195 
196 	qa = bsize;						/* For simpler tracing */
197 
198 	lerr = 0;
199 	bread = 0;
200 
201 	top = (int)(tout * 1000 + 0.5);		/* Timeout period in msecs */
202 
203 	/* Bug workaround - on some OS's for some devices */
204 	if (p->uflags & icomuf_resetep_before_read
205 	 && (ep & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_IN) {
206 		msec_sleep(1);		/* Let device recover ? */
207 		p->usb_resetep(p, ep);
208 		msec_sleep(1);		/* Let device recover (ie. Spyder 3) */
209 	}
210 
211 	/* Until data is all read/written, we get a short read/write, we time out, or the user aborts */
212 //	a1logd(p->log, 8, "icoms_usb_rw: read/write of %d bytes, timout %f\n",bsize,tout);
213 	while (bsize > 0) {
214 		int rv, rbytes;
215 		int rsize = bsize > qa ? qa : bsize;
216 
217 //		a1logd(p->log, 8, "icoms_usb_rw: read/write %d bytes this time\n",rsize);
218 		rv = icoms_usb_transaction(p, cancelt, &rbytes, type, (unsigned char)ep, rbuf, rsize, top);
219 		if (rv != 0 && rv != ICOM_SHORT) {
220 			lerr = rv;
221 			break;
222 		} else {	/* Account for bytes read/written */
223 			bsize -= rbytes;
224 			rbuf += rbytes;
225 			bread += rbytes;
226 		}
227 		if (rbytes != rsize) {
228 			lerr |= ICOM_SHORT;
229 			break;
230 		}
231 	}
232 
233 	if (breadp != NULL)
234 		*breadp = bread;
235 
236 	a1logd(p->log, 8, "icoms_usb_rw: returning %d bytes, ICOM err 0x%x\n",bread, lerr);
237 
238 	return lerr;
239 }
240 
241 /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
242 
243 /* Static list so that all open USB/HID connections can be closed on a SIGKILL */
244 static icoms *icoms_list = NULL;
245 
246 /* Clean up any open USB ports ready for exit on signal */
icoms_cleanup()247 static void icoms_cleanup() {
248 	icoms *pp, *np;
249 
250 #if defined(UNIX)
251 	/* This is a bit of a hack to compensate for the fact */
252 	/* that a ^C will kill the program while ICANON is off. */
253 	/* It's really better to restore the original attributes, */
254 	/* even when USB is not compiled in. */
255 	struct termios news;
256 	if (tcgetattr(STDIN_FILENO, &news) >= 0) {
257 		news.c_lflag |= (ICANON | ECHO);
258 		tcsetattr(STDIN_FILENO,TCSANOW, &news);
259 	}
260 #endif
261 
262 	for (pp = icoms_list; pp != NULL; pp = np) {
263 		np = pp->next;
264 		a1logd(pp->log, 6, "icoms_cleanup: closing usb port 0x%x\n",pp);
265 		/* There's a problem here if have more than one USB port */
266 		/* open - win32 doesn't return from the system call. */
267 		/* Should we depend on usb read/write routines to call cleanup ? */
268 		pp->close_port(pp);
269 	}
270 }
271 
272 #ifdef NT
273 void (__cdecl *usbio_int)(int sig) = SIG_DFL;
274 void (__cdecl *usbio_term)(int sig) = SIG_DFL;
275 #endif
276 #ifdef UNIX
277 void (*usbio_hup)(int sig) = SIG_DFL;
278 void (*usbio_int)(int sig) = SIG_DFL;
279 void (*usbio_term)(int sig) = SIG_DFL;
280 #endif
281 
282 /* On something killing our process, deal with USB cleanup */
icoms_sighandler(int arg)283 static void icoms_sighandler(int arg) {
284 	static amutex_static(lock);
285 
286 	a1logd(g_log, 6, "icoms_sighandler: invoked with arg = %d\n",arg);
287 
288 	/* Make sure we don't re-enter */
289 	if (amutex_trylock(lock)) {
290 		return;
291 	}
292 
293 	if (in_usb_rw != 0)
294 		in_usb_rw = -1;
295 	icoms_cleanup();
296 
297 	/* Call the existing handlers */
298 #ifdef UNIX
299 	if (arg == SIGHUP && usbio_hup != SIG_DFL && usbio_hup != SIG_IGN)
300 		usbio_hup(arg);
301 #endif /* UNIX */
302 	if (arg == SIGINT && usbio_int != SIG_DFL && usbio_int != SIG_IGN)
303 		usbio_int(arg);
304 	if (arg == SIGTERM && usbio_term != SIG_DFL && usbio_term != SIG_IGN)
305 		usbio_term(arg);
306 
307 	a1logd(g_log, 6, "icoms_sighandler: calling exit()\n");
308 
309 	amutex_unlock(lock);
310 	exit(0);
311 }
312 
313 /* - - - - - - - - - - - - - - - - - - - */
314 
315 /* Install the cleanup signal handlers */
usb_install_signal_handlers(icoms * p)316 void usb_install_signal_handlers(icoms *p) {
317 	if (icoms_list == NULL) {
318 		a1logd(g_log, 6, "usb_install_signal_handlers: called\n");
319 #if defined(UNIX)
320 		usbio_hup = signal(SIGHUP, icoms_sighandler);
321 #endif /* UNIX */
322 		usbio_int = signal(SIGINT, icoms_sighandler);
323 		usbio_term = signal(SIGTERM, icoms_sighandler);
324 	}
325 
326 	/* Add it to our static list, to allow automatic cleanup on signal */
327 	p->next = icoms_list;
328 	icoms_list = p;
329 	a1logd(g_log, 6, "usb_install_signal_handlers: done\n");
330 }
331 
332 /* Delete an icoms from our static signal cleanup list */
usb_delete_from_cleanup_list(icoms * p)333 void usb_delete_from_cleanup_list(icoms *p) {
334 
335 	/* Find it and delete it from our static cleanup list */
336 	if (icoms_list != NULL) {
337 		if (icoms_list == p) {
338 			icoms_list = p->next;
339 			if (icoms_list == NULL) {
340 #if defined(UNIX)
341 				signal(SIGHUP, usbio_hup);
342 #endif /* UNIX */
343 				signal(SIGINT, usbio_int);
344 				signal(SIGTERM, usbio_term);
345 			}
346 		} else {
347 			icoms *pp;
348 			for (pp = icoms_list; pp != NULL; pp = pp->next) {
349 				if (pp->next == p) {
350 					pp->next = p->next;
351 					break;
352 				}
353 			}
354 		}
355 	}
356 }
357 
358 /* ========================================================= */
359 /* USB write/read "serial" imitation */
360 
361 /* Write the characters in the buffer out */
362 /* Data will be written up to the terminating nul */
363 /* Return relevant error status bits */
364 static int
icoms_usb_ser_write(icoms * p,char * wbuf,int nwch,double tout)365 icoms_usb_ser_write(
366 icoms *p,
367 char *wbuf,			/* null terminated unless nch > 0 */
368 int nwch,			/* if > 0, number of characters to write */
369 double tout)
370 {
371 	int len, wbytes;
372 	long ttop, top;			/* Total timeout period, timeout period */
373 	unsigned int stime, etime;		/* Start and end times of USB operation */
374 	int ep = p->wr_ep;		/* End point */
375 	icom_usb_trantype type;	/* bulk or interrupt */
376 	int retrv = ICOM_OK;
377 
378 	a1logd(p->log, 8, "\nicoms_usb_ser_write: writing '%s'\n",
379 	       nwch > 0 ? icoms_tohex((unsigned char *)wbuf, nwch) : icoms_fix(wbuf));
380 
381 	if (!p->is_open) {
382 		a1loge(p->log, ICOM_SYS, "icoms_usb_ser_write: device is not open\n");
383 		return ICOM_SYS;
384 	}
385 
386 	if (p->EPINFO(ep).valid == 0) {
387 		a1loge(p->log, ICOM_SYS, "icoms_usb_ser_write: invalid end point 0x%02x\n",ep);
388 		return ICOM_SYS;
389 	}
390 
391 	if (p->EPINFO(ep).type != ICOM_EP_TYPE_BULK
392 	 && p->EPINFO(ep).type != ICOM_EP_TYPE_INTERRUPT) {
393 		a1loge(p->log, ICOM_SYS, "icoms_usb_ser_write: unhandled end point type %d",p->EPINFO(ep).type);
394 		return ICOM_SYS;
395 	}
396 
397 	if (p->EPINFO(ep).type == ICOM_EP_TYPE_BULK)
398 		type = icom_usb_trantype_bulk;
399 	else
400 		type = icom_usb_trantype_interrutpt;
401 
402 	if (nwch != 0)
403 		len = nwch;
404 	else
405 		len = strlen(wbuf);
406 
407 	ttop = (int)(tout * 1000.0 + 0.5);        /* Total timeout period in msecs */
408 
409 	a1logd(p->log, 8, "\nicoms_usb_ser_write: ep 0x%x, bytes %d, ttop %d, quant %d\n", p->rd_ep, len, ttop, p->rd_qa);
410 
411 	etime = stime = msec_time();
412 
413 	/* Until data is all written, we time out, or the user aborts */
414 	for (top = ttop; top > 0 && len > 0;) {
415 		int rv;
416 		a1logd(p->log, 8, "icoms_usb_ser_write: attempting to write %d bytes to usb top = %d\n",len,top);
417 
418 		rv = icoms_usb_transaction(p, NULL, &wbytes, type, (unsigned char)ep, (unsigned char *)wbuf, len, top);
419 		etime = msec_time();
420 		if (rv != ICOM_OK) {
421 			if (rv != ICOM_TO) {
422 				retrv |= rv;
423 				break;
424 			}
425 		} else {	/* Account for bytes written */
426 			a1logd(p->log, 8, "icoms_usb_ser_write: wrote %d bytes\n",wbytes);
427 			wbuf += wbytes;
428 			len -= wbytes;
429 		}
430 		top = ttop - (etime - stime);	/* Remaining time */
431 	}
432 
433 	if (top <= 0) {			/* Must have timed out */
434 		a1logd(p->log, 8, "icoms_usb_ser_write: timeout, took %d msec out of %d\n",etime - stime,ttop);
435 		retrv |= ICOM_TO;
436 	}
437 
438 	a1logd(p->log, 8, "icoms_usb_ser_write: took %d msec, returning ICOM err 0x%x\n",etime - stime,retrv);
439 
440 	return retrv;
441 }
442 
443 
444 /* Read characters into the buffer */
445 /* Return string will be terminated with a nul */
446 /* Read only in packet sized chunks, and retry if */
447 /* the bytes requested aren't read, untill we get a */
448 /* timeout or a terminating char is read */
449 static int
icoms_usb_ser_read(icoms * p,char * rbuf,int bsize,int * pbread,char * tc,int ntc,double tout)450 icoms_usb_ser_read(icoms *p,
451 char *rbuf,			/* Buffer to store characters read */
452 int bsize,			/* Buffer size */
453 int *pbread,		/* Bytes read (not including forced '\000') */
454 char *tc,			/* Terminating characers, NULL for none or char count mode */
455 int ntc,			/* Number of terminating characters or char count needed */
456 double tout)		/* Time out in seconds */
457 {
458 	int j, rbytes;
459 	long ttop, top;			/* Total timeout period, timeout period */
460 	unsigned int stime, etime;		/* Start and end times of USB operation */
461 	char *rrbuf = rbuf;		/* Start of return buffer */
462 	int ep = p->rd_ep;		/* End point */
463 	icom_usb_trantype type;	/* bulk or interrupt */
464 	int retrv = ICOM_OK;
465 	int nreads;			/* Number of reads performed */
466 	int fastserial = 0;		/* If fast serial type */
467 
468 	if (!p->is_open) {
469 		a1loge(p->log, ICOM_SYS, "icoms_usb_ser_read: device is not open\n");
470 		return ICOM_SYS;
471 	}
472 
473 	if (p->EPINFO(ep).valid == 0) {
474 		a1loge(p->log, ICOM_SYS, "icoms_usb_ser_read: invalid end point 0x%02x\n",ep);
475 		return ICOM_SYS;
476 	}
477 
478 	if (p->EPINFO(ep).type != ICOM_EP_TYPE_BULK
479 	 && p->EPINFO(ep).type != ICOM_EP_TYPE_INTERRUPT) {
480 		a1loge(p->log, ICOM_SYS, "icoms_usb_ser_read: unhandled end point type %d",p->EPINFO(ep).type);
481 		return ICOM_SYS;
482 	}
483 
484 	if (p->EPINFO(ep).type == ICOM_EP_TYPE_BULK)
485 		type = icom_usb_trantype_bulk;
486 	else
487 		type = icom_usb_trantype_interrutpt;
488 
489 	if (bsize < 3) {
490 		a1loge(p->log, ICOM_SYS, "icoms_usb_ser_read given too small a buffer (%d)", bsize);
491 		return ICOM_SYS;
492 	}
493 
494 	if (p->dctype & icomt_fastserial)
495 		fastserial  = 1;
496 
497 	for (j = 0; j < bsize; j++)
498 		rbuf[j] = 0;
499 
500 	/* The DTP94 doesn't cope with a timeout on OS X, so we need to avoid */
501 	/* them by giving each read the largest timeout period possible. */
502 	/* This also reduces the problem of libusb 0.1 not returning the */
503 	/* number of characters read on a timeout. */
504 
505 	ttop = (int)(tout * 1000.0 + 0.5);        /* Total timeout period in msecs */
506 
507 	a1logd(p->log, 8, "\nicoms_usb_ser_read: ep 0x%x, bytes %d, ttop %d, ntc %d, quant %d\n", p->rd_ep, bsize, ttop, ntc, p->rd_qa);
508 
509 	bsize -= 1;				/* Allow space for null */
510 	bsize -= p->ms_bytes;	/* Allow space for modem status bytes */
511 
512 	j = (tc == NULL && ntc <= 0) ? -1 : 0;
513 	etime = stime = msec_time();
514 
515 	/* Until data is all read, we time out, or the user aborts */
516 	for (top = ttop, nreads = 0; top > 0 && bsize > 0 && j < ntc ;) {
517 		int c, rv;
518 		int rsize = bsize;
519 
520 		/* If not a fast USB/BT serial port, read in quanta size chunks */
521 		if (!fastserial && rsize > p->rd_qa)
522 			rsize = p->rd_qa;
523 
524 		a1logd(p->log, 8, "icoms_usb_ser_read: attempting to read %d bytes from usb, top = %d, j = %d\n",rsize,top,j);
525 
526 		rv = icoms_usb_transaction(p, NULL, &rbytes, type, (unsigned char)ep, (unsigned char *)rbuf, rsize, top);
527 		etime = msec_time();
528 		nreads++;
529 
530 		if (rbytes > 0) {	/* Account for bytes read */
531 
532 			/* Account for modem status bytes. Modem bytes are per usb read, */
533 			/* or every p->rd_qa bytes. */
534 			if (p->ms_bytes) {		/* Throw away modem bytes */
535 				char *bp = rbuf;
536 				int rb = rbytes;
537 				for (; rb > 0; ) {
538 					int nb = rb < p->ms_bytes ? rb : p->ms_bytes;	/* Bytes to shift */
539 					if (p->interp_ms != NULL && nb >= p->ms_bytes)
540 						retrv |= p->interp_ms(p, (unsigned char *)bp);	/* Deal with error flags in ms bytes */
541 					a1logd(p->log, 8, "icoms_usb_ser_read: discarded %d modem bytes 0x%02x 0x%02x\n",nb,nb >= 1 ? (bp[0] & 0xff) : 0, nb >= 2 ? (bp[1] & 0xff) : 0);
542 					rb -= nb;
543 					rbytes -= nb;
544 					memmove(bp, bp+nb, rb);
545 					bp += p->rd_qa - p->ms_bytes;
546 					rb -= p->rd_qa - p->ms_bytes;
547 				}
548 				rbuf[rbytes] = 0;
549 			}
550 
551 			a1logd(p->log, 8, "icoms_usb_ser_read: read %d bytes, rbuf = '%s'\n",rbytes,icoms_fix(rrbuf));
552 
553 			bsize -= rbytes;
554 			if (tc != NULL) {
555 				while(rbytes--) {	/* Count termination characters */
556 					char ch = *rbuf++, *tcp = tc;
557 
558 					while(*tcp != '\000') {
559 						if (ch == *tcp)
560 							j++;
561 						tcp++;
562 					}
563 				}
564 				a1logd(p->log, 8, "icoms_usb_ser_read: tc count %d\n",j);
565 			} else {
566 				if (ntc > 0)
567 					j += rbytes;
568 				rbuf += rbytes;
569 			}
570 		}
571 
572 		/* Deal with any errors */
573 		if (rv != ICOM_OK && rv != ICOM_SHORT) {
574 			a1logd(p->log, 8, "icoms_usb_ser_read: read failed with 0x%x, rbuf = '%s'\n",rv,icoms_fix(rrbuf));
575 			retrv |= rv;
576 			break;
577 		}
578 		top = ttop - (etime - stime);	/* Remaining time */
579 	}
580 
581 	*rbuf = '\000';
582 	a1logd(p->log, 8, "icoms_usb_ser_read: read %d total bytes with %d reads\n",rbuf - rrbuf, nreads);
583 	if (pbread != NULL)
584 		*pbread = (rbuf - rrbuf);
585 
586 	/* If ran out of time and not completed */
587 	a1logd(p->log, 8, "icoms_usb_ser_read: took %d msec\n",etime - stime);
588 	if (top <= 0 && bsize > 0 && j < ntc) {
589 		a1logd(p->log, 8, "icoms_usb_ser_read: timeout, took %d msec out of %d\n",etime - stime,ttop);
590 		retrv |= ICOM_TO;
591 	}
592 
593 	a1logd(p->log, 8, "icoms_usb_ser_read: took %d msec, returning '%s' ICOM err 0x%x\n",
594 	       etime - stime, tc == NULL && ntc > 0 ? icoms_tohex((unsigned char*)rrbuf, rbuf - rrbuf) : icoms_fix(rrbuf), retrv);
595 
596 	return retrv;
597 }
598 
599 
600 /* ------------------------------------------------- */
601 
602 /* Set the usb port number and characteristics. */
603 /* This may be called to re-establish a connection that has failed */
604 /* return an icom error */
605 static int
icoms_set_usb_port(icoms * p,int config,int wr_ep,int rd_ep,icomuflags usbflags,int retries,char ** pnames)606 icoms_set_usb_port(
607 icoms *p,
608 int    config,			/* Configuration */
609 int    wr_ep,			/* "Serial" Write end point */
610 int    rd_ep,			/* "Serial" Read end point */
611 icomuflags usbflags,	/* Any special handling flags */
612 int retries,			/* > 0 if we should retry set_configuration (100msec) */
613 char **pnames			/* List of process names to try and kill before opening */
614 ) {
615 	a1logd(p->log, 8, "icoms_set_usb_port: About to set usb port characteristics\n");
616 
617 	if (p->port_type(p) & icomt_usb) {
618 		int rv;
619 
620 		if (p->is_open)
621 			p->close_port(p);
622 
623 		if ((rv = usb_open_port(p, config, wr_ep, rd_ep, usbflags, retries, pnames)) != ICOM_OK) {
624 			return rv;
625 		}
626 
627 		p->write = icoms_usb_ser_write;
628 		p->read = icoms_usb_ser_read;
629 
630 	} else {
631 		a1logd(p->log, 8, "icoms_set_usb_port: Not a USB port!\n");
632 		return ICOM_NOTS;
633 	}
634 	a1logd(p->log, 6, "icoms_set_usb_port: usb port characteristics set ok\n");
635 
636 
637 	return ICOM_OK;
638 }
639 
640 /* ---------------------------------------------------------------------------------*/
641 
642 /* Set the USB specific icoms methods */
usb_set_usb_methods(icoms * p)643 void usb_set_usb_methods(
644 icoms *p
645 ) {
646 	p->set_usb_port     = icoms_set_usb_port;
647 	p->usb_control      = icoms_usb_control;
648 	p->usb_read         = icoms_usb_rw;
649 	p->usb_write        = icoms_usb_rw;
650 	p->usb_wait_io      = icoms_usb_wait_io;
651 	p->usb_cancel_io    = icoms_usb_cancel_io;
652 	p->usb_resetep      = icoms_usb_resetep;
653 	p->usb_clearhalt    = icoms_usb_clearhalt;
654 }
655 
656 /* ---------------------------------------------------------------------------------*/
657 
658 
659 #endif /* ENABLE_USB */
660