4505 lines
141 KiB
Diff
4505 lines
141 KiB
Diff
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()
|