Featured image of post How Plug and Play Works in Linux

How Plug and Play Works in Linux


Plug & Play on Steam


How Plug and Play in C++ and Linux


What Is Plug and Play, Really?

Plug and Play is that magical technology that makes your mouse, keyboard, USB drive, or even a toaster (if you’re into weird hardware hacking) work the moment you plug it into your computer.

In Windows, PnP is mostly automatic. You plug something in, it installs drivers, and boom—you’re good to go. In Linux? Well… let’s just say it’s a little more “hands-on.”


The Linux PnP Model

Linux handles Plug and Play through the udev system, which is part of the device manager. Here’s how it typically works:

  1. You plug in a device.
  2. The kernel detects it and assigns it a device node (like /dev/sdb for a USB drive).
  3. udev creates a device file in /dev/, so you can interact with it.
  4. System services (or your C++ program) handle it as needed.

You can see device events in real-time using:

1
udevadm monitor

Plug in a device while running that command, and you’ll see events pop up like a hacker movie.


Writing a C++ Program to Detect Plug and Play Events

If you want to interact with Plug and Play in C++, you’ll need to listen to udev events. Thankfully, there’s a library called libudev that helps with this.

First, install libudev-dev (if you haven’t already):

1
sudo apt install libudev-dev

Now, let’s write a simple C++ program to detect device events:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <libudev.h>
#include <iostream>

int main() {
    struct udev *udev = udev_new();
    if (!udev) {
        std::cerr << "Failed to initialize udev" << std::endl;
        return 1;
    }

    struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev");
    udev_monitor_enable_receiving(mon);
    int fd = udev_monitor_get_fd(mon);

    std::cout << "Listening for device events..." << std::endl;

    while (true) {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(fd, &fds);

        if (select(fd + 1, &fds, nullptr, nullptr, nullptr) > 0) {
            struct udev_device *dev = udev_monitor_receive_device(mon);
            if (dev) {
                std::cout << "Device event: " << udev_device_get_action(dev) << " - "
                          << udev_device_get_devnode(dev) << std::endl;
                udev_device_unref(dev);
            }
        }
    }
    udev_unref(udev);
    return 0;
}

Compile and run it with:

1
2
g++ -o pnp_listener pnp_listener.cpp -ludev
./pnp_listener

Now plug in a USB device, and watch the magic happen!


Handling Specific Devices

If you want to handle a specific type of device, you can filter based on properties like vendor ID or device type. Modify the event listener like this:

1
2
3
4
const char* subsystem = udev_device_get_subsystem(dev);
if (subsystem && std::string(subsystem) == "usb") {
    std::cout << "USB device detected: " << udev_device_get_devnode(dev) << std::endl;
}

Why Linux PnP Is Both Awesome and Annoying

The Awesome Parts:

  • It’s lightweight and doesn’t hog system resources.
  • Full control! No mysterious background processes eating your CPU.
  • Works on embedded systems (hello, Raspberry Pi nerds!).

The Annoying Parts:

  • Debugging can be painful.
  • Different distributions handle things slightly differently (of course they do!).
  • Some devices just don’t play nice (looking at you, weird off-brand USB dongles).