Merge branch 'wip/hadess/elan-update' into 'master'

Update Elan driver

See merge request libfprint/libfprint!10
This commit is contained in:
Bastien Nocera 2018-08-10 12:06:35 +00:00
commit 56c96dde8b
3 changed files with 667 additions and 310 deletions

File diff suppressed because it is too large Load diff

View file

@ -21,17 +21,38 @@
#ifndef __ELAN_H #ifndef __ELAN_H
#define __ELAN_H #define __ELAN_H
#include <string.h>
#include <libusb.h> #include <libusb.h>
/* number of pixels to discard on left and right (along raw image height) #define ELAN_VEND_ID 0x04f3
* because they have different intensity from the rest of the frame */
#define ELAN_FRAME_MARGIN 12 /* a default device type */
#define ELAN_ALL_DEV 0
/* devices with quirks */
#define ELAN_0907 (1 << 0)
#define ELAN_0C03 (1 << 1)
/* devices which don't require frame rotation before assembling */
#define ELAN_NOT_ROTATED ELAN_0C03
/* min FW version that supports calibration */
#define ELAN_MIN_CALIBRATION_FW 0x0138
/* max difference between background image mean and calibration mean
* (the response value of get_calib_mean_cmd)*/
#define ELAN_CALIBRATION_MAX_DELTA 500
/* times to retry reading calibration status during one session
* generally prevents calibration from looping indefinitely */
#define ELAN_CALIBRATION_ATTEMPTS 10
/* min and max frames in a capture */ /* min and max frames in a capture */
#define ELAN_MIN_FRAMES 7 #define ELAN_MIN_FRAMES 7
#define ELAN_MAX_FRAMES 30 #define ELAN_MAX_FRAMES 30
/* crop frames to this height to improve stitching */
#define ELAN_MAX_FRAME_HEIGHT 50
/* number of frames to drop at the end of capture because frames captured /* number of frames to drop at the end of capture because frames captured
* while the finger is being lifted can be bad */ * while the finger is being lifted can be bad */
#define ELAN_SKIP_LAST_FRAMES 1 #define ELAN_SKIP_LAST_FRAMES 1
@ -41,6 +62,9 @@
#define ELAN_EP_CMD_IN (0x3 | LIBUSB_ENDPOINT_IN) #define ELAN_EP_CMD_IN (0x3 | LIBUSB_ENDPOINT_IN)
#define ELAN_EP_IMG_IN (0x2 | LIBUSB_ENDPOINT_IN) #define ELAN_EP_IMG_IN (0x2 | LIBUSB_ENDPOINT_IN)
/* used as response length to tell the driver to skip reading response */
#define ELAN_CMD_SKIP_READ 0
/* usual command timeout and timeout for when we need to check if the finger is /* usual command timeout and timeout for when we need to check if the finger is
* still on the device */ * still on the device */
#define ELAN_CMD_TIMEOUT 10000 #define ELAN_CMD_TIMEOUT 10000
@ -50,127 +74,149 @@ struct elan_cmd {
unsigned char cmd[ELAN_CMD_LEN]; unsigned char cmd[ELAN_CMD_LEN];
int response_len; int response_len;
int response_in; int response_in;
unsigned short devices;
}; };
static const struct elan_cmd get_sensor_dim_cmds[] = { static const struct elan_cmd get_sensor_dim_cmd = {
{ .cmd = {0x00, 0x0c},
.cmd = {0x00, 0x0c}, .response_len = 0x4,
.response_len = 0x4, .response_in = ELAN_EP_CMD_IN,
.response_in = ELAN_EP_CMD_IN, .devices = ELAN_ALL_DEV,
},
}; };
static const size_t get_sensor_dim_cmds_len = static const struct elan_cmd get_fw_ver_cmd = {
G_N_ELEMENTS(get_sensor_dim_cmds); .cmd = {0x40, 0x19},
.response_len = 0x2,
static const struct elan_cmd init_start_cmds[] = { .response_in = ELAN_EP_CMD_IN,
{ .devices = ELAN_ALL_DEV,
.cmd = {0x40, 0x19},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
},
{
.cmd = {0x40, 0x2a},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
},
}; };
static const size_t init_start_cmds_len = G_N_ELEMENTS(init_start_cmds); /* unknown, returns 0x0 0x1 on 0907 */
static const struct elan_cmd activate_cmd_1 = {
.cmd = {0x40, 0x2a},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_0907,
};
static const struct elan_cmd read_cmds[] = { static const struct elan_cmd get_image_cmd = {
/* raw frame sizes are calculated from image dimesions reported by the .cmd = {0x00, 0x09},
/* raw frame sizes are calculated from image dimensions reported by the
* device */ * device */
{ .response_len = -1,
.cmd = {0x00, 0x09}, .response_in = ELAN_EP_IMG_IN,
.response_len = -1, .devices = ELAN_ALL_DEV,
.response_in = ELAN_EP_IMG_IN,
},
}; };
const size_t read_cmds_len = G_N_ELEMENTS(read_cmds); static const struct elan_cmd read_sensor_status_cmd = {
.cmd = {0x40, 0x13},
/* issued after data reads during init and calibration */ .response_len = 0x1,
static const struct elan_cmd init_end_cmds[] = { .response_in = ELAN_EP_CMD_IN,
{ .devices = ELAN_ALL_DEV,
.cmd = {0x40, 0x24},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
},
}; };
static const size_t init_end_cmds_len = G_N_ELEMENTS(init_end_cmds); static const struct elan_cmd get_calib_status_cmd = {
.cmd = {0x40, 0x23},
/* same command 2 times .response_len = 0x1,
* original driver may observe return value to determine how many times it .response_in = ELAN_EP_CMD_IN,
* should be repeated */ .devices = ELAN_ALL_DEV,
static const struct elan_cmd calibrate_start_cmds[] = {
{
.cmd = {0x40, 0x23},
.response_len = 0x1,
.response_in = ELAN_EP_CMD_IN,
},
{
.cmd = {0x40, 0x23},
.response_len = 0x1,
.response_in = ELAN_EP_CMD_IN,
},
}; };
static const size_t calibrate_start_cmds_len = static const struct elan_cmd get_calib_mean_cmd = {
G_N_ELEMENTS(calibrate_start_cmds); .cmd = {0x40, 0x24},
.response_len = 0x2,
/* issued after data reads during init and calibration */ .response_in = ELAN_EP_CMD_IN,
static const struct elan_cmd calibrate_end_cmds[] = { .devices = ELAN_ALL_DEV,
{
.cmd = {0x40, 0x24},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
},
}; };
static const size_t calibrate_end_cmds_len = static const struct elan_cmd led_on_cmd = {
G_N_ELEMENTS(calibrate_end_cmds); .cmd = {0x40, 0x31},
.response_len = ELAN_CMD_SKIP_READ,
static const struct elan_cmd capture_start_cmds[] = { .response_in = ELAN_EP_CMD_IN,
/* led on */ .devices = ELAN_ALL_DEV,
{
.cmd = {0x40, 0x31},
.response_len = 0x0,
.response_in = ELAN_EP_CMD_IN,
},
}; };
static size_t capture_start_cmds_len = G_N_ELEMENTS(capture_start_cmds); /* wait for finger
* subsequent read will not complete until finger is placed on the reader */
static const struct elan_cmd capture_wait_finger_cmds[] = { static const struct elan_cmd pre_scan_cmd = {
/* wait for finger .cmd = {0x40, 0x3f},
* subsequent read will not complete until finger is placed on the reader */ .response_len = 0x1,
{ .response_in = ELAN_EP_CMD_IN,
.cmd = {0x40, 0x3f}, .devices = ELAN_ALL_DEV,
.response_len = 0x1,
.response_in = ELAN_EP_CMD_IN,
},
}; };
static size_t capture_wait_finger_cmds_len = /* led off, stop waiting for finger */
G_N_ELEMENTS(capture_wait_finger_cmds); static const struct elan_cmd stop_cmd = {
.cmd = {0x00, 0x0b},
static const struct elan_cmd deactivate_cmds[] = { .response_len = ELAN_CMD_SKIP_READ,
/* led off */ .response_in = ELAN_EP_CMD_IN,
{ .devices = ELAN_ALL_DEV,
.cmd = {0x00, 0x0b},
.response_len = 0x0,
.response_in = ELAN_EP_CMD_IN,
},
}; };
static const size_t deactivate_cmds_len = G_N_ELEMENTS(deactivate_cmds); static const struct usb_id elan_id_table[] = {
{.vendor = ELAN_VEND_ID,.product = 0x0903,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0907,.driver_data = ELAN_0907},
{.vendor = ELAN_VEND_ID,.product = 0x0c01,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c02,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c03,.driver_data = ELAN_0C03},
{.vendor = ELAN_VEND_ID,.product = 0x0c04,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c05,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c06,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c07,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c08,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c09,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0a,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0b,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0c,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0d,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0e,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0f,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c10,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c11,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c12,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c13,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c14,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c15,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c16,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c17,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c18,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c19,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1a,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1b,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1c,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1d,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1e,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1f,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c20,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c21,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c22,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c23,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c24,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c25,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c26,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c27,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c28,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c29,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2a,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2b,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2c,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2d,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2e,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2f,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c30,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c31,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c32,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c33,.driver_data = ELAN_ALL_DEV},
{0, 0, 0,},
};
static void elan_cmd_cb(struct libusb_transfer *transfer); static void elan_cmd_done(struct fpi_ssm *ssm);
static void elan_cmd_read(struct fpi_ssm *ssm); static void elan_cmd_read(struct fpi_ssm *ssm);
static void elan_run_next_cmd(struct fpi_ssm *ssm);
static void elan_calibrate(struct fp_img_dev *dev);
static void elan_capture(struct fp_img_dev *dev); static void elan_capture(struct fp_img_dev *dev);
static void elan_deactivate(struct fp_img_dev *dev);
static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state);
#endif #endif

View file

@ -24,10 +24,6 @@
#include "fp_internal.h" #include "fp_internal.h"
static const struct usb_id whitelist_id_table[] = { static const struct usb_id whitelist_id_table[] = {
/* Unsupported (for now) Elantech finger print readers */
{ .vendor = 0x04f3, .product = 0x0c03 },
{ .vendor = 0x04f3, .product = 0x0c16 },
{ .vendor = 0x04f3, .product = 0x0c26 },
/* Unsupported (for now) Validity Sensors finger print readers */ /* Unsupported (for now) Validity Sensors finger print readers */
{ .vendor = 0x138a, .product = 0x0090 }, /* Found on e.g. Lenovo T460s */ { .vendor = 0x138a, .product = 0x0090 }, /* Found on e.g. Lenovo T460s */
{ .vendor = 0x138a, .product = 0x0091 }, { .vendor = 0x138a, .product = 0x0091 },