From: Breno Rodrigues Alves <breno3011alves@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: Andy Whitcroft <apw@canonical.com>, Joe Perches <joe@perches.com>,
Dwaipayan Ray <dwaipayanray1@gmail.com>,
Lukas Bulwahn <lukas.bulwahn@gmail.com>,
Breno Rodrigues Alves <breno3011alves@gmail.com>
Subject: [RFC PATCH 3/7] scripts: convert extract_xc3028.pl to Python
Date: Sun, 18 Oct 2026 08:06:34 -0300 [thread overview]
Message-ID: <20261018110638.9947-4-breno3011alves@gmail.com> (raw)
In-Reply-To: <20261018110638.9947-1-breno3011alves@gmail.com>
Faithful re-implementation of extract_xc3028.pl in Python 3
(stdlib only). The firmware layout tables replace the
~1900 lines of repetitive write_leXX() calls with data;
byte-for-byte identical output for the same .sys input.
Signed-off-by: Breno Rodrigues Alves <breno3011alves@gmail.com>
---
| 284 ++++++++++++++++++++++++++++++++++++++
1 file changed, 284 insertions(+)
create mode 100755 scripts/extract_xc3028.py
--git a/scripts/extract_xc3028.py b/scripts/extract_xc3028.py
new file mode 100755
index 000000000..ef02b5d2f
--- /dev/null
+++ b/scripts/extract_xc3028.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (c) Mauro Carvalho Chehab <mchehab@kernel.org>
+#
+# Extract xc3028 firmware from vendor Windows drivers.
+#
+# 1) Download one of:
+# wget http://www.twinhan.com/files/AW/BDA T/20080303_V1.0.6.7.zip
+# wget http://www.stefanringel.de/pub/20080303_V1.0.6.7.zip
+# wget http://www.steventoth.net/linux/xc5000/HVR-12x0-14x0-17x0_1_25_25271_WHQL.zip
+# 2) unzip -j 20080303_V1.0.6.7.zip 20080303_v1.0.6.7/UDXTTM6000.sys
+# unzip -j HVR-12x0-14x0-17x0_1_25_25271_WHQL.zip Driver85/hcw85bda.sys
+# 3) ./extract_xc3028.py
+# 4) cp xc3028-v24.fw xc3028-v27.fw /lib/firmware
+
+import hashlib
+import shutil
+import struct
+import sys
+
+# Entry layout:
+# regular: (offset, length, type, id_hi, id_lo)
+# scode: (offset, length, type, id_hi, id_lo, if_mhz) # 5th=None => regular
+
+# Firmware 24 (from UDXTTM6000.sys, hash cb9deb5508a5e150af2880f5b0066d78)
+FW24 = [
+ (257752, 6635, 0x00000003, 0, 0),
+ (264392, 6635, 0x00000007, 0, 0),
+ (271040, 6525, 0x00000401, 0, 0),
+ (277568, 6539, 0x00000c01, 0, 0),
+ (284120, 6633, 0x00000001, 0, 0),
+ (290760, 6617, 0x00000005, 0, 0),
+ (297384, 161, 0x00000000, 0x1, 0x7),
+ (297552, 169, 0x00000004, 0x1, 0x7),
+ (297728, 161, 0x00000000, 0x2, 0x7),
+ (297896, 169, 0x00000004, 0x2, 0x7),
+ (298072, 161, 0x00000000, 0x4, 0x7),
+ (298240, 169, 0x00000004, 0x4, 0x7),
+ (298416, 161, 0x00000000, 0x8, 0x7),
+ (298584, 169, 0x00000004, 0x8, 0x7),
+ (298760, 161, 0x00000000, 0x3, 0xe0),
+ (298928, 169, 0x00000004, 0x3, 0xe0),
+ (299104, 161, 0x00000000, 0xc, 0xe0),
+ (299272, 169, 0x00000004, 0xc, 0xe0),
+ (299448, 161, 0x00000000, 0x0, 0x200000),
+ (299616, 169, 0x00000004, 0x0, 0x200000),
+ (299792, 161, 0x00000000, 0x0, 0x4000000),
+ (299960, 169, 0x00000004, 0x0, 0x4000000),
+ (300136, 149, 0x00010030, 0, 0),
+ (300296, 149, 0x00000068, 0, 0),
+ (300448, 149, 0x00000070, 0, 0),
+ (300608, 149, 0x00000088, 0, 0),
+ (300760, 149, 0x00000090, 0, 0),
+ (300920, 149, 0x00000108, 0, 0),
+ (301072, 149, 0x00000110, 0, 0),
+ (301232, 149, 0x00000208, 0, 0),
+ (301384, 149, 0x00000210, 0, 0),
+ (301554, 135, 0x00000400, 0, 0),
+ (301688, 161, 0x00000000, 0x0, 0x10),
+ (301856, 169, 0x00000004, 0x0, 0x10),
+ # 34 commented out in Perl
+ (302032, 161, 0x00000000, 0xc, 0x400000),
+ (302200, 161, 0x00000000, 0x0, 0x800000),
+ (302368, 161, 0x00000000, 0x0, 0x8000),
+ (302536, 161, 0x00001000, 0x0, 0x8000),
+ (302704, 161, 0x00003000, 0x0, 0x8000),
+ (302872, 169, 0x00000004, 0x0, 0x8000),
+ (303048, 161, 0x00000000, 0x0, 0xb700),
+ (303216, 161, 0x00001000, 0x0, 0xb700),
+ (303384, 161, 0x00003000, 0x0, 0xb700),
+ (303552, 161, 0x00000000, 0x0, 0x2000),
+ (303720, 169, 0x00000004, 0x0, 0xb700),
+ (303896, 169, 0x00001004, 0x0, 0xb700),
+ (304072, 169, 0x00003004, 0x0, 0xb700),
+ # SCODE: (offset, length, type, id_hi, id_lo, if_mhz)
+ (309048, 192, 0x60000000, 0x0, 0x0, 3280),
+ # 49 commented out
+ (309432, 192, 0x60000000, 0x0, 0x0, 3440),
+ (309624, 192, 0x60000000, 0x0, 0x0, 3460),
+ (306936, 192, 0x60210020, 0x0, 0x0, 3800),
+ (309240, 192, 0x60000000, 0x0, 0x0, 4000),
+ (307128, 192, 0x60410020, 0x0, 0x0, 4080),
+ (308856, 192, 0x60000000, 0x0, 0x0, 4200),
+ (305208, 192, 0x60008000, 0x0, 0x8000, 4320),
+ (309816, 192, 0x60000000, 0x0, 0x0, 4450),
+ (304824, 192, 0x6002b004, 0x0, 0xb700, 4500),
+ (305016, 192, 0x60023000, 0x0, 0x8000, 4600),
+ (304440, 192, 0x620003e0, 0x0, 0x0, 4760),
+ (308664, 192, 0x60000000, 0x0, 0x0, 4940),
+ (307704, 192, 0x60000000, 0x0, 0x0, 5260),
+ (307896, 192, 0x60008000, 0xf, 0x7, 5320),
+ (304248, 192, 0x65000380, 0x0, 0x0, 5400),
+ (306744, 192, 0x60110020, 0x0, 0x0, 5580),
+ (305592, 192, 0x60000000, 0x3, 0x7, 5640),
+ (305784, 192, 0x60000000, 0xc, 0x7, 5740),
+ (307512, 192, 0x60000000, 0x0, 0x0, 5900),
+ (305576, 192, 0x60008000, 0xc, 0x4c000f0, 6000),
+ (306552, 192, 0x68050060, 0x0, 0x0, 6200),
+ (305400, 192, 0x60000000, 0x0, 0x10, 6240),
+ (308472, 192, 0x60008000, 0x0, 0x200000, 6320),
+ (306360, 192, 0x60000000, 0x0, 0x200000, 6340),
+ (308280, 192, 0x60008000, 0xc, 0x44000e0, 6500),
+ (304632, 192, 0x60090020, 0x0, 0x0, 6580),
+ (306168, 192, 0x60000000, 0x3, 0xe0, 6600),
+ (308088, 192, 0x60008000, 0x3, 0xe0, 6680),
+ (307320, 192, 0x60810020, 0x0, 0x0, 8140),
+ # 79 commented out
+]
+
+# Firmware 27 (from hcw85bda.sys, hash 0e44dbf63bb0169d57446aec21881ff2)
+FW27 = [
+ (813432, 8718, 0x00000003, 0, 0),
+ (822152, 8712, 0x00000007, 0, 0),
+ (830872, 8562, 0x00000401, 0, 0),
+ (839440, 8576, 0x00000c01, 0, 0),
+ (848024, 8706, 0x00000001, 0, 0),
+ (856736, 8682, 0x00000005, 0, 0),
+ (865424, 161, 0x00000000, 0x1, 0x7),
+ (865592, 169, 0x00000004, 0x1, 0x7),
+ (865424, 161, 0x00000000, 0x2, 0x7),
+ (865592, 169, 0x00000004, 0x2, 0x7),
+ (866112, 161, 0x00000000, 0x4, 0x7),
+ (866280, 169, 0x00000004, 0x4, 0x7),
+ (866112, 161, 0x00000000, 0x8, 0x7),
+ (866280, 169, 0x00000004, 0x8, 0x7),
+ (866800, 161, 0x00000000, 0x3, 0xe0),
+ (866968, 169, 0x00000004, 0x3, 0xe0),
+ (867144, 161, 0x00000000, 0xc, 0xe0),
+ (867312, 169, 0x00000004, 0xc, 0xe0),
+ (867488, 161, 0x00000000, 0x0, 0x200000),
+ (867656, 169, 0x00000004, 0x0, 0x200000),
+ (867832, 161, 0x00000000, 0x0, 0x4000000),
+ (868000, 169, 0x00000004, 0x0, 0x4000000),
+ (868176, 149, 0x00010030, 0, 0),
+ (868336, 149, 0x00000068, 0, 0),
+ (868488, 149, 0x00000070, 0, 0),
+ (868648, 149, 0x00000088, 0, 0),
+ (868800, 149, 0x00000090, 0, 0),
+ (868960, 149, 0x00000108, 0, 0),
+ (869112, 149, 0x00000110, 0, 0),
+ (868648, 149, 0x00000208, 0, 0),
+ (868800, 149, 0x00000210, 0, 0),
+ (869584, 135, 0x00000400, 0, 0),
+ (869728, 161, 0x00000000, 0x0, 0x10),
+ (869896, 169, 0x00000004, 0x0, 0x10),
+ (870072, 169, 0x00000000, 0x10, 0x400000),
+ (870248, 161, 0x00000000, 0xc, 0x400000),
+ (870416, 161, 0x00000000, 0x0, 0x800000),
+ (870584, 161, 0x00000000, 0x0, 0x8000),
+ (870752, 161, 0x00001000, 0x0, 0x8000),
+ (870920, 161, 0x00003000, 0x0, 0x8000),
+ (871088, 169, 0x00000004, 0x0, 0x8000),
+ (871264, 161, 0x00000000, 0x0, 0xb700),
+ (871432, 161, 0x00001000, 0x0, 0xb700),
+ (871600, 161, 0x00003000, 0x0, 0xb700),
+ (871264, 161, 0x00000000, 0x0, 0x2000),
+ (871936, 169, 0x00000004, 0x0, 0xb700),
+ (872112, 169, 0x00001004, 0x0, 0xb700),
+ (872288, 169, 0x00003004, 0x0, 0xb700),
+ (811896, 192, 0x60000000, 0x0, 0x0, 3280),
+ (813048, 192, 0x60000000, 0x0, 0x0, 3300),
+ (812280, 192, 0x60000000, 0x0, 0x0, 3440),
+ (812472, 192, 0x60000000, 0x0, 0x0, 3460),
+ (809784, 192, 0x60210020, 0x0, 0x0, 3800),
+ (812088, 192, 0x60000000, 0x0, 0x0, 4000),
+ (809976, 192, 0x60410020, 0x0, 0x0, 4080),
+ (811704, 192, 0x60000000, 0x0, 0x0, 4200),
+ (808056, 192, 0x60008000, 0x0, 0x8000, 4320),
+ (812664, 192, 0x60000000, 0x0, 0x0, 4450),
+ (807672, 192, 0x6002b004, 0x0, 0xb700, 4500),
+ (807864, 192, 0x60023000, 0x0, 0x8000, 4600),
+ (807288, 192, 0x620003e0, 0x0, 0x0, 4760),
+ (811512, 192, 0x60000000, 0x0, 0x0, 4940),
+ (810552, 192, 0x60000000, 0x0, 0x0, 5260),
+ (810744, 192, 0x60008000, 0xf, 0x7, 5320),
+ (807096, 192, 0x65000380, 0x0, 0x0, 5400),
+ (809592, 192, 0x60110020, 0x0, 0x0, 5580),
+ (808440, 192, 0x60000000, 0x3, 0x7, 5640),
+ (808632, 192, 0x60000000, 0xc, 0x7, 5740),
+ (810360, 192, 0x60000000, 0x0, 0x0, 5900),
+ (808824, 192, 0x60008000, 0xc, 0x4c000f0, 6000),
+ (809400, 192, 0x68050060, 0x0, 0x0, 6200),
+ (808248, 192, 0x60000000, 0x0, 0x10, 6240),
+ (811320, 192, 0x60008000, 0x0, 0x200000, 6320),
+ (809208, 192, 0x60000000, 0x0, 0x200000, 6340),
+ (811128, 192, 0x60008000, 0xc, 0x44000e0, 6500),
+ (807480, 192, 0x60090020, 0x0, 0x0, 6580),
+ (809016, 192, 0x60000000, 0x3, 0xe0, 6600),
+ (810936, 192, 0x60008000, 0x3, 0xe0, 6680),
+ (810168, 192, 0x60810020, 0x0, 0x0, 8140),
+ (812856, 192, 0x60000000, 0x0, 0x0, 8200),
+]
+
+
+def verify(filename, expected_hash):
+ """Verify the md5 of the downloaded .sys file."""
+ try:
+ h = hashlib.md5()
+ with open(filename, 'rb') as f:
+ for chunk in iter(lambda: f.read(8192), b''):
+ h.update(chunk)
+ except OSError as e:
+ sys.exit(f"Cannot open {filename}: {e}")
+ if h.hexdigest() != expected_hash:
+ sys.exit(f"Hash of extracted file does not match "
+ f"(found {h.hexdigest()}, expected {expected_hash}!)")
+
+
+def get_hunk(infile, offset, length):
+ infile.seek(offset)
+ data = infile.read(length)
+ if len(data) != length:
+ sys.exit("Ran out of data")
+ return data
+
+
+def write_hunk(outfile, infile, offset, length):
+ outfile.write(get_hunk(infile, offset, length))
+
+
+def write_hunk_fix_endian(outfile, infile, offset, length):
+ """Read a length-prefixed sequence of endian-swapped records."""
+ data = get_hunk(infile, offset, length)
+ out = bytearray()
+ i = 0
+ while i < length:
+ size = data[i] * 256 + data[i + 1]
+ out += data[i + 1:i + 2] + data[i:i + 1]
+ i += 2
+ if 0 < size < 0x8000:
+ out += data[i:i + size]
+ i += size
+ outfile.write(out)
+
+
+def main_firmware(outfile_path, name, version, nr_desc, entries, infile):
+ """Write one complete firmware file."""
+ name_bytes = name.encode('ascii').ljust(32, b'\x00')
+ with open(outfile_path, 'wb') as out:
+ out.write(name_bytes)
+ out.write(struct.pack('<H', version))
+ out.write(struct.pack('<H', nr_desc))
+
+ for entry in entries:
+ offset, length, typ, id_hi, id_lo = entry[:5]
+ is_scode = len(entry) > 5
+ if_mhz = entry[5] if is_scode else None
+
+ out.write(struct.pack('<I', typ))
+ out.write(struct.pack('<Q', (id_hi << 32) | id_lo))
+ if is_scode:
+ out.write(struct.pack('<H', if_mhz))
+ out.write(struct.pack('<I', length))
+ if is_scode:
+ write_hunk(out, infile, offset, length)
+ else:
+ write_hunk_fix_endian(out, infile, offset, length)
+
+
+def extract_firmware():
+ targets = [
+ ('UDXTTM6000.sys',
+ 'cb9deb5508a5e150af2880f5b0066d78',
+ 'xc3028-v24.fw', 'xc2028 firmware', 516, 77, FW24),
+ ('hcw85bda.sys',
+ '0e44dbf63bb0169d57446aec21881ff2',
+ 'xc3028-v27.fw', 'xc2028 firmware', 519, 80, FW27),
+ ]
+ for sysfile, md5, outfile, name, version, nr_desc, entries in targets:
+ try:
+ with open(sysfile, 'rb') as infile:
+ verify(sysfile, md5)
+ # Count active (non-SCODE) entries for the header;
+ # the Perl passes 77/80 regardless, so use that.
+ main_firmware(outfile, name, version, nr_desc, entries,
+ infile)
+ except FileNotFoundError:
+ continue
+
+
+if __name__ == '__main__':
+ extract_firmware()
+ print("Firmwares generated.")
\ No newline at end of file
--
2.43.0
next prev parent reply other threads:[~2026-10-18 11:06 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-18 11:06 [RFC PATCH 0/7] scripts: convert seven more Perl utilities " Breno Rodrigues Alves
2026-10-18 11:06 ` [RFC PATCH 1/7] scripts: convert checkdeclares.pl " Breno Rodrigues Alves
2026-10-18 11:06 ` [RFC PATCH 2/7] scripts: convert checkversion.pl " Breno Rodrigues Alves
2026-10-18 11:06 ` Breno Rodrigues Alves [this message]
2026-10-18 11:06 ` [RFC PATCH 4/7] scripts: convert generate_initcall_order.pl " Breno Rodrigues Alves
2026-10-18 11:06 ` [RFC PATCH 5/7] scripts: convert get_maintainer.pl " Breno Rodrigues Alves
2026-10-18 11:06 ` [RFC PATCH 6/7] scripts: convert leaking_addresses.pl " Breno Rodrigues Alves
2026-10-18 11:06 ` [RFC PATCH 7/7] scripts: convert recordmcount.pl " Breno Rodrigues Alves
2026-10-18 20:59 ` [RFC PATCH 0/7] scripts: convert seven more Perl utilities " Jonathan Corbet
2026-10-18 23:19 ` Breno Rodrigues Alves
2026-10-19 10:05 ` Heiko Carstens
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261018110638.9947-4-breno3011alves@gmail.com \
--to=breno3011alves@gmail.com \
--cc=apw@canonical.com \
--cc=dwaipayanray1@gmail.com \
--cc=joe@perches.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lukas.bulwahn@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®