diff options
Diffstat (limited to 'src/sensors/huey/huey-device.c')
-rw-r--r-- | src/sensors/huey/huey-device.c | 516 |
1 files changed, 516 insertions, 0 deletions
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; +} |