summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2017-07-24 15:13:45 +0100
committerRichard Hughes <richard@hughsie.com>2017-07-24 16:13:43 +0100
commitdd22416e8119db4fe91c7d4c01a505d96c6c2e2c (patch)
tree23920e5e0ea6c7a5e5aaa291c67b2dd4cd6a3b9f /src
parent8fdf55d17437133bf6fbd23897236121f48c5e8e (diff)
downloadcolord-dd22416e8119db4fe91c7d4c01a505d96c6c2e2c.tar.gz
trivial: Build the private sensor code into the .so itself
Diffstat (limited to 'src')
-rw-r--r--src/sensors/Makefile.am7
-rw-r--r--src/sensors/cd-parse-beagle.c5
-rw-r--r--src/sensors/dtp94/Makefile.am7
-rw-r--r--src/sensors/dtp94/cd-sensor-dtp94.c3
-rw-r--r--src/sensors/dtp94/dtp94-device.c357
-rw-r--r--src/sensors/dtp94/dtp94-device.h67
-rw-r--r--src/sensors/dtp94/dtp94-enum.c122
-rw-r--r--src/sensors/dtp94/dtp94-enum.h66
-rw-r--r--src/sensors/huey/Makefile.am9
-rw-r--r--src/sensors/huey/cd-sensor-huey.c5
-rw-r--r--src/sensors/huey/huey-ctx.c555
-rw-r--r--src/sensors/huey/huey-ctx.h88
-rw-r--r--src/sensors/huey/huey-device.c516
-rw-r--r--src/sensors/huey/huey-device.h100
-rw-r--r--src/sensors/huey/huey-enum.c108
-rw-r--r--src/sensors/huey/huey-enum.h321
-rw-r--r--src/sensors/munki/Makefile.am5
-rw-r--r--src/sensors/munki/cd-sensor-munki.c2
-rw-r--r--src/sensors/munki/munki-enum.c96
-rw-r--r--src/sensors/munki/munki-enum.h125
-rw-r--r--src/sensors/spark/Makefile.am7
-rw-r--r--src/sensors/spark/cd-sensor-spark.c4
-rw-r--r--src/sensors/spark/osp-device.c864
-rw-r--r--src/sensors/spark/osp-device.h76
-rw-r--r--src/sensors/spark/osp-enum.c201
-rw-r--r--src/sensors/spark/osp-enum.h163
-rw-r--r--src/sensors/spark/osp-self-test.c190
27 files changed, 4054 insertions, 15 deletions
diff --git a/src/sensors/Makefile.am b/src/sensors/Makefile.am
index d369259..58a49ff 100644
--- a/src/sensors/Makefile.am
+++ b/src/sensors/Makefile.am
@@ -11,6 +11,7 @@ AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
$(GUSB_CFLAGS) \
$(GUDEV_CFLAGS) \
+ $(POLKIT_CFLAGS) \
-I$(top_srcdir)/src \
-I$(top_srcdir)/lib \
-I$(top_builddir)/lib \
@@ -33,13 +34,13 @@ noinst_PROGRAMS = \
cd-parse-beagle
cd_parse_beagle_SOURCES = \
+ huey/huey-enum.c \
+ munki/munki-enum.c \
cd-parse-beagle.c
cd_parse_beagle_LDADD = \
$(COLORD_PRIVATE_LIBS) \
- $(GLIB_LIBS) \
- $(top_builddir)/lib/huey/libhuey-private.la \
- $(top_builddir)/lib/munki/libmunki-private.la
+ $(GLIB_LIBS)
cd_parse_beagle_CFLAGS = \
$(WARN_CFLAGS)
diff --git a/src/sensors/cd-parse-beagle.c b/src/sensors/cd-parse-beagle.c
index 31bda23..36a1974 100644
--- a/src/sensors/cd-parse-beagle.c
+++ b/src/sensors/cd-parse-beagle.c
@@ -25,8 +25,9 @@
#include <sys/types.h>
#include <stdlib.h>
#include <glib.h>
-#include <huey/huey.h>
-#include <munki/munki.h>
+
+#include "huey/huey-enum.h"
+#include "munki/munki-enum.h"
#include "cd-sensor.h"
diff --git a/src/sensors/dtp94/Makefile.am b/src/sensors/dtp94/Makefile.am
index 6df053b..0b688ab 100644
--- a/src/sensors/dtp94/Makefile.am
+++ b/src/sensors/dtp94/Makefile.am
@@ -18,11 +18,14 @@ plugin_LTLIBRARIES = \
libcolord_sensor_dtp94.la
libcolord_sensor_dtp94_la_SOURCES = \
+ dtp94-device.c \
+ dtp94-device.h \
+ dtp94-enum.c \
+ dtp94-enum.h \
cd-sensor-dtp94.c
libcolord_sensor_dtp94_la_LIBADD = \
$(GLIB_LIBS) \
- $(GUSB_LIBS) \
- $(top_builddir)/lib/dtp94/libdtp94-private.la
+ $(GUSB_LIBS)
libcolord_sensor_dtp94_la_LDFLAGS = -module -avoid-version
libcolord_sensor_dtp94_la_CFLAGS = $(WARN_CFLAGS)
endif
diff --git a/src/sensors/dtp94/cd-sensor-dtp94.c b/src/sensors/dtp94/cd-sensor-dtp94.c
index cbd744b..82be057 100644
--- a/src/sensors/dtp94/cd-sensor-dtp94.c
+++ b/src/sensors/dtp94/cd-sensor-dtp94.c
@@ -28,7 +28,8 @@
#include "../src/cd-sensor.h"
-#include <dtp94/dtp94.h>
+#include "dtp94-enum.h"
+#include "dtp94-device.h"
typedef struct
{
diff --git a/src/sensors/dtp94/dtp94-device.c b/src/sensors/dtp94/dtp94-device.c
new file mode 100644
index 0000000..65ee254
--- /dev/null
+++ b/src/sensors/dtp94/dtp94-device.c
@@ -0,0 +1,357 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * SECTION:dtp94-enum
+ * @short_description: Types used by dtp94 and libdtp94
+ *
+ * These helper functions provide a way to marshal enumerated values to
+ * text and back again.
+ *
+ * See also: #CdClient, #CdDevice
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <string.h>
+#include <colord-private.h>
+
+#include "dtp94-device.h"
+#include "dtp94-enum.h"
+
+#define DTP94_MAX_READ_RETRIES 5
+#define DTP94_CONTROL_MESSAGE_TIMEOUT 50000 /* ms */
+
+/**
+ * dtp94_device_error_quark:
+ **/
+GQuark
+dtp94_device_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("Dtp94DeviceError");
+ return quark;
+}
+
+/**
+ * dtp94_device_send_data:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+dtp94_device_send_data (GUsbDevice *device,
+ const guint8 *request,
+ gsize request_len,
+ guint8 *reply,
+ gsize reply_len,
+ gsize *reply_read,
+ GError **error)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (request != NULL, FALSE);
+ g_return_val_if_fail (request_len != 0, FALSE);
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (reply_len != 0, FALSE);
+ g_return_val_if_fail (reply_read != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* request data from device */
+ cd_buffer_debug (CD_BUFFER_KIND_REQUEST,
+ request, request_len);
+ ret = g_usb_device_interrupt_transfer (device,
+ 0x2,
+ (guint8 *) request,
+ request_len,
+ NULL,
+ DTP94_CONTROL_MESSAGE_TIMEOUT,
+ NULL,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* get sync response */
+ ret = g_usb_device_interrupt_transfer (device,
+ 0x81,
+ (guint8 *) reply,
+ reply_len,
+ reply_read,
+ DTP94_CONTROL_MESSAGE_TIMEOUT,
+ NULL,
+ error);
+ if (!ret)
+ return FALSE;
+ if (reply_read == 0) {
+ g_set_error_literal (error,
+ DTP94_DEVICE_ERROR,
+ DTP94_DEVICE_ERROR_INTERNAL,
+ "failed to get data from device");
+ return FALSE;
+ }
+ cd_buffer_debug (CD_BUFFER_KIND_RESPONSE,
+ reply, *reply_read);
+ return TRUE;
+}
+
+static gboolean
+dtp94_device_send_cmd_issue (GUsbDevice *device,
+ const gchar *command,
+ GError **error)
+{
+ gboolean ret;
+ gsize reply_read;
+ guint8 buffer[128];
+ guint8 rc;
+ guint command_len;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* sent command raw */
+ command_len = strlen (command);
+ ret = dtp94_device_send_data (device,
+ (const guint8 *) command,
+ command_len,
+ buffer,
+ sizeof (buffer),
+ &reply_read,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* device busy */
+ rc = dtp94_rc_parse (buffer, reply_read);
+ if (rc == DTP94_RC_BAD_COMMAND) {
+ g_set_error_literal (error,
+ DTP94_DEVICE_ERROR,
+ DTP94_DEVICE_ERROR_NO_DATA,
+ "device busy");
+ return FALSE;
+ }
+
+ /* no success */
+ if (rc != DTP94_RC_OK) {
+ buffer[reply_read] = '\0';
+ g_set_error (error,
+ DTP94_DEVICE_ERROR,
+ DTP94_DEVICE_ERROR_INTERNAL,
+ "unexpected response from device: %s [%s]",
+ (const gchar *) buffer,
+ dtp94_rc_to_string (rc));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * dtp94_device_send_cmd:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+dtp94_device_send_cmd (GUsbDevice *device,
+ const gchar *command,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GError *error_local = NULL;
+ guint error_cnt = 0;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (command != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* repeat until the device is ready */
+ for (error_cnt = 0; ret != TRUE; error_cnt++) {
+ ret = dtp94_device_send_cmd_issue (device, command, &error_local);
+ if (!ret) {
+ if (error_cnt < DTP94_MAX_READ_RETRIES &&
+ g_error_matches (error_local,
+ DTP94_DEVICE_ERROR,
+ DTP94_DEVICE_ERROR_NO_DATA)) {
+ g_debug ("ignoring %s", error_local->message);
+ g_clear_error (&error_local);
+ continue;
+ }
+ g_propagate_error (error, error_local);
+ break;
+ }
+ };
+ return ret;
+}
+
+/**
+ * dtp94_device_setup:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+dtp94_device_setup (GUsbDevice *device, GError **error)
+{
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* reset device */
+ if (!dtp94_device_send_cmd (device, "0PR\r", error))
+ return FALSE;
+
+ /* reset device again */
+ if (!dtp94_device_send_cmd (device, "0PR\r", error))
+ return FALSE;
+
+ /* set color data separator to '\t' */
+ if (!dtp94_device_send_cmd (device, "0207CF\r", error))
+ return FALSE;
+
+ /* set delimeter to CR */
+ if (!dtp94_device_send_cmd (device, "0008CF\r", error))
+ return FALSE;
+
+ /* set extra digit resolution */
+ if (!dtp94_device_send_cmd (device, "010ACF\r", error))
+ return FALSE;
+
+ /* no black point subtraction */
+ if (!dtp94_device_send_cmd (device, "0019CF\r", error))
+ return FALSE;
+
+ /* set to factory calibration */
+ if (!dtp94_device_send_cmd (device, "EFC\r", error))
+ return FALSE;
+
+ /* compensate for offset drift */
+ if (!dtp94_device_send_cmd (device, "0117CF\r", error))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * dtp94_device_take_sample:
+ *
+ * Since: 0.1.29
+ **/
+CdColorXYZ *
+dtp94_device_take_sample (GUsbDevice *device, CdSensorCap cap, GError **error)
+{
+ CdColorXYZ *result = NULL;
+ gboolean ret = FALSE;
+ gchar *tmp;
+ gsize reply_read;
+ guint8 buffer[128];
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* set hardware support */
+ switch (cap) {
+ case CD_SENSOR_CAP_CRT:
+ case CD_SENSOR_CAP_PLASMA:
+ /* CRT = 01 */
+ ret = dtp94_device_send_cmd (device, "0116CF\r", error);
+ break;
+ case CD_SENSOR_CAP_LCD:
+ /* LCD = 02 */
+ ret = dtp94_device_send_cmd (device, "0216CF\r", error);
+ break;
+ default:
+ g_set_error (error,
+ DTP94_DEVICE_ERROR,
+ DTP94_DEVICE_ERROR_NO_SUPPORT,
+ "DTP94 cannot measure in %s mode",
+ cd_sensor_cap_to_string (cap));
+ break;
+ }
+ if (!ret)
+ return NULL;
+
+ /* get sample */
+ ret = dtp94_device_send_data (device,
+ (const guint8 *) "RM\r", 3,
+ buffer, sizeof (buffer),
+ &reply_read,
+ error);
+ if (!ret)
+ return NULL;
+ tmp = g_strstr_len ((const gchar *) buffer, reply_read, "\r");
+ if (tmp == NULL || memcmp (tmp + 1, "<00>", 4) != 0) {
+ buffer[reply_read] = '\0';
+ g_set_error (error,
+ DTP94_DEVICE_ERROR,
+ DTP94_DEVICE_ERROR_INTERNAL,
+ "unexpected response from device: %s",
+ (const gchar *) buffer);
+ return NULL;
+ }
+
+ /* format is raw ASCII with fixed formatting:
+ * 'X 10.29 Y 10.33 Z 4.65\u000d<00>' */
+ tmp = (gchar *) buffer;
+ g_strdelimit (tmp, "\t\r", '\0');
+
+ /* success */
+ result = cd_color_xyz_new ();
+ cd_color_xyz_set (result,
+ g_ascii_strtod (tmp + 1, NULL),
+ g_ascii_strtod (tmp + 13, NULL),
+ g_ascii_strtod (tmp + 25, NULL));
+ return result;
+}
+
+/**
+ * dtp94_device_get_serial:
+ *
+ * Since: 0.1.29
+ **/
+gchar *
+dtp94_device_get_serial (GUsbDevice *device, GError **error)
+{
+ gboolean ret;
+ gchar *tmp;
+ gsize reply_read;
+ guint8 buffer[128];
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ ret = dtp94_device_send_data (device,
+ (const guint8 *) "SV\r", 3,
+ buffer, sizeof (buffer),
+ &reply_read,
+ error);
+ if (!ret)
+ return NULL;
+ tmp = g_strstr_len ((const gchar *) buffer, reply_read, "\r");
+ if (tmp == NULL || memcmp (tmp + 1, "<00>", 4) != 0) {
+ buffer[reply_read] = '\0';
+ g_set_error (error,
+ DTP94_DEVICE_ERROR,
+ DTP94_DEVICE_ERROR_INTERNAL,
+ "unexpected response from device: %s",
+ (const gchar *) buffer);
+ return NULL;
+ }
+ tmp[0] = '\0';
+ return g_strdup (tmp);
+}
diff --git a/src/sensors/dtp94/dtp94-device.h b/src/sensors/dtp94/dtp94-device.h
new file mode 100644
index 0000000..2b29b6b
--- /dev/null
+++ b/src/sensors/dtp94/dtp94-device.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __DTP94_DEVICE_H
+#define __DTP94_DEVICE_H
+
+#include <glib-object.h>
+#include <gusb.h>
+#include <colord-private.h>
+
+G_BEGIN_DECLS
+
+#define DTP94_DEVICE_ERROR dtp94_device_error_quark()
+
+typedef enum {
+ DTP94_DEVICE_ERROR_INTERNAL,
+ DTP94_DEVICE_ERROR_NO_DATA,
+ DTP94_DEVICE_ERROR_NO_SUPPORT,
+ DTP94_DEVICE_ERROR_LAST
+} Dtp94DeviceError;
+
+GQuark dtp94_device_error_quark (void);
+gboolean dtp94_device_send_data (GUsbDevice *device,
+ const guint8 *request,
+ gsize request_len,
+ guint8 *reply,
+ gsize reply_len,
+ gsize *reply_read,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean dtp94_device_send_cmd (GUsbDevice *device,
+ const gchar *command,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+CdColorXYZ *dtp94_device_take_sample (GUsbDevice *device,
+ CdSensorCap cap,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gchar *dtp94_device_get_serial (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean dtp94_device_setup (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+
+G_END_DECLS
+
+#endif /* __DTP94_DEVICE_H */
+
diff --git a/src/sensors/dtp94/dtp94-enum.c b/src/sensors/dtp94/dtp94-enum.c
new file mode 100644
index 0000000..4d2000e
--- /dev/null
+++ b/src/sensors/dtp94/dtp94-enum.c
@@ -0,0 +1,122 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * SECTION:dtp94-enum
+ * @short_description: Types used by dtp94 and libdtp94
+ *
+ * These helper functions provide a way to marshal enumerated values to
+ * text and back again.
+ *
+ * See also: #CdClient, #CdDevice
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "dtp94-enum.h"
+
+/**
+ * dtp94_rc_parse:
+ *
+ * Since: 0.1.29
+ **/
+guint8
+dtp94_rc_parse (const guint8 *data, gsize length)
+{
+ gchar *endptr = NULL;
+ guint64 tmp = DTP94_RC_UNKNOWN;
+
+ /* invalid data */
+ if (length < 4 ||
+ data[0] != '<' ||
+ data[1] == '\0' ||
+ data[2] == '\0' ||
+ data[3] != '>') {
+ return tmp;
+ }
+
+ /* parse number */
+ tmp = g_ascii_strtoull ((const gchar *) data + 1, &endptr, 16);
+ if (endptr == (const gchar *) data + 1)
+ return tmp; // valid?
+ return tmp;
+}
+
+/**
+ * dtp94_rc_to_string:
+ *
+ * Since: 0.1.29
+ **/
+const gchar *
+dtp94_rc_to_string (guint8 value)
+{
+ if (value == DTP94_RC_OK)
+ return "ok";
+ if (value == DTP94_RC_BAD_COMMAND)
+ return "bad-command";
+ if (value == DTP94_RC_PRM_RANGE)
+ return "prm-range";
+ if (value == DTP94_RC_MEMORY_OVERFLOW)
+ return "memory-overflow";
+ if (value == DTP94_RC_INVALID_BAUD_RATE)
+ return "invalid-baud-rate";
+ if (value == DTP94_RC_TIMEOUT)
+ return "timeout";
+ if (value == DTP94_RC_SYNTAX_ERROR)
+ return "syntax-error";
+ if (value == DTP94_RC_NO_DATA_AVAILABLE)
+ return "no-data-available";
+ if (value == DTP94_RC_MISSING_PARAMETER)
+ return "missing-parameter";
+ if (value == DTP94_RC_CALIBRATION_DENIED)
+ return "calibration-denied";
+ if (value == DTP94_RC_NEEDS_OFFSET_CAL)
+ return "needs-offset-cal";
+ if (value == DTP94_RC_NEEDS_RATIO_CAL)
+ return "needs-ratio-cal";
+ if (value == DTP94_RC_NEEDS_LUMINANCE_CAL)
+ return "needs-luminance-cal";
+ if (value == DTP94_RC_NEEDS_WHITE_POINT_CAL)
+ return "needs-white-point-cal";
+ if (value == DTP94_RC_NEEDS_BLACK_POINT_CAL)
+ return "needs-black-point-cal";
+ if (value == DTP94_RC_INVALID_READING)
+ return "invalid-reading";
+ if (value == DTP94_RC_BAD_COMP_TABLE)
+ return "bad-comp-table";
+ if (value == DTP94_RC_TOO_MUCH_LIGHT)
+ return "too-much-light";
+ if (value == DTP94_RC_NOT_ENOUGH_LIGHT)
+ return "not-enough-light";
+ if (value == DTP94_RC_BAD_SERIAL_NUMBER)
+ return "bad-serial-number";
+ if (value == DTP94_RC_NO_MODULATION)
+ return "no-modulation";
+ if (value == DTP94_RC_EEPROM_FAILURE)
+ return "eeprom-failure";
+ if (value == DTP94_RC_FLASH_WRITE_FAILURE)
+ return "flash-write-failure";
+ if (value == DTP94_RC_INST_INTERNAL_ERROR)
+ return "inst-internal-error";
+ return NULL;
+}
diff --git a/src/sensors/dtp94/dtp94-enum.h b/src/sensors/dtp94/dtp94-enum.h
new file mode 100644
index 0000000..9c5f949
--- /dev/null
+++ b/src/sensors/dtp94/dtp94-enum.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __DTP94_ENUM_H
+#define __DTP94_ENUM_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define DTP94_VENDOR_ID 0x0765
+#define DTP94_PRODUCT_ID 0xd094
+
+/* return values from the commands */
+#define DTP94_RC_OK 0x00
+#define DTP94_RC_BAD_COMMAND 0x01
+#define DTP94_RC_PRM_RANGE 0x02
+#define DTP94_RC_MEMORY_OVERFLOW 0x04
+#define DTP94_RC_INVALID_BAUD_RATE 0x05
+#define DTP94_RC_TIMEOUT 0x07
+#define DTP94_RC_SYNTAX_ERROR 0x08
+#define DTP94_RC_NO_DATA_AVAILABLE 0x0b
+#define DTP94_RC_MISSING_PARAMETER 0x0c
+#define DTP94_RC_CALIBRATION_DENIED 0x0d
+#define DTP94_RC_NEEDS_OFFSET_CAL 0x16
+#define DTP94_RC_NEEDS_RATIO_CAL 0x17
+#define DTP94_RC_NEEDS_LUMINANCE_CAL 0x18
+#define DTP94_RC_NEEDS_WHITE_POINT_CAL 0x19
+#define DTP94_RC_NEEDS_BLACK_POINT_CAL 0x2a
+#define DTP94_RC_INVALID_READING 0x20
+#define DTP94_RC_BAD_COMP_TABLE 0x25
+#define DTP94_RC_TOO_MUCH_LIGHT 0x28
+#define DTP94_RC_NOT_ENOUGH_LIGHT 0x29
+#define DTP94_RC_BAD_SERIAL_NUMBER 0x40
+#define DTP94_RC_NO_MODULATION 0x50
+#define DTP94_RC_EEPROM_FAILURE 0x70
+#define DTP94_RC_FLASH_WRITE_FAILURE 0x71
+#define DTP94_RC_INST_INTERNAL_ERROR 0x7f
+#define DTP94_RC_UNKNOWN 0xff
+
+guint8 dtp94_rc_parse (const guint8 *data,
+ gsize length);
+const gchar *dtp94_rc_to_string (guint8 value);
+
+G_END_DECLS
+
+#endif /* __DTP94_ENUM_H */
+
diff --git a/src/sensors/huey/Makefile.am b/src/sensors/huey/Makefile.am
index a927f53..1d1cb9e 100644
--- a/src/sensors/huey/Makefile.am
+++ b/src/sensors/huey/Makefile.am
@@ -18,11 +18,16 @@ plugin_LTLIBRARIES = \
libcolord_sensor_huey.la
libcolord_sensor_huey_la_SOURCES = \
+ huey-ctx.c \
+ huey-ctx.h \
+ huey-device.c \
+ huey-device.h \
+ huey-enum.c \
+ huey-enum.h \
cd-sensor-huey.c
libcolord_sensor_huey_la_LIBADD = \
$(GLIB_LIBS) \
- $(GUSB_LIBS) \
- $(top_builddir)/lib/huey/libhuey-private.la
+ $(GUSB_LIBS)
libcolord_sensor_huey_la_LDFLAGS = -module -avoid-version
libcolord_sensor_huey_la_CFLAGS = $(WARN_CFLAGS)
endif
diff --git a/src/sensors/huey/cd-sensor-huey.c b/src/sensors/huey/cd-sensor-huey.c
index 967c17e..9a8ff87 100644
--- a/src/sensors/huey/cd-sensor-huey.c
+++ b/src/sensors/huey/cd-sensor-huey.c
@@ -25,10 +25,13 @@
#include <glib-object.h>
#include <gusb.h>
#include <colord-private.h>
-#include <huey/huey.h>
#include "../src/cd-sensor.h"
+#include "huey-ctx.h"
+#include "huey-device.h"
+#include "huey-enum.h"
+
typedef struct
{
GUsbDevice *device;
diff --git a/src/sensors/huey/huey-ctx.c b/src/sensors/huey/huey-ctx.c
new file mode 100644
index 0000000..42feb41
--- /dev/null
+++ b/src/sensors/huey/huey-ctx.c
@@ -0,0 +1,555 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <lcms2.h>
+#include <stdlib.h>
+
+#include "huey-ctx.h"
+#include "huey-device.h"
+#include "huey-enum.h"
+
+static void huey_ctx_class_init (HueyCtxClass *klass);
+static void huey_ctx_init (HueyCtx *ctx);
+static void huey_ctx_finalize (GObject *object);
+
+#define GET_PRIVATE(o) (huey_ctx_get_instance_private (o))
+
+#define HUEY_CONTROL_MESSAGE_TIMEOUT 50000 /* ms */
+#define HUEY_MAX_READ_RETRIES 5
+
+/* The CY7C63001 is paired with a 6.00Mhz crystal */
+#define HUEY_CLOCK_FREQUENCY 6e6
+
+/* It takes 6 clock pulses to process a single 16bit increment (INC)
+ * instruction and check for the carry so this is the fastest a loop
+ * can be processed. */
+#define HUEY_POLL_FREQUENCY 1e6
+
+/* Picked out of thin air, just to try to match reality...
+ * I have no idea why we need to do this, although it probably
+ * indicates we doing something wrong. */
+#define HUEY_XYZ_POST_MULTIPLY_FACTOR 3.428
+
+/**
+ * HueyCtxPrivate:
+ *
+ * Private #HueyCtx data
+ **/
+typedef struct
+{
+ CdMat3x3 calibration_crt;
+ CdMat3x3 calibration_lcd;
+ CdVec3 dark_offset;
+ gchar *unlock_string;
+ gfloat calibration_value;
+ GUsbDevice *device;
+} HueyCtxPrivate;
+
+enum {
+ PROP_0,
+ PROP_DEVICE,
+ PROP_LAST
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (HueyCtx, huey_ctx, G_TYPE_OBJECT)
+
+/**
+ * huey_ctx_error_quark:
+ *
+ * Return value: An error quark.
+ *
+ * Since: 0.1.0
+ **/
+GQuark
+huey_ctx_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark) {
+ quark = g_quark_from_static_string ("huey_ctx_error");
+ }
+ return quark;
+}
+
+
+/**
+ * huey_ctx_get_device:
+ *
+ * Since: 0.1.29
+ **/
+GUsbDevice *
+huey_ctx_get_device (HueyCtx *ctx)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+ g_return_val_if_fail (HUEY_IS_CTX (ctx), NULL);
+ return priv->device;
+}
+
+/**
+ * huey_ctx_set_device:
+ *
+ * Since: 0.1.29
+ **/
+void
+huey_ctx_set_device (HueyCtx *ctx, GUsbDevice *device)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+ g_return_if_fail (HUEY_IS_CTX (ctx));
+ priv->device = g_object_ref (device);
+}
+
+/**
+ * huey_ctx_setup:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_ctx_setup (HueyCtx *ctx, GError **error)
+{
+ gboolean ret;
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+
+ g_return_val_if_fail (HUEY_IS_CTX (ctx), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* get matrix */
+ cd_mat33_clear (&priv->calibration_lcd);
+ ret = huey_device_read_register_matrix (priv->device,
+ HUEY_EEPROM_ADDR_CALIBRATION_DATA_LCD,
+ &priv->calibration_lcd,
+ error);
+ if (!ret)
+ return FALSE;
+ g_debug ("device calibration LCD: %s",
+ cd_mat33_to_string (&priv->calibration_lcd));
+
+ /* get another matrix, although this one is different... */
+ cd_mat33_clear (&priv->calibration_crt);
+ ret = huey_device_read_register_matrix (priv->device,
+ HUEY_EEPROM_ADDR_CALIBRATION_DATA_CRT,
+ &priv->calibration_crt,
+ error);
+ if (!ret)
+ return FALSE;
+ g_debug ("device calibration CRT: %s",
+ cd_mat33_to_string (&priv->calibration_crt));
+
+ /* this number is different on all three hueys */
+ ret = huey_device_read_register_float (priv->device,
+ HUEY_EEPROM_ADDR_AMBIENT_CALIB_VALUE,
+ &priv->calibration_value,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* this vector changes between sensor 1 and 3 */
+ ret = huey_device_read_register_vector (priv->device,
+ HUEY_EEPROM_ADDR_DARK_OFFSET,
+ &priv->dark_offset,
+ error);
+ if (!ret)
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * huey_ctx_get_calibration_lcd:
+ *
+ * Since: 0.1.29
+ **/
+const CdMat3x3 *
+huey_ctx_get_calibration_lcd (HueyCtx *ctx)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+ g_return_val_if_fail (HUEY_IS_CTX (ctx), NULL);
+ return &priv->calibration_lcd;
+}
+
+/**
+ * huey_ctx_get_calibration_crt:
+ *
+ * Since: 0.1.29
+ **/
+const CdMat3x3 *
+huey_ctx_get_calibration_crt (HueyCtx *ctx)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+ g_return_val_if_fail (HUEY_IS_CTX (ctx), NULL);
+ return &priv->calibration_crt;
+}
+
+/**
+ * huey_ctx_get_calibration_value:
+ *
+ * Since: 0.1.29
+ **/
+gfloat
+huey_ctx_get_calibration_value (HueyCtx *ctx)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+ g_return_val_if_fail (HUEY_IS_CTX (ctx), -1);
+ return priv->calibration_value;
+}
+
+/**
+ * huey_ctx_get_dark_offset:
+ *
+ * Since: 0.1.29
+ **/
+const CdVec3 *
+huey_ctx_get_dark_offset (HueyCtx *ctx)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+ g_return_val_if_fail (HUEY_IS_CTX (ctx), NULL);
+ return &priv->dark_offset;
+}
+
+/**
+ * huey_ctx_get_unlock_string:
+ *
+ * Since: 0.1.29
+ **/
+const gchar *
+huey_ctx_get_unlock_string (HueyCtx *ctx)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+ g_return_val_if_fail (HUEY_IS_CTX (ctx), NULL);
+ return priv->unlock_string;
+}
+
+typedef struct {
+ guint16 R;
+ guint16 G;
+ guint16 B;
+} HueyCtxMultiplier;
+
+typedef struct {
+ guint32 R;
+ guint32 G;
+ guint32 B;
+} HueyCtxDeviceRaw;
+
+static gboolean
+huey_ctx_sample_for_threshold (HueyCtx *ctx,
+ HueyCtxMultiplier *threshold,
+ HueyCtxDeviceRaw *raw,
+ GError **error)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+ guint8 request[] = { HUEY_CMD_SENSOR_MEASURE_RGB,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ guint8 reply[8];
+ gboolean ret;
+ gsize reply_read;
+
+ /* these are 16 bit gain values */
+ cd_buffer_write_uint16_be (request + 1, threshold->R);
+ cd_buffer_write_uint16_be (request + 3, threshold->G);
+ cd_buffer_write_uint16_be (request + 5, threshold->B);
+
+ /* measure, and get red */
+ ret = huey_device_send_data (priv->device,
+ request, 8,
+ reply, 8,
+ &reply_read,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* get value */
+ raw->R = cd_buffer_read_uint32_be (reply+2);
+
+ /* get green */
+ request[0] = HUEY_CMD_READ_GREEN;
+ ret = huey_device_send_data (priv->device,
+ request, 8,
+ reply, 8,
+ &reply_read,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* get value */
+ raw->G = cd_buffer_read_uint32_be (reply+2);
+
+ /* get blue */
+ request[0] = HUEY_CMD_READ_BLUE;
+ ret = huey_device_send_data (priv->device,
+ request, 8,
+ reply, 8,
+ &reply_read,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* get value */
+ raw->B = cd_buffer_read_uint32_be (reply+2);
+ return TRUE;
+}
+
+/**
+ * huey_ctx_convert_device_RGB_to_XYZ:
+ *
+ * / X \ ( / R \ / c a l \ )
+ * | Y | = ( | G | * | m a t | ) x post_scale
+ * \ Z / ( \ B / \ l c d / )
+ *
+ **/
+static void
+huey_ctx_convert_device_RGB_to_XYZ (CdColorRGB *src,
+ CdColorXYZ *dest,
+ CdMat3x3 *calibration,
+ gdouble post_scale)
+{
+ CdVec3 *result;
+
+ /* convolve */
+ result = (CdVec3 *) dest;
+ cd_mat33_vector_multiply (calibration, (CdVec3 *) src, result);
+
+ /* post-multiply */
+ cd_vec3_scalar_multiply (result,
+ post_scale,
+ result);
+}
+
+
+/**
+ * huey_ctx_take_sample:
+ *
+ * Since: 0.1.29
+ **/
+CdColorXYZ *
+huey_ctx_take_sample (HueyCtx *ctx, CdSensorCap cap, GError **error)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+ CdColorRGB values;
+ CdColorXYZ color_result;
+ CdMat3x3 *device_calibration;
+ CdVec3 *temp;
+ gboolean ret;
+ HueyCtxDeviceRaw color_native;
+ HueyCtxMultiplier multiplier;
+
+ g_return_val_if_fail (HUEY_IS_CTX (ctx), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* no hardware support */
+ if (cap == CD_SENSOR_CAP_PROJECTOR) {
+ g_set_error_literal (error,
+ HUEY_CTX_ERROR,
+ HUEY_CTX_ERROR_NO_SUPPORT,
+ "Huey cannot measure in projector mode");
+ return NULL;
+ }
+
+ /* set this to one value for a quick approximate value */
+ multiplier.R = 1;
+ multiplier.G = 1;
+ multiplier.B = 1;
+ ret = huey_ctx_sample_for_threshold (ctx,
+ &multiplier,
+ &color_native,
+ error);
+ if (!ret)
+ return NULL;
+ g_debug ("initial values: red=%u, green=%u, blue=%u",
+ color_native.R, color_native.G, color_native.B);
+
+ /* try to fill the 16 bit register for accuracy */
+ multiplier.R = HUEY_POLL_FREQUENCY / color_native.R;
+ multiplier.G = HUEY_POLL_FREQUENCY / color_native.G;
+ multiplier.B = HUEY_POLL_FREQUENCY / color_native.B;
+
+ /* don't allow a value of zero */
+ if (multiplier.R == 0)
+ multiplier.R = 1;
+ if (multiplier.G == 0)
+ multiplier.G = 1;
+ if (multiplier.B == 0)
+ multiplier.B = 1;
+ g_debug ("using multiplier factor: red=%i, green=%i, blue=%i",
+ multiplier.R, multiplier.G, multiplier.B);
+ ret = huey_ctx_sample_for_threshold (ctx,
+ &multiplier,
+ &color_native,
+ error);
+ if (!ret)
+ return NULL;
+ g_debug ("raw values: red=%u, green=%u, blue=%u",
+ color_native.R, color_native.G, color_native.B);
+
+ /* get DeviceRGB values */
+ values.R = (gdouble) multiplier.R * 0.5f * HUEY_POLL_FREQUENCY / ((gdouble) color_native.R);
+ values.G = (gdouble) multiplier.G * 0.5f * HUEY_POLL_FREQUENCY / ((gdouble) color_native.G);
+ values.B = (gdouble) multiplier.B * 0.5f * HUEY_POLL_FREQUENCY / ((gdouble) color_native.B);
+ g_debug ("scaled values: red=%0.6lf, green=%0.6lf, blue=%0.6lf",
+ values.R, values.G, values.B);
+
+ /* remove dark offset */
+ temp = (CdVec3*) &values;
+ cd_vec3_subtract (temp,
+ &priv->dark_offset,
+ temp);
+
+ g_debug ("dark offset values: red=%0.6lf, green=%0.6lf, blue=%0.6lf",
+ values.R, values.G, values.B);
+
+ /* negative values don't make sense (device needs recalibration) */
+ if (values.R < 0.0f)
+ values.R = 0.0f;
+ if (values.G < 0.0f)
+ values.G = 0.0f;
+ if (values.B < 0.0f)
+ values.B = 0.0f;
+
+ /* we use different calibration matrices for each output type */
+ switch (cap) {
+ case CD_SENSOR_CAP_CRT:
+ case CD_SENSOR_CAP_PLASMA:
+ g_debug ("using CRT calibration matrix");
+ device_calibration = &priv->calibration_crt;
+ break;
+ default:
+ g_debug ("using LCD calibration matrix");
+ device_calibration = &priv->calibration_lcd;
+ break;
+ }
+
+ /* convert from device RGB to XYZ */
+ huey_ctx_convert_device_RGB_to_XYZ (&values,
+ &color_result,
+ device_calibration,
+ HUEY_XYZ_POST_MULTIPLY_FACTOR);
+ g_debug ("finished values: red=%0.6lf, green=%0.6lf, blue=%0.6lf",
+ color_result.X, color_result.Y, color_result.Z);
+
+ /* save result */
+ return cd_color_xyz_dup (&color_result);
+}
+
+static void
+huey_ctx_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ HueyCtx *ctx = HUEY_CTX (object);
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ g_value_set_object (value, priv->device);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+huey_ctx_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ HueyCtx *ctx = HUEY_CTX (object);
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ priv->device = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*
+ * huey_ctx_class_init:
+ */
+static void
+huey_ctx_class_init (HueyCtxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = huey_ctx_get_property;
+ object_class->set_property = huey_ctx_set_property;
+ object_class->finalize = huey_ctx_finalize;
+
+ /**
+ * HueyCtx:device:
+ *
+ * Since: 0.1.29
+ **/
+ g_object_class_install_property (object_class,
+ PROP_DEVICE,
+ g_param_spec_object ("device",
+ NULL, NULL,
+ G_USB_TYPE_DEVICE,
+ G_PARAM_READWRITE));
+}
+
+/*
+ * huey_ctx_init:
+ */
+static void
+huey_ctx_init (HueyCtx *ctx)
+{
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+
+ cd_mat33_clear (&priv->calibration_lcd);
+ cd_mat33_clear (&priv->calibration_crt);
+
+ /* ensure the remote errors are registered */
+ huey_ctx_error_quark ();
+}
+
+static void
+huey_ctx_finalize (GObject *object)
+{
+ HueyCtx *ctx = HUEY_CTX (object);
+ HueyCtxPrivate *priv = GET_PRIVATE (ctx);
+
+ g_return_if_fail (HUEY_IS_CTX (object));
+
+ g_free (priv->unlock_string);
+
+ G_OBJECT_CLASS (huey_ctx_parent_class)->finalize (object);
+}
+
+/**
+ * huey_ctx_new:
+ *
+ * Creates a new #HueyCtx object.
+ *
+ * Return value: a new HueyCtx object.
+ *
+ * Since: 0.1.29
+ **/
+HueyCtx *
+huey_ctx_new (void)
+{
+ HueyCtx *ctx;
+ ctx = g_object_new (HUEY_TYPE_CTX, NULL);
+ return HUEY_CTX (ctx);
+}
diff --git a/src/sensors/huey/huey-ctx.h b/src/sensors/huey/huey-ctx.h
new file mode 100644
index 0000000..cf32e4d
--- /dev/null
+++ b/src/sensors/huey/huey-ctx.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __HUEY_CTX_H
+#define __HUEY_CTX_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gusb.h>
+#include <colord-private.h>
+
+G_BEGIN_DECLS
+
+#define HUEY_CTX_ERROR (huey_ctx_error_quark ())
+#define HUEY_CTX_TYPE_ERROR (huey_ctx_error_get_type ())
+
+#define HUEY_TYPE_CTX (huey_ctx_get_type ())
+G_DECLARE_DERIVABLE_TYPE (HueyCtx, huey_ctx, HUEY, CTX, GObject)
+
+struct _HueyCtxClass
+{
+ GObjectClass parent_class;
+ /*< private >*/
+ /* Padding for future expansion */
+ void (*_huey_ctx_reserved1) (void);
+ void (*_huey_ctx_reserved2) (void);
+ void (*_huey_ctx_reserved3) (void);
+ void (*_huey_ctx_reserved4) (void);
+ void (*_huey_ctx_reserved5) (void);
+ void (*_huey_ctx_reserved6) (void);
+ void (*_huey_ctx_reserved7) (void);
+ void (*_huey_ctx_reserved8) (void);
+};
+
+/**
+ * HueyCtxError:
+ * @HUEY_CTX_ERROR_FAILED: the request failed for an unknown reason
+ *
+ * Errors that can be thrown
+ */
+typedef enum
+{
+ HUEY_CTX_ERROR_FAILED,
+ HUEY_CTX_ERROR_NO_SUPPORT,
+ HUEY_CTX_ERROR_LAST
+} HueyCtxError;
+
+GQuark huey_ctx_error_quark (void);
+HueyCtx *huey_ctx_new (void);
+
+CdColorXYZ *huey_ctx_take_sample (HueyCtx *ctx,
+ CdSensorCap cap,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+GUsbDevice *huey_ctx_get_device (HueyCtx *ctx);
+void huey_ctx_set_device (HueyCtx *ctx,
+ GUsbDevice *device);
+gboolean huey_ctx_setup (HueyCtx *ctx,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+const CdMat3x3 *huey_ctx_get_calibration_lcd (HueyCtx *ctx);
+const CdMat3x3 *huey_ctx_get_calibration_crt (HueyCtx *ctx);
+gfloat huey_ctx_get_calibration_value (HueyCtx *ctx);
+const CdVec3 *huey_ctx_get_dark_offset (HueyCtx *ctx);
+const gchar *huey_ctx_get_unlock_string (HueyCtx *ctx);
+
+G_END_DECLS
+
+#endif /* __HUEY_CTX_H */
+
diff --git a/src/sensors/huey/huey-device.c b/src/sensors/huey/huey-device.c
new file mode 100644
index 0000000..a070158
--- /dev/null
+++ b/src/sensors/huey/huey-device.c
@@ -0,0 +1,516 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <string.h>
+
+#include "huey-device.h"
+#include "huey-enum.h"
+
+#define HUEY_MAX_READ_RETRIES 5
+#define HUEY_CONTROL_MESSAGE_TIMEOUT 50000 /* ms */
+
+/* fudge factor to convert the value of HUEY_CMD_GET_AMBIENT to Lux */
+#define HUEY_AMBIENT_UNITS_TO_LUX 125.0f
+
+/**
+ * huey_device_error_quark:
+ **/
+GQuark
+huey_device_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("HueyError");
+ return quark;
+}
+
+/**
+ * huey_device_send_data:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_device_send_data (GUsbDevice *device,
+ const guint8 *request,
+ gsize request_len,
+ guint8 *reply,
+ gsize reply_len,
+ gsize *reply_read,
+ GError **error)
+{
+ gboolean ret;
+ guint i;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (request != NULL, FALSE);
+ g_return_val_if_fail (request_len != 0, FALSE);
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (reply_len != 0, FALSE);
+ g_return_val_if_fail (reply_read != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* control transfer */
+ cd_buffer_debug (CD_BUFFER_KIND_REQUEST,
+ request, request_len);
+ ret = g_usb_device_control_transfer (device,
+ G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
+ G_USB_DEVICE_REQUEST_TYPE_CLASS,
+ G_USB_DEVICE_RECIPIENT_INTERFACE,
+ 0x09,
+ 0x0200,
+ 0,
+ (guint8 *) request,
+ request_len,
+ NULL,
+ HUEY_CONTROL_MESSAGE_TIMEOUT,
+ NULL,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* some commands need to retry the read */
+ for (i = 0; i < HUEY_MAX_READ_RETRIES; i++) {
+
+ /* get sync response */
+ ret = g_usb_device_interrupt_transfer (device,
+ 0x81,
+ (guint8 *) reply,
+ reply_len,
+ reply_read,
+ HUEY_CONTROL_MESSAGE_TIMEOUT,
+ NULL,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* the second byte seems to be the command again */
+ cd_buffer_debug (CD_BUFFER_KIND_RESPONSE,
+ reply, *reply_read);
+ if (reply[1] != request[0]) {
+ g_set_error (error,
+ HUEY_DEVICE_ERROR,
+ HUEY_DEVICE_ERROR_INTERNAL,
+ "wrong command reply, got 0x%02x, "
+ "expected 0x%02x",
+ reply[1],
+ request[0]);
+ return FALSE;
+ }
+
+ /* the first byte is status */
+ if (reply[0] == HUEY_RC_SUCCESS)
+ return TRUE;
+
+ /* failure, the return buffer is set to "Locked" */
+ if (reply[0] == HUEY_RC_LOCKED) {
+ g_set_error_literal (error,
+ HUEY_DEVICE_ERROR,
+ HUEY_DEVICE_ERROR_INTERNAL,
+ "the device is locked");
+ return FALSE;
+ }
+
+ /* failure, the return buffer is set to "NoCmd" */
+ if (reply[0] == HUEY_RC_ERROR) {
+ g_set_error (error,
+ HUEY_DEVICE_ERROR,
+ HUEY_DEVICE_ERROR_INTERNAL,
+ "failed to issue command: %s", &reply[2]);
+ return FALSE;
+ }
+
+ /* we ignore retry */
+ if (reply[0] != HUEY_RC_RETRY) {
+ g_set_error (error,
+ HUEY_DEVICE_ERROR,
+ HUEY_DEVICE_ERROR_INTERNAL,
+ "return value unknown: 0x%02x", reply[0]);
+ return FALSE;
+ }
+ }
+
+ /* no success */
+ g_set_error (error,
+ HUEY_DEVICE_ERROR,
+ HUEY_DEVICE_ERROR_INTERNAL,
+ "gave up retrying after %i reads",
+ HUEY_MAX_READ_RETRIES);
+ return FALSE;
+}
+
+/**
+ * huey_device_unlock:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_device_unlock (GUsbDevice *device, GError **error)
+{
+ guint8 request[8];
+ guint8 reply[8];
+ gboolean ret;
+ gsize reply_read;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ request[0] = HUEY_CMD_UNLOCK;
+ request[1] = 'G';
+ request[2] = 'r';
+ request[3] = 'M';
+ request[4] = 'b';
+ request[5] = '\0';
+ request[6] = '\0';
+ request[7] = '\0';
+
+ /* no idea why the hardware gets 'locked' */
+ ret = huey_device_send_data (device,
+ request, 8,
+ reply, 8,
+ &reply_read,
+ error);
+ if (!ret)
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * huey_device_get_serial_number:
+ *
+ * Since: 0.1.29
+ **/
+gchar *
+huey_device_get_serial_number (GUsbDevice *device, GError **error)
+{
+ gboolean ret;
+ guint32 tmp;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ ret = huey_device_read_register_word (device,
+ HUEY_EEPROM_ADDR_SERIAL,
+ &tmp,
+ error);
+ if (!ret)
+ return NULL;
+ return g_strdup_printf ("%" G_GUINT32_FORMAT, tmp);
+}
+
+/**
+ * huey_device_get_unlock_string:
+ *
+ * Since: 0.1.29
+ **/
+gchar *
+huey_device_get_unlock_string (GUsbDevice *device, GError **error)
+{
+ gboolean ret;
+ gchar tmp[5];
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ ret = huey_device_read_register_string (device,
+ HUEY_EEPROM_ADDR_UNLOCK,
+ tmp,
+ sizeof (tmp),
+ error);
+ if (!ret)
+ return NULL;
+ return g_strndup (tmp, sizeof (tmp));
+}
+
+/**
+ * huey_device_set_leds:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_device_set_leds (GUsbDevice *device, guint8 value, GError **error)
+{
+ guint8 reply[8];
+ gsize reply_read;
+ guint8 payload[] = { HUEY_CMD_SET_LEDS,
+ 0x00,
+ ~value,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00 };
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return huey_device_send_data (device,
+ payload, 8, reply, 8,
+ &reply_read,
+ error);
+}
+
+/**
+ * huey_device_read_register_byte:
+ *
+ * Return value: -1 for error.
+ *
+ * Since: 0.1.29
+ **/
+gdouble
+huey_device_get_ambient (GUsbDevice *device, GError **error)
+{
+ gboolean ret;
+ gsize reply_read;
+ guint8 reply[8];
+ guint8 request[] = { HUEY_CMD_GET_AMBIENT,
+ 0x03,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00 };
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), -1);
+ g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
+ /* just use LCD mode */
+ request[2] = 0x00;
+ ret = huey_device_send_data (device,
+ request, 8,
+ reply, 8,
+ &reply_read,
+ error);
+ if (!ret)
+ return -1.f;
+
+ /* parse the value */
+ return (gdouble) cd_buffer_read_uint16_be (reply+5) / HUEY_AMBIENT_UNITS_TO_LUX;
+}
+
+/**
+ * huey_device_read_register_byte:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_device_read_register_byte (GUsbDevice *device,
+ guint8 addr,
+ guint8 *value,
+ GError **error)
+{
+ guint8 request[] = { HUEY_CMD_REGISTER_READ,
+ 0xff,
+ 0x00,
+ 0x10,
+ 0x3c,
+ 0x06,
+ 0x00,
+ 0x00 };
+ guint8 reply[8];
+ gboolean ret;
+ gsize reply_read;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* hit hardware */
+ request[1] = addr;
+ ret = huey_device_send_data (device,
+ request, 8,
+ reply, 8,
+ &reply_read,
+ error);
+ if (!ret)
+ return FALSE;
+ *value = reply[3];
+ return TRUE;
+}
+
+/**
+ * huey_device_read_register_string:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_device_read_register_string (GUsbDevice *device,
+ guint8 addr,
+ gchar *value,
+ gsize len,
+ GError **error)
+{
+ guint8 i;
+ gboolean ret;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* get each byte of the string */
+ for (i = 0; i < len; i++) {
+ ret = huey_device_read_register_byte (device,
+ addr+i,
+ (guint8*) &value[i],
+ error);
+ if (!ret)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * huey_device_read_register_word:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_device_read_register_word (GUsbDevice *device,
+ guint8 addr,
+ guint32 *value,
+ GError **error)
+{
+ guint8 i;
+ guint8 tmp[4];
+ gboolean ret;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* get each byte of the 32 bit number */
+ for (i = 0; i < 4; i++) {
+ ret = huey_device_read_register_byte (device,
+ addr+i,
+ tmp+i,
+ error);
+ if (!ret)
+ return FALSE;
+ }
+
+ /* convert to a 32 bit integer */
+ *value = cd_buffer_read_uint32_be (tmp);
+ return TRUE;
+}
+
+/**
+ * huey_device_read_register_float:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_device_read_register_float (GUsbDevice *device,
+ guint8 addr,
+ gfloat *value,
+ GError **error)
+{
+ gboolean ret;
+ guint32 tmp = 0;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* first read in 32 bit integer */
+ ret = huey_device_read_register_word (device,
+ addr,
+ &tmp,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* convert to float */
+ *((guint32 *)value) = tmp;
+ return TRUE;
+}
+
+/**
+ * huey_device_read_register_vector:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_device_read_register_vector (GUsbDevice *device,
+ guint8 addr,
+ CdVec3 *value,
+ GError **error)
+{
+ gboolean ret;
+ guint i;
+ gfloat tmp = 0.0f;
+ gdouble *vector_data;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* get this to avoid casting */
+ vector_data = cd_vec3_get_data (value);
+
+ /* read in vec3 */
+ for (i = 0; i < 3; i++) {
+ ret = huey_device_read_register_float (device,
+ addr + (i*4),
+ &tmp,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* save in matrix */
+ *(vector_data+i) = tmp;
+ }
+ return TRUE;
+}
+
+/**
+ * huey_device_read_register_matrix:
+ *
+ * Since: 0.1.29
+ **/
+gboolean
+huey_device_read_register_matrix (GUsbDevice *device,
+ guint8 addr,
+ CdMat3x3 *value,
+ GError **error)
+{
+ gboolean ret;
+ guint i;
+ gfloat tmp = 0.0f;
+ gdouble *matrix_data;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* get this to avoid casting */
+ matrix_data = cd_mat33_get_data (value);
+
+ /* read in 3d matrix */
+ for (i = 0; i < 9; i++) {
+ ret = huey_device_read_register_float (device,
+ addr + (i*4),
+ &tmp,
+ error);
+ if (!ret)
+ return FALSE;
+
+ /* save in matrix */
+ *(matrix_data+i) = tmp;
+ }
+ return TRUE;
+}
diff --git a/src/sensors/huey/huey-device.h b/src/sensors/huey/huey-device.h
new file mode 100644
index 0000000..c488132
--- /dev/null
+++ b/src/sensors/huey/huey-device.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __HUEY_DEVICE_H
+#define __HUEY_DEVICE_H
+
+#include <glib-object.h>
+#include <gusb.h>
+#include <colord-private.h>
+
+G_BEGIN_DECLS
+
+#define HUEY_DEVICE_ERROR huey_device_error_quark()
+
+typedef enum {
+ HUEY_DEVICE_ERROR_INTERNAL,
+ HUEY_DEVICE_ERROR_NO_DATA,
+ HUEY_DEVICE_ERROR_NO_SUPPORT,
+ HUEY_DEVICE_ERROR_LAST
+} HueyError;
+
+GQuark huey_device_error_quark (void);
+gboolean huey_device_send_data (GUsbDevice *device,
+ const guchar *request,
+ gsize request_len,
+ guchar *reply,
+ gsize reply_len,
+ gsize *reply_read,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean huey_device_set_leds (GUsbDevice *device,
+ guint8 value,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gchar *huey_device_get_serial_number (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gchar *huey_device_get_unlock_string (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean huey_device_unlock (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gdouble huey_device_get_ambient (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean huey_device_read_register_byte (GUsbDevice *device,
+ guint8 addr,
+ guint8 *value,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean huey_device_read_register_string (GUsbDevice *device,
+ guint8 addr,
+ gchar *value,
+ gsize len,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean huey_device_read_register_word (GUsbDevice *device,
+ guint8 addr,
+ guint32 *value,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean huey_device_read_register_float (GUsbDevice *device,
+ guint8 addr,
+ gfloat *value,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean huey_device_read_register_vector (GUsbDevice *device,
+ guint8 addr,
+ CdVec3 *value,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean huey_device_read_register_matrix (GUsbDevice *device,
+ guint8 addr,
+ CdMat3x3 *value,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+
+G_END_DECLS
+
+#endif /* __HUEY_DEVICE_H */
+
diff --git a/src/sensors/huey/huey-enum.c b/src/sensors/huey/huey-enum.c
new file mode 100644
index 0000000..385aaa9
--- /dev/null
+++ b/src/sensors/huey/huey-enum.c
@@ -0,0 +1,108 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * SECTION:huey-enum
+ * @short_description: Types used by huey and libhuey
+ *
+ * These helper functions provide a way to marshal enumerated values to
+ * text and back again.
+ *
+ * See also: #CdClient, #CdDevice
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "huey-enum.h"
+
+/**
+ * huey_rc_to_string:
+ *
+ * Since: 0.1.29
+ **/
+const gchar *
+huey_rc_to_string (guchar value)
+{
+ if (value == HUEY_RC_SUCCESS)
+ return "success";
+ if (value == HUEY_RC_LOCKED)
+ return "locked";
+ if (value == HUEY_RC_ERROR)
+ return "error";
+ if (value == HUEY_RC_RETRY)
+ return "retry";
+ if (value == HUEY_RC_UNKNOWN_5A)
+ return "unknown5a";
+ if (value == HUEY_RC_UNKNOWN_81)
+ return "unknown81";
+ return NULL;
+}
+
+/**
+ * huey_cmd_code_to_string:
+ *
+ * Since: 0.1.29
+ **/
+const gchar *
+huey_cmd_code_to_string (guchar value)
+{
+ if (value == HUEY_CMD_GET_STATUS)
+ return "get-status";
+ if (value == HUEY_CMD_READ_GREEN)
+ return "read-green";
+ if (value == HUEY_CMD_READ_BLUE)
+ return "read-blue";
+ if (value == HUEY_CMD_SET_INTEGRATION_TIME)
+ return "set-integration-time";
+ if (value == HUEY_CMD_GET_INTEGRATION_TIME)
+ return "get-integration-time";
+ if (value == HUEY_CMD_REGISTER_WRITE)
+ return "reg-write";
+ if (value == HUEY_CMD_REGISTER_READ)
+ return "reg-read";
+ if (value == HUEY_CMD_UNLOCK)
+ return "unlock";
+ if (value == HUEY_CMD_UNKNOWN_0F)
+ return "unknown0f";
+ if (value == HUEY_CMD_UNKNOWN_10)
+ return "unknown10";
+ if (value == HUEY_CMD_UNKNOWN_11)
+ return "unknown11";
+ if (value == HUEY_CMD_UNKNOWN_12)
+ return "unknown12";
+ if (value == HUEY_CMD_SENSOR_MEASURE_RGB_CRT)
+ return "measure-rgb-crt";
+ if (value == HUEY_CMD_UNKNOWN_15)
+ return "unknown15(status?)";
+ if (value == HUEY_CMD_SENSOR_MEASURE_RGB)
+ return "measure-rgb";
+ if (value == HUEY_CMD_UNKNOWN_21)
+ return "unknown21";
+ if (value == HUEY_CMD_GET_AMBIENT)
+ return "get-ambient";
+ if (value == HUEY_CMD_SET_LEDS)
+ return "set-leds";
+ if (value == HUEY_CMD_SENSOR_MEASURE_RGB_ALT)
+ return "measure-rgb-alt";
+ return NULL;
+}
diff --git a/src/sensors/huey/huey-enum.h b/src/sensors/huey/huey-enum.h
new file mode 100644
index 0000000..7317558
--- /dev/null
+++ b/src/sensors/huey/huey-enum.h
@@ -0,0 +1,321 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __HUEY_ENUM_H
+#define __HUEY_ENUM_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* external hueys */
+#define CD_SENSOR_HUEY_VENDOR_ID 0x0971
+#define CD_SENSOR_HUEY_PRODUCT_ID 0x2005
+
+/* integrated and newer devices */
+#define CD_SENSOR_HUEY_VENDOR_ID2 0x0765
+#define CD_SENSOR_HUEY_PRODUCT_ID2 0x5001
+
+/* Return values from the commands */
+#define HUEY_RC_SUCCESS 0x00
+#define HUEY_RC_LOCKED 0xc0
+#define HUEY_RC_UNKNOWN_5A 0x5a /* seen in profiling */
+#define HUEY_RC_ERROR 0x80
+#define HUEY_RC_UNKNOWN_81 0x81 /* seen once in init */
+#define HUEY_RC_RETRY 0x90
+
+/*
+ * Get the currect status of the device
+ *
+ * input: 00 00 00 00 3f 00 00 00
+ * returns: 00 00 43 69 72 30 30 31 (or)
+ * "Cir001" --^^^^^^^^^^^^^^^^^ (Pantone Huey)
+ * c0 00 xx xx xx xx xx xx
+ * "huL002" --^^^^^^^^^^^^^^^^^ (Lenovo Huey)
+ * c0 00 4c 6f 63 6b 65 64
+ * "locked" --^^^^^^^^^^^^^^^^^
+ */
+#define HUEY_CMD_GET_STATUS 0x00
+
+/*
+ * Read the green sample data
+ *
+ * input: 02 xx xx xx xx xx xx xx
+ * returns: 00 02 00 00 0a 00 00 00 (or)
+ * 00 02 00 0e c6 80 00 00
+ * data --^^^^^ ^-- only ever 00 or 80
+ * |
+ * \-- for RGB(00,00,00) is 09 f2
+ * RGB(ff,ff,ff) is 00 00
+ * RGB(ff,00,00) is 02 a5
+ * RGB(00,ff,00) is 00 f1
+ * RGB(00,00,ff) is 08 56
+ *
+ * This doesn't do a sensor read, it seems to be a simple accessor.
+ * HUEY_CMD_SENSOR_MEASURE_RGB has to be used before this one.
+ */
+#define HUEY_CMD_READ_GREEN 0x02
+
+/*
+ * Read the blue sample data
+ *
+ * input: 03 xx xx xx xx xx xx xx
+ * returns: 00 03 00 0f 18 00 00 00
+ * data --^^^^^ ^-- only ever 00 or 80
+ * |
+ * \-- for RGB(00,00,00) is 09 64
+ * RGB(ff,ff,ff) is 08 80
+ * RGB(ff,00,00) is 03 22
+ * RGB(00,ff,00) is 00 58
+ * RGB(00,00,ff) is 00 59
+ *
+ * This doesn't do a sensor read, it seems to be a simple accessor.
+ * HUEY_CMD_SENSOR_MEASURE_RGB has to be used before this one.
+ */
+#define HUEY_CMD_READ_BLUE 0x03
+
+/*
+ * Sets a 32 bit integration time register.
+ *
+ * input: 05 ?? 11 12 13 14 xx xx
+ * returns: 00 05 00 00 00 00 00 00
+ * ^--- always the same no matter the input
+ *
+ * This is not normally used during profiling
+ */
+#define HUEY_CMD_SET_INTEGRATION_TIME 0x05
+
+/*
+ * Get the value of the 32 bit integration time register.
+ *
+ * input: 06 xx xx xx xx xx xx xx
+ * returns: 00 06 11 12 13 14 00 00
+ * 4 bytes ----^^^^^^^^^^^ (from HUEY_CMD_SET_INTEGRATION_TIME)
+ *
+ * The default value at plug-in is 00 0f 42 40, although during
+ * profiling it is set to 00 00 6f 00 and then 00 00 61 00.
+ */
+#define HUEY_CMD_GET_INTEGRATION_TIME 0x06
+
+/*
+ * Writes a register value.
+ *
+ * (sent at startup after the unlock)
+ * input: 07 0b ff xx xx xx xx xx
+ * ^^-- data value
+ * ^^-- register address
+ * returns: 00 xx xx xx xx xx xx xx
+ *
+ * You can only write one byte at a time.
+ */
+#define HUEY_CMD_REGISTER_WRITE 0x07
+
+/*
+ * Reads a register value.
+ *
+ * (sent at startup after the unlock)
+ * input: 08 0b xx xx xx xx xx xx
+ * ^^-- register address
+ * returns: 00 08 0b b8 00 00 00 00
+ * address --^^ ^^-- value
+ *
+ * You can only read one byte at a time.
+ */
+#define HUEY_CMD_REGISTER_READ 0x08
+
+/*
+ * Unlock a locked sensor.
+ *
+ * input: 0e 47 72 4d 62 6b 65 64 (Pantone Huey)
+ * "GrMb"--^^^^^^^^^^^
+ * 0e 47 72 4d 62 6b 65 64 (Lenovo Huey)
+ * "huyL"--^^^^^^^^^^^
+ * returns: 00 0e 00 00 00 00 00 00
+ *
+ * Only GrMbk or huyL is needed to unlock, the rest is just junk data.
+ * We still don't know how to 'lock' a device, it just kinda happens.
+ */
+#define HUEY_CMD_UNLOCK 0x0e
+
+/*
+ * Unknown command
+ *
+ * returns: all NULL all of the time */
+#define HUEY_CMD_UNKNOWN_0F 0x0f
+
+/*
+ * Unknown command
+ *
+ * Something to do with sampling */
+#define HUEY_CMD_UNKNOWN_10 0x10
+
+/*
+ * Unknown command
+ *
+ * Something to do with sampling (that needs a retry with code 5a)
+ */
+#define HUEY_CMD_UNKNOWN_11 0x11
+
+/*
+ * Unknown command
+ *
+ * something to do with sampling
+ */
+#define HUEY_CMD_UNKNOWN_12 0x12
+
+/*
+ * Measures RGB value, and return the red value (only used in CRT mode).
+ *
+ * Seems to have to retry, every single time.
+ *
+ * Gain?
+ * _______|_______
+ * /---\ /---\ /---\
+ * input: 13 02 41 00 54 00 49 00
+ * returns: 00 13 00 00 01 99 02 00
+ * ^^^^^ - would match HUEY_CMD_SENSOR_MEASURE_RGB
+ *
+ * The gain seems not to change for different measurements with different
+ * colors. This seems to be a less precise profile too.
+ */
+#define HUEY_CMD_SENSOR_MEASURE_RGB_CRT 0x13
+
+/*
+ * Unknown command
+ *
+ * returns: seems to be sent, but not requested
+ */
+#define HUEY_CMD_UNKNOWN_15 0x15
+
+/*
+ * Sample a color and return the red component
+ *
+ * input: 16 00 01 00 01 00 01 00
+ * returns: 00 16 00 00 00 00 00 00
+ *
+ * or:
+ * ,,-,,-,,-,,-,,-,,-- 'gain control'
+ * || || || || || ||
+ * input: 16 00 35 00 48 00 1d 03
+ * returns: 00 16 00 0b d0 00 00 00
+ * data --^^^^^ ^^-- only ever 00 or 80
+ *
+ * This is used when profiling, and all commands are followed by
+ * HUEY_CMD_READ_GREEN and HUEY_COMMAND_READ_BLUE.
+ *
+ * The returned values are some kind of 16 bit register count that
+ * indicate how much light fell on a sensor. If the sensors are
+ * converting light to pulses, then the 'gain' control tells the sensor
+ * how long to read. It's therefore quicker to read white than black.
+ *
+ * Given there exists only GREEN and BLUE accessors, and that RED comes
+ * first in a RGB sequence, I think it's safe to assume that this command
+ * does the measurement, and the others just return cached data.
+ *
+ * argyll does (for #ff0000)
+ *
+ * -> 16 00 01 00 01 00 01 00
+ * <- 00 00 0b 00 00 00
+ * -> 02 xx xx xx xx xx xx xx
+ * <- 00 00 12 00 00 00
+ * -> 03 xx xx xx xx xx xx xx
+ * <- 00 03 41 00 00 00
+ *
+ * then does:
+ *
+ * -> 16 01 63 00 d9 00 04 00
+ * <- 00 0f ce 80 00 00
+ * -> 02 xx xx xx xx xx xx xx
+ * <- 00 0e d0 80 00 00
+ * -> 03 xx xx xx xx xx xx xx
+ * <- 00 0d 3c 00 00 00
+ *
+ * then returns XYZ=87.239169 45.548708 1.952249
+ */
+#define HUEY_CMD_SENSOR_MEASURE_RGB 0x16
+
+/*
+ * Unknown command (some sort of poll?)
+ *
+ * input: 21 09 00 02 00 00 08 00 (or)
+ * returns: [never seems to return a value]
+ *
+ * Only when profiling, and over and over.
+ */
+#define HUEY_CMD_UNKNOWN_21 0x21
+
+/*
+ * Get the level of ambient light from the sensor
+ *
+ * ,,--- The output-type, where 00 is LCD and 02 is CRT
+ * input: 17 03 00 xx xx xx xx xx
+ * returns: 90 17 03 00 00 00 00 00 then on second read:
+ * 00 17 03 00 00 62 57 00 in light (or)
+ * 00 17 03 00 00 00 08 00 in dark
+ * no idea --^^ ^---^ = 16bits data
+ */
+#define HUEY_CMD_GET_AMBIENT 0x17
+
+/*
+ * Set the LEDs on the sensor
+ *
+ * input: 18 00 f0 xx xx xx xx xx
+ * returns: 00 18 f0 00 00 00 00 00
+ * led mask ----^^
+ */
+#define HUEY_CMD_SET_LEDS 0x18
+
+/*
+ * Unknown command
+ *
+ * returns: all NULL for NULL input: times out for f1 f2 f3 f4 f5 f6 f7 f8 */
+#define HUEY_CMD_SENSOR_MEASURE_RGB_ALT 0x19
+
+/*
+ * Register map:
+ * x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF
+ * 0x [serial-number.][matrix-lcd....................................|
+ * 1x ...............................................................|
+ * 2x .......] |
+ * 3x [calib-lcd-time][matrix-crt............................|
+ * 4x ...............................................................|
+ * 5x .......................................][calib-crt-time] |
+ * 6x [calib_vector......................|
+ * 7x ...........] [unlock-string.....] |
+ * 8x |
+ * 9x [calib_value...] |
+ */
+#define HUEY_EEPROM_ADDR_SERIAL 0x00 /* 4 bytes */
+#define HUEY_EEPROM_ADDR_CALIBRATION_DATA_LCD 0x04 /* 36 bytes */
+#define HUEY_EEPROM_ADDR_CALIBRATION_TIME_LCD 0x32 /* 4 bytes */
+#define HUEY_EEPROM_ADDR_CALIBRATION_DATA_CRT 0x36 /* 36 bytes */
+#define HUEY_EEPROM_ADDR_CALIBRATION_TIME_CRT 0x5a /* 4 bytes */
+#define HUEY_EEPROM_ADDR_DARK_OFFSET 0x67 /* 12 bytes */
+#define HUEY_EEPROM_ADDR_UNLOCK 0x7a /* 5 bytes */
+#define HUEY_EEPROM_ADDR_AMBIENT_CALIB_VALUE 0x94 /* 4 bytes */
+
+const gchar *huey_rc_to_string (guchar value);
+const gchar *huey_cmd_code_to_string (guchar value);
+
+G_END_DECLS
+
+#endif /* __HUEY_ENUM_H */
+
diff --git a/src/sensors/munki/Makefile.am b/src/sensors/munki/Makefile.am
index c820af7..008cac1 100644
--- a/src/sensors/munki/Makefile.am
+++ b/src/sensors/munki/Makefile.am
@@ -1,5 +1,8 @@
if FALSE
-libcolord_sensor_munki_la_SOURCES = cd-sensor-munki.c
+libcolord_sensor_munki_la_SOURCES = \
+ munki-enum.c \
+ munki-enum.h \
+ cd-sensor-munki.c
libcolord_sensor_munki_la_LIBADD = $(GLIB_LIBS)
libcolord_sensor_munki_la_LDFLAGS = -module -avoid-version
libcolord_sensor_munki_la_CFLAGS = $(WARN_CFLAGS)
diff --git a/src/sensors/munki/cd-sensor-munki.c b/src/sensors/munki/cd-sensor-munki.c
index b5f7ef0..52ce132 100644
--- a/src/sensors/munki/cd-sensor-munki.c
+++ b/src/sensors/munki/cd-sensor-munki.c
@@ -29,6 +29,8 @@
#include "cd-buffer.h"
#include "cd-sensor.h"
+#include "munki-enum.h"
+
typedef struct
{
gboolean done_startup;
diff --git a/src/sensors/munki/munki-enum.c b/src/sensors/munki/munki-enum.c
new file mode 100644
index 0000000..e3ae53e
--- /dev/null
+++ b/src/sensors/munki/munki-enum.c
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * SECTION:munki-enum
+ * @short_description: Types used by munki and libmunki
+ *
+ * These helper functions provide a way to marshal enumerated values to
+ * text and back again.
+ *
+ * See also: #CdClient, #CdDevice
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "munki-enum.h"
+
+/**
+ * munki_command_value_to_string:
+ **/
+const gchar *
+munki_command_value_to_string (guint8 value)
+{
+ if (value == MUNKI_COMMAND_DIAL_ROTATE)
+ return "dial-rotate";
+ if (value == MUNKI_COMMAND_BUTTON_PRESSED)
+ return "button-released";
+ if (value == MUNKI_COMMAND_BUTTON_RELEASED)
+ return "button-released";
+ return NULL;
+}
+
+/**
+ * munki_button_state_to_string:
+ **/
+const gchar *
+munki_button_state_to_string (guint8 value)
+{
+ if (value == MUNKI_BUTTON_STATE_RELEASED)
+ return "released";
+ if (value == MUNKI_BUTTON_STATE_PRESSED)
+ return "pressed";
+ return NULL;
+}
+
+/**
+ * munki_dial_position_to_string:
+ **/
+const gchar *
+munki_dial_position_to_string (guint8 value)
+{
+ if (value == MUNKI_DIAL_POSITION_PROJECTOR)
+ return "projector";
+ if (value == MUNKI_DIAL_POSITION_SURFACE)
+ return "surface";
+ if (value == MUNKI_DIAL_POSITION_CALIBRATION)
+ return "calibration";
+ if (value == MUNKI_DIAL_POSITION_AMBIENT)
+ return "ambient";
+ return NULL;
+}
+
+/**
+ * munki_endpoint_to_string:
+ **/
+const gchar *
+munki_endpoint_to_string (guint value)
+{
+ if (value == MUNKI_EP_CONTROL)
+ return "control";
+ if (value == MUNKI_EP_DATA)
+ return "data";
+ if (value == MUNKI_EP_EVENT)
+ return "event";
+ return NULL;
+}
diff --git a/src/sensors/munki/munki-enum.h b/src/sensors/munki/munki-enum.h
new file mode 100644
index 0000000..d43c15c
--- /dev/null
+++ b/src/sensors/munki/munki-enum.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __MUNKI_ENUM_H
+#define __MUNKI_ENUM_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MUNKI_COMMAND_DIAL_ROTATE 0x00
+#define MUNKI_COMMAND_BUTTON_PRESSED 0x01
+#define MUNKI_COMMAND_BUTTON_RELEASED 0x02
+
+#define MUNKI_BUTTON_STATE_RELEASED 0x00
+#define MUNKI_BUTTON_STATE_PRESSED 0x01
+
+#define MUNKI_DIAL_POSITION_PROJECTOR 0x00
+#define MUNKI_DIAL_POSITION_SURFACE 0x01
+#define MUNKI_DIAL_POSITION_CALIBRATION 0x02
+#define MUNKI_DIAL_POSITION_AMBIENT 0x03
+#define MUNKI_DIAL_POSITION_UNKNOWN 0xff
+
+
+/*
+ * Triggers a request for a bulk transfer of EEPROM
+ * Length: 8 bytes
+ *
+ * address length (LE)
+ * ____|____ ____|____
+ * / \ / \
+ * 04 00 00 00 04 00 00 00
+ */
+#define MUNKI_REQUEST_EEPROM_DATA 0x81
+
+/*
+ * Gets the next hardware event
+ * Length: 8 bytes
+ *
+ * This blocks until the hardware sends an event, and must either be
+ * run in a mainloop or thread to avoid blocking.
+ *
+ * subcmd ----\ /------------ 32 bit event time
+ * cmd ----|\ || || || || ||
+ * Returns: 02 00 00 00 ac 62 07 00
+ * always zero ---||-||
+ *
+ * cmd is:
+ * 00 dial rotate
+ * 01 button pressed
+ * 02 button released
+ *
+ * subcmd is:
+ * 00 button event
+ * 01 dial rotate
+ */
+#define MUNKI_REQUEST_INTERRUPT 0x83
+
+/*
+ * Returns the major and minor version numbers
+ * Length: 24 bytes
+ */
+#define MUNKI_REQUEST_VERSION_STRING 0x85
+
+/*
+ * Returns the chip id
+ * Length: 8 bytes
+ */
+#define MUNKI_REQUEST_FIRMWARE_PARAMS 0x86
+
+/*
+ * Gets the device status
+ * Length: 2 bytes
+ *
+ * Returns: 00 00
+ * |/ ||
+ * dial pos -/ \--- button value
+ * - 00 = projector
+ * - 01 = surface
+ * - 02 = calibration
+ * - 03 = ambient
+ */
+#define MUNKI_REQUEST_GET_STATUS 0x87
+
+/*
+ * Returns the version string
+ * Length: 36 bytes
+ */
+#define MUNKI_REQUEST_CHIP_ID 0x8A
+
+/* USB endpoints in use */
+#define MUNKI_EP_CONTROL 0x00
+#define MUNKI_EP_DATA 0x01
+#define MUNKI_EP_EVENT 0x03
+
+/* EEPROM is massive */
+#define COLORMUNKI_EEPROM_OFFSET_SERIAL_NUMBER 0x0018 /* 10 bytes */
+
+const gchar *munki_button_state_to_string (guint8 value);
+const gchar *munki_dial_position_to_string (guint8 value);
+const gchar *munki_command_value_to_string (guint8 value);
+const gchar *munki_endpoint_to_string (guint value);
+
+G_END_DECLS
+
+#endif /* __MUNKI_ENUM_H */
+
diff --git a/src/sensors/spark/Makefile.am b/src/sensors/spark/Makefile.am
index 8962815..cb65f0e 100644
--- a/src/sensors/spark/Makefile.am
+++ b/src/sensors/spark/Makefile.am
@@ -18,11 +18,14 @@ plugin_LTLIBRARIES = \
libcolord_sensor_spark.la
libcolord_sensor_spark_la_SOURCES = \
+ osp-device.c \
+ osp-device.h \
+ osp-enum.c \
+ osp-enum.h \
cd-sensor-spark.c
libcolord_sensor_spark_la_LIBADD = \
$(GLIB_LIBS) \
- $(GUSB_LIBS) \
- $(top_builddir)/lib/ospark/libospark-private.la
+ $(GUSB_LIBS)
libcolord_sensor_spark_la_LDFLAGS = -module -avoid-version
libcolord_sensor_spark_la_CFLAGS = $(WARN_CFLAGS)
endif
diff --git a/src/sensors/spark/cd-sensor-spark.c b/src/sensors/spark/cd-sensor-spark.c
index 62ecf07..fcddf7a 100644
--- a/src/sensors/spark/cd-sensor-spark.c
+++ b/src/sensors/spark/cd-sensor-spark.c
@@ -24,10 +24,12 @@
#include <glib-object.h>
#include <colord-private.h>
-#include <ospark/ospark.h>
#include "../src/cd-sensor.h"
+#include "osp-device.h"
+#include "osp-enum.h"
+
typedef struct
{
GUsbDevice *device;
diff --git a/src/sensors/spark/osp-device.c b/src/sensors/spark/osp-device.c
new file mode 100644
index 0000000..84fcdb2
--- /dev/null
+++ b/src/sensors/spark/osp-device.c
@@ -0,0 +1,864 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * SECTION:ospark-enum
+ * @short_description: Types used by libospark
+ *
+ * See also: #CdClient, #CdDevice
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <string.h>
+#include <colord-private.h>
+
+#include "osp-device.h"
+#include "osp-enum.h"
+
+#define OSP_USB_TIMEOUT_MS 50000 /* ms */
+#define OSP_DEVICE_MAX_MSG_LENGTH (10240 + 64) /* bytes */
+#define OSP_DEVICE_EP_SIZE 64 /* bytes */
+
+/**
+ * osp_device_error_quark:
+ **/
+GQuark
+osp_device_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("OspDeviceError");
+ return quark;
+}
+
+/**
+ * osp_device_open:
+ * @device: a #GUsbDevice instance
+ * @error: A #GError or %NULL
+ *
+ * Opens the device and claims the interface
+ *
+ * Return value: %TRUE for success
+ *
+ * Since: 1.2.11
+ **/
+gboolean
+osp_device_open (GUsbDevice *device, GError **error)
+{
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (!g_usb_device_open (device, error))
+ return FALSE;
+ if (!g_usb_device_claim_interface (device, 0x00, 0, error)) {
+ g_prefix_error (error, "Failed to claim interface: ");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * osp_device_query:
+ *
+ * Since: 1.2.11
+ **/
+static gboolean
+osp_device_query (GUsbDevice *device, OspCmd cmd,
+ const guint8 *data_in, gsize data_in_length,
+ guint8 **data_out, gsize *data_out_length,
+ GError **error)
+{
+ OspProtocolFooter *ftr;
+ OspProtocolHeader *hdr;
+ gsize actual_length;
+ gsize checksum_length = 16; /* always for MD5 */
+ gsize payload_length = 0;
+ gsize offset_rd = 0;
+ gsize offset_wr = 0;
+ guint i;
+ g_autoptr(GChecksum) csum = NULL;
+ g_autofree guint8 *buffer_in = NULL;
+ g_autofree guint8 *buffer_out = NULL;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (data_in_length <= 16, FALSE); //FIXME
+ g_assert (sizeof(OspProtocolHeader) + sizeof(OspProtocolFooter) == 64);
+
+ /* write header to buffer */
+ buffer_in = g_new0 (guint8, OSP_DEVICE_MAX_MSG_LENGTH);
+ hdr = (OspProtocolHeader *) buffer_in;
+ hdr->start_bytes = GUINT16_TO_BE (0xc1c0);
+ hdr->protocol_version = 0x1000;
+ hdr->checksum_type = OSP_HEADER_CHECKSUM_KIND_MD5;
+ hdr->message_type = cmd;
+ hdr->bytes_remaining = sizeof(OspProtocolFooter);
+ if (data_out == NULL)
+ hdr->flags = OSP_HEADER_FLAG_ACK_REQUIRED;
+ if (data_in_length > 0) {
+ if (data_in_length <= 16) {
+ /* avoid another USB packet if we can */
+ hdr->immediate_data_length = data_in_length;
+ memcpy (hdr->immediate_data, data_in, data_in_length);
+ } else {
+ payload_length = data_in_length;
+ }
+ }
+ offset_wr += sizeof(OspProtocolHeader);
+ hdr->bytes_remaining = sizeof(OspProtocolFooter) + payload_length;
+
+ /* write payload to buffer, if any */
+ if (payload_length > 0) {
+ memcpy (buffer_in + offset_wr, data_in, data_in_length);
+ offset_wr += payload_length;
+ }
+
+ /* write footer to buffer */
+ ftr = (OspProtocolFooter *) (buffer_in + offset_wr);
+ ftr->end_bytes = GUINT32_TO_BE (0xc5c4c3c2);
+ csum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (csum, (const guchar *) buffer_in, offset_wr);
+ g_checksum_get_digest (csum, ftr->checksum, &checksum_length);
+ offset_wr += sizeof(OspProtocolFooter);
+
+ /* send data */
+ if (g_getenv ("SPARK_PROTOCOL_DEBUG") != NULL)
+ cd_buffer_debug (CD_BUFFER_KIND_REQUEST, buffer_in, offset_wr);
+ if (!g_usb_device_bulk_transfer (device, 0x01,
+ buffer_in, offset_wr,
+ &actual_length,
+ OSP_USB_TIMEOUT_MS, NULL, error))
+ return FALSE;
+
+ /* get reply */
+ buffer_out = g_new0 (guint8, 64);
+ if (!g_usb_device_bulk_transfer (device, 0x81,
+ buffer_out, OSP_DEVICE_EP_SIZE,
+ &actual_length,
+ OSP_USB_TIMEOUT_MS, NULL, error))
+ return FALSE;
+ if (g_getenv ("SPARK_PROTOCOL_DEBUG") != NULL)
+ cd_buffer_debug (CD_BUFFER_KIND_RESPONSE, buffer_out, actual_length);
+
+ /* check the error code */
+ hdr = (OspProtocolHeader *) buffer_out;
+ switch (hdr->error_code) {
+ case OSP_ERROR_CODE_SUCCESS:
+ break;
+ case OSP_ERROR_CODE_MESSAGE_TOO_LARGE:
+ case OSP_ERROR_CODE_UNKNOWN_CHECKSUM_TYPE:
+ case OSP_ERROR_CODE_UNSUPPORTED_PROTOCOL:
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_NO_SUPPORT,
+ "Failed to %s",
+ osp_cmd_to_string (cmd));
+ return FALSE;
+ break;
+ case OSP_ERROR_CODE_COMMAND_DATA_MISSING:
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_NO_DATA,
+ "Failed to %s",
+ osp_cmd_to_string (cmd));
+ return FALSE;
+ break;
+ default:
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Failed to %s: %s",
+ osp_cmd_to_string (cmd),
+ osp_error_code_to_string (hdr->error_code));
+ return FALSE;
+ break;
+ }
+
+ /* copy out the data */
+ offset_rd = sizeof(OspProtocolHeader);
+ if (data_out != NULL && data_out_length != NULL) {
+ if (hdr->immediate_data_length > 0) {
+ *data_out_length = hdr->immediate_data_length;
+ *data_out = g_memdup (hdr->immediate_data,
+ hdr->immediate_data_length);
+ } else if (hdr->bytes_remaining >= sizeof(OspProtocolFooter)) {
+ *data_out_length = hdr->bytes_remaining - sizeof(OspProtocolFooter);
+ *data_out = g_new0 (guint8, hdr->bytes_remaining);
+
+ /* copy the first chunk of data */
+ offset_wr = 64 - offset_rd;
+ memcpy (*data_out, buffer_out + offset_rd, offset_wr);
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+
+ /* read the rest of the payload */
+ payload_length = hdr->bytes_remaining - sizeof(OspProtocolFooter);
+ for (i = 0; i < payload_length / 64; i++) {
+ if (!g_usb_device_bulk_transfer (device, 0x81,
+ buffer_out, OSP_DEVICE_EP_SIZE,
+ &actual_length,
+ OSP_USB_TIMEOUT_MS, NULL, error))
+ return FALSE;
+ if (data_out != NULL) {
+ memcpy (*data_out + offset_wr,
+ buffer_out, OSP_DEVICE_EP_SIZE);
+ }
+ if (g_getenv ("SPARK_PROTOCOL_DEBUG") != NULL)
+ cd_buffer_debug (CD_BUFFER_KIND_RESPONSE, buffer_out, OSP_DEVICE_EP_SIZE);
+ offset_wr += 64;
+ }
+ offset_rd += payload_length;
+
+ /* verify the footer is intact */
+ ftr = (OspProtocolFooter *) (buffer_out + OSP_DEVICE_EP_SIZE - sizeof(OspProtocolFooter));
+ if (ftr->end_bytes != GUINT32_TO_BE (0xc5c4c3c2)) {
+ g_set_error_literal (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Footer invalid");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * osp_device_send_command:
+ *
+ * Since: 1.2.11
+ **/
+static gboolean
+osp_device_send_command (GUsbDevice *device, OspCmd cmd,
+ const guint8 *data_in, gsize data_in_length,
+ GError **error)
+{
+ return osp_device_query (device, cmd,
+ data_in, data_in_length,
+ NULL, NULL, error);
+}
+
+/**
+ * osp_device_get_serial:
+ * @device: a #GUsbDevice instance
+ * @error: A #GError or %NULL
+ *
+ * Gets the device serial number.
+ *
+ * Return value: A string, or %NULL for failure
+ *
+ * Since: 1.2.11
+ **/
+gchar *
+osp_device_get_serial (GUsbDevice *device, GError **error)
+{
+ gsize data_len;
+ g_autofree guint8 *data = NULL;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* query hardware */
+ if (!osp_device_query (device, OSP_CMD_GET_SERIAL_NUMBER,
+ NULL, 0, &data, &data_len, error))
+ return NULL;
+
+ /* check values */
+ if (data_len == 0) {
+ g_set_error_literal (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected serial number, got nothing");
+ return NULL;
+ }
+
+ /* format value */
+ return g_strndup ((const gchar *) data, data_len);
+}
+
+/**
+ * osp_device_get_fw_version:
+ * @device: a #GUsbDevice instance
+ * @error: A #GError or %NULL
+ *
+ * Opens the device and claims the interface
+ *
+ * Return value: The firmware version, or %NULL for error
+ *
+ * Since: 1.2.11
+ **/
+gchar *
+osp_device_get_fw_version (GUsbDevice *device, GError **error)
+{
+ gsize data_len;
+ g_autofree guint8 *data = NULL;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* query hardware */
+ if (!osp_device_query (device, OSP_CMD_GET_FIRMWARE_VERSION,
+ NULL, 0, &data, &data_len, error))
+ return NULL;
+
+ /* check values */
+ if (data_len != 2) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected %i bytes, got %" G_GSIZE_FORMAT,
+ 2, data_len);
+ return NULL;
+ }
+
+ /* format value */
+ return g_strdup_printf ("%u.%u", data[1], data[0]);
+}
+
+/**
+ * osp_device_get_wavelength_cal_for_idx:
+ *
+ * Since: 1.3.1
+ **/
+static gboolean
+osp_device_get_wavelength_cal_for_idx (GUsbDevice *device,
+ guint idx,
+ gfloat *cal,
+ GError **error)
+{
+ gsize data_len;
+ guint8 idx_buf[1] = { idx };
+ g_autofree guint8 *data = NULL;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* query hardware */
+ if (!osp_device_query (device, OSP_CMD_GET_WAVELENGTH_COEFFICIENT,
+ idx_buf, 1, &data, &data_len, error))
+ return FALSE;
+
+ /* check values */
+ if (data_len != 4) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected %i bytes, got %" G_GSIZE_FORMAT,
+ 4, data_len);
+ return FALSE;
+ }
+
+ /* convert to floating point */
+ if (cal != NULL)
+ *cal = *((gfloat *) data);
+
+ /* format value */
+ return TRUE;
+}
+
+/**
+ * osp_device_get_wavelength_start:
+ * @device: a #GUsbDevice instance
+ * @error: A #GError or %NULL
+ *
+ * Gets the starting wavelength for the sensor.
+ *
+ * Return value: A value in nm, or -1 for error
+ *
+ * Since: 1.3.1
+ **/
+gdouble
+osp_device_get_wavelength_start (GUsbDevice *device, GError **error)
+{
+ gfloat tmp = -1.f;
+
+ /* get from hardware */
+ if (!osp_device_get_wavelength_cal_for_idx (device, 0, &tmp, error))
+ return -1.f;
+
+ /* check values */
+ if (tmp < 0) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Not a valid start, got %f", tmp);
+ return -1.f;
+ }
+
+ return (gdouble) tmp;
+}
+
+/**
+ * osp_device_get_wavelength_cal:
+ * @device: a #GUsbDevice instance
+ * @length: the size of the returned array
+ * @error: A #GError or %NULL
+ *
+ * Gets the wavelength coefficients for the sensor.
+ *
+ * Return value: An array of coefficients
+ *
+ * Since: 1.3.1
+ **/
+gdouble *
+osp_device_get_wavelength_cal (GUsbDevice *device, guint *length, GError **error)
+{
+ gboolean ret;
+ gdouble *coefs = NULL;
+ gfloat cx;
+ gsize data_len;
+ guint i;
+ g_autofree guint8 *data = NULL;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* query hardware */
+ if (!osp_device_query (device, OSP_CMD_GET_WAVELENGTH_COEFFICIENT_COUNT,
+ NULL, 0, &data, &data_len, error))
+ return NULL;
+
+ /* check values */
+ if (data_len != 1) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected 1 bytes, got %" G_GSIZE_FORMAT,
+ data_len);
+ return NULL;
+ }
+
+ /* check sanity */
+ if (data[0] != 4) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected 4 coefs, got %u", data[0]);
+ return NULL;
+ }
+
+ /* get the coefs */
+ coefs = g_new0 (gdouble, data[0] - 1);
+ for (i = 0; i < (guint) data[0] - 1; i++) {
+ ret = osp_device_get_wavelength_cal_for_idx (device,
+ i + 1,
+ &cx,
+ error);
+ if (!ret)
+ return NULL;
+ coefs[i] = cx;
+ }
+
+ /* this is optional */
+ if (length != NULL)
+ *length = data[0] - 1;
+
+ /* success */
+ return coefs;
+}
+
+/**
+ * osp_device_get_nonlinearity_cal_for_idx:
+ *
+ * Since: 1.3.1
+ **/
+static gboolean
+osp_device_get_nonlinearity_cal_for_idx (GUsbDevice *device,
+ guint idx,
+ gfloat *cal,
+ GError **error)
+{
+ gsize data_len;
+ guint8 idx_buf[1] = { idx };
+ g_autofree guint8 *data = NULL;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* query hardware */
+ if (!osp_device_query (device, OSP_CMD_GET_NONLINEARITY_COEFFICIENT,
+ idx_buf, 1, &data, &data_len, error))
+ return FALSE;
+
+ /* check values */
+ if (data_len != 4) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected %i bytes, got %" G_GSIZE_FORMAT,
+ 4, data_len);
+ return FALSE;
+ }
+
+ /* convert to floating point */
+ if (cal != NULL)
+ *cal = *((gfloat *) data);
+
+ /* format value */
+ return TRUE;
+}
+
+/**
+ * osp_device_get_nonlinearity_cal:
+ * @device: a #GUsbDevice instance
+ * @length: the size of the returned array
+ * @error: A #GError or %NULL
+ *
+ * Gets the nonlinearity values for the sensor.
+ *
+ * Return value: An array of coefficients
+ *
+ * Since: 1.3.1
+ **/
+gdouble *
+osp_device_get_nonlinearity_cal (GUsbDevice *device, guint *length, GError **error)
+{
+ gboolean ret;
+ gdouble *coefs = NULL;
+ gfloat cx;
+ gsize data_len;
+ guint i;
+ g_autofree guint8 *data = NULL;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* query hardware */
+ if (!osp_device_query (device, OSP_CMD_GET_NONLINEARITY_COEFFICIENT_COUNT,
+ NULL, 0, &data, &data_len, error))
+ return NULL;
+
+ /* check values */
+ if (data_len != 1) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected 1 bytes, got %" G_GSIZE_FORMAT,
+ data_len);
+ return NULL;
+ }
+
+ /* check sanity */
+ if (data[0] != 8) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected 8 coefs, got %i", data[0]);
+ return NULL;
+ }
+
+ /* get the coefs */
+ coefs = g_new0 (gdouble, data[0]);
+ for (i = 0; i < data[0]; i++) {
+ ret = osp_device_get_nonlinearity_cal_for_idx (device,
+ i,
+ &cx,
+ error);
+ if (!ret)
+ return NULL;
+ coefs[i] = cx;
+ }
+
+ /* this is optional */
+ if (length != NULL)
+ *length = data[0];
+
+ /* success */
+ return coefs;
+}
+
+/**
+ * osp_device_get_irradiance_cal:
+ * @device: a #GUsbDevice instance
+ * @length: the size of the returned array
+ * @error: A #GError or %NULL
+ *
+ * Gets the irradiance spectrum for the sensor.
+ *
+ * Return value: An array of coefficients
+ *
+ * Since: 1.3.1
+ **/
+gdouble *
+osp_device_get_irradiance_cal (GUsbDevice *device, guint *length, GError **error)
+{
+ gdouble *coefs = NULL;
+ gsize data_len;
+ guint i;
+ g_autofree guint8 *data = NULL;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* query hardware */
+ if (!osp_device_query (device, OSP_CMD_GET_IRRADIANCE_CALIBRATION,
+ NULL, 0, &data, &data_len, error))
+ return NULL;
+
+ /* check values */
+ if (data_len != 4096 * 4) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected %i bytes, got %" G_GSIZE_FORMAT,
+ 4096 * 4, data_len);
+ return NULL;
+ }
+
+ /* copy out the coefs */
+ coefs = g_new0 (gdouble, 4096);
+ for (i = 0; i < 4096; i++)
+ coefs[i] = *((gfloat *) &data[i*4]);
+
+ /* this is optional */
+ if (length != NULL)
+ *length = 4096;
+
+ /* success */
+ return coefs;
+}
+
+static CdSpectrum *
+osp_device_take_spectrum_internal (GUsbDevice *device,
+ guint64 sample_duration,
+ GError **error)
+{
+ CdSpectrum *sp;
+ gdouble val;
+ gsize data_len;
+ guint i;
+ g_autofree guint8 *data = NULL;
+ g_autoptr(GTimer) t = NULL;
+
+ /* set integral time in us */
+ if (!osp_device_send_command (device, OSP_CMD_SET_INTEGRATION_TIME,
+ (guint8 *) &sample_duration, 4, error))
+ return NULL;
+
+ /* get spectrum */
+ t = g_timer_new ();
+ if (!osp_device_query (device, OSP_CMD_GET_AND_SEND_RAW_SPECTRUM,
+ NULL, 0, &data, &data_len, error))
+ return NULL;
+ g_debug ("For integration of %.0fms, sensor took %.0fms",
+ sample_duration / 1000.f, g_timer_elapsed (t, NULL) * 1000.f);
+
+ /* check values */
+ if (data_len != 2048) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "Expected %i bytes, got %" G_GSIZE_FORMAT,
+ 2048, data_len);
+ return NULL;
+ }
+
+ /* export */
+ sp = cd_spectrum_sized_new (1024);
+ for (i = 0; i < 1024; i++) {
+ val = data[i*2+1] * 256 + data[i*2+0];
+ cd_spectrum_add_value (sp, val / (gdouble) 0xffff);
+ }
+
+ /* the maximum value the hardware can return is 0x3fff */
+ val = cd_spectrum_get_value_max (sp);
+ if (val > 0.25) {
+ g_set_error (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_INTERNAL,
+ "spectral max should be <= 0.25f, was %f",
+ val);
+ cd_spectrum_free (sp);
+ return NULL;
+ }
+
+ return sp;
+}
+
+/**
+ * osp_device_take_spectrum_full:
+ * @device: a #GUsbDevice instance
+ * @sample_duration: the sample duration in µs
+ * @error: A #GError or %NULL
+ *
+ * Returns a spectrum for a set sample duration.
+ *
+ * Return value: A #CdSpectrum, or %NULL for error
+ *
+ * Since: 1.3.1
+ **/
+CdSpectrum *
+osp_device_take_spectrum_full (GUsbDevice *device,
+ guint64 sample_duration,
+ GError **error)
+{
+ CdSpectrum *sp;
+ gdouble start;
+ guint8 bin_factor = 0;
+ g_autofree gdouble *cx = NULL;
+ g_autoptr(CdSpectrum) sp_dc = NULL;
+ g_autoptr(CdSpectrum) sp_raw = NULL;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* return every pixel */
+ if (!osp_device_send_command (device, OSP_CMD_SET_PIXEL_BINNING_FACTOR,
+ &bin_factor, 1, error))
+ return NULL;
+
+ /* get spectrum */
+ sp_raw = osp_device_take_spectrum_internal (device, sample_duration, error);
+ if (sp_raw == NULL)
+ return NULL;
+ cd_spectrum_set_id (sp_raw, "raw");
+
+ /* remove any DC offset from the sensor by doing a 10us reading --
+ * ideally this would be 0us, but we have to use what we have */
+ sp_dc = osp_device_take_spectrum_internal (device, 10, error);
+ if (sp_dc == NULL)
+ return NULL;
+ cd_spectrum_set_id (sp_dc, "dc");
+
+ /* get coefficients */
+ cx = osp_device_get_wavelength_cal (device, NULL, error);
+ if (cx == NULL)
+ return NULL;
+
+ /* get start */
+ start = osp_device_get_wavelength_start (device, error);
+ if (start < 0)
+ return NULL;
+
+ /* return the reading without a DC component */
+ sp = cd_spectrum_subtract (sp_raw, sp_dc, 5);
+ cd_spectrum_set_start (sp, start);
+ cd_spectrum_set_norm (sp, 4);
+ cd_spectrum_set_wavelength_cal (sp, cx[0], cx[1], cx[2]);
+ return sp;
+}
+
+/**
+ * osp_device_take_spectrum:
+ * @device: a #GUsbDevice instance.
+ * @error: A #GError or %NULL
+ *
+ * Returns a spectrum. The optimal sample duration is calculated automatically.
+ *
+ * Return value: A #CdSpectrum, or %NULL for error
+ *
+ * Since: 1.2.11
+ **/
+CdSpectrum *
+osp_device_take_spectrum (GUsbDevice *device, GError **error)
+{
+ const guint sample_duration_max_secs = 3;
+ gboolean relax_requirements = FALSE;
+ gdouble max;
+ gdouble scale = 0.f;
+ guint64 sample_duration = 10000; /* us */
+ CdSpectrum *sp = NULL;
+ guint i;
+
+ g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* loop until we're in 1/4 to 3/4 FSD */
+ for (i = 0; i < 5; i++) {
+ g_autoptr(CdSpectrum) sp_probe = NULL;
+
+ /* for the last try, relax what we deem acceptable so we can
+ * measure very black things with a long integration time */
+ if (i == 4)
+ relax_requirements = TRUE;
+
+ /* take a measurement */
+ sp_probe = osp_device_take_spectrum_full (device,
+ sample_duration,
+ error);
+ if (sp_probe == NULL)
+ return NULL;
+
+ /* sensor picked up nothing, take action */
+ max = cd_spectrum_get_value_max (sp_probe);
+ if (max < 0.001f) {
+ sample_duration *= 100.f;
+ g_debug ("sensor read no data, setting duration "
+ "to %" G_GUINT64_FORMAT ,
+ sample_duration);
+ continue;
+ }
+
+ /* sensor is saturated, take action */
+ if (max > 0.99f) {
+ sample_duration /= 100.f;
+ g_debug ("sensor saturated, setting duration "
+ "to %" G_GUINT64_FORMAT,
+ sample_duration);
+ continue;
+ }
+
+ /* break out if we got valid readings */
+ if (max > 0.25f && max < 0.75f) {
+ sp = cd_spectrum_dup (sp_probe);
+ break;
+ }
+
+ /* be more accepting */
+ if (relax_requirements && max > 0.01f) {
+ sp = cd_spectrum_dup (sp_probe);
+ break;
+ }
+
+ /* aim for FSD / 2 */
+ scale = (gdouble) 0.5 / max;
+ sample_duration *= scale;
+ g_debug ("for max of %f, using scale=%f for duration %" G_GUINT64_FORMAT,
+ max, scale, sample_duration);
+
+ /* limit this to something sane */
+ if (sample_duration / G_USEC_PER_SEC > sample_duration_max_secs) {
+ g_debug ("limiting duration from %us to %us",
+ (guint) (sample_duration / G_USEC_PER_SEC),
+ (guint) (sample_duration_max_secs));
+ sample_duration = sample_duration_max_secs * G_USEC_PER_SEC;
+ relax_requirements = TRUE;
+ }
+ }
+
+ /* no suitable readings */
+ if (sp == NULL) {
+ g_set_error_literal (error,
+ OSP_DEVICE_ERROR,
+ OSP_DEVICE_ERROR_NO_DATA,
+ "Got no valid data");
+ return NULL;
+ }
+
+ /* scale with the new integral time */
+ cd_spectrum_set_norm (sp, cd_spectrum_get_norm (sp) / scale);
+ g_debug ("normalised spectral max is %f", cd_spectrum_get_value_max (sp));
+ return sp;
+}
diff --git a/src/sensors/spark/osp-device.h b/src/sensors/spark/osp-device.h
new file mode 100644
index 0000000..617e9dd
--- /dev/null
+++ b/src/sensors/spark/osp-device.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __OSP_DEVICE_H
+#define __OSP_DEVICE_H
+
+#include <glib-object.h>
+#include <gusb.h>
+#include <colord-private.h>
+
+G_BEGIN_DECLS
+
+#define OSP_DEVICE_ERROR osp_device_error_quark()
+
+typedef enum {
+ OSP_DEVICE_ERROR_INTERNAL,
+ OSP_DEVICE_ERROR_NO_DATA,
+ OSP_DEVICE_ERROR_NO_SUPPORT,
+ OSP_DEVICE_ERROR_LAST
+} OspDeviceError;
+
+GQuark osp_device_error_quark (void);
+CdSpectrum *osp_device_take_spectrum (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+CdSpectrum *osp_device_take_spectrum_full (GUsbDevice *device,
+ guint64 sample_duration,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gchar *osp_device_get_serial (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gchar *osp_device_get_fw_version (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gdouble *osp_device_get_nonlinearity_cal(GUsbDevice *device,
+ guint *length,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gdouble *osp_device_get_irradiance_cal (GUsbDevice *device,
+ guint *length,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gdouble *osp_device_get_wavelength_cal (GUsbDevice *device,
+ guint *length,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gdouble osp_device_get_wavelength_start (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+gboolean osp_device_open (GUsbDevice *device,
+ GError **error)
+ G_GNUC_WARN_UNUSED_RESULT;
+
+G_END_DECLS
+
+#endif /* __OSP_DEVICE_H */
+
diff --git a/src/sensors/spark/osp-enum.c b/src/sensors/spark/osp-enum.c
new file mode 100644
index 0000000..9c92b80
--- /dev/null
+++ b/src/sensors/spark/osp-enum.c
@@ -0,0 +1,201 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "config.h"
+
+#include <glib.h>
+
+#include "osp-enum.h"
+
+/**
+ * osp_cmd_to_string:
+ *
+ * Since: 1.2.11
+ **/
+const gchar *
+osp_cmd_to_string (OspCmd cmd)
+{
+ if (cmd == OSP_CMD_RESET)
+ return "reset";
+ if (cmd == OSP_CMD_RESET_TO_DEFAULTS)
+ return "reset-to-defaults";
+ if (cmd == OSP_CMD_GET_HARDWARE_VERSION)
+ return "get-hardware-version";
+ if (cmd == OSP_CMD_GET_FIRMWARE_VERSION)
+ return "get-firmware-version";
+ if (cmd == OSP_CMD_GET_SERIAL_NUMBER)
+ return "get-serial-number";
+ if (cmd == OSP_CMD_GET_SERIAL_NUMBER_LENGTH)
+ return "get-serial-number-length";
+ if (cmd == OSP_CMD_GET_DEVICE_ALIAS)
+ return "get-device-alias";
+ if (cmd == OSP_CMD_GET_DEVICE_ALIAS_LENGTH)
+ return "get-device-alias-length";
+ if (cmd == OSP_CMD_SET_DEVICE_ALIAS)
+ return "set-device-alias";
+ if (cmd == OSP_CMD_GET_NUMBER_OF_AVAILABLE_USER_STRINGS)
+ return "get-number-of-available-user-strings";
+ if (cmd == OSP_CMD_GET_USER_STRING_LENGTH)
+ return "get-user-string-length";
+ if (cmd == OSP_CMD_GET_USER_STRING)
+ return "get-user-string";
+ if (cmd == OSP_CMD_SET_USER_STRING)
+ return "set-user-string";
+ if (cmd == OSP_CMD_SET_LED)
+ return "configures-status-led";
+ if (cmd == OSP_CMD_PUT_DEVICE_IN_REPROGRAMMING_MODE)
+ return "put-device-in-reprogramming-mode";
+ if (cmd == OSP_CMD_GET_AND_SEND_CORRECTED_SPECTRUM)
+ return "get-and-send-corrected-spectrum";
+ if (cmd == OSP_CMD_GET_AND_SEND_RAW_SPECTRUM)
+ return "get-and-send-raw-spectrum";
+ if (cmd == OSP_CMD_GET_PARTIAL_SPECTRUM_MODE)
+ return "get-partial-spectrum-mode";
+ if (cmd == OSP_CMD_SET_PARTIAL_SPECTRUM_MODE)
+ return "set-partial-spectrum-mode";
+ if (cmd == OSP_CMD_GET_AND_SEND_PARTIAL_CORRECTED_SPECTRUM)
+ return "get-and-send-partial-corrected-spectrum";
+ if (cmd == OSP_CMD_SET_INTEGRATION_TIME)
+ return "set-integration-time";
+ if (cmd == OSP_CMD_GET_PIXEL_BINNING_FACTOR)
+ return "get-pixel-binning-factor";
+ if (cmd == OSP_CMD_GET_MAXIMUM_BINNING_FACTOR)
+ return "get-maximum-binning-factor";
+ if (cmd == OSP_CMD_GET_DEFAULT_BINNING_FACTOR)
+ return "get-default-binning-factor";
+ if (cmd == OSP_CMD_SET_PIXEL_BINNING_FACTOR)
+ return "set-pixel-binning-factor";
+ if (cmd == OSP_CMD_SET_DEFAULT_BINNING_FACTOR)
+ return "set-default-binning-factor";
+ if (cmd == OSP_CMD_SET_TRIGGER_DELAY_MS)
+ return "set-trigger-delay-ms";
+ if (cmd == OSP_CMD_GET_SCANS_TO_AVERAGE)
+ return "get-scans-to-average";
+ if (cmd == OSP_CMD_SET_SCANS_TO_AVERAGE)
+ return "set-scans-to-average";
+ if (cmd == OSP_CMD_GET_BOXCAR_WIDTH)
+ return "get-boxcar-width";
+ if (cmd == OSP_CMD_SET_BOXCAR_WIDTH)
+ return "set-boxcar-width";
+ if (cmd == OSP_CMD_GET_WAVELENGTH_COEFFICIENT_COUNT)
+ return "get-wavelength-coefficient-count";
+ if (cmd == OSP_CMD_GET_WAVELENGTH_COEFFICIENT)
+ return "get-wavelength-coefficient";
+ if (cmd == OSP_CMD_SET_WAVELENGTH_COEFFICIENT)
+ return "set-wavelength-coefficient";
+ if (cmd == OSP_CMD_GET_NONLINEARITY_COEFFICIENT_COUNT)
+ return "get-nonlinearity-coefficient-count";
+ if (cmd == OSP_CMD_GET_NONLINEARITY_COEFFICIENT)
+ return "get-nonlinearity-coefficient";
+ if (cmd == OSP_CMD_SET_NONLINEARITY_COEFFICIENT)
+ return "set-nonlinearity-coefficient";
+ if (cmd == OSP_CMD_GET_IRRADIANCE_CALIBRATION)
+ return "get-irradiance-calibration";
+ if (cmd == OSP_CMD_GET_IRRADIANCE_CALIBRATION_COUNT)
+ return "get-irradiance-calibration-count";
+ if (cmd == OSP_CMD_GET_IRRADIANCE_CALIBRATION_COLLECTION_AREA)
+ return "get-irradiance-calibration-collection-area";
+ if (cmd == OSP_CMD_SET_IRRADIANCE_CALIBRATION)
+ return "set-irradiance-calibration";
+ if (cmd == OSP_CMD_SET_IRRADIANCE_CALIBRATION_COLLECTION_AREA)
+ return "set-irradiance-calibration-collection-area";
+ if (cmd == OSP_CMD_GET_NUMBER_OF_STRAY_LIGHT_COEFFICIENTS)
+ return "get-number-of-stray-light-coefficients";
+ if (cmd == OSP_CMD_GET_STRAY_LIGHT_COEFFICIENT)
+ return "get-stray-light-coefficient";
+ if (cmd == OSP_CMD_SET_STRAY_LIGHT_COEFFICIENT)
+ return "set-stray-light-coefficient";
+ if (cmd == OSP_CMD_GET_HOT_PIXEL_INDICES)
+ return "get-hot-pixel-indices";
+ if (cmd == OSP_CMD_SET_HOT_PIXEL_INDICES)
+ return "set-hot-pixel-indices";
+ if (cmd == OSP_CMD_GET_BENCH_ID)
+ return "get-bench-id";
+ if (cmd == OSP_CMD_GET_BENCH_SERIAL_NUMBER)
+ return "get-bench-serial-number";
+ if (cmd == OSP_CMD_GET_SLIT_WIDTH_MICRONS)
+ return "get-slit-width-microns";
+ if (cmd == OSP_CMD_GET_FIBER_DIAMETER_MICRONS)
+ return "get-fiber-diameter-microns";
+ if (cmd == OSP_CMD_GET_GRATING)
+ return "get-grating";
+ if (cmd == OSP_CMD_GET_FILTER)
+ return "get-filter";
+ if (cmd == OSP_CMD_GET_COATING)
+ return "get-coating";
+ if (cmd == OSP_CMD_GET_GET_TEMPERATURE_SENSOR_COUNT)
+ return "get-temperature-sensor-count";
+ if (cmd == OSP_CMD_GET_READ_TEMPERATURE_SENSOR)
+ return "read-temperature-sensor";
+ if (cmd == OSP_CMD_GET_READ_ALL_TEMPERATURE_SENSORS)
+ return "read-all-temperature-sensors";
+ return NULL;
+}
+
+/**
+ * osp_error_code_to_string:
+ *
+ * Since: 1.2.11
+ **/
+const gchar *
+osp_error_code_to_string (OspErrorCode error_code)
+{
+ if (error_code == OSP_ERROR_CODE_SUCCESS)
+ return "success";
+ if (error_code == OSP_ERROR_CODE_UNSUPPORTED_PROTOCOL)
+ return "unsupported-protocol";
+ if (error_code == OSP_ERROR_CODE_UNKNOWN_MESSAGE_TYPE)
+ return "unknown-message-type";
+ if (error_code == OSP_ERROR_CODE_BAD_CHECKSUM)
+ return "bad-checksum";
+ if (error_code == OSP_ERROR_CODE_MESSAGE_TOO_LARGE)
+ return "message-too-large";
+ if (error_code == OSP_ERROR_CODE_PAYLOAD_LENGTH_INVALID)
+ return "payload-length-invalid";
+ if (error_code == OSP_ERROR_CODE_PAYLOAD_DATA_INVALID)
+ return "payload-data-invalid";
+ if (error_code == OSP_ERROR_CODE_DEVICE_NOT_READY)
+ return "device-not-ready";
+ if (error_code == OSP_ERROR_CODE_UNKNOWN_CHECKSUM_TYPE)
+ return "unknown-checksum-type";
+ if (error_code == OSP_ERROR_CODE_DEVICE_RESET)
+ return "device-reset";
+ if (error_code == OSP_ERROR_CODE_TOO_MANY_BUSES)
+ return "too-many-busses";
+ if (error_code == OSP_ERROR_CODE_OUT_OF_MEMORY)
+ return "out-of-memory";
+ if (error_code == OSP_ERROR_CODE_COMMAND_DATA_MISSING)
+ return "command-data-missing";
+ if (error_code == OSP_ERROR_CODE_INTERNAL_ERROR)
+ return "internal-error";
+ if (error_code == OSP_ERROR_CODE_COULD_NOT_DECRYPT)
+ return "could-not-decrypt";
+ if (error_code == OSP_ERROR_CODE_FIRMWARE_LAYOUT_INVALID)
+ return "firmware-layout-invalid";
+ if (error_code == OSP_ERROR_CODE_DATA_PACKET_INVALID_SIZE)
+ return "packet-invalid-size";
+ if (error_code == OSP_ERROR_CODE_HW_REVISION_INVALID)
+ return "hardware-revision-invalid";
+ if (error_code == OSP_ERROR_CODE_FLASH_MAP_INVALID)
+ return "flash-map-invalid";
+ if (error_code == OSP_ERROR_CODE_RESPONSE_DEFERRED)
+ return "response-deferred";
+ return NULL;
+}
diff --git a/src/sensors/spark/osp-enum.h b/src/sensors/spark/osp-enum.h
new file mode 100644
index 0000000..8772000
--- /dev/null
+++ b/src/sensors/spark/osp-enum.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef OSP_ENUM_H
+#define OSP_ENUM_H
+
+#include <glib.h>
+#include <gusb.h>
+
+G_BEGIN_DECLS
+
+#define OSP_USB_VID 0x2457
+#define OSP_USB_PID 0x4200
+
+typedef enum {
+ OSP_HEADER_FLAG_NONE = 0,
+ OSP_HEADER_FLAG_RESPONSE = 1 << 0, /* set by device */
+ OSP_HEADER_FLAG_ACK = 1 << 1, /* set by device */
+ OSP_HEADER_FLAG_ACK_REQUIRED = 1 << 2, /* set by host */
+ OSP_HEADER_FLAG_NACK = 1 << 3, /* set by device */
+ OSP_HEADER_FLAG_ERROR = 1 << 4, /* set by device */
+ OSP_HEADER_FLAG_DEPRECATED = 1 << 5, /* set by device */
+ /*< private >*/
+ OSP_HEADER_FLAG_LAST
+} OspHeaderFlag;
+
+typedef enum {
+ OSP_ERROR_CODE_SUCCESS = 0,
+ OSP_ERROR_CODE_UNSUPPORTED_PROTOCOL = 1,
+ OSP_ERROR_CODE_UNKNOWN_MESSAGE_TYPE = 2,
+ OSP_ERROR_CODE_BAD_CHECKSUM = 3,
+ OSP_ERROR_CODE_MESSAGE_TOO_LARGE = 4,
+ OSP_ERROR_CODE_PAYLOAD_LENGTH_INVALID = 5,
+ OSP_ERROR_CODE_PAYLOAD_DATA_INVALID = 6,
+ OSP_ERROR_CODE_DEVICE_NOT_READY = 7,
+ OSP_ERROR_CODE_UNKNOWN_CHECKSUM_TYPE = 8,
+ OSP_ERROR_CODE_DEVICE_RESET = 9,
+ OSP_ERROR_CODE_TOO_MANY_BUSES = 10,
+ OSP_ERROR_CODE_OUT_OF_MEMORY = 11,
+ OSP_ERROR_CODE_COMMAND_DATA_MISSING = 12,
+ OSP_ERROR_CODE_INTERNAL_ERROR = 13,
+ OSP_ERROR_CODE_COULD_NOT_DECRYPT = 100,
+ OSP_ERROR_CODE_FIRMWARE_LAYOUT_INVALID = 101,
+ OSP_ERROR_CODE_DATA_PACKET_INVALID_SIZE = 102,
+ OSP_ERROR_CODE_HW_REVISION_INVALID = 103,
+ OSP_ERROR_CODE_FLASH_MAP_INVALID = 104,
+ OSP_ERROR_CODE_RESPONSE_DEFERRED = 255,
+ /*< private >*/
+ OSP_ERROR_CODE_LAST,
+} OspErrorCode;
+
+typedef enum {
+ OSP_HEADER_CHECKSUM_KIND_NONE = 0,
+ OSP_HEADER_CHECKSUM_KIND_MD5 = 1,
+ /*< private >*/
+ OSP_HEADER_CHECKSUM_KIND_LAST
+} OspHeaderChecksumKind;
+
+typedef enum {
+ OSP_CMD_RESET = 0x00000000,
+ OSP_CMD_RESET_TO_DEFAULTS = 0x00000001,
+ OSP_CMD_GET_HARDWARE_VERSION = 0x00000080,
+ OSP_CMD_GET_FIRMWARE_VERSION = 0x00000090,
+ OSP_CMD_GET_SERIAL_NUMBER = 0x00000100,
+ OSP_CMD_GET_SERIAL_NUMBER_LENGTH = 0x00000101,
+ OSP_CMD_GET_DEVICE_ALIAS = 0x00000200,
+ OSP_CMD_GET_DEVICE_ALIAS_LENGTH = 0x00000201,
+ OSP_CMD_SET_DEVICE_ALIAS = 0x00000210,
+ OSP_CMD_GET_NUMBER_OF_AVAILABLE_USER_STRINGS = 0x00000300,
+ OSP_CMD_GET_USER_STRING_LENGTH = 0x00000301,
+ OSP_CMD_GET_USER_STRING = 0x00000302,
+ OSP_CMD_SET_USER_STRING = 0x00000310,
+ OSP_CMD_SET_LED = 0x00001010,
+ OSP_CMD_PUT_DEVICE_IN_REPROGRAMMING_MODE = 0x000fff00,
+ OSP_CMD_GET_AND_SEND_CORRECTED_SPECTRUM = 0x00101000,
+ OSP_CMD_GET_AND_SEND_RAW_SPECTRUM = 0x00101100,
+ OSP_CMD_GET_PARTIAL_SPECTRUM_MODE = 0x00102000,
+ OSP_CMD_SET_PARTIAL_SPECTRUM_MODE = 0x00102010,
+ OSP_CMD_GET_AND_SEND_PARTIAL_CORRECTED_SPECTRUM = 0x00102080,
+ OSP_CMD_SET_INTEGRATION_TIME = 0x00110010,
+ OSP_CMD_GET_PIXEL_BINNING_FACTOR = 0x00110280,
+ OSP_CMD_GET_MAXIMUM_BINNING_FACTOR = 0x00110281,
+ OSP_CMD_GET_DEFAULT_BINNING_FACTOR = 0x00110285,
+ OSP_CMD_SET_PIXEL_BINNING_FACTOR = 0x00110290,
+ OSP_CMD_SET_DEFAULT_BINNING_FACTOR = 0x00110295,
+ OSP_CMD_SET_TRIGGER_DELAY_MS = 0x00110510,
+ OSP_CMD_GET_SCANS_TO_AVERAGE = 0x00120000,
+ OSP_CMD_SET_SCANS_TO_AVERAGE = 0x00120010,
+ OSP_CMD_GET_BOXCAR_WIDTH = 0x00121000,
+ OSP_CMD_SET_BOXCAR_WIDTH = 0x00121010,
+ OSP_CMD_GET_WAVELENGTH_COEFFICIENT_COUNT = 0x00180100,
+ OSP_CMD_GET_WAVELENGTH_COEFFICIENT = 0x00180101,
+ OSP_CMD_SET_WAVELENGTH_COEFFICIENT = 0x00180111,
+ OSP_CMD_GET_NONLINEARITY_COEFFICIENT_COUNT = 0x00181100,
+ OSP_CMD_GET_NONLINEARITY_COEFFICIENT = 0x00181101,
+ OSP_CMD_SET_NONLINEARITY_COEFFICIENT = 0x00181111,
+ OSP_CMD_GET_IRRADIANCE_CALIBRATION = 0x00182001,
+ OSP_CMD_GET_IRRADIANCE_CALIBRATION_COUNT = 0x00182002,
+ OSP_CMD_GET_IRRADIANCE_CALIBRATION_COLLECTION_AREA = 0x00182003,
+ OSP_CMD_SET_IRRADIANCE_CALIBRATION = 0x00182011,
+ OSP_CMD_SET_IRRADIANCE_CALIBRATION_COLLECTION_AREA = 0x00182013,
+ OSP_CMD_GET_NUMBER_OF_STRAY_LIGHT_COEFFICIENTS = 0x00183100,
+ OSP_CMD_GET_STRAY_LIGHT_COEFFICIENT = 0x00183101,
+ OSP_CMD_SET_STRAY_LIGHT_COEFFICIENT = 0x00183111,
+ OSP_CMD_GET_HOT_PIXEL_INDICES = 0x00186000,
+ OSP_CMD_SET_HOT_PIXEL_INDICES = 0x00186010,
+ OSP_CMD_GET_BENCH_ID = 0x001b0000,
+ OSP_CMD_GET_BENCH_SERIAL_NUMBER = 0x001b0100,
+ OSP_CMD_GET_SLIT_WIDTH_MICRONS = 0x001b0200,
+ OSP_CMD_GET_FIBER_DIAMETER_MICRONS = 0x001b0300,
+ OSP_CMD_GET_GRATING = 0x001b0400,
+ OSP_CMD_GET_FILTER = 0x001b0500,
+ OSP_CMD_GET_COATING = 0x001b0600,
+ OSP_CMD_GET_GET_TEMPERATURE_SENSOR_COUNT = 0x00400000,
+ OSP_CMD_GET_READ_TEMPERATURE_SENSOR = 0x00400001,
+ OSP_CMD_GET_READ_ALL_TEMPERATURE_SENSORS = 0x00400002,
+ /*< private >*/
+ OSP_CMD_LAST
+} OspCmd;
+
+typedef struct {
+ guint16 start_bytes;
+ guint16 protocol_version;
+ guint16 flags;
+ guint16 error_code;
+ guint32 message_type;
+ guint32 regarding;
+ guint8 reserved[6];
+ guint8 checksum_type;
+ guint8 immediate_data_length;
+ guint8 immediate_data[16];
+ guint32 bytes_remaining;
+} OspProtocolHeader;
+
+typedef struct {
+ guint8 checksum[16];
+ guint32 end_bytes;
+} OspProtocolFooter;
+
+const gchar *osp_error_code_to_string (OspErrorCode error_code);
+const gchar *osp_cmd_to_string (OspCmd cmd);
+
+G_END_DECLS
+
+#endif
diff --git a/src/sensors/spark/osp-self-test.c b/src/sensors/spark/osp-self-test.c
new file mode 100644
index 0000000..0c48464
--- /dev/null
+++ b/src/sensors/spark/osp-self-test.c
@@ -0,0 +1,190 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <colord.h>
+
+#include "osp-device.h"
+#include "osp-enum.h"
+
+static GUsbDevice *
+osp_client_get_default (GError **error)
+{
+ g_autoptr(GUsbContext) usb_ctx = NULL;
+ g_autoptr(GUsbDevice) device = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* try to find the Spark device */
+ usb_ctx = g_usb_context_new (NULL);
+ if (usb_ctx == NULL) {
+ g_set_error (error,
+ G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_NOT_SUPPORTED,
+ "No device found; USB initialisation failed");
+ return NULL;
+ }
+ device = g_usb_context_find_by_vid_pid (usb_ctx,
+ OSP_USB_VID,
+ OSP_USB_PID,
+ error);
+ if (device == NULL)
+ return NULL;
+ g_debug ("Found Spark device %s",
+ g_usb_device_get_platform_id (device));
+ if (!osp_device_open (device, error))
+ return NULL;
+ return g_object_ref (device);
+}
+
+static void
+osp_test_protocol_func (void)
+{
+ gchar *tmp;
+ guint32 cmd_val;
+ guint8 cmd[4];
+ guint i;
+ guint j;
+ g_auto(GStrv) lines = NULL;
+ g_autofree gchar *data = NULL;
+ g_autoptr(GError) error = NULL;
+
+ if (!g_file_test ("protocol-dump.csv", G_FILE_TEST_EXISTS))
+ return;
+ g_file_get_contents ("protocol-dump.csv", &data, NULL, &error);
+ g_assert_no_error (error);
+
+ lines = g_strsplit (data, "\n", -1);
+ for (i = 0; lines[i] != NULL; i++) {
+ g_auto(GStrv) tokens = NULL;
+ tmp = g_strstr_len (lines[i], -1, "OUT txn");
+ if (tmp == NULL)
+ continue;
+ tokens = g_strsplit (tmp, " ", -1);
+ for (j = 0; j < 4; j++)
+ cmd[j] = g_ascii_strtoull (tokens[j + 9], NULL, 16);
+ cmd_val = *((guint32 *) cmd);
+ g_print ("%08x = %s\n", cmd_val, osp_cmd_to_string (cmd_val));
+ }
+}
+
+static void
+osp_test_reading_xyz_func (void)
+{
+ guint i, j;
+ g_autofree gchar *fwver = NULL;
+ g_autofree gchar *serial = NULL;
+ g_autoptr(CdSpectrum) sp = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GUsbDevice) device = NULL;
+
+ /* load the device */
+ device = osp_client_get_default (&error);
+ if (device == NULL && g_error_matches (error,
+ G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_NO_DEVICE)) {
+ g_debug ("skipping tests: %s", error->message);
+ return;
+ }
+ g_assert_no_error (error);
+ g_assert (device != NULL);
+
+ serial = osp_device_get_serial (device, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (serial, !=, NULL);
+
+ fwver = osp_device_get_fw_version (device, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (fwver, ==, "0.4");
+
+ sp = osp_device_take_spectrum (device, &error);
+ g_assert_no_error (error);
+ g_assert (sp != NULL);
+ for (i = 0; i < 1024; i+=5) {
+ g_print ("%.1fnm: ", cd_spectrum_get_wavelength (sp, i));
+ for (j = 0; j < cd_spectrum_get_value_raw (sp, i) * 1000.f; j++)
+ g_print ("*");
+ g_print ("\n");
+ }
+}
+
+static void
+osp_test_wavelength_cal_func (void)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GUsbDevice) device = NULL;
+ g_autofree gdouble *coefficients = NULL;
+ gdouble start;
+
+ /* load the device */
+ device = osp_client_get_default (&error);
+ if (device == NULL && g_error_matches (error,
+ G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_NO_DEVICE)) {
+ g_debug ("skipping tests: %s", error->message);
+ return;
+ }
+ g_assert_no_error (error);
+ g_assert (device != NULL);
+
+ /* get coefficients */
+ coefficients = osp_device_get_wavelength_cal (device, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (coefficients != NULL);
+ g_assert_cmpfloat (ABS (coefficients[0] - 0.37f), <, 0.1f);
+ g_assert_cmpfloat (ABS (coefficients[1] - 0.00f), <, 0.1f);
+ g_assert_cmpfloat (ABS (coefficients[2] - 0.00f), <, 0.1f);
+
+ /* get start */
+ start = osp_device_get_wavelength_start (device, &error);
+ g_assert_no_error (error);
+ g_assert (start > 0);
+ g_assert_cmpfloat (ABS (start - 355), <, 5);
+
+ /* get irradiance coefficients */
+ coefficients = osp_device_get_irradiance_cal (device, NULL, &error);
+ g_assert_error (error, OSP_DEVICE_ERROR, OSP_DEVICE_ERROR_NO_DATA);
+ g_clear_error (&error);
+
+ /* get nonlinearity coefficients */
+ coefficients = osp_device_get_nonlinearity_cal (device, NULL, &error);
+ g_assert_error (error, OSP_DEVICE_ERROR, OSP_DEVICE_ERROR_NO_DATA);
+ g_clear_error (&error);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ /* only critical and error are fatal */
+ g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
+
+ /* tests go here */
+ g_test_add_func ("/Spark/protocol", osp_test_protocol_func);
+ g_test_add_func ("/Spark/wavelength-cal", osp_test_wavelength_cal_func);
+ g_test_add_func ("/Spark/reading-xyz", osp_test_reading_xyz_func);
+
+ return g_test_run ();
+}
+