Skip to content

Commit f040b46

Browse files
committed
libhoth_usb: Refactor reconnection and user_ctx lifecycle
The current code does not allow multiple reconnect attempts if a previous one fails. To alleviate this, we need to refactor the usb context a bit so that we keep the context around and only free it with the libusb_device struct. This allows us to store the usb_loc in the struct even if the underlying libusb struct is destroyed due to failed initializations. We also fix a possible null ptr dereference that existed in the reconnect function. Signed-off-by: William A. Kennington III <wak@google.com>
1 parent 9b3cdae commit f040b46

6 files changed

Lines changed: 81 additions & 77 deletions

File tree

examples/htool_usb.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "htool_cmd.h"
3030
#include "protocol/util.h"
3131
#include "transports/libhoth_usb.h"
32+
#include "transports/libhoth_usb_device.h"
3233

3334
static int enumerate_devices(
3435
libusb_context* libusb_ctx,

transports/libhoth_usb.c

Lines changed: 68 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -92,41 +92,52 @@ static int libhoth_usb_release(struct libhoth_device* dev) {
9292
return libusb_release_interface(usb_dev->handle, usb_dev->info.interface_id);
9393
}
9494

95+
static int libhoth_usb_close_internal(struct libhoth_usb_device* usb_dev) {
96+
int status = LIBUSB_SUCCESS;
97+
if (usb_dev->handle != NULL) {
98+
switch (usb_dev->info.type) {
99+
case LIBHOTH_USB_INTERFACE_TYPE_MAILBOX:
100+
status = libhoth_usb_mailbox_close(usb_dev);
101+
break;
102+
case LIBHOTH_USB_INTERFACE_TYPE_FIFO:
103+
status = libhoth_usb_fifo_close(usb_dev);
104+
break;
105+
default:
106+
status = LIBHOTH_ERR_INTERFACE_NOT_FOUND;
107+
break;
108+
}
109+
libusb_release_interface(usb_dev->handle, usb_dev->info.interface_id);
110+
libusb_close(usb_dev->handle);
111+
usb_dev->handle = NULL;
112+
}
113+
return status;
114+
}
115+
95116
static int libhoth_usb_reconnect(struct libhoth_device* dev) {
96117
struct libhoth_usb_device* usb_dev = dev->user_ctx;
118+
if (usb_dev == NULL) {
119+
return LIBUSB_ERROR_INVALID_PARAM;
120+
}
97121
libusb_context* usb_ctx = usb_dev->ctx;
98122
uint64_t timeout_us = usb_dev->claim_timeout_us;
99123

100-
struct libusb_device* libusb_dev = libusb_get_device(usb_dev->handle);
101-
102-
struct libhoth_usb_loc usb_loc;
103-
usb_loc.bus = libusb_get_bus_number(libusb_dev);
104-
usb_loc.num_ports = libusb_get_port_numbers(
105-
libusb_dev, (uint8_t*)&usb_loc.ports, LIBHOTH_NUM_PORTS);
106-
if (usb_loc.num_ports == LIBUSB_ERROR_OVERFLOW) {
107-
fprintf(stderr, "Failed to list port numbers when reconnecting. (%s)\n",
108-
libusb_strerror(LIBUSB_ERROR_OVERFLOW));
109-
return LIBUSB_ERROR_OVERFLOW;
110-
}
111-
112-
libhoth_usb_close(dev);
113-
114124
uint64_t start_time_ms = libhoth_get_monotonic_ms();
115-
116125
while (1) {
117-
libusb_exit(usb_ctx);
118-
int ret = libusb_init(&usb_ctx);
119-
if (ret != 0) {
120-
fprintf(
121-
stderr,
122-
"libusb_init_context failed while reconnecting (error: %d (%s))\n",
123-
ret, libusb_strerror(ret));
124-
}
125-
126-
ret = libhoth_usb_get_device(usb_ctx, &usb_loc, &libusb_dev);
126+
libusb_device* new_libusb_dev = NULL;
127+
int ret = libhoth_usb_get_device(usb_ctx, &usb_dev->loc, &new_libusb_dev);
127128
if (ret == 0) {
128-
// Found the device
129-
break;
129+
struct libhoth_usb_device_init_options opts;
130+
opts.usb_ctx = usb_ctx;
131+
opts.usb_device = new_libusb_dev;
132+
opts.timeout_us = timeout_us;
133+
opts.prng_seed = libhoth_prng_seed();
134+
135+
// Close the old handle and driver data, but keep the usb_dev structure.
136+
libhoth_usb_close_internal(usb_dev);
137+
138+
ret = libhoth_usb_device_open(&opts, dev);
139+
libusb_unref_device(new_libusb_dev);
140+
return ret;
130141
}
131142

132143
uint64_t current_time_ms = libhoth_get_monotonic_ms();
@@ -136,27 +147,18 @@ static int libhoth_usb_reconnect(struct libhoth_device* dev) {
136147
stderr,
137148
"libhoth_usb_open timed out while reconnecting (error: %d (%s))\n",
138149
ret, libusb_strerror(ret));
139-
libusb_exit(usb_ctx);
140150
return ret; // Timeout
141151
}
142152

143153
// 100ms delay
144154
usleep(100 * 1000);
145155
}
146-
147-
struct libhoth_usb_device_init_options opts;
148-
opts.usb_ctx = usb_ctx;
149-
opts.usb_device = libusb_dev;
150-
opts.timeout_us = timeout_us;
151-
opts.prng_seed = libhoth_prng_seed();
152-
153-
return libhoth_usb_device_open(&opts, dev);
154156
}
155157

156158
static int libhoth_usb_device_open(
157159
const struct libhoth_usb_device_init_options* options,
158160
struct libhoth_device* dev) {
159-
struct libhoth_usb_device* usb_dev = NULL;
161+
struct libhoth_usb_device* usb_dev = dev->user_ctx;
160162
struct libusb_device_descriptor device_descriptor;
161163
int status =
162164
libusb_get_device_descriptor(options->usb_device, &device_descriptor);
@@ -185,14 +187,23 @@ static int libhoth_usb_device_open(
185187
goto err_out;
186188
}
187189

188-
usb_dev = calloc(1, sizeof(struct libhoth_usb_device));
189190
if (usb_dev == NULL) {
190-
status = LIBHOTH_ERR_MALLOC_FAILED;
191-
goto err_out;
191+
usb_dev = calloc(1, sizeof(struct libhoth_usb_device));
192+
if (usb_dev == NULL) {
193+
status = LIBHOTH_ERR_MALLOC_FAILED;
194+
goto err_out;
195+
}
196+
dev->user_ctx = usb_dev;
192197
}
193198
usb_dev->info = info;
194199
usb_dev->ctx = options->usb_ctx;
195200
usb_dev->claim_timeout_us = options->timeout_us;
201+
202+
if (libhoth_get_usb_loc(options->usb_device, &usb_dev->loc) != 0) {
203+
status = LIBUSB_ERROR_OTHER;
204+
goto err_out;
205+
}
206+
196207
status = libusb_open(options->usb_device, &usb_dev->handle);
197208
if (status != LIBUSB_SUCCESS) {
198209
goto err_out;
@@ -204,7 +215,6 @@ static int libhoth_usb_device_open(
204215
dev->claim = libhoth_usb_claim;
205216
dev->release = libhoth_usb_release;
206217
dev->reconnect = libhoth_usb_reconnect;
207-
dev->user_ctx = usb_dev;
208218

209219
status = libhoth_claim_device(dev, options->timeout_us);
210220
if (status != LIBHOTH_OK) {
@@ -231,14 +241,10 @@ static int libhoth_usb_device_open(
231241
return LIBHOTH_OK;
232242

233243
err_out:
234-
if (dev != NULL) {
235-
if (usb_dev != NULL) {
236-
if (usb_dev->handle != NULL) {
237-
libusb_release_interface(usb_dev->handle, usb_dev->info.interface_id);
238-
libusb_close(usb_dev->handle);
239-
}
240-
free(usb_dev);
241-
}
244+
if (usb_dev != NULL && usb_dev->handle != NULL) {
245+
libusb_release_interface(usb_dev->handle, usb_dev->info.interface_id);
246+
libusb_close(usb_dev->handle);
247+
usb_dev->handle = NULL;
242248
}
243249
libusb_free_config_descriptor(config_descriptor);
244250
return status;
@@ -257,6 +263,9 @@ int libhoth_usb_open(const struct libhoth_usb_device_init_options* options,
257263

258264
int ret = libhoth_usb_device_open(options, dev);
259265
if (ret != LIBUSB_SUCCESS) {
266+
if (dev->user_ctx != NULL) {
267+
free(dev->user_ctx);
268+
}
260269
free(dev);
261270
return ret;
262271
}
@@ -273,6 +282,9 @@ int libhoth_usb_send_request(struct libhoth_device* dev, const void* request,
273282

274283
struct libhoth_usb_device* usb_dev =
275284
(struct libhoth_usb_device*)dev->user_ctx;
285+
if (usb_dev->handle == NULL) {
286+
return LIBUSB_ERROR_NO_DEVICE;
287+
}
276288
switch (usb_dev->info.type) {
277289
case LIBHOTH_USB_INTERFACE_TYPE_MAILBOX:
278290
return libhoth_usb_mailbox_send_request(usb_dev, request, request_size);
@@ -293,6 +305,9 @@ int libhoth_usb_receive_response(struct libhoth_device* dev, void* response,
293305

294306
struct libhoth_usb_device* usb_dev =
295307
(struct libhoth_usb_device*)dev->user_ctx;
308+
if (usb_dev->handle == NULL) {
309+
return LIBUSB_ERROR_NO_DEVICE;
310+
}
296311
switch (usb_dev->info.type) {
297312
case LIBHOTH_USB_INTERFACE_TYPE_MAILBOX:
298313
return libhoth_usb_mailbox_receive_response(
@@ -307,33 +322,17 @@ int libhoth_usb_receive_response(struct libhoth_device* dev, void* response,
307322
}
308323

309324
int libhoth_usb_close(struct libhoth_device* dev) {
310-
int status;
311325
if (dev->user_ctx == NULL) {
312-
return LIBUSB_ERROR_INVALID_PARAM;
326+
return LIBUSB_SUCCESS;
313327
}
314328

315329
struct libhoth_usb_device* usb_dev =
316330
(struct libhoth_usb_device*)dev->user_ctx;
317331
dev->user_ctx = NULL;
318-
switch (usb_dev->info.type) {
319-
case LIBHOTH_USB_INTERFACE_TYPE_MAILBOX:
320-
status = libhoth_usb_mailbox_close(usb_dev);
321-
break;
322-
case LIBHOTH_USB_INTERFACE_TYPE_FIFO:
323-
status = libhoth_usb_fifo_close(usb_dev);
324-
break;
325-
default:
326-
return LIBHOTH_ERR_INTERFACE_NOT_FOUND;
327-
}
328-
if (status != LIBHOTH_OK) {
329-
return status;
330-
}
331-
if (usb_dev->handle != NULL) {
332-
libusb_release_interface(usb_dev->handle, usb_dev->info.interface_id);
333-
libusb_close(usb_dev->handle);
334-
}
332+
333+
int status = libhoth_usb_close_internal(usb_dev);
335334
free(usb_dev);
336-
return LIBHOTH_OK;
335+
return status;
337336
}
338337

339338
enum libusb_error transfer_status_to_error(

transports/libhoth_usb.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extern "C" {
2626
#endif
2727

2828
struct libhoth_device;
29+
struct libhoth_usb_loc;
2930

3031
struct libhoth_usb_device_init_options {
3132
// The device to open
@@ -40,14 +41,6 @@ struct libhoth_usb_device_init_options {
4041
uint32_t timeout_us;
4142
};
4243

43-
#define LIBHOTH_NUM_PORTS 16
44-
45-
struct libhoth_usb_loc {
46-
uint8_t bus;
47-
uint8_t ports[LIBHOTH_NUM_PORTS];
48-
int num_ports;
49-
};
50-
5144
// Note that the options struct only needs to to live for the duration of
5245
// this function call. It can be destroyed once libhoth_usb_open returns.
5346
int libhoth_usb_open(const struct libhoth_usb_device_init_options* options,

transports/libhoth_usb_device.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ struct libhoth_usb_mailbox {
4747
uint8_t ep_out;
4848
};
4949

50+
#define LIBHOTH_NUM_PORTS 16
51+
52+
struct libhoth_usb_loc {
53+
uint8_t bus;
54+
uint8_t ports[LIBHOTH_NUM_PORTS];
55+
int num_ports;
56+
};
57+
5058
struct libhoth_usb_fifo {
5159
struct libusb_transfer* in_transfer;
5260
struct libusb_transfer* out_transfer;
@@ -72,6 +80,7 @@ struct libhoth_usb_device {
7280
libusb_context* ctx;
7381
libusb_device_handle* handle;
7482
uint32_t claim_timeout_us;
83+
struct libhoth_usb_loc loc;
7584
struct libhoth_usb_interface_info info;
7685
union driver_data {
7786
struct libhoth_usb_mailbox mailbox;

transports/libhoth_usb_fifo.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,5 +297,6 @@ int libhoth_usb_fifo_close(struct libhoth_usb_device* dev) {
297297
if (drvdata->out_buffer != NULL) free(drvdata->out_buffer);
298298
libusb_free_transfer(drvdata->in_transfer);
299299
libusb_free_transfer(drvdata->out_transfer);
300+
memset(drvdata, 0, sizeof(*drvdata));
300301
return LIBHOTH_OK;
301302
}

transports/libhoth_usb_mailbox.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,5 +232,6 @@ int libhoth_usb_mailbox_close(struct libhoth_usb_device* dev) {
232232
if (dev == NULL) {
233233
return LIBUSB_ERROR_INVALID_PARAM;
234234
}
235+
memset(&dev->driver_data.mailbox, 0, sizeof(dev->driver_data.mailbox));
235236
return LIBHOTH_OK;
236237
}

0 commit comments

Comments
 (0)