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