diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..4997f6f1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: mpi3d +custom: "btc.com/btc/address/1MPi3D1HxS71k83XnqK3yB8CNmyHQeerZZ" diff --git a/.gitignore b/.gitignore index 07d73995..0ab23946 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.o *.swp _build +*.pgm diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..6994aa63 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "version": 4, + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu17", + "cppStandard": "gnu++14", + "intelliSenseMode": "linux-gcc-x64", + "compileCommands": "${workspaceFolder}/_build/compile_commands.json" + } + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..68646c13 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Image Capture", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/_build/examples/img-capture", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "${defaultBuildTask}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0e6ce268 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "files.associations": { + "*.c": "c", + "*.h": "c", + "limits": "c", + "array": "c", + "string": "c", + "string_view": "c", + "ranges": "c", + "new": "c", + "streambuf": "c", + "functional": "c", + "typeinfo": "c" + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..6f515c8c --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "Configure the project", + "command": "/usr/bin/meson", + "args": [ + "${workspaceFolder}/_build" + ], + "problemMatcher": [] + }, + { + "type": "shell", + "label": "Build the project", + "command": "/usr/bin/meson", + "args": [ + "compile" + ], + "options": { + "cwd": "${workspaceFolder}/_build" + }, + "problemMatcher": { + "base": "$gcc", + "fileLocation": [ + "relative", + "${workspaceFolder}/_build" + ] + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 7f59b4e8..68f18953 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +This is an experimental libfprint driver implementation for Goodix drivers. + +Currently in the works: +- 27c6x5110 (80x64 resolution) + # libfprint libfprint is part of the fprint project: diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 6e3fd396..9862d188 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -146,6 +146,7 @@ usb:v04F3p0C58* usb:v04F3p0C7D* usb:v04F3p0C7E* usb:v04F3p0C82* +usb:v04F3p0C88* ID_AUTOSUSPEND=1 ID_PERSIST=0 @@ -172,6 +173,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 @@ -317,19 +329,16 @@ usb:v1C7Ap0300* usb:v1C7Ap0575* usb:v1C7Ap0576* usb:v27C6p5042* -usb:v27C6p5110* usb:v27C6p5117* usb:v27C6p5125* usb:v27C6p5201* usb:v27C6p521D* usb:v27C6p5301* usb:v27C6p530C* -usb:v27C6p532D* usb:v27C6p533C* usb:v27C6p5381* usb:v27C6p5385* usb:v27C6p538C* -usb:v27C6p538D* usb:v27C6p5395* usb:v27C6p5503* usb:v27C6p5584* diff --git a/libfprint/drivers/elanmoc/elanmoc.c b/libfprint/drivers/elanmoc/elanmoc.c index 3185ee7a..ad23e93e 100644 --- a/libfprint/drivers/elanmoc/elanmoc.c +++ b/libfprint/drivers/elanmoc/elanmoc.c @@ -28,6 +28,7 @@ static const FpIdEntry id_table[] = { { .vid = 0x04f3, .pid = 0x0c7d, }, { .vid = 0x04f3, .pid = 0x0c7e, }, { .vid = 0x04f3, .pid = 0x0c82, }, + { .vid = 0x04f3, .pid = 0x0c88, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; diff --git a/libfprint/drivers/goodixmoc/goodix.c b/libfprint/drivers/goodixmoc/goodix.c index 4eeb7215..cfc69c94 100644 --- a/libfprint/drivers/goodixmoc/goodix.c +++ b/libfprint/drivers/goodixmoc/goodix.c @@ -661,7 +661,7 @@ fp_enroll_capture_cb (FpiDeviceGoodixMoc *self, /* */ if (resp->result >= GX_FAILED) { - fp_warn ("Capture sample failed, result: 0x%x", resp->result); + fp_info ("Capture sample failed, result: 0x%x", resp->result); fpi_device_enroll_progress (FP_DEVICE (self), self->enroll_stage, NULL, @@ -675,7 +675,7 @@ fp_enroll_capture_cb (FpiDeviceGoodixMoc *self, if ((resp->capture_data_resp.img_quality < self->sensorcfg->config[4]) || (resp->capture_data_resp.img_coverage < self->sensorcfg->config[5])) { - fp_warn ("Capture sample poor quality(%d): %d or coverage(%d): %d", + fp_info ("Capture sample poor quality(%d): %d or coverage(%d): %d", self->sensorcfg->config[4], resp->capture_data_resp.img_quality, self->sensorcfg->config[5], @@ -1041,6 +1041,47 @@ fp_init_config_cb (FpiDeviceGoodixMoc *self, fpi_ssm_next_state (self->task_ssm); } +static void +fp_init_cb_reset_or_complete (FpiDeviceGoodixMoc *self, + gxfp_cmd_response_t *resp, + GError *error) +{ + if (error) + { + fp_warn ("Template storage appears to have been corrupted! Error was: %s", error->message); + fp_warn ("A known reason for this to happen is a firmware bug triggered by another storage area being initialized."); + fpi_ssm_jump_to_state (self->task_ssm, FP_INIT_RESET_DEVICE); + } + else + { + fpi_ssm_mark_completed (self->task_ssm); + } +} + +static void +fp_init_reset_device_cb (FpiDeviceGoodixMoc *self, + gxfp_cmd_response_t *resp, + GError *error) +{ + if (error) + { + fp_warn ("Reset failed: %s", error->message); + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + if ((resp->result >= GX_FAILED) && (resp->result != GX_ERROR_FINGER_ID_NOEXIST)) + { + fp_warn ("Reset failed, device reported: 0x%x", resp->result); + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Failed clear storage, result: 0x%x", + resp->result)); + return; + } + + fp_warn ("Reset completed"); + fpi_ssm_mark_completed (self->task_ssm); +} static void fp_init_sm_run_state (FpiSsm *ssm, FpDevice *device) @@ -1065,6 +1106,30 @@ fp_init_sm_run_state (FpiSsm *ssm, FpDevice *device) sizeof (gxfp_sensor_cfg_t), fp_init_config_cb); break; + + case FP_INIT_TEMPLATE_LIST: + /* List prints to check whether the template DB was corrupted. + * As of 2022-06-13 there is a known firmware issue that can cause the + * stored templates for Linux to be corrupted when the Windows storage + * area is initialized. + * In that case, we'll get a protocol failure trying to retrieve the + * list of prints. + */ + goodix_sensor_cmd (self, MOC_CMD0_GETFINGERLIST, MOC_CMD1_DEFAULT, + FALSE, + (const guint8 *) &dummy, + 1, + fp_init_cb_reset_or_complete); + break; + + case FP_INIT_RESET_DEVICE: + fp_warn ("Resetting device storage, you will need to enroll all prints again!"); + goodix_sensor_cmd (self, MOC_CMD0_DELETETEMPLATE, MOC_CMD1_DELETE_ALL, + FALSE, + NULL, + 0, + fp_init_reset_device_cb); + break; } diff --git a/libfprint/drivers/goodixmoc/goodix.h b/libfprint/drivers/goodixmoc/goodix.h index 23e142ac..56b2d171 100644 --- a/libfprint/drivers/goodixmoc/goodix.h +++ b/libfprint/drivers/goodixmoc/goodix.h @@ -35,6 +35,8 @@ typedef enum { typedef enum { FP_INIT_VERSION = 0, FP_INIT_CONFIG, + FP_INIT_TEMPLATE_LIST, + FP_INIT_RESET_DEVICE, FP_INIT_NUM_STATES, } FpInitState; diff --git a/libfprint/drivers/goodixmoc/goodix_proto.c b/libfprint/drivers/goodixmoc/goodix_proto.c index b615dbaa..72511a88 100644 --- a/libfprint/drivers/goodixmoc/goodix_proto.c +++ b/libfprint/drivers/goodixmoc/goodix_proto.c @@ -393,10 +393,8 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint16_t buffer_len, pgxfp_c fingerid_length, &presp->finger_list_resp.finger_list[num]) != 0) { - g_error ("parse fingerlist error"); - presp->finger_list_resp.finger_num = 0; - presp->result = GX_FAILED; - break; + g_warning ("Failed to parse finger list"); + return -1; } offset += fingerid_length; } 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 +// Copyright (C) 2021 Matthieu CHARETTE + +// 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 +#include +#include +#include +#include +#include +#include + +#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 +// Copyright (C) 2021 Matthieu CHARETTE + +// 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 +// Copyright (C) 2021 Matthieu CHARETTE + +// 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 +#include +#define FP_COMPONENT "goodixtls511" + +#include +#include + +#include "drivers_api.h" +#include "goodix.h" +#include "goodix_proto.h" +#include "goodix511.h" + +#include + +#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 +// Copyright (C) 2021 Matthieu CHARETTE + +// 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 +// Copyright (C) 2021 Matthieu CHARETTE +// Copyright (C) 2021 Michael Teuscher + +// 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 +#include +#define FP_COMPONENT "goodixtls53xd" + +#include +#include + +#include "drivers_api.h" +#include "goodix.h" +#include "goodix_proto.h" +#include "goodix53xd.h" + +#include + +#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 +// Copyright (C) 2021 Matthieu CHARETTE +// Copyright (C) 2021 Michael Teuscher + +// 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 +// Copyright (C) 2021 Matthieu CHARETTE + +// 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 +#include +#include + +#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 +// Copyright (C) 2021 Matthieu CHARETTE + +// 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 +// Copyright (C) 2021 Matthieu CHARETTE + +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +// Copyright (C) 2021 Matthieu CHARETTE + +// 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 + +#include + +#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/fpi-print.h b/libfprint/fpi-print.h index fb388097..f3e72b40 100644 --- a/libfprint/fpi-print.h +++ b/libfprint/fpi-print.h @@ -42,9 +42,9 @@ gboolean fpi_print_add_from_image (FpPrint *print, FpImage *image, GError **error); -FpiMatchResult fpi_print_bz3_match (FpPrint * template, - FpPrint * print, - gint bz3_threshold, +FpiMatchResult fpi_print_bz3_match (FpPrint *temp, + FpPrint *print, + gint bz3_threshold, GError **error); /* Helpers to encode metadata into user ID strings. */ 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 @@ -88,19 +88,16 @@ 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 = 0x5125 }, { .vid = 0x27c6, .pid = 0x5201 }, { .vid = 0x27c6, .pid = 0x521d }, { .vid = 0x27c6, .pid = 0x5301 }, { .vid = 0x27c6, .pid = 0x530c }, - { .vid = 0x27c6, .pid = 0x532d }, { .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 = 0x5584 }, 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' ] } helper_sources = { @@ -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() diff --git a/scripts/uncrustify.cfg b/scripts/uncrustify.cfg index 1dbd3bad..34b9a358 100644 --- a/scripts/uncrustify.cfg +++ b/scripts/uncrustify.cfg @@ -120,7 +120,7 @@ nl_multi_line_cond true # Not clear what to do about that... mod_full_brace_for Remove mod_full_brace_if Remove -mod_full_brace_if_chain True +mod_full_brace_if_chain 1 mod_full_brace_while Remove mod_full_brace_do Remove mod_full_brace_nl 3 diff --git a/tests/goodixmoc/custom.pcapng b/tests/goodixmoc/custom.pcapng index eb58d865..b5e2d89c 100644 Binary files a/tests/goodixmoc/custom.pcapng and b/tests/goodixmoc/custom.pcapng differ diff --git a/tests/goodixmoc/custom.py b/tests/goodixmoc/custom.py index 1fb513a1..38fdd26c 100755 --- a/tests/goodixmoc/custom.py +++ b/tests/goodixmoc/custom.py @@ -25,6 +25,9 @@ d.open_sync() +# 1. verify clear storage command, 2. make sure later asserts are good +d.clear_storage_sync() + template = FPrint.Print.new(d) def enroll_progress(*args): diff --git a/tests/goodixmoc/device b/tests/goodixmoc/device index 9fb39e59..1e209a1d 100644 --- a/tests/goodixmoc/device +++ b/tests/goodixmoc/device @@ -1,14 +1,14 @@ P: /devices/pci0000:00/0000:00:14.0/usb1/1-3 -N: bus/usb/001/053=12010002EF000040C627966400010102030109022000010103A0320904000002FF0000040705830240000007050102400000 -E: DEVNAME=/dev/bus/usb/001/053 +N: bus/usb/001/023=12010002EF000040C627966400010102030109022000010103A0320904000002FF0000040705830240000007050102400000 +E: DEVNAME=/dev/bus/usb/001/023 E: DEVTYPE=usb_device E: DRIVER=usb E: PRODUCT=27c6/6496/100 E: TYPE=239/0/0 E: BUSNUM=001 -E: DEVNUM=053 +E: DEVNUM=023 E: MAJOR=189 -E: MINOR=52 +E: MINOR=22 E: SUBSYSTEM=usb E: ID_VENDOR=Goodix_Technology_Co.__Ltd. E: ID_VENDOR_ENC=Goodix\x20Technology\x20Co.\x2c\x20Ltd. @@ -23,6 +23,7 @@ E: ID_BUS=usb E: ID_USB_INTERFACES=:ff0000: E: ID_VENDOR_FROM_DATABASE=Shenzhen Goodix Technology Co.,Ltd. E: ID_AUTOSUSPEND=1 +E: ID_PERSIST=0 E: ID_PATH=pci-0000:00:14.0-usb-0:3 E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_3 A: authorized=1\n @@ -40,8 +41,8 @@ A: bmAttributes=a0\n A: busnum=1\n A: configuration=XXXX_MOC_B0\n H: descriptors=12010002EF000040C627966400010102030109022000010103A0320904000002FF0000040705830240000007050102400000 -A: dev=189:52\n -A: devnum=53\n +A: dev=189:22\n +A: devnum=23\n A: devpath=3\n L: driver=../../../../../bus/usb/drivers/usb L: firmware_node=../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:1c/device:1d/device:20 @@ -51,16 +52,16 @@ A: ltm_capable=no\n A: manufacturer=Goodix Technology Co., Ltd.\n A: maxchild=0\n L: port=../1-0:1.0/usb1-port3 -A: power/active_duration=29262\n +A: power/active_duration=22667\n A: power/autosuspend=2\n A: power/autosuspend_delay_ms=2000\n -A: power/connected_duration=57399\n +A: power/connected_duration=917616\n A: power/control=auto\n A: power/level=auto\n A: power/persist=1\n -A: power/runtime_active_time=29308\n +A: power/runtime_active_time=22809\n A: power/runtime_status=active\n -A: power/runtime_suspended_time=27850\n +A: power/runtime_suspended_time=894564\n A: power/wakeup=disabled\n A: power/wakeup_abort_count=\n A: power/wakeup_active=\n @@ -77,29 +78,29 @@ A: rx_lanes=1\n A: serial=XXXX_MOC_B0\n A: speed=12\n A: tx_lanes=1\n -A: urbnum=394\n +A: urbnum=298\n A: version= 2.00\n P: /devices/pci0000:00/0000:00:14.0/usb1 -N: bus/usb/001/001=12010002090001406B1D020013050302010109021900010100E0000904000001090000000705810304000C +N: bus/usb/001/001=12010002090001406B1D020017050302010109021900010100E0000904000001090000000705810304000C E: DEVNAME=/dev/bus/usb/001/001 E: DEVTYPE=usb_device E: DRIVER=usb -E: PRODUCT=1d6b/2/513 +E: PRODUCT=1d6b/2/517 E: TYPE=9/0/1 E: BUSNUM=001 E: DEVNUM=001 E: MAJOR=189 E: MINOR=0 E: SUBSYSTEM=usb -E: ID_VENDOR=Linux_5.13.15-200.fc34.x86_64_xhci-hcd -E: ID_VENDOR_ENC=Linux\x205.13.15-200.fc34.x86_64\x20xhci-hcd +E: ID_VENDOR=Linux_5.17.12-300.fc36.x86_64_xhci-hcd +E: ID_VENDOR_ENC=Linux\x205.17.12-300.fc36.x86_64\x20xhci-hcd E: ID_VENDOR_ID=1d6b E: ID_MODEL=xHCI_Host_Controller E: ID_MODEL_ENC=xHCI\x20Host\x20Controller E: ID_MODEL_ID=0002 -E: ID_REVISION=0513 -E: ID_SERIAL=Linux_5.13.15-200.fc34.x86_64_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 +E: ID_REVISION=0517 +E: ID_SERIAL=Linux_5.17.12-300.fc36.x86_64_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 E: ID_SERIAL_SHORT=0000:00:14.0 E: ID_BUS=usb E: ID_USB_INTERFACES=:090000: @@ -122,11 +123,11 @@ A: bMaxPacketSize0=64\n A: bMaxPower=0mA\n A: bNumConfigurations=1\n A: bNumInterfaces= 1\n -A: bcdDevice=0513\n +A: bcdDevice=0517\n A: bmAttributes=e0\n A: busnum=1\n A: configuration=\n -H: descriptors=12010002090001406B1D020013050302010109021900010100E0000904000001090000000705810304000C +H: descriptors=12010002090001406B1D020017050302010109021900010100E0000904000001090000000705810304000C A: dev=189:0\n A: devnum=1\n A: devpath=0\n @@ -136,15 +137,15 @@ A: idProduct=0002\n A: idVendor=1d6b\n A: interface_authorized_default=1\n A: ltm_capable=no\n -A: manufacturer=Linux 5.13.15-200.fc34.x86_64 xhci-hcd\n +A: manufacturer=Linux 5.17.12-300.fc36.x86_64 xhci-hcd\n A: maxchild=12\n -A: power/active_duration=219578717\n +A: power/active_duration=164289796\n A: power/autosuspend=0\n A: power/autosuspend_delay_ms=0\n -A: power/connected_duration=219649620\n +A: power/connected_duration=164360220\n A: power/control=auto\n A: power/level=auto\n -A: power/runtime_active_time=219589127\n +A: power/runtime_active_time=164331876\n A: power/runtime_status=active\n A: power/runtime_suspended_time=0\n A: power/wakeup=disabled\n @@ -163,14 +164,14 @@ A: rx_lanes=1\n A: serial=0000:00:14.0\n A: speed=480\n A: tx_lanes=1\n -A: urbnum=4325\n +A: urbnum=2097\n A: version= 2.00\n P: /devices/pci0000:00/0000:00:14.0 E: DRIVER=xhci_hcd E: PCI_CLASS=C0330 E: PCI_ID=8086:9DED -E: PCI_SUBSYS_ID=17AA:2292\n +E: PCI_SUBSYS_ID=17AA:2292 E: PCI_SLOT_NAME=0000:00:14.0 E: MODALIAS=pci:v00008086d00009DEDsv000017AAsd00002292bc0Csc03i30 E: SUBSYSTEM=pci @@ -183,7 +184,7 @@ E: ID_MODEL_FROM_DATABASE=Cannon Point-LP USB 3.1 xHCI Controller A: ari_enabled=0\n A: broken_parity_status=0\n A: class=0x0c0330\n -H: config=8680ED9D060490021130030C00008000040022EA000000000000000000000000000000000000000000000000AA179222000000007000000000000000FF010000FD0134808FC6FF8300000000000000007F6DDC0F000000004C084B0100000000316000000000000000000000000000000180C2C1080000000000000000000000059087001803E0FE0000000000000000090014F01000400100000000C10A080000080E00001800008F40020000010000000000000000000008000000040000000000000000000000000000000000000000000000000000000800000004000000000000000000000000000000000000000000000000000000B50F320112000000 +H: config=8680ED9D060490021130030C00008000040022EA000000000000000000000000000000000000000000000000AA179222000000007000000000000000FF010000FD0134808FC6FF8300000000000000007F6DDC0F00000000F507312600000000316000000000000000000000000000000180C2C1080000000000000000000000059087001803E0FE0000000000000000090014F01000400100000000C10A080000080E00001800008F40020000010000000000000000000008000000040000000000000000000000000000000000000000000000000000000800000004000000000000000000000000000000000000000000000000000000B50F320112000000 A: consistent_dma_mask_bits=64\n A: d3cold_allowed=1\n A: dbc=disabled\n @@ -201,8 +202,8 @@ A: msi_bus=1\n A: msi_irqs/128=msi\n A: numa_node=-1\n A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 11 12 2112 12\nxHCI ring segments 46 50 4096 50\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 6 32 128 1\nbuffer-32 0 0 32 0\n -A: power/control=on\n -A: power/runtime_active_time=219589302\n +A: power/control=auto\n +A: power/runtime_active_time=164332777\n A: power/runtime_status=active\n A: power/runtime_suspended_time=0\n A: power/wakeup=enabled\n