Skip to content

Commit 3e7950b

Browse files
svenpeter42marcan
authored andcommitted
firmware.bluetooth: Add Bluetooth firmware extraction
Signed-off-by: Sven Peter <[email protected]>
1 parent 16f8428 commit 3e7950b

2 files changed

Lines changed: 127 additions & 1 deletion

File tree

src/firmware/bluetooth.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# SPDX-License-Identifier: MIT
2+
import logging, os, os.path, re, sys
3+
from collections import namedtuple, defaultdict
4+
5+
from . import FWFile
6+
7+
BluetoothChip = namedtuple(
8+
"BluetoothChip", ("chip", "stepping", "board_type", "vendor")
9+
)
10+
11+
12+
class BluetoothFWCollection(object):
13+
VENDORMAP = {
14+
"MUR": "m",
15+
"USI": "u",
16+
"GEN": None,
17+
}
18+
19+
def __init__(self, source_path):
20+
self.fwfiles = defaultdict(lambda: [None, None])
21+
self.load(source_path)
22+
23+
def load(self, source_path):
24+
for fname in os.listdir(source_path):
25+
root, ext = os.path.splitext(fname)
26+
27+
# index for bin and ptb inside self.fwfiles
28+
if ext == ".bin":
29+
idx = 0
30+
elif ext == ".ptb":
31+
idx = 1
32+
else:
33+
# skip firmware for older (UART) chips
34+
continue
35+
36+
# skip T2 _DEV firmware
37+
if "_DEV" in root:
38+
continue
39+
40+
chip = self.parse_fname(root)
41+
if chip is None:
42+
continue
43+
44+
if self.fwfiles[chip][idx] is not None:
45+
logging.warning(f"duplicate entry for {chip}: {self.fwfiles[chip][idx].name} and now {fname + ext}")
46+
continue
47+
48+
path = os.path.join(source_path, fname)
49+
with open(path, "rb") as f:
50+
data = f.read()
51+
52+
self.fwfiles[chip][idx] = FWFile(fname, data)
53+
54+
def parse_fname(self, fname):
55+
fname = fname.split("_")
56+
57+
match = re.fullmatch("bcm(43[0-9]{2})([a-z][0-9])", fname[0].lower())
58+
if not match:
59+
logging.warning(f"Unexpected firmware file: {fname}")
60+
return None
61+
chip, stepping = match.groups()
62+
63+
# board type is either preceeded by PCIE_macOS or by PCIE
64+
try:
65+
pcie_offset = fname.index("PCIE")
66+
except:
67+
logging.warning(f"Can't find board type in {fname}")
68+
return None
69+
70+
if fname[pcie_offset + 1] == "macOS":
71+
board_type = fname[pcie_offset + 2]
72+
else:
73+
board_type = fname[pcie_offset + 1]
74+
board_type = "apple," + board_type.lower()
75+
76+
# make sure we can identify exactly one vendor
77+
otp_values = set()
78+
for vendor, otp_value in self.VENDORMAP.items():
79+
if vendor in fname:
80+
otp_values.add(otp_value)
81+
if len(otp_values) != 1:
82+
logging.warning(f"Unable to determine vendor ({otp_values}) in {fname}")
83+
return None
84+
vendor = otp_values.pop()
85+
86+
return BluetoothChip(
87+
chip=chip, stepping=stepping, board_type=board_type, vendor=vendor
88+
)
89+
90+
def files(self):
91+
for chip, (bin, ptb) in self.fwfiles.items():
92+
fname_base = f"brcm/brcmbt{chip.chip}{chip.stepping}-{chip.board_type}"
93+
if chip.vendor is not None:
94+
fname_base += f"-{chip.vendor}"
95+
96+
if bin is None:
97+
logging.warning(f"no bin for {chip}")
98+
continue
99+
else:
100+
yield fname_base + ".bin", bin
101+
102+
if ptb is None:
103+
logging.warning(f"no ptb for {chip}")
104+
continue
105+
else:
106+
yield fname_base + ".ptb", ptb
107+
108+
109+
if __name__ == "__main__":
110+
col = BluetoothFWCollection(sys.argv[1])
111+
112+
if len(sys.argv) > 2:
113+
from . import FWPackage
114+
115+
pkg = FWPackage(sys.argv[2])
116+
pkg.add_files(sorted(col.files()))
117+
pkg.close()
118+
119+
for i in pkg.manifest:
120+
print(i)
121+
else:
122+
for name, fwfile in col.files():
123+
print(name, f"{fwfile.name} ({len(fwfile.data)} bytes)")

src/stub.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: MIT
22
import os, os.path, plistlib, shutil, sys, stat, subprocess, urlcache, zipfile, logging, json
3-
import osenum, firmware.wifi
3+
import osenum, firmware.wifi, firmware.bluetooth
44
from util import *
55

66
class StubInstaller(PackageInstaller):
@@ -324,6 +324,9 @@ def collect_firmware(self, pkg):
324324
logging.info("Collecting WiFi firmware")
325325
col = firmware.wifi.WiFiFWCollection("recovery/usr/share/firmware/wifi/")
326326
pkg.add_files(sorted(col.files()))
327+
logging.info("Collecting Bluetooth firmware")
328+
col = firmware.bluetooth.BluetoothFWCollection("recovery/usr/share/firmware/bluetooth/")
329+
pkg.add_files(sorted(col.files()))
327330
logging.info("Making fallback firmware archive")
328331
subprocess.run(["tar", "czf", "all_firmware.tar.gz",
329332
"fud_firmware",

0 commit comments

Comments
 (0)