[gnome-color-manager: 52/80] huey: split up the heuy code and put into the per-sensor framework objects



commit 2b9c37a061cca6f472595a448a220a5a40414332
Author: Richard Hughes <richard hughsie com>
Date:   Sat Jul 17 23:15:21 2010 +0100

    huey: split up the heuy code and put into the per-sensor framework objects

 src/gcm-huey-example.c |  872 +----------------------------------------------
 src/gcm-sensor-huey.c  |  738 +++++++++++++++++++++++++++++++++++++++--
 src/gcm-sensor-huey.h  |   24 +-
 src/gcm-sensor.c       |   22 +-
 src/gcm-sensor.h       |   22 +-
 5 files changed, 762 insertions(+), 916 deletions(-)
---
diff --git a/src/gcm-huey-example.c b/src/gcm-huey-example.c
index bcf5586..d361b7a 100644
--- a/src/gcm-huey-example.c
+++ b/src/gcm-huey-example.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2009 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU Lesser General Public License Version 2.1
  *
@@ -21,790 +21,14 @@
 
 /*
  * I've reversed engineered this myself using USB Snoop on Windows XP,
- * in a KVM virtual machine. Please see ../docs/huey for data.
+ * in a KVM virtual machine. I've also used the verbose logging in argyll
+ * without actually looking at the code. Please see ../docs/huey for data.
  */
 
-#include <stdio.h>
-#include <sys/types.h>
-
-#include <libusb-1.0/libusb.h>
 #include <glib.h>
 #include <libcolor-glib.h>
 
-#define HUEY_VENDOR_ID			0x0971
-#define HUEY_PRODUCT_ID			0x2005
-#define HUEY_CONTROL_MESSAGE_TIMEOUT	50000 /* ms */
-#define HUEY_MAX_READ_RETRIES		5
-
-#define HUEY_RETVAL_SUCCESS		0x00
-#define HUEY_RETVAL_LOCKED		0xc0
-#define HUEY_RETVAL_UNKNOWN_5A		0x5a /* seen in profiling */
-#define HUEY_RETVAL_ERROR		0x80
-#define HUEY_RETVAL_UNKNOWN_81		0x81 /* seen once in init */
-#define HUEY_RETVAL_RETRY		0x90
-
-/* input:   00 00 00 00 3f 00 00 00
- * returns: 00 00 43 69 72 30 30 31  (or)
- *     "Cir001" --^^^^^^^^^^^^^^^^^ -- Cirrus Logic? Circuit1?...
- *          c0 00 4c 6f 63 6b 65 64
- *     "locked" --^^^^^^^^^^^^^^^^^
- *
- * Seems to get the currect status of the device.
- */
-#define HUEY_COMMAND_GET_STATUS		0x00
-
-/* 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
- *
- * only when profiling
- * has to be preceeded by HUEY_COMMAND_SENSOR_MEASURE_RGB (00 1e 00 27 00 15 03)
- */
-#define HUEY_COMMAND_READ_GREEN		0x02
-
-/* 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
- *
- * Only used when doing profiling
- * has to be preceeded by HUEY_COMMAND_SENSOR_MEASURE_RGB (00 01 00 01 00 01 7f)
- */
-#define HUEY_COMMAND_READ_BLUE	0x03
-
-/* input:   05 ?? 11 12 13 14 xx xx
- * returns: 00 05 00 00 00 00 00 00
- *              ^--- always the same no matter the input
- *
- * never used in profiling */
-#define HUEY_COMMAND_SET_VALUE		0x05
-
-/* input:   06 xx xx xx xx xx xx xx
- * returns: 00 06 11 12 13 14 00 00
- *    4 bytes ----^^^^^^^^^^^ (from HUEY_COMMAND_SET_VALUE)
- *
- * This is some sort of 32bit register on the device -- 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_COMMAND_GET_VALUE		0x06
-
-/* NEVER USED */
-#define HUEY_COMMAND_UNKNOWN_07		0x07
-
-/* (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
- */
-#define HUEY_COMMAND_REGISTER_READ	0x08
-
-/* input:   0e 47 72 4d 62 6b 65 64
- *  "GrMbked"--^^^^^^^^^^^^^^^^^^^^
- * returns: 00 0e 00 00 00 00 00 00
- */
-#define HUEY_COMMAND_UNLOCK		0x0e
-
-/* returns: all NULL all of the time */
-#define HUEY_COMMAND_UNKNOWN_0F		0x0f
-
-/* something to do with sampling */
-#define HUEY_COMMAND_UNKNOWN_10		0x10
-
-/* something to do with sampling (that needs a retry with code 5a) */
-#define HUEY_COMMAND_UNKNOWN_11		0x11
-
-/* something to do with sampling */
-#define HUEY_COMMAND_UNKNOWN_12		0x12
-
-/* returns: all NULL all of the time */
-#define HUEY_COMMAND_UNKNOWN_13		0x13
-
-/* returns: seems to be sent, but not requested */
-#define HUEY_COMMAND_UNKNOWN_15		0x15
-
-/* input:   16 00 01 00 01 00 01 00
- * returns: 00 16 00 00 00 00 00 00
- *
- * or:
- *                ||----||----||-- numbers steadily increase -- some kind of gain control?
- *    0 or 1 ---.-----.-----.    ,,-- only 00 7f or 03 in the profile-complete
- * input:   16 00 35 00 48 00 1d 03
- * returns: 00 16 00 0b d0 00 00 00
- *            data --^^^^^ ^^-- only ever 00 or 80
- *                    \-- for RGB(00,00,00) is odd	(00 16 02 20 f4 ee 07 00)
- *                            RGB(ff,ff,ff) is odd	(00 16 00 03 ac 80 00 00)
- *                            RGB(ff,00,00) is 06 ea	(00 16 00 06 ed 00 00 00)
- *                            RGB(00,ff,00) is 08 9b	(00 16 00 08 a0 80 00 00)
- *                            RGB(00,00,ff) is 55 5e	(00 16 00 55 73 80 00 00)
- *
- * only when profiling, and used with blue and green
- * THIS COMMAND TAKES A LONG TIME TO EXECUTE
- *
- * 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
- *
- * IT'S QUICKER TO READ WHITE THAN BLACK!! -- maybe amount of time to count a number of photons?
- */
-#define HUEY_COMMAND_SENSOR_MEASURE_RGB		0x16
-
-/* input:   21 09 00 02 00 00 08 00 (or)
- * returns: [never seems to return a value]
- *
- * only when profiling, and over and over -- some sort of poll? */
-#define HUEY_COMMAND_UNKNOWN_21		0x21
-
-/* 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?
- *                    \-- only ever 0 or 2 (only ever saw 2 once...)
- */
-#define HUEY_COMMAND_AMBIENT		0x17
-
-/* input:   18 00 f0 xx xx xx xx xx
- * returns: 00 18 f0 00 00 00 00 00
- *   led mask ----^^
- */
-#define HUEY_COMMAND_SET_LEDS		0x18
-
-/* returns: all NULL for NULL input: times out for f1 f2 f3 f4 f5 f6 f7 f8 */
-#define HUEY_COMMAND_UNKNOWN_19		0x19
-
-#define HUEY_AMBIENT_UNITS_TO_LUX	125.0f /* fudge factor */
-
-typedef struct {
-	gboolean		 connected;
-	libusb_device		*device;
-	libusb_device_handle	*handle;
-	GcmMat3x3		 calibration_matrix1;
-	GcmMat3x3		 calibration_matrix2;
-} GcmSensorHuey;
-
-/**
- * gcm_sensor_huey_find_device:
- **/
-static gboolean
-gcm_sensor_huey_find_device (GcmSensorHuey *huey, GError **error)
-{
-	struct libusb_device_descriptor desc;
-	libusb_device **devs = NULL;
-	libusb_device *dev;
-	gint retval;
-	gsize cnt;
-	guint i = 0;
-	gboolean ret = FALSE;
-
-	/* get device */
-	cnt = libusb_get_device_list (NULL, &devs);
-	if (cnt < 0) {
-		g_set_error (error, 1, 0, "failed to get device list: %s", libusb_strerror (cnt));
-		goto out;
-	}
-
-	/* find device */
-	for (i=0; i<cnt; i++) {
-		dev = devs[i];
-
-		/* get descriptor */
-		retval = libusb_get_device_descriptor (dev, &desc);
-		if (retval < 0) {
-			g_warning ("failed to get device descriptor for %02x:%02x, possibly faulty hardware",
-				   libusb_get_bus_number (dev), libusb_get_device_address (dev));
-			continue;
-		}
-
-		/* does match HUEY? */
-		if (desc.idVendor == HUEY_VENDOR_ID &&
-		    desc.idProduct == HUEY_PRODUCT_ID) {
-			g_debug ("got huey!");
-			ret = TRUE;
-			huey->device = libusb_ref_device (dev);
-			break;
-		}
-	}
-
-	/* not found */
-	if (!ret) {
-		g_set_error_literal (error, 1, 0, "no compatible hardware attached");
-		goto out;
-	}
-
-	/* open device */
-	retval = libusb_open (huey->device, &huey->handle);
-	if (retval < 0) {
-		ret = FALSE;
-		g_set_error (error, 1, 0, "failed to open device: %s", libusb_strerror (retval));
-		goto out;
-	}
-
-	/* set configuration and interface, the only values we've got in lsusb-vvv */
-	retval = libusb_set_configuration (huey->handle, 1);
-	if (retval < 0) {
-		ret = FALSE;
-		g_set_error (error, 1, 0, "failed to set configuration: %s", libusb_strerror (retval));
-		goto out;
-	}
-	retval = libusb_claim_interface (huey->handle, 0);
-	if (retval < 0) {
-		ret = FALSE;
-		g_set_error (error, 1, 0, "failed to claim interface: %s", libusb_strerror (retval));
-		goto out;
-	}
-out:
-	libusb_free_device_list (devs, 1);
-	return ret;
-}
-
-#define CONSOLE_RESET		0
-#define CONSOLE_BLACK 		30
-#define CONSOLE_RED		31
-#define CONSOLE_GREEN		32
-#define CONSOLE_YELLOW		33
-#define CONSOLE_BLUE		34
-#define CONSOLE_MAGENTA		35
-#define CONSOLE_CYAN		36
-#define CONSOLE_WHITE		37
-
-/**
- * gcm_sensor_huey_print_data:
- **/
-static void
-gcm_sensor_huey_print_data (const gchar *title, const guchar *data, gsize length)
-{
-	guint i;
-
-	if (g_strcmp0 (title, "request") == 0)
-		g_print ("%c[%dm", 0x1B, CONSOLE_RED);
-	if (g_strcmp0 (title, "reply") == 0)
-		g_print ("%c[%dm", 0x1B, CONSOLE_BLUE);
-	g_print ("%s\t", title);
-
-	for (i=0; i< length; i++)
-		g_print ("%02x [%c]\t", data[i], g_ascii_isprint (data[i]) ? data[i] : '?');
-		//g_print ("%02x,", data[i]);
-
-	g_print ("%c[%dm", 0x1B, CONSOLE_RESET);
-
-	g_print ("\n");
-}
-
-/**
- * gcm_sensor_huey_send_data:
- **/
-static gboolean
-gcm_sensor_huey_send_data (GcmSensorHuey *huey,
-			   const guchar *request, gsize request_len,
-			   guchar *reply, gsize reply_len,
-			   gsize *reply_read, GError **error)
-{
-	gint retval;
-	gboolean ret = FALSE;
-	guint i;
-
-	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);
-
-	/* show what we've got */
-	gcm_sensor_huey_print_data ("request", request, request_len);
-
-	/* do sync request */
-	retval = libusb_control_transfer (huey->handle,
-					  LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
-					  0x09, 0x0200, 0,
-					  (guchar *) request, request_len,
-					  HUEY_CONTROL_MESSAGE_TIMEOUT);
-	if (retval < 0) {
-		g_set_error (error, 1, 0, "failed to send request: %s", libusb_strerror (retval));
-		goto out;
-	}
-
-	/* some commands need to retry the read, unknown reason */
-	for (i=0; i<HUEY_MAX_READ_RETRIES; i++) {
-
-		/* get sync response, from bEndpointAddress */
-		retval = libusb_interrupt_transfer (huey->handle, 0x81,
-						    reply, (gint) reply_len, (gint*)reply_read,
-						    HUEY_CONTROL_MESSAGE_TIMEOUT);
-		if (retval < 0) {
-			g_set_error (error, 1, 0, "failed to get reply: %s", libusb_strerror (retval));
-			goto out;
-		}
-
-		/* show what we've got */
-		gcm_sensor_huey_print_data ("reply", reply, *reply_read);
-
-		/* the second byte seems to be the command again */
-		if (reply[1] != request[0]) {
-			g_set_error (error, 1, 0, "wrong command reply, got 0x%02x, expected 0x%02x", reply[1], request[0]);
-			goto out;
-		}
-
-		/* the first byte is status */
-		if (reply[0] == HUEY_RETVAL_SUCCESS) {
-			ret = TRUE;
-			break;
-		}
-
-		/* failure, the return buffer is set to "Locked" */
-		if (reply[0] == HUEY_RETVAL_LOCKED) {
-			g_set_error_literal (error, 1, 0, "the device is locked");
-			goto out;
-		}
-
-		/* failure, the return buffer is set to "NoCmd" */
-		if (reply[0] == HUEY_RETVAL_ERROR) {
-			g_set_error (error, 1, 0, "failed to issue command: %s", &reply[2]);
-			goto out;
-		}
-
-		/* we ignore retry */
-		if (reply[0] != HUEY_RETVAL_RETRY) {
-			g_set_error (error, 1, 0, "return value unknown: 0x%02x", reply[0]);
-			goto out;
-		}
-	}
-
-	/* no success */
-	if (!ret) {
-		g_set_error (error, 1, 0, "gave up retrying after %i reads", HUEY_MAX_READ_RETRIES);
-		goto out;
-	}
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_send_command:
- **/
-static gboolean
-gcm_sensor_huey_send_command (GcmSensorHuey *huey, guchar command, const guchar *payload, GError **error)
-{
-	guchar request[8];
-	guchar reply[8];
-	gboolean ret;
-	gsize reply_read;
-	guint i;
-
-	/* first byte seems to be a command */
-	request[0] = command;
-	for (i=1; i<8; i++)
-		request[i] = payload[i-1];
-
-	/* show what we've got */
-	g_print ("cmd 0x%02x\n", command);
-
-	ret = gcm_sensor_huey_send_data (huey, request, 8, reply, 8, &reply_read, error);
-	if (!ret)
-		goto out;
-
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_send_unlock:
- **/
-static gboolean
-gcm_sensor_huey_send_unlock (GcmSensorHuey *huey, GError **error)
-{
-	guchar request[8];
-	guchar reply[8];
-	gboolean ret;
-	gsize reply_read;
-
-	request[0] = HUEY_COMMAND_UNLOCK;
-	request[1] = 'G';
-	request[2] = 'r';
-	request[3] = 'M';
-	request[4] = 'b';
-	request[5] = 'k'; // <- perhaps junk, need to test next time locked */
-	request[6] = 'e'; // <-         "" */
-	request[7] = 'd'; // <-         "" */
-
-	/* GrMbked == I have no idea, neither does google */
-	ret = gcm_sensor_huey_send_data (huey, request, 8, reply, 8, &reply_read, error);
-	if (!ret)
-		goto out;
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_send_leds:
- **/
-static gboolean
-gcm_sensor_huey_send_leds (GcmSensorHuey *huey, guchar mask, GError **error)
-{
-	guchar payload[] = { 0x00, ~mask, 0x00, 0x00, 0x00, 0x00, 0x00 };
-	return gcm_sensor_huey_send_command (huey, HUEY_COMMAND_SET_LEDS, payload, error);
-}
-
-/**
- * gcm_sensor_huey_get_ambient:
- **/
-static gboolean
-gcm_sensor_huey_get_ambient (GcmSensorHuey *huey, gdouble *value, GError **error)
-{
-	const guchar request[] = { HUEY_COMMAND_AMBIENT, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-	guchar reply[8];
-	gboolean ret;
-	gsize reply_read;
-
-	/* hit hardware */
-	ret = gcm_sensor_huey_send_data (huey, request, 8, reply, 8, &reply_read, error);
-	if (!ret)
-		goto out;
-
-	/* parse the value */
-	g_debug ("%i, %i", reply[5], reply[5]);
-	*value = (gdouble) (reply[5] * 0xff + reply[6]) / HUEY_AMBIENT_UNITS_TO_LUX;
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_data_to_float:
- **/
-static gfloat
-gcm_sensor_huey_data_to_float (guint8 *value)
-{
-	guint32 big;
-	gfloat retval;
-
-	/* first, convert the guchar data into one long int */
-	big = (value[0] << 24) + (value[1] << 16) + (value[2] << 8) + (value[3] << 0);
-
-	/* then convert it to a float */
-	*((guint32 *)(&retval)) = big;
-	return retval;
-}
-
-/**
- * gcm_sensor_huey_read_register_byte:
- **/
-static gboolean
-gcm_sensor_huey_read_register_byte (GcmSensorHuey *huey, guint8 addr, guint8 *value, GError **error)
-{
-	guchar request[] = { HUEY_COMMAND_REGISTER_READ, 0xff, 0x00, 0x10, 0x3c, 0x06, 0x00, 0x00 };
-	guchar reply[8];
-	gboolean ret;
-	gsize reply_read;
-
-	/* hit hardware */
-	request[1] = addr;
-	ret = gcm_sensor_huey_send_data (huey, request, 8, reply, 8, &reply_read, error);
-	if (!ret)
-		goto out;
-
-	/* this seems like the only byte of data that's useful -- it would be
-	 * good to be able to get more than one byte of data at a time... */
-	*value = reply[3];
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_read_register_string:
- **/
-static gboolean
-gcm_sensor_huey_read_register_string (GcmSensorHuey *huey, guint8 addr, gchar *value, gsize len, GError **error)
-{
-	guint8 i;
-	gboolean ret = TRUE;
-
-	/* get each byte of the string */
-	for (i=0; i<len; i++) {
-		ret = gcm_sensor_huey_read_register_byte (huey, addr+i, (guint8*) &value[i], error);
-		if (!ret)
-			goto out;
-	}
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_read_register_word:
- **/
-static gboolean
-gcm_sensor_huey_read_register_word (GcmSensorHuey *huey, guint8 addr, guint32 *value, GError **error)
-{
-	guint8 i;
-	guint8 tmp[4];
-	gboolean ret = TRUE;
-
-	/* get each byte of the 32 bit number */
-	for (i=0; i<4; i++) {
-		ret = gcm_sensor_huey_read_register_byte (huey, addr+i, tmp+i, error);
-		if (!ret)
-			goto out;
-	}
-
-	/* convert to a 32 bit integer */
-	*value = (tmp[0] << 24) + (tmp[1] << 16) + (tmp[2] << 8) + (tmp[3] << 0);
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_read_register_float:
- **/
-static gboolean
-gcm_sensor_huey_read_register_float (GcmSensorHuey *huey, guint8 addr, gfloat *value, GError **error)
-{
-	gboolean ret;
-	guint32 tmp;
-
-	/* first read in 32 bit integer */
-	ret = gcm_sensor_huey_read_register_word (huey, addr, &tmp, error);
-	if (!ret)
-		goto out;
-
-	/* convert to float */
-	*((guint32 *)value) = tmp;
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_read_register_matrix:
- **/
-static gboolean
-gcm_sensor_huey_read_register_matrix (GcmSensorHuey *huey, guint8 addr, GcmMat3x3 *value, GError **error)
-{
-	gboolean ret = TRUE;
-	guint i;
-	gfloat tmp;
-	gdouble *matrix_data;
-
-	/* get this to avoid casting */
-	matrix_data = gcm_mat33_get_data (value);
-
-	/* read in 3d matrix */
-	for (i=0; i<9; i++) {
-		ret = gcm_sensor_huey_read_register_float (huey, addr + (i*4), &tmp, error);
-		if (!ret)
-			goto out;
-
-		/* save in matrix */
-		*(matrix_data+i) = tmp;
-	}
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_read_registers:
- **/
-static gboolean
-gcm_sensor_huey_read_registers (GcmSensorHuey *huey, GError **error)
-{
-	gboolean ret;
-	guint8 i, j;
-	guint len = 0xf0;
-	guint8 data[len];
-	gchar unlock[5];
-
-if (1) {
-	/* get unlock string */
-	ret = gcm_sensor_huey_read_register_string (huey, 0x7a, unlock, 5, error);
-	if (!ret)
-		goto out;
-	g_print ("Unlock string: %s\n", unlock);
-
-
-	/* get matrix */
-	gcm_mat33_clear (&huey->calibration_matrix1);
-	ret = gcm_sensor_huey_read_register_matrix (huey, 0x04, &huey->calibration_matrix1, error);
-	if (!ret)
-		goto out;
-	g_print ("device matrix1: %s\n", gcm_mat33_to_string (&huey->calibration_matrix1));
-
-	/* get another matrix, although this one is worse... */
-	gcm_mat33_clear (&huey->calibration_matrix2);
-	ret = gcm_sensor_huey_read_register_matrix (huey, 0x36, &huey->calibration_matrix2, error);
-	if (!ret)
-		goto out;
-	g_print ("device matrix2: %s\n", gcm_mat33_to_string (&huey->calibration_matrix2));
-}
-
-	goto out;
-
-	/* We read from 0x04 to 0x72 at startup */
-	for (i=2; i<=len; i++) {
-		ret = gcm_sensor_huey_read_register_byte (huey, i, &data[i], error);
-		if (!ret)
-			goto out;
-	}
-
-	/* try to find patterns */
-	for (i=2; i<len; i+=4) {
-		g_print ("0x%02x\t", i);
-		for (j=0; j<4; j++)
-			g_print ("%c ", g_ascii_isprint (data[i+j]) ? data[i+j] : '?');
-		g_print ("\n");
-	}
-	g_print ("\n");
-
-	for (i=2; i<len; i+=4) {
-		g_print ("0x%02x\t", i);
-		for (j=0; j<4; j++)
-			g_print ("%02i ", data[i+j]);
-		g_print ("\n");
-	}
-	g_print ("\n");
-
-	for (i=2; i<len; i+=4) {
-		g_print ("0x%02x\t", i);
-		g_print ("%.4f ", gcm_sensor_huey_data_to_float (&data[i]));
-		g_print ("\n");
-	}
-	g_print ("\n");
-
-
-
-out:
-	return ret;
-}
-
-/**
- * gcm_sensor_huey_get_color_for_threshold:
- **/
-static gboolean
-gcm_sensor_huey_get_color_for_threshold (GcmSensorHuey *huey, GcmColorRgbInt *threshold, GcmColorRgb *values, GError **error)
-{
-	guchar request[] = { HUEY_COMMAND_SENSOR_MEASURE_RGB, 0x00, threshold->red, 0x00, threshold->green, 0x00, threshold->blue, 0x00 };
-	guchar reply[8];
-	gboolean ret;
-	gsize reply_read;
-
-	/* measure, and get red */
-	ret = gcm_sensor_huey_send_data (huey, request, 8, reply, 8, &reply_read, error);
-	if (!ret)
-		goto out;
-
-	/* get value */
-	values->red = 1.0f / ((reply[3] * 0xff) + reply[4]);
-
-	/* get green */
-	request[0] = HUEY_COMMAND_READ_GREEN;
-	ret = gcm_sensor_huey_send_data (huey, request, 8, reply, 8, &reply_read, error);
-	if (!ret)
-		goto out;
-
-	/* get value */
-	values->green = 1.0f / ((reply[3] * 0xff) + reply[4]);
-
-	/* get blue */
-	request[0] = HUEY_COMMAND_READ_BLUE;
-	ret = gcm_sensor_huey_send_data (huey, request, 8, reply, 8, &reply_read, error);
-	if (!ret)
-		goto out;
-
-	/* get value */
-	values->blue = 1.0f / ((reply[3] * 0xff) + reply[4]);
-out:
-	return ret;
-}
-
-/* this is a random number chosen to find the best accuracy whilst
- * maintaining a fast read. We scale each RGB value seporately. */
-#define HUEY_PRECISION_TIME_VALUE		0.15f
-
-/* picked out of thin air, just to try to match reality */
-#define HUEY_XYZ_POST_MULTIPLY_SCALE_FACTOR	6880.0f
-
-/**
- * gcm_sensor_huey_get_color:
- **/
-static gboolean
-gcm_sensor_huey_get_color (GcmSensorHuey *huey, GcmColorXYZ *values, GError **error)
-{
-	gboolean ret;
-	gdouble value;
-	GcmColorRgb native;
-	GcmColorRgbInt multiplier;
-	GcmVec3 *input = (GcmVec3 *) &native;
-	GcmVec3 *output = (GcmVec3 *) values;
-
-	/* set this to one value for a quick approximate value */
-	multiplier.red = 1;
-	multiplier.green = 1;
-	multiplier.blue = 1;
-	ret = gcm_sensor_huey_get_color_for_threshold (huey, &multiplier, &native, error);
-	if (!ret)
-		goto out;
-	g_debug ("initial values: red=%0.4lf, green=%0.4lf, blue=%0.4lf", native.red, native.green, native.blue);
-
-	/* compromise between the amount of time and the precision */
-	value = HUEY_PRECISION_TIME_VALUE;
-	if (native.red < value)
-		multiplier.red = (gfloat)value / native.red;
-	if (native.green < value)
-		multiplier.green = (gfloat)value / native.green;
-	if (native.blue < value)
-		multiplier.blue = (gfloat)value / native.blue;
-	g_debug ("using multiplier factor: red=%i, green=%i, blue=%i", multiplier.red, multiplier.green, multiplier.blue);
-	ret = gcm_sensor_huey_get_color_for_threshold (huey, &multiplier, &native, error);
-	if (!ret)
-		goto out;
-	g_debug ("prescaled values: red=%0.4lf, green=%0.4lf, blue=%0.4lf", native.red, native.green, native.blue);
-	native.red = native.red * (gdouble)multiplier.red;
-	native.green = native.green * (gdouble)multiplier.green;
-	native.blue = native.blue * (gdouble)multiplier.blue;
-	g_debug ("scaled values: red=%0.4lf, green=%0.4lf, blue=%0.4lf", native.red, native.green, native.blue);
-
-	g_print ("PRE MULTIPLY: %s\n", gcm_vec3_to_string (input));
-
-	/* it would be rediculous for the device to emit RGB, it would be completely arbitrary --
-	 * we assume the matrix of data is designed to convert to LAB or XYZ */
-	gcm_mat33_vector_multiply (&huey->calibration_matrix1, input, output);
-
-	/* scale correct */
-	gcm_vec3_scalar_multiply (output, HUEY_XYZ_POST_MULTIPLY_SCALE_FACTOR, output);
-
-	g_print ("POST MULTIPLY: %s\n", gcm_vec3_to_string (output));
-out:
-	return ret;
-}
+#include "gcm-sensor-huey.h"
 
 /**
  * main:
@@ -812,117 +36,59 @@ out:
 int
 main (void)
 {
-	gint retval;
-	gdouble value;
 	gboolean ret;
-	GcmSensorHuey *huey;
 	GError *error = NULL;
+	GcmSensor *sensor;
+	gdouble value;
+	GcmColorXYZ values;
 
-	huey = g_new0 (GcmSensorHuey, 1);
-
-	/* connect */
-	retval = libusb_init (NULL);
-	if (retval < 0) {
-		g_warning ("failed to init libusb: %s", libusb_strerror (retval));
-		goto out;
-	}
-	huey->connected = TRUE;
-
-	/* find device */
-	ret = gcm_sensor_huey_find_device (huey, &error);
-	if (!ret) {
-		g_warning ("failed to find sensor: %s", error->message);
-		g_error_free (error);
-		goto out;
-	}
+	g_type_init ();
 
-if(0){
-	/* unlock */
-	ret = gcm_sensor_huey_send_unlock (huey, &error);
+	/* start sensor */
+	sensor = gcm_sensor_huey_new ();
+	ret = gcm_sensor_startup (sensor, &error);
 	if (!ret) {
-		g_warning ("failed to unlock: %s", error->message);
+		g_warning ("failed to start sensor: %s", error->message);
 		g_error_free (error);
 		goto out;
 	}
-}
-
-if (1) {
-	/* this is done by the windows driver */
-	ret = gcm_sensor_huey_read_registers (huey, &error);
-	if (!ret) {
-		g_warning ("failed to do read register: %s", error->message);
-		g_error_free (error);
-		goto out;
-	}
-}
 
 	/* set LEDs */
-	ret = gcm_sensor_huey_send_leds (huey, 0x0f, &error);
+	ret = gcm_sensor_set_leds (sensor, 0x0f, &error);
 	if (!ret) {
 		g_warning ("failed to send leds: %s", error->message);
 		g_error_free (error);
 		goto out;
 	}
 
-if (1) {
 	/* get ambient */
-	ret = gcm_sensor_huey_get_ambient (huey, &value, &error);
+	ret = gcm_sensor_get_ambient (sensor, &value, &error);
 	if (!ret) {
 		g_warning ("failed to get ambient: %s", error->message);
 		g_error_free (error);
 		goto out;
 	}
 	g_debug ("ambient = %.1lf Lux", value);
-}
-
-if (0) {
-	guchar payload[] = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
-	g_warning ("moo");
-
-	/* get default value when device is plugged */
-	gcm_sensor_huey_send_command (huey, HUEY_COMMAND_GET_VALUE, payload, &error);
 
-	gcm_sensor_huey_send_command (huey, HUEY_COMMAND_SET_VALUE, payload, &error);
-
-	payload[0] = 0x01;
-	payload[1] = 0x02;
-	payload[2] = 0x03;
-	payload[3] = 0x04;
-	payload[4] = 0x05;
-	payload[5] = 0x06;
-	payload[6] = 0x07;
-
-	gcm_sensor_huey_send_command (huey, HUEY_COMMAND_GET_VALUE, payload, &error);
-}
-
-/* try to get color value */
-if (0) {
-
-	GcmColorXYZ values;
-	ret = gcm_sensor_huey_get_color (huey, &values, &error);
+	/* sample color */
+	ret = gcm_sensor_sample (sensor, &values, &error);
 	if (!ret) {
 		g_warning ("failed to measure: %s", error->message);
 		g_error_free (error);
 		goto out;
 	}
 	g_debug ("X=%0.4lf, Y=%0.4lf, Z=%0.4lf", values.X, values.Y, values.Z);
-}
+
 	/* set LEDs */
-	ret = gcm_sensor_huey_send_leds (huey, 0x00, &error);
+	ret = gcm_sensor_set_leds (sensor, 0x00, &error);
 	if (!ret) {
 		g_warning ("failed to send leds: %s", error->message);
 		g_error_free (error);
 		goto out;
 	}
 
-	/* close device */
-	libusb_close (huey->handle);
 out:
-	if (huey->device != NULL)
-		libusb_unref_device (huey->device);
-	if (huey->connected)
-		libusb_exit (NULL);
-	g_free (huey);
+	g_object_unref (sensor);
 	return 0;
 }
 
diff --git a/src/gcm-sensor-huey.c b/src/gcm-sensor-huey.c
index 4988b23..9a43943 100644
--- a/src/gcm-sensor-huey.c
+++ b/src/gcm-sensor-huey.c
@@ -2,21 +2,21 @@
  *
  * Copyright (C) 2010 Richard Hughes <richard hughsie com>
  *
- * Licensed under the GNU General Public License Version 2
+ * Licensed under the GNU Lesser General Public License Version 2.1
  *
- * 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 library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
+ * This library 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.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
 /**
@@ -29,6 +29,7 @@
 #include "config.h"
 
 #include <glib-object.h>
+#include <libusb-1.0/libusb.h>
 
 #include "gcm-sensor-huey.h"
 
@@ -43,49 +44,721 @@ static void     gcm_sensor_huey_finalize	(GObject     *object);
  **/
 struct _GcmSensorHueyPrivate
 {
-	guint				 dave;
+	gboolean			 connected;
+	libusb_device			*device;
+	libusb_device_handle		*handle;
+	GcmMat3x3			 calibration_matrix1;
+	GcmMat3x3			 calibration_matrix2;
+	gchar				 unlock_string[5];
 };
 
 G_DEFINE_TYPE (GcmSensorHuey, gcm_sensor_huey, GCM_TYPE_SENSOR)
 
+#define HUEY_VENDOR_ID			0x0971
+#define HUEY_PRODUCT_ID			0x2005
+#define HUEY_CONTROL_MESSAGE_TIMEOUT	50000 /* ms */
+#define HUEY_MAX_READ_RETRIES		5
+
+#define HUEY_RETVAL_SUCCESS		0x00
+#define HUEY_RETVAL_LOCKED		0xc0
+#define HUEY_RETVAL_UNKNOWN_5A		0x5a /* seen in profiling */
+#define HUEY_RETVAL_ERROR		0x80
+#define HUEY_RETVAL_UNKNOWN_81		0x81 /* seen once in init */
+#define HUEY_RETVAL_RETRY		0x90
+
+/* input:   00 00 00 00 3f 00 00 00
+ * returns: 00 00 43 69 72 30 30 31  (or)
+ *     "Cir001" --^^^^^^^^^^^^^^^^^ -- Cirrus Logic? Circuit1?...
+ *          c0 00 4c 6f 63 6b 65 64
+ *     "locked" --^^^^^^^^^^^^^^^^^
+ *
+ * Seems to get the currect status of the device.
+ */
+#define HUEY_COMMAND_GET_STATUS		0x00
+
+/* 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
+ *
+ * only when profiling
+ * has to be preceeded by HUEY_COMMAND_SENSOR_MEASURE_RGB (00 1e 00 27 00 15 03)
+ */
+#define HUEY_COMMAND_READ_GREEN		0x02
+
+/* 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
+ *
+ * Only used when doing profiling
+ * has to be preceeded by HUEY_COMMAND_SENSOR_MEASURE_RGB (00 01 00 01 00 01 7f)
+ */
+#define HUEY_COMMAND_READ_BLUE	0x03
+
+/* input:   05 ?? 11 12 13 14 xx xx
+ * returns: 00 05 00 00 00 00 00 00
+ *              ^--- always the same no matter the input
+ *
+ * never used in profiling */
+#define HUEY_COMMAND_SET_VALUE		0x05
+
+/* input:   06 xx xx xx xx xx xx xx
+ * returns: 00 06 11 12 13 14 00 00
+ *    4 bytes ----^^^^^^^^^^^ (from HUEY_COMMAND_SET_VALUE)
+ *
+ * This is some sort of 32bit register on the device -- 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_COMMAND_GET_VALUE		0x06
+
+/* NEVER USED */
+#define HUEY_COMMAND_UNKNOWN_07		0x07
+
+/* (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
+ */
+#define HUEY_COMMAND_REGISTER_READ	0x08
+
+/* input:   0e 47 72 4d 62 6b 65 64
+ *  "GrMbked"--^^^^^^^^^^^^^^^^^^^^
+ * returns: 00 0e 00 00 00 00 00 00
+ */
+#define HUEY_COMMAND_UNLOCK		0x0e
+
+/* returns: all NULL all of the time */
+#define HUEY_COMMAND_UNKNOWN_0F		0x0f
+
+/* something to do with sampling */
+#define HUEY_COMMAND_UNKNOWN_10		0x10
+
+/* something to do with sampling (that needs a retry with code 5a) */
+#define HUEY_COMMAND_UNKNOWN_11		0x11
+
+/* something to do with sampling */
+#define HUEY_COMMAND_UNKNOWN_12		0x12
+
+/* returns: all NULL all of the time */
+#define HUEY_COMMAND_UNKNOWN_13		0x13
+
+/* returns: seems to be sent, but not requested */
+#define HUEY_COMMAND_UNKNOWN_15		0x15
+
+/* input:   16 00 01 00 01 00 01 00
+ * returns: 00 16 00 00 00 00 00 00
+ *
+ * or:
+ *                ||----||----||-- numbers steadily increase -- some kind of gain control?
+ *    0 or 1 ---.-----.-----.    ,,-- only 00 7f or 03 in the profile-complete
+ * input:   16 00 35 00 48 00 1d 03
+ * returns: 00 16 00 0b d0 00 00 00
+ *            data --^^^^^ ^^-- only ever 00 or 80
+ *                    \-- for RGB(00,00,00) is odd	(00 16 02 20 f4 ee 07 00)
+ *                            RGB(ff,ff,ff) is odd	(00 16 00 03 ac 80 00 00)
+ *                            RGB(ff,00,00) is 06 ea	(00 16 00 06 ed 00 00 00)
+ *                            RGB(00,ff,00) is 08 9b	(00 16 00 08 a0 80 00 00)
+ *                            RGB(00,00,ff) is 55 5e	(00 16 00 55 73 80 00 00)
+ *
+ * only when profiling, and used with blue and green
+ * THIS COMMAND TAKES A LONG TIME TO EXECUTE
+ *
+ * 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
+ *
+ * IT'S QUICKER TO READ WHITE THAN BLACK!! -- maybe amount of time to count a number of photons?
+ */
+#define HUEY_COMMAND_SENSOR_MEASURE_RGB		0x16
+
+/* input:   21 09 00 02 00 00 08 00 (or)
+ * returns: [never seems to return a value]
+ *
+ * only when profiling, and over and over -- some sort of poll? */
+#define HUEY_COMMAND_UNKNOWN_21		0x21
+
+/* 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?
+ *                    \-- only ever 0 or 2 (only ever saw 2 once...)
+ */
+#define HUEY_COMMAND_AMBIENT		0x17
+
+/* input:   18 00 f0 xx xx xx xx xx
+ * returns: 00 18 f0 00 00 00 00 00
+ *   led mask ----^^
+ */
+#define HUEY_COMMAND_SET_LEDS		0x18
+
+/* returns: all NULL for NULL input: times out for f1 f2 f3 f4 f5 f6 f7 f8 */
+#define HUEY_COMMAND_UNKNOWN_19		0x19
+
+/* fudge factor */
+#define HUEY_AMBIENT_UNITS_TO_LUX	125.0f
+
+/* this is a random number chosen to find the best accuracy whilst
+ * maintaining a fast read. We scale each RGB value seporately. */
+#define HUEY_PRECISION_TIME_VALUE		0.15f
+
+/* picked out of thin air, just to try to match reality */
+#define HUEY_XYZ_POST_MULTIPLY_SCALE_FACTOR	6880.0f
+
+
+#define CONSOLE_RESET		0
+#define CONSOLE_BLACK 		30
+#define CONSOLE_RED		31
+#define CONSOLE_GREEN		32
+#define CONSOLE_YELLOW		33
+#define CONSOLE_BLUE		34
+#define CONSOLE_MAGENTA		35
+#define CONSOLE_CYAN		36
+#define CONSOLE_WHITE		37
+
+/**
+ * gcm_sensor_huey_print_data:
+ **/
+static void
+gcm_sensor_huey_print_data (const gchar *title, const guchar *data, gsize length)
+{
+	guint i;
+
+	if (g_strcmp0 (title, "request") == 0)
+		g_print ("%c[%dm", 0x1B, CONSOLE_RED);
+	if (g_strcmp0 (title, "reply") == 0)
+		g_print ("%c[%dm", 0x1B, CONSOLE_BLUE);
+	g_print ("%s\t", title);
+
+	for (i=0; i< length; i++)
+		g_print ("%02x [%c]\t", data[i], g_ascii_isprint (data[i]) ? data[i] : '?');
+		//g_print ("%02x,", data[i]);
+
+	g_print ("%c[%dm\n", 0x1B, CONSOLE_RESET);
+}
+
+/**
+ * gcm_sensor_huey_send_data:
+ **/
+static gboolean
+gcm_sensor_huey_send_data (GcmSensorHuey *sensor_huey,
+			   const guchar *request, gsize request_len,
+			   guchar *reply, gsize reply_len,
+			   gsize *reply_read, GError **error)
+{
+	gint retval;
+	gboolean ret = FALSE;
+	guint i;
+
+	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);
+
+	/* show what we've got */
+	gcm_sensor_huey_print_data ("request", request, request_len);
+
+	/* do sync request */
+	retval = libusb_control_transfer (sensor_huey->priv->handle,
+					  LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+					  0x09, 0x0200, 0,
+					  (guchar *) request, request_len,
+					  HUEY_CONTROL_MESSAGE_TIMEOUT);
+	if (retval < 0) {
+		g_set_error (error, 1, 0, "failed to send request: %s", libusb_strerror (retval));
+		goto out;
+	}
+
+	/* some commands need to retry the read, unknown reason */
+	for (i=0; i<HUEY_MAX_READ_RETRIES; i++) {
+
+		/* get sync response, from bEndpointAddress */
+		retval = libusb_interrupt_transfer (sensor_huey->priv->handle, 0x81,
+						    reply, (gint) reply_len, (gint*)reply_read,
+						    HUEY_CONTROL_MESSAGE_TIMEOUT);
+		if (retval < 0) {
+			g_set_error (error, 1, 0, "failed to get reply: %s", libusb_strerror (retval));
+			goto out;
+		}
+
+		/* show what we've got */
+		gcm_sensor_huey_print_data ("reply", reply, *reply_read);
+
+		/* the second byte seems to be the command again */
+		if (reply[1] != request[0]) {
+			g_set_error (error, 1, 0, "wrong command reply, got 0x%02x, expected 0x%02x", reply[1], request[0]);
+			goto out;
+		}
+
+		/* the first byte is status */
+		if (reply[0] == HUEY_RETVAL_SUCCESS) {
+			ret = TRUE;
+			break;
+		}
+
+		/* failure, the return buffer is set to "Locked" */
+		if (reply[0] == HUEY_RETVAL_LOCKED) {
+			g_set_error_literal (error, 1, 0, "the device is locked");
+			goto out;
+		}
+
+		/* failure, the return buffer is set to "NoCmd" */
+		if (reply[0] == HUEY_RETVAL_ERROR) {
+			g_set_error (error, 1, 0, "failed to issue command: %s", &reply[2]);
+			goto out;
+		}
+
+		/* we ignore retry */
+		if (reply[0] != HUEY_RETVAL_RETRY) {
+			g_set_error (error, 1, 0, "return value unknown: 0x%02x", reply[0]);
+			goto out;
+		}
+	}
+
+	/* no success */
+	if (!ret) {
+		g_set_error (error, 1, 0, "gave up retrying after %i reads", HUEY_MAX_READ_RETRIES);
+		goto out;
+	}
+out:
+	return ret;
+}
+
+/**
+ * gcm_sensor_huey_read_register_byte:
+ **/
+static gboolean
+gcm_sensor_huey_read_register_byte (GcmSensorHuey *sensor_huey, guint8 addr, guint8 *value, GError **error)
+{
+	guchar request[] = { HUEY_COMMAND_REGISTER_READ, 0xff, 0x00, 0x10, 0x3c, 0x06, 0x00, 0x00 };
+	guchar reply[8];
+	gboolean ret;
+	gsize reply_read;
+
+	/* hit hardware */
+	request[1] = addr;
+	ret = gcm_sensor_huey_send_data (sensor_huey, request, 8, reply, 8, &reply_read, error);
+	if (!ret)
+		goto out;
+
+	/* this seems like the only byte of data that's useful -- it would be
+	 * good to be able to get more than one byte of data at a time... */
+	*value = reply[3];
+out:
+	return ret;
+}
+
+/**
+ * gcm_sensor_huey_read_register_string:
+ **/
+static gboolean
+gcm_sensor_huey_read_register_string (GcmSensorHuey *huey, guint8 addr, gchar *value, gsize len, GError **error)
+{
+	guint8 i;
+	gboolean ret = TRUE;
+
+	/* get each byte of the string */
+	for (i=0; i<len; i++) {
+		ret = gcm_sensor_huey_read_register_byte (huey, addr+i, (guint8*) &value[i], error);
+		if (!ret)
+			goto out;
+	}
+out:
+	return ret;
+}
+
+/**
+ * gcm_sensor_huey_read_register_word:
+ **/
+static gboolean
+gcm_sensor_huey_read_register_word (GcmSensorHuey *huey, guint8 addr, guint32 *value, GError **error)
+{
+	guint8 i;
+	guint8 tmp[4];
+	gboolean ret = TRUE;
+
+	/* get each byte of the 32 bit number */
+	for (i=0; i<4; i++) {
+		ret = gcm_sensor_huey_read_register_byte (huey, addr+i, tmp+i, error);
+		if (!ret)
+			goto out;
+	}
+
+	/* convert to a 32 bit integer */
+	*value = (tmp[0] << 24) + (tmp[1] << 16) + (tmp[2] << 8) + (tmp[3] << 0);
+out:
+	return ret;
+}
+
+/**
+ * gcm_sensor_huey_read_register_float:
+ **/
+static gboolean
+gcm_sensor_huey_read_register_float (GcmSensorHuey *huey, guint8 addr, gfloat *value, GError **error)
+{
+	gboolean ret;
+	guint32 tmp;
+
+	/* first read in 32 bit integer */
+	ret = gcm_sensor_huey_read_register_word (huey, addr, &tmp, error);
+	if (!ret)
+		goto out;
+
+	/* convert to float */
+	*((guint32 *)value) = tmp;
+out:
+	return ret;
+}
+
+/**
+ * gcm_sensor_huey_read_register_matrix:
+ **/
+static gboolean
+gcm_sensor_huey_read_register_matrix (GcmSensorHuey *huey, guint8 addr, GcmMat3x3 *value, GError **error)
+{
+	gboolean ret = TRUE;
+	guint i;
+	gfloat tmp;
+	gdouble *matrix_data;
+
+	/* get this to avoid casting */
+	matrix_data = gcm_mat33_get_data (value);
+
+	/* read in 3d matrix */
+	for (i=0; i<9; i++) {
+		ret = gcm_sensor_huey_read_register_float (huey, addr + (i*4), &tmp, error);
+		if (!ret)
+			goto out;
+
+		/* save in matrix */
+		*(matrix_data+i) = tmp;
+	}
+out:
+	return ret;
+}
+
 /**
  * gcm_sensor_huey_get_ambient:
  **/
 static gboolean
-gcm_sensor_huey_get_ambient (GcmSensor *sensor_, gdouble *value, GError **error)
+gcm_sensor_huey_get_ambient (GcmSensor *sensor, gdouble *value, GError **error)
 {
-//	GcmSensorHuey *sensor = GCM_SENSOR_HUEY (sensor_);
-	return TRUE;
+	guchar reply[8];
+	gboolean ret;
+	gsize reply_read;
+	const guchar request[] = { HUEY_COMMAND_AMBIENT, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	GcmSensorHuey *sensor_huey = GCM_SENSOR_HUEY (sensor);
+
+	/* hit hardware */
+	ret = gcm_sensor_huey_send_data (sensor_huey, request, 8, reply, 8, &reply_read, error);
+	if (!ret)
+		goto out;
+
+	/* parse the value */
+	g_debug ("%i, %i", reply[5], reply[5]);
+	*value = (gdouble) (reply[5] * 0xff + reply[6]) / HUEY_AMBIENT_UNITS_TO_LUX;
+out:
+	return ret;
 }
 
 /**
  * gcm_sensor_huey_set_leds:
  **/
 static gboolean
-gcm_sensor_huey_set_leds (GcmSensor *sensor_, guint8 value, GError **error)
+gcm_sensor_huey_set_leds (GcmSensor *sensor, guint8 value, GError **error)
 {
-//	GcmSensorHuey *sensor = GCM_SENSOR_HUEY (sensor_);
-	return TRUE;
+	guchar reply[8];
+	gsize reply_read;
+	GcmSensorHuey *sensor_huey = GCM_SENSOR_HUEY (sensor);
+	guchar payload[] = { HUEY_COMMAND_SET_LEDS, 0x00, ~value, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	return gcm_sensor_huey_send_data (sensor_huey, payload, 8, reply, 8, &reply_read, error);
+}
+
+
+/**
+ * gcm_sensor_huey_sample_for_threshold:
+ **/
+static gboolean
+gcm_sensor_huey_sample_for_threshold (GcmSensorHuey *sensor_huey, GcmColorRgbInt *threshold, GcmColorRgb *values, GError **error)
+{
+	guchar request[] = { HUEY_COMMAND_SENSOR_MEASURE_RGB, 0x00, threshold->red, 0x00, threshold->green, 0x00, threshold->blue, 0x00 };
+	guchar reply[8];
+	gboolean ret;
+	gsize reply_read;
+
+	/* measure, and get red */
+	ret = gcm_sensor_huey_send_data (sensor_huey, request, 8, reply, 8, &reply_read, error);
+	if (!ret)
+		goto out;
+
+	/* get value */
+	values->red = 1.0f / ((reply[3] * 0xff) + reply[4]);
+
+	/* get green */
+	request[0] = HUEY_COMMAND_READ_GREEN;
+	ret = gcm_sensor_huey_send_data (sensor_huey, request, 8, reply, 8, &reply_read, error);
+	if (!ret)
+		goto out;
+
+	/* get value */
+	values->green = 1.0f / ((reply[3] * 0xff) + reply[4]);
+
+	/* get blue */
+	request[0] = HUEY_COMMAND_READ_BLUE;
+	ret = gcm_sensor_huey_send_data (sensor_huey, request, 8, reply, 8, &reply_read, error);
+	if (!ret)
+		goto out;
+
+	/* get value */
+	values->blue = 1.0f / ((reply[3] * 0xff) + reply[4]);
+out:
+	return ret;
 }
 
 /**
  * gcm_sensor_huey_sample:
  **/
 static gboolean
-gcm_sensor_huey_sample (GcmSensor *sensor_, GcmColorXYZ *value, GError **error)
+gcm_sensor_huey_sample (GcmSensor *sensor, GcmColorXYZ *value, GError **error)
 {
-//	GcmSensorHuey *sensor = GCM_SENSOR_HUEY (sensor_);
-	return TRUE;
+	gboolean ret;
+	gdouble precision_value;
+	GcmColorRgb native;
+	GcmColorRgbInt multiplier;
+	GcmVec3 *input = (GcmVec3 *) &native;
+	GcmVec3 *output = (GcmVec3 *) value;
+	GcmSensorHuey *sensor_huey = GCM_SENSOR_HUEY (sensor);
+
+	/* set this to one value for a quick approximate value */
+	multiplier.red = 1;
+	multiplier.green = 1;
+	multiplier.blue = 1;
+	ret = gcm_sensor_huey_sample_for_threshold (sensor_huey, &multiplier, &native, error);
+	if (!ret)
+		goto out;
+	g_debug ("initial values: red=%0.4lf, green=%0.4lf, blue=%0.4lf", native.red, native.green, native.blue);
+
+	/* compromise between the amount of time and the precision */
+	precision_value = (gdouble) HUEY_PRECISION_TIME_VALUE;
+	if (native.red < precision_value)
+		multiplier.red = precision_value / native.red;
+	if (native.green < precision_value)
+		multiplier.green = precision_value / native.green;
+	if (native.blue < precision_value)
+		multiplier.blue = precision_value / native.blue;
+	g_debug ("using multiplier factor: red=%i, green=%i, blue=%i", multiplier.red, multiplier.green, multiplier.blue);
+	ret = gcm_sensor_huey_sample_for_threshold (sensor_huey, &multiplier, &native, error);
+	if (!ret)
+		goto out;
+	g_debug ("prescaled values: red=%0.4lf, green=%0.4lf, blue=%0.4lf", native.red, native.green, native.blue);
+	native.red = native.red * (gdouble)multiplier.red;
+	native.green = native.green * (gdouble)multiplier.green;
+	native.blue = native.blue * (gdouble)multiplier.blue;
+	g_debug ("scaled values: red=%0.4lf, green=%0.4lf, blue=%0.4lf", native.red, native.green, native.blue);
+
+	g_print ("PRE MULTIPLY: %s\n", gcm_vec3_to_string (input));
+
+	/* it would be rediculous for the device to emit RGB, it would be completely arbitrary --
+	 * we assume the matrix of data is designed to convert to LAB or XYZ */
+	gcm_mat33_vector_multiply (&sensor_huey->priv->calibration_matrix1, input, output);
+
+	/* scale correct */
+	gcm_vec3_scalar_multiply (output, HUEY_XYZ_POST_MULTIPLY_SCALE_FACTOR, output);
+
+	g_print ("POST MULTIPLY: %s\n", gcm_vec3_to_string (output));
+out:
+	return ret;
+}
+
+/**
+ * gcm_sensor_huey_send_unlock:
+ **/
+static gboolean
+gcm_sensor_huey_send_unlock (GcmSensorHuey *sensor_huey, GError **error)
+{
+	guchar request[8];
+	guchar reply[8];
+	gboolean ret;
+	gsize reply_read;
+
+	request[0] = HUEY_COMMAND_UNLOCK;
+	request[1] = 'G';
+	request[2] = 'r';
+	request[3] = 'M';
+	request[4] = 'b';
+	request[5] = 'k'; // <- perhaps junk, need to test next time locked */
+	request[6] = 'e'; // <-         "" */
+	request[7] = 'd'; // <-         "" */
+
+	/* GrMbked == I have no idea, neither does google */
+	ret = gcm_sensor_huey_send_data (sensor_huey, request, 8, reply, 8, &reply_read, error);
+	if (!ret)
+		goto out;
+out:
+	return ret;
+}
+
+/**
+ * gcm_sensor_huey_find_device:
+ **/
+static gboolean
+gcm_sensor_huey_find_device (GcmSensorHuey *sensor_huey, GError **error)
+{
+	struct libusb_device_descriptor desc;
+	libusb_device **devs = NULL;
+	libusb_device *dev;
+	gint retval;
+	gsize cnt;
+	guint i = 0;
+	gboolean ret = FALSE;
+
+	/* get device */
+	cnt = libusb_get_device_list (NULL, &devs);
+	if (cnt < 0) {
+		g_set_error (error, 1, 0, "failed to get device list: %s", libusb_strerror (cnt));
+		goto out;
+	}
+
+	/* find device */
+	for (i=0; i<cnt; i++) {
+		dev = devs[i];
+
+		/* get descriptor */
+		retval = libusb_get_device_descriptor (dev, &desc);
+		if (retval < 0) {
+			g_warning ("failed to get device descriptor for %02x:%02x, possibly faulty hardware",
+				   libusb_get_bus_number (dev), libusb_get_device_address (dev));
+			continue;
+		}
+
+		/* does match HUEY? */
+		if (desc.idVendor == HUEY_VENDOR_ID &&
+		    desc.idProduct == HUEY_PRODUCT_ID) {
+			g_debug ("got huey!");
+			ret = TRUE;
+			sensor_huey->priv->device = libusb_ref_device (dev);
+			break;
+		}
+	}
+
+	/* not found */
+	if (!ret) {
+		g_set_error_literal (error, 1, 0, "no compatible hardware attached");
+		goto out;
+	}
+
+	/* open device */
+	retval = libusb_open (sensor_huey->priv->device, &sensor_huey->priv->handle);
+	if (retval < 0) {
+		ret = FALSE;
+		g_set_error (error, 1, 0, "failed to open device: %s", libusb_strerror (retval));
+		goto out;
+	}
+
+	/* set configuration and interface, the only values we've got in lsusb-vvv */
+	retval = libusb_set_configuration (sensor_huey->priv->handle, 1);
+	if (retval < 0) {
+		ret = FALSE;
+		g_set_error (error, 1, 0, "failed to set configuration: %s", libusb_strerror (retval));
+		goto out;
+	}
+	retval = libusb_claim_interface (sensor_huey->priv->handle, 0);
+	if (retval < 0) {
+		ret = FALSE;
+		g_set_error (error, 1, 0, "failed to claim interface: %s", libusb_strerror (retval));
+		goto out;
+	}
+out:
+	libusb_free_device_list (devs, 1);
+	return ret;
 }
 
 /**
  * gcm_sensor_huey_startup:
  **/
 static gboolean
-gcm_sensor_huey_startup (GcmSensor *sensor_, GError **error)
+gcm_sensor_huey_startup (GcmSensor *sensor, GError **error)
 {
-//	GcmSensorHuey *sensor = GCM_SENSOR_HUEY (sensor_);
-	return TRUE;
+	gboolean ret = FALSE;
+	gint retval;
+	GcmSensorHuey *sensor_huey = GCM_SENSOR_HUEY (sensor);
+	GcmSensorHueyPrivate *priv = sensor_huey->priv;
+
+	/* connect */
+	retval = libusb_init (NULL);
+	if (retval < 0) {
+		g_warning ("failed to init libusb: %s", libusb_strerror (retval));
+		goto out;
+	}
+	priv->connected = TRUE;
+
+	/* find device */
+	ret = gcm_sensor_huey_find_device (sensor_huey, error);
+	if (!ret)
+		goto out;
+
+	/* unlock */
+	ret = gcm_sensor_huey_send_unlock (sensor_huey, error);
+	if (!ret)
+		goto out;
+
+	/* get unlock string */
+	ret = gcm_sensor_huey_read_register_string (sensor_huey, 0x7a, priv->unlock_string, 5, error);
+	if (!ret)
+		goto out;
+	g_debug ("Unlock string: %s", priv->unlock_string);
+
+	/* get matrix */
+	gcm_mat33_clear (&priv->calibration_matrix1);
+	ret = gcm_sensor_huey_read_register_matrix (sensor_huey, 0x04, &priv->calibration_matrix1, error);
+	if (!ret)
+		goto out;
+	g_debug ("device matrix1: %s", gcm_mat33_to_string (&priv->calibration_matrix1));
+
+	/* get another matrix, although this one is worse... */
+	gcm_mat33_clear (&priv->calibration_matrix2);
+	ret = gcm_sensor_huey_read_register_matrix (sensor_huey, 0x36, &priv->calibration_matrix2, error);
+	if (!ret)
+		goto out;
+	g_debug ("device matrix2: %s", gcm_mat33_to_string (&priv->calibration_matrix2));
+
+out:
+	return ret;
 }
 
 /**
@@ -125,8 +798,15 @@ gcm_sensor_huey_init (GcmSensorHuey *sensor)
 static void
 gcm_sensor_huey_finalize (GObject *object)
 {
-//	GcmSensorHuey *sensor = GCM_SENSOR_HUEY (object);
-//	GcmSensorHueyPrivate *priv = sensor->priv;
+	GcmSensorHuey *sensor = GCM_SENSOR_HUEY (object);
+	GcmSensorHueyPrivate *priv = sensor->priv;
+
+	/* close device */
+	libusb_close (priv->handle);
+	if (priv->device != NULL)
+		libusb_unref_device (priv->device);
+	if (priv->connected)
+		libusb_exit (NULL);
 
 	G_OBJECT_CLASS (gcm_sensor_huey_parent_class)->finalize (object);
 }
@@ -134,13 +814,13 @@ gcm_sensor_huey_finalize (GObject *object)
 /**
  * gcm_sensor_huey_new:
  *
- * Return value: a new GcmSensorHuey object.
+ * Return value: a new #GcmSensor object.
  **/
-GcmSensorHuey *
+GcmSensor *
 gcm_sensor_huey_new (void)
 {
 	GcmSensorHuey *sensor;
 	sensor = g_object_new (GCM_TYPE_SENSOR_HUEY, NULL);
-	return GCM_SENSOR_HUEY (sensor);
+	return GCM_SENSOR (sensor);
 }
 
diff --git a/src/gcm-sensor-huey.h b/src/gcm-sensor-huey.h
index ab460bb..4941286 100644
--- a/src/gcm-sensor-huey.h
+++ b/src/gcm-sensor-huey.h
@@ -2,21 +2,21 @@
  *
  * Copyright (C) 2010 Richard Hughes <richard hughsie com>
  *
- * Licensed under the GNU General Public License Version 2
+ * Licensed under the GNU Lesser General Public License Version 2.1
  *
- * 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 library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
+ * This library 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.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
 #ifndef __GCM_SENSOR_HUEY_H
@@ -58,7 +58,7 @@ struct _GcmSensorHueyClass
 };
 
 GType		 gcm_sensor_huey_get_type		 (void);
-GcmSensorHuey	*gcm_sensor_huey_new			 (void);
+GcmSensor	*gcm_sensor_huey_new			 (void);
 
 G_END_DECLS
 
diff --git a/src/gcm-sensor.c b/src/gcm-sensor.c
index 49fe0fc..155b620 100644
--- a/src/gcm-sensor.c
+++ b/src/gcm-sensor.c
@@ -2,21 +2,21 @@
  *
  * Copyright (C) 2010 Richard Hughes <richard hughsie com>
  *
- * Licensed under the GNU General Public License Version 2
+ * Licensed under the GNU Lesser General Public License Version 2.1
  *
- * 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 library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
+ * This library 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.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
 /**
diff --git a/src/gcm-sensor.h b/src/gcm-sensor.h
index 5fd1f6d..302d11e 100644
--- a/src/gcm-sensor.h
+++ b/src/gcm-sensor.h
@@ -2,21 +2,21 @@
  *
  * Copyright (C) 2010 Richard Hughes <richard hughsie com>
  *
- * Licensed under the GNU General Public License Version 2
+ * Licensed under the GNU Lesser General Public License Version 2.1
  *
- * 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 library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
+ * This library 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.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
 #ifndef __GCM_SENSOR_H



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]