727 lines
21 KiB
C
727 lines
21 KiB
C
|
|
/* General USB I/O support */
|
|
|
|
/*
|
|
* Argyll Color Management System
|
|
*
|
|
* Author: Graeme W. Gill
|
|
* Date: 2006/22/4
|
|
*
|
|
* Copyright 2006 - 2013 Graeme W. Gill
|
|
* All rights reserved.
|
|
*
|
|
* This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
|
|
* see the License2.txt file for licencing details.
|
|
*/
|
|
|
|
/* These routines supliement the class code in icoms_nt.c and icoms_ux.c */
|
|
/* with common and USB specific routines */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#if defined(UNIX)
|
|
#include <termios.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifndef SALONEINSTLIB
|
|
#include "copyright.h"
|
|
#include "aconfig.h"
|
|
#else
|
|
#include "sa_config.h"
|
|
#endif
|
|
#include "numsup.h"
|
|
#include "cgats.h"
|
|
#include "xspect.h"
|
|
#include "insttypes.h"
|
|
#include "conv.h"
|
|
#include "icoms.h"
|
|
#include "inst.h"
|
|
|
|
#ifdef ENABLE_USB
|
|
|
|
/* Counter set when we're in a USB read or write */
|
|
/* Note - this isn't perfectly thread safe */
|
|
int in_usb_rw = 0;
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
/* Cancel token utility functions */
|
|
|
|
/* Used by caller of icoms to init and uninit token */
|
|
void usb_init_cancel(usb_cancelt *p) {
|
|
|
|
amutex_init(p->cmtx);
|
|
amutex_init(p->condx);
|
|
}
|
|
|
|
void usb_uninit_cancel(usb_cancelt *p) {
|
|
amutex_del(p->cmtx);
|
|
amutex_del(p->condx);
|
|
}
|
|
|
|
/* Used by caller of icoms to re-init for wait_io */
|
|
/* Must be called before icoms_usb_wait_io() */
|
|
void usb_reinit_cancel(usb_cancelt *cancelt) {
|
|
|
|
amutex_lock(cancelt->cmtx);
|
|
|
|
cancelt->state = 0;
|
|
amutex_lock(cancelt->condx); /* Block until IO is started */
|
|
|
|
amutex_unlock(cancelt->cmtx);
|
|
}
|
|
|
|
/* Wait for the given transaction to be pending or complete. */
|
|
static int icoms_usb_wait_io(
|
|
icoms *p,
|
|
usb_cancelt *cancelt
|
|
) {
|
|
amutex_lock(cancelt->condx); /* Wait for unlock */
|
|
amutex_unlock(cancelt->condx); /* Free it up for next time */
|
|
return ICOM_OK;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/* Include the USB implementation dependent function implementations */
|
|
#ifdef NT
|
|
# if !defined(EN_LIBUSB0) && !defined(EN_USBDK)
|
|
# error "One or both of USE_LIBUSB0 and USE_USBDK must be set in Jamtop!"
|
|
# endif
|
|
# ifdef EN_LIBUSB0
|
|
# include "usbio_w0.c" /* libusb0.sys based */
|
|
# endif
|
|
# ifdef EN_USBDK
|
|
# include "usbio_dk.c" /* UsbDk based */
|
|
# endif
|
|
# include "usbio_nt.c" /* multiplexing shims */
|
|
#endif
|
|
#if defined(UNIX_APPLE)
|
|
# include "usbio_ox.c"
|
|
#endif
|
|
#if defined(UNIX_X11)
|
|
# if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__FreeBSD_kernel__)
|
|
# include "usbio_bsd.c"
|
|
# else
|
|
# include "usbio_lx.c"
|
|
# endif
|
|
#endif
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
/* I/O routines supported by icoms - uses platform independent */
|
|
/* USB routines implemented by code in usbio_*.c above */
|
|
|
|
/* USB control message */
|
|
static int
|
|
icoms_usb_control(
|
|
icoms *p,
|
|
int requesttype, /* 8 bit request type (USB bmRequestType) */
|
|
int request, /* 8 bit request code (USB bRequest) */
|
|
int value, /* 16 bit value (USB wValue) */
|
|
int index, /* 16 bit index (USB wIndex) */
|
|
unsigned char *rwbuf, /* Write or read buffer */
|
|
int rwsize, /* Size to write/max bytes to read */
|
|
int *xferred, /* Bytes written or read (may be NULL) */
|
|
double tout /* Timeout in seconds */
|
|
) {
|
|
int rv = 0; /* Return value */
|
|
int rwbytes; /* Data bytes read or written */
|
|
long top; /* timeout in msec */
|
|
|
|
if (p->log->debug >= 8) {
|
|
a1logd(p->log, 8, "icoms_usb_control: message %02x, %02x %04x %04x %04x\n",
|
|
requesttype, request, value, index, rwsize);
|
|
if ((requesttype & IUSB_ENDPOINT_IN) == 0)
|
|
a1logd(p->log, 8, " writing data %s\n",icoms_tohex(rwbuf, rwsize));
|
|
}
|
|
|
|
if (!p->is_open) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_control: device not open\n");
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
top = (int)(tout * 1000.0 + 0.5); /* Timeout in msec */
|
|
|
|
#ifdef QUIET_MEMCHECKERS
|
|
if (requesttype & IUSB_ENDPOINT_IN)
|
|
memset(rwbuf, 0, rwsize);
|
|
#endif
|
|
|
|
/* Call back end implementation */
|
|
rv = icoms_usb_control_msg(p, &rwbytes, requesttype, request, value, index,
|
|
rwbuf, rwsize, top);
|
|
a1logd(p->log, 8, "icoms_usb_control: returning ICOM err 0x%x\n",rv);
|
|
|
|
if (p->log->debug >= 8 && (requesttype & IUSB_ENDPOINT_IN))
|
|
a1logd(p->log, 8, " read data %s\n",icoms_tohex(rwbuf, rwbytes));
|
|
|
|
if (xferred != NULL)
|
|
*xferred = rwbytes;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - */
|
|
/* USB Read/Write. Direction is set by ep. */
|
|
/* Don't retry on a short read, return ICOM_SHORT. */
|
|
static int
|
|
icoms_usb_rw(icoms *p,
|
|
usb_cancelt *cancelt, /* Cancel handle */
|
|
int ep, /* End point address */
|
|
unsigned char *rbuf, /* Read/Write buffer */
|
|
int bsize, /* Bytes to read */
|
|
int *breadp, /* Bytes read/written */
|
|
double tout /* Timeout in seconds */
|
|
) {
|
|
int lerr; /* Last error */
|
|
int bread, qa;
|
|
long top; /* Timeout period */
|
|
icom_usb_trantype type; /* bulk or interrupt */
|
|
|
|
#ifdef QUIET_MEMCHECKERS
|
|
memset(rbuf, 0, bsize);
|
|
#endif
|
|
|
|
if (!p->is_open) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_rw: device not initialised\n");
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->EPINFO(ep).valid == 0) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_rw: invalid end point 0x%02x\n",ep);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->EPINFO(ep).type != ICOM_EP_TYPE_BULK
|
|
&& p->EPINFO(ep).type != ICOM_EP_TYPE_INTERRUPT) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_rw: unhandled end point type %d\n",p->EPINFO(ep).type);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->EPINFO(ep).type == ICOM_EP_TYPE_BULK)
|
|
type = icom_usb_trantype_bulk;
|
|
else
|
|
type = icom_usb_trantype_interrutpt;
|
|
|
|
qa = bsize; /* For simpler tracing */
|
|
|
|
lerr = 0;
|
|
bread = 0;
|
|
|
|
top = (int)(tout * 1000 + 0.5); /* Timeout period in msecs */
|
|
|
|
/* Bug workaround - on some OS's for some devices */
|
|
if (p->uflags & icomuf_resetep_before_read
|
|
&& (ep & IUSB_ENDPOINT_DIR_MASK) == IUSB_ENDPOINT_IN) {
|
|
msec_sleep(1); /* Let device recover ? */
|
|
p->usb_resetep(p, ep);
|
|
msec_sleep(1); /* Let device recover (ie. Spyder 3) */
|
|
}
|
|
|
|
/* Until data is all read/written, we get a short read/write, we time out, or the user aborts */
|
|
// a1logd(p->log, 8, "icoms_usb_rw: read/write of %d bytes, timeout %f\n",bsize,tout);
|
|
while (bsize > 0) {
|
|
int rv, rbytes;
|
|
int rsize = bsize > qa ? qa : bsize;
|
|
|
|
// a1logd(p->log, 8, "icoms_usb_rw: read/write %d bytes this time\n",rsize);
|
|
rv = icoms_usb_transaction(p, cancelt, &rbytes, type, (unsigned char)ep, rbuf, rsize, top);
|
|
if (rv != 0 && rv != ICOM_SHORT) {
|
|
lerr = rv;
|
|
break;
|
|
} else { /* Account for bytes read/written */
|
|
bsize -= rbytes;
|
|
rbuf += rbytes;
|
|
bread += rbytes;
|
|
}
|
|
if (rbytes != rsize) {
|
|
lerr |= ICOM_SHORT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (breadp != NULL)
|
|
*breadp = bread;
|
|
|
|
a1logd(p->log, 8, "icoms_usb_rw: returning %d bytes, ICOM err 0x%x\n",bread, lerr);
|
|
|
|
return lerr;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/* Static list so that all open USB/HID connections can be closed on a SIGKILL */
|
|
static icoms *icoms_list = NULL;
|
|
|
|
/* Clean up any open USB ports ready for exit on signal */
|
|
static void icoms_cleanup() {
|
|
icoms *pp, *np;
|
|
|
|
#if defined(UNIX)
|
|
/* This is a bit of a hack to compensate for the fact */
|
|
/* that a ^C will kill the program while ICANON is off. */
|
|
/* It's really better to restore the original attributes, */
|
|
/* even when USB is not compiled in. */
|
|
struct termios news;
|
|
if (tcgetattr(STDIN_FILENO, &news) >= 0) {
|
|
news.c_lflag |= (ICANON | ECHO);
|
|
tcsetattr(STDIN_FILENO,TCSANOW, &news);
|
|
}
|
|
#endif
|
|
|
|
for (pp = icoms_list; pp != NULL; pp = np) {
|
|
np = pp->next;
|
|
a1logd(pp->log, 6, "icoms_cleanup: closing usb port 0x%x\n",pp);
|
|
/* There's a problem here if have more than one USB port */
|
|
/* open - win32 doesn't return from the system call. */
|
|
/* Should we depend on usb read/write routines to call cleanup ? */
|
|
pp->close_port(pp);
|
|
}
|
|
}
|
|
|
|
#ifdef NT
|
|
void (__cdecl *usbio_int)(int sig) = SIG_DFL;
|
|
void (__cdecl *usbio_term)(int sig) = SIG_DFL;
|
|
#endif
|
|
#ifdef UNIX
|
|
void (*usbio_hup)(int sig) = SIG_DFL;
|
|
void (*usbio_int)(int sig) = SIG_DFL;
|
|
void (*usbio_term)(int sig) = SIG_DFL;
|
|
#endif
|
|
|
|
/* On something killing our process, deal with USB cleanup */
|
|
static void icoms_sighandler(int arg) {
|
|
static amutex_static(lock);
|
|
|
|
a1logd(g_log, 6, "icoms_sighandler: invoked with arg = %d\n",arg);
|
|
|
|
/* Make sure we don't re-enter */
|
|
if (amutex_trylock(lock)) {
|
|
return;
|
|
}
|
|
|
|
if (in_usb_rw != 0)
|
|
in_usb_rw = -1;
|
|
icoms_cleanup();
|
|
|
|
/* Call through to previous handlers */
|
|
#ifdef UNIX
|
|
if (arg == SIGHUP && usbio_hup != SIG_DFL && usbio_hup != SIG_IGN)
|
|
usbio_hup(arg);
|
|
#endif /* UNIX */
|
|
if (arg == SIGINT && usbio_int != SIG_DFL && usbio_int != SIG_IGN)
|
|
usbio_int(arg);
|
|
if (arg == SIGTERM && usbio_term != SIG_DFL && usbio_term != SIG_IGN)
|
|
usbio_term(arg);
|
|
|
|
a1logd(g_log, 6, "icoms_sighandler: calling exit()\n");
|
|
|
|
amutex_unlock(lock);
|
|
exit(0);
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/* Use sigaction() if we can, to get SA_RESTART behavior */
|
|
#if defined(UNIX)
|
|
//sighandler_t signal_x(int signum, sighandler_t handler) {
|
|
static void (*signal_x(int signum, void (*handler)(int)))(int) {
|
|
struct sigaction new, old;
|
|
int rv = 0;
|
|
|
|
new.sa_handler = handler;
|
|
sigemptyset(&new.sa_mask);
|
|
new.sa_flags = SA_RESTART;
|
|
|
|
rv = sigaction(signum, &new, &old);
|
|
|
|
return old.sa_handler;
|
|
}
|
|
#else
|
|
# define signal_x signal
|
|
#endif
|
|
|
|
/* Install the cleanup signal handlers */
|
|
void usb_install_signal_handlers(icoms *p) {
|
|
if (icoms_list == NULL) {
|
|
a1logd(g_log, 6, "usb_install_signal_handlers: called\n");
|
|
#if defined(UNIX)
|
|
usbio_hup = signal_x(SIGHUP, icoms_sighandler);
|
|
#endif /* UNIX */
|
|
usbio_int = signal_x(SIGINT, icoms_sighandler);
|
|
usbio_term = signal_x(SIGTERM, icoms_sighandler);
|
|
}
|
|
|
|
/* Add it to our static list, to allow automatic cleanup on signal */
|
|
p->next = icoms_list;
|
|
icoms_list = p;
|
|
a1logd(g_log, 6, "usb_install_signal_handlers: done\n");
|
|
}
|
|
|
|
/* Delete an icoms from our static signal cleanup list */
|
|
void usb_delete_from_cleanup_list(icoms *p) {
|
|
|
|
/* Find it and delete it from our static cleanup list */
|
|
if (icoms_list != NULL) {
|
|
if (icoms_list == p) {
|
|
icoms_list = p->next;
|
|
if (icoms_list == NULL) {
|
|
#if defined(UNIX)
|
|
signal_x(SIGHUP, usbio_hup);
|
|
#endif /* UNIX */
|
|
signal_x(SIGINT, usbio_int);
|
|
signal_x(SIGTERM, usbio_term);
|
|
}
|
|
} else {
|
|
icoms *pp;
|
|
for (pp = icoms_list; pp != NULL; pp = pp->next) {
|
|
if (pp->next == p) {
|
|
pp->next = p->next;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ========================================================= */
|
|
/* USB write/read "serial" imitation */
|
|
|
|
/* Write the characters in the buffer out */
|
|
/* Data will be written up to the terminating nul */
|
|
/* Return relevant error status bits */
|
|
static int
|
|
icoms_usb_ser_write(
|
|
icoms *p,
|
|
char *wbuf, /* null terminated unless nch > 0 */
|
|
int nwch, /* if > 0, number of characters to write */
|
|
double tout)
|
|
{
|
|
int len, wbytes;
|
|
long ttop, top; /* Total timeout period, timeout period */
|
|
unsigned int stime, etime; /* Start and end times of USB operation */
|
|
int ep = p->wr_ep; /* End point */
|
|
icom_usb_trantype type; /* bulk or interrupt */
|
|
int retrv = ICOM_OK;
|
|
|
|
a1logd(p->log, 8, "\nicoms_usb_ser_write: writing '%s'\n",
|
|
nwch > 0 ? icoms_tohex((unsigned char *)wbuf, nwch) : icoms_fix(wbuf));
|
|
|
|
if (!p->is_open) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_ser_write: device is not open\n");
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->EPINFO(ep).valid == 0) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_ser_write: invalid end point 0x%02x\n",ep);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->EPINFO(ep).type != ICOM_EP_TYPE_BULK
|
|
&& p->EPINFO(ep).type != ICOM_EP_TYPE_INTERRUPT) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_ser_write: unhandled end point type %d",p->EPINFO(ep).type);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->EPINFO(ep).type == ICOM_EP_TYPE_BULK)
|
|
type = icom_usb_trantype_bulk;
|
|
else
|
|
type = icom_usb_trantype_interrutpt;
|
|
|
|
if (nwch != 0)
|
|
len = nwch;
|
|
else
|
|
len = strlen(wbuf);
|
|
|
|
ttop = (int)(tout * 1000.0 + 0.5); /* Total timeout period in msecs */
|
|
|
|
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);
|
|
|
|
etime = stime = msec_time();
|
|
|
|
/* Until data is all written, we time out, or the user aborts */
|
|
for (top = ttop; top > 0 && len > 0;) {
|
|
int rv;
|
|
a1logd(p->log, 8, "icoms_usb_ser_write: attempting to write %d bytes to usb top = %d\n",len,top);
|
|
|
|
rv = icoms_usb_transaction(p, NULL, &wbytes, type, (unsigned char)ep, (unsigned char *)wbuf, len, top);
|
|
etime = msec_time();
|
|
if (rv != ICOM_OK) {
|
|
if (rv != ICOM_TO) {
|
|
retrv |= rv;
|
|
break;
|
|
}
|
|
} else { /* Account for bytes written */
|
|
a1logd(p->log, 8, "icoms_usb_ser_write: wrote %d bytes\n",wbytes);
|
|
wbuf += wbytes;
|
|
len -= wbytes;
|
|
}
|
|
top = ttop - (etime - stime); /* Remaining time */
|
|
}
|
|
|
|
if (top <= 0) { /* Must have timed out */
|
|
a1logd(p->log, 8, "icoms_usb_ser_write: timeout, took %d msec out of %d\n",etime - stime,ttop);
|
|
retrv |= ICOM_TO;
|
|
}
|
|
|
|
a1logd(p->log, 8, "icoms_usb_ser_write: took %d msec, returning ICOM err 0x%x\n",etime - stime,retrv);
|
|
|
|
return retrv;
|
|
}
|
|
|
|
|
|
/* Read characters into the buffer */
|
|
/* Return string will be terminated with a nul */
|
|
/* Read only in packet sized chunks, and retry if */
|
|
/* the bytes requested aren't read, untill we get a */
|
|
/* timeout or a terminating char is read */
|
|
static int
|
|
icoms_usb_ser_read(icoms *p,
|
|
char *rbuf, /* Buffer to store characters read */
|
|
int bsize, /* Buffer size */
|
|
int *pbread, /* Bytes read (not including forced '\000') */
|
|
char *tc, /* Terminating characers, NULL for none or char count mode */
|
|
int ntc, /* Number of terminating characters or char count needed */
|
|
double tout) /* Time out in seconds */
|
|
{
|
|
int j, rbytes;
|
|
long ttop, top; /* Total timeout period, timeout period */
|
|
unsigned int stime, etime; /* Start and end times of USB operation */
|
|
char *rrbuf = rbuf; /* Start of return buffer */
|
|
int ep = p->rd_ep; /* End point */
|
|
icom_usb_trantype type; /* bulk or interrupt */
|
|
int retrv = ICOM_OK;
|
|
int nreads; /* Number of reads performed */
|
|
int fastserial = 0; /* If fast serial type */
|
|
|
|
if (!p->is_open) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_ser_read: device is not open\n");
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->EPINFO(ep).valid == 0) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_ser_read: invalid end point 0x%02x\n",ep);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->EPINFO(ep).type != ICOM_EP_TYPE_BULK
|
|
&& p->EPINFO(ep).type != ICOM_EP_TYPE_INTERRUPT) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_ser_read: unhandled end point type %d",p->EPINFO(ep).type);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->EPINFO(ep).type == ICOM_EP_TYPE_BULK)
|
|
type = icom_usb_trantype_bulk;
|
|
else
|
|
type = icom_usb_trantype_interrutpt;
|
|
|
|
if (bsize < 3) {
|
|
a1loge(p->log, ICOM_SYS, "icoms_usb_ser_read given too small a buffer (%d)", bsize);
|
|
return ICOM_SYS;
|
|
}
|
|
|
|
if (p->dctype & icomt_fastserial)
|
|
fastserial = 1;
|
|
|
|
for (j = 0; j < bsize; j++)
|
|
rbuf[j] = 0;
|
|
|
|
/* The DTP94 doesn't cope with a timeout on OS X, so we need to avoid */
|
|
/* them by giving each read the largest timeout period possible. */
|
|
/* This also reduces the problem of libusb 0.1 not returning the */
|
|
/* number of characters read on a timeout. */
|
|
|
|
ttop = (int)(tout * 1000.0 + 0.5); /* Total timeout period in msecs */
|
|
|
|
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);
|
|
|
|
bsize -= 1; /* Allow space for null */
|
|
bsize -= p->ms_bytes; /* Allow space for modem status bytes */
|
|
|
|
j = (tc == NULL && ntc <= 0) ? -1 : 0;
|
|
etime = stime = msec_time();
|
|
|
|
/* Until data is all read, we time out, or the user aborts */
|
|
for (top = ttop, nreads = 0; top > 0 && bsize > 0 && j < ntc ;) {
|
|
int c, rv;
|
|
int rsize = bsize;
|
|
|
|
/* If not a fast USB/BT serial port, read in quanta size chunks */
|
|
if (!fastserial && rsize > p->rd_qa)
|
|
rsize = p->rd_qa;
|
|
|
|
a1logd(p->log, 8, "icoms_usb_ser_read: attempting to read %d bytes from usb, top = %d, j = %d\n",rsize,top,j);
|
|
|
|
rv = icoms_usb_transaction(p, NULL, &rbytes, type, (unsigned char)ep, (unsigned char *)rbuf, rsize, top);
|
|
etime = msec_time();
|
|
nreads++;
|
|
|
|
if (rbytes > 0) { /* Account for bytes read */
|
|
|
|
/* Account for modem status bytes. Modem bytes are per usb read, */
|
|
/* or every p->rd_qa bytes. */
|
|
if (p->ms_bytes) { /* Throw away modem bytes */
|
|
char *bp = rbuf;
|
|
int rb = rbytes;
|
|
for (; rb > 0; ) {
|
|
int nb = rb < p->ms_bytes ? rb : p->ms_bytes; /* Bytes to shift */
|
|
if (p->interp_ms != NULL && nb >= p->ms_bytes)
|
|
retrv |= p->interp_ms(p, (unsigned char *)bp); /* Deal with error flags in ms bytes */
|
|
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);
|
|
rb -= nb;
|
|
rbytes -= nb;
|
|
memmove(bp, bp+nb, rb);
|
|
bp += p->rd_qa - p->ms_bytes;
|
|
rb -= p->rd_qa - p->ms_bytes;
|
|
}
|
|
rbuf[rbytes] = 0;
|
|
}
|
|
|
|
a1logd(p->log, 8, "icoms_usb_ser_read: read %d bytes, rbuf = '%s'\n",rbytes,icoms_fix(rrbuf));
|
|
|
|
bsize -= rbytes;
|
|
if (tc != NULL) {
|
|
while(rbytes--) { /* Count termination characters */
|
|
char ch = *rbuf++, *tcp = tc;
|
|
|
|
while(*tcp != '\000') {
|
|
if (ch == *tcp)
|
|
j++;
|
|
tcp++;
|
|
}
|
|
}
|
|
a1logd(p->log, 8, "icoms_usb_ser_read: tc count %d\n",j);
|
|
} else {
|
|
if (ntc > 0)
|
|
j += rbytes;
|
|
rbuf += rbytes;
|
|
}
|
|
}
|
|
|
|
/* Deal with any errors */
|
|
if (rv != ICOM_OK && rv != ICOM_SHORT) {
|
|
a1logd(p->log, 8, "icoms_usb_ser_read: read failed with 0x%x, rbuf = '%s'\n",rv,icoms_fix(rrbuf));
|
|
retrv |= rv;
|
|
break;
|
|
}
|
|
top = ttop - (etime - stime); /* Remaining time */
|
|
}
|
|
|
|
*rbuf = '\000';
|
|
a1logd(p->log, 8, "icoms_usb_ser_read: read %d total bytes with %d reads\n",rbuf - rrbuf, nreads);
|
|
if (pbread != NULL)
|
|
*pbread = (rbuf - rrbuf);
|
|
|
|
/* If ran out of time and not completed */
|
|
a1logd(p->log, 8, "icoms_usb_ser_read: took %d msec\n",etime - stime);
|
|
if (top <= 0 && bsize > 0 && j < ntc) {
|
|
a1logd(p->log, 8, "icoms_usb_ser_read: timeout, took %d msec out of %d\n",etime - stime,ttop);
|
|
retrv |= ICOM_TO;
|
|
}
|
|
|
|
a1logd(p->log, 8, "icoms_usb_ser_read: took %d msec, returning '%s' ICOM err 0x%x\n",
|
|
etime - stime, tc == NULL && ntc > 0 ? icoms_tohex((unsigned char*)rrbuf, rbuf - rrbuf) : icoms_fix(rrbuf), retrv);
|
|
|
|
return retrv;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------- */
|
|
|
|
/* Set the usb port number and characteristics. */
|
|
/* This may be called to re-establish a connection that has failed */
|
|
/* return an icom error */
|
|
static int
|
|
icoms_set_usb_port(
|
|
icoms *p,
|
|
int config, /* Configuration */
|
|
int wr_ep, /* "Serial" Write end point */
|
|
int rd_ep, /* "Serial" Read end point */
|
|
icomuflags usbflags, /* Any special handling flags */
|
|
int retries, /* > 0 if we should retry set_configuration (100msec) */
|
|
char **pnames /* List of process names to try and kill before opening */
|
|
) {
|
|
a1logd(p->log, 8, "icoms_set_usb_port: About to set usb port characteristics\n");
|
|
|
|
if (p->port_type(p) & icomt_usb) {
|
|
int rv;
|
|
|
|
if (p->is_open)
|
|
p->close_port(p);
|
|
|
|
if ((rv = usb_open_port(p, config, wr_ep, rd_ep, usbflags, retries, pnames)) != ICOM_OK) {
|
|
return rv;
|
|
}
|
|
|
|
p->write = icoms_usb_ser_write;
|
|
p->read = icoms_usb_ser_read;
|
|
|
|
} else {
|
|
a1logd(p->log, 8, "icoms_set_usb_port: Not a USB port!\n");
|
|
return ICOM_NOTS;
|
|
}
|
|
a1logd(p->log, 6, "icoms_set_usb_port: usb port characteristics set ok\n");
|
|
|
|
return ICOM_OK;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------*/
|
|
|
|
/* Set the USB specific icoms methods */
|
|
void usb_set_usb_methods(
|
|
icoms *p
|
|
) {
|
|
p->set_usb_port = icoms_set_usb_port;
|
|
p->usb_control = icoms_usb_control;
|
|
p->usb_read = icoms_usb_rw;
|
|
p->usb_write = icoms_usb_rw;
|
|
p->usb_wait_io = icoms_usb_wait_io;
|
|
p->usb_cancel_io = icoms_usb_cancel_io;
|
|
p->usb_resetep = icoms_usb_resetep;
|
|
p->usb_clearhalt = icoms_usb_clearhalt;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------*/
|
|
/* Utility code */
|
|
|
|
/* USB descriptors are little endian */
|
|
|
|
/* Take a word sized return buffer, and convert it to an unsigned int */
|
|
unsigned int usb2uint(unsigned char *buf) {
|
|
unsigned int val;
|
|
val = buf[3];
|
|
val = ((val << 8) + (0xff & buf[2]));
|
|
val = ((val << 8) + (0xff & buf[1]));
|
|
val = ((val << 8) + (0xff & buf[0]));
|
|
return val;
|
|
}
|
|
|
|
/* Take a short sized return buffer, and convert it to an int */
|
|
unsigned int usb2ushort(unsigned char *buf) {
|
|
unsigned int val;
|
|
val = buf[1];
|
|
val = ((val << 8) + (0xff & buf[0]));
|
|
return val;
|
|
}
|
|
|
|
/* Take an int, and convert it into a byte buffer */
|
|
void int2usb(unsigned char *buf, int inv) {
|
|
buf[0] = (inv >> 0) & 0xff;
|
|
buf[1] = (inv >> 8) & 0xff;
|
|
buf[2] = (inv >> 16) & 0xff;
|
|
buf[3] = (inv >> 24) & 0xff;
|
|
}
|
|
|
|
/* Take a short, and convert it into a byte buffer */
|
|
void short2usb(unsigned char *buf, int inv) {
|
|
buf[0] = (inv >> 0) & 0xff;
|
|
buf[1] = (inv >> 8) & 0xff;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------*/
|
|
|
|
|
|
#endif /* ENABLE_USB */
|