/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2012-2013 Richard Hughes * * 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 #include #include #include #include #include "ch-common.h" #include "ch-device.h" #include "ch-device-queue.h" #include "ch-math.h" static void ch_device_queue_finalize (GObject *object); #define GET_PRIVATE(o) (ch_device_queue_get_instance_private (o)) /** * ChDeviceQueuePrivate: * * Private #ChDeviceQueue data **/ typedef struct { GPtrArray *data_array; GHashTable *devices_in_use; } ChDeviceQueuePrivate; enum { SIGNAL_DEVICE_FAILED, SIGNAL_PROGRESS_CHANGED, SIGNAL_LAST }; G_DEFINE_TYPE_WITH_PRIVATE (ChDeviceQueue, ch_device_queue, G_TYPE_OBJECT) typedef gboolean (*ChDeviceQueueParseFunc) (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error); typedef enum { CH_DEVICE_QUEUE_DATA_STATE_PENDING, CH_DEVICE_QUEUE_DATA_STATE_WAITING_FOR_HW, CH_DEVICE_QUEUE_DATA_STATE_CANCELLED, CH_DEVICE_QUEUE_DATA_STATE_COMPLETE, CH_DEVICE_QUEUE_DATA_STATE_UNKNOWN } ChDeviceQueueDataState; typedef struct { ChDeviceQueueDataState state; GUsbDevice *device; guint8 cmd; guint8 *buffer_in; gsize buffer_in_len; guint8 *buffer_out; gsize buffer_out_len; GDestroyNotify buffer_out_destroy_func; ChDeviceQueueParseFunc parse_func; gpointer user_data; GDestroyNotify user_data_destroy_func; } ChDeviceQueueData; typedef struct { ChDeviceQueue *device_queue; ChDeviceQueueProcessFlags process_flags; GCancellable *cancellable; GSimpleAsyncResult *res; GPtrArray *failures; } ChDeviceQueueTaskData; static guint signals[SIGNAL_LAST] = { 0 }; static gboolean ch_device_queue_process_data (GTask *task, ChDeviceQueueData *data); static void ch_device_queue_data_free (ChDeviceQueueData *data) { if (data->buffer_out_destroy_func != NULL) data->buffer_out_destroy_func (data->buffer_out); if (data->user_data_destroy_func != NULL) data->user_data_destroy_func (data->user_data); g_free (data->buffer_in); g_object_unref (data->device); g_free (data); } static void ch_device_queue_task_data_free (ChDeviceQueueTaskData *data) { g_object_unref (data->device_queue); g_ptr_array_unref (data->failures); g_free (data); } static void ch_device_queue_device_force_complete (ChDeviceQueue *device_queue, GUsbDevice *device) { ChDeviceQueuePrivate *priv = GET_PRIVATE (device_queue); ChDeviceQueueData *data; const gchar *device_id; const gchar *device_id_tmp; guint i; /* go through the list of commands and cancel them all */ device_id = g_usb_device_get_platform_id (device); for (i = 0; i < priv->data_array->len; i++) { data = g_ptr_array_index (priv->data_array, i); device_id_tmp = g_usb_device_get_platform_id (data->device); if (g_strcmp0 (device_id_tmp, device_id) == 0) data->state = CH_DEVICE_QUEUE_DATA_STATE_CANCELLED; } } static void ch_device_queue_update_progress (ChDeviceQueue *device_queue) { ChDeviceQueuePrivate *priv = GET_PRIVATE (device_queue); guint complete = 0; guint i; guint percentage; ChDeviceQueueData *data; /* no devices */ if (priv->data_array->len == 0) return; /* find out how many commands are complete */ for (i = 0; i < priv->data_array->len; i++) { data = g_ptr_array_index (priv->data_array, i); if (data->state == CH_DEVICE_QUEUE_DATA_STATE_COMPLETE || data->state == CH_DEVICE_QUEUE_DATA_STATE_CANCELLED) complete++; } /* emit a signal with our progress */ percentage = (complete * 100) / priv->data_array->len; g_signal_emit (device_queue, signals[SIGNAL_PROGRESS_CHANGED], 0, percentage); } static guint ch_device_queue_count_in_state (ChDeviceQueue *device_queue, ChDeviceQueueDataState state) { ChDeviceQueuePrivate *priv = GET_PRIVATE (device_queue); guint i; guint cnt = 0; ChDeviceQueueData *data; /* find any data objects in a specific state */ for (i = 0; i < priv->data_array->len; i++) { data = g_ptr_array_index (priv->data_array, i); if (data->state == state) cnt++; } return cnt; } static void ch_device_queue_process_write_command_cb (GObject *source, GAsyncResult *res, gpointer user_data) { ChDeviceQueueData *data; GTask *task = G_TASK (user_data); ChDeviceQueueTaskData *tdata = g_task_get_task_data (task); ChDeviceQueue *device_queue = CH_DEVICE_QUEUE (tdata->device_queue); ChDeviceQueuePrivate *priv = GET_PRIVATE (device_queue); const gchar *device_id; const gchar *tmp; gboolean ret; g_autoptr(GError) error = NULL; guint i; guint pending_commands; ChError last_error_code = 0; GUsbDevice *device = G_USB_DEVICE (source); g_autofree gchar *error_msg = NULL; /* mark it as not in use */ device_id = g_usb_device_get_platform_id (device); data = g_hash_table_lookup (priv->devices_in_use, device_id); g_hash_table_remove (priv->devices_in_use, device_id); /* get data */ ret = ch_device_write_command_finish (device, res, &error); if (ret && data->parse_func != NULL) { /* do any conversion function */ ret = data->parse_func (data->buffer_out, data->buffer_out_len, data->user_data, &error); } if (!ret) { /* tell the client the device has failed */ g_debug ("emit device-failed: %s", error->message); g_signal_emit (device_queue, signals[SIGNAL_DEVICE_FAILED], 0, device, error->message); /* save this so we can possibly use when we're done */ last_error_code = error->code; g_ptr_array_add (tdata->failures, g_strdup_printf ("%s: %s", g_usb_device_get_platform_id (device), error->message)); /* should we mark complete other commands as complete */ if ((tdata->process_flags & CH_DEVICE_QUEUE_PROCESS_FLAGS_CONTINUE_ERRORS) == 0) { ch_device_queue_device_force_complete (device_queue, device); ch_device_queue_update_progress (device_queue); goto out; } } /* update progress */ data->state = CH_DEVICE_QUEUE_DATA_STATE_COMPLETE; ch_device_queue_update_progress (device_queue); /* is there another pending command for this device */ for (i = 0; i < priv->data_array->len; i++) { data = g_ptr_array_index (priv->data_array, i); ret = ch_device_queue_process_data (task, data); if (ret) break; } out: /* any more pending commands? */ pending_commands = ch_device_queue_count_in_state (device_queue, CH_DEVICE_QUEUE_DATA_STATE_PENDING); pending_commands += ch_device_queue_count_in_state (device_queue, CH_DEVICE_QUEUE_DATA_STATE_WAITING_FOR_HW); g_debug ("Pending commands: %u", pending_commands); if (pending_commands == 0) { /* should we return the process with an error, or just * rely on the signal? */ if (tdata->failures->len == 1 && (tdata->process_flags & CH_DEVICE_QUEUE_PROCESS_FLAGS_NONFATAL_ERRORS) == 0) { tmp = g_ptr_array_index (tdata->failures, 0); g_task_return_new_error (task, CH_DEVICE_ERROR, last_error_code, "%s", tmp); } else if (tdata->failures->len > 1 && (tdata->process_flags & CH_DEVICE_QUEUE_PROCESS_FLAGS_NONFATAL_ERRORS) == 0) { g_ptr_array_add (tdata->failures, NULL); error_msg = g_strjoinv (", ", (gchar**) tdata->failures->pdata); g_task_return_new_error (task, CH_DEVICE_ERROR, last_error_code, "There were %u failures: %s", tdata->failures->len - 1, error_msg); } else { g_task_return_boolean (task, TRUE); } /* remove all commands from the queue, as they are done */ #if 0 /* FIXME: some sort of race uncovered in the port to GTask means that we try * to remove data objects before they are marked complete; for now disable */ g_ptr_array_set_size (priv->data_array, 0); #endif g_object_unref (task); } } /** * ch_device_queue_process_data: * * Returns TRUE if the command was submitted **/ static gboolean ch_device_queue_process_data (GTask *task, ChDeviceQueueData *data) { ChDeviceQueue *device_queue = CH_DEVICE_QUEUE (g_task_get_source_object (task)); ChDeviceQueuePrivate *priv = GET_PRIVATE (device_queue); ChDeviceQueueData *data_tmp; const gchar *device_id; /* is this command already complete? */ if (data->state == CH_DEVICE_QUEUE_DATA_STATE_COMPLETE) return FALSE; /* is this device already busy? */ device_id = g_usb_device_get_platform_id (data->device); data_tmp = g_hash_table_lookup (priv->devices_in_use, device_id); if (data_tmp != NULL) return FALSE; /* write this command and wait for a response */ ch_device_write_command_async (data->device, data->cmd, data->buffer_in, data->buffer_in_len, data->buffer_out, data->buffer_out_len, g_task_get_cancellable (task), ch_device_queue_process_write_command_cb, task); /* mark this as in use */ g_hash_table_insert (priv->devices_in_use, g_strdup (device_id), data); /* remove this from the command queue -- TODO: retries? */ data->state = CH_DEVICE_QUEUE_DATA_STATE_WAITING_FOR_HW; return TRUE; } /** * ch_device_queue_process_async: * @device_queue: A #ChDeviceQueue * @cancellable: A #GCancellable, or %NULL * @callback: A #GAsyncReadyCallback that will be called when finished. * @user_data: User data passed to @callback * * Processes all commands in the command queue. * * Since: 0.1.29 **/ void ch_device_queue_process_async (ChDeviceQueue *device_queue, ChDeviceQueueProcessFlags process_flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ChDeviceQueuePrivate *priv = GET_PRIVATE (device_queue); ChDeviceQueueTaskData *tdata; ChDeviceQueueData *data; GTask *task = NULL; guint i; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); task = g_task_new (device_queue, cancellable, callback, user_data); tdata = g_new0 (ChDeviceQueueTaskData, 1); tdata->process_flags = process_flags; tdata->device_queue = g_object_ref (device_queue); tdata->failures = g_ptr_array_new_with_free_func (g_free); g_task_set_task_data (task, tdata, (GDestroyNotify) ch_device_queue_task_data_free); /* go through the list of commands and try to submit them all */ ch_device_queue_update_progress (device_queue); for (i = 0; i < priv->data_array->len; i++) { data = g_ptr_array_index (priv->data_array, i); ch_device_queue_process_data (task, data); } /* is anything pending? */ if (g_hash_table_size (priv->devices_in_use) == 0) { g_task_return_boolean (task, TRUE); g_object_unref (task); } } /** * ch_device_queue_process_finish: * @device_queue: a #ChDeviceQueue instance. * @res: the #GAsyncResult * @error: A #GError or %NULL * * Gets the result from the asynchronous function. * * Return value: %TRUE if the request was fulfilled. * * Since: 0.1.29 **/ gboolean ch_device_queue_process_finish (ChDeviceQueue *device_queue, GAsyncResult *res, GError **error) { g_return_val_if_fail (g_task_is_valid (res, device_queue), FALSE); return g_task_propagate_boolean (G_TASK (res), error); } /**********************************************************************/ /* tiny helper to help us do the async operation */ typedef struct { GError **error; GMainLoop *loop; gboolean ret; } ChDeviceQueueSyncHelper; static void ch_device_queue_process_finish_cb (GObject *source, GAsyncResult *res, gpointer user_data) { ChDeviceQueue *device_queue = CH_DEVICE_QUEUE (source); ChDeviceQueueSyncHelper *helper = (ChDeviceQueueSyncHelper *) user_data; helper->ret = ch_device_queue_process_finish (device_queue, res, helper->error); g_main_loop_quit (helper->loop); } /** * ch_device_queue_process: * @device_queue: A #ChDeviceQueue * @process_flags: Flags how to process the queue, e.g. %CH_DEVICE_QUEUE_PROCESS_FLAGS_CONTINUE_ERRORS * @cancellable: #GCancellable or %NULL * @error: A #GError, or %NULL * * Processes all commands in the command queue. * WARNING: this function is synchronous and will block. * * Return value: %TRUE if the commands were executed successfully. * * Since: 0.1.29 **/ gboolean ch_device_queue_process (ChDeviceQueue *device_queue, ChDeviceQueueProcessFlags process_flags, GCancellable *cancellable, GError **error) { ChDeviceQueueSyncHelper helper; g_return_val_if_fail (CH_IS_DEVICE_QUEUE (device_queue), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* create temp object */ helper.ret = FALSE; helper.loop = g_main_loop_new (NULL, FALSE); helper.error = error; /* run async method */ ch_device_queue_process_async (device_queue, process_flags, cancellable, ch_device_queue_process_finish_cb, &helper); g_main_loop_run (helper.loop); /* free temp object */ g_main_loop_unref (helper.loop); return helper.ret; } /**********************************************************************/ static void ch_device_queue_add_internal (ChDeviceQueue *device_queue, GUsbDevice *device, guint8 cmd, const guint8 *buffer_in, gsize buffer_in_len, guint8 *buffer_out, gsize buffer_out_len, GDestroyNotify buffer_out_destroy_func, ChDeviceQueueParseFunc parse_func, gpointer user_data, GDestroyNotify user_data_destroy_func) { ChDeviceQueueData *data; ChDeviceQueuePrivate *priv = GET_PRIVATE (device_queue); g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); data = g_new0 (ChDeviceQueueData, 1); data->state = CH_DEVICE_QUEUE_DATA_STATE_PENDING; data->parse_func = parse_func; data->user_data = user_data; data->user_data_destroy_func = user_data_destroy_func; data->cmd = cmd; data->device = g_object_ref (device); if (buffer_in != NULL) data->buffer_in = g_memdup (buffer_in, buffer_in_len); data->buffer_in_len = buffer_in_len; data->buffer_out = buffer_out; data->buffer_out_len = buffer_out_len; data->buffer_out_destroy_func = buffer_out_destroy_func; g_ptr_array_add (priv->data_array, data); } /** * ch_device_queue_add: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @cmd: The command, e.g. %CH_CMD_TAKE_READINGS * @buffer_in: The input buffer, or %NULL * @buffer_in_len: The size of @buffer_in * @buffer_out: The output buffer, or %NULL * @buffer_out_len: The size of @buffer_out * * Adds a raw command to the device queue. * * Since: 0.1.29 **/ void ch_device_queue_add (ChDeviceQueue *device_queue, GUsbDevice *device, guint8 cmd, const guint8 *buffer_in, gsize buffer_in_len, guint8 *buffer_out, gsize buffer_out_len) { ch_device_queue_add_internal (device_queue, device, cmd, buffer_in, buffer_in_len, buffer_out, buffer_out_len, NULL, NULL, NULL, NULL); } /**********************************************************************/ /** * ch_device_queue_get_color_select: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @color_select: The color select, e.g. %CH_COLOR_SELECT_RED * * Gets the selected sensor color. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_color_select (ChDeviceQueue *device_queue, GUsbDevice *device, ChColorSelect *color_select) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (color_select != NULL); ch_device_queue_add (device_queue, device, CH_CMD_GET_COLOR_SELECT, NULL, 0, (guint8 *) color_select, 1); } /** * ch_device_queue_set_color_select: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @color_select: The color select, e.g. %CH_COLOR_SELECT_RED * * Sets the sensor measurement color. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_color_select (ChDeviceQueue *device_queue, GUsbDevice *device, ChColorSelect color_select) { guint8 csel8 = color_select; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); ch_device_queue_add (device_queue, device, CH_CMD_SET_COLOR_SELECT, &csel8, 1, NULL, 0); } /** * ch_device_queue_get_multiplier: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @multiplier: The device multiplier, e.g. %CH_FREQ_SCALE_100 * * Gets the sensor multiplier. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_multiplier (ChDeviceQueue *device_queue, GUsbDevice *device, ChFreqScale *multiplier) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (multiplier != NULL); ch_device_queue_add (device_queue, device, CH_CMD_GET_MULTIPLIER, NULL, 0, (guint8 *) multiplier, 1); } /** * ch_device_queue_set_multiplier: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @multiplier: The device multiplier, e.g. %CH_FREQ_SCALE_100 * * Sets the sensor multiplier. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_multiplier (ChDeviceQueue *device_queue, GUsbDevice *device, ChFreqScale multiplier) { guint8 mult8 = multiplier; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); ch_device_queue_add (device_queue, device, CH_CMD_SET_MULTIPLIER, &mult8, 1, NULL, 0); } static gboolean ch_device_queue_buffer_uint16_from_le_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { guint16 tmp; /* check buffer size */ if (output_buffer_size != sizeof (guint16)) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %" G_GSIZE_FORMAT ", got %" G_GSIZE_FORMAT, sizeof (guint16), output_buffer_size); return FALSE; } tmp = cd_buffer_read_uint16_le (output_buffer); memcpy (output_buffer, &tmp, sizeof (tmp)); return TRUE; } static gboolean ch_device_queue_buffer_uint32_from_le_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { guint32 tmp; /* check buffer size */ if (output_buffer_size != sizeof (guint32)) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %" G_GSIZE_FORMAT ", got %" G_GSIZE_FORMAT, sizeof (guint32), output_buffer_size); return FALSE; } tmp = cd_buffer_read_uint32_le (output_buffer); memcpy (output_buffer, &tmp, sizeof (tmp)); return TRUE; } /** * ch_device_queue_get_integral_time: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @integral_time: The sensor integral time in device units * * Gets the reading integral time. * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_integral_time (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 *integral_time) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (integral_time != NULL); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_INTEGRAL_TIME, NULL, 0, (guint8 *) integral_time, 2, NULL, ch_device_queue_buffer_uint16_from_le_cb, NULL, NULL); } /** * ch_device_queue_set_integral_time: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @integral_time: The sensor integral time in device units * * Sets the reading integral time. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_integral_time (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 integral_time) { guint16 integral_le; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (integral_time > 0); integral_le = GUINT16_TO_LE (integral_time); ch_device_queue_add (device_queue, device, CH_CMD_SET_INTEGRAL_TIME, (const guint8 *) &integral_le, sizeof(guint16), NULL, 0); } /** * ch_device_queue_get_calibration_map: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @calibration_map: An array of slot positions * * Gets the calibration map. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_calibration_map (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 *calibration_map) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (calibration_map != NULL); ch_device_queue_add (device_queue, device, CH_CMD_GET_CALIBRATION_MAP, NULL, 0, (guint8 *) calibration_map, CH_CALIBRATION_INDEX_MAX * sizeof(guint16)); } /** * ch_device_queue_set_calibration_map: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @calibration_map: An array of slot positions * * Sets the calibration map. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_calibration_map (ChDeviceQueue *device_queue, GUsbDevice *device, const guint16 *calibration_map) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (calibration_map != NULL); ch_device_queue_add (device_queue, device, CH_CMD_SET_CALIBRATION_MAP, (const guint8 *) calibration_map, CH_CALIBRATION_INDEX_MAX * sizeof(guint16), NULL, 0); } /* tiny helper */ typedef struct { guint16 *major; guint16 *minor; guint16 *micro; } ChDeviceQueueGetFirmwareVerHelper; static gboolean ch_device_queue_buffer_to_firmware_ver_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { ChDeviceQueueGetFirmwareVerHelper *helper = (void *) user_data; /* check buffer size */ if (output_buffer_size != sizeof (guint16) * 3) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %" G_GSIZE_FORMAT ", got %" G_GSIZE_FORMAT, sizeof (guint16) * 3, output_buffer_size); return FALSE; } *helper->major = cd_buffer_read_uint16_le (output_buffer + 0); *helper->minor = cd_buffer_read_uint16_le (output_buffer + 2); *helper->micro = cd_buffer_read_uint16_le (output_buffer + 4); return TRUE; } /** * ch_device_queue_get_firmware_ver: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @major: The firmware major version * @minor: The firmware minor version * @micro: The firmware micro version * * Gets the firmware version. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_firmware_ver (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 *major, guint16 *minor, guint16 *micro) { guint8 *buffer; ChDeviceQueueGetFirmwareVerHelper *helper; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (major != NULL); g_return_if_fail (minor != NULL); g_return_if_fail (micro != NULL); /* create a helper structure */ helper = g_new0 (ChDeviceQueueGetFirmwareVerHelper, 1); helper->major = major; helper->minor = minor; helper->micro = micro; buffer = g_new0 (guint8, sizeof (guint16) * 3); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_FIRMWARE_VERSION, NULL, 0, buffer, sizeof (guint16) * 3, g_free, ch_device_queue_buffer_to_firmware_ver_cb, helper, g_free); } /* tiny helper */ typedef struct { CdMat3x3 *calibration; guint8 *types; gchar *description; } ChDeviceQueueGetCalibrationHelper; static gboolean ch_device_queue_buffer_to_get_calibration_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { ChDeviceQueueGetCalibrationHelper *helper = (void *) user_data; ChPackedFloat pf_tmp; gdouble *calibration_tmp; guint i; /* check buffer size */ if (output_buffer_size != 60) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %i, got %" G_GSIZE_FORMAT, 60, output_buffer_size); return FALSE; } /* convert back into floating point */ if (helper->calibration != NULL) { calibration_tmp = cd_mat33_get_data (helper->calibration); for (i = 0; i < 9; i++) { memcpy (&pf_tmp, &output_buffer[i*4], sizeof (pf_tmp)); ch_packed_float_to_double (&pf_tmp, &calibration_tmp[i]); } } /* get the supported types */ if (helper->types != NULL) *helper->types = output_buffer[9*4]; /* get the description */ if (helper->description != NULL) { strncpy (helper->description, (const char *) output_buffer + 9*4 + 1, CH_CALIBRATION_DESCRIPTION_LEN); } return TRUE; } /** * ch_device_queue_get_calibration: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @calibration_index: The slot position * @calibration: the 3x3 calibration matrix * @types: The types the matrix supports * @description: The description of the calibration * * Gets the calibration data. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_calibration (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 calibration_index, CdMat3x3 *calibration, guint8 *types, gchar *description) { guint8 *buffer; ChDeviceQueueGetCalibrationHelper *helper; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (calibration_index < CH_CALIBRATION_MAX); /* create a helper structure */ helper = g_new0 (ChDeviceQueueGetCalibrationHelper, 1); helper->calibration = calibration; helper->types = types; helper->description = description; buffer = g_new0 (guint8, 9*4 + 1 + CH_CALIBRATION_DESCRIPTION_LEN); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_CALIBRATION, (guint8 *) &calibration_index, sizeof(guint16), (guint8 *) buffer, 9*4 + 1 + CH_CALIBRATION_DESCRIPTION_LEN, g_free, ch_device_queue_buffer_to_get_calibration_cb, helper, g_free); } /** * ch_device_queue_set_calibration: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @calibration_index: The slot position * @calibration: the 3x3 calibration matrix * @types: The types the matrix supports * @description: The description of the calibration * * Sets the calibration data. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_calibration (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 calibration_index, const CdMat3x3 *calibration, guint8 types, const gchar *description) { ChPackedFloat pf_tmp; gdouble *calibration_tmp; guint8 buffer[9*4 + 2 + 1 + CH_CALIBRATION_DESCRIPTION_LEN]; guint i; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (calibration_index < CH_CALIBRATION_MAX); g_return_if_fail (calibration != NULL); g_return_if_fail (description != NULL); /* write index */ memcpy (buffer, &calibration_index, sizeof(guint16)); /* convert from float to signed value */ for (i = 0; i < 9; i++) { calibration_tmp = cd_mat33_get_data (calibration); ch_double_to_packed_float (calibration_tmp[i], &pf_tmp); memcpy (&buffer[i*4 + 2], &pf_tmp, sizeof (pf_tmp)); } /* write types */ buffer[9*4 + 2] = types; /* write description */ strncpy ((gchar *) buffer + 9*4 + 2 + 1, description, CH_CALIBRATION_DESCRIPTION_LEN); ch_device_queue_add (device_queue, device, CH_CMD_SET_CALIBRATION, (guint8 *) buffer, sizeof(buffer), NULL, 0); } /** * ch_device_queue_set_calibration_ccmx: * * Since: 0.1.29 **/ gboolean ch_device_queue_set_calibration_ccmx (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 calibration_index, CdIt8 *ccmx, GError **error) { const CdMat3x3 *calibration; const gchar *description; gdouble *calibration_tmp; guint8 types = 0; guint i; g_return_val_if_fail (CD_IS_IT8 (ccmx), FALSE); g_return_val_if_fail (CH_IS_DEVICE_QUEUE (device_queue), FALSE); g_return_val_if_fail (G_USB_IS_DEVICE (device), FALSE); /* ensure correct kind */ if (cd_it8_get_kind (ccmx) != CD_IT8_KIND_CCMX) { g_set_error (error, 1, 0, "is not a CCMX file"); return FALSE; } /* get the supported display types */ if (cd_it8_has_option (ccmx, "TYPE_FACTORY")) { types = CH_CALIBRATION_TYPE_ALL; } else { if (cd_it8_has_option (ccmx, "TYPE_LCD")) types += CH_CALIBRATION_TYPE_LCD; if (cd_it8_has_option (ccmx, "TYPE_LED")) types += CH_CALIBRATION_TYPE_LED; if (cd_it8_has_option (ccmx, "TYPE_CRT")) types += CH_CALIBRATION_TYPE_CRT; if (cd_it8_has_option (ccmx, "TYPE_PROJECTOR")) types += CH_CALIBRATION_TYPE_PROJECTOR; } /* no types set in CCMX file */ if (types == 0) { g_set_error_literal (error, 1, 0, "No TYPE_x in ccmx file"); return FALSE; } /* get the description from the ccmx file */ description = cd_it8_get_title (ccmx); if (description == NULL) { g_set_error_literal (error, 1, 0, "CCMX file does not have DISPLAY"); return FALSE; } /* get the values and check for sanity */ calibration = cd_it8_get_matrix (ccmx); calibration_tmp = cd_mat33_get_data (calibration); for (i = 0; i < 9; i++) { if (calibration_tmp[i] < -100.0f || calibration_tmp[i] > 100.0f) { g_set_error (error, 1, 0, "Matrix value %u out of range %f", i, calibration_tmp[i]); return FALSE; } } /* set to HW */ ch_device_queue_set_calibration (device_queue, device, calibration_index, calibration, types, description); return TRUE; } /** * ch_device_queue_write_firmware: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @data: (array length=len): Firmware binary data * @len: Size of @data * * Writes new firmware to the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_write_firmware (ChDeviceQueue *device_queue, GUsbDevice *device, const guint8 *data, gsize len) { gsize chunk_len; guint idx; guint16 runcode_addr; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (data != NULL); /* erase flash */ runcode_addr = ch_device_get_runcode_address (device); g_debug ("Erasing at %04x size %" G_GSIZE_FORMAT, runcode_addr, len); ch_device_queue_erase_flash (device_queue, device, runcode_addr, len); /* just write in 32 byte chunks, as we're sure that the firmware * image has been prepared to end on a 64 byte chunk with * colorhug-inhx32-to-bin >= 0.1.5 */ idx = 0; chunk_len = CH_FLASH_TRANSFER_BLOCK_SIZE; do { if (idx + chunk_len > len) chunk_len = len - idx; g_debug ("Writing at %04x size %" G_GSIZE_FORMAT, runcode_addr + idx, chunk_len); ch_device_queue_write_flash (device_queue, device, runcode_addr + idx, (guint8 *) data + idx, chunk_len); idx += chunk_len; } while (idx < len); } /** * ch_device_queue_verify_firmware: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @data: (array length=len): Firmware binary data * @len: Size of @data * * Verifies firmware on the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_verify_firmware (ChDeviceQueue *device_queue, GUsbDevice *device, const guint8 *data, gsize len) { gsize chunk_len; guint idx; guint16 runcode_addr; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (data != NULL); /* read in 60 byte chunks */ idx = 0; chunk_len = 60; runcode_addr = ch_device_get_runcode_address (device); do { if (idx + chunk_len > len) chunk_len = len - idx; g_debug ("Verifying at %04x size %" G_GSIZE_FORMAT, runcode_addr + idx, chunk_len); ch_device_queue_verify_flash (device_queue, device, runcode_addr + idx, data + idx, chunk_len); idx += chunk_len; } while (idx < len); } /** * ch_device_queue_read_firmware: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @data: (array length=len): Firmware binary data * @len: Size of @data * * Reads firmware on the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 1.2.11 **/ void ch_device_queue_read_firmware (ChDeviceQueue *device_queue, GUsbDevice *device, guint8 **data, gsize *len) { gsize chunk_len = 60; guint idx = 0; guint16 runcode_addr; guint8 *data_tmp; gsize len_tmp; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (data != NULL); /* assume firmware is padded */ len_tmp = ch_device_get_runcode_address (device); data_tmp = g_malloc0 (len_tmp); /* read in 60 byte chunks */ runcode_addr = ch_device_get_runcode_address (device); do { if (idx + chunk_len > len_tmp) chunk_len = len_tmp - idx; g_debug ("Reading at %04x size %" G_GSIZE_FORMAT, runcode_addr + idx, chunk_len); ch_device_queue_read_flash (device_queue, device, runcode_addr + idx, data_tmp + idx, chunk_len); idx += chunk_len; } while (idx < len_tmp); /* return */ *data = data_tmp; if (len != NULL) *len = len_tmp; } /** * ch_device_queue_clear_calibration: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @calibration_index: Slot * * Clears a calibration slot. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_clear_calibration (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 calibration_index) { guint8 buffer[9*4 + 2 + 1 + CH_CALIBRATION_DESCRIPTION_LEN]; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (calibration_index < CH_CALIBRATION_MAX); /* write index */ memcpy (buffer, &calibration_index, sizeof(guint16)); /* clear data */ memset (buffer + 2, 0xff, sizeof (buffer) - 2); ch_device_queue_add (device_queue, device, CH_CMD_SET_CALIBRATION, (guint8 *) buffer, sizeof(buffer), NULL, 0); } static gboolean ch_device_queue_buffer_to_double_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { ChPackedFloat pf_tmp; gdouble tmp; /* check buffer size */ if (output_buffer_size != sizeof (ChPackedFloat)) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %" G_GSIZE_FORMAT ", got %" G_GSIZE_FORMAT, sizeof (ChPackedFloat), output_buffer_size); return FALSE; } /* convert back into floating point */ memcpy (&pf_tmp, output_buffer, sizeof (ChPackedFloat)); ch_packed_float_to_double (&pf_tmp, &tmp); memcpy (user_data, &tmp, sizeof (tmp)); return TRUE; } /** * ch_device_queue_get_pre_scale: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @pre_scale: Pre-scale value * * Gets the pre scale value. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_pre_scale (ChDeviceQueue *device_queue, GUsbDevice *device, gdouble *pre_scale) { guint8 *buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (pre_scale != NULL); *pre_scale = 0.0f; buffer = g_new0 (guint8, sizeof (ChPackedFloat)); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_PRE_SCALE, NULL, 0, buffer, sizeof(ChPackedFloat), g_free, ch_device_queue_buffer_to_double_cb, pre_scale, NULL); } /** * ch_device_queue_set_pre_scale: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @pre_scale: Pre-scale value * * Sets the pre-scale value. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_pre_scale (ChDeviceQueue *device_queue, GUsbDevice *device, gdouble pre_scale) { ChPackedFloat buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); /* convert from float to signed value */ ch_double_to_packed_float (pre_scale, &buffer); ch_device_queue_add (device_queue, device, CH_CMD_SET_PRE_SCALE, (guint8 *) &buffer, sizeof(buffer), NULL, 0); } /** * ch_device_queue_get_temperature: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @temperature: Temperature in Celsius * * Gets the device temperature. * * NOTE: This command is available on hardware version: 2 * * Since: 0.1.29 **/ void ch_device_queue_get_temperature (ChDeviceQueue *device_queue, GUsbDevice *device, gdouble *temperature) { guint8 *buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (temperature != NULL); *temperature = 0.0f; buffer = g_new0 (guint8, sizeof (ChPackedFloat)); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_TEMPERATURE, NULL, 0, buffer, sizeof(ChPackedFloat), g_free, ch_device_queue_buffer_to_double_cb, temperature, NULL); } /** * ch_device_queue_get_dac_value: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @dac_value: value between 0.0 and 0.99 * * Gets the DAC value. * * NOTE: This command is available on hardware version: 2 * * Since: 1.1.6 **/ void ch_device_queue_get_dac_value (ChDeviceQueue *device_queue, GUsbDevice *device, gdouble *dac_value) { guint8 *buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (dac_value != NULL); *dac_value = 0.0f; buffer = g_new0 (guint8, sizeof (ChPackedFloat)); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_DAC_VALUE, NULL, 0, buffer, sizeof(ChPackedFloat), g_free, ch_device_queue_buffer_to_double_cb, dac_value, NULL); } /** * ch_device_queue_set_dac_value: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @dac_value: The DAC value * * Sets the post scale value. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 1.1.6 **/ void ch_device_queue_set_dac_value (ChDeviceQueue *device_queue, GUsbDevice *device, gdouble dac_value) { ChPackedFloat buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); /* convert from float to signed value */ ch_double_to_packed_float (dac_value, &buffer); ch_device_queue_add (device_queue, device, CH_CMD_SET_DAC_VALUE, (guint8 *) &buffer, sizeof(buffer), NULL, 0); } /** * ch_device_queue_get_adc_vref_pos: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @vref: The voltage reference, ranging from 0.0 to 3.3V. * * Gets the ADC Vref+ calibration value. * * NOTE: This command is available on hardware version: 2 * * Since: 0.1.31 **/ void ch_device_queue_get_adc_vref_pos (ChDeviceQueue *device_queue, GUsbDevice *device, gdouble *vref) { guint8 *buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (vref != NULL); *vref = 0.0f; buffer = g_new0 (guint8, sizeof (ChPackedFloat)); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_ADC_CALIBRATION_POS, NULL, 0, buffer, sizeof(ChPackedFloat), g_free, ch_device_queue_buffer_to_double_cb, vref, NULL); } /** * ch_device_queue_get_adc_vref_neg: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @vref: The voltage reference, ranging from 0.0 to 3.3V. * * Gets the ADC Vref- calibration value. * * NOTE: This command is available on hardware version: 2 * * Since: 0.1.31 **/ void ch_device_queue_get_adc_vref_neg (ChDeviceQueue *device_queue, GUsbDevice *device, gdouble *vref) { guint8 *buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (vref != NULL); *vref = 0.0f; buffer = g_new0 (guint8, sizeof (ChPackedFloat)); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_ADC_CALIBRATION_NEG, NULL, 0, buffer, sizeof(ChPackedFloat), g_free, ch_device_queue_buffer_to_double_cb, vref, NULL); } /** * ch_device_queue_take_reading_spectral: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @sram_addr: The SRAM location where the data is held * * Takes a raw spectral reading. * * NOTE: This command is available on hardware version: 2 * * Since: 0.1.31 **/ void ch_device_queue_take_reading_spectral (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 *sram_addr) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (sram_addr != NULL); *sram_addr = 0x0000; ch_device_queue_add (device_queue, device, CH_CMD_TAKE_READING_SPECTRAL, NULL, 0, (guint8 *) sram_addr, sizeof (guint16)); } /** * ch_device_queue_get_ccd_calibration: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @indexes: An array of red, green, blue indexes * * Gets the CCD spectral calibration indexes. These are "pointers" to specific * spectral peaks returned by the CCD. * * NOTE: This command is available on hardware version: 2 * * Since: 0.1.31 **/ void ch_device_queue_get_ccd_calibration (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 *indexes) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (indexes != NULL); /* three uint16_t values */ ch_device_queue_add (device_queue, device, CH_CMD_GET_CCD_CALIBRATION, NULL, 0, (guint8 *) indexes, 3 * sizeof (guint16)); } /** * ch_device_queue_set_ccd_calibration: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @indexes: An array of red, green, blue indexes * * Sets the CCD spectral calibration indexes. These are "pointers" to specific * spectral peaks returned by the CCD. * * NOTE: This command is available on hardware version: 2 * * Since: 0.1.31 **/ void ch_device_queue_set_ccd_calibration (ChDeviceQueue *device_queue, GUsbDevice *device, const guint16 *indexes) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (indexes != NULL); g_return_if_fail (indexes[0] < CH_CCD_SPECTRAL_RESOLUTION); g_return_if_fail (indexes[1] < CH_CCD_SPECTRAL_RESOLUTION); g_return_if_fail (indexes[2] < CH_CCD_SPECTRAL_RESOLUTION); /* three uint16_t values */ ch_device_queue_add (device_queue, device, CH_CMD_SET_CCD_CALIBRATION, (const guint8 *) indexes, 3 * sizeof (guint16), NULL, 0); } /** * ch_device_queue_get_post_scale: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @post_scale: The post-scale value * * Gets the post scale value. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_post_scale (ChDeviceQueue *device_queue, GUsbDevice *device, gdouble *post_scale) { guint8 *buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (post_scale != NULL); *post_scale = 0.0f; buffer = g_new0 (guint8, sizeof (ChPackedFloat)); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_POST_SCALE, NULL, 0, buffer, sizeof(ChPackedFloat), g_free, ch_device_queue_buffer_to_double_cb, post_scale, NULL); } /** * ch_device_queue_set_post_scale: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @post_scale: The post-scale value * * Sets the post scale value. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_post_scale (ChDeviceQueue *device_queue, GUsbDevice *device, gdouble post_scale) { ChPackedFloat buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); /* convert from float to signed value */ ch_double_to_packed_float (post_scale, &buffer); ch_device_queue_add (device_queue, device, CH_CMD_SET_POST_SCALE, (guint8 *) &buffer, sizeof(buffer), NULL, 0); } /** * ch_device_queue_get_serial_number: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @serial_number: The device serial number * * Gets the device serial number. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_serial_number (ChDeviceQueue *device_queue, GUsbDevice *device, guint32 *serial_number) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (serial_number != NULL); *serial_number = 0; ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_SERIAL_NUMBER, NULL, 0, (guint8 *) serial_number, sizeof(guint32), NULL, ch_device_queue_buffer_uint32_from_le_cb, NULL, NULL); } /** * ch_device_queue_set_serial_number: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @serial_number: The device serial number * * Sets the device serial number. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_serial_number (ChDeviceQueue *device_queue, GUsbDevice *device, guint32 serial_number) { guint32 serial_le; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (serial_number > 0); serial_le = GUINT32_TO_LE (serial_number); ch_device_queue_add (device_queue, device, CH_CMD_SET_SERIAL_NUMBER, (const guint8 *) &serial_le, sizeof(serial_le), NULL, 0); } /** * ch_device_queue_get_leds: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @leds: The LED bitfield * * Gets the LED status. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_leds (ChDeviceQueue *device_queue, GUsbDevice *device, guint8 *leds) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (leds != NULL); *leds = 0; ch_device_queue_add (device_queue, device, CH_CMD_GET_LEDS, NULL, 0, leds, 1); } /** * ch_device_queue_set_leds: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @leds: The LEDs bitfield * @repeat: Sets the number of times to repeat the pattern * @on_time: Set the on time * @off_time: Set the off time * * Sets the LED status. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_leds (ChDeviceQueue *device_queue, GUsbDevice *device, guint8 leds, guint8 repeat, guint8 on_time, guint8 off_time) { guint8 buffer[4]; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (leds < 0x08); buffer[0] = leds; buffer[1] = repeat; buffer[2] = on_time; buffer[3] = off_time; ch_device_queue_add (device_queue, device, CH_CMD_SET_LEDS, (const guint8 *) buffer, sizeof (buffer), NULL, 0); } /** * ch_device_queue_get_pcb_errata: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @pcb_errata: The PCB errata, e.g. %CH_PCB_ERRATA_SWAPPED_LEDS * * Gets the PCB errata level. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_pcb_errata (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 *pcb_errata) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (pcb_errata != NULL); *pcb_errata = CH_PCB_ERRATA_NONE; ch_device_queue_add (device_queue, device, CH_CMD_GET_PCB_ERRATA, NULL, 0, (guint8 *) pcb_errata, sizeof (guint16)); } /** * ch_device_queue_set_pcb_errata: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @pcb_errata: The PCB errata, e.g. %CH_PCB_ERRATA_SWAPPED_LEDS * * Sets the PCB board errata. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_pcb_errata (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 pcb_errata) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); ch_device_queue_add (device_queue, device, CH_CMD_SET_PCB_ERRATA, (const guint8 *) &pcb_errata, sizeof (guint16), NULL, 0); } /** * ch_device_queue_get_remote_hash: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @remote_hash: A #ChSha1 * * Gets the remote hash stored on the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_remote_hash (ChDeviceQueue *device_queue, GUsbDevice *device, ChSha1 *remote_hash) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (remote_hash != NULL); ch_device_queue_add (device_queue, device, CH_CMD_GET_REMOTE_HASH, NULL, 0, (guint8 *) remote_hash, sizeof (ChSha1)); } /** * ch_device_queue_set_remote_hash: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @remote_hash: A #ChSha1 * * Sets the remote hash on the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_remote_hash (ChDeviceQueue *device_queue, GUsbDevice *device, ChSha1 *remote_hash) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); ch_device_queue_add (device_queue, device, CH_CMD_SET_REMOTE_HASH, (const guint8 *) remote_hash, sizeof (ChSha1), NULL, 0); } /** * ch_device_queue_write_eeprom: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @magic: The magic sekret string * * Writes values to the firmware to be set at device startup. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_write_eeprom (ChDeviceQueue *device_queue, GUsbDevice *device, const gchar *magic) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (magic != NULL); ch_device_queue_add (device_queue, device, CH_CMD_WRITE_EEPROM, (const guint8 *) magic, strlen(magic), NULL, 0); } static gboolean ch_device_queue_buffer_dark_offsets_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { CdColorRGB *value = (CdColorRGB *) user_data; guint16 tmp; /* check buffer size */ if (output_buffer_size != sizeof (guint16) * 3) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %" G_GSIZE_FORMAT ", got %" G_GSIZE_FORMAT, sizeof (guint16) * 3, output_buffer_size); return FALSE; } /* convert back into floating point */ tmp = cd_buffer_read_uint16_le (output_buffer + 0); value->R = (gdouble) tmp / (gdouble) 0xffff; tmp = cd_buffer_read_uint16_le (output_buffer + 2); value->G = (gdouble) tmp / (gdouble) 0xffff; tmp = cd_buffer_read_uint16_le (output_buffer + 4); value->B = (gdouble) tmp / (gdouble) 0xffff; return TRUE; } /** * ch_device_queue_get_dark_offsets: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @value: A #CdColorRGB * * Gets the device dark offsets. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_dark_offsets (ChDeviceQueue *device_queue, GUsbDevice *device, CdColorRGB *value) { guint8 *buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (value != NULL); buffer = g_new0 (guint8, sizeof(guint16) * 3); ch_device_queue_add_internal (device_queue, device, CH_CMD_GET_DARK_OFFSETS, NULL, 0, buffer, sizeof(guint16) * 3, g_free, ch_device_queue_buffer_dark_offsets_cb, value, NULL); } /** * ch_device_queue_set_dark_offsets: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @value: A #CdColorRGB * * Sets the device dark offsets. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_dark_offsets (ChDeviceQueue *device_queue, GUsbDevice *device, CdColorRGB *value) { guint16 buffer[3]; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); buffer[0] = value->R * (gdouble) 0xffff; buffer[1] = value->G * (gdouble) 0xffff; buffer[2] = value->B * (gdouble) 0xffff; ch_device_queue_add (device_queue, device, CH_CMD_SET_DARK_OFFSETS, (const guint8 *) buffer, sizeof(buffer), NULL, 0); } /** * ch_device_queue_take_reading_raw: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @take_reading: A raw reading value * * Take a raw reading from the sensor. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_take_reading_raw (ChDeviceQueue *device_queue, GUsbDevice *device, guint32 *take_reading) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (take_reading != NULL); ch_device_queue_add_internal (device_queue, device, CH_CMD_TAKE_READING_RAW, NULL, 0, (guint8 *) take_reading, sizeof(guint32), NULL, ch_device_queue_buffer_uint32_from_le_cb, NULL, NULL); } static gboolean ch_device_queue_buffer_triple_rgb_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { CdColorRGB *value = (CdColorRGB *) user_data; ChPackedFloat tmp; /* check buffer size */ if (output_buffer_size != sizeof (ChPackedFloat) * 3) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %" G_GSIZE_FORMAT ", got %" G_GSIZE_FORMAT, sizeof (ChPackedFloat) * 3, output_buffer_size); return FALSE; } /* convert back into floating point */ memcpy (&tmp, output_buffer + 0, sizeof (ChPackedFloat)); ch_packed_float_to_double (&tmp, &value->R); memcpy (&tmp, output_buffer + 4, sizeof (ChPackedFloat)); ch_packed_float_to_double (&tmp, &value->G); memcpy (&tmp, output_buffer + 8, sizeof (ChPackedFloat)); ch_packed_float_to_double (&tmp, &value->B); return TRUE; } /** * ch_device_queue_take_readings: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @value: The #CdColorRGB of the raw reading * * Take a RGB triplet of readings from the sensor without applying the * calibration matrix. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_take_readings (ChDeviceQueue *device_queue, GUsbDevice *device, CdColorRGB *value) { guint8 *buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (value != NULL); buffer = g_new0 (guint8, sizeof(ChPackedFloat) * 3); ch_device_queue_add_internal (device_queue, device, CH_CMD_TAKE_READINGS, NULL, 0, buffer, sizeof(ChPackedFloat) * 3, g_free, ch_device_queue_buffer_triple_rgb_cb, value, NULL); } static gboolean ch_device_queue_buffer_triple_xyz_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { CdColorXYZ *value = (CdColorXYZ *) user_data; ChPackedFloat tmp; /* check buffer size */ if (output_buffer_size != sizeof (ChPackedFloat) * 3) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %" G_GSIZE_FORMAT ", got %" G_GSIZE_FORMAT, sizeof (ChPackedFloat) * 3, output_buffer_size); return FALSE; } /* convert back into floating point */ memcpy (&tmp, output_buffer + 0, sizeof (ChPackedFloat)); ch_packed_float_to_double (&tmp, &value->X); memcpy (&tmp, output_buffer + 4, sizeof (ChPackedFloat)); ch_packed_float_to_double (&tmp, &value->Y); memcpy (&tmp, output_buffer + 8, sizeof (ChPackedFloat)); ch_packed_float_to_double (&tmp, &value->Z); return TRUE; } /** * ch_device_queue_take_readings_xyz: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @value: The #CdColorXYZ for a given calibration slot * * Take an XYZ fully cooked reading from the sensor. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_take_readings_xyz (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 calibration_index, CdColorXYZ *value) { guint8 *buffer; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (value != NULL); buffer = g_new0 (guint8, sizeof(ChPackedFloat) * 3); ch_device_queue_add_internal (device_queue, device, CH_CMD_TAKE_READING_XYZ, (guint8 *) &calibration_index, sizeof(guint16), buffer, sizeof(ChPackedFloat) * 3, g_free, ch_device_queue_buffer_triple_xyz_cb, value, NULL); } /** * ch_device_queue_reset: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * * Resets the device back to bootloader mode. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_reset (ChDeviceQueue *device_queue, GUsbDevice *device) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); ch_device_queue_add (device_queue, device, CH_CMD_RESET, NULL, 0, NULL, 0); } static guint8 ch_device_queue_calculate_checksum (const guint8 *data, gsize len) { guint8 checksum = 0xff; guint i; for (i = 0; i < len; i++) checksum ^= data[i]; return checksum; } /** * ch_device_queue_write_flash: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @address: The device EEPROM address * @data: (array length=len): Binary data * @len: The length of @data * * Write flash code to the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_write_flash (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 address, const guint8 *data, gsize len) { guint16 addr_le; guint8 buffer_tx[64]; /* set address, length, checksum, data */ addr_le = GUINT16_TO_LE (address); memcpy (buffer_tx + 0, &addr_le, 2); buffer_tx[2] = len; buffer_tx[3] = ch_device_queue_calculate_checksum (data, len); memcpy (buffer_tx + 4, data, len); ch_device_queue_add (device_queue, device, CH_CMD_WRITE_FLASH, buffer_tx, len + 4, NULL, 0); } /* tiny helper */ typedef struct { guint16 address; guint8 *data; gsize len; } ChDeviceQueueReadFlashHelper; static gboolean ch_device_queue_buffer_read_flash_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { ChDeviceQueueReadFlashHelper *helper = (ChDeviceQueueReadFlashHelper *) user_data; guint8 expected_checksum; /* check buffer size */ if (output_buffer_size != helper->len + 1) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %" G_GSIZE_FORMAT ", got %" G_GSIZE_FORMAT, helper->len + 1, output_buffer_size); return FALSE; } /* verify checksum */ expected_checksum = ch_device_queue_calculate_checksum (output_buffer + 1, helper->len); if (output_buffer[0] != expected_checksum) { g_set_error (error, 1, 0, "Checksum @0x%04x invalid", helper->address); return FALSE; } /* copy data to final location */ memcpy (helper->data, output_buffer + 1, helper->len); return TRUE; } /** * ch_device_queue_read_flash: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @address: The device EEPROM address * @data: (array length=len): Binary data * @len: The length of @data * * Read flash code from the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_read_flash (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 address, guint8 *data, gsize len) { ChDeviceQueueReadFlashHelper *helper; guint16 addr_le; guint8 *buffer; guint8 buffer_tx[3]; /* set address, length, checksum, data */ addr_le = GUINT16_TO_LE (address); memcpy (buffer_tx + 0, &addr_le, 2); buffer_tx[2] = len; /* create a helper structure as the checksum needs an extra * byte for the checksum */ helper = g_new0 (ChDeviceQueueReadFlashHelper, 1); helper->data = data; helper->len = len; helper->address = address; buffer = g_new0 (guint8, len + 1); ch_device_queue_add_internal (device_queue, device, CH_CMD_READ_FLASH, buffer_tx, sizeof(buffer_tx), buffer, len + 1, g_free, ch_device_queue_buffer_read_flash_cb, helper, g_free); } static gboolean ch_device_queue_buffer_verify_flash_cb (guint8 *output_buffer, gsize output_buffer_size, gpointer user_data, GError **error) { ChDeviceQueueReadFlashHelper *helper = (ChDeviceQueueReadFlashHelper *) user_data; guint16 i; guint8 expected_checksum; /* check buffer size */ if (output_buffer_size != helper->len + 1) { g_set_error (error, 1, 0, "Wrong output buffer size, expected %" G_GSIZE_FORMAT ", got %" G_GSIZE_FORMAT, helper->len + 1, output_buffer_size); return FALSE; } /* verify checksum */ expected_checksum = ch_device_queue_calculate_checksum (output_buffer + 1, helper->len); if (output_buffer[0] != expected_checksum) { g_set_error (error, 1, 0, "Checksum @0x%04x invalid", helper->address); return FALSE; } /* verify data */ if (memcmp (helper->data, output_buffer + 1, helper->len) != 0) { for (i = 0; i < helper->len; i++) { if (helper->data[i] != output_buffer[i + 1]) break; } g_set_error (error, 1, 0, "Failed to verify at @0x%04x", (guint) (helper->address + i)); return FALSE; } return TRUE; } static void ch_device_queue_verify_flash_helper_destroy (gpointer data) { ChDeviceQueueReadFlashHelper *helper = (ChDeviceQueueReadFlashHelper *) data; g_free (helper->data); g_free (helper); } /** * ch_device_queue_verify_flash: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @address: The device EEPROM address * @data: (array length=len): Binary data * @len: The length of @data * * Verify flash code from the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_verify_flash (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 address, const guint8 *data, gsize len) { ChDeviceQueueReadFlashHelper *helper; guint16 addr_le; guint8 *buffer; guint8 buffer_tx[3]; /* set address, length, checksum, data */ addr_le = GUINT16_TO_LE (address); memcpy (buffer_tx + 0, &addr_le, 2); buffer_tx[2] = len; /* create a helper structure as the checksum needs an extra * byte for the checksum */ helper = g_new0 (ChDeviceQueueReadFlashHelper, 1); helper->data = g_memdup (data, len + 1); helper->len = len; helper->address = address; buffer = g_new0 (guint8, len + 1); ch_device_queue_add_internal (device_queue, device, CH_CMD_READ_FLASH, buffer_tx, sizeof(buffer_tx), buffer, len + 1, g_free, ch_device_queue_buffer_verify_flash_cb, helper, ch_device_queue_verify_flash_helper_destroy); } /** * ch_device_queue_erase_flash: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @address: (array length=len): The device EEPROM address * @len: The length of @data * * Erase program code on the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_erase_flash (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 address, gsize len) { guint8 buffer_tx[4]; guint16 addr_le; guint16 len_le; /* set address, length, checksum, data */ addr_le = GUINT16_TO_LE (address); memcpy (buffer_tx + 0, &addr_le, 2); len_le = GUINT16_TO_LE (len); memcpy (buffer_tx + 2, &len_le, 2); ch_device_queue_add (device_queue, device, CH_CMD_ERASE_FLASH, buffer_tx, sizeof(buffer_tx), NULL, 0); } /** * ch_device_queue_set_flash_success: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @value: Success value * * Sets the firmware flash success value. * Be careful using this function as misuse can result in a 'bricked' * ColorHug device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_flash_success (ChDeviceQueue *device_queue, GUsbDevice *device, guint8 value) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); /* set flash success true */ ch_device_queue_add (device_queue, device, CH_CMD_SET_FLASH_SUCCESS, (guint8 *) &value, 1, NULL, 0); } /** * ch_device_queue_boot_flash: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * * Boots the device from bootloader to firmware mode. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_boot_flash (ChDeviceQueue *device_queue, GUsbDevice *device) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); /* boot into new code */ ch_device_queue_add (device_queue, device, CH_CMD_BOOT_FLASH, NULL, 0, NULL, 0); } /** * ch_device_queue_self_test: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * * Performs some self tests on the device. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_self_test (ChDeviceQueue *device_queue, GUsbDevice *device) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); /* do a really simple self test */ ch_device_queue_add (device_queue, device, CH_CMD_SELF_TEST, NULL, 0, NULL, 0); } /** * ch_device_queue_get_hardware_version: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @hw_version: The hardware version * * Gets the hardware version. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_hardware_version (ChDeviceQueue *device_queue, GUsbDevice *device, guint8 *hw_version) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (hw_version != NULL); ch_device_queue_add (device_queue, device, CH_CMD_GET_HARDWARE_VERSION, NULL, 0, hw_version, 1); } /** * ch_device_queue_get_owner_name: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @name: The owner name * * Gets the owner name. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_owner_name (ChDeviceQueue *device_queue, GUsbDevice *device, gchar *name) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (name != NULL); ch_device_queue_add (device_queue, device, CH_CMD_GET_OWNER_NAME, NULL, 0, (guint8 *) name, sizeof(gchar) * CH_OWNER_LENGTH_MAX); name[CH_OWNER_LENGTH_MAX-1] = 0; } /** * ch_device_queue_set_owner_name: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @name: The owner name * * Sets the owner name. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_owner_name (ChDeviceQueue *device_queue, GUsbDevice *device, const gchar *name) { gchar buf[CH_OWNER_LENGTH_MAX]; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (name != NULL); memset(buf, 0, CH_OWNER_LENGTH_MAX); (void)g_strlcpy(buf, name, CH_OWNER_LENGTH_MAX); ch_device_queue_add (device_queue, device, CH_CMD_SET_OWNER_NAME, (const guint8 *) buf, sizeof(gchar) * CH_OWNER_LENGTH_MAX, NULL, 0); } /** * ch_device_queue_get_owner_email: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @email: An email address * * Gets the owner email address. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_owner_email (ChDeviceQueue *device_queue, GUsbDevice *device, gchar *email) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (email != NULL); ch_device_queue_add (device_queue, device, CH_CMD_GET_OWNER_EMAIL, NULL, 0, (guint8 *) email, sizeof(gchar) * CH_OWNER_LENGTH_MAX); email[CH_OWNER_LENGTH_MAX-1] = 0; } /** * ch_device_queue_set_owner_email: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @email: An email address * * Sets the owner email address. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_owner_email (ChDeviceQueue *device_queue, GUsbDevice *device, const gchar *email) { gchar buf[CH_OWNER_LENGTH_MAX]; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (email != NULL); memset (buf, 0, CH_OWNER_LENGTH_MAX); (void)g_strlcpy (buf, email, CH_OWNER_LENGTH_MAX); ch_device_queue_add (device_queue, device, CH_CMD_SET_OWNER_EMAIL, (const guint8 *) buf, sizeof(gchar) * CH_OWNER_LENGTH_MAX, NULL, 0); } /** * ch_device_queue_take_reading_array: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @reading_array: An array of raw readings * * Get an array of raw readings in quick succession. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_take_reading_array (ChDeviceQueue *device_queue, GUsbDevice *device, guint8 *reading_array) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (reading_array != NULL); ch_device_queue_add (device_queue, device, CH_CMD_TAKE_READING_ARRAY, NULL, 0, reading_array, 30); } /** * ch_device_queue_get_measure_mode: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @measure_mode: The #ChMeasureMode, e.g. %CH_MEASURE_MODE_DURATION * * Gets the measurement mode. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_get_measure_mode (ChDeviceQueue *device_queue, GUsbDevice *device, ChMeasureMode *measure_mode) { g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (measure_mode != NULL); ch_device_queue_add (device_queue, device, CH_CMD_GET_MEASURE_MODE, NULL, 0, (guint8 *) measure_mode, 1); } /** * ch_device_queue_set_measure_mode: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @measure_mode: The #ChMeasureMode, e.g. %CH_MEASURE_MODE_DURATION * * Sets the measurement mode. * * NOTE: This command is available on hardware version: 1 & 2 * * Since: 0.1.29 **/ void ch_device_queue_set_measure_mode (ChDeviceQueue *device_queue, GUsbDevice *device, ChMeasureMode measure_mode) { guint8 tmp = measure_mode; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); ch_device_queue_add (device_queue, device, CH_CMD_SET_MEASURE_MODE, &tmp, 1, NULL, 0); } static void ch_device_queue_write_sram_internal (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 address, const guint8 *data, gsize len) { guint16 addr_le; guint8 buffer_tx[CH_USB_HID_EP_SIZE]; /* set address, length, checksum, data */ addr_le = GUINT16_TO_LE (address); memcpy (buffer_tx + 0, &addr_le, 2); buffer_tx[2] = len; memcpy (buffer_tx + 3, data, len); ch_device_queue_add (device_queue, device, CH_CMD_WRITE_SRAM, buffer_tx, len + 3, NULL, 0); } /** * ch_device_queue_write_sram: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @address: The device memory address * @data: (array length=len): The binary data * @len: Size of @data * * Writes binary data to the SRAM. * * NOTE: This command is available on hardware version: 2 * * Since: 0.1.29 **/ void ch_device_queue_write_sram (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 address, const guint8 *data, gsize len) { gsize chunk_len = 60; guint idx = 0; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (data != NULL); g_return_if_fail (len > 0); /* write in 60 byte chunks */ do { if (idx + chunk_len > len) chunk_len = len - idx; g_debug ("Writing SRAM at %04x size %" G_GSIZE_FORMAT, idx, chunk_len); ch_device_queue_write_sram_internal (device_queue, device, idx, data + idx, chunk_len); idx += chunk_len; } while (idx < len); } static void ch_device_queue_read_sram_internal (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 address, guint8 *data, gsize len) { guint16 addr_le; guint8 buffer_tx[3]; /* set address, length, data */ addr_le = GUINT16_TO_LE (address); memcpy (buffer_tx + 0, &addr_le, 2); buffer_tx[2] = len; ch_device_queue_add (device_queue, device, CH_CMD_READ_SRAM, buffer_tx, sizeof(buffer_tx), data, len); } /** * ch_device_queue_read_sram: * @device_queue: A #ChDeviceQueue * @device: A #GUsbDevice * @address: The device memory address * @data: (array length=len): The binary data * @len: Size of @data * * Reads binary data from the SRAM. * * NOTE: This command is available on hardware version: 2 * * Since: 0.1.29 **/ void ch_device_queue_read_sram (ChDeviceQueue *device_queue, GUsbDevice *device, guint16 address, guint8 *data, gsize len) { gsize chunk_len = 60; guint idx = 0; g_return_if_fail (CH_IS_DEVICE_QUEUE (device_queue)); g_return_if_fail (G_USB_IS_DEVICE (device)); g_return_if_fail (data != NULL); g_return_if_fail (len > 0); /* write in 60 byte chunks */ do { if (idx + chunk_len > len) chunk_len = len - idx; g_debug ("Reading SRAM at %04x size %" G_GSIZE_FORMAT, idx, chunk_len); ch_device_queue_read_sram_internal (device_queue, device, idx, data + idx, chunk_len); idx += chunk_len; } while (idx < len); } /**********************************************************************/ static void ch_device_queue_class_init (ChDeviceQueueClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = ch_device_queue_finalize; /** * ChDeviceQueueClass::device-failed: * @device_queue: the #ChDeviceQueue instance that emitted the signal * @device: the device that failed * @error_message: the error that caused the failure * * The ::device-failed signal is emitted when a device has failed. **/ signals[SIGNAL_DEVICE_FAILED] = g_signal_new ("device-failed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ChDeviceQueueClass, device_failed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_STRING); /** * ChDeviceQueueClass::progress-changed: * @device_queue: the #ChDeviceQueue instance that emitted the signal * @percentage: the percentage complete the action is * @error_message: the error that caused the failure * * The ::progress-changed signal is emitted when a the commands * are being submitted. **/ signals[SIGNAL_PROGRESS_CHANGED] = g_signal_new ("progress-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ChDeviceQueueClass, progress_changed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_UINT); } static void ch_device_queue_init (ChDeviceQueue *device_queue) { ChDeviceQueuePrivate *priv = GET_PRIVATE (device_queue); priv->data_array = g_ptr_array_new_with_free_func ((GDestroyNotify) ch_device_queue_data_free); priv->devices_in_use = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } static void ch_device_queue_finalize (GObject *object) { ChDeviceQueue *device_queue = CH_DEVICE_QUEUE (object); ChDeviceQueuePrivate *priv = GET_PRIVATE (device_queue); g_ptr_array_unref (priv->data_array); g_hash_table_unref (priv->devices_in_use); G_OBJECT_CLASS (ch_device_queue_parent_class)->finalize (object); } /** * ch_device_queue_new: * * Since: 0.1.29 **/ ChDeviceQueue * ch_device_queue_new (void) { ChDeviceQueue *device_queue; device_queue = g_object_new (CH_TYPE_DEVICE_QUEUE, NULL); return CH_DEVICE_QUEUE (device_queue); }