Compare commits
9 Commits
main
...
driver_ray
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ad8705650 | ||
|
|
acc94b109e | ||
|
|
0ca4a10aa4 | ||
|
|
46e2d50a55 | ||
|
|
95889d091c | ||
|
|
32c039da54 | ||
|
|
16119c5725 | ||
|
|
2931340eee | ||
|
|
aa29c8b2a5 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
build
|
||||
compile_commands.json
|
||||
.module-commands.o
|
||||
|
||||
BIN
.module-common.o
BIN
.module-common.o
Binary file not shown.
4
Makefile
4
Makefile
@@ -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
216
README.md
@@ -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 l’interface entre le noyau du système d’exploitation et un périphérique matériel ou une couche d’abstraction. 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 d’appels : implémenter les callbacks et API attendus par le sous‑systè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 d’attente, copies de buffers et synchronisation.
|
||||
- Gestion d’erreurs et robustesse : détecter conditions d’erreur, reprendre, nettoyer proprement à la déconnexion ou en cas d’échec.
|
||||
- Exposition des fonctionnalités avancées : support d’options 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 d’exécution (process/context IRQ/softirq).
|
||||
|
||||
Types et niveaux
|
||||
- Pilotes noyau (in‑kernel) : s’exé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 vendor‑specific : les pilotes de classe implémentent une interface standard (ex. CDC, HID) interopérable ; les pilotes vendor‑specific 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 sous‑systè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 RISC‑V QingKe, un contrôleur USB2.0/2.1 avec transceiver PHY conforme USB2.1, ainsi qu’un sous‑système Ethernet complet (MAC + PHY) conforme IEEE 802.3, supportant 10/100 Mbps.
|
||||
|
||||
Architecture matérielle
|
||||
- **SoC tout‑en‑un :** processeur embarqué QingKe RISC‑V, 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/High‑Speed :** 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 (10BASE‑T / 100BASE‑TX) ; support de l’auto‑négociation 10/100 Mbps, Auto‑MDIX 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 l’hôte.
|
||||
- **Fonctions matérielles supplémentaires :** LDO interne, support ESD amélioré (6 kV), configuration LED matérielle, wake‑up réseau et modes basse consommation.
|
||||
|
||||
Interfaces réseau disponibles
|
||||
- **CDC‑ECM, CDC‑NCM et RNDIS :** support natif de CDC‑ECM et CDC‑NCM (USB Ethernet classes) et RNDIS — possibilité d’utilisation sans pilote propriétaire sur de nombreux hôtes, ou avec pilote vendor‑specific si souhaité.
|
||||
- **Endpoints USB standard :** modes vendor‑specific (bulk endpoints) et classes réseau standard (interfaces Communications/Data avec alt‑setting pour endpoints bulk IN/OUT).
|
||||
- **Fonctionnalités réseau hardware‑assist :**
|
||||
- génération et vérification de checksums IPv4/IPv6 (TCP/UDP/HEAD),
|
||||
- support IEEE 802.3x flow control et fallback half‑duplex,
|
||||
- prise en charge VLAN (802.3Q),
|
||||
- wake‑on‑LAN (magic packet / wake packets).
|
||||
|
||||
Contraintes et exigences spécifiques
|
||||
- **Choix de configuration USB :** la puce expose plusieurs configurations (vendor, CDC‑ECM, NCM, RNDIS) — le pilote doit détecter la configuration active et sélectionner l’interface/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. ≥ 1600–2048 octets) et gérer l’alignement skb.
|
||||
- **Performances et flux :** le CH397 prend en charge features hardware‑assist (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 basse‑consommation et wake‑up :** la puce propose modes Sleep et wake‑on‑LAN — 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 d’oscillateur 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é multi‑OS :** grâce au support CDC‑ECM/NCM/RNDIS, le CH397 peut fonctionner sans pilote propriétaire sur de nombreux systèmes ; toutefois, un pilote dédié permet d’exploiter au mieux les optimisations matérielles et les modes vendor‑specific.
|
||||
|
||||
En synthèse, le CH397 fournit une solution USB→Ethernet complète et optimisée, offrant à la fois compatibilité plug‑and‑play via classes USB standard et options avancées (offloads, VLAN, wake‑up, basse consommation) pour une intégration performante sur hôte via un pilote USB‑Ethernet 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 d’opé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 d’entré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 d’un 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 l’adresse 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 d’adresse MAC.
|
||||
- Conséquences :
|
||||
- Le driver s’appuie sur les helpers pour respecter les conventions Ethernet sans réimplémenter la validation/gestion MAC.
|
||||
- L’alignement 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 d’impression 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 d’identification: 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 : l’URB 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 l’URB 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 d’URB pour haute performance non implémenté ; actuellement allocation à la volée.
|
||||
- Attention à l’utilisation de skb->data directement : garantir que le skb n’est pas modifié tant que l’URB 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 d’initialisation et d’args parsing
|
||||
- Initialisation principale dans ch397_probe :
|
||||
- Vérifier configuration/interface USB et ignorer l’interface 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 l’altsetting courant pour découvrir bulk-in et bulk-out ; allouer bulk_in_buffer (kmalloc).
|
||||
- Récupérer l’adresse MAC via ch397_get_mac_address (usb_string) et l’appliquer via eth_hw_addr_set.
|
||||
- Initialiser netdev->netdev_ops, watchdog_timeo, et appeler register_netdev.
|
||||
- En cas d’erreur, faire cleanup (usb_put_dev, kfree buffer, free_netdev).
|
||||
- Pas d’args parsing via module_param dans ce code — stratégie : valeurs par défaut et auto-détection depuis l’USB device string/config.
|
||||
|
||||
### 5.2 Routine d’attach/detach
|
||||
- Attach (probe) : allouer structures, configurer endpoints, set MAC, register_netdev, netif_carrier_off (attente de l’open).
|
||||
- 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 d’attente et gestion
|
||||
- Flux d’envoi :
|
||||
- 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é jusqu’au ch397_write_callback qui libère le skb et l’URB.
|
||||
- Gestion de la queue :
|
||||
- Implémentation actuelle ne stoppe pas la queue quand trop d’URB en vol (commentaire présent). Pour production : ajouter compteur d’URB 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 ; s’assurer 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 l’URB via usb_submit_urb(urb, GFP_ATOMIC) pour continuer la réception.
|
||||
- Robustesse :
|
||||
- Gestion d’allocation skb échouée : incrémenter rx_dropped.
|
||||
- Toujours resoumettre URB tant que l’interface reste active ; gérer échecs de resubmit (logger, incrémenter erreur).
|
||||
- S’assurer que le buffer dev->bulk_in_buffer reste alloué pendant la durée de vie de l’URB (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 d’initialisation : nettoyage et retour d’erreur 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 l’USB (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 d’exécution :
|
||||
- Callbacks URB s’exé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) s’exé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 d’erreurs.
|
||||
- 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
210
Rapport.md
Normal file
@@ -0,0 +1,210 @@
|
||||
## 1. Introduction
|
||||
|
||||
Un driver (pilote) est un composant logiciel qui fait l’interface entre le noyau du système d’exploitation et un périphérique matériel ou une couche d’abstraction. 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 d’appels : implémenter les callbacks et API attendus par le sous‑systè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 d’attente, copies de buffers et synchronisation.
|
||||
- Gestion d’erreurs et robustesse : détecter conditions d’erreur, reprendre, nettoyer proprement à la déconnexion ou en cas d’échec.
|
||||
- Exposition des fonctionnalités avancées : support d’options 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 d’exécution (process/context IRQ/softirq).
|
||||
|
||||
Types et niveaux
|
||||
|
||||
- Pilotes noyau (in‑kernel) : s’exé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 vendor‑specific : les pilotes de classe implémentent une interface standard (ex. CDC, HID) interopérable ; les pilotes vendor‑specific 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 sous‑systè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 RISC‑V QingKe, un contrôleur USB2.0/2.1 avec transceiver PHY conforme USB2.1, ainsi qu’un sous‑système Ethernet complet (MAC + PHY) conforme IEEE 802.3, supportant 10/100 Mbps.
|
||||
|
||||
Architecture matérielle
|
||||
|
||||
- **SoC tout‑en‑un :** processeur embarqué QingKe RISC‑V, 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/High‑Speed :** 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 (10BASE‑T / 100BASE‑TX) ; support de l’auto‑négociation 10/100 Mbps, Auto‑MDIX 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 l’hôte.
|
||||
- **Fonctions matérielles supplémentaires :** LDO interne, support ESD amélioré (6 kV), configuration LED matérielle, wake‑up réseau et modes basse consommation.
|
||||
|
||||
Interfaces réseau disponibles
|
||||
|
||||
- **CDC‑ECM, CDC‑NCM et RNDIS :** support natif de CDC‑ECM et CDC‑NCM (USB Ethernet classes) et RNDIS — possibilité d’utilisation sans pilote propriétaire sur de nombreux hôtes, ou avec pilote vendor‑specific si souhaité.
|
||||
- **Endpoints USB standard :** modes vendor‑specific (bulk endpoints) et classes réseau standard (interfaces Communications/Data avec alt‑setting pour endpoints bulk IN/OUT).
|
||||
- **Fonctionnalités réseau hardware‑assist :**
|
||||
- génération et vérification de checksums IPv4/IPv6 (TCP/UDP/HEAD),
|
||||
- support IEEE 802.3x flow control et fallback half‑duplex,
|
||||
- prise en charge VLAN (802.3Q),
|
||||
- wake‑on‑LAN (magic packet / wake packets).
|
||||
|
||||
Contraintes et exigences spécifiques
|
||||
|
||||
- **Choix de configuration USB :** la puce expose plusieurs configurations (vendor, CDC‑ECM, NCM, RNDIS) — le pilote doit détecter la configuration active et sélectionner l’interface/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. >= 1600–2048 octets) et gérer l’alignement skb.
|
||||
- **Performances et flux :** le CH397 prend en charge features hardware‑assist (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 basse‑consommation et wake‑up :** la puce propose modes Sleep et wake‑on‑LAN — 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 d’oscillateur 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é multi‑OS :** grâce au support CDC‑ECM/NCM/RNDIS, le CH397 peut fonctionner sans pilote propriétaire sur de nombreux systèmes ; toutefois, un pilote dédié permet d’exploiter au mieux les optimisations matérielles et les modes vendor‑specific.
|
||||
|
||||
En synthèse, le CH397 fournit une solution USB→Ethernet complète et optimisée, offrant à la fois compatibilité plug‑and‑play via classes USB standard et options avancées (offloads, VLAN, wake‑up, basse consommation) pour une intégration performante sur hôte via un pilote USB‑Ethernet 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 d’opé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 d’entré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 d’un 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 l’adresse 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 d’adresse MAC.
|
||||
- Conséquences :
|
||||
- Le driver s’appuie sur les helpers pour respecter les conventions Ethernet sans réimplémenter la validation/gestion MAC.
|
||||
- L’alignement 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 d’impression 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 d’identification: 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 : l’URB 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 l’URB 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 d’URB pour haute performance non implémenté ; actuellement allocation à la volée.
|
||||
- Attention à l’utilisation de skb->data directement : garantir que le skb n’est pas modifié tant que l’URB 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 d’initialisation et d’args parsing
|
||||
- Initialisation principale dans ch397_probe :
|
||||
- Vérifier configuration/interface USB et ignorer l’interface 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 l’altsetting courant pour découvrir bulk-in et bulk-out ; allouer bulk_in_buffer (kmalloc).
|
||||
- Récupérer l’adresse MAC via ch397_get_mac_address (usb_string) et l’appliquer via eth_hw_addr_set.
|
||||
- Initialiser netdev->netdev_ops, watchdog_timeo, et appeler register_netdev.
|
||||
- En cas d’erreur, faire cleanup (usb_put_dev, kfree buffer, free_netdev).
|
||||
- Pas d’args parsing via module_param dans ce code — stratégie : valeurs par défaut et auto-détection depuis l’USB device string/config.
|
||||
|
||||
### 5.2 Routine d’attach/detach
|
||||
- Attach (probe) : allouer structures, configurer endpoints, set MAC, register_netdev, netif_carrier_off (attente de l’open).
|
||||
- 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 d’attente et gestion
|
||||
- Flux d’envoi :
|
||||
- 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é jusqu’au ch397_write_callback qui libère le skb et l’URB.
|
||||
- Gestion de la queue :
|
||||
- Implémentation actuelle ne stoppe pas la queue quand trop d’URB en vol (commentaire présent). Pour production : ajouter compteur d’URB 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 ; s’assurer 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 l’URB via usb_submit_urb(urb, GFP_ATOMIC) pour continuer la réception.
|
||||
- Robustesse :
|
||||
- Gestion d’allocation skb échouée : incrémenter rx_dropped.
|
||||
- Toujours resoumettre URB tant que l’interface reste active ; gérer échecs de resubmit (logger, incrémenter erreur).
|
||||
- S’assurer que le buffer dev->bulk_in_buffer reste alloué pendant la durée de vie de l’URB (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 d’initialisation : nettoyage et retour d’erreur 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 l’USB (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 d’exécution :
|
||||
- Callbacks URB s’exé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) s’exé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 d’erreurs.
|
||||
- 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
BIN
Rapport.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||
}
|
||||
]
|
||||
@@ -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
81
send_eth.c
Normal 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", ðertype) != 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;
|
||||
}
|
||||
@@ -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");
|
||||
Reference in New Issue
Block a user