mirror of
https://github.com/linux-msm/qdl.git
synced 2026-10-07 06:29:17 -07:00
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>
143 lines
3.4 KiB
C
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);
|
|
}
|