1#!/usr/bin/env python3
2#
3# make-pci-ids - Creates a file containing PCI IDs.
4# It use the databases from
5# https://github.com/pciutils/pciids/raw/master/pci.ids
6# to create our file epan/dissectors/pci-ids.c
7#
8# Wireshark - Network traffic analyzer
9#
10# By Caleb Chiu <caleb.chiu@macnica.com>
11# Copyright 2021
12#
13# SPDX-License-Identifier: GPL-2.0-or-later
14#
15
16import string
17import sys
18import urllib.request, urllib.error, urllib.parse
19
20OUTPUT_FILE = "epan/pci-ids.c"
21
22MIN_VENDOR_COUNT = 2250 # 2261 on 2021-11-01
23MIN_DEVICE_COUNT = 33000 # 33724 on 2021-11-01
24
25CODE_PREFIX = """\
26 *
27 * Generated by tools/make-pci-ids.py
28 * By Caleb Chiu <caleb.chiu@macnica.com>
29 * Copyright 2021
30 *
31 *
32 * SPDX-License-Identifier: GPL-2.0-or-later
33 */
34
35#include <config.h>
36#include \"pci-ids.h\"
37
38typedef struct
39{
40  guint16 vid;
41  guint16 did;
42  guint16 svid;
43  guint16 ssid;
44  gchar *name;
45
46} pci_id_t;
47
48typedef struct
49{
50  guint16 vid;
51  guint16 count;
52  pci_id_t *ids_ptr;
53
54} pci_vid_index_t;
55
56"""
57
58CODE_POSTFIX = """
59static pci_vid_index_t *get_vid_index(guint16 vid)
60{
61    guint32 start_index = 0;
62    guint32 end_index = 0;
63    guint32 idx = 0;
64
65    end_index = sizeof(pci_vid_index)/sizeof(pci_vid_index[0]);
66
67    while(start_index != end_index)
68    {
69        if(end_index - start_index == 1)
70        {
71            if(pci_vid_index[start_index].vid == vid)
72                return &pci_vid_index[start_index];
73
74            break;
75        }
76
77        idx = (start_index + end_index)/2;
78
79        if(pci_vid_index[idx].vid < vid)
80            start_index = idx;
81        else
82        if(pci_vid_index[idx].vid > vid)
83            end_index = idx;
84        else
85            return &pci_vid_index[idx];
86
87    }
88
89    return NULL;
90
91}
92
93const char *pci_id_str(guint16 vid, guint16 did, guint16 svid, guint16 ssid)
94{
95    unsigned int i;
96    static char *not_found = \"Not found\";
97    pci_vid_index_t *index_ptr;
98    pci_id_t *ids_ptr;
99
100    index_ptr = get_vid_index(vid);
101
102    if(index_ptr == NULL)
103        return not_found;
104
105    ids_ptr = index_ptr->ids_ptr;
106    for(i = 0; i < index_ptr->count; ids_ptr++, i++)
107        if(vid == ids_ptr->vid &&
108           did == ids_ptr->did &&
109           svid == ids_ptr->svid &&
110           ssid == ids_ptr->ssid)
111           return ids_ptr->name;
112    return  not_found;
113
114}
115"""
116
117
118id_list=[]
119count_list=[]
120
121
122def exit_msg(msg=None, status=1):
123    if msg is not None:
124        sys.stderr.write(msg + '\n')
125    sys.exit(status)
126
127
128def main():
129    req_headers = { 'User-Agent': 'Wireshark make-pci-ids' }
130    req = urllib.request.Request('https://github.com/pciutils/pciids/raw/master/pci.ids', headers=req_headers)
131    response = urllib.request.urlopen(req)
132    lines = response.read().decode('UTF-8', 'replace').splitlines()
133
134    out_lines = '''\
135/* pci-ids.c
136 *
137 * pci-ids.c is based on the pci.ids of The PCI ID Repository at
138 * https://pci-ids.ucw.cz/, fetched indirectly via
139 * https://github.com/pciutils/pciids
140'''
141    vid = -1
142    did = -1
143    svid = -1
144    entries = 0
145    line_num = 0
146
147    for line in lines:
148        line = line.strip('\n')
149        line_num += 1
150
151        if line_num <= 15:
152            line = line.replace('#', ' ', 1)
153            line = line.lstrip()
154            line = line.replace("GNU General Public License", "GPL")
155            if line:
156                line = ' * ' + line
157            else:
158                line = ' *' + line
159            out_lines += line + '\n'
160        if line_num == 15:
161            out_lines += CODE_PREFIX
162
163        line = line.replace("\\","\\\\")
164        line = line.replace("\"","\\\"")
165        line = line.replace("?","?-")
166        tabs = len(line) - len(line.lstrip('\t'))
167        if tabs == 0:
168            #print line
169            words = line.split(" ", 1)
170            if len(words) < 2:
171                continue
172            if len(words[0]) != 4:
173                continue
174            if all(c in string.hexdigits for c in words[0]):
175                hex_int = int(words[0], 16)
176                if vid != -1:
177                    out_lines += "}; /* pci_vid_%04X[] */\n\n" % (vid)
178                    count_list.append(entries)
179                vid = hex_int
180                entries = 1
181                did = -1
182                svid = -1
183                ssid = -1
184                out_lines += "pci_id_t pci_vid_%04X[] = {\n" % (vid)
185                out_lines += "{0x%04X, 0xFFFF, 0xFFFF, 0xFFFF, \"%s(0x%04X)\"},\n" % (vid, words[1].strip(), vid)
186                id_list.append(vid)
187                continue
188
189        if tabs == 1:
190            line = line.strip('\t')
191            words = line.split(" ", 1)
192            if len(words) < 2:
193                continue
194            if len(words[0]) != 4:
195                continue
196            if all(c in string.hexdigits for c in words[0]):
197                hex_int = int(words[0], 16)
198                did = hex_int
199                svid = -1
200                ssid = -1
201                out_lines += "{0x%04X, 0x%04X, 0xFFFF, 0xFFFF, \"%s(0x%04X)\"},\n" % (vid, did, words[1].strip(), did)
202                entries += 1
203                continue
204
205        if tabs == 2:
206            line = line.strip('\t')
207            words = line.split(" ", 2)
208            if len(words[0]) != 4:
209                continue
210            if all(c in string.hexdigits for c in words[0]):
211                hex_int = int(words[0], 16)
212                svid = hex_int
213
214            if all(c in string.hexdigits for c in words[1]):
215                hex_int = int(words[1], 16)
216                ssid = hex_int
217
218            out_lines += "{0x%04X, 0x%04X, 0x%04X, 0x%04X, \"%s(0x%04X-0x%04X)\"},\n" % (vid, did, svid, ssid, words[2].strip(), svid, ssid)
219            entries += 1
220            svid = -1
221            ssid = -1
222            continue
223
224    out_lines += "}; /* pci_vid_%04X[] */\n" % (vid)
225    count_list.append(entries)
226
227    out_lines += "\npci_vid_index_t pci_vid_index[] = {\n"
228
229    vendor_count = len(id_list)
230    device_count = 0
231    for i in range(vendor_count):
232        out_lines += "{0x%04X, %d, pci_vid_%04X },\n" % (id_list[i], count_list[i], id_list[i])
233        device_count += count_list[i]
234
235    out_lines += "}; /* We have %d VIDs */\n" % (vendor_count)
236
237    out_lines += CODE_POSTFIX
238
239    if vendor_count < MIN_VENDOR_COUNT:
240        exit_msg(f'Too view vendors. Wanted {MIN_VENDOR_COUNT}, got {vendor_count}.')
241
242    if device_count < MIN_DEVICE_COUNT:
243        exit_msg(f'Too view devices. Wanted {MIN_DEVICE_COUNT}, got {device_count}.')
244
245    with open(OUTPUT_FILE, "w", encoding="utf-8") as pci_ids_f:
246        pci_ids_f.write(out_lines)
247
248if __name__ == '__main__':
249    main()
250