Files
qdl/io.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

143 lines
3.4 KiB
C

// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "qdl.h"
struct qdl_device *qdl_init(enum QDL_DEVICE_TYPE type)
{
if (type == QDL_DEVICE_USB)
return usb_init();
if (type == QDL_DEVICE_SIM)
return sim_init();
if (type == QDL_DEVICE_QUD)
return qud_init();
if (type == QDL_DEVICE_AUTO)
return auto_init();
return NULL;
}
void qdl_deinit(struct qdl_device *qdl)
{
if (qdl) {
free(qdl->pending_buf);
free(qdl);
}
}
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size)
{
qdl->set_out_chunk_size(qdl, size);
}
int qdl_open(struct qdl_device *qdl, const char *serial)
{
return qdl->open(qdl, serial);
}
void qdl_close(struct qdl_device *qdl)
{
qdl->close(qdl);
}
/**
* qdl_read() - Read a message from the device
* @qdl: device handle
* @buf: buffer to write the data into
* @len: maximum length of data to be read
* @timeout: timeout for the read, in milliseconds
*
* Drains the pushback buffer (qdl_push_back()) before touching the underlying
* transport, so a previous read that crossed a Firehose message boundary can
* deliver the trailing bytes here.
*
* Returns: number of bytes read, might be zero for a ZLP
* negative errno on failure (notably -ETIMEDOUT)
*/
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
{
size_t available;
size_t copy;
if (qdl->pending_buf) {
available = qdl->pending_len - qdl->pending_off;
copy = available < len ? available : len;
memcpy(buf, qdl->pending_buf + qdl->pending_off, copy);
qdl->pending_off += copy;
if (qdl->pending_off >= qdl->pending_len) {
free(qdl->pending_buf);
qdl->pending_buf = NULL;
qdl->pending_len = 0;
qdl->pending_off = 0;
}
return (int)copy;
}
return qdl->read(qdl, buf, len, timeout);
}
/**
* qdl_push_back() - Stash unread bytes for a future qdl_read()
* @qdl: device handle
* @buf: bytes to remember
* @len: number of bytes
*
* Concatenates @buf onto whatever is already pending (after any already-served
* prefix). Used by firehose_read() when a single transport read returned more
* than one Firehose message - or an XML envelope followed immediately by its
* rawmode binary payload - and we need the trailing bytes to surface on the
* next qdl_read() before any new transport I/O happens.
*
* Returns: 0 on success, negative errno on allocation failure.
*/
int qdl_push_back(struct qdl_device *qdl, const void *buf, size_t len)
{
char *grown;
size_t total;
if (!len)
return 0;
if (qdl->pending_off > 0) {
size_t leftover = qdl->pending_len - qdl->pending_off;
memmove(qdl->pending_buf, qdl->pending_buf + qdl->pending_off,
leftover);
qdl->pending_len = leftover;
qdl->pending_off = 0;
}
total = qdl->pending_len + len;
grown = realloc(qdl->pending_buf, total);
if (!grown)
return -ENOMEM;
memcpy(grown + qdl->pending_len, buf, len);
qdl->pending_buf = grown;
qdl->pending_len = total;
return 0;
}
/**
* qdl_write() - Write a message from the device
* @qdl: device handle
* @buf: buffer with data to be written
* @len: length of data to be written
* @timeout: timeout for write, in milliseconds
*
* Returns: number of bytes read
* negative errno on failure (notably -ETIMEDOUT)
*/
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, unsigned int timeout)
{
return qdl->write(qdl, buf, len, timeout);
}