Files
qdl/ramdump.c
T
Igor Opaniuk 8e9082bc21 qdl: add auto backend that resolves USB/QUD inside the wait loop
Add a meta-backend QDL_DEVICE_AUTO that defers the transport choice
to qdl_open(): instead of picking libusb or QUD upfront, its open()
runs a unified wait loop and binds whichever transport first reaches
an EDL device. The "auto" --backend token (and the default) maps to
it.

Each 250 ms tick the loop does:

  1. usb_open_attempt() - one libusb scan-and-open. On success the
     libusb device is bound as the inner transport.
  2. On Windows, if libusb saw the device but could not open it
     (-EBUSY: the QDLoader 9008 driver has claimed the interface)
     or SetupAPI sees a Qualcomm COM port libusb missed, calls
     qud_open(). On success the QUD device is bound as the inner.
  3. Otherwise sleeps and retries. No timeout: the loop waits
     indefinitely, same cadence as the prior libusb-only loop.

qdl_read/write/close on the outer auto wrapper forward to the inner.
Pre-open out_chunk_size is stashed on the wrapper and applied at
bind time; vip_data and slot live on the outer where firehose
accesses them, so no forwarding is needed for those.

usb_open_attempt() (factored out of usb_open()) returns
0 / -ENODEV / -EBUSY / -EIO. The --backend usb path loops over it
with visibility-transition messaging. The auto loop owns its own
messaging and never emits the -EBUSY diagnostic because that
condition triggers the QUD attempt in the same tick.

qdl-ramdump now uses QDL_DEVICE_AUTO so Windows users with the
official driver can collect crash dumps without re-binding the USB
driver to WinUSB.

QDL_DEVICE_AUTO is explicitly a meta-backend, not a peer of the USB
and QUD backends: its job is to defer the transport choice to the
moment a probe reaches a device. The libusb and QUD backends remain
standalone and selectable via --backend usb / --backend qud.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-05-14 21:40:37 -05:00

108 lines
1.9 KiB
C

// SPDX-License-Identifier: BSD-3-Clause
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "qdl.h"
#ifdef _WIN32
const char *__progname = "ramdump";
#endif
bool qdl_debug;
static void print_usage(FILE *out)
{
extern const char *__progname;
fprintf(out,
"%s [--debug] [-o <ramdump-path>] [segment-filter]\n\n"
" -d, --debug\t\tEnable debug output\n"
" -v, --version\t\tPrint version\n"
" -o, --output=PATH\tDirectory to write ramdump segments into (default: .)\n"
" -S, --serial=T\t\tSelect target by serial number T (e.g. <0AA94EFD>)\n"
" -h, --help\t\tPrint this usage info\n"
" [segment-filter]\tOptional comma-separated list of segment names to dump\n",
__progname);
}
int main(int argc, char **argv)
{
struct qdl_device *qdl;
qdl = qdl_init(QDL_DEVICE_AUTO);
if (!qdl)
return 1;
char *ramdump_path = ".";
char *filter = NULL;
char *serial = NULL;
int ret = 0;
int opt;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"version", no_argument, 0, 'v'},
{"output", required_argument, 0, 'o'},
{"serial", required_argument, 0, 'S'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "dvo:S:h", options, NULL)) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case 'v':
print_version();
ret = 0;
goto out_cleanup;
case 'o':
ramdump_path = optarg;
break;
case 'S':
serial = optarg;
break;
case 'h':
print_usage(stdout);
return 0;
default:
print_usage(stderr);
return 1;
}
}
if (optind < argc)
filter = argv[optind++];
if (optind != argc) {
print_usage(stderr);
return 1;
}
ux_init();
if (qdl_debug)
print_version();
ret = qdl_open(qdl, serial);
if (ret) {
ret = 1;
goto out_cleanup;
}
ret = sahara_run(qdl, NULL, ramdump_path, filter);
if (ret < 0) {
ret = 1;
goto out_cleanup;
}
out_cleanup:
qdl_close(qdl);
qdl_deinit(qdl);
return ret;
}