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