Skip to content

Commit fd4caf4

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 fd4caf4

6 files changed

Lines changed: 82 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: 69 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -92,41 +92,53 @@ 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;
99-
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);
123+
struct libhoth_usb_loc usb_loc = usb_dev->loc;
113124

114125
uint64_t start_time_ms = libhoth_get_monotonic_ms();
115-
116126
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);
127+
libusb_device* new_libusb_dev = NULL;
128+
int ret = libhoth_usb_get_device(usb_ctx, &usb_loc, &new_libusb_dev);
127129
if (ret == 0) {
128-
// Found the device
129-
break;
130+
struct libhoth_usb_device_init_options opts;
131+
opts.usb_ctx = usb_ctx;
132+
opts.usb_device = new_libusb_dev;
133+
opts.timeout_us = timeout_us;
134+
opts.prng_seed = libhoth_prng_seed();
135+
136+
// Close the old handle and driver data, but keep the usb_dev structure.
137+
libhoth_usb_close_internal(usb_dev);
138+
139+
ret = libhoth_usb_device_open(&opts, dev);
140+
libusb_unref_device(new_libusb_dev);
141+
return ret;
130142
}
131143

132144
uint64_t current_time_ms = libhoth_get_monotonic_ms();
@@ -136,27 +148,18 @@ static int libhoth_usb_reconnect(struct libhoth_device* dev) {
136148
stderr,
137149
"libhoth_usb_open timed out while reconnecting (error: %d (%s))\n",
138150
ret, libusb_strerror(ret));
139-
libusb_exit(usb_ctx);
140151
return ret; // Timeout
141152
}
142153

143154
// 100ms delay
144155
usleep(100 * 1000);
145156
}
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);
154157
}
155158

156159
static int libhoth_usb_device_open(
157160
const struct libhoth_usb_device_init_options* options,
158161
struct libhoth_device* dev) {
159-
struct libhoth_usb_device* usb_dev = NULL;
162+
struct libhoth_usb_device* usb_dev = dev->user_ctx;
160163
struct libusb_device_descriptor device_descriptor;
161164
int status =
162165
libusb_get_device_descriptor(options->usb_device, &device_descriptor);
@@ -185,14 +188,23 @@ static int libhoth_usb_device_open(
185188
goto err_out;
186189
}
187190

188-
usb_dev = calloc(1, sizeof(struct libhoth_usb_device));
189191
if (usb_dev == NULL) {
190-
status = LIBHOTH_ERR_MALLOC_FAILED;
191-
goto err_out;
192+
usb_dev = calloc(1, sizeof(struct libhoth_usb_device));
193+
if (usb_dev == NULL) {
194+
status = LIBHOTH_ERR_MALLOC_FAILED;
195+
goto err_out;
196+
}
197+
dev->user_ctx = usb_dev;
192198
}
193199
usb_dev->info = info;
194200
usb_dev->ctx = options->usb_ctx;
195201
usb_dev->claim_timeout_us = options->timeout_us;
202+
203+
if (libhoth_get_usb_loc(options->usb_device, &usb_dev->loc) != 0) {
204+
status = LIBUSB_ERROR_OTHER;
205+
goto err_out;
206+
}
207+
196208
status = libusb_open(options->usb_device, &usb_dev->handle);
197209
if (status != LIBUSB_SUCCESS) {
198210
goto err_out;
@@ -204,7 +216,6 @@ static int libhoth_usb_device_open(
204216
dev->claim = libhoth_usb_claim;
205217
dev->release = libhoth_usb_release;
206218
dev->reconnect = libhoth_usb_reconnect;
207-
dev->user_ctx = usb_dev;
208219

209220
status = libhoth_claim_device(dev, options->timeout_us);
210221
if (status != LIBHOTH_OK) {
@@ -231,14 +242,10 @@ static int libhoth_usb_device_open(
231242
return LIBHOTH_OK;
232243

233244
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-
}
245+
if (usb_dev != NULL && usb_dev->handle != NULL) {
246+
libusb_release_interface(usb_dev->handle, usb_dev->info.interface_id);
247+
libusb_close(usb_dev->handle);
248+
usb_dev->handle = NULL;
242249
}
243250
libusb_free_config_descriptor(config_descriptor);
244251
return status;
@@ -257,6 +264,9 @@ int libhoth_usb_open(const struct libhoth_usb_device_init_options* options,
257264

258265
int ret = libhoth_usb_device_open(options, dev);
259266
if (ret != LIBUSB_SUCCESS) {
267+
if (dev->user_ctx != NULL) {
268+
free(dev->user_ctx);
269+
}
260270
free(dev);
261271
return ret;
262272
}
@@ -273,6 +283,9 @@ int libhoth_usb_send_request(struct libhoth_device* dev, const void* request,
273283

274284
struct libhoth_usb_device* usb_dev =
275285
(struct libhoth_usb_device*)dev->user_ctx;
286+
if (usb_dev->handle == NULL) {
287+
return LIBUSB_ERROR_NO_DEVICE;
288+
}
276289
switch (usb_dev->info.type) {
277290
case LIBHOTH_USB_INTERFACE_TYPE_MAILBOX:
278291
return libhoth_usb_mailbox_send_request(usb_dev, request, request_size);
@@ -293,6 +306,9 @@ int libhoth_usb_receive_response(struct libhoth_device* dev, void* response,
293306

294307
struct libhoth_usb_device* usb_dev =
295308
(struct libhoth_usb_device*)dev->user_ctx;
309+
if (usb_dev->handle == NULL) {
310+
return LIBUSB_ERROR_NO_DEVICE;
311+
}
296312
switch (usb_dev->info.type) {
297313
case LIBHOTH_USB_INTERFACE_TYPE_MAILBOX:
298314
return libhoth_usb_mailbox_receive_response(
@@ -307,33 +323,17 @@ int libhoth_usb_receive_response(struct libhoth_device* dev, void* response,
307323
}
308324

309325
int libhoth_usb_close(struct libhoth_device* dev) {
310-
int status;
311326
if (dev->user_ctx == NULL) {
312-
return LIBUSB_ERROR_INVALID_PARAM;
327+
return LIBUSB_SUCCESS;
313328
}
314329

315330
struct libhoth_usb_device* usb_dev =
316331
(struct libhoth_usb_device*)dev->user_ctx;
317332
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-
}
333+
334+
int status = libhoth_usb_close_internal(usb_dev);
335335
free(usb_dev);
336-
return LIBHOTH_OK;
336+
return status;
337337
}
338338

339339
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)