Files
argyllcms/spectro/icoms.h
2026-08-20 20:28:40 +01:00

672 lines
22 KiB
C

#ifndef ICOMS_H
/* An abstracted instrument serial and USB communication class. */
/*
* Argyll Color Management System
*
* Author: Graeme W. Gill
* Date: 2006/4/20
*
* Copyright 1996 - 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.
*
* Derived from serio.h
*/
/* Some MSWin specific stuff is in icoms, and used by usbio & hidio */
#if defined (NT)
/* Set minimum OS target as XP */
# if !defined(WINVER) || WINVER < 0x0501
# if defined(WINVER)
# undef WINVER
# endif
# define WINVER 0x0501
# endif
# if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0501
# if defined(_WIN32_WINNT)
# undef _WIN32_WINNT
# endif
# define _WIN32_WINNT 0x0501
# endif
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# ifdef EN_USBDK
# include <objbase.h> /* For GUID type definition */
# if VER_PRODUCTBUILD >= 9600 /* SDK's that have these... */
# include <cfgmgr32.h>
# include <usbspec.h>
# include <Usbiodef.h>
# else
# include "UsbDkHlprCompat.h"
# endif
# include "UsbDkHelper.h"
# endif
#endif
#if defined(UNIX_APPLE)
#include <IOKit/usb/IOUSBLib.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#undef QUIET_MEMCHECKERS /* #define to memset coms read buffers before reading */
/* USB open/handling/buf workaround flags */
typedef enum {
icomuf_none = 0x0000,
icomuf_detach = 0x0001, /* Attempt to detach from system driver */
icomuf_no_open_clear = 0x0002, /* Don't send a clear_halt after opening the port */
icomuf_reset_before_close = 0x0004, /* Reset port before closing it */
/* ??? Could change to reset before open ??? */
icomuf_resetep_before_read = 0x0008 /* Do a usb_resetep before each ep read */
} icomuflags;
typedef enum {
icomt_unknown = 0x0000,
/* Category of device */
icomt_instrument = 0x010000, /* Color measurement instrument (default) */
icomt_v3dlut = 0x020000, /* A Video 3D cLUT box */
icomt_vtpg = 0x040000, /* A Video test patern generator box */
icomt_printer = 0x080000, /* A printing device */
icomt_cat_any = 0x3f0000, /* Could be any device category */
icomt_cat_mask = 0xff0000, /* Mask for device category */
/* Type of underlying communication port */
/* (fastserio driver will have icomt_serial and icomt_usb set) */
icomt_serial = 0x000001, /* Serial port (i.e. has baud rate etc.) */
icomt_usb = 0x000002, /* USB port */
icomt_hid = 0x000004, /* HID USB port */
icomt_bt = 0x000008, /* Bluetooth (non-serial) */
// icomt_net = 0x000010, /* Network Connected */
icomt_port_mask = 0x0000ff, /* Mask for port type */
/* Attributes */
icomt_fastserial = 0x000100, /* Fast Serial (i.e. USB, fastserio, Bluetooth) */
icomt_btserial = 0x000200, /* Bluetooth serial port */
icomt_seriallike = 0x000400, /* Uses serial message methods, but */
/* may not be actual icomt_serial. */
icomt_attr_mask = 0x00ff00, /* Mask for port attributes */
icomt_portattr_mask = icomt_port_mask | icomt_attr_mask,
icomt_portattr_all = icomt_portattr_mask /* Scan for all port types */
} icom_type;
/* Status bits/return values */
#define ICOM_OK 0x000000 /* No error */
#define ICOM_NOTS 0x001000 /* Not supported */
#define ICOM_SIZE 0x002000 /* Request/response size exceeded limits */
#define ICOM_TO 0x004000 /* Timed out, but there may be bytes read/written */
#define ICOM_SHORT 0x008000 /* No timeout but target number of bytes wasn't read/written */
#define ICOM_CANC 0x010000 /* Was cancelled */
#define ICOM_SYS 0x020000 /* System error (ie. malloc, system call fail) */
#define ICOM_VER 0x040000 /* Version error - need up to date kernel driver */
#define ICOM_USBR 0x000100 /* Unspecified USB read error */
#define ICOM_USBW 0x000200 /* Unspecified USB write error */
#define ICOM_SERR 0x000400 /* Unspecified Serial read error */
#define ICOM_SERW 0x000800 /* Unspecified Serial write error */
#define ICOM_XRE 0x000040 /* Xmit shift reg empty */
#define ICOM_XHE 0x000020 /* Xmit hold reg empty */
#define ICOM_BRK 0x000010 /* Break detected */
#define ICOM_FER 0x000008 /* Framing error */
#define ICOM_PER 0x000004 /* Parity error */
#define ICOM_OER 0x000002 /* Overun error */
#define ICOM_DRY 0x000001 /* Recv data ready */
typedef struct _icompath icompath;
typedef struct _icompaths icompaths;
typedef struct _icoms icoms;
#ifdef ENABLE_USB
struct usb_idevice; /* Forward declarations */
struct hid_idevice;
/* Information about each end point */
typedef struct {
int valid; /* Flag, nz if this endpoint is valid */
int addr; /* Address of end point */
int packetsize; /* The max packet size */
int type; /* 2 = bulk, 3 = interrupt */
int ifaceno; /* interface number */
#if defined(UNIX_APPLE)
int pipe; /* pipe number (1..N, OS X only) */
#endif
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
int fd; /* ep file descriptor */
#endif
} usb_ep;
#define ICOM_EP_TYPE_BULK 2
#define ICOM_EP_TYPE_INTERRUPT 3
#include "usbio.h"
#include "hidio.h"
#endif /* ENABLE_USB */
/* - - - - - - - - - - - - - - - - - - - - */
/* Store information about a possible instrument communication path */
/* (Note a path doesn't have a reference to icompaths or its log) */
struct _icompath {
devType dtype; /* Type of device if known */
char *name; /* instance description */
icom_type dctype; /* Device and com. type */
#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
char *spath; /* Serial device path */
#endif
#ifdef ENABLE_USB
int nep; /* Number of end points */
unsigned int vid, pid; /* USB vendor and product id's */
struct usb_idevice *usbd; /* USB port, NULL if not USB */
struct hid_idevice *hidd; /* HID port, NULL if not HID */
#endif /* ENABLE_USB */
};
extern icompath icomFakeDevice; /* Declare fake device */
/* Device type specific list index */
typedef enum {
dtix_combined = 0, /* Combined list */
dtix_inst, /* Instrument */
dtix_3dlut,
dtix_vtpg,
dtix_printer,
dtix_number /* Number of entries */
} icom_dtix;
/* The available instrument communication paths */
struct _icompaths {
a1log *log; /* Verbose, debuge & error logging */
/* Combined and device category specific path lists (read only). */
/* Paths may appear on more than one, if they are multi-function, */
/* or the category can't be determined without opening them. */
/* (dpaths[dtix_combined] is the owner of the icompath, */
/* and all the others are only populated after discovery.) */
icompath **dpaths[dtix_number]; /* Device paths if any */
int ndpaths[dtix_number]; /* Number of device paths */
/* Alias of dpaths[dtix_inst] for backwards compatibility, */
/* only set after discovery. */
icompath **paths; /* Device instrument if any */
int npaths; /* Number of instrument paths */
/* Re-discover and populate the available instrument list */
/* return icom error */
int (*refresh)(struct _icompaths *p);
/* Same as above, but just scan for selected types of devices and/or port types */
int (*refresh_sel)(struct _icompaths *p, icom_type mask);
/* Return the instrument path corresponding to the port number, or NULL if out of range */
icompath *(*get_path)(
struct _icompaths *p,
int port); /* Enumerated port number, 1..n */
#define FAKE_DEVICE_PORT -98 /* Fake display & instrument */
#define DEMO_DEVICE_PORT -99 /* Fake Demo instrument */
/* Return the device path corresponding to the port number, or NULL if out of range */
icompath *(*get_path_sel)(
struct _icompaths *p,
icom_dtix dtix, /* Device type list */
int port); /* Enumerated port number, 1..n */
/* Clear all the device paths */
void (*clear)(struct _icompaths *p);
/* We're done */
void (*del)(struct _icompaths *p);
/* ====== internal implementation ======= */
/* Fast serial scan exclusion list - from ARGYLL_EXCLUDE_SERIAL_SCAN env. var. */
int exno;
char **exlist;
#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
/* Return nz if the serial port is on the fast serial scan exclusion list */
int (*fs_excluded)(struct _icompaths *p, struct _icompath *path);
/* Add a serial path to combined. path is copied. Return icom error */
int (*add_serial)(struct _icompaths *p, char *name, char *spath, icom_type dctype);
#endif /* ENABLE_SERIAL */
#ifdef ENABLE_USB
/* Check a usb vid/pid against combined list and return nz if already on it. */
int (*check_usb)(struct _icompaths *p, unsigned int vid, unsigned int pid);
/* Return the current combined path count */
/* (Used to set upto value for check_usb_upto() */
int (*get_cur_count)(icompaths *p);
/* Check a usb itype against combined list up to given index. */
/* return nz if on the list */
int (*check_usb_upto)(icompaths *p, int upto, devType itype);
/* Add a usb path to combined. usbd is taken, others are copied. Return icom error */
int (*add_usb)(struct _icompaths *p, char *name, unsigned int vid, unsigned int pid,
int nep, struct usb_idevice *usbd, devType dtype);
/* Add an hid path to combined. hidd is taken, others are copied. Return icom error */
int (*add_hid)(struct _icompaths *p, char *name, unsigned int vid, unsigned int pid,
int nep, struct hid_idevice *hidd, devType dtype);
#endif /* ENABLE_USB */
/* Delete the last combined path */
void (*del_last_path)(struct _icompaths *p);
/* Return the last combined path */
icompath *(*get_last_path)(struct _icompaths *p);
};
/* Allocate an icom paths and set it to the list of available devices */
/* Use g_log as argument for default logging. */
/* Return NULL on error */
icompaths *new_icompaths(a1log *log);
/* Same as above, but just scan for selected types of devices and/or port types */
icompaths *new_icompaths_sel(a1log *log, icom_type mask);
/* - - - - - - - - - - - */
/* Serial related stuff */
/* Flow control */
typedef enum {
fc_nc = 0, /* not configured/default */
fc_None,
fc_XonXOff,
fc_Hardware, /* RTS CTS flow control */
fc_HardwareDTR /* DTR DSR flow control */
} flow_control;
/* baud rate available on all systems */
typedef enum {
baud_nc = 0, /* Not Configured */
baud_110 = 1,
baud_300 = 2,
baud_600 = 3,
baud_1200 = 4,
baud_2400 = 5,
baud_4800 = 6,
baud_9600 = 7,
baud_14400 = 8,
baud_19200 = 9,
baud_38400 = 10,
baud_57600 = 11,
baud_115200 = 12,
baud_230400 = 13,
baud_921600 = 14
} baud_rate;
char *baud_rate_to_str(baud_rate br);
/* Possible parity */
typedef enum {
parity_nc,
parity_none,
parity_odd,
parity_even
} parity;
/* Possible stop bits */
typedef enum {
stop_nc,
stop_1,
stop_2
} stop_bits;
/* Possible word length */
typedef enum {
length_nc,
length_5,
length_6,
length_7,
length_8
} word_length;
/* Interrupt callback type */
typedef enum {
icomi_data_available /* Data is available to be read */
} icom_int;
/* Cancelation token. */
/* Note that reinit & usb_transaction's must balance! */
typedef struct _usb_cancelt usb_cancelt;
#ifdef ENABLE_USB
void usb_init_cancel(usb_cancelt *p);
void usb_uninit_cancel(usb_cancelt *p);
void usb_reinit_cancel(usb_cancelt *p);
#endif
struct _icoms {
/* Private: */
amutex lock; /* Protect against thread races for some operations */
/* (i.e. close) */
/* Copy of some of icompath contents: */
icom_type dctype; /* Device cat. and com. type */
devType dtype; /* Type of device if known */
char *name; /* Device description */
int is_open; /* Flag, NZ if this port is open */
void *icntx; /* Optional instrument context */
#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
/* Serial port parameters */
char *spath; /* Serial port path */
#if defined (MSDOS)
unsigned short porta; /* Hardware port address */
#endif
#if defined (NT)
HANDLE phandle; /* NT handle */
#endif
#if defined (UNIX)
int fd; /* Unix file descriptor */
#endif
flow_control fc;
baud_rate br;
parity py;
stop_bits sb;
word_length wl;
#endif /* ENABLE_SERIAL */
#ifdef ENABLE_USB
/* USB port parameters */
struct usb_idevice *usbd; /* USB port - copy of ppath->usbd */
icomuflags uflags; /* Bug workaround flags */
unsigned int vid, pid; /* USB vendor and product id's, used to distiguish instruments */
int nconfig; /* Number of configurations */
int config; /* Config this info applies to (always 1 ?) */
int cconfig; /* Current configuration (0 or 1 ?) */
int nifce; /* Number of interfaces */
int nep; /* Number of end points */
int wr_ep, rd_ep; /* Default end points to use for "serial" read/write */
int rd_qa; /* Read quanta size */
int ms_bytes; /* No. of Modem status bytes to strip from each read */
int latmsec; /* Latency timeout in msec for modem status bytes */
int (*interp_ms)(struct _icoms *p, unsigned char *msbytes);
/* return icom error from ms bytes, NULL if none */
usb_ep ep[32]; /* Information about each end point for general usb i/o */
/* Macro to access end point information */
#define EPINFO(addr) ep[((addr >> 3) & 0x10) + (addr & 0x0f)]
/* HID port parameters */
struct hid_idevice *hidd; /* HID port - copy of ppath->hidd */
#endif /* ENABLE_USB */
/* General parameters */
int lserr; /* Last serial communication error code */
a1log *log; /* Debug & Error log */
int debug; /* legacy - Flag, nz to print instrument io info to stderr */
/* Linked list to automate SIGKILL cleanup */
struct _icoms *next;
/* Public: */
/* Return the device category */
/* (Returns bit flags) */
icom_type (*dev_cat)(struct _icoms *p);
/* Return the communication port type */
/* (Can use equality tests on return value for normal ports, */
/* or bit flag for fastserio USB/serial port) */
icom_type (*port_type)(struct _icoms *p);
/* Return the communication port attributes */
/* (Returns bit flags) */
icom_type (*port_attr)(struct _icoms *p);
#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
/* Select the serial communications port and characteristics - extended version */
/* and open or close and re-open it */
/* return icom error */
int (*set_ser_port_ex)(
struct _icoms *p,
flow_control fc, /* Flow control */
baud_rate baud,
parity parity,
stop_bits stop_bits,
word_length word_length,
int delayms); /* Delay in ms after open */
/* Select the serial communications port and characteristics */
/* and open or close and re-open it */
/* return icom error */
int (*set_ser_port)(
struct _icoms *p,
flow_control fc, /* Flow control */
baud_rate baud,
parity parity,
stop_bits stop_bits,
word_length word_length);
#endif /* ENABLE_SERIAL */
#ifdef ENABLE_USB
/* Select the USB communications port and characteristics and open or close and re-open it */
/* return icom error */
int (*set_usb_port)(
struct _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 */
);
/* Select the HID communications port and characteristics and open or close and re-open it */
/* return icom error */
int (*set_hid_port)(
struct _icoms *p,
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 */
);
#endif /* ENABLE_USB */
/* Close the port */
void (*close_port)(struct _icoms *p);
/* "Serial" write the characters in the buffer out */
/* Data will be written up to the terminating nul. */
/* return icom error */
int (*write)(
struct _icoms *p,
char *buf, /* nul terminated unless nch > 0 */
int nch, /* if > 0, number of characters to write, else nul terminated */
double tout); /* Timeout in seconds */
/* "Serial" read characters into the buffer */
/* The returned data will be terminated by a nul. */
/* return icom error */
int (*read)(
struct _icoms *p,
char *buf, /* Buffer to store characters read */
int bsize, /* Buffer size. Make this larger than chars required! */
int *bread, /* Bytes read (not including forced '\000') */
char *tc, /* Terminating characters, NULL for none or char count mode */
int ntc, /* Number of terminating characters or char count needed. */
double tout); /* Timeout in seconds */
/* "Serial" write and read */
/* return icom error */
int (*write_read)(
struct _icoms *p,
char *wbuf, /* Write puffer */
int nwch, /* if > 0, number of characters to write, else nul terminated */
char *rbuf, /* Read buffer */
int bsize, /* Buffer size. Make this larger than chars required! */
int *bread, /* Bytes read (not including forced '\000') */
char *tc, /* Terminating characers, NULL for none or char count mode */
int ntc, /* Number of any terminating characters needed, or char count needed */
double tout); /* Timeout in seconds */
/* "Serial" write and read with read flush */
/* return icom error */
int (*write_read_ex)(
struct _icoms *p,
char *wbuf, /* Write puffer */
int nwch, /* if > 0, number of characters to write, else nul terminated */
char *rbuf, /* Read buffer */
int bsize, /* Buffer size. Make this larger than chars required! */
int *bread, /* Bytes read (not including forced '\000') */
char *tc, /* Terminating characers, NULL for none or char count mode */
int ntc, /* Number of any terminating characters needed, or char count needed */
double tout, /* Timeout in seconds */
int frbw); /* nz to Flush Read Before Write */
/* For serial device, clear any errors */
void (*ser_clearerr)(struct _icoms *p);
/* For a USB device, do a control message */
/* return icom error */
int (*usb_control)(struct _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, sent little endian) */
int index, /* 16 bit index (USB wIndex, sent little endian) */
unsigned char *rwbuf, /* Read or write buffer */
int rwsize, /* Size to write/max bytes to read */
int *xferred, /* Bytes written or read (may be NULL) */
double tout); /* Timeout in seconds */
/* For a USB device, do a bulk or interrupt read from an end point */
/* return icom error */
int (*usb_read)(struct _icoms *p,
usb_cancelt *cancelt, /* Optionaly tokem that can be used to cancel */
int ep, /* End point address */
unsigned char *buf, /* Read buffer */
int bsize, /* Bytes to read or write */
int *bread, /* Bytes read */
double tout); /* Timeout in seconds */
/* For a USB device, do a bulk or interrupt write to an end point */
/* return icom error */
int (*usb_write)(struct _icoms *p,
usb_cancelt *cancelt, /* Optionaly tokem that can be used to cancel */
int ep, /* End point address */
unsigned char *wbuf, /* Write buffer */
int wsize, /* Bytes to or write */
int *bwritten, /* Bytes written */
double tout); /* Timeout in seconds */
/* Wait until a read/write in another thread has started. */
/* return icom error */
int (*usb_wait_io)(struct _icoms *p,
usb_cancelt *cantelt); /* Cancel handle */
/* Cancel a read/write in another thread */
/* return icom error */
int (*usb_cancel_io)(struct _icoms *p,
usb_cancelt *cantelt); /* Cancel handle */
/* Reset and end point toggle state to 0 */
/* return icom error */
int (*usb_resetep)(struct _icoms *p,
int ep); /* End point address */
/* Clear an end point halt */
/* return icom error */
int (*usb_clearhalt)(struct _icoms *p,
int ep); /* End point address */
/* For an HID device, read a message from the device. */
/* return icom error */
int (*hid_read)(struct _icoms *p,
unsigned char *buf, /* Read buffer */
int bsize, /* Bytes to read or write */
int *bread, /* Bytes read */
double tout, /* Timeout in seconds */
int id /* If nz, then first byte of buffer is HID ID */
);
/* For an HID device, write a message to the device */
/* return icom error */
int (*hid_write)(struct _icoms *p,
unsigned char *wbuf, /* Write buffer */
int wsize, /* Bytes to or write */
int *bwritten, /* Bytes written */
double tout, /* Timeout in seconds */
int id /* If nz, then first byte of buffer is HID ID */
);
/* Optional callback to client from device */
/* Default implementation is a NOOP */
int (*interrupt)(struct _icoms *p,
int icom_int); /* Interrupt cause */
/* Destroy ourselves */
void (*del)(struct _icoms *p);
};
/* Constructor */
extern icoms *new_icoms(icompath *ipath, a1log *log);
/* - - - - - - - - - - - - - - - - - - -- */
/* Utilities */
/* Convert control chars to ^[A-Z] notation in a string. */
/* Returns a maximum of 1000 characters in static buffer. */
char *icoms_fix(char *s);
/* Convert a limited binary buffer to a list of hex */
char *icoms_tohex(unsigned char *s, int len);
/* Implementation declarations */
#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
int icoms_ser_write(icoms *p, char *wbuf, int nwch, double tout);
int icoms_ser_read(icoms *p, char *rbuf, int bsize, int *bread,
char *tc, int ntc, double tout);
#endif
/* Modify a serial path to add the device type */
int icompaths_set_serial_itype(icompath *p, devType itype);
#ifdef __cplusplus
}
#endif
#define ICOMS_H
#endif /* ICOMS_H */