GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 71.7% 134 / 0 / 187
Functions: 82.4% 14 / 0 / 17
Branches: 48.9% 43 / 0 / 88

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