diff -r 978487ba3d58 -r 04fccc0fc66b spectro/Jamfile --- a/spectro/Jamfile Mon Jan 04 23:30:41 2016 +0100 +++ b/spectro/Jamfile Mon Jan 25 22:14:44 2016 +0100 @@ -96,7 +96,7 @@ USB_INSTS = dtp20.c i1disp.c i1d3.c i1pro.c i1pro_imp.c munki.c munki_imp.c hcfr.c spyd2.c huey.c - colorhug.c ex1.c usbio.c hidio.c ; + colorhug.c acb8300.c ex1.c usbio.c hidio.c ; FAST_SER_INSTS = specbos.c kleink10.c smcube.c ; diff -r 978487ba3d58 -r 04fccc0fc66b spectro/acb8300.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spectro/acb8300.c Mon Jan 25 22:14:44 2016 +0100 @@ -0,0 +1,1149 @@ + + +/* + * Argyll Color Correction System + * + * LG ACB8300 related functions + * + * Author: Andreas Boehler + * Date: 01/01/2016 + * + * Copyright 2006 - 2014, Graeme W. Gill + * Copyright 2011, Richard Hughes + * All rights reserved. + * + * (Based on huey.c) + * + * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- + * see the License2.txt file for licencing details. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef SALONEINSTLIB +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#else /* SALONEINSTLIB */ +#include "sa_config.h" +#include "numsup.h" +#endif /* SALONEINSTLIB */ +#include "xspect.h" +#include "insttypes.h" +#include "conv.h" +#include "icoms.h" +#include "acb8300.h" + +static inst_code acb8300_interp_code(inst *pp, int ec); + +/* Interpret an icoms error into a ACB8300 error */ +static int icoms2acb8300_err(int se) { + if (se != ICOM_OK) + return ACB8300_COMS_FAIL; + return ACB8300_OK; +} + +/* ACB8300 commands that we care about */ +typedef enum { + acb_init_device = 0x01, /* Initialize the device */ + acb_get_calib1 = 0x51, /* Get first part of calibration matrix */ + acb_get_calib2 = 0x52, /* Get second part of calibration matrix */ + acb_get_calib3 = 0x54, /* Get third part of calibration matrix */ + acb_get_calib4 = 0x55, /* Get fourth part of calibration matrix */ + acb_take_reading = 0x31, /* Takes a raw reading */ + acb_get_fwversion = 0x80, /* Get Firmware version */ +} Acb8300Cmd; + +static char *inst_desc_rx(int cc) { + static char buf[40]; + switch(cc) { + case 0x09: + return "InitDevice"; + case 0x53: + return "GetCalibration"; + case 0x32: + return "TakeReading"; + case 0x88: + return "GetFWVersion"; + } + sprintf(buf, "Unknown %02x",cc); + return buf; +} + +/* Diagnostic - return a description given the instruction code */ +static char *inst_desc(int cc) { + static char buf[40]; + switch(cc) { + case 0x01: + return "InitDevice"; + case 0x51: + return "GetCalibration1"; + case 0x52: + return "GetCalibration2"; + case 0x54: + return "GetCalibration3"; + case 0x55: + return "GetCalibration4"; + case 0x31: + return "TakeReading"; + case 0x80: + return "GetFWVersion"; + } + sprintf(buf,"Unknown %02x",cc); + return buf; +} + +/* Error codes interpretation */ +static char * +acb8300_interp_error(inst *pp, int ec) { + ec &= inst_imask; + switch (ec) { + case ACB8300_INTERNAL_ERROR: + return "Internal software error"; + case ACB8300_COMS_FAIL: + return "Communications failure"; + case ACB8300_UNKNOWN_MODEL: + return "Not a known ACB8300 Model"; + + case ACB8300_OK: + return "OK"; + case ACB8300_UNKNOWN_CMD: + return "Unknown connamd"; + case ACB8300_WRONG_UNLOCK_CODE: + return "Wrong unlock code"; + case ACB8300_NOT_IMPLEMENTED: + return "Not implemented"; + case ACB8300_UNDERFLOW_SENSOR: + return "Sensor underflow"; + case ACB8300_NO_SERIAL: + return "No serial"; + case ACB8300_WATCHDOG: + return "Watchdog"; + case ACB8300_INVALID_ADDRESS: + return "Invalid address"; + case ACB8300_INVALID_LENGTH: + return "Invalid length"; + case ACB8300_INVALID_CHECKSUM: + return "Invlid checksum"; + case ACB8300_INVALID_VALUE: + return "Invalid value"; + case ACB8300_UNKNOWN_CMD_FOR_BOOTLOADER: + return "Unknown command for bootloader"; + case ACB8300_NO_CALIBRATION: + return "No calibration"; + case ACB8300_OVERFLOW_MULTIPLY: + return "Multiply overflow"; + case ACB8300_OVERFLOW_ADDITION: + return "Addition overflow"; + case ACB8300_OVERFLOW_SENSOR: + return "Sensor overflow"; + case ACB8300_OVERFLOW_STACK: + return "Stack overflow"; + case ACB8300_DEVICE_DEACTIVATED: + return "Device deactivated"; + case ACB8300_INCOMPLETE_REQUEST: + return "Incomplete request"; + + /* Internal errors */ + case ACB8300_NO_COMS: + return "Communications hasn't been established"; + case ACB8300_NOT_INITED: + return "Instrument hasn't been initialised"; + case ACB8300_WRONG_MODEL: + return "Attempt to use wrong command for model"; + default: + return "Unknown error code"; + } +} + +/* Do a command/response exchange with the ACB8300 */ +static inst_code +acb8300_command(acb8300 *p, + Acb8300Cmd cmd, + unsigned char *in, unsigned int in_size, + unsigned char *out, unsigned int out_size, + double timeout) +{ + int i; + unsigned char buf[43]; + int xwbytes, wbytes; + int xrbytes, rbytes; + int se, ua = 0, rv = inst_ok; + int ishid = p->icom->port_type(p->icom) == icomt_hid; + + a1logd(p->log,5,"acb8300_command: sending cmd '%s' args '%s'\n", + inst_desc(cmd), icoms_tohex(in, in_size)); + + /* Send the command with any specified data */ + memset(buf, 0, 43); + buf[0] = cmd; + if (in != NULL) + memcpy(buf + 1, in, in_size); + xwbytes = 43; + if (ishid) { + se = p->icom->hid_write(p->icom, buf, xwbytes, &wbytes, timeout); + } else { + se = p->icom->usb_write(p->icom, NULL, 0x01, buf, xwbytes, &wbytes, timeout); + } + a1logd(p->log,8,"acb8300_command: Send %d bytes and %d sent\n",xwbytes,wbytes); + if (se != 0) { + a1logd(p->log,1,"acb8300_command: command send failed with ICOM err 0x%x\n",se); + return acb8300_interp_code((inst *)p, ACB8300_COMS_FAIL); + } + rv = acb8300_interp_code((inst *)p, icoms2acb8300_err(ua)); + if (rv == inst_ok && wbytes != xwbytes) + rv = acb8300_interp_code((inst *)p, ACB8300_BAD_WR_LENGTH); + a1logd(p->log,6,"acb8300_command: got inst code %i\n",rv); + + if (rv != inst_ok) { + /* Flush any response if write failed */ + if (ishid) + p->icom->hid_read(p->icom, buf, 43, &rbytes, timeout); + else + p->icom->usb_read(p->icom, NULL, 0x81, buf, out_size + 2, &rbytes, timeout); + return rv; + } + + /* Now fetch the response */ + a1logd(p->log,6,"acb8300_command: Reading response\n"); + xrbytes = 43; + if (ishid) { + se = p->icom->hid_read(p->icom, buf, xrbytes, &rbytes, timeout); + } else { + se = p->icom->usb_read(p->icom, NULL, 0x81, buf, xrbytes, &rbytes, timeout); + } + + a1logd(p->log,8,"acb8300_command: Read %d bytes and %d read\n",xrbytes,rbytes); + if (rbytes >= 2) { + a1logd(p->log,6,"acb8300_command: recieved cmd '%s' error '%s' args '%s'\n", + inst_desc_rx(buf[0]), + acb8300_interp_error((inst *) p, buf[0]), + icoms_tohex(buf, rbytes - 2)); + } + + if (se != 0) { + /* deal with underrun or overrun */ + if (rbytes != xrbytes) { + a1logd(p->log,1,"acb8300_command: got underrun or overrun\n"); + rv = acb8300_interp_code((inst *)p, ACB8300_BAD_RD_LENGTH); + return rv; + } + + if (se != ICOM_SHORT) { /* Allow short read for firware compatibility */ + /* there's another reason it failed */ + a1logd(p->log,1,"acb8300_command: read failed with ICOM err 0x%x\n",se); + return acb8300_interp_code((inst *)p, ACB8300_COMS_FAIL); + } + } + rv = acb8300_interp_code((inst *)p, icoms2acb8300_err(ua)); + + if (rv == inst_ok && out != NULL) + memcpy(out, buf, out_size); + + a1logd(p->log,5,"acb8300_command: returning '%s' ICOM err 0x%x\n", + icoms_tohex(out, out_size),ua); + return rv; +} + +/* Converts 8 bytes of packed double into a double */ +static double buf2double(unsigned char *buf) +{ + double val; + memcpy(&val, buf, 8); + return val; +} + +/* Take a measurement from the device */ +static inst_code +acb8300_take_measurement(acb8300 *p, double XYZ[3]) +{ + inst_code ev; + int i; + int num_meas = 3; // Average three measurements + unsigned char resp[43]; + short adc[3] = {0, 0, 0}; + + if (!p->inited) + return acb8300_interp_code((inst *)p, ACB8300_NOT_INITED); + + for(int i=0; ilog,5,"acb8300_take_measurement: ADC = %i %i %i\n",adc[0],adc[1],adc[2]); + + XYZ[0] = adc[0] - p->adc_offset[0]; + XYZ[1] = adc[1] - p->adc_offset[1]; + XYZ[2] = adc[2] - p->adc_offset[2]; + + a1logd(p->log,5,"acb8300_take_measurement: XYZ after ADC offset = %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]); + + icmMulBy3x3(XYZ, p->adc2xyz, XYZ); + + a1logd(p->log,5,"acb8300_take_measurement: XYZ after first mult = %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]); + + XYZ[0] = XYZ[0] + p->xyz_offset[0]; + XYZ[1] = XYZ[1] + p->xyz_offset[1]; + XYZ[2] = XYZ[2] + p->xyz_offset[2]; + + a1logd(p->log,5,"acb8300_take_measurement: XYZ after XYZ offset = %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]); + + a1logd(p->log,5,"acb8300_take_measurement: ICX is %d\n", p->icx); + + switch(p->icx) + { + case 6: + icmMulBy3x3(XYZ, p->dtype_mat6, XYZ); + break; + case 5: + icmMulBy3x3(XYZ, p->dtype_mat5, XYZ); + break; + case 4: + icmMulBy3x3(XYZ, p->dtype_mat4, XYZ); + break; + case 3: + icmMulBy3x3(XYZ, p->dtype_mat3, XYZ); + break; + case 2: + icmMulBy3x3(XYZ, p->dtype_mat2, XYZ); + break; + case 1: + icmMulBy3x3(XYZ, p->dtype_mat1, XYZ); + break; + case 0: + default: + icmMulBy3x3(XYZ, p->dtype_mat0, XYZ); + } + + if(XYZ[0] < 0) + XYZ[0] = 0.001; + if(XYZ[1] < 0) + XYZ[1] = 0.001; + if(XYZ[2] < 0) + XYZ[2] = 0.001; + + a1logd(p->log,5,"acb8300_take_measurement: XYZ after internal display correction matrix = %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]); + + /* Apply the colorimeter correction matrix */ + icmMulBy3x3(XYZ, p->ccmat, XYZ); + + a1logd(p->log,3,"acb8300_take_measurement: XYZ = %f %f %f\n",XYZ[0],XYZ[1],XYZ[2]); + + return inst_ok; +} + +/* Establish communications with a ACB8300 */ +static inst_code +acb8300_init_coms(inst *pp, baud_rate br, flow_control fc, double tout) { + int se; + acb8300 *p = (acb8300 *) pp; + + a1logd(p->log, 2, "acb8300_init_coms: About to init coms\n"); + + /* Open as an HID if available */ + if (p->icom->port_type(p->icom) == icomt_hid) { + + a1logd(p->log, 3, "acb8300_init_coms: About to init HID\n"); + + /* Set config, interface */ + if ((se = p->icom->set_hid_port(p->icom, icomuf_none, 0, NULL)) != ICOM_OK) { + a1logd(p->log, 1, "acb8300_init_coms: set_hid_port failed ICOM err 0x%x\n",se); + return acb8300_interp_code((inst *)p, icoms2acb8300_err(se)); + } + + } else if (p->icom->port_type(p->icom) == icomt_usb) { + + a1logd(p->log, 3, "acb8300_init_coms: About to init USB\n"); + + /* Set config, interface, write end point, read end point */ + // ~~ does Linux need icomuf_reset_before_close ? Why ? + if ((se = p->icom->set_usb_port(p->icom, 1, 0x00, 0x00, icomuf_detach, 0, NULL)) + != ICOM_OK) { + a1logd(p->log, 1, "acb8300_init_coms: set_usb_port failed ICOM err 0x%x\n",se); + return acb8300_interp_code((inst *)p, icoms2acb8300_err(se)); + } + + } else { + a1logd(p->log, 1, "acb8300_init_coms: wrong communications type for device!\n"); + return inst_internal_error; + } + + a1logd(p->log, 2, "acb8300_init_coms: inited coms OK\n"); + + p->gotcoms = 1; + return inst_ok; +} + +/* Get the firmware version */ +static inst_code +acb8300_get_firmwareversion (acb8300 *p) +{ + a1logd(p->log,2,"acb8300: Get Firmware called\n"); + + inst_code ev; + unsigned char buf[43]; + int v_long; + + ev = acb8300_command(p, acb_get_fwversion, + NULL, 0, + buf, 43, + 2.0); + if (ev != inst_ok) + return ev; + + v_long = buf[1]; + p->version = v_long * 0.01; + + a1logd(p->log,2,"acb8300: Firware version = %2.1f\n",p->version); + + return ev; +} + +static inst_code set_default_disp_type(acb8300 *p); + +/* Initialise the ACB8300 */ +static inst_code +acb8300_init_inst(inst *pp) +{ + acb8300 *p = (acb8300 *)pp; + inst_code ev; + int i; + unsigned char resp[43]; + + a1logd(p->log, 2, "acb8300_init_inst: About to init inst\n"); + + /* Must establish coms first */ + if (p->gotcoms == 0) + return acb8300_interp_code((inst *)p, ACB8300_NO_COMS); + + /* Get the firmware version */ + ev = acb8300_get_firmwareversion(p); + if (ev != inst_ok) + return ev; + + ev = acb8300_command(p, + acb_init_device, + NULL, 0, + resp, 43, + 2.0); + if(ev != inst_ok) + return ev; + + /* Get Calibration values; the first floats are the matrix */ + ev = acb8300_command(p, + acb_get_calib1, + NULL, 0, + resp, 43, + 2.0); + if(ev != inst_ok) + return ev; + + p->adc2xyz[0][0] = buf2double(resp + 1 + 8*0); + p->adc2xyz[0][1] = buf2double(resp + 1 + 8*1); + p->adc2xyz[0][2] = buf2double(resp + 1 + 8*2); + p->adc2xyz[1][0] = buf2double(resp + 1 + 8*3); + p->adc2xyz[1][1] = buf2double(resp + 1 + 8*4); + + ev = acb8300_command(p, + acb_get_calib2, + NULL, 0, + resp, 43, + 2.0); + if(ev != inst_ok) + return ev; + + p->adc2xyz[1][2] = buf2double(resp + 1 + 8*0); + p->adc2xyz[2][0] = buf2double(resp + 1 + 8*1); + p->adc2xyz[2][1] = buf2double(resp + 1 + 8*2); + p->adc2xyz[2][2] = buf2double(resp + 1 + 8*3); + + if (p->log->debug >= 4) { + a1logd(p->log,4,"adc2xyz = %f %f %f\n", + p->adc2xyz[0][0], p->adc2xyz[0][1], p->adc2xyz[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->adc2xyz[1][0], p->adc2xyz[1][1], p->adc2xyz[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->adc2xyz[2][0], p->adc2xyz[2][1], p->adc2xyz[2][2]); + a1logd(p->log,4,"\n"); + } + + /* ADC offset correction */ + ev = acb8300_command(p, + acb_get_calib3, + NULL, 0, + resp, 43, + 2.0); + if(ev != inst_ok) + return ev; + + p->adc_offset[0] = buf2double(resp + 1 + 8*0); + p->adc_offset[1] = buf2double(resp + 1 + 8*1); + p->adc_offset[2] = buf2double(resp + 1 + 8*2); + + p->xyz_offset[0] = buf2double(resp + 1 + 8*3); + p->xyz_offset[1] = buf2double(resp + 1 + 8*4); + + a1logd(p->log, 4, "acb8300_init_inst: Read ADC offset calibration %f, %f, %f\n", + p->adc_offset[0], p->adc_offset[1], p->adc_offset[2]); + + ev = acb8300_command(p, + acb_get_calib4, + NULL, 0, + resp, 43, + 2.0); + if(ev != inst_ok) + return ev; + + p->xyz_offset[2] = buf2double(resp + 1); + + // The remaining bytes are probably either junk or simply the content + // of the previous buffer + + a1logd(p->log, 4, "acb8300_init_inst: Read XYZ offset calibration: %f, %f, %f\n", + p->xyz_offset[0], p->xyz_offset[1], p->xyz_offset[2]); + + p->trig = inst_opt_trig_user; + + /* Setup the default display type */ + if ((ev = set_default_disp_type(p)) != inst_ok) { + return ev; + } + + p->inited = 1; + a1logd(p->log, 2, "acb8300_init: inited coms OK\n"); + + a1logv(p->log,1,"Firmware Version: %2.1f\n" + ,p->version); + + return inst_ok; +} + +/* Read a single sample */ +static inst_code +acb8300_read_sample( +inst *pp, +char *name, /* Strip name (7 chars) */ +ipatch *val, /* Pointer to instrument patch value */ +instClamping clamp) { /* NZ if clamp XYZ/Lab to be +ve */ + acb8300 *p = (acb8300 *)pp; + int user_trig = 0; + int rv = inst_protocol_error; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if (p->trig == inst_opt_trig_user) { + + if (p->uicallback == NULL) { + a1logd(p->log, 1, "acb8300: inst_opt_trig_user but no uicallback function set!\n"); + return inst_unsupported; + } + + for (;;) { + if ((rv = p->uicallback(p->uic_cntx, inst_armed)) != inst_ok) { + if (rv == inst_user_abort) + return rv; /* Abort */ + if (rv == inst_user_trig) { + user_trig = 1; + break; /* Trigger */ + } + } + msec_sleep(200); + } + /* Notify of trigger */ + if (p->uicallback) + p->uicallback(p->uic_cntx, inst_triggered); + + /* Progromatic Trigger */ + } else { + /* Check for abort */ + if (p->uicallback != NULL + && (rv = p->uicallback(p->uic_cntx, inst_armed)) == inst_user_abort) + return rv; /* Abort */ + } + + /* Read the XYZ value */ + rv = acb8300_take_measurement(p, val->XYZ); + + if (rv != inst_ok) + return rv; + + /* This may not change anything since instrument may clamp */ + if (clamp) + icmClamp3(val->XYZ, val->XYZ); + + val->mtype = inst_mrt_emission; + val->XYZ_v = 1; /* These are absolute XYZ readings ? */ + val->sp.spec_n = 0; + val->duration = 0.0; + + + if (user_trig) + return inst_user_trig; + return rv; +} + +static inst_code set_base_disp_type(acb8300 *p, int cbid); + +/* Insert a colorimetric correction matrix */ +inst_code acb8300_col_cor_mat( +inst *pp, +disptech dtech, /* Use disptech_unknown if not known */ +int cbid, /* Calibration display type base ID required, 1 if unknown */ +double mtx[3][3] +) { + acb8300 *p = (acb8300 *)pp; + inst_code ev; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + if ((ev = set_base_disp_type(p, cbid)) != inst_ok) + return ev; + if (mtx == NULL) + icmSetUnity3x3(p->ccmat); + else + icmCpy3x3(p->ccmat, mtx); + + p->dtech = dtech; + p->refrmode = disptech_get_id(dtech)->refr; + p->cbid = 0; /* Base has been overwitten */ + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + + return inst_ok; +} + +/* Convert a machine specific error code into an abstract dtp code */ +static inst_code +acb8300_interp_code(inst *pp, int ec) { + ec &= inst_imask; + switch (ec) { + + case ACB8300_OK: + return inst_ok; + + case ACB8300_INTERNAL_ERROR: + case ACB8300_NO_COMS: + case ACB8300_NOT_INITED: + case ACB8300_WRONG_MODEL: + return inst_internal_error | ec; + + case ACB8300_COMS_FAIL: + return inst_coms_fail | ec; + + case ACB8300_UNKNOWN_MODEL: + return inst_unknown_model | ec; + + case ACB8300_UNKNOWN_CMD: + case ACB8300_WRONG_UNLOCK_CODE: + case ACB8300_NOT_IMPLEMENTED: + case ACB8300_UNDERFLOW_SENSOR: + case ACB8300_NO_SERIAL: + case ACB8300_WATCHDOG: + case ACB8300_INVALID_ADDRESS: + case ACB8300_INVALID_LENGTH: + case ACB8300_INVALID_CHECKSUM: + case ACB8300_INVALID_VALUE: + case ACB8300_UNKNOWN_CMD_FOR_BOOTLOADER: + case ACB8300_NO_CALIBRATION: + case ACB8300_OVERFLOW_MULTIPLY: + case ACB8300_OVERFLOW_ADDITION: + case ACB8300_OVERFLOW_SENSOR: + case ACB8300_OVERFLOW_STACK: + case ACB8300_DEVICE_DEACTIVATED: + case ACB8300_INCOMPLETE_REQUEST: + case ACB8300_BAD_WR_LENGTH: + case ACB8300_BAD_RD_LENGTH: + case ACB8300_BAD_RET_CMD: + case ACB8300_BAD_RET_STAT: + return inst_protocol_error | ec; + } + return inst_other_error | ec; +} + +/* Destroy ourselves */ +static void +acb8300_del(inst *pp) { + acb8300 *p = (acb8300 *)pp; + if (p != NULL) { + if (p->icom != NULL) + p->icom->del(p->icom); + inst_del_disptype_list(p->dtlist, p->ndtlist); + p->vdel(pp); + free(p); + } +} + +/* Return the instrument mode capabilities */ +void acb8300_capabilities(inst *pp, +inst_mode *pcap1, +inst2_capability *pcap2, +inst3_capability *pcap3) { + acb8300 *p = (acb8300 *)pp; + inst_mode cap = 0; + inst2_capability cap2 = 0; + + cap |= inst_mode_emis_spot + | inst_mode_colorimeter + ; + + cap2 |= inst2_prog_trig + | inst2_user_trig + | inst2_disptype + | inst2_ccmx + ; + + if (pcap1 != NULL) + *pcap1 = cap; + if (pcap2 != NULL) + *pcap2 = cap2; + if (pcap3 != NULL) + *pcap3 = inst3_none; +} + +/* Check device measurement mode */ +inst_code acb8300_check_mode(inst *pp, inst_mode m) { + acb8300 *p = (acb8300 *)pp; + inst_mode cap; + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + pp->capabilities(pp, &cap, NULL, NULL); + + /* Simple test */ + if (m & ~cap) + return inst_unsupported; + + /* only display emission mode supported */ + if (!IMODETST(m, inst_mode_emis_spot)) { + return inst_unsupported; + } + + return inst_ok; +} + +/* Set device measurement mode */ +inst_code acb8300_set_mode(inst *pp, inst_mode m) { + acb8300 *p = (acb8300 *)pp; + inst_code ev; + + if ((ev = acb8300_check_mode(pp, m)) != inst_ok) + return ev; + + p->mode = m; + + return inst_ok; +} + +static inst_disptypesel acb8300_disptypesel[5] = { + { + inst_dtflags_default, /* flags */ + 0, /* cbix */ + "l", /* sel */ + "MONITOR_IPS7", /* desc */ + 0, /* refr */ + disptech_lcd_wled_ips, /* disptype */ + 0 /* ix */ + }, + { + inst_dtflags_none, + 0, + "8", + "MONITOR_IPS8", + 0, + disptech_lcd_wled_ips, + 1 + }, + { + inst_dtflags_none, + 0, + "2", + "MONITOR_IPS88RGB", + 0, + disptech_lcd_rgbled_ips, + 2 + }, + { + inst_dtflags_none, + 0, + "9", + "MONITOR_IPS9", + 0, + disptech_lcd_wled_ips, + 3 + }, + { + inst_dtflags_end, + 0, + "", + "", + 0, + disptech_none, + 0 + } +}; + +/* Get mode and option details */ +static inst_code acb8300_get_disptypesel( +inst *pp, +int *pnsels, /* Return number of display types */ +inst_disptypesel **psels, /* Return the array of display types */ +int allconfig, /* nz to return list for all configs, not just current. */ +int recreate /* nz to re-check for new ccmx & ccss files */ +) { + acb8300 *p = (acb8300 *)pp; + inst_code rv = inst_ok; + + /* Create/Re-create a current list of abailable display types */ + if (p->dtlist == NULL || recreate) { + if ((rv = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + acb8300_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return rv; + } + + if (pnsels != NULL) + *pnsels = p->ndtlist; + + if (psels != NULL) + *psels = p->dtlist; + + return inst_ok; +} + +/* Given a display type implementation from inst_disptypesel */ +static inst_code set_disp_type(acb8300 *p, inst_disptypesel *dentry) { + int ix; + + if (dentry->flags & inst_dtflags_ccmx) { + inst_code ev; + if ((ev = set_base_disp_type(p, dentry->cc_cbid)) != inst_ok) + return ev; + icmCpy3x3(p->ccmat, dentry->mat); + p->dtech = dentry->dtech; + p->cbid = 0; /* Can't be a base type */ + + } else { + + /* The HW handles up to 6 calibrations */ + ix = dentry->ix; + if (ix != 10 && ix != 11 && (ix < 0 || ix > 3)) + return inst_unsupported; + + p->icx = ix; + p->dtech = dentry->dtech; + p->cbid = dentry->cbid; + p->ucbid = dentry->cbid; /* This is underying base if dentry is base selection */ + icmSetUnity3x3(p->ccmat); + } + p->refrmode = dentry->refr; + + if (p->log->debug >= 4) { + a1logd(p->log,4,"ccmat = %f %f %f\n", + p->ccmat[0][0], p->ccmat[0][1], p->ccmat[0][2]); + a1logd(p->log,4," %f %f %f\n", + p->ccmat[1][0], p->ccmat[1][1], p->ccmat[1][2]); + a1logd(p->log,4," %f %f %f\n\n", + p->ccmat[2][0], p->ccmat[2][1], p->ccmat[2][2]); + a1logd(p->log,4,"ucbid = %d, cbid = %d\n",p->ucbid, p->cbid); + a1logd(p->log,4,"\n"); + } + + return inst_ok; +} + +/* Set the display type */ +static inst_code acb8300_set_disptype(inst *pp, int ix) { + acb8300 *p = (acb8300 *)pp; + inst_code ev; + inst_disptypesel *dentry; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list(pp, &p->ndtlist, &p->dtlist, + acb8300_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + if (ix < 0 || ix >= p->ndtlist) + return inst_unsupported; + + dentry = &p->dtlist[ix]; + + if ((ev = set_disp_type(p, dentry)) != inst_ok) { + return ev; + } + + return inst_ok; +} + +/* Setup the default display type */ +static inst_code set_default_disp_type(acb8300 *p) { + inst_code ev; + int i; + + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, + acb8300_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (p->dtlist[i].flags & inst_dtflags_default) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_default_disp_type: failed to find type!\n"); + return inst_internal_error; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { + return ev; + } + + return inst_ok; +} + +/* Setup the display type to the given base type */ +static inst_code set_base_disp_type(acb8300 *p, int cbid) { + inst_code ev; + int i; + + if (cbid == 0) { + a1loge(p->log, 1, "acb8300 set_base_disp_type: can't set base display type of 0\n"); + return inst_wrong_setup; + } + if (p->dtlist == NULL) { + if ((ev = inst_creat_disptype_list((inst *)p, &p->ndtlist, &p->dtlist, + acb8300_disptypesel, 0 /* doccss*/, 1 /* doccmx */)) != inst_ok) + return ev; + } + + for (i = 0; !(p->dtlist[i].flags & inst_dtflags_end); i++) { + if (!(p->dtlist[i].flags & inst_dtflags_ccmx) /* Prevent infinite recursion */ + && p->dtlist[i].cbid == cbid) + break; + } + if (p->dtlist[i].flags & inst_dtflags_end) { + a1loge(p->log, 1, "set_base_disp_type: failed to find cbid %d!\n",cbid); + return inst_wrong_setup; + } + if ((ev = set_disp_type(p, &p->dtlist[i])) != inst_ok) { + return ev; + } + + return inst_ok; +} + +/* Get the disptech and other corresponding info for the current */ +/* selected display type. Returns disptype_unknown by default. */ +/* Because refrmode can be overridden, it may not match the refrmode */ +/* of the dtech. (Pointers may be NULL if not needed) */ +static inst_code acb8300_get_disptechi( +inst *pp, +disptech *dtech, +int *refrmode, +int *cbid) { + acb8300 *p = (acb8300 *)pp; + if (dtech != NULL) + *dtech = p->dtech; + if (refrmode != NULL) + *refrmode = p->refrmode; + if (cbid != NULL) + *cbid = p->cbid; + return inst_ok; +} + +/* + * Set or reset an optional mode. + * + * Some options talk to the instrument, and these will + * error if it hasn't been initialised. + */ +static inst_code +acb8300_get_set_opt(inst *pp, inst_opt_type m, ...) +{ + acb8300 *p = (acb8300 *)pp; + inst_code ev = inst_ok; + + /* Record the trigger mode */ + if (m == inst_opt_trig_prog + || m == inst_opt_trig_user) { + p->trig = m; + return inst_ok; + } + + if (!p->gotcoms) + return inst_no_coms; + if (!p->inited) + return inst_no_init; + + return inst_unsupported; +} + +/* Constructor */ +extern acb8300 *new_acb8300(icoms *icom, instType itype) { + acb8300 *p; + int i; + + if ((p = (acb8300 *)calloc(sizeof(acb8300),1)) == NULL) { + a1loge(icom->log, 1, "new_acb8300: malloc failed!\n"); + return NULL; + } + + p->log = new_a1log_d(icom->log); + + p->init_coms = acb8300_init_coms; + p->init_inst = acb8300_init_inst; + p->capabilities = acb8300_capabilities; + p->check_mode = acb8300_check_mode; + p->set_mode = acb8300_set_mode; + p->get_disptypesel = acb8300_get_disptypesel; + p->set_disptype = acb8300_set_disptype; + p->get_disptechi = acb8300_get_disptechi; + p->get_set_opt = acb8300_get_set_opt; + p->read_sample = acb8300_read_sample; + p->col_cor_mat = acb8300_col_cor_mat; + p->interp_error = acb8300_interp_error; + p->del = acb8300_del; + + p->icom = icom; + p->itype = itype; + + icmSetUnity3x3(p->ccmat); + + /* To be read by calibration */ + icmSetUnity3x3(p->adc2xyz); + + p->xyz_offset[0] = 0.0; + p->xyz_offset[1] = 0.0; + p->xyz_offset[2] = 0.0; + p->adc_offset[0] = 0.0; + p->adc_offset[1] = 0.0; + p->adc_offset[2] = 0.0; + + /* Conversion matrix for Display Type 0 */ + p->dtype_mat0[0][0] = 0.95099; + p->dtype_mat0[0][1] = -0.00356; + p->dtype_mat0[0][2] = 0.00587; + + p->dtype_mat0[1][0] = -0.01592; + p->dtype_mat0[1][1] = 1.0654; + p->dtype_mat0[1][2] = 0.00556; + + p->dtype_mat0[2][0] = 0.06368; + p->dtype_mat0[2][1] = 0.02183; + p->dtype_mat0[2][2] = 0.99207; + + /* Conversion matrix for Display Type 1 is a unity matrix */ + + icmSetUnity3x3(p->dtype_mat1); + + /* Conversion matrix for Display Type 2 */ + + p->dtype_mat2[0][0] = 0.88704; + p->dtype_mat2[0][1] = -0.02045; + p->dtype_mat2[0][2] = 0.00975; + + p->dtype_mat2[1][0] = -0.00493; + p->dtype_mat2[1][1] = 0.96338; + p->dtype_mat2[1][2] = 0.00386; + + p->dtype_mat2[2][0] = 0.04496; + p->dtype_mat2[2][1] = 0.00158; + p->dtype_mat2[2][2] = 0.89155; + + /* Conversion matrix for Display Type 3 */ + + p->dtype_mat3[0][0] = 0.89671; + p->dtype_mat3[0][1] = -0.01634; + p->dtype_mat3[0][2] = 0.01728; + + p->dtype_mat3[1][0] = -0.00719; + p->dtype_mat3[1][1] = 0.97726; + p->dtype_mat3[1][2] = 0.00438; + + p->dtype_mat3[2][0] = 0.045; + p->dtype_mat3[2][1] = 0.0056; + p->dtype_mat3[2][2] = 0.93933; + + /* Conversion matrix for Display Type 4 */ + + p->dtype_mat4[0][0] = 0.90682; + p->dtype_mat4[0][1] = -0.0191; + p->dtype_mat4[0][2] = 0.00781; + + p->dtype_mat4[1][0] = -0.00721; + p->dtype_mat4[1][1] = 0.98713; + p->dtype_mat4[1][2] = 0.00482; + + p->dtype_mat4[2][0] = 0.04544; + p->dtype_mat4[2][1] = 0.01126; + p->dtype_mat4[2][2] = 0.90933; + + /* Conversion matrix for Display Type 5 */ + + p->dtype_mat5[0][0] = 0.93897; + p->dtype_mat5[0][1] = -0.00303; + p->dtype_mat5[0][2] = -0.00907; + + p->dtype_mat5[1][0] = 0.01207; + p->dtype_mat5[1][1] = 0.93885; + p->dtype_mat5[1][2] = -0.00211; + + p->dtype_mat5[2][0] = 0.00543; + p->dtype_mat5[2][1] = -0.01027; + p->dtype_mat5[2][2] = 0.89657; + + /* Conversion matrix for Display Type 6 */ + + p->dtype_mat6[0][0] = 0.91415; + p->dtype_mat6[0][1] = 0.03203; + p->dtype_mat6[0][2] = 0.00421; + + p->dtype_mat6[1][0] = -0.01378; + p->dtype_mat6[1][1] = 0.97898; + p->dtype_mat6[1][2] = 0.00276; + + p->dtype_mat6[2][0] = 0.02092; + p->dtype_mat6[2][1] = -0.01174; + p->dtype_mat6[2][2] = 0.94551; + + p->dtech = disptech_unknown; + + return p; +} + diff -r 978487ba3d58 -r 04fccc0fc66b spectro/acb8300.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spectro/acb8300.h Mon Jan 25 22:14:44 2016 +0100 @@ -0,0 +1,99 @@ +#ifndef ACB8300_H + +/* + * Argyll Color Correction System + * + * LG ACB8300 related defines + * + * Author: Andreas Boehler + * Date: 01/01/2016 + * + * Copyright 2006 - 2013, Graeme W. Gill + * Copyright 2011, Richard Hughes + * All rights reserved. + * + * (Based on colorhug.h) + * + * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- + * see the License2.txt file for licencing details. + */ + +#include "inst.h" + +/* Note: update acb8300_interp_error() and acb8300_interp_code() in acb8300.c */ +/* if anything of these #defines are added or subtracted */ + +/* Fake Error codes */ +#define ACB8300_INTERNAL_ERROR 0x61 /* Internal software error */ +#define ACB8300_COMS_FAIL 0x62 /* Communication failure */ +#define ACB8300_UNKNOWN_MODEL 0x63 /* Not an ACB8300 */ +#define ACB8300_DATA_PARSE_ERROR 0x64 /* Read data parsing error */ + +/* Real error codes */ +#define ACB8300_OK 0x00 +#define ACB8300_UNKNOWN_CMD 0x01 +#define ACB8300_WRONG_UNLOCK_CODE 0x02 +#define ACB8300_NOT_IMPLEMENTED 0x03 +#define ACB8300_UNDERFLOW_SENSOR 0x04 +#define ACB8300_NO_SERIAL 0x05 +#define ACB8300_WATCHDOG 0x06 +#define ACB8300_INVALID_ADDRESS 0x07 +#define ACB8300_INVALID_LENGTH 0x08 +#define ACB8300_INVALID_CHECKSUM 0x09 +#define ACB8300_INVALID_VALUE 0x0a +#define ACB8300_UNKNOWN_CMD_FOR_BOOTLOADER 0x0b +#define ACB8300_NO_CALIBRATION 0x0c +#define ACB8300_OVERFLOW_MULTIPLY 0x0d +#define ACB8300_OVERFLOW_ADDITION 0x0e +#define ACB8300_OVERFLOW_SENSOR 0x0f +#define ACB8300_OVERFLOW_STACK 0x10 +#define ACB8300_DEVICE_DEACTIVATED 0x11 +#define ACB8300_INCOMPLETE_REQUEST 0x12 + +/* Internal errors */ +#define ACB8300_NO_COMS 0x22 +#define ACB8300_NOT_INITED 0x23 +#define ACB8300_BAD_WR_LENGTH 0x25 +#define ACB8300_BAD_RD_LENGTH 0x26 +#define ACB8300_BAD_RET_CMD 0x27 +#define ACB8300_BAD_RET_STAT 0x28 +#define ACB8300_WRONG_MODEL 0x29 + +/* ACB8300 communication object */ +struct _acb8300 { + INST_OBJ_BASE + + inst_mode mode; /* Currently selected mode */ + + inst_opt_type trig; /* Reading trigger mode */ + + float version; /* Version number */ + + inst_disptypesel *dtlist; /* Display Type list */ + int ndtlist; /* Number of valid dtlist entries */ + int icx; /* Internal calibration matrix index, 0=default */ + disptech dtech; /* Display technology enum */ + int cbid; /* current calibration base ID, 0 if not a base */ + int ucbid; /* Underlying base ID if being used for matrix, 0 othewise */ + int refrmode; /* Refresh mode (always 0) */ + double ccmat[3][3]; /* Colorimeter correction matrix */ + double adc2xyz[3][3]; /* ADC to XYZ correction matrix */ + double adc_offset[3]; /* ADC offset correction */ + double xyz_offset[3]; /* XYZ offset correction */ + double dtype_mat0[3][3]; /* Display Type 0 correction matrix */ + double dtype_mat1[3][3]; /* Display Type 1 correction matrix */ + double dtype_mat2[3][3]; /* Display Type 2 correction matrix */ + double dtype_mat3[3][3]; /* Display Type 3 correction matrix */ + double dtype_mat4[3][3]; /* Display Type 4 correction matrix */ + double dtype_mat5[3][3]; /* Display Type 5 correction matrix */ + double dtype_mat6[3][3]; /* Display Type 6 correction matrix */ + + +}; typedef struct _acb8300 acb8300; + +/* Constructor */ +extern acb8300 *new_acb8300(icoms *icom, instType itype); + + +#define ACB8300_H +#endif /* ACB8300_H */ diff -r 978487ba3d58 -r 04fccc0fc66b spectro/inst.c --- a/spectro/inst.c Mon Jan 04 23:30:41 2016 +0100 +++ b/spectro/inst.c Mon Jan 25 22:14:44 2016 +0100 @@ -703,6 +703,8 @@ else if (itype == instColorHug || itype == instColorHug2) p = (inst *)new_colorhug(icom, itype); + else if (itype == instAcb8300) + p = (inst *)new_acb8300(icom, itype); #endif /* ENABLE_USB */ diff -r 978487ba3d58 -r 04fccc0fc66b spectro/insttypeinst.h --- a/spectro/insttypeinst.h Mon Jan 04 23:30:41 2016 +0100 +++ b/spectro/insttypeinst.h Mon Jan 25 22:14:44 2016 +0100 @@ -35,6 +35,7 @@ # include "ex1.h" # include "hcfr.h" # include "colorhug.h" +# include "acb8300.h" #endif diff -r 978487ba3d58 -r 04fccc0fc66b spectro/insttypes.c --- a/spectro/insttypes.c Mon Jan 04 23:30:41 2016 +0100 +++ b/spectro/insttypes.c Mon Jan 25 22:14:44 2016 +0100 @@ -104,6 +104,8 @@ return "ColorHug"; case instColorHug2: return "ColorHug2"; + case instAcb8300: + return "ACB8300"; default: break; } @@ -177,6 +179,8 @@ return "Hughski ColorHug"; case instColorHug2: return "Hughski ColorHug2"; + case instAcb8300: + return "LG ACB8300"; default: break; } @@ -268,6 +272,8 @@ return instColorHug; else if (strcmp(name, "Hughski ColorHug2") == 0) return instColorHug2; + else if (strcmp(name, "LG ACB8300") == 0) + return instAcb8300; return instUnknown; @@ -356,6 +362,10 @@ if (idVendor == 0x273f && idProduct == 0x1004) { /* Hughski & ColorHug2 */ return instColorHug2; } + + if (idVendor == 0x043e && idProduct == 0x9af0) { /* LG ACB8300 */ + return instAcb8300; + } /* Add other instruments here */ @@ -460,6 +470,8 @@ case instColorHug2: return 1; /* Not applicable */ + case instAcb8300: + return 1; /* Not applicable */ default: break; diff -r 978487ba3d58 -r 04fccc0fc66b spectro/insttypes.h --- a/spectro/insttypes.h Mon Jan 04 23:30:41 2016 +0100 +++ b/spectro/insttypes.h Mon Jan 25 22:14:44 2016 +0100 @@ -58,6 +58,7 @@ instEX1, /* Image Engineering EX1 */ instColorHug, /* Hughski ColorHug */ instColorHug2, /* Hughski ColorHug2 */ + instAcb8300, /* LG ACB8300 */ instFakeDisp = 9998, /* Fake display & instrument device id */ diff -r 978487ba3d58 -r 04fccc0fc66b usb/45-Argyll.rules --- a/usb/45-Argyll.rules Mon Jan 04 23:30:41 2016 +0100 +++ b/usb/45-Argyll.rules Mon Jan 25 22:14:44 2016 +0100 @@ -65,5 +65,8 @@ SYSFS{idVendor}=="273f", SYSFS{idProduct}=="1001", PROGRAM="/bin/sh -c 'K=%k; K=$${K#usbdev}; printf bus/usb/%%03i/%%03i $${K%%%%.*} $${K#*.}'", NAME="%c", MODE="660", GROUP="colord" SYSFS{idVendor}=="273f", SYSFS{idProduct}=="1004", PROGRAM="/bin/sh -c 'K=%k; K=$${K#usbdev}; printf bus/usb/%%03i/%%03i $${K%%%%.*} $${K#*.}'", NAME="%c", MODE="660", GROUP="colord" +# ACB8300 +SYSFS{idVendor}=="043e", SYSFS{idProduct}=="9af0", PROGRAM="/bin/sh -c 'K=%k; K=$${K#usbdev}; printf bus/usb/%%03i/%%03i $${K%%%%.*} $${K#*.}'", NAME="%c", MODE="660", GROUP="colord" + # Image Engineering EX1 SYSFS{idVendor}=="2457", SYSFS{idProduct}=="4000", PROGRAM="/bin/sh -c 'K=%k; K=$${K#usbdev}; printf bus/usb/%%03i/%%03i $${K%%%%.*} $${K#*.}'", NAME="%c", MODE="660", GROUP="colord" diff -r 978487ba3d58 -r 04fccc0fc66b usb/55-Argyll.rules --- a/usb/55-Argyll.rules Mon Jan 04 23:30:41 2016 +0100 +++ b/usb/55-Argyll.rules Mon Jan 25 22:14:44 2016 +0100 @@ -98,6 +98,9 @@ # ColorHug Spectro ATTRS{idVendor}=="273f", ATTRS{idProduct}=="1002", ENV{COLORD_SENSOR_KIND}="colorhug-spectro", ENV{COLORD_SENSOR_CAPS}="lcd" +# ACB8300 +ATTRS{idVendor}=="043e", ATTRS{idProduct}=="9af0", ENV{COLORD_SENSOR_KIND}="acb8300", ENV{COLORD_SENSOR_CAPS}="lcd" + # Image Engineering EX1 ATTRS{idVendor}=="2457", ATTRS{idProduct}=="4000", ENV{COLORD_SENSOR_KIND}="ex1", ENV{COLORD_SENSOR_CAPS}="lcd crt ambient" diff -r 978487ba3d58 -r 04fccc0fc66b usb/Argyll.usermap --- a/usb/Argyll.usermap Mon Jan 04 23:30:41 2016 +0100 +++ b/usb/Argyll.usermap Mon Jan 25 22:14:44 2016 +0100 @@ -55,5 +55,8 @@ Argyll 0x0003 0x04D8 0xF8DA 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 Argyll 0x0003 0x273F 0x1001 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # +# ACB8300 +Argyll 0x0003 0x043E 0x9AF0 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# # Image Engineering EX1 Argyll 0x0003 0x2457 0x4000 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 diff -r 978487ba3d58 -r 04fccc0fc66b usb/ArgyllCMS.inf --- a/usb/ArgyllCMS.inf Mon Jan 04 23:30:41 2016 +0100 +++ b/usb/ArgyllCMS.inf Mon Jan 25 22:14:44 2016 +0100 @@ -108,6 +108,7 @@ "Gretag Macbeth/X-Rite"=GM_X_Rite_Devices,NTx86,NTamd64 "Hughski Ltd"=Hughski_Devices,NTx86,NTamd64 "Image Engineering"=ImageEngineering_Devices,NTx86,NTamd64 +"LG"=LG_Devices,NTx86,NTamd64 ; ==== Devices ==== @@ -149,6 +150,8 @@ [ImageEngineering_Devices] "EX1 (Argyll)" = LIBUSB0_DEV, USB\VID_2457&PID_4000 +[LG_Devices] +"ACB8300 (Argyll)" = LIBUSB0_DEV, USB\VID_043E&PID_9AF0 [HCFR_Devices.NTx86] "Colorimtre HCFR V3.1 (Argyll)" = LIBUSB0_DEV, USB\VID_04DB&PID_005B @@ -188,6 +191,9 @@ [ImageEngineering_Devices.NTx86] "EX1 (Argyll)" = LIBUSB0_DEV, USB\VID_2457&PID_4000 +[LG_Devices.NTx86] +"ACB8300 (Argyll)" = LIBUSB0_DEV, USB\VID_043E&PID_9AF0 + [HCFR_Devices.NTamd64] "Colorimtre HCFR V3.1 (Argyll)" = LIBUSB0_DEV, USB\VID_04DB&PID_005B @@ -227,3 +233,5 @@ [ImageEngineering_Devices.NTamd64] "EX1 (Argyll)" = LIBUSB0_DEV, USB\VID_2457&PID_4000 +[LG_Devices.NTamd64] +"ACB8300 (Argyll)" = LIBUSB0_DEV, USB\VID_043E&PID_9AF0 diff -r 978487ba3d58 -r 04fccc0fc66b usb/ArgyllCMS.inf.d --- a/usb/ArgyllCMS.inf.d Mon Jan 04 23:30:41 2016 +0100 +++ b/usb/ArgyllCMS.inf.d Mon Jan 25 22:14:44 2016 +0100 @@ -37,3 +37,5 @@ [ImageEngineering_Devices#PLAT#] "EX1 (Argyll)" = LIBUSB0_DEV, USB\VID_2457&PID_4000 +[LG_Devices#PLAT#] +"ACB8300 (Argyll)" = LIBUSB0_DEV, USB\VID_043E&PID_9AF0