/* * dependencies: * * libusb-dev * */ #include #include #include #include #include #include #define CH397_VENDOR_ID 0x1a86 #define CH397_PRODUCT_ID 0x5397 struct ch397_device { struct usb_device *udev; struct usb_interface *interface; struct mutex io_mutex; __u8 bulk_in_endpointAddr; __u8 bulk_out_endpointAddr; size_t bulk_in_size; unsigned char *bulk_in_buffer; struct urb *rx_urb; struct net_device *netdev; }; static const struct usb_device_id ch397_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(CH397_VENDOR_ID, CH397_PRODUCT_ID, USB_CLASS_CDC_DATA, 0, 0) }, {} }; MODULE_DEVICE_TABLE(usb, ch397_table); // Fonction pour récupérer la MAC static int ch397_get_mac_address(struct ch397_device *dev, u8 *mac) { char buf[13]; int ret, i; ret = usb_string(dev->udev, 3, buf, sizeof(buf)); if (ret != 12) { dev_err(&dev->interface->dev, "Cannot read MAC (got %d bytes)\n", ret); // MAC par défaut si échec eth_random_addr(mac); return 0; } // Convertir "DC3262229393" -> DC:32:62:22:93:93 for (i = 0; i < 6; i++) { char hex[3] = { buf[i * 2], buf[i * 2 + 1], 0 }; if (kstrtou8(hex, 16, &mac[i])) { dev_err(&dev->interface->dev, "Invalid MAC format\n"); eth_random_addr(mac); return 0; } } return 0; } static void ch397_hexdump(const struct device *dev, const char *prefix, const void *data, size_t len) { const unsigned char *buf = data; char line[80]; size_t i, j; for (i = 0; i < len; i += 16) { char *p = line; p += sprintf(p, "%s %04zx: ", prefix, i); // Hexa for (j = 0; j < 16; j++) { if (i + j < len) p += sprintf(p, "%02x ", buf[i + j]); else p += sprintf(p, " "); } p += sprintf(p, " "); // ASCII for (j = 0; j < 16 && i + j < len; j++) { unsigned char c = buf[i + j]; p += sprintf(p, "%c", (c >= 32 && c < 127) ? c : '.'); } dev_info(dev, "%s\n", line); } } static void ch397_read_callback(struct urb *urb) { 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, "ch397: Received %d bytes\n", urb->actual_length); ch397_hexdump(&dev->interface->dev, "RX", urb->transfer_buffer, urb->actual_length); // Créer un sk_buff et passer au network stack skb = netdev_alloc_skb(netdev, urb->actual_length + 2); if (skb) { skb_reserve(skb, 2); // Alignement IP skb_put_data(skb, urb->transfer_buffer, urb->actual_length); skb->protocol = eth_type_trans(skb, netdev); // Passer au kernel network stack netif_rx(skb); netdev->stats.rx_packets++; netdev->stats.rx_bytes += urb->actual_length; printk(KERN_INFO "ch397: Packet passed to network stack\n"); } else { netdev->stats.rx_dropped++; printk(KERN_ERR "ch397: Cannot allocate skb\n"); } } break; case -ENOENT: case -ECONNRESET: case -ESHUTDOWN: return; default: dev_err(&dev->interface->dev, "RX URB error: %d\n", status); netdev->stats.rx_errors++; break; } // Resoumettre l'URB status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { dev_err(&dev->interface->dev, "Failed to resubmit URB: %d\n", status); netdev->stats.rx_errors++; } } // Fonction pour démarrer la lecture static int ch397_start_rx(struct ch397_device *dev) { struct urb *urb; int retval; // Allouer un URB urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { dev_err(&dev->interface->dev, "Cannot allocate URB\n"); return -ENOMEM; } // En CDC-ECM, les packets peuvent être jusqu'à ~1600 bytes // Préparer l'URB usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), dev->bulk_in_buffer, dev->bulk_in_size, ch397_read_callback, dev); // Sauvegarder l'URB dans la structure dev->rx_urb = urb; // Soumettre l'URB retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { dev_err(&dev->interface->dev, "Failed to submit URB: %d\n", retval); usb_free_urb(urb); dev->rx_urb = NULL; return retval; } dev_info(&dev->interface->dev, "RX started, waiting for packets...\n"); return 0; } // Fonction pour arrêter la lecture static void ch397_stop_rx(struct ch397_device *dev) { if (dev->rx_urb) { usb_kill_urb(dev->rx_urb); usb_free_urb(dev->rx_urb); dev->rx_urb = NULL; } } static int ch397_net_open(struct net_device *netdev) { struct ch397_device *dev = netdev_priv(netdev); int retval; printk(KERN_INFO "ch397: Interface UP\n"); retval = ch397_start_rx(dev); if (retval) return retval; netif_start_queue(netdev); return 0; } static int ch397_net_stop(struct net_device *netdev) { struct ch397_device *dev = netdev_priv(netdev); printk(KERN_INFO "ch397: Interface DOWN\n"); netif_stop_queue(netdev); ch397_stop_rx(dev); return 0; } static netdev_tx_t ch397_start_xmit(struct sk_buff *skb, struct net_device *netdev) { // Pour l'instant, on drop juste les packets TX dev_kfree_skb(skb); netdev->stats.tx_dropped++; return NETDEV_TX_OK; } 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 ch397_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct ch397_device *dev; struct net_device *netdev; struct usb_endpoint_descriptor *endpoint; u8 mac[ETH_ALEN]; int i; int retval = -ENOMEM; dev_info(&interface->dev, "Probing CH397 device\n"); // Allouer net_device + notre structure netdev = alloc_etherdev(sizeof(struct ch397_device)); if (!netdev) { dev_err(&interface->dev, "Cannot allocate netdev\n"); return -ENOMEM; } dev = netdev_priv(netdev); dev->netdev = netdev; mutex_init(&dev->io_mutex); dev->udev = usb_get_dev(interface_to_usbdev(interface)); 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; } dev_info(&interface->dev, "Interface obtained\n"); for (i = 0; i < interface->cur_altsetting->desc.bNumEndpoints; i++) { endpoint = &interface->cur_altsetting->endpoint[i].desc; dev_info(&interface->dev, "Found endpoint 0x%02x\n", endpoint->bEndpointAddress); // Debugging line if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_size = 2048; // Buffer ethernet dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL); if (!dev->bulk_in_buffer) { dev_err(&interface->dev, "Could not allocate bulk_in_buffer\n"); goto error; } dev_info(&interface->dev, "Bulk IN endpoint found: 0x%02x\n", dev->bulk_in_endpointAddr); } if (!dev->bulk_out_endpointAddr && usb_endpoint_is_bulk_out(endpoint)) { dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; dev_info(&interface->dev, "Bulk OUT endpoint found: 0x%02x\n", dev->bulk_out_endpointAddr); } } if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { dev_err(&interface->dev, "Could not find both bulk-in and bulk-out endpoints\n"); goto error; } // Récupère la MAC ch397_get_mac_address(dev, mac); eth_hw_addr_set(netdev, mac); // Configure net_device netdev->netdev_ops = &ch397_netdev_ops; netdev->watchdog_timeo = 5 * HZ; usb_set_intfdata(interface, dev); // Enregistre l'interface réseau retval = register_netdev(netdev); if (retval) { dev_err(&interface->dev, "Cannot register netdev: %d\n", retval); goto error; } dev_info(&interface->dev, "CH397 attached as %s (MAC: %pM)\n", netdev->name, mac); //if (retval) // goto error; return 0; error: if (dev) { kfree(dev->bulk_in_buffer); usb_put_dev(dev->udev); } if (netdev) free_netdev(netdev); return retval; } static void ch397_disconnect(struct usb_interface *interface) { struct ch397_device *dev; struct net_device *netdev; dev = usb_get_intfdata(interface); if (!dev) return; netdev = dev->netdev; usb_set_intfdata(interface, NULL); unregister_netdev(netdev); ch397_stop_rx(dev); kfree(dev->bulk_in_buffer); usb_put_dev(dev->udev); free_netdev(netdev); dev_info(&interface->dev, "CH397 device now disconnected\n"); } static int ch397_suspend(struct usb_interface *intf, pm_message_t message) { struct ch397_device *dev = usb_get_intfdata(intf); if (!dev) return 0; mutex_lock(&dev->io_mutex); mutex_unlock(&dev->io_mutex); return 0; } static int ch397_resume(struct usb_interface *intf) { struct ch397_device *dev = usb_get_intfdata(intf); if (!dev) return 0; return 0; } 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(ch397_driver); MODULE_AUTHOR("bob industries"); MODULE_DESCRIPTION("Driver USB pour CH397A"); MODULE_LICENSE("GPL");