Interfacing with serial ports within the Windows Subsystem for Linux (WSL) environment can present challenges,particularly when aiming to establish communication between Windows and Linux processes or when Linux applications require direct serial device access. This is often encountered during development and testing phases where, for example, a loopback setup using serial ports is desired between the host Windows system and a WSL distribution, or when connecting to external hardware like microcontrollers.
This article outlines the underlying reasons for these challenges and provides practical methods to enable serial port functionality in both WSL1 and WSL2 environments.
Understanding Serial Port Access in WSL
The behavior of serial port access differs significantly between WSL1 and WSL2 due to their distinct architectures. WSL1 translates Linux system calls into Windows system calls, which can offer more straightforward access to certain Windows resources, including serial ports. These often appear as /dev/ttyS#
devices corresponding to Windows COM ports.
WSL2, on the other hand, operates with a full Linux kernel within a lightweight virtual machine. This provides better performance and compatibility for many applications but means direct hardware access, including to serial ports, is not inherently available.
To use serial devices in WSL2, mechanisms are required to pass the hardware from the Windows host to the WSL2 virtual environment, and the WSL2 kernel must possess the appropriate drivers to recognize and manage these devices. Standard WSL2 kernels, optimized by Microsoft, may not include all possible hardware drivers by default to maintain a lean profile.
Read: Identifying Processes Using Specific TCP/UDP Ports on Windows
Solutions for Enabling Serial Port Access
Several approaches can facilitate serial port usage within WSL, catering to different WSL versions and device types, particularly USB-to-serial adapters.
Method 1: Utilizing `usbipd-win` for USB Serial Adapters in WSL2 (Recommended)
For systems running WSL2 and utilizing USB-to-serial adapters, the usbipd-win
project provides a robust solution by enabling USB device sharing between Windows and WSL2.
- Download and Install `usbipd-win`: Obtain the latest release of
usbipd-win
from its official source and install it on your Windows system. - List USB Devices: Open PowerShell with administrator privileges and list available USB devices connected to Windows:
usbipd list
- Share the USB Device: Identify the BUSID of the target USB-to-serial adapter (e.g.,
4-4
) from the list. Share this device so it can be attached to WSL:usbipd bind --busid #BUSID#
This command requires administrator privileges and typically only needs to be performed once per device.
- Attach the Device to WSL: Attach the shared USB device to your WSL2 distribution:
usbipd attach --wsl --busid #BUSID#
- Verify in WSL: Within your WSL2 terminal, you can check for the presence of the USB device:
lsusb
And inspect kernel messages for device recognition and allocation of a TTY device (e.g.,
/dev/ttyUSB0
):dmesg | tail
This method allows a USB serial device connected to Windows to be fully managed by the Linux kernel within WSL2. For a loopback test between Windows and WSL, two USB-to-serial adapters can be used. One adapter remains with Windows, and the other is attached to WSL2 using usbipd-win
. Physically connecting the Tx/Rx lines of these two adapters creates a serial loopback path.
Read: How to Close a Specific Port on Linux and Windows
Method 2: Custom WSL2 Kernel Compilation for Broader Driver Support (Advanced)
In some instances, even after attaching a USB device via usbipd-win
, WSL2 might not create a corresponding /dev/tty*
device. This often indicates that the default WSL2 kernel lacks the necessary driver module for that specific USB-to-serial chipset (e.g., PL2303, FTDI).
The solution involves recompiling the WSL2 Linux kernel with the required drivers enabled:
Read: How to Configure X11 Forwarding for WSL2
- Install Build Dependencies: Within your WSL distribution, install the necessary tools for kernel compilation:
sudo apt update sudo apt install build-essential flex bison libssl-dev libelf-dev libncurses-dev autoconf libudev-dev libtool
- Identify Current Kernel Version:
uname -r
Note this version (e.g.,
5.15.153.1-microsoft-standard-WSL2
). You’ll need the base version like5.15.153.1
. - Clone WSL2 Kernel Source:
git clone https://github.com/microsoft/WSL2-Linux-Kernel.git cd WSL2-Linux-Kernel
- Checkout Corresponding Kernel Branch: Use the version number identified previously (e.g.,
linux-msft-wsl-5.15.153.1
if your running kernel was5.15.153.1-microsoft-standard-WSL2
):git checkout linux-msft-wsl-#your-kernel-version#
- Prepare Kernel Configuration: Copy your current WSL2 kernel configuration to use as a base:
cp /proc/config.gz config.gz gunzip config.gz mv config .config
- Customize Kernel Configuration: Launch the menu-based kernel configuration utility:
sudo make menuconfig
Navigate and enable the necessary options. Key settings often include:
- Device Drivers → USB support (
CONFIG_USB=y
)- USB announce new devices
- USB Modem (CDC ACM) support (
CONFIG_USB_ACM=m
or=y
) - USB/IP support (
CONFIG_USBIP_CORE=m
or=y
)- VHCI HCD (
CONFIG_USBIP_VHCI_HCD=m
or=y
)
- VHCI HCD (
- Device Drivers → USB Serial Converter support (
CONFIG_USB_SERIAL=m
or=y
)- Enable specific drivers for your adapter, e.g., USB FTDI Single Port Serial Driver (
CONFIG_USB_SERIAL_FTDI_SIO=m
or=y
), Prolific PL2303 (CONFIG_USB_SERIAL_PL2303=m
or=y
), CH341 (CONFIG_USB_SERIAL_CH341=m
or=y
), CP210x (CONFIG_USB_SERIAL_CP210X=m
or=y
).
- Enable specific drivers for your adapter, e.g., USB FTDI Single Port Serial Driver (
Set options as modules (
M
) where possible, or built-in (Y
). Save the configuration and exit. - Device Drivers → USB support (
- Compile the Kernel:
sudo make -j $(nproc)
- Copy the New Kernel Image: Copy the compiled kernel (
bzImage
) to a location accessible by Windows (e.g., your Windows user directory):cp arch/x86/boot/bzImage /mnt/c/Users/#your-windows-user#/usb-bzImage
Replace
#your-windows-user#
with your actual Windows username. - Configure WSL to Use the Custom Kernel: Create or edit the
.wslconfig
file in your Windows user profile directory (C:\Users\#your-windows-user#\.wslconfig
). Add the following content, ensuring paths use double backslashes:
Section Setting Value [wsl2]
kernel
C:\\Users\\#your-windows-user#\\usb-bzImage
Ensure there are no leading spaces on lines in this file.
- Restart WSL: From a Windows Command Prompt or PowerShell, shut down all WSL instances and then restart WSL:
wsl --shutdown wsl
After these steps, WSL2 will boot with your custom kernel. You can then proceed with attaching the USB serial device using usbipd-win
as described in Method 1. The custom kernel should now correctly recognize the device and create the appropriate /dev/ttyUSB#
entry.
Read: How to Fix SSH Error: ‘Bad owner or permissions on ~/.ssh/config’;
Method 3: Native Support in Newer WSL2 Kernels
It’s worth noting that Microsoft continues to enhance WSL2. Some newer WSL2 kernel versions (e.g., starting around v5.10.93.2) have incorporated built-in drivers for common USB serial interface chips like CH341 and CP210X. If you are using a recent version of WSL, your USB-to-serial adapter might work with usbipd-win
(Method 1) without requiring a custom kernel compilation.
Read: How to Fix Unmet Dependencies Errors on Ubuntu
Method 4: Direct Serial Port Access in WSL1
If you are operating under WSL1, or if reverting to WSL1 is an option for your use case, serial port access is generally more direct.
- Verify WSL Version: Check if your distribution is running under WSL1:
wsl -l -v
The output will list your distributions and their WSL versions.
- Accessing Serial Ports: In WSL1, Windows COM ports (e.g.,
COM14
) typically map directly to Linux TTY devices (e.g.,/dev/ttyS14
). The port number usually corresponds.
A Perl script example to read from COM14
(mapped to /dev/ttyS14
) in WSL1:
#!/usr/bin/perl -w
use strict;
$|=1; # autoflush
use Device::SerialPort;
# Corresponds to COM14 on Windows
my $port_path = "/dev/ttyS14";
my $port = Device::SerialPort->new($port_path);
# Configure port settings to match your device
$port->baudrate(115200);
$port->databits(8);
$port->parity("none");
$port->stopbits(0); # Typically 1 or 2, 0 might be specific/unusual
$port->debug(1); # Enable debugging messages
# Non-blocking read settings
$port->read_char_time(0); # Do not wait for each character
$port->read_const_time(1); # 1ms timeout for unfulfilled read
print "Attempting to read from $port_path...\n";
while (1) {
my ($count_in, $str_in) = $port->read(255); # Read up to 255 bytes
if ($count_in > 0) {
print "Received: $str_in";
}
# Add a small delay to prevent high CPU usage in a tight loop
# select(undef, undef, undef, 0.01); # 10ms delay
}
$port->close();
If your distribution is currently WSL2 and you prefer the WSL1 approach for serial access, you can back up your WSL2 distribution, import it as a new WSL1 instance, or convert an existing one using commands like wsl --export
, wsl --import
, and wsl --set-version
.
Alternative: TCP/IP Sockets for Inter-Process Communication
If the primary goal is inter-process communication (IPC) between a Windows application and a Linux application running in WSL, and serial port emulation is not a strict requirement, using TCP/IP sockets can be a simpler and more robust alternative. Both Windows and WSL environments have excellent support for network socket communication over localhost
.
Verification and Testing
Once a solution is implemented, verify its success:
- Device Listing (WSL2): Use
lsusb
to confirm the USB device is visible to WSL2. - Kernel Messages (WSL2): Check
dmesg
output for messages related to USB device attachment and TTY port creation (e.g., “ttyUSB0 now attached
“). - Port Communication: Utilize serial terminal applications like
minicom
orpicocom
on Linux, or PuTTY/Termite on Windows, to open the respective ports and attempt to send/receive data.
For instance, to connect to/dev/ttyUSB0
in WSL with minicom:sudo minicom -D /dev/ttyUSB0 -b 115200
- Loopback Test Setup: For a loopback test (e.g., Windows process to WSL process), connect two USB-to-serial adapters. Attach one to WSL2 using
usbipd-win
(it appears as, say,/dev/ttyUSB0
). The other remains on Windows (as, say,COM3
). Physically connect the TX pin of one adapter to the RX pin of the other, and vice-versa. Then, data sent from a Windows application toCOM3
should be receivable by a Linux application on/dev/ttyUSB0
, and vice-versa.
Potential Issues and Considerations
- Administrative Privileges: Commands like
usbipd bind
require administrator privileges in Windows. - WSL Restart: Changes to
.wslconfig
(like specifying a custom kernel) require a full WSL shutdown (wsl --shutdown
) and restart to take effect. - Path Formatting: When specifying paths in
.wslconfig
, Windows paths require double backslashes (e.g.,C:\\path\\to\\kernel
). - Kernel Recompilation Complexity: Recompiling the kernel is an advanced procedure. Ensure you select the correct kernel source version matching your running WSL2 kernel and enable all necessary modules. This process may need to be repeated if the base WSL2 kernel is updated by Microsoft.
- Device Persistence: The
usbipd attach
command may need to be re-run after a WSL restart or if the USB device is disconnected and reconnected, unless scripts are used to automate this. Theusbipd bind
command is generally persistent for the device on the Windows host.
Conclusion
Accessing serial ports from within Windows Subsystem for Linux, especially WSL2, is achievable through several methods. For USB-to-serial adapters in WSL2, usbipd-win
is the primary tool, potentially supplemented by custom kernel compilation for comprehensive driver support. WSL1 offers more direct, albeit architecturally different, access to serial ports.