Compare commits

9 Commits

Author SHA1 Message Date
TIBERGHIEN corentin
6ad8705650 rapport pdf 2026-02-24 14:23:20 +01:00
TIBERGHIEN corentin
acc94b109e rapport 2026-02-24 14:20:07 +01:00
rltbg
0ca4a10aa4 working usb negociation
networkmanager compatibility
2026-02-17 15:23:16 +01:00
Tiago Batista Cardoso
46e2d50a55 renamed 349 -> 397 2026-02-17 15:22:20 +01:00
TIBERGHIEN corentin
95889d091c gitignore 2026-02-17 15:11:25 +01:00
TIBERGHIEN corentin
32c039da54 send_eth 2026-02-17 15:10:34 +01:00
TIBERGHIEN corentin
16119c5725 gitignore 2026-02-17 15:09:48 +01:00
TIBERGHIEN corentin
2931340eee compile commands 2026-02-17 15:06:38 +01:00
rltbg
aa29c8b2a5 working usb negociation
networkmanager compatibility
2026-02-17 13:44:11 +01:00
10 changed files with 667 additions and 385 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
build
compile_commands.json
.module-commands.o

Binary file not shown.

View File

@@ -1,4 +1,4 @@
MODULE_NAME := ch349_driver
MODULE_NAME := ch397_driver
obj-m := $(MODULE_NAME).o
SRC_DIR := src
@@ -6,7 +6,7 @@ BUILD_DIR := build
KDIR := /lib/modules/$(shell uname -r)/build
$(MODULE_NAME)-objs := $(SRC_DIR)/ch349_driver.o
$(MODULE_NAME)-objs := $(SRC_DIR)/ch397_driver.o
ccflags-y := -std=gnu11 -Wno-declaration-after-statement

216
README.md
View File

@@ -26,7 +26,7 @@ sudo update-initramfs -u
```
En l'etat (5/2/26) le driver s'installe, s'attache au device ch349 puis creer une interface reseau sur la machine. on peut alors recuperer le nom de l'interface grace a :
En l'etat (5/2/26) le driver s'installe, s'attache au device ch397 puis creer une interface reseau sur la machine. on peut alors recuperer le nom de l'interface grace a :
```
ip a
@@ -39,7 +39,7 @@ sudo ip addr add 192.168.1.100/24 dev <nom_interface>
sudo ip link set <nom_interface> up
```
ce qui nous permet ensuite, depuis une autre machine d'envoyer des pings a l'ip (192.168.1.100) qui sont ensuite parses en hexadecimal et affiches sur l'interface usb (visibles en executant dmesg | grep ch349)
ce qui nous permet ensuite, depuis une autre machine d'envoyer des pings a l'ip (192.168.1.100) qui sont ensuite parses en hexadecimal et affiches sur l'interface usb (visibles en executant dmesg | grep ch397)
## Todo
@@ -63,3 +63,215 @@ ce qui nous permet ensuite, depuis une autre machine d'envoyer des pings a l'ip
## Utile
<https://cdn.promelec.ru/upload/items/2025/09/18/CH397_.pdf>
## Introduction
Un driver (pilote) est un composant logiciel qui fait linterface entre le noyau du système dexploitation et un périphérique matériel ou une couche dabstraction. Il traduit les demandes génériques du système (lire/écrire, configurer, envoyer/recevoir paquets, etc.) en opérations spécifiques au matériel, et inversement expose létat et les événements matériels au système.
Principales responsabilités
- Initialisation et énumération : détecter le matériel, allouer ressources et configurer le périphérique.
- Traduction dappels : implémenter les callbacks et API attendus par le soussystème du noyau (ex. net_device ops, block ops, file ops) pour que le reste du système utilise le périphérique de façon standardisée.
- Gestion des I/O : orchestrer transferts (DMA, URB, interruptions), mise en file dattente, copies de buffers et synchronisation.
- Gestion derreurs et robustesse : détecter conditions derreur, reprendre, nettoyer proprement à la déconnexion ou en cas déchec.
- Exposition des fonctionnalités avancées : support doptions matérielles (offloads, mise en veille, statistiques, configurations spécifiques).
- Concurrence et sécurité : protéger les ressources partagées (mutex, spinlock), respecter les contextes dexécution (process/context IRQ/softirq).
Types et niveaux
- Pilotes noyau (inkernel) : sexécutent au sein du noyau, offrent faibles latences et accès direct au matériel (ex. pilotes réseaux, disques, USB).
- Pilotes en espace utilisateur : utilisent un mécanisme de médiation (ex. UIO, libusb) et conviennent pour prototypage ou usages moins critiques.
- Pilotes de classe vs vendorspecific : les pilotes de classe implémentent une interface standard (ex. CDC, HID) interopérable ; les pilotes vendorspecific gèrent fonctionnalités propriétaires.
Propriétés attendues
- Stabilité et sécurité : ne pas corrompre la mémoire noyau, gérer correctement les erreurs et concurrents.
- Performance : minimiser copies, utiliser offloads matériels, gérer pools/URB/queues pour hauts débits.
- Portabilité et intégration : respecter les conventions du soussystème kernel concerné (APIs, structures, callbacks, sysfs/ethtool si applicable).
## Présentation CH397
Le CH397 est une puce NIC USB hautement intégrée et basse consommation, destinée à étendre une interface Ethernet via USB pour ordinateurs de bureau, portables, tablettes, consoles de jeu, etc. Elle intègre un processeur RISCV QingKe, un contrôleur USB2.0/2.1 avec transceiver PHY conforme USB2.1, ainsi quun soussystème Ethernet complet (MAC + PHY) conforme IEEE 802.3, supportant 10/100 Mbps.
Architecture matérielle
- **SoC toutenun :** processeur embarqué QingKe RISCV, contrôleur USB, transceiver PHY USB et contrôleur Ethernet MAC+PHY intégrés sur une unique puce (packages QFN24/QFN32).
- **USB 2.0/2.1 Full/HighSpeed :** contrôleur et transceiver conformes à la spécification USB2.1, avec LDO intégré et composants périphériques réduits (résistance 50 Ω, condensateurs oscillateur).
- **Ethernet 10/100M :** MAC et PHY conformes IEEE 802.3 (10BASET / 100BASETX) ; support de lautonégociation 10/100 Mbps, AutoMDIX et distances jusquà 120 m sur câbles CAT5/CAT6.
- **Mémoire tampon interne :** buffers TX/RX intégrés pour stocker les trames avant transfert sur USB, réduisant la charge CPU de lhôte.
- **Fonctions matérielles supplémentaires :** LDO interne, support ESD amélioré (6 kV), configuration LED matérielle, wakeup réseau et modes basse consommation.
Interfaces réseau disponibles
- **CDCECM, CDCNCM et RNDIS :** support natif de CDCECM et CDCNCM (USB Ethernet classes) et RNDIS — possibilité dutilisation sans pilote propriétaire sur de nombreux hôtes, ou avec pilote vendorspecific si souhaité.
- **Endpoints USB standard :** modes vendorspecific (bulk endpoints) et classes réseau standard (interfaces Communications/Data avec altsetting pour endpoints bulk IN/OUT).
- **Fonctionnalités réseau hardwareassist :**
- génération et vérification de checksums IPv4/IPv6 (TCP/UDP/HEAD),
- support IEEE 802.3x flow control et fallback halfduplex,
- prise en charge VLAN (802.3Q),
- wakeonLAN (magic packet / wake packets).
Contraintes et exigences spécifiques
- **Choix de configuration USB :** la puce expose plusieurs configurations (vendor, CDCECM, NCM, RNDIS) — le pilote doit détecter la configuration active et sélectionner linterface/altsetting offrant les endpoints bulk nécessaires (ex. altsetting 1 pour CDC Data).
- **Taille et gestion des buffers :** présence de buffers internes signifie que le périphérique peut livrer des trames agrégées ou de taille > MTU standard ; prévoir côté pilote des buffers hôtes suffisamment grands (ex. ≥ 16002048 octets) et gérer lalignement skb.
- **Performances et flux :** le CH397 prend en charge features hardwareassist (checksum offload, flow control, VLAN), le pilote peut en tirer parti pour réduire la charge CPU et améliorer le débit ; envisager gestion des URB/queues pour atteindre des débits proches du 100 Mbps.
- **Gestion basseconsommation et wakeup :** la puce propose modes Sleep et wakeonLAN — le pilote doit exposer et coordonner ces capacités via PM (suspend/resume, autosuspend) et notifications réseau.
- **Robustesse physique et contraintes matérielles :** 6 kV ESD, exigences doscillateur externe et simple filtrage passif — le design matériel autour du CH397 est simplifié, mais le firmware peut exposer ou non certaines fonctionnalités (NCM, paramètres LED).
- **Compatibilité multiOS :** grâce au support CDCECM/NCM/RNDIS, le CH397 peut fonctionner sans pilote propriétaire sur de nombreux systèmes ; toutefois, un pilote dédié permet dexploiter au mieux les optimisations matérielles et les modes vendorspecific.
En synthèse, le CH397 fournit une solution USB→Ethernet complète et optimisée, offrant à la fois compatibilité plugandplay via classes USB standard et options avancées (offloads, VLAN, wakeup, basse consommation) pour une intégration performante sur hôte via un pilote USBEthernet adapté.
## Environnement de dev
compilateur make gcc
arborescence du projet
dependances etherdevice et kernel
## 4. Conception
### 4.1 Architecture logicielle (modules et interfaces)
- **Composants principaux**
- Module USB driver linux : probe, disconnect, suspend, resume.
- Couche réseau (net_device): instance net_device créée via alloc_etherdev, opérateurs netdev (open, stop, start_xmit, validate_addr, set_mac).
- Gestion des transferts USB: URB pour RX (dev->rx_urb) et URB temporaires pour TX.
- Buffering utilisateurs: dev->bulk_in_buffer pour réception, skb pour transmission/réception vers/depuis la pile réseau.
- **Interactions**
- usb core <-> driver : probe/ disconnect, usb_submit_urb / usb_kill_urb.
- driver <-> network stack : netif_rx, netdev_ops callbacks, register_netdev/unregister_netdev.
- Synchronisation locale : mutex io_mutex pour protéger opérations I/O critiques (prise/relâche autour dopérations longues).
### 4.2 API exposée au noyau (structures, callbacks)
- Structures principales :
- struct ch397_device : état du périphérique, pointeurs usb/netdev, endpoints, buffer et URB.
- struct net_device : structure standard exposée au stack réseau, initialisée via alloc_etherdev.
- Callbacks et points dentrée :
- usb driver callbacks : ch397_probe, ch397_disconnect, ch397_suspend, ch397_resume.
- net_device ops : ch397_net_open (ndo_open), ch397_net_stop (ndo_stop), ch397_start_xmit (ndo_start_xmit), eth_validate_addr (ndo_validate_addr), eth_mac_addr (ndo_set_mac_address).
- URB callbacks : ch397_read_callback (RX), ch397_write_callback (TX).
- Comportement attendu :
- Lors de probe : allouer netdev, initialiser ch397_device, trouver endpoints, récupérer MAC, register_netdev.
- Lors de open : lancer réception (soumettre URB RX), mettre la carrier on et démarrer la queue.
- Lors de start_xmit : encapsuler skb dans URB bulk-out, soumettre, gérer compteurs et erreurs.
- Lors de disconnect : arrêter RX, unregister_netdev, libérer ressources.
### 4.3 Intégration avec etherdevice.h
- Utilisation :
- alloc_etherdev(sizeof(struct ch397_device)) pour allocation et initialisation dun net_device avec espace privé.
- eth_random_addr/mac helpers : eth_random_addr utilisé comme fallback si lecture MAC échoue ; eth_hw_addr_set pour positionner ladresse MAC.
- eth_validate_addr/eth_mac_addr fournis comme ndo_validate_addr/ndo_set_mac_address, ce qui délègue la validation et le réglage standard dadresse MAC.
- Conséquences :
- Le driver sappuie sur les helpers pour respecter les conventions Ethernet sans réimplémenter la validation/gestion MAC.
- Lalignement des sk_buffs est fait via skb_reserve(skb, 2) pour correspondre aux exigences de la pile Ethernet/IP.
### 4.4 Rôle de kernel.h et interactions avec le noyau
- Inclusion de <linux/kernel.h> pour :
- Fonctions dimpression et de logging et macros comme KERN_INFO (utilisées dans le code).
- Types et API kernel de base (HZ, GFP_KERNEL/GFP_ATOMIC, etc.).
- Interactions principales :
- Allocation mémoire: kmalloc/kfree, netdev/skb allocation.
- Gestion des URB: usb_alloc_urb/usb_free_urb, usb_submit_urb, usb_kill_urb.
- Enregistrement/espace didentification: MODULE_DEVICE_TABLE, module_usb_driver.
- Impact : le driver utilise les primitives noyau pour mémoire, synchronisation (mutex), temporisation (watchdog_timeo), et communication inter-sous-systèmes (network/usb).
### 4.5 Gestion des buffers et des descriptors
- Buffers RX :
- Buffer statique alloué kmalloc(dev->bulk_in_size) (2048 octets) pour réception.
- RX URB configuré pour pointer sur dev->bulk_in_buffer ; urb->actual_length indique la taille reçue.
- Après réception, données copiées dans un skb via netdev_alloc_skb + skb_put_data, puis remises au stack réseau.
- Buffers TX :
- Pas de buffer de copie : lURB de transmission référence directement skb->data (risque si le stack modifie ou libère le skb) — dans ce code, le skb est passé comme contexte de lURB et libéré dans write_callback.
- URB allouées par transmission et libérées dans ch397_write_callback.
- Descriptors / URB lifecycle :
- RX : un URB long-vie (dev->rx_urb) est alloué lors de ch397_start_rx, soumis, et resoumis par le callback. En stop, usb_kill_urb + usb_free_urb.
- TX : URB allouées par start_xmit, soumises, et libérées dans le callback de fin.
- Statistiques et erreurs :
- netdev->stats.{rx_packets, rx_bytes, tx_packets, tx_bytes, rx_errors, tx_errors, rx_dropped, tx_dropped} mis à jour aux emplacements appropriés.
- Remarques sur robustesse :
- Vérifier et gérer le cas où usb_submit_urb échoue (déjà partiellement géré).
- Potentiel reusage / pool dURB pour haute performance non implémenté ; actuellement allocation à la volée.
- Attention à lutilisation de skb->data directement : garantir que le skb nest pas modifié tant que lURB est en vol (ici le skb est conservé comme contexte et libéré dans le callback, ce qui est acceptable).
## 5. Implémentation
- Le code fournit une base fonctionnelle pour un driver USB Ethernet CDC-like : découverte endpoints, RX continu via URB ré-submis, TX via URB à la demande, intégration basique avec la stack réseau.
### 5.1 Stratégie dinitialisation et dargs parsing
- Initialisation principale dans ch397_probe :
- Vérifier configuration/interface USB et ignorer linterface CDC Comm quand nécessaire.
- Allouer net_device via alloc_etherdev et récupérer ch397_device avec netdev_priv.
- Initialiser mutex, obtenir référence usb_get_dev, définir altsetting si nécessaire (usb_set_interface pour CDC Data).
- Parcourir endpoints de laltsetting courant pour découvrir bulk-in et bulk-out ; allouer bulk_in_buffer (kmalloc).
- Récupérer ladresse MAC via ch397_get_mac_address (usb_string) et lappliquer via eth_hw_addr_set.
- Initialiser netdev->netdev_ops, watchdog_timeo, et appeler register_netdev.
- En cas derreur, faire cleanup (usb_put_dev, kfree buffer, free_netdev).
- Pas dargs parsing via module_param dans ce code — stratégie : valeurs par défaut et auto-détection depuis lUSB device string/config.
### 5.2 Routine dattach/detach
- Attach (probe) : allouer structures, configurer endpoints, set MAC, register_netdev, netif_carrier_off (attente de lopen).
- Detach (disconnect) :
- Récupérer dev via usb_get_intfdata, retirer association usb_set_intfdata(NULL).
- unregister_netdev (qui arrêtera la queue si nécessaire).
- Arrêter RX via ch397_stop_rx (usb_kill_urb + usb_free_urb).
- Libérer bulk_in_buffer, usb_put_dev, free_netdev.
- Logging de déconnexion.
- Robustesse :
- Garantir que disconnect peut être appelé même si probe a échoué partiellement (vérifier NULLs avant free).
- Utiliser usb_kill_urb pour attendre la fin des callbacks avant de free les ressources partagées.
### 5.3 Transmissions (TX) : file dattente et gestion
- Flux denvoi :
- ch397_start_xmit est appelé avec un skb.
- Allouer une URB (usb_alloc_urb) ; si échec, libérer skb et incrémenter tx_dropped.
- Préparer URB avec usb_fill_bulk_urb en pointant sur skb->data et en passant skb comme contexte.
- Soumettre URB (usb_submit_urb). En cas déchec : libérer urb et skb, incrémenter tx_errors.
- Si succès : skb conservé jusquau ch397_write_callback qui libère le skb et lURB.
- Gestion de la queue :
- Implémentation actuelle ne stoppe pas la queue quand trop dURB en vol (commentaire présent). Pour production : ajouter compteur dURB en vol et netif_stop_queue/netif_wake_queue.
- Sécurité mémoire :
- Le skb est référencé via urb->context et libéré dans le callback ; sassurer que rien ne libère ou modifie skb avant callback.
- Erreurs et métriques :
- Incrémenter netdev->stats.tx_errors sur échec de submit, tx_packets/tx_bytes sur succès (dans callback).
### 5.4 Réception (RX) : traitement des paquets
- Setup :
- ch397_start_rx alloue dev->rx_urb, le configure via usb_fill_bulk_urb sur bulk_in_buffer et soumet.
- Traitement dans ch397_read_callback :
- Vérifier urb->status ; si success et actual_length > 0 :
- hexdump pour debug.
- Allouer skb via netdev_alloc_skb, réserver 2 octets pour alignement, copier données avec skb_put_data.
- Déterminer protocole via eth_type_trans et injecter via netif_rx.
- Mettre à jour stats rx_packets/rx_bytes.
- Sur erreurs terminales (-ENOENT, -ECONNRESET, -ESHUTDOWN) : retourner (pas de resubmit).
- Sur autres erreurs : incrémenter rx_errors.
- Resoumettre lURB via usb_submit_urb(urb, GFP_ATOMIC) pour continuer la réception.
- Robustesse :
- Gestion dallocation skb échouée : incrémenter rx_dropped.
- Toujours resoumettre URB tant que linterface reste active ; gérer échecs de resubmit (logger, incrémenter erreur).
- Sassurer que le buffer dev->bulk_in_buffer reste alloué pendant la durée de vie de lURB (libéré seulement à disconnect).
### 5.5 Gestion des erreurs et reprise
- Erreurs USB :
- Dans read/write callbacks, vérifier urb->status et traiter codes connus.
- Echec de usb_submit_urb lors dinitialisation : nettoyage et retour derreur dans probe/start_rx.
- Reprise :
- Probe refuse config non supportées (config 3, interface 0 en config 2) évitant états incohérents.
- On peut améliorer la reprise en ajoutant tentatives limitées de resubmit et backoff si submit échoue.
- Nettoyage : toujours free les URB (usb_free_urb) et buffers si submit échoue.
- Consistance :
- Utiliser usb_kill_urb dans disconnect pour garantir callbacks terminés avant free.
- mutex io_mutex disponible pour séquencer opérations critiques sur lUSB (bien que son usage soit minimal actuellement).
### 5.6 Synchronisation et verrouillage
- Mutex :
- mutex_init(&dev->io_mutex) lors du probe.
- mutex utilisé dans suspend pour verrouiller les opérations I/O (exemple minimal présent). Recommandation : entourer submit/kill URB et manipulations dev->rx_urb/dev->bulk_in_buffer par la mutex si nécessaire.
- Contexte dexécution :
- Callbacks URB sexécutent en contexte interruption ou softirq ; utiliser GFP_ATOMIC pour allocations faites dans callbacks si nécessaire.
- Les fonctions net_device ops (ndo_start_xmit, ndo_open/stop) sexécutent en context process ; utiliser GFP_KERNEL pour allocations là.
- Protection des ressources :
- Accès concurrent à rx_urb, bulk_in_buffer et autres champs doivent être protégés lors de modification (start/stop Rx, disconnect).
- Lors du disconnect, utiliser usb_set_intfdata(interface, NULL) avant de libérer afin déviter race avec callbacks.
### 5.7 Extraits de code clés (fonctions importantes)
- ch397_probe : allocation netdev, découverte endpoints, set_interface altsetting, récupération MAC, register_netdev, gestion derreurs.
- ch397_start_rx / ch397_stop_rx : allocation/soumission et arrêt/libération de RX URB.
- ch397_read_callback : traitement packet -> skb -> netif_rx, resubmit URB.
- ch397_start_xmit / ch397_write_callback : envoi via URB, statistiques, libération skb/urb.
- ch397_disconnect : cleanup complet — unregister_netdev, usb_kill_urb, free buffers/structures.

210
Rapport.md Normal file
View File

@@ -0,0 +1,210 @@
## 1. Introduction
Un driver (pilote) est un composant logiciel qui fait linterface entre le noyau du système dexploitation et un périphérique matériel ou une couche dabstraction. Il traduit les demandes génériques du système (lire/écrire, configurer, envoyer/recevoir paquets, etc.) en opérations spécifiques au matériel, et inversement expose létat et les événements matériels au système.
Principales responsabilités
- Initialisation et énumération : détecter le matériel, allouer ressources et configurer le périphérique.
- Traduction dappels : implémenter les callbacks et API attendus par le soussystème du noyau (ex. net_device ops, block ops, file ops) pour que le reste du système utilise le périphérique de façon standardisée.
- Gestion des I/O : orchestrer transferts (DMA, URB, interruptions), mise en file dattente, copies de buffers et synchronisation.
- Gestion derreurs et robustesse : détecter conditions derreur, reprendre, nettoyer proprement à la déconnexion ou en cas déchec.
- Exposition des fonctionnalités avancées : support doptions matérielles (offloads, mise en veille, statistiques, configurations spécifiques).
- Concurrence et sécurité : protéger les ressources partagées (mutex, spinlock), respecter les contextes dexécution (process/context IRQ/softirq).
Types et niveaux
- Pilotes noyau (inkernel) : sexécutent au sein du noyau, offrent faibles latences et accès direct au matériel (ex. pilotes réseaux, disques, USB).
- Pilotes en espace utilisateur : utilisent un mécanisme de médiation (ex. UIO, libusb) et conviennent pour prototypage ou usages moins critiques.
- Pilotes de classe vs vendorspecific : les pilotes de classe implémentent une interface standard (ex. CDC, HID) interopérable ; les pilotes vendorspecific gèrent fonctionnalités propriétaires.
Propriétés attendues
- Stabilité et sécurité : ne pas corrompre la mémoire noyau, gérer correctement les erreurs et concurrents.
- Performance : minimiser copies, utiliser offloads matériels, gérer pools/URB/queues pour hauts débits.
- Portabilité et intégration : respecter les conventions du soussystème kernel concerné (APIs, structures, callbacks, sysfs/ethtool si applicable).
## 2. Présentation CH397
Le CH397 est une puce NIC USB hautement intégrée et basse consommation, destinée à étendre une interface Ethernet via USB pour ordinateurs de bureau, portables, tablettes, consoles de jeu, etc. Elle intègre un processeur RISCV QingKe, un contrôleur USB2.0/2.1 avec transceiver PHY conforme USB2.1, ainsi quun soussystème Ethernet complet (MAC + PHY) conforme IEEE 802.3, supportant 10/100 Mbps.
Architecture matérielle
- **SoC toutenun :** processeur embarqué QingKe RISCV, contrôleur USB, transceiver PHY USB et contrôleur Ethernet MAC+PHY intégrés sur une unique puce (packages QFN24/QFN32).
- **USB 2.0/2.1 Full/HighSpeed :** contrôleur et transceiver conformes à la spécification USB2.1, avec LDO intégré et composants périphériques réduits (résistance 50, condensateurs oscillateur).
- **Ethernet 10/100M :** MAC et PHY conformes IEEE 802.3 (10BASET / 100BASETX) ; support de lautonégociation 10/100 Mbps, AutoMDIX et distances jusquà 120 m sur câbles CAT5/CAT6.
- **Mémoire tampon interne :** buffers TX/RX intégrés pour stocker les trames avant transfert sur USB, réduisant la charge CPU de lhôte.
- **Fonctions matérielles supplémentaires :** LDO interne, support ESD amélioré (6 kV), configuration LED matérielle, wakeup réseau et modes basse consommation.
Interfaces réseau disponibles
- **CDCECM, CDCNCM et RNDIS :** support natif de CDCECM et CDCNCM (USB Ethernet classes) et RNDIS — possibilité dutilisation sans pilote propriétaire sur de nombreux hôtes, ou avec pilote vendorspecific si souhaité.
- **Endpoints USB standard :** modes vendorspecific (bulk endpoints) et classes réseau standard (interfaces Communications/Data avec altsetting pour endpoints bulk IN/OUT).
- **Fonctionnalités réseau hardwareassist :**
- génération et vérification de checksums IPv4/IPv6 (TCP/UDP/HEAD),
- support IEEE 802.3x flow control et fallback halfduplex,
- prise en charge VLAN (802.3Q),
- wakeonLAN (magic packet / wake packets).
Contraintes et exigences spécifiques
- **Choix de configuration USB :** la puce expose plusieurs configurations (vendor, CDCECM, NCM, RNDIS) — le pilote doit détecter la configuration active et sélectionner linterface/altsetting offrant les endpoints bulk nécessaires (ex. altsetting 1 pour CDC Data).
- **Taille et gestion des buffers :** présence de buffers internes signifie que le périphérique peut livrer des trames agrégées ou de taille > MTU standard ; prévoir côté pilote des buffers hôtes suffisamment grands (ex. >= 16002048 octets) et gérer lalignement skb.
- **Performances et flux :** le CH397 prend en charge features hardwareassist (checksum offload, flow control, VLAN), le pilote peut en tirer parti pour réduire la charge CPU et améliorer le débit ; envisager gestion des URB/queues pour atteindre des débits proches du 100 Mbps.
- **Gestion basseconsommation et wakeup :** la puce propose modes Sleep et wakeonLAN — le pilote doit exposer et coordonner ces capacités via PM (suspend/resume, autosuspend) et notifications réseau.
- **Robustesse physique et contraintes matérielles :** 6 kV ESD, exigences doscillateur externe et simple filtrage passif — le design matériel autour du CH397 est simplifié, mais le firmware peut exposer ou non certaines fonctionnalités (NCM, paramètres LED).
- **Compatibilité multiOS :** grâce au support CDCECM/NCM/RNDIS, le CH397 peut fonctionner sans pilote propriétaire sur de nombreux systèmes ; toutefois, un pilote dédié permet dexploiter au mieux les optimisations matérielles et les modes vendorspecific.
En synthèse, le CH397 fournit une solution USB→Ethernet complète et optimisée, offrant à la fois compatibilité plugandplay via classes USB standard et options avancées (offloads, VLAN, wakeup, basse consommation) pour une intégration performante sur hôte via un pilote USBEthernet adapté.
## 4. Conception
### 4.1 Architecture logicielle (modules et interfaces)
- **Composants principaux**
- Module USB driver linux : probe, disconnect, suspend, resume.
- Couche réseau (net_device): instance net_device créée via alloc_etherdev, opérateurs netdev (open, stop, start_xmit, validate_addr, set_mac).
- Gestion des transferts USB: URB pour RX (dev->rx_urb) et URB temporaires pour TX.
- Buffering utilisateurs: dev->bulk_in_buffer pour réception, skb pour transmission/réception vers/depuis la pile réseau.
- **Interactions**
- usb core <-> driver : probe/ disconnect, usb_submit_urb / usb_kill_urb.
- driver <-> network stack : netif_rx, netdev_ops callbacks, register_netdev/unregister_netdev.
- Synchronisation locale : mutex io_mutex pour protéger opérations I/O critiques (prise/relâche autour dopérations longues).
### 4.2 API exposée au noyau (structures, callbacks)
- Structures principales :
- struct ch397_device : état du périphérique, pointeurs usb/netdev, endpoints, buffer et URB.
- struct net_device : structure standard exposée au stack réseau, initialisée via alloc_etherdev.
- Callbacks et points dentrée :
- usb driver callbacks : ch397_probe, ch397_disconnect, ch397_suspend, ch397_resume.
- net_device ops : ch397_net_open (ndo_open), ch397_net_stop (ndo_stop), ch397_start_xmit (ndo_start_xmit), eth_validate_addr (ndo_validate_addr), eth_mac_addr (ndo_set_mac_address).
- URB callbacks : ch397_read_callback (RX), ch397_write_callback (TX).
- Comportement attendu :
- Lors de probe : allouer netdev, initialiser ch397_device, trouver endpoints, récupérer MAC, register_netdev.
- Lors de open : lancer réception (soumettre URB RX), mettre la carrier on et démarrer la queue.
- Lors de start_xmit : encapsuler skb dans URB bulk-out, soumettre, gérer compteurs et erreurs.
- Lors de disconnect : arrêter RX, unregister_netdev, libérer ressources.
### 4.3 Intégration avec etherdevice.h
- Utilisation :
- alloc_etherdev(sizeof(struct ch397_device)) pour allocation et initialisation dun net_device avec espace privé.
- eth_random_addr/mac helpers : eth_random_addr utilisé comme fallback si lecture MAC échoue ; eth_hw_addr_set pour positionner ladresse MAC.
- eth_validate_addr/eth_mac_addr fournis comme ndo_validate_addr/ndo_set_mac_address, ce qui délègue la validation et le réglage standard dadresse MAC.
- Conséquences :
- Le driver sappuie sur les helpers pour respecter les conventions Ethernet sans réimplémenter la validation/gestion MAC.
- Lalignement des sk_buffs est fait via skb_reserve(skb, 2) pour correspondre aux exigences de la pile Ethernet/IP.
### 4.4 Rôle de kernel.h et interactions avec le noyau
- Inclusion de <linux/kernel.h> pour :
- Fonctions dimpression et de logging et macros comme KERN_INFO (utilisées dans le code).
- Types et API kernel de base (HZ, GFP_KERNEL/GFP_ATOMIC, etc.).
- Interactions principales :
- Allocation mémoire: kmalloc/kfree, netdev/skb allocation.
- Gestion des URB: usb_alloc_urb/usb_free_urb, usb_submit_urb, usb_kill_urb.
- Enregistrement/espace didentification: MODULE_DEVICE_TABLE, module_usb_driver.
- Impact : le driver utilise les primitives noyau pour mémoire, synchronisation (mutex), temporisation (watchdog_timeo), et communication inter-sous-systèmes (network/usb).
### 4.5 Gestion des buffers et des descriptors
- Buffers RX :
- Buffer statique alloué kmalloc(dev->bulk_in_size) (2048 octets) pour réception.
- RX URB configuré pour pointer sur dev->bulk_in_buffer ; urb->actual_length indique la taille reçue.
- Après réception, données copiées dans un skb via netdev_alloc_skb + skb_put_data, puis remises au stack réseau.
- Buffers TX :
- Pas de buffer de copie : lURB de transmission référence directement skb->data (risque si le stack modifie ou libère le skb) — dans ce code, le skb est passé comme contexte de lURB et libéré dans write_callback.
- URB allouées par transmission et libérées dans ch397_write_callback.
- Descriptors / URB lifecycle :
- RX : un URB long-vie (dev->rx_urb) est alloué lors de ch397_start_rx, soumis, et resoumis par le callback. En stop, usb_kill_urb + usb_free_urb.
- TX : URB allouées par start_xmit, soumises, et libérées dans le callback de fin.
- Statistiques et erreurs :
- netdev->stats.{rx_packets, rx_bytes, tx_packets, tx_bytes, rx_errors, tx_errors, rx_dropped, tx_dropped} mis à jour aux emplacements appropriés.
- Remarques sur robustesse :
- Vérifier et gérer le cas où usb_submit_urb échoue (déjà partiellement géré).
- Potentiel reusage / pool dURB pour haute performance non implémenté ; actuellement allocation à la volée.
- Attention à lutilisation de skb->data directement : garantir que le skb nest pas modifié tant que lURB est en vol (ici le skb est conservé comme contexte et libéré dans le callback, ce qui est acceptable).
## 5. Implémentation
- Le code fournit une base fonctionnelle pour un driver USB Ethernet CDC-like : découverte endpoints, RX continu via URB ré-submis, TX via URB à la demande, intégration basique avec la stack réseau.
### 5.1 Stratégie dinitialisation et dargs parsing
- Initialisation principale dans ch397_probe :
- Vérifier configuration/interface USB et ignorer linterface CDC Comm quand nécessaire.
- Allouer net_device via alloc_etherdev et récupérer ch397_device avec netdev_priv.
- Initialiser mutex, obtenir référence usb_get_dev, définir altsetting si nécessaire (usb_set_interface pour CDC Data).
- Parcourir endpoints de laltsetting courant pour découvrir bulk-in et bulk-out ; allouer bulk_in_buffer (kmalloc).
- Récupérer ladresse MAC via ch397_get_mac_address (usb_string) et lappliquer via eth_hw_addr_set.
- Initialiser netdev->netdev_ops, watchdog_timeo, et appeler register_netdev.
- En cas derreur, faire cleanup (usb_put_dev, kfree buffer, free_netdev).
- Pas dargs parsing via module_param dans ce code — stratégie : valeurs par défaut et auto-détection depuis lUSB device string/config.
### 5.2 Routine dattach/detach
- Attach (probe) : allouer structures, configurer endpoints, set MAC, register_netdev, netif_carrier_off (attente de lopen).
- Detach (disconnect) :
- Récupérer dev via usb_get_intfdata, retirer association usb_set_intfdata(NULL).
- unregister_netdev (qui arrêtera la queue si nécessaire).
- Arrêter RX via ch397_stop_rx (usb_kill_urb + usb_free_urb).
- Libérer bulk_in_buffer, usb_put_dev, free_netdev.
- Logging de déconnexion.
- Robustesse :
- Garantir que disconnect peut être appelé même si probe a échoué partiellement (vérifier NULLs avant free).
- Utiliser usb_kill_urb pour attendre la fin des callbacks avant de free les ressources partagées.
### 5.3 Transmissions (TX) : file dattente et gestion
- Flux denvoi :
- ch397_start_xmit est appelé avec un skb.
- Allouer une URB (usb_alloc_urb) ; si échec, libérer skb et incrémenter tx_dropped.
- Préparer URB avec usb_fill_bulk_urb en pointant sur skb->data et en passant skb comme contexte.
- Soumettre URB (usb_submit_urb). En cas déchec : libérer urb et skb, incrémenter tx_errors.
- Si succès : skb conservé jusquau ch397_write_callback qui libère le skb et lURB.
- Gestion de la queue :
- Implémentation actuelle ne stoppe pas la queue quand trop dURB en vol (commentaire présent). Pour production : ajouter compteur dURB en vol et netif_stop_queue/netif_wake_queue.
- Sécurité mémoire :
- Le skb est référencé via urb->context et libéré dans le callback ; sassurer que rien ne libère ou modifie skb avant callback.
- Erreurs et métriques :
- Incrémenter netdev->stats.tx_errors sur échec de submit, tx_packets/tx_bytes sur succès (dans callback).
### 5.4 Réception (RX) : traitement des paquets
- Setup :
- ch397_start_rx alloue dev->rx_urb, le configure via usb_fill_bulk_urb sur bulk_in_buffer et soumet.
- Traitement dans ch397_read_callback :
- Vérifier urb->status ; si success et actual_length > 0 :
- hexdump pour debug.
- Allouer skb via netdev_alloc_skb, réserver 2 octets pour alignement, copier données avec skb_put_data.
- Déterminer protocole via eth_type_trans et injecter via netif_rx.
- Mettre à jour stats rx_packets/rx_bytes.
- Sur erreurs terminales (-ENOENT, -ECONNRESET, -ESHUTDOWN) : retourner (pas de resubmit).
- Sur autres erreurs : incrémenter rx_errors.
- Resoumettre lURB via usb_submit_urb(urb, GFP_ATOMIC) pour continuer la réception.
- Robustesse :
- Gestion dallocation skb échouée : incrémenter rx_dropped.
- Toujours resoumettre URB tant que linterface reste active ; gérer échecs de resubmit (logger, incrémenter erreur).
- Sassurer que le buffer dev->bulk_in_buffer reste alloué pendant la durée de vie de lURB (libéré seulement à disconnect).
### 5.5 Gestion des erreurs et reprise
- Erreurs USB :
- Dans read/write callbacks, vérifier urb->status et traiter codes connus.
- Echec de usb_submit_urb lors dinitialisation : nettoyage et retour derreur dans probe/start_rx.
- Reprise :
- Probe refuse config non supportées (config 3, interface 0 en config 2) évitant états incohérents.
- On peut améliorer la reprise en ajoutant tentatives limitées de resubmit et backoff si submit échoue.
- Nettoyage : toujours free les URB (usb_free_urb) et buffers si submit échoue.
- Consistance :
- Utiliser usb_kill_urb dans disconnect pour garantir callbacks terminés avant free.
- mutex io_mutex disponible pour séquencer opérations critiques sur lUSB (bien que son usage soit minimal actuellement).
### 5.6 Synchronisation et verrouillage
- Mutex :
- mutex_init(&dev->io_mutex) lors du probe.
- mutex utilisé dans suspend pour verrouiller les opérations I/O (exemple minimal présent). Recommandation : entourer submit/kill URB et manipulations dev->rx_urb/dev->bulk_in_buffer par la mutex si nécessaire.
- Contexte dexécution :
- Callbacks URB sexécutent en contexte interruption ou softirq ; utiliser GFP_ATOMIC pour allocations faites dans callbacks si nécessaire.
- Les fonctions net_device ops (ndo_start_xmit, ndo_open/stop) sexécutent en context process ; utiliser GFP_KERNEL pour allocations là.
- Protection des ressources :
- Accès concurrent à rx_urb, bulk_in_buffer et autres champs doivent être protégés lors de modification (start/stop Rx, disconnect).
- Lors du disconnect, utiliser usb_set_intfdata(interface, NULL) avant de libérer afin déviter race avec callbacks.
### 5.7 Extraits de code clés (fonctions importantes)
- ch397_probe : allocation netdev, découverte endpoints, set_interface altsetting, récupération MAC, register_netdev, gestion derreurs.
- ch397_start_rx / ch397_stop_rx : allocation/soumission et arrêt/libération de RX URB.
- ch397_read_callback : traitement packet -> skb -> netif_rx, resubmit URB.
- ch397_start_xmit / ch397_write_callback : envoi via URB, statistiques, libération skb/urb.
- ch397_disconnect : cleanup complet — unregister_netdev, usb_kill_urb, free buffers/structures.

BIN
Rapport.pdf Normal file

Binary file not shown.

View File

@@ -1,307 +0,0 @@
[
{
"file": "src/ch349_driver.c",
"arguments": [
"gcc",
"-Wp,-MMD,src/.ch349_driver.o.d",
"-nostdinc",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include/generated",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include/uapi",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include/generated/uapi",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include/uapi",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include/generated/uapi",
"-include",
"/usr/lib/modules/6.18.7-arch1-1/build/include/linux/compiler-version.h",
"-include",
"/usr/lib/modules/6.18.7-arch1-1/build/include/linux/kconfig.h",
"-include",
"/usr/lib/modules/6.18.7-arch1-1/build/include/linux/compiler_types.h",
"-D__KERNEL__",
"-std=gnu11",
"-fshort-wchar",
"-funsigned-char",
"-fno-common",
"-fno-PIE",
"-fno-strict-aliasing",
"-mno-sse",
"-mno-mmx",
"-mno-sse2",
"-mno-3dnow",
"-mno-avx",
"-mno-sse4a",
"-fcf-protection=branch",
"-fno-jump-tables",
"-m64",
"-falign-jumps=1",
"-falign-loops=1",
"-mno-80387",
"-mno-fp-ret-in-387",
"-mskip-rax-setup",
"-march=x86-64",
"-mtune=generic",
"-mno-red-zone",
"-mcmodel=kernel",
"-mstack-protector-guard-reg=gs",
"-mstack-protector-guard-symbol=__ref_stack_chk_guard",
"-Wno-sign-compare",
"-fno-asynchronous-unwind-tables",
"-mindirect-branch-cs-prefix",
"-mfunction-return=thunk-extern",
"-fno-jump-tables",
"-mharden-sls=all",
"-fpatchable-function-entry=16,16",
"-fno-delete-null-pointer-checks",
"-O2",
"-Wno-declaration-after-statement",
"-DMODULE",
"-DKBUILD_BASENAME=\"ch349_driver\"",
"-DKBUILD_MODNAME=\"ch349_driver\"",
"-D__KBUILD_MODNAME=kmod_ch349_driver",
"-c",
"-o",
"src/ch349_driver.o",
"src/ch349_driver.c"
],
"directory": "/home/bob/Projects/rj45toUSB_adapter/driver",
"output": "src/ch349_driver.o"
},
{
"file": "ch349_driver.mod.c",
"arguments": [
"gcc",
"-Wp,-MMD,./.ch349_driver.mod.o.d",
"-nostdinc",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include/generated",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include/uapi",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include/generated/uapi",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include/uapi",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include/generated/uapi",
"-include",
"/usr/lib/modules/6.18.7-arch1-1/build/include/linux/compiler-version.h",
"-include",
"/usr/lib/modules/6.18.7-arch1-1/build/include/linux/kconfig.h",
"-include",
"/usr/lib/modules/6.18.7-arch1-1/build/include/linux/compiler_types.h",
"-D__KERNEL__",
"-std=gnu11",
"-fshort-wchar",
"-funsigned-char",
"-fno-common",
"-fno-PIE",
"-fno-strict-aliasing",
"-mno-sse",
"-mno-mmx",
"-mno-sse2",
"-mno-3dnow",
"-mno-avx",
"-mno-sse4a",
"-fcf-protection=branch",
"-fno-jump-tables",
"-m64",
"-falign-jumps=1",
"-falign-loops=1",
"-mno-80387",
"-mno-fp-ret-in-387",
"-mskip-rax-setup",
"-march=x86-64",
"-mtune=generic",
"-mno-red-zone",
"-mcmodel=kernel",
"-mstack-protector-guard-reg=gs",
"-mstack-protector-guard-symbol=__ref_stack_chk_guard",
"-Wno-sign-compare",
"-fno-asynchronous-unwind-tables",
"-mindirect-branch-cs-prefix",
"-mfunction-return=thunk-extern",
"-fno-jump-tables",
"-mharden-sls=all",
"-fpatchable-function-entry=16,16",
"-fno-delete-null-pointer-checks",
"-O2",
"-fstack-protector-strong",
"-fno-builtin-wcslen",
"-Wall",
"-Wextra",
"-Wundef",
"-Werror=implicit-function-declaration",
"-Werror=implicit-int",
"-Werror=return-type",
"-Werror=strict-prototypes",
"-Wno-format-security",
"-Wno-trigraphs",
"-Wno-frame-address",
"-Wno-address-of-packed-member",
"-Wmissing-declarations",
"-Wmissing-prototypes",
"-Wframe-larger-than=2048",
"-Wno-main",
"-Wno-dangling-pointer",
"-Wvla-larger-than=1",
"-Wno-pointer-sign",
"-Wcast-function-type",
"-Wno-unterminated-string-initialization",
"-Wno-array-bounds",
"-Wno-stringop-overflow",
"-Wno-alloc-size-larger-than",
"-Wimplicit-fallthrough=5",
"-Werror=date-time",
"-Werror=incompatible-pointer-types",
"-Werror=designated-init",
"-Wenum-conversion",
"-Wunused",
"-Wno-unused-but-set-variable",
"-Wno-unused-const-variable",
"-Wno-packed-not-aligned",
"-Wno-format-overflow",
"-Wno-format-truncation",
"-Wno-stringop-truncation",
"-Wno-override-init",
"-Wno-missing-field-initializers",
"-Wno-type-limits",
"-Wno-shift-negative-value",
"-Wno-maybe-uninitialized",
"-Wno-sign-compare",
"-Wno-unused-parameter",
"-g",
"-gdwarf-5",
"-DMODULE",
"-DKBUILD_BASENAME=\"ch349_driver.mod\"",
"-DKBUILD_MODNAME=\"ch349_driver\"",
"-D__KBUILD_MODNAME=kmod_ch349_driver",
"-c",
"-o",
"ch349_driver.mod.o",
"ch349_driver.mod.c"
],
"directory": "/home/bob/Projects/rj45toUSB_adapter/driver",
"output": "ch349_driver.mod.o"
},
{
"file": "/usr/lib/modules/6.18.7-arch1-1/build/scripts/module-common.c",
"arguments": [
"gcc",
"-Wp,-MMD,./..module-common.o.d",
"-nostdinc",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include/generated",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include/uapi",
"-I/usr/lib/modules/6.18.7-arch1-1/build/arch/x86/include/generated/uapi",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include/uapi",
"-I/usr/lib/modules/6.18.7-arch1-1/build/include/generated/uapi",
"-include",
"/usr/lib/modules/6.18.7-arch1-1/build/include/linux/compiler-version.h",
"-include",
"/usr/lib/modules/6.18.7-arch1-1/build/include/linux/kconfig.h",
"-include",
"/usr/lib/modules/6.18.7-arch1-1/build/include/linux/compiler_types.h",
"-D__KERNEL__",
"-std=gnu11",
"-fshort-wchar",
"-funsigned-char",
"-fno-common",
"-fno-PIE",
"-fno-strict-aliasing",
"-mno-sse",
"-mno-mmx",
"-mno-sse2",
"-mno-3dnow",
"-mno-avx",
"-mno-sse4a",
"-fcf-protection=branch",
"-fno-jump-tables",
"-m64",
"-falign-jumps=1",
"-falign-loops=1",
"-mno-80387",
"-mno-fp-ret-in-387",
"-mskip-rax-setup",
"-march=x86-64",
"-mtune=generic",
"-mno-red-zone",
"-mcmodel=kernel",
"-mstack-protector-guard-reg=gs",
"-mstack-protector-guard-symbol=__ref_stack_chk_guard",
"-Wno-sign-compare",
"-fno-asynchronous-unwind-tables",
"-mindirect-branch-cs-prefix",
"-mfunction-return=thunk-extern",
"-fno-jump-tables",
"-mharden-sls=all",
"-fpatchable-function-entry=16,16",
"-fno-delete-null-pointer-checks",
"-O2",
"-fstack-protector-strong",
"-ftrivial-auto-var-init=zero",
"-fno-stack-clash-protection",
"-pg",
"-mfentry",
"-DCC_USING_FENTRY",
"-fstrict-flex-arrays=3",
"-fno-strict-overflow",
"-fno-stack-check",
"-fno-builtin-wcslen",
"-Wall",
"-Wextra",
"-Wundef",
"-Werror=implicit-function-declaration",
"-Werror=implicit-int",
"-Werror=return-type",
"-Werror=strict-prototypes",
"-Wno-format-security",
"-Wno-trigraphs",
"-Wno-frame-address",
"-Wno-address-of-packed-member",
"-Wmissing-declarations",
"-Wmissing-prototypes",
"-Wframe-larger-than=2048",
"-Wno-main",
"-Wno-dangling-pointer",
"-Wvla-larger-than=1",
"-Wno-pointer-sign",
"-Wcast-function-type",
"-Wno-unterminated-string-initialization",
"-Wno-array-bounds",
"-Wno-stringop-overflow",
"-Wno-alloc-size-larger-than",
"-Wimplicit-fallthrough=5",
"-Werror=date-time",
"-Werror=incompatible-pointer-types",
"-Werror=designated-init",
"-Wenum-conversion",
"-Wunused",
"-Wno-unused-but-set-variable",
"-Wno-unused-const-variable",
"-Wno-packed-not-aligned",
"-Wno-format-overflow",
"-Wno-format-truncation",
"-Wno-stringop-truncation",
"-Wno-override-init",
"-Wno-missing-field-initializers",
"-Wno-type-limits",
"-Wno-shift-negative-value",
"-Wno-maybe-uninitialized",
"-Wno-sign-compare",
"-Wno-unused-parameter",
"-g",
"-gdwarf-5",
"-DMODULE",
"-DKBUILD_BASENAME=\".module_common\"",
"-DKBUILD_MODNAME=\".module_common.o\"",
"-D__KBUILD_MODNAME=kmod_.module_common.o",
"-c",
"-o",
".module-common.o",
"/usr/lib/modules/6.18.7-arch1-1/build/scripts/module-common.c"
],
"directory": "/home/bob/Projects/rj45toUSB_adapter/driver",
"output": ".module-common.o"
}
]

View File

@@ -11,4 +11,4 @@ make -- compile
make clean -- clean
make install -- build and load driver
make uninstall -- deload driver
make bear -- use bear to generate compile_commands.json to fix lsp errors
make bear -- use bear to generate compile_commands.json to fix lsp errors (might need to manually tweak compile_commands.json to suppress some unsupported arguments errors)

81
send_eth.c Normal file
View File

@@ -0,0 +1,81 @@
// commande : sudo ./send_eth <ifname> <dst-mac> <src-mac> <ethertype(hex)> <payload-as-string>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if.h>
static int mac_from_str(const char *s, unsigned char *mac) {
int vals[6];
if (sscanf(s, "%x:%x:%x:%x:%x:%x",
&vals[0], &vals[1], &vals[2],
&vals[3], &vals[4], &vals[5]) != 6) return -1;
for (int i = 0; i < 6; ++i) mac[i] = (unsigned char)vals[i];
return 0;
}
int main(int argc, char **argv) {
if (argc < 6) {
fprintf(stderr, "Usage: %s <ifname> <dst-mac> <src-mac> <ethertype(hex)> <payload>\n", argv[0]);
return 1;
}
const char *ifname = argv[1];
unsigned char dst_mac[6], src_mac[6];
if (mac_from_str(argv[2], dst_mac) < 0) { fprintf(stderr, "Bad dst-mac\n"); return 1; }
if (mac_from_str(argv[3], src_mac) < 0) { fprintf(stderr, "Bad src-mac\n"); return 1; }
unsigned int ethertype;
if (sscanf(argv[4], "%x", &ethertype) != 1) { fprintf(stderr, "Bad ethertype\n"); return 1; }
const char *payload = argv[5];
size_t payload_len = strlen(payload);
int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock == -1) { perror("socket"); return 1; }
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) { perror("SIOCGIFINDEX"); close(sock); return 1; }
int ifindex = ifr.ifr_ifindex;
unsigned char frame[ETH_FRAME_LEN];
size_t frame_len = 0;
memcpy(frame + frame_len, dst_mac, 6); frame_len += 6;
memcpy(frame + frame_len, src_mac, 6); frame_len += 6;
frame[frame_len++] = (ethertype >> 8) & 0xff;
frame[frame_len++] = ethertype & 0xff;
if (payload_len + frame_len > ETH_DATA_LEN + ETH_HLEN) {
fprintf(stderr, "Payload too large\n"); close(sock); return 1;
}
memcpy(frame + frame_len, payload, payload_len);
frame_len += payload_len;
if (frame_len < 60) {
memset(frame + frame_len, 0, 60 - frame_len);
frame_len = 60;
}
struct sockaddr_ll sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sll_ifindex = ifindex;
sock_addr.sll_halen = ETH_ALEN;
memcpy(sock_addr.sll_addr, dst_mac, 6);
ssize_t sent = sendto(sock, frame, frame_len, 0, (struct sockaddr*)&sock_addr, sizeof(sock_addr));
if (sent == -1) { perror("sendto"); close(sock); return 1; }
printf("Sent %zd bytes on %s\n", sent, ifname);
close(sock);
return 0;
}

View File

@@ -12,10 +12,10 @@
#include <linux/slab.h>
#include <linux/usb.h>
#define CH349_VENDOR_ID 0x1a86
#define CH349_PRODUCT_ID 0x5397
#define CH397_VENDOR_ID 0x1a86
#define CH397_PRODUCT_ID 0x5397
struct ch349_device {
struct ch397_device {
struct usb_device *udev;
struct usb_interface *interface;
struct mutex io_mutex;
@@ -28,15 +28,15 @@ struct ch349_device {
struct net_device *netdev;
};
static const struct usb_device_id ch349_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(CH349_VENDOR_ID, CH349_PRODUCT_ID,
USB_CLASS_CDC_DATA, 0, 0) },
static const struct usb_device_id ch397_table[] = {
{ USB_DEVICE(CH397_VENDOR_ID, CH397_PRODUCT_ID) },
{}
};
MODULE_DEVICE_TABLE(usb, ch349_table);
MODULE_DEVICE_TABLE(usb, ch397_table);
// Fonction pour récupérer la MAC
static int ch349_get_mac_address(struct ch349_device *dev, u8 *mac)
static int ch397_get_mac_address(struct ch397_device *dev, u8 *mac)
{
char buf[13];
int ret, i;
@@ -63,7 +63,7 @@ static int ch349_get_mac_address(struct ch349_device *dev, u8 *mac)
return 0;
}
static void ch349_hexdump(const struct device *dev, const char *prefix,
static void ch397_hexdump(const struct device *dev, const char *prefix,
const void *data, size_t len)
{
const unsigned char *buf = data;
@@ -94,21 +94,21 @@ static void ch349_hexdump(const struct device *dev, const char *prefix,
}
}
static void ch349_read_callback(struct urb *urb)
static void ch397_read_callback(struct urb *urb)
{
struct ch349_device *dev = urb->context;
struct ch397_device *dev = urb->context;
struct net_device *netdev = dev->netdev;
struct sk_buff *skb;
struct ethhdr *eth;
int status = urb->status;
switch (status) {
case 0: // Success!
if (urb->actual_length > 0) {
dev_info(&dev->interface->dev,
"ch349: Received %d bytes\n",
"ch397: Received %d bytes\n",
urb->actual_length);
ch349_hexdump(&dev->interface->dev, "RX",
ch397_hexdump(&dev->interface->dev, "RX",
urb->transfer_buffer, urb->actual_length);
// Créer un sk_buff et passer au network stack
@@ -127,10 +127,10 @@ static void ch349_read_callback(struct urb *urb)
netdev->stats.rx_bytes += urb->actual_length;
printk(KERN_INFO
"ch349: Packet passed to network stack\n");
"ch397: Packet passed to network stack\n");
} else {
netdev->stats.rx_dropped++;
printk(KERN_ERR "ch349: Cannot allocate skb\n");
printk(KERN_ERR "ch397: Cannot allocate skb\n");
}
}
break;
@@ -156,7 +156,7 @@ static void ch349_read_callback(struct urb *urb)
}
// Fonction pour démarrer la lecture
static int ch349_start_rx(struct ch349_device *dev)
static int ch397_start_rx(struct ch397_device *dev)
{
struct urb *urb;
int retval;
@@ -174,7 +174,7 @@ static int ch349_start_rx(struct ch349_device *dev)
usb_fill_bulk_urb(urb, dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer, dev->bulk_in_size,
ch349_read_callback, dev);
ch397_read_callback, dev);
// Sauvegarder l'URB dans la structure
dev->rx_urb = urb;
@@ -194,7 +194,7 @@ static int ch349_start_rx(struct ch349_device *dev)
}
// Fonction pour arrêter la lecture
static void ch349_stop_rx(struct ch349_device *dev)
static void ch397_stop_rx(struct ch397_device *dev)
{
if (dev->rx_urb) {
usb_kill_urb(dev->rx_urb);
@@ -203,65 +203,144 @@ static void ch349_stop_rx(struct ch349_device *dev)
}
}
static int ch349_net_open(struct net_device *netdev)
static int ch397_net_open(struct net_device *netdev)
{
struct ch349_device *dev = netdev_priv(netdev);
struct ch397_device *dev = netdev_priv(netdev);
int retval;
printk(KERN_INFO "ch349: Interface UP\n");
printk(KERN_INFO "ch397: Interface UP\n");
retval = ch349_start_rx(dev);
retval = ch397_start_rx(dev);
if (retval)
return retval;
netif_carrier_on(netdev);
netif_start_queue(netdev);
return 0;
}
static int ch349_net_stop(struct net_device *netdev)
static int ch397_net_stop(struct net_device *netdev)
{
struct ch349_device *dev = netdev_priv(netdev);
struct ch397_device *dev = netdev_priv(netdev);
printk(KERN_INFO "ch349: Interface DOWN\n");
printk(KERN_INFO "ch397: Interface DOWN\n");
netif_stop_queue(netdev);
ch349_stop_rx(dev);
netif_carrier_off(netdev);
ch397_stop_rx(dev);
netif_carrier_off(netdev);
return 0;
}
static netdev_tx_t ch349_start_xmit(struct sk_buff *skb,
static void ch397_write_callback(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct net_device *netdev = skb->dev;
if (urb->status == 0) {
// Transmission réussie
netdev->stats.tx_packets++;
netdev->stats.tx_bytes += skb->len;
} else {
netdev->stats.tx_errors++;
}
// Libérer le skb et l'urb
dev_kfree_skb_any(skb);
usb_free_urb(urb);
}
static netdev_tx_t ch397_start_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
// Pour l'instant, on drop juste les packets TX
//printk(KERN_INFO "ch349: TX packet dropped (not implemented yet)\n");
dev_kfree_skb(skb);
netdev->stats.tx_dropped++;
struct ch397_device *dev = netdev_priv(netdev);
struct urb *urb;
int retval;
// Allouer un URB pour l'envoi
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
dev_kfree_skb(skb);
netdev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
// Préparer l'URB (Bulk OUT)
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev,
dev->bulk_out_endpointAddr),
skb->data, skb->len, ch397_write_callback, skb);
// Soumettre l'URB
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval) {
dev_err(&dev->interface->dev, "Failed to submit TX URB: %d\n",
retval);
usb_free_urb(urb);
dev_kfree_skb(skb);
netdev->stats.tx_errors++;
} else {
// Arrêter la queue si on a trop d'URB en vol (simplification ici: on laisse couler)
// netif_stop_queue(netdev);
}
return NETDEV_TX_OK;
}
static const struct net_device_ops ch349_netdev_ops = {
.ndo_open = ch349_net_open,
.ndo_stop = ch349_net_stop,
.ndo_start_xmit = ch349_start_xmit,
static const struct net_device_ops ch397_netdev_ops = {
.ndo_open = ch397_net_open,
.ndo_stop = ch397_net_stop,
.ndo_start_xmit = ch397_start_xmit,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
};
static int ch349_probe(struct usb_interface *interface,
static int ch397_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct ch349_device *dev;
struct ch397_device *dev;
struct net_device *netdev;
struct usb_endpoint_descriptor *endpoint;
u8 mac[ETH_ALEN];
int i;
int retval = -ENOMEM;
int retval = 0;
struct usb_device *udev = interface_to_usbdev(interface);
int config_val = udev->actconfig->desc.bConfigurationValue;
int intf_num = interface->cur_altsetting->desc.bInterfaceNumber;
dev_info(&interface->dev, "Probing CH349 device\n");
dev_info(&interface->dev,
"Probing CH397 device (config %d, interface %d)\n", config_val,
intf_num);
/*
* This device exposes 3 USB configurations:
* Config 1: Vendor Specific - Interface 0 has bulk endpoints directly
* Config 2: CDC Ethernet - Interface 0 = CDC Comm (no bulk)
* Interface 1, Alt 1 = CDC Data (bulk endpoints)
* Config 3: CDC NCM
*
* We handle Config 1 and Config 2. In Config 2, we skip Interface 0
* (CDC Comm) and only claim Interface 1 (CDC Data) with Alt Setting 1.
*/
// Config 2: Skip Interface 0 (CDC Communications, no bulk endpoints)
if (config_val == 2 && intf_num == 0) {
dev_info(&interface->dev,
"Skipping CDC Comm interface (no bulk endpoints)\n");
return -ENODEV;
}
// Config 3: Not supported for now
if (config_val == 3) {
dev_info(&interface->dev, "Config 3 (NCM) not supported\n");
return -ENODEV;
}
// Allouer net_device + notre structure
netdev = alloc_etherdev(sizeof(struct ch349_device));
netdev = alloc_etherdev(sizeof(struct ch397_device));
if (!netdev) {
dev_err(&interface->dev, "Cannot allocate netdev\n");
return -ENOMEM;
@@ -271,19 +350,23 @@ static int ch349_probe(struct usb_interface *interface,
dev->netdev = netdev;
mutex_init(&dev->io_mutex);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->udev = usb_get_dev(udev);
dev->interface = interface;
// Sélectionne l'altsetting 1
retval = usb_set_interface(
dev->udev, interface->cur_altsetting->desc.bInterfaceNumber, 1);
if (retval) {
dev_err(&interface->dev, "Cannot set altsetting 1: %d\n",
retval);
goto error;
// In Config 2, Interface 1: switch to Alt Setting 1 to get bulk endpoints
if (config_val == 2 && intf_num == 1) {
retval = usb_set_interface(udev, intf_num, 1);
if (retval) {
dev_err(&interface->dev,
"Cannot set altsetting 1: %d\n", retval);
goto error;
}
dev_info(&interface->dev,
"Switched to altsetting 1 for CDC Data\n");
}
dev_info(&interface->dev, "Interface obtained\n");
dev_info(&interface->dev, "Interface %d obtained (altsetting %d)\n",
intf_num, interface->cur_altsetting->desc.bAlternateSetting);
for (i = 0; i < interface->cur_altsetting->desc.bNumEndpoints; i++) {
endpoint = &interface->cur_altsetting->endpoint[i].desc;
@@ -319,15 +402,16 @@ static int ch349_probe(struct usb_interface *interface,
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
dev_err(&interface->dev,
"Could not find both bulk-in and bulk-out endpoints\n");
retval = -ENODEV;
goto error;
}
// Récupère la MAC
ch349_get_mac_address(dev, mac);
ch397_get_mac_address(dev, mac);
eth_hw_addr_set(netdev, mac);
// Configure net_device
netdev->netdev_ops = &ch349_netdev_ops;
netdev->netdev_ops = &ch397_netdev_ops;
netdev->watchdog_timeo = 5 * HZ;
usb_set_intfdata(interface, dev);
@@ -340,12 +424,12 @@ static int ch349_probe(struct usb_interface *interface,
goto error;
}
dev_info(&interface->dev, "CH349 attached as %s (MAC: %pM)\n",
netif_carrier_off(netdev);
dev_info(&interface->dev, "CH397 attached as %s (MAC: %pM)\n",
netdev->name, mac);
//retval = ch349_start_rx(dev);
//if (retval)
// goto error;
netif_carrier_off(netdev);
return 0;
@@ -359,9 +443,9 @@ error:
return retval;
}
static void ch349_disconnect(struct usb_interface *interface)
static void ch397_disconnect(struct usb_interface *interface)
{
struct ch349_device *dev;
struct ch397_device *dev;
struct net_device *netdev;
dev = usb_get_intfdata(interface);
@@ -372,18 +456,18 @@ static void ch349_disconnect(struct usb_interface *interface)
usb_set_intfdata(interface, NULL);
unregister_netdev(netdev);
ch349_stop_rx(dev);
ch397_stop_rx(dev);
kfree(dev->bulk_in_buffer);
usb_put_dev(dev->udev);
free_netdev(netdev);
dev_info(&interface->dev, "CH349 device now disconnected\n");
dev_info(&interface->dev, "CH397 device now disconnected\n");
}
static int ch349_suspend(struct usb_interface *intf, pm_message_t message)
static int ch397_suspend(struct usb_interface *intf, pm_message_t message)
{
struct ch349_device *dev = usb_get_intfdata(intf);
struct ch397_device *dev = usb_get_intfdata(intf);
if (!dev)
return 0;
@@ -394,9 +478,9 @@ static int ch349_suspend(struct usb_interface *intf, pm_message_t message)
return 0;
}
static int ch349_resume(struct usb_interface *intf)
static int ch397_resume(struct usb_interface *intf)
{
struct ch349_device *dev = usb_get_intfdata(intf);
struct ch397_device *dev = usb_get_intfdata(intf);
if (!dev)
return 0;
@@ -404,18 +488,18 @@ static int ch349_resume(struct usb_interface *intf)
return 0;
}
static struct usb_driver ch349_driver = {
.name = "ch349",
.probe = ch349_probe,
.disconnect = ch349_disconnect,
.suspend = ch349_suspend,
.resume = ch349_resume,
.id_table = ch349_table,
static struct usb_driver ch397_driver = {
.name = "ch397",
.probe = ch397_probe,
.disconnect = ch397_disconnect,
.suspend = ch397_suspend,
.resume = ch397_resume,
.id_table = ch397_table,
.supports_autosuspend = 1,
};
module_usb_driver(ch349_driver);
module_usb_driver(ch397_driver);
MODULE_AUTHOR("bob industries");
MODULE_DESCRIPTION("Driver USB pour CH349A");
MODULE_DESCRIPTION("Driver USB pour CH397A");
MODULE_LICENSE("GPL");