4343DESC_INTERFACE = 0x04
4444DESC_ENDPOINT = 0x05
4545DESC_HID = 0x21
46- DESC_REPORT = 0X22
46+ DESC_REPORT = 0x22
4747
4848INTERFACE_HID = 0x03
4949SUBCLASS_BOOT = 0x01
5252PROTOCOL_KEYBOARD = 0x01
5353
5454# --- HID Report Descriptor Item Tags (The "Command") ---
55- HID_TAG_USAGE_PAGE = 0x05 # Defines the category (e.g., Generic Desktop, Game Controls)
56- HID_TAG_USAGE = 0x09 # Defines the specific item (e.g., Mouse, Joystick)
55+ HID_TAG_USAGE_PAGE = 0x05 # Defines the category (e.g., Generic Desktop, Game Controls)
56+ HID_TAG_USAGE = 0x09 # Defines the specific item (e.g., Mouse, Joystick)
5757
5858# --- Usage Page IDs (Values for 0x05) ---
5959USAGE_PAGE_GENERIC_DESKTOP = 0x01
6060
6161# --- Usage IDs (Values for 0x09, inside Generic Desktop) ---
62- USAGE_MOUSE = 0x02
62+ USAGE_MOUSE = 0x02
6363USAGE_JOYSTICK = 0x04
64- USAGE_GAMEPAD = 0x05
64+ USAGE_GAMEPAD = 0x05
6565USAGE_KEYBOARD = 0x06
6666
6767
@@ -100,11 +100,15 @@ def get_configuration_descriptor(device, index):
100100 get_descriptor (device , DESC_CONFIGURATION , index , full_buf )
101101 return full_buf
102102
103+
103104def get_report_descriptor (device , interface_num , length ):
104105 """
105106 Fetches the HID Report Descriptor.
106107 This tells us what the device actually IS (Mouse vs Joystick).
107108 """
109+ if length < 1 :
110+ return None
111+
108112 buf = bytearray (length )
109113 try :
110114 # 0x81 = Dir: IN | Type: Standard | Recipient: Interface
@@ -114,13 +118,14 @@ def get_report_descriptor(device, interface_num, length):
114118 _REQ_GET_DESCRIPTOR ,
115119 DESC_REPORT << 8 ,
116120 interface_num ,
117- buf
121+ buf ,
118122 )
119123 return buf
120124 except Exception as e :
121125 print (f"Failed to read Report Descriptor: { e } " )
122126 return None
123127
128+
124129def _is_confirmed_mouse (report_desc ):
125130 """
126131 Scans the raw descriptor bytes for:
@@ -129,33 +134,33 @@ def _is_confirmed_mouse(report_desc):
129134 """
130135 if not report_desc :
131136 return False
132-
137+
133138 # Simple byte scan check
134139 # We look for Usage Page Generic Desktop (0x05 0x01)
135140 has_generic_desktop = False
136141 for i in range (len (report_desc ) - 1 ):
137- if report_desc [i ] == HID_TAG_USAGE_PAGE and report_desc [i + 1 ] == USAGE_PAGE_GENERIC_DESKTOP :
142+ if (
143+ report_desc [i ] == HID_TAG_USAGE_PAGE
144+ and report_desc [i + 1 ] == USAGE_PAGE_GENERIC_DESKTOP
145+ ):
138146 has_generic_desktop = True
139-
147+
140148 # We look for Usage Mouse (0x09 0x02)
141149 has_mouse_usage = False
142150 for i in range (len (report_desc ) - 1 ):
143- if report_desc [i ] == HID_TAG_USAGE and report_desc [i + 1 ] == USAGE_MOUSE :
151+ if report_desc [i ] == HID_TAG_USAGE and report_desc [i + 1 ] == USAGE_MOUSE :
144152 has_mouse_usage = True
145-
146- return has_generic_desktop and has_mouse_usage
147153
154+ return has_generic_desktop and has_mouse_usage
148155
149- def _find_endpoint (device , count , protocol_type : Literal [PROTOCOL_MOUSE , PROTOCOL_KEYBOARD ], subclass ):
150- # pass a count of <= 0 to return all HID interfaces/endpoints of selected protocol_type on the device
151156
157+ def _find_endpoint (device , protocol_type : Literal [PROTOCOL_MOUSE , PROTOCOL_KEYBOARD ], subclass ):
152158 config_descriptor = get_configuration_descriptor (device , 0 )
153159 i = 0
154160 mouse_interface_index = None
155161 found_mouse = False
156162 candidate_found = False
157163 hid_desc_len = 0
158- endpoints = []
159164 while i < len (config_descriptor ):
160165 descriptor_len = config_descriptor [i ]
161166 descriptor_type = config_descriptor [i + 1 ]
@@ -180,7 +185,7 @@ def _find_endpoint(device, count, protocol_type: Literal[PROTOCOL_MOUSE, PROTOCO
180185 ):
181186 found_mouse = True
182187 mouse_interface_index = interface_number
183-
188+
184189 # May be trackpad interface if it's not a keyboard and looking for mouse
185190 elif (
186191 interface_class == INTERFACE_HID
@@ -195,44 +200,29 @@ def _find_endpoint(device, count, protocol_type: Literal[PROTOCOL_MOUSE, PROTOCO
195200 # The HID descriptor stores the Report Descriptor length at offset 7
196201 # Bytes: [Length, Type, BCD, BCD, Country, Count, ReportType, ReportLenL, ReportLenH]
197202 if descriptor_len >= 9 :
198- hid_desc_len = config_descriptor [i + 7 ] + (config_descriptor [i + 8 ] << 8 )
203+ hid_desc_len = config_descriptor [i + 7 ] + (config_descriptor [i + 8 ] << 8 )
199204
200205 elif descriptor_type == DESC_ENDPOINT :
201206 endpoint_address = config_descriptor [i + 2 ]
202207 if endpoint_address & _DIR_IN :
203208 if found_mouse :
204- endpoints .append ((mouse_interface_index , endpoint_address ))
205- if len (endpoints ) == count :
206- return endpoints
209+ return mouse_interface_index , endpoint_address
207210
208211 elif candidate_found :
209212 print (f"Checking Interface { interface_number } ..." )
210-
213+
211214 # If it's Protocol 2, it's definitely a mouse (Standard).
212215 # If it's Protocol 0, we must check the descriptor.
213- is_mouse = False
214-
215- if hid_desc_len > 0 :
216- rep_desc = get_report_descriptor (device , interface_number , hid_desc_len )
217- if _is_confirmed_mouse (rep_desc ):
218- is_mouse = True
219- print (f" -> CONFIRMED: It is a Mouse/Trackpad (Usage 0x09 0x02)" )
220- else :
221- print (f" -> REJECTED: Generic HID, but not a mouse (Joystick/Ups?)" )
222- else :
223- # Fallback if we missed the HID descriptor, assume no if Candidate
224- print (" -> Warning: Could not verify Usage, assuming no." )
225- is_mouse = False
226-
227- if is_mouse :
228- endpoints .append ((interface_number , endpoint_address ))
229- if len (endpoints ) == count :
230- return endpoints
231- else :
232- candidate_found = False # Stop looking at this interface
216+
217+ rep_desc = get_report_descriptor (device , interface_number , hid_desc_len )
218+ if _is_confirmed_mouse (rep_desc ):
219+ print (f" -> CONFIRMED: It is a Mouse/Trackpad (Usage 0x09 0x02)" )
220+ return interface_number , endpoint_address
221+
222+ candidate_found = False # Stop looking at this interface
233223
234224 i += descriptor_len
235- return [( None , None )]
225+ return None , None
236226
237227
238228def find_boot_mouse_endpoint (device ):
@@ -242,7 +232,8 @@ def find_boot_mouse_endpoint(device):
242232 :param device: The device to search within
243233 :return: mouse_interface_index, mouse_endpoint_address if found, or None, None otherwise
244234 """
245- return _find_endpoint (device , 1 , PROTOCOL_MOUSE , SUBCLASS_BOOT )[0 ]
235+ return _find_endpoint (device , PROTOCOL_MOUSE , SUBCLASS_BOOT )
236+
246237
247238def find_report_mouse_endpoint (device ):
248239 """
@@ -251,7 +242,8 @@ def find_report_mouse_endpoint(device):
251242 :param device: The device to search within
252243 :return: mouse_interface_index, mouse_endpoint_address if found, or None, None otherwise
253244 """
254- return _find_endpoint (device , 1 , PROTOCOL_MOUSE , SUBCLASS_REPORT )[0 ]
245+ return _find_endpoint (device , PROTOCOL_MOUSE , SUBCLASS_REPORT )
246+
255247
256248def find_boot_keyboard_endpoint (device ):
257249 """
@@ -260,5 +252,4 @@ def find_boot_keyboard_endpoint(device):
260252 :param device: The device to search within
261253 :return: keyboard_interface_index, keyboard_endpoint_address if found, or None, None otherwise
262254 """
263- return _find_endpoint (device , 1 , PROTOCOL_KEYBOARD , SUBCLASS_BOOT )[0 ]
264-
255+ return _find_endpoint (device , PROTOCOL_KEYBOARD , SUBCLASS_BOOT )
0 commit comments