GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 74.4% 134 / 0 / 180
Functions: 82.4% 14 / 0 / 17
Branches: 51.2% 43 / 0 / 84

libfprint/fpi-usb-transfer.c
Line Branch Exec Source
1 /*
2 * FPrint USB transfer handling
3 * Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
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 #include "fpi-usb-transfer.h"
21 #include "fpi-log.h"
22
23 /**
24 * SECTION:fpi-usb-transfer
25 * @title: USB transfer helpers
26 * @short_description: Helpers for libgusb to ease transfer handling
27 *
28 * #FpiUsbTransfer is a structure to simplify the USB transfer handling.
29 * The main goal is to ease memory management and provide more parameters
30 * to callbacks that are useful for libfprint drivers.
31 *
32 * Drivers should use this API only rather than accessing the GUsbDevice
33 * directly in most cases.
34 *
35 * Setting %G_MESSAGES_DEBUG and %FP_DEBUG_TRANSFER will result in the message
36 * content to be dumped.
37 */
38
39
40 G_DEFINE_BOXED_TYPE (FpiUsbTransfer, fpi_usb_transfer, fpi_usb_transfer_ref, fpi_usb_transfer_unref)
41
42 static void
43 41966 log_transfer (FpiUsbTransfer *transfer, gboolean submit, GError *error)
44 {
45
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 18 taken 41966 times.
41966 if (fpi_log_is_debug_transfer_enabled ())
46 {
47 gboolean is_incoming = !!(transfer->endpoint & FPI_USB_ENDPOINT_IN);
48
49 if (!submit)
50 {
51 g_autofree gchar *error_str = NULL;
52 if (error)
53 error_str = g_strdup_printf ("with error (%s)", error->message);
54 else
55 error_str = g_strdup ("successfully");
56
57 g_debug ("Transfer %p completed %s, requested length %zd, actual length %zd, endpoint 0x%x",
58 transfer,
59 error_str,
60 transfer->length,
61 transfer->actual_length,
62 transfer->endpoint);
63 }
64 else
65 {
66 g_debug ("Transfer %p submitted, requested length %zd, endpoint 0x%x",
67 transfer,
68 transfer->length,
69 transfer->endpoint);
70 }
71
72 if (submit != is_incoming)
73 {
74 gsize dump_length = is_incoming ? transfer->actual_length : transfer->length;
75
76 /* Skip hex dump if actual_length is invalid (e.g., -1 on cancelled IN transfers) */
77 if (dump_length <= transfer->length)
78 fp_dbg_hex_dump_data (transfer->buffer, dump_length);
79 }
80 }
81 41966 }
82
83 /**
84 * fpi_usb_transfer_new:
85 * @device: The #FpDevice the transfer is for
86 *
87 * Creates a new #FpiUsbTransfer.
88 *
89 * Returns: (transfer full): A newly created #FpiUsbTransfer
90 */
91 FpiUsbTransfer *
92 20898 fpi_usb_transfer_new (FpDevice * device)
93 {
94 20898 FpiUsbTransfer *self;
95
96
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 20898 times.
20898 g_assert (device != NULL);
97
98 20898 self = g_slice_new0 (FpiUsbTransfer);
99 20898 self->ref_count = 1;
100 20898 self->type = FP_TRANSFER_NONE;
101
102 20898 self->device = device;
103
104 20898 return self;
105 }
106
107 static void
108 20898 fpi_usb_transfer_free (FpiUsbTransfer *self)
109 {
110
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 20898 times.
20898 g_assert (self);
111
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 20898 times.
20898 g_assert_cmpint (self->ref_count, ==, 0);
112
113
4/4
✓ Branch 6 → 7 taken 13798 times.
✓ Branch 6 → 9 taken 7100 times.
✓ Branch 7 → 8 taken 13055 times.
✓ Branch 7 → 9 taken 743 times.
20898 if (self->free_buffer && self->buffer)
114 13055 self->free_buffer (self->buffer);
115 20898 self->buffer = NULL;
116
117 20898 g_slice_free (FpiUsbTransfer, self);
118 20898 }
119
120 /**
121 * fpi_usb_transfer_ref:
122 * @self: A #FpiUsbTransfer
123 *
124 * Increments the reference count of @self by one.
125 *
126 * Returns: (transfer full): @self
127 */
128 FpiUsbTransfer *
129 2417 fpi_usb_transfer_ref (FpiUsbTransfer *self)
130 {
131
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2417 times.
2417 g_return_val_if_fail (self, NULL);
132
1/2
✓ Branch 4 → 5 taken 2417 times.
✗ Branch 4 → 6 not taken.
2417 g_return_val_if_fail (self->ref_count, NULL);
133
134 2417 g_atomic_int_inc (&self->ref_count);
135
136 2417 return self;
137 }
138
139 /**
140 * fpi_usb_transfer_unref:
141 * @self: A #FpiUsbTransfer
142 *
143 * Decrements the reference count of @self by one, freeing the structure when
144 * the reference count reaches zero.
145 */
146 void
147 23315 fpi_usb_transfer_unref (FpiUsbTransfer *self)
148 {
149
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 23315 times.
23315 g_return_if_fail (self);
150
1/2
✓ Branch 4 → 5 taken 23315 times.
✗ Branch 4 → 6 not taken.
23315 g_return_if_fail (self->ref_count);
151
152
2/2
✓ Branch 5 → 7 taken 20898 times.
✓ Branch 5 → 8 taken 2417 times.
23315 if (g_atomic_int_dec_and_test (&self->ref_count))
153 20898 fpi_usb_transfer_free (self);
154 }
155
156 /**
157 * fpi_usb_transfer_fill_bulk:
158 * @transfer: The #FpiUsbTransfer
159 * @endpoint: The endpoint to send the transfer to
160 * @length: The buffer size to allocate
161 *
162 * Prepare a bulk transfer. A buffer will be created for you, use
163 * fpi_usb_transfer_fill_bulk_full() if you want to send a static buffer
164 * or receive a pre-defined buffer.
165 */
166 void
167 11792 fpi_usb_transfer_fill_bulk (FpiUsbTransfer *transfer,
168 guint8 endpoint,
169 gsize length)
170 {
171 11792 fpi_usb_transfer_fill_bulk_full (transfer,
172 endpoint,
173 11792 g_malloc0 (length),
174 length,
175 g_free);
176 11792 }
177
178 /**
179 * fpi_usb_transfer_fill_bulk_full:
180 * @transfer: The #FpiUsbTransfer
181 * @endpoint: The endpoint to send the transfer to
182 * @buffer: The data to send.
183 * @length: The size of @buffer
184 * @free_func: (destroy buffer): Destroy notify for @buffer
185 *
186 * Prepare a bulk transfer.
187 */
188 void
189 20294 fpi_usb_transfer_fill_bulk_full (FpiUsbTransfer *transfer,
190 guint8 endpoint,
191 guint8 *buffer,
192 gsize length,
193 GDestroyNotify free_func)
194 {
195
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 20294 times.
20294 g_assert (transfer->type == FP_TRANSFER_NONE);
196
1/2
✓ Branch 4 → 5 taken 20294 times.
✗ Branch 4 → 6 not taken.
20294 g_assert (buffer != NULL);
197
198 20294 transfer->type = FP_TRANSFER_BULK;
199 20294 transfer->endpoint = endpoint;
200
201 20294 transfer->buffer = buffer;
202 20294 transfer->length = length;
203 20294 transfer->free_buffer = free_func;
204 20294 }
205
206 /**
207 * fpi_usb_transfer_fill_control:
208 * @transfer: The #FpiUsbTransfer
209 * @direction: The direction of the control transfer
210 * @request_type: The request type
211 * @recipient: The recipient
212 * @request: The control transfer request
213 * @value: The control transfer value
214 * @idx: The control transfer index
215 * @length: The size of the transfer
216 *
217 * Prepare a control transfer. The function will create a new buffer,
218 * you can initialize the buffer after calling this function.
219 */
220 void
221 336 fpi_usb_transfer_fill_control (FpiUsbTransfer *transfer,
222 GUsbDeviceDirection direction,
223 GUsbDeviceRequestType request_type,
224 GUsbDeviceRecipient recipient,
225 guint8 request,
226 guint16 value,
227 guint16 idx,
228 gsize length)
229 {
230
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 336 times.
336 g_assert (transfer->type == FP_TRANSFER_NONE);
231
232 336 transfer->type = FP_TRANSFER_CONTROL;
233 336 transfer->direction = direction;
234 336 transfer->request_type = request_type;
235 336 transfer->recipient = recipient;
236 336 transfer->request = request;
237 336 transfer->value = value;
238 336 transfer->idx = idx;
239
240 336 transfer->length = length;
241 336 transfer->buffer = g_malloc0 (length);
242 336 transfer->free_buffer = g_free;
243 336 }
244
245 /**
246 * fpi_usb_transfer_fill_interrupt:
247 * @transfer: The #FpiUsbTransfer
248 * @endpoint: The endpoint to send the transfer to
249 * @length: The size of the transfer
250 *
251 * Prepare an interrupt transfer. The function will create a new buffer,
252 * you can initialize the buffer after calling this function.
253 */
254 void
255 260 fpi_usb_transfer_fill_interrupt (FpiUsbTransfer *transfer,
256 guint8 endpoint,
257 gsize length)
258 {
259 260 fpi_usb_transfer_fill_interrupt_full (transfer,
260 endpoint,
261 260 g_malloc0 (length),
262 length,
263 g_free);
264 260 }
265
266 /**
267 * fpi_usb_transfer_fill_interrupt_full:
268 * @transfer: The #FpiUsbTransfer
269 * @endpoint: The endpoint to send the transfer to
270 * @buffer: The data to send.
271 * @length: The size of @buffer
272 * @free_func: (destroy buffer): Destroy notify for @buffer
273 *
274 * Prepare an interrupt transfer.
275 */
276 void
277 264 fpi_usb_transfer_fill_interrupt_full (FpiUsbTransfer *transfer,
278 guint8 endpoint,
279 guint8 *buffer,
280 gsize length,
281 GDestroyNotify free_func)
282 {
283
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 264 times.
264 g_assert (transfer->type == FP_TRANSFER_NONE);
284
1/2
✓ Branch 4 → 5 taken 264 times.
✗ Branch 4 → 6 not taken.
264 g_assert (buffer != NULL);
285
286 264 transfer->type = FP_TRANSFER_INTERRUPT;
287 264 transfer->endpoint = endpoint;
288
289 264 transfer->buffer = buffer;
290 264 transfer->length = length;
291 264 transfer->free_buffer = free_func;
292 264 }
293
294 static void
295 20895 transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
296 {
297 20895 GError *error = NULL;
298 20895 FpiUsbTransfer *transfer = user_data;
299 20895 FpiUsbTransferCallback callback;
300
301
3/4
✓ Branch 2 → 3 taken 20295 times.
✓ Branch 2 → 5 taken 336 times.
✓ Branch 2 → 7 taken 264 times.
✗ Branch 2 → 9 not taken.
20895 switch (transfer->type)
302 {
303 case FP_TRANSFER_BULK:
304 40590 transfer->actual_length =
305 20295 g_usb_device_bulk_transfer_finish (G_USB_DEVICE (source_object),
306 res,
307 &error);
308 20295 break;
309
310 case FP_TRANSFER_CONTROL:
311 672 transfer->actual_length =
312 336 g_usb_device_control_transfer_finish (G_USB_DEVICE (source_object),
313 res,
314 &error);
315 336 break;
316
317 case FP_TRANSFER_INTERRUPT:
318 528 transfer->actual_length =
319 264 g_usb_device_interrupt_transfer_finish (G_USB_DEVICE (source_object),
320 res,
321 &error);
322 264 break;
323
324 case FP_TRANSFER_NONE:
325 default:
326 g_assert_not_reached ();
327 }
328
329 20895 log_transfer (transfer, FALSE, error);
330
331 /* Check for short error, and set an error if requested */
332
2/2
✓ Branch 11 → 12 taken 20889 times.
✓ Branch 11 → 18 taken 6 times.
20895 if (error == NULL &&
333
2/2
✓ Branch 12 → 13 taken 11228 times.
✓ Branch 12 → 18 taken 9661 times.
20889 transfer->short_is_error &&
334
2/2
✓ Branch 13 → 14 taken 11226 times.
✓ Branch 13 → 18 taken 2 times.
11228 transfer->actual_length > 0 &&
335
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 18 taken 11226 times.
11226 transfer->actual_length != transfer->length)
336 {
337 error = g_error_new (G_USB_DEVICE_ERROR,
338 G_USB_DEVICE_ERROR_IO,
339 "Unexpected short error of %zd size (expected %zd)",
340 transfer->actual_length, transfer->length);
341 }
342
343 20895 callback = transfer->callback;
344 20895 transfer->callback = NULL;
345 20895 callback (transfer, transfer->device, transfer->user_data, error);
346
347 20895 fpi_usb_transfer_unref (transfer);
348 20895 }
349
350 static void
351 transfer_cancel_cb (FpDevice *device, gpointer user_data)
352 {
353 FpiUsbTransfer *transfer = user_data;
354 GError *error;
355 FpiUsbTransferCallback callback;
356
357 error = g_error_new_literal (G_IO_ERROR,
358 G_IO_ERROR_CANCELLED,
359 "Transfer was cancelled before being started");
360 callback = transfer->callback;
361 transfer->callback = NULL;
362 transfer->actual_length = -1;
363 callback (transfer, transfer->device, transfer->user_data, error);
364
365 fpi_usb_transfer_unref (transfer);
366 }
367
368 /**
369 * fpi_usb_transfer_submit:
370 * @transfer: (transfer full): The transfer to submit, must have been filled.
371 * @timeout_ms: Timeout for the transfer in ms
372 * @cancellable: Cancellable to use, e.g. fpi_device_get_cancellable()
373 * @callback: Callback on completion or error
374 * @user_data: Data to pass to callback
375 *
376 * Submit a USB transfer with a specific timeout and callback functions.
377 *
378 * Note that #FpiUsbTransfer will be stolen when this function is called.
379 * So that all associated data will be free'ed automatically, after the
380 * callback ran unless fpi_usb_transfer_ref() is explicitly called.
381 */
382 void
383 20895 fpi_usb_transfer_submit (FpiUsbTransfer *transfer,
384 guint timeout_ms,
385 GCancellable *cancellable,
386 FpiUsbTransferCallback callback,
387 gpointer user_data)
388 {
389
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 20895 times.
20895 g_return_if_fail (transfer);
390
1/2
✓ Branch 4 → 5 taken 20895 times.
✗ Branch 4 → 6 not taken.
20895 g_return_if_fail (callback);
391
392 /* Recycling is allowed, but not two at the same time. */
393
1/2
✓ Branch 5 → 7 taken 20895 times.
✗ Branch 5 → 9 not taken.
20895 g_return_if_fail (transfer->callback == NULL);
394
395 20895 transfer->callback = callback;
396 20895 transfer->user_data = user_data;
397
398 20895 log_transfer (transfer, TRUE, NULL);
399
400 /* Work around libgusb cancellation issue, see
401 * https://github.com/hughsie/libgusb/pull/42
402 * should be fixed with libgusb 0.3.7.
403 * Note that this is not race free, we rely on libfprint and API users
404 * not cancelling from a different thread here.
405 */
406
3/4
✓ Branch 8 → 10 taken 2726 times.
✓ Branch 8 → 14 taken 18169 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 14 taken 2726 times.
20895 if (cancellable && g_cancellable_is_cancelled (cancellable))
407 {
408 fpi_device_add_timeout (transfer->device, 0,
409 transfer_cancel_cb, transfer, NULL);
410 return;
411 }
412
413
3/4
✓ Branch 14 → 15 taken 20295 times.
✓ Branch 14 → 18 taken 336 times.
✓ Branch 14 → 21 taken 264 times.
✗ Branch 14 → 24 not taken.
20895 switch (transfer->type)
414 {
415 20295 case FP_TRANSFER_BULK:
416 40590 g_usb_device_bulk_transfer_async (fpi_device_get_usb_device (transfer->device),
417 transfer->endpoint,
418 20295 transfer->buffer,
419 20295 transfer->length,
420 timeout_ms,
421 cancellable,
422 transfer_finish_cb,
423 transfer);
424 20295 break;
425
426 336 case FP_TRANSFER_CONTROL:
427 672 g_usb_device_control_transfer_async (fpi_device_get_usb_device (transfer->device),
428 transfer->direction,
429 transfer->request_type,
430 transfer->recipient,
431 transfer->request,
432 transfer->value,
433 transfer->idx,
434 336 transfer->buffer,
435 336 transfer->length,
436 timeout_ms,
437 cancellable,
438 transfer_finish_cb,
439 transfer);
440 336 break;
441
442 264 case FP_TRANSFER_INTERRUPT:
443 528 g_usb_device_interrupt_transfer_async (fpi_device_get_usb_device (transfer->device),
444 transfer->endpoint,
445 264 transfer->buffer,
446 264 transfer->length,
447 timeout_ms,
448 cancellable,
449 transfer_finish_cb,
450 transfer);
451 264 break;
452
453 case FP_TRANSFER_NONE:
454 default:
455 fpi_usb_transfer_unref (transfer);
456 g_return_if_reached ();
457 }
458 }
459
460 /**
461 * fpi_usb_transfer_submit_sync:
462 * @transfer: The transfer to submit, must have been filled.
463 * @timeout_ms: Timeout for the transfer in millisecnods
464 * @error: Location to store #GError to
465 *
466 * Synchronously submit a USB transfer with a specific timeout.
467 * Only use this function with short timeouts as the application will
468 * be blocked otherwise.
469 *
470 * Note that you still need to fpi_usb_transfer_unref() the
471 * #FpiUsbTransfer afterwards.
472 *
473 * Returns: #TRUE on success, otherwise #FALSE and @error will be set
474 */
475 gboolean
476 88 fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer,
477 guint timeout_ms,
478 GError **error)
479 {
480 88 gboolean res;
481 88 gsize actual_length;
482
483
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 88 times.
88 g_return_val_if_fail (transfer, FALSE);
484
485 /* Recycling is allowed, but not two at the same time. */
486
1/2
✓ Branch 4 → 5 taken 88 times.
✗ Branch 4 → 7 not taken.
88 g_return_val_if_fail (transfer->callback == NULL, FALSE);
487
488 88 log_transfer (transfer, TRUE, NULL);
489
490
1/4
✓ Branch 6 → 8 taken 88 times.
✗ Branch 6 → 10 not taken.
✗ Branch 6 → 13 not taken.
✗ Branch 6 → 16 not taken.
88 switch (transfer->type)
491 {
492 88 case FP_TRANSFER_BULK:
493 176 res = g_usb_device_bulk_transfer (fpi_device_get_usb_device (transfer->device),
494 transfer->endpoint,
495 88 transfer->buffer,
496 88 transfer->length,
497 &actual_length,
498 timeout_ms,
499 NULL,
500 error);
501 88 break;
502
503 case FP_TRANSFER_CONTROL:
504 res = g_usb_device_control_transfer (fpi_device_get_usb_device (transfer->device),
505 transfer->direction,
506 transfer->request_type,
507 transfer->recipient,
508 transfer->request,
509 transfer->value,
510 transfer->idx,
511 transfer->buffer,
512 transfer->length,
513 &actual_length,
514 timeout_ms,
515 NULL,
516 error);
517 break;
518
519 case FP_TRANSFER_INTERRUPT:
520 res = g_usb_device_interrupt_transfer (fpi_device_get_usb_device (transfer->device),
521 transfer->endpoint,
522 transfer->buffer,
523 transfer->length,
524 &actual_length,
525 timeout_ms,
526 NULL,
527 error);
528 break;
529
530 case FP_TRANSFER_NONE:
531 default:
532 g_return_val_if_reached (FALSE);
533 }
534
535 88 log_transfer (transfer, FALSE, *error);
536
537
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 88 times.
88 if (!res)
538 transfer->actual_length = -1;
539 else
540 88 transfer->actual_length = actual_length;
541
542 return res;
543 }
544
545 /**
546 * fpi_usb_transfer_set_short_error:
547 * @transfer: The transfer to submit, must have been filled.
548 * @short_is_error: Whether a short transfer should be considered an error
549 *
550 * Sets whether a short transfer (a transfer in which the transferred length
551 * does not match the expected length) should be considered an error
552 *
553 * By default, short transfers are not considered an error, but
554 * drivers can enforce a further check by setting this flag.
555 */
556 void
557 107 fpi_usb_transfer_set_short_error (FpiUsbTransfer *transfer,
558 gboolean short_is_error)
559 {
560
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 107 times.
107 g_return_if_fail (transfer);
561
562 107 transfer->short_is_error = short_is_error;
563 }
564