{"id":22807969,"url":"https://github.com/andyrids/void-wsl-dev","last_synced_at":"2025-03-30T20:52:34.417Z","repository":{"id":267706279,"uuid":"902049985","full_name":"andyrids/void-wsl-dev","owner":"andyrids","description":"Void Linux setup on Windows Subsystem for Linux (WSL2)","archived":false,"fork":false,"pushed_at":"2025-01-27T21:51:35.000Z","size":275,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-06T00:01:37.466Z","etag":null,"topics":["hotplug","powershell-script","raspberrypi","udev","udev-rules","usbip-win","voidlinux","wsl"],"latest_commit_sha":null,"homepage":"","language":"PowerShell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andyrids.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-11T20:07:54.000Z","updated_at":"2025-01-27T21:51:39.000Z","dependencies_parsed_at":"2024-12-12T00:23:38.527Z","dependency_job_id":"d5f1aec1-7ea4-4866-bd09-35fb83b7c102","html_url":"https://github.com/andyrids/void-wsl-dev","commit_stats":null,"previous_names":["andyrids/void-wsl-dev"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyrids%2Fvoid-wsl-dev","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyrids%2Fvoid-wsl-dev/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyrids%2Fvoid-wsl-dev/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyrids%2Fvoid-wsl-dev/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andyrids","download_url":"https://codeload.github.com/andyrids/void-wsl-dev/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246379380,"owners_count":20767696,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hotplug","powershell-script","raspberrypi","udev","udev-rules","usbip-win","voidlinux","wsl"],"created_at":"2024-12-12T11:07:24.055Z","updated_at":"2025-03-30T20:52:34.397Z","avatar_url":"https://github.com/andyrids.png","language":"PowerShell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Void WSL Dev\n\nThis repository contains instructions and scripts used to install and setup a Void Linux minimal root filesystem on WSL2.\n\n[Void Linux](https://voidlinux.org/) is a general purpose operating system, based on the monolithic Linux kernel. Its package system allows you to quickly install, update and remove software; software is provided in binary packages or can be built directly from sources with the help of the XBPS source packages collection.\n\n## Introduction\n\nThis setup is used mainly for Python development on WSL, but is also used for front-end projects and embedded projects using MicroPython and C on Raspberry Pi devices, which must be attached to WSL using [usbipd-win](https://github.com/dorssel/usbipd-win).\n\nVoid uses the `runit` supervision suite to run system services and daemons.\n\nI prefer to use [Windows Terminal](https://apps.microsoft.com/detail/9n0dx20hk701?hl=en-US\u0026gl=US) to manage my profiles for PowerShell and WSL distributions.\n\n\u003c!-- [!TIP] [!IMPORTANT] [!WARNING] [!CAUTION] --\u003e\n\n## 1. Download \u0026 install Void Linux\n\nThe minimal root filesystem can be downloaded from [Void Linux Downloads](https://voidlinux.org/download/). I create a WSL directory and a subdirectory for each distribution (e.g. `C:\\WSL\\Void`), where I keep my distribution images and where I have WSL create the vhdx image files. I download the [rootfs tarball](https://repo-default.voidlinux.org/live/current/void-x86_64-ROOTFS-20240314.tar.xz) from the Void downloads page.\n\nOn an administrator PowerShell, Void can be installed using the following commands:\n\n```ps1\nwsl --import Void C:\\WSL\\Void C:\\WSL\\Void\\void-x86_64-ROOTFS-20240314.tar.xz\n```\n\nCheck installation by listing distributions with verbose output:\n\n```ps1\nwsl -l -v\n```\n\n## 2. Setup Void Linux\n\nI use a profile within Windows Terminal (Microsoft Store), which is set to run Void with the following command `C:\\windows\\system32\\wsl.exe -d Void`.\n\n### New user setup\n\nNew user setup - replace `$USERNAME` with your username:\n\n```bash\nuseradd -m -G wheel -s /bin/bash $USERNAME\npasswd $USERNAME\n```\n\nMake `$USERNAME` default user:\n\n```sh\nprintf \"[user]\\ndefault=$USERNAME\" \u003e /etc/wsl.conf\n```\n\nGrant `sudo` to `$USERNAME`:\n\n```sh\nsed -i 's/# %wheel ALL=(ALL:ALL) ALL/%wheel ALL=(ALL:ALL) ALL/' /etc/sudoers\n```\n\nMicrocontrollers attached to WSL are often placed in the dialout group by udev rules (/dev/ttyACM[0-9]). We will also create a custom rule\nto enforce this in a later step. The command below adds the user to the 'dialout' group to facilitate connecting to these devices (if needed):\n\n```sh\nusermod -a -G dialout $USERNAME\n```\n\n### Update package manager \u0026 packages\n\nXBPS is the native system package manager, written from scratch with a 2-clause BSD license. XBPS allows you to quickly install/update/remove software in your system and features detection of incompatible shared libraries and dependencies while updating or removing packages (among others).\n\n```sh\nxbps-install -Su xbps\nxbps-install -Su\n```\n\n### Exit and restart void\n\n```sh\nexit\n```\n\nCheck Void status in Powershell terminal:\n\n```sh\nwsl --list --running\n```\n\nIt can take up to 7-8 seconds to terminate. If still active, run:\n\n\n```sh\nwsl --terminate Void\n```\n\n### WSL configuration\n\nI configure WSL with the following config file at `/etc/wsl.conf`. I set the default WSL username under the `[user]`\nsettings and facilitate runit starting with WSL, by using the command `command = \"/etc/runit/1 \u0026\u0026 /etc/runit/2 \u0026\u0026 /etc/runit/3\"`\nunder `[boot]`. The `appendWindowsPath = true` setting under `[interop]`, allows the addition of Windows tools to be added to the\nWSL distro `$PATH` automatically. The command `code .` would for example, be available in WSL and launch VSCode in the current working\nWSL directory.\n\n```sh\nvi /etc/wsl.conf\n```\n\nAdd the following to the config file:\n\n```sh\n# /etc/wsl.conf\n\n[automount]\nenabled = true\nmountFsTab = true\n\n[network]\ngenerateHosts = true\ngenerateResolvConf = true\n\n[interop]\nenabled = true\nappendWindowsPath = true\n\n[boot]\ncommand = \"/etc/runit/1 \u0026\u0026 /etc/runit/2 \u0026\u0026 /etc/runit/3\"\n```\n\nThe `[boot]` section will allow runit to start correctly.\n\n### Generate SSH key \u0026 add to SSH agent\n\nYou can generate a new SSH key on your local machine. After you generate the key, you can add the public key to your account on GitHub.com to enable authentication for Git operations over SSH\n\n```sh\nssh-keygen -t ed25519 -C \"your_email@example.com\"\n```\n\nWhen you're prompted to \"Enter a file in which to save the key\", you can press Enter to accept the default file location. At the prompt, type a secure passphrase.\n\nI use the following command line setting in Windows Terminal for the Void Linux profile - `C:\\WINDOWS\\system32\\wsl.exe -d Void -e /bin/bash -li` along with the following `.bash_profile` config:\n\n```bash\n# .bash_profile\n\n# Define cleanup function for SSH agent\n# stops WSL 2 from failing to terminate\n# due to SSH agent still running\nfunction cleanup_ssh_agent() {\n    # Check if ssh-agent is running before attempting to kill it\n    if [ -n \"$SSH_AGENT_PID\" ]; then\n        ssh-agent -k \u003e /dev/null 2\u003e\u00261\n        unset SSH_AGENT_PID\n        unset SSH_AUTH_SOCK\n    fi\n}\n\n# Register the cleanup function to execute when the shell exits\ntrap cleanup_ssh_agent EXIT\n\n# Your existing SSH agent initialization code\n# check if an SSH agent is running\nssh-add -l \u0026\u003e/dev/null\n# exit code 2 means no agent is running at all\nif [ \"$?\" == 2 ]; then\n    # Could not open a connection to your authentication agent.\n\n    # Load stored agent connection info.\n    test -r ~/.ssh-agent \u0026\u0026 \\\n        eval \"$(\u003c~/.ssh-agent)\" \u003e/dev/null\n\n    ssh-add -l \u0026\u003e/dev/null\n    if [ \"$?\" == 2 ]; then\n        # Start agent and store agent connection info.\n        (umask 066; ssh-agent \u003e ~/.ssh-agent)\n        eval \"$(\u003c~/.ssh-agent)\" \u003e/dev/null\n    fi\nfi\n\n# Load identities\nssh-add -l \u0026\u003e/dev/null\nif [ \"$?\" == 1 ]; then\n    # The agent has no identities.\n    # Time to add one.\n    ssh-add -t 4h\nfi\n\n# Get the aliases and functions\n[ -f $HOME/.bashrc ] \u0026\u0026 . $HOME/.bashrc\n\n. \"$HOME/.local/bin/env\"\n```\n\nThis command and profile combination starts the SSH agent if not already and loads the SSH keys from `~/.ssh`, asking for the password you set. The SSH key is added to the agent with a 4 hour time window (`ssh-add -t 4h`) and the `cleanup_ssh_agent` function kills the agent if\nyou type exit or close the Void Linux terminal window. This stops a running agent from preventing WSL2 automatic shutdown.\n\n## 3. Setup USB Device Sharing to WSL2 (Microcontrollers)\n\nI use usbipd-win to share locally connected USB devices to other machines, including Hyper-V guests and WSL 2.\nBy default devices are not shared with USBIP clients. To lookup and share devices, run the following commands with\nadministrator privileges:\n\nInstall usbipd-win with winget:\n\n```ps1\nwinget install usbipd\n```\n\nList devices with busid \u0026 state information using `usbipd list` command:\n\n```ps1\nusbipd list\n```\n\nThe below image shows a connected Raspberry Pi Pico W device (highlighted in blue), with MicroPython installed. Raspberry Pi Vendor ID (VID) is '0x2e8a' and the\nProduct ID (PID) for MicroPython is '0x0005'. The device has been 'Shared' using the command `usbipd bind --busid 1-7`. All devices must be shared before they can be attached.\n\n![usbipd list example](./images/usbipd-list.png)\n\nTo share your device run the following command with the relevant busid value seen in the `usbipd list` output:\n\n```ps1\nusbipd bind --busid 1-7\n```\n\n![usbipd list example](images/usbipd-bind.png)\n\nAs long as WSL is running, you can then attach the device to WSL using the following command:\n\n```ps1\nusbipd attach --wsl --busid 1-7\n```\n\n![usbipd list example](images/usbipd-attach.png)\n\n### Udev setup\n\nUdev is a Linux subsystem for managing device events, which is used by Void Linux. By default, the rules for udev will be in `/usr/lib/udev/rules.d/`. A useful guide for writing udev rules is at [opensource.com](https://opensource.com/article/18/11/udev). If you list the directory contents, you will see the following rules:\n\n```sh\n$ ls -a /usr/lib/udev/rules.d/\n/usr/lib/udev/rules.d/50-udev-default.rules\n/usr/lib/udev/rules.d/60-autosuspend.rules\n/usr/lib/udev/rules.d/60-block.rules\n/usr/lib/udev/rules.d/60-cdrom_id.rules\n/usr/lib/udev/rules.d/60-drm.rules\n/usr/lib/udev/rules.d/60-evdev.rules\n/usr/lib/udev/rules.d/60-fido-id.rules\n/usr/lib/udev/rules.d/60-input-id.rules\n/usr/lib/udev/rules.d/60-persistent-alsa.rules\n/usr/lib/udev/rules.d/60-persistent-input.rules\n/usr/lib/udev/rules.d/60-persistent-storage-tape.rules\n/usr/lib/udev/rules.d/60-persistent-storage.rules\n/usr/lib/udev/rules.d/60-persistent-v4l.rules\n/usr/lib/udev/rules.d/60-serial.rules\n/usr/lib/udev/rules.d/64-btrfs-dm.rules\n/usr/lib/udev/rules.d/64-btrfs-zoned.rules\n/usr/lib/udev/rules.d/64-btrfs.rules\n/usr/lib/udev/rules.d/66-kvm.rules\n/usr/lib/udev/rules.d/70-camera.rules\n/usr/lib/udev/rules.d/70-joystick.rules\n/usr/lib/udev/rules.d/70-memory.rules\n/usr/lib/udev/rules.d/70-mouse.rules\n/usr/lib/udev/rules.d/70-touchpad.rules\n/usr/lib/udev/rules.d/75-net-description.rules\n/usr/lib/udev/rules.d/75-probe_mtd.rules\n/usr/lib/udev/rules.d/78-sound-card.rules\n/usr/lib/udev/rules.d/80-drivers.rules\n/usr/lib/udev/rules.d/80-net-name-slot.rules\n/usr/lib/udev/rules.d/81-net-dhcp.rules\n/usr/lib/udev/rules.d/90-usbmon.rules\n```\n\nWe are going to create a new folder at `/etc/udev/rules.d/` and create a `60-micropython-rpi.rules` in order to demonstrate udev rule creation for Raspberry Pi devices running MicroPython. This should keep our custom rules separate from the default system rules. Hopefully they should take\nprecedence over system-provided rules and persist across system upgrades.\n\n```sh\nsudo mkdir /etc/udev/rules.d/\nsudo vi /etc/udev/rules.d/60-micropython-rpi.rules\n```\n\nInside the `60-micropython-rpi.rules` file, write the following:\n\n```sh\n# VID: 0x2E8A\n# PID: 0x0005\n\nSUBSYSTEM==\"tty\", \\\nATTRS{idVendor}==\"2e8a\", \\\nATTRS{idProduct}==\"0005\", \\\nMODE=\"0666\", \\\nGROUP=\"dialout\", \\\nSYMLINK+=\"/dev/ttyACM%n\"\n\n# Alternative rule\nSUBSYSTEM==\"tty\", \\\nATTRS{idVendor}==\"2e8a\", \\\nATTRS{product}==\"MicroPython*\", \\\nMODE=\"0666\", \\\nGROUP=\"dialout\", \\\nSYMLINK+=\"/dev/ttyACM%n\"\n```\n\n\u003e [!TIP]\n\u003e Note the `0666` permissions. These give read \u0026 write permissions for all users. If you\n\u003e want to limit read \u0026 write for members of the 'dialout' group, then set to `0660`.\n\nReload udev rules and trigger a udev scan:\n\n```sh\nsudo udevadm control --reload-rules\nsudo udevadm trigger\n```\n\nIn Void Linux you can run the following command to see if the device has been detected and you should hopefully\nsee something like the following:\n\n```sh\n$ ls -la /dev/ttyACM*\ncrw-rw-rw- 1 root dialout 166, 0 Dec 11 21:20 /dev/ttyACM0\n```\n\nWe will now test our new udev rules using the device path indicated by the previous command:\n\n```sh\n$ sudo udevadm test $(udevadm info -q path -n /dev/ttyACM0)\n=== trie on-disk ===\ntool version:          3\nfile size:         9732328 bytes\nheader size             80 bytes\nstrings            2424072 bytes\nnodes              7308176 bytes\nLoad module index\ntimestamp of '/etc/udev/rules.d' changed\nReading rules file: /usr/lib/udev/rules.d/50-udev-default.rules\nReading rules file: /usr/lib/udev/rules.d/60-autosuspend.rules\nReading rules file: /usr/lib/udev/rules.d/60-block.rules\nReading rules file: /usr/lib/udev/rules.d/60-cdrom_id.rules\nReading rules file: /usr/lib/udev/rules.d/60-drm.rules\nReading rules file: /usr/lib/udev/rules.d/60-evdev.rules\nReading rules file: /usr/lib/udev/rules.d/60-fido-id.rules\nReading rules file: /usr/lib/udev/rules.d/60-input-id.rules\nReading rules file: /etc/udev/rules.d/60-micropython-rpi.rules\nReading rules file: /usr/lib/udev/rules.d/60-persistent-alsa.rules\nReading rules file: /usr/lib/udev/rules.d/60-persistent-input.rules\nReading rules file: /usr/lib/udev/rules.d/60-persistent-storage-tape.rules\nReading rules file: /usr/lib/udev/rules.d/60-persistent-storage.rules\nReading rules file: /usr/lib/udev/rules.d/60-persistent-v4l.rules\nReading rules file: /usr/lib/udev/rules.d/60-sensor.rules\nReading rules file: /usr/lib/udev/rules.d/60-serial.rules\nReading rules file: /usr/lib/udev/rules.d/64-btrfs-dm.rules\nReading rules file: /usr/lib/udev/rules.d/64-btrfs-zoned.rules\nReading rules file: /usr/lib/udev/rules.d/64-btrfs.rules\nReading rules file: /usr/lib/udev/rules.d/66-kvm.rules\nReading rules file: /usr/lib/udev/rules.d/70-camera.rules\nReading rules file: /usr/lib/udev/rules.d/70-joystick.rules\nReading rules file: /usr/lib/udev/rules.d/70-memory.rules\nReading rules file: /usr/lib/udev/rules.d/70-mouse.rules\nReading rules file: /usr/lib/udev/rules.d/70-touchpad.rules\nReading rules file: /usr/lib/udev/rules.d/75-net-description.rules\nReading rules file: /usr/lib/udev/rules.d/75-probe_mtd.rules\nReading rules file: /usr/lib/udev/rules.d/78-sound-card.rules\nReading rules file: /usr/lib/udev/rules.d/80-drivers.rules\nReading rules file: /usr/lib/udev/rules.d/80-net-name-slot.rules\nReading rules file: /usr/lib/udev/rules.d/81-net-dhcp.rules\nReading rules file: /usr/lib/udev/rules.d/90-usbmon.rules\nrules contain 24576 bytes tokens (2048 * 12 bytes), 10496 bytes strings\n1342 strings (17908 bytes), 843 de-duplicated (7912 bytes), 500 trie nodes used\nGROUP 11 /usr/lib/udev/rules.d/50-udev-default.rules:25\nGROUP 11 /etc/udev/rules.d/60-micropython-rpi.rules:9\nMODE 0666 /etc/udev/rules.d/60-micropython-rpi.rules:9\nLINK '/dev/ttyACM0' /etc/udev/rules.d/60-micropython-rpi.rules:9\nIMPORT builtin 'usb_id' /usr/lib/udev/rules.d/60-serial.rules:8\n/sys/devices/platform/vhci_hcd.0/usb1/1-1/1-1:1.0: if_class 2 protocol 0\nIMPORT builtin 'hwdb' /usr/lib/udev/rules.d/60-serial.rules:8\nIMPORT builtin 'path_id' /usr/lib/udev/rules.d/60-serial.rules:15\nLINK 'serial/by-path/platform-vhci_hcd.0-usb-0:1:1.0' /usr/lib/udev/rules.d/60-serial.rules:16\nIMPORT builtin skip 'usb_id' /usr/lib/udev/rules.d/60-serial.rules:19\nLINK 'serial/by-id/usb-MicroPython_Board_in_FS_mode_e66164084373532b-if00' /usr/lib/udev/rules.d/60-serial.rules:23\nhandling device node '/dev/ttyACM0', devnum=c166:0, mode=0666, uid=0, gid=11\npreserve permissions /dev/ttyACM0, 020666, uid=0, gid=11\npreserve already existing symlink '/dev/char/166:0' to '../ttyACM0'\nfound 'c166:0' claiming '/run/udev/links/\\x2f\\x2fdev\\x2fttyACM0'\ncreating link '/dev//dev/ttyACM0' to '/dev/ttyACM0'\npreserve already existing symlink '/dev//dev/ttyACM0' to '../../ttyACM0'\nfound 'c166:0' claiming '/run/udev/links/\\x2fserial\\x2fby-id\\x2fusb-MicroPython_Board_in_FS_mode_e66164084373532b-if00'\ncreating link '/dev/serial/by-id/usb-MicroPython_Board_in_FS_mode_e66164084373532b-if00' to '/dev/ttyACM0'\npreserve already existing symlink '/dev/serial/by-id/usb-MicroPython_Board_in_FS_mode_e66164084373532b-if00' to '../../ttyACM0'\nfound 'c166:0' claiming '/run/udev/links/\\x2fserial\\x2fby-path\\x2fplatform-vhci_hcd.0-usb-0:1:1.0'\ncreating link '/dev/serial/by-path/platform-vhci_hcd.0-usb-0:1:1.0' to '/dev/ttyACM0'\npreserve already existing symlink '/dev/serial/by-path/platform-vhci_hcd.0-usb-0:1:1.0' to '../../ttyACM0'\ncreated db file '/run/udev/data/c166:0' for '/devices/platform/vhci_hcd.0/usb1/1-1/1-1:1.0/tty/ttyACM0'\nACTION=add\nDEVLINKS=/dev//dev/ttyACM0 /dev/serial/by-id/usb-MicroPython_Board_in_FS_mode_e66164084373532b-if00 /dev/serial/by-path/platform-vhci_hcd.0-usb-0:1:1.0\nDEVNAME=/dev/ttyACM0\nDEVPATH=/devices/platform/vhci_hcd.0/usb1/1-1/1-1:1.0/tty/ttyACM0\nID_BUS=usb\nID_MODEL=Board_in_FS_mode\nID_MODEL_ENC=Board\\x20in\\x20FS\\x20mode\nID_MODEL_ID=0005\nID_PATH=platform-vhci_hcd.0-usb-0:1:1.0\nID_PATH_TAG=platform-vhci_hcd_0-usb-0_1_1_0\nID_REVISION=0100\nID_SERIAL=MicroPython_Board_in_FS_mode_e66164084373532b\nID_SERIAL_SHORT=e66164084373532b\nID_TYPE=generic\nID_USB_CLASS_FROM_DATABASE=Miscellaneous Device\nID_USB_DRIVER=cdc_acm\nID_USB_INTERFACES=:020200:0a0000:\nID_USB_INTERFACE_NUM=00\nID_USB_PROTOCOL_FROM_DATABASE=Interface Association\nID_VENDOR=MicroPython\nID_VENDOR_ENC=MicroPython\nID_VENDOR_ID=2e8a\nMAJOR=166\nMINOR=0\nSUBSYSTEM=tty\nUSEC_INITIALIZED=7267427405\nUnload module index\n```\n\nThe custom rule at `/etc/udev/rules.d/60-micropython-rpi.rules` was successfully read. We can see the device details at the bottom of the output and the\nfollowing output indicates the rules were process correctly:\n\n```sh\nGROUP 11 /etc/udev/rules.d/60-micropython-rpi.rules:9\nMODE 0666 /etc/udev/rules.d/60-micropython-rpi.rules:9\nLINK '/dev/ttyACM0' /etc/udev/rules.d/60-micropython-rpi.rules:9\n```\n\n\u003e [!Tip]\n\u003e Group 11 is the `dialout` group.\n\n## 4. Powershell script to automate device sharing to WSL2\n\nThe `Watch-PicoConnect.ps1` script can be used to automate Pico attachment to WSL on connection. I run this script in an Administrator PowerShell instance using the following command:\n\n```ps1\n. C:\\WSL\\1_Scripts\\Watch-PicoConnect.ps1\n```\n\n![usbipd list example](images/pico-connect-script.png)\n\n\u003e [!WARNING]\n\u003e I had to modify my script recently, to monitor Event ID 6416, which is related to Plug\n\u003e and Play (PnP) device connection. By default this Event is not typically logged.\n\nCheck that this Event 6416 is currently logged:\n\n```ps1\n$ auditpol /get /subcategory:\"Plug and Play Events\"\nSystem audit policy\nCategory/Subcategory                      Setting\nDetailed Tracking\n  Plug and Play Events                    Success and Failure\n```\n\nYou are looking for 'Success and Failure' under the 'Setting' column. If not seen, enable with the following command:\n\n```ps1\n$ auditpol /set /subcategory:\"Plug and Play Events\" /success:enable /failure:enable\nThe command was successfully executed.\n```\n\nYou can also check for recent Event logs with the following command:\n\n```ps1\n$ Get-WinEvent -FilterHashtable @{LogName = 'Security'; ID = 6416 } -MaxEvents 5\nTimeCreated                     Id LevelDisplayName Message\n-----------                     -- ---------------- -------\n11/12/2024 21:17:19           6416 Information      A new external device was recognized by the system.…\n11/12/2024 21:17:15           6416 Information      A new external device was recognized by the system.…\n11/12/2024 21:17:15           6416 Information      A new external device was recognized by the system.…\n11/12/2024 19:15:08           6416 Information      A new external device was recognized by the system.…\n11/12/2024 19:15:08           6416 Information      A new external device was recognized by the system.…\n```\n\nBelow is the Event log in Windows Event Viewer:\n\n![Event Viewer](images/event-viewer-6416.png)\n\n\u003e [!Tip]\n\u003e Note the MicroPython PID 0x0005 and Raspberry Pi VID 0x2E8A. The value after the PID is the device serial number.\n\nBelow is the current Powershell script to monitor for Raspberry Pi devices with MicroPython installed being connected. The script automatically shares  and attaches them to a running WSL Linux distribution.\n\n```ps1\n\u003c#\nThis script is used to watch the 'Microsoft-Windows-DeviceSetupManager/Admin' Windows Event log for\nRaspberry Pi Pico device attachment events (ID 6416) and attach them to any running WSL distributions,\nusing usbipd-win. WSL must be running for device attachment to take place and so, the script watches\nfor WSL startup events (System/Hyper-V-VmSwitch), in case a device is attached before starting WSL, \\\nprompting a follow-up attachment.\n\nREQUIREMENTS: usbipd-win (https://github.com/dorssel/usbipd-win) \u0026 WSL installed\n\nThe auditing of device connect and disconnect is disabled by default:\n1. Run gpedit.msc\n2. Computer Configuration \u003e Windows Settings \u003e Security Settings \u003e Advanced Audit Policy Configuration \u003e System Audit Policies \u003e Detailed Tracking\n3. Double-click \"Audit PNP Activity\"\n4. Check both \"Success\" and \"Failure\"\n5. Click OK.\n\n\nNOTE: For these events, we are looking for EventData DeviceName property containing values such as;\n'Board in FS mode' (MicroPython), 'Pico' (Pico SDK) and 'RP2 Boot' (RP2040 Boot). An Administrator\nPowerShell instance is needed to bind a device for the first time, to enable its attachment to WSL.\n\nI usually run this script when I open Windows Terminal, inside an Administrator PowerShell instance,\nbefore opening my Alpine Linux WSL instance - for MicroPython development on the RPi Pico and Pico W.\nI use the Python library rshell to interface with my devices in MicroPython.\n\nWe can filter connected COM devices by Vendor ID and Product ID values, which are detailed below:\n\nFor a full list of Raspberry Pi vendor ID (VID) \u0026 product ID (PID) values,\nsee https://github.com/raspberrypi/usb-pid.\n\nRaspberry Pi VID: 0x2E8A\n\nRaspberry Pi PID:\n\n┌────────┬──────────────────────────────────────────────┐\n│   PID  │                    Product                   │\n├────────┼──────────────────────────────────────────────┤\n│ 0x0003 │ Raspberry Pi Pico W                          │\n├────────┼──────────────────────────────────────────────┤\n│ 0x0004 │ Raspberry Pi PicoProbe                       │\n├────────┼──────────────────────────────────────────────┤\n│ 0x0005 │ Raspberry Pi Pico MicroPython firmware (CDC) │\n├────────┼──────────────────────────────────────────────┤\n│ 0x000A │ Raspberry Pi Pico SDK CDC UART               │\n├────────┼──────────────────────────────────────────────┤\n│ 0x000B │ Raspberry Pi Pico CircuitPython firmware     │\n├────────┼──────────────────────────────────────────────┤\n│ 0x1000 │ Cytron Maker Pi RP2040                       │\n└────────┴──────────────────────────────────────────────┘\n#\u003e\n\n#requires -RunAsAdministrator\n#requires -version 5.1\n\nusing namespace System.Diagnostics.Eventing\nusing namespace System.Management.Automation\n\n# import usbipd-win PowerShell module\nImport-Module $env:ProgramW6432'\\usbipd-win\\PowerShell\\Usbipd.Powershell.dll'\n\n# Used to represent all WSL distribution status values\nenum WSLStatus {\n    Stopped\n    Running\n    Installing\n    Uninstalling\n    Converting\n}\n\n\u003c#\nUsed as a switch value relating to actions for binding and attaching COM\ndevices' data bus to active WSL distributions. Enum values are based on\nthe addition of IsBound \u0026 IsAttached Boolean properties of the objects\nreturned by Get-UsbipdDevice (usbipd-win).\n\nA device can be:\n\n1. Not shared or attached\n2. Shared \u0026 not attached\n3. Shared \u0026 attached\n#\u003e\nenum COMStatus {\n    NotShared\n    Shared\n    Attached\n}\n\n\n# Pico PID enumerations\nenum PicoPID {\n    RP2040Boot = 0x0003\n    MicroPython = 0x0005\n    PicoSDK = 0x000A\n    CircuitPython = 0x000B\n}\n\n\nfunction global:Get-WSLDistributionInformation {\n    \u003c#\n    .SYNOPSIS\n        Get all WSL distribution details.\n\n    .DESCRIPTION\n        Parses the string output from wsl --list --verbose command\n        into a hashtable with the following structure:\n\n        [string]Name: distribution name\n        [bool]Default: default distribution flag\n        [string]Status: distribution status\n        [int]Version: WSL version\n\n    .INPUTS\n        None\n\n    .OUTPUTS\n        System.hashtable\n    #\u003e\n    [CmdletBinding()]\n    [OutputType([hashtable])]\n    param (\n        # [ValidateSetAttribute(\"Stopped\", \"Running\", \"Installing\", \"Uninstalling\", \"Converting\")]\n        [string[]]$Filter = [WSLStatus].GetEnumNames()\n    )\n\n    process {\n        # Available WSL distributions \u0026 status\n        $WSLOutput = wsl --list --verbose |\n            Select-Object -Skip 1 -Unique |\n            Where-Object -Property Length -gt 1\n\n        $WSLOutput | ForEach-Object {\n            \u003c#\n            An '*' before a distribution name would denote that\n            particular WSL distribution as the WSL default.\n\n            $WSLOutput:\n                * Alpine    Stopped      2\n\n            [Regex]::new(\"\\b[^\\w]{2,}\").Replace($WSLOutput, \"-\"):\n                * Alpine-Stopped-2\n\n            ConvertFrom-String -Delimiter \"-\":\n                P1: \"* Alpine\", P2: \"Stopped\", P3: \"2\"\n\n            Select-Object:\n                Name: Alpine, Default: True, Status: Stopped, Version: 2\n            #\u003e\n\n            # Calculated properties for each WSL distribution object\n            $Name = @{label=\"Name\"; expression={$_.P1 -replace \"[\\*\\s]\", \"\"}}\n            $Default = @{label=\"Default\"; expression={$_.P1.StartsWith(\"*\")}}\n            $Status = @{label=\"Status\"; expression={$_.P2}}\n            $Version = @{label=\"Version\"; expression={$_.P3}}\n\n            # WSL distribution objects converted from distribution details string\n            [Regex]::new(\"\\b[^\\w]{2,}\").Replace($_.Trim(), \"-\") |\n                ConvertFrom-String -Delimiter \"-\" |\n                Select-Object -Property $Name, $Default, $Status, $Version |\n                Where-Object Status -In $Filter\n        }\n    }\n}\n\n\nfunction global:Get-ConnectedDevice {\n    \u003c#\n    .SYNOPSIS\n        Get details for connected Raspberry Pi devices.\n\n    .DESCRIPTION\n        Invoke the usbipd-win PowerShell function Get-UsbipdDevice,\n        filtering on Raspberry Pi Vendor ID (2E8A) and the passed\n        Pico Product ID values (PicoPID enum). 'VerboseDescription'\n        \u0026 'Status' properties are added to each output object:\n\n        [string]Status - NotShared | Shared | Attached\n        [string]VerboseDescription - Description, PID name \u0026 bus\n\n    .INPUTS\n        None\n\n    .OUTPUTS\n        Usbipd.Automation.Device\n\n    .NOTES\n    #\u003e\n    [CmdletBinding()]\n    param (\n        [Parameter(Mandatory)]\n        [PicoPID[]]$PIDValues\n    )\n\n    process {\n        $PIDRegex = ($PIDValues | ForEach-Object { \"{0:x4}\" -f[int]$_ }) -Join \"|\"\n        Get-UsbipdDevice |\n            Where-Object { $_.IsConnected -and $_.HardwareId -match \"2e8a:($PIDRegex)\" } |\n            ForEach-Object {\n                # Create calculated properties for future use\n                $PIDName = [PicoPID][int]$_.HardwareId.Pid\n                $verboseText = \"$($_.Description) [$PIDName PID] on bus $($_.BusId)\"\n                $VerboseDescription = @{ l=\"VerboseDescription\"; e={ $verboseText } }\n                $Status = @{ l=\"Status\"; e={ [COMStatus][int]$_.IsBound + $_.IsAttached } }\n                # Add new calculated properties - VerboseDescription \u0026 Status\n                $_ | Select-Object -Property *, $VerboseDescription, $Status\n            }\n    }\n}\n\n\nfunction global:Approve-COMDeviceMountToWSL {\n    \u003c#\n    .SYNOPSIS\n        Share connected device to allow WSL attachment.\n\n    .DESCRIPTION\n        Invoke the command usbipd bind --busid $BusId to share\n        a connected device, allowing it to be attached to WSL.\n\n    .INPUTS\n        None\n\n    .OUTPUTS\n        System.Void\n\n    .NOTES\n    #\u003e\n    [CmdletBinding()]\n    [OutputType([Void])]\n    param (\n        [Parameter(Mandatory)]\n        [ValidatePattern(\"^[1-9]-([1-9]$|1[0-5])\")]\n        [string]$BusId\n    )\n\n    process {\n        usbipd bind --busid $BusId\n    }\n}\n\n\nfunction global:Mount-COMDeviceToWSL {\n    \u003c#\n    .SYNOPSIS\n        Attach connected device to WSL distributions if active.\n\n    .DESCRIPTION\n        Invoke the command usbipd --wsl --busid $busId to attach a\n        connected device (if shared) to WSL distributions (if active).\n\n    .INPUTS\n        None\n\n    .OUTPUTS\n        System.Void\n\n    .NOTES\n        A connected device must be shared before it can be attached\n        to WSL. The Approve-COMDeviceMountToWSL function can be used\n        to share the device if administrator rights are active.\n\n        Use the common parameter -InformationAction Continue to display\n        verbose information regarding device attachment to WSL.\n    #\u003e\n    [CmdletBinding()]\n    [OutputType([Void])]\n    param (\n        [Parameter(Mandatory)]\n        [ValidatePattern(\"^[1-9]-([1-9]$|1[0-5])\")]\n        [string]$BusId\n    )\n\n    process {\n        $device = Get-ConnectedDevice -PIDValues $([PicoPID].getEnumNames()) |\n            Where-Object { $_.BusId -eq $BusId -and $_.IsBound }\n\n        if (-not $device) {\n            Write-Information -MessageData \"[Not shared] cannot attach device on bus $BusId\"\n            return $null\n        }\n\n        $deviceDescription = $device.VerboseDescription\n\n        $isActiveWSL = (Get-WSLDistributionInformation -Filter Running).Count -gt 0\n\n        if ($isActiveWSL) {\n            Write-Information -MessageData \"[Attach to WSL] WSL active - attaching $deviceDescription\"\n            usbipd attach --wsl --busid $busId | Write-Information\n        } else {\n            Write-Information -MessageData \"[Attach to WSL] WSL inactive - not attaching $deviceDescription\"\n        }\n        Out-Null\n    }\n}\n\n\nfunction global:Connect-COMDeviceToWSL {\n    \u003c#\n    .SYNOPSIS\n        Facilitate device connection to active WSL distributions.\n\n    .DESCRIPTION\n        Invokes usbipd-win commands for binding and attaching a\n        COM device's data bus to active WSL distributions based\n        on the connected device status:\n\n        1. Not shared (or attached)\n        2. Shared (but not attached)\n        3. Shared \u0026 attached\n\n        NOTE: If there are active WSL distributions, then\n        a device will be shared \u0026 attached.\n\n    .INPUTS\n        None.\n\n    .OUTPUTS\n        hashtable.\n    #\u003e\n    [CmdletBinding()]\n    [OutputType([System.Void])]\n    param (\n        [Parameter(Mandatory = $false)]\n        [PicoPID[]]$PIDValues = @(\"RP2040Boot\", \"MicroPython\", \"PicoSDK\", \"CircuitPython\")\n    )\n\n    process {\n        $connectedDevices = Get-ConnectedDevice -PIDValues $PIDValues\n\n        switch ($connectedDevices) {\n            ({ $_.Status -eq \"NotShared\" }) {\n                Approve-COMDeviceMountToWSL -BusId $_.BusId\n                Mount-COMDeviceToWSL -BusId $_.BusId\n                continue\n            }\n            ({ $_.Status -eq \"Shared\" }) {\n                Mount-COMDeviceToWSL -BusId $_.BusId\n                continue\n            }\n            ({ $_.Status -eq \"Attached\" }) {\n                Write-Information \"[WSL active] - $($_.VerboseDescription) already attached\"\n                continue\n            }\n            Default { Write-Information \"[Not found] No devices with matching PID; $PIDValues\"}\n        }\n    }\n}\n\n\u003c#\nStart a WSL distribution\n-----------------------------------\nLog Name: System\nSource: Hyper-V-VmSwitch\nEvent ID: 232\n-----------------------------------\nNIC B944A522-3D6A-4950-A844-16DC623D891A--6B4A85CC-55A1-4C9F-8C85-44C2A33AAE4A (Friendly Name: )\nsuccessfully connected to port 6B4A85CC-55A1-4C9F-8C85-44C2A33AAE4A (Friendly Name: 6B4A85CC-55A1-4C9F-8C85-44C2A33AAE4A)\non switch 790E58B4-7939-4434-9358-89AE7DDBE87E(Friendly Name: WSL (Hyper-V firewall)).\n-----------------------------------\nEventData\n  NicNameLen 74\n  NicName B944A522-3D6A-4950-A844-16DC623D891A--6B4A85CC-55A1-4C9F-8C85-44C2A33AAE4A\n  NicFNameLen 0\n  NicFName\n  PortNameLen 36\n  PortName 6B4A85CC-55A1-4C9F-8C85-44C2A33AAE4A\n  PortFNameLen 36\n  PortFName 6B4A85CC-55A1-4C9F-8C85-44C2A33AAE4A\n  SwitchNameLen 36\n  SwitchName 790E58B4-7939-4434-9358-89AE7DDBE87E\n  SwitchFNameLen 22\n  SwitchFName WSL (Hyper-V firewall)\n#\u003e\n\n# Query options \u0026 filter\n$SystemLog = \"System\"\n$HyperVFilter = \"*[System[(EventID=232)] and EventData[(Data='WSL (Hyper-V firewall)')]]\"\n$HyperVQuery = [Reader.EventLogQuery]::new($SystemLog, [Reader.PathType]::LogName, $HyperVFilter)\n\n# Overload used: EventLogWatcher(EventLogQuery, EventBookmark, Boolean)\n# Boolean determines inclusion of pre-existing events that match the EventLogQuery\n$HyperVWatcher = [Reader.EventLogWatcher]::new($HyperVQuery, $null, $false)\n$HyperVWatcher.Enabled = $true\n\n\u003c#\nAttach Raspberry Pi Pico\n-----------------------------------\nLog Name: Security\nSource: Microsoft Windows Security\nEvent ID: 6416\nTask Category: Plug and Play Events\nLevel: Information\n-----------------------------------\nA new external device was recognized by the system.\n-----------------------------------\nEventData\n  DeviceId USB\\VID_2E8A\u0026PID_0005\\e66164084373532b\n  DeviceDescription USB Composite Device\n  VendorIds USB\\VID_2E8A\u0026PID_0005\u0026REV_0100 USB\\VID_2E8A\u0026PID_0005 \n  ...\n#\u003e\n\n# Query options \u0026 filter\n# $DeviceSetupManagerLog = \"Microsoft-Windows-DeviceSetupManager/Admin\"\n$DeviceSetupManagerLog = \"Security\"\n# Event ID 112 would no longer work and so I switched to 6416\n$FSModeFilter = \"*[System[(EventID=6416)]]\"\n$DeviceQuery = [Reader.EventLogQuery]::new($DeviceSetupManagerLog, [Reader.PathType]::LogName, $FSModeFilter)\n\n# Overload used: EventLogWatcher(EventLogQuery, EventBookmark, Boolean)\n# Boolean determines inclusion of pre-existing events that match the EventLogQuery\n$DeviceWatcher = [Reader.EventLogWatcher]::new($DeviceQuery, $null, $false)\n$DeviceWatcher.Enabled = $true\n\n\n$Action = {\n    \u003c#\n    NOTE: The commands in the Action run when an event is raised, instead of sending the\n    event to the event queue, making the 'Wait-Event' (Ln 285) act as an indefinite wait.\n\n    This script has access to automatic variables;\n\n    $Event, $EventSubscriber, $Sender, $EventArgs \u0026 $Args.\n    #\u003e\n\n    # $EventArgs.EventRecord.Properties | Get-Member -MemberType Property | Out-Host\n    # $EventArgs.EventRecord.Properties | Select-Object | Out-Host\n\n    $PIDValues = [PicoPID].GetEnumNames()\n    $availableDevices = Get-ConnectedDevice -PIDValues $PIDValues | Where-Object Status -NE \"Attached\"\n\n    \u003c#\n    When usbipd-win attaches a device, it creates a another event, which matches the 112 ID events\n    we are watching for, in the 'Microsoft-Windows-DeviceSetupManager/Admin' events log.\n\n    TODO: Implement logic to ignore these duplicate events, possibly by temporarily watching the Application log\n    for Event ID 1 from 'usbipd-win' source detailing an event with a 'Usbipd.ConnectedClient' event category and\n    switching of the event watcher until this 'usbipd-win' event has passed.\n\n    As a temporary solution, I check for any devices, which are not already attached and are therefore either not shared \n    or shared and therefore available for attachment to WSL (if running). If this was a duplicate event, then the device will\n    already be attached.\n\n    NOTE: Technically, Connect-COMDeviceToWSL handles devices that are already attached and does not attempt to attach them.\n    #\u003e\n\n    if ($availableDevices) {\n        $recordProperties = $EventArgs.EventRecord.Properties\n        $identifierMap = @{\n            DeviceConnection = $recordProperties | Select-Object -Skip 5 | Select-Object -First 1 -Property Value -ExpandProperty Value\n            HyperVConnection = $recordProperties | Select-Object -Last 1 -Property Value -ExpandProperty Value\n        }\n\n        $sourceIdentifier = $Event.SourceIdentifier\n        # Get-WinEvent -MaxEvents 1 -FilterHashtable @{LogName=\"Security\"; ID=6416;} | Where {$_.message -like \"*VID_2E8A\u0026PID_0005*\"}\n        $informationParams = @{\n            MessageData = \"`n[Event detected - $($identifierMap[$SourceIdentifier])]\"\n            Tags = $sourceIdentifier\n            InformationAction = [ActionPreference]::Continue\n        }\n        Write-Information @informationParams\n\n        Connect-COMDeviceToWSL -InformationAction Continue\n    }\n}\n\n$HyperVEventParams = @{\n    InputObject = $HyperVWatcher\n    EventName = \"EventRecordWritten\"\n    SourceIdentifier = \"HyperVConnection\"\n    Action =  $Action\n}\n\n$DeviceEventParams = @{\n    InputObject = $DeviceWatcher\n    EventName = \"EventRecordWritten\"\n    SourceIdentifier = \"DeviceConnection\"\n    Action =  $Action\n}\n\n$JobDevice = Register-ObjectEvent @DeviceEventParams\n$JobHyperV = Register-ObjectEvent @HyperVEventParams\n\ntry {\n    $informationParams = @{\n        MessageData = \"`nWatching for device connections`n\"\n        InformationAction = [ActionPreference]::Continue\n    }\n    Write-Information @informationParams\n\n    \u003c#\n    Indefinite wait for 'DeviceConnection' \u0026 'HyperVConnection' events, as they are handled by\n    the $Action script. Wait-Event call prevents the script from exiting, without blocking.\n    NOTE: Use Ctrl-C to exit the script\n    #\u003e\n    Wait-Event\n}\ncatch {\n    Write-Error $_\n}\nfinally {\n    Write-Warning \"Script terminated - WSL \u0026 RPi device connection monitoring stopped.\"\n\n    # Unregister events for each EventSubscriber\n    Get-EventSubscriber -SourceIdentifier \"DeviceConnection\" | Unregister-Event\n    Get-EventSubscriber -SourceIdentifier \"HyperVConnection\" | Unregister-Event\n\n    # Delete the background JobDevice \u0026 JobHyperV background jobs\n    $JobDevice | Remove-Job -Force\n    $JobHyperV | Remove-Job -Force\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandyrids%2Fvoid-wsl-dev","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandyrids%2Fvoid-wsl-dev","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandyrids%2Fvoid-wsl-dev/lists"}