Skip to content

Repository files navigation

USB HID + CDC-ACM Stack

C Build Tests CI Domain License

A deterministic, host-side USB 2.0 composite-device simulation implementing a HID keyboard-style interface and CDC-ACM control interface, with explicit protocol validation, descriptor handling, endpoint management, a replaceable HAL, virtual enumeration, negative testing, and reproducible CMake/CTest validation.


Project at a Glance

This repository is a portfolio-grade embedded-systems project built to demonstrate how a firmware-oriented USB device stack can be structured as a set of testable engineering layers rather than as one monolithic implementation.

The simulated device exposes:

  • USB device state management
  • Standard control requests
  • 8-byte SETUP packet encoding/decoding
  • Device, configuration, string, HID and HID-report descriptors
  • HID class requests and deterministic reports
  • CDC-ACM line-coding and control-line-state requests
  • Endpoint configuration and packet-size validation
  • A replaceable hardware-abstraction layer
  • A deterministic virtual USB host and bus
  • Positive, integration and negative tests
  • Python validation utilities
  • CMake/CTest build and CI automation
  • Architecture, protocol, testing and design documentation

Scope

This is deliberately a host-side protocol simulation.

It does not claim physical USB electrical compliance, PHY timing validation, controller-register integration, DMA/cache behavior, or USB-IF certification. Those concerns belong behind the HAL boundary in a hardware-backed implementation.


Why This Project Matters

The engineering boundary demonstrated by the project is:

USB request bytes
       │
       ▼
SETUP packet decoder
       │
       ▼
Control-transfer validation
       │
       ├───────────────┐
       ▼               ▼
Standard requests   Class requests
       │               │
       ▼          ┌────┴────┐
Device state      HID      CDC-ACM
       │          │          │
       └──────────┴──────────┘
                  │
                  ▼
          Descriptor / endpoint
               validation
                  │
                  ▼
             HAL boundary
                  │
                  ▼
          Virtual USB backend

The project therefore exercises several areas relevant to embedded C, firmware, USB protocol development, device drivers, validation engineering and systems programming.

Engineering area Demonstrated implementation
Embedded C C11, fixed-width types, explicit status codes
USB protocol SETUP packets, standard requests, class requests
Device state Default → Addressed → Configured
Descriptors Device, configuration, string and HID descriptors
HID Report descriptor, input/output report handling
CDC-ACM Line coding and control-line-state requests
Endpoint management Direction/type/packet-size validation
Defensive programming Null, length, state and capability checks
HAL architecture Transport operations isolated behind usb_hal_t
Virtual validation Deterministic host/bus simulation
Testing 17 CTest cases including negative paths
Automation CMake + CTest + GitHub Actions
Tooling Python descriptor, trace and test-report utilities
Documentation Architecture, protocol, design and testing notes

Architecture

                    ┌─────────────────────────┐
                    │     Examples / Tests     │
                    └────────────┬────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │     Virtual USB Host     │
                    │  enumeration + requests  │
                    └────────────┬────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │    USB Device Core       │
                    │ state + control transfer │
                    └────────────┬────────────┘
                                 │
              ┌──────────────────┴──────────────────┐
              │                                     │
   ┌──────────▼──────────┐              ┌──────────▼──────────┐
   │ Standard Requests   │              │ Class Request Layer │
   │ descriptors/state   │              │ HID + CDC-ACM       │
   └──────────┬──────────┘              └──────────┬──────────┘
              │                                     │
              └──────────────────┬──────────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │ Descriptor / Endpoint    │
                    │ validation + management  │
                    └────────────┬────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │     USB HAL boundary     │
                    └────────────┬────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │  Deterministic simulator │
                    └─────────────────────────┘

Module ownership

Module Responsibility
src/usb Device state, control dispatch, standard requests and descriptors
src/common Shared validation and status handling
usb/protocol Byte-level SETUP and transfer validation
hid HID semantics and report representation
cdc CDC-ACM semantics and functional descriptors
include/hal Transport abstraction contract
hal/simulation In-memory endpoint transport
simulation/usb_host Host-side enumeration and class transactions
simulation/usb_bus Bus/HAL attachment boundary
tests Unit, integration and negative coverage
tools/python Host-side validation/reporting utilities

Enumeration Flow

The virtual host performs a deterministic control-transfer sequence:

RESET / DEFAULT
      │
      ├── GET_DESCRIPTOR(Device)
      │
      ├── SET_ADDRESS(1)
      │
      ▼
ADDRESSED
      │
      ├── GET_DESCRIPTOR(Configuration, first 9 bytes)
      │
      ├── GET_DESCRIPTOR(Configuration, full descriptor)
      │
      ├── Validate descriptor chain
      │
      ├── SET_CONFIGURATION(1)
      │
      ▼
CONFIGURED
      │
      ├── GET_CONFIGURATION
      ├── HID class request
      └── CDC-ACM class request

The host intentionally retrieves the configuration descriptor header first, obtains wTotalLength, then requests the complete descriptor. This makes the simulation closer to a real host enumeration flow than simply reading a static blob.


Core Features

1. USB Device State Machine

The device models:

DEFAULT
   │
   │ SET_ADDRESS(1..127)
   ▼
ADDRESSED
   │
   │ SET_CONFIGURATION(1)
   ▼
CONFIGURED
   │
   │ SET_CONFIGURATION(0)
   ▼
ADDRESSED

SET_ADDRESS(0) returns the device to DEFAULT.

Configuration changes reset non-control endpoints and interface alternate settings.

The implementation rejects invalid state transitions instead of silently mutating device state.


2. SETUP Packet Processing

USB control transfers begin with an 8-byte SETUP packet.

The protocol layer provides explicit little-endian conversion:

usb_status_t usb_setup_decode(const uint8_t raw[8],
                              usb_setup_packet_t *setup);

usb_status_t usb_setup_encode(const usb_setup_packet_t *setup,
                              uint8_t raw[8]);

The control layer then validates:

  • request type
  • direction
  • recipient
  • request length
  • OUT payload presence
  • supported request classes
  • destination-buffer capacity

3. Standard USB Requests

The stack implements deterministic handling for:

GET_STATUS
SET_FEATURE
CLEAR_FEATURE
SET_ADDRESS
GET_DESCRIPTOR
GET_CONFIGURATION
SET_CONFIGURATION
GET_INTERFACE
SET_INTERFACE

Unsupported vendor/reserved request types are rejected.

Remote-wakeup state is modeled for the device recipient.


4. Composite Descriptor Model

The configuration contains three interfaces:

Interface 0
└── HID keyboard-style interface
    ├── EP1 IN  interrupt, 8 bytes
    └── EP1 OUT interrupt, 1 byte

Interface 1
└── CDC-ACM control interface
    └── EP2 IN  interrupt, 8 bytes

Interface 2
└── CDC data interface
    ├── EP3 IN  bulk, 64 bytes
    └── EP3 OUT bulk, 64 bytes

The configuration descriptor advertises a total length of 100 bytes and three interfaces.

Descriptor-chain validation checks every descriptor boundary before accepting a descriptor stream.


5. HID

The HID layer provides:

GET_REPORT
SET_REPORT

The deterministic report model contains:

  • 63-byte keyboard-style HID report descriptor
  • 8-byte input report
  • 1-byte output report

The simulation deliberately keeps report behavior deterministic so that tests are reproducible.


6. CDC-ACM

The CDC control interface implements:

SET_LINE_CODING
GET_LINE_CODING
SET_CONTROL_LINE_STATE

Line coding is represented as:

typedef struct {
    uint32_t baud;
    uint8_t stop_bits;
    uint8_t parity;
    uint8_t data_bits;
} cdc_line_coding_t;

Validation rejects:

  • null payloads
  • incorrect seven-byte payloads
  • zero baud rates
  • unsupported stop-bit values
  • unsupported parity values
  • invalid data-bit widths
  • invalid control-line-state bits

7. Endpoint Manager

The endpoint manager maintains independent IN and OUT state for each endpoint number.

Each endpoint records:

configured
enabled
direction
transfer type
maximum packet size

Configuration is validated before state mutation.

Packet checks reject transfers larger than the endpoint's maximum packet size.


8. Hardware Abstraction Layer

Hardware-dependent operations are represented by:

typedef struct {
    int (*init)(void);
    int (*ep_write)(uint8_t endpoint,
                    const uint8_t *data,
                    size_t length);
    int (*ep_read)(uint8_t endpoint,
                   uint8_t *data,
                   size_t capacity,
                   size_t *length);
} usb_hal_t;

The simulation backend provides an in-memory endpoint transport.

The separation means a hardware controller adapter can replace the simulation backend without changing the protocol semantics.


9. Virtual Host and Bus

The host-side simulator exercises the same public device-control API that a hardware controller adapter would feed.

The virtual bus provides the explicit HAL attachment boundary.

This keeps:

protocol semantics

separate from:

transport implementation

which is an important embedded-systems architectural boundary.


Testing Strategy

The project uses CTest as the test execution layer.

Current validation contains:

17 tests
17 passed
0 failed
100% pass rate

Test categories

Category Coverage
USB unit tests setup packets, state machine, descriptors, endpoints, standard requests
HID tests reports and class behavior
CDC tests line coding and class requests
Integration tests enumeration, HID, CDC and HAL transport
Negative tests malformed descriptors, invalid transfers and invalid requests

Important negative-path coverage

The suite explicitly exercises:

  • null pointers
  • missing OUT payloads
  • incorrect OUT lengths
  • undersized IN buffers
  • invalid USB request types
  • unsupported requests
  • invalid addresses
  • invalid configuration transitions
  • invalid endpoint numbers
  • invalid endpoint directions
  • excessive packet sizes
  • malformed descriptor lengths
  • unsupported HID report IDs
  • invalid CDC line coding

Build

Prerequisites

  • C compiler with C11 support
  • CMake 3.16 or newer
  • CTest
  • Python 3 for host utilities

On a Unix-like system:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
ctest --test-dir build --output-on-failure

The build enables aggressive warnings and treats warnings as errors on GCC/Clang:

-Wall
-Wextra
-Wpedantic
-Wshadow
-Wconversion
-Wsign-conversion
-Werror

Debug assertions remain enabled for validation builds.


Run the Examples

After building:

./build/hid_demo
./build/cdc_demo
./build/composite_demo

Expected behavior:

HID demo: configured, input report 8 bytes
CDC demo: configured, line coding 7 bytes, baud 115200
Composite demo: HID + CDC device configured

On Windows/MSYS2 environments, the generated executable may carry .exe.


Run the Project Scripts

./scripts/build.sh
./scripts/run_tests.sh

The scripts intentionally remain thin wrappers around the canonical CMake/CTest commands so that local and CI validation use the same build system.


Python Utilities

The repository contains small standard-library-only host tools.

Descriptor validation

python3 tools/python/descriptor_validator.py fixtures/descriptors/composite_descriptor.hex

SETUP trace parsing

printf 'IN GET_DESCRIPTOR 0x0100 0x0000 18\n' \
  | python3 tools/python/usb_trace_parser.py

Test report

python3 tools/python/test_report.py 17 17

No third-party Python packages are required.


Repository Structure

.
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── workflows/
│   │   └── ci.yml
│   └── pull_request_template.md
│
├── docs/
│   ├── architecture/
│   │   ├── module_boundaries.md
│   │   └── system_architecture.md
│   ├── design/
│   │   ├── error_handling.md
│   │   └── state_machine.md
│   ├── protocol/
│   │   ├── cdc_acm.md
│   │   ├── descriptors.md
│   │   ├── hid.md
│   │   └── usb_control_transfers.md
│   └── testing/
│       ├── test_matrix.md
│       └── test_strategy.md
│
├── examples/
│   ├── cdc_demo/
│   ├── composite_demo/
│   └── hid_demo/
│
├── fixtures/
│   ├── descriptors/
│   ├── reports/
│   └── setup_packets/
│
├── hal/
│   └── simulation/
│
├── hid/
├── cdc/
├── include/
│   ├── cdc/
│   ├── hal/
│   ├── hid/
│   └── usb/
│
├── scripts/
├── simulation/
│   ├── scenarios/
│   ├── usb_bus/
│   └── usb_host/
│
├── src/
│   ├── common/
│   └── usb/
│
├── tests/
│   ├── cdc/
│   ├── hid/
│   ├── integration/
│   ├── negative/
│   ├── usb/
│   └── test_common.h
│
├── tools/
│   ├── descriptor_dump/
│   └── python/
│
├── CMakeLists.txt
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Doxyfile
├── LICENSE
├── SECURITY.md
└── README.md

CI and Repository Engineering

GitHub Actions validates the repository on pushes and pull requests to main.

The workflow performs:

  1. CMake configuration
  2. Release build
  3. CTest execution
  4. Python syntax validation
  5. Descriptor fixture validation

The repository also includes:

.clang-format
.editorconfig
.gitignore
CONTRIBUTING.md
CODE_OF_CONDUCT.md
SECURITY.md
CHANGELOG.md
Doxyfile

The goal is to keep source formatting, review expectations, security reporting and automated verification explicit.


Documentation

Engineering documentation is organized by responsibility:

Document area Purpose
docs/architecture module ownership and system layering
docs/protocol USB control, descriptor, HID and CDC behavior
docs/design state-machine and error-handling decisions
docs/testing coverage strategy and test matrix

Doxygen configuration is included for API documentation generation:

doxygen Doxyfile

Generated documentation is treated as a build artifact rather than source-controlled output.


Validation Evidence

The repository contains development screenshots under:

screenshots/

These are retained as historical development evidence. The authoritative release validation is always the reproducible command sequence in this README and the CI workflow, rather than a screenshot.

For a fresh verification:

rm -rf build
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
ctest --test-dir build --output-on-failure
./build/hid_demo
./build/cdc_demo
./build/composite_demo
python3 tools/python/descriptor_validator.py fixtures/descriptors/composite_descriptor.hex

Engineering Boundaries and Limitations

This project is intentionally a protocol simulation.

It does not validate:

  • physical USB signaling
  • electrical timing
  • USB PHY behavior
  • controller registers
  • DMA/cache coherency
  • real host-driver installation
  • USB-IF certification
  • electrical interoperability
  • real HID keyboard operation
  • real serial-port transport over hardware USB

Those limitations are not hidden; they define the scope of the project.

A production hardware port would retain the protocol/class layers and replace the HAL and controller integration boundary.


Future Hardware Port

A natural next-stage implementation would add:

USB controller driver
       │
       ├── endpoint FIFO / DMA
       ├── interrupt handling
       ├── controller reset
       ├── PHY integration
       └── cache / memory barriers

while preserving:

device state
control requests
descriptors
HID
CDC-ACM
validation
tests

This is the architectural reason for keeping transport-specific operations behind the HAL.


Project Status

Final Engineering Polish

Status: Complete for the documented host-side simulation scope.

Validated locally:

  • C11 compilation
  • warnings-as-errors build
  • USB SETUP encode/decode
  • standard request handling
  • device state transitions
  • composite descriptor validation
  • endpoint configuration
  • HID report path
  • CDC-ACM line coding path
  • virtual host enumeration
  • virtual HAL transport
  • negative-path validation
  • 17/17 CTest tests
  • example applications
  • Python utility validation
  • CI workflow
  • engineering documentation
  • repository metadata and governance

Important claim boundary

This repository should be described as a host-side USB protocol/composite-device simulation, not as a physically validated USB firmware stack.

That distinction is deliberate and technically important.


Author

Vedula China Venkata Prasanth

Embedded Systems & Firmware Developer — Project Author / Lead Developer

B.Tech — Electronics and Communication Engineering (ECE)
Lendi Institute of Engineering and Technology
Vizianagaram, Andhra Pradesh, India
Expected Graduation: 2027

Engineering Focus

Embedded Systems
Embedded C
Firmware Development
Embedded Linux
Linux Device Drivers
RTOS
Systems Programming
Protocol Engineering
Hardware / Software Interfaces

Portfolio Objective

This repository is part of a broader embedded-systems portfolio focused on demonstrating:

low-level programming
        ↓
hardware/software boundaries
        ↓
protocol implementation
        ↓
defensive systems engineering
        ↓
automated validation
        ↓
professional documentation

Contact

  • GitHub: github.com/prasanth-vedula
  • Email: prasanthvedula2006@gmail.com

License

MIT License.

See LICENSE for the complete license text.


Final Engineering Perspective

The purpose of this project is not merely to show that C code can compile.

It demonstrates an engineering workflow:

Requirements
    ↓
Architecture
    ↓
Protocol model
    ↓
Implementation
    ↓
Validation
    ↓
Negative testing
    ↓
Integration testing
    ↓
Automation
    ↓
Documentation
    ↓
Release packaging

The resulting repository is intentionally transparent about what has been implemented, what has been validated, and what remains outside the simulation boundary.

That is the standard expected from a serious embedded-systems portfolio: strong implementation, explicit assumptions, reproducible evidence, defensive interfaces, and honest engineering claims.

About

Deterministic host-testable USB HID + CDC-ACM composite device stack in portable C11.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages