17 KiB
Executable File
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
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)
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
# 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
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
# 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
# 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
# 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
# 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
# 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
# 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
{
// 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
-
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
- Lignes 104-240 :
-
backend/app/utils/device_classifier.py
- Lignes 153-171 : Nouveaux mappings
USB_INTERFACE_CLASS_MAPPINGetUSB_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
- Lignes 153-171 : Nouveaux mappings
-
backend/app/utils/usb_info_parser.py
- Lignes 30-135 : Section parsing complètement refaite avec mappings conformes
- Lignes 158-217 :
extract_interfaces()stockecodeen int - Lignes 178-199 : Extraction
interface_classespour classification + détection firmware
-
backend/app/api/endpoints/peripherals.py
- Lignes 786-814 :
suggestedavec nouveaux mappings de champs - Lignes 899-936 : Import USB structuré avec
interface_classespassées au classificateur
- Lignes 786-814 :
Documentation
-
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
-
- Lignes 1-49 : Section complètement réécrite avec focus conformité USB
- Documentation des nouveaux champs
- Explication des détections normatives
-
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 :
-
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)
-
frontend/css/peripherals.css(Lignes 668-685)- Styles pour la grille
.usb-details-grid - Mise en forme des champs readonly
- Styles pour la grille
-
frontend/js/peripherals.js- Lignes 32-107 : Nouvelle fonction
fillUSBDetails(caracteristiques) - Ligne 459 : Appel depuis
importSelectedUSBDevice() - Ligne 629 : Appel depuis
importUSBStructured()
- Lignes 32-107 : Nouvelle fonction
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
- Logs de Debug : Ajouter logs pour voir quelle stratégie de classification a été utilisée
- Multi-Interface Support : Détecter périphériques combinés (ex: Hub + Ethernet)
- USB4 / Thunderbolt : Ajouter patterns pour vitesses 40 Gbps
- Power Delivery : Support USB-C PD avec négociation > 5V
- 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 :
- Classification Mass Storage via
bInterfaceClass(normative) au lieu debDeviceClass - Type USB basé sur vitesse négociée au lieu de
bcdUSB - Mappings de champs conformes :
marque=idVendor,modele=iProduct,fabricant=iManufacturer - Analyse de puissance normative avec calcul de suffisance
- 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