Coverage for linuxpy/usb/usbfs.py: 0%

60 statements  

« prev     ^ index     » next       coverage.py v7.6.8, created at 2025-05-27 13:54 +0200

1# 

2# This file is part of the linuxpy project 

3# 

4# Copyright (c) 2023 Tiago Coutinho 

5# Distributed under the GPLv3 license. See LICENSE for more info. 

6 

7import collections 

8import functools 

9import struct 

10 

11from .base import USB_DEV_PATH, USB_DEV_TMPFS_PATH, BaseDevice 

12 

13USBFS_MAXDRIVERNAME = 255 

14 

15 

16DeviceDescriptorDecoder = struct.Struct("<BBHBBBBHHHBBBB") 

17DeviceDescriptor = collections.namedtuple( 

18 "DeviceDescriptor", 

19 ( 

20 "length", 

21 "descriptor_type", 

22 "bcdUSB", 

23 "device_class", 

24 "device_sub_class", 

25 "protocol", 

26 "max_pack_size", 

27 "vendor_id", 

28 "product_id", 

29 "bcdDevice", 

30 "manufacturer", 

31 "product_index", 

32 "serial_number_index", 

33 "nb_configs", 

34 ), 

35) 

36 

37 

38def read_descriptor(fd): 

39 # just after the open() call we can read the ubfs device descriptor 

40 # it will contain at least 18 bytes with basic usb device information 

41 data = fd.read(256) 

42 return DeviceDescriptor(*DeviceDescriptorDecoder.unpack_from(data)) 

43 

44 

45class Device(BaseDevice): 

46 def __init__(self, path): 

47 super().__init__(path) 

48 self.descriptor = None 

49 

50 @functools.cached_property 

51 def bus_number(self): 

52 return int(self.filename.parent.name) 

53 

54 @functools.cached_property 

55 def device_address(self): 

56 return int(self.filename.name) 

57 

58 @functools.cached_property 

59 def session_id(self): 

60 return self.bus_number << 8 | self.device_address 

61 

62 @property 

63 def vendor_id(self): 

64 return self.descriptor.vendor_id 

65 

66 @property 

67 def product_id(self): 

68 return self.descriptor.product_id 

69 

70 @property 

71 def manufacturer(self): 

72 return self.descriptor.manufacturer 

73 

74 def open(self): 

75 self._fobj = open(self.filename, "rb") 

76 self.descriptor = read_descriptor(self._fobj) 

77 

78 

79@functools.cache 

80def usbfs_path(): 

81 for path in (USB_DEV_TMPFS_PATH, USB_DEV_PATH): 

82 if path.is_dir(): 

83 # assume if we find any files that it must be the right place 

84 if next(path.iterdir(), None): 

85 return path 

86 

87 # On udev based systems without any usb-devices /dev/bus/usb will not 

88 # exist. So if we've not found anything and we're using udev for hotplug 

89 # simply assume /dev/bus/usb rather then making this fail 

90 return USB_DEV_TMPFS_PATH 

91 

92 

93def iter_paths(): 

94 for path in usbfs_path().iterdir(): 

95 try: 

96 int(path.name) 

97 except ValueError: 

98 continue 

99 for sub_path in path.iterdir(): 

100 try: 

101 int(sub_path.name) 

102 except ValueError: 

103 continue 

104 yield sub_path 

105 

106 

107def iter_devices(): 

108 for path in iter_paths(): 

109 yield Device(path) 

110 

111 

112def lsusb(): 

113 for dev in iter_devices(): 

114 dev.open()