Files
serv_benchmark/docs/SESSION_2025-12-31_USB_COMPLIANCE.md
Gilles Soulier c67befc549 addon
2026-01-05 16:08:01 +01:00

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

  1. 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 bmAttributesis_bus_powered, is_self_powered
  2. 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

    • 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

    • Lignes 786-814 : suggested avec nouveaux mappings de champs
    • Lignes 899-936 : Import USB structuré avec interface_classes passées au classificateur

Documentation

  1. 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
  2. CHANGELOG.md

    • Lignes 1-49 : Section complètement réécrite avec focus conformité USB
    • Documentation des nouveaux champs
    • Explication des détections normatives
  3. 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