Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion example.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
pci_vendor_id = '1002'
pci_device_id = '687f'
pci_subsystem_id = '1043:04c4'

pci_class = "0c"
pci_subclass = "07"
pci_prog_if = "02"
usb_vendor_id = '03f0'
usb_device_id = '1f12'

Expand All @@ -16,6 +18,9 @@
print("Vendor: %s" % pci.get_vendor(pci_vendor_id))
print("Device: %s" % pci.get_device(pci_vendor_id, pci_device_id))
print("Subsystem: %s" % pci.get_subsystem(pci_vendor_id, pci_device_id, pci_subsystem_id))
print("Class: %s" % pci.get_class(pci_class))
print("subclass: %s" % pci.get_subclass(pci_class, pci_subclass))
print("prog_if: %s" % pci.get_prog_if(pci_class, pci_subclass, pci_prog_if))


usb = USB()
Expand Down
104 changes: 99 additions & 5 deletions hwdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# pylint: disable=misplaced-bare-raise

class USB:
""" Interace to usb.ids from hwdata package """
""" Interface to usb.ids from hwdata package """
filename = '/usr/share/hwdata/usb.ids'
devices = None

Expand Down Expand Up @@ -103,9 +103,10 @@ def get_device(self, vendor, device):
raise NotImplementedError()

class PCI:
""" Interace to pci.ids from hwdata package """
""" Interface to pci.ids from hwdata package """
filename = '/usr/share/hwdata/pci.ids'
devices = None
devices_class = None

def __init__(self, filename=None):
""" Load pci.ids from file to internal data structure.
Expand All @@ -117,14 +118,23 @@ def __init__(self, filename=None):
self.filename = PCI.filename
self.cache = 1

if self.cache and not PCI.devices:
if self.cache and not PCI.devices and not PCI.devices_class:
# parse pci.ids
PCI.devices = {}
PCI.devices_class = {}

f = open(self.filename, encoding='ISO8859-1')
vendor = None
device = None
for line in f.readlines():
while True:
line = f.readline()
if not line:
break

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you decide to change the for-loop to while-loop?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i did this a few months ago, i'm trying to remember why. i think it was because i've duplicate the parsing code to avoid having to fully understand it, since the pci class part is the same format. i can take a closer look to make it more logical

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok now i remember, the while-loop was for using f.readline() instead of for f.readlines(), so i can use the same loop twice without changing too much code. using readlines() force you to read the entire file in one loop. readline() retain the read offset so you can continue to read the file in another loop after a break.
do you want me to change it ?

l = line.split()

# Break to exit the loop and parse device classes
if line.startswith('# C class'):
break

@xsuchy xsuchy Apr 3, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Ah. I see now why. But can you document the "why" in the comment?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if line.startswith('#'):
continue
elif len(l) == 0:
Expand All @@ -145,6 +155,36 @@ def __init__(self, filename=None):
else: # this should not happen
PCI.devices[vendor][0] = vendor_name

device_class = None
subclass = None
while True:
line = f.readline()
if not line:
break
l = line.split()

if line.startswith('#'):
continue
elif len(l) == 0:
continue
elif line.startswith('\t\t'):
prog_if = l[0].lower()
prog_if_name = ' '.join(l[1:])
PCI.devices_class[device_class][1][subclass][1][prog_if] = prog_if_name
elif line.startswith('\t'):
subclass = l[0].lower()
subclass_name = ' '.join(l[1:])
PCI.devices_class[device_class][1][subclass] = [subclass_name, {}]
else:
device_class = l[1].lower()
device_class_name = ' '.join(l[2:])
if not device_class in list(PCI.devices_class.keys()):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Membership testing in a dictionary is more efficient when done directly on the dictionary object. Also, per PEP 8, item not in dict is preferred over not item in ....

Suggested change
if not device_class in list(PCI.devices_class.keys()):
if device_class not in PCI.devices_class:
References
  1. PEP 8 recommends using the 'not in' operator for membership tests and avoiding redundant list creation for dictionary key checks. (link)

PCI.devices_class[device_class] = [device_class_name, {}]
else:
PCI.devices_class[device_class][0] = device_class_name
f.close()


def get_vendor(self, vendor):
""" Return description of vendor. Parameter is two byte code in hexa.
If vendor is unknown None is returned.
Expand Down Expand Up @@ -198,8 +238,62 @@ def get_subsystem(self, vendor, device, subsystem):
else:
raise NotImplementedError()

def get_class(self, device_class):
""" Return device_class name of pci_class.
'device_class' is a bytes code variables in hexa of pci_class.
If subclass is unknown None is returned.
"""
device_class = device_class.lower()
if self.cache:
if device_class in list(PCI.devices_class.keys()):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Redundant use of list(...keys()). Membership testing should be performed directly on the dictionary for better performance and adherence to Python idioms (PEP 8).

Suggested change
if device_class in list(PCI.devices_class.keys()):
if device_class in PCI.devices_class:
References
  1. PEP 8 recommends avoiding redundant list creation for dictionary key checks. (link)

return PCI.devices_class[device_class][0]
else:
return None
else:
raise NotImplementedError()

def get_subclass(self, device_class, subclass):
""" Return subclass name of pci_class.
'device_class' and 'subclass' are two bytes code variables in hexa of pci_class.
If subclass is unknown None is returned.
"""
device_class = device_class.lower()
subclass = subclass.lower()
if self.cache:
if device_class in list(PCI.devices_class.keys()):
if subclass in list(PCI.devices_class[device_class][1].keys()):
Comment on lines +263 to +264

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Redundant use of list(...keys()) for membership testing. Checking directly against the dictionary is more efficient and follows PEP 8 guidelines.

Suggested change
if device_class in list(PCI.devices_class.keys()):
if subclass in list(PCI.devices_class[device_class][1].keys()):
if device_class in PCI.devices_class:
if subclass in PCI.devices_class[device_class][1]:
References
  1. PEP 8 recommends avoiding redundant list creation for dictionary key checks. (link)

return PCI.devices_class[device_class][1][subclass][0]
else:
return None
else:
return None
else:
raise NotImplementedError()

def get_prog_if(self, device_class, subclass, prog_if):
""" Return prog_if name of pci_class.
'device_class', 'subclass' and prog_if are three byte code variables in hexa of pci_class.
If prog_if is unknown None is returned.
"""
device_class = device_class.lower()
subclass = subclass.lower()
prog_if = prog_if.lower()
if self.cache:
if device_class in list(PCI.devices_class.keys()):
if subclass in list(PCI.devices_class[device_class][1].keys()):
if prog_if in list(PCI.devices_class[device_class][1][subclass][1].keys()):
Comment on lines +282 to +284

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Multiple redundant calls to list(...keys()). These membership checks are more efficient when performed directly on the dictionary objects, as per PEP 8.

Suggested change
if device_class in list(PCI.devices_class.keys()):
if subclass in list(PCI.devices_class[device_class][1].keys()):
if prog_if in list(PCI.devices_class[device_class][1][subclass][1].keys()):
if device_class in PCI.devices_class:
if subclass in PCI.devices_class[device_class][1]:
if prog_if in PCI.devices_class[device_class][1][subclass][1]:
References
  1. PEP 8 recommends avoiding redundant list creation for dictionary key checks. (link)

return PCI.devices_class[device_class][1][subclass][1][prog_if]
else:
return None
else:
return None
else:
return None
else:
raise NotImplementedError()

class PNP:
""" Interace to pnp.ids from hwdata package """
""" Interface to pnp.ids from hwdata package """
filename = '/usr/share/hwdata/pnp.ids'
VENDORS = None

Expand Down