Hannikainen's blog

Snippet: Prevent USB devices from sleeping



This little C program prevents USB devices from sleeping while under aggressive power saving settings. Grab your USB devices from lsusb and throw your mouse/keyboard into the devices-array.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>

/*
Throw into Makefile & compile with sudo make:
no_mouse_sleep: no-mouse-sleep.c
	gcc no-mouse-sleep.c -o no_mouse_sleep -Wall -Wextra -Werror
	sudo chown root:root no_mouse_sleep
	sudo chmod +s no_mouse_sleep
*/

const char *dev_path = "/sys/bus/usb/devices/";

// Find your device with `lsusb`
const char *devices[] = {
    "046d", "c31c", // Logitech, Inc. Keyboard K120
    "8564", "4000", // Transcend Information, Inc. RDF8
    "046d", "c069", // Logitech, Inc. M-U0007 [Corded Mouse M500]
};

int main() {
    if(setuid(0)) {
        perror("Couldn't set uid to root");
        return 1;
    }
    if(setgid(0)) {
        perror("Couldn't set gid to root");
        return 1;
    }

    DIR *dp;
    struct dirent *ep;
    dp = opendir(dev_path);
    if (!dp) {
        perror("Couldn't open usb device list");
        return 1;
    }
    while ((ep = readdir(dp))) {
        if(ep->d_name && ep->d_name[0] != '.') {
            char product[5] = {0}, vendor[5] = {0};

            size_t path_len = sizeof(char) * (strlen(dev_path) + strlen(ep->d_name) + strlen("/power/control") + 1);
            char *path = calloc(path_len + 1, 1);

            snprintf(path, path_len, "%s%s/idProduct", dev_path, ep->d_name);
            FILE *fp = fopen(path, "r");
            if (!fp) {
                free(path);
                continue;
            }
            fread(product, 4, sizeof(char), fp);
            fclose(fp);

            snprintf(path, path_len, "%s%s/idVendor", dev_path, ep->d_name);
            fp = fopen(path, "r");
            if (!fp) {
                free(path);
                continue;
            }
            fread(vendor, 4, sizeof(char), fp);
            fclose(fp);

            for (unsigned int i = 0; i < sizeof(devices)/sizeof(devices[0])/2; i++) {
                if (!strcmp(vendor, devices[i*2]) && !strcmp(product, devices[i*2+1])) {
                    snprintf(path, path_len, "%s%s/power/control", dev_path, ep->d_name);
                    fp = fopen(path, "w");
                    if (!fp) {
                        perror("Couldn't open power control file for writing");
                        continue;
                    }
                    printf("Disabling autosuspend for %s:%s\n", vendor, product);
                    fwrite("on\n", 4, sizeof(char), fp);
                    fclose(fp);
                }
            }

            free(path);
        }
    }
    closedir(dp);
    return 0;
}
Copyright (c) 2024 Jaakko Hannikainen