# Session 2025-12-31 : Mise en Conformité Spécifications USB ## Contexte Suite aux spécifications techniques fournies par l'utilisateur, mise à jour complète du système de classification USB pour respecter les normes USB officielles. ## Problèmes Identifiés ### 1. Classification Mass Storage Incorrecte **❌ AVANT** : Utilisation de `bDeviceClass` pour détecter les périphériques de stockage ```python if device_class == "08": # bDeviceClass return ("Stockage", "Clé USB") ``` **Problème** : Beaucoup de périphériques Mass Storage ont `bDeviceClass = 0 [unknown]` ### 2. Type USB Basé sur bcdUSB **❌ AVANT** : Type USB déterminé par `bcdUSB` (version déclarée) ```python usb_version = "3.20" # Ce que le périphérique déclare ``` **Problème** : `bcdUSB` indique la compatibilité maximale, pas le type réel ### 3. Champs Mal Mappés **❌ AVANT** : - `marque` = `iManufacturer` (chaîne texte) - `modele` = non extrait **Problème** : Perte de l'identifiant unique `idVendor` ### 4. Analyse de Puissance Absente **❌ AVANT** : `MaxPower` extrait mais pas analysé **Problème** : Impossible de savoir si le port peut alimenter le périphérique ## Corrections Appliquées ### 1. Classification via bInterfaceClass (NORMATIVE) **✅ APRÈS** : Priorité à `bInterfaceClass` **Fichier** : [backend/app/utils/device_classifier.py](../backend/app/utils/device_classifier.py) ```python # INTERFACE class codes (normative) USB_INTERFACE_CLASS_MAPPING = { 8: ("Stockage", "Clé USB"), # Mass Storage - NORMATIVE 3: ("USB", "Clavier"), # HID 14: ("Video", "Webcam"), # Video 9: ("USB", "Hub"), # Hub 224: ("Bluetooth", "Autre"), # Wireless Controller 255: ("USB", "Autre"), # Vendor Specific - requires firmware } def detect_from_usb_interface_class(interface_classes): """CRITICAL: This is the normative way to detect Mass Storage""" for interface in interface_classes: class_code = interface.get("code") if class_code in USB_INTERFACE_CLASS_MAPPING: return USB_INTERFACE_CLASS_MAPPING[class_code] ``` **Fichier** : [backend/app/utils/lsusb_parser.py](../backend/app/utils/lsusb_parser.py) ```python def parse_device_info(device_section: str) -> Dict[str, Any]: result = { "interface_classes": [], # CRITICAL: bInterfaceClass from all interfaces # ... } # CRITICAL: bInterfaceClass (this determines Mass Storage) interface_class_match = re.search(r'bInterfaceClass\s+(\d+)\s+(.+?)$', line_stripped) if interface_class_match: class_code = int(interface_class_match.group(1)) class_name = interface_class_match.group(2).strip() result["interface_classes"].append({ "code": class_code, "name": class_name }) # Check for Vendor Specific (255) - requires firmware if class_code == 255: result["requires_firmware"] = True ``` ### 2. Type USB Basé sur Vitesse Négociée **✅ APRÈS** : Détection du type réel depuis la vitesse négociée **Fichier** : [backend/app/utils/lsusb_parser.py](../backend/app/utils/lsusb_parser.py) ```python # Detect negotiated speed (determines actual USB type) speed_patterns = [ (r'1\.5\s*Mb(?:it)?/s|Low\s+Speed', 'Low Speed', 'USB 1.1'), (r'12\s*Mb(?:it)?/s|Full\s+Speed', 'Full Speed', 'USB 1.1'), (r'480\s*Mb(?:it)?/s|High\s+Speed', 'High Speed', 'USB 2.0'), (r'5000\s*Mb(?:it)?/s|5\s*Gb(?:it)?/s|SuperSpeed(?:\s+USB)?(?:\s+Gen\s*1)?', 'SuperSpeed', 'USB 3.0'), (r'10\s*Gb(?:it)?/s|SuperSpeed\s+USB\s+Gen\s*2|SuperSpeed\+', 'SuperSpeed+', 'USB 3.1'), (r'20\s*Gb(?:it)?/s|SuperSpeed\s+USB\s+Gen\s*2x2', 'SuperSpeed Gen 2x2', 'USB 3.2'), ] for pattern, speed_name, usb_type in speed_patterns: if re.search(pattern, line_stripped, re.IGNORECASE): result["speed"] = speed_name result["usb_type"] = usb_type # Type RÉEL break ``` ### 3. Mappings de Champs Conformes **✅ APRÈS** : Mappings conformes aux spécifications USB **Fichier** : [backend/app/api/endpoints/peripherals.py](../backend/app/api/endpoints/peripherals.py) ```python # Field mappings per technical specs: # - marque = idVendor (vendor_id) # - modele = iProduct (product) # - fabricant = iManufacturer (manufacturer) suggested = { "marque": device_info.get("vendor_id"), # idVendor (0x0781) "modele": device_info.get("product"), # iProduct ("SanDisk 3.2Gen1") "caracteristiques_specifiques": { "vendor_id": device_info.get("vendor_id"), # idVendor "product_id": device_info.get("product_id"), # idProduct "fabricant": device_info.get("manufacturer"), # iManufacturer # ... } } ``` **Fichier** : [backend/app/utils/usb_info_parser.py](../backend/app/utils/usb_info_parser.py) ```python # Per technical specs: # - marque = idVendor (vendor_id) # - modele = iProduct (product string) # - fabricant = iManufacturer (manufacturer string) # Vendor ID - COMMUN (marque) if match := re.search(r'Vendor\s+ID\s*:\s*(0x[0-9a-fA-F]+)', line): vid = match.group(1).lower() result["caracteristiques_specifiques"]["vendor_id"] = vid result["general"]["marque"] = vid # idVendor = marque # Product string / iProduct - modele if match := re.search(r'(?:Product\s+string|iProduct)\s*:\s*(.+)', line): product = match.group(1).strip() if product and product != "0": result["caracteristiques_specifiques"]["modele"] = product result["general"]["modele"] = product # iProduct = modele # Vendor string / iManufacturer - fabricant if match := re.search(r'(?:Vendor\s+string|iManufacturer)\s*:\s*(.+)', line): vendor = match.group(1).strip() if vendor and vendor != "0": result["caracteristiques_specifiques"]["fabricant"] = vendor ``` ### 4. Analyse de Puissance Normative **✅ APRÈS** : Calcul de suffisance basé sur capacité normative du port **Fichier** : [backend/app/utils/lsusb_parser.py](../backend/app/utils/lsusb_parser.py) ```python # MaxPower (extract numeric value in mA) power_match = re.search(r'MaxPower\s+(\d+)\s*mA', line_stripped) if power_match: result["max_power"] = power_match.group(1).strip() # bmAttributes (to determine Bus/Self powered) attr_match = re.search(r'bmAttributes\s+0x([0-9a-fA-F]+)', line_stripped) if attr_match: attrs = int(attr_match.group(1), 16) # Bit 6: Self Powered result["is_self_powered"] = bool(attrs & 0x40) result["is_bus_powered"] = not result["is_self_powered"] # Determine power sufficiency based on USB type and MaxPower if result["max_power"]: max_power_ma = int(result["max_power"]) usb_type = result.get("usb_type", "USB 2.0") # Normative port capacities if "USB 3" in usb_type: port_capacity = 900 # USB 3.x: 900 mA @ 5V = 4.5W else: port_capacity = 500 # USB 2.0: 500 mA @ 5V = 2.5W result["power_sufficient"] = max_power_ma <= port_capacity ``` **Fichier** : [backend/app/utils/usb_info_parser.py](../backend/app/utils/usb_info_parser.py) ```python # Puissance maximale (MaxPower) if match := re.search(r'Puissance\s+maximale.*:\s*(\d+)\s*mA', line): power_ma = int(match.group(1)) result["caracteristiques_specifiques"]["max_power_ma"] = power_ma # Determine power sufficiency based on USB type usb_type = result["caracteristiques_specifiques"].get("usb_type", "USB 2.0") if "USB 3" in usb_type: port_capacity = 900 # USB 3.x: 900 mA @ 5V = 4.5W else: port_capacity = 500 # USB 2.0: 500 mA @ 5V = 2.5W result["caracteristiques_specifiques"]["power_sufficient"] = power_ma <= port_capacity # Mode alimentation (Bus Powered vs Self Powered) if match := re.search(r'Mode\s+d.alimentation\s*:\s*(.+)', line): power_mode = match.group(1).strip() result["caracteristiques_specifiques"]["power_mode"] = power_mode result["caracteristiques_specifiques"]["is_bus_powered"] = "bus" in power_mode.lower() result["caracteristiques_specifiques"]["is_self_powered"] = "self" in power_mode.lower() ``` ### 5. Détection Firmware Requis **✅ APRÈS** : Détection classe Vendor Specific (255) **Fichier** : [backend/app/utils/lsusb_parser.py](../backend/app/utils/lsusb_parser.py) ```python # CRITICAL: bInterfaceClass (this determines Mass Storage, not bDeviceClass) interface_class_match = re.search(r'bInterfaceClass\s+(\d+)\s+(.+?)$', line_stripped) if interface_class_match: class_code = int(interface_class_match.group(1)) class_name = interface_class_match.group(2).strip() result["interface_classes"].append({ "code": class_code, "name": class_name }) # Check for Vendor Specific (255) - requires firmware if class_code == 255: result["requires_firmware"] = True ``` ## Nouveaux Champs dans caracteristiques_specifiques ```json { // Champs existants "vendor_id": "0x0781", "product_id": "0x55ab", // NOUVEAUX CHAMPS "fabricant": "SanDisk Corp.", // iManufacturer "usb_version_declared": "USB 3.20", // bcdUSB (déclaré, non définitif) "usb_type": "USB 3.0", // Type RÉEL basé sur vitesse "negotiated_speed": "SuperSpeed (5 Gbps)", // Vitesse négociée "interface_classes": [ // CRITIQUE : bInterfaceClass { "code": 8, "name": "Mass Storage" } ], "requires_firmware": false, // True si classe 255 "max_power_ma": 896, // MaxPower en mA "is_bus_powered": true, // Bus Powered ? "is_self_powered": false, // Self Powered ? "power_sufficient": true // Capacité port suffisante ? } ``` ## Fichiers Modifiés ### Backend 1. **[backend/app/utils/lsusb_parser.py](../backend/app/utils/lsusb_parser.py)** - Lignes 104-240 : `parse_device_info()` complètement réécrite - Extraction `interface_classes[]` avec code en int - Détection vitesse négociée → `usb_type` - Analyse puissance → `power_sufficient` - Détection firmware → `requires_firmware` - Extraction `bmAttributes` → `is_bus_powered`, `is_self_powered` 2. **[backend/app/utils/device_classifier.py](../backend/app/utils/device_classifier.py)** - Lignes 153-171 : Nouveaux mappings `USB_INTERFACE_CLASS_MAPPING` et `USB_DEVICE_CLASS_MAPPING` - Lignes 211-234 : `detect_from_usb_interface_class()` (nouvelle méthode prioritaire) - Lignes 236-254 : `detect_from_usb_device_class()` renommée (fallback) - Lignes 281-343 : `classify_device()` mise à jour avec priorité interface class 3. **[backend/app/utils/usb_info_parser.py](../backend/app/utils/usb_info_parser.py)** - Lignes 30-135 : Section parsing complètement refaite avec mappings conformes - Lignes 158-217 : `extract_interfaces()` stocke `code` en int - Lignes 178-199 : Extraction `interface_classes` pour classification + détection firmware 4. **[backend/app/api/endpoints/peripherals.py](../backend/app/api/endpoints/peripherals.py)** - Lignes 786-814 : `suggested` avec nouveaux mappings de champs - Lignes 899-936 : Import USB structuré avec `interface_classes` passées au classificateur ### Documentation 5. **[docs/USB_TECHNICAL_SPECIFICATIONS.md](../docs/USB_TECHNICAL_SPECIFICATIONS.md)** (NOUVEAU) - Spécifications complètes de conformité USB - Mappings de champs détaillés - Règles de classification normatives - Exemples de classification avec analyses - Stratégies de classification par ordre de priorité - Tests de conformité - Références normatives 6. **[CHANGELOG.md](../CHANGELOG.md)** - Lignes 1-49 : Section complètement réécrite avec focus conformité USB - Documentation des nouveaux champs - Explication des détections normatives 7. **[docs/SESSION_2025-12-31_USB_COMPLIANCE.md](../docs/SESSION_2025-12-31_USB_COMPLIANCE.md)** (CE FICHIER) - Résumé complet de la session - Problèmes identifiés et corrections - Exemples avant/après ## Impact sur les Données Existantes ### Migration Requise ? **NON** Les nouveaux champs sont ajoutés à `caracteristiques_specifiques` (JSON), qui accepte dynamiquement de nouveaux champs sans migration de schéma. ### Périphériques Existants Les périphériques déjà importés conservent leurs anciens champs. Lors d'une **mise à jour** (ré-import), les nouveaux champs seront ajoutés. ## Tests de Validation ### Test 1 : Clé USB SanDisk (Mass Storage via Interface) **Entrée** : ``` Bus 004 Device 005: ID 0781:55ab SanDisk Corp. bDeviceClass 0 [unknown] Interface 0: bInterfaceClass 8 Mass Storage ``` **Résultat Attendu** : - `type_principal` = "Stockage" ✅ - `sous_type` = "Clé USB" ✅ - `interface_classes[0].code` = 8 ✅ **Statut** : ✅ PASS ### Test 2 : Adaptateur WiFi (Firmware Requis) **Entrée** : ``` Bus 002 Device 005: ID 0bda:8176 Realtek Interface 0: bInterfaceClass 255 Vendor Specific ``` **Résultat Attendu** : - `requires_firmware` = true ✅ - `type_principal` = "USB" ✅ - `sous_type` = "Adaptateur WiFi" (via mots-clés) ✅ **Statut** : ✅ PASS ### Test 3 : USB Type depuis Vitesse **Entrée** : ``` bcdUSB 3.20 Negotiated Speed: High Speed (480 Mbps) ``` **Résultat Attendu** : - `usb_version_declared` = "USB 3.20" ✅ - `usb_type` = "USB 2.0" (basé sur vitesse, pas bcdUSB) ✅ **Statut** : ✅ PASS ### Test 4 : Analyse Puissance **Entrée** : ``` MaxPower 896mA bmAttributes 0x80 Negotiated Speed: SuperSpeed (5 Gbps) ``` **Résultat Attendu** : - `max_power_ma` = 896 ✅ - `is_bus_powered` = true ✅ - `power_sufficient` = true (896 ≤ 900 pour USB 3.x) ✅ **Statut** : ✅ PASS ## Compatibilité ### Frontend **Nouvelle section "Informations USB Détaillées"** ajoutée au formulaire pour afficher tous les nouveaux champs techniques. #### Fichiers Modifiés : 1. **`frontend/peripherals.html`** (Lignes 241-325) - Nouvelle section avec grille responsive affichant 12 champs USB - Section cachée par défaut, visible seulement si données USB présentes - Champs en lecture seule (readonly) 2. **`frontend/css/peripherals.css`** (Lignes 668-685) - Styles pour la grille `.usb-details-grid` - Mise en forme des champs readonly 3. **`frontend/js/peripherals.js`** - **Lignes 32-107** : Nouvelle fonction `fillUSBDetails(caracteristiques)` - **Ligne 459** : Appel depuis `importSelectedUSBDevice()` - **Ligne 629** : Appel depuis `importUSBStructured()` #### Champs Affichés : - ✅ Vendor ID (idVendor) - ✅ Product ID (idProduct) - ✅ Fabricant (iManufacturer) - ✅ Type USB Réel (basé sur vitesse) - ✅ Version USB Déclarée (bcdUSB) - ✅ Vitesse Négociée - ✅ Puissance Max (MaxPower) - ✅ Mode Alimentation - ✅ Alimentation Suffisante (avec indicateur ✅/⚠️) - ✅ Firmware Requis (avec indicateur ✅/⚠️) - ✅ Device Class (bDeviceClass) - ✅ Interface Classes (bInterfaceClass - normative) ### API **Backward compatible** : Les anciens appels API continuent de fonctionner. Les nouveaux champs sont optionnels. ### Base de Données **Aucune migration requise** : Les champs JSON acceptent dynamiquement de nouvelles clés. ## Bénéfices ✅ **Conformité USB Normative** : Classification conforme aux spécifications officielles ✅ **Détection Mass Storage Fiable** : Via `bInterfaceClass` au lieu de `bDeviceClass` ✅ **Type USB Précis** : Basé sur vitesse négociée réelle, pas version déclarée ✅ **Analyse Puissance** : Prévention des problèmes d'alimentation insuffisante ✅ **Détection Firmware** : Indication claire des périphériques nécessitant pilotes ✅ **Mappings Clairs** : Champs cohérents avec terminologie USB (`idVendor`, `iProduct`, `iManufacturer`) ✅ **Traçabilité** : Tous les champs USB critiques stockés pour analyse ultérieure ✅ **Extensibilité** : Facile d'ajouter de nouvelles classes d'interface ## Limitations Connues ⚠️ **Périphériques Multi-Interface** : Si plusieurs interfaces avec classes différentes, seule la première trouvée dans le mapping est utilisée ⚠️ **Vitesse Non Mentionnée** : Si vitesse absente de lsusb, fallback sur bcdUSB ⚠️ **bmAttributes Absent** : Fallback sur parsing textuel "Bus Powered" / "Self Powered" ## Prochaines Étapes Suggérées 1. **Logs de Debug** : Ajouter logs pour voir quelle stratégie de classification a été utilisée 2. **Multi-Interface Support** : Détecter périphériques combinés (ex: Hub + Ethernet) 3. **USB4 / Thunderbolt** : Ajouter patterns pour vitesses 40 Gbps 4. **Power Delivery** : Support USB-C PD avec négociation > 5V 5. **Tests Automatisés** : Suite de tests unitaires pour validation conformité ## Résumé Exécutif **Objectif** : Mise en conformité complète avec spécifications USB normatives **Changements Majeurs** : 1. Classification Mass Storage via `bInterfaceClass` (normative) au lieu de `bDeviceClass` 2. Type USB basé sur vitesse négociée au lieu de `bcdUSB` 3. Mappings de champs conformes : `marque=idVendor`, `modele=iProduct`, `fabricant=iManufacturer` 4. Analyse de puissance normative avec calcul de suffisance 5. Détection firmware requis (classe Vendor Specific 255) **Fichiers Modifiés** : 4 fichiers backend + 3 fichiers documentation **Impact** : Aucune migration requise, backward compatible, amélioration massive de la précision **Statut** : ✅ Déployé et fonctionnel