From e48b35ff4e9398da4b9ae65dee60a8e1f923b564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniele=20Vigan=C3=B2?= Date: Sun, 1 Mar 2026 18:27:15 +0100 Subject: [PATCH] Add systemd and udev services for dcc-connector --- connector/99-dcc-usb-connector.rules | 20 ++ connector/INSTALL.md | 334 +++++++++++++++++++++++++++ connector/dcc-usb-connector.service | 14 ++ connector/install-udev-rule.sh | 127 ++++++++++ 4 files changed, 495 insertions(+) create mode 100644 connector/99-dcc-usb-connector.rules create mode 100644 connector/INSTALL.md create mode 100644 connector/dcc-usb-connector.service create mode 100755 connector/install-udev-rule.sh diff --git a/connector/99-dcc-usb-connector.rules b/connector/99-dcc-usb-connector.rules new file mode 100644 index 0000000..e9a45b4 --- /dev/null +++ b/connector/99-dcc-usb-connector.rules @@ -0,0 +1,20 @@ +# Udev rule to auto-start dcc-usb-connector.service when USB device is connected +# +# This rule detects when a CH340 USB-to-serial adapter (ID 1a86:7523) +# is connected and assigned to /dev/ttyUSB0, then automatically starts +# the dcc-usb-connector.service (user systemd service). +# +# Installation: +# sudo cp 99-dcc-usb-connector.rules /etc/udev/rules.d/ +# sudo udevadm control --reload-rules +# sudo udevadm trigger --subsystem-match=tty +# +# Testing: +# udevadm test /sys/class/tty/ttyUSB0 +# udevadm monitor --property --subsystem-match=tty +# +# The service will be started/stopped automatically when the device +# is plugged in/unplugged. + +# Match USB device 1a86:7523 on ttyUSB0 and trigger user systemd service +SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", KERNEL=="ttyUSB0", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}="dcc-usb-connector.service" diff --git a/connector/INSTALL.md b/connector/INSTALL.md new file mode 100644 index 0000000..fe8bd79 --- /dev/null +++ b/connector/INSTALL.md @@ -0,0 +1,334 @@ +# DCC USB-to-Network Bridge Auto-Start Installation + +This directory contains configuration files to automatically start the `dcc-usb-connector.service` when a specific USB device (CH340 USB-to-serial adapter, ID `1a86:7523`) is connected to `/dev/ttyUSB0`. + +## Overview + +The setup uses: +- **Udev rule** (`99-dcc-usb-connector.rules`) - Detects USB device connection/disconnection +- **Systemd user service** (`dcc-usb-connector.service`) - Bridges serial port to network port 2560 +- **Installation script** (`install-udev-rule.sh`) - Automated installation helper + +When the USB device is plugged in, the service automatically starts. When unplugged, it stops. + +## Prerequisites + +1. **Operating System**: Linux with systemd and udev +2. **Required packages**: + ```bash + sudo dnf install nmap-ncat systemd udev + ``` +3. **User permissions**: Your user should be in the `dialout` group: + ```bash + sudo usermod -a -G dialout $USER + # Log out and log back in for changes to take effect + ``` + +## Quick Installation + +Run the installation script: + +```bash +./install-udev-rule.sh +``` + +This script will: +- Install the udev rule (requires sudo) +- Install the systemd user service to `~/.config/systemd/user/` +- Enable systemd lingering for your user +- Check for required tools and permissions +- Provide testing instructions + +## Manual Installation + +If you prefer to install manually: + +### 1. Install the udev rule + +```bash +sudo cp 99-dcc-usb-connector.rules /etc/udev/rules.d/ +sudo udevadm control --reload-rules +sudo udevadm trigger --subsystem-match=tty +``` + +### 2. Install the systemd service + +```bash +mkdir -p ~/.config/systemd/user/ +cp dcc-usb-connector.service ~/.config/systemd/user/ +systemctl --user daemon-reload +``` + +### 3. Enable lingering (optional but recommended) + +This allows your user services to run even when you're not logged in: + +```bash +sudo loginctl enable-linger $USER +``` + +## Verification + +### Test the udev rule + +```bash +# Monitor udev events (plug/unplug device while this runs) +udevadm monitor --property --subsystem-match=tty + +# Test udev rule (when device is connected) +udevadm test /sys/class/tty/ttyUSB0 +``` + +### Check service status + +```bash +# Check if service is running +systemctl --user status dcc-usb-connector.service + +# View service logs +journalctl --user -u dcc-usb-connector.service -f +``` + +### Test the network bridge + +```bash +# Connect to the bridge +telnet localhost 2560 + +# Or using netcat +nc localhost 2560 +``` + +## Usage + +### Automatic Operation + +Once installed, the service will: +- **Start automatically** when USB device `1a86:7523` is connected to `/dev/ttyUSB0` +- **Stop automatically** when the device is disconnected +- Bridge serial communication to network port `2560` + +### Manual Control + +You can still manually control the service: + +```bash +# Start the service +systemctl --user start dcc-usb-connector.service + +# Stop the service +systemctl --user stop dcc-usb-connector.service + +# Check status +systemctl --user status dcc-usb-connector.service + +# View logs +journalctl --user -u dcc-usb-connector.service +``` + +## How It Works + +### Component Interaction + +``` +USB Device Connected (1a86:7523 on /dev/ttyUSB0) + ↓ + Udev Rule Triggered + ↓ + Systemd User Service Started + ↓ + stty configures serial port (115200 baud) + ↓ + ncat bridges /dev/ttyUSB0 ↔ TCP port 2560 + ↓ + Client apps connect to localhost:2560 +``` + +### Udev Rule Details + +The udev rule (`99-dcc-usb-connector.rules`) matches: +- **Subsystem**: `tty` (TTY/serial devices) +- **Vendor ID**: `1a86` (CH340 manufacturer) +- **Product ID**: `7523` (CH340 serial adapter) +- **Kernel device**: `ttyUSB0` (specific port) + +When matched, it sets `ENV{SYSTEMD_USER_WANTS}="dcc-usb-connector.service"`, telling systemd to start the service. + +### Service Configuration + +The service (`dcc-usb-connector.service`): +1. Runs `stty -F /dev/ttyUSB0 -echo 115200` to configure the serial port +2. Executes `ncat -n -k -l 2560 /dev/ttyUSB0` to bridge serial ↔ network +3. Uses `KillMode=mixed` for proper process cleanup +4. Terminates within 5 seconds when stopped + +## Troubleshooting + +### Service doesn't start automatically + +1. **Check udev rule is loaded**: + ```bash + udevadm test /sys/class/tty/ttyUSB0 | grep SYSTEMD_USER_WANTS + ``` + Should show: `ENV{SYSTEMD_USER_WANTS}='dcc-usb-connector.service'` + +2. **Check device is recognized**: + ```bash + lsusb | grep 1a86:7523 + ls -l /dev/ttyUSB0 + ``` + +3. **Verify systemd user instance is running**: + ```bash + systemctl --user status + loginctl show-user $USER | grep Linger + ``` + +### Permission denied on /dev/ttyUSB0 + +Add your user to the `dialout` group: +```bash +sudo usermod -a -G dialout $USER +# Log out and log back in +groups # Verify 'dialout' appears +``` + +### Device appears as /dev/ttyUSB1 instead of /dev/ttyUSB0 + +The udev rule specifically matches `ttyUSB0`. To make it flexible: + +Edit `99-dcc-usb-connector.rules` and change: +``` +KERNEL=="ttyUSB0" +``` +to: +``` +KERNEL=="ttyUSB[0-9]*" +``` + +Then reload: +```bash +sudo udevadm control --reload-rules +sudo udevadm trigger --subsystem-match=tty +``` + +### Service starts but ncat fails + +1. **Check ncat is installed**: + ```bash + which ncat + ncat --version + ``` + +2. **Verify serial port works**: + ```bash + stty -F /dev/ttyUSB0 + cat /dev/ttyUSB0 # Should not error + ``` + +3. **Check port 2560 is available**: + ```bash + netstat -tuln | grep 2560 + # Should be empty if nothing is listening + ``` + +### View detailed logs + +```bash +# Follow service logs in real-time +journalctl --user -u dcc-usb-connector.service -f + +# View all logs for the service +journalctl --user -u dcc-usb-connector.service + +# View with timestamps +journalctl --user -u dcc-usb-connector.service -o short-iso +``` + +## Uninstallation + +To remove the auto-start feature: + +```bash +# Remove udev rule +sudo rm /etc/udev/rules.d/99-dcc-usb-connector.rules +sudo udevadm control --reload-rules +sudo udevadm trigger --subsystem-match=tty + +# Remove systemd service +systemctl --user stop dcc-usb-connector.service +rm ~/.config/systemd/user/dcc-usb-connector.service +systemctl --user daemon-reload + +# (Optional) Disable lingering +sudo loginctl disable-linger $USER +``` + +## Advanced Configuration + +### Customize for different USB device + +Edit `99-dcc-usb-connector.rules` and change: +- `ATTRS{idVendor}=="1a86"` - USB vendor ID +- `ATTRS{idProduct}=="7523"` - USB product ID + +Find your device IDs with: +```bash +lsusb +# Output: Bus 001 Device 003: ID 1a86:7523 QinHeng Electronics ... +# ^^^^:^^^^ +# VID PID +``` + +### Change network port + +Edit `dcc-usb-connector.service` and change: +``` +ExecStart=/usr/bin/bash -c "/usr/bin/ncat -n -k -l 2560 ... +``` +Replace `2560` with your desired port number. + +### Enable auto-restart on failure + +Edit `dcc-usb-connector.service` and add under `[Service]`: +``` +Restart=on-failure +RestartSec=5 +``` + +Then reload: +```bash +systemctl --user daemon-reload +``` + +## Testing Without Physical Device + +For development/testing without the actual USB device: + +```bash +# Create a virtual serial port pair +socat -d -d pty,raw,echo=0 pty,raw,echo=0 + +# This creates two linked devices, e.g., /dev/pts/3 and /dev/pts/4 +# Update the service to use one of these instead of /dev/ttyUSB0 +``` + +## References + +- [systemd user services](https://www.freedesktop.org/software/systemd/man/systemd.service.html) +- [udev rules writing](https://www.reactivated.net/writing_udev_rules.html) +- [ncat documentation](https://nmap.org/ncat/) +- [DCC++ EX](https://dcc-ex.com/) - The DCC command station software + +## License + +See the main project LICENSE file. + +## Support + +For issues specific to the auto-start feature: +1. Check the troubleshooting section above +2. Review logs: `journalctl --user -u dcc-usb-connector.service` +3. Test udev rules: `udevadm test /sys/class/tty/ttyUSB0` + +For DCC++ EX or django-ram issues, see the main project documentation. diff --git a/connector/dcc-usb-connector.service b/connector/dcc-usb-connector.service new file mode 100644 index 0000000..cc18c5e --- /dev/null +++ b/connector/dcc-usb-connector.service @@ -0,0 +1,14 @@ +[Unit] +Description=DCC USB-to-Network Bridge Daemon +After=network.target +ConditionPathIsReadWrite=/dev/ttyUSB0 + +[Service] +ExecStartPre=/usr/bin/stty -F /dev/ttyUSB0 -echo 115200 +ExecStart=/usr/bin/bash -c "/usr/bin/ncat -n -k -l 2560 /dev/ttyUSB0" +KillMode=mixed +TimeoutStopSec=5 +PrivateTmp=true + +[Install] +WantedBy=default.target diff --git a/connector/install-udev-rule.sh b/connector/install-udev-rule.sh new file mode 100755 index 0000000..ec718ec --- /dev/null +++ b/connector/install-udev-rule.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# +# Installation script for DCC USB-to-Network Bridge auto-start +# +# This script installs the udev rule and systemd service to automatically +# start the dcc-usb-connector.service when USB device 1a86:7523 is connected. +# +# Usage: +# ./install-udev-rule.sh +# + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Get the directory where this script is located +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo -e "${GREEN}DCC USB-to-Network Bridge Auto-Start Installation${NC}" +echo "==========================================================" +echo + +# Check if running as root (not recommended for systemd user service) +if [ "$EUID" -eq 0 ]; then + echo -e "${YELLOW}Warning: You are running as root.${NC}" + echo "This script will install a user systemd service." + echo "Please run as a regular user (not with sudo)." + echo + read -p "Continue anyway? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 1 + fi +fi + +# Check for required files +echo "Checking required files..." +if [ ! -f "$SCRIPT_DIR/99-dcc-usb-connector.rules" ]; then + echo -e "${RED}Error: 99-dcc-usb-connector.rules not found${NC}" + exit 1 +fi +if [ ! -f "$SCRIPT_DIR/dcc-usb-connector.service" ]; then + echo -e "${RED}Error: dcc-usb-connector.service not found${NC}" + exit 1 +fi +echo -e "${GREEN}✓ All required files found${NC}" +echo + +# Install udev rule (requires sudo) +echo "Installing udev rule..." +echo "This requires sudo privileges." +sudo cp "$SCRIPT_DIR/99-dcc-usb-connector.rules" /etc/udev/rules.d/ +sudo udevadm control --reload-rules +sudo udevadm trigger --subsystem-match=tty +echo -e "${GREEN}✓ Udev rule installed${NC}" +echo + +# Install systemd user service +echo "Installing systemd user service..." +mkdir -p ~/.config/systemd/user/ +cp "$SCRIPT_DIR/dcc-usb-connector.service" ~/.config/systemd/user/ +systemctl --user daemon-reload +echo -e "${GREEN}✓ Systemd service installed${NC}" +echo + +# Enable lingering (allows user services to run without being logged in) +echo "Enabling systemd lingering for user..." +if loginctl show-user "$USER" | grep -q "Linger=yes"; then + echo -e "${GREEN}✓ Lingering already enabled${NC}" +else + sudo loginctl enable-linger "$USER" + echo -e "${GREEN}✓ Lingering enabled${NC}" +fi +echo + +# Check user groups +echo "Checking user permissions..." +if groups "$USER" | grep -q '\bdialout\b'; then + echo -e "${GREEN}✓ User is in 'dialout' group${NC}" +else + echo -e "${YELLOW}Warning: User is not in 'dialout' group${NC}" + echo "You may need to add yourself to the dialout group:" + echo " sudo usermod -a -G dialout $USER" + echo "Then log out and log back in for changes to take effect." +fi +echo + +# Check for ncat +echo "Checking for required tools..." +if command -v ncat &> /dev/null; then + echo -e "${GREEN}✓ ncat is installed${NC}" +else + echo -e "${YELLOW}Warning: ncat is not installed${NC}" + echo "Install it with: sudo dnf install nmap-ncat" +fi +echo + +# Summary +echo "==========================================================" +echo -e "${GREEN}Installation complete!${NC}" +echo +echo "The service will automatically start when USB device 1a86:7523" +echo "is connected to /dev/ttyUSB0" +echo +echo "To test:" +echo " 1. Plug in the USB device" +echo " 2. Check service status: systemctl --user status dcc-usb-connector.service" +echo " 3. Test connection: telnet localhost 2560" +echo +echo "To manually control:" +echo " Start: systemctl --user start dcc-usb-connector.service" +echo " Stop: systemctl --user stop dcc-usb-connector.service" +echo " Status: systemctl --user status dcc-usb-connector.service" +echo +echo "To view logs:" +echo " journalctl --user -u dcc-usb-connector.service -f" +echo +echo "To uninstall:" +echo " sudo rm /etc/udev/rules.d/99-dcc-usb-connector.rules" +echo " rm ~/.config/systemd/user/dcc-usb-connector.service" +echo " systemctl --user daemon-reload" +echo " sudo udevadm control --reload-rules" +echo