]>
Commit | Line | Data |
---|---|---|
3a58a430 | 1 | #!/usr/bin/env python3 |
703c70cc | 2 | # SPDX-License-Identifier: LGPL-2.1-or-later |
3a58a430 ZJS |
3 | |
4 | import re | |
5 | import sys | |
6 | from pyparsing import (Word, White, Literal, Regex, | |
7 | LineEnd, SkipTo, | |
8 | ZeroOrMore, OneOrMore, Combine, Optional, Suppress, | |
f73d6895 | 9 | Group, ParserElement, |
3a58a430 ZJS |
10 | stringEnd, pythonStyleComment) |
11 | ||
12 | EOL = LineEnd().suppress() | |
13 | NUM1 = Word('0123456789abcdefABCDEF', exact=1) | |
14 | NUM2 = Word('0123456789abcdefABCDEF', exact=2) | |
15 | NUM3 = Word('0123456789abcdefABCDEF', exact=3) | |
16 | NUM4 = Word('0123456789abcdefABCDEF', exact=4) | |
17 | NUM6 = Word('0123456789abcdefABCDEF', exact=6) | |
18 | TAB = White('\t', exact=1).suppress() | |
19 | COMMENTLINE = pythonStyleComment + EOL | |
20 | EMPTYLINE = LineEnd() | |
21 | text_eol = lambda name: Regex(r'[^\n]+')(name) + EOL | |
3a58a430 | 22 | |
133a0003 | 23 | ParserElement.setDefaultWhitespaceChars(' \n') |
f73d6895 | 24 | |
3a58a430 ZJS |
25 | def klass_grammar(): |
26 | klass_line = Literal('C ').suppress() + NUM2('klass') + text_eol('text') | |
27 | subclass_line = TAB + NUM2('subclass') + text_eol('text') | |
28 | protocol_line = TAB + TAB + NUM2('protocol') + text_eol('name') | |
29 | subclass = (subclass_line('SUBCLASS') - | |
2615de9d | 30 | ZeroOrMore(Group(protocol_line)('PROTOCOLS*') |
3a58a430 ZJS |
31 | ^ COMMENTLINE.suppress())) |
32 | klass = (klass_line('KLASS') - | |
2615de9d | 33 | ZeroOrMore(Group(subclass)('SUBCLASSES*') |
3a58a430 ZJS |
34 | ^ COMMENTLINE.suppress())) |
35 | return klass | |
36 | ||
37 | def usb_ids_grammar(): | |
38 | vendor_line = NUM4('vendor') + text_eol('text') | |
39 | device_line = TAB + NUM4('device') + text_eol('text') | |
f73d6895 ZJS |
40 | interface_line = TAB + TAB + NUM4('interface') + NUM4('interface2') + text_eol('text') |
41 | device = (device_line + | |
42 | ZeroOrMore(Group(interface_line) | |
43 | ^ COMMENTLINE.suppress())) | |
3a58a430 | 44 | vendor = (vendor_line('VENDOR') + |
f73d6895 | 45 | ZeroOrMore(Group(device)('VENDOR_DEV*') ^ COMMENTLINE.suppress())) |
3a58a430 ZJS |
46 | |
47 | klass = klass_grammar() | |
48 | ||
49 | other_line = (Literal('AT ') ^ Literal('HID ') ^ Literal('R ') | |
50 | ^ Literal('PHY ') ^ Literal('BIAS ') ^ Literal('HUT ') | |
51 | ^ Literal('L ') ^ Literal('VT ') ^ Literal('HCC ')) + text_eol('text') | |
52 | other_group = (other_line - ZeroOrMore(TAB + text_eol('text'))) | |
53 | ||
54 | commentgroup = OneOrMore(COMMENTLINE).suppress() ^ EMPTYLINE.suppress() | |
2615de9d ZJS |
55 | grammar = OneOrMore(Group(vendor)('VENDORS*') |
56 | ^ Group(klass)('CLASSES*') | |
3a58a430 ZJS |
57 | ^ other_group.suppress() ^ commentgroup) + stringEnd() |
58 | ||
59 | grammar.parseWithTabs() | |
60 | return grammar | |
61 | ||
62 | def pci_ids_grammar(): | |
63 | vendor_line = NUM4('vendor') + text_eol('text') | |
64 | device_line = TAB + NUM4('device') + text_eol('text') | |
65 | subvendor_line = TAB + TAB + NUM4('a') + White(' ') + NUM4('b') + text_eol('name') | |
66 | ||
67 | device = (device_line('DEVICE') + | |
2615de9d | 68 | ZeroOrMore(Group(subvendor_line)('SUBVENDORS*') ^ COMMENTLINE.suppress())) |
3a58a430 | 69 | vendor = (vendor_line('VENDOR') + |
2615de9d | 70 | ZeroOrMore(Group(device)('DEVICES*') ^ COMMENTLINE.suppress())) |
3a58a430 ZJS |
71 | |
72 | klass = klass_grammar() | |
73 | ||
74 | commentgroup = OneOrMore(COMMENTLINE).suppress() ^ EMPTYLINE.suppress() | |
2615de9d ZJS |
75 | grammar = OneOrMore(Group(vendor)('VENDORS*') |
76 | ^ Group(klass)('CLASSES*') | |
3a58a430 ZJS |
77 | ^ commentgroup) + stringEnd() |
78 | ||
79 | grammar.parseWithTabs() | |
80 | return grammar | |
81 | ||
82 | def sdio_ids_grammar(): | |
83 | vendor_line = NUM4('vendor') + text_eol('text') | |
84 | device_line = TAB + NUM4('device') + text_eol('text') | |
85 | vendor = (vendor_line('VENDOR') + | |
2615de9d | 86 | ZeroOrMore(Group(device_line)('DEVICES*') ^ COMMENTLINE.suppress())) |
3a58a430 ZJS |
87 | |
88 | klass = klass_grammar() | |
89 | ||
90 | commentgroup = OneOrMore(COMMENTLINE).suppress() ^ EMPTYLINE.suppress() | |
2615de9d ZJS |
91 | grammar = OneOrMore(Group(vendor)('VENDORS*') |
92 | ^ Group(klass)('CLASSES*') | |
93 | ^ commentgroup) + stringEnd() | |
3a58a430 ZJS |
94 | |
95 | grammar.parseWithTabs() | |
96 | return grammar | |
97 | ||
98 | def oui_grammar(type): | |
99 | prefix_line = (Combine(NUM2 - Suppress('-') - NUM2 - Suppress('-') - NUM2)('prefix') | |
100 | - Literal('(hex)') - text_eol('text')) | |
101 | if type == 'small': | |
102 | vendor_line = (NUM3('start') - '000-' - NUM3('end') - 'FFF' | |
103 | - Literal('(base 16)') - text_eol('text2')) | |
104 | elif type == 'medium': | |
105 | vendor_line = (NUM1('start') - '00000-' - NUM1('end') - 'FFFFF' | |
106 | - Literal('(base 16)') - text_eol('text2')) | |
107 | else: | |
108 | assert type == 'large' | |
109 | vendor_line = (NUM6('start') | |
110 | - Literal('(base 16)') - text_eol('text2')) | |
111 | ||
112 | extra_line = TAB - TAB - TAB - TAB - SkipTo(EOL) | |
113 | vendor = prefix_line + vendor_line + ZeroOrMore(extra_line) + Optional(EMPTYLINE) | |
114 | ||
115 | grammar = (Literal('OUI') + text_eol('header') | |
116 | + text_eol('header') + text_eol('header') + EMPTYLINE | |
2615de9d | 117 | + OneOrMore(Group(vendor)('VENDORS*')) + stringEnd()) |
3a58a430 ZJS |
118 | |
119 | grammar.parseWithTabs() | |
120 | return grammar | |
121 | ||
122 | ||
123 | def header(file, *sources): | |
124 | print('''\ | |
125 | # This file is part of systemd. | |
126 | # | |
127 | # Data imported from:{}{}'''.format(' ' if len(sources) == 1 else '\n# ', | |
128 | '\n# '.join(sources)), | |
129 | file=file) | |
130 | ||
e4804bda ZJS |
131 | def add_item(items, key, value): |
132 | if key in items: | |
133 | print(f'Ignoring duplicate entry: {key} = "{items[key]}", "{value}"') | |
134 | else: | |
135 | items[key] = value | |
136 | ||
3a58a430 | 137 | def usb_vendor_model(p): |
53f4c5fa ZJS |
138 | items = {} |
139 | ||
140 | for vendor_group in p.VENDORS: | |
2615de9d ZJS |
141 | vendor = vendor_group.vendor.upper() |
142 | text = vendor_group.text.strip() | |
53f4c5fa ZJS |
143 | add_item(items, (vendor,), text) |
144 | ||
145 | for vendor_dev in vendor_group.VENDOR_DEV: | |
146 | device = vendor_dev.device.upper() | |
147 | text = vendor_dev.text.strip() | |
148 | add_item(items, (vendor, device), text) | |
149 | ||
3a58a430 ZJS |
150 | with open('20-usb-vendor-model.hwdb', 'wt') as out: |
151 | header(out, 'http://www.linux-usb.org/usb.ids') | |
152 | ||
53f4c5fa ZJS |
153 | for key in sorted(items): |
154 | if len(key) == 1: | |
155 | p, n = 'usb:v{}*', 'VENDOR' | |
156 | else: | |
157 | p, n = 'usb:v{}p{}*', 'MODEL', | |
158 | print('', p.format(*key), | |
159 | f' ID_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) | |
160 | ||
3a58a430 ZJS |
161 | print(f'Wrote {out.name}') |
162 | ||
163 | def usb_classes(p): | |
53f4c5fa ZJS |
164 | items = {} |
165 | ||
166 | for klass_group in p.CLASSES: | |
2615de9d ZJS |
167 | klass = klass_group.klass.upper() |
168 | text = klass_group.text.strip() | |
53f4c5fa ZJS |
169 | |
170 | if klass != '00' and not re.match(r'(\?|None|Unused)\s*$', text): | |
171 | add_item(items, (klass,), text) | |
172 | ||
173 | for subclass_group in klass_group.SUBCLASSES: | |
174 | subclass = subclass_group.subclass.upper() | |
175 | text = subclass_group.text.strip() | |
176 | if subclass != '00' and not re.match(r'(\?|None|Unused)\s*$', text): | |
177 | add_item(items, (klass, subclass), text) | |
178 | ||
179 | for protocol_group in subclass_group.PROTOCOLS: | |
180 | protocol = protocol_group.protocol.upper() | |
181 | text = protocol_group.name.strip() | |
182 | if klass != '00' and not re.match(r'(\?|None|Unused)\s*$', text): | |
183 | add_item(items, (klass, subclass, protocol), text) | |
184 | ||
3a58a430 ZJS |
185 | with open('20-usb-classes.hwdb', 'wt') as out: |
186 | header(out, 'http://www.linux-usb.org/usb.ids') | |
187 | ||
53f4c5fa ZJS |
188 | for key in sorted(items): |
189 | if len(key) == 1: | |
190 | p, n = 'usb:v*p*d*dc{}*', 'CLASS' | |
191 | elif len(key) == 2: | |
192 | p, n = 'usb:v*p*d*dc{}dsc{}*', 'SUBCLASS' | |
193 | else: | |
194 | p, n = 'usb:v*p*d*dc{}dsc{}dp{}*', 'PROTOCOL' | |
195 | print('', p.format(*key), | |
196 | f' ID_USB_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) | |
197 | ||
3a58a430 ZJS |
198 | print(f'Wrote {out.name}') |
199 | ||
200 | def pci_vendor_model(p): | |
0a91fd3b ZJS |
201 | items = {} |
202 | ||
203 | for vendor_group in p.VENDORS: | |
2615de9d ZJS |
204 | vendor = vendor_group.vendor.upper() |
205 | text = vendor_group.text.strip() | |
0a91fd3b ZJS |
206 | add_item(items, (vendor,), text) |
207 | ||
208 | for device_group in vendor_group.DEVICES: | |
209 | device = device_group.device.upper() | |
210 | text = device_group.text.strip() | |
211 | add_item(items, (vendor, device), text) | |
212 | ||
213 | for subvendor_group in device_group.SUBVENDORS: | |
214 | sub_vendor = subvendor_group.a.upper() | |
215 | sub_model = subvendor_group.b.upper() | |
216 | sub_text = subvendor_group.name.strip() | |
217 | if sub_text.startswith(text): | |
218 | sub_text = sub_text[len(text):].lstrip() | |
219 | if sub_text: | |
220 | sub_text = f' ({sub_text})' | |
221 | add_item(items, (vendor, device, sub_vendor, sub_model), text + sub_text) | |
222 | ||
3a58a430 ZJS |
223 | with open('20-pci-vendor-model.hwdb', 'wt') as out: |
224 | header(out, 'http://pci-ids.ucw.cz/v2.2/pci.ids') | |
225 | ||
0a91fd3b ZJS |
226 | for key in sorted(items): |
227 | if len(key) == 1: | |
228 | p, n = 'pci:v0000{}*', 'VENDOR' | |
229 | elif len(key) == 2: | |
230 | p, n = 'pci:v0000{}d0000{}*', 'MODEL' | |
231 | else: | |
232 | p, n = 'pci:v0000{}d0000{}sv0000{}sd0000{}*', 'MODEL' | |
233 | print('', p.format(*key), | |
234 | f' ID_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) | |
3a58a430 | 235 | |
3a58a430 ZJS |
236 | print(f'Wrote {out.name}') |
237 | ||
238 | def pci_classes(p): | |
0a91fd3b | 239 | items = {} |
3a58a430 | 240 | |
0a91fd3b | 241 | for klass_group in p.CLASSES: |
2615de9d ZJS |
242 | klass = klass_group.klass.upper() |
243 | text = klass_group.text.strip() | |
0a91fd3b | 244 | add_item(items, (klass,), text) |
3a58a430 | 245 | |
0a91fd3b ZJS |
246 | for subclass_group in klass_group.SUBCLASSES: |
247 | subclass = subclass_group.subclass.upper() | |
248 | text = subclass_group.text.strip() | |
249 | add_item(items, (klass, subclass), text) | |
3a58a430 | 250 | |
0a91fd3b ZJS |
251 | for protocol_group in subclass_group.PROTOCOLS: |
252 | protocol = protocol_group.protocol.upper() | |
253 | text = protocol_group.name.strip() | |
254 | add_item(items, (klass, subclass, protocol), text) | |
255 | ||
256 | with open('20-pci-classes.hwdb', 'wt') as out: | |
257 | header(out, 'http://pci-ids.ucw.cz/v2.2/pci.ids') | |
258 | ||
259 | for key in sorted(items): | |
260 | if len(key) == 1: | |
261 | p, n = 'pci:v*d*sv*sd*bc{}*', 'CLASS' | |
262 | elif len(key) == 2: | |
263 | p, n = 'pci:v*d*sv*sd*bc{}sc{}*', 'SUBCLASS' | |
264 | else: | |
265 | p, n = 'pci:v*d*sv*sd*bc{}sc{}i{}*', 'INTERFACE' | |
266 | print('', p.format(*key), | |
267 | f' ID_PCI_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) | |
3a58a430 | 268 | |
3a58a430 ZJS |
269 | print(f'Wrote {out.name}') |
270 | ||
271 | def sdio_vendor_model(p): | |
e979e502 ZJS |
272 | items = {} |
273 | ||
274 | for vendor_group in p.VENDORS: | |
2615de9d ZJS |
275 | vendor = vendor_group.vendor.upper() |
276 | text = vendor_group.text.strip() | |
e979e502 ZJS |
277 | add_item(items, (vendor,), text) |
278 | ||
279 | for device_group in vendor_group.DEVICES: | |
280 | device = device_group.device.upper() | |
281 | text = device_group.text.strip() | |
282 | add_item(items, (vendor, device), text) | |
283 | ||
3a58a430 | 284 | with open('20-sdio-vendor-model.hwdb', 'wt') as out: |
4f10b807 | 285 | header(out, 'hwdb.d/sdio.ids') |
3a58a430 | 286 | |
e979e502 ZJS |
287 | for key in sorted(items): |
288 | if len(key) == 1: | |
289 | p, n = 'sdio:c*v{}*', 'VENDOR' | |
290 | else: | |
291 | p, n = 'sdio:c*v{}d{}*', 'MODEL' | |
292 | print('', p.format(*key), | |
293 | f' ID_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) | |
3a58a430 | 294 | |
3a58a430 ZJS |
295 | print(f'Wrote {out.name}') |
296 | ||
297 | def sdio_classes(p): | |
e979e502 ZJS |
298 | items = {} |
299 | ||
300 | for klass_group in p.CLASSES: | |
2615de9d ZJS |
301 | klass = klass_group.klass.upper() |
302 | text = klass_group.text.strip() | |
e979e502 ZJS |
303 | add_item(items, klass, text) |
304 | ||
3a58a430 | 305 | with open('20-sdio-classes.hwdb', 'wt') as out: |
4f10b807 | 306 | header(out, 'hwdb.d/sdio.ids') |
3a58a430 | 307 | |
e979e502 | 308 | for klass in sorted(items): |
3a58a430 ZJS |
309 | print(f'', |
310 | f'sdio:c{klass}v*d*', | |
e979e502 ZJS |
311 | f' ID_SDIO_CLASS_FROM_DATABASE={items[klass]}', sep='\n', file=out) |
312 | ||
3a58a430 ZJS |
313 | print(f'Wrote {out.name}') |
314 | ||
315 | # MAC Address Block Large/Medium/Small | |
316 | # Large MA-L 24/24 bit (OUI) | |
317 | # Medium MA-M 28/20 bit (OUI prefix owned by IEEE) | |
318 | # Small MA-S 36/12 bit (OUI prefix owned by IEEE) | |
319 | def oui(p1, p2, p3): | |
e4804bda ZJS |
320 | prefixes = set() |
321 | items = {} | |
322 | ||
323 | for p, check in ((p1, False), (p2, False), (p3, True)): | |
324 | for vendor_group in p.VENDORS: | |
325 | prefix = vendor_group.prefix.upper() | |
326 | if check: | |
327 | if prefix in prefixes: | |
328 | continue | |
329 | else: | |
330 | prefixes.add(prefix) | |
331 | start = vendor_group.start.upper() | |
332 | end = vendor_group.end.upper() | |
333 | ||
334 | if end and start != end: | |
335 | print(f'{prefix:} {start} != {end}', file=sys.stderr) | |
336 | text = vendor_group.text.strip() | |
337 | ||
338 | key = prefix + start if end else prefix | |
339 | add_item(items, key, text) | |
340 | ||
3a58a430 ZJS |
341 | with open('20-OUI.hwdb', 'wt') as out: |
342 | header(out, | |
343 | 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-L&format=txt', | |
344 | 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-M&format=txt', | |
345 | 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-S&format=txt') | |
346 | ||
e4804bda ZJS |
347 | for pattern in sorted(items): |
348 | print(f'', | |
349 | f'OUI:{pattern}*', | |
350 | f' ID_OUI_FROM_DATABASE={items[pattern]}', sep='\n', file=out) | |
3a58a430 | 351 | |
3a58a430 ZJS |
352 | print(f'Wrote {out.name}') |
353 | ||
354 | if __name__ == '__main__': | |
53d22844 ZJS |
355 | args = sys.argv[1:] |
356 | ||
357 | if not args or 'usb' in args: | |
af49df62 | 358 | p = usb_ids_grammar().parseFile(open('usb.ids', errors='replace')) |
53d22844 ZJS |
359 | usb_vendor_model(p) |
360 | usb_classes(p) | |
361 | ||
362 | if not args or 'pci' in args: | |
af49df62 | 363 | p = pci_ids_grammar().parseFile(open('pci.ids', errors='replace')) |
53d22844 ZJS |
364 | pci_vendor_model(p) |
365 | pci_classes(p) | |
366 | ||
367 | if not args or 'sdio' in args: | |
af49df62 | 368 | p = pci_ids_grammar().parseFile(open('sdio.ids', errors='replace')) |
53d22844 ZJS |
369 | sdio_vendor_model(p) |
370 | sdio_classes(p) | |
371 | ||
372 | if not args or 'oui' in args: | |
373 | p = oui_grammar('small').parseFile(open('ma-small.txt')) | |
374 | p2 = oui_grammar('medium').parseFile(open('ma-medium.txt')) | |
375 | p3 = oui_grammar('large').parseFile(open('ma-large.txt')) | |
376 | oui(p, p2, p3) |