How to Access Serial Ports (COM & ttyS) in Windows Subsystem for Linux (WSL1 & WSL2)

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.

WSL1 vs. WSL2: Serial Port Access Differences

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.

How to enable serial port access in WSL?

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.

  1. Download and Install `usbipd-win`: Obtain the latest release of usbipd-win from its official source and install it on your Windows system.
  2. List USB Devices: Open PowerShell with administrator privileges and list available USB devices connected to Windows:
    usbipd list
  3. 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.

  4. Attach the Device to WSL: Attach the shared USB device to your WSL2 distribution:
    usbipd attach --wsl --busid #BUSID#
  5. 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

Enabling USB Serial Adapters in WSL2 with usbipd-win

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:

Steps to Compile a Custom WSL2 Kernel for Serial Port Access

Read: How to Configure X11 Forwarding for WSL2

  1. 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
  2. Identify Current Kernel Version:
    uname -r

    Note this version (e.g., 5.15.153.1-microsoft-standard-WSL2). You’ll need the base version like 5.15.153.1.

  3. Clone WSL2 Kernel Source:
    git clone https://github.com/microsoft/WSL2-Linux-Kernel.git
    cd WSL2-Linux-Kernel
  4. Checkout Corresponding Kernel Branch: Use the version number identified previously (e.g., linux-msft-wsl-5.15.153.1 if your running kernel was 5.15.153.1-microsoft-standard-WSL2):
    git checkout linux-msft-wsl-#your-kernel-version#
  5. 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
  6. 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)
    • 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).

    Set options as modules (M) where possible, or built-in (Y). Save the configuration and exit.

  7. Compile the Kernel:
    sudo make -j $(nproc)
  8. 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.

  9. 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.

  10. 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.

  1. Verify WSL Version: Check if your distribution is running under WSL1:
    wsl -l -v

    The output will list your distributions and their WSL versions.

  2. 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 or picocom 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 to COM3 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. The usbipd 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. 

 

Alex Chen

Alex is a seasoned Linux Systems Administrator and DevOps Engineer with over 12 years of experience architecting and maintaining high-availability infrastructure. He has a deep expertise in kernel tuning, shell scripting, containerization (Docker, Kubernetes), and implementing robust CI/CD pipelines. Alex is passionate about open-source software and finding elegant, automated solutions to complex IT challenges across various distributions.