Added support for 1c7a:0576

This commit is contained in:
Sprayxe 2026-03-11 22:13:53 +01:00
parent 2c7842c905
commit 034e7e9bee
6 changed files with 808 additions and 2 deletions

View file

@ -350,6 +350,11 @@ usb:v138Ap0091*
ID_AUTOSUSPEND=1
ID_PERSIST=0
# Supported by libfprint driver egis0576
usb:v1C7Ap0576*
ID_AUTOSUSPEND=1
ID_PERSIST=0
# Known unsupported devices
usb:v0A5Cp5802*
usb:v047Dp00F2*
@ -444,7 +449,6 @@ usb:v1491p0088*
usb:v16D1p1027*
usb:v1C7Ap0300*
usb:v1C7Ap0575*
usb:v1C7Ap0576*
usb:v1C7Ap0577*
usb:v1C7Ap057E*
usb:v2541p0236*

View file

@ -0,0 +1,595 @@
/*
* Egis Technology Inc. (aka. LighTuning) 0576 driver for libfprint
* Copyright (C) 2026 Marcel (Sprayxe) <sprayxe.marcel@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "egis0576"
#include "egis0576.h"
#include "drivers_api.h"
/* Sequence types */
typedef enum
{
SEQ_INIT,
SEQ_REPEAT,
SEQ_POLL,
SEQ_IMAGE
} seq_types;
/* SSM States */
enum sm_states
{
DEV_OPEN,
DEV_START,
DEV_REQ,
DEV_RESP,
DEV_FULFILLED,
NUM_STATES
};
/* Struct */
struct _FpDeviceEgis0576
{
FpImageDevice parent;
gboolean running;
gboolean stop;
gboolean has_background;
guchar background[EGIS0576_IMG_SIZE];
seq_types seq_type;
int seq_pkt_index;
Egis0576Pkt last_sent_pkt;
};
G_DECLARE_FINAL_TYPE(FpDeviceEgis0576, fpi_device_egis0576, FPI, DEVICE_EGIS0576, FpImageDevice);
G_DEFINE_TYPE(FpDeviceEgis0576, fpi_device_egis0576, FP_TYPE_IMAGE_DEVICE);
/*
* ========================
* Processing
* ========================
*/
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
static void normalize_img(guchar *bg, guchar *img, double *dark_portion)
{
// Find diffs, min and max
int diff[EGIS0576_IMG_SIZE];
int min = 255;
int max = 0;
for (int i = 0; i < EGIS0576_IMG_SIZE; i++)
{
diff[i] = (int)bg[i] - (int)img[i];
if (diff[i] < min)
min = diff[i];
if (diff[i] > max)
max = diff[i];
}
max -= EGIS0576_CONTRAST;
min += EGIS0576_CONTRAST;
int range = max - min;
if (range == 0)
range = 1; // Prevent division by zero
// Adjust contrast / normalize
int count_ridges = 0;
for (int i = 0; i < EGIS0576_IMG_SIZE; i++)
{
int normalized = ((diff[i] - min) * 255) / range;
if (normalized < 0)
normalized = 0;
else if (normalized > 255)
normalized = 255;
img[i] = (unsigned char)normalized;
if (img[i] < 170)
count_ridges++;
}
*dark_portion = (double)count_ridges / EGIS0576_IMG_SIZE;
}
/* Uses bilinear interpolation */
static void upscale_img(guchar *src_img, guchar *dst_img)
{
const int scale = EGIS0576_IMG_UPSCALE;
const int src_w = EGIS0576_IMG_WIDTH;
const int src_h = EGIS0576_IMG_HEIGHT;
const int dst_w = EGIS0576_IMG_WIDTH_UPSCALE;
const int dst_h = EGIS0576_IMG_HEIGHT_UPSCALE;
for (int y = 0; y < dst_h; y++)
{
float gy = ((float)y) / scale;
int y1 = (int)gy;
int y2 = (y1 >= src_h - 1) ? src_h - 1 : y1 + 1;
float ty = gy - y1;
for (int x = 0; x < dst_w; x++)
{
float gx = ((float)x) / scale;
int x1 = (int)gx;
int x2 = (x1 >= src_w - 1) ? src_w - 1 : x1 + 1;
float tx = gx - x1;
float p00 = src_img[y1 * src_w + x1]; // Top left
float p10 = src_img[y1 * src_w + x2]; // Top right
float p01 = src_img[y2 * src_w + x1]; // Bottom left
float p11 = src_img[y2 * src_w + x2]; // Bottom right
// Interp. horizontally across the top and bottom
float top_p = p00 * (1.0f - tx) + p10 * tx;
float bottom_p = p01 * (1.0f - tx) + p11 * tx;
// Interp. vertically between the two horizontal results
float pixel = top_p * (1.0f - ty) + bottom_p * ty;
dst_img[y * dst_w + x] = (guchar)pixel;
}
}
}
/*
* As it is already known, libfprint absolutely sucks at processing small images.
* Therefore the 'trick' is to create a canvas that is big enough to be liked by libfprint,
* fill it with 255 (white background) and put an upscaled version of the sensor image into the
* center of that canvas.
* With this technique I have managed to get scores as high as 32!
*/
static void upscale_and_pad_img(guchar *img, guchar *canvas)
{
const int img_width = EGIS0576_IMG_WIDTH_UPSCALE;
const int img_height = EGIS0576_IMG_HEIGHT_UPSCALE;
// Upscale sensor image
guchar upscaled_img[EGIS0576_IMG_SIZE_UPSCALE];
upscale_img(img, upscaled_img);
// Prepare canvas
memset(canvas, 255, EGIS0576_CANVAS_SIZE);
int offset_x = (EGIS0576_CANVAS_WIDTH - img_width) / 2;
int offset_y = (EGIS0576_CANVAS_HEIGHT - img_height) / 2;
for (int y = 0; y < img_height; y++)
{
for (int x = 0; x < img_width; x++)
{
int dest_y = y + offset_y;
int dest_x = x + offset_x;
canvas[dest_y * EGIS0576_CANVAS_WIDTH + dest_x] = upscaled_img[y * img_width + x];
}
}
}
static void process_finger(FpDevice *dev, FpiUsbTransfer *transfer)
{
FpImageDevice *img_self = FP_IMAGE_DEVICE(dev);
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
guchar *img = transfer->buffer;
gint variance = fpi_std_sq_dev(img, EGIS0576_IMG_SIZE);
if (!self->has_background)
{
/* Background has been gathered, user can put finger on sensor. */
if (variance < EGIS0576_BG_VARIANCE)
{
memcpy(self->background, img, EGIS0576_IMG_SIZE);
self->has_background = TRUE;
fpi_device_report_finger_status(dev, FP_FINGER_STATUS_NEEDED);
self->seq_type = SEQ_REPEAT;
fpi_ssm_next_state_delayed(transfer->ssm, 50);
return;
}
/* User should remove finger so the driver can grab a clear image. */
fpi_image_device_retry_scan(img_self, FP_DEVICE_RETRY_REMOVE_FINGER);
self->seq_type = SEQ_REPEAT;
fpi_ssm_next_state_delayed(transfer->ssm, 500);
return;
}
gboolean finger_present = FALSE;
double dark_portion = -1;
if (variance > EGIS0576_VARIANCE)
{
normalize_img(self->background, img, &dark_portion);
finger_present = dark_portion > EGIS0576_DARK_PORTION;
}
fp_dbg("Finger status (present, variance, dark port) : "
"%d , %d, %.2f",
finger_present, variance, dark_portion);
if (!finger_present)
{
self->seq_type = SEQ_REPEAT;
fpi_image_device_report_finger_status(img_self, FALSE);
fpi_ssm_next_state_delayed(transfer->ssm, 50);
return;
}
FpImage *fp_img = fp_image_new(EGIS0576_CANVAS_WIDTH, EGIS0576_CANVAS_HEIGHT);
/* Sensor returns full image */
upscale_and_pad_img(img, fp_img->data);
fpi_image_device_report_finger_status(img_self, TRUE);
fpi_image_device_image_captured(img_self, fp_img);
fpi_ssm_next_state_delayed(transfer->ssm, 50);
}
static void process_poll_transfer(FpDevice *dev, FpiUsbTransfer *transfer)
{
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
if (transfer->actual_length < 6)
{
GError *error
= fpi_device_error_new_msg(FP_DEVICE_ERROR_DATA_INVALID, "Device reported invalid poll.");
fpi_ssm_mark_failed(transfer->ssm, error);
g_error_free(error);
return;
}
if ((transfer->buffer[6] & 0x01) == 0x01)
{
self->seq_type = SEQ_IMAGE;
fpi_ssm_jump_to_state(transfer->ssm, DEV_REQ);
return;
}
self->seq_pkt_index += 1;
if (self->seq_pkt_index < EGIS0576_POLL_COUNT)
{
fpi_ssm_jump_to_state(transfer->ssm, DEV_REQ);
return;
}
GError *error
= fpi_device_error_new_msg(FP_DEVICE_ERROR_GENERAL, "Device exceeded maximum poll count.");
fpi_ssm_mark_failed(transfer->ssm, error);
g_error_free(error);
}
/* Verifies that received data is processable. */
static void process_image_transfer(FpDevice *dev, FpiUsbTransfer *transfer)
{
guchar *buffer = transfer->buffer;
gssize buffer_len = transfer->actual_length;
if (buffer_len != EGIS0576_IMG_SIZE)
{
GError *error = fpi_device_error_new_msg(
FP_DEVICE_ERROR_DATA_INVALID, "Device image data size does not match expected size.");
fpi_ssm_mark_failed(transfer->ssm, error);
g_error_free(error);
return;
}
uint sum = 0;
/* Roughly check whether the buffer is empty aka invalid. */
for (int i = 0; i < MIN(buffer_len, 255); i++)
{
sum += buffer[i];
}
/* No/invalid data was present. */
if (sum == 0)
{
GError *error
= fpi_device_error_new_msg(FP_DEVICE_ERROR_DATA_INVALID, "Device reported invalid data.");
fpi_ssm_mark_failed(transfer->ssm, error);
g_error_free(error);
return;
}
process_finger(dev, transfer);
}
/*
* ========================
* I / O
* ========================
*/
static void cmd_resp_cb(FpiUsbTransfer *transfer, FpDevice *dev, gpointer user_data, GError *error)
{
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
if (error)
{
fp_dbg("During the %d sequence an error occurred at pkt index %d", self->seq_type,
self->seq_pkt_index);
fpi_ssm_mark_failed(transfer->ssm, error);
return;
}
switch (self->seq_type)
{
/* not processed */
case SEQ_INIT:
case SEQ_REPEAT:
fpi_ssm_jump_to_state(transfer->ssm, DEV_REQ);
break;
case SEQ_POLL:
process_poll_transfer(dev, transfer);
break;
case SEQ_IMAGE:
process_image_transfer(dev, transfer);
break;
}
}
static void recv_cmd_resp(FpiSsm *ssm, FpDevice *dev, Egis0576Pkt last_pkt)
{
FpiUsbTransfer *transfer = fpi_usb_transfer_new(dev);
fpi_usb_transfer_fill_bulk(transfer, EGIS0576_EPIN, last_pkt.res_len);
transfer->ssm = ssm;
fpi_usb_transfer_submit(transfer, EGIS0576_TIMEOUT, NULL, cmd_resp_cb, NULL);
}
static void send_cmd_req(FpiSsm *ssm, FpDevice *dev, Egis0576Pkt pkt)
{
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
FpiUsbTransfer *transfer = fpi_usb_transfer_new(dev);
self->last_sent_pkt = pkt;
fpi_usb_transfer_fill_bulk_full(transfer, EGIS0576_EPOUT, pkt.cmd, pkt.len, NULL);
transfer->ssm = ssm;
transfer->short_is_error = TRUE;
fpi_usb_transfer_submit(transfer, EGIS0576_TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL);
}
static gboolean init_repeat_last_pkt(FpDevice *dev)
{
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
int type = self->seq_type;
int index = self->seq_pkt_index;
return (type == SEQ_INIT && index == EGIS0576_INIT_PACKETS_LENGTH - 1)
|| (type == SEQ_REPEAT && index == EGIS0576_REPEAT_PACKETS_LENGTH - 1);
}
static void recv_cmd(FpiSsm *ssm, FpDevice *dev)
{
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
Egis0576Pkt last_pkt = self->last_sent_pkt;
switch (self->seq_type)
{
case SEQ_INIT:
case SEQ_REPEAT:
if (!init_repeat_last_pkt(dev))
{
recv_cmd_resp(ssm, dev, last_pkt);
self->seq_pkt_index += 1;
}
else
{
self->seq_pkt_index = 0;
self->seq_type = SEQ_POLL;
fpi_ssm_jump_to_state(ssm, DEV_REQ);
}
break;
case SEQ_POLL:
case SEQ_IMAGE:
recv_cmd_resp(ssm, dev, last_pkt);
break;
}
}
static void send_cmd(FpiSsm *ssm, FpDevice *dev)
{
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
switch (self->seq_type)
{
case SEQ_INIT:
send_cmd_req(ssm, dev, EGIS0576_INIT_PACKETS[self->seq_pkt_index]);
break;
case SEQ_REPEAT:
send_cmd_req(ssm, dev, EGIS0576_REPEAT_PACKETS[self->seq_pkt_index]);
break;
case SEQ_POLL:
send_cmd_req(ssm, dev, EGIS0576_POLL_PACKET);
break;
case SEQ_IMAGE:
send_cmd_req(ssm, dev, EGIS0576_IMAGE_PACKET);
break;
}
}
static void ssm_run_state(FpiSsm *ssm, FpDevice *dev)
{
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
switch (fpi_ssm_get_cur_state(ssm))
{
case DEV_OPEN:
self->seq_type = SEQ_INIT;
fpi_ssm_jump_to_state(ssm, DEV_START);
break;
case DEV_START:
if (self->stop)
{
fp_dbg("Deactivating device, marking completed.");
fpi_ssm_mark_completed(ssm);
return;
}
self->seq_pkt_index = 0;
fpi_ssm_jump_to_state(ssm, DEV_REQ);
break;
case DEV_REQ:
send_cmd(ssm, dev);
break;
case DEV_RESP:
recv_cmd(ssm, dev);
break;
case DEV_FULFILLED:
fpi_ssm_jump_to_state(ssm, DEV_START);
break;
default:
g_assert_not_reached();
}
}
/*
* ========================
* SETUP
* ========================
*/
static void sm_cb(FpiSsm *ssm, FpDevice *dev, GError *error)
{
FpImageDevice *img_dev = FP_IMAGE_DEVICE(dev);
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
self->running = FALSE;
if (error && !self->stop)
fpi_image_device_session_error(img_dev, error);
else if (error)
g_error_free(error);
if (self->stop)
fpi_image_device_deactivate_complete(img_dev, NULL);
}
/*
* Device activate
*/
static void dev_activate(FpImageDevice *dev)
{
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
FpiSsm *ssm = fpi_ssm_new(FP_DEVICE(dev), ssm_run_state, NUM_STATES);
self->stop = FALSE;
fpi_ssm_start(ssm, sm_cb);
self->running = TRUE;
fpi_image_device_activate_complete(dev, NULL);
}
/*
* Img open
*/
static void dev_init(FpImageDevice *dev)
{
GError *error = NULL;
g_usb_device_claim_interface(fpi_device_get_usb_device(FP_DEVICE(dev)), EGIS0576_INTF, 0, &error);
fpi_image_device_open_complete(dev, error);
}
/*
* Img close
*/
static void dev_deinit(FpImageDevice *dev)
{
GError *error = NULL;
g_usb_device_release_interface(fpi_device_get_usb_device(FP_DEVICE(dev)), EGIS0576_INTF, 0,
&error);
fpi_image_device_close_complete(dev, error);
}
/*
* Device deactivate
*/
static void dev_deactivate(FpImageDevice *dev)
{
FpDeviceEgis0576 *self = FPI_DEVICE_EGIS0576(dev);
if (self->running)
self->stop = TRUE;
else
fpi_image_device_deactivate_complete(dev, NULL);
}
/*
* Driver ID
*/
static const FpIdEntry id_table[] = {
{
.vid = 0x1c7a,
.pid = 0x0576,
},
{
.vid = 0,
.pid = 0,
},
};
static void fpi_device_egis0576_init(FpDeviceEgis0576 *self)
{
}
static void fpi_device_egis0576_class_init(FpDeviceEgis0576Class *klass)
{
FpDeviceClass *dev_class = FP_DEVICE_CLASS(klass);
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS(klass);
dev_class->id = "egis0576";
dev_class->full_name = "Egis Technology Inc. (aka. LighTuning) 0576";
dev_class->type = FP_DEVICE_TYPE_USB;
dev_class->id_table = id_table;
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
dev_class->nr_enroll_stages = 20;
dev_class->temp_hot_seconds = 0;
img_class->img_open = dev_init;
img_class->img_close = dev_deinit;
img_class->activate = dev_activate;
img_class->deactivate = dev_deactivate;
img_class->img_width = EGIS0576_CANVAS_WIDTH;
img_class->img_height = EGIS0576_CANVAS_HEIGHT;
img_class->bz3_threshold = 10; /* security issue, can score more but not reliably */
}

View file

@ -0,0 +1,205 @@
/*
* Egis Technology Inc. (aka. LighTuning) 0576 driver for libfprint
* Copyright (C) 2026 Marcel (Sprayxe) <sprayxe.marcel@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Device config */
#define EGIS0576_INTF 0
/* Device endpoints */
#define EGIS0576_EPOUT 0x01
#define EGIS0576_EPIN 0x82
/* Device transfers */
#define EGIS0576_TIMEOUT 10000
#define EGIS0576_POLL_COUNT 3000
/* Sensor image */
#define EGIS0576_IMG_WIDTH 70
#define EGIS0576_IMG_HEIGHT 57
#define EGIS0576_IMG_SIZE ((EGIS0576_IMG_WIDTH) * (EGIS0576_IMG_HEIGHT))
/* Upscaled sensor image */
#define EGIS0576_IMG_UPSCALE 2
#define EGIS0576_IMG_WIDTH_UPSCALE ((EGIS0576_IMG_WIDTH) * (EGIS0576_IMG_UPSCALE))
#define EGIS0576_IMG_HEIGHT_UPSCALE ((EGIS0576_IMG_HEIGHT) * (EGIS0576_IMG_UPSCALE))
#define EGIS0576_IMG_SIZE_UPSCALE ((EGIS0576_IMG_WIDTH_UPSCALE) * (EGIS0576_IMG_HEIGHT_UPSCALE))
/* Canvas image */
#define EGIS0576_CANVAS_WIDTH 256
#define EGIS0576_CANVAS_HEIGHT 256
#define EGIS0576_CANVAS_SIZE ((EGIS0576_CANVAS_WIDTH) * (EGIS0576_CANVAS_HEIGHT))
/*
* These values were acquired by testing with finger present and without finger
* present.
*/
#define EGIS0576_CONTRAST 4
#define EGIS0576_VARIANCE (3.2 * 3.2)
#define EGIS0576_DARK_PORTION 0.05
#define EGIS0576_BG_VARIANCE (2.5 * 2.5)
/*
*
* All packets work like this:
* E G I S
* 0x45, 0x47, 0x49, 0x53, [CMD], [REG], [VALUE(s)]
*
* [CMD] = Command
* [REG] = Register
* [VALUE(s)] = Values to write or read
* [CMD] seem to be:
* 0x60: Read [REG]
* - [VALUE] is set to 0x00? Not sure, 0575 pkts sometimes had a value but 0x00
* seems to work
*
* 0x61: Write [VALUE] into [REG]
* - [VALUE] is the single byte to write
*
* 0x62: Read starting from [REG] (burst read)
* - [VALUE] is the amount of bytes to read
*
* 0x63: Write starting from [REG] (burst read)
* - [VALUE] consists of: [AMOUNT OF BYTES TO WRITE], [BYTES TO WRITE]
* - example: "0x63, 0x01, 0x02, 0x0f, 0x03"
* -> 0x63 [CMD]; 0x01 [REG]; 0x02 [two bytes]; 0x0f, 0x03 [bytes]
*
* 0x64: Seems to be the command that fetches image data
* - no separate [REG] or [VALUE]
* - it is just the image size (width * size), in our case 70 * 57 = 3990
* (0x0f96)
* -> So the command is "0x64, 0x0f, 0x96"
*
* Responses always (except the image response) echo back:
* S I G E
* 0x53, 0x49, 0x47, 0x45, ...[CMD], [REG], [VALUE(s)] ?
*/
typedef struct
{
int len;
unsigned char *cmd;
int res_len;
} Egis0576Pkt;
/*
* Huge credit goes to Animeshz's efforts on github
* (https://github.com/Animeshz/EgisTec-EH575)
* and on libfprint
* (https://gitlab.freedesktop.org/libfprint/libfprint/-/merge_requests/317) as
* those laid the base for this effort.
*
* Initial tests proved that the EH0575 and the EH0576 seem to work basically
* the same as by just providing the EH0575 packets I was able to occasionally
* get little data out of mine.
*
* I was unable to get the fingerprint reader to work in a VM (both Win10 and
* Win11) and Wireshark USBcap does not allow to be run on Windows-To-Go so I
* could not use any USB captures from Windows.
*
* Therefore, the rest of the reverse engineering was solely done with static
* analysis in Ghidra of the EgisTouchFP0576.dll UMDF driver provided
* by Lenovo
* (https://pcsupport.lenovo.com/de/en/products/laptops-and-netbooks/flex-series/flex-5-14alc7).
* I am no professional "hacker" by any means so I cannot guarantee that these
* are 100% correct but they work :D
*/
/*
* According to static analysis the driver polls using this packet a max. amount
* of EGIS0576_POLL_COUNT before requesting the image.
* Translated to this driver it checks the following condition in order to
* proceed to requesting the image:
*
* (response_buffer[6] & 0x01) == 0x01
*
* Based on observation this has been always true for already the first request
* so it essentially runs once.
*/
static const Egis0576Pkt EGIS0576_POLL_PACKET
= { .len = 7,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x00, 0x00 },
.res_len = 9 };
/* Fetches the image data. */
static const Egis0576Pkt EGIS0576_IMAGE_PACKET
= { .len = 7,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x64, 0x0f, 0x96 },
.res_len = EGIS0576_IMG_SIZE };
/* Initialization packets. */
#define EGIS0576_INIT_PACKETS_LENGTH 30
static const Egis0576Pkt EGIS0576_INIT_PACKETS[] = {
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x00, 0x00 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x01, 0x00 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x10, 0xfd }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x35, 0x02 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x80, 0x00 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x80, 0x00 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x10, 0xfc }, .res_len = 7 },
{ .len = 9,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x63, 0x01, 0x02, 0x0f, 0x03 },
.res_len = 9 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x0c, 0x22 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x09, 0x83 }, .res_len = 7 },
{ .len = 13,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x63, 0x26, 0x06, 0x06, 0x60, 0x06, 0x05,
0x2f, 0x06 },
.res_len = 13 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x10, 0xf4 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x0c, 0x44 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x50, 0x03 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x50, 0x00 }, .res_len = 7 },
EGIS0576_IMAGE_PACKET,
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x40, 0x00 }, .res_len = 7 },
{ .len = 18,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x63, 0x09, 0x0b, 0x83, 0x24, 0x00, 0x44,
0x0f, 0x08, 0x20, 0x20, 0x00, 0x00, 0x52 },
.res_len = 18 },
{ .len = 13,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x63, 0x26, 0x06, 0x06, 0x60, 0x06, 0x05,
0x2f, 0x06 },
.res_len = 13 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x23, 0x00 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x24, 0x38 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x20, 0x00 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x61, 0x21, 0x45 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x00, 0x00 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x01, 0x00 }, .res_len = 7 },
{ .len = 9,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x63, 0x2c, 0x02, 0x00, 0x57 },
.res_len = 9 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x2d, 0x00 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x62, 0x67, 0x03 }, .res_len = 10 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x0f, 0x00 }, .res_len = 7 },
{ .len = 9,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x63, 0x2c, 0x02, 0x00, 0x13 },
.res_len = 9 }
};
/* Repeat packets. */
#define EGIS0576_REPEAT_PACKETS_LENGTH 5
static const Egis0576Pkt EGIS0576_REPEAT_PACKETS[] = {
{ .len = 9,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x63, 0x2c, 0x02, 0x00, 0x57 },
.res_len = 9 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x2d, 0x00 }, .res_len = 7 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x62, 0x67, 0x03 }, .res_len = 10 },
{ .len = 7, .cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x60, 0x0f, 0x00 }, .res_len = 7 },
{ .len = 9,
.cmd = (unsigned char[]){ 0x45, 0x47, 0x49, 0x53, 0x63, 0x2c, 0x02, 0x00, 0x13 },
.res_len = 9 }
};

View file

@ -122,7 +122,6 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x16d1, .pid = 0x1027 },
{ .vid = 0x1c7a, .pid = 0x0300 },
{ .vid = 0x1c7a, .pid = 0x0575 },
{ .vid = 0x1c7a, .pid = 0x0576 },
{ .vid = 0x1c7a, .pid = 0x0577 },
{ .vid = 0x1c7a, .pid = 0x057e },
{ .vid = 0x2541, .pid = 0x0236 },

View file

@ -125,6 +125,8 @@ driver_sources = {
[ 'drivers/etes603.c' ],
'egis0570' :
[ 'drivers/egis0570.c' ],
'egis0576' :
[ 'drivers/egis0576.c' ],
'egismoc' :
[ 'drivers/egismoc/egismoc.c' ],
'vfs0050' :

View file

@ -130,6 +130,7 @@ default_drivers = [
'vfs0050',
'etes603',
'egis0570',
'egis0576',
'egismoc',
'vcom5s',
'synaptics',