11 Commits
Author SHA1 Message Date
Igor Opaniuk ee3d93fc22 ux: route remaining diagnostics through shared logging
USB warnings, Sahara ramdump failures, fatal CLI and archive errors,
and packet hex dumps bypass the ux helpers, so they never get a
--debug timestamp and leave gaps in the protocol timeline.

Route them through shared logging via new warn and die helpers that
keep the program-name prefix, errno text, and exit status. Output
without --debug is unchanged and hex dumps stay visible in both modes.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-10-01 10:21:20 +02:00
Igor Opaniuk 65711e63a1 ux: own the program name used in diagnostics
The error prefix comes from __progname, a BSD-ism that glibc and the
Apple libc export but Windows lacks, so qdl.c and every unit test that
links util.c define their own copy. The nbdkit plugin links neither.

Keep the name in ux.c with a "qdl" default and set it through
ux_set_progname(): from argv[0] in main() and from the plugin's load
hook. Move print_version() into qdl.c, its only caller, so util.c and
the tests no longer need the symbol.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-10-01 10:21:20 +02:00
Igor Opaniuk 9f465fa274 util: move backend and specifier parsing out of qdl.c
decode_backend() (a --backend value to enum) and qdl_split_specifier()
(splitting "<file>::<selector>") were static string parsers trapped in
qdl.c, which cannot be unit-tested because qdl.c defines main(). Move
them to util.c alongside the other argument parsers (parse_storage_
address, decode_storage_type) and declare them in qdl.h. Pure code
move, no behaviour change.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-08-25 14:34:28 +02:00
Igor Opaniuk 047c87fe38 ufs: replace module globals with a caller-owned context
The provisioning loader kept its parsed state in module globals, which
made it impossible to re-run or unit test, and its error paths left
the globals dangling - a caller that continued after a failed load
acted on freed or stale data, and several body strings were leaked
outright. A context owned by the caller removes the shared state, ties
the provisioning data's lifetime to the flash flow that uses it, and
gives every error path a single cleanup to go through.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-08-25 12:57:33 +02:00
Igor Opaniuk 6354672439 sahara: add a device reset helper
The Sahara protocol has carried a reset command since version 1.0 and
qdl already uses it internally on error paths and after ramdump
collection, but there is no way to just reset a device sitting in EDL
or crash mode without uploading a Firehose programmer first.

Add sahara_device_reset(), which acknowledges the device's HELLO with
whatever mode the device asked for - fresh EDL announces image
transfer, a crashed device announces memory debug - and then issues
the reset command. When the first exchange reveals a running Firehose
programmer instead of Sahara, report that to the caller so it can
fall back to a Firehose power reset. The missing-HELLO case is
handled like sahara_chipinfo() does, by prodding the device with an
unsolicited HELLO response, since the QUD driver eats the HELLO on
many targets.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-08-19 11:27:09 +02:00
Igor Opaniuk 4c300a243c qdl: drop the EDL product id allowlist
EDL, crash-mode and ramdump devices enumerate with a growing set of
product ids, and every newly discovered variant needed another
allowlist entry - devices with ids not yet on the list were silently
invisible until someone updated it. Both backends now identify the
EDL function by the vendor-specific interface signature instead: the
libusb backend via the config descriptors, the QUD backend via the
Windows compatible ids derived from them.

Trust the vendor id alone in qdl_is_edl_device() and drop the
now-redundant pid gate in the QUD enumeration. The pid parameter
stays in the signature so call sites are unchanged; the interface
gates are what reject non-EDL Qualcomm devices from here on.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 16:21:49 +02:00
Bjorn Andersson a8f21545f0 firehose: add block-level read/write/size interface
The firehose layer only drives file-oriented flashing: <program>/<read>
operations described by XML and executed as an op list. Nothing let an
in-process consumer configure the programmer on its own, learn how large a
LUN is, or read and write arbitrary sector ranges to and from memory
buffers.

The upcoming nbdkit plugin needs exactly that - random-access block I/O
against a live device - so add the missing primitives:

  - firehose_open()    configure the programmer without running any ops
  - firehose_getsize() report a LUN's sector size and count, parsed from
                       the <getstorageinfo> JSON response
  - firehose_pread()   read a sector range into a buffer, reusing the
                       chunked read path
  - firehose_pwrite()  write a buffer to a sector range, streaming it in
                       max_payload_size chunks
  - firehose_reset()   made non-static, out-of-tree callers can reuse it

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 09:34:14 +02:00
Igor Opaniuk 29629b784d usb: extract EDL device and interface matching helpers
usb_open_device() mixes three concerns in one hundred-line function:
deciding whether a USB device is an EDL candidate, locating the
vendor-specific EDL interface with its bulk endpoint pair, and
actually opening and claiming it. usb_open_once() and usb_list()
additionally duplicate the vid/pid gate inline, so the answer to
"what does qdl consider an EDL device" is currently scattered over
three call sites that can (and in the past did) drift apart.

Centralize the policy:

 - qdl_is_edl_pid()/qdl_is_edl_device() in qdl.h hold the shared,
   transport-agnostic identity check (Qualcomm vid, known EDL pids).
   Living in the common header, the same policy is available to the
   QUD backend, which parses pids from Windows hardware ids.
 - usb_is_edl_device() wraps it for libusb descriptor types and is
   now the single gate used by the open, scan and list paths.
 - usb_match_edl_interface() checks one interface descriptor for the
   ff/ff class/subclass signature and a known protocol, and extracts
   the bulk endpoint pair.

usb_open_device() shrinks to the actual open/claim sequence. Future
changes to device acceptance (for example crash-mode devices exposing
a different interface protocol or pid) become a one-line change in
exactly one place, applied consistently everywhere.

No functional change.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-16 16:51:54 -05:00
Igor Opaniuk b8e5a2f525 usb: rename open-path functions to clarify layering
try_usb_open() and usb_try_open() differ only in word order, which
makes the libusb open path needlessly hard to follow: one is a full
enumeration pass over the bus, the other opens a single candidate
device.

Rename them so the names encode the layering instead of hiding it:

  usb_open()        - backend .open op, wait loop (unchanged)
  usb_open_once()   - one enumeration pass (was try_usb_open())
  usb_open_device() - open a single candidate (was usb_try_open())

Document the resulting call chain, including the auto backend that
sits on top of usb_open_once() on Windows, at the top of usb.c.

No functional change.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-16 16:51:54 -05:00
Igor Opaniuk 47f420c011 sahara: add chipinfo command to read chip identity
QDL only ever drives Sahara in its image-transfer and memory-debug
modes. Sahara also defines a command mode that lets the primary
bootloader be queried for the device's identity before any programmer
is uploaded. Expose that as a new "chipinfo" subcommand.

It enters command mode by requesting SAHARA_MODE_COMMAND in the HELLO
response and echoing the device's advertised protocol version, then
drives EXECUTE transactions to read the chip serial number, the HW ID
(broken out into MSM_ID / OEM_ID / MODEL_ID) and the OEM PK hash.
Version 3 targets no longer answer MSM_HW_ID_READ, so the same fields
are recovered through READ_CHIP_ID_V3 on those devices.

Command mode is left by switching back to image-transfer mode, which
returns the device to its power-on HELLO state and keeps it usable for
a subsequent query or flash. A Sahara reset is deliberately avoided
here: on some targets it leaves the device enumerated on PID 0x9008 but
no longer answering Sahara until it is power-cycled back into EDL.

This is handy for identifying an attached target and, in particular,
for troubleshooting secure-boot provisioning: the fused OEM PK hash and
OEM ID can be read straight from the PBL without loading a programmer.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-13 19:25:46 +02:00
Igor Opaniuk 9f0a50bc4b tree: reorganize sources
The repository root had accumulated ~45 .c/.h files sitting directly
next to the project files (meson.build, scripts/, tests/, docs).

Move all sources under src/ and the public header to include/ so the
top level reflects the shape of the project rather than its source
file count.

This commit is a pure git mv with no content changes, so git log
--follow keeps working across the move.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-06-20 14:47:46 -05:00