<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/mocking-a-usb-controller-one-byte-at-a-time-mkmpu1edp" -->

---
title: Mocking a USB controller one byte at a time | daily.dev
description: A live coding session where the author reverse-engineers and mocks a USB HID gamepad controller at the protocol level on Linux. The goal is to intercept inputs...
canonical: https://daily.dev/posts/mocking-a-usb-controller-one-byte-at-a-time-mkmpu1edp
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Mocking a USB controller one byte at a time | daily.dev
og:description: A live coding session where the author reverse-engineers and mocks a USB HID gamepad controller at the protocol level on Linux. The goal is to intercept inputs...
og:url: https://daily.dev/posts/mocking-a-usb-controller-one-byte-at-a-time-mkmpu1edp
og:image: https://api.daily.dev/og/posts/MKMPu1edP.png
og:image:alt: Mocking a USB controller one byte at a time
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Mocking a USB controller one byte at a time

**[sphaerophoria](https://daily.dev/sources/sphaerophoria)** · 196 min read · 1 upvotes · 1 comments

## Summary

A live coding session where the author reverse-engineers and mocks a USB HID gamepad controller at the protocol level on Linux. The goal is to intercept inputs from a Gameser G7 Pro controller (which exposes gyro as a mouse and paddles as keyboard keys) and re-expose them as a proper gamepad using the UHID kernel interface and the SDL S-input open protocol. The session covers opening /dev/uhid, constructing HID descriptors, registering a virtual HID device, reading raw HID events, and mapping button/axis state into the S-input report format so Steam recognizes the device with full gyro and paddle support.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.youtube.com/watch?v=Ypp3j9myWtM>

## Questions this post answers

### How do you create a virtual HID device in Linux that Steam will recognize as a gamepad with paddles and gyro?

You open /dev/uhid and write a UHID_CREATE2 event containing an HID report descriptor, then handle UHID_OUTPUT and UHID_GET_REPORT events by writing back UHID_INPUT2 events with the report data. Implementing SDL's sinput protocol on top of this lets Steam detect all four paddles and gyro axes correctly, since sinput was merged into SDL on July 15, 2025 specifically to let non-standard controllers describe their own feature sets dynamically.

_Developers wiring custom HID devices into Steam can track SDL and UHID protocol changes like this one on daily.dev._

### Why does the Gameser G7 Pro controller output its gyro as mouse movement instead of as a native gamepad axis?

The controller's Windows configuration software lets users remap gyro output to behave as a mouse (for aiming) rather than exposing it as a proper controller axis, and it can only output two of the three gyro axes at once rather than all three simultaneously. Paddles are similarly remapped to keyboard key presses rather than native controller buttons, requiring a custom driver to convert both back into standard gamepad inputs.

_Anyone working around quirky controller firmware behavior can find similar hardware deep-dives on daily.dev._

### Why does SDL's UHID-based virtual controller require both gyroscope and accelerometer data to show up correctly in Steam?

Steam calculates full 3D orientation (yaw, pitch, roll) from the combination of gyroscope (angular velocity, degrees per second) and accelerometer (G-force) data together, so a virtual device that reports only gyro values without accelerometer data will not be recognized as having motion controls in Steam's settings panel, even though SDL itself detects the gyro axes fine.

_Developers building motion-control support into custom input devices can follow SDL and Steam input protocol details on daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@trevorsuna** · 0 upvotes

> Mapping the odd gyro and paddle interfaces back into one virtual UHID device is a clever way to make Steam see the controller users expect. Capturing and replaying a few known HID reports would also make regression testing the descriptor changes much easier.

## Similar posts on daily.dev

- [Linux 7.3 To Fix Longstanding Gap In The Native Driver For Original Steam Controller](https://daily.dev/posts/linux-7-3-to-fix-longstanding-gap-in-the-native-driver-for-original-steam-controller-mxvmog3kj) · Phoronix · 0 upvotes · 0 comments
- [An ESP32-S3 can pretend to be a "normal" USB device, and it's one of the most useful tricks it can pull](https://daily.dev/posts/an-esp32-s3-can-pretend-to-be-a-normal-usb-device-and-it-s-one-of-the-most-useful-tricks-it-can-p-usz4svjxh) · XDA Developers · 0 upvotes · 0 comments
- [Sega Master System Controllers, Now With USB C](https://daily.dev/posts/sega-master-system-controllers-now-with-usb-c-swkckgvyt) · Hackaday · 1 upvotes · 0 comments

---

Tags: [#linux](https://daily.dev/tags/linux), [#zig](https://daily.dev/tags/zig)

[View this post on daily.dev](https://daily.dev/posts/mocking-a-usb-controller-one-byte-at-a-time-mkmpu1edp)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Mocking a USB controller one byte at a time","url":"https://daily.dev/posts/mocking-a-usb-controller-one-byte-at-a-time-mkmpu1edp","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/mocking-a-usb-controller-one-byte-at-a-time-mkmpu1edp"},"datePublished":"2026-08-02T01:11:08.318Z","dateModified":"2026-09-14T06:28:42.273Z","description":"A live coding session where the author reverse-engineers and mocks a USB HID gamepad controller at the protocol level on Linux. The goal is to intercept inputs...","image":"https://i.ytimg.com/vi/Ypp3j9myWtM/sddefault.jpg","thumbnailUrl":"https://i.ytimg.com/vi/Ypp3j9myWtM/sddefault.jpg","isAccessibleForFree":true,"articleSection":"sphaerophoria","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"sphaerophoria","logo":"https://media.daily.dev/image/upload/s--dgPUg2T7--/f_auto/v1728806109/logos/sphaerophoria","url":"https://daily.dev/sources/sphaerophoria"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/mocking-a-usb-controller-one-byte-at-a-time-mkmpu1edp","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"linux,zig","timeRequired":"PT196M","video":{"@type":"VideoObject","name":"Mocking a USB controller one byte at a time","description":"A live coding session where the author reverse-engineers and mocks a USB HID gamepad controller at the protocol level on Linux. The goal is to intercept inputs...","thumbnailUrl":"https://i.ytimg.com/vi/Ypp3j9myWtM/sddefault.jpg","uploadDate":"2026-08-02T01:11:08.318Z","duration":"PT196M","url":"https://api.daily.dev/r/MKMPu1edP","embedUrl":"https://www.youtube.com/embed/Ypp3j9myWtM"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"sphaerophoria","item":"https://daily.dev/sources/sphaerophoria"},{"@type":"ListItem","position":3,"name":"Mocking a USB controller one byte at a time"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/mocking-a-usb-controller-one-byte-at-a-time-mkmpu1edp","comment":[{"@type":"Comment","text":"Mapping the odd gyro and paddle interfaces back into one virtual UHID device is a clever way to make Steam see the controller users expect. Capturing and replaying a few known HID reports would also make regression testing the descriptor changes much easier.","datePublished":"2026-08-02T04:12:10.263Z","url":"https://daily.dev/posts/MKMPu1edP#c-c8Qq7yxto","author":{"@type":"Person","name":"Trevor Suna","url":"https://daily.dev/trevorsuna","image":"https://media.daily.dev/image/upload/s--dZ7gXxpp--/f_auto/v1784081551/avatars/avatar_EMoP47rpuw8DNjhp6R1b6?_a=BAMAMicg0"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/mocking-a-usb-controller-one-byte-at-a-time-mkmpu1edp#faq","mainEntity":[{"@type":"Question","name":"How do you create a virtual HID device in Linux that Steam will recognize as a gamepad with paddles and gyro?","acceptedAnswer":{"@type":"Answer","text":"You open /dev/uhid and write a UHID_CREATE2 event containing an HID report descriptor, then handle UHID_OUTPUT and UHID_GET_REPORT events by writing back UHID_INPUT2 events with the report data. Implementing SDL's sinput protocol on top of this lets Steam detect all four paddles and gyro axes correctly, since sinput was merged into SDL on July 15, 2025 specifically to let non-standard controllers describe their own feature sets dynamically. Developers wiring custom HID devices into Steam can track SDL and UHID protocol changes like this one on daily.dev."}},{"@type":"Question","name":"Why does the Gameser G7 Pro controller output its gyro as mouse movement instead of as a native gamepad axis?","acceptedAnswer":{"@type":"Answer","text":"The controller's Windows configuration software lets users remap gyro output to behave as a mouse (for aiming) rather than exposing it as a proper controller axis, and it can only output two of the three gyro axes at once rather than all three simultaneously. Paddles are similarly remapped to keyboard key presses rather than native controller buttons, requiring a custom driver to convert both back into standard gamepad inputs. Anyone working around quirky controller firmware behavior can find similar hardware deep-dives on daily.dev."}},{"@type":"Question","name":"Why does SDL's UHID-based virtual controller require both gyroscope and accelerometer data to show up correctly in Steam?","acceptedAnswer":{"@type":"Answer","text":"Steam calculates full 3D orientation (yaw, pitch, roll) from the combination of gyroscope (angular velocity, degrees per second) and accelerometer (G-force) data together, so a virtual device that reports only gyro values without accelerometer data will not be recognized as having motion controls in Steam's settings panel, even though SDL itself detects the gyro axes fine. Developers building motion-control support into custom input devices can follow SDL and Steam input protocol details on daily.dev."}}]}
```

