config/patches/base/libfprint/532d_support.patch

4506 lines
141 KiB
Diff
Raw Normal View History

diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb
index 6e3fd396..9862d188 100644
--- a/data/autosuspend.hwdb
+++ b/data/autosuspend.hwdb
@@ -190,6 +190,17 @@ usb:v27C6p6A94*
ID_AUTOSUSPEND=1
ID_PERSIST=0
+# Supported by libfprint driver goodixtls511
+usb:v27C6p5110*
+ ID_AUTOSUSPEND=1
+ ID_PERSIST=0
+
+# Supported by libfprint driver goodixtls53xd
+usb:v27C6p538D*
+usb:v27C6p532D*
+ ID_AUTOSUSPEND=1
+ ID_PERSIST=0
+
# Supported by libfprint driver nb1010
usb:v298Dp1010*
ID_AUTOSUSPEND=1
@@ -343,7 +354,6 @@ usb:v1C7Ap0300*
usb:v1C7Ap0575*
usb:v1C7Ap0576*
usb:v27C6p5042*
-usb:v27C6p5110*
usb:v27C6p5117*
usb:v27C6p5120*
usb:v27C6p5125*
@@ -351,13 +361,11 @@ usb:v27C6p5201*
usb:v27C6p521D*
usb:v27C6p5301*
usb:v27C6p530C*
-usb:v27C6p532D*
usb:v27C6p5335*
usb:v27C6p533C*
usb:v27C6p5381*
usb:v27C6p5385*
usb:v27C6p538C*
-usb:v27C6p538D*
usb:v27C6p5395*
usb:v27C6p5503*
usb:v27C6p550A*
diff --git a/libfprint/drivers/goodixtls/goodix.c b/libfprint/drivers/goodixtls/goodix.c
new file mode 100644
index 00000000..0d2f0b7f
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodix.c
@@ -0,0 +1,1448 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#include "fpi-log.h"
+#include "fpi-ssm.h"
+#include "fpi-usb-transfer.h"
+#define FP_COMPONENT "goodixtls"
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <gusb.h>
+#include <openssl/ssl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "drivers_api.h"
+#include "goodix.h"
+#include "goodix_proto.h"
+#include "goodixtls.h"
+
+typedef struct
+{
+ GoodixTlsServer *tls_hop;
+
+ GSource *timeout;
+
+ guint8 cmd;
+
+ gboolean ack;
+ gboolean reply;
+
+ GoodixCmdCallback callback;
+ gpointer user_data;
+
+ guint8 *data;
+ guint32 length;
+
+ GoodixCallbackInfo *tls_ready_callback;
+
+ GCancellable *transfer_cancel_tkn;
+ gboolean inited;
+} FpiDeviceGoodixTlsPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(FpiDeviceGoodixTls, fpi_device_goodixtls,
+ FP_TYPE_IMAGE_DEVICE);
+
+// TODO remove every GDestroyNotify
+// TODO add cmd timeouts
+
+gchar *data_to_str(guint8 *data, guint32 length)
+{
+ gchar *string = g_malloc((length * 2) + 1);
+
+ for (guint32 i = 0; i < length; i++)
+ sprintf(string + i * 2, "%02x", data[i]);
+
+ return string;
+}
+
+// ---- GOODIX RECEIVE SECTION START ----
+
+void goodix_receive_done(FpDevice *dev, guint8 *data, guint16 length,
+ GError *error)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ GoodixCmdCallback callback = priv->callback;
+ gpointer user_data = priv->user_data;
+
+ if (!(priv->ack || priv->reply))
+ return;
+
+ goodix_reset_state(dev);
+ if (!error)
+ fp_dbg("Completed command: 0x%02x", priv->cmd);
+
+ if (callback)
+ callback(dev, data, length, user_data, error);
+}
+
+void goodix_receive_none(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error)
+{
+ g_autofree GoodixCallbackInfo *cb_info = user_data;
+ GoodixNoneCallback callback = (GoodixNoneCallback)cb_info->callback;
+
+ callback(dev, cb_info->user_data, error);
+}
+
+void goodix_receive_default(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error)
+{
+ g_autofree GoodixCallbackInfo *cb_info = user_data;
+ GoodixDefaultCallback callback = (GoodixDefaultCallback)cb_info->callback;
+
+ callback(dev, data, length, cb_info->user_data, error);
+}
+
+void goodix_receive_success(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error)
+{
+ g_autofree GoodixCallbackInfo *cb_info = user_data;
+ GoodixSuccessCallback callback = (GoodixSuccessCallback)cb_info->callback;
+
+ if (error)
+ {
+ callback(dev, FALSE, cb_info->user_data, error);
+ return;
+ }
+
+ if (length != sizeof(guint8) * 2)
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid success reply length: %d", length);
+ callback(dev, FALSE, cb_info->user_data, error);
+ return;
+ }
+
+ callback(dev, data[0] == 0x00 ? FALSE : TRUE, cb_info->user_data, NULL);
+}
+
+void goodix_receive_reset(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error)
+{
+ g_autofree GoodixCallbackInfo *cb_info = user_data;
+ GoodixResetCallback callback = (GoodixResetCallback)cb_info->callback;
+
+ if (error)
+ {
+ callback(dev, FALSE, 0, cb_info->user_data, error);
+ return;
+ }
+
+ if (length != sizeof(guint8) + sizeof(guint16))
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid reset reply length: %d", length);
+ callback(dev, FALSE, 0, cb_info->user_data, error);
+ return;
+ }
+
+ callback(dev, data[0] == 0x00 ? FALSE : TRUE,
+ GUINT16_FROM_LE(*(guint16 *)(data + sizeof(guint8))), // TODO
+ cb_info->user_data, NULL);
+}
+
+void goodix_receive_preset_psk_read(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error)
+{
+ guint32 psk_len;
+ g_autofree GoodixCallbackInfo *cb_info = user_data;
+ GoodixPresetPskReadCallback callback =
+ (GoodixPresetPskReadCallback)cb_info->callback;
+
+ if (error)
+ {
+ callback(dev, FALSE, 0x00000000, NULL, 0, cb_info->user_data, error);
+ return;
+ }
+
+ if (length < sizeof(guint8))
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid preset PSK read reply length: %d", length);
+ callback(dev, FALSE, 0x00000000, NULL, 0, cb_info->user_data, error);
+ return;
+ }
+
+ if (data[0] != 0x00)
+ {
+ callback(dev, FALSE, 0x00000000, NULL, 0, cb_info->user_data, NULL);
+ return;
+ }
+
+ if (length < sizeof(guint8) + sizeof(GoodixPresetPsk))
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid preset PSK read reply length: %d", length);
+ callback(dev, FALSE, 0x00000000, NULL, 0, cb_info->user_data, error);
+ return;
+ }
+
+ GoodixPresetPskResponse *response = (GoodixPresetPskResponse*)(data + sizeof(guint8));
+ psk_len = response->length;
+ if (length < psk_len + sizeof(guint8) + sizeof(GoodixPresetPskResponse))
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid preset PSK read reply length: %d", length);
+ callback(dev, FALSE, 0x00000000, NULL, 0, cb_info->user_data, error);
+ return;
+ }
+
+ callback(dev, TRUE,
+ GUINT32_FROM_LE(response->flags),
+ data + sizeof(guint8) + sizeof(GoodixPresetPskResponse), psk_len,
+ cb_info->user_data, NULL);
+}
+
+void goodix_receive_preset_psk_write(FpDevice *dev, guint8 *data,
+ guint16 length, gpointer user_data,
+ GError *error)
+{
+ g_autofree GoodixCallbackInfo *cb_info = user_data;
+ GoodixSuccessCallback callback = (GoodixSuccessCallback)cb_info->callback;
+
+ if (error)
+ {
+ callback(dev, FALSE, cb_info->user_data, error);
+ return;
+ }
+
+ if (length < sizeof(guint8))
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid preset PSK write reply length: %d", length);
+ callback(dev, FALSE, cb_info->user_data, error);
+ return;
+ }
+
+ callback(dev, data[0] == 0x00 ? TRUE : FALSE, cb_info->user_data, NULL);
+}
+
+void goodix_receive_firmware_version(FpDevice *dev, guint8 *data,
+ guint16 length, gpointer user_data,
+ GError *error)
+{
+ g_autofree gchar *payload = g_malloc(length + sizeof(gchar));
+ g_autofree GoodixCallbackInfo *cb_info = user_data;
+ GoodixFirmwareVersionCallback callback =
+ (GoodixFirmwareVersionCallback)cb_info->callback;
+
+ if (error)
+ {
+ callback(dev, NULL, cb_info->user_data, error);
+ return;
+ }
+
+ memcpy(payload, data, length);
+
+ // Some device send the firmware without the null terminator
+ payload[length] = 0x00;
+
+ callback(dev, payload, cb_info->user_data, NULL);
+}
+
+void goodix_receive_ack(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ GoodixAck *ack = (GoodixAck *)data;
+ guint8 cmd;
+
+ if (length != sizeof(GoodixAck))
+ {
+ fp_warn("Invalid ACK length: %d", length);
+ return;
+ }
+
+ if (!ack->always_true)
+ {
+ // Warn about error.
+ fp_warn("Invalid ACK flags: 0x%02x", data[sizeof(guint8)]);
+ return;
+ }
+
+ cmd = ack->cmd;
+
+ if (ack->has_no_config)
+ fp_warn("MCU has no config");
+
+ if (priv->cmd != cmd)
+ {
+ fp_warn("Invalid ACK command: 0x%02x", cmd);
+ return;
+ }
+
+ if (!priv->ack)
+ {
+ fp_warn("Didn't excpect an ACK for command: 0x%02x", priv->cmd);
+ return;
+ }
+
+ if (!priv->reply)
+ {
+ G_DEBUG_HERE();
+ goodix_receive_done(dev, NULL, 0, NULL);
+ return;
+ }
+
+ priv->ack = FALSE;
+}
+
+void goodix_receive_protocol(FpDevice *dev, guint8 *data, guint32 length)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ guint8 cmd;
+ g_autofree guint8 *payload = NULL;
+ guint16 payload_len;
+ gboolean valid_checksum, valid_null_checksum; // TODO implement checksum.
+
+ if (!goodix_decode_protocol(data, length, &cmd, &payload, &payload_len,
+ &valid_checksum, &valid_null_checksum))
+ {
+ fp_err("Incomplete, size: %d", length);
+ // Protocol is not full, we still need data.
+ // TODO implement protocol assembling.
+ return;
+ }
+
+ if (cmd == GOODIX_CMD_ACK)
+ {
+ fp_dbg("got ack");
+ goodix_receive_ack(dev, payload, payload_len, NULL, NULL);
+ return;
+ }
+
+ if (priv->cmd != cmd)
+ {
+ fp_warn("Invalid protocol command: 0x%02x", cmd);
+ return;
+ }
+
+ if (!priv->reply)
+ {
+ fp_warn("Didn't expect a reply for command: 0x%02x", priv->cmd);
+ return;
+ }
+
+ if (priv->ack)
+ fp_warn("Didn't got ACK for command: 0x%02x", priv->cmd);
+
+ goodix_receive_done(dev, payload, payload_len, NULL);
+}
+
+void goodix_receive_pack(FpDevice *dev, guint8 *data, guint32 length)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ guint8 flags;
+ g_autofree guint8 *payload = NULL;
+ guint16 payload_len;
+ gboolean valid_checksum; // TODO implement checksum.
+
+ priv->data = g_realloc(priv->data, priv->length + length);
+ memcpy(priv->data + priv->length, data, length);
+ priv->length += length;
+
+ if (!goodix_decode_pack(priv->data, priv->length, &flags, &payload,
+ &payload_len, &valid_checksum))
+ {
+ // Packet is not full, we still need data.
+ fp_dbg("not full packet");
+ return;
+ }
+
+ switch (flags)
+ {
+ case GOODIX_FLAGS_MSG_PROTOCOL:
+ fp_dbg("Got protocol msg");
+ goodix_receive_protocol(dev, payload, payload_len);
+ break;
+
+ case GOODIX_FLAGS_TLS:
+ fp_dbg("Got TLS msg");
+ goodix_receive_done(dev, payload, payload_len, NULL);
+
+ // TLS message sending it to TLS server.
+ // TODO
+ break;
+
+ case GOODIX_FLAGS_TLS_DATA:
+ fp_dbg("Got TLS data msg");
+ // GOODIX 52xd: Remove first 9 to get valid TLS content
+ goodix_receive_done(dev, payload+9, payload_len-9, NULL);
+ break;
+
+ default:
+ fp_warn("Unknown flags: 0x%02x", flags);
+ break;
+ }
+
+ g_clear_pointer(&priv->data, g_free);
+ priv->length = 0;
+}
+
+void goodix_receive_data_cb(FpiUsbTransfer *transfer, FpDevice *dev,
+ gpointer user_data, GError *error)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ if (g_cancellable_is_cancelled(priv->transfer_cancel_tkn))
+ {
+ fp_dbg("transfer cancelled, aborting read loop...");
+ return;
+ }
+ if (error)
+ {
+ // Warn about error and free it.
+ fp_warn("Receive data error: %s", error->message);
+ g_error_free(error);
+
+ // Retry receiving data and return.
+ goodix_receive_data(dev);
+ return;
+ }
+
+ goodix_receive_pack(dev, transfer->buffer, transfer->actual_length);
+
+ goodix_receive_data(dev);
+}
+
+void goodix_receive_timeout_cb(FpDevice *dev, gpointer user_data)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ GError *error = NULL;
+
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ "Command timed out: 0x%02x", priv->cmd);
+ goodix_receive_done(dev, NULL, 0, error);
+}
+
+void goodix_start_read_loop(FpDevice *dev)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ if (priv->inited)
+ {
+ // Already going
+ return;
+ }
+ else
+ {
+ priv->inited = TRUE;
+ }
+ if (g_cancellable_is_cancelled(priv->transfer_cancel_tkn))
+ {
+ g_cancellable_reset(priv->transfer_cancel_tkn);
+ }
+
+ goodix_receive_data(dev);
+}
+
+void goodix_receive_data(FpDevice *dev)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsClass *class = FPI_DEVICE_GOODIXTLS_GET_CLASS(self);
+ FpiUsbTransfer *transfer = fpi_usb_transfer_new(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ transfer->short_is_error = FALSE;
+
+ fpi_usb_transfer_fill_bulk(transfer, class->ep_in,
+ GOODIX_EP_IN_MAX_BUF_SIZE);
+
+ fpi_usb_transfer_submit(transfer, 0, priv->transfer_cancel_tkn,
+ goodix_receive_data_cb, NULL);
+}
+
+// ---- GOODIX RECEIVE SECTION END ----
+
+// -----------------------------------------------------------------------------
+
+// ---- GOODIX SEND SECTION START ----
+
+gboolean goodix_send_data(FpDevice *dev, guint8 *data, guint32 length,
+ GDestroyNotify free_func, GError **error)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsClass *class = FPI_DEVICE_GOODIXTLS_GET_CLASS(self);
+
+ for (guint32 i = 0; i < length; i += GOODIX_EP_OUT_MAX_BUF_SIZE)
+ {
+ FpiUsbTransfer *transfer = fpi_usb_transfer_new(dev);
+ transfer->short_is_error = TRUE;
+
+ fpi_usb_transfer_fill_bulk_full(transfer, class->ep_out, data + i,
+ GOODIX_EP_OUT_MAX_BUF_SIZE, NULL);
+
+ if (!fpi_usb_transfer_submit_sync(transfer, GOODIX_TIMEOUT,
+ error))
+ {
+ if (free_func)
+ free_func(data);
+ fpi_usb_transfer_unref(transfer);
+ return FALSE;
+ }
+ fpi_usb_transfer_unref(transfer);
+ }
+
+ if (free_func)
+ free_func(data);
+ return TRUE;
+}
+
+gboolean goodix_send_pack(FpDevice *dev, guint8 flags, guint8 *payload,
+ guint16 length, GDestroyNotify free_func,
+ GError **error)
+{
+ guint8 *data;
+ guint32 data_len;
+
+ goodix_encode_pack(flags, payload, length, TRUE, &data, &data_len);
+ if (free_func)
+ free_func(payload);
+
+ return goodix_send_data(dev, data, data_len, g_free, error);
+}
+
+void goodix_send_protocol(
+ FpDevice *dev, guint8 cmd, guint8 *payload, guint16 length,
+ GDestroyNotify free_func, gboolean calc_checksum, guint timeout_ms,
+ gboolean reply, GoodixCmdCallback callback, gpointer user_data)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ GError *error = NULL;
+ guint8 *data;
+ guint32 data_len;
+
+ if (priv->ack || priv->reply || priv->timeout)
+ {
+ // A command is already running.
+ fp_warn("A command is already running: 0x%02x", priv->cmd);
+ if (free_func)
+ free_func(payload);
+ return;
+ }
+
+ fp_dbg("Running command: 0x%02x", cmd);
+
+ if (timeout_ms)
+ priv->timeout = fpi_device_add_timeout(
+ dev, timeout_ms, goodix_receive_timeout_cb, NULL, NULL);
+ priv->cmd = cmd;
+ priv->ack = TRUE;
+ priv->reply = reply;
+ priv->callback = callback;
+ priv->user_data = user_data;
+
+ goodix_encode_protocol(cmd, payload, length, calc_checksum, FALSE,
+ &data, &data_len);
+ if (free_func)
+ free_func(payload);
+
+ if (!goodix_send_pack(dev, GOODIX_FLAGS_MSG_PROTOCOL, data, data_len,
+ g_free, &error))
+ {
+ goodix_receive_done(dev, NULL, 0, error);
+ return;
+ };
+}
+void goodix_send_nop(FpDevice *dev, GoodixNoneCallback callback,
+ gpointer user_data)
+{
+ GoodixNop payload = {.unknown = 0x00000000};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_NOP, (guint8 *)&payload,
+ sizeof(payload), NULL, FALSE, GOODIX_TIMEOUT, FALSE,
+ goodix_receive_none, cb_info);
+ goodix_receive_done(dev, NULL, 0, NULL);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_NOP, (guint8 *)&payload, sizeof(payload),
+ NULL, FALSE, GOODIX_TIMEOUT, FALSE, NULL, NULL);
+ goodix_receive_done(dev, NULL, 0, NULL);
+}
+
+void goodix_send_mcu_get_image(FpDevice *dev, GoodixImageCallback callback,
+ gpointer user_data)
+{
+ GoodixDefault payload = {.unused_flags = 0x01};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_GET_IMAGE, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ goodix_receive_default, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_GET_IMAGE, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ NULL, NULL);
+}
+
+void goodix_send_mcu_switch_to_fdt_down(FpDevice *dev, guint8 *mode,
+ guint16 length,
+ GDestroyNotify free_func,
+ GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_DOWN, mode, length,
+ free_func, TRUE, 0, TRUE, goodix_receive_default,
+ cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_DOWN, mode, length,
+ free_func, TRUE, 0, TRUE, NULL, NULL);
+}
+
+void goodix_send_mcu_switch_to_fdt_up(FpDevice *dev, guint8 *mode,
+ guint16 length, GDestroyNotify free_func,
+ GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_UP, mode, length,
+ free_func, TRUE, 0, TRUE, goodix_receive_default,
+ cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_UP, mode, length,
+ free_func, TRUE, 0, TRUE, NULL, NULL);
+}
+
+void goodix_send_mcu_switch_to_fdt_mode(FpDevice *dev, guint8 *mode,
+ guint16 length,
+ GDestroyNotify free_func,
+ GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_MODE, mode, length,
+ free_func, TRUE, 0, TRUE, goodix_receive_default,
+ cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_MODE, mode, length,
+ free_func, TRUE, 0, TRUE, NULL, NULL);
+}
+
+void goodix_send_nav_0(FpDevice *dev, GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ GoodixDefault payload = {.unused_flags = 0x01};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_NAV_0, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ goodix_receive_default, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_NAV_0, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE, NULL,
+ NULL);
+}
+
+void goodix_send_mcu_switch_to_idle_mode(FpDevice *dev, guint8 sleep_time,
+ GoodixNoneCallback callback,
+ gpointer user_data)
+{
+ GoodixMcuSwitchToIdleMode payload = {.sleep_time = sleep_time};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_IDLE_MODE,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, FALSE, goodix_receive_none, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_IDLE_MODE,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, FALSE, NULL, NULL);
+}
+
+void goodix_send_write_sensor_register(FpDevice *dev, guint16 address,
+ guint16 value,
+ GoodixNoneCallback callback,
+ gpointer user_data)
+{
+ // Only support one address and one value
+
+ GoodixWriteSensorRegister payload = {.multiples = FALSE,
+ .address = GUINT16_TO_LE(address),
+ .value = GUINT16_TO_LE(value)};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_WRITE_SENSOR_REGISTER,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, FALSE, goodix_receive_none, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_WRITE_SENSOR_REGISTER,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, FALSE, NULL, NULL);
+}
+
+void goodix_send_read_sensor_register(FpDevice *dev, guint16 address,
+ guint8 length,
+ GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ // Only support one address
+
+ GoodixReadSensorRegister payload = {
+ .multiples = FALSE, .address = GUINT16_TO_LE(address), .length = length};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_READ_SENSOR_REGISTER,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, TRUE, goodix_receive_default, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_READ_SENSOR_REGISTER, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE, NULL,
+ NULL);
+}
+
+void goodix_send_upload_config_mcu(FpDevice *dev, guint8 *config,
+ guint16 length, GDestroyNotify free_func,
+ GoodixSuccessCallback callback,
+ gpointer user_data)
+{
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_UPLOAD_CONFIG_MCU, config, length,
+ free_func, TRUE, GOODIX_TIMEOUT, TRUE,
+ goodix_receive_success, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_UPLOAD_CONFIG_MCU, config, length,
+ free_func, TRUE, GOODIX_TIMEOUT, TRUE, NULL, NULL);
+}
+
+void goodix_send_set_powerdown_scan_frequency(FpDevice *dev,
+ guint16 powerdown_scan_frequency,
+ GoodixSuccessCallback callback,
+ gpointer user_data)
+{
+ GoodixSetPowerdownScanFrequency payload = {
+ .powerdown_scan_frequency = GUINT16_TO_LE(powerdown_scan_frequency)};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_SET_POWERDOWN_SCAN_FREQUENCY,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, TRUE, goodix_receive_success, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_SET_POWERDOWN_SCAN_FREQUENCY,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, TRUE, NULL, NULL);
+}
+
+void goodix_send_enable_chip(FpDevice *dev, gboolean enable,
+ GoodixNoneCallback callback, gpointer user_data)
+{
+ GoodixEnableChip payload = {.enable = enable ? TRUE : FALSE};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_ENABLE_CHIP, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, FALSE,
+ goodix_receive_none, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_ENABLE_CHIP, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, FALSE, NULL,
+ NULL);
+}
+
+void goodix_send_reset(FpDevice *dev, gboolean reset_sensor, guint8 sleep_time,
+ GoodixResetCallback callback, gpointer user_data)
+{
+ // Only support reset sensor
+
+ GoodixReset payload = {.soft_reset_mcu = FALSE,
+ .reset_sensor = reset_sensor ? TRUE : FALSE,
+ .sleep_time = sleep_time};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_RESET, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ goodix_receive_reset, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_RESET, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE, NULL,
+ NULL);
+}
+
+void goodix_send_firmware_version(FpDevice *dev,
+ GoodixFirmwareVersionCallback callback,
+ gpointer user_data)
+{
+ GoodixNone payload = {};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_FIRMWARE_VERSION, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ goodix_receive_firmware_version, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_FIRMWARE_VERSION, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE, NULL,
+ NULL);
+}
+
+void goodix_send_query_mcu_state(FpDevice *dev, GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ GoodixQueryMcuState payload = {.unused_flags = 0x55};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_QUERY_MCU_STATE, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ goodix_receive_default, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_QUERY_MCU_STATE, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE, NULL,
+ NULL);
+}
+
+void goodix_send_request_tls_connection(FpDevice *dev,
+ GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ GoodixNone payload = {};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_REQUEST_TLS_CONNECTION,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE, 0,
+ TRUE, goodix_receive_default, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_REQUEST_TLS_CONNECTION,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, TRUE, NULL, NULL);
+}
+
+void goodix_send_tls_successfully_established(FpDevice *dev,
+ GoodixNoneCallback callback,
+ gpointer user_data)
+{
+ GoodixNone payload = {};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_TLS_SUCCESSFULLY_ESTABLISHED,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, FALSE, goodix_receive_none, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_TLS_SUCCESSFULLY_ESTABLISHED,
+ (guint8 *)&payload, sizeof(payload), NULL, TRUE,
+ GOODIX_TIMEOUT, FALSE, NULL, NULL);
+}
+
+void goodix_send_read_otp(FpDevice *dev, GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ GoodixNone payload = {};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_READ_OTP, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ goodix_receive_default, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_READ_OTP, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ NULL, NULL);
+}
+
+void goodix_send_preset_psk_write(FpDevice *dev, guint32 flags, guint8 *psk,
+ guint16 length, GDestroyNotify free_func,
+ GoodixSuccessCallback callback,
+ gpointer user_data)
+{
+ // Only support one flags, one payload and one length
+
+ guint8 *payload = g_malloc(sizeof(GoodixPresetPsk) + length);
+ GoodixPresetPsk *preset_psk = (GoodixPresetPsk *)payload;
+ GoodixCallbackInfo *cb_info;
+
+ preset_psk->flags = GUINT32_TO_LE(flags);
+ preset_psk->length = GUINT32_TO_LE(length);
+ memcpy(payload + sizeof(GoodixPresetPsk), psk, length);
+ if (free_func)
+ free_func(psk);
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_PRESET_PSK_WRITE, payload,
+ sizeof(payload) + length, g_free, TRUE, GOODIX_TIMEOUT,
+ TRUE, goodix_receive_preset_psk_write, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_PRESET_PSK_WRITE, payload,
+ sizeof(payload) + length, g_free, TRUE, GOODIX_TIMEOUT,
+ TRUE, NULL, NULL);
+}
+
+void goodix_send_preset_psk_read(FpDevice *dev, guint32 flags, guint16 length,
+ GoodixPresetPskReadCallback callback,
+ gpointer user_data)
+{
+ GoodixPresetPsk payload = {.flags = GUINT32_TO_LE(flags),
+ .length = GUINT32_TO_LE(length)};
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_PRESET_PSK_READ, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ goodix_receive_preset_psk_read, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_PRESET_PSK_READ, (guint8 *)&payload,
+ sizeof(payload), NULL, TRUE, GOODIX_TIMEOUT, TRUE, NULL,
+ NULL);
+}
+
+// ---- GOODIX SEND SECTION END ----
+
+// -----------------------------------------------------------------------------
+
+// ---- DEV SECTION START ----
+
+gboolean goodix_dev_init(FpDevice *dev, GError **error)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsClass *class = FPI_DEVICE_GOODIXTLS_GET_CLASS(self);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ priv->timeout = NULL;
+ priv->ack = FALSE;
+ priv->reply = FALSE;
+ priv->callback = NULL;
+ priv->user_data = NULL;
+ priv->data = NULL;
+ priv->length = 0;
+ priv->transfer_cancel_tkn = g_cancellable_new();
+
+ return g_usb_device_claim_interface(fpi_device_get_usb_device(dev),
+ class->interface, 0, error);
+}
+void goodix_reset_state(FpDevice *dev)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ if (priv->timeout)
+ g_clear_pointer(&priv->timeout, g_source_destroy);
+ priv->ack = FALSE;
+ priv->reply = FALSE;
+ priv->callback = NULL;
+ priv->user_data = NULL;
+}
+
+gboolean goodix_dev_deinit(FpDevice *dev, GError **error)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsClass *class = FPI_DEVICE_GOODIXTLS_GET_CLASS(self);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ if (priv->timeout)
+ g_source_destroy(priv->timeout);
+ g_free(priv->data);
+ g_cancellable_cancel(priv->transfer_cancel_tkn);
+ goodix_shutdown_tls(dev, error);
+
+ goodix_reset_state(dev);
+ priv->inited = FALSE;
+
+ return g_usb_device_release_interface(fpi_device_get_usb_device(dev),
+ class->interface, 0, error);
+}
+
+// ---- DEV SECTION END ----
+
+// -----------------------------------------------------------------------------
+
+// ---- TLS SECTION START ----
+
+void goodix_read_tls(FpDevice *dev, GoodixTlsCallback callback,
+ gpointer user_data)
+{
+
+ fp_dbg("goodix_read_tls()");
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ priv->callback = callback;
+ priv->user_data = user_data;
+ priv->reply = TRUE;
+ priv->cmd = 0;
+}
+
+enum tls_states
+{
+ TLS_SERVER_INIT,
+ TLS_SERVER_HANDSHAKE_INIT,
+ TLS_NUM_STATES,
+};
+
+static void on_goodix_tls_server_ready(GoodixTlsServer *server, GError *err,
+ gpointer dev)
+{
+ if (err)
+ {
+ fp_err("server ready failed: %s", err->message);
+ return;
+ }
+ fp_dbg("TLS connection ready");
+}
+
+static void on_goodix_tls_read_handshake(FpDevice *dev, guint8 *data,
+ guint16 length, gpointer user_data,
+ GError *error)
+{
+ // goodix_tls_handshake_state* state = (goodix_tls_handshake_state*)
+ // user_data;
+ FpiSsm *ssm = user_data;
+ if (error)
+ {
+ fpi_ssm_mark_failed(ssm, error);
+ return;
+ }
+ FpiDeviceGoodixTls *self =
+ FPI_DEVICE_GOODIXTLS(fpi_ssm_get_data(user_data));
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ int sent = goodix_tls_client_send(priv->tls_hop, data, length);
+ if (sent < 0)
+ {
+ fpi_ssm_mark_failed(ssm, g_error_new(g_io_error_quark(), sent,
+ "failed to sent data to "
+ "tls server"));
+ return;
+ }
+ fpi_ssm_next_state(ssm);
+}
+
+enum goodix_tls_handshake_stages
+{
+ TLS_HANDSHAKE_STAGE_HELLO_S,
+ TLS_HANDSHAKE_STAGE_KH_EXCHANGE,
+ TLS_HANDSHAKE_STAGE_CHANGE_CIPHER_C,
+ TLS_HANDSHAKE_STAGE_HANDSHAKE_C,
+ TLS_HANDSHAKE_STAGE_CHANGE_CIPHER_S,
+
+ TLS_HANDSHAKE_STAGE_NUM,
+};
+
+static void on_tls_successfully_established(FpDevice *dev, gpointer user_data,
+ GError *error)
+{
+ fp_dbg("HANDSHAKE DONE");
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ ((GoodixNoneCallback)priv->tls_ready_callback->callback)(
+ dev, priv->tls_ready_callback->user_data, NULL);
+}
+static void tls_handshake_done(FpiSsm *ssm, FpDevice *dev, GError *error)
+{
+ if (error)
+ {
+ fp_dbg("failed to do tls handshake: %s (code: %d)", error->message,
+ error->code);
+ }
+ goodix_send_tls_successfully_established(
+ dev, on_tls_successfully_established, NULL);
+}
+
+static void tls_handshake_run(FpiSsm *ssm, FpDevice *dev)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ int stage = fpi_ssm_get_cur_state(ssm);
+ if (stage == TLS_HANDSHAKE_STAGE_HELLO_S)
+ {
+ guint8 buff[1024];
+ int size = goodix_tls_client_recv(priv->tls_hop, buff, sizeof(buff));
+ if (size < 0)
+ {
+ fpi_ssm_mark_failed(ssm, g_error_new(g_io_error_quark(), size,
+ "failed to read tls server "
+ "hello"));
+ return;
+ }
+ GError *err = NULL;
+ if (!goodix_send_pack(dev, GOODIX_FLAGS_TLS, buff, size, NULL, &err))
+ {
+ fpi_ssm_mark_failed(ssm, err);
+ return;
+ }
+ fpi_ssm_next_state(ssm);
+ }
+ else if (stage < TLS_HANDSHAKE_STAGE_CHANGE_CIPHER_S)
+ {
+ // Still proxying from hardware
+ fpi_ssm_set_data(ssm, dev, NULL);
+ goodix_read_tls(dev, on_goodix_tls_read_handshake, ssm);
+ }
+ else if (stage == TLS_HANDSHAKE_STAGE_CHANGE_CIPHER_S)
+ {
+ fp_dbg("Reading to proxy back");
+ guint8 buff[1024];
+ int size = goodix_tls_client_recv(priv->tls_hop, buff, sizeof(buff));
+ if (size < 0)
+ {
+ fpi_ssm_mark_failed(ssm, g_error_new(g_io_error_quark(), size,
+ "failed to read server "
+ "handshake"));
+
+ return;
+ }
+ GError *err = NULL;
+ if (!goodix_send_pack(dev, GOODIX_FLAGS_TLS, buff, size, NULL, &err))
+ {
+ fpi_ssm_mark_failed(ssm, err);
+ return;
+ }
+ fpi_ssm_next_state(ssm);
+ }
+}
+
+static void do_tls_handshake(FpDevice *dev)
+{
+ fpi_ssm_start(fpi_ssm_new(dev, tls_handshake_run, TLS_HANDSHAKE_STAGE_NUM),
+ tls_handshake_done);
+}
+
+static void on_goodix_request_tls_connection(FpDevice *dev, guint8 *data,
+ guint16 length, gpointer user_data,
+ GError *error)
+{
+ if (error)
+ {
+ fp_err("failed to get tls handshake: %s", error->message);
+ goodix_send_tls_successfully_established(FP_DEVICE(dev), NULL, NULL);
+ return;
+ }
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(user_data);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ goodix_tls_client_send(priv->tls_hop, data, length);
+
+ do_tls_handshake(dev);
+}
+
+static void goodix_tls_ready(GoodixTlsServer *server, GError *err, gpointer dev)
+{
+ if (err)
+ {
+ fp_err("failed to init tls server: %s, code: %d", err->message,
+ err->code);
+ return;
+ }
+ goodix_send_request_tls_connection(FP_DEVICE(dev),
+ on_goodix_request_tls_connection, dev);
+}
+
+void goodix_tls_complete(FpiSsm *ssm, FpDevice *dev, GError *error)
+{
+ fpi_image_device_activate_complete(FP_IMAGE_DEVICE(dev), error);
+}
+
+void goodix_tls(FpDevice *dev, GoodixNoneCallback callback, gpointer user_data)
+{
+ fp_dbg("Starting up goodix tls server");
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ g_assert(priv->tls_hop == NULL);
+ priv->tls_hop = malloc(sizeof(GoodixTlsServer));
+
+ if (!priv->tls_ready_callback)
+ {
+ priv->tls_ready_callback = malloc(sizeof(GoodixCallbackInfo));
+ }
+ priv->tls_ready_callback->callback = G_CALLBACK(callback);
+ priv->tls_ready_callback->user_data = user_data;
+ GoodixTlsServer *s = priv->tls_hop;
+ s->connection_callback = on_goodix_tls_server_ready;
+ s->user_data = self;
+ GError *err = NULL;
+ if (!goodix_tls_server_init(priv->tls_hop, &err))
+ {
+ fp_err("failed to init tls server, error: %s, code: %d", err->message,
+ err->code);
+ return;
+ }
+
+ goodix_tls_ready(s, err, self);
+}
+
+gboolean goodix_shutdown_tls(FpDevice *dev, GError **error)
+{
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+
+ if (priv->tls_hop)
+ {
+ gboolean rs = goodix_tls_server_deinit(priv->tls_hop, error);
+ g_free(priv->tls_hop);
+ priv->tls_hop = NULL;
+ return rs;
+ }
+ return TRUE;
+}
+void goodix_tls_ready_image_handler(FpDevice *dev, guint8 *data,
+ guint16 length, gpointer user_data,
+ GError *error)
+{
+
+ GoodixCallbackInfo *cb_info = user_data;
+ GoodixImageCallback callback = (GoodixImageCallback)cb_info->callback;
+ if (error)
+ {
+ callback(dev, NULL, 0, cb_info->user_data, error);
+ g_free(cb_info);
+ return;
+ }
+ FpiDeviceGoodixTls *self = FPI_DEVICE_GOODIXTLS(dev);
+ FpiDeviceGoodixTlsPrivate *priv =
+ fpi_device_goodixtls_get_instance_private(self);
+ goodix_tls_client_send(priv->tls_hop, data, length);
+
+ const guint16 size = -1;
+ guint8 *buff = malloc(size);
+ GError *err = NULL;
+ int read_size = goodix_tls_server_receive(priv->tls_hop, buff, size, &err);
+ if (read_size <= 0)
+ {
+ callback(dev, NULL, 0, cb_info->user_data, err);
+ g_free(cb_info);
+ return;
+ }
+
+ callback(dev, buff, read_size, cb_info->user_data, NULL);
+ g_free(cb_info);
+}
+
+void goodix_tls_read_image(FpDevice *dev, GoodixImageCallback callback,
+ gpointer user_data)
+{
+ g_assert(callback);
+ GoodixCallbackInfo *cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_mcu_get_image(dev, goodix_tls_ready_image_handler, cb_info);
+}
+
+// ---- TLS SECTION END ----
+
+static void fpi_device_goodixtls_init(FpiDeviceGoodixTls *self) {}
+
+static void fpi_device_goodixtls_class_init(FpiDeviceGoodixTlsClass *class) {}
diff --git a/libfprint/drivers/goodixtls/goodix.h b/libfprint/drivers/goodixtls/goodix.h
new file mode 100644
index 00000000..208f58eb
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodix.h
@@ -0,0 +1,260 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#pragma once
+
+// 1 seconds USB timeout
+#define GOODIX_TIMEOUT (1000)
+
+G_DECLARE_DERIVABLE_TYPE(FpiDeviceGoodixTls, fpi_device_goodixtls, FPI,
+ DEVICE_GOODIXTLS, FpImageDevice)
+
+#define FPI_TYPE_DEVICE_GOODIXTLS (fpi_device_goodixtls_get_type())
+
+struct _FpiDeviceGoodixTlsClass
+{
+ FpImageDeviceClass parent;
+
+ gint interface;
+ guint8 ep_in;
+ guint8 ep_out;
+};
+
+typedef struct __attribute__((__packed__)) _GoodixCallbackInfo
+{
+ GCallback callback;
+ gpointer user_data;
+} GoodixCallbackInfo;
+
+typedef void (*GoodixCmdCallback)(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error);
+
+typedef void (*GoodixFirmwareVersionCallback)(FpDevice *dev, gchar *firmware,
+ gpointer user_data,
+ GError *error);
+
+typedef void (*GoodixPresetPskReadCallback)(FpDevice *dev, gboolean success,
+ guint32 flags, guint8 *psk,
+ guint16 length, gpointer user_data,
+ GError *error);
+
+typedef void (*GoodixSuccessCallback)(FpDevice *dev, gboolean success,
+ gpointer user_data, GError *error);
+
+typedef void (*GoodixResetCallback)(FpDevice *dev, gboolean success,
+ guint16 number, gpointer user_data,
+ GError *error);
+
+typedef void (*GoodixNoneCallback)(FpDevice *dev, gpointer user_data,
+ GError *error);
+
+typedef void (*GoodixDefaultCallback)(FpDevice *dev, guint8 *data,
+ guint16 length, gpointer user_data,
+ GError *error);
+typedef GoodixDefaultCallback GoodixTlsCallback;
+
+typedef void (*GoodixImageCallback)(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error);
+
+gchar *data_to_str(guint8 *data, guint32 length);
+
+// ---- GOODIX RECEIVE SECTION START ----
+
+void goodix_receive_done(FpDevice *dev, guint8 *data, guint16 length,
+ GError *error);
+
+void goodix_receive_success(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error);
+
+void goodix_receive_reset(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error);
+
+void goodix_receive_none(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error);
+
+void goodix_receive_default(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error);
+
+void goodix_receive_preset_psk_read(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error);
+
+void goodix_receive_preset_psk_write(FpDevice *dev, guint8 *data,
+ guint16 length, gpointer user_data,
+ GError *error);
+
+void goodix_receive_ack(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer user_data, GError *error);
+
+void goodix_receive_firmware_version(FpDevice *dev, guint8 *data,
+ guint16 length, gpointer user_data,
+ GError *error);
+
+void goodix_receive_protocol(FpDevice *dev, guint8 *data, guint32 length);
+
+void goodix_receive_pack(FpDevice *dev, guint8 *data, guint32 length);
+
+void goodix_receive_data_cb(FpiUsbTransfer *transfer, FpDevice *dev,
+ gpointer user_data, GError *error);
+
+void goodix_receive_timeout_cb(FpDevice *dev, gpointer user_data);
+
+void goodix_receive_data(FpDevice *dev);
+
+void goodix_start_read_loop(FpDevice *dev);
+// ---- GOODIX RECEIVE SECTION END ----
+
+// -----------------------------------------------------------------------------
+
+// ---- GOODIX SEND SECTION START ----
+
+gboolean goodix_send_data(FpDevice *dev, guint8 *data, guint32 length,
+ GDestroyNotify free_func, GError **error);
+
+gboolean goodix_send_pack(FpDevice *dev, guint8 flags, guint8 *payload,
+ guint16 length, GDestroyNotify free_func,
+ GError **error);
+
+void goodix_send_protocol(FpDevice *dev, guint8 cmd, guint8 *payload,
+ guint16 length, GDestroyNotify free_func,
+ gboolean calc_checksum, guint timeout_ms,
+ gboolean reply, GoodixCmdCallback callback,
+ gpointer user_data);
+
+void goodix_send_nop(FpDevice *dev, GoodixNoneCallback callback,
+ gpointer user_data);
+
+void goodix_send_mcu_get_image(FpDevice *dev, GoodixImageCallback callback,
+ gpointer user_data);
+
+void goodix_send_mcu_switch_to_fdt_down(FpDevice *dev, guint8 *mode,
+ guint16 length,
+ GDestroyNotify free_func,
+ GoodixDefaultCallback callback,
+ gpointer user_data);
+
+void goodix_send_mcu_switch_to_fdt_up(FpDevice *dev, guint8 *mode,
+ guint16 length, GDestroyNotify free_func,
+ GoodixDefaultCallback callback,
+ gpointer user_data);
+
+void goodix_send_mcu_switch_to_fdt_mode(FpDevice *dev, guint8 *mode,
+ guint16 length,
+ GDestroyNotify free_func,
+ GoodixDefaultCallback callback,
+ gpointer user_data);
+
+void goodix_send_nav_0(FpDevice *dev, GoodixDefaultCallback callback,
+ gpointer user_data);
+
+void goodix_send_mcu_switch_to_idle_mode(FpDevice *dev, guint8 sleep_time,
+ GoodixNoneCallback callback,
+ gpointer user_data);
+
+void goodix_send_write_sensor_register(FpDevice *dev, guint16 address,
+ guint16 value,
+ GoodixNoneCallback callback,
+ gpointer user_data);
+
+void goodix_send_read_sensor_register(FpDevice *dev, guint16 address,
+ guint8 length,
+ GoodixDefaultCallback callback,
+ gpointer user_data);
+
+void goodix_send_upload_config_mcu(FpDevice *dev, guint8 *config,
+ guint16 length, GDestroyNotify free_func,
+ GoodixSuccessCallback callback,
+ gpointer user_data);
+
+void goodix_send_set_powerdown_scan_frequency(FpDevice *dev,
+ guint16 powerdown_scan_frequency,
+ GoodixSuccessCallback callback,
+ gpointer user_data);
+
+void goodix_send_enable_chip(FpDevice *dev, gboolean enable,
+ GoodixNoneCallback callback, gpointer user_data);
+
+void goodix_send_reset(FpDevice *dev, gboolean reset_sensor, guint8 sleep_time,
+ GoodixResetCallback callback, gpointer user_data);
+
+void goodix_send_firmware_version(FpDevice *dev,
+ GoodixFirmwareVersionCallback callback,
+ gpointer user_data);
+
+void goodix_send_query_mcu_state(FpDevice *dev, GoodixDefaultCallback callback,
+ gpointer user_data);
+
+void goodix_send_request_tls_connection(FpDevice *dev,
+ GoodixDefaultCallback callback,
+ gpointer user_data);
+
+void goodix_send_tls_successfully_established(FpDevice *dev,
+ GoodixNoneCallback callback,
+ gpointer user_data);
+
+void goodix_send_preset_psk_write(FpDevice *dev, guint32 flags, guint8 *psk,
+ guint16 length, GDestroyNotify free_func,
+ GoodixSuccessCallback callback,
+ gpointer user_data);
+
+void goodix_send_preset_psk_read(FpDevice *dev, guint32 flags, guint16 length,
+ GoodixPresetPskReadCallback callback,
+ gpointer user_data);
+
+void goodix_send_read_otp(FpDevice *dev, GoodixDefaultCallback callback,
+ gpointer user_data);
+
+// ---- GOODIX SEND SECTION END ----
+
+// -----------------------------------------------------------------------------
+
+// ---- DEV SECTION START ----
+
+gboolean goodix_dev_init(FpDevice *dev, GError **error);
+
+gboolean goodix_dev_deinit(FpDevice *dev, GError **error);
+
+void goodix_reset_state(FpDevice *dev);
+
+// ---- DEV SECTION END ----
+
+// -----------------------------------------------------------------------------
+
+// ---- TLS SECTION START ----
+
+void goodix_read_tls(FpDevice *dev, GoodixTlsCallback callback,
+ gpointer user_data);
+
+void goodix_tls_run_state(FpiSsm *ssm, FpDevice *dev);
+
+void goodix_tls_complete(FpiSsm *ssm, FpDevice *dev, GError *error);
+
+void goodix_tls(FpDevice *dev, GoodixNoneCallback callback, gpointer user_data);
+
+gboolean goodix_shutdown_tls(FpDevice *dev, GError **error);
+
+void goodix_tls_ready_image_handler(FpDevice *dev, guint8 *data,
+ guint16 length, gpointer user_data,
+ GError *error);
+
+void goodix_tls_read_image(FpDevice *dev, GoodixImageCallback callback,
+ gpointer user_data);
+
+void goodix_tls_decrypt_image(FpDevice *dev, guint8 **data, guint16 *len);
+
+// ---- TLS SECTION END ----
diff --git a/libfprint/drivers/goodixtls/goodix511.c b/libfprint/drivers/goodixtls/goodix511.c
new file mode 100644
index 00000000..cbefe218
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodix511.c
@@ -0,0 +1,898 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#include "fp-device.h"
+#include "fp-image-device.h"
+#include "fp-image.h"
+#include "fpi-assembling.h"
+#include "fpi-context.h"
+#include "fpi-image-device.h"
+#include "fpi-image.h"
+#include "fpi-ssm.h"
+#include "glibconfig.h"
+#include "gusb/gusb-device.h"
+#include <stdio.h>
+#include <stdlib.h>
+#define FP_COMPONENT "goodixtls511"
+
+#include <glib.h>
+#include <string.h>
+
+#include "drivers_api.h"
+#include "goodix.h"
+#include "goodix_proto.h"
+#include "goodix511.h"
+
+#include <math.h>
+
+#define GOODIX511_WIDTH 64
+#define GOODIX511_HEIGHT 80
+#define GOODIX511_SCAN_WIDTH 88
+#define GOODIX511_FRAME_SIZE (GOODIX511_WIDTH * GOODIX511_HEIGHT)
+// For every 4 pixels there are 6 bytes and there are 8 extra start bytes and 5
+// extra end
+#define GOODIX511_RAW_FRAME_SIZE \
+ 8 + (GOODIX511_HEIGHT * GOODIX511_SCAN_WIDTH) / 4 * 6 + 5
+#define GOODIX511_CAP_FRAMES 40 // Number of frames we capture per swipe
+
+typedef unsigned short Goodix511Pix;
+
+struct _FpiDeviceGoodixTls511
+{
+ FpiDeviceGoodixTls parent;
+
+ guint8 *otp;
+
+ GSList *frames;
+
+ Goodix511Pix empty_img[GOODIX511_FRAME_SIZE];
+};
+
+G_DECLARE_FINAL_TYPE(FpiDeviceGoodixTls511, fpi_device_goodixtls511, FPI,
+ DEVICE_GOODIXTLS511, FpiDeviceGoodixTls);
+
+G_DEFINE_TYPE(FpiDeviceGoodixTls511, fpi_device_goodixtls511,
+ FPI_TYPE_DEVICE_GOODIXTLS);
+
+// ---- ACTIVE SECTION START ----
+
+enum activate_states
+{
+ ACTIVATE_READ_AND_NOP,
+ ACTIVATE_ENABLE_CHIP,
+ ACTIVATE_NOP,
+ ACTIVATE_CHECK_FW_VER,
+ ACTIVATE_CHECK_PSK,
+ ACTIVATE_RESET,
+ ACTIVATE_SET_MCU_IDLE,
+ ACTIVATE_SET_ODP,
+ ACTIVATE_SET_MCU_CONFIG,
+ ACTIVATE_SET_POWERDOWN_SCAN_FREQUENCY,
+ ACTIVATE_NUM_STATES,
+};
+
+#ifdef GOODIX511_DUMP_FRAMES
+static gboolean save_image_to_pgm(FpImage *img, const char *path)
+{
+ FILE *fd = fopen(path, "w");
+ size_t write_size;
+ const guchar *data = fp_image_get_data(img, &write_size);
+ int r;
+
+ if (!fd)
+ {
+ g_warning("could not open '%s' for writing: %d", path, errno);
+ return FALSE;
+ }
+
+ r = fprintf(fd, "P5 %d %d 255\n", fp_image_get_width(img),
+ fp_image_get_height(img));
+ if (r < 0)
+ {
+ fclose(fd);
+ g_critical("pgm header write failed, error %d", r);
+ return FALSE;
+ }
+
+ r = fwrite(data, 1, write_size, fd);
+ if (r < write_size)
+ {
+ fclose(fd);
+ g_critical("short write (%d)", r);
+ return FALSE;
+ }
+
+ fclose(fd);
+ g_debug("written to '%s'", path);
+
+ return TRUE;
+}
+#endif
+
+static void check_none(FpDevice *dev, gpointer user_data, GError *error)
+{
+ if (error)
+ {
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fpi_ssm_next_state(user_data);
+}
+
+static void check_firmware_version(FpDevice *dev, gchar *firmware,
+ gpointer user_data, GError *error)
+{
+ if (error)
+ {
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fp_dbg("Device firmware: \"%s\"", firmware);
+
+ if (strcmp(firmware, GOODIX_511_FIRMWARE_VERSION))
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device firmware: \"%s\"", firmware);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fpi_ssm_next_state(user_data);
+}
+
+static void check_reset(FpDevice *dev, gboolean success, guint16 number,
+ gpointer user_data, GError *error)
+{
+ if (error)
+ {
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ if (!success)
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to reset device");
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fp_dbg("Device reset number: %d", number);
+
+ if (number != GOODIX_511_RESET_NUMBER)
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device reset number: %d", number);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fpi_ssm_next_state(user_data);
+}
+
+static void check_preset_psk_read(FpDevice *dev, gboolean success,
+ guint32 flags, guint8 *psk, guint16 length,
+ gpointer user_data, GError *error)
+{
+ g_autofree gchar *psk_str = data_to_str(psk, length);
+
+ if (error)
+ {
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ if (!success)
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to read PSK from device");
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fp_dbg("Device PSK: 0x%s", psk_str);
+ fp_dbg("Device PSK flags: 0x%08x", flags);
+
+ if (flags != GOODIX_511_PSK_FLAGS)
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device PSK flags: 0x%08x", flags);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ if (length != sizeof(goodix_511_psk_0))
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device PSK: 0x%s", psk_str);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ if (memcmp(psk, goodix_511_psk_0, sizeof(goodix_511_psk_0)))
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device PSK: 0x%s", psk_str);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fpi_ssm_next_state(user_data);
+}
+static void check_idle(FpDevice *dev, gpointer user_data, GError *err)
+{
+
+ if (err)
+ {
+ fpi_ssm_mark_failed(user_data, err);
+ return;
+ }
+ fpi_ssm_next_state(user_data);
+}
+static void check_config_upload(FpDevice *dev, gboolean success,
+ gpointer user_data, GError *error)
+{
+ if (error)
+ {
+ fpi_ssm_mark_failed(user_data, error);
+ }
+ else if (!success)
+ {
+ fpi_ssm_mark_failed(user_data,
+ g_error_new(FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO,
+ "failed to upload mcu config"));
+ }
+ else
+ {
+ fpi_ssm_next_state(user_data);
+ }
+}
+static void check_powerdown_scan_freq(FpDevice *dev, gboolean success,
+ gpointer user_data, GError *error)
+{
+ if (error)
+ {
+ fpi_ssm_mark_failed(user_data, error);
+ }
+ else if (!success)
+ {
+ fpi_ssm_mark_failed(user_data,
+ g_error_new(FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO,
+ "failed to set powerdown freq"));
+ }
+ else
+ {
+ fpi_ssm_next_state(user_data);
+ }
+}
+
+enum otp_write_states
+{
+ OTP_WRITE_1,
+ OTP_WRITE_2,
+ OTP_WRITE_3,
+ OTP_WRITE_4,
+
+ OTP_WRITE_NUM,
+};
+
+static guint16 otp_write_addrs[] = {0x0220, 0x0236, 0x0238, 0x023a};
+
+static void otp_write_run(FpiSsm *ssm, FpDevice *dev)
+{
+ guint16 data;
+ FpiDeviceGoodixTls511 *self = FPI_DEVICE_GOODIXTLS511(dev);
+ guint8 *otp = self->otp;
+ switch (fpi_ssm_get_cur_state(ssm))
+ {
+ case OTP_WRITE_1:
+ data = otp[46] << 4 | 8;
+ break;
+ case OTP_WRITE_2:
+ data = otp[47];
+ break;
+ case OTP_WRITE_3:
+ data = otp[48];
+ break;
+ case OTP_WRITE_4:
+ data = otp[49];
+ break;
+ }
+
+ goodix_send_write_sensor_register(
+ dev, otp_write_addrs[fpi_ssm_get_cur_state(ssm)], data, check_none,
+ ssm);
+ if (fpi_ssm_get_cur_state(ssm) == OTP_WRITE_NUM - 1)
+ {
+ free(self->otp);
+ }
+}
+
+static void read_otp_callback(FpDevice *dev, guint8 *data, guint16 len,
+ gpointer ssm, GError *err)
+{
+ if (err)
+ {
+ fpi_ssm_mark_failed(ssm, err);
+ return;
+ }
+ if (len < 64)
+ {
+ fpi_ssm_mark_failed(ssm, g_error_new(FP_DEVICE_ERROR,
+ FP_DEVICE_ERROR_DATA_INVALID,
+ "OTP is invalid (len: %d)", 64));
+ return;
+ }
+ FpiDeviceGoodixTls511 *self = FPI_DEVICE_GOODIXTLS511(dev);
+ self->otp = malloc(len);
+ memcpy(self->otp, data, len);
+ FpiSsm *otp_ssm = fpi_ssm_new(dev, otp_write_run, OTP_WRITE_NUM);
+ fpi_ssm_start_subsm(ssm, otp_ssm);
+}
+
+static void activate_run_state(FpiSsm *ssm, FpDevice *dev)
+{
+
+ switch (fpi_ssm_get_cur_state(ssm))
+ {
+ case ACTIVATE_READ_AND_NOP:
+ // Nop seems to clear the previous command buffer. But we are
+ // unable to do so.
+ goodix_start_read_loop(dev);
+ goodix_send_nop(dev, check_none, ssm);
+ break;
+
+ case ACTIVATE_ENABLE_CHIP:
+ goodix_send_enable_chip(dev, TRUE, check_none, ssm);
+ break;
+
+ case ACTIVATE_NOP:
+ goodix_send_nop(dev, check_none, ssm);
+ break;
+
+ case ACTIVATE_CHECK_FW_VER:
+ goodix_send_firmware_version(dev, check_firmware_version, ssm);
+ break;
+
+ case ACTIVATE_CHECK_PSK:
+ goodix_send_preset_psk_read(dev, GOODIX_511_PSK_FLAGS, 0,
+ check_preset_psk_read, ssm);
+ break;
+
+ case ACTIVATE_RESET:
+ goodix_send_reset(dev, TRUE, 20, check_reset, ssm);
+ break;
+
+ case ACTIVATE_SET_MCU_IDLE:
+ goodix_send_mcu_switch_to_idle_mode(dev, 20, check_idle, ssm);
+ break;
+
+ case ACTIVATE_SET_ODP:
+ goodix_send_read_otp(dev, read_otp_callback, ssm);
+ break;
+ case ACTIVATE_SET_MCU_CONFIG:
+ goodix_send_upload_config_mcu(dev, goodix_511_config,
+ sizeof(goodix_511_config), NULL,
+ check_config_upload, ssm);
+ break;
+
+ case ACTIVATE_SET_POWERDOWN_SCAN_FREQUENCY:
+ goodix_send_set_powerdown_scan_frequency(
+ dev, 100, check_powerdown_scan_freq, ssm);
+ break;
+ }
+}
+
+static void tls_activation_complete(FpDevice *dev, gpointer user_data,
+ GError *error)
+{
+ if (error)
+ {
+ fp_err("failed to complete tls activation: %s", error->message);
+ return;
+ }
+ FpImageDevice *image_dev = FP_IMAGE_DEVICE(dev);
+
+ fpi_image_device_activate_complete(image_dev, error);
+}
+
+static void activate_complete(FpiSsm *ssm, FpDevice *dev, GError *error)
+{
+ G_DEBUG_HERE();
+ if (!error)
+ goodix_tls(dev, tls_activation_complete, NULL);
+ else
+ {
+ fp_err("failed during activation: %s (code: %d)", error->message,
+ error->code);
+ fpi_image_device_activate_complete(FP_IMAGE_DEVICE(dev), error);
+ }
+}
+
+// ---- ACTIVE SECTION END ----
+
+// -----------------------------------------------------------------------------
+
+// ---- SCAN SECTION START ----
+
+enum SCAN_STAGES
+{
+ SCAN_STAGE_CALIBRATE,
+ SCAN_STAGE_SWITCH_TO_FDT_MODE,
+ SCAN_STAGE_SWITCH_TO_FDT_DOWN,
+ SCAN_STAGE_GET_IMG,
+
+ SCAN_STAGE_NUM,
+};
+
+static void check_none_cmd(FpDevice *dev, guint8 *data, guint16 len,
+ gpointer ssm, GError *err)
+{
+ if (err)
+ {
+ fpi_ssm_mark_failed(ssm, err);
+ return;
+ }
+ fpi_ssm_next_state(ssm);
+}
+
+static unsigned char get_pix(struct fpi_frame_asmbl_ctx *ctx,
+ struct fpi_frame *frame, unsigned int x,
+ unsigned int y)
+{
+ return frame->data[x + y * GOODIX511_WIDTH];
+}
+
+// Bitdepth is 12, but we have to fit it in a byte
+static unsigned char squash(int v) { return v / 16; }
+
+static void decode_frame(Goodix511Pix frame[GOODIX511_FRAME_SIZE],
+ const guint8 *raw_frame)
+{
+
+ Goodix511Pix uncropped[GOODIX511_SCAN_WIDTH * GOODIX511_HEIGHT];
+ Goodix511Pix *pix = uncropped;
+ for (int i = 8; i != GOODIX511_RAW_FRAME_SIZE - 5; i += 6)
+ {
+ const guint8 *chunk = raw_frame + i;
+ *pix++ = ((chunk[0] & 0xf) << 8) + chunk[1];
+ *pix++ = (chunk[3] << 4) + (chunk[0] >> 4);
+ *pix++ = ((chunk[5] & 0xf) << 8) + chunk[2];
+ *pix++ = (chunk[4] << 4) + (chunk[5] >> 4);
+ }
+ for (int y = 0; y != GOODIX511_HEIGHT; ++y)
+ {
+ for (int x = 0; x != GOODIX511_WIDTH; ++x)
+ {
+ const int idx = x + y * GOODIX511_SCAN_WIDTH;
+ frame[x + y * GOODIX511_WIDTH] = uncropped[idx];
+ }
+ }
+}
+static int goodix_cmp_short(const void *a, const void *b)
+{
+ return (int)(*(short *)a - *(short *)b);
+}
+
+static void rotate_frame(Goodix511Pix frame[GOODIX511_FRAME_SIZE])
+{
+ Goodix511Pix buff[GOODIX511_FRAME_SIZE];
+
+ for (int y = 0; y != GOODIX511_HEIGHT; ++y)
+ {
+ for (int x = 0; x != GOODIX511_WIDTH; ++x)
+ {
+ buff[x * GOODIX511_WIDTH + y] = frame[x + y * GOODIX511_WIDTH];
+ }
+ }
+ memcpy(frame, buff, GOODIX511_FRAME_SIZE);
+}
+static void squash_frame(Goodix511Pix *frame, guint8 *squashed)
+{
+ for (int i = 0; i != GOODIX511_FRAME_SIZE; ++i)
+ {
+ squashed[i] = squash(frame[i]);
+ }
+}
+/**
+ * @brief Squashes the 12 bit pixels of a raw frame into the 4 bit pixels used
+ * by libfprint.
+ * @details Borrowed from the elan driver. We reduce frames to
+ * within the max and min.
+ *
+ * @param frame
+ * @param squashed
+ */
+static void squash_frame_linear(Goodix511Pix *frame, guint8 *squashed)
+{
+ Goodix511Pix min = 0xffff;
+ Goodix511Pix max = 0;
+
+ for (int i = 0; i != GOODIX511_FRAME_SIZE; ++i)
+ {
+ const Goodix511Pix pix = frame[i];
+ if (pix < min)
+ {
+ min = pix;
+ }
+ if (pix > max)
+ {
+ max = pix;
+ }
+ }
+
+ for (int i = 0; i != GOODIX511_FRAME_SIZE; ++i)
+ {
+ const Goodix511Pix pix = frame[i];
+ if (pix - min == 0 || max - min == 0)
+ {
+ squashed[i] = 0;
+ }
+ else
+ {
+ squashed[i] = (pix - min) * 0xff / (max - min);
+ }
+ }
+}
+
+/**
+ * @brief Subtracts the background from the frame
+ *
+ * @param frame
+ * @param background
+ */
+static gboolean postprocess_frame(Goodix511Pix frame[GOODIX511_FRAME_SIZE],
+ Goodix511Pix background[GOODIX511_FRAME_SIZE])
+{
+ int sum = 0;
+ for (int i = 0; i != GOODIX511_FRAME_SIZE; ++i)
+ {
+ Goodix511Pix *og_px = frame + i;
+ Goodix511Pix bg_px = background[i];
+
+ if (*og_px >= bg_px)
+ {
+ *og_px = 4095;
+ }
+ else
+ {
+ *og_px -= bg_px;
+ }
+
+ sum += *og_px;
+ }
+ if (sum == 0)
+ {
+ fp_warn("frame darker than background, finger on scanner during "
+ "calibration?");
+ }
+
+ return sum != 0;
+}
+
+typedef struct _frame_processing_info
+{
+ FpiDeviceGoodixTls511 *dev;
+ GSList **frames;
+
+} frame_processing_info;
+
+static void process_frame(Goodix511Pix *raw_frame, frame_processing_info *info)
+{
+ struct fpi_frame *frame =
+ g_malloc(GOODIX511_FRAME_SIZE + sizeof(struct fpi_frame));
+ postprocess_frame(raw_frame, info->dev->empty_img);
+ squash_frame_linear(raw_frame, frame->data);
+
+ *(info->frames) = g_slist_append(*(info->frames), frame);
+}
+
+static void save_frame(FpiDeviceGoodixTls511 *self, guint8 *raw)
+{
+ Goodix511Pix *frame = malloc(GOODIX511_FRAME_SIZE * sizeof(Goodix511Pix));
+ decode_frame(frame, raw);
+ self->frames = g_slist_append(self->frames, frame);
+#ifdef GOODIX511_DUMP_FRAMES
+ char buff[2014];
+ snprintf(buff, sizeof(buff), "cut2/f_%d.pgm", g_slist_length(self->frames));
+ FpImage *img = fp_image_new(GOODIX511_WIDTH, GOODIX511_HEIGHT);
+ postprocess_frame(frame, self->empty_img);
+ squash_frame_linear(frame, img->data);
+ save_image_to_pgm(img, buff);
+#endif
+}
+
+static void scan_on_read_img(FpDevice *dev, guint8 *data, guint16 len,
+ gpointer ssm, GError *err)
+{
+ if (err)
+ {
+ fpi_ssm_mark_failed(ssm, err);
+ return;
+ }
+
+ FpiDeviceGoodixTls511 *self = FPI_DEVICE_GOODIXTLS511(dev);
+ save_frame(self, data);
+ if (g_slist_length(self->frames) <= GOODIX511_CAP_FRAMES)
+ {
+ fpi_ssm_jump_to_state(ssm, SCAN_STAGE_SWITCH_TO_FDT_MODE);
+ }
+ else
+ {
+ GSList *raw_frames = g_slist_nth(self->frames, 1);
+
+ FpImageDevice *img_dev = FP_IMAGE_DEVICE(dev);
+ struct fpi_frame_asmbl_ctx assembly_ctx;
+ assembly_ctx.frame_width = GOODIX511_WIDTH;
+ assembly_ctx.frame_height = GOODIX511_HEIGHT;
+ assembly_ctx.image_width = GOODIX511_WIDTH * 2;
+ assembly_ctx.get_pixel = get_pix;
+
+ GSList *frames = NULL;
+ frame_processing_info pinfo = {.dev = self, .frames = &frames};
+
+ g_slist_foreach(raw_frames, (GFunc)process_frame, &pinfo);
+ // frames = g_slist_reverse(frames);
+
+ fpi_do_movement_estimation(&assembly_ctx, frames);
+ FpImage *img = fpi_assemble_frames(&assembly_ctx, frames);
+ img->flags |= FPI_IMAGE_PARTIAL;
+
+ g_slist_free_full(frames, g_free);
+ // g_slist_free_full(self->frames, g_free);
+ // self->frames = g_slist_alloc();
+
+ fpi_image_device_image_captured(img_dev, img);
+ fpi_image_device_report_finger_status(img_dev, FALSE);
+
+ fpi_ssm_next_state(ssm);
+ }
+}
+
+enum scan_empty_img_state
+{
+ SCAN_EMPTY_NAV0,
+ SCAN_EMPTY_GET_IMG,
+
+ SCAN_EMPTY_NUM,
+};
+
+static void on_scan_empty_img(FpDevice *dev, guint8 *data, guint16 length,
+ gpointer ssm, GError *error)
+{
+ if (error)
+ {
+ fpi_ssm_mark_failed(ssm, error);
+ return;
+ }
+ FpiDeviceGoodixTls511 *self = FPI_DEVICE_GOODIXTLS511(dev);
+ decode_frame(self->empty_img, data);
+#ifdef GOODIX511_DUMP_FRAMES
+ FpImage *bgk = fp_image_new(GOODIX511_WIDTH, GOODIX511_HEIGHT);
+ squash_frame(self->empty_img, bgk->data);
+ save_image_to_pgm(bgk, "./background.pgm");
+#endif
+ fpi_ssm_next_state(ssm);
+}
+static void scan_empty_run(FpiSsm *ssm, FpDevice *dev)
+{
+
+ switch (fpi_ssm_get_cur_state(ssm))
+ {
+ case SCAN_EMPTY_NAV0:
+ goodix_send_nav_0(dev, check_none_cmd, ssm);
+ break;
+
+ case SCAN_EMPTY_GET_IMG:
+ goodix_tls_read_image(dev, on_scan_empty_img, ssm);
+ break;
+ }
+}
+
+static void scan_empty_img(FpDevice *dev, FpiSsm *ssm)
+{
+ fpi_ssm_start_subsm(ssm, fpi_ssm_new(dev, scan_empty_run, SCAN_EMPTY_NUM));
+}
+
+static void scan_get_img(FpDevice *dev, FpiSsm *ssm)
+{
+ goodix_tls_read_image(dev, scan_on_read_img, ssm);
+}
+
+const guint8 fdt_switch_state_mode[] = {
+ 0x0d,
+ 0x01,
+ 0x80,
+ 0xaf,
+ 0x80,
+ 0xbf,
+ 0x80,
+ 0xa4,
+ 0x80,
+ 0xb8,
+ 0x80,
+ 0xa8,
+ 0x80,
+ 0xb7,
+};
+
+const guint8 fdt_switch_state_down[] = {
+ 0x0c,
+ 0x01,
+ 0x80,
+ 0xaf,
+ 0x80,
+ 0xbf,
+ 0x80,
+ 0xa4,
+ 0x80,
+ 0xb8,
+ 0x80,
+ 0xa8,
+ 0x80,
+ 0xb7,
+};
+
+static void scan_run_state(FpiSsm *ssm, FpDevice *dev)
+{
+ FpImageDevice *img_dev = FP_IMAGE_DEVICE(dev);
+
+ switch (fpi_ssm_get_cur_state(ssm))
+ {
+ case SCAN_STAGE_CALIBRATE:
+ scan_empty_img(dev, ssm);
+ break;
+
+ case SCAN_STAGE_SWITCH_TO_FDT_MODE:
+ goodix_send_mcu_switch_to_fdt_mode(dev, (guint8 *)fdt_switch_state_mode,
+ sizeof(fdt_switch_state_mode), NULL,
+ check_none_cmd, ssm);
+ break;
+
+ case SCAN_STAGE_SWITCH_TO_FDT_DOWN:
+ goodix_send_mcu_switch_to_fdt_down(dev, (guint8 *)fdt_switch_state_down,
+ sizeof(fdt_switch_state_down), NULL,
+ check_none_cmd, ssm);
+ break;
+ case SCAN_STAGE_GET_IMG:
+ fpi_image_device_report_finger_status(img_dev, TRUE);
+ scan_get_img(dev, ssm);
+ break;
+ }
+}
+
+static void scan_complete(FpiSsm *ssm, FpDevice *dev, GError *error)
+{
+ if (error)
+ {
+ fp_err("failed to scan: %s (code: %d)", error->message, error->code);
+ return;
+ }
+ fp_dbg("finished scan");
+}
+
+static void scan_start(FpiDeviceGoodixTls511 *dev)
+{
+ fpi_ssm_start(fpi_ssm_new(FP_DEVICE(dev), scan_run_state, SCAN_STAGE_NUM),
+ scan_complete);
+}
+
+// ---- SCAN SECTION END ----
+
+// ---- DEV SECTION START ----
+
+static void dev_init(FpImageDevice *img_dev)
+{
+ FpDevice *dev = FP_DEVICE(img_dev);
+ GError *error = NULL;
+
+ if (goodix_dev_init(dev, &error))
+ {
+ fpi_image_device_open_complete(img_dev, error);
+ return;
+ }
+
+ fpi_image_device_open_complete(img_dev, NULL);
+}
+
+static void dev_deinit(FpImageDevice *img_dev)
+{
+ FpDevice *dev = FP_DEVICE(img_dev);
+ GError *error = NULL;
+
+ if (goodix_dev_deinit(dev, &error))
+ {
+ fpi_image_device_close_complete(img_dev, error);
+ return;
+ }
+
+ fpi_image_device_close_complete(img_dev, NULL);
+}
+
+static void dev_activate(FpImageDevice *img_dev)
+{
+ FpDevice *dev = FP_DEVICE(img_dev);
+
+ fpi_ssm_start(fpi_ssm_new(dev, activate_run_state, ACTIVATE_NUM_STATES),
+ activate_complete);
+}
+
+static void dev_change_state(FpImageDevice *img_dev, FpiImageDeviceState state)
+{
+ FpiDeviceGoodixTls511 *self = FPI_DEVICE_GOODIXTLS511(img_dev);
+ G_DEBUG_HERE();
+
+ if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
+ {
+ scan_start(self);
+ }
+}
+
+static void goodix511_reset_state(FpiDeviceGoodixTls511 *self) {}
+
+static void dev_deactivate(FpImageDevice *img_dev)
+{
+ FpDevice *dev = FP_DEVICE(img_dev);
+ goodix_reset_state(dev);
+ GError *error = NULL;
+ goodix_shutdown_tls(dev, &error);
+ goodix511_reset_state(FPI_DEVICE_GOODIXTLS511(img_dev));
+ fpi_image_device_deactivate_complete(img_dev, error);
+}
+
+// ---- DEV SECTION END ----
+
+static void fpi_device_goodixtls511_init(FpiDeviceGoodixTls511 *self)
+{
+ self->frames = g_slist_alloc();
+}
+
+static void fpi_device_goodixtls511_class_init(
+ FpiDeviceGoodixTls511Class *class)
+{
+ FpiDeviceGoodixTlsClass *gx_class = FPI_DEVICE_GOODIXTLS_CLASS(class);
+ FpDeviceClass *dev_class = FP_DEVICE_CLASS(class);
+ FpImageDeviceClass *img_dev_class = FP_IMAGE_DEVICE_CLASS(class);
+
+ gx_class->interface = GOODIX_511_INTERFACE;
+ gx_class->ep_in = GOODIX_511_EP_IN;
+ gx_class->ep_out = GOODIX_511_EP_OUT;
+
+ dev_class->id = "goodixtls511";
+ dev_class->full_name = "Goodix TLS Fingerprint Sensor 511";
+ dev_class->type = FP_DEVICE_TYPE_USB;
+ dev_class->id_table = id_table;
+
+ dev_class->scan_type = FP_SCAN_TYPE_PRESS;
+
+ // TODO
+ img_dev_class->bz3_threshold = 24;
+ img_dev_class->img_width = GOODIX511_WIDTH;
+ img_dev_class->img_height = GOODIX511_HEIGHT;
+
+ img_dev_class->img_open = dev_init;
+ img_dev_class->img_close = dev_deinit;
+ img_dev_class->activate = dev_activate;
+ img_dev_class->change_state = dev_change_state;
+ img_dev_class->deactivate = dev_deactivate;
+
+ fpi_device_class_auto_initialize_features(dev_class);
+}
diff --git a/libfprint/drivers/goodixtls/goodix511.h b/libfprint/drivers/goodixtls/goodix511.h
new file mode 100644
index 00000000..7e00fa5b
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodix511.h
@@ -0,0 +1,64 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#pragma once
+
+#define GOODIX_511_INTERFACE (0)
+#define GOODIX_511_EP_IN (0x1 | FPI_USB_ENDPOINT_IN)
+#define GOODIX_511_EP_OUT (0x1 | FPI_USB_ENDPOINT_OUT)
+
+#define GOODIX_511_FIRMWARE_VERSION ("GF_ST411SEC_APP_12117")
+
+#define GOODIX_511_PSK_FLAGS (0xbb020003)
+
+#define GOODIX_511_RESET_NUMBER (2048)
+
+const guint8 goodix_511_psk_0[] = {
+ 0xba, 0x1a, 0x86, 0x03, 0x7c, 0x1d, 0x3c, 0x71, 0xc3, 0xaf, 0x34,
+ 0x49, 0x55, 0xbd, 0x69, 0xa9, 0xa9, 0x86, 0x1d, 0x9e, 0x91, 0x1f,
+ 0xa2, 0x49, 0x85, 0xb6, 0x77, 0xe8, 0xdb, 0xd7, 0x2d, 0x43};
+
+guint8 goodix_511_config[] = {
+ 0x70, 0x11, 0x60, 0x71, 0x2c, 0x9d, 0x2c, 0xc9, 0x1c, 0xe5, 0x18, 0xfd,
+ 0x00, 0xfd, 0x00, 0xfd, 0x03, 0xba, 0x00, 0x01, 0x80, 0xca, 0x00, 0x04,
+ 0x00, 0x84, 0x00, 0x15, 0xb3, 0x86, 0x00, 0x00, 0xc4, 0x88, 0x00, 0x00,
+ 0xba, 0x8a, 0x00, 0x00, 0xb2, 0x8c, 0x00, 0x00, 0xaa, 0x8e, 0x00, 0x00,
+ 0xc1, 0x90, 0x00, 0xbb, 0xbb, 0x92, 0x00, 0xb1, 0xb1, 0x94, 0x00, 0x00,
+ 0xa8, 0x96, 0x00, 0x00, 0xb6, 0x98, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00,
+ 0x00, 0xd2, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0xd6, 0x00, 0x00,
+ 0x00, 0xd8, 0x00, 0x00, 0x00, 0x50, 0x00, 0x01, 0x05, 0xd0, 0x00, 0x00,
+ 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x78, 0x56, 0x74, 0x00, 0x34,
+ 0x12, 0x20, 0x00, 0x10, 0x40, 0x2a, 0x01, 0x02, 0x04, 0x22, 0x00, 0x01,
+ 0x20, 0x24, 0x00, 0x32, 0x00, 0x80, 0x00, 0x01, 0x00, 0x5c, 0x00, 0x80,
+ 0x00, 0x56, 0x00, 0x04, 0x20, 0x58, 0x00, 0x03, 0x02, 0x32, 0x00, 0x0c,
+ 0x02, 0x66, 0x00, 0x03, 0x00, 0x7c, 0x00, 0x00, 0x58, 0x82, 0x00, 0x80,
+ 0x15, 0x2a, 0x01, 0x82, 0x03, 0x22, 0x00, 0x01, 0x20, 0x24, 0x00, 0x14,
+ 0x00, 0x80, 0x00, 0x01, 0x00, 0x5c, 0x00, 0x00, 0x01, 0x56, 0x00, 0x04,
+ 0x20, 0x58, 0x00, 0x03, 0x02, 0x32, 0x00, 0x0c, 0x02, 0x66, 0x00, 0x03,
+ 0x00, 0x7c, 0x00, 0x00, 0x58, 0x82, 0x00, 0x80, 0x1f, 0x2a, 0x01, 0x08,
+ 0x00, 0x5c, 0x00, 0x80, 0x00, 0x54, 0x00, 0x10, 0x01, 0x62, 0x00, 0x04,
+ 0x03, 0x64, 0x00, 0x19, 0x00, 0x66, 0x00, 0x03, 0x00, 0x7c, 0x00, 0x01,
+ 0x58, 0x2a, 0x01, 0x08, 0x00, 0x5c, 0x00, 0x00, 0x01, 0x52, 0x00, 0x08,
+ 0x00, 0x54, 0x00, 0x00, 0x01, 0x66, 0x00, 0x03, 0x00, 0x7c, 0x00, 0x01,
+ 0x58, 0x00, 0x89, 0x2e};
+
+static const FpIdEntry id_table[] = {
+ {.vid = 0x27c6, .pid = 0x5110},
+ {.vid = 0, .pid = 0, .driver_data = 0},
+};
diff --git a/libfprint/drivers/goodixtls/goodix53xd.c b/libfprint/drivers/goodixtls/goodix53xd.c
new file mode 100644
index 00000000..6bf8d5f1
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodix53xd.c
@@ -0,0 +1,860 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+// Copyright (C) 2021 Michael Teuscher <michael.teuscher@pm.me>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#include "fp-device.h"
+#include "fp-image-device.h"
+#include "fp-image.h"
+#include "fpi-assembling.h"
+#include "fpi-context.h"
+#include "fpi-image-device.h"
+#include "fpi-image.h"
+#include "fpi-ssm.h"
+#include "glibconfig.h"
+#include "gusb/gusb-device.h"
+#include <stdio.h>
+#include <stdlib.h>
+#define FP_COMPONENT "goodixtls53xd"
+
+#include <glib.h>
+#include <string.h>
+
+#include "drivers_api.h"
+#include "goodix.h"
+#include "goodix_proto.h"
+#include "goodix53xd.h"
+
+#include <math.h>
+
+#define GOODIX53XD_WIDTH 64
+#define GOODIX53XD_HEIGHT 80
+#define GOODIX53XD_SCAN_WIDTH 64
+#define GOODIX53XD_FRAME_SIZE (GOODIX53XD_WIDTH * GOODIX53XD_HEIGHT)
+// For every 4 pixels there are 6 bytes and there are 8 extra start bytes and 5
+// extra end
+#define GOODIX53XD_RAW_FRAME_SIZE \
+ (GOODIX53XD_HEIGHT * GOODIX53XD_SCAN_WIDTH) / 4 * 6
+#define GOODIX53XD_CAP_FRAMES 1 // Number of frames we capture per swipe
+
+typedef unsigned short Goodix53xdPix;
+
+struct _FpiDeviceGoodixTls53XD {
+ FpiDeviceGoodixTls parent;
+
+ guint8* otp;
+
+ GSList* frames;
+
+ Goodix53xdPix empty_img[GOODIX53XD_FRAME_SIZE];
+};
+
+G_DECLARE_FINAL_TYPE(FpiDeviceGoodixTls53XD, fpi_device_goodixtls53xd, FPI,
+ DEVICE_GOODIXTLS53XD, FpiDeviceGoodixTls);
+
+G_DEFINE_TYPE(FpiDeviceGoodixTls53XD, fpi_device_goodixtls53xd,
+ FPI_TYPE_DEVICE_GOODIXTLS);
+
+typedef struct
+_frame_processing_info {
+ FpiDeviceGoodixTls53XD* dev;
+ GSList** frames;
+
+} frame_processing_info;
+// ---- ACTIVE SECTION START ----
+
+/**
+ * @brief Checks nothing and moves the state machine to the next state
+ *
+ * @param dev
+ * @param user_data
+ * @param error
+ */
+static void
+check_none(FpDevice *dev, gpointer user_data, GError *error) {
+ if (error) {
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fpi_ssm_next_state(user_data);
+}
+/**
+ * @brief Checks that firmware name is expected and advances to next state
+ *
+ * @param dev
+ * @param firmware
+ * @param user_data
+ * @param error
+ */
+static void
+check_firmware_version(FpDevice *dev, gchar *firmware,
+ gpointer user_data, GError *error) {
+ if (error) {
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fp_dbg("Device firmware: \"%s\"", firmware);
+
+ if (strcmp(firmware, GOODIX_53XD_FIRMWARE_VERSION)) {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device firmware: \"%s\"", firmware);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fpi_ssm_next_state(user_data);
+}
+/**
+ * @brief Checks that device was reset properly and advances to next state
+ *
+ * @param dev
+ * @param success
+ * @param number
+ * @param user_data
+ * @param error
+ */
+static void
+check_reset(FpDevice *dev, gboolean success, guint16 number,
+ gpointer user_data, GError *error) {
+ if (error) {
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ if (!success) {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to reset device");
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fp_dbg("Device reset number: %d", number);
+
+ if (number != GOODIX_53XD_RESET_NUMBER) {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device reset number: %d", number);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fpi_ssm_next_state(user_data);
+}
+/**
+ * @brief Check if preshared key is as expected then advances to next state
+ *
+ * @param dev
+ * @param success
+ * @param flags
+ * @param psk
+ * @param length
+ * @param user_data
+ * @param error
+ */
+static void
+check_preset_psk_read(FpDevice *dev, gboolean success,
+ guint32 flags, guint8 *psk, guint16 length,
+ gpointer user_data, GError *error) {
+ g_autofree gchar *psk_str = data_to_str(psk, length);
+
+ if (error) {
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ if (!success) {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to read PSK from device");
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fp_dbg("Device PSK: 0x%s", psk_str);
+ fp_dbg("Device PSK flags: 0x%08x", flags);
+
+ if (flags != GOODIX_53XD_PSK_FLAGS) {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device PSK flags: 0x%08x", flags);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ if (length != sizeof(goodix_53xd_psk_0)) {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device PSK: 0x%s", psk_str);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ if (memcmp(psk, goodix_53xd_psk_0, sizeof(goodix_53xd_psk_0))) {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "Invalid device PSK: 0x%s", psk_str);
+ fpi_ssm_mark_failed(user_data, error);
+ return;
+ }
+
+ fpi_ssm_next_state(user_data);
+}
+/**
+ * @brief Checks for error and advances to next state
+ *
+ * @param dev
+ * @param user_data
+ * @param err
+ */
+static void
+check_idle(FpDevice* dev, gpointer user_data, GError* err)
+{
+
+ if (err) {
+ fpi_ssm_mark_failed(user_data, err);
+ return;
+ }
+ fpi_ssm_next_state(user_data);
+}
+
+static void
+check_config_upload(FpDevice* dev, gboolean success,
+ gpointer user_data, GError* error)
+{
+ if (error) {
+ fpi_ssm_mark_failed(user_data, error);
+ }
+ else if (!success) {
+ fpi_ssm_mark_failed(user_data,
+ g_error_new(FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO,
+ "failed to upload mcu config"));
+ }
+ else {
+ fpi_ssm_next_state(user_data);
+ }
+}
+
+static void
+read_otp_callback(FpDevice* dev, guint8* data, guint16 len,
+ gpointer ssm, GError* err)
+{
+ if (err) {
+ fpi_ssm_mark_failed(ssm, err);
+ return;
+ }
+ if (len < 64) {
+ fpi_ssm_mark_failed(ssm, g_error_new(FP_DEVICE_ERROR,
+ FP_DEVICE_ERROR_DATA_INVALID,
+ "OTP is invalid (len: %d)", 64));
+ return;
+ }
+ FpiDeviceGoodixTls53XD* self = FPI_DEVICE_GOODIXTLS53XD(dev);
+ self->otp = malloc(64);
+ memcpy(self->otp, data, len);
+ fpi_ssm_next_state(ssm);
+}
+
+/**
+ * @brief Runs functions depending on current state of SSM
+ * @details This is the main part of the driver. This function runs every time
+ * the state machine moves to the next state. This function looks at the
+ * current state of the ssm responds accordingly
+ *
+ * @param ssm
+ * @param dev
+ */
+static void
+activate_run_state(FpiSsm* ssm, FpDevice* dev)
+{
+
+ switch (fpi_ssm_get_cur_state(ssm)) {
+ case ACTIVATE_READ_AND_NOP:
+ // Nop seems to clear the previous command buffer. But we are
+ // unable to do so.
+ goodix_start_read_loop(dev);
+ goodix_send_nop(dev, check_none, ssm);
+ break;
+
+ case ACTIVATE_ENABLE_CHIP:
+ goodix_send_enable_chip(dev, TRUE, check_none, ssm);
+ break;
+
+ case ACTIVATE_NOP:
+ goodix_send_nop(dev, check_none, ssm);
+ break;
+
+ case ACTIVATE_CHECK_FW_VER:
+ goodix_send_firmware_version(dev, check_firmware_version, ssm);
+ break;
+
+ case ACTIVATE_CHECK_PSK:
+ goodix_send_preset_psk_read(dev, GOODIX_53XD_PSK_FLAGS, 32,
+ check_preset_psk_read, ssm);
+ break;
+
+ case ACTIVATE_RESET:
+ goodix_send_reset(dev, TRUE, 20, check_reset, ssm);
+ break;
+
+ case ACTIVATE_OTP:
+ goodix_send_read_otp(dev, read_otp_callback, ssm);
+ break;
+
+ case ACTIVATE_SET_MCU_IDLE:
+ goodix_send_mcu_switch_to_idle_mode(dev, 20, check_idle, ssm);
+ break;
+
+ case ACTIVATE_SET_MCU_CONFIG:
+ goodix_send_upload_config_mcu(dev, goodix_53xd_config,
+ sizeof(goodix_53xd_config), NULL,
+ check_config_upload, ssm);
+ break;
+ default: // TODO What happens if we have a bad state?
+ }
+}
+/**
+ * @brief Checks for error, then marks device activation as complete if no error
+ * @details Called when finishing device activation, either successful or not
+ *
+ * @param dev
+ * @param user_data
+ * @param error
+ */
+static void
+tls_activation_complete(FpDevice* dev, gpointer user_data,
+ GError* error)
+{
+ if (error) {
+ fp_err("failed to complete tls activation: %s", error->message);
+ return;
+ }
+ FpImageDevice* image_dev = FP_IMAGE_DEVICE(dev);
+
+ fpi_image_device_activate_complete(image_dev, error);
+}
+/**
+ * @brief Activates TLS if no errors have occured, otherwise spit an error
+ *
+ * @param ssm
+ * @param dev
+ * @param error
+ */
+static void
+activate_complete(FpiSsm* ssm, FpDevice* dev, GError* error)
+{
+ G_DEBUG_HERE();
+ if (!error)
+ goodix_tls(dev, tls_activation_complete, NULL);
+ else {
+ fp_err("failed during activation: %s (code: %d)", error->message,
+ error->code);
+ fpi_image_device_activate_complete(FP_IMAGE_DEVICE(dev), error);
+ }
+}
+
+// ---- ACTIVE SECTION END ----
+
+// -----------------------------------------------------------------------------
+
+// ---- SCAN SECTION START ----
+/**
+ * @brief Checks for errors and moves to next state in ssm
+ *
+ * @param dev
+ * @param data
+ * @param len
+ * @param ssm
+ * @param err
+ */
+static void
+check_none_cmd(FpDevice* dev, guint8* data, guint16 len,
+ gpointer ssm, GError* err)
+{
+ if (err) {
+ fpi_ssm_mark_failed(ssm, err);
+ return;
+ }
+ fpi_ssm_next_state(ssm);
+}
+/**
+ * @brief Get the pixel located at (x,y)
+ *
+ * @param ctx
+ * @param frame
+ * @param x
+ * @param y
+ * @return unsigned char
+ */
+static unsigned char
+get_pix(struct fpi_frame_asmbl_ctx* ctx,
+ struct fpi_frame* frame, unsigned int x,
+ unsigned int y)
+{
+ return frame->data[x + y * GOODIX53XD_WIDTH];
+}
+/**
+ * @brief Transforms raw image data from the sensor to usable image data
+ *
+ * @param frame
+ * @param raw_frame
+ */
+static void
+decode_frame(Goodix53xdPix frame[GOODIX53XD_FRAME_SIZE],
+ const guint8* raw_frame)
+{
+ Goodix53xdPix uncropped[GOODIX53XD_SCAN_WIDTH * GOODIX53XD_HEIGHT];
+ Goodix53xdPix* pix = uncropped;
+ for (int i = 0; i < GOODIX53XD_RAW_FRAME_SIZE; i += 6) {
+ const guint8* chunk = raw_frame + i;
+ *pix++ = ((chunk[0] & 0xf) << 8) + chunk[1];
+ *pix++ = (chunk[3] << 4) + (chunk[0] >> 4);
+ *pix++ = ((chunk[5] & 0xf) << 8) + chunk[2];
+ *pix++ = (chunk[4] << 4) + (chunk[5] >> 4);
+ }
+
+ for (int y = 0; y != GOODIX53XD_HEIGHT; ++y) {
+ for (int x = 0; x != GOODIX53XD_WIDTH; ++x) {
+ const int idx = x + y * GOODIX53XD_SCAN_WIDTH;
+ frame[x + y * GOODIX53XD_WIDTH] = uncropped[idx];
+ }
+ }
+}
+
+/**
+ * @brief Squashes the 12 bit pixels of a raw frame into the 4 bit pixels used
+ * by libfprint.
+ * @details Borrowed from the elan driver. We reduce frames to
+ * within the max and min.
+ *
+ * @param frame
+ * @param squashed
+ */
+static void
+squash_frame_linear(Goodix53xdPix* frame, guint8* squashed)
+{
+ Goodix53xdPix min = 0xffff;
+ Goodix53xdPix max = 0;
+
+ for (int i = 0; i != GOODIX53XD_FRAME_SIZE; ++i) {
+ const Goodix53xdPix pix = frame[i];
+ if (pix < min) {
+ min = pix;
+ }
+ if (pix > max) {
+ max = pix;
+ }
+ }
+
+ for (int i = 0; i != GOODIX53XD_FRAME_SIZE; ++i) {
+ const Goodix53xdPix pix = frame[i];
+ if (pix - min == 0 || max - min == 0) {
+ squashed[i] = 0;
+ }
+ else {
+ squashed[i] = (pix - min) * 0xff / (max - min);
+ }
+ }
+}
+
+/**
+ * @brief Subtracts the background from the frame
+ *
+ * @param frame
+ * @param background
+ */
+
+static void
+process_frame(Goodix53xdPix* raw_frame, frame_processing_info* info)
+{
+ struct fpi_frame* frame =
+ g_malloc(GOODIX53XD_FRAME_SIZE + sizeof(struct fpi_frame));
+ //postprocess_frame(raw_frame, info->dev->empty_img);
+ squash_frame_linear(raw_frame, frame->data);
+
+ *(info->frames) = g_slist_append(*(info->frames), frame);
+}
+/**
+ * @brief Save the frame to an internal buffer
+ *
+ * @param self
+ * @param raw
+ */
+static void
+save_frame(FpiDeviceGoodixTls53XD* self, guint8* raw)
+{
+ Goodix53xdPix* frame =
+ malloc(GOODIX53XD_FRAME_SIZE * sizeof(Goodix53xdPix));
+ decode_frame(frame, raw);
+ self->frames = g_slist_append(self->frames, frame);
+}
+/**
+ * @brief
+ *
+ * @param dev
+ * @param data
+ * @param len
+ * @param ssm
+ * @param err
+ */
+static void
+scan_on_read_img(FpDevice* dev, guint8* data, guint16 len,
+ gpointer ssm, GError* err)
+{
+ if (err) {
+ fpi_ssm_mark_failed(ssm, err);
+ return;
+ }
+
+
+ FpiDeviceGoodixTls53XD* self = FPI_DEVICE_GOODIXTLS53XD(dev);
+ save_frame(self, data);
+ if (g_slist_length(self->frames) <= GOODIX53XD_CAP_FRAMES) {
+ fpi_ssm_jump_to_state(ssm, SCAN_STAGE_SWITCH_TO_FDT_MODE);
+ }
+ else {
+ GSList* raw_frames = g_slist_nth(self->frames, 1);
+
+ FpImageDevice* img_dev = FP_IMAGE_DEVICE(dev);
+ struct fpi_frame_asmbl_ctx assembly_ctx;
+ assembly_ctx.frame_width = GOODIX53XD_WIDTH;
+ assembly_ctx.frame_height = GOODIX53XD_HEIGHT;
+ assembly_ctx.image_width = GOODIX53XD_WIDTH*3;
+ assembly_ctx.get_pixel = get_pix;
+
+ GSList* frames = NULL;
+ frame_processing_info pinfo = {.dev = self, .frames = &frames};
+
+ g_slist_foreach(raw_frames, (GFunc) process_frame, &pinfo);
+ frames = g_slist_reverse(frames);
+
+ fpi_do_movement_estimation(&assembly_ctx, frames);
+ FpImage* img = fpi_assemble_frames(&assembly_ctx, frames);
+
+ g_slist_free_full(frames, g_free);
+ g_slist_free_full(self->frames, g_free);
+ self->frames = g_slist_alloc();
+
+ fpi_image_device_image_captured(img_dev, img);
+
+
+ fpi_image_device_report_finger_status(img_dev, FALSE);
+
+ fpi_ssm_next_state(ssm);
+ }
+}
+
+
+static void
+goodix_53xd_send_mcu_get_image(FpDevice* dev, guint8* payload, guint16 length,
+ GoodixImageCallback callback, gpointer user_data)
+{
+
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_GET_IMAGE, payload,
+ length, NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ goodix_receive_default, cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_GET_IMAGE, payload,
+ length, NULL, TRUE, GOODIX_TIMEOUT, TRUE,
+ NULL, NULL);
+}
+
+static void
+goodix_53xd_tls_read_image(FpDevice* dev, guint8* payload,
+ guint16 length,
+ GoodixImageCallback callback,
+ gpointer user_data)
+{
+ g_assert(callback);
+ GoodixCallbackInfo *cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_53xd_send_mcu_get_image(dev, payload, length,
+ goodix_tls_ready_image_handler, cb_info);
+}
+
+static void
+scan_get_img(FpDevice* dev, FpiSsm* ssm)
+{
+ FpImageDevice* img_dev = FP_IMAGE_DEVICE(dev);
+ FpiDeviceGoodixTls53XD* self = FPI_DEVICE_GOODIXTLS53XD(img_dev);
+ guint8 payload[] = {0x41, 0x03, self->otp[26], 0x00,
+ self->otp[26] - 6, 0x00, self->otp[45], 0x00, self->otp[45] - 4, 0x00};
+ goodix_53xd_tls_read_image(dev, (guint8 *)&payload,
+ sizeof(payload), scan_on_read_img, ssm);
+}
+
+static void
+goodix_send_mcu_switch_to_fdt_down_with_no_reply(FpDevice *dev,
+ guint8 *mode,
+ guint16 length,
+ GDestroyNotify free_func,
+ GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_DOWN, mode, length,
+ free_func, TRUE, 0, FALSE, goodix_receive_default,
+ cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_DOWN, mode, length,
+ free_func, TRUE, 0, FALSE, NULL, NULL);
+}
+
+static void
+goodix_send_mcu_switch_to_fdt_mode_no_reply(FpDevice *dev,
+ guint8 *mode,
+ guint16 length,
+ GDestroyNotify free_func,
+ GoodixDefaultCallback callback,
+ gpointer user_data)
+{
+ GoodixCallbackInfo *cb_info;
+
+ if (callback)
+ {
+ cb_info = malloc(sizeof(GoodixCallbackInfo));
+
+ cb_info->callback = G_CALLBACK(callback);
+ cb_info->user_data = user_data;
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_MODE, mode, length,
+ free_func, TRUE, 0, FALSE, goodix_receive_default,
+ cb_info);
+ return;
+ }
+
+ goodix_send_protocol(dev, GOODIX_CMD_MCU_SWITCH_TO_FDT_MODE, mode, length,
+ free_func, TRUE, 0, FALSE, NULL, NULL);
+}
+
+static void
+scan_run_state(FpiSsm* ssm, FpDevice* dev)
+{
+ FpImageDevice* img_dev = FP_IMAGE_DEVICE(dev);
+ FpiDeviceGoodixTls53XD* self = FPI_DEVICE_GOODIXTLS53XD(img_dev);
+
+
+ switch (fpi_ssm_get_cur_state(ssm)) {
+
+ case SCAN_STAGE_SWITCH_TO_FDT_MODE:
+ goodix_send_mcu_switch_to_fdt_mode_no_reply(dev,
+ (guint8 *)fdt_switch_state_mode_53xd,
+ sizeof(fdt_switch_state_mode_53xd), NULL,
+ check_none_cmd, ssm);
+ break;
+
+ case SCAN_STAGE_SWITCH_TO_FDT_DOWN:
+ // FDT Down Cali
+ fdt_switch_state_down_53xd[2] = self->otp[33];
+ fdt_switch_state_down_53xd[4] = self->otp[41];
+ fdt_switch_state_down_53xd[6] = self->otp[42];
+ fdt_switch_state_down_53xd[8] = self->otp[43];
+
+ // First FDT down must not send a reply
+ fdt_switch_state_down_53xd[26] = 0x00;
+ goodix_send_mcu_switch_to_fdt_down_with_no_reply(dev,
+ (guint8*) fdt_switch_state_down_53xd,
+ sizeof(fdt_switch_state_down_53xd),
+ NULL, receive_fdt_down_ack, ssm);
+ break;
+ case SCAN_STAGE_GET_IMG:
+ fpi_image_device_report_finger_status(img_dev, TRUE);
+ guint16 payload = {0x05};
+ goodix_send_write_sensor_register(dev,
+ 556, payload, write_sensor_complete, ssm);
+ break;
+ }
+}
+
+static void
+receive_fdt_down_ack(FpDevice* dev, guint8* data, guint16 len,
+ gpointer ssm, GError* err)
+{
+ if (err) {
+ fpi_ssm_mark_failed(ssm, err);
+ return;
+ }
+
+ // Second FDT down must send a response
+ fdt_switch_state_down_53xd[26] = 0x01;
+ goodix_send_mcu_switch_to_fdt_down(dev,
+ (guint8*) fdt_switch_state_down_53xd,
+ sizeof(fdt_switch_state_down_53xd), NULL,
+ check_none_cmd, ssm);
+}
+
+static void
+write_sensor_complete(FpDevice *dev,
+ gpointer user_data, GError *error)
+{
+ if (error) {
+ fp_err("failed to scan: %s (code: %d)", error->message, error->code);
+ return;
+ }
+ scan_get_img(dev, user_data);
+}
+
+static void
+scan_complete(FpiSsm* ssm, FpDevice* dev, GError* error)
+{
+ if (error) {
+ fp_err("failed to scan: %s (code: %d)", error->message, error->code);
+ return;
+ }
+ fp_dbg("finished scan");
+}
+
+static void
+scan_start(FpiDeviceGoodixTls53XD* dev)
+{
+ fpi_ssm_start(fpi_ssm_new(FP_DEVICE(dev), scan_run_state, SCAN_STAGE_NUM),
+ scan_complete);
+}
+
+// ---- SCAN SECTION END ----
+
+// -----------------------------------------------------------------------------
+
+// ---- DEV SECTION START ----
+
+static void
+dev_init(FpImageDevice *img_dev) {
+ FpDevice *dev = FP_DEVICE(img_dev);
+ GError *error = NULL;
+
+ if (goodix_dev_init(dev, &error)) {
+ fpi_image_device_open_complete(img_dev, error);
+ return;
+ }
+
+ fpi_image_device_open_complete(img_dev, NULL);
+}
+
+static void
+dev_deinit(FpImageDevice *img_dev) {
+ FpDevice *dev = FP_DEVICE(img_dev);
+ GError *error = NULL;
+
+ if (goodix_dev_deinit(dev, &error)) {
+ fpi_image_device_close_complete(img_dev, error);
+ return;
+ }
+
+ fpi_image_device_close_complete(img_dev, NULL);
+}
+
+static void
+dev_activate(FpImageDevice *img_dev) {
+ FpDevice* dev = FP_DEVICE(img_dev);
+ FpiSsm* ssm = fpi_ssm_new(dev, activate_run_state, ACTIVATE_NUM_STATES);
+ fpi_ssm_start(ssm, activate_complete);
+}
+
+static void
+dev_change_state(FpImageDevice* img_dev, FpiImageDeviceState state)
+{
+ FpiDeviceGoodixTls53XD* self = FPI_DEVICE_GOODIXTLS53XD(img_dev);
+ G_DEBUG_HERE();
+
+ if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) {
+ scan_start(self);
+ }
+}
+
+static void
+goodix53xd_reset_state(FpiDeviceGoodixTls53XD* self) {}
+
+static void
+dev_deactivate(FpImageDevice *img_dev) {
+ FpDevice* dev = FP_DEVICE(img_dev);
+ goodix_reset_state(dev);
+ GError* error = NULL;
+ goodix_shutdown_tls(dev, &error);
+ goodix53xd_reset_state(FPI_DEVICE_GOODIXTLS53XD(img_dev));
+ fpi_image_device_deactivate_complete(img_dev, error);
+}
+
+// ---- DEV SECTION END ----
+
+static void
+fpi_device_goodixtls53xd_init(FpiDeviceGoodixTls53XD* self)
+{
+ self->frames = g_slist_alloc();
+}
+
+/*
+ This is the only thing the rest of libfprint cares about. This tells
+ libfprint how to interact with the device, that it is a image based device,
+ usb based device, which endpoints to use for it, etc
+*/
+static void
+fpi_device_goodixtls53xd_class_init(FpiDeviceGoodixTls53XDClass *class) {
+ FpiDeviceGoodixTlsClass *gx_class = FPI_DEVICE_GOODIXTLS_CLASS(class);
+ FpDeviceClass *dev_class = FP_DEVICE_CLASS(class);
+ FpImageDeviceClass *img_dev_class = FP_IMAGE_DEVICE_CLASS(class);
+
+ gx_class->interface = GOODIX_53XD_INTERFACE;
+ gx_class->ep_in = GOODIX_53XD_EP_IN;
+ gx_class->ep_out = GOODIX_53XD_EP_OUT;
+
+ dev_class->id = "goodixtls53xd";
+ dev_class->full_name = "Goodix TLS Fingerprint Sensor 53XD";
+ dev_class->type = FP_DEVICE_TYPE_USB;
+ dev_class->id_table = id_table; // Devices supported by driver
+
+ dev_class->scan_type = FP_SCAN_TYPE_PRESS; // Either scan or swipe
+
+ img_dev_class->bz3_threshold = 24; // Detection threshold
+ img_dev_class->img_width = GOODIX53XD_WIDTH; // Only given for constant width
+ img_dev_class->img_height = GOODIX53XD_HEIGHT; // Same but height
+
+ img_dev_class->img_open = dev_init; // Called to claim and open device
+ img_dev_class->img_close = dev_deinit; // Called to close and
+ // release device
+ img_dev_class->activate = dev_activate; // Called to start finger scanning
+ // and/or finger detection
+ img_dev_class->deactivate = dev_deactivate; // Called to stop waiting
+ //for finger
+ img_dev_class->change_state = dev_change_state; // Called anytime the device
+ // changes state, such as
+ // going from idle to
+ // waiting for finger
+
+
+ fpi_device_class_auto_initialize_features(dev_class);
+}
diff --git a/libfprint/drivers/goodixtls/goodix53xd.h b/libfprint/drivers/goodixtls/goodix53xd.h
new file mode 100644
index 00000000..171e4fdd
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodix53xd.h
@@ -0,0 +1,209 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+// Copyright (C) 2021 Michael Teuscher <michael.teuscher@pm.me>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#pragma once
+
+#define GOODIX_53XD_INTERFACE (0)
+#define GOODIX_53XD_EP_IN (0x83 | FPI_USB_ENDPOINT_IN)
+#define GOODIX_53XD_EP_OUT (0x1 | FPI_USB_ENDPOINT_OUT)
+
+#define GOODIX_53XD_FIRMWARE_VERSION ("GF5298_GM168SEC_APP_13016")
+
+#define GOODIX_53XD_PSK_FLAGS (0xbb020001)
+
+#define GOODIX_53XD_RESET_NUMBER (2048)
+
+/*
+ The preshared key that is used for TLS communication. This should NOT be
+ hardcoded, we need to address this before shipping the driver
+ TODO: Do not let this go into prod for fuck's sake
+
+ Preshared key is 32 bytes in length
+*/
+const guint8
+goodix_53xd_psk_0[] = {
+ 0x66, 0x68, 0x7a, 0xad, 0xf8, 0x62, 0xbd, 0x77,
+ 0x6c, 0x8f, 0xc1, 0x8b, 0x8e, 0x9f, 0x8e, 0x20,
+ 0x08, 0x97, 0x14, 0x85, 0x6e, 0xe2, 0x33, 0xb3,
+ 0x90, 0x2a, 0x59, 0x1d, 0x0d, 0x5f, 0x29, 0x25
+};
+
+/*
+ This is a binary blob that the proprietary driver calls the config. We have
+ no idea what it means, but here it is!
+
+ 256 bytes in length
+*/
+guint8
+goodix_53xd_config[] = {
+ // 0-31
+ 0x70, 0x11, 0x60, 0x71, 0x2c, 0x9d, 0x2c, 0xc9,
+ 0x1c, 0xe5, 0x18, 0xfd, 0x00, 0xfd, 0x00, 0xfd,
+ 0x03, 0xba, 0x00, 0x01, 0x80, 0xca, 0x00, 0x08,
+ 0x00, 0x84, 0x00, 0xbe, 0xc3, 0x86, 0x00, 0xb1,
+ // 32-63
+ 0xb6, 0x88, 0x00, 0xba, 0xba, 0x8a, 0x00, 0xb3,
+ 0xb3, 0x8c, 0x00, 0xbc, 0xbc, 0x8e, 0x00, 0xb1,
+ 0xb1, 0x90, 0x00, 0xbb, 0xbb, 0x92, 0x00, 0xb1,
+ 0xb1, 0x94, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
+ // 64-95
+ 0x00, 0x98, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00,
+ 0x00, 0xd2, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00,
+ 0x00, 0xd6, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00,
+ 0x00, 0x50, 0x00, 0x01, 0x05, 0xd0, 0x00, 0x00,
+ // 96-127
+ 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x78,
+ 0x56, 0x74, 0x00, 0x34, 0x12, 0x20, 0x00, 0x10,
+ 0x40, 0x2a, 0x01, 0x02, 0x04, 0x22, 0x00, 0x01,
+ 0x20, 0x24, 0x00, 0x32, 0x00, 0x80, 0x00, 0x01,
+ // 128-159
+ 0x00, 0x5c, 0x00, 0x01, 0x01, 0x56, 0x00, 0x24,
+ 0x20, 0x58, 0x00, 0x01, 0x02, 0x32, 0x00, 0x04,
+ 0x02, 0x66, 0x00, 0x00, 0x02, 0x7c, 0x00, 0x00,
+ 0x58, 0x82, 0x00, 0x7f, 0x08, 0x2a, 0x01, 0x82,
+ // 160-191
+ 0x07, 0x22, 0x00, 0x01, 0x20, 0x24, 0x00, 0x14,
+ 0x00, 0x80, 0x00, 0x01, 0x40, 0x5c, 0x00, 0xe7,
+ 0x00, 0x56, 0x00, 0x06, 0x14, 0x58, 0x00, 0x04,
+ 0x02, 0x32, 0x00, 0x0c, 0x02, 0x66, 0x00, 0x00,
+ // 192-223
+ 0x02, 0x7c, 0x00, 0x00, 0x58, 0x82, 0x00, 0x80,
+ 0x08, 0x2a, 0x01, 0x08, 0x00, 0x5c, 0x00, 0x01,
+ 0x01, 0x54, 0x00, 0x00, 0x01, 0x62, 0x00, 0x08,
+ 0x04, 0x64, 0x00, 0x10, 0x00, 0x66, 0x00, 0x00,
+ // 224-255
+ 0x02, 0x7c, 0x00, 0x00, 0x58, 0x2a, 0x01, 0x08,
+ 0x00, 0x5c, 0x00, 0xdc, 0x00, 0x52, 0x00, 0x08,
+ 0x00, 0x54, 0x00, 0x00, 0x01, 0x66, 0x00, 0x00,
+ 0x02, 0x7c, 0x00, 0x00, 0x58, 0x20, 0xc5, 0x1d
+};
+
+/*
+ Another binary blob from the proprietary windows driver, switches to
+ "fdt mode". No idea what fdt stands for. Could stand for "Finger Down Touch"
+ or "Flattened Device Tree"?
+
+ 21 bytes in length (weird size but ok)
+*/
+
+guint8
+fdt_switch_state_mode_53xd[] = {
+ 0x0d, 0x01, 0x28, 0x01, 0x22, 0x01, 0x28, 0x01,
+ 0x24, 0x01, 0x91, 0x91, 0x8b, 0x8b, 0x96, 0x96,
+ 0x91, 0x91, 0x98, 0x98, 0x90, 0x90, 0x92, 0x92,
+ 0x88, 0x88, 0x00
+};
+
+/*
+ Another binary blob from the proprietary windows driver, switches to
+ "fdt down" or deactivates the fdt? No idea what fdt stands for.
+ Could stand for "Finger Down Touch" or "Flattened Device Tree"?
+
+ 21 bytes in length (weird size but ok)
+*/
+
+guint8
+fdt_switch_state_down_53xd[] = {
+ 0x8c, 0x01, 0x28, 0x01, 0x22, 0x01, 0x28, 0x01,
+ 0x24, 0x01, 0x91, 0x91, 0x8b, 0x8b, 0x96, 0x96,
+ 0x91, 0x91, 0x98, 0x98, 0x90, 0x90, 0x92, 0x92,
+ 0x88, 0x88, 0x00
+};
+
+/*
+ States to go through for scanning an empty image, processed by the ssm
+ TODO: Is this necessary?
+*/
+enum
+scan_empty_img_state {
+ SCAN_EMPTY_NAV0,
+ SCAN_EMPTY_GET_IMG,
+
+ SCAN_EMPTY_NUM,
+};
+
+/*
+ These are the states the driver iterates through when a device it supports
+ is "activated", with the final state just being a marker of how many states
+ there are in total
+
+ TODO: Checking the PSK and firmware can probably be moved to device init
+*/
+enum
+activate_states {
+ ACTIVATE_READ_AND_NOP, // First, send a no-operation
+ // command to get a clean slate
+ ACTIVATE_ENABLE_CHIP, // Second, tell the sensor to start
+ // listening for a finger
+ ACTIVATE_NOP, // Third, do nothing...? TODO
+ ACTIVATE_CHECK_FW_VER, // Fourth, check the running firmware
+ // is what we expect
+ ACTIVATE_CHECK_PSK, // Fifth, check that the preshared key
+ // used for TLS communication is as expected
+ ACTIVATE_RESET, // Sixth, no idea TODO
+ ACTIVATE_OTP, // Seventh, establish one time
+ // password for TLS communication
+ ACTIVATE_SET_MCU_IDLE, // Eighth, tell the sensor to idle
+ ACTIVATE_SET_MCU_CONFIG, // Ninth, pass configuration sensor
+ ACTIVATE_NUM_STATES, // Number of states in state machine, currently nine
+};
+
+/*
+ Stages for establishing one-time password for TLS communication
+*/
+enum
+otp_write_states {
+ OTP_WRITE_1,
+ OTP_WRITE_2,
+
+ OTP_WRITE_NUM,
+};
+
+/*
+ Stages to go through to acquire an image from the device, based on
+ windows proprietary driver
+*/
+enum
+SCAN_STAGES {
+ SCAN_STAGE_SWITCH_TO_FDT_DOWN, // Set FDT (no idea what it is) to down
+ SCAN_STAGE_SWITCH_TO_FDT_MODE, // Then immediately set it to "mode"?
+ SCAN_STAGE_GET_IMG, // Actually get the image
+
+ SCAN_STAGE_NUM
+};
+
+
+
+static void
+write_sensor_complete(FpDevice *dev, gpointer user_data, GError *error);
+static void
+receive_fdt_down_ack(FpDevice* dev, guint8* data, guint16 len, gpointer ssm,
+ GError* err);
+
+/*
+ These are the devices that are supported by this driver, identified
+ by the vendor id (.vid) and product id (.pid)
+*/
+static const
+FpIdEntry id_table[] = {
+ {.vid = 0x27c6, .pid = 0x538d},
+ {.vid = 0x27c6, .pid = 0x532d}, // XPS 13 2 in 1 (7390)
+ {.vid = 0, .pid = 0, .driver_data = 0},
+};
\ No newline at end of file
diff --git a/libfprint/drivers/goodixtls/goodix_proto.c b/libfprint/drivers/goodixtls/goodix_proto.c
new file mode 100644
index 00000000..d1a2c182
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodix_proto.c
@@ -0,0 +1,133 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <stdio.h>
+
+#include "goodix_proto.h"
+
+guint8 goodix_calc_checksum(guint8 *data, guint16 length)
+{
+ guint8 checksum = 0;
+
+ for (guint16 i = 0; i < length; i++)
+ checksum += data[i];
+
+ return checksum;
+}
+
+void goodix_encode_pack(guint8 flags, guint8 *payload, guint16 payload_len,
+ gboolean pad_data, guint8 **data, guint32 *data_len)
+{
+ GoodixPack *pack;
+ *data_len = sizeof(GoodixPack) + sizeof(guint8) + payload_len;
+
+ if (pad_data && *data_len % GOODIX_EP_OUT_MAX_BUF_SIZE)
+ *data_len +=
+ GOODIX_EP_OUT_MAX_BUF_SIZE - *data_len % GOODIX_EP_OUT_MAX_BUF_SIZE;
+
+ *data = g_malloc0(*data_len);
+ pack = (GoodixPack *)*data;
+
+ pack->flags = flags;
+ pack->length = GUINT16_TO_LE(payload_len);
+ (*data)[sizeof(GoodixPack)] = goodix_calc_checksum(*data, sizeof(GoodixPack));
+
+ memcpy(*data + sizeof(GoodixPack) + sizeof(guint8), payload, payload_len);
+}
+
+void goodix_encode_protocol(guint8 cmd, guint8 *payload, guint16 payload_len,
+ gboolean calc_checksum, gboolean pad_data,
+ guint8 **data, guint32 *data_len)
+{
+ GoodixProtocol *protocol;
+ *data_len = sizeof(GoodixProtocol) + payload_len + sizeof(guint8);
+
+ if (pad_data && *data_len % GOODIX_EP_OUT_MAX_BUF_SIZE)
+ *data_len +=
+ GOODIX_EP_OUT_MAX_BUF_SIZE - *data_len % GOODIX_EP_OUT_MAX_BUF_SIZE;
+
+ *data = g_malloc0(*data_len);
+ protocol = (GoodixProtocol *)*data;
+
+ protocol->cmd = cmd;
+ protocol->length = GUINT16_TO_LE(payload_len + sizeof(guint8));
+
+ memcpy(*data + sizeof(GoodixProtocol), payload, payload_len);
+
+ if (calc_checksum)
+ (*data)[sizeof(GoodixProtocol) + payload_len] =
+ 0xaa -
+ goodix_calc_checksum(*data, sizeof(GoodixProtocol) + payload_len);
+ else
+ (*data)[sizeof(GoodixProtocol) + payload_len] = GOODIX_NULL_CHECKSUM;
+}
+
+gboolean goodix_decode_pack(guint8 *data, guint32 data_len, guint8 *flags,
+ guint8 **payload, guint16 *payload_len,
+ gboolean *valid_checksum)
+{
+ GoodixPack *pack = (GoodixPack *)data;
+ guint16 length;
+
+ if (data_len < sizeof(GoodixPack) + sizeof(guint8))
+ return FALSE;
+
+ length = GUINT16_FROM_LE(pack->length);
+
+ if (data_len < length + sizeof(GoodixPack) + sizeof(guint8))
+ return FALSE;
+
+ *flags = pack->flags;
+ *payload = g_memdup(data + sizeof(GoodixPack) + sizeof(guint8), length);
+ *payload_len = length;
+ *valid_checksum = goodix_calc_checksum(data, sizeof(GoodixPack)) ==
+ data[sizeof(GoodixPack)];
+
+ return TRUE;
+}
+
+gboolean goodix_decode_protocol(guint8 *data, guint32 data_len, guint8 *cmd,
+ guint8 **payload, guint16 *payload_len,
+ gboolean *valid_checksum,
+ gboolean *valid_null_checksum)
+{
+ GoodixProtocol *protocol = (GoodixProtocol *)data;
+ guint16 length;
+
+ if (data_len < sizeof(GoodixProtocol) + sizeof(guint8))
+ return FALSE;
+
+ length = GUINT16_FROM_LE(protocol->length) - sizeof(guint8);
+
+ if (data_len < length + sizeof(GoodixProtocol) + sizeof(guint8))
+ return FALSE;
+
+ *cmd = protocol->cmd;
+ *payload = g_memdup(data + sizeof(GoodixProtocol), length);
+ *payload_len = length;
+ *valid_checksum =
+ 0xaa - goodix_calc_checksum(data, sizeof(GoodixProtocol) + length) ==
+ data[sizeof(GoodixProtocol) + length];
+ *valid_null_checksum =
+ GOODIX_NULL_CHECKSUM == data[sizeof(GoodixProtocol) + length];
+
+ return TRUE;
+}
diff --git a/libfprint/drivers/goodixtls/goodix_proto.h b/libfprint/drivers/goodixtls/goodix_proto.h
new file mode 100644
index 00000000..56cc2946
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodix_proto.h
@@ -0,0 +1,161 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#pragma once
+
+#define GOODIX_EP_IN_MAX_BUF_SIZE (0x10000)
+#define GOODIX_EP_OUT_MAX_BUF_SIZE (0x40)
+
+#define GOODIX_NULL_CHECKSUM (0x88)
+
+#define GOODIX_FLAGS_MSG_PROTOCOL (0xa0)
+#define GOODIX_FLAGS_TLS (0xb0)
+#define GOODIX_FLAGS_TLS_DATA (0xb2)
+
+#define GOODIX_CMD_NOP (0x00)
+#define GOODIX_CMD_MCU_GET_IMAGE (0x20)
+#define GOODIX_CMD_MCU_SWITCH_TO_FDT_DOWN (0x32)
+#define GOODIX_CMD_MCU_SWITCH_TO_FDT_UP (0x34)
+#define GOODIX_CMD_MCU_SWITCH_TO_FDT_MODE (0x36)
+#define GOODIX_CMD_NAV_0 (0x50)
+#define GOODIX_CMD_MCU_SWITCH_TO_IDLE_MODE (0x70)
+#define GOODIX_CMD_WRITE_SENSOR_REGISTER (0x80)
+#define GOODIX_CMD_READ_SENSOR_REGISTER (0x82)
+#define GOODIX_CMD_UPLOAD_CONFIG_MCU (0x90)
+#define GOODIX_CMD_SET_POWERDOWN_SCAN_FREQUENCY (0x94)
+#define GOODIX_CMD_ENABLE_CHIP (0x96)
+#define GOODIX_CMD_RESET (0xa2)
+#define GOODIX_CMD_READ_OTP (0xa6)
+#define GOODIX_CMD_FIRMWARE_VERSION (0xa8)
+#define GOODIX_CMD_QUERY_MCU_STATE (0xae)
+#define GOODIX_CMD_ACK (0xb0)
+#define GOODIX_CMD_REQUEST_TLS_CONNECTION (0xd0)
+#define GOODIX_CMD_TLS_SUCCESSFULLY_ESTABLISHED (0xd4)
+#define GOODIX_CMD_PRESET_PSK_WRITE (0xe0)
+#define GOODIX_CMD_PRESET_PSK_READ (0xe4)
+
+typedef struct __attribute__((__packed__)) _GoodixPack
+{
+ guint8 flags;
+ guint16 length;
+} GoodixPack;
+
+typedef struct __attribute__((__packed__)) _GoodixProtocol
+{
+ guint8 cmd;
+ guint16 length;
+} GoodixProtocol;
+
+typedef struct __attribute__((__packed__)) _GoodixAck
+{
+ guint8 cmd;
+ guint8 always_true : 1;
+ guint8 has_no_config : 1;
+ guint8 : 6;
+} GoodixAck;
+
+typedef struct __attribute__((__packed__)) _GoodixNop
+{
+ guint32 unknown;
+} GoodixNop;
+
+typedef struct __attribute__((__packed__)) _GoodixMcuSwitchToIdleMode
+{
+ guint8 sleep_time;
+ guint8 : 8;
+} GoodixMcuSwitchToIdleMode;
+
+typedef struct __attribute__((__packed__)) _GoodixWriteSensorRegister
+{
+ guint8 multiples;
+ guint16 address;
+ guint16 value;
+} GoodixWriteSensorRegister;
+
+typedef struct __attribute__((__packed__)) _GoodixReadSensorRegister
+{
+ guint8 multiples;
+ guint16 address;
+ guint8 length;
+ guint8 : 8;
+} GoodixReadSensorRegister;
+
+typedef struct __attribute__((__packed__)) _GoodixSetPowerdownScanFrequency
+{
+ guint16 powerdown_scan_frequency;
+} GoodixSetPowerdownScanFrequency;
+
+typedef struct __attribute__((__packed__)) _GoodixEnableChip
+{
+ guint8 enable;
+ guint8 : 8;
+} GoodixEnableChip;
+
+typedef struct __attribute__((__packed__)) _GoodixReset
+{
+ guint8 reset_sensor : 1;
+ guint8 soft_reset_mcu : 1;
+ guint8 : 6;
+ guint8 sleep_time;
+} GoodixReset;
+
+typedef struct __attribute__((__packed__)) _GoodixQueryMcuState
+{
+ guint8 unused_flags;
+} GoodixQueryMcuState;
+
+typedef struct __attribute__((__packed__)) _GoodixPresetPsk {
+ guint32 length;
+ guint32 offset;
+ guint32 flags;
+} GoodixPresetPsk;
+
+typedef struct __attribute__((__packed__)) _GoodixPresetPskResponse {
+ guint32 flags;
+ guint32 length;
+} GoodixPresetPskResponse;
+
+typedef struct __attribute__((__packed__)) _GoodixDefault
+{
+ guint8 unused_flags;
+ guint8 : 8;
+} GoodixDefault;
+
+typedef struct __attribute__((__packed__)) _GoodixNone
+{
+ guint16 : 16;
+} GoodixNone;
+
+guint8 goodix_calc_checksum(guint8 *data, guint16 length);
+
+void goodix_encode_pack(guint8 flags, guint8 *payload, guint16 payload_len,
+ gboolean pad_data, guint8 **data, guint32 *data_len);
+
+void goodix_encode_protocol(guint8 cmd, guint8 *payload, guint16 payload_len,
+ gboolean calc_checksum, gboolean pad_data,
+ guint8 **data, guint32 *data_len);
+
+gboolean goodix_decode_pack(guint8 *data, guint32 data_len, guint8 *flags,
+ guint8 **payload, guint16 *payload_len,
+ gboolean *valid_checksum);
+
+gboolean goodix_decode_protocol(guint8 *data, guint32 data_len, guint8 *cmd,
+ guint8 **payload, guint16 *payload_len,
+ gboolean *valid_checksum,
+ gboolean *valid_null_checksum);
diff --git a/libfprint/drivers/goodixtls/goodixtls.c b/libfprint/drivers/goodixtls/goodixtls.c
new file mode 100644
index 00000000..c4288626
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodixtls.c
@@ -0,0 +1,185 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <glib.h>
+#include <netinet/in.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <openssl/tls1.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "drivers_api.h"
+#include "fp-device.h"
+#include "fpi-device.h"
+#include "glibconfig.h"
+#include "goodix.h"
+#include "goodixtls.h"
+
+static GError* err_from_ssl(void)
+{
+ GError* err = malloc(sizeof(GError));
+ unsigned long code = ERR_get_error();
+ err->code = code;
+ const char* msg = ERR_reason_error_string(code);
+ err->message = malloc(strlen(msg));
+ strcpy(err->message, msg);
+ return err;
+}
+
+static unsigned int tls_server_psk_server_callback(SSL *ssl,
+ const char *identity,
+ unsigned char *psk,
+ unsigned int max_psk_len) {
+ if (sizeof(goodix_511_psk_0) > max_psk_len) {
+ fp_dbg("Provided PSK R is too long for OpenSSL");
+ return 0;
+ }
+ fp_dbg("PSK WANTED %d", max_psk_len);
+ // I don't know why we must use OPENSSL_hexstr2buf but just copying zeros
+ // doesn't work
+ const char* buff = "000000000000000000000000000000000000000000000000000000000"
+ "0000000";
+ long len = 0;
+ unsigned char* key = OPENSSL_hexstr2buf(buff, &len);
+ memcpy(psk, key, len);
+ OPENSSL_free(key);
+
+ return len;
+}
+
+static SSL_CTX* tls_server_create_ctx(void)
+{
+ const SSL_METHOD* method;
+
+ method = TLS_server_method();
+
+ SSL_CTX* ctx = SSL_CTX_new(method);
+ if (!ctx) {
+ return NULL;
+ }
+
+ return ctx;
+}
+
+static void tls_server_config_ctx(SSL_CTX* ctx)
+{
+ SSL_CTX_set_ecdh_auto(ctx, 1);
+ SSL_CTX_set_dh_auto(ctx, 1);
+ SSL_CTX_set_cipher_list(ctx, "ALL");
+ SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
+ SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
+ SSL_CTX_set_psk_server_callback(ctx, tls_server_psk_server_callback);
+}
+
+int goodix_tls_client_send(GoodixTlsServer* self, guint8* data, guint16 length)
+{
+ return write(self->client_fd, data, length * sizeof(guint8));
+}
+int goodix_tls_client_recv(GoodixTlsServer* self, guint8* data, guint16 length) {
+ return read(self->client_fd, data, length * sizeof(guint8));
+}
+
+int goodix_tls_server_receive(GoodixTlsServer* self, guint8* data,
+ guint32 length, GError** error)
+{
+ int retr = SSL_read(self->ssl_layer, data, length * sizeof(guint8));
+ if (retr <= 0) {
+ *error = err_from_ssl();
+ }
+ return retr;
+}
+
+static void tls_config_ssl(SSL* ssl)
+{
+ SSL_set_min_proto_version(ssl, TLS1_2_VERSION);
+ SSL_set_max_proto_version(ssl, TLS1_2_VERSION);
+ SSL_set_psk_server_callback(ssl, tls_server_psk_server_callback);
+ SSL_set_cipher_list(ssl, "ALL");
+}
+
+static void* goodix_tls_init_serve(void* me)
+{
+ GoodixTlsServer* self = me;
+
+ fp_dbg("TLS server waiting to accept...");
+ int retr = SSL_accept(self->ssl_layer);
+ fp_dbg("TLS server accept done");
+ if (retr <= 0) {
+ self->connection_callback(self, err_from_ssl(), self->user_data);
+ }
+ else {
+ self->connection_callback(self, NULL, self->user_data);
+ }
+ return NULL;
+}
+
+gboolean goodix_tls_server_deinit(GoodixTlsServer* self, GError** error)
+{
+ SSL_shutdown(self->ssl_layer);
+ SSL_free(self->ssl_layer);
+
+ close(self->client_fd);
+ close(self->sock_fd);
+
+ SSL_CTX_free(self->ssl_ctx);
+
+ return TRUE;
+}
+
+gboolean goodix_tls_server_init(GoodixTlsServer* self, GError** error)
+{
+ g_assert(self->connection_callback);
+ SSL_load_error_strings();
+ OpenSSL_add_ssl_algorithms();
+ SSL_library_init();
+ self->ssl_ctx = tls_server_create_ctx();
+ tls_server_config_ctx(self->ssl_ctx);
+
+ int socks[2] = {0, 0};
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) != 0) {
+ g_set_error(error, G_FILE_ERROR, errno,
+ "failed to create socket pair: %s", strerror(errno));
+ return FALSE;
+ }
+ self->sock_fd = socks[0];
+ self->client_fd = socks[1];
+
+ if (self->ssl_ctx == NULL) {
+ fp_dbg("Unable to create TLS server context\n");
+ *error = fpi_device_error_new_msg(FP_DEVICE_ERROR_GENERAL, "Unable to "
+ "create TLS "
+ "server "
+ "context");
+ return FALSE;
+ }
+ self->ssl_layer = SSL_new(self->ssl_ctx);
+ tls_config_ssl(self->ssl_layer);
+ SSL_set_fd(self->ssl_layer, self->sock_fd);
+
+ pthread_create(&self->serve_thread, 0, goodix_tls_init_serve, self);
+
+ return TRUE;
+}
diff --git a/libfprint/drivers/goodixtls/goodixtls.h b/libfprint/drivers/goodixtls/goodixtls.h
new file mode 100644
index 00000000..66c61f45
--- /dev/null
+++ b/libfprint/drivers/goodixtls/goodixtls.h
@@ -0,0 +1,91 @@
+// Goodix Tls driver for libfprint
+
+// Copyright (C) 2021 Alexander Meiler <alex.meiler@protonmail.com>
+// Copyright (C) 2021 Matthieu CHARETTE <matthieu.charette@gmail.com>
+
+// 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 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
+// Lesser General Public License for more details.
+
+// 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
+
+#pragma once
+
+#include <glib.h>
+
+#include <stdatomic.h>
+
+#define GOODIX_TLS_SERVER_PORT 4433
+
+// static const guint8 goodix_511_psk_0[64] = {0};
+static const guint8 goodix_511_psk_0[] = {
+ 0xba, 0x1a, 0x86, 0x03, 0x7c, 0x1d, 0x3c, 0x71, 0xc3, 0xaf, 0x34,
+ 0x49, 0x55, 0xbd, 0x69, 0xa9, 0xa9, 0x86, 0x1d, 0x9e, 0x91, 0x1f,
+ 0xa2, 0x49, 0x85, 0xb6, 0x77, 0xe8, 0xdb, 0xd7, 0x2d, 0x43};
+
+struct _GoodixTlsServer;
+
+typedef void (*GoodixTlsServerSendCallback)(struct _GoodixTlsServer *self,
+ guint8 *data, guint16 length);
+
+typedef void (*GoodixTlsServerConnectionCallback)(struct _GoodixTlsServer *self,
+ GError *error,
+ gpointer user_data);
+
+typedef void (*GoodixTlsServerDecodedCallback)(struct _GoodixTlsServer *self,
+ guint8 *data, gsize length,
+ GError *error);
+
+typedef struct _GoodixTlsServer
+{
+ // This callback should be called when a TLS packet must be send to the
+ // device
+ // GoodixTlsServerSendCallback send_callback;
+
+ // This callback should be called when the connection is established. The
+ // error should be NULL. It can also be called when the connection fail. In
+ // this case, the error should not be NULL.
+ GoodixTlsServerConnectionCallback connection_callback;
+
+ // This callback should be called when a TLS packet is decoded. The error
+ // should be NULL.
+ // It can also be called when the server fail to decode a packet. In this
+ // case, the error should not be NULL.
+ // GoodixTlsServerDecodedCallback decoded_callback;
+
+ // Put what you need here.
+ gpointer user_data; // Passed to all callbacks
+ SSL_CTX *ssl_ctx;
+ int sock_fd;
+ SSL *ssl_layer;
+ // SSL* cli_ssl_layer;
+ int client_fd;
+ pthread_t serve_thread;
+} GoodixTlsServer;
+
+// This is called only once to init the TLS server.
+// Return TRUE on success, FALSE otherwise and error should be set.
+gboolean goodix_tls_server_init(GoodixTlsServer *self, GError **error);
+
+gboolean goodix_tls_init_cli(GoodixTlsServer *self, GError **err);
+
+// This can be called multiple times. It is called when the device send a TLS
+// packet.
+int goodix_tls_server_receive(GoodixTlsServer *self, guint8 *data,
+ guint32 length, GError **error);
+
+int goodix_tls_client_send(GoodixTlsServer *self, guint8 *data, guint16 length);
+
+int goodix_tls_client_recv(GoodixTlsServer *self, guint8 *data, guint16 length);
+
+// This is called only once to deinit the TLS server.
+// Return TRUE on success, FALSE otherwise and error should be set.
+gboolean goodix_tls_server_deinit(GoodixTlsServer *self, GError **error);
diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c
index 16b6d18f..b768d610 100644
--- a/libfprint/fprint-list-udev-hwdb.c
+++ b/libfprint/fprint-list-udev-hwdb.c
@@ -94,7 +94,6 @@ static const FpIdEntry whitelist_id_table[] = {
{ .vid = 0x1c7a, .pid = 0x0575 },
{ .vid = 0x1c7a, .pid = 0x0576 },
{ .vid = 0x27c6, .pid = 0x5042 },
- { .vid = 0x27c6, .pid = 0x5110 },
{ .vid = 0x27c6, .pid = 0x5117 },
{ .vid = 0x27c6, .pid = 0x5120 },
{ .vid = 0x27c6, .pid = 0x5125 },
@@ -102,13 +101,11 @@ static const FpIdEntry whitelist_id_table[] = {
{ .vid = 0x27c6, .pid = 0x521d },
{ .vid = 0x27c6, .pid = 0x5301 },
{ .vid = 0x27c6, .pid = 0x530c },
- { .vid = 0x27c6, .pid = 0x532d },
{ .vid = 0x27c6, .pid = 0x5335 },
{ .vid = 0x27c6, .pid = 0x533c },
{ .vid = 0x27c6, .pid = 0x5381 },
{ .vid = 0x27c6, .pid = 0x5385 },
{ .vid = 0x27c6, .pid = 0x538c },
- { .vid = 0x27c6, .pid = 0x538d },
{ .vid = 0x27c6, .pid = 0x5395 },
{ .vid = 0x27c6, .pid = 0x5503 },
{ .vid = 0x27c6, .pid = 0x550a },
diff --git a/libfprint/meson.build b/libfprint/meson.build
index 25ed10f3..6ee02900 100644
--- a/libfprint/meson.build
+++ b/libfprint/meson.build
@@ -139,6 +139,10 @@ driver_sources = {
[ 'drivers/synaptics/synaptics.c', 'drivers/synaptics/bmkt_message.c' ],
'goodixmoc' :
[ 'drivers/goodixmoc/goodix.c', 'drivers/goodixmoc/goodix_proto.c' ],
+ 'goodixtls511' :
+ [ 'drivers/goodixtls/goodix511.c' ],
+ 'goodixtls53xd' :
+ [ 'drivers/goodixtls/goodix53xd.c' ],
'fpcmoc' :
[ 'drivers/fpcmoc/fpc.c' ],
}
@@ -148,6 +152,8 @@ helper_sources = {
[ 'drivers/aesx660.c' ],
'aes3k' :
[ 'drivers/aes3k.c' ],
+ 'goodixtls' :
+ [ 'drivers/goodixtls/goodix_proto.c', 'drivers/goodixtls/goodix.c', 'drivers/goodixtls/goodixtls.c' ],
'nss' :
[ ],
'udev' :
diff --git a/meson.build b/meson.build
index 823728c7..ac31a3d8 100644
--- a/meson.build
+++ b/meson.build
@@ -124,6 +124,8 @@ default_drivers = [
'upeksonly',
'upekts',
'goodixmoc',
+ 'goodixtls511',
+ 'goodixtls53xd',
'nb1010',
# SPI
@@ -157,6 +159,8 @@ driver_helper_mapping = {
'aes4000' : [ 'aeslib', 'aes3k' ],
'uru4000' : [ 'nss' ],
'elanspi' : [ 'udev' ],
+ 'goodixtls511' : [ 'goodixtls' ],
+ 'goodixtls53xd' : [ 'goodixtls' ],
'virtual_image' : [ 'virtual' ],
'virtual_device' : [ 'virtual' ],
'virtual_device_storage' : [ 'virtual' ],
@@ -212,6 +216,18 @@ foreach i : driver_helpers
libfprint_conf.set10('HAVE_PIXMAN', true)
optional_deps += imaging_dep
+ elif i == 'goodixtls'
+ openssl_dep = dependency('openssl', required: false)
+ if not openssl_dep.found()
+ error('openssl is required for @0@ and possibly others'.format(driver))
+ endif
+ optional_deps += openssl_dep
+
+ threads_dep = dependency('threads', required: false)
+ if not threads_dep.found()
+ error('threads is required for @0@ and possibly others'.format(driver))
+ endif
+ optional_deps += threads_dep
elif i == 'nss'
nss_dep = dependency('nss', required: false)
if not nss_dep.found()