GCC Code Coverage Report


Directory: ./
File: libfprint/fpi-usb-transfer.c
Date: 2024-09-16 14:36:32
Exec Total Coverage
Lines: 137 193 71.0%
Functions: 13 16 81.2%
Branches: 42 86 48.8%

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 22988 log_transfer (FpiUsbTransfer *transfer, gboolean submit, GError *error)
40 {
41
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22988 times.
22988 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 22988 }
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 11409 fpi_usb_transfer_new (FpDevice * device)
101 {
102 11409 FpiUsbTransfer *self;
103
104
1/2
✓ Branch 0 taken 11409 times.
✗ Branch 1 not taken.
11409 g_assert (device != NULL);
105
106 11409 self = g_slice_new0 (FpiUsbTransfer);
107 11409 self->ref_count = 1;
108 11409 self->type = FP_TRANSFER_NONE;
109
110 11409 self->device = device;
111
112 11409 return self;
113 }
114
115 static void
116 11409 fpi_usb_transfer_free (FpiUsbTransfer *self)
117 {
118
1/2
✓ Branch 0 taken 11409 times.
✗ Branch 1 not taken.
11409 g_assert (self);
119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11409 times.
11409 g_assert_cmpint (self->ref_count, ==, 0);
120
121
4/4
✓ Branch 0 taken 8207 times.
✓ Branch 1 taken 3202 times.
✓ Branch 2 taken 7562 times.
✓ Branch 3 taken 645 times.
11409 if (self->free_buffer && self->buffer)
122 7562 self->free_buffer (self->buffer);
123 11409 self->buffer = NULL;
124
125 11409 g_slice_free (FpiUsbTransfer, self);
126 11409 }
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 0 taken 2417 times.
✗ Branch 1 not taken.
2417 g_return_val_if_fail (self, NULL);
140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2417 times.
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 13826 fpi_usb_transfer_unref (FpiUsbTransfer *self)
156 {
157
1/2
✓ Branch 0 taken 13826 times.
✗ Branch 1 not taken.
13826 g_return_if_fail (self);
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13826 times.
13826 g_return_if_fail (self->ref_count);
159
160
2/2
✓ Branch 0 taken 11409 times.
✓ Branch 1 taken 2417 times.
13826 if (g_atomic_int_dec_and_test (&self->ref_count))
161 11409 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 7067 fpi_usb_transfer_fill_bulk (FpiUsbTransfer *transfer,
176 guint8 endpoint,
177 gsize length)
178 {
179 7067 fpi_usb_transfer_fill_bulk_full (transfer,
180 endpoint,
181 7067 g_malloc0 (length),
182 length,
183 g_free);
184 7067 }
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 11057 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 0 taken 11057 times.
✗ Branch 1 not taken.
11057 g_assert (transfer->type == FP_TRANSFER_NONE);
204
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11057 times.
11057 g_assert (buffer != NULL);
205
206 11057 transfer->type = FP_TRANSFER_BULK;
207 11057 transfer->endpoint = endpoint;
208
209 11057 transfer->buffer = buffer;
210 11057 transfer->length = length;
211 11057 transfer->free_buffer = free_func;
212 11057 }
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 0 taken 118 times.
✗ Branch 1 not taken.
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 226 fpi_usb_transfer_fill_interrupt (FpiUsbTransfer *transfer,
264 guint8 endpoint,
265 gsize length)
266 {
267 226 fpi_usb_transfer_fill_interrupt_full (transfer,
268 endpoint,
269 226 g_malloc0 (length),
270 length,
271 g_free);
272 226 }
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 230 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 0 taken 230 times.
✗ Branch 1 not taken.
230 g_assert (transfer->type == FP_TRANSFER_NONE);
292
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 230 times.
230 g_assert (buffer != NULL);
293
294 230 transfer->type = FP_TRANSFER_INTERRUPT;
295 230 transfer->endpoint = endpoint;
296
297 230 transfer->buffer = buffer;
298 230 transfer->length = length;
299 230 transfer->free_buffer = free_func;
300 230 }
301
302 static void
303 11406 transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
304 {
305 11406 GError *error = NULL;
306 11406 FpiUsbTransfer *transfer = user_data;
307 11406 FpiUsbTransferCallback callback;
308
309
3/4
✓ Branch 0 taken 11058 times.
✓ Branch 1 taken 118 times.
✓ Branch 2 taken 230 times.
✗ Branch 3 not taken.
11406 switch (transfer->type)
310 {
311 case FP_TRANSFER_BULK:
312 22116 transfer->actual_length =
313 11058 g_usb_device_bulk_transfer_finish (G_USB_DEVICE (source_object),
314 res,
315 &error);
316 11058 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 460 transfer->actual_length =
327 230 g_usb_device_interrupt_transfer_finish (G_USB_DEVICE (source_object),
328 res,
329 &error);
330 230 break;
331
332 case FP_TRANSFER_NONE:
333 default:
334 g_assert_not_reached ();
335 }
336
337 11406 log_transfer (transfer, FALSE, error);
338
339 /* Check for short error, and set an error if requested */
340
2/2
✓ Branch 0 taken 11403 times.
✓ Branch 1 taken 3 times.
11406 if (error == NULL &&
341
2/2
✓ Branch 0 taken 6846 times.
✓ Branch 1 taken 4557 times.
11403 transfer->short_is_error &&
342
2/2
✓ Branch 0 taken 6844 times.
✓ Branch 1 taken 2 times.
6846 transfer->actual_length > 0 &&
343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6844 times.
6844 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)", transfer->actual_length, transfer->length);
348 }
349
350 11406 callback = transfer->callback;
351 11406 transfer->callback = NULL;
352 11406 callback (transfer, transfer->device, transfer->user_data, error);
353
354 11406 fpi_usb_transfer_unref (transfer);
355 11406 }
356
357 static void
358 transfer_cancel_cb (FpDevice *device, gpointer user_data)
359 {
360 FpiUsbTransfer *transfer = user_data;
361 GError *error;
362 FpiUsbTransferCallback callback;
363
364 error = g_error_new_literal (G_IO_ERROR,
365 G_IO_ERROR_CANCELLED,
366 "Transfer was cancelled before being started");
367 callback = transfer->callback;
368 transfer->callback = NULL;
369 transfer->actual_length = -1;
370 callback (transfer, transfer->device, transfer->user_data, error);
371
372 fpi_usb_transfer_unref (transfer);
373 }
374
375 /**
376 * fpi_usb_transfer_submit:
377 * @transfer: (transfer full): The transfer to submit, must have been filled.
378 * @timeout_ms: Timeout for the transfer in ms
379 * @cancellable: Cancellable to use, e.g. fpi_device_get_cancellable()
380 * @callback: Callback on completion or error
381 * @user_data: Data to pass to callback
382 *
383 * Submit a USB transfer with a specific timeout and callback functions.
384 *
385 * Note that #FpiUsbTransfer will be stolen when this function is called.
386 * So that all associated data will be free'ed automatically, after the
387 * callback ran unless fpi_usb_transfer_ref() is explicitly called.
388 */
389 void
390 11406 fpi_usb_transfer_submit (FpiUsbTransfer *transfer,
391 guint timeout_ms,
392 GCancellable *cancellable,
393 FpiUsbTransferCallback callback,
394 gpointer user_data)
395 {
396
1/2
✓ Branch 0 taken 11406 times.
✗ Branch 1 not taken.
11406 g_return_if_fail (transfer);
397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11406 times.
11406 g_return_if_fail (callback);
398
399 /* Recycling is allowed, but not two at the same time. */
400
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11406 times.
11406 g_return_if_fail (transfer->callback == NULL);
401
402 11406 transfer->callback = callback;
403 11406 transfer->user_data = user_data;
404
405 11406 log_transfer (transfer, TRUE, NULL);
406
407 /* Work around libgusb cancellation issue, see
408 * https://github.com/hughsie/libgusb/pull/42
409 * should be fixed with libgusb 0.3.7.
410 * Note that this is not race free, we rely on libfprint and API users
411 * not cancelling from a different thread here.
412 */
413
3/4
✓ Branch 0 taken 1910 times.
✓ Branch 1 taken 9496 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1910 times.
11406 if (cancellable && g_cancellable_is_cancelled (cancellable))
414 {
415 fpi_device_add_timeout (transfer->device, 0,
416 transfer_cancel_cb, transfer, NULL);
417 return;
418 }
419
420
3/4
✓ Branch 0 taken 11058 times.
✓ Branch 1 taken 118 times.
✓ Branch 2 taken 230 times.
✗ Branch 3 not taken.
11406 switch (transfer->type)
421 {
422 11058 case FP_TRANSFER_BULK:
423 22116 g_usb_device_bulk_transfer_async (fpi_device_get_usb_device (transfer->device),
424 11058 transfer->endpoint,
425 11058 transfer->buffer,
426 11058 transfer->length,
427 timeout_ms,
428 cancellable,
429 transfer_finish_cb,
430 transfer);
431 11058 break;
432
433 118 case FP_TRANSFER_CONTROL:
434 236 g_usb_device_control_transfer_async (fpi_device_get_usb_device (transfer->device),
435 transfer->direction,
436 transfer->request_type,
437 transfer->recipient,
438 118 transfer->request,
439 118 transfer->value,
440 118 transfer->idx,
441 118 transfer->buffer,
442 118 transfer->length,
443 timeout_ms,
444 cancellable,
445 transfer_finish_cb,
446 transfer);
447 118 break;
448
449 230 case FP_TRANSFER_INTERRUPT:
450 460 g_usb_device_interrupt_transfer_async (fpi_device_get_usb_device (transfer->device),
451 230 transfer->endpoint,
452 230 transfer->buffer,
453 230 transfer->length,
454 timeout_ms,
455 cancellable,
456 transfer_finish_cb,
457 transfer);
458 230 break;
459
460 case FP_TRANSFER_NONE:
461 default:
462 fpi_usb_transfer_unref (transfer);
463 g_return_if_reached ();
464 }
465 }
466
467 /**
468 * fpi_usb_transfer_submit_sync:
469 * @transfer: The transfer to submit, must have been filled.
470 * @timeout_ms: Timeout for the transfer in millisecnods
471 * @error: Location to store #GError to
472 *
473 * Synchronously submit a USB transfer with a specific timeout.
474 * Only use this function with short timeouts as the application will
475 * be blocked otherwise.
476 *
477 * Note that you still need to fpi_usb_transfer_unref() the
478 * #FpiUsbTransfer afterwards.
479 *
480 * Returns: #TRUE on success, otherwise #FALSE and @error will be set
481 */
482 gboolean
483 88 fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer,
484 guint timeout_ms,
485 GError **error)
486 {
487 88 gboolean res;
488 88 gsize actual_length;
489
490
1/2
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
88 g_return_val_if_fail (transfer, FALSE);
491
492 /* Recycling is allowed, but not two at the same time. */
493
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
88 g_return_val_if_fail (transfer->callback == NULL, FALSE);
494
495 88 log_transfer (transfer, TRUE, NULL);
496
497
1/4
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
88 switch (transfer->type)
498 {
499 88 case FP_TRANSFER_BULK:
500 176 res = g_usb_device_bulk_transfer (fpi_device_get_usb_device (transfer->device),
501 88 transfer->endpoint,
502 88 transfer->buffer,
503 88 transfer->length,
504 &actual_length,
505 timeout_ms,
506 NULL,
507 error);
508 88 break;
509
510 case FP_TRANSFER_CONTROL:
511 res = g_usb_device_control_transfer (fpi_device_get_usb_device (transfer->device),
512 transfer->direction,
513 transfer->request_type,
514 transfer->recipient,
515 transfer->request,
516 transfer->value,
517 transfer->idx,
518 transfer->buffer,
519 transfer->length,
520 &actual_length,
521 timeout_ms,
522 NULL,
523 error);
524 break;
525
526 case FP_TRANSFER_INTERRUPT:
527 res = g_usb_device_interrupt_transfer (fpi_device_get_usb_device (transfer->device),
528 transfer->endpoint,
529 transfer->buffer,
530 transfer->length,
531 &actual_length,
532 timeout_ms,
533 NULL,
534 error);
535 break;
536
537 case FP_TRANSFER_NONE:
538 default:
539 g_return_val_if_reached (FALSE);
540 }
541
542 88 log_transfer (transfer, FALSE, *error);
543
544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
88 if (!res)
545 transfer->actual_length = -1;
546 else
547 88 transfer->actual_length = actual_length;
548
549 return res;
550 }
551