GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 12.2% 17 / 0 / 139
Functions: 18.8% 3 / 0 / 16
Branches: 13.3% 4 / 0 / 30

libfprint/drivers/vcom5s.c
Line Branch Exec Source
1 /*
2 * Veridicom 5thSense driver for libfprint
3 * Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #define FP_COMPONENT "vcom5s"
21
22 #include "drivers_api.h"
23
24 /* TODO:
25 * calibration?
26 * image size: windows gets 300x300 through vpas enrollment util?
27 * (probably just increase bulk read size?)
28 * powerdown? does windows do anything special on exit?
29 */
30
31 #define CTRL_TIMEOUT 1000
32 #define EP_IN (1 | FPI_USB_ENDPOINT_IN)
33
34 #define IMG_WIDTH 300
35 #define IMG_HEIGHT 288
36 #define ROWS_PER_RQ 12
37 #define NR_REQS (IMG_HEIGHT / ROWS_PER_RQ)
38 #define RQ_SIZE (IMG_WIDTH * ROWS_PER_RQ)
39 #define IMG_SIZE (IMG_WIDTH * IMG_HEIGHT)
40
41 struct _FpDeviceVcom5s
42 {
43 FpImageDevice parent;
44
45 int capture_iteration;
46 FpImage *capture_img;
47 gboolean loop_running;
48 gboolean deactivating;
49 };
50 G_DECLARE_FINAL_TYPE (FpDeviceVcom5s, fpi_device_vcom5s, FPI, DEVICE_VCOM5S,
51 FpImageDevice);
52
4/6
fpi_device_vcom5s_class_intern_init:
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 128 times.
fpi_device_vcom5s_get_type:
✓ Branch 2 → 3 taken 128 times.
✓ Branch 2 → 7 taken 24 times.
✓ Branch 4 → 5 taken 128 times.
✗ Branch 4 → 7 not taken.
408 G_DEFINE_TYPE (FpDeviceVcom5s, fpi_device_vcom5s, FP_TYPE_IMAGE_DEVICE);
53
54 enum v5s_regs {
55 /* when using gain 0x29:
56 * a value of 0x00 produces mostly-black image
57 * 0x09 destroys ridges (too white)
58 * 0x01 or 0x02 seem good values */
59 REG_CONTRAST = 0x02,
60
61 /* when using contrast 0x01:
62 * a value of 0x00 will produce an all-black image.
63 * 0x29 produces a good contrast image: ridges quite dark, but some
64 * light grey noise as background
65 * 0x46 produces all-white image with grey ridges (not very dark) */
66 REG_GAIN = 0x03,
67 };
68
69 enum v5s_cmd {
70 /* scan one row. has parameter, at a guess this is which row to scan? */
71 CMD_SCAN_ONE_ROW = 0xc0,
72
73 /* scan whole image */
74 CMD_SCAN = 0xc1,
75 };
76
77 /***** REGISTER I/O *****/
78
79 static void
80 ✗ sm_write_reg (FpiSsm *ssm,
81 FpDevice *dev,
82 unsigned char reg,
83 unsigned char value)
84 {
85 ✗ FpiUsbTransfer *transfer = fpi_usb_transfer_new (dev);
86
87 ✗ fp_dbg ("set %02x=%02x", reg, value);
88 ✗ fpi_usb_transfer_fill_control (transfer,
89 G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
90 G_USB_DEVICE_REQUEST_TYPE_VENDOR,
91 G_USB_DEVICE_RECIPIENT_DEVICE,
92 reg, value, 0, 0);
93 ✗ transfer->ssm = ssm;
94 ✗ fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL,
95 fpi_ssm_usb_transfer_cb, NULL);
96 ✗ }
97
98 static void
99 ✗ sm_exec_cmd (FpiSsm *ssm,
100 FpDevice *dev,
101 unsigned char cmd,
102 unsigned char param)
103 {
104 ✗ FpiUsbTransfer *transfer = fpi_usb_transfer_new (dev);
105
106 ✗ fp_dbg ("cmd %02x param %02x", cmd, param);
107 ✗ fpi_usb_transfer_fill_control (transfer,
108 G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
109 G_USB_DEVICE_REQUEST_TYPE_VENDOR,
110 G_USB_DEVICE_RECIPIENT_DEVICE,
111 cmd, param, 0, 0);
112 ✗ transfer->ssm = ssm;
113 ✗ fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL,
114 fpi_ssm_usb_transfer_cb, NULL);
115 ✗ }
116
117 /***** FINGER DETECTION *****/
118
119 /* We take 64x64 pixels at the center of the image, determine the average
120 * pixel intensity, and threshold it. */
121 #define DETBOX_ROW_START 111
122 #define DETBOX_COL_START 117
123 #define DETBOX_ROWS 64
124 #define DETBOX_COLS 64
125 #define DETBOX_ROW_END (DETBOX_ROW_START + DETBOX_ROWS)
126 #define DETBOX_COL_END (DETBOX_COL_START + DETBOX_COLS)
127 #define FINGER_PRESENCE_THRESHOLD 100
128
129 static gboolean
130 ✗ finger_is_present (unsigned char *data)
131 {
132 ✗ int row;
133 ✗ guint16 imgavg = 0;
134
135 ✗ for (row = DETBOX_ROW_START; row < DETBOX_ROW_END; row++)
136 {
137 ✗ unsigned char *rowdata = data + (row * IMG_WIDTH);
138 ✗ guint16 rowavg = 0;
139 ✗ int col;
140
141 ✗ for (col = DETBOX_COL_START; col < DETBOX_COL_END; col++)
142 ✗ rowavg += rowdata[col];
143 ✗ rowavg /= DETBOX_COLS;
144 ✗ imgavg += rowavg;
145 }
146 ✗ imgavg /= DETBOX_ROWS;
147 ✗ fp_dbg ("img avg %d", imgavg);
148
149 ✗ return imgavg <= FINGER_PRESENCE_THRESHOLD;
150 }
151
152
153
154 /***** IMAGE ACQUISITION *****/
155
156 static void capture_iterate (FpiSsm *ssm,
157 FpDevice *dev);
158
159 static void
160 ✗ capture_cb (FpiUsbTransfer *transfer, FpDevice *device,
161 gpointer user_data, GError *error)
162 {
163 ✗ FpImageDevice *imgdev = FP_IMAGE_DEVICE (device);
164 ✗ FpDeviceVcom5s *self = FPI_DEVICE_VCOM5S (device);
165
166 ✗ if (error)
167 {
168 ✗ fpi_ssm_mark_failed (transfer->ssm, error);
169 ✗ return;
170 }
171
172 ✗ if (++self->capture_iteration == NR_REQS)
173 {
174 ✗ FpImage *img = self->capture_img;
175 /* must clear this early, otherwise the call chain takes us into
176 * loopsm_complete where we would free it, when in fact we are
177 * supposed to be handing off this image */
178 ✗ self->capture_img = NULL;
179
180 ✗ fpi_image_device_report_finger_status (imgdev,
181 ✗ finger_is_present (img->data));
182 ✗ fpi_image_device_image_captured (imgdev, img);
183 ✗ fpi_ssm_next_state (transfer->ssm);
184 }
185 else
186 {
187 ✗ capture_iterate (transfer->ssm, device);
188 }
189 }
190
191 static void
192 ✗ capture_iterate (FpiSsm *ssm,
193 FpDevice *dev)
194 {
195 ✗ FpDeviceVcom5s *self = FPI_DEVICE_VCOM5S (dev);
196 ✗ int iteration = self->capture_iteration;
197 ✗ FpiUsbTransfer *transfer = fpi_usb_transfer_new (FP_DEVICE (dev));
198
199 ✗ transfer->ssm = ssm;
200 ✗ transfer->short_is_error = TRUE;
201 ✗ fpi_usb_transfer_fill_bulk_full (transfer,
202 EP_IN,
203 ✗ self->capture_img->data + (RQ_SIZE * iteration),
204 RQ_SIZE,
205 NULL);
206
207 ✗ fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, capture_cb, NULL);
208 ✗ }
209
210
211 static void
212 ✗ sm_do_capture (FpiSsm *ssm,
213 FpDevice *dev)
214 {
215 ✗ FpDeviceVcom5s *self = FPI_DEVICE_VCOM5S (dev);
216 ✗ FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (dev);
217
218 ✗ G_DEBUG_HERE ();
219 ✗ self->capture_img = fp_image_new (cls->img_width, cls->img_height);
220 ✗ self->capture_iteration = 0;
221 ✗ capture_iterate (ssm, dev);
222 ✗ }
223
224 /***** CAPTURE LOOP *****/
225
226 enum loop_states {
227 LOOP_SET_CONTRAST,
228 LOOP_SET_GAIN,
229 LOOP_CMD_SCAN,
230 LOOP_CAPTURE,
231 LOOP_CAPTURE_DONE,
232 LOOP_NUM_STATES,
233 };
234
235 static void
236 ✗ loop_run_state (FpiSsm *ssm, FpDevice *dev)
237 {
238 ✗ FpDeviceVcom5s *self = FPI_DEVICE_VCOM5S (dev);
239
240 ✗ switch (fpi_ssm_get_cur_state (ssm))
241 {
242 ✗ case LOOP_SET_CONTRAST:
243 ✗ sm_write_reg (ssm, dev, REG_CONTRAST, 0x01);
244 ✗ break;
245
246 ✗ case LOOP_SET_GAIN:
247 ✗ sm_write_reg (ssm, dev, REG_GAIN, 0x29);
248 ✗ break;
249
250 ✗ case LOOP_CMD_SCAN:
251 ✗ if (self->deactivating)
252 {
253 ✗ fp_dbg ("deactivating, marking completed");
254 ✗ fpi_ssm_mark_completed (ssm);
255 }
256 else
257 {
258 ✗ sm_exec_cmd (ssm, dev, CMD_SCAN, 0x00);
259 }
260 break;
261
262 ✗ case LOOP_CAPTURE:
263 ✗ sm_do_capture (ssm, dev);
264 ✗ break;
265
266 ✗ case LOOP_CAPTURE_DONE:
267 ✗ fpi_ssm_jump_to_state (ssm, LOOP_CMD_SCAN);
268 ✗ break;
269
270 ✗ default:
271 ✗ g_assert_not_reached ();
272 }
273 ✗ }
274
275 static void
276 ✗ loopsm_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
277 {
278 ✗ FpImageDevice *imgdev = FP_IMAGE_DEVICE (dev);
279 ✗ FpDeviceVcom5s *self = FPI_DEVICE_VCOM5S (dev);
280
281 ✗ g_object_unref (self->capture_img);
282 ✗ self->capture_img = NULL;
283 ✗ self->loop_running = FALSE;
284
285 ✗ if (error && !self->deactivating)
286 ✗ fpi_image_device_session_error (imgdev, error);
287 ✗ else if (error)
288 ✗ g_error_free (error);
289
290 ✗ if (self->deactivating)
291 ✗ fpi_image_device_deactivate_complete (imgdev, NULL);
292 ✗ }
293
294 static void
295 ✗ dev_activate (FpImageDevice *dev)
296 {
297 ✗ FpDeviceVcom5s *self = FPI_DEVICE_VCOM5S (dev);
298 ✗ FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (dev), loop_run_state, LOOP_NUM_STATES);
299
300 ✗ self->deactivating = FALSE;
301 ✗ fpi_ssm_start (ssm, loopsm_complete);
302 ✗ self->loop_running = TRUE;
303 ✗ fpi_image_device_activate_complete (dev, NULL);
304 ✗ }
305
306 static void
307 ✗ dev_deactivate (FpImageDevice *dev)
308 {
309 ✗ FpDeviceVcom5s *self = FPI_DEVICE_VCOM5S (dev);
310
311 ✗ if (self->loop_running)
312 ✗ self->deactivating = TRUE;
313 else
314 ✗ fpi_image_device_deactivate_complete (dev, NULL);
315 ✗ }
316
317 static void
318 ✗ dev_init (FpImageDevice *dev)
319 {
320 ✗ GError *error = NULL;
321
322 ✗ g_usb_device_claim_interface (fpi_device_get_usb_device (FP_DEVICE (dev)), 0, 0, &error);
323
324 ✗ fpi_image_device_open_complete (dev, error);
325 ✗ }
326
327 static void
328 ✗ dev_deinit (FpImageDevice *dev)
329 {
330 ✗ GError *error = NULL;
331
332 ✗ g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (dev)),
333 0, 0, &error);
334
335 ✗ fpi_image_device_close_complete (dev, error);
336 ✗ }
337
338 static const FpIdEntry id_table[] = {
339 { .vid = 0x061a, .pid = 0x0110, },
340 { .vid = 0, .pid = 0, .driver_data = 0 },
341 };
342
343 static void
344 ✗ fpi_device_vcom5s_init (FpDeviceVcom5s *self)
345 {
346 ✗ }
347
348 static void
349 128 fpi_device_vcom5s_class_init (FpDeviceVcom5sClass *klass)
350 {
351 128 FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
352 128 FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
353
354 128 dev_class->id = "vcom5s";
355 128 dev_class->full_name = "Veridicom 5thSense";
356 128 dev_class->type = FP_DEVICE_TYPE_USB;
357 128 dev_class->id_table = id_table;
358 128 dev_class->scan_type = FP_SCAN_TYPE_PRESS;
359
360 128 img_class->img_open = dev_init;
361 128 img_class->img_close = dev_deinit;
362 128 img_class->activate = dev_activate;
363 128 img_class->deactivate = dev_deactivate;
364
365 128 img_class->img_width = IMG_WIDTH;
366 128 img_class->img_height = IMG_HEIGHT;
367 }
368