""" Linux BenchTools - USB Device Parser Parses output from 'lsusb -v' command """ import re from typing import Dict, Any, Optional, List def parse_lsusb_verbose(lsusb_output: str) -> Dict[str, Any]: """ Parse the output of 'lsusb -v' command Args: lsusb_output: Raw text output from 'lsusb -v' command Returns: Dictionary with parsed USB device information """ result = { "vendor_id": None, "product_id": None, "usb_device_id": None, "marque": None, "modele": None, "fabricant": None, "produit": None, "numero_serie": None, "usb_version": None, "device_class": None, "device_subclass": None, "device_protocol": None, "max_power_ma": None, "speed": None, "manufacturer": None, "product": None, "interfaces": [], "raw_info": {} } lines = lsusb_output.strip().split('\n') current_interface = None for line in lines: # Bus and Device info # Example: Bus 002 Device 003: ID 0781:5567 SanDisk Corp. Cruzer Blade match = re.match(r'Bus\s+(\d+)\s+Device\s+(\d+):\s+ID\s+([0-9a-f]{4}):([0-9a-f]{4})\s+(.*)', line) if match: result["raw_info"]["bus"] = match.group(1) result["raw_info"]["device"] = match.group(2) result["vendor_id"] = match.group(3) result["product_id"] = match.group(4) result["usb_device_id"] = f"{match.group(3)}:{match.group(4)}" # Parse manufacturer and product from the description desc = match.group(5) parts = desc.split(' ', 1) if len(parts) == 2: result["marque"] = parts[0] result["modele"] = parts[1] else: result["modele"] = desc continue # idVendor match = re.search(r'idVendor\s+0x([0-9a-f]{4})\s+(.*)', line) if match: if not result["vendor_id"]: result["vendor_id"] = match.group(1) result["manufacturer"] = match.group(2).strip() if not result["marque"]: result["marque"] = result["manufacturer"] if result.get("vendor_id") and result.get("product_id") and not result.get("usb_device_id"): result["usb_device_id"] = f"{result['vendor_id']}:{result['product_id']}" continue # idProduct match = re.search(r'idProduct\s+0x([0-9a-f]{4})\s+(.*)', line) if match: if not result["product_id"]: result["product_id"] = match.group(1) result["product"] = match.group(2).strip() if not result["modele"]: result["modele"] = result["product"] if result.get("vendor_id") and result.get("product_id") and not result.get("usb_device_id"): result["usb_device_id"] = f"{result['vendor_id']}:{result['product_id']}" continue # bcdUSB (USB version) match = re.search(r'bcdUSB\s+([\d.]+)', line) if match: result["usb_version"] = match.group(1) continue # bDeviceClass match = re.search(r'bDeviceClass\s+(\d+)\s+(.*)', line) if match: result["device_class"] = match.group(2).strip() result["raw_info"]["device_class_code"] = match.group(1) continue # bDeviceSubClass match = re.search(r'bDeviceSubClass\s+(\d+)\s*(.*)', line) if match: result["device_subclass"] = match.group(2).strip() if match.group(2) else match.group(1) continue # bDeviceProtocol match = re.search(r'bDeviceProtocol\s+(\d+)\s*(.*)', line) if match: result["device_protocol"] = match.group(2).strip() if match.group(2) else match.group(1) continue # MaxPower match = re.search(r'MaxPower\s+(\d+)mA', line) if match: result["max_power_ma"] = int(match.group(1)) continue # iManufacturer match = re.search(r'iManufacturer\s+\d+\s+(.*)', line) if match and not result["manufacturer"]: result["manufacturer"] = match.group(1).strip() if not result["fabricant"]: result["fabricant"] = result["manufacturer"] continue # iProduct match = re.search(r'iProduct\s+\d+\s+(.*)', line) if match and not result["product"]: result["product"] = match.group(1).strip() if not result["produit"]: result["produit"] = result["product"] continue # iSerial match = re.search(r'iSerial\s+\d+\s+(.*)', line) if match: serial = match.group(1).strip() if serial and serial != "0": result["numero_serie"] = serial continue # Speed (from Device Descriptor or Status) match = re.search(r'Device Status:.*?Speed:\s*(\w+)', line) if match: result["speed"] = match.group(1) continue # Alternative speed detection if "480M" in line or "high-speed" in line.lower() or "high speed" in line.lower(): result["speed"] = "High Speed (480 Mbps)" elif "5000M" in line or "super-speed" in line.lower() or "super speed" in line.lower(): result["speed"] = "Super Speed (5 Gbps)" elif "10000M" in line or "superspeed+" in line.lower(): result["speed"] = "SuperSpeed+ (10 Gbps)" elif "12M" in line or "full-speed" in line.lower() or "full speed" in line.lower(): result["speed"] = "Full Speed (12 Mbps)" elif "1.5M" in line or "low-speed" in line.lower() or "low speed" in line.lower(): result["speed"] = "Low Speed (1.5 Mbps)" # Interface information match = re.search(r'Interface Descriptor:', line) if match: current_interface = {} result["interfaces"].append(current_interface) continue if current_interface is not None: # bInterfaceClass match = re.search(r'bInterfaceClass\s+(\d+)\s+(.*)', line) if match: current_interface["class"] = match.group(2).strip() current_interface["class_code"] = match.group(1) continue # bInterfaceSubClass match = re.search(r'bInterfaceSubClass\s+(\d+)\s*(.*)', line) if match: current_interface["subclass"] = match.group(2).strip() if match.group(2) else match.group(1) continue # bInterfaceProtocol match = re.search(r'bInterfaceProtocol\s+(\d+)\s*(.*)', line) if match: current_interface["protocol"] = match.group(2).strip() if match.group(2) else match.group(1) continue # Clean up empty values for key in list(result.keys()): if result[key] == "" or result[key] == "0": result[key] = None # Determine peripheral type from class result["type_principal"] = _determine_peripheral_type(result) result["sous_type"] = _determine_peripheral_subtype(result) return result def _determine_peripheral_type(usb_info: Dict[str, Any]) -> str: """Determine peripheral type from USB class information""" device_class = (usb_info.get("device_class") or "").lower() # Check interfaces if device class is not specific if not device_class or "vendor specific" in device_class or device_class == "0": interfaces = usb_info.get("interfaces", []) if interfaces: interface_class = (interfaces[0].get("class") or "").lower() else: interface_class = "" else: interface_class = device_class # Map USB classes to peripheral types class_map = { "hub": "USB", "audio": "Audio", "hid": "USB", "human interface device": "USB", "printer": "Imprimante", "mass storage": "Stockage", "video": "Video", "wireless": "Sans-fil", "bluetooth": "Bluetooth", "smart card": "Securite", "application specific": "USB", "vendor specific": "USB" } for key, ptype in class_map.items(): if key in interface_class: return ptype # Default return "USB" def _determine_peripheral_subtype(usb_info: Dict[str, Any]) -> Optional[str]: """Determine peripheral subtype from USB class information""" device_class = (usb_info.get("device_class") or "").lower() interfaces = usb_info.get("interfaces", []) if interfaces: interface_class = (interfaces[0].get("class") or "").lower() interface_subclass = (interfaces[0].get("subclass") or "").lower() else: interface_class = "" interface_subclass = "" # HID devices if "hid" in device_class or "hid" in interface_class or "human interface" in interface_class: if "mouse" in interface_subclass or "mouse" in str(usb_info.get("modele", "")).lower(): return "Souris" elif "keyboard" in interface_subclass or "keyboard" in str(usb_info.get("modele", "")).lower(): return "Clavier" elif "gamepad" in interface_subclass or "joystick" in interface_subclass: return "Manette" else: return "Peripherique HID" # Mass storage if "mass storage" in interface_class: model = str(usb_info.get("modele", "")).lower() if "card reader" in model or "reader" in model: return "Lecteur de cartes" else: return "Cle USB" # Audio if "audio" in interface_class: if "microphone" in interface_subclass: return "Microphone" elif "speaker" in interface_subclass: return "Haut-parleur" else: return "Audio" # Video if "video" in interface_class: return "Webcam" # Wireless if "wireless" in interface_class or "bluetooth" in interface_class: if "bluetooth" in interface_class: return "Bluetooth" else: return "Adaptateur sans-fil" # Printer if "printer" in interface_class: return "Imprimante" return None def parse_lsusb_simple(lsusb_output: str) -> List[Dict[str, Any]]: """ Parse the output of simple 'lsusb' command (without -v) Args: lsusb_output: Raw text output from 'lsusb' command Returns: List of dictionaries with basic USB device information """ devices = [] for line in lsusb_output.strip().split('\n'): # Example: Bus 002 Device 003: ID 0781:5567 SanDisk Corp. Cruzer Blade match = re.match(r'Bus\s+(\d+)\s+Device\s+(\d+):\s+ID\s+([0-9a-f]{4}):([0-9a-f]{4})\s+(.*)', line) if match: desc = match.group(5) parts = desc.split(' ', 1) device = { "bus": match.group(1), "device": match.group(2), "vendor_id": match.group(3), "product_id": match.group(4), "marque": parts[0] if len(parts) >= 1 else None, "modele": parts[1] if len(parts) == 2 else desc, "type_principal": "USB", "sous_type": None } devices.append(device) return devices def create_device_name(usb_info: Dict[str, Any]) -> str: """Generate a readable device name from USB info""" parts = [] if usb_info.get("marque"): parts.append(usb_info["marque"]) if usb_info.get("modele"): parts.append(usb_info["modele"]) if not parts: parts.append("Peripherique USB") if usb_info.get("vendor_id") and usb_info.get("product_id"): parts.append(f"({usb_info['vendor_id']}:{usb_info['product_id']})") return " ".join(parts)