GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 75.7% 134 / 0 / 177
Functions: 82.4% 14 / 0 / 17
Branches: 52.4% 43 / 0 / 82

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