Compare commits

..

19 Commits

Author SHA1 Message Date
Benjamin Berg
65bd095ea9 examples: Add delete command support to virtmissensor.py 2019-06-12 17:48:39 +02:00
Benjamin Berg
0d9c53e018 virtual_misdev: Add support to delete fingers 2019-06-12 17:48:39 +02:00
Vincent Huang
314cfba9bb examples: Add example for deleting prints
When deleting prints, the API user should also delete the print from the
storage (for devices that support on-device storage). Add an example to
show how to do this.
2019-06-12 17:48:39 +02:00
Vincent Huang
95337c71cd lib: Add API to query support for device internal storage
This is useful as these devices may require extra work by API users,
such as also deleting prints from the device when deleting them from
the local storage.
2019-06-12 17:48:39 +02:00
Vincent Huang
ef3519854d lib: Add API to delete prints stored on sensors
Some new fingerprint devices store the information required for matching
on the sensor itself, without ever disclosing this data to the host. In
this case we need to delete prints not only from local storage but also
from the device storage.

This commits adds the internal and external API required to do so.
2019-06-12 17:48:39 +02:00
Benjamin Berg
0240e0801c examples: Add virtmissensor.py script for virtual_misdev
The virtual match-in-sensor (MIS) device requires a script to simulate
the device. virtmissensor.py provides a simple implementation for this
purpose.
2019-06-12 17:48:39 +02:00
Benjamin Berg
9982916294 virtual_misdev: Add virtual match-in-sensor device with internal storage
We will need new API for these devices such as deleting prints from the
device. This is a basic initial driver only implementing the currently
available API allowing tests and further development.
2019-06-12 17:48:39 +02:00
Benjamin Berg
8cdeeeaaf1 examples: Add sendvirtimg.py script to send a print to virtual_imgdev
With this script it is possible to test libfprint/fprintd without any
hardware device. The image needs to be provides as a PNG with the alpha
channel storing the print data.

See the comment in the file on how the script can be used.
2019-06-12 17:48:39 +02:00
Benjamin Berg
cd308ee34f examples: Add a few example prints from NIST
These prints are from NIST and are not copyrighted. PNG files are
provided where the print is stored in the alpha channel (for consumption
by test scripts).
2019-06-12 17:48:39 +02:00
Benjamin Berg
971a2a0ef1 virtual_imgdev: Add new driver for debugging purposes 2019-06-12 17:48:39 +02:00
Benjamin Berg
12748d348b poll: Add internal API for IO Conditions
This is needed to support busses other than USB.
2019-06-12 17:48:39 +02:00
Benjamin Berg
afe5e1ad4c poll: Port to use GMainContext
To support other bus types (including virtual) we will need to poll
other source than libusb. So use GMainContext so that we will not
need to implement polling ourselves, hopefully simplifying the logic to
add more event sources.
2019-06-12 17:48:39 +02:00
Benjamin Berg
8a53591766 core: Split out USB discovery and add virtual device discovery 2019-06-12 17:48:39 +02:00
Bastien Nocera
5c0bc90677 lib: Add new bus types for drivers
Add a way for drivers to declare they support a bus type other than USB.
We have declarations for SPI and virtual drivers, though there's no
device discovery implemented yet.

https://bugs.freedesktop.org/show_bug.cgi?id=106279

Patch modified from the original by Benjamin Berg <bberg@redhat.com>.
The drivers updates were mainly done using the following spatch:

@drv1@
identifier driver_name;
identifier id_table_var;
@@
struct fp_driver driver_name = {
	...,
-	.id_table = id_table_var,
+	.bus = BUS_TYPE_USB,
+	.id_table.usb = id_table_var,
	...
};
@imgdrv1@
identifier driver_name;
identifier id_table_var;
@@
struct fp_img_driver driver_name = {
	...,
	.driver = {
		...,
-		.id_table = id_table_var,
+		.bus = BUS_TYPE_USB,
+		.id_table.usb = id_table_var,
		...
	},
	...,
};
@imgdrv2@
identifier driver_name;
identifier discover_func;
@@
struct fp_img_driver driver_name = {
	...,
	.driver = {
		...,
-		.discover = discover_func,
+		.usb_discover = discover_func,
		...
	},
	...
};
@idtable1@
identifier drv;
expression x;
@@
	struct fp_driver *drv;
	<...
-	drv->id_table[x]
+	drv->id_table.usb[x]
	...>
@idtable2@
identifier drv;
identifier func;
expression x;
@@
func (..., struct fp_driver *drv, ...)
{
	<...
-	drv->id_table[x]
+	drv->id_table.usb[x]
	...>
}
2019-06-12 17:48:39 +02:00
Bastien Nocera
ef2cf067ea lib: Rename USB-specific discovery functions
https://bugs.freedesktop.org/show_bug.cgi?id=106279
2019-06-12 17:48:39 +02:00
Benjamin Berg
66891274a7 build: Remove header files from nbis_sources
There is no need to list them in the sources.
2019-06-12 16:10:04 +02:00
Benjamin Berg
f52276bd06 build: Remove header files from libfprint_sources
There is no need to list them in the sources.
2019-06-12 16:07:44 +02:00
Benjamin Berg
7dce8dbfaa build: Remove header files from drivers_sources
It is not necessary to list all the headers in the drivers_sources list,
so remove them.
2019-06-12 16:07:08 +02:00
Benjamin Berg
3b757ee738 build: Fix source files of upekts and upketc drivers
The upekts driver needs upek_proto.c while the upektc driver does not.
Move the corresponding source file entries so that both drivers compile
standalone.
2019-06-12 16:07:05 +02:00
65 changed files with 1510 additions and 3755 deletions

View File

@@ -27,7 +27,6 @@ private_headers = [
'vfs301_proto_fragments.h', 'vfs301_proto_fragments.h',
'vfs301_proto.h', 'vfs301_proto.h',
'vfs5011_proto.h', 'vfs5011_proto.h',
'synaptics.h',
# NBIS # NBIS
'morph.h', 'morph.h',

3
examples/prints/README Normal file
View File

@@ -0,0 +1,3 @@
These are example images from NIST and are not copyrighted.
The PNG files have been generated by using the greyscale data as a mask.

BIN
examples/prints/arch.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
examples/prints/arch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
examples/prints/whorl.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
examples/prints/whorl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

86
examples/sendvirtimg.py Executable file
View File

@@ -0,0 +1,86 @@
#!/usr/bin/env python3
# This script can be used together with the virtual_imgdev to simulate an
# image based fingerprint reader.
#
# To use, set the FP_VIRTUAL_IMGDEV environment variable for both the
# libfprint using program (e.g. fprintd) and this script.
#
# Usually this would work by adding it into the systemd unit file. The
# best way of doing so is to create
# /etc/systemd/system/fprintd.service.d/fprintd-test.conf
#
# [Service]
# RuntimeDirectory=fprint
# Environment=FP_VIRTUAL_IMGDEV=/run/fprint/virtimg_sock
# Environment=G_MESSAGES_DEBUG=all
# ReadWritePaths=$RUNTIME_DIR
#
# After that run:
#
# systemctl daemon-reload
# systemctl restart fprintd.service
#
# You may also need to disable selinux.
#
# Then run this script with e.g.
# FP_VIRTUAL_IMGDEV=/run/fprint/virtimg_sock ./sendvirtimg.py prints/whorl.png
import cairo
import sys
import os
import socket
import struct
if len(sys.argv) == 2:
png = cairo.ImageSurface.create_from_png(sys.argv[1])
# Cairo wants 4 byte aligned rows, so just add a few pixel if necessary
w = png.get_width()
h = png.get_height()
w = (w + 3) // 4 * 4
h = (h + 3) // 4 * 4
img = cairo.ImageSurface(cairo.Format.A8, w, h)
cr = cairo.Context(img)
cr.set_source_rgba(1, 1, 1, 1)
cr.paint()
cr.set_source_rgba(0, 0, 0, 0)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.set_source_surface(png)
cr.paint()
else:
sys.stderr.write('You need to pass a PNG with an alpha channel!\n')
sys.exit(1)
def write_dbg_img():
dbg_img_rgb = cairo.ImageSurface(cairo.Format.RGB24, img.get_width(), img.get_height())
dbg_cr = cairo.Context(dbg_img_rgb)
dbg_cr.set_source_rgb(0, 0, 0)
dbg_cr.paint()
dbg_cr.set_source_rgb(1, 1, 1)
dbg_cr.mask_surface(img, 0, 0)
dbg_img_rgb.write_to_png('/tmp/test.png')
#write_dbg_img()
# Send image through socket
sockaddr = os.environ['FP_VIRTUAL_IMGDEV']
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(sockaddr)
mem = img.get_data()
mem = mem.tobytes()
assert len(mem) == img.get_width() * img.get_height()
encoded_img = struct.pack('ii', img.get_width(), img.get_height())
encoded_img += mem
sock.sendall(encoded_img)

148
examples/virtmissensor.py Executable file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python3
# This script can be used together with the virtual_misdev to simulate an
# match-in-sensor device with internal storage.
#
# To use, set the FP_VIRTUAL_MISDEV environment variable for both the
# libfprint using program (e.g. fprintd) and this script.
#
# Usually this would work by adding it into the systemd unit file. The
# best way of doing so is to create
# /etc/systemd/system/fprintd.service.d/fprintd-test.conf
#
# [Service]
# RuntimeDirectory=fprint
# Environment=FP_VIRTUAL_IMGDEV=/run/fprint/virtimg_sock
# Environment=G_MESSAGES_DEBUG=all
# ReadWritePaths=$RUNTIME_DIR
#
# After that run:
#
# systemctl daemon-reload
# systemctl restart fprintd.service
#
# You may also need to disable selinux.
#
# Then run this script with e.g.
# FP_VIRTUAL_IMGDEV=/run/fprint/virtimg_sock ./virtmissensor.py /tmp/storage
#
# Please note that the storage file should be pre-created with a few lines
# Each line represents a slot, if a print is stored, then it will contain a
# UUID (defined by the driver) and a matching string to identify it again.
# Note that the last slot line should not end with a \n
import sys
import os
import socket
import struct
import argparse
parser = argparse.ArgumentParser(description='Play virtual fingerprint device with internal storage.')
parser.add_argument('storage', metavar='storage', type=argparse.FileType('r+'),
help='The "storage" database (one line per slot)')
parser.add_argument('-e', dest='enroll', type=str,
help='Enroll a print using the string as identifier')
parser.add_argument('-v', dest='verify', type=str,
help='Verify print if the stored identifier matches the given identifier')
parser.add_argument('-d', dest='delete', action='store_const', const=True,
help='Delete print as requested by driver')
args = parser.parse_args()
cnt = 0
if args.enroll:
cnt += 1
if args.verify:
cnt += 1
if args.delete:
cnt += 1
assert cnt == 1, 'You need to give exactly one command argument, -e or -v'
prints = []
for slot in args.storage.read().split('\n'):
split = slot.split(' ', 1)
if len(split) == 2:
prints.append(split)
else:
prints.append(None)
# Send image through socket
sockaddr = os.environ['FP_VIRTUAL_MISDEV']
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(sockaddr)
# Assume we get a full message
msg = sock.recv(1024)
assert(msg[-1] == ord(b'\n'))
if args.enroll:
if not msg.startswith(b'ENROLL '):
sys.stderr.write('Expected to enroll, but driver is not ready for enrolling (%s)\n' % str(msg.split(b' ', 1)[0]))
sys.exit(1)
uuid = msg[7:-1].decode('utf-8')
for slot in prints:
if slot is not None and slot[0] == uuid:
sock.sendall(b'2\n') # ENROLL_FAIL
sys.stderr.write('Failed to enroll; UUID has already been stored!\n')
sys.exit(1)
# Find an empty slot
for i, slot in enumerate(prints):
if slot is not None:
continue
prints[i] = (uuid, args.enroll)
sock.sendall(b'1\n') # ENROLL_COMPLETE
break
else:
# TODO: 2: ENROLL_FAIL, but we should send no empty slot!
sock.sendall(b'2\n') # ENROLL_FAIL
sys.stderr.write('Failed to enroll, no free slots!\n')
sys.exit(1)
elif args.verify:
if not msg.startswith(b'VERIFY '):
sys.stderr.write('Expected to verify, but driver is not ready for verifying (%s)\n' % str(msg.split(b' ', 1)[0]))
sys.exit(1)
uuid = msg[7:-1].decode('utf-8')
for slot in prints:
if slot is not None and slot[0] == uuid:
if slot[1] == args.verify:
sock.sendall(b'1\n') # VERIFY_MATCH
else:
sock.sendall(b'0\n') # VERIFY_NO_MATCH
sys.exit(0)
else:
sys.stderr.write('Slot ID is unknown, returning error\n')
sock.sendall(b'-1') # error, need way to report that print is unkown
elif args.delete:
if not msg.startswith(b'DELETE '):
sys.stderr.write('Expected to delete, but driver is not ready for deleting (%s)\n' % str(msg.split(b' ', 1)[0]))
sys.exit(1)
uuid = msg[7:-1].decode('utf-8')
for i, slot in enumerate(prints):
if slot is not None and slot[0] == uuid:
if slot[0] == uuid:
prints[i] = None
sock.sendall(b'0\n') # DELETE_COMPLETE
break
else:
sys.stderr.write('Slot ID is unknown, just report back complete\n')
sock.sendall(b'0') # DELETE_COMPLETE
prints_str = '\n'.join('' if p is None else '%s %s' % (p[0], p[1]) for p in prints)
prints_human_str = '\n'.join('empty slot' if p is None else '%s %s' % (p[0], p[1]) for p in prints)
print('Prints stored now:')
print(prints_human_str)
args.storage.seek(0)
args.storage.truncate()
args.storage.write(prints_str)

View File

@@ -820,7 +820,8 @@ struct fp_img_driver aes1610_driver = {
.id = AES1610_ID, .id = AES1610_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "AuthenTec AES1610", .full_name = "AuthenTec AES1610",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },
.flags = 0, .flags = 0,

View File

@@ -97,7 +97,8 @@ struct fp_img_driver aes1660_driver = {
.id = AES1660_ID, .id = AES1660_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "AuthenTec AES1660", .full_name = "AuthenTec AES1660",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },
.flags = 0, .flags = 0,

View File

@@ -862,7 +862,8 @@ struct fp_img_driver aes2501_driver = {
.id = AES2501_ID, .id = AES2501_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "AuthenTec AES2501", .full_name = "AuthenTec AES2501",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },
.flags = 0, .flags = 0,

View File

@@ -606,7 +606,8 @@ struct fp_img_driver aes2550_driver = {
.id = AES2550_ID, .id = AES2550_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "AuthenTec AES2550/AES2810", .full_name = "AuthenTec AES2550/AES2810",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },
.flags = 0, .flags = 0,

View File

@@ -100,7 +100,8 @@ struct fp_img_driver aes2660_driver = {
.id = AES2660_ID, .id = AES2660_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "AuthenTec AES2660", .full_name = "AuthenTec AES2660",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },
.flags = 0, .flags = 0,

View File

@@ -165,7 +165,8 @@ struct fp_img_driver aes3500_driver = {
.id = AES3500_ID, .id = AES3500_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "AuthenTec AES3500", .full_name = "AuthenTec AES3500",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_PRESS, .scan_type = FP_SCAN_TYPE_PRESS,
}, },
.flags = 0, .flags = 0,

View File

@@ -162,7 +162,8 @@ struct fp_img_driver aes4000_driver = {
.id = AES4000_ID, .id = AES4000_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "AuthenTec AES4000", .full_name = "AuthenTec AES4000",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_PRESS, .scan_type = FP_SCAN_TYPE_PRESS,
}, },
.flags = 0, .flags = 0,

View File

@@ -82,7 +82,7 @@ static int do_write_regv(struct write_regv_data *wdata, int upper_bound)
data[data_offset++] = regwrite->value; data[data_offset++] = regwrite->value;
} }
libusb_fill_bulk_transfer(transfer, FP_DEV(wdata->imgdev)->udev, EP_OUT, data, libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev (FP_DEV(wdata->imgdev)), EP_OUT, data,
alloc_size, write_regv_trf_complete, wdata, BULK_TIMEOUT); alloc_size, write_regv_trf_complete, wdata, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer); r = libusb_submit_transfer(transfer);
if (r < 0) { if (r < 0) {

View File

@@ -42,7 +42,8 @@ enum {
VFS5011_ID = 19, VFS5011_ID = 19,
VFS0050_ID = 20, VFS0050_ID = 20,
ELAN_ID = 21, ELAN_ID = 21,
SYNAPTICS_ID = 22, VIRTUAL_IMG_ID = 22,
VIRTUAL_MIS_ID = 23,
}; };
#endif #endif

View File

@@ -973,7 +973,8 @@ struct fp_img_driver elan_driver = {
.id = ELAN_ID, .id = ELAN_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "ElanTech Fingerprint Sensor", .full_name = "ElanTech Fingerprint Sensor",
.id_table = elan_id_table, .bus = BUS_TYPE_USB,
.id_table.usb = elan_id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },
.flags = 0, .flags = 0,

View File

@@ -1481,7 +1481,8 @@ struct fp_img_driver etes603_driver = {
.id = ETES603_ID, .id = ETES603_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "EgisTec ES603", .full_name = "EgisTec ES603",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },
.flags = 0, .flags = 0,

View File

@@ -305,7 +305,8 @@ struct fp_img_driver fdu2000_driver = {
.id = FDU2000_ID, .id = FDU2000_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "Secugen FDU 2000", .full_name = "Secugen FDU 2000",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_PRESS, .scan_type = FP_SCAN_TYPE_PRESS,
}, },
.img_height = RAW_IMAGE_HEIGTH, .img_height = RAW_IMAGE_HEIGTH,

View File

@@ -1,260 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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 "bmkt_internal.h"
#include "bmkt_message.h"
#include "sensor.h"
struct bmkt_ctx
{
bmkt_sensor_t sensor;
};
bmkt_ctx_t g_ctx;
int bmkt_init(bmkt_ctx_t **ctx)
{
if (ctx == NULL)
{
return BMKT_INVALID_PARAM;
}
memset(&g_ctx, 0, sizeof(bmkt_ctx_t));
*ctx = &g_ctx;
bmkt_dbg_log("%s: context size: %ld", __func__, sizeof(bmkt_ctx_t));
return BMKT_SUCCESS;
}
void bmkt_exit(bmkt_ctx_t *ctx)
{
if (ctx == NULL)
{
return;
}
}
int bmkt_open(bmkt_ctx_t *ctx, bmkt_sensor_t **sensor,
bmkt_general_error_cb_t err_cb, void *err_cb_ctx, libusb_device_handle *usb_handle)
{
int ret;
if (ctx == NULL || sensor == NULL)
{
return BMKT_INVALID_PARAM;
}
*sensor = &ctx->sensor;
memset(*sensor, 0, sizeof(bmkt_sensor_t));
(*sensor)->usb_xport.handle = usb_handle;
ret = bmkt_sensor_open(*sensor, err_cb, err_cb_ctx);
if (ret != BMKT_SUCCESS)
{
return ret;
}
return BMKT_SUCCESS;
}
int bmkt_init_fps(bmkt_sensor_t *sensor)
{
int ret;
uint8_t *resp_buf;
int resp_len;
bmkt_response_t resp;
if (sensor->sensor_state != BMKT_SENSOR_STATE_UNINIT)
{
//sensor is already initialized
return BMKT_OPERATION_DENIED;
}
ret = bmkt_sensor_send_message_sync(sensor, BMKT_CMD_FPS_INIT, 0, NULL, &resp_buf, &resp_len, &resp);
if (ret != BMKT_SUCCESS)
{
return ret;
}
if (resp.result != BMKT_SUCCESS)
{
return resp.result;
}
return bmkt_sensor_init_fps(sensor);
}
int bmkt_close(bmkt_sensor_t *sensor)
{
if (sensor == NULL)
{
return BMKT_INVALID_PARAM;
}
return bmkt_sensor_close(sensor);
}
int bmkt_delete_enrolled_user(bmkt_sensor_t *sensor, uint8_t finger_id, const char *user_id, uint32_t user_id_len,
bmkt_resp_cb_t resp_cb, void *cb_ctx)
{
int ret;
uint8_t payload[BMKT_MAX_USER_ID_LEN + sizeof(finger_id)];
uint8_t payload_len;
if (sensor == NULL)
{
return BMKT_INVALID_PARAM;
}
if (user_id_len > BMKT_MAX_USER_ID_LEN)
{
return BMKT_INVALID_PARAM;
}
memset(payload, 0, sizeof(payload));
payload_len = user_id_len + sizeof(finger_id);
payload[0] = finger_id;
memcpy(&payload[1], user_id, user_id_len);
ret = bmkt_sensor_send_message(sensor, BMKT_CMD_DEL_USER_FP, payload_len, payload, resp_cb, cb_ctx);
if (ret != BMKT_SUCCESS)
{
return ret;
}
return BMKT_SUCCESS;
}
int bmkt_enroll(bmkt_sensor_t *sensor, const uint8_t *user_id, uint32_t user_id_len,
uint8_t finger_id, bmkt_resp_cb_t resp_cb, void *cb_ctx)
{
int ret = BMKT_GENERAL_ERROR;
/* Payload data for enroll_user [1 byte<backup option> 1 byte<finger Id> maximum length: 100 bytes]*/
uint8_t payload[BMKT_MAX_USER_ID_LEN + 2];
uint8_t payload_len = 0;
/* Backup options is not supported for Prometheus. */
uint8_t backup_opt = 0;
if (sensor == NULL || user_id == NULL)
{
return BMKT_INVALID_PARAM;
}
if (user_id_len > BMKT_MAX_USER_ID_LEN)
{
return BMKT_INVALID_PARAM;
}
payload_len = user_id_len + 2;
payload[0] = backup_opt;
payload[1] = finger_id;
memcpy(&payload[2], user_id, user_id_len);
ret = bmkt_sensor_send_message(sensor, BMKT_CMD_ENROLL_USER, payload_len, payload, resp_cb, cb_ctx);
if (ret != BMKT_SUCCESS)
{
return ret;
}
return BMKT_SUCCESS;
}
int bmkt_verify(bmkt_sensor_t *sensor, bmkt_user_id_t *user,
bmkt_resp_cb_t resp_cb, void *cb_ctx)
{
int ret;
uint8_t payload[BMKT_MAX_USER_ID_LEN + 1];
uint8_t payload_len;
if (sensor == NULL || user == NULL || user->user_id == NULL)
{
return BMKT_INVALID_PARAM;
}
if (user->user_id_len == 0 || user->user_id_len > BMKT_MAX_USER_ID_LEN)
{
return BMKT_INVALID_PARAM;
}
payload_len = user->user_id_len;
memset(payload, 0, sizeof(payload));
memcpy(&payload[0], user->user_id, user->user_id_len);
ret = bmkt_sensor_send_message(sensor, BMKT_CMD_VERIFY_USER, payload_len, payload, resp_cb,
cb_ctx);
if (ret != BMKT_SUCCESS)
{
return ret;
}
return BMKT_SUCCESS;
}
void bmkt_op_set_state(bmkt_sensor_t* sensor, bmkt_op_state_t state)
{
sensor->op_state = state;
}
void bmkt_op_sm(bmkt_sensor_t *sensor)
{
int ret;
int len = 0;
bmkt_dbg_log("bmkt_op_sm state = %d", sensor->op_state);
switch(sensor->op_state)
{
case BMKT_OP_STATE_GET_RESP:
ret = usb_receive_resp_async(&sensor->usb_xport, &len);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("bmkt_op_sm: usb_receive_resp_async failed %d", ret);
}
break;
case BMKT_OP_STATE_WAIT_INTERRUPT:
ret = usb_check_interrupt(&sensor->usb_xport);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("bmkt_op_sm: check_interrupt failed %d", ret);
}
break;
case BMKT_OP_STATE_SEND_ASYNC:
ret = bmkt_sensor_send_async_read_command(sensor);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("bmkt_op_sm: bmkt_sensor_send_async_read_command failed %d", ret);
}
break;
case BMKT_OP_STATE_COMPLETE:
break;
default:
break;
}
}
void bmkt_op_next_state(bmkt_sensor_t* sensor)
{
if(sensor->op_state != BMKT_OP_STATE_COMPLETE)
sensor->op_state = (sensor->op_state + 1) % BMKT_OP_STATE_COMPLETE;
bmkt_op_sm(sensor);
}

View File

@@ -1,452 +0,0 @@
/*
* Synaptics MiS Fingerprint Sensor Interface
* Copyright (C) 2019 Synaptics Inc
*
* 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
*/
#ifndef _BMKT_H_
#define _BMKT_H_
/**< User ID maximum length allowed */
#define BMKT_MAX_USER_ID_LEN 100
/**< Software Part Number length */
#define BMKT_PART_NUM_LEN 10
/**< Software supplier identification length */
#define BMKT_SUPPLIER_ID_LEN 2
/**< Maximum namber of templates for storing in internal flash of the fingerprint sensor */
#define BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH 15
#include <stdint.h>
#include "libusb-1.0/libusb.h"
#include "bmkt_response.h"
/*!
*******************************************************************************
** Type definition for result
*/
/** No error; Operation successfully completed. */
#define BMKT_SUCCESS 0
/** Fingerprint system not initialized */
#define BMKT_FP_SYSTEM_NOT_INITIALIZED 101
/** Fingerprint system busy performing another operation */
#define BMKT_FP_SYSTEM_BUSY 102
/** Operation not allowed */
#define BMKT_OPERATION_DENIED 103
/** System ran out of memory while performing operation */
#define BMKT_OUT_OF_MEMORY 104
/** Corrupt message, CRC check fail or truncated message */
#define BMKT_CORRUPT_MESSAGE 110
/** One of the command parameters is outside the range of valid values */
#define BMKT_INVALID_PARAM 111
/** Unrecognized message or message with invalid message ID */
#define BMKT_UNRECOGNIZED_MESSAGE 112
/** Operation time out */
#define BMKT_OP_TIME_OUT 113
/** General error cause of error cannot be determined */
#define BMKT_GENERAL_ERROR 114
#define BMKT_SET_SECURITY_LEVEL_FAIL 120
#define BMKT_GET_SECURITY_LEVEL_FAIL 121
/** Fingerprint sensor reset while operation was being performed */
#define BMKT_SENSOR_RESET 201
/** Fingerprint sensor malfunctioned */
#define BMKT_SENSOR_MALFUNCTION 202
/** Fingerprint sensor cannot be accessed despite repeated attempts */
#define BMKT_SENSOR_TAMPERED 203
/**
* BMKT_SENSOR_NOT_INIT:
* Fingerprint sensor module not initialized yet not ready for use
* (different from error code 101 which indicates that the entire system
* has not been initialized)
*/
#define BMKT_SENSOR_NOT_INIT 204
/** Number of re-pairing operations exceeded limit or re-pairing has been disabled */
#define BMKT_OWNERSHIP_RESET_MAX_EXCEEDED 205
/**
* BMKT_SENSOR_STIMULUS_ERROR:
* There is a finger or debris on the sensor that needs to be removed
* before issuing this command
*/
#define BMKT_SENSOR_STIMULUS_ERROR 213
/**
* BMKT_CORRUPT_TEMPLATE_DATA:
* One of the fingerprint templates stored on flash is corrupt.
* This error code is returned in case of failure in finding a fingerprint match
* during identify or verify operations while also detecting that one or more
* fingerprint templates stored on the flash has become corrupted
*/
#define BMKT_CORRUPT_TEMPLATE_DATA 300
/** Failed to extract features from fingerprint image acquired by sensor */
#define BMKT_FEATURE_EXTRACT_FAIL 301
/** Failed to generate fingerprint template */
#define BMKT_ENROLL_FAIL 302
/** Specified finger already enrolled for this user */
#define BMKT_ENROLLMENT_EXISTS 303
/** Invalid fingerprint image */
#define BMKT_INVALID_FP_IMAGE 304
/** No matching user fingerprint template found in database */
#define BMKT_FP_NO_MATCH 404
/** Fingerprint database is full */
#define BMKT_FP_DATABASE_FULL 501
/** Fingerprint database is empty */
#define BMKT_FP_DATABASE_EMPTY 502
/** Cannot access fingerprint database */
#define BMKT_FP_DATABASE_ACCESS_FAIL 503
/** Fingerprint template record does not exist */
#define BMKT_FP_DATABASE_NO_RECORD_EXISTS 504
/** Failed to read/write system parameters stored on flash */
#define BMKT_FP_PARAM_ACCESS_FAIL 505
/** Fingerprint is a spoof */
#define BMKT_FP_SPOOF_ALERT 801
/** Anti-spoof module failure */
#define BMKT_ANTI_SPOOF_MODULE_FAIL 802
#define BMKT_CORRUPT_UPDATE_IMAGE 901
#define BMKT_SYSTEM_UPDATE_FAIL 902
#define BMKT_EVENT_NOT_SET 1000
#define BMKT_SENSOR_NOT_READY 1001
#define BMKT_TIMEOUT 1002
#define BMKT_SENSOR_RESPONSE_PENDING 1003
#ifdef __cplusplus
extern "C" {
#endif
/**
* bmkt_mode:
* Fingerprint system operational mode values level 1
*/
typedef enum bmkt_mode
{
BMKT_STATE_UNINIT = 0xFF,
BMKT_STATE_IDLE = 0x00,
BMKT_STATE_ENROLL = 0x10,
BMKT_STATE_IDENTIFY = 0x20,
BMKT_STATE_VERIFY = 0x30,
BMKT_STATE_DB_OPS = 0x40,
BMKT_STATE_SYS_TEST = 0x50,
BMKT_STATE_SYS_OPS = 0x60,
} bmkt_mode_t;
/**
* bmkt_mode_level2:
* Fingerprint system operational mode values level 2
*/
typedef enum bmkt_mode_level2
{
BMKT_STATE_L2_IDLE = 0x00,
BMKT_STATE_L2_STARTING = 0x11,
BMKT_STATE_L2_WAITING_FOR_FINGER = 0x12,
BMKT_STATE_L2_CAPTURE_IMAGE = 0x13,
BMKT_STATE_L2_CAPTURE_COMPLETE = 0x14,
BMKT_STATE_L2_EXTRACT_FEATURE = 0x15,
BMKT_STATE_L2_CREATE_TEMPLATE = 0x16,
BMKT_STATE_L2_READING_FROM_FLASH = 0x17,
BMKT_STATE_L2_WRITING_TO_FLASH = 0x18,
BMKT_STATE_L2_FINISHING = 0x19,
BMKT_STATE_L2_CANCELING_OP = 0x20,
BMKT_STATE_L2_MATCHING = 0x21,
BMKT_STATE_L2_TRANSMITTING_RESPONSE = 0x22,
BMKT_STATE_L2_READY_POWER_DOWN = 0xF0,
} bmkt_mode_level2_t;
/**
* bmkt_transport_type:
* Fingerprint system transport types
*/
typedef enum bmkt_transport_type
{
BMKT_TRANSPORT_TYPE_USB = 0,
} bmkt_transport_type_t;
/**
* bmkt_usb_config:
* Structure represcontainingenting USB configuration details
*/
typedef struct bmkt_usb_config
{
int product_id; /**< USB device product ID */
} bmkt_usb_config_t;
/**
* bmkt_transport_config_t:
* Union containing transport configuration details
*/
typedef union
{
bmkt_usb_config_t usb_config;
} bmkt_transport_config_t;
/**
* bmkt_sensor_desc_t:
* Structure containing fingerprint system description
*/
typedef struct bmkt_sensor_desc
{
int product_id;
int flags;
} bmkt_sensor_desc_t;
/**
* bmkt_finger_state_t:
* Finger state representation values.
*/
typedef enum
{
BMKT_FINGER_STATE_UNKNOWN = 0,
BMKT_FINGER_STATE_ON_SENSOR,
BMKT_FINGER_STATE_NOT_ON_SENSOR,
} bmkt_finger_state_t;
/**
* bmkt_finger_event_t:
* Structure containing finger state
*/
typedef struct bmkt_finger_event
{
bmkt_finger_state_t finger_state;
} bmkt_finger_event_t;
typedef struct bmkt_user_id
{
uint8_t user_id_len;
uint8_t user_id[BMKT_MAX_USER_ID_LEN];
} bmkt_user_id_t;
typedef struct bmkt_ctx bmkt_ctx_t;
typedef struct bmkt_sensor bmkt_sensor_t;
typedef struct bmkt_sensor_desc bmkt_sensor_desc_t;
typedef struct bmkt_event bmkt_event_t;
typedef int (*bmkt_resp_cb_t)(bmkt_response_t *resp, void *cb_ctx);
typedef int (*bmkt_event_cb_t)(bmkt_finger_event_t *event, void *cb_ctx);
typedef int (*bmkt_general_error_cb_t)(uint16_t error, void *cb_ctx);
/**
* bmkt_init:
* @brief Initialize the bmkt library.
*
* @param[out] ctx A double pointer to return the created library module context pointer.
*
* @return BMKT_SUCCESS
* BMKT_INVALID_PARAM
*
* The bmkt_init function must be invoked to intialize the bmkt library before calling other functions.
* The library module context pointer is returned, which must be passed to all other library interface functions.
*/
int
bmkt_init(
bmkt_ctx_t ** ctx);
/**
* bmkt_exit:
* @brief Uninitialize the bmkt library.
*
* @param[in] ctx Context pointer created by bmkt_init.
*
* @return none
*
* The bmkt_exit function must be invoked when the module is no longer needed.
*/
void
bmkt_exit(
bmkt_ctx_t * ctx);
/**
* bmkt_open:
* @brief Open the specified sensor module.
*
* @param[in] ctx Context pointer created by bmkt_init.
* @param[out] sensor A double pointer to return the created sensor module pointer
* @param[in] err_cb General Error callback function
* @param[in] err_cb_ctx General Error callback user context
*
* @return VCS_RESULT_OK if success
*
* The bmkt_open function must be called to open a specific sensor module. Returned sensor module pointer
* must be passed to all other interface functions that expect a sensor pointer.
*/
int
bmkt_open(
bmkt_ctx_t * ctx,
bmkt_sensor_t ** sensor,
bmkt_general_error_cb_t err_cb,
void * err_cb_ctx,
libusb_device_handle * handle);
/**
* bmkt_close:
* @brief Close the specified sensor module, and release all the resources
*
* @param[in] sensor The sensor module pointer
*
* @return VCS_RESULT_OK if success
*
* The bmkt_close function must be invoked when the sensor module is no longer needed.
*/
int
bmkt_close(
bmkt_sensor_t * sensor);
/**
* bmkt_init_fps:
* @brief Initialize the sensor module.
*
* @param[in] sensor The sensor module pointer
*
* @return VCS_RESULT_OK if success
*
* Initializes the fingerprint sensor module. Must be the first command to be issued to the fingerprint sensor module, before any other commands are issued.
*/
int
bmkt_init_fps(
bmkt_sensor_t * sensor);
/**
* bmkt_enroll:
* @brief Put the fingerprint sensor module into enrollment mode to Enroll a users fingerprint into the system.
*
* @param[in] sensor The sensor module pointer
* @param[in] user_id Enrolled User ID
* @param[in] user_id_len Enrolled User ID lenght
* @param[in] finger_id Enrolled finger ID
* @param[in] resp_cb Responce callback function. Available responses:
* - BMKT_RSP_ENROLL_READY
* - BMKT_RSP_CAPTURE_COMPLETE
* - BMKT_RSP_ENROLL_REPORT
* - BMKT_RSP_ENROLL_PAUSED
* - BMKT_RSP_ENROLL_RESUMED
* - BMKT_RSP_ENROLL_FAIL
* - BMKT_RSP_ENROLL_OK
* @param[in] cb_ctx Responce callback user context
*
* @return VCS_RESULT_OK if success
*
* Enrolled users have to touch the fingerprint sensor multiple times based on cues provided by the system.
* After successful enrollment, a template is generated from features of the users fingerprint and stored
* in encrypted storage within the fingerprint sensor module.
* When this command is being executed, fingerprint sensor modules mode is: Enrollment
*/
int
bmkt_enroll(
bmkt_sensor_t * sensor,
const uint8_t * user_id,
uint32_t user_id_len,
uint8_t finger_id,
bmkt_resp_cb_t resp_cb,
void * cb_ctx);
/**
* bmkt_verify:
* @brief Put the fingerprint sensor module into verification mode.
*
* @param[in] sensor The sensor module pointer
* @param[in] user Enrolled User
* @param[in] resp_cb Responce callback function. Available responses:
* - BMKT_RSP_CAPTURE_COMPLETE
* - BMKT_RSP_VERIFY_READY
* - BMKT_RSP_VERIFY_FAIL
* - BMKT_RSP_VERIFY_OK
* @param[in] cb_ctx Responce callback user context
*
* @return VCS_RESULT_OK if success
*
* The user being verifyed has to touch the fingerprint sensor once based on a cue provided by the system.
* The Captured fingerprint is matched only against the stored templates corresponding to specifyed user ID,
* If a users fingerprint cannot be matched to any of the stored fingerprint templates of the specified user or
* if the fingerprint sensor module detects that the fingerprint being presented to the sensor is a spoof,
* then an error response is generated.
* When this command is being executed, fingerprint sensor modules mode is: Verification
*/
int
bmkt_verify(
bmkt_sensor_t * sensor,
bmkt_user_id_t* user,
bmkt_resp_cb_t resp_cb,
void * cb_ctx);
/**
* bmkt_delete_enrolled_user:
* @brief Delete a specific fingerprint template of an enrolled user from the database.
*
* @param[in] sensor The sensor module pointer
* @param[in] finger_id Finger ID to be deleted
* @param[in] user_id User ID to be deleted
* @param[in] user_id_len User ID lenght
* @param[in] resp_cb Responce callback function. Available responses:
* - BMKT_RSP_DEL_USER_FP_FAIL
* - BMKT_RSP_DEL_USER_FP_OK
* @param[in] cb_ctx Responce callback user context
*
* @return VCS_RESULT_OK if success
*
* If the value of finger ID is set equal to 0 then all fingerprints of that user will be deleted from the database.
* If the value of user ID is set to an empty string (string with length 0) and the finger ID is set equal to 0 then
* all templates stored in the fingerprint database which are marked as corrupt will be deleted.
* When this command is being executed, fingerprint sensor modules mode is: Database operations
*/
int
bmkt_delete_enrolled_user(
bmkt_sensor_t * sensor,
uint8_t finger_id,
const char * user_id,
uint32_t user_id_len,
bmkt_resp_cb_t resp_cb,
void * cb_ctx);
/**
* bmkt_register_finger_event_notification:
* @brief Register finger presence event callback function
*
* @param[in] sensor The sensor module pointer
* @param[in] cb Event callback function
* @param[in] cb_ctx Event callback user context
*
* @return VCS_RESULT_OK if success
*
* The registered callback function will be called whenever a finger is detected as being placed on the sensor or removed from the sensor.
*/
int
bmkt_register_finger_event_notification(
bmkt_sensor_t * sensor,
bmkt_event_cb_t cb,
void * cb_ctx);
typedef enum
{
BMKT_OP_STATE_START = -1,
BMKT_OP_STATE_GET_RESP,
BMKT_OP_STATE_WAIT_INTERRUPT,
BMKT_OP_STATE_SEND_ASYNC,
BMKT_OP_STATE_COMPLETE,
} bmkt_op_state_t;
void bmkt_op_set_state(bmkt_sensor_t* sensor, bmkt_op_state_t state);
#ifdef __cplusplus
}
#endif
#endif /* _BMKT_H_ */

View File

@@ -1,44 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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
*/
#ifndef _BMKT_INTERNAL_H_
#define _BMKT_INTERNAL_H_
#include "bmkt.h"
#include "bmkt_message.h"
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include "fp_internal.h"
uint32_t extract32(const uint8_t *buf, int *offset);
uint16_t extract16(const uint8_t *buf, int *offset);
uint8_t extract8(const uint8_t *buf, int *offset);
void print_buffer(uint8_t *buf, int len);
#define bmkt_dbg_log fp_dbg
#define bmkt_info_log fp_info
#define bmkt_warn_log fp_warn
#define bmkt_err_log fp_err
void bmkt_op_next_state(bmkt_sensor_t *sensor);
#endif /* _BMKT_INTERNAL_H_ */

View File

@@ -1,397 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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 "bmkt_internal.h"
#include "bmkt_response.h"
#include "bmkt_message.h"
#include "usb_transport.h"
#include "sensor.h"
static int parse_error_response(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
if (msg_resp->payload_len != 2)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
resp->result = (msg_resp->payload[0] << 8) | msg_resp->payload[1];
return BMKT_SUCCESS;
}
static int parse_init_ok(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
bmkt_init_resp_t *init_resp = &resp->response.init_resp;
if (msg_resp->payload_len != 1)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
init_resp->finger_presence = extract8(msg_resp->payload, NULL);
return BMKT_SUCCESS;
}
static int parse_fps_mode_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
int offset = 0;
bmkt_fps_mode_resp_t *fps_mode_resp = &resp->response.fps_mode_resp;
if (msg_resp->payload_len != sizeof(bmkt_fps_mode_resp_t))
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
fps_mode_resp->mode = extract8(msg_resp->payload, &offset);
fps_mode_resp->level2_mode = extract8(msg_resp->payload, &offset);
fps_mode_resp->cmd_id = extract8(msg_resp->payload, &offset);
fps_mode_resp->finger_presence = extract8(msg_resp->payload, &offset);
return BMKT_SUCCESS;
}
static int parse_enroll_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
bmkt_enroll_resp_t *enroll_resp = &resp->response.enroll_resp;
if (msg_resp->payload_len != 1)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
enroll_resp->progress = extract8(msg_resp->payload, NULL);
return BMKT_SUCCESS;
}
static int parse_enroll_ok(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
bmkt_enroll_resp_t *enroll_resp = &resp->response.enroll_resp;
if (msg_resp->payload_len < 1 || msg_resp->payload_len > (BMKT_MAX_USER_ID_LEN + 1))
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
enroll_resp->finger_id = msg_resp->payload[0];
memcpy(enroll_resp->user_id, &msg_resp->payload[1], msg_resp->payload_len - 1);
return BMKT_SUCCESS;
}
static int parse_auth_ok(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
bmkt_identify_resp_t *id_resp = &resp->response.id_resp;
if (msg_resp->payload_len < 3 || msg_resp->payload_len > (BMKT_MAX_USER_ID_LEN + 3))
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
id_resp->match_result = (double)msg_resp->payload[0] + 0.01 * (double)msg_resp->payload[1];
id_resp->finger_id = msg_resp->payload[2];
memcpy(id_resp->user_id, &msg_resp->payload[3], msg_resp->payload_len - 3);
return BMKT_SUCCESS;
}
static int parse_security_level_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
bmkt_set_sec_level_resp_t *sec_level_resp = &resp->response.sec_level_resp;
if (msg_resp->payload_len != 1)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
sec_level_resp->sec_level = extract8(msg_resp->payload, NULL);
return BMKT_SUCCESS;
}
static int parse_del_all_users_progress_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
bmkt_del_all_users_resp_t *del_all_users_resp = &resp->response.del_all_users_resp;
if (msg_resp->payload_len != 1)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
del_all_users_resp->progress = extract8(msg_resp->payload, NULL);
return BMKT_SUCCESS;
}
static int parse_db_cap_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
bmkt_get_db_capacity_resp_t *db_cap_resp = &resp->response.db_cap_resp;
int offset = 0;
if (msg_resp->payload_len < 2 || msg_resp->payload_len > 4)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
db_cap_resp->total = extract8(msg_resp->payload, &offset);
db_cap_resp->empty = extract8(msg_resp->payload, &offset);
if (msg_resp->payload_len == 4)
{
db_cap_resp->bad_slots = extract8(msg_resp->payload, &offset);
db_cap_resp->corrupt_templates = extract8(msg_resp->payload, &offset);
}
return BMKT_SUCCESS;
}
static int parse_get_enrolled_fingers_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
int offset = 0;
int i = 0;
if (msg_resp->payload_len < 2)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
/* 2 bytes per finger so calculate the total number of fingers to process*/
int num_fingers = (msg_resp->payload_len) / 2;
bmkt_enrolled_fingers_resp_t *get_enrolled_fingers_resp = &resp->response.enrolled_fingers_resp;
for (i = 0; i < num_fingers; i++)
{
get_enrolled_fingers_resp->fingers[i].finger_id = extract8(msg_resp->payload, &offset);
get_enrolled_fingers_resp->fingers[i].template_status = extract8(msg_resp->payload, &offset);
}
return BMKT_SUCCESS;
}
static int parse_get_enrolled_users_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
int offset = 0;
int i = 0;
/* the payload is 2 bytes + template data */
if (msg_resp->payload_len < 2)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
bmkt_enroll_templates_resp_t *get_enroll_templates_resp = &resp->response.enroll_templates_resp;
get_enroll_templates_resp->total_query_messages = extract8(msg_resp->payload, &offset);
get_enroll_templates_resp->query_sequence = extract8(msg_resp->payload, &offset);
int n = 0;
for (n = 0; n < BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH; n++)
{
if (offset >= msg_resp->payload_len)
break;
get_enroll_templates_resp->templates[n].user_id_len = extract8(msg_resp->payload, &offset) - 2;
if(get_enroll_templates_resp->templates[n].user_id_len > BMKT_MAX_USER_ID_LEN)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
get_enroll_templates_resp->templates[n].template_status = extract8(msg_resp->payload, &offset);
get_enroll_templates_resp->templates[n].finger_id = extract8(msg_resp->payload, &offset);
for (i = 0; i < get_enroll_templates_resp->templates[n].user_id_len; i++)
{
get_enroll_templates_resp->templates[n].user_id[i] = extract8(msg_resp->payload, &offset);
}
get_enroll_templates_resp->templates[n].user_id[i] = '\0';
}
return BMKT_SUCCESS;
}
static int parse_get_version_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
bmkt_get_version_resp_t *get_version_resp = &resp->response.get_version_resp;
int offset = 0;
if (msg_resp->payload_len != 15)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
memcpy(get_version_resp->part, msg_resp->payload, BMKT_PART_NUM_LEN);
offset += BMKT_PART_NUM_LEN;
get_version_resp->year = extract8(msg_resp->payload, &offset);
get_version_resp->week = extract8(msg_resp->payload, &offset);
get_version_resp->patch = extract8(msg_resp->payload, &offset);
memcpy(get_version_resp->supplier_id, msg_resp->payload + offset, BMKT_SUPPLIER_ID_LEN);
return BMKT_SUCCESS;
}
int bmkt_compose_message(uint8_t *cmd, int *cmd_len, uint8_t msg_id, uint8_t seq_num,
uint8_t payload_size, uint8_t *payload)
{
int message_len = BMKT_MESSAGE_HEADER_LEN + payload_size;
if (*cmd_len < message_len)
{
return BMKT_OUT_OF_MEMORY;
}
cmd[BMKT_MESSAGE_HEADER_ID_FIELD] = BMKT_MESSAGE_HEADER_ID;
cmd[BMKT_MESSAGE_SEQ_NUM_FIELD] = seq_num;
cmd[BMKT_MESSAGE_ID_FIELD] = msg_id;
cmd[BMKT_MESSAGE_PAYLOAD_LEN_FIELD] = payload_size;
memcpy(&cmd[BMKT_MESSAGE_PAYLOAD_FIELD], payload, payload_size);
*cmd_len = message_len;
return BMKT_SUCCESS;
}
int bmkt_parse_message_header(uint8_t *resp_buf, int resp_len, bmkt_msg_resp_t *msg_resp)
{
if (resp_buf[BMKT_MESSAGE_HEADER_ID_FIELD] != BMKT_MESSAGE_HEADER_ID)
{
return BMKT_CORRUPT_MESSAGE;
}
msg_resp->seq_num = resp_buf[BMKT_MESSAGE_SEQ_NUM_FIELD];
msg_resp->msg_id = resp_buf[BMKT_MESSAGE_ID_FIELD];
msg_resp->payload_len = resp_buf[BMKT_MESSAGE_PAYLOAD_LEN_FIELD];
if (msg_resp->payload_len > 0)
{
msg_resp->payload = &resp_buf[BMKT_MESSAGE_PAYLOAD_FIELD];
}
else
{
msg_resp->payload = NULL;
}
return BMKT_SUCCESS;
}
int bmkt_parse_message_payload(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp)
{
int ret = BMKT_SUCCESS;
memset(resp, 0, sizeof(bmkt_response_t));
resp->response_id = msg_resp->msg_id;
switch(msg_resp->msg_id)
{
case BMKT_RSP_CONTINUOUS_IMAGE_CAPTURE_FAIL:
case BMKT_RSP_SENSOR_MODULE_TEST_FAIL:
case BMKT_RSP_FPS_INIT_FAIL:
case BMKT_RSP_FPS_MODE_FAIL:
case BMKT_RSP_SET_SECURITY_LEVEL_FAIL:
case BMKT_RSP_GET_SECURITY_LEVEL_FAIL:
case BMKT_RSP_CANCEL_OP_FAIL:
case BMKT_RSP_ENROLL_FAIL:
case BMKT_RSP_ID_FAIL:
case BMKT_RSP_VERIFY_FAIL:
case BMKT_RSP_QUERY_FAIL:
case BMKT_RSP_DEL_USER_FP_FAIL:
case BMKT_RSP_DEL_FULL_DB_FAIL:
case BMKT_RSP_REPEAT_LAST_BMKT_RSP_FAIL:
case BMKT_RSP_POWER_DOWN_FAIL:
case BMKT_RSP_GET_VERSION_FAIL:
case BMKT_RSP_DISABLE_PAIRING_FAIL:
case BMKT_RSP_QUERY_PAIRING_FAIL:
case BMKT_RSP_SENSOR_STATUS_FAIL:
case BMKT_RSP_RETRIEVE_FINAL_RESULT_FAIL:
ret = parse_error_response(msg_resp, resp);
resp->complete = 1;
break;
case BMKT_RSP_FPS_INIT_OK:
ret = parse_init_ok(msg_resp, resp);
resp->complete = 1;
break;
case BMKT_RSP_CANCEL_OP_OK:
case BMKT_RSP_DEL_FULL_DB_OK:
case BMKT_RSP_DEL_USER_FP_OK:
/* responses with a payload of 0
so the response indicates success */
resp->result = BMKT_SUCCESS;
resp->complete = 1;
break;
case BMKT_RSP_FPS_MODE_REPORT:
// parse_fps_mode
ret = parse_fps_mode_report(msg_resp, resp);
resp->complete = 1;
break;
case BMKT_RSP_GET_SECURITY_LEVEL_REPORT:
case BMKT_RSP_SET_SECURITY_LEVEL_REPORT:
/* parse security level result */
ret = parse_security_level_report(msg_resp, resp);
resp->complete = 1;
break;
case BMKT_RSP_DELETE_PROGRESS:
ret = parse_del_all_users_progress_report(msg_resp, resp);
break;
case BMKT_RSP_CAPTURE_COMPLETE:
resp->result = BMKT_SUCCESS;
break;
case BMKT_RSP_ENROLL_READY:
resp->result = BMKT_SUCCESS;
break;
case BMKT_RSP_ENROLL_REPORT:
ret = parse_enroll_report(msg_resp, resp);
break;
case BMKT_RSP_ENROLL_OK:
resp->complete = 1;
ret = parse_enroll_ok(msg_resp, resp);
break;
case BMKT_RSP_ID_OK:
case BMKT_RSP_VERIFY_OK:
ret = parse_auth_ok(msg_resp, resp);
resp->complete = 1;
break;
case BMKT_RSP_GET_ENROLLED_FINGERS_REPORT:
ret = parse_get_enrolled_fingers_report(msg_resp, resp);
resp->complete = 1;
break;
case BMKT_RSP_DATABASE_CAPACITY_REPORT:
resp->complete = 1;
ret = parse_db_cap_report(msg_resp, resp);
break;
case BMKT_RSP_TEMPLATE_RECORDS_REPORT:
ret = parse_get_enrolled_users_report(msg_resp, resp);
break;
case BMKT_RSP_QUERY_RESPONSE_COMPLETE:
resp->complete = 1;
break;
case BMKT_RSP_VERSION_INFO:
ret = parse_get_version_report(msg_resp, resp);
resp->complete = 1;
break;
}
return ret;
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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
*/
#ifndef BMKT_MESSAGE_H_
#define BMKT_MESSAGE_H_
#include "bmkt_internal.h"
#define BMKT_MESSAGE_HEADER_ID 0xFE
#define BMKT_MESSAGE_HEADER_LEN (4)
#define BMKT_MESSAGE_CRC32_LEN (4)
#define BMKT_MESSAGE_HEADER_ID_FIELD 0
#define BMKT_MESSAGE_SEQ_NUM_FIELD 1
#define BMKT_MESSAGE_ID_FIELD 2
#define BMKT_MESSAGE_PAYLOAD_LEN_FIELD 3
#define BMKT_MESSAGE_PAYLOAD_FIELD 4
// Command messages
#define BMKT_CMD_CONTINUOUS_IMAGE_CAPTURE 0x01
#define BMKT_CMD_CONTINUOUS_IMAGE_CAPTURE_STOP 0x04
#define BMKT_CMD_SENSOR_MODULE_TEST 0x06
#define BMKT_CMD_SENSOR_MODULE_TEST_START 0x08
#define BMKT_CMD_NEXT_TEST_REPORT_CHUNK 0x0B
#define BMKT_CMD_FPS_INIT 0x11
#define BMKT_CMD_GET_FPS_MODE 0x21
#define BMKT_CMD_SET_SECURITY_LEVEL 0x31
#define BMKT_CMD_GET_SECURITY_LEVEL 0x34
#define BMKT_CMD_CANCEL_OP 0x41
#define BMKT_CMD_ENROLL_USER 0x51
#define BMKT_CMD_ENROLL_PAUSE 0x52
#define BMKT_CMD_ENROLL_RESUME 0x53
#define BMKT_CMD_ID_USER 0x61
#define BMKT_CMD_VERIFY_USER 0x65
#define BMKT_CMD_GET_TEMPLATE_RECORDS 0x71
#define BMKT_CMD_GET_NEXT_QUERY_RESPONSE 0x72
#define BMKT_CMD_GET_ENROLLED_FINGERS 0x73
#define BMKT_CMD_GET_DATABASE_CAPACITY 0x74
#define BMKT_CMD_DEL_USER_FP 0x81
#define BMKT_CMD_DEL_FULL_DB 0x84
#define BMKT_CMD_REPEAT_LAST_RSP 0x92
#define BMKT_CMD_POWER_DOWN_NOTIFY 0xA1
#define BMKT_CMD_GET_VERSION 0xB1
#define BMKT_CMD_DISABLE_PAIRING 0xC2
#define BMKT_CMD_QUERY_PAIRING 0xC5
#define BMKT_CMD_SENSOR_STATUS 0xD1
#define BMKT_CMD_ID_USER_IN_ORDER 0xE1
#define BMKT_CMD_ID_NEXT_USER 0xE3
#define BMKT_CMD_VERIFY_USER_IN_ORDER 0xF1
#define BMKT_CMD_VERIFY_FINGERS_IN_ORDER 0xF2
#define BMKT_CMD_GET_FINAL_RESULT 0xE4
#define BMKT_EVT_FINGER_REPORT 0x91
#define BMKT_EVT_FINGER_STATE_NOT_ON_SENSOR 0x00
#define BMKT_EVT_FINGER_STATE_ON_SENSOR 0x01
typedef struct bmkt_msg_resp
{
uint8_t msg_id;
uint8_t seq_num;
uint8_t payload_len;
uint8_t *payload;
int result;
} bmkt_msg_resp_t;
typedef struct bmkt_session_ctx
{
uint8_t seq_num;
bmkt_resp_cb_t resp_cb;
void *cb_ctx;
} bmkt_session_ctx_t;
int bmkt_compose_message(uint8_t *cmd, int *cmd_len, uint8_t msg_id, uint8_t seq_num,
uint8_t payload_size, uint8_t *payload);
int bmkt_parse_message_header(uint8_t *resp_buf, int resp_len, bmkt_msg_resp_t *msg_resp);
int bmkt_parse_message_payload(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp);
#endif /* BMKT_MESSAGE_H_ */

View File

@@ -1,487 +0,0 @@
/*
* Synaptics MiS Fingerprint Sensor Response Data Interface
* Copyright (C) 2019 Synaptics Inc
*
* 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
*/
#ifndef _BMKT_RESPONSE_H_
#define _BMKT_RESPONSE_H_
/** List of response message IDs */
#define BMKT_RSP_CONTINUOUS_IMAGE_CAPTURE_FAIL 0x02
#define BMKT_RSP_CONTINUOUS_IMAGE_CAPTURE_READY 0x03
#define BMKT_RSP_CONTINUOUS_IMAGE_CAPTURE_STOPPED 0x05
#define BMKT_RSP_SENSOR_MODULE_TEST_READY 0x07
#define BMKT_RSP_SENSOR_MODULE_TEST_FAIL 0x09
#define BMKT_RSP_SENSOR_MODULE_TEST_REPORT 0x0A
#define BMKT_RSP_NEXT_TEST_REPORT_CHUNK 0x0C
/*! \addtogroup init
* Response IDs returned by fingerprint initialization operation
* @{
*/
/** Failed to initialize fingerprint sensor module */
#define BMKT_RSP_FPS_INIT_FAIL 0x12
/** Successfully initialized fingerprint sensor module */
#define BMKT_RSP_FPS_INIT_OK 0x13
/*! @} */
/*! \addtogroup mode
* Response IDs returned by get fingerprint mode operation
* @{
*/
/** Failed to get fingerprint sensor modules current operational mode */
#define BMKT_RSP_FPS_MODE_FAIL 0x22
/**
* BMKT_RSP_FPS_MODE_REPORT:
* Response containing the current operational mode of the fingerprint sensor module
* <br>Payload data represented in \ref bmkt_fps_mode_resp_t struct
*/
#define BMKT_RSP_FPS_MODE_REPORT 0x23
/*! @} */
/*! \addtogroup setseclevel
* Response IDs returned by set security level operation
* @{
*/
/** Failed to set fingerprint sensor module security level */
#define BMKT_RSP_SET_SECURITY_LEVEL_FAIL 0x32
/**
* BMKT_RSP_SET_SECURITY_LEVEL_REPORT:
* Security level of the fingerprint sensor module was set successfully
* <br>Contains payload data represented in \ref bmkt_set_sec_level_resp_t struct
*/
#define BMKT_RSP_SET_SECURITY_LEVEL_REPORT 0x33
/*! @} */
/*! \addtogroup getseclevel
* Response IDs returned by get security level operation
* @{
*/
/** Failed to get fingerprint sensor module security level */
#define BMKT_RSP_GET_SECURITY_LEVEL_FAIL 0x35
/**
* BMKT_RSP_GET_SECURITY_LEVEL_REPORT:
* Returns the current security level of the fingerprint sensor module
* <br>Contains payload data represented in \ref bmkt_set_sec_level_resp_t struct
*/
#define BMKT_RSP_GET_SECURITY_LEVEL_REPORT 0x36
/*! @} */
/*! \addtogroup cancelop
* Response IDs returned by cancel_operation operation
* @{
*/
/**
* BMKT_RSP_CANCEL_OP_OK:
* Successfully canceled the current operation and returned
* fingerprint sensor module to idle mode
*/
#define BMKT_RSP_CANCEL_OP_OK 0x42
/** Failed to cancel the current operation */
#define BMKT_RSP_CANCEL_OP_FAIL 0x43
/*! @} */
/*! \addtogroup enrollment
* Response IDs returned by enrollment operation
* @{
*/
/**
* BMKT_RSP_ENROLL_READY:
* Fingerprint enrollment session has begun and the user can place
* their finger on the sensor
*/
#define BMKT_RSP_ENROLL_READY 0x54
/** Progress of the currently on-going fingerprint enrollment session */
#define BMKT_RSP_ENROLL_REPORT 0x55
/** Enrollment has been paused */
#define BMKT_RSP_ENROLL_PAUSED 0x56
/** Enrollment has been resume */
#define BMKT_RSP_ENROLL_RESUMED 0x57
/** The current enrollment session has encountered an error */
#define BMKT_RSP_ENROLL_FAIL 0x58
/**
* BMKT_RSP_ENROLL_OK:
* User has been successfully enrolled into the fingerprint sensor module
* <br>Contains payload data represented in \ref bmkt_enroll_resp_t struct
*/
#define BMKT_RSP_ENROLL_OK 0x59
/**
* BMKT_RSP_CAPTURE_COMPLETE:
* Fingerprint image capture is complete and it is safe for the user
* to lift their finger off the sensor
*/
#define BMKT_RSP_CAPTURE_COMPLETE 0x60
/*! @} */
/*! \addtogroup identify
* Response IDs returned by identify operation.
* @{
*/
/* Fingerprint identification session has begun */
#define BMKT_RSP_ID_READY 0x62
/* Identification has failed */
#define BMKT_RSP_ID_FAIL 0x63
/**
* BMKT_RSP_ID_OK:
* User has been successfully identified
* <br>Contains payload data represented in \ref bmkt_auth_resp struct
*/
#define BMKT_RSP_ID_OK 0x64
/*! @} */
/*! \addtogroup verify
* Response IDs returned by identify operation.
* @{
*/
/** Fingerprint verification session has begun */
#define BMKT_RSP_VERIFY_READY 0x66
/** Verification has failed */
#define BMKT_RSP_VERIFY_FAIL 0x67
/**
* BMKT_RSP_VERIFY_OK:
* Users identity has been successfully verified
* <br>Contains payload data represented in \ref bmkt_auth_resp struct
*/
#define BMKT_RSP_VERIFY_OK 0x68
/*! @} */
/**
* BMKT_RSP_TEMPLATE_RECORDS_REPORT:
* Response ID returned by get enrolled users templates record operation
* <br>Returns list of template records containing user IDs and corresponding finger IDs
* <br>Payload data represented in \ref bmkt_enroll_templates_resp_t struct
*/
#define BMKT_RSP_TEMPLATE_RECORDS_REPORT 0x75
/**
* BMKT_RSP_QUERY_RESPONSE_COMPLETE:
* Response ID returned by get next query response operation
* <br>Complete sequence of messages containing the template records query response has been sent
*/
#define BMKT_RSP_QUERY_RESPONSE_COMPLETE 0x76
/**
* BMKT_RSP_GET_ENROLLED_FINGERS_REPORT:
* Response ID returned by get enrolled fingers operation
* <br> Returns list of IDs of enrolled fingers for a specific user,
* along with template record status corresponding to each enrolled finger
* <br>Contains payload data represented in \ref bmkt_enrolled_fingers_resp_t struct
*/
#define BMKT_RSP_GET_ENROLLED_FINGERS_REPORT 0x77
/*! \addtogroup dbcapacity
* Response IDs returned by get database capacity operation
* @{
*/
/**
* BMKT_RSP_DATABASE_CAPACITY_REPORT:
* Response specifying total capacity of fingerprint template database and
* how much free capacity is remaining along with how many templates are corrupted and
* how many bad (permanently unusable) storage slots are there.
* <br>Payload data represented in \ref bmkt_get_db_capacity_resp_t struct
*/
#define BMKT_RSP_DATABASE_CAPACITY_REPORT 0x78
/** Failed to execute database query */
#define BMKT_RSP_QUERY_FAIL 0x79
/*! @} */
/*! \addtogroup deluser
* Response IDs returned by delete fingerprint of specific user operation
* @{
*/
/** Failed to delete a users fingerprint template from the database */
#define BMKT_RSP_DEL_USER_FP_FAIL 0x82
/**
* BMKT_RSP_DEL_USER_FP_OK:
* Fingerprint template successfully deleted from the database.
* Returns the user ID and finger ID deleted. If value of finger ID is set equal to 0,
* then all fingerprint templates for that user have been deleted from the database
* <br>Payload data represented in \ref bmkt_del_user_resp_t struct
*/
#define BMKT_RSP_DEL_USER_FP_OK 0x83
/*! @} */
/*! \addtogroup delfulldb
* Response IDs returned by delete entire fingerprint template DB operation
* @{
*/
/** Failed to erase entire fingerprint template database */
#define BMKT_RSP_DEL_FULL_DB_FAIL 0x85
/** Successfully erased entire fingerprint template database */
#define BMKT_RSP_DEL_FULL_DB_OK 0x86
/**
* BMKT_RSP_DELETE_PROGRESS:
* Notify progress made during the on-going deletion of the full template database
* <br>Payload data represented in \ref bmkt_del_all_users_resp_t struct
*/
#define BMKT_RSP_DELETE_PROGRESS 0x87
/*! @} */
/**
* BMKT_RSP_REPEAT_LAST_BMKT_RSP_FAIL:
* Response ID returned by repeate last response operation
* <br>Failed to retrieve and re-send last response
*/
#define BMKT_RSP_REPEAT_LAST_BMKT_RSP_FAIL 0x93
/*! \addtogroup pwrdwn
* Response IDs returned by power down notify operation
* @{
*/
/** Fingerprint sensor module is ready to be powered down */
#define BMKT_RSP_POWER_DOWN_READY 0xA2
/** Failed to go into power down mode */
#define BMKT_RSP_POWER_DOWN_FAIL 0xA3
/*! @} */
/*! \addtogroup versioninfo
* Response IDs returned by get version operation
* @{
*/
/**
* BMKT_RSP_VERSION_INFO:
* System version information of the fingerprint sensor module
* <br>Payload data represented in \ref bmkt_get_version_resp_t struct
*/
#define BMKT_RSP_VERSION_INFO 0xB2
/* Failed to retrieve and send last response */
#define BMKT_RSP_GET_VERSION_FAIL 0xB3
/*! @} */
/**
* BMKT_RSP_GENERAL_ERROR:
* Not tied to a specific command-response session.
* <br>Could be caused by corrupt or truncated command message
*/
#define BMKT_RSP_GENERAL_ERROR 0xC1
#define BMKT_RSP_DISABLE_PAIRING_FAIL 0xC3
#define BMKT_RSP_DISABLE_PAIRING_OK 0xC4
#define BMKT_RSP_QUERY_PAIRING_FAIL 0xC6
#define BMKT_RSP_SENSOR_PAIRING_REPORT 0xC7
/*! \addtogroup versioninfo
* Response IDs returned by get sensor module status operation
* @{
*/
/**
* BMKT_RSP_SENSOR_STATUS_REPORT:
* Response returning the current status of the sensor module
* <br>Payload data represented in bmkt_XXX struct
*/
#define BMKT_RSP_SENSOR_STATUS_REPORT 0xD2
/** Failed to retrieve sensor status */
#define BMKT_RSP_SENSOR_STATUS_FAIL 0xD3
/*! @} */
/**
* BMKT_RSP_SEND_NEXT_USER_ID:
* Response ID returned by identify user in order operation
* <br>Notify to send the next batch of user IDs in the priority list
*/
#define BMKT_RSP_SEND_NEXT_USER_ID 0xE2
/**
* BMKT_RSP_RETRIEVE_FINAL_RESULT_FAIL:
* Response IDs returned by retrieve final result operation
* <br>Failed to retrieve and re-send cached final result
*/
#define BMKT_RSP_RETRIEVE_FINAL_RESULT_FAIL 0xE5
/**
* Response payload data structure returned by sensor initialization operation.
*/
typedef struct bmkt_init_resp
{
uint8_t finger_presence; /**< Indicates finger existence on the sensor during startup */
} bmkt_init_resp_t;
/**
* bmkt_enroll_resp:
* Response payload data structure returned by enrollment operation.
*/
typedef struct bmkt_enroll_resp
{
int progress; /**< Shows current progress stutus [0-100] */
uint8_t finger_id; /**< User's finger id [1-10] */
uint8_t user_id[BMKT_MAX_USER_ID_LEN]; /**< User name to be enrolled */
} bmkt_enroll_resp_t;
/**
* bmkt_auth_resp:
* Response payload data structure returned by identify and verify operations.
*/
struct bmkt_auth_resp
{
double match_result; /**< match result returned by matcher */
uint8_t finger_id; /**< Matched templates's finger id */
uint8_t user_id[BMKT_MAX_USER_ID_LEN]; /**< Matched template's user id */
};
typedef struct bmkt_auth_resp bmkt_verify_resp_t; /**< Returned by verify */
typedef struct bmkt_auth_resp bmkt_identify_resp_t; /**< Returned by identify */
/**
* bmkt_fps_mode_resp:
* Response payload data structure returned by get fingerprint mode operation.
*/
typedef struct bmkt_fps_mode_resp
{
uint8_t mode; /**< One of the Level I bmkt_mode_t values */
uint8_t level2_mode; /**< One of the Level II bmkt_mode_level2_t values */
uint8_t cmd_id; /**< Message ID of command being executed when bmkt_get_fps_mode was called */
uint8_t finger_presence; /**< Finger presence status value finger on sensor 1 / finger not on sensor 0 */
} bmkt_fps_mode_resp_t;
/**
* bmkt_get_version_resp:
* Response payload data structure returned by get version operation.
*/
typedef struct bmkt_get_version_resp
{
uint8_t part[BMKT_PART_NUM_LEN]; /**< Software Part Number */
uint8_t year; /**< Software Version Year */
uint8_t week; /**< Software Version Week */
uint8_t patch; /**< Software Version Patch Level */
uint8_t supplier_id[BMKT_SUPPLIER_ID_LEN]; /**< Software Supplier Identification */
} bmkt_get_version_resp_t;
/**
* bmkt_get_db_capacity_resp:
* Response payload data structure returned by get DB capacity operation.
*/
typedef struct bmkt_get_db_capacity_resp
{
uint8_t total; /**< Total Available Capacity: Total number of template records that can be stored */
uint8_t empty; /**< Free Capacity: Number of template records that can still be stored */
uint8_t bad_slots; /**< Number of bad template storage slots */
uint8_t corrupt_templates; /**< Number of corrupt templates */
} bmkt_get_db_capacity_resp_t;
/**
* bmkt_sec_level:
* Security level values.
*/
typedef enum bmkt_sec_level
{
BMKT_SECURITY_LEVEL_LOW = 0x10,
BMKT_SECURITY_LEVEL_MEDIUM = 0x40,
BMKT_SECURITY_LEVEL_HIGH = 0x60,
} bmkt_sec_level_t;
/**
* bmkt_set_sec_level_resp:
* Response payload data structure returned by get/set security level operations.
*/
typedef struct bmkt_set_sec_level_resp
{
bmkt_sec_level_t sec_level; /**< One of the bmkt_sec_level_t values */
} bmkt_set_sec_level_resp_t;
/**
* bmkt_del_all_users_resp:
* Response payload data structure returned by delete all enrolled users operation.
*/
typedef struct bmkt_del_all_users_resp
{
int progress; /**< Progress indicator as a percentage */
} bmkt_del_all_users_resp_t;
/**
* bmkt_del_user_resp:
* Response payload data structure returned by delete enrolled user operation.
*/
typedef struct bmkt_del_user_resp
{
int progress; /**< Progress indicator as a percentage */
} bmkt_del_user_resp_t;
/**
* bmkt_enroll_template:
* Structure of enrolled users template record data.
*/
typedef struct bmkt_enroll_template
{
uint8_t user_id_len; /**< Length of user_id string */
uint8_t template_status; /**< Template record status */
uint8_t finger_id; /**< ID of enrolled finger */
uint8_t user_id[BMKT_MAX_USER_ID_LEN + 1]; /**< Name of the enrolled user */
} bmkt_enroll_template_t;
/**
* bmkt_enroll_templates_resp:
* Response payload data structure returned by get enrolled user list operation.
*/
typedef struct bmkt_enroll_templates_resp
{
uint8_t total_query_messages; /**< Total query response messages */
uint8_t query_sequence; /**< Query response sequence number */
bmkt_enroll_template_t templates[BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH]; /**< Enrolled user template records list */
} bmkt_enroll_templates_resp_t;
/**
* bmkt_enrolled_fingers:
* Structure of template record status corresponding to each enrolled finger.
*/
typedef struct bmkt_enrolled_fingers
{
uint8_t finger_id; /**< ID of enrolled finger */
uint8_t template_status; /**< Template record status of finger_id */
} bmkt_enrolled_fingers_t;
/**
* bmkt_enrolled_fingers_resp:
* Response payload data structure returned by get enrolled fingers operation.
*/
typedef struct bmkt_enrolled_fingers_resp
{
bmkt_enrolled_fingers_t fingers[10]; /**< List of enroled fingers, max number of supported fingers per user is 10 */
} bmkt_enrolled_fingers_resp_t;
/**
* bmkt_response_data_t:
* Union combining all response payload data types.
*/
typedef union {
bmkt_init_resp_t init_resp;
bmkt_enroll_resp_t enroll_resp;
bmkt_verify_resp_t verify_resp;
bmkt_identify_resp_t id_resp;
bmkt_fps_mode_resp_t fps_mode_resp;
bmkt_get_version_resp_t get_version_resp;
bmkt_get_db_capacity_resp_t db_cap_resp;
bmkt_set_sec_level_resp_t sec_level_resp;
bmkt_del_all_users_resp_t del_all_users_resp;
bmkt_enroll_templates_resp_t enroll_templates_resp;
bmkt_del_user_resp_t del_user_resp;
bmkt_enrolled_fingers_resp_t enrolled_fingers_resp;
} bmkt_response_data_t;
/**
* bmkt_response:
* Structure to abstract different response structure types in one API
* to be used in bmkt_resp_cb_t callback function.
*/
typedef struct bmkt_response
{
int response_id; /**< Response message ID, one of th BMKT_RSP_XXX */
int result; /**< Operation execution result code */
int complete; /**< Operation completion status 1: complete / 0: not completed */
bmkt_response_data_t response; /**< Operation specific response union */
} bmkt_response_t;
#endif /* _BMKT_RESPONSE_H_ */

View File

@@ -1,481 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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 "bmkt_internal.h"
#include "bmkt_message.h"
#include "sensor.h"
#define SENSOR_CMD_GET_VERSION 1
#define SENSOR_CMD_ACE_COMMAND 167
#define SENSOR_CMD_ASYNCMSG_READ 168
#define SENSOR_FW_CMD_HEADER_LEN 1
#define SENSOR_FW_REPLY_HEADER_LEN 2
static int get_version(bmkt_sensor_t *sensor, bmkt_sensor_version_t *mis_version)
{
int ret;
uint8_t *resp = NULL;
int resp_len = 40;
uint16_t status = 0;
uint8_t *cmd;
int cmd_len = 0;
int cmd_buf_len;
int offset = 0;
ret = usb_get_command_buffer(&sensor->usb_xport, &cmd, &cmd_buf_len);
if (ret != BMKT_SUCCESS)
{
return BMKT_OUT_OF_MEMORY;
}
if (cmd_buf_len < SENSOR_FW_CMD_HEADER_LEN)
{
return BMKT_OUT_OF_MEMORY;
}
cmd[0] = SENSOR_CMD_GET_VERSION;
cmd_len = 1;
ret = usb_send_command_sync(&sensor->usb_xport, cmd_len, &resp, &resp_len);
if (ret != BMKT_SUCCESS)
{
return ret;
}
status = extract16(resp, &offset);
if (status)
{
bmkt_err_log("The sensor reported an error when sending get version command: 0x%x",
status);
return BMKT_SENSOR_MALFUNCTION;
}
if (resp_len < 38)
{
return BMKT_SENSOR_MALFUNCTION;
}
mis_version->build_time = extract32(resp, &offset);
mis_version->build_num = extract32(resp, &offset);
mis_version->version_major = extract8(resp, &offset);
mis_version->version_minor = extract8(resp, &offset);
mis_version->target = extract8(resp, &offset);
mis_version->product = extract8(resp, &offset);
ret = usb_release_command_buffer(&sensor->usb_xport);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("%s: failed to release command buffer: %d", __func__, ret);
return ret;
}
return BMKT_SUCCESS;
}
static bmkt_session_ctx_t *get_empty_session_ctx(bmkt_sensor_t *sensor)
{
bmkt_session_ctx_t *ctx;
int i;
int idx;
for (i = 0; i < BMKT_MAX_PENDING_SESSIONS; i++)
{
idx = (sensor->empty_session_idx + i) % BMKT_MAX_PENDING_SESSIONS;
ctx = &sensor->pending_sessions[idx];
if (ctx->seq_num == 0)
{
sensor->empty_session_idx = (idx + 1) % BMKT_MAX_PENDING_SESSIONS;
return ctx;
}
}
return NULL;
}
static bmkt_session_ctx_t *get_session_ctx(bmkt_sensor_t *sensor, int seq_num)
{
int i;
bmkt_session_ctx_t *ctx;
/* Sequence number of 0 is not valid for a response to
a command.*/
if (seq_num == 0)
{
return NULL;
}
for (i = 0; i < BMKT_MAX_PENDING_SESSIONS; i++)
{
ctx = &sensor->pending_sessions[i];
if (ctx->seq_num == seq_num)
{
return ctx;
}
}
return NULL;
}
static int release_session_ctx(bmkt_sensor_t *sensor, bmkt_session_ctx_t *ctx)
{
memset(ctx, 0, sizeof(bmkt_session_ctx_t));
return BMKT_SUCCESS;
}
int bmkt_sensor_open(bmkt_sensor_t *sensor, bmkt_general_error_cb_t err_cb, void *err_cb_ctx)
{
int ret;
sensor->seq_num = 1;
sensor->sensor_state = BMKT_SENSOR_STATE_UNINIT;
sensor->usb_xport.sensor = sensor;
ret = usb_open(&sensor->usb_xport);
if (ret != BMKT_SUCCESS)
{
bmkt_err_log("Failed to open transport: %d", ret);
return ret;
}
sensor->gen_err_cb = err_cb;
sensor->gen_err_cb_ctx = err_cb_ctx;
ret = get_version(sensor, &sensor->version);
if (ret != BMKT_SUCCESS)
{
bmkt_err_log("Failed to get version info: %d", ret);
return ret;
}
bmkt_dbg_log("Build Time: %d", sensor->version.build_time);
bmkt_dbg_log("Build Num: %d", sensor->version.build_num);
bmkt_dbg_log("Version: %d.%d", sensor->version.version_major, sensor->version.version_minor);
bmkt_dbg_log("Target: %d", sensor->version.target);
bmkt_dbg_log("Product: %d", sensor->version.product);
return BMKT_SUCCESS;
}
int bmkt_sensor_close(bmkt_sensor_t *sensor)
{
int ret;
sensor->sensor_state = BMKT_SENSOR_STATE_EXIT;
ret = usb_close(&sensor->usb_xport);
if (ret != BMKT_SUCCESS)
{
return ret;
}
sensor->sensor_state = BMKT_SENSOR_STATE_EXIT;
return BMKT_SUCCESS;
}
int bmkt_sensor_init_fps(bmkt_sensor_t *sensor)
{
sensor->sensor_state = BMKT_SENSOR_STATE_INIT;
return BMKT_SUCCESS;
}
int bmkt_sensor_send_message(bmkt_sensor_t *sensor, uint8_t msg_id, uint8_t payload_size,
uint8_t *payload, bmkt_resp_cb_t resp_cb, void *cb_ctx)
{
int ret;
uint8_t *cmd;
int cmd_buf_len = 0;
int msg_len;
int seq_num = 0;
bmkt_session_ctx_t *session_ctx = get_empty_session_ctx(sensor);
if (session_ctx == NULL)
{
return BMKT_OPERATION_DENIED;
}
if (sensor->seq_num > 255) {
/* seq. number is in range [1 255]. After it reaches 255, it rolls over to 1 and starts over again.
(0 is reserved for special purposes) */
sensor->seq_num = 1;
}
session_ctx->seq_num = sensor->seq_num++;
session_ctx->resp_cb = resp_cb;
session_ctx->cb_ctx = cb_ctx;
bmkt_dbg_log("session_ctx->seq_num=%d, sensor->seq_num=%d", session_ctx->seq_num, sensor->seq_num);
bmkt_op_set_state(sensor, BMKT_OP_STATE_START);
ret = usb_get_command_buffer(&sensor->usb_xport, &cmd, &cmd_buf_len);
if (ret != BMKT_SUCCESS)
{
return BMKT_OUT_OF_MEMORY;
}
/* MIS sensors send ACE commands encapsulated in FW commands*/
cmd[0] = SENSOR_CMD_ACE_COMMAND;
msg_len = cmd_buf_len - SENSOR_FW_CMD_HEADER_LEN;
if (session_ctx != NULL)
{
seq_num = session_ctx->seq_num;
}
ret = bmkt_compose_message(&cmd[1], &msg_len, msg_id, seq_num, payload_size, payload);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("Failed to compose ace message: %d", ret);
goto cleanup;
}
ret = usb_send_command(&sensor->usb_xport, msg_len + SENSOR_FW_CMD_HEADER_LEN);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("%s: failed to send ACE command: %d", __func__, ret);
goto cleanup;
}
cleanup:
usb_release_command_buffer(&sensor->usb_xport);
if (ret != BMKT_SUCCESS)
{
release_session_ctx(sensor, session_ctx);
}
return ret;
}
int bmkt_sensor_send_async_read_command(bmkt_sensor_t *sensor)
{
int ret;
uint8_t *cmd;
int cmd_buf_len = 0;
ret = usb_get_command_buffer(&sensor->usb_xport, &cmd, &cmd_buf_len);
if (ret != BMKT_SUCCESS)
{
return BMKT_OUT_OF_MEMORY;
}
/* MIS sensors send ACE commands encapsulated in FW commands */
cmd[0] = SENSOR_CMD_ASYNCMSG_READ;
ret = usb_send_command(&sensor->usb_xport, SENSOR_FW_CMD_HEADER_LEN);
if (ret == BMKT_SENSOR_RESPONSE_PENDING)
{
/* The caller needs to handle the response before we can send this command */
goto cleanup;
}
else if (ret != BMKT_SUCCESS)
{
if (ret != BMKT_SENSOR_NOT_READY)
{
bmkt_dbg_log("%s: failed to send ACE ASYNC READ command: %d", __func__, ret);
}
goto cleanup;
}
cleanup:
usb_release_command_buffer(&sensor->usb_xport);
return ret;
}
int bmkt_sensor_send_message_sync(bmkt_sensor_t *sensor, uint8_t msg_id, uint8_t payload_size,
uint8_t *payload, uint8_t **resp_buf, int *resp_len, bmkt_response_t *resp)
{
int ret;
uint8_t *cmd;
int cmd_buf_len = 0;
int msg_len;
bmkt_msg_resp_t msg_resp;
*resp_len = BMKT_MAX_TRANSFER_LEN;
ret = usb_get_command_buffer(&sensor->usb_xport, &cmd, &cmd_buf_len);
if (ret != BMKT_SUCCESS)
{
return BMKT_OUT_OF_MEMORY;
}
cmd[0] = SENSOR_CMD_ACE_COMMAND;
msg_len = cmd_buf_len - SENSOR_FW_CMD_HEADER_LEN;
ret = bmkt_compose_message(&cmd[1], &msg_len, msg_id, sensor->seq_num++, payload_size,
payload);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("Failed to compose ace message: %d", ret);
goto cleanup;
}
ret = usb_send_command_sync(&sensor->usb_xport, msg_len + SENSOR_FW_CMD_HEADER_LEN,
resp_buf, resp_len);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("%s: failed to send ACE command: %d", __func__, ret);
goto cleanup;
}
ret = bmkt_parse_message_header(&(*resp_buf)[2], *resp_len - 2, &msg_resp);
if (ret != BMKT_SUCCESS)
{
goto cleanup;
}
ret = bmkt_parse_message_payload(&msg_resp, resp);
if (ret != BMKT_SUCCESS)
{
goto cleanup;
}
cleanup:
ret = usb_release_command_buffer(&sensor->usb_xport);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("%s: failed to release command buffer: %d", __func__, ret);
return ret;
}
return ret;
}
int bmkt_sensor_handle_response(bmkt_sensor_t *sensor, uint8_t *resp_buf, int resp_len, bmkt_msg_resp_t *msg_resp)
{
int ret;
bmkt_session_ctx_t *session_ctx;
bmkt_response_t resp;
int i;
ret = bmkt_parse_message_header(&resp_buf[2], resp_len - 2, msg_resp);
if (ret == BMKT_CORRUPT_MESSAGE)
{
bmkt_warn_log("Corrupt Message Received");
return ret;
}
else if (ret != BMKT_SUCCESS)
{
return ret;
}
if (msg_resp->msg_id == BMKT_EVT_FINGER_REPORT)
{
/* finger event message */
bmkt_info_log("Finger event!");
bmkt_finger_event_t finger_event;
if (msg_resp->payload_len != 1)
{
return BMKT_UNRECOGNIZED_MESSAGE;
}
if (msg_resp->payload[0] == 0x01)
{
finger_event.finger_state = BMKT_FINGER_STATE_ON_SENSOR;
}
else
{
finger_event.finger_state = BMKT_FINGER_STATE_NOT_ON_SENSOR;
}
if (sensor->finger_event_cb != NULL)
{
sensor->finger_event_cb(&finger_event, sensor->finger_cb_ctx);
}
return BMKT_SUCCESS;
}
if (msg_resp->seq_num == 0)
{
if (msg_resp->msg_id == BMKT_RSP_GENERAL_ERROR)
{
/* report general error */
bmkt_info_log("General Error!");
uint16_t err;
if (sensor->gen_err_cb != NULL)
{
err = (msg_resp->payload[0] << 8) | msg_resp->payload[1];
sensor->gen_err_cb(err, sensor->gen_err_cb_ctx);
}
return BMKT_SUCCESS;
}
}
ret = bmkt_parse_message_payload(msg_resp, &resp);
if (ret != BMKT_SUCCESS)
{
bmkt_warn_log("Failed to process response: %d", ret);
return ret;
}
session_ctx = get_session_ctx(sensor, msg_resp->seq_num);
if (session_ctx == NULL)
{
bmkt_warn_log("Response received with invalid sequence number: %d, return BMKT_UNRECOGNIZED_MESSAGE(112)", msg_resp->seq_num);
return BMKT_UNRECOGNIZED_MESSAGE;
}
if (session_ctx->resp_cb != NULL)
{
ret = session_ctx->resp_cb(&resp, session_ctx->cb_ctx);
if (ret != BMKT_SUCCESS)
{
bmkt_warn_log("response callback failed: %d", ret);
}
}
if (resp.complete == 1)
{
ret = release_session_ctx(sensor, session_ctx);
if (ret != BMKT_SUCCESS)
{
return ret;
}
}
if (resp.response_id == BMKT_RSP_CANCEL_OP_OK && resp.result == BMKT_SUCCESS)
{
/* The previous commands have been canceled. Release all session ctx */
for (i = 0; i < BMKT_MAX_PENDING_SESSIONS; i++)
{
release_session_ctx(sensor, &sensor->pending_sessions[i]);
}
}
return BMKT_SUCCESS;
}
int bmkt_register_finger_event_notification(bmkt_sensor_t *sensor, bmkt_event_cb_t cb, void *cb_ctx)
{
if (sensor == NULL || cb == NULL)
{
return BMKT_INVALID_PARAM;
}
sensor->finger_event_cb = cb;
sensor->finger_cb_ctx = cb_ctx;
return BMKT_SUCCESS;
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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
*/
#ifndef _SENSOR_H_
#define _SENSOR_H_
#include "usb_transport.h"
#define BMKT_MAX_PENDING_SESSIONS 2
typedef enum bmkt_sensor_state
{
BMKT_SENSOR_STATE_UNINIT = 0,
BMKT_SENSOR_STATE_IDLE,
BMKT_SENSOR_STATE_INIT,
BMKT_SENSOR_STATE_EXIT,
} bmkt_sensor_state_t;
typedef struct bmkt_sensor_drv bmkt_sensor_drv_t;
typedef struct bmkt_sensor_version
{
uint32_t build_time;
uint32_t build_num;
uint8_t version_major;
uint8_t version_minor;
uint8_t target;
uint8_t product;
uint8_t silicon_rev;
uint8_t formal_release;
uint8_t platform;
uint8_t patch;
uint8_t serial_number[6];
uint16_t security;
uint8_t iface;
uint8_t device_type;
} bmkt_sensor_version_t;
typedef struct bmkt_sensor
{
bmkt_usb_transport_t usb_xport;
bmkt_sensor_version_t version;
bmkt_session_ctx_t pending_sessions[BMKT_MAX_PENDING_SESSIONS];
int empty_session_idx;
int flags;
int seq_num;
bmkt_sensor_state_t sensor_state;
bmkt_event_cb_t finger_event_cb;
void *finger_cb_ctx;
bmkt_general_error_cb_t gen_err_cb;
void *gen_err_cb_ctx;
bmkt_op_state_t op_state;
} bmkt_sensor_t;
int bmkt_sensor_open(bmkt_sensor_t *sensor,
bmkt_general_error_cb_t err_cb, void *err_cb_ctx);
int bmkt_sensor_close(bmkt_sensor_t *sensor);
int bmkt_sensor_init_fps(bmkt_sensor_t *sensor);
int bmkt_sensor_send_message(bmkt_sensor_t *sensor, uint8_t msg_id, uint8_t payload_size,
uint8_t *payload, bmkt_resp_cb_t resp_cb, void *resp_data);
int bmkt_sensor_send_message_sync(bmkt_sensor_t *sensor, uint8_t msg_id, uint8_t payload_size,
uint8_t *payload, uint8_t **resp_buf, int *resp_len, bmkt_response_t *resp);
int bmkt_sensor_handle_response(bmkt_sensor_t *sensor, uint8_t *resp_buf, int resp_len, bmkt_msg_resp_t *msg_resp);
int bmkt_sensor_send_async_read_command(bmkt_sensor_t *sensor);
#endif /* _SENSOR_H_ */

View File

@@ -1,488 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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 "synaptics"
#include "drivers_api.h"
#include "fpi-async.h"
#include "fp_internal.h"
#include "synaptics.h"
static const struct usb_id id_table[] = {
{ .vendor = SYNAPTICS_VENDOR_ID, .product = SYNAPTICS_PRODUCT_ID_A9, },
{ 0, 0, 0, }, /* terminating entry */
};
static int general_error_callback(uint16_t error, void *ctx)
{
fp_err("Received General Error %d from the sensor", error);
return 0;
}
static int finger_event_callback(bmkt_finger_event_t *event, void *ctx)
{
struct fp_dev *dev=(struct fp_dev *)ctx;
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
switch (event->finger_state)
{
case BMKT_FINGER_STATE_UNKNOWN:
fp_info("Finger state is not known");
break;
case BMKT_FINGER_STATE_ON_SENSOR:
sdev->isFingerOnSensor = TRUE;
fp_info("Finger in on the sensor");
break;
case BMKT_FINGER_STATE_NOT_ON_SENSOR:
sdev->isFingerOnSensor = FALSE;
fp_info("Finger is not on the sensor");
if(sdev->state == SYNA_STATE_VERIFY_DELAY_RESULT)
{
fp_info("verify no match");
bmkt_op_set_state(sdev->sensor, BMKT_OP_STATE_COMPLETE);
fpi_drvcb_report_verify_result(dev, FP_VERIFY_NO_MATCH, NULL);
}
break;
}
return BMKT_SUCCESS;
}
struct syna_mis_print_data
{
uint8_t finger_id;
uint8_t user_id[BMKT_MAX_USER_ID_LEN];
};
static int enroll_response(bmkt_response_t *resp, void *ctx)
{
bmkt_enroll_resp_t *enroll_resp = &resp->response.enroll_resp;
struct fp_dev *dev=(struct fp_dev *)ctx;
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
switch (resp->response_id)
{
case BMKT_RSP_ENROLL_READY:
{
fpi_drvcb_enroll_started(dev, 0);
sdev->enroll_resp_data.progress = 0;
fp_info("Place Finger on the Sensor!");
break;
}
case BMKT_RSP_CAPTURE_COMPLETE:
{
fp_info("Fingerprint image capture complete!");
break;
}
case BMKT_RSP_ENROLL_REPORT:
{
fp_info("Enrollment is %d %% ", enroll_resp->progress);
if(enroll_resp->progress < 100)
{
if(sdev->enroll_resp_data.progress == enroll_resp->progress)
fpi_drvcb_enroll_stage_completed(dev, FP_ENROLL_RETRY, NULL, NULL);
else
fpi_drvcb_enroll_stage_completed(dev, FP_ENROLL_PASS, NULL, NULL);
}
sdev->enroll_resp_data.progress = enroll_resp->progress;
break;
}
case BMKT_RSP_ENROLL_PAUSED:
{
fp_info("Enrollment has been paused!");
break;
}
case BMKT_RSP_ENROLL_RESUMED:
{
fp_info("Enrollment has been resumed!");
break;
}
case BMKT_RSP_ENROLL_FAIL:
{
fp_info("Enrollment has failed!: %d", resp->result);
break;
}
case BMKT_RSP_ENROLL_OK:
{
struct syna_mis_print_data mis_data;
struct fp_print_data *fdata = NULL;
struct fp_print_data_item *item = NULL;
fdata = fpi_print_data_new(dev);
item = fpi_print_data_item_new(sizeof(mis_data));
fp_info("Enrollment was successful!");
mis_data.finger_id = enroll_resp->finger_id;
memcpy(mis_data.user_id, enroll_resp->user_id,
BMKT_MAX_USER_ID_LEN);
memcpy(item->data, &mis_data,
sizeof(struct syna_mis_print_data));
fdata->prints = g_slist_prepend(fdata->prints, item);
bmkt_op_set_state(sdev->sensor, BMKT_OP_STATE_COMPLETE);
fpi_drvcb_enroll_stage_completed(dev, 1, fdata, NULL);
break;
}
}
return 0;
}
static int dev_init(struct fp_dev *dev, unsigned long driver_data)
{
synaptics_dev *sdev = NULL;
int result = 0, ret = 0;
fp_info("%s ", __func__);
/* Set enroll stage number */
fpi_dev_set_nr_enroll_stages(dev, ENROLL_SAMPLES);
/* Initialize private structure */
sdev = g_malloc0(sizeof(synaptics_dev));
result = bmkt_init(&(sdev->ctx));
if (result != BMKT_SUCCESS)
{
fp_err("Failed to initialize bmkt context: %d", result);
return -1;
}
fp_info("bmkt_init successfully.");
result = bmkt_open(sdev->ctx, &sdev->sensor, general_error_callback, NULL, fpi_dev_get_usb_dev(dev));
if (result != BMKT_SUCCESS)
{
fp_err("Failed to open bmkt sensor: %d", result);
goto bmkt_cleanup;
}
result = bmkt_register_finger_event_notification(sdev->sensor, finger_event_callback, dev);
if (result != BMKT_SUCCESS)
{
fp_err("Failed to register finger event notification: %d", result);
goto bmkt_cleanup;
}
result = bmkt_init_fps(sdev->sensor);
if (result == BMKT_SUCCESS)
{
fp_info("Successfully initialized the FPS");
}
else if (result == BMKT_OPERATION_DENIED)
{
/* sensor already intialized...allow operations to continue */
fp_info("FPS already initialized");
result = BMKT_SUCCESS;
}
else
{
fp_err("Failed to initialize the FPS: %d", result);
goto bmkt_cleanup;
}
fp_dev_set_instance_data(dev, sdev);
/* Notify open complete */
fpi_drvcb_open_complete(dev, 0);
return result;
bmkt_cleanup:
ret = bmkt_close(sdev->sensor);
if (ret != BMKT_SUCCESS)
{
fp_err("Failed to close bmkt sensor: %d", ret);
goto cleanup;
}
bmkt_exit(sdev->ctx);
g_free(sdev);
cleanup:
fpi_drvcb_open_complete(dev, 1);
return result;
}
static void dev_exit(struct fp_dev *dev)
{
int ret = 0;
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
ret = bmkt_close(sdev->sensor);
if (ret != BMKT_SUCCESS)
{
fp_err("Failed to close bmkt sensor: %d", ret);
return;
}
bmkt_exit(sdev->ctx);
g_free(sdev);
fpi_drvcb_close_complete(dev);
}
static gboolean rand_string(char *str, size_t size)
{
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
srand(time(NULL));
if (size) {
--size;
for (size_t n = 0; n < size; n++) {
int key = rand() % (int) (sizeof charset - 1);
str[n] = charset[key];
}
str[size] = '\0';
}
else
return FALSE;
return TRUE;
}
#define TEMPLATE_ID_SIZE 20
static int del_enrolled_user_resp(bmkt_response_t *resp, void *ctx)
{
bmkt_del_user_resp_t *del_user_resp = &resp->response.del_user_resp;
struct fp_dev *dev=(struct fp_dev *)ctx;
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
switch (resp->response_id)
{
case BMKT_RSP_DELETE_PROGRESS:
fp_info("Deleting Enrolled Users is %d%% complete",
del_user_resp->progress);
break;
case BMKT_RSP_DEL_USER_FP_FAIL:
fp_info("Failed to delete enrolled user: %d", resp->result);
bmkt_op_set_state(sdev->sensor, BMKT_OP_STATE_COMPLETE);
if(sdev->state == SYNA_STATE_DELETE)
{
/* Return result complete when record doesn't exist, otherwise host data
won't be deleted. */
if(resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS)
fpi_drvcb_delete_complete(dev, FP_DELETE_COMPLETE);
else
fpi_drvcb_delete_complete(dev, FP_DELETE_FAIL);
}
break;
case BMKT_RSP_DEL_USER_FP_OK:
fp_info("Successfully deleted enrolled user");
bmkt_op_set_state(sdev->sensor, BMKT_OP_STATE_COMPLETE);
if(sdev->state == SYNA_STATE_DELETE)
{
fpi_drvcb_delete_complete(dev, FP_DELETE_COMPLETE);
}
break;
}
return 0;
}
static int enroll_start(struct fp_dev *dev)
{
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
int result = 0;
char userid[TEMPLATE_ID_SIZE + 1];
fp_info("enroll_start");
rand_string(userid, TEMPLATE_ID_SIZE);
int useridlength =0;
int finger_id;
finger_id = 1;
useridlength = strlen(userid);
sdev->state = SYNA_STATE_ENROLL;
result = bmkt_enroll(sdev->sensor, userid, useridlength,
finger_id, enroll_response, dev);
if (result)
{
fp_err("Failed to enroll finger: %d", result);
}
return 0;
}
static int enroll_stop(struct fp_dev *dev)
{
fp_info("syna enroll stop");
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
sdev->state = SYNA_STATE_IDLE;
fpi_drvcb_enroll_stopped(dev);
return 1;
}
static int verify_response(bmkt_response_t *resp, void *ctx)
{
bmkt_verify_resp_t *verify_resp = &resp->response.verify_resp;
struct fp_dev *dev=(struct fp_dev *)ctx;
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
switch (resp->response_id)
{
case BMKT_RSP_VERIFY_READY:
{
fp_info("Place Finger on the Sensor!");
fpi_drvcb_verify_started(dev, 0);
break;
}
case BMKT_RSP_CAPTURE_COMPLETE:
{
fp_info("Fingerprint image capture complete!");
break;
}
case BMKT_RSP_VERIFY_FAIL:
{
fp_err("Verify has failed!: %d", resp->result);
if(resp->result == BMKT_SENSOR_STIMULUS_ERROR || resp->result == BMKT_FP_NO_MATCH)
{
sdev->state = SYNA_STATE_VERIFY_DELAY_RESULT;
}
else
{
bmkt_op_set_state(sdev->sensor, BMKT_OP_STATE_COMPLETE);
fpi_drvcb_report_verify_result(dev, FP_VERIFY_NO_MATCH, NULL);
}
break;
}
case BMKT_RSP_VERIFY_OK:
{
fp_info("Verify was successful! for user: %s finger: %d score: %f",
verify_resp->user_id, verify_resp->finger_id, verify_resp->match_result);
bmkt_op_set_state(sdev->sensor, BMKT_OP_STATE_COMPLETE);
fpi_drvcb_report_verify_result(dev, FP_VERIFY_MATCH, NULL);
break;
}
}
return 0;
}
static int delete_finger(struct fp_dev *dev)
{
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
int result = 0;
struct fp_print_data *print = fpi_dev_get_delete_data(dev);;
struct fp_print_data_item *item = print->prints->data;
struct syna_mis_print_data *print_data;
bmkt_user_id_t user;
if(item->length != sizeof(struct syna_mis_print_data))
{
fp_err("print data is incorrect !");
goto cleanup;
}
print_data = (struct syna_mis_print_data *)item->data;
memset(&user, 0, sizeof(bmkt_user_id_t));
memcpy(user.user_id, print_data->user_id, sizeof(print_data->user_id));
fp_info("delete finger !");
user.user_id_len = strlen(user.user_id);
if (user.user_id_len <= 0 || user.user_id[0] == ' ')
{
fp_err("Invalid user name.");
goto cleanup;
}
sdev->state = SYNA_STATE_DELETE;
result = bmkt_delete_enrolled_user(sdev->sensor, 1, print_data->user_id,
user.user_id_len, del_enrolled_user_resp, dev);
if (result != BMKT_SUCCESS)
{
fp_err("Failed to delete enrolled user: %d", result);
goto cleanup;
}
return 0;
cleanup:
return -1;
}
static int verify_start(struct fp_dev *dev)
{
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
int result = 0;
struct fp_print_data *print = fpi_dev_get_verify_data(dev);;
struct fp_print_data_item *item = print->prints->data;
struct syna_mis_print_data *print_data;
bmkt_user_id_t user;
if(item->length != sizeof(struct syna_mis_print_data))
{
fp_err("print data is incorrect !");
goto cleanup;
}
print_data = (struct syna_mis_print_data *)item->data;
memset(&user, 0, sizeof(bmkt_user_id_t));
memcpy(user.user_id, print_data->user_id, sizeof(print_data->user_id));
fp_info("syna verify_start !");
user.user_id_len = strlen(user.user_id);
if (user.user_id_len <= 0 || user.user_id[0] == ' ')
{
fp_err("Invalid user name.");
goto cleanup;
}
sdev->state = SYNA_STATE_VERIFY;
result = bmkt_verify(sdev->sensor, &user, verify_response, dev);
if (result != BMKT_SUCCESS)
{
fp_err("Failed to verify finger: %d", result);
}
return 0;
cleanup:
fpi_drvcb_verify_started(dev, 1);
return -1;
}
static int verify_stop(struct fp_dev *dev, gboolean iterating)
{
fp_info("syna verify_stop");
synaptics_dev *sdev = FP_INSTANCE_DATA(dev);
sdev->state = SYNA_STATE_IDLE;
fpi_drvcb_verify_stopped(dev);
return 0;
}
struct fp_driver synaptics_driver = {
.id = SYNAPTICS_ID,
.name = FP_COMPONENT,
.full_name = SYNAPTICS_DRIVER_FULLNAME,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
.open = dev_init,
.close = dev_exit,
.enroll_start = enroll_start,
.enroll_stop = enroll_stop,
.verify_start = verify_start,
.verify_stop = verify_stop,
.delete_finger = delete_finger,
};

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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
*/
#ifndef __synaptics_h__
#define __synaptics_h__
#define SYNAPTICS_VENDOR_ID 0x06cb
#define SYNAPTICS_PRODUCT_ID_A9 0x00a9
/* Number of enroll stages */
#define ENROLL_SAMPLES 12
#define SYNAPTICS_DRIVER_FULLNAME "Synaptics Sensors"
#include "bmkt.h"
#include "bmkt_response.h"
struct syna_enroll_resp_data
{
int progress;
};
typedef enum syna_state
{
SYNA_STATE_UNINIT = 0,
SYNA_STATE_IDLE ,
SYNA_STATE_ENROLL ,
SYNA_STATE_IDENTIFY ,
SYNA_STATE_IDENTIFY_DELAY_RESULT ,
SYNA_STATE_VERIFY ,
SYNA_STATE_VERIFY_DELAY_RESULT ,
SYNA_STATE_DELETE ,
} syna_state_t;
typedef struct synaptics_dev_s
{
bmkt_ctx_t *ctx;
bmkt_sensor_t *sensor;
struct syna_enroll_resp_data enroll_resp_data;
gboolean isFingerOnSensor;
syna_state_t state;
}synaptics_dev;
#endif //__synaptics_h__

View File

@@ -1,386 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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 "bmkt_internal.h"
#include "sensor.h"
#include "drivers_api.h"
#define USB_ASYNC_MESSAGE_PENDING 0x4
static void usb_int_callback(struct libusb_transfer *transfer)
{
bmkt_usb_transport_t *usb_xport = (bmkt_usb_transport_t *)transfer->user_data;
#ifdef TRANSPORT_DEBUG
bmkt_dbg_log("INTERRUPT: (%d) ", transfer->actual_length);
print_buffer(transfer->buffer, transfer->actual_length);
#endif
if (transfer->buffer[0] & USB_ASYNC_MESSAGE_PENDING)
{
libusb_free_transfer(transfer);
bmkt_op_next_state(usb_xport->sensor);
}
else
libusb_submit_transfer(transfer);
}
int usb_check_interrupt(bmkt_usb_transport_t *usb_xport)
{
int ret;
struct libusb_transfer *interrupt_xfer;
interrupt_xfer = libusb_alloc_transfer(0);
if (interrupt_xfer == NULL)
{
return BMKT_GENERAL_ERROR;
}
libusb_fill_interrupt_transfer(interrupt_xfer, usb_xport->handle, USB_EP_INTERRUPT,
usb_xport->interrupt_data, sizeof(usb_xport->interrupt_data), usb_int_callback, usb_xport, 0);
ret = libusb_submit_transfer(interrupt_xfer);
if (ret != LIBUSB_SUCCESS)
{
libusb_free_transfer(interrupt_xfer);
if (ret == LIBUSB_ERROR_NO_DEVICE)
{
return BMKT_SENSOR_MALFUNCTION;
}
else
{
return BMKT_GENERAL_ERROR;
}
}
return BMKT_SUCCESS;
}
int usb_open(bmkt_usb_transport_t *usb_xport)
{
int ret;
struct libusb_config_descriptor *configDesc;
const struct libusb_interface *iface;
const struct libusb_interface_descriptor *ifaceDesc;
const struct libusb_endpoint_descriptor *endpointDesc;
int config;
int i;
usb_xport->device = libusb_get_device(usb_xport->handle);
ret = libusb_reset_device(usb_xport->handle);
if (ret)
{
bmkt_dbg_log("Failed to reset device\n");
}
ret = libusb_get_config_descriptor(usb_xport->device, USB_DEFAULT_CONFIGURATION, &configDesc);
if (ret)
{
ret = BMKT_SENSOR_MALFUNCTION;
return ret;
}
ret = libusb_get_configuration(usb_xport->handle, &config);
if (ret)
{
ret = BMKT_SENSOR_MALFUNCTION;
goto free_config;
}
if (configDesc->bConfigurationValue != config)
{
ret = libusb_set_configuration(usb_xport->handle, config);
if (ret)
{
ret = BMKT_SENSOR_MALFUNCTION;
goto free_config;
}
}
ret = libusb_kernel_driver_active(usb_xport->handle, 0);
if (ret == 1)
{
bmkt_err_log("Failed to detect kernel driver\n");
ret = BMKT_SENSOR_MALFUNCTION;
goto free_config;
}
ret = libusb_claim_interface(usb_xport->handle, USB_DEFAULT_INTERFACE);
if (ret)
{
ret = BMKT_SENSOR_MALFUNCTION;
goto free_config;
}
iface = configDesc->interface + USB_DEFAULT_INTERFACE;
ifaceDesc = iface->altsetting + USB_DEFAULT_ALT_SETTING;
endpointDesc = ifaceDesc->endpoint;
for (i = 0; i < ifaceDesc->bNumEndpoints; i++)
{
ret = libusb_clear_halt(usb_xport->handle, endpointDesc->bEndpointAddress);
if (ret)
{
ret = BMKT_SENSOR_MALFUNCTION;
goto free_config;
}
++endpointDesc;
}
free_config:
libusb_free_config_descriptor(configDesc);
return ret;
}
int usb_close(bmkt_usb_transport_t *usb_xport)
{
if (usb_xport->handle)
{
libusb_release_interface(usb_xport->handle, USB_DEFAULT_INTERFACE);
}
return BMKT_SUCCESS;
}
void usb_in_cb(struct libusb_transfer *transfer)
{
uint8_t *resp_buf;
int resp_len;
bmkt_msg_resp_t msg_resp;
bmkt_usb_transport_t *usb_xport = (bmkt_usb_transport_t *)transfer->user_data;
#ifdef TRANSPORT_DEBUG
bmkt_dbg_log("RX_ASYNC: (%d) ", transfer->actual_length);
print_buffer(transfer->buffer, transfer->actual_length);
#endif
resp_buf = transfer->buffer;
resp_len = transfer->actual_length;
bmkt_sensor_handle_response(usb_xport->sensor, resp_buf, resp_len, &msg_resp);
libusb_free_transfer(transfer);
bmkt_op_next_state(usb_xport->sensor);
}
void usb_out_cb(struct libusb_transfer *transfer)
{
bmkt_usb_transport_t *usb_xport = (bmkt_usb_transport_t *)transfer->user_data;
libusb_free_transfer(transfer);
bmkt_op_next_state(usb_xport->sensor);
}
static int bulk_transfer_async(bmkt_usb_transport_t *usb_xport, uint8_t *buf, int size, uint8_t endpoint,
int *transferred, uint32_t timeout, libusb_transfer_cb_fn callback)
{
int ret;
struct libusb_transfer *transfer;
#ifdef TRANSPORT_DEBUG
if (!(endpoint & 0x80))
{
bmkt_dbg_log("TX2: (%d) ", size);
print_buffer(buf, size);
}
#endif
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer( transfer, usb_xport->handle, endpoint,
buf, size, callback, usb_xport, 0);
ret = libusb_submit_transfer(transfer);
if (ret != LIBUSB_SUCCESS)
{
libusb_free_transfer(transfer);
if (ret == LIBUSB_ERROR_NO_DEVICE)
{
return BMKT_SENSOR_MALFUNCTION;
}
else
{
return BMKT_GENERAL_ERROR;
}
}
return BMKT_SUCCESS;
}
static int bulk_transfer(bmkt_usb_transport_t *usb_xport, uint8_t *buf, int size, uint8_t endpoint,
int *transferred, uint32_t timeout)
{
int ret;
#ifdef TRANSPORT_DEBUG
if (!(endpoint & 0x80))
{
bmkt_dbg_log("TX: (%d) ", size);
print_buffer(buf, size);
}
#endif
ret = libusb_bulk_transfer(usb_xport->handle, endpoint, buf, size, transferred, timeout);
if (ret)
{
bmkt_warn_log("libusb_bulk_transfer: bulk transfer failed: %d\n", ret);
if (ret == LIBUSB_ERROR_TIMEOUT)
{
return BMKT_OP_TIME_OUT;
}
else
{
return BMKT_SENSOR_MALFUNCTION;
}
}
bmkt_dbg_log("transferred: %d\n", *transferred);
#ifdef TRANSPORT_DEBUG
if (endpoint & 0x80)
{
bmkt_dbg_log("RX: (%d) ", *transferred);
print_buffer(buf, *transferred);
}
#endif
return BMKT_SUCCESS;
}
int usb_send_command(bmkt_usb_transport_t *usb_xport, int len)
{
int ret;
int tx_len = 0;
ret = bulk_transfer_async(usb_xport, usb_xport->transfer, len, USB_EP_REQUEST, &tx_len, 0, usb_out_cb);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("Failed to send usb command\n");
return ret;
}
return BMKT_SUCCESS;
}
int usb_get_command_buffer(bmkt_usb_transport_t *usb_xport, uint8_t **cmd, int *len)
{
*len = BMKT_MAX_TRANSFER_LEN;
*cmd = usb_xport->transfer;
return BMKT_SUCCESS;
}
int usb_get_response_buffer(bmkt_usb_transport_t *usb_xport, uint8_t **resp, int *len)
{
*len = BMKT_MAX_TRANSFER_LEN;
*resp = usb_xport->transfer;
return BMKT_SUCCESS;
}
int usb_receive_resp_async(bmkt_usb_transport_t *usb_xport, int *len)
{
int ret;
*len = BMKT_MAX_TRANSFER_LEN;
/* Check to make sure the buffer is clear */
memset(usb_xport->transfer, 0, BMKT_MAX_TRANSFER_LEN);
ret = bulk_transfer_async(usb_xport, usb_xport->transfer, *len, USB_EP_REPLY, len, 0, usb_in_cb);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("Failed to send usb command\n");
return ret;
}
return BMKT_SUCCESS;
}
int usb_receive_resp(bmkt_usb_transport_t *usb_xport, int *len)
{
int ret;
*len = BMKT_MAX_TRANSFER_LEN;
/* Check to make sure the buffer is clear */
memset(usb_xport->transfer, 0, BMKT_MAX_TRANSFER_LEN);
ret = bulk_transfer(usb_xport, usb_xport->transfer, *len, USB_EP_REPLY, len, 0);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("Failed to send usb command\n");
return ret;
}
return BMKT_SUCCESS;
}
int usb_send_command_sync(bmkt_usb_transport_t *usb_xport, int len, uint8_t **resp_buf,
int *resp_len)
{
int ret;
int tx_len = 0;
ret = bulk_transfer(usb_xport, usb_xport->transfer, len, USB_EP_REQUEST, &tx_len, 0);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("Failed to send usb command\n");
return ret;
}
/* Check to make sure the buffer is clear */
memset(usb_xport->transfer, 0, BMKT_MAX_TRANSFER_LEN);
ret = bulk_transfer(usb_xport, usb_xport->transfer, *resp_len, USB_EP_REPLY, resp_len, 0);
if (ret != BMKT_SUCCESS)
{
bmkt_dbg_log("Failed to send usb command\n");
return ret;
}
*resp_buf = usb_xport->transfer;
return BMKT_SUCCESS;
}
int usb_reset(bmkt_usb_transport_t *usb_xport)
{
return BMKT_OPERATION_DENIED;
}
int usb_release_command_buffer(bmkt_usb_transport_t *usb_xport)
{
return BMKT_SUCCESS;
}
int usb_release_response_buffer(bmkt_usb_transport_t *usb_xport)
{
return BMKT_SUCCESS;
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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
*/
#ifndef _USB_TRANSPORT_H_
#define _USB_TRANSPORT_H_
#include "bmkt_internal.h"
#include "libusb-1.0/libusb.h"
#define BMKT_MAX_TRANSFER_LEN 263 + 1 /* SPI Header */ + 2 /* VCSFW header */
#define BMKT_XPORT_INT_NONE 0x0
#define BMKT_XPORT_INT_RESPONSE 0x1
#define BMKT_XPORT_INT_FINGER 0x2
#define BMKT_XPORT_INT_ASYNC 0x4
#define USB_DEFAULT_CONFIGURATION 0
#define USB_DEFAULT_INTERFACE 0
#define USB_DEFAULT_ALT_SETTING 0
#define USB_EP_REQUEST 0x01
#define USB_EP_REPLY 0x81
#define USB_EP_FINGERPRINT 0x82
#define USB_EP_INTERRUPT 0x83
#define USB_INTERRUPT_DATA_SIZE 7
typedef struct bmkt_usb_transport
{
libusb_context *ctx;
libusb_device *device;
libusb_device_handle *handle;
uint8_t interrupt_data[USB_INTERRUPT_DATA_SIZE];
bmkt_sensor_t *sensor;
uint8_t transfer[BMKT_MAX_TRANSFER_LEN];
} bmkt_usb_transport_t;
int usb_release_command_buffer(bmkt_usb_transport_t *xport);
int usb_release_response_buffer(bmkt_usb_transport_t *xport);
int usb_open(bmkt_usb_transport_t *xport);
int usb_close(bmkt_usb_transport_t *xport);
int usb_send_command(bmkt_usb_transport_t *xport, int len);
int usb_get_command_buffer(bmkt_usb_transport_t *xport, uint8_t **cmd, int *len);
int usb_get_response_buffer(bmkt_usb_transport_t *xport, uint8_t **resp, int *len);
int usb_receive_resp(bmkt_usb_transport_t *xport, int *len);
int usb_send_command_sync(bmkt_usb_transport_t *xport, int len, uint8_t **resp_buf,
int *resp_len);
int usb_receive_resp_async(bmkt_usb_transport_t *usb_xport, int *len);
int usb_check_interrupt(bmkt_usb_transport_t *usb_xport);
#endif /* _USB_TRANSPORT_H_ */

View File

@@ -1,87 +0,0 @@
/*
* Copyright (C) 2019 Synaptics Inc
*
* 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 "bmkt_internal.h"
#include "sensor.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-zero-length"
void print_buffer(uint8_t *buf, int len)
{
int i;
for (i = 0; i < len; i++)
{
bmkt_dbg_log("0x%02x ", buf[i]);
if ((i % 16) == 15)
{
bmkt_dbg_log("");
}
}
bmkt_dbg_log("");
}
#pragma GCC diagnostic pop
uint32_t extract32(const uint8_t *buf, int *offset)
{
uint32_t ret = 0;
int off = 0;
if (offset)
{
off = *offset;
}
ret = GUINT32_FROM_LE(*(uint32_t*)(buf + off));
if (offset)
{
*offset += 4;
}
return ret;
}
uint16_t extract16(const uint8_t *buf, int *offset)
{
uint16_t ret = 0;
int off = 0;
if (offset)
{
off = *offset;
}
ret = GUINT16_FROM_LE(*(uint16_t*)(buf + off));
if (offset)
{
*offset += 2;
}
return ret;
}
uint8_t extract8(const uint8_t *buf, int *offset)
{
uint8_t ret = 0;
int off = 0;
if (offset)
{
off = *offset;
}
ret = *(buf + off);
if (offset)
{
*offset += 1;
}
return ret;
}

View File

@@ -1348,9 +1348,10 @@ struct fp_img_driver upeksonly_driver = {
.id = UPEKSONLY_ID, .id = UPEKSONLY_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "UPEK TouchStrip Sensor-Only", .full_name = "UPEK TouchStrip Sensor-Only",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
.discover = dev_discover, .usb_discover = dev_discover,
}, },
.flags = 0, .flags = 0,
.img_width = -1, .img_width = -1,

View File

@@ -463,7 +463,8 @@ struct fp_img_driver upektc_driver = {
.id = UPEKTC_ID, .id = UPEKTC_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "UPEK TouchChip/Eikon Touch 300", .full_name = "UPEK TouchChip/Eikon Touch 300",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_PRESS, .scan_type = FP_SCAN_TYPE_PRESS,
}, },
.flags = 0, .flags = 0,

View File

@@ -631,9 +631,10 @@ struct fp_img_driver upektc_img_driver = {
.id = UPEKTC_IMG_ID, .id = UPEKTC_IMG_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "Upek TouchChip Fingerprint Coprocessor", .full_name = "Upek TouchChip Fingerprint Coprocessor",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
.discover = discover, .usb_discover = discover,
}, },
.flags = 0, .flags = 0,
.img_height = IMAGE_HEIGHT, .img_height = IMAGE_HEIGHT,

View File

@@ -1424,7 +1424,8 @@ struct fp_driver upekts_driver = {
.id = UPEKTS_ID, .id = UPEKTS_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "UPEK TouchStrip", .full_name = "UPEK TouchStrip",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
.open = dev_init, .open = dev_init,
.close = dev_exit, .close = dev_exit,

View File

@@ -1431,7 +1431,8 @@ struct fp_img_driver uru4000_driver = {
.id = URU4000_ID, .id = URU4000_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "Digital Persona U.are.U 4000/4000B/4500", .full_name = "Digital Persona U.are.U 4000/4000B/4500",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_PRESS, .scan_type = FP_SCAN_TYPE_PRESS,
}, },
.flags = FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE, .flags = FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE,

View File

@@ -360,7 +360,8 @@ struct fp_img_driver vcom5s_driver = {
.id = VCOM5S_ID, .id = VCOM5S_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "Veridicom 5thSense", .full_name = "Veridicom 5thSense",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_PRESS, .scan_type = FP_SCAN_TYPE_PRESS,
}, },
.flags = 0, .flags = 0,

View File

@@ -773,7 +773,8 @@ struct fp_img_driver vfs0050_driver = {
.id = VFS0050_ID, .id = VFS0050_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "Validity VFS0050", .full_name = "Validity VFS0050",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },

View File

@@ -1529,7 +1529,8 @@ struct fp_img_driver vfs101_driver =
.id = VFS101_ID, .id = VFS101_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "Validity VFS101", .full_name = "Validity VFS101",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },

View File

@@ -271,7 +271,8 @@ struct fp_img_driver vfs301_driver =
.id = VFS301_ID, .id = VFS301_ID,
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "Validity VFS301", .full_name = "Validity VFS301",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },

View File

@@ -890,7 +890,8 @@ struct fp_img_driver vfs5011_driver = {
.id = VFS5011_ID, .id = VFS5011_ID,
.name = "vfs5011", .name = "vfs5011",
.full_name = "Validity VFS5011", .full_name = "Validity VFS5011",
.id_table = id_table, .bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE, .scan_type = FP_SCAN_TYPE_SWIPE,
}, },

View File

@@ -0,0 +1,225 @@
/*
* Virtual driver for image device debugging
*
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.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
*/
/*
* This is a virtual driver to debug the image based drivers. A small
* python script is provided to connect to it via a socket, allowing
* prints to be sent to this device programatically.
* Using this it is possible to test libfprint and fprintd.
*/
#define FP_COMPONENT "virtual_imgdev"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <stdio.h>
#include "drivers_api.h"
struct virt_dev {
fpi_io_condition *socket_io_cond;
fpi_io_condition *client_io_cond;
gint socket_fd;
gint client_fd;
struct fp_img *recv_img;
gssize recv_img_data_bytes;
gssize recv_img_hdr_bytes;
gint recv_img_hdr[2];
};
static void
client_socket_cb (struct fp_dev *dev, int fd, short int events, void *data)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
gboolean nodata = FALSE;
gssize len;
if (!virt->recv_img) {
/* Reading the header, i.e. width/height. */
len = read(fd,
(guint8*)virt->recv_img_hdr + virt->recv_img_hdr_bytes,
sizeof(virt->recv_img_hdr) - virt->recv_img_hdr_bytes);
fp_dbg("Received %zi bytes from client!", len);
if (len > 0) {
virt->recv_img_hdr_bytes += len;
/* Got the full header, create an image for further processing. */
if (virt->recv_img_hdr_bytes == sizeof(virt->recv_img_hdr)) {
virt->recv_img_data_bytes = 0;
virt->recv_img = fpi_img_new (virt->recv_img_hdr[0] * virt->recv_img_hdr[1]);
virt->recv_img->width = virt->recv_img_hdr[0];
virt->recv_img->height = virt->recv_img_hdr[1];
virt->recv_img->flags = 0;
}
}
} else {
len = read(fd,
(guint8*)virt->recv_img->data + virt->recv_img_data_bytes,
virt->recv_img->length - virt->recv_img_data_bytes);
fp_dbg("Received %zi bytes from client!", len);
if (len > 0) {
virt->recv_img_data_bytes += len;
if (virt->recv_img_data_bytes == virt->recv_img->length) {
/* Submit received image to frontend */
fpi_imgdev_report_finger_status (FP_IMG_DEV (dev), TRUE);
fpi_imgdev_image_captured(FP_IMG_DEV (dev), virt->recv_img);
virt->recv_img = NULL;
fpi_imgdev_report_finger_status (FP_IMG_DEV (dev), FALSE);
}
}
}
if (len <= 0) {
fp_dbg("Client disconnected!");
close (virt->client_fd);
virt->client_fd = -1;
virt->recv_img_hdr_bytes = 0;
if (virt->recv_img)
fp_img_free (virt->recv_img);
virt->recv_img = NULL;
fpi_io_condition_remove (virt->client_io_cond);
virt->client_io_cond = NULL;
}
}
static void
new_connection_cb (struct fp_dev *dev, int fd, short int events, void *data)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
int new_client_fd;
new_client_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
fp_dbg("Got a new connection!");
/* Already have a connection, reject this one */
if (virt->client_fd >= 0) {
fp_warn("Rejecting new connection as we already have one!");
close (new_client_fd);
return;
}
virt->client_fd = new_client_fd;
virt->client_io_cond = fpi_io_condition_add (virt->client_fd, POLL_IN, client_socket_cb, dev, NULL);
}
static int
dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
struct virt_dev *virt;
const char *env;
struct sockaddr_un addr = {
.sun_family = AF_UNIX
};
G_DEBUG_HERE();
virt = g_new0(struct virt_dev, 1);
fp_dev_set_instance_data(FP_DEV(dev), virt);
virt->client_fd = -1;
env = fpi_dev_get_virtual_env (FP_DEV (dev));
virt->socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (virt->socket_fd < 0) {
fp_err("Could not create socket: %m");
return virt->socket_fd;
}
strncpy (addr.sun_path, env, sizeof(addr.sun_path) - 1);
unlink(env);
if (bind(virt->socket_fd, &addr, sizeof(struct sockaddr_un)) < 0) {
fp_err("Could not bind address '%s': %m", addr.sun_path);
close (virt->socket_fd);
virt->socket_fd = -1;
return -1;
}
if (listen (virt->socket_fd, 1) < 0) {
fp_err("Could not open socket for listening: %m");
close (virt->socket_fd);
virt->socket_fd = -1;
return -1;
}
virt->socket_io_cond = fpi_io_condition_add (virt->socket_fd, POLL_IN, new_connection_cb, FP_DEV (dev), NULL);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
if (virt->client_fd >= 0) {
fpi_io_condition_remove (virt->client_io_cond);
close (virt->client_fd);
}
if (virt->socket_fd >= 0) {
fpi_io_condition_remove (virt->socket_io_cond);
close (virt->socket_fd);
}
g_free(virt);
fpi_imgdev_close_complete(dev);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
G_DEBUG_HERE();
fpi_imgdev_activate_complete (dev, 0);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
G_DEBUG_HERE();
fpi_imgdev_deactivate_complete (dev);
}
struct fp_img_driver virtual_imgdev_driver = {
.driver = {
.id = VIRTUAL_IMG_ID,
.name = FP_COMPONENT,
.full_name = "Virtual image device for debugging",
.bus = BUS_TYPE_VIRTUAL,
.id_table.virtual_envvar = "FP_VIRTUAL_IMGDEV",
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

@@ -0,0 +1,424 @@
/*
* Virtual match-in-sensor device with internal storage
*
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.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
*/
/*
* This is a virtual driver to debug features that are relevant for
* match-in-sensor (MIS) devices that store data on the sensor itself.
* In this case data needs to be deleted both locally and from the device
* and we should support garbage collection.
*
* The protocol is line based, when a verify/enroll/etc. command is started
* (or is active when connecting) then we send the command and the UUID
* terminated by a newline.
*
* IDLE\n
* VERIFY UUID\n
* ENROLL UUID\n
* DELETE UUID\n (planned)
* LIST (planned)
*
* The other end simply responds with an integer (terminated by newline)
* that matches the internal fprint return codes.
*/
#define FP_COMPONENT "virtual_misdev"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <stdio.h>
#include "drivers_api.h"
#include "fpi-async.h"
#define VIRT_ENROLL_STAGES 1
enum virtdev_state {
STATE_IDLE = 0,
STATE_VERIFY,
STATE_ENROLL,
STATE_DELETE,
};
struct virt_dev {
enum virtdev_state state;
gchar *curr_uuid;
fpi_io_condition *socket_io_cond;
fpi_io_condition *client_io_cond;
gint socket_fd;
gint client_fd;
gssize recv_len;
guchar *recv_buf;
};
static void send_status(struct fp_dev *dev);
static void
handle_response (struct fp_dev *dev, guchar *buf, gssize len)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
gint result = atoi ((gchar*) buf);
switch (virt->state) {
case STATE_IDLE:
fp_info ("Received unexpected status code %i\n", virt->state);
break;
case STATE_VERIFY:
fp_info ("Reporting verify results back %i\n", result);
fpi_drvcb_report_verify_result (dev, result, NULL);
break;
case STATE_ENROLL: {
struct fp_print_data * fdata = NULL;
fp_info ("Reporting enroll results back %i\n", result);
/* If the enroll is "done", then report back the UUID for the print. */
if (result == FP_ENROLL_COMPLETE) {
struct fp_print_data_item *item = NULL;
fdata = fpi_print_data_new (dev);
item = fpi_print_data_item_new(strlen(virt->curr_uuid));
memcpy(item->data, virt->curr_uuid, strlen(virt->curr_uuid));
fpi_print_data_add_item(fdata, item);
}
fpi_drvcb_enroll_stage_completed (dev, result, fdata, NULL);
break;
}
case STATE_DELETE:
fp_info ("Reporting delete results back %i\n", result);
virt->state = STATE_IDLE;
g_free (virt->curr_uuid);
virt->curr_uuid = NULL;
fpi_drvcb_delete_complete (dev, result);
send_status(dev);
break;
default:
g_assert_not_reached();
}
}
static void
send_status(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
gchar *msg = NULL;
if (virt->client_fd < 0)
return;
switch (virt->state) {
case STATE_IDLE:
msg = g_strdup ("IDLE\n");
break;
case STATE_ENROLL:
msg = g_strdup_printf ("ENROLL %s\n", virt->curr_uuid);
break;
case STATE_VERIFY:
msg = g_strdup_printf ("VERIFY %s\n", virt->curr_uuid);
break;
case STATE_DELETE:
msg = g_strdup_printf ("DELETE %s\n", virt->curr_uuid);
break;
}
send(virt->client_fd, msg, strlen(msg), MSG_NOSIGNAL);
g_free (msg);
}
static void
client_socket_cb (struct fp_dev *dev, int fd, short int events, void *data)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
guchar *pos;
guchar buf[512];
gssize len;
len = read(fd, buf, sizeof(buf));
fp_dbg("Received %zi bytes from client!", len);
if (len > 0) {
virt->recv_buf = g_realloc(virt->recv_buf, virt->recv_len + len);
memcpy(virt->recv_buf + virt->recv_len, buf, len);
virt->recv_len += len;
while ((pos = memmem(virt->recv_buf, virt->recv_len, "\n", 1))) {
/* Found a newline, parse the command */
fp_dbg("got a command response! %p %p", virt->recv_buf, pos);
*pos = '\0';
handle_response(dev, virt->recv_buf, pos - virt->recv_buf);
/* And remove the parsed part from the buffer */
virt->recv_len = virt->recv_len - (pos - virt->recv_buf) - 1;
memmove(pos, virt->recv_buf, virt->recv_len);
virt->recv_buf = realloc(virt->recv_buf, virt->recv_len);
}
} else {
fp_dbg("Client disconnected!");
close (virt->client_fd);
virt->client_fd = -1;
fpi_io_condition_remove (virt->client_io_cond);
virt->client_io_cond = NULL;
g_free(virt->recv_buf);
virt->recv_buf = NULL;
virt->recv_len = 0;
}
}
static void
new_connection_cb (struct fp_dev *dev, int fd, short int events, void *data)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
int new_client_fd;
new_client_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
fp_dbg("Got a new connection!");
/* Already have a connection, reject this one */
if (virt->client_fd >= 0) {
fp_warn("Rejecting new connection as we already have one!");
close (new_client_fd);
return;
}
virt->client_fd = new_client_fd;
virt->client_io_cond = fpi_io_condition_add (virt->client_fd, POLL_IN, client_socket_cb, dev, NULL);
send_status(dev);
}
static int
dev_init(struct fp_dev *dev, unsigned long driver_data)
{
struct virt_dev *virt;
const char *env;
struct sockaddr_un addr = {
.sun_family = AF_UNIX
};
G_DEBUG_HERE();
fpi_dev_set_nr_enroll_stages(dev, VIRT_ENROLL_STAGES);
virt = g_new0(struct virt_dev, 1);
fp_dev_set_instance_data(dev, virt);
virt->client_fd = -1;
env = fpi_dev_get_virtual_env (dev);
virt->socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (virt->socket_fd < 0) {
fp_err("Could not create socket: %m");
return virt->socket_fd;
}
strncpy (addr.sun_path, env, sizeof(addr.sun_path) - 1);
unlink(env);
if (bind(virt->socket_fd, &addr, sizeof(struct sockaddr_un)) < 0) {
fp_err("Could not bind address '%s': %m", addr.sun_path);
close (virt->socket_fd);
virt->socket_fd = -1;
return -1;
}
if (listen (virt->socket_fd, 1) < 0) {
fp_err("Could not open socket for listening: %m");
close (virt->socket_fd);
virt->socket_fd = -1;
return -1;
}
virt->socket_io_cond = fpi_io_condition_add (virt->socket_fd, POLL_IN, new_connection_cb, dev, NULL);
fpi_drvcb_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
G_DEBUG_HERE();
if (virt->client_fd >= 0) {
fpi_io_condition_remove (virt->client_io_cond);
close (virt->client_fd);
}
if (virt->socket_fd >= 0) {
fpi_io_condition_remove (virt->socket_io_cond);
close (virt->socket_fd);
}
g_free (virt->curr_uuid);
virt->curr_uuid = NULL;
g_free(virt);
fpi_drvcb_close_complete(dev);
}
static int enroll_start(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
G_DEBUG_HERE();
if (virt->state != STATE_IDLE)
return -1;
g_assert (virt->curr_uuid == NULL);
virt->state = STATE_ENROLL;
virt->curr_uuid = g_uuid_string_random ();
send_status(dev);
fpi_drvcb_enroll_started(dev, 0);
return 0;
}
static int enroll_stop(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
G_DEBUG_HERE();
if (virt->state != STATE_ENROLL)
return -1;
virt->state = STATE_IDLE;
g_free (virt->curr_uuid);
virt->curr_uuid = NULL;
send_status(dev);
fpi_drvcb_enroll_stopped(dev);
return 0;
}
static int verify_start(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
struct fp_print_data *print;
struct fp_print_data_item *item;
G_DEBUG_HERE();
if (virt->state != STATE_IDLE)
return -1;
g_assert (virt->curr_uuid == NULL);
virt->state = STATE_VERIFY;
print = fpi_dev_get_verify_data(dev);
item = fpi_print_data_get_item(print);
/* We expecte a UUID, that means 36 bytes. */
g_assert(item->length == 36);
virt->curr_uuid = g_malloc(37);
virt->curr_uuid[36] = '\0';
memcpy(virt->curr_uuid, item->data, 36);
g_assert(g_uuid_string_is_valid (virt->curr_uuid));
send_status(dev);
fpi_drvcb_verify_started(dev, 0);
return 0;
}
static int verify_stop(struct fp_dev *dev, gboolean iterating)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
G_DEBUG_HERE();
if (virt->state != STATE_VERIFY)
return -1;
virt->state = STATE_IDLE;
g_free (virt->curr_uuid);
virt->curr_uuid = NULL;
send_status(dev);
fpi_drvcb_verify_stopped(dev);
return 0;
}
static int delete_finger(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
struct fp_print_data *print;
struct fp_print_data_item *item;
G_DEBUG_HERE();
if (virt->state != STATE_IDLE)
return -1;
g_assert (virt->curr_uuid == NULL);
virt->state = STATE_DELETE;
print = fpi_dev_get_delete_data(dev);
item = fpi_print_data_get_item(print);
/* We expecte a UUID, that means 36 bytes. */
g_assert(item->length == 36);
virt->curr_uuid = g_malloc(37);
virt->curr_uuid[36] = '\0';
memcpy(virt->curr_uuid, item->data, 36);
g_assert(g_uuid_string_is_valid (virt->curr_uuid));
send_status(dev);
return 0;
}
struct fp_driver virtual_misdev_driver = {
.id = VIRTUAL_MIS_ID,
.name = FP_COMPONENT,
.full_name = "Virtual match-in-sensor device with internal storage",
.bus = BUS_TYPE_VIRTUAL,
.id_table.virtual_envvar = "FP_VIRTUAL_MISDEV",
.scan_type = FP_SCAN_TYPE_PRESS,
.open = dev_init,
.close = dev_deinit,
.enroll_start = enroll_start,
.enroll_stop = enroll_stop,
.verify_start = verify_start,
.verify_stop = verify_stop,
.delete_finger = delete_finger,
};

View File

@@ -93,13 +93,15 @@ struct fp_dev {
int nr_enroll_stages; int nr_enroll_stages;
/* FIXME: This will eventually have a bus type */ enum fp_bus_type bus;
libusb_device_handle *udev; union {
libusb_device_handle *usb;
const char *virtual_env;
int i2c;
} device;
/* read-only to drivers */ /* read-only to drivers */
struct fp_print_data *verify_data; struct fp_print_data *verify_data;
struct fp_print_data *delete_data; struct fp_print_data *delete_data;
/* drivers should not mess with any of the below */ /* drivers should not mess with any of the below */
@@ -164,7 +166,13 @@ struct fp_img_dev {
/* fp_dscv_dev structure definition */ /* fp_dscv_dev structure definition */
struct fp_dscv_dev { struct fp_dscv_dev {
struct libusb_device *udev; enum fp_bus_type bus;
union {
struct libusb_device *usb;
const char *virtual_env;
char *spi_path;
} desc;
struct fp_driver *drv; struct fp_driver *drv;
unsigned long driver_data; unsigned long driver_data;
uint32_t devtype; uint32_t devtype;

View File

@@ -66,7 +66,6 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb call
{ {
struct fp_driver *drv; struct fp_driver *drv;
struct fp_dev *dev; struct fp_dev *dev;
libusb_device_handle *udevh;
int r; int r;
g_return_val_if_fail(ddev != NULL, -ENODEV); g_return_val_if_fail(ddev != NULL, -ENODEV);
@@ -75,20 +74,32 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb call
drv = ddev->drv; drv = ddev->drv;
G_DEBUG_HERE(); G_DEBUG_HERE();
r = libusb_open(ddev->udev, &udevh);
if (r < 0) {
fp_err("usb_open failed, error %d", r);
return r;
}
dev = g_malloc0(sizeof(*dev)); dev = g_malloc0(sizeof(*dev));
dev->drv = drv; dev->drv = drv;
dev->udev = udevh; dev->bus = ddev->bus;
dev->__enroll_stage = -1; dev->__enroll_stage = -1;
dev->state = DEV_STATE_INITIALIZING; dev->state = DEV_STATE_INITIALIZING;
dev->open_cb = callback; dev->open_cb = callback;
dev->open_cb_data = user_data; dev->open_cb_data = user_data;
switch (ddev->bus) {
case BUS_TYPE_USB:
r = libusb_open(ddev->desc.usb, &dev->device.usb);
if (r < 0) {
fp_err("usb_open failed, error %d", r);
g_free (dev);
return r;
}
break;
case BUS_TYPE_SPI:
/* TODO: Implement */
break;
case BUS_TYPE_VIRTUAL:
dev->device.virtual_env = ddev->desc.virtual_env;
break;
}
if (!drv->open) { if (!drv->open) {
fpi_drvcb_open_complete(dev, 0); fpi_drvcb_open_complete(dev, 0);
return 0; return 0;
@@ -98,7 +109,14 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb call
r = drv->open(dev, ddev->driver_data); r = drv->open(dev, ddev->driver_data);
if (r) { if (r) {
fp_err("device initialisation failed, driver=%s", drv->name); fp_err("device initialisation failed, driver=%s", drv->name);
libusb_close(udevh); switch (ddev->bus) {
case BUS_TYPE_USB:
libusb_close(dev->device.usb);
case BUS_TYPE_SPI:
case BUS_TYPE_VIRTUAL:
/* Nothing to do (this might change for SPI) */
break;
}
g_free(dev); g_free(dev);
} }
@@ -112,7 +130,16 @@ void fpi_drvcb_close_complete(struct fp_dev *dev)
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING); BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
dev->state = DEV_STATE_DEINITIALIZED; dev->state = DEV_STATE_DEINITIALIZED;
fpi_timeout_cancel_all_for_dev(dev); fpi_timeout_cancel_all_for_dev(dev);
libusb_close(dev->udev);
switch (dev->bus) {
case BUS_TYPE_USB:
libusb_close(dev->device.usb);
case BUS_TYPE_SPI:
case BUS_TYPE_VIRTUAL:
/* Nothing to do (this might change for SPI) */
break;
}
if (dev->close_cb) if (dev->close_cb)
dev->close_cb(dev, dev->close_cb_data); dev->close_cb(dev, dev->close_cb_data);
g_free(dev); g_free(dev);
@@ -692,7 +719,6 @@ API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
* *
* Returns: 0 on success, non-zero on error * Returns: 0 on success, non-zero on error
*/ */
API_EXPORTED int fp_async_delete_finger(struct fp_dev *dev, API_EXPORTED int fp_async_delete_finger(struct fp_dev *dev,
struct fp_print_data *data, fp_delete_cb callback, void *user_data) struct fp_print_data *data, fp_delete_cb callback, void *user_data)
{ {
@@ -721,17 +747,14 @@ API_EXPORTED int fp_async_delete_finger(struct fp_dev *dev,
} }
return r; return r;
} }
/* Drivers call this when delete done */ /* Drivers call this when delete done */
void fpi_drvcb_delete_complete(struct fp_dev *dev, int status) void fpi_drvcb_delete_complete(struct fp_dev *dev, int status)
{ {
fp_dbg("status %d", status); fp_dbg("status %d", status);
BUG_ON(dev->state != DEV_STATE_DELETING); BUG_ON(dev->state != DEV_STATE_DELETING);
dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_DELETE_DONE; dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_DELETE_DONE;
if (dev->delete_cb) if (dev->delete_cb)
dev->delete_cb(dev, status, dev->delete_cb_data); dev->delete_cb(dev, status, dev->delete_cb_data);
} }

View File

@@ -38,5 +38,4 @@ void fpi_drvcb_verify_stopped(struct fp_dev *dev);
void fpi_drvcb_delete_complete(struct fp_dev *dev, int status); void fpi_drvcb_delete_complete(struct fp_dev *dev, int status);
#endif #endif

View File

@@ -181,7 +181,7 @@ API_EXPORTED struct fp_driver **fprint_get_drivers (void)
return (struct fp_driver **) g_ptr_array_free (array, FALSE); return (struct fp_driver **) g_ptr_array_free (array, FALSE);
} }
static struct fp_driver *find_supporting_driver(libusb_device *udev, static struct fp_driver *find_supporting_usb_driver(libusb_device *udev,
const struct usb_id **usb_id, uint32_t *devtype) const struct usb_id **usb_id, uint32_t *devtype)
{ {
int ret; int ret;
@@ -207,10 +207,13 @@ static struct fp_driver *find_supporting_driver(libusb_device *udev,
uint32_t type = 0; uint32_t type = 0;
const struct usb_id *id; const struct usb_id *id;
for (id = drv->id_table; id->vendor; id++) { if (drv->bus != BUS_TYPE_USB)
continue;
for (id = drv->id_table.usb; id->vendor; id++) {
if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) { if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) {
if (drv->discover) { if (drv->usb_discover) {
int r = drv->discover(&dsc, &type); int r = drv->usb_discover(&dsc, &type);
if (r < 0) if (r < 0)
fp_err("%s discover failed, code %d", drv->name, r); fp_err("%s discover failed, code %d", drv->name, r);
if (r <= 0) if (r <= 0)
@@ -246,26 +249,81 @@ static struct fp_driver *find_supporting_driver(libusb_device *udev,
return best_drv; return best_drv;
} }
static struct fp_dscv_dev *discover_dev(libusb_device *udev) static struct fp_dscv_dev *discover_usb_dev(libusb_device *udev)
{ {
const struct usb_id *usb_id; const struct usb_id *usb_id;
struct fp_driver *drv; struct fp_driver *drv;
struct fp_dscv_dev *ddev; struct fp_dscv_dev *ddev;
uint32_t devtype; uint32_t devtype;
drv = find_supporting_driver(udev, &usb_id, &devtype); drv = find_supporting_usb_driver(udev, &usb_id, &devtype);
if (!drv) if (!drv)
return NULL; return NULL;
ddev = g_malloc0(sizeof(*ddev)); ddev = g_malloc0(sizeof(*ddev));
ddev->drv = drv; ddev->drv = drv;
ddev->udev = udev; ddev->bus = BUS_TYPE_USB;
ddev->desc.usb = udev;
ddev->driver_data = usb_id->driver_data; ddev->driver_data = usb_id->driver_data;
ddev->devtype = devtype; ddev->devtype = devtype;
return ddev; return ddev;
} }
static void discover_usb_devs(GPtrArray *found_devices)
{
libusb_device *udev;
libusb_device **devs;
int r;
int i = 0;
r = libusb_get_device_list(fpi_usb_ctx, &devs);
if (r < 0) {
fp_err("couldn't enumerate USB devices, error %d", r);
return;
}
/* Check each device against each driver, temporarily storing successfully
* discovered devices in a GPtrArray. */
while ((udev = devs[i++]) != NULL) {
struct fp_dscv_dev *ddev = discover_usb_dev(udev);
if (!ddev)
continue;
/* discover_usb_dev() doesn't hold a reference to the udev,
* so increase the reference for ddev to hold this ref */
libusb_ref_device(udev);
g_ptr_array_add (found_devices, (gpointer) ddev);
}
libusb_free_device_list(devs, 1);
}
static void discover_virtual_devs(GPtrArray *found_devices)
{
GSList *elem;
for (elem = registered_drivers; elem; elem = g_slist_next(elem)) {
struct fp_driver *drv = elem->data;
struct fp_dscv_dev *ddev = NULL;
const gchar *var;
if (drv->bus != BUS_TYPE_VIRTUAL)
continue;
var = g_getenv (drv->id_table.virtual_envvar);
if (var == NULL)
continue;
ddev = g_malloc0(sizeof(*ddev));
ddev->drv = drv;
ddev->bus = BUS_TYPE_VIRTUAL;
ddev->desc.virtual_env = var;
ddev->devtype = 0;
g_ptr_array_add (found_devices, ddev);
}
}
/** /**
* fp_discover_devs: * fp_discover_devs:
* *
@@ -279,39 +337,25 @@ static struct fp_dscv_dev *discover_dev(libusb_device *udev)
*/ */
API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void) API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void)
{ {
GPtrArray *tmparray; GPtrArray *found_devices;
libusb_device *udev;
libusb_device **devs;
int r;
int i = 0;
g_return_val_if_fail (registered_drivers != NULL, NULL); g_return_val_if_fail (registered_drivers != NULL, NULL);
r = libusb_get_device_list(fpi_usb_ctx, &devs); found_devices = g_ptr_array_new ();
if (r < 0) {
fp_err("couldn't enumerate USB devices, error %d", r); discover_usb_devs (found_devices);
discover_virtual_devs (found_devices);
/* Return NULL if no devices were found. */
if (found_devices->len == 0) {
g_ptr_array_free (found_devices, TRUE);
return NULL; return NULL;
} }
tmparray = g_ptr_array_new ();
/* Check each device against each driver, temporarily storing successfully
* discovered devices in a GPtrArray. */
while ((udev = devs[i++]) != NULL) {
struct fp_dscv_dev *ddev = discover_dev(udev);
if (!ddev)
continue;
/* discover_dev() doesn't hold a reference to the udev,
* so increase the reference for ddev to hold this ref */
libusb_ref_device(udev);
g_ptr_array_add (tmparray, (gpointer) ddev);
}
libusb_free_device_list(devs, 1);
/* Convert our temporary array into a standard NULL-terminated pointer /* Convert our temporary array into a standard NULL-terminated pointer
* array. */ * array. */
g_ptr_array_add (tmparray, NULL); g_ptr_array_add (found_devices, NULL);
return (struct fp_dscv_dev **) g_ptr_array_free (tmparray, FALSE); return (struct fp_dscv_dev **) g_ptr_array_free (found_devices, FALSE);
} }
/** /**
@@ -330,7 +374,17 @@ API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs)
return; return;
for (i = 0; devs[i]; i++) { for (i = 0; devs[i]; i++) {
libusb_unref_device(devs[i]->udev); switch (devs[i]->bus) {
case BUS_TYPE_USB:
libusb_unref_device(devs[i]->desc.usb);
break;
case BUS_TYPE_SPI:
g_free(devs[i]->desc.spi_path);
break;
case BUS_TYPE_VIRTUAL:
/* Nothing to do */
break;
}
g_free(devs[i]); g_free(devs[i]);
} }
g_free(devs); g_free(devs);
@@ -714,10 +768,12 @@ API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
*/ */
API_EXPORTED int fp_dev_supports_data_in_sensor(struct fp_dev *dev) API_EXPORTED int fp_dev_supports_data_in_sensor(struct fp_dev *dev)
{ {
g_return_val_if_fail(dev, 0);
g_return_val_if_fail(dev->drv, 0);
return dev->drv->delete_finger != NULL; return dev->drv->delete_finger != NULL;
} }
/** /**
* fp_dev_get_img_width: * fp_dev_get_img_width:
* @dev: the struct #fp_dev device * @dev: the struct #fp_dev device

View File

@@ -67,16 +67,37 @@ enum fp_driver_type {
DRIVER_IMAGING = 1, DRIVER_IMAGING = 1,
}; };
/**
* fp_bus_type:
* @BUS_TYPE_USB: USB device
* @BUS_TYPE_SPI: SPI device
* @BUS_TYPE_VIRTUAL: Virtual test bus
*
* The bus type of the device/driver.
*/
enum fp_bus_type {
BUS_TYPE_USB,
BUS_TYPE_SPI,
BUS_TYPE_VIRTUAL
};
struct fp_driver { struct fp_driver {
const uint16_t id; const uint16_t id;
const char *name; const char *name;
const char *full_name; const char *full_name;
const struct usb_id * const id_table;
enum fp_bus_type bus;
union {
const struct usb_id * const usb;
const char * const *i2c;
const char * virtual_envvar;
} id_table;
enum fp_driver_type type; enum fp_driver_type type;
enum fp_scan_type scan_type; enum fp_scan_type scan_type;
/* Device operations */ /* Device operations */
int (*discover)(struct libusb_device_descriptor *dsc, uint32_t *devtype); int (*usb_discover)(struct libusb_device_descriptor *dsc, uint32_t *devtype);
int (*open)(struct fp_dev *dev, unsigned long driver_data); int (*open)(struct fp_dev *dev, unsigned long driver_data);
void (*close)(struct fp_dev *dev); void (*close)(struct fp_dev *dev);
int (*enroll_start)(struct fp_dev *dev); int (*enroll_start)(struct fp_dev *dev);

View File

@@ -114,7 +114,34 @@ FP_INSTANCE_DATA (struct fp_dev *dev)
libusb_device_handle * libusb_device_handle *
fpi_dev_get_usb_dev(struct fp_dev *dev) fpi_dev_get_usb_dev(struct fp_dev *dev)
{ {
return dev->udev; g_assert (dev->bus == BUS_TYPE_USB);
return dev->device.usb;
}
/**
* fpi_dev_get_virtual_env:
* @dev: a struct #fp_dev
*
* Returns the value of the environment variable that is assicated with
* the virtual device.
*
* Returns: the value of the environment variable
*/
const char *
fpi_dev_get_virtual_env(struct fp_dev *dev)
{
g_assert (dev->bus == BUS_TYPE_VIRTUAL);
return dev->device.virtual_env;
}
int
fpi_dev_get_spi_dev(struct fp_dev *dev)
{
g_assert (dev->bus == BUS_TYPE_SPI);
return dev->device.i2c;
} }
/** /**
@@ -154,6 +181,7 @@ fpi_dev_get_verify_data(struct fp_dev *dev)
* @dev: a struct #fp_dev * @dev: a struct #fp_dev
* *
* Returns the delete data associated with @dev. * Returns the delete data associated with @dev.
*
* Returns: a struct #fp_print_data pointer or %NULL * Returns: a struct #fp_print_data pointer or %NULL
*/ */
struct fp_print_data * struct fp_print_data *
@@ -161,4 +189,3 @@ fpi_dev_get_delete_data(struct fp_dev *dev)
{ {
return dev->delete_data; return dev->delete_data;
} }

View File

@@ -40,10 +40,11 @@ void fp_dev_set_instance_data (struct fp_dev *dev,
void *FP_INSTANCE_DATA (struct fp_dev *dev); void *FP_INSTANCE_DATA (struct fp_dev *dev);
libusb_device_handle *fpi_dev_get_usb_dev(struct fp_dev *dev); libusb_device_handle *fpi_dev_get_usb_dev(struct fp_dev *dev);
const char *fpi_dev_get_virtual_env(struct fp_dev *dev);
int fpi_dev_get_spi_dev(struct fp_dev *dev);
void fpi_dev_set_nr_enroll_stages(struct fp_dev *dev, void fpi_dev_set_nr_enroll_stages(struct fp_dev *dev,
int nr_enroll_stages); int nr_enroll_stages);
struct fp_print_data *fpi_dev_get_verify_data(struct fp_dev *dev); struct fp_print_data *fpi_dev_get_verify_data(struct fp_dev *dev);
struct fp_print_data *fpi_dev_get_delete_data(struct fp_dev *dev); struct fp_print_data *fpi_dev_get_delete_data(struct fp_dev *dev);
#endif #endif

View File

@@ -28,6 +28,7 @@
#include <sys/time.h> #include <sys/time.h>
#include <glib.h> #include <glib.h>
#include <glib-unix.h>
#include <libusb.h> #include <libusb.h>
/** /**
@@ -75,64 +76,38 @@
* for example. * for example.
*/ */
/* this is a singly-linked list of pending timers, sorted with the timer that static GMainContext *fpi_main_ctx = NULL;
* is expiring soonest at the head. */
static GSList *active_timers = NULL; static GSList *active_timers = NULL;
/* notifiers for added or removed poll fds */ /* notifiers for added or removed poll fds */
static fp_pollfd_added_cb fd_added_cb = NULL; static fp_pollfd_added_cb fd_added_cb = NULL;
static fp_pollfd_removed_cb fd_removed_cb = NULL; static fp_pollfd_removed_cb fd_removed_cb = NULL;
struct fpi_timeout { struct fpi_timeout {
struct timeval expiry;
fpi_timeout_fn callback; fpi_timeout_fn callback;
struct fp_dev *dev; struct fp_dev *dev;
void *data; void *data;
char *name; GSource *source;
}; };
static int timeout_sort_fn(gconstpointer _a, gconstpointer _b)
{
fpi_timeout *a = (fpi_timeout *) _a;
fpi_timeout *b = (fpi_timeout *) _b;
struct timeval *tv_a = &a->expiry;
struct timeval *tv_b = &b->expiry;
if (timercmp(tv_a, tv_b, <))
return -1;
else if (timercmp(tv_a, tv_b, >))
return 1;
else
return 0;
}
static void static void
fpi_timeout_free(fpi_timeout *timeout) fpi_timeout_destroy (gpointer data)
{ {
if (timeout == NULL) fpi_timeout *timeout = data;
return;
g_free(timeout->name); active_timers = g_slist_remove (active_timers, timeout);
g_free(timeout); g_free (timeout);
} }
/** static gboolean
* fpi_timeout_set_name: fpi_timeout_wrapper_cb (gpointer data)
* @timeout: a #fpi_timeout
* @name: the name to give the timeout
*
* Sets a name for a timeout, allowing that name to be printed
* along with any timeout related debug.
*/
void
fpi_timeout_set_name(fpi_timeout *timeout,
const char *name)
{ {
g_return_if_fail (timeout != NULL); fpi_timeout *timeout = (fpi_timeout*) data;
g_return_if_fail (name != NULL);
g_return_if_fail (timeout->name == NULL);
timeout->name = g_strdup(name); timeout->callback (timeout->dev, timeout->data);
return G_SOURCE_REMOVE;
} }
/** /**
@@ -154,45 +129,39 @@ fpi_timeout_set_name(fpi_timeout *timeout,
* *
* Returns: an #fpi_timeout structure * Returns: an #fpi_timeout structure
*/ */
fpi_timeout *fpi_timeout_add(unsigned int msec, fpi_timeout *
fpi_timeout_add(unsigned int msec,
fpi_timeout_fn callback, fpi_timeout_fn callback,
struct fp_dev *dev, struct fp_dev *dev,
void *data) void *data)
{ {
struct timespec ts;
struct timeval add_msec;
fpi_timeout *timeout; fpi_timeout *timeout;
int r;
g_return_val_if_fail (dev != NULL, NULL); timeout = g_new0 (fpi_timeout, 1);
timeout->source = g_timeout_source_new (msec);
active_timers = g_slist_prepend (active_timers, timeout);
fp_dbg("in %dms", msec); g_source_set_callback (timeout->source, fpi_timeout_wrapper_cb, timeout, fpi_timeout_destroy);
g_source_attach (timeout->source, fpi_main_ctx);
r = clock_gettime(CLOCK_MONOTONIC, &ts);
if (r < 0) {
fp_err("failed to read monotonic clock, errno=%d", errno);
BUG();
return NULL;
}
timeout = g_new0(fpi_timeout, 1);
timeout->callback = callback;
timeout->dev = dev;
timeout->data = data;
TIMESPEC_TO_TIMEVAL(&timeout->expiry, &ts);
/* calculate timeout expiry by adding delay to current monotonic clock */
timerclear(&add_msec);
add_msec.tv_sec = msec / 1000;
add_msec.tv_usec = (msec % 1000) * 1000;
timeradd(&timeout->expiry, &add_msec, &timeout->expiry);
active_timers = g_slist_insert_sorted(active_timers, timeout,
timeout_sort_fn);
return timeout; return timeout;
} }
/**
* fpi_timeout_set_name:
* @timeout: a #fpi_timeout
* @name: the name to give the timeout
*
* Sets a name for a timeout, allowing that name to be printed
* along with any timeout related debug.
*/
void
fpi_timeout_set_name(fpi_timeout *timeout,
const char *name)
{
g_source_set_name (timeout->source, name);
}
/** /**
* fpi_timeout_cancel: * fpi_timeout_cancel:
* @timeout: an #fpi_timeout structure * @timeout: an #fpi_timeout structure
@@ -200,81 +169,110 @@ fpi_timeout *fpi_timeout_add(unsigned int msec,
* Cancels a timeout scheduled with fpi_timeout_add(), and frees the * Cancels a timeout scheduled with fpi_timeout_add(), and frees the
* @timeout structure. * @timeout structure.
*/ */
void fpi_timeout_cancel(fpi_timeout *timeout) void
fpi_timeout_cancel(fpi_timeout *timeout)
{ {
G_DEBUG_HERE(); g_source_destroy (timeout->source);
active_timers = g_slist_remove(active_timers, timeout);
fpi_timeout_free(timeout);
} }
/* get the expiry time and optionally the timeout structure for the next struct fpi_io_condition {
* timeout. returns 0 if there are no expired timers, or 1 if the fpi_io_condition_fn callback;
* timeval/timeout output parameters were populated. if the returned timeval int fd;
* is zero then it means the timeout has already expired and should be handled struct fp_dev *dev;
* ASAP. */ void *data;
static int get_next_timeout_expiry(struct timeval *out, GSource *source;
struct fpi_timeout **out_timeout) };
static gboolean
fpi_io_condition_wrapper_cb (int fd, GIOCondition cond, gpointer data)
{ {
struct timespec ts; fpi_io_condition *io_cond = data;
struct timeval tv; short events = 0;
struct fpi_timeout *next_timeout;
int r;
if (active_timers == NULL) if (cond & G_IO_IN)
return 0; events |= POLL_IN;
if (cond & G_IO_OUT)
events |= POLL_OUT;
if (cond & G_IO_PRI)
events |= POLL_PRI;
if (cond & G_IO_ERR)
events |= POLL_ERR;
if (cond & G_IO_HUP)
events |= POLL_HUP;
r = clock_gettime(CLOCK_MONOTONIC, &ts); io_cond->callback (io_cond->dev, fd, cond, io_cond->data);
if (r < 0) {
fp_err("failed to read monotonic clock, errno=%d", errno);
return r;
}
TIMESPEC_TO_TIMEVAL(&tv, &ts);
next_timeout = active_timers->data; return G_SOURCE_CONTINUE;
if (out_timeout)
*out_timeout = next_timeout;
if (timercmp(&tv, &next_timeout->expiry, >=)) {
if (next_timeout->name)
fp_dbg("first timeout '%s' already expired", next_timeout->name);
else
fp_dbg("first timeout already expired");
timerclear(out);
} else {
timersub(&next_timeout->expiry, &tv, out);
if (next_timeout->name)
fp_dbg("next timeout '%s' in %ld.%06lds", next_timeout->name,
out->tv_sec, out->tv_usec);
else
fp_dbg("next timeout in %ld.%06lds", out->tv_sec, out->tv_usec);
}
return 1;
} }
/* handle a timeout that has expired */ static void
static void handle_timeout(struct fpi_timeout *timeout) fpi_io_condition_destroy (gpointer data)
{ {
G_DEBUG_HERE(); fpi_io_condition *io_cond = data;
timeout->callback(timeout->dev, timeout->data);
active_timers = g_slist_remove(active_timers, timeout); if (fd_removed_cb)
fpi_timeout_free(timeout); fd_removed_cb(io_cond->fd);
g_free (io_cond);
} }
static int handle_timeouts(void) fpi_io_condition *
fpi_io_condition_add(int fd,
short int events,
fpi_io_condition_fn callback,
struct fp_dev *dev,
void *data)
{ {
struct timeval next_timeout_expiry; fpi_io_condition *io_cond;
struct fpi_timeout *next_timeout; GIOCondition cond = 0;
int r;
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout); if (events & POLL_IN)
if (r <= 0) cond |= G_IO_IN;
return r; if (events & POLL_OUT)
cond |= G_IO_OUT;
if (events & POLL_PRI)
cond |= G_IO_PRI;
if (events & POLL_ERR)
cond |= G_IO_ERR;
if (events & POLL_HUP)
cond |= G_IO_HUP;
if (!timerisset(&next_timeout_expiry)) io_cond = g_new0 (fpi_io_condition, 1);
handle_timeout(next_timeout); io_cond->source = g_unix_fd_source_new (fd, cond);
io_cond->fd = fd;
io_cond->callback = callback;
io_cond->data = data;
io_cond->dev = dev;
return 0; g_source_set_callback (io_cond->source,
G_SOURCE_FUNC (fpi_io_condition_wrapper_cb),
io_cond,
fpi_io_condition_destroy);
g_source_attach (io_cond->source, fpi_main_ctx);
if (fd_added_cb)
fd_added_cb(fd, events);
return io_cond;
}
void
fpi_io_condition_set_name(fpi_io_condition *io_cond,
const char *name)
{
g_source_set_name (io_cond->source, name);
}
void
fpi_io_condition_remove(fpi_io_condition *io_cond)
{
g_source_destroy(io_cond->source);
}
static gboolean
dummy_cb (gpointer user_data)
{
return G_SOURCE_REMOVE;
} }
/** /**
@@ -290,37 +288,22 @@ static int handle_timeouts(void)
*/ */
API_EXPORTED int fp_handle_events_timeout(struct timeval *timeout) API_EXPORTED int fp_handle_events_timeout(struct timeval *timeout)
{ {
struct timeval next_timeout_expiry; GSource *timeout_source;
struct timeval select_timeout;
struct fpi_timeout *next_timeout;
int r;
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout); if (timeout->tv_sec == 0 && timeout->tv_usec == 0) {
if (r < 0) g_main_context_iteration (fpi_main_ctx, FALSE);
return r;
if (r) {
/* timer already expired? */
if (!timerisset(&next_timeout_expiry)) {
handle_timeout(next_timeout);
return 0; return 0;
} }
/* choose the smallest of next URB timeout or user specified timeout */ /* Register a timeout on the mainloop and then run in blocking mode */
if (timercmp(&next_timeout_expiry, timeout, <)) timeout_source = g_timeout_source_new (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
select_timeout = next_timeout_expiry; g_source_set_name (timeout_source, "fpi poll timeout");
else g_source_set_callback (timeout_source, dummy_cb, NULL, NULL);
select_timeout = *timeout; g_source_attach (timeout_source, fpi_main_ctx);
} else { g_main_context_iteration (fpi_main_ctx, TRUE);
select_timeout = *timeout; g_source_destroy (timeout_source);
}
r = libusb_handle_events_timeout(fpi_usb_ctx, &select_timeout); return 0;
*timeout = select_timeout;
if (r < 0)
return r;
return handle_timeouts();
} }
/** /**
@@ -350,35 +333,37 @@ API_EXPORTED int fp_handle_events(void)
*/ */
API_EXPORTED int fp_get_next_timeout(struct timeval *tv) API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
{ {
struct timeval fprint_timeout = { 0, 0 }; int timeout_;
struct timeval libusb_timeout = { 0, 0 };
int r_fprint;
int r_libusb;
r_fprint = get_next_timeout_expiry(&fprint_timeout, NULL); g_return_val_if_fail (g_main_context_acquire (fpi_main_ctx), 0);
r_libusb = libusb_get_next_timeout(fpi_usb_ctx, &libusb_timeout);
/* if we have no pending timeouts and the same is true for libusb, g_main_context_query (fpi_main_ctx,
* indicate that we have no pending timouts */ G_MININT,
if (r_fprint <= 0 && r_libusb <= 0) &timeout_,
NULL,
0);
if (timeout_ < 0)
return 0; return 0;
/* if fprint have no pending timeouts return libusb timeout */ tv->tv_sec = timeout_ / 1000;
else if (r_fprint == 0) tv->tv_usec = (timeout_ % 1000) * 1000;
*tv = libusb_timeout;
/* if libusb have no pending timeouts return fprint timeout */
else if (r_libusb == 0)
*tv = fprint_timeout;
/* otherwise return the smaller of the 2 timeouts */
else if (timercmp(&fprint_timeout, &libusb_timeout, <))
*tv = fprint_timeout;
else
*tv = libusb_timeout;
return 1; return 1;
} }
typedef struct {
GSource source;
GSList *fds;
} fpi_libusb_source;
typedef struct {
int fd;
gpointer tag;
} fpi_libusb_fd;
static fpi_libusb_source *libusb_source = NULL;
/** /**
* fp_get_pollfds: * fp_get_pollfds:
* @pollfds: output location for a list of pollfds. If non-%NULL, must be * @pollfds: output location for a list of pollfds. If non-%NULL, must be
@@ -394,33 +379,52 @@ API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
*/ */
API_EXPORTED ssize_t fp_get_pollfds(struct fp_pollfd **pollfds) API_EXPORTED ssize_t fp_get_pollfds(struct fp_pollfd **pollfds)
{ {
const struct libusb_pollfd **usbfds; gint timeout_;
const struct libusb_pollfd *usbfd; GPollFD fds_static[16];
struct fp_pollfd *ret; GPollFD *fds = fds_static;
ssize_t cnt = 0; gint n_fds;
size_t i = 0; int i;
g_return_val_if_fail (fpi_usb_ctx != NULL, -EIO); g_return_val_if_fail (g_main_context_acquire (fpi_main_ctx), -1);
usbfds = libusb_get_pollfds(fpi_usb_ctx); n_fds = g_main_context_query (fpi_main_ctx,
if (!usbfds) { G_MININT,
*pollfds = NULL; &timeout_,
return -EIO; fds,
G_N_ELEMENTS (fds_static));
if (n_fds > G_N_ELEMENTS (fds_static)) {
fds = g_new0 (GPollFD, n_fds);
n_fds = g_main_context_query (fpi_main_ctx,
G_MININT,
&timeout_,
fds,
n_fds);
} }
while ((usbfd = usbfds[i++]) != NULL) g_main_context_release (fpi_main_ctx);
cnt++;
ret = g_malloc(sizeof(struct fp_pollfd) * cnt); *pollfds = g_new0 (struct fp_pollfd, n_fds);
i = 0; for (i = 0; i < n_fds; i++) {
while ((usbfd = usbfds[i]) != NULL) { (*pollfds)[i].fd = fds[i].fd;
ret[i].fd = usbfd->fd;
ret[i].events = usbfd->events; if (fds[i].events & G_IO_IN)
i++; (*pollfds)[i].events |= POLL_IN;
if (fds[i].events & G_IO_OUT)
(*pollfds)[i].events |= POLL_OUT;
if (fds[i].events & G_IO_PRI)
(*pollfds)[i].events |= POLL_PRI;
if (fds[i].events & G_IO_ERR)
(*pollfds)[i].events |= POLL_ERR;
if (fds[i].events & G_IO_HUP)
(*pollfds)[i].events |= POLL_HUP;
} }
*pollfds = ret; if (fds != fds_static)
return cnt; g_free (fds);
return n_fds;
} }
/** /**
@@ -440,30 +444,129 @@ API_EXPORTED void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
static void add_pollfd(int fd, short events, void *user_data) static void add_pollfd(int fd, short events, void *user_data)
{ {
GIOCondition io_cond = 0;
fpi_libusb_fd *data;
gpointer tag;
if (events & POLL_IN)
io_cond |= G_IO_IN;
if (events & POLL_OUT)
io_cond |= G_IO_OUT;
if (events & POLL_PRI)
io_cond |= G_IO_PRI;
if (events & POLL_ERR)
io_cond |= G_IO_ERR;
if (events & POLL_HUP)
io_cond |= G_IO_HUP;
tag = g_source_add_unix_fd (&libusb_source->source, fd, io_cond);
data = g_new0 (fpi_libusb_fd, 1);
data->fd = fd;
data->tag = tag;
libusb_source->fds = g_slist_prepend (libusb_source->fds, data);
if (fd_added_cb) if (fd_added_cb)
fd_added_cb(fd, events); fd_added_cb(fd, events);
} }
static void remove_pollfd(int fd, void *user_data) static void remove_pollfd(int fd, void *user_data)
{ {
GSList *elem = g_slist_find_custom (libusb_source->fds, &fd, g_int_equal);
fpi_libusb_fd *item;
g_return_if_fail (elem != NULL);
item = (fpi_libusb_fd*) elem->data;
g_source_remove_unix_fd (&libusb_source->source, item->tag);
libusb_source->fds = g_slist_remove_link (libusb_source->fds, elem);
g_slist_free (elem);
g_free (item);
if (fd_removed_cb) if (fd_removed_cb)
fd_removed_cb(fd); fd_removed_cb(fd);
} }
static gboolean
fpi_libusb_prepare (GSource *source,
gint *timeout_)
{
struct timeval tv;
*timeout_ = -1;
if (libusb_get_next_timeout(fpi_usb_ctx, &tv) == 1) {
if (tv.tv_sec == 0 && tv.tv_usec == 0)
return TRUE;
*timeout_ = tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
return FALSE;
}
static gboolean
fpi_libusb_check (GSource *source)
{
/* Just call into libusb for every mainloop cycle */
return TRUE;
}
static gboolean
fpi_libusb_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
struct timeval zero_tv = { 0, 0 };
libusb_handle_events_timeout (fpi_usb_ctx, &zero_tv);
return G_SOURCE_CONTINUE;
}
static void
fpi_libusb_finalize (GSource *source)
{
fpi_libusb_source *fpi_source = (fpi_libusb_source*) source;
g_slist_free_full (fpi_source->fds, g_free);
}
GSourceFuncs libusb_source_funcs = {
.prepare = fpi_libusb_prepare,
.check = fpi_libusb_check,
.dispatch = fpi_libusb_dispatch,
.finalize = fpi_libusb_finalize,
};
void fpi_poll_init(void) void fpi_poll_init(void)
{ {
fpi_main_ctx = g_main_context_new ();
libusb_source = (fpi_libusb_source*) g_source_new (&libusb_source_funcs, sizeof(fpi_libusb_source));
g_source_set_name (&libusb_source->source, "libfprint internal libusb source");
g_source_attach (&libusb_source->source, fpi_main_ctx);
libusb_set_pollfd_notifiers(fpi_usb_ctx, add_pollfd, remove_pollfd, NULL); libusb_set_pollfd_notifiers(fpi_usb_ctx, add_pollfd, remove_pollfd, NULL);
} }
void fpi_poll_exit(void) void fpi_poll_exit(void)
{ {
g_slist_free_full(active_timers, (GDestroyNotify) fpi_timeout_free); g_source_destroy (&libusb_source->source);
active_timers = NULL; libusb_source = NULL;
g_main_context_unref (fpi_main_ctx);
fpi_main_ctx = NULL;
fd_added_cb = NULL; fd_added_cb = NULL;
fd_removed_cb = NULL; fd_removed_cb = NULL;
libusb_set_pollfd_notifiers(fpi_usb_ctx, NULL, NULL, NULL); libusb_set_pollfd_notifiers(fpi_usb_ctx, NULL, NULL, NULL);
} }
void void
fpi_timeout_cancel_all_for_dev(struct fp_dev *dev) fpi_timeout_cancel_all_for_dev(struct fp_dev *dev)
{ {
@@ -473,13 +576,10 @@ fpi_timeout_cancel_all_for_dev(struct fp_dev *dev)
l = active_timers; l = active_timers;
while (l) { while (l) {
struct fpi_timeout *timeout = l->data; fpi_timeout *cb_data = l->data;
GSList *current = l;
l = l->next; l = l->next;
if (timeout->dev == dev) { if (cb_data->dev == dev)
g_free (timeout); g_source_destroy (cb_data->source);
active_timers = g_slist_delete_link (active_timers, current);
}
} }
} }

View File

@@ -48,4 +48,34 @@ void fpi_timeout_set_name(fpi_timeout *timeout,
const char *name); const char *name);
void fpi_timeout_cancel(fpi_timeout *timeout); void fpi_timeout_cancel(fpi_timeout *timeout);
/**
* fpi_io_condition_fn:
* @dev: the struct #fp_dev passed to fpi_io_condition_add()
* @fd: the registered file descriptor
* @events: The events that poll returend for the descriptor
* @data: the data passed to fpi_io_condition_add()
*
* The prototype of the callback function for fpi_io_condition_add().
* Note that structure will be free'ed when unregistering the condition.
*/
typedef void (*fpi_io_condition_fn)(struct fp_dev *dev, int fd, short int events, void *data);
/**
* fpi_io_cond:
*
* An opaque structure representing a pollable file descriptor and a
* callback function created with fpi_io_condition_add().
*/
typedef struct fpi_io_condition fpi_io_condition;
fpi_io_condition *fpi_io_condition_add(int fd,
short int events,
fpi_io_condition_fn callback,
struct fp_dev *dev,
void *data);
void fpi_io_condition_set_name(fpi_io_condition *io_cond,
const char *name);
void fpi_io_condition_remove(fpi_io_condition *io_cond);
#endif #endif

View File

@@ -445,6 +445,7 @@ struct sync_delete_data {
gboolean populated; gboolean populated;
int result; int result;
}; };
static void sync_delete_cb(struct fp_dev *dev, int result, void *user_data) static void sync_delete_cb(struct fp_dev *dev, int result, void *user_data)
{ {
struct sync_delete_data *ddata = user_data; struct sync_delete_data *ddata = user_data;
@@ -458,8 +459,8 @@ static void sync_delete_cb(struct fp_dev *dev, int result, void *user_data)
* @enrolled_data: the id need to delete on sensor. This id is * @enrolled_data: the id need to delete on sensor. This id is
* returned in previously enrolled with a MIS device. * returned in previously enrolled with a MIS device.
* *
* Perform a delete data operation on sensor. When print data is stored on sensor, * Perform a delete data operation on sensor. When print data is stored on
* this function is needed when host deletes enrolled finger. * sensor, this function is needed when host deletes enrolled finger.
* *
* Returns: negative code on error, otherwise a code from #fp_delete_result * Returns: negative code on error, otherwise a code from #fp_delete_result
*/ */
@@ -504,9 +505,6 @@ out:
return r; return r;
} }
struct sync_identify_data { struct sync_identify_data {
gboolean populated; gboolean populated;
int result; int result;

View File

@@ -31,10 +31,11 @@ static GList *insert_driver (GList *list,
{ {
int i; int i;
for (i = 0; driver->id_table[i].vendor != 0; i++) { for (i = 0; driver->id_table.usb[i].vendor != 0; i++) {
char *key; char *key;
key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product); key = g_strdup_printf ("%04x:%04x", driver->id_table.usb[i].vendor,
driver->id_table.usb[i].product);
if (g_hash_table_lookup (printed, key) != NULL) { if (g_hash_table_lookup (printed, key) != NULL) {
g_free (key); g_free (key);

View File

@@ -40,7 +40,8 @@ static const struct usb_id blacklist_id_table[] = {
}; };
struct fp_driver whitelist = { struct fp_driver whitelist = {
.id_table = whitelist_id_table, .bus = BUS_TYPE_USB,
.id_table.usb = whitelist_id_table,
.full_name = "Hardcoded whitelist" .full_name = "Hardcoded whitelist"
}; };
@@ -52,13 +53,13 @@ static void print_driver (struct fp_driver *driver)
num_printed = 0; num_printed = 0;
for (i = 0; driver->id_table[i].vendor != 0; i++) { for (i = 0; driver->id_table.usb[i].vendor != 0; i++) {
char *key; char *key;
blacklist = 0; blacklist = 0;
for (j = 0; blacklist_id_table[j].vendor != 0; j++) { for (j = 0; blacklist_id_table[j].vendor != 0; j++) {
if (driver->id_table[i].vendor == blacklist_id_table[j].vendor && if (driver->id_table.usb[i].vendor == blacklist_id_table[j].vendor &&
driver->id_table[i].product == blacklist_id_table[j].product) { driver->id_table.usb[i].product == blacklist_id_table[j].product) {
blacklist = 1; blacklist = 1;
break; break;
} }
@@ -66,7 +67,8 @@ static void print_driver (struct fp_driver *driver)
if (blacklist) if (blacklist)
continue; continue;
key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product); key = g_strdup_printf ("%04x:%04x", driver->id_table.usb[i].vendor,
driver->id_table.usb[i].product);
if (g_hash_table_lookup (printed, key) != NULL) { if (g_hash_table_lookup (printed, key) != NULL) {
g_free (key); g_free (key);
@@ -78,8 +80,12 @@ static void print_driver (struct fp_driver *driver)
if (num_printed == 0) if (num_printed == 0)
printf ("# %s\n", driver->full_name); printf ("# %s\n", driver->full_name);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n", driver->id_table[i].vendor, driver->id_table[i].product); printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n",
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n", driver->id_table[i].vendor, driver->id_table[i].product, driver->full_name); driver->id_table.usb[i].vendor,
driver->id_table.usb[i].product);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n",
driver->id_table.usb[i].vendor,
driver->id_table.usb[i].product, driver->full_name);
num_printed++; num_printed++;
} }

View File

@@ -471,7 +471,6 @@ typedef void (*fp_delete_cb)(struct fp_dev *dev, int status, void *user_data);
int fp_async_delete_finger(struct fp_dev *dev, struct fp_print_data *data, fp_delete_cb callback, void *user_data); int fp_async_delete_finger(struct fp_dev *dev, struct fp_print_data *data, fp_delete_cb callback, void *user_data);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,40 +1,18 @@
libfprint_sources = [ libfprint_sources = [
'fp_internal.h',
'nbis-helpers.h',
'drivers_api.h',
'fpi-async.c', 'fpi-async.c',
'fpi-async.h',
'fpi-assembling.c', 'fpi-assembling.c',
'fpi-assembling.h',
'fpi-core.c', 'fpi-core.c',
'fpi-core.h',
'fpi-data.c', 'fpi-data.c',
'fpi-data.h',
'fpi-dev.c', 'fpi-dev.c',
'fpi-dev.h',
'fpi-dev-img.c', 'fpi-dev-img.c',
'fpi-dev-img.h',
'fpi-img.c', 'fpi-img.c',
'fpi-img.h',
'fpi-log.h',
'fpi-ssm.c', 'fpi-ssm.c',
'fpi-ssm.h',
'fpi-sync.c', 'fpi-sync.c',
'fpi-poll.h',
'fpi-poll.c', 'fpi-poll.c',
'fpi-usb.h',
'fpi-usb.c', 'fpi-usb.c',
'drivers/driver_ids.h',
] ]
nbis_sources = [ nbis_sources = [
'nbis/include/bozorth.h',
'nbis/include/bz_array.h',
'nbis/include/defs.h',
'nbis/include/lfs.h',
'nbis/include/log.h',
'nbis/include/morph.h',
'nbis/include/sunrast.h',
'nbis/bozorth3/bozorth3.c', 'nbis/bozorth3/bozorth3.c',
'nbis/bozorth3/bz_alloc.c', 'nbis/bozorth3/bz_alloc.c',
'nbis/bozorth3/bz_drvrs.c', 'nbis/bozorth3/bz_drvrs.c',
@@ -74,16 +52,15 @@ aesx660 = false
aes3k = false aes3k = false
drivers_sources = [] drivers_sources = []
drivers_cflags = [] drivers_cflags = []
foreach driver: drivers foreach driver: drivers
if driver == 'upekts' if driver == 'upekts'
drivers_sources += [ 'drivers/upekts.c' ] drivers_sources += [ 'drivers/upekts.c', 'drivers/upek_proto.c' ]
endif endif
if driver == 'upektc' if driver == 'upektc'
drivers_sources += [ 'drivers/upektc.c', 'drivers/upektc.h', 'drivers/upek_proto.c', 'drivers/upek_proto.h' ] drivers_sources += [ 'drivers/upektc.c' ]
endif endif
if driver == 'upeksonly' if driver == 'upeksonly'
drivers_sources += [ 'drivers/upeksonly.c', 'drivers/upeksonly.h' ] drivers_sources += [ 'drivers/upeksonly.c' ]
endif endif
if driver == 'uru4000' if driver == 'uru4000'
drivers_sources += [ 'drivers/uru4000.c' ] drivers_sources += [ 'drivers/uru4000.c' ]
@@ -93,20 +70,20 @@ foreach driver: drivers
aeslib = true aeslib = true
endif endif
if driver == 'aes1660' if driver == 'aes1660'
drivers_sources += [ 'drivers/aes1660.c', 'drivers/aes1660.h' ] drivers_sources += [ 'drivers/aes1660.c' ]
aeslib = true aeslib = true
aesx660 = true aesx660 = true
endif endif
if driver == 'aes2501' if driver == 'aes2501'
drivers_sources += [ 'drivers/aes2501.c', 'drivers/aes2501.h' ] drivers_sources += [ 'drivers/aes2501.c' ]
aeslib = true aeslib = true
endif endif
if driver == 'aes2550' if driver == 'aes2550'
drivers_sources += [ 'drivers/aes2550.c', 'drivers/aes2550.h' ] drivers_sources += [ 'drivers/aes2550.c' ]
aeslib = true aeslib = true
endif endif
if driver == 'aes2660' if driver == 'aes2660'
drivers_sources += [ 'drivers/aes2660.c', 'drivers/aes2660.h' ] drivers_sources += [ 'drivers/aes2660.c' ]
aeslib = true aeslib = true
aesx660 = true aesx660 = true
endif endif
@@ -130,43 +107,39 @@ foreach driver: drivers
drivers_sources += [ 'drivers/vfs101.c' ] drivers_sources += [ 'drivers/vfs101.c' ]
endif endif
if driver == 'vfs301' if driver == 'vfs301'
drivers_sources += [ 'drivers/vfs301.c', 'drivers/vfs301_proto.c', 'drivers/vfs301_proto.h', 'drivers/vfs301_proto_fragments.h' ] drivers_sources += [ 'drivers/vfs301.c', 'drivers/vfs301_proto.c' ]
endif endif
if driver == 'vfs5011' if driver == 'vfs5011'
drivers_sources += [ 'drivers/vfs5011.c', 'drivers/vfs5011_proto.h' ] drivers_sources += [ 'drivers/vfs5011.c' ]
endif endif
if driver == 'upektc_img' if driver == 'upektc_img'
drivers_sources += [ 'drivers/upektc_img.c', 'drivers/upektc_img.h', 'drivers/upek_proto.c', 'drivers/upek_proto.h' ] drivers_sources += [ 'drivers/upektc_img.c', 'drivers/upek_proto.c' ]
endif endif
if driver == 'etes603' if driver == 'etes603'
drivers_sources += [ 'drivers/etes603.c' ] drivers_sources += [ 'drivers/etes603.c' ]
endif endif
if driver == 'vfs0050' if driver == 'vfs0050'
drivers_sources += [ 'drivers/vfs0050.c', 'drivers/vfs0050.h' ] drivers_sources += [ 'drivers/vfs0050.c' ]
endif endif
if driver == 'elan' if driver == 'elan'
drivers_sources += [ 'drivers/elan.c', 'drivers/elan.h' ] drivers_sources += [ 'drivers/elan.c' ]
endif endif
if driver == 'synaptics' if driver == 'virtual_imgdev'
drivers_sources += [ drivers_sources += [ 'drivers/virtual_imgdev.c' ]
'drivers/synaptics/synaptics.c', endif
'drivers/synaptics/bmkt.c', if driver == 'virtual_misdev'
'drivers/synaptics/util.c', drivers_sources += [ 'drivers/virtual_misdev.c' ]
'drivers/synaptics/bmkt_message.c',
'drivers/synaptics/sensor.c',
'drivers/synaptics/usb_transport.c',
]
endif endif
endforeach endforeach
if aeslib if aeslib
drivers_sources += [ 'drivers/aeslib.c', 'drivers/aeslib.h' ] drivers_sources += [ 'drivers/aeslib.c' ]
endif endif
if aesx660 if aesx660
drivers_sources += ['drivers/aesx660.c', 'drivers/aesx660.h' ] drivers_sources += ['drivers/aesx660.c' ]
endif endif
if aes3k if aes3k
drivers_sources += ['drivers/aes3k.c', 'drivers/aes3k.h' ] drivers_sources += ['drivers/aes3k.c' ]
endif endif
other_sources = [] other_sources = []
@@ -191,7 +164,6 @@ libfprint_sources += configure_file(input: 'empty_file',
]) ])
deps = [ mathlib_dep, glib_dep, libusb_dep, nss_dep, imaging_dep ] deps = [ mathlib_dep, glib_dep, libusb_dep, nss_dep, imaging_dep ]
libfprint = library('fprint', libfprint = library('fprint',
libfprint_sources + drivers_sources + nbis_sources + other_sources, libfprint_sources + drivers_sources + nbis_sources + other_sources,
soversion: soversion, soversion: soversion,

View File

@@ -45,8 +45,8 @@ mathlib_dep = cc.find_library('m', required: false)
# Drivers # Drivers
drivers = get_option('drivers').split(',') drivers = get_option('drivers').split(',')
all_drivers = [ 'upekts', 'upektc', 'upeksonly', 'vcom5s', 'uru4000', 'aes1610', 'aes1660', 'aes2501', 'aes2550', 'aes2660', 'aes3500', 'aes4000', 'vfs101', 'vfs301', 'vfs5011', 'upektc_img', 'etes603', 'vfs0050', 'elan', 'synaptics' ] all_drivers = [ 'upekts', 'upektc', 'upeksonly', 'vcom5s', 'uru4000', 'aes1610', 'aes1660', 'aes2501', 'aes2550', 'aes2660', 'aes3500', 'aes4000', 'vfs101', 'vfs301', 'vfs5011', 'upektc_img', 'etes603', 'vfs0050', 'elan', 'virtual_imgdev', 'virtual_misdev' ]
primitive_drivers = [ 'upekts', 'synaptics' ] primitive_drivers = [ 'upekts', 'virtual_misdev' ]
if drivers == [ 'all' ] if drivers == [ 'all' ]
drivers = all_drivers drivers = all_drivers