How Do I Fix Calling a Method from the Linux Repo in My Code?

As a Ruby developer exploring the lower level Linux world, I recently ventured into trying to control the lights on PlayStation 2 Buzz controllers (wired). I discovered a promising method in the Linux kernel source but hit a few brick walls. Here’s the journey, the mistakes, and how I improved the code and added more functionality for practice.

The Goal

Control the LEDs on PS2 Buzz controllers from a Linux system using C. The method I want to invoke is:

static void buzz_set_leds(struct sony_sc *sc)

Found in the Linux kernel source file:

/linux/drivers/hid/hid-sony.c (line 1512)

Initial (Fail) Attempt

I tried this C code:

#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include <linux/power_supply.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/idr.h>
#include <linux/input/mt.h>

#include "hid-ids.h"

However, compiling it gave the error:

fatal error: linux/device.h: No such file or directory

Explanation of the Error

This error occurs because you’re trying to include Linux kernel headers in user-space C code. Kernel headers (like linux/device.h) are only available and meaningful inside the Linux kernel or when building kernel modules not user-space applications.

Mistake: Including kernel-space headers in user-space code.

Correct Approach: Use user-space interfaces like libusb, hidraw, or udev, instead of trying to directly compile kernel source code.

How to Actually Control Buzz LEDs

If buzz_set_leds() is defined inside the kernel module (hid-sony.c), and your device is recognized by the system, you should interact with it using user-space HID or USB libraries.

Using hidraw in C to Send LED Command

Here’s a minimal practice program to write data to a hidraw device:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/hidraw.h>
#include <sys/ioctl.h>

int main() {
const char *device = "/dev/hidraw0"; // Adjust as needed
int fd = open(device, O_RDWR | O_NONBLOCK);

if (fd < 0) {
perror("Failed to open device");
return 1;
}

unsigned char buf[8];
memset(buf, 0x00, sizeof(buf));

// Construct an example LED command (hypothetical, may need adjustment)
buf[0] = 0x00; // Report ID
buf[1] = 0x01; // LED control code (depends on device)
buf[2] = 0x0F; // Turn on all LEDs

ssize_t res = write(fd, buf, sizeof(buf));
if (res < 0) {
perror("Write failed");
} else {
printf("Wrote %zd bytes to %s\n", res, device);
}

close(fd);
return 0;
}

More Practice Functionality

Add the following features to extend the program:

Detect HID Device Name

char name[256];
if (ioctl(fd, HIDIOCGRAWNAME(256), name) >= 0)
printf("Device name: %s\n", name);

Send Different LED Patterns

Loop through various LED configurations:

for (int i = 0; i < 16; ++i) {
buf[2] = i; // Pattern
write(fd, buf, sizeof(buf));
usleep(500000); // 0.5 seconds
}

Add CLI Input

Allow users to pass LED states from command line:

if (argc > 1)
buf[2] = (unsigned char)strtol(argv[1], NULL, 0);

Better Way to Work With Kernel Code

If you really want to call buzz_set_leds() from C directly, you’d need to:

  1. Build your own custom kernel module
  2. Include the relevant source code (sony_sc, buzz_set_leds) inside your module
  3. Hook into the Buzz device properly via kernel APIs

However, this is strongly discouraged unless you’re developing drivers or customizing the kernel itself.

Conclusion

You don’t need to compile or include kernel source code to control your Buzz controller LEDs. Instead, use user-space interfaces like hidraw or libusb. That buzz_set_leds() method is used internally by the kernel driver your interaction should go through /dev/hidrawX.

Related blog posts