summaryrefslogtreecommitdiffstats
path: root/server/lib/pci/index.js
blob: 282ad1fb9cb7090ed1a2eb95007857069ffa717e (plain) (blame)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* global __appdir */
var path = require('path')
const fs = require('fs')
const crypto = require('crypto')
const axios = require('axios')
const pciRepoApi = 'https://api.github.com/repos/pciutils/pciids/contents/'
const pciRepoDataApi = 'https://api.github.com/repos/pciutils/pciids/git/blobs/'
const pciIdsFilePath = path.join(__appdir, 'lib', 'pci', 'pci.ids')
var parsedPciList = {}
var parsedDeviceClasses = {}

// Non-Api way to the the raw file:
// const pciIdsUrl = 'https://github.com/pciutils/pciids/raw/master/pci.ids'

module.exports = {
  parseIds: async function (vendorId, deviceId, subvendorId, subdeviceId, classId, subclassId) {
    await updatePciIds()
    if (Object.keys(parsedPciList).length === 0) await parsePciList()

    let pciInformation = {
      'vendor': vendorId && vendorId.length === 4 ? {
        'id': vendorId,
        'name': parsedPciList[vendorId].name,
        'device': deviceId && deviceId.length === 4 ? {
          'id': deviceId,
          'name': deviceId ? parsedPciList[vendorId].devices[deviceId].name : undefined,
          'subvendor': subvendorId && subdeviceId && subvendorId.length === 4 && subdeviceId.length === 4 ? {
            'id': subvendorId,
            'name': parsedPciList[vendorId].devices[deviceId].subsystems[subvendorId][subdeviceId].name
          } : undefined
        } : undefined
      } : undefined,
      'class': classId && classId.length === 2 ? {
        'id': classId,
        'name': parsedDeviceClasses[classId].name,
        'subclass': subclassId && subclassId.length === 2 ? {
          'id': subclassId,
          'name': parsedDeviceClasses[classId].subclasses[subclassId].name
        } : undefined
      } : undefined
    }

    return pciInformation
  }
}

async function updatePciIds () {
  // Check if the file in the git is newer than the file created.

  let response = await axios.get(pciRepoApi)
  response = response.data

  // Get SHA of the pci.ids file
  const sha = response.filter(x => x.name === 'pci.ids')[0].sha

  if (fs.existsSync(pciIdsFilePath)) {
    /* Calculate own sha value
     * Git sha1 value of a file is calculated like this:
     * 'blob' + ' ' + content.length + '\0' + content
     */
    const pciids = fs.readFileSync(pciIdsFilePath)
    const blobprefix = 'blob' + ' ' + pciids.length + '\0'
    const sha1sum = crypto.createHash('sha1').update(blobprefix + pciids).digest('hex')

    // Exit if there isn't a new version
    if (sha === sha1sum) return
  }

  // Download the new list
  await downloadPciIds(sha)
}

async function downloadPciIds (sha) {
  // Get the newest version from the github api
  let request = await axios.get(pciRepoDataApi + sha)
  request = request.data

  // Decode base64 encoding
  const buffer = Buffer.from(request.content, 'base64')
  const file = buffer.toString('utf-8')

  // Override the content in the pci.ids file with the new one
  fs.writeFile(pciIdsFilePath, file, { flag: 'w' }, (err) => {
    if (err) throw err

    // Reparse the pci file
    parsePciList()
  })
}

/*
 * Syntax of the pci.ids file:
 *  # vendor  vendor_name
 *  #   device  device_name                     <-- single tab
 *  #      subvendor subdevice  subsystem_name  <-- two tabs
 *  parsed structure:
 *  {
 *      '<vendor_id>': {
 *          'name': '<vendor_name>',
 *          'devices': {
 *              '<device_id>': {
 *                  'name': '<device_name>',
 *                  'subsystems': {
 *                      '<subvendor_id>': {
 *                          '<subdevice_id>': {
 *                              'name': '<subsystem_name>'
 *                          }
 *                      }
 *                  }
 *              }
 *          }
 *      }
 *  }
 *
 *  # C class class_name
 *  #   subclass  subclass_name     <-- single tab
 *  #     prog-if  prog-if_name     <-- two tabs
 *  parsed structure:
 *  {
 *      '<class_id>': {
 *          'name': '<class_name>',
 *          'subclasses': {
 *              '<subclass_id>': {
 *                  'name': '<subclass_name>',
 *                  'interfaces': {
 *                      '<interface_id>': {
 *                        'name': '<interface_name>'
 *                      }
 *                  }
 *              }
 *          }
 *      }
 *  }
 */
function parsePciList () {
  let result = {}
  let deviceClasses = {}

  // Read file
  const lines = fs.readFileSync(pciIdsFilePath).toString().split('\n')
  const regexVendor = /^[0-9A-Fa-f]{4}/
  const regexDevice = /^\t[0-9A-Fa-f]{4}/
  const regexSubVD = /^\t\t[0-9A-Fa-f]{4}/

  const regexClass = /^C [0-9A-Fa-f]{2}/
  const regexSubclass = /^\t[0-9A-Fa-f]{2}/
  const regexProgIf = /^\t\t[0-9A-Fa-f]{2}/

  let lastVendorId = ''
  let lastDeviceId = ''
  let lastClassId = ''
  let lastSubClassId = ''

  for (let line of lines) {
    if (line.startsWith('#')) continue
    else if (line === '') continue
    else if (line.match(regexVendor)) {
      const vendor = line.substring(0, 4)
      const name = line.substring(6)
      result[vendor] = {
        'name': name,
        'devices': {}
      }
      lastVendorId = vendor
    } else if (line.match(regexDevice)) {
      const device = line.substring(1, 5)
      const name = line.substring(7)
      result[lastVendorId].devices[device] = {
        'name': name,
        'subsystems': {}
      }
      lastDeviceId = device
    } else if (line.match(regexSubVD)) {
      const subvendor = line.substring(2, 6)
      const subdevice = line.substring(7, 11)
      const subsystem = line.substring(13)

      if (result[lastVendorId].devices[lastDeviceId].subsystems[subvendor] === undefined) result[lastVendorId].devices[lastDeviceId].subsystems[subvendor] = {}
      result[lastVendorId].devices[lastDeviceId].subsystems[subvendor][subdevice] = {
        'name': subsystem
      }
    } else if (line.match(regexClass)) {
      deviceClasses[line.substring(2, 4)] = {
        'name': line.substring(6),
        'subclasses': {}
      }
      lastClassId = line.substring(2, 4)
    } else if (line.match(regexSubclass)) {
      deviceClasses[lastClassId].subclasses[line.substring(1, 3)] = {
        'name': line.substring(5),
        'interfaces': {}
      }
      lastSubClassId = line.substring(1, 3)
    } else if (line.match(regexProgIf)) {
      deviceClasses[lastClassId].subclasses[lastSubClassId].interfaces[line.substring(2, 4)] = {
        'name': line.substring(6)
      }
    }
  }
  parsedPciList = result
  parsedDeviceClasses = deviceClasses
}